private async Task SaveResponse(PgPurchaseOrderResponse payload, Cost cost, SystemAdminUserIdentity adminUser) { var purchaseOrderData = await _customDataService .GetCustomData <PgPurchaseOrderResponse>(cost.LatestCostStageRevisionId.Value, CustomObjectDataKeys.PgPurchaseOrderResponse) ?? new PgPurchaseOrderResponse(); _mapper.Map(payload, purchaseOrderData); if (string.Compare(payload.ApprovalStatus, ApprovalStatuses.Rejected, StringComparison.OrdinalIgnoreCase) == 0) { //null requisition ID should be allowed here purchaseOrderData.Requisition = payload.Requisition; purchaseOrderData.ApprovalStatus = payload.ApprovalStatus; } await _customDataService.Save(cost.LatestCostStageRevisionId.Value, CustomObjectDataKeys.PgPurchaseOrderResponse, purchaseOrderData, adminUser); await UpdatePaymentDetails(cost.LatestCostStageRevisionId.Value, purchaseOrderData, adminUser); var logEntries = new List <IActivityLogEntry> { new PoCreated(cost.CostNumber, purchaseOrderData.PoNumber, adminUser), new GoodsReceiptAllocated(cost.CostNumber, purchaseOrderData.GrNumber, adminUser), new RequisitionNumber(cost.CostNumber, purchaseOrderData.Requisition, adminUser) }; await _activityLogService.LogRange(logEntries); }
public async Task Consume(PurchaseOrderResponse message) { var cost = await _efContext.Cost .AsNoTracking() .FirstOrDefaultAsync(c => c.CostNumber == message.CostNumber); if (cost == null) { throw new XmgException($"Could't find the cost with Cost Number {message.CostNumber}") { ClientName = message.ClientName }; } try { var payload = JsonConvert.DeserializeObject <PgPurchaseOrderResponse>(message.Payload.ToString()); var adminUser = await _efContext.CostUser.FirstOrDefaultAsync(u => u.Email == ApprovalMemberModel.BrandApprovalUserEmail); var adminUserIdentity = new SystemAdminUserIdentity(adminUser); await _efContext.InTransactionAsync(async() => { await SaveResponse(payload, cost, adminUserIdentity); await ActionOnCost(message, payload, cost, adminUserIdentity); if (payload.ApprovalStatus == ApprovalStatuses.Approved || // ADC-1731 (payload.ApprovalStatus == ApprovalStatuses.AwaitingDecisionInCost && ApprovalRequired(cost) && await HasCostBeenApproved(cost, payload))) { // payload details are required in the email so the response needs to be saved before we can send the email await _emailNotificationService.CostHasBeenApproved(cost.Id, adminUserIdentity.Id, ApprovalType.Brand.ToString()); } }, () => _eventService.SendAllPendingAsync()); } catch (Exception ex) { var messageJson = JsonConvert.SerializeObject(message); throw new XmgException($"Failed to consume response message: {messageJson} from Coupa {ex}", ex); } }
private async Task <PgPaymentDetails> UpdatePaymentDetails( Guid costStageRevisionId, PgPurchaseOrderResponse purchaseOrderData, SystemAdminUserIdentity adminUser) { var paymentDetails = await _customDataService .GetCustomData <PgPaymentDetails>(costStageRevisionId, CustomObjectDataKeys.PgPaymentDetails) ?? new PgPaymentDetails(); _mapper.Map(purchaseOrderData, paymentDetails); if (string.Compare(purchaseOrderData.ApprovalStatus, ApprovalStatuses.Rejected, StringComparison.OrdinalIgnoreCase) == 0) { //null requisition ID should be allowed here paymentDetails.Requisition = purchaseOrderData.Requisition; } await _customDataService.Save(costStageRevisionId, CustomObjectDataKeys.PgPaymentDetails, paymentDetails, adminUser); return(paymentDetails); }
public async Task Handle_ErrorMessageFromXmg_ShouldRejectCost(ResponseErrorType errorType) { // Arrange var cost = MockCost(); var payload = new { errorMessages = new[] { new { type = ((int)errorType).ToString(), message = "Error messages" } } }; var message = new PurchaseOrderErrorResponse { ActivityType = "Error", ClientName = BuType.Pg.ToString(), EventTimeStamp = DateTime.Now, CostNumber = cost.CostNumber, Payload = JObject.Parse(JsonConvert.SerializeObject(payload)) }; var response = new ApprovalServiceActionResult { Success = true, ApprovalType = "Brand" }; var costUser = new CostUser { GdamUserId = "alsjdnaljsdn" }; var adminUser = new CostUser { Email = ApprovalMemberModel.BrandApprovalUserEmail }; var adminUserIdentity = new SystemAdminUserIdentity(adminUser); var costUserSetMock = _efContextMock.MockAsyncQueryable(new List <CostUser> { costUser, adminUser }.AsQueryable(), context => context.CostUser); costUserSetMock.Setup(u => u.FindAsync(It.IsAny <Guid>())).ReturnsAsync(costUser); _approvalServiceMock.Setup(a => a.Reject(cost.Id, adminUserIdentity, BuType.Pg, "Error messages", SourceSystem.Coupa)).ReturnsAsync(response); // Act await _handler.Handle(message); // Assert _approvalServiceMock.Verify(s => s.Reject(cost.Id, adminUserIdentity, BuType.Pg, "Error messages", SourceSystem.Coupa)); }
private async Task Reject(PurchaseOrderResponse message, PurchaseOrderErrorMessage errorMessage) { var adminUser = await _efContext.CostUser.FirstOrDefaultAsync(u => u.Email == ApprovalMemberModel.BrandApprovalUserEmail); var adminUserIdentity = new SystemAdminUserIdentity(adminUser); var cost = await _efContext.Cost.SingleAsync(c => c.CostNumber == message.CostNumber); var result = await _approvalService.Reject(cost.Id, adminUserIdentity, GetBuType(message.ClientName), errorMessage.Message, SourceSystem.Coupa); if (!result.Success) { await OnTechnicalError(message.CostNumber, $"Failed to reject cost due to error! {result.Messages}"); } else { var error = $"Cost {message.CostNumber} rejected due to : {errorMessage.Type.GetStringValue()} {errorMessage.Message}"; _logger.Information(error); await _emailNotificationService.CostHasBeenRejected( cost.Id, adminUser.Id, ApprovalType.Brand.ToString(), error); } }
private async Task ActionOnCost(PurchaseOrderResponse message, PgPurchaseOrderResponse payload, Cost cost, SystemAdminUserIdentity adminUser) { var adminUserId = adminUser.Id; OperationResponse response; switch (message.ActivityType) { case ActivityTypes.Updated: switch (payload.ApprovalStatus) { case ApprovalStatuses.Approved: if (ApprovalRequired(cost)) { await ApproveCost(cost.Id, adminUser); } break; case ApprovalStatuses.Rejected: message.Payload.TryGetValue(nameof(PgPurchaseOrderResponse.Comments), StringComparison.OrdinalIgnoreCase, out var commentsToken); var comments = commentsToken != null?commentsToken.ToObject <string>() : string.Empty; response = await _approvalService.Reject(cost.Id, adminUser, BuType.Pg, comments, SourceSystem.Coupa); if (response.Success) { await _emailNotificationService.CostHasBeenRejected(cost.Id, adminUserId, ApprovalType.Brand.ToString(), comments); } break; // ADC-1731 Dealing with COUPA's limitations case ApprovalStatuses.AwaitingDecisionInCost: if (ApprovalRequired(cost) && await HasCostBeenApproved(cost, payload)) { await ApproveCost(cost.Id, adminUser); } break; } break; case ActivityTypes.Cancelled: response = await _costActionService.CompleteCancel(cost.Id, BuType.Pg); if (response.Success) { await _emailNotificationService.CostHasBeenCancelled(cost.Id); } break; case ActivityTypes.Recalled: await _costActionService.CompleteRecall(cost.Id, adminUser); break; } }
public async Task UpdateLedgerMaterialCodes(Guid costStageRevisionId) { var costStageRevision = await _efContext.CostStageRevision .Include(csr => csr.StageDetails) .Include(csr => csr.ExpectedAssets) .Include(csr => csr.CostStage) .ThenInclude(cs => cs.Cost) .FirstOrDefaultAsync(csr => csr.Id == costStageRevisionId); var costType = costStageRevision.CostStage.Cost.CostType.ToString(); var multipleOvalTypesOption = await _efContext.DictionaryEntry .Where(d => d.Dictionary.Name == Constants.DictionaryNames.OvalType && d.Key == MultipleOptions) .FirstAsync(); var multipleMediaTypesOption = await _efContext.DictionaryEntry .Where(d => d.Dictionary.Name == Constants.DictionaryNames.MediaType && d.Key == MultipleOptions) .FirstAsync(); var stageDetailsForm = JsonConvert.DeserializeObject <PgStageDetailsForm>(costStageRevision.StageDetails.Data); // Eligible for Production cost var contentTypeId = stageDetailsForm.ContentType?.Id; var productionTypeId = stageDetailsForm.ProductionType?.Id; var expectedAssets = costStageRevision.ExpectedAssets; Guid?mediaTypeId = null; Guid?ovalTypeId = null; var mediaTypeIds = expectedAssets.Where(a => a.MediaTypeId != null).Select(a => a.MediaTypeId).Distinct().ToArray(); if (mediaTypeIds.Length > 0) { mediaTypeId = mediaTypeIds.Length == 1 ? mediaTypeIds[0] : multipleMediaTypesOption.Id; } var ovalTypeIds = expectedAssets.Where(a => a.OvalTypeId != null).Select(a => a.OvalTypeId).Distinct().ToArray(); if (ovalTypeIds.Length > 0) { ovalTypeId = ovalTypeIds.Length == 1 ? ovalTypeIds[0] : multipleOvalTypesOption.Id; } // Eligible for Usage/Buyout cost var usageTypeId = stageDetailsForm.UsageType?.Id; var ledgerMaterialCode = await _efContext.PgLedgerMaterialCode .OrderBy(c => c.ProductionTypeId) .FirstOrDefaultAsync(c => (c.CostType == costType && c.ContentTypeId == contentTypeId && c.ProductionTypeId == productionTypeId && c.MediaTypeId == mediaTypeId && c.OvalId == ovalTypeId && c.UsageTypeId == usageTypeId ) || //Default values needed for AIPE (costType == CostType.Production.ToString() && c.ContentTypeId == contentTypeId && c.ProductionTypeId == null && c.MediaTypeId == null && c.OvalId == null && c.UsageTypeId == null ) || (c.CostType == CostType.Trafficking.ToString() && costType == CostType.Trafficking.ToString()) ); var mgCode = ledgerMaterialCode?.MaterialGroupCode; var glCode = ledgerMaterialCode?.GeneralLedgerCode; var codeModel = new PgLedgerMaterialCodeModel { GlCode = glCode, MgCode = mgCode }; var adminUser = await _efContext.CostUser.FirstOrDefaultAsync(u => u.Email == ApprovalMemberModel.BrandApprovalUserEmail); var adminUserIdentity = new SystemAdminUserIdentity(adminUser); await _customDataService.Save(costStageRevisionId, CustomObjectDataKeys.PgMaterialLedgerCodes, codeModel, adminUserIdentity); }