Ejemplo n.º 1
0
            /// <summary>
            /// Gets the discount data.
            /// </summary>
            /// <param name="tradeAgreements">Trade agreement collection to calculate on.</param>
            /// <param name="relation">The relation (line, multiline, total).</param>
            /// <param name="itemRelation">The item relation.</param>
            /// <param name="accountRelation">The account relation.</param>
            /// <param name="itemCode">The item code (table, group, all).</param>
            /// <param name="accountCode">The account code (table, group, all).</param>
            /// <param name="quantityAmount">The quantity or amount that sets the minimum quantity or amount needed.</param>
            /// <param name="priceContext">The price context.</param>
            /// <param name="itemDimensions">The item dimensions.</param>
            /// <param name="includeDimensions">A value indicating whether to include item dimensions.</param>
            /// <returns>
            /// A collection of discount agreement arguments.
            /// </returns>
            internal static ReadOnlyCollection <TradeAgreement> GetPriceDiscData(
                List <TradeAgreement> tradeAgreements,
                PriceDiscountType relation,
                string itemRelation,
                string accountRelation,
                PriceDiscountItemCode itemCode,
                PriceDiscountAccountCode accountCode,
                decimal quantityAmount,
                PriceContext priceContext,
                ProductVariant itemDimensions,
                bool includeDimensions)
            {
                accountRelation = accountRelation ?? string.Empty;
                itemRelation    = itemRelation ?? string.Empty;
                string targetCurrencyCode = priceContext.CurrencyCode ?? string.Empty;
                string inventColorId      = (itemDimensions != null && itemDimensions.ColorId != null && includeDimensions) ? itemDimensions.ColorId : string.Empty;
                string inventSizeId       = (itemDimensions != null && itemDimensions.SizeId != null && includeDimensions) ? itemDimensions.SizeId : string.Empty;
                string inventStyleId      = (itemDimensions != null && itemDimensions.StyleId != null && includeDimensions) ? itemDimensions.StyleId : string.Empty;
                string inventConfigId     = (itemDimensions != null && itemDimensions.ConfigId != null && includeDimensions) ? itemDimensions.ConfigId : string.Empty;

                DateTime today  = priceContext.ActiveDate.DateTime;
                DateTime noDate = new DateTime(1900, 1, 1);

                ReadOnlyCollection <TradeAgreement> foundAgreements;

                foundAgreements = GetAgreementsFromCollection(tradeAgreements, relation, itemRelation, accountRelation, itemCode, accountCode, quantityAmount, targetCurrencyCode, inventColorId, inventSizeId, inventStyleId, inventConfigId, today, noDate);

                return(foundAgreements);
            }
Ejemplo n.º 2
0
            /// <summary>
            /// Returns true or false, whether a certain relation is active for a discount search.
            /// </summary>
            /// <param name="relation">The trade agreement relation(price, line discount, multiline discount, total discount).</param>
            /// <param name="accountCode">The account code(table,group,all).</param>
            /// <param name="itemCode">The item code(table,group,all).</param>
            /// <returns>Returns true if the relation is active, else false.</returns>
            public bool Activation(PriceDiscountType relation, PriceDiscountAccountCode accountCode, PriceDiscountItemCode itemCode)
            {
                // if parameters haven't been fetched, return false for all
                if (this.enabledCombinations == null)
                {
                    return(false);
                }

                // look up whether the combination is enabled
                return(this.enabledCombinations.Contains(CreateCombo(relation, accountCode, itemCode)));
            }
            /// <summary>
            /// This function takes arguments (customer, item, currency, etc.) related to price (trade) agreement
            /// as well as the set of currently enabled trade agreement types. It returns the best trade agreement
            /// price for the given constraints.
            /// As in AX, the method searches for a price on the given item which has been given to a
            /// customer, price group, or anyone (in given precedence order). If a price is found and marked as
            /// SearchAgain=False, the search will terminate. Otherwise, search for lowest price will continue.
            /// To recap, the logic is that three searches are done for customer, price group, and all, each bracket
            /// will return the lowest price it has for the constraints. If it has SearchAgain=True, then the search
            /// for lowest price continues to the next bracket.
            /// </summary>
            /// <param name="tradeAgreementRules">Trade agreements applicable to each item, keyed by item relation (i.e. item Id).</param>
            /// <param name="args">Arguments for price agreement search.</param>
            /// <param name="priceParameters">Set of enabled price agreement types.</param>
            /// <param name="salesLines">Sales lines.</param>
            /// <param name="priceContext">Price context.</param>
            /// <param name="activeDate">Date to use for querying trade agreement rules.</param>
            /// <returns>
            /// Most applicable price for the given price agreement constraints.
            /// </returns>
            private static PriceResult ApplyPriceTradeAgreements(
                IDictionary <string, IList <TradeAgreement> > tradeAgreementRules,
                PriceAgreementArgs args,
                DiscountParameters priceParameters,
                IEnumerable <SalesLine> salesLines,
                PriceContext priceContext,
                DateTimeOffset activeDate)
            {
                PriceResult priceResult = new PriceResult(0M, PriceGroupIncludesTax.NotSpecified);

                var itemCodes    = new PriceDiscountItemCode[] { PriceDiscountItemCode.Item, PriceDiscountItemCode.ItemGroup, PriceDiscountItemCode.AllItems };
                var accountCodes = new PriceDiscountAccountCode[] { PriceDiscountAccountCode.Customer, PriceDiscountAccountCode.CustomerGroup, PriceDiscountAccountCode.AllCustomers };

                // Search through combinations of item/account codes from most to least specific.
                // This needs to match the behavior of AX code PriceDisc.findPriceAgreement().
                foreach (var accountCode in accountCodes)
                {
                    foreach (var itemCode in itemCodes)
                    {
                        if (priceParameters.Activation(PriceDiscountType.PriceSales, accountCode, itemCode))
                        {
                            IList <string> accountRelations = args.GetAccountRelations(accountCode);
                            string         itemRelation     = args.GetItemRelation(itemCode);

                            if (accountRelations.All(a => ValidRelation(accountCode, a)) &&
                                ValidRelation(itemCode, itemRelation))
                            {
                                bool searchAgain;
                                IEnumerable <TradeAgreement> tradeAgreements = FindPriceAgreements(tradeAgreementRules, args, itemCode, accountCode, salesLines, priceContext, activeDate);
                                PriceResult currentPriceResult = GetBestPriceAgreement(tradeAgreements, out searchAgain);

                                if (priceResult.Price == 0M ||
                                    (currentPriceResult.Price > 0M && currentPriceResult.Price < priceResult.Price))
                                {
                                    priceResult = currentPriceResult;
                                }

                                if (!searchAgain)
                                {
                                    break;
                                }
                            }
                        }
                    }
                }

                return(priceResult);
            }
            /// <summary>
            /// True if there is a valid relation between the account code and relation.
            /// </summary>
            /// <param name="accountCode">The customer account code to validate against (customer/customer group/all).</param>
            /// <param name="relation">The relation to validate.</param>
            /// <returns>True if the relation is compatible with the account code.</returns>
            private static bool ValidRelation(PriceDiscountAccountCode accountCode, string relation)
            {
                bool ok = true;

                if (!string.IsNullOrEmpty(relation) && (accountCode == PriceDiscountAccountCode.AllCustomers))
                {
                    ok = false;
                }

                if (string.IsNullOrEmpty(relation) && (accountCode != PriceDiscountAccountCode.AllCustomers))
                {
                    ok = false;
                }

                return(ok);
            }
Ejemplo n.º 5
0
            /// <summary>
            /// Gets price agreement 'account relations' based on arguments and given account relation code.
            /// </summary>
            /// <param name="accountCode">Account relation code (customer/price group/all).</param>
            /// <returns>
            /// Returns customer if 'customer' code given, price groups if 'group' code given, otherwise empty.
            /// </returns>
            public ReadOnlyCollection <string> GetAccountRelations(PriceDiscountAccountCode accountCode)
            {
                ReadOnlyCollection <string> accountRelations = new ReadOnlyCollection <string>(new List <string> {
                    string.Empty
                });

                if (accountCode == PriceDiscountAccountCode.Customer && !string.IsNullOrEmpty(this.CustomerId))
                {
                    accountRelations = new ReadOnlyCollection <string>(new List <string> {
                        this.CustomerId
                    });
                }
                else if (accountCode == PriceDiscountAccountCode.CustomerGroup && this.PriceGroups.Count > 0)
                {
                    accountRelations = this.PriceGroups;
                }

                return(accountRelations);
            }
Ejemplo n.º 6
0
            /// <summary>
            /// True if there is a valid relation between the account code and relation.
            /// </summary>
            /// <param name="accountCode">The account code (table,group,all).</param>
            /// <param name="relation">The account relation.</param>
            /// <returns>True if the account code allows the given relation.</returns>
            public static bool ValidRelation(PriceDiscountAccountCode accountCode, string relation)
            {
                if (relation == null)
                {
                    throw new ArgumentNullException("relation");
                }

                bool ok = true;

                if (!string.IsNullOrEmpty(relation) && (accountCode == PriceDiscountAccountCode.AllCustomers))
                {
                    ok = false;
                }

                if (string.IsNullOrEmpty(relation) && (accountCode != PriceDiscountAccountCode.AllCustomers))
                {
                    ok = false;
                }

                return(ok);
            }
Ejemplo n.º 7
0
 /// <summary>
 /// Helper method to create new 3-tuple of trade agreement combination.
 /// </summary>
 /// <param name="relation">The type of trade agreement (price, line discount, etc.).</param>
 /// <param name="accountCode">The type of customer (single, group, all).</param>
 /// <param name="itemCode">The type of item (single, group, all).</param>
 /// <returns>3-tuple containing the given combination.</returns>
 private static Tuple <PriceDiscountType, PriceDiscountAccountCode, PriceDiscountItemCode> CreateCombo(PriceDiscountType relation, PriceDiscountAccountCode accountCode, PriceDiscountItemCode itemCode)
 {
     return(new Tuple <PriceDiscountType, PriceDiscountAccountCode, PriceDiscountItemCode>(relation, accountCode, itemCode));
 }
            /// <summary>
            /// Find and total all multiline discount trade agreements that match the given relations and quantity.
            /// </summary>
            /// <param name="tradeAgreements">Trade agreement collection to calculate on.</param>
            /// <param name="itemCode">The item code to search by (item group or all).</param>
            /// <param name="retailTransaction">The transaction context with Id and customer Id.</param>
            /// <param name="priceGroup">Multiline price group.</param>
            /// <param name="salesQuantity">Aggregated quantity for multiline price group.</param>
            /// <param name="percent1">Percentage one.</param>
            /// <param name="percent2">Percentage two.</param>
            /// <param name="discountAmount">Discount amount.</param>
            private void GetMultiLineDiscountLine(
                List <TradeAgreement> tradeAgreements,
                PriceDiscountItemCode itemCode,
                SalesTransaction retailTransaction,
                string priceGroup,
                decimal salesQuantity,
                out decimal percent1,
                out decimal percent2,
                out decimal discountAmount)
            {
                PriceDiscountType relation  = PriceDiscountType.MultilineDiscountSales; // Sales multiline discount - 6
                ProductVariant    dimension = new ProductVariant();

                percent1       = decimal.Zero;
                percent2       = decimal.Zero;
                discountAmount = decimal.Zero;

                bool searchAgain = true;
                var  codes       = new PriceDiscountAccountCode[] { PriceDiscountAccountCode.Customer, PriceDiscountAccountCode.CustomerGroup, PriceDiscountAccountCode.AllCustomers };

                foreach (var accountCode in codes)
                {
                    // skip to next configuration if this one isn't enabled
                    if (!this.discountParameters.Activation(relation, accountCode, itemCode))
                    {
                        continue;
                    }

                    // get item relation based on item code
                    string itemRelation = (itemCode == PriceDiscountItemCode.ItemGroup) ? priceGroup : string.Empty;
                    itemRelation = itemRelation ?? string.Empty;

                    // get customer relation based on account code
                    string accountRelation = string.Empty;
                    if (accountCode == PriceDiscountAccountCode.Customer)
                    {
                        accountRelation = retailTransaction.CustomerId;
                    }
                    else if (accountCode == PriceDiscountAccountCode.CustomerGroup)
                    {
                        accountRelation = this.priceContext.CustomerMultipleLinePriceGroup;
                    }

                    accountRelation = accountRelation ?? string.Empty;

                    // if both relations are valid for the given item and account codes, look for trade agreements matching these relations
                    if (DiscountParameters.ValidRelation(accountCode, accountRelation) &&
                        DiscountParameters.ValidRelation(itemCode, itemRelation))
                    {
                        // get any active multiline discount trade agreement matching relations and quantity
                        var priceDiscTable = Discount.GetPriceDiscData(tradeAgreements, relation, itemRelation, accountRelation, itemCode, accountCode, salesQuantity, this.priceContext, dimension, false);

                        // compute running sum of discount values found
                        foreach (TradeAgreement row in priceDiscTable)
                        {
                            percent1       += row.PercentOne;
                            percent2       += row.PercentTwo;
                            discountAmount += row.Amount;

                            // stop search when we find a trade agreement set to not find next trade agreement
                            if (!row.ShouldSearchAgain)
                            {
                                searchAgain = false;
                            }
                        }
                    }

                    // stop search if we found a discount without "find next" marked
                    if (!searchAgain)
                    {
                        break;
                    }
                }
            }
Ejemplo n.º 9
0
            private static ReadOnlyCollection <TradeAgreement> GetAgreementsFromCollection(List <TradeAgreement> tradeAgreements, PriceDiscountType relation, string itemRelation, string accountRelation, PriceDiscountItemCode itemCode, PriceDiscountAccountCode accountCode, decimal quantityAmount, string targetCurrencyCode, string inventColorId, string inventSizeId, string inventStyleId, string inventConfigId, DateTime today, DateTime noDate)
            {
                ReadOnlyCollection <TradeAgreement> foundAgreements;

                foundAgreements = tradeAgreements
                                  .Where(ta =>
                                         ta.Relation == relation &&
                                         ta.ItemCode == itemCode &&
                                         string.Equals(ta.ItemRelation, itemRelation, StringComparison.OrdinalIgnoreCase) &&
                                         ta.AccountCode == accountCode &&
                                         string.Equals(ta.AccountRelation, accountRelation, StringComparison.OrdinalIgnoreCase) &&
                                         string.Equals(ta.Currency, targetCurrencyCode, StringComparison.OrdinalIgnoreCase) &&
                                         ta.QuantityAmountFrom <= Math.Abs(quantityAmount) &&
                                         (ta.QuantityAmountTo >= Math.Abs(quantityAmount) || ta.QuantityAmountTo == 0) &&
                                         ((ta.FromDate <= today || ta.FromDate <= noDate) &&
                                          (ta.ToDate >= today || ta.ToDate <= noDate)) &&
                                         (string.IsNullOrWhiteSpace(ta.ColorId) || ta.ColorId.Equals(inventColorId, StringComparison.OrdinalIgnoreCase)) &&
                                         (string.IsNullOrWhiteSpace(ta.SizeId) || ta.SizeId.Equals(inventSizeId, StringComparison.OrdinalIgnoreCase)) &&
                                         (string.IsNullOrWhiteSpace(ta.StyleId) || ta.StyleId.Equals(inventStyleId, StringComparison.OrdinalIgnoreCase)) &&
                                         (string.IsNullOrWhiteSpace(ta.ConfigId) || ta.ConfigId.Equals(inventConfigId, StringComparison.OrdinalIgnoreCase)))
                                  .AsReadOnly();

                return(foundAgreements);
            }
Ejemplo n.º 10
0
            private void GetLineDiscountLines(
                List <TradeAgreement> tradeAgreements,
                SalesLine saleItem,
                ref decimal absQty,
                ref decimal discountAmount,
                ref decimal percent1,
                ref decimal percent2,
                ref decimal minQty)
            {
                int idx = 0;

                while (idx < 9)
                {
                    PriceDiscountItemCode    itemCode    = (PriceDiscountItemCode)(idx % 3); // Mod divsion
                    PriceDiscountAccountCode accountCode = (PriceDiscountAccountCode)(idx / 3);

                    string accountRelation = string.Empty;
                    if (accountCode == PriceDiscountAccountCode.Customer)
                    {
                        accountRelation = this.priceContext.CustomerAccount;
                    }
                    else if (accountCode == PriceDiscountAccountCode.CustomerGroup)
                    {
                        accountRelation = this.priceContext.CustomerLinePriceGroup;
                    }

                    accountRelation = accountRelation ?? string.Empty;

                    string itemRelation;
                    if (itemCode == PriceDiscountItemCode.Item)
                    {
                        itemRelation = saleItem.ItemId;
                    }
                    else
                    {
                        Item item = PriceContextHelper.GetItem(this.priceContext, saleItem.ItemId);
                        itemRelation = item != null ? item.LineDiscountGroupId : string.Empty;
                    }

                    itemRelation = itemRelation ?? string.Empty;

                    PriceDiscountType relation = PriceDiscountType.LineDiscountSales; // Sales line discount - 5

                    if (this.discountParameters.Activation(relation, accountCode, itemCode))
                    {
                        if (DiscountParameters.ValidRelation(accountCode, accountRelation) &&
                            DiscountParameters.ValidRelation(itemCode, itemRelation))
                        {
                            bool dimensionDiscountFound = false;

                            if (saleItem.Variant != null && !string.IsNullOrEmpty(saleItem.Variant.VariantId))
                            {
                                var dimensionPriceDiscTable = Discount.GetPriceDiscData(tradeAgreements, relation, itemRelation, accountRelation, itemCode, accountCode, absQty, this.priceContext, saleItem.Variant, true);

                                foreach (TradeAgreement row in dimensionPriceDiscTable)
                                {
                                    bool unitsAreUndefinedOrEqual =
                                        string.IsNullOrEmpty(row.UnitOfMeasureSymbol) ||
                                        string.Equals(row.UnitOfMeasureSymbol, saleItem.SalesOrderUnitOfMeasure, StringComparison.OrdinalIgnoreCase);

                                    if (unitsAreUndefinedOrEqual)
                                    {
                                        percent1       += row.PercentOne;
                                        percent2       += row.PercentTwo;
                                        discountAmount += row.Amount;
                                        minQty         += row.QuantityAmountFrom;
                                    }

                                    if (percent1 > 0M || percent2 > 0M || discountAmount > 0M)
                                    {
                                        dimensionDiscountFound = true;
                                    }

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

                            if (!dimensionDiscountFound)
                            {
                                var priceDiscTable = Discount.GetPriceDiscData(tradeAgreements, relation, itemRelation, accountRelation, itemCode, accountCode, absQty, this.priceContext, saleItem.Variant, false);

                                foreach (TradeAgreement row in priceDiscTable)
                                {
                                    // Apply default if the unit of measure is not set from the cart.
                                    string unitOfMeasure = Discount.GetUnitOfMeasure(saleItem);

                                    bool unitsAreUndefinedOrEqual =
                                        string.IsNullOrEmpty(row.UnitOfMeasureSymbol) ||
                                        string.Equals(row.UnitOfMeasureSymbol, unitOfMeasure, StringComparison.OrdinalIgnoreCase);

                                    if (unitsAreUndefinedOrEqual)
                                    {
                                        percent1       += row.PercentOne;
                                        percent2       += row.PercentTwo;
                                        discountAmount += row.Amount;
                                        minQty         += row.QuantityAmountFrom;
                                    }

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

                    idx++;
                }
            }
            private static IEnumerable <TradeAgreement> FindPriceAgreements(
                IDictionary <string, IList <TradeAgreement> > tradeAgreementRules,
                PriceAgreementArgs args,
                PriceDiscountItemCode itemCode,
                PriceDiscountAccountCode accountCode,
                IEnumerable <SalesLine> salesLines,
                PriceContext priceContext,
                DateTimeOffset activeDate)
            {
                string         itemRelation     = args.GetItemRelation(itemCode);
                IList <string> accountRelations = args.GetAccountRelations(accountCode);
                string         unitId           = args.GetUnitId(itemCode);

                // price trade agreements are always item-specific, so first filter by itemId.
                IList <TradeAgreement> rulesForItem;

                if (!tradeAgreementRules.TryGetValue(itemRelation, out rulesForItem))
                {
                    return(new List <TradeAgreement>(0));
                }

                List <TradeAgreement> tradeAgreementsOfVariantUnFilteredByQuantity = new List <TradeAgreement>();
                List <TradeAgreement> tradeAgreementsOfMasterOrProduct             = new List <TradeAgreement>();

                // Get the initial filtered trade agreements, not checking quantity.
                for (int i = 0; i < rulesForItem.Count; i++)
                {
                    var t = rulesForItem[i];
                    if (t.ItemRelation.Equals(itemRelation, StringComparison.OrdinalIgnoreCase) &&
                        t.ItemCode == itemCode &&
                        t.AccountCode == accountCode &&
                        accountRelations.Contains(t.AccountRelation) &&
                        t.Currency.Equals(args.CurrencyCode, StringComparison.OrdinalIgnoreCase) &&
                        (t.FromDate.DateTime <= activeDate.Date || t.FromDate.DateTime <= NoDate) &&
                        (t.ToDate.DateTime >= activeDate.Date || t.ToDate.DateTime <= NoDate) &&
                        (string.IsNullOrWhiteSpace(unitId) ||
                         t.UnitOfMeasureSymbol.Equals(unitId, StringComparison.OrdinalIgnoreCase)) &&
                        t.IsVariantMatch(args.Dimensions))
                    {
                        if (t.IsVariant)
                        {
                            tradeAgreementsOfVariantUnFilteredByQuantity.Add(t);
                        }
                        else
                        {
                            if (t.IsQuantityMatch(args.Quantity))
                            {
                                tradeAgreementsOfMasterOrProduct.Add(t);
                            }
                        }
                    }
                }

                // For variants
                if (args.IsVariant)
                {
                    List <TradeAgreement> tradeAgreementsOfVariant           = new List <TradeAgreement>();
                    List <TradeAgreement> tradeAgreementsOfVariantExactMatch = new List <TradeAgreement>();

                    foreach (TradeAgreement t in tradeAgreementsOfVariantUnFilteredByQuantity)
                    {
                        if (t.IsVariant)
                        {
                            decimal aggregatedQuantityByAgreementVariant = decimal.Zero;
                            foreach (SalesLine salesLine in salesLines)
                            {
                                if (string.Equals(args.ItemId, salesLine.ItemId, StringComparison.OrdinalIgnoreCase) && t.IsVariantMatch(salesLine.Variant))
                                {
                                    aggregatedQuantityByAgreementVariant += salesLine.Quantity;
                                }
                            }

                            if (aggregatedQuantityByAgreementVariant == decimal.Zero)
                            {
                                aggregatedQuantityByAgreementVariant = 1m;
                            }

                            if (t.IsQuantityMatch(aggregatedQuantityByAgreementVariant))
                            {
                                if (t.IsVariantExactMatch(args.Dimensions))
                                {
                                    tradeAgreementsOfVariantExactMatch.Add(t);
                                }

                                tradeAgreementsOfVariant.Add(t);
                            }
                        }
                    }

                    // 1. Return exact matches if any
                    if (tradeAgreementsOfVariantExactMatch != null && tradeAgreementsOfVariantExactMatch.Any())
                    {
                        if (accountCode == PriceDiscountAccountCode.CustomerGroup)
                        {
                            RetainTopPriorityTradeAgreements(tradeAgreementsOfVariantExactMatch, priceContext);
                        }

                        tradeAgreementsOfVariantExactMatch.Sort(AgreementSortMethod);
                        return(tradeAgreementsOfVariantExactMatch);
                    }

                    // 2. Return (partial) variant matches if any.
                    if (tradeAgreementsOfVariant.Count > 0)
                    {
                        if (accountCode == PriceDiscountAccountCode.CustomerGroup)
                        {
                            RetainTopPriorityTradeAgreements(tradeAgreementsOfVariant, priceContext);
                        }

                        TradeAgreementComparer tradeAgreementComparator = new TradeAgreementComparer(tradeAgreementsOfVariant, args.Dimensions);
                        tradeAgreementComparator.SortTradeAgreement();
                        return(tradeAgreementsOfVariant);
                    }
                }

                // 3. Return non-variant matches.
                if (accountCode == PriceDiscountAccountCode.CustomerGroup)
                {
                    RetainTopPriorityTradeAgreements(tradeAgreementsOfMasterOrProduct, priceContext);
                }

                tradeAgreementsOfMasterOrProduct.Sort(AgreementSortMethod);

                return(tradeAgreementsOfMasterOrProduct);
            }
Ejemplo n.º 12
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);
            }