private Dictionary <string, decimal> GetPriceGroupSalesQuantityLookup(SalesTransaction transaction)
            {
                // Sum up all the linegroup discount lines in the same group
                // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                Dictionary <string, decimal> priceGroupSalesQuantityLookup = new Dictionary <string, decimal>(StringComparer.OrdinalIgnoreCase);

                foreach (SalesLine salesLine in transaction.PriceCalculableSalesLines)
                {
                    Item item = PriceContextHelper.GetItem(this.priceContext, salesLine.ItemId);
                    if (item != null && !string.IsNullOrEmpty(item.MultilineDiscountGroupId))
                    {
                        decimal quantity = decimal.Zero;

                        if (priceGroupSalesQuantityLookup.TryGetValue(item.MultilineDiscountGroupId, out quantity))
                        {
                            priceGroupSalesQuantityLookup[item.MultilineDiscountGroupId] = quantity + salesLine.Quantity;
                        }
                        else
                        {
                            priceGroupSalesQuantityLookup.Add(item.MultilineDiscountGroupId, salesLine.Quantity);
                        }
                    }
                }

                return(priceGroupSalesQuantityLookup);
            }
Пример #2
0
            /// <summary>
            /// Check whether the discount is allowed for the item.
            /// </summary>
            /// <param name="priceContext">Price context.</param>
            /// <param name="itemId">Item identifier.</param>
            /// <returns>True if  the discount is allowed for the item, otherwise false.</returns>
            public static bool IsDiscountAllowed(PriceContext priceContext, string itemId)
            {
                Item item = PriceContextHelper.GetItem(priceContext, itemId);
                bool isDiscountAllowed = item != null ? !item.NoDiscountAllowed : true;

                return(isDiscountAllowed);
            }
Пример #3
0
            private bool IsTotalDiscountAllowed(string itemId)
            {
                Item item = PriceContextHelper.GetItem(this.priceContext, itemId);
                bool isTotalDiscountAllowed = item != null && item.IsTotalDiscountAllowed && !item.NoDiscountAllowed;

                return(isTotalDiscountAllowed);
            }
            /// <summary>
            /// The calculation of a customer multiline discount.
            /// </summary>
            /// <remarks>
            /// Calculation of multiline discount is done as follows:
            ///   1. Create working table for calculation.
            ///   2. Populate working table with total quantities for all the multiline groups encountered on the sales lines.
            ///   3. For all rows (and therefore multiline groups) found, search for trade agreements in the database.
            ///      a. The search is first for customer-specific, then customer multiline discount group, then all customers.
            ///      b. The search stops when a trade agreement is encountered with "Find next" unmarked.
            ///      c. If nothing is found for the store currency the search is attempted again with the company accounting currency.
            ///   4. All found agreements are summed in the working table and applied to each sales line which matches the multiline groups.
            ///   5. If there are sales lines which weren't discounted with a multiline discount.
            ///      a. Find their total quantity and search for any multiline trade agreements marked for "All items".
            ///      b. If any agreements were found apply them to any lines that weren't already discounted with a multiline discount.
            /// </remarks>
            /// <param name="tradeAgreements">Trade agreement collection to calculate on. If null, uses the pricing data manager to find agreements.</param>
            /// <param name="transaction">The sales transaction which needs multiline discounts attached.</param>
            /// <returns>
            /// The sales transaction.
            /// </returns>
            public SalesTransaction CalcMultiLineDiscount(List <TradeAgreement> tradeAgreements, SalesTransaction transaction)
            {
                if (tradeAgreements != null && tradeAgreements.Any())
                {
                    // collection of salesLine not discounted by multiline discount group
                    // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                    var nondiscountedSalesLines = new List <SalesLine>(transaction.PriceCalculableSalesLines);

                    Dictionary <string, decimal> priceGroupSalesQuantityLookup = this.GetPriceGroupSalesQuantityLookup(transaction);

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

                    // Find discounts for the different multiline discount groups
                    foreach (KeyValuePair <string, decimal> priceGroupQuantityPair in priceGroupSalesQuantityLookup)
                    {
                        // we've found some multiline discount groups, so clear non-discounted lines from the default of "all"
                        nondiscountedSalesLines.Clear();

                        // find multiline discounts for this multiline discount row
                        this.GetMultiLineDiscountLine(tradeAgreements, PriceDiscountItemCode.ItemGroup, transaction, priceGroupQuantityPair.Key, priceGroupQuantityPair.Value, out percent1, out percent2, out discountAmount);

                        // Update the sale items.
                        // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                        foreach (var saleItem in transaction.PriceCalculableSalesLines)
                        {
                            Item   item            = PriceContextHelper.GetItem(this.priceContext, saleItem.ItemId);
                            string discountGroupId = item != null ? item.MultilineDiscountGroupId : string.Empty;
                            if (string.Equals(discountGroupId, priceGroupQuantityPair.Key, StringComparison.OrdinalIgnoreCase))
                            {
                                // if line is part of discounted item group, apply the discount
                                ApplyMultilineDiscount(saleItem, percent1, percent2, discountAmount);
                            }
                            else
                            {
                                // otherwise, add to non-discounted lines
                                nondiscountedSalesLines.Add(saleItem);
                            }
                        }
                    }

                    // find total quantity of items on lines still eligible for multiline discount
                    decimal lineSum = nondiscountedSalesLines.Aggregate(0M, (acc, sl) => acc + sl.Quantity);

                    // find any multiline discounts to apply to "all items"
                    this.GetMultiLineDiscountLine(tradeAgreements, PriceDiscountItemCode.AllItems, transaction, string.Empty, lineSum, out percent1, out percent2, out discountAmount);

                    // Update the sale items.
                    foreach (var saleItem in nondiscountedSalesLines)
                    {
                        ApplyMultilineDiscount(saleItem, percent1, percent2, discountAmount);
                    }
                }

                return(transaction);
            }
Пример #5
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));
                }
            }
Пример #6
0
            /// <summary>
            /// For all sales lines on the transaction, retrieve the product rec id if it's not already set.
            /// </summary>
            /// <param name="pricingDataManager">Provides data access to the calculation.</param>
            /// <param name="priceContext">Price context.</param>
            /// <param name="salesLines">Sales lines.</param>
            private static void PopulateProductIds(IPricingDataAccessor pricingDataManager, PriceContext priceContext, IEnumerable <SalesLine> salesLines)
            {
                var itemVariantIds = new HashSet <ItemVariantInventoryDimension>();

                foreach (var line in salesLines)
                {
                    if ((line.Variant == null || line.Variant.DistinctProductVariantId == 0) && !string.IsNullOrWhiteSpace(line.InventoryDimensionId))
                    {
                        var itemVariantId = new ItemVariantInventoryDimension(line.ItemId, line.InventoryDimensionId);
                        itemVariantIds.Add(itemVariantId);
                    }
                }

                // We make a single database call to retrieve all variant identifiers that we need
                // and create a map using the ItemVariantInventoryDimension as its key.
                var variantsMap = new Dictionary <ItemVariantInventoryDimension, ProductVariant>();

                if (itemVariantIds.Any())
                {
                    variantsMap = ((IEnumerable <ProductVariant>)pricingDataManager.GetVariants(itemVariantIds)).ToDictionary(key => new ItemVariantInventoryDimension(key.ItemId, key.InventoryDimensionId));
                }

                // Consider calculable lines only. Ignore voided or return-by-receipt lines.
                foreach (var line in salesLines)
                {
                    if (line.MasterProductId == 0)
                    {
                        Item item = PriceContextHelper.GetItem(priceContext, line.ItemId);

                        line.MasterProductId = (item != null) ? item.Product : 0L;

                        if (item != null && string.IsNullOrWhiteSpace(line.OriginalSalesOrderUnitOfMeasure))
                        {
                            line.OriginalSalesOrderUnitOfMeasure = item.SalesUnitOfMeasure;
                        }
                    }

                    if ((line.Variant == null || line.Variant.DistinctProductVariantId == 0) && !string.IsNullOrWhiteSpace(line.InventoryDimensionId))
                    {
                        ProductVariant variant;
                        var            itemVariant = new ItemVariantInventoryDimension(line.ItemId, line.InventoryDimensionId);
                        if (variantsMap.TryGetValue(itemVariant, out variant))
                        {
                            line.Variant = variant;
                        }
                    }

                    if (line.ProductId == 0)
                    {
                        line.ProductId = line.Variant != null ? line.Variant.DistinctProductVariantId : line.MasterProductId;
                    }
                }
            }
Пример #7
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++;
                }
            }