Exemplo n.º 1
0
            private static decimal AllocateLineDiscountLines(
                SalesLine salesLine,
                List <DiscountLine> customerLineDiscountItemList,
                DiscountLine manualLineDiscountItem,
                LineDiscountCalculationType lineDiscountCalculationType,
                decimal customerLineAmountForMixOrMax,
                decimal customerLinePercentForMixOrMax,
                RoundingRule roundingRule)
            {
                decimal lineDiscountEffectiveAmount     = 0;
                decimal lineDiscountEffectivePercentage = 0;
                decimal grossAmountDiscountable         = (salesLine.Price * salesLine.Quantity) - salesLine.PeriodicDiscount;

                if (grossAmountDiscountable != decimal.Zero)
                {
                    // Round 1: amount off first for customer line discounts and manual line discount, in that order.
                    if (customerLineDiscountItemList.Any())
                    {
                        foreach (DiscountLine customerLine in customerLineDiscountItemList)
                        {
                            if (salesLine.LineMultilineDiscOnItem != LineMultilineDiscountOnItem.Both ||
                                lineDiscountCalculationType == LineDiscountCalculationType.LinePlusMultiline ||
                                lineDiscountCalculationType == LineDiscountCalculationType.LineMultiplyMultiline ||
                                (lineDiscountCalculationType == LineDiscountCalculationType.MaxLineMultiline && customerLine.Amount == customerLineAmountForMixOrMax) ||
                                (lineDiscountCalculationType == LineDiscountCalculationType.MinLineMultiline && customerLine.Amount == customerLineAmountForMixOrMax) ||
                                (lineDiscountCalculationType == LineDiscountCalculationType.Line && customerLine.CustomerDiscountType == CustomerDiscountType.LineDiscount) ||
                                (lineDiscountCalculationType == LineDiscountCalculationType.Multiline && customerLine.CustomerDiscountType == CustomerDiscountType.MultilineDiscount))
                            {
                                customerLine.SetEffectiveAmountForAmountOff(grossAmountDiscountable - lineDiscountEffectiveAmount, salesLine.Quantity, roundingRule);
                                lineDiscountEffectiveAmount += customerLine.EffectiveAmount;

                                // In case of Min and Max, customerLineAmountForMixOrMax can only be applied once.
                                customerLineAmountForMixOrMax = decimal.Zero;
                            }
                            else
                            {
                                customerLine.EffectiveAmount = decimal.Zero;
                            }
                        }
                    }

                    if (manualLineDiscountItem != null)
                    {
                        manualLineDiscountItem.SetEffectiveAmountForAmountOff(grossAmountDiscountable - lineDiscountEffectiveAmount, salesLine.Quantity, roundingRule);
                        lineDiscountEffectiveAmount += manualLineDiscountItem.EffectiveAmount;
                    }

                    decimal grossAmountDiscountableLessAmountOff         = grossAmountDiscountable - lineDiscountEffectiveAmount;
                    decimal lineDiscountEffectiveAmountForPercentageOnly = 0;

                    // Round 2: percentage off for customer line discounts and manual line discount, in that order.
                    if (customerLineDiscountItemList.Any())
                    {
                        foreach (DiscountLine customerLine in customerLineDiscountItemList)
                        {
                            if (salesLine.LineMultilineDiscOnItem != LineMultilineDiscountOnItem.Both ||
                                lineDiscountCalculationType == LineDiscountCalculationType.LinePlusMultiline ||
                                lineDiscountCalculationType == LineDiscountCalculationType.LineMultiplyMultiline ||
                                (lineDiscountCalculationType == LineDiscountCalculationType.MaxLineMultiline && customerLine.Percentage == customerLinePercentForMixOrMax) ||
                                (lineDiscountCalculationType == LineDiscountCalculationType.MinLineMultiline && customerLine.Percentage == customerLinePercentForMixOrMax) ||
                                (lineDiscountCalculationType == LineDiscountCalculationType.Line && customerLine.CustomerDiscountType == CustomerDiscountType.LineDiscount) ||
                                (lineDiscountCalculationType == LineDiscountCalculationType.Multiline && customerLine.CustomerDiscountType == CustomerDiscountType.MultilineDiscount))
                            {
                                // Compound only when LineDiscountCalculationType.LineMultiplyMultiline.
                                decimal grossAmountBase = grossAmountDiscountableLessAmountOff;
                                if (lineDiscountCalculationType == LineDiscountCalculationType.LineMultiplyMultiline)
                                {
                                    grossAmountBase = grossAmountDiscountableLessAmountOff - lineDiscountEffectiveAmountForPercentageOnly;
                                }

                                decimal maxDiscountAmount   = grossAmountDiscountable - lineDiscountEffectiveAmount;
                                decimal discountAmountAdded = customerLine.AddEffectiveAmountForPercentOff(grossAmountBase, maxDiscountAmount, roundingRule);

                                lineDiscountEffectiveAmount += discountAmountAdded;
                                lineDiscountEffectiveAmountForPercentageOnly += discountAmountAdded;

                                // In case of Min and Max, customerLineAmountForMixOrMax can only be applied once.
                                customerLineAmountForMixOrMax = decimal.Zero;
                            }
                        }
                    }

                    if (manualLineDiscountItem != null)
                    {
                        // Whether to add or to compound for manual discount follows the same rule for customer line and multiline discount.
                        decimal grossAmountBase = grossAmountDiscountableLessAmountOff;
                        if (lineDiscountCalculationType == LineDiscountCalculationType.LineMultiplyMultiline)
                        {
                            grossAmountBase = grossAmountDiscountableLessAmountOff - lineDiscountEffectiveAmountForPercentageOnly;
                        }

                        decimal maxDiscountAmount   = grossAmountDiscountable - lineDiscountEffectiveAmount;
                        decimal discountAmountAdded = manualLineDiscountItem.AddEffectiveAmountForPercentOff(grossAmountBase, maxDiscountAmount, roundingRule);

                        lineDiscountEffectiveAmount += discountAmountAdded;
                        lineDiscountEffectiveAmountForPercentageOnly += discountAmountAdded;
                    }

                    lineDiscountEffectivePercentage = (lineDiscountEffectiveAmount / grossAmountDiscountable) * 100m;
                }

                salesLine.LineDiscount           = lineDiscountEffectiveAmount;
                salesLine.LinePercentageDiscount = Math.Round(lineDiscountEffectivePercentage, 2);

                return(lineDiscountEffectiveAmount);
            }
Exemplo n.º 2
0
            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;
                }
            }
Exemplo n.º 3
0
            private static void AllocateDiscountAmountToDiscountLines(SalesLine salesLine, LineDiscountCalculationType lineDiscountCalculationType, RoundingRule roundingRule)
            {
                if (salesLine.DiscountLines.Count == 0)
                {
                    return;
                }

                // customerLineAmountForMixOrMax and customerLinePercentForMixOrMax are for LineDiscountCalculationType.MaxLineMultiline and MinLineMultiline
                decimal customerLineAmountForMixOrMax  = 0;
                decimal customerLinePercentForMixOrMax = 0;

                List <DiscountLine> periodicDiscountItemList          = new List <DiscountLine>();
                List <DiscountLine> periodicThresholdDiscountItemList = new List <DiscountLine>();
                List <DiscountLine> customerLineDiscountItemList      = new List <DiscountLine>();

                // Manual line discount at most one.
                DiscountLine        manualLineDiscountItem  = null;
                List <DiscountLine> totalDiscountItemList   = new List <DiscountLine>();
                List <DiscountLine> loyaltyDiscountLineList = new List <DiscountLine>();

                //// Step 1: split discount lines into 5 groups: Periodic less threshold, Periodic threshold, Customer line, Manual line and Total and Loyalty
                ////         and figure out customerLineAmountForMixOrMax & customerLinePercentForMixOrMax along the way.
                foreach (DiscountLine discountLineItem in salesLine.DiscountLines)
                {
                    discountLineItem.FixInvalidAmountAndPercentage();

                    if (discountLineItem.DiscountLineType == DiscountLineType.CustomerDiscount)
                    {
                        // Customer Total
                        if (discountLineItem.CustomerDiscountType == CustomerDiscountType.TotalDiscount)
                        {
                            totalDiscountItemList.Add(discountLineItem);
                        }
                        else
                        {
                            // Customer Line
                            if (salesLine.LineMultilineDiscOnItem == LineMultilineDiscountOnItem.Both)
                            {
                                if (customerLineAmountForMixOrMax == decimal.Zero ||
                                    (lineDiscountCalculationType == LineDiscountCalculationType.MaxLineMultiline && discountLineItem.Amount > customerLineAmountForMixOrMax) ||
                                    (lineDiscountCalculationType == LineDiscountCalculationType.MinLineMultiline && discountLineItem.Amount > 0 && discountLineItem.Amount < customerLineAmountForMixOrMax))
                                {
                                    customerLineAmountForMixOrMax = discountLineItem.Amount;
                                }

                                if (customerLinePercentForMixOrMax == decimal.Zero ||
                                    (lineDiscountCalculationType == LineDiscountCalculationType.MaxLineMultiline && discountLineItem.Percentage > customerLinePercentForMixOrMax) ||
                                    (lineDiscountCalculationType == LineDiscountCalculationType.MinLineMultiline && discountLineItem.Percentage > 0 && discountLineItem.Percentage < customerLinePercentForMixOrMax))
                                {
                                    customerLinePercentForMixOrMax = discountLineItem.Percentage;
                                }
                            }

                            customerLineDiscountItemList.Add(discountLineItem);
                        }
                    }
                    else if (discountLineItem.DiscountLineType == DiscountLineType.PeriodicDiscount)
                    {
                        // Periodic a.k.a. Retail
                        if (discountLineItem.PeriodicDiscountType == PeriodicDiscountOfferType.Threshold)
                        {
                            periodicThresholdDiscountItemList.Add(discountLineItem);
                        }
                        else
                        {
                            periodicDiscountItemList.Add(discountLineItem);
                        }
                    }
                    else if (discountLineItem.DiscountLineType == DiscountLineType.ManualDiscount &&
                             (discountLineItem.ManualDiscountType == ManualDiscountType.LineDiscountAmount ||
                              discountLineItem.ManualDiscountType == ManualDiscountType.LineDiscountPercent))
                    {
                        // Line Manual
                        manualLineDiscountItem = discountLineItem;
                    }
                    else if (discountLineItem.DiscountLineType == DiscountLineType.ManualDiscount &&
                             (discountLineItem.ManualDiscountType == ManualDiscountType.TotalDiscountAmount ||
                              discountLineItem.ManualDiscountType == ManualDiscountType.TotalDiscountPercent))
                    {
                        // Total manual
                        totalDiscountItemList.Add(discountLineItem);
                    }
                    else if (discountLineItem.DiscountLineType == DiscountLineType.LoyaltyDiscount)
                    {
                        // Loyalty discount
                        loyaltyDiscountLineList.Add(discountLineItem);
                    }
                }

                salesLine.PeriodicDiscount           = 0;
                salesLine.PeriodicPercentageDiscount = 0;

                // Step 2: allocate effective discount amount for periodic less threshold discount lines.
                AllocatePeriodicDiscountLines(salesLine, periodicDiscountItemList, roundingRule);

                // Step 3: allocate effective discount amount for periodic threshold discount lines.
                AllocatePeriodicDiscountLines(salesLine, periodicThresholdDiscountItemList, roundingRule);

                // Stpe 4: allocate effective discount amount for customer and manual line discounts.
                AllocateLineDiscountLines(salesLine, customerLineDiscountItemList, manualLineDiscountItem, lineDiscountCalculationType, customerLineAmountForMixOrMax, customerLinePercentForMixOrMax, roundingRule);

                // Stpe 4: allocate effective discount amount for total line discounts.
                AllocateTotalDiscountLines(salesLine, totalDiscountItemList, roundingRule);

                // Stpe 5: allocate effective discount amount for loyalty line discounts
                AllocateLoyaltyDiscountLines(salesLine, loyaltyDiscountLineList, roundingRule);
            }
Exemplo n.º 4
0
 /// <summary>
 /// Populates the fields for total amount, total discount, and total taxes on the sales line.
 /// </summary>
 /// <param name="transactionBeginDateTime">Transaction begin date time.</param>
 /// <param name="lineDiscountCalculationType">Line discount calculation type.</param>
 /// <param name="salesLine">The sales line to total.</param>
 /// <param name="salesRoundingRule">Delegate which can do sales rounding.</param>
 public static void CalculateLine(DateTime transactionBeginDateTime, LineDiscountCalculationType lineDiscountCalculationType, SalesLine salesLine, RoundingRule salesRoundingRule)
 {
     CalculateLine(transactionBeginDateTime, lineDiscountCalculationType, salesLine, salesRoundingRule, true);
 }
Exemplo n.º 5
0
            /// <summary>
            /// Compares the discounts on each of the sale line items:
            ///  If the sale line has both a customer and a periodic discount (other then Mix and Match) then
            ///     the discounts are compared and the better one is chosen and the other one taken of the sale line.
            ///  If the sale line has a customer discount and a Mix &amp; Match discount then the M&amp;M discount is always
            ///     chosen because there is no way to know the total discount of the M&amp;M because it consists of 2 or more sale lines.
            ///     So we assume that the M&amp;M is always better.
            /// </summary>
            /// <param name="transactionBeginDateTime">The transaction start date.</param>
            /// <param name="lineDiscountCalculationType">The line discount calculation type.</param>
            /// <param name="salesLine">The sales line.</param>
            /// <param name="salesRoundingRule">The rounding rule to use for the sales line.</param>
            private static void ComparingDiscounts(DateTimeOffset transactionBeginDateTime, LineDiscountCalculationType lineDiscountCalculationType, SalesLine salesLine, RoundingRule salesRoundingRule)
            {
                bool periodicDiscFound      = false;
                bool customerDiscFound      = false;
                bool mixAndMatchFound       = false;
                bool thresholdFound         = false;
                bool customerTotalDiscFound = false;

                // Go through all the discount lines and figure out what type of discount lines are available.
                foreach (var discountLine in salesLine.DiscountLines)
                {
                    if (discountLine.DiscountLineType == DiscountLineType.PeriodicDiscount)
                    {
                        if (discountLine.PeriodicDiscountType == PeriodicDiscountOfferType.MixAndMatch)
                        {
                            mixAndMatchFound = true;
                        }
                        else if (discountLine.PeriodicDiscountType == PeriodicDiscountOfferType.Threshold)
                        {
                            thresholdFound = true;
                        }
                        else
                        {
                            periodicDiscFound = true;
                        }
                    }

                    if (discountLine.DiscountLineType == DiscountLineType.CustomerDiscount)
                    {
                        if (discountLine.CustomerDiscountType != CustomerDiscountType.TotalDiscount)
                        {
                            customerDiscFound = true;
                        }
                        else
                        {
                            customerTotalDiscFound = true;
                        }
                    }
                }

                bool discardCustomerDiscounts     = mixAndMatchFound || thresholdFound;
                bool discardTotalCustomerDiscount = thresholdFound;

                // If (Mix & Match or Threshold) and Customer Total Discount were found
                // then they will override any other discounts that are found on the sale line
                // no need to compare prices or discounts.
                if (discardCustomerDiscounts && customerTotalDiscFound)
                {
                    ClearCustomerDiscountLines(salesLine, discardTotalCustomerDiscount);
                    customerTotalDiscFound = false;
                    customerDiscFound      = false;
                }

                // If (Mix & Match or Threshold) and Customer discount both found on the sale item
                // then the Mix & Match will override any customer discount
                if (discardCustomerDiscounts && customerDiscFound)
                {
                    ClearCustomerDiscountLines(salesLine, discardTotalCustomerDiscount);
                    customerDiscFound = false;
                }

                // If a Customer Discount is found and either a Multibuy or a Discount offer
                // the best price is found from either discount and the better one chosen.
                if (customerDiscFound && periodicDiscFound)
                {
                    // clone line and keep only customer discounts
                    SalesLine custSaleLineItem = salesLine.Clone <SalesLine>();
                    ClearDiscountLinesOfType(custSaleLineItem, DiscountLineType.PeriodicDiscount);

                    // clone line and keep only periodic discounts
                    SalesLine periodicSaleLineItem = salesLine.Clone <SalesLine>();
                    ClearDiscountLinesOfType(periodicSaleLineItem, DiscountLineType.CustomerDiscount);

                    CalculateLine(transactionBeginDateTime, lineDiscountCalculationType, custSaleLineItem, salesRoundingRule, false);
                    CalculateLine(transactionBeginDateTime, lineDiscountCalculationType, periodicSaleLineItem, salesRoundingRule, false);

                    if (Math.Abs(custSaleLineItem.NetAmount) >= Math.Abs(periodicSaleLineItem.NetAmount))
                    {
                        ClearCustomerDiscountLines(salesLine, false);
                    }
                    else
                    {
                        ClearDiscountLinesOfType(salesLine, DiscountLineType.PeriodicDiscount);
                    }
                }
            }