/// <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)); } } }
/// <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)); } }
/// <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)); } }