/// <summary> /// Gets price agreement 'item relation' based on args and given item relation code /// </summary> /// <param name="args">Price agreement args (item, customer, etc.)</param> /// <param name="itemCode">Item relation code (item/group/all)</param> /// <returns>Returns item if 'item' relation code given, otherwise empty string</returns> public string GetItemRelation(PriceDiscItemCode itemCode) { string itemRelation = String.Empty; if (itemCode == PriceDiscItemCode.Table && !String.IsNullOrEmpty(this.ItemId)) { itemRelation = this.ItemId; } return(itemRelation); }
/// <summary> /// Is there a valid relation between the itemcode and relation? /// </summary> /// <param name="itemCode"></param> /// <param name="relation"></param> /// <returns></returns> private static bool ValidRelation(PriceDiscItemCode itemCode, string relation) { bool ok = true; if (!string.IsNullOrEmpty(relation) && (itemCode == PriceDiscItemCode.All)) { ok = false; } if (string.IsNullOrEmpty(relation) && (itemCode != PriceDiscItemCode.All)) { ok = false; } return(ok); }
/// <summary> /// Retrieve all multiline discount amounts for given item code and transaction. /// If nothing is found for channel currency, fall back to company currency to search /// </summary> /// <param name="itemCode">The item code to search by (item group or all)</param> /// <param name="transaction">The transaction context containing Id and customer Id</param> /// <param name="discountRow">The working multiline discount row to populate</param> /// <param name="channelCurrency">The channel currency of the current channel</param> /// <param name="companyCurrency">The company currency of the channel's company</param> /// <returns>Discount row populated with total discount amounts found</returns> private DataRow GetMultilineDiscountLineForCurrencies( PriceDiscItemCode itemCode, RetailTransaction transaction, DataRow discountRow, string channelCurrency, string companyCurrency) { DataRow mlRow = this.GetMultiLineDiscountLine(itemCode, transaction, discountRow, channelCurrency); if (mlRow.Field <decimal>("PERCENT1") == 0M && mlRow.Field <decimal>("PERCENT2") == 0M && mlRow.Field <decimal>("AMOUNT") == 0M && (!channelCurrency.Equals(companyCurrency, StringComparison.OrdinalIgnoreCase))) { mlRow = this.GetMultiLineDiscountLine(itemCode, transaction, discountRow, companyCurrency); mlRow["AMOUNT"] = this.Application.Services.Currency.CurrencyToCurrency(this.CompanyCurrency, this.ChannelCurrency, mlRow.Field <decimal>("AMOUNT")); } return(mlRow); }
/// <summary> /// Returns if a certain relation is active for a price search. /// </summary> /// <param name="accountCode"></param> /// <param name="itemCode"></param> /// <returns></returns> public bool IsRelationActive(PriceDiscAccountCode accountCode, PriceDiscItemCode itemCode) { switch (accountCode) { case PriceDiscAccountCode.Table: switch (itemCode) { case PriceDiscItemCode.Table: return(this.salesPriceAccountItem); case PriceDiscItemCode.GroupId: return(false); case PriceDiscItemCode.All: return(false); } break; case PriceDiscAccountCode.GroupId: switch (itemCode) { case PriceDiscItemCode.Table: return(this.salesPriceGroupItem); case PriceDiscItemCode.GroupId: return(false); case PriceDiscItemCode.All: return(false); } break; case PriceDiscAccountCode.All: switch (itemCode) { case PriceDiscItemCode.Table: return(this.salesPriceAllItem); case PriceDiscItemCode.GroupId: return(false); case PriceDiscItemCode.All: return(false); } break; } return(false); }
/// <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="args">Arguments for price agreement search</param> /// <param name="priceParameters">Set of enabled price agreement types</param> /// <returns>Most applicable price for the given price agreement constraints.</returns> public static PriceResult priceAgr(PriceAgreementArgs args, PriceParameters priceParameters) { PriceResult priceResult = new PriceResult(0M, PriceGroupIncludesTax.NotSpecified); for (int idx = 0; idx < 9; idx++) { // Enum values for ItemCode/AccountCode: 0=Table, 1=Group, 2=All PriceDiscItemCode itemCode = (PriceDiscItemCode)(idx % 3); //Mod divsion PriceDiscAccountCode accountCode = (PriceDiscAccountCode)(idx / 3); //three possible item-/account-Codes, as described in the ENUMs. if (priceParameters.IsRelationActive(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; PriceResult currentPriceResult = GetBestPriceAgreement(FindPriceAgreements(args, itemCode, accountCode), out searchAgain); if (priceResult.Price == 0M || (currentPriceResult.Price > 0M && currentPriceResult.Price < priceResult.Price)) { priceResult = currentPriceResult; } if (!searchAgain) { break; } } } } return(priceResult); }
/// <summary> /// Find and total all multiline discount trade agreements that match the given relations and quantity /// </summary> /// <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="discountRow">Current row in multiline discount working table. Will be populated and returned.</param> /// <param name="currencyCode">The currency code to filter by</param> /// <returns>Discount row populated with sums for all discounts found</returns> private DataRow GetMultiLineDiscountLine(PriceDiscItemCode itemCode, RetailTransaction retailTransaction, DataRow discountRow, string currencyCode) { PriceDiscType relation = PriceDiscType.MultiLineDiscSales; //Sales multiline discount - 6 Dimensions dimension = (Dimensions)this.Application.BusinessLogic.Utility.CreateDimension(); bool searchAgain = true; var codes = new PriceDiscAccountCode[] { PriceDiscAccountCode.Table, PriceDiscAccountCode.GroupId, PriceDiscAccountCode.All }; foreach (var accountCode in codes) { // skip to next configuration if this one isn't enabled if (!DiscountParameters.Activation(relation, accountCode, itemCode)) { continue; } // get item relation based on item code string itemRelation = (itemCode == PriceDiscItemCode.GroupId) ? discountRow.Field <string>("MULTILINEGROUP") : string.Empty; itemRelation = itemRelation ?? String.Empty; // get customer relation based on account code string accountRelation = String.Empty; if (accountCode == PriceDiscAccountCode.Table) { accountRelation = retailTransaction.Customer.CustomerId; } else if (accountCode == PriceDiscAccountCode.GroupId) { accountRelation = retailTransaction.Customer.MultiLineDiscountGroup; } accountRelation = accountRelation ?? String.Empty; // if both relations are valid for the given item and account codes, look for trade agreements matching these relations if ((Discount.ValidRelation(accountCode, accountRelation)) && (Discount.ValidRelation(itemCode, itemRelation))) { // get any active multiline discount trade agreement matching relations and quantity decimal quantityAmount = discountRow.Field <decimal>("QUANTITY"); var priceDiscTable = this.DiscountService.GetPriceDiscDataCached( retailTransaction.TransactionId, relation, itemRelation, accountRelation, (int)itemCode, (int)accountCode, quantityAmount, currencyCode, dimension, false); // compute running sum of discount values found foreach (Discount.DiscountAgreementArgs row in priceDiscTable) { discountRow["PERCENT1"] = discountRow.Field <decimal>("PERCENT1"); discountRow["PERCENT1"] = discountRow.Field <decimal>("PERCENT1") + row.Percent1; discountRow["PERCENT2"] = discountRow.Field <decimal>("PERCENT2") + row.Percent2; discountRow["AMOUNT"] = discountRow.Field <decimal>("AMOUNT") + row.Amount; discountRow["MINQTY"] = discountRow.Field <decimal>("MINQTY") + row.QuantityAmountFrom; // stop search when we find a trade agreement set to not find next trade agreement if (!row.SearchAgain) { searchAgain = false; } } } // stop search if we found a discount without "find next" marked if (!searchAgain) { break; } } return(discountRow); }
public bool Activation(PriceDiscType relation, PriceDiscAccountCode accountCode, PriceDiscItemCode itemCode) { switch (accountCode) { case PriceDiscAccountCode.Table: switch (itemCode) { case PriceDiscItemCode.Table: switch (relation) { case PriceDiscType.LineDiscSales: return(SalesLineAccountItem); default: return(false); } case PriceDiscItemCode.GroupId: switch (relation) { case PriceDiscType.LineDiscSales: return(SalesLineAccountGroup); case PriceDiscType.MultiLineDiscSales: return(SalesMultiLineAccountGroup); default: return(false); } case PriceDiscItemCode.All: switch (relation) { case PriceDiscType.LineDiscSales: return(SalesLineAccountAll); case PriceDiscType.MultiLineDiscSales: return(SalesMultiLineAccountAll); case PriceDiscType.EndDiscSales: return(SalesEndAccountAll); default: return(false); } default: NetTracer.Warning("DiscountParameters::Activation: itemCode is out of range: {0}", itemCode); throw new ArgumentOutOfRangeException("itemCode"); } case PriceDiscAccountCode.GroupId: switch (itemCode) { case PriceDiscItemCode.Table: switch (relation) { case PriceDiscType.LineDiscSales: return(SalesLineGroupItem); default: return(false); } case PriceDiscItemCode.GroupId: switch (relation) { case PriceDiscType.LineDiscSales: return(SalesLineGroupGroup); case PriceDiscType.MultiLineDiscSales: return(SalesMultiLineGroupGroup); default: return(false); } case PriceDiscItemCode.All: switch (relation) { case PriceDiscType.LineDiscSales: return(SalesLineGroupAll); case PriceDiscType.MultiLineDiscSales: return(SalesMultiLineGroupAll); case PriceDiscType.EndDiscSales: return(SalesEndGroupAll); default: return(false); } default: NetTracer.Warning("DiscountParameters::Activation: itemCode is out of range: {0}", itemCode); throw new ArgumentOutOfRangeException("itemCode"); } case PriceDiscAccountCode.All: switch (itemCode) { case PriceDiscItemCode.Table: switch (relation) { case PriceDiscType.LineDiscSales: return(SalesLineAllItem); default: return(false); } case PriceDiscItemCode.GroupId: switch (relation) { case PriceDiscType.LineDiscSales: return(SalesLineAllGroup); case PriceDiscType.MultiLineDiscSales: return(SalesMultiLineAllGroup); default: return(false); } case PriceDiscItemCode.All: switch (relation) { case PriceDiscType.LineDiscSales: return(SalesLineAllAll); case PriceDiscType.MultiLineDiscSales: return(SalesMultiLineAllAll); case PriceDiscType.EndDiscSales: return(SalesEndAllAll); default: return(false); } default: NetTracer.Warning("DiscountParameters::Activation: itemCode is out of range: {0}", itemCode); throw new ArgumentOutOfRangeException("itemCode"); } default: NetTracer.Warning("DiscountParameters::Activation: accountCode is out of range: {0}", accountCode); throw new ArgumentOutOfRangeException("accountCode"); } }
/// <summary> /// This function retrieves all possible price agreements for the given args (item, customer, currency, etc.), /// item relation code (item/group/all), and account relation code (customer/price group/all). /// </summary> /// <param name="args">Arguments for price agreement search</param> /// <param name="itemCode">Item relation code to look for (in order: item/group/all)</param> /// <param name="accountCode">Account relation code to look for (in order: customer/price group/all)</param> /// <returns>Collection of applicable price agreements, sorted by price amount ascending</returns> static private IEnumerable <DE.PriceDiscTable> FindPriceAgreements(PriceAgreementArgs args, PriceDiscItemCode itemCode, PriceDiscAccountCode accountCode) { List <DE.PriceDiscTable> tradeAgreements = new List <DE.PriceDiscTable>(); string itemRelation = args.GetItemRelation(itemCode); IList <string> accountRelations = args.GetAccountRelations(accountCode); string unitId = args.GetUnitId(itemCode); DateTime today = DateTime.Now.Date; SqlConnection connection = Price.InternalApplication.Settings.Database.Connection; string dataAreaId = Price.InternalApplication.Settings.Database.DataAreaID; bool isIndia = Functions.CountryRegion == SupportedCountryRegion.IN; try { // convert account relations list to data-table for use as TVP in the query. string queryString = @" SELECT ta.PRICEUNIT, ta.ALLOCATEMARKUP, ta.AMOUNT, ta.SEARCHAGAIN" + (isIndia ? ", ta.MAXIMUMRETAILPRICE_IN" : string.Empty) + @" FROM PRICEDISCTABLE ta LEFT JOIN INVENTDIM invdim ON ta.INVENTDIMID = invdim.INVENTDIMID AND ta.DATAAREAID = invdim.DATAAREAID WHERE ta.RELATION = 4 AND ta.ITEMCODE = @ITEMCODE AND ta.ITEMRELATION = @ITEMRELATION AND ta.ACCOUNTCODE = @ACCOUNTCODE -- USES Tvp: CREATE TYPE FINDPRICEAGREEMENT_ACCOUNTRELATIONS_TABLETYPE AS TABLE(ACCOUNTRELATION nvarchar(20) NOT NULL); AND (ta.ACCOUNTRELATION) IN (SELECT ar.ACCOUNTRELATION FROM @ACCOUNTRELATIONS ar) AND ta.CURRENCY = @CURRENCYCODE AND ta.UNITID = @UNITID AND ta.QUANTITYAMOUNTFROM <= abs(@QUANTITY) AND (ta.QUANTITYAMOUNTTO >= abs(@QUANTITY) OR ta.QUANTITYAMOUNTTO = 0) AND ta.DATAAREAID = @DATAAREAID AND ((ta.FROMDATE <= @TODAY OR ta.FROMDATE <= @NODATE) AND (ta.TODATE >= @TODAY OR ta.TODATE <= @NODATE)) AND (invdim.INVENTCOLORID in (@COLORID, '')) AND (invdim.INVENTSIZEID in (@SIZEID,'')) AND (invdim.INVENTSTYLEID in (@STYLEID, '')) AND (invdim.CONFIGID in (@CONFIGID, '')) --// ORDERBY CLAUSE MUST MATCH THAT IN AX TO ENSURE COMPATIBLE PRICING BEHAVIOR. --// SEE THE CLASS PRICEDISC.FINDPRICEAGREEMENT() AND TABLE PRICEDISCTABLE.PRICEDISCIDX order by ta.AMOUNT, ta.QUANTITYAMOUNTFROM, ta.QUANTITYAMOUNTTO, ta.FROMDATE"; using (SqlCommand command = new SqlCommand(queryString, connection)) { command.Parameters.AddWithValue("@DATAAREAID", dataAreaId); command.Parameters.AddWithValue("@ITEMCODE", itemCode); command.Parameters.AddWithValue("@ITEMRELATION", itemRelation); command.Parameters.AddWithValue("@ACCOUNTCODE", accountCode); command.Parameters.AddWithValue("@UNITID", unitId); command.Parameters.AddWithValue("@CURRENCYCODE", args.CurrencyCode); command.Parameters.AddWithValue("@QUANTITY", args.Quantity); command.Parameters.AddWithValue("@COLORID", (args.Dimensions.ColorId) ?? string.Empty); command.Parameters.AddWithValue("@SIZEID", (args.Dimensions.SizeId) ?? string.Empty); command.Parameters.AddWithValue("@STYLEID", (args.Dimensions.StyleId) ?? string.Empty); command.Parameters.AddWithValue("@CONFIGID", (args.Dimensions.ConfigId) ?? string.Empty); command.Parameters.AddWithValue("@TODAY", today); command.Parameters.AddWithValue("@NODATE", DateTime.Parse("1900-01-01")); // Fill out TVP for account relations list using (DataTable accountRelationsTable = new DataTable()) { accountRelationsTable.Columns.Add("ACCOUNTRELATION", typeof(string)); foreach (string relation in accountRelations) { accountRelationsTable.Rows.Add(relation); } SqlParameter param = command.Parameters.Add("@ACCOUNTRELATIONS", SqlDbType.Structured); param.Direction = ParameterDirection.Input; param.TypeName = "FINDPRICEAGREEMENT_ACCOUNTRELATIONS_TABLETYPE"; param.Value = accountRelationsTable; if (connection.State != ConnectionState.Open) { connection.Open(); } SqlDataReader reader = command.ExecuteReader(); while (reader.Read()) { DE.PriceDiscTable pdt = new DE.PriceDiscTable() { PriceUnit = reader.GetDecimal(reader.GetOrdinal("PRICEUNIT")), ShouldAllocateMarkup = reader.GetInt32(reader.GetOrdinal("ALLOCATEMARKUP")), Amount = reader.GetDecimal(reader.GetOrdinal("AMOUNT")), ShouldSearchAgain = reader.GetInt32(reader.GetOrdinal("SEARCHAGAIN")), IndiaMRP = (isIndia) ? reader.GetDecimal(reader.GetOrdinal("MAXIMUMRETAILPRICE_IN")) : Decimal.Zero }; tradeAgreements.Add(pdt); } } } } finally { if (connection.State == ConnectionState.Open) { connection.Close(); } } return(tradeAgreements); }
/// <summary> /// Gets price agreement unit of measure based on args and given item relation code /// </summary> /// <param name="args">Price agreement args (item, customer, uom, etc.)</param> /// <param name="itemCode">Item relation code (item/group/all)</param> /// <returns>Return unit of measure id if 'item' code specified, otherwise empty</returns> public string GetUnitId(PriceDiscItemCode itemCode) { return(itemCode == PriceDiscItemCode.Table && !String.IsNullOrEmpty(this.UnitOfMeasure) ? this.UnitOfMeasure : String.Empty); }