Quantcast
Viewing all articles
Browse latest Browse all 4

Minimum Pricing Customizations

Minimum Pricing – Problem Defined

Overview and Client UI

My company (referred to as ‘RES’ through the remainder of this post) has trade agreements which contain all the pricing for our various inventory items. Now, we use AX in a somewhat backwards way (well, backwards to the way most companies would consider using an ERP system to look at inventory).. instead of selling inventory out to customers, customers ship us inventory (namely, containers of waste product). They’re paying us for the services we perform against their various waste streams which we set up as “inventory items” in AX.

Thus, we added custom fields to our trade agreements which allow us to specify a minimum price:

Image may be NSFW.
Clik here to view.

When a trade agreement is configured to use minimum pricing, the logic would read as follows:

Item X is priced at $1.84 USD per pound, with a minimum charge of $20.00 USD.

If a customer wants us to process a container full of item X that only weighs 5 pounds, then, instead of charging 5 x 1.84 = $9.20, the minimum price would be applied instead: $20.00

We also have a custom unit of measure field on the lines of the sales order which records the type of pricing which was applied. If an item bills using minimum pricing, instead of showing “lb” it would show “MIN”.

Image may be NSFW.
Clik here to view.

 

AOT

Some of the aforementioned logic implemented in the AOT is shown below:

Trade Agreement custom fields: Sales Line custom UOM field:

Image may be NSFW.
Clik here to view.

Image may be NSFW.
Clik here to view.
These SalesLine methods were also updated to help calculate min pricing:
  • Insert
  • Update
  • setResPriceDiscRecid
  • getPricingUnit

Heart of the Solution: Understanding the SalesPurchLine Map

Maps in the AOT

If anyone should ever want to fully understand or ever, *gasp*, update this minimum pricing logic, they MUST start by gaining an expert understanding of how Maps work in AX. While it’s not the scope of this post to describe what Maps do, it should at least be noted that Maps can sit atop a whole host of tables in the AOT. A single map might have relations with several tables that are not necessarily all related to each other.

Early Problems with the SalesPurchLine Map

Not realizing the cautions stated above, several developers edited the SalesPurchLine.calc2LineAmount method in efforts to implement minimum pricing logic in regards to the SalesLine table. However, these edits were starting to have adverse affects on un-related modules, e.g. Accounts Payable. So, carefully note in the final code snippet below how the table currently interacting with SalesPurchLine must be detected, so that custom logic is only run in appropriate contexts.

The Final Solution

        The following code snippet shows the entire calcPrice2LineAmount method found on the SalesPurchLine map.

Note the comment related to tenth of penny pricing. If you’re taking advantage of the quirky way AX allows you to do fraction of a peny pricing (where you set up prices at a level of ten units or a hundred units instead of against a single unit) that one line will be very critical.

// NOTE: References to RES and RIZ in the comments below are not really hard-coded in X++ logic,

// thus, they should be taken figuatively. ‘RES’ simply means: ‘the company initiating a customer-facing transaction’

// and RIZ would mean ‘the company partaking in a resulting intercompany transaction, not customer facing’

// In reality, this logic could potentially allow for any Ross company to employ minimum pricing.

AmountCur calcPrice2LineAmount(Qty _qty, boolean _forceLineAmountCalculation)

{

AmountCur lineAmount;

PriceDiscTable mPrice;

ItemGroupId itemGroupId;

Price tmpPrice;

Qty tmpQty;

Qty tmpQtyNow;

boolean minPriceInEffect;

;

if(this.TableId == TableNum(SalesLine) || this.TableId == TableNum(PurchLine))

{


// Only SalesLine and PurchLine tables should execute

// RES’ custom minimum pricing logic

if (_qty)

{


// If RIZ sales order

if (this.InterCompanyOrigin == InterCompanyOrigin::Derived && !this.RESPriceDiscRecId)

{

// Do calculation using standard AX logic

lineAmount = PriceDisc::price2Amount(this.Price,

this.PriceUnit,

this.discAmount(),

_qty, // deliver now

this.SalesPurchQty,

this.Markup,

this.discPercent(),

this.CurrencyCode,

this.LineAmount,

_forceLineAmountCalculation);

lineAmount = this.LineAmount;

}


// Either intercopmany or RES order so we must apply min pricing logic

else

{

// Do initial calculation with no discounts

lineAmount = PriceDisc::price2Amount(this.Price,

this.PriceUnit,

0,

_qty, // deliver now

this.SalesPurchQty,

this.Markup,

0,

this.CurrencyCode,

this.LineAmount,

_forceLineAmountCalculation);


// Apply min pricing if applicable

mPrice = PriceDiscTable::findRecId(this.RESPriceDiscRecId);

if (mPrice)

{

if (mPrice.ResMinimumLineItemPriceYN == NoYes::Yes)

{

if (mPrice.ResMinimumPrice > lineamount)

{

lineamount = Currency::amount(mPrice.ResMinimumPrice, this.CurrencyCode);

minPriceInEffect = true;

}

}

}


// Min price is used so modify price to assigned min price and adjust quantity to a single item

if(minPriceInEffect)

{

tmpPrice = lineAmount;

tmpQty = this.PriceUnit < 1 ? 1 : this.PriceUnit;//tenth-of-penny pricing requires this

tmpQtyNow = tmpQty;

}


// We are using standard pricing so transfer over the values to the temp variables

else

{

tmpPrice = this.Price;

tmpQty = this.SalesPurchQty;

tmpQtyNow = _qty;

}


// Calculate price using the correct price (minimum or standard)

lineAmount = PriceDisc::price2Amount(tmpPrice,

this.PriceUnit,

this.discAmount(),

tmpQtyNow,

tmpQty,

this.Markup,

this.discPercent(),

this.CurrencyCode,

this.LineAmount,

_forceLineAmountCalculation);

}

}

}

else

{


// All other tables mapped to SalesPurchLine

// fall to this block and execute standard AX logic:

if (_qty)

{

lineAmount = PriceDisc::price2Amount(this.Price,

this.PriceUnit,

this.discAmount(),

_qty,

this.SalesPurchQty,

this.Markup,

this.discPercent(),

this.CurrencyCode,

this.LineAmount,

_forceLineAmountCalculation);

}

}

return lineAmount;

}

OtherEdits to SalesPurchLine Map

When we worked on another AX customization, namely, utilizing custom dates for invoicing, we also had to make an additional change to the SalesPurchLine map. In this case, we added a whole new field, RESPickupDate, to the Mappings node.

Additional Resources

Undestanding MAPS in the AOT

This is an interesting MSDN link that actually gives a Maps code example with comments, and then a verbose explanation about what the comments mean:
http://msdn.microsoft.com/en-us/library/ee330226(v=AX.50).aspx


Viewing all articles
Browse latest Browse all 4

Trending Articles