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