Esempio n. 1
0
            /// <summary>
            /// Calculate the manual line discount.
            /// </summary>
            /// <param name="transaction">The transaction receiving total discount lines.</param>
            /// <param name="saleItem">The sale item that contains the discount lines.</param>
            /// <param name="lineDiscountItem">The line discount amount to discount the transaction.</param>
            private void AddLineDiscount(SalesTransaction transaction, SalesLine saleItem, DiscountLine lineDiscountItem)
            {
                Item item = PriceContextHelper.GetItem(this.priceContext, saleItem.ItemId);
                bool isDiscountAllowed = item != null ? !item.NoDiscountAllowed : true;

                if (isDiscountAllowed)
                {
                    saleItem.DiscountLines.Add(lineDiscountItem);
                    SalesLineTotaller.CalculateLine(transaction, saleItem, d => this.priceContext.CurrencyAndRoundingHelper.Round(d));
                }
            }
Esempio n. 2
0
 /// <summary>
 /// Adds total discount lines to the item lines.
 /// </summary>
 /// <param name="transaction">The transaction receiving total discount lines.</param>
 private void AddTotalDiscPctLines(SalesTransaction transaction)
 {
     // Consider calculable lines only. Ignore voided or return-by-receipt lines.
     // Add the total discount to each item.
     foreach (var saleItem in transaction.PriceCalculableSalesLines)
     {
         if (PriceContextHelper.IsDiscountAllowed(this.priceContext, saleItem.ItemId) && saleItem.Quantity > 0)
         {
             // Add a new total discount
             DiscountLine totalDiscountItem = new DiscountLine
             {
                 DiscountLineType   = DiscountLineType.ManualDiscount,
                 ManualDiscountType = ManualDiscountType.TotalDiscountPercent,
                 Percentage         = transaction.TotalManualDiscountPercentage,
             };
             saleItem.DiscountLines.Add(totalDiscountItem);
             SalesLineTotaller.CalculateLine(transaction, saleItem, d => this.priceContext.CurrencyAndRoundingHelper.Round(d));
         }
     }
 }
Esempio n. 3
0
            /// <summary>
            /// This method will distribute the amountToDiscount across all the sale items in the transaction
            ///     proportionally except for the line item with the largest amount.  The remainder will be distributed
            ///     to the line item with the largest amount to ensure the amount to discount is exactly applied.
            /// This method currently works when the redeem loyalty points button is applied.
            /// </summary>
            /// <param name="transaction">The transaction receiving loyalty discount lines.</param>
            /// <param name="amountToDiscount">The amount to discount the transaction.</param>
            private void AddLoyaltyDiscAmountLines(
                SalesTransaction transaction,
                decimal amountToDiscount)
            {
                decimal totalAmtAvailableForDiscount = decimal.Zero;

                // Build a list of the discountable items with the largest value item last
                var discountableSaleItems = (from s in transaction.SalesLines
                                             where ((s.IsEligibleForDiscount() && PriceContextHelper.IsDiscountAllowed(this.priceContext, s.ItemId)) ||
                                                    s.IsLoyaltyDiscountApplied)
                                             orderby Math.Abs(s.NetAmount), s.LineId
                                             select s).ToList();

                // Iterate through all non voided items whether we are going to discount or not so that they get added
                // back to the totals
                foreach (SalesLine salesLine in transaction.SalesLines.Where(sl => !sl.IsVoided))
                {
                    Discount.ClearDiscountLinesOfType(salesLine, DiscountLineType.LoyaltyDiscount);
                    SalesLineTotaller.CalculateLine(transaction, salesLine, d => this.priceContext.CurrencyAndRoundingHelper.Round(d));

                    if (salesLine.IsEligibleForDiscount() || salesLine.IsLoyaltyDiscountApplied)
                    {
                        // Calculate the total amount that is available for discount
                        totalAmtAvailableForDiscount += Math.Abs(salesLine.NetAmountWithAllInclusiveTax);
                    }
                }

                // Calculate the percentage (as a fraction) that we should attempt to discount each discountable item
                // to reach the total
                decimal discountFactor = totalAmtAvailableForDiscount != decimal.Zero ? (amountToDiscount / totalAmtAvailableForDiscount) : decimal.Zero;

                decimal totalAmtDistributed = decimal.Zero;

                // Iterate through all discountable items.
                foreach (SalesLine salesLine in discountableSaleItems)
                {
                    decimal amountToDiscountForThisItem = decimal.Zero;

                    if (salesLine != discountableSaleItems.Last())
                    {
                        // for every item except for the last in the list (which will have the largest value)
                        // discount by the rounded amount that is closest to the percentage desired for the transaction
                        decimal itemPrice = salesLine.NetAmount;
                        amountToDiscountForThisItem = this.priceContext.CurrencyAndRoundingHelper.Round(discountFactor * Math.Abs(itemPrice));
                        totalAmtDistributed        += amountToDiscountForThisItem;
                    }
                    else
                    {
                        // Discount the last item by the remainder to ensure that the exact desired discount is applied
                        amountToDiscountForThisItem = amountToDiscount - totalAmtDistributed;
                    }

                    DiscountLine discountLine;
                    if (amountToDiscountForThisItem != decimal.Zero)
                    {
                        // Add a new loyalty points discount item
                        discountLine                  = new DiscountLine();
                        discountLine.Amount           = salesLine.Quantity != 0 ? amountToDiscountForThisItem / salesLine.Quantity : amountToDiscountForThisItem;
                        discountLine.DiscountLineType = DiscountLineType.LoyaltyDiscount;

                        salesLine.DiscountLines.Add(discountLine);
                        salesLine.IsLoyaltyDiscountApplied = true;
                    }

                    SalesLineTotaller.CalculateLine(transaction, salesLine, d => this.priceContext.CurrencyAndRoundingHelper.Round(d));
                }
            }
Esempio n. 4
0
            private static void CalculateAmountDue(RequestContext context, SalesTransaction salesTransaction)
            {
                if (salesTransaction == null)
                {
                    throw new ArgumentNullException("salesTransaction");
                }

                ChannelConfiguration channelConfiguration = context.GetChannelConfiguration();
                string cancellationcode = channelConfiguration.CancellationChargeCode;
                string currencyCode     = channelConfiguration.Currency;

                RoundingRule roundingRule = amountToRound => RoundWithPricesRounding(context, amountToRound, currencyCode);

                SalesTransactionTotaler.ClearTotalAmounts(salesTransaction);
                salesTransaction.NumberOfItems = 0m;

                // initialize with header-level charges list
                var charges = new List <ChargeLine>();

                if (salesTransaction.ChargeLines.Any())
                {
                    charges.AddRange(salesTransaction.ChargeLines);
                }

                // Calculate totals for sale items , which might also include line-level misc charge in it.
                foreach (SalesLine saleLineItem in salesTransaction.SalesLines)
                {
                    if (saleLineItem.IsVoided == false)
                    {
                        // Calculate the sum of items
                        salesTransaction.NumberOfItems += Math.Abs(saleLineItem.Quantity);

                        // calculate the line item cost excluding charges and tax on charges.
                        SalesLineTotaller.CalculateLine(salesTransaction, saleLineItem, roundingRule);

                        UpdateTotalAmounts(salesTransaction, saleLineItem);
                    }
                    else
                    {
                        saleLineItem.PeriodicDiscountPossibilities.Clear();
                        SalesLineTotaller.CalculateLine(salesTransaction, saleLineItem, roundingRule);
                    }

                    saleLineItem.WasChanged = false;
                }

                // Add eligible charges on sales lines
                foreach (SalesLine salesLine in salesTransaction.ChargeCalculableSalesLines)
                {
                    charges.AddRange(salesLine.ChargeLines);
                }

                decimal incomeExpenseTotalAmount = 0m;

                foreach (IncomeExpenseLine incomeExpense in salesTransaction.IncomeExpenseLines)
                {
                    if (incomeExpense.AccountType != IncomeExpenseAccountType.None)
                    {
                        salesTransaction.NetAmountWithTax         += incomeExpense.Amount;
                        salesTransaction.NetAmountWithNoTax       += incomeExpense.Amount;
                        salesTransaction.GrossAmount              += incomeExpense.Amount;
                        salesTransaction.IncomeExpenseTotalAmount += incomeExpense.Amount;

                        // The total is done to calculate the payment amount.
                        incomeExpenseTotalAmount += incomeExpense.Amount;
                    }
                }

                foreach (ChargeLine charge in charges)
                {
                    AddToTaxItems(salesTransaction, charge);

                    // Calculate tax on the charge item.
                    CalculateTaxForCharge(charge);

                    if (charge.ChargeCode.Equals(cancellationcode, StringComparison.OrdinalIgnoreCase) && IsSeparateTaxInCancellationCharge(context))
                    {
                        salesTransaction.TaxOnCancellationCharge += charge.TaxAmount;
                    }
                    else
                    {
                        salesTransaction.TaxAmount += charge.TaxAmount;
                    }

                    // Later there is "TotalAmount = NetAmountWithTax + ChargeAmount", so we should add TaxAmountExclusive here
                    salesTransaction.NetAmountWithTax += charge.TaxAmountExclusive;
                }

                salesTransaction.DiscountAmount = salesTransaction.PeriodicDiscountAmount + salesTransaction.LineDiscount + salesTransaction.TotalDiscount;
                salesTransaction.ChargeAmount   = salesTransaction.ChargesTotal();

                // Subtotal is the net amount for the transaction (which includes the discounts) and optionally the tax amount if tax inclusive
                salesTransaction.SubtotalAmount = roundingRule(salesTransaction.NetAmountWithNoTax + salesTransaction.TaxAmountInclusive);

                salesTransaction.SubtotalAmountWithoutTax = context.GetChannelConfiguration().PriceIncludesSalesTax
                                                                ? salesTransaction.SubtotalAmount - salesTransaction.TaxAmount
                                                                : salesTransaction.SubtotalAmount;

                // Net amount when saved should include charges, it should be done after Subtotal amount calc because Subtotal does not include charge amount.
                salesTransaction.NetAmountWithNoTax = roundingRule(salesTransaction.NetAmountWithNoTax + salesTransaction.ChargeAmount);

                if (salesTransaction.IncomeExpenseLines.Any())
                {
                    // Setting the total amount sames as Payment amount for Income/ expense accounts.
                    salesTransaction.TotalAmount = incomeExpenseTotalAmount;
                }
                else if (salesTransaction.TransactionType == SalesTransactionType.CustomerAccountDeposit && salesTransaction.CustomerAccountDepositLines.Any())
                {
                    CustomerAccountDepositLine customerAccountDepositLine = salesTransaction.CustomerAccountDepositLines.SingleOrDefault();
                    salesTransaction.SubtotalAmountWithoutTax = customerAccountDepositLine.Amount;
                    salesTransaction.TotalAmount = customerAccountDepositLine.Amount;
                }
                else
                {
                    // NetAmountWithTax already includes the discounts
                    salesTransaction.TotalAmount = roundingRule(salesTransaction.NetAmountWithTax + salesTransaction.ChargeAmount);
                }
            }
Esempio n. 5
0
            /// <summary>
            /// Populates the fields for total amount, total discount, and total taxes on the sales line.
            /// </summary>
            /// <param name="context">The request context.</param>
            /// <param name="salesTransaction">The sales transaction.</param>
            /// <param name="salesLine">The sales line.</param>
            public static void CalculateLine(RequestContext context, SalesTransaction salesTransaction, SalesLine salesLine)
            {
                RoundingRule roundingRule = SalesTransactionTotaler.GetRoundingRule(context);

                SalesLineTotaller.CalculateLine(salesTransaction, salesLine, roundingRule);
            }
            private static void CalculateLine(DateTimeOffset transactionBeginDateTime, LineDiscountCalculationType lineDiscountCalculationType, SalesLine salesLine, RoundingRule salesRoundingRule, bool compareDiscounts)
            {
                if (salesLine == null)
                {
                    throw new ArgumentNullException("salesLine");
                }

                if (salesRoundingRule == null)
                {
                    throw new ArgumentNullException("salesRoundingRule");
                }

                decimal discountAmount = decimal.Zero;

                SalesLineTotaller.CalculateTax(salesLine);

                if ((salesLine.Blocked == false) && (salesLine.DateToActivateItem <= transactionBeginDateTime))
                {
                    salesLine.GrossAmount = salesRoundingRule(salesLine.Price * salesLine.Quantity);

                    if (!salesLine.IsPriceLocked || salesLine.QuantityOrdered != salesLine.Quantity)
                    {
                        salesLine.LineDiscount               = 0;
                        salesLine.LinePercentageDiscount     = 0;
                        salesLine.PeriodicDiscount           = 0;
                        salesLine.PeriodicPercentageDiscount = 0;
                        salesLine.TotalDiscount              = decimal.Zero;
                        salesLine.TotalPercentageDiscount    = decimal.Zero;
                        salesLine.LoyaltyDiscountAmount      = decimal.Zero;
                        salesLine.LoyaltyPercentageDiscount  = decimal.Zero;

                        if (salesLine.IsVoided || salesLine.IsPriceOverridden)
                        {
                            salesLine.DiscountLines.Clear();
                        }

                        if (compareDiscounts)
                        {
                            ComparingDiscounts(transactionBeginDateTime, lineDiscountCalculationType, salesLine, salesRoundingRule);
                        }

                        AllocateDiscountAmountToDiscountLines(salesLine, lineDiscountCalculationType, salesRoundingRule);
                    }
                    else
                    {
                        FixDiscountAmountsOnSalesLine(salesLine, salesRoundingRule);
                    }

                    discountAmount = salesLine.PeriodicDiscount + salesLine.LineDiscount + salesLine.TotalDiscount + salesLine.LoyaltyDiscountAmount;

                    salesLine.NetAmountWithAllInclusiveTax = salesLine.GrossAmount - discountAmount;

                    // Removing exempt inclusive taxes.
                    // Update - Bug 938614, - not deducting anymore. If PM's later decide to deduct this inclusive tax then uncomment line below.
                    salesLine.NetAmount = salesLine.NetAmountWithAllInclusiveTax; // -salesLine.TaxAmountExemptInclusive;

                    if (salesLine.Quantity != 0)
                    {
                        salesLine.NetAmountPerUnit = salesLine.NetAmountWithNoTax() / salesLine.Quantity;
                    }

                    salesLine.UnitQuantity = salesLine.UnitOfMeasureConversion.Convert(salesLine.Quantity);

                    salesLine.DiscountAmount = discountAmount;

                    salesLine.TotalAmount         = salesLine.NetAmountWithTax();
                    salesLine.NetAmountWithoutTax = salesLine.NetAmount - salesLine.TaxAmountInclusive;
                }
            }
Esempio n. 7
0
            /// <summary>
            /// Calculates all of the discount lines for the transactions.
            /// </summary>
            /// <param name="pricingDataManager">Provides data access to the calculation.</param>
            /// <param name="transaction">The sales transaction.</param>
            /// <param name="shouldTotalLines">True if discount lines should be totaled for each line. False if they should be left as raw discount lines.</param>
            /// <param name="priceContext">Price context.</param>
            /// <remarks>Each sales line will have a collection of DiscountLines and a net discount total in DiscountAmount property (if totaling is enabled).</remarks>
            public static void CalculateDiscountsForLines(
                IPricingDataAccessor pricingDataManager,
                SalesTransaction transaction,
                bool shouldTotalLines,
                PriceContext priceContext)
            {
                if (transaction == null)
                {
                    throw new ArgumentNullException("transaction");
                }

                List <SalesLine> existingSalesLines = new List <SalesLine>();
                List <SalesLine> newSalesLines      = new List <SalesLine>();

                if (priceContext.CalculateForNewSalesLinesOnly)
                {
                    foreach (SalesLine salesLine in transaction.SalesLines)
                    {
                        if (priceContext.NewSalesLineIdSet.Contains(salesLine.LineId))
                        {
                            newSalesLines.Add(salesLine);
                        }
                        else
                        {
                            existingSalesLines.Add(salesLine);
                        }
                    }

                    // Calculate for new sales lines only.
                    transaction.SalesLines.Clear();
                    transaction.SalesLines.AddRange(newSalesLines);
                }

                Discount discountEngine = InitializeDiscountEngine(pricingDataManager);

                discountEngine.CalculateDiscount(pricingDataManager, transaction, priceContext);

                if (priceContext.CalculateForNewSalesLinesOnly)
                {
                    // Add existing sales lines back after calculating for new sales lines only.
                    List <SalesLine> newSalesLinesFromCalculation = new List <SalesLine>();
                    foreach (SalesLine salesLine in transaction.SalesLines)
                    {
                        if (!priceContext.NewSalesLineIdSet.Contains(salesLine.LineId))
                        {
                            newSalesLinesFromCalculation.Add(salesLine);
                        }
                    }

                    transaction.SalesLines.Clear();
                    transaction.SalesLines.AddRange(existingSalesLines);
                    transaction.SalesLines.AddRange(newSalesLines);
                    transaction.SalesLines.AddRange(newSalesLinesFromCalculation);
                }

                if (shouldTotalLines)
                {
                    // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                    foreach (var salesLine in transaction.PriceCalculableSalesLines)
                    {
                        SalesLineTotaller.CalculateLine(transaction, salesLine, d => priceContext.CurrencyAndRoundingHelper.Round(d)); // technically rounding rule should be "sales rounding" rule
                    }
                }
            }
Esempio n. 8
0
            /// <summary>
            /// The calculation of the total customer discount.
            /// </summary>
            /// <param name="tradeAgreements">Trade agreement collection to calculate on. If null, uses the pricing data manager to find agreements.</param>
            /// <param name="retailTransaction">The retail transaction which needs total discounts.</param>
            /// <returns>
            /// The retail transaction.
            /// </returns>
            public SalesTransaction CalcTotalCustomerDiscount(
                List <TradeAgreement> tradeAgreements,
                SalesTransaction retailTransaction)
            {
                if (tradeAgreements != null && tradeAgreements.Any())
                {
                    decimal totalAmount = 0;

                    // Find the total amount as a basis for the total discount
                    // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                    var clonedTransaction = retailTransaction.Clone <SalesTransaction>();

                    foreach (var clonedSalesLine in clonedTransaction.PriceCalculableSalesLines)
                    {
                        if (this.IsTotalDiscountAllowed(clonedSalesLine.ItemId))
                        {
                            SalesLineTotaller.CalculateLine(clonedTransaction, clonedSalesLine, d => this.priceContext.CurrencyAndRoundingHelper.Round(d));
                            totalAmount += clonedSalesLine.NetAmountWithAllInclusiveTax;
                        }
                    }

                    decimal absTotalAmount = Math.Abs(totalAmount);

                    // Find the total discounts.
                    PriceDiscountType        relation    = PriceDiscountType.EndDiscountSales; // Total sales discount - 7
                    PriceDiscountItemCode    itemCode    = PriceDiscountItemCode.AllItems;     // All items - 2
                    PriceDiscountAccountCode accountCode = 0;
                    string         itemRelation          = string.Empty;
                    decimal        percent1       = 0m;
                    decimal        percent2       = 0m;
                    decimal        discountAmount = 0m;
                    ProductVariant dimension      = new ProductVariant();

                    int idx = 0;
                    while (idx < /* Max(PriceDiscAccountCode) */ 3)
                    {   // Check discounts for Store Currency
                        accountCode = (PriceDiscountAccountCode)idx;

                        string accountRelation = string.Empty;
                        if (accountCode == PriceDiscountAccountCode.Customer)
                        {
                            accountRelation = retailTransaction.CustomerId;
                        }
                        else if (accountCode == PriceDiscountAccountCode.CustomerGroup)
                        {
                            accountRelation = this.priceContext.CustomerTotalPriceGroup;
                        }

                        accountRelation = accountRelation ?? string.Empty;

                        // Only get Active discount combinations
                        if (this.discountParameters.Activation(relation, (PriceDiscountAccountCode)accountCode, (PriceDiscountItemCode)itemCode))
                        {
                            var priceDiscTable = Discount.GetPriceDiscData(tradeAgreements, relation, itemRelation, accountRelation, itemCode, accountCode, absTotalAmount, this.priceContext, dimension, false);

                            foreach (TradeAgreement row in priceDiscTable)
                            {
                                percent1       += row.PercentOne;
                                percent2       += row.PercentTwo;
                                discountAmount += row.Amount;

                                if (!row.ShouldSearchAgain)
                                {
                                    idx = 3;
                                }
                            }
                        }

                        idx++;
                    }

                    decimal totalPercentage = DiscountLine.GetCompoundedPercentage(percent1, percent2);

                    if (discountAmount != decimal.Zero)
                    {
                        this.AddTotalDiscAmountLines(retailTransaction, DiscountLineType.CustomerDiscount, discountAmount);
                    }

                    if (totalPercentage != 0)
                    {
                        // Update the sale items.
                        // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                        foreach (var saleItem in retailTransaction.PriceCalculableSalesLines)
                        {
                            if (this.IsTotalDiscountAllowed(saleItem.ItemId))
                            {
                                DiscountLine discountItem = GetCustomerDiscountItem(saleItem, CustomerDiscountType.TotalDiscount, DiscountLineType.CustomerDiscount);
                                discountItem.Percentage = totalPercentage;
                            }
                        }
                    }
                }

                return(retailTransaction);
            }
Esempio n. 9
0
            /// <summary>
            /// This method will distribute the amountToDiscount across all the sale items in the transaction
            ///   proportionally except for the line item with the largest amount.  The remainder will be distributed
            ///   to the line item with the largest amount to ensure the amount to discount is exactly applied.
            /// This method currently works for either the customer discount or when the total discount button is applied.
            /// </summary>
            /// <param name="transaction">The transaction receiving total discount lines.</param>
            /// <param name="discountType">Whether this discount is for a customer or for the total discount item.</param>
            /// <param name="amountToDiscount">The amount to discount the transaction.</param>
            private void AddTotalDiscAmountLines(
                SalesTransaction transaction,
                DiscountLineType discountType,
                decimal amountToDiscount)
            {
                decimal totalAmtAvailableForDiscount = decimal.Zero;

                // Build a list of the discountable items with the largest value item last.
                // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                var discountableSaleItems = (from s in transaction.PriceCalculableSalesLines
                                             where s.IsEligibleForDiscount() && s.Quantity > 0 &&
                                             PriceContextHelper.IsDiscountAllowed(this.priceContext, s.ItemId)
                                             orderby Math.Abs(s.NetAmount), s.LineId
                                             select s).ToList();

                // Iterate through all non voided items whether we are going to discount or not so that they get added
                // back to the totals
                // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                foreach (var saleItem in transaction.PriceCalculableSalesLines)
                {
                    // We can clear the discount line for total discount because a total manual amount discount
                    // will override a total manual percent discount, whereas customer discount can have both
                    // amount and percentage applied simultaneously.
                    if (discountType == DiscountLineType.ManualDiscount)
                    {
                        Discount.ClearManualDiscountLinesOfType(saleItem, ManualDiscountType.TotalDiscountAmount);
                        Discount.ClearManualDiscountLinesOfType(saleItem, ManualDiscountType.TotalDiscountPercent);
                    }

                    SalesLineTotaller.CalculateLine(transaction, saleItem, d => this.priceContext.CurrencyAndRoundingHelper.Round(d));

                    if (saleItem.IsEligibleForDiscount() && saleItem.Quantity > 0)
                    {
                        // Calculate the total amount that is available for discount
                        totalAmtAvailableForDiscount += Math.Abs(saleItem.NetAmountWithAllInclusiveTax);
                    }
                }

                // Calculate the percentage (as a fraction) that we should attempt to discount each discountable item
                // to reach the total.
                decimal discountFactor = totalAmtAvailableForDiscount != decimal.Zero ? (amountToDiscount / totalAmtAvailableForDiscount) : decimal.Zero;

                decimal totalAmtDistributed = decimal.Zero;

                // Iterate through all discountable items.
                foreach (var saleItem in discountableSaleItems)
                {
                    decimal amountToDiscountForThisItem = decimal.Zero;

                    if (saleItem != discountableSaleItems.Last())
                    {
                        // for every item except for the last in the list (which will have the largest value)
                        // discount by the rounded amount that is closest to the percentage desired for the transaction
                        decimal itemPrice = saleItem.NetAmount;
                        amountToDiscountForThisItem = this.priceContext.CurrencyAndRoundingHelper.Round(discountFactor * Math.Abs(itemPrice));
                        totalAmtDistributed        += amountToDiscountForThisItem;
                    }
                    else
                    {
                        // Discount the last item by the remainder to ensure that the exact desired discount is applied
                        amountToDiscountForThisItem = amountToDiscount - totalAmtDistributed;
                    }

                    DiscountLine discountItem;
                    if (amountToDiscountForThisItem != decimal.Zero)
                    {
                        if (discountType == DiscountLineType.ManualDiscount)
                        {
                            // Add a new total discount item
                            discountItem = new DiscountLine();
                            discountItem.DiscountLineType   = DiscountLineType.ManualDiscount;
                            discountItem.ManualDiscountType = ManualDiscountType.TotalDiscountAmount;
                            saleItem.DiscountLines.Add(discountItem);
                        }
                        else
                        {
                            // for customer discounts we need to either update the existing one, or add a new one.
                            discountItem = GetCustomerDiscountItem(saleItem, CustomerDiscountType.TotalDiscount, DiscountLineType.CustomerDiscount);
                        }

                        discountItem.Amount = saleItem.Quantity != 0 ? amountToDiscountForThisItem / saleItem.Quantity : amountToDiscountForThisItem;
                    }

                    SalesLineTotaller.CalculateLine(transaction, saleItem, d => this.priceContext.CurrencyAndRoundingHelper.Round(d));
                }
            }