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 void PgPurchaseOrderResponse_When_TotalAmountIsNull_Should_HaveValidSchema() { // Arrange var pgPurchaseOrder = new PgPurchaseOrderResponse { AccountCode = "TestAccountCode", ItemIdCode = "ItemIdCode", PoNumber = "PONumber", Comments = "Comments", Type = "Type", GlAccount = "GLAccountCode", GrDate = DateTime.Parse("2017-08-09T11:26:45.345Z"), GrNumber = "GR number", ApproverEmail = "Email address of approver here", IoNumberOwner = "Internal order number owner", PoDate = DateTime.Parse("2017-08-30T11:26:45.345Z"), Requisition = "Requisition", ApprovalStatus = "Approved", TotalAmount = null }; var serialized = JsonConvert.SerializeObject(pgPurchaseOrder, Formatting.None, SerializerSettings); // Act var jobject = JObject.Parse(serialized); // Assert jobject.IsValid(_schema, out IList <string> errors).Should().BeTrue(GetSchemaValidationAssertionReason(errors)); }
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 Consume_whenUpdatedAndStatusApprovedAndPendingApproval_shouldApproveTheCost() { // Arrange var activityType = ActivityTypes.Updated; var approvalStatus = ApprovalStatuses.Approved; var requisition = "requisitionId"; var purchaseOrderResponse = new PurchaseOrderResponse { ActivityType = activityType, CostNumber = CostNumber, ClientName = BuType.Pg.ToString(), Payload = JObject.Parse(JsonConvert.SerializeObject(new PgPurchaseOrderResponse { ApprovalStatus = approvalStatus })) }; var operationResponse = new ApprovalServiceActionResult { Success = true }; var dbPurchaseOrderResponse = new PgPurchaseOrderResponse() { Requisition = requisition, }; var dbPaymentDetails = new PgPaymentDetails() { Requisition = requisition, }; var cost = _efContext.Cost.Find(_costId); cost.Status = CostStageRevisionStatus.PendingBrandApproval; _efContext.Cost.Update(cost); await _efContext.SaveChangesAsync(); _customDataServiceMock.Setup(cds => cds.GetCustomData <PgPurchaseOrderResponse>(It.IsAny <Guid>(), It.IsAny <string>())).Returns(Task.FromResult(dbPurchaseOrderResponse)); _customDataServiceMock.Setup(cds => cds.GetCustomData <PgPaymentDetails>(It.IsAny <Guid>(), It.IsAny <string>())).Returns(Task.FromResult(dbPaymentDetails)); _customDataServiceMock.Setup(cds => cds.Save(It.IsAny <Guid>(), It.IsAny <string>(), It.IsAny <object>(), It.IsAny <UserIdentity>())).Returns(Task.FromResult(new CustomObjectData())); _approvalServiceMock.Setup(a => a.Approve(It.IsAny <Guid>(), It.IsAny <UserIdentity>(), BuType.Pg, SourceSystem.Coupa)).Returns(Task.FromResult(operationResponse)); _emailNotificationServiceMock.Setup(em => em.CostHasBeenApproved(It.IsAny <Guid>(), It.IsAny <Guid>(), It.IsAny <string>())).Returns(Task.FromResult(true)); // Act await _consumer.Consume(purchaseOrderResponse); // Assert _approvalServiceMock.Verify(s => s.Approve(It.IsAny <Guid>(), It.IsAny <UserIdentity>(), BuType.Pg, SourceSystem.Coupa), Times.Once); _emailNotificationServiceMock.Verify(em => em.CostHasBeenApproved(It.IsAny <Guid>(), It.IsAny <Guid>(), It.IsAny <string>()), Times.Once); Assert.AreEqual(requisition, dbPurchaseOrderResponse.Requisition); Assert.AreEqual(requisition, dbPaymentDetails.Requisition); }
public async Task Consume_whenUpdatedAndStatusRejected_shouldRejectTheCost() { // Arrange var activityType = ActivityTypes.Updated; var approvalStatus = ApprovalStatuses.Rejected; var purchaseOrderResponse = new PurchaseOrderResponse { ActivityType = activityType, CostNumber = CostNumber, ClientName = BuType.Pg.ToString(), Payload = JObject.Parse(JsonConvert.SerializeObject(new PgPurchaseOrderResponse { ApprovalStatus = approvalStatus })) }; var operationResponse = new ApprovalServiceActionResult() { Success = true }; var dbPurchaseOrderResponse = new PgPurchaseOrderResponse() { Requisition = "requisitionId", }; var dbPaymentDetails = new PgPaymentDetails() { Requisition = "requisitionId", }; _customDataServiceMock.Setup(cds => cds.GetCustomData <PgPurchaseOrderResponse>(It.IsAny <Guid>(), It.IsAny <string>())).Returns(Task.FromResult(dbPurchaseOrderResponse)); _customDataServiceMock.Setup(cds => cds.GetCustomData <PgPaymentDetails>(It.IsAny <Guid>(), It.IsAny <string>())).Returns(Task.FromResult(dbPaymentDetails)); _customDataServiceMock.Setup(cds => cds.Save(It.IsAny <Guid>(), It.IsAny <string>(), It.IsAny <object>(), It.IsAny <UserIdentity>())).Returns(Task.FromResult(new CustomObjectData())); _approvalServiceMock.Setup(a => a.Reject(It.IsAny <Guid>(), It.IsAny <UserIdentity>(), BuType.Pg, It.IsAny <string>(), It.IsAny <SourceSystem>())).Returns(Task.FromResult(operationResponse)); _emailNotificationServiceMock.Setup(em => em.CostHasBeenRejected(It.IsAny <Guid>(), It.IsAny <Guid>(), It.IsAny <string>(), It.IsAny <string>())).Returns(Task.FromResult(true)); // Act await _consumer.Consume(purchaseOrderResponse); // Assert _approvalServiceMock.Verify(s => s.Reject(It.IsAny <Guid>(), It.IsAny <UserIdentity>(), BuType.Pg, It.IsAny <string>(), It.IsAny <SourceSystem>()), Times.Once); _emailNotificationServiceMock.Verify(em => em.CostHasBeenRejected(It.IsAny <Guid>(), It.IsAny <Guid>(), It.IsAny <string>(), It.IsAny <string>()), Times.Once); _customDataServiceMock.Verify(cds => cds.GetCustomData <PgPurchaseOrderResponse>(It.IsAny <Guid>(), It.IsAny <string>()), Times.Once); _customDataServiceMock.Verify(cds => cds.GetCustomData <PgPaymentDetails>(It.IsAny <Guid>(), It.IsAny <string>()), Times.Once); Assert.IsNull(dbPurchaseOrderResponse.Requisition); Assert.IsNull(dbPaymentDetails.Requisition); }
private async Task <bool> HasCostBeenApproved(Cost cost, PgPurchaseOrderResponse payload) { // If TotalAmount in incoming messaage is the same as current cost total amount consider const as "Approved" var costStageRevisionId = cost.LatestCostStageRevisionId.Value; var totalAmountInApplicableCurrency = 0m; var paymentAmount = await _pgPaymentService.GetPaymentAmount(costStageRevisionId, false); if (paymentAmount != null) { var rateMultiplier = cost.ExchangeRate ?? 1m; totalAmountInApplicableCurrency = (paymentAmount.TotalCostAmount ?? 0) / rateMultiplier; } return(payload.TotalAmount.HasValue && Math.Round(totalAmountInApplicableCurrency, 2) == Math.Round(payload.TotalAmount.Value, 2)); }
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); }
private PgPurchaseOrder.LongTextField GetLongTextField(CostStageRevisionStatusChanged stageRevisionStatusChanged, PgPurchaseOrderDTO purchaseOrderDto, PgStageDetailsForm stageDetailsForm, PgPurchaseOrderResponse purchaseOrderResponse, decimal paymentAmount, string currencyCode, CostType costType) { var longText = new PgPurchaseOrder.LongTextField(); switch (stageRevisionStatusChanged.Status) { case CostStageRevisionStatus.PendingBrandApproval: longText.VN.AddRange(new[] { "Purchase order does not authorize committing funds without approved EPCAT sheet.", "The services within this Purchase Order can only be ordered from 3rd parties after EPCAT approval." }); var productionTypeIfProductionCost = stageDetailsForm.ProductionType != null ? $"{stageDetailsForm.ProductionType.Key} " : ""; longText.BN.Add($"{purchaseOrderDto.CostStageRevisionName} APPROVED {costType} {productionTypeIfProductionCost}{purchaseOrderResponse?.PoNumber}".TrimEnd()); longText.AN.Add($"{_appSettings.FrontendUrl.TrimEnd('/')}/#/cost/{stageRevisionStatusChanged.AggregateId}/review"); break; case CostStageRevisionStatus.PendingCancellation: longText.VN.Add("PROJECT CANCELLED. PLEASE CANCEL PO AND REQUEST CN FOR ANY AMOUNTS PAID"); longText.BN.Add($"PROJECT CANCELLED. PLEASE CANCEL PO {purchaseOrderResponse?.PoNumber} AND REQUEST CN FOR ANY AMOUNTS PAID"); break; case CostStageRevisionStatus.Approved: if (purchaseOrderDto.CostStageRevisionKey == CostStages.FinalActual.ToString() || purchaseOrderDto.CostStageRevisionKey == CostStages.FinalActualRevision.ToString()) { //ADC-2412 //Strange issue when the payment amount looks something like -1560.00000000000000000000000000002 paymentAmount = Math.Round(paymentAmount, 2); // Get calculated credit amount if (paymentAmount < 0) { longText.BN.Add($"A credit note of { paymentAmount } {currencyCode} is needed, please update PO accordingly."); } } break; } return(longText); }