Beispiel #1
0
            /// <summary>
            /// Calculates manual line discount sent from cashier.
            /// Should be called only after other discounts are calculated.
            /// </summary>
            /// <param name="transaction">Transaction to calculate manual total discounts on.</param>
            public void CalculateLineManualDiscount(SalesTransaction transaction)
            {
                if (transaction == null)
                {
                    throw new ArgumentNullException("transaction");
                }

                // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                foreach (var salesLine in transaction.PriceCalculableSalesLines)
                {
                    Discount.ClearManualDiscountLinesOfType(salesLine, ManualDiscountType.LineDiscountAmount);
                    Discount.ClearManualDiscountLinesOfType(salesLine, ManualDiscountType.LineDiscountPercent);

                    DiscountLine lineDiscountItem = null;

                    if (salesLine.LineManualDiscountPercentage != 0)
                    {
                        // Add a new line discount
                        lineDiscountItem = new DiscountLine
                        {
                            DiscountLineType   = DiscountLineType.ManualDiscount,
                            ManualDiscountType = ManualDiscountType.LineDiscountPercent,
                            Percentage         = salesLine.LineManualDiscountPercentage,
                        };

                        this.AddLineDiscount(transaction, salesLine, lineDiscountItem);
                    }

                    if (salesLine.LineManualDiscountAmount != 0)
                    {
                        // Add a new line discount
                        lineDiscountItem = new DiscountLine
                        {
                            DiscountLineType   = DiscountLineType.ManualDiscount,
                            ManualDiscountType = ManualDiscountType.LineDiscountAmount,
                            Amount             = salesLine.Quantity != decimal.Zero ? salesLine.LineManualDiscountAmount / salesLine.Quantity : decimal.Zero,
                        };

                        this.AddLineDiscount(transaction, salesLine, lineDiscountItem);
                    }
                }
            }
Beispiel #2
0
            /// <summary>
            /// Calculates distribution of manual total discounts across the transaction.
            /// Should be called only after other discounts are calculated.
            /// </summary>
            /// <param name="transaction">Transaction to calculate manual total discounts on.</param>
            public void CalculateTotalManualDiscount(SalesTransaction transaction)
            {
                if (transaction == null)
                {
                    throw new ArgumentNullException("transaction");
                }

                Discount.ClearManualDiscountLinesOfType(transaction, ManualDiscountType.TotalDiscountPercent);
                Discount.ClearManualDiscountLinesOfType(transaction, ManualDiscountType.TotalDiscountAmount);

                // It's either $ off or % off, not both. In case of a bug where both are present, $ off wins.
                if (transaction.TotalManualDiscountAmount != 0)
                {
                    this.AddTotalDiscAmountLines(transaction, DiscountLineType.ManualDiscount, transaction.TotalManualDiscountAmount);
                }
                else if (transaction.TotalManualDiscountPercentage != 0)
                {
                    this.AddTotalDiscPctLines(transaction);
                }
            }
Beispiel #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 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));
                }
            }