/// <summary> /// Static entry point to calculate amount paid and due. /// </summary> /// <param name="context">The request context.</param> /// <param name="salesTransaction">The sales transaction.</param> public static void CalculateAmountPaidAndDue(RequestContext context, SalesTransaction salesTransaction) { ThrowIf.Null(context, "context"); ThrowIf.Null(salesTransaction, "salesTransaction"); decimal paymentRequiredAmount; salesTransaction.AmountPaid = SalesTransactionTotaler.GetPaymentsSum(salesTransaction.TenderLines); // decides what is expected to be paid for this transaction switch (salesTransaction.CartType) { case CartType.CustomerOrder: paymentRequiredAmount = SalesTransactionTotaler.CalculateRequiredPaymentAmount(context, salesTransaction); break; case CartType.Shopping: case CartType.Checkout: case CartType.AccountDeposit: paymentRequiredAmount = salesTransaction.TotalAmount; break; case CartType.IncomeExpense: paymentRequiredAmount = salesTransaction.IncomeExpenseTotalAmount; break; default: throw new DataValidationException( DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_InvalidRequest, string.Format("SalesTransactionTotaler::CalculateAmountPaidAndDue: CartType '{0}' not supported.", salesTransaction.CartType)); } salesTransaction.SalesPaymentDifference = paymentRequiredAmount - salesTransaction.AmountPaid; salesTransaction.AmountDue = paymentRequiredAmount - salesTransaction.AmountPaid; TenderLine lastTenderLine = null; if (!salesTransaction.TenderLines.IsNullOrEmpty()) { lastTenderLine = salesTransaction.ActiveTenderLines.LastOrDefault(); } if (lastTenderLine != null) { // Calculate the expected (rounded) amount due for last payment. decimal amountDueBeforeLastPayment = paymentRequiredAmount - salesTransaction.ActiveTenderLines.Take(salesTransaction.ActiveTenderLines.Count - 1).Sum(t => t.Amount); GetPaymentRoundedValueServiceRequest roundAmountDueBeforeLastPaymentRequest = new GetPaymentRoundedValueServiceRequest(amountDueBeforeLastPayment, lastTenderLine.TenderTypeId, isChange: false); GetRoundedValueServiceResponse roundAmountDueBeforeLastPaymentResponse = context.Execute <GetRoundedValueServiceResponse>(roundAmountDueBeforeLastPaymentRequest); // Set amont due to zero if payment amount equals to expected rounded payment amount. Otherwise another payment should be required (that could use different rounding settings). if (roundAmountDueBeforeLastPaymentResponse.RoundedValue == lastTenderLine.Amount) { salesTransaction.AmountDue = decimal.Zero; } } // When required amount is positive, amount due must be zero or negative (overtender), otherwise (e.g. for refunds or exchanges) exact amount has to refunded (zero balance). salesTransaction.IsRequiredAmountPaid = (paymentRequiredAmount > 0 && salesTransaction.AmountDue <= 0) || (paymentRequiredAmount <= 0 && salesTransaction.AmountDue == 0); }
/// <summary> /// Round amount by tender type id. /// </summary> /// <param name="context">The request context.</param> /// <param name="tenderTypeId">Tender type id.</param> /// <param name="amount">The amount.</param> /// <param name="isChange">Value indicating whether this request is for change.</param> /// <returns>Rounded amount.</returns> private static decimal RoundAmountByTenderType(RequestContext context, string tenderTypeId, decimal amount, bool isChange) { ThrowIf.Null(context, "context"); GetPaymentRoundedValueServiceRequest request = new GetPaymentRoundedValueServiceRequest(amount, tenderTypeId, isChange); GetRoundedValueServiceResponse response = context.Execute <GetRoundedValueServiceResponse>(request); return(response.RoundedValue); }
/// <summary> /// Round for channel payment method. /// </summary> /// <param name="request">Service request.</param> /// <returns>Rounded value.</returns> private GetRoundedValueServiceResponse GetPaymentRoundedValue(GetPaymentRoundedValueServiceRequest request) { GetChannelTenderTypesDataRequest dataServiceRequest = new GetChannelTenderTypesDataRequest(request.RequestContext.GetPrincipal().ChannelId, QueryResultSettings.AllRecords); EntityDataServiceResponse <TenderType> response = request.RequestContext.Execute <EntityDataServiceResponse <TenderType> >(dataServiceRequest); TenderType tenderType = response.PagedEntityCollection.Results.SingleOrDefault(channelTenderType => string.Equals(channelTenderType.TenderTypeId, request.TenderTypeId, StringComparison.OrdinalIgnoreCase)); RoundingMethod roundingMethod = tenderType.RoundingMethod; if (roundingMethod == RoundingMethod.None) { return(new GetRoundedValueServiceResponse(request.Value)); } decimal currencyUnit = tenderType.RoundOff; if (currencyUnit == decimal.Zero) { currencyUnit = Rounding.DefaultRoundingValue; } decimal roundedValue; if (request.IsChange) { // For change rounding up/down should be applied in opposite direction. if (roundingMethod == RoundingMethod.Down) { roundingMethod = RoundingMethod.Up; } else if (roundingMethod == RoundingMethod.Up) { roundingMethod = RoundingMethod.Down; } } // Using absolute value so payment and refund is rounded same way when rounding up or down. decimal absoluteAmount = Math.Abs(request.Value); roundedValue = Rounding.RoundToUnit(absoluteAmount, currencyUnit, roundingMethod); if (request.Value < 0) { // Revert sign back to original. roundedValue = decimal.Negate(roundedValue); } return(new GetRoundedValueServiceResponse(roundedValue)); }