In Microsoft Dynamics 365 Finance and Operations (D365FO), trade agreements are used to manage prices and discounts for customers, vendors, and items. Frequently, developers need to retrieve the applicable line discount percentage for a specific item, customer, and date without creating a sales order.
The recommended approach is to use the PriceDisc framework, which is the standard pricing and discount engine used throughout D365FO. Microsoft identifies the PriceDisc class as the search engine for prices and discounts, while PriceDiscParameters acts as the container for all pricing criteria.
This article explains a practical X++ implementation that retrieves the line discount percentage from trade agreements.
Complete Method
server static DiscPct getLineDiscountPct( ItemId _itemId, CustAccount _custAccount, TransDate _priceDate = systemDateGet())
{
InventTable inventTable;
InventDim inventDim;
PriceDisc priceDisc;
PriceDiscParameters priceDiscParameters = PriceDiscParameters::construct();
DiscPct lineDiscPct;
boolean discExist;
inventTable = InventTable::find(_itemId);
if (!inventTable)
return 0;
// Fix blank date
if (_priceDate == dateNull())
{ _priceDate = today();
}
// Build parameters
priceDiscParameters.parmModuleType(ModuleInventPurchSales::Sales);
priceDiscParameters.parmItemId(inventTable.ItemId);
priceDiscParameters.parmInventDim(InventDim::findOrCreate(inventDim));
priceDiscParameters.parmUnitID(inventTable.inventTableModuleSales().UnitId);
priceDiscParameters.parmPriceDiscDate(_priceDate);
priceDiscParameters.parmQty(1);
priceDiscParameters.parmAccountNum(_custAccount);
priceDiscParameters.parmCurrencyCode(
Ledger::reportingCurrency(CompanyInfo::current()));
priceDisc = PriceDisc::newFromPriceDiscParameters(priceDiscParameters);
discExist = priceDisc.findLineDisc('', '');
if (discExist)
{ lineDiscPct = priceDisc.lineDiscPct();
}
else {
lineDiscPct = 0;
}
return lineDiscPct;
}
Understanding the Logic
The method determines the line discount percentage that would be applied to an item for a customer on a specific date based on existing trade agreements.
Input Parameters
ItemId _itemId
CustAccount _custAccount
TransDate _priceDate
| Parameter | Purpose |
|---|---|
| _itemId | Item to evaluate |
| _custAccount | Customer account |
| _priceDate | Date used for trade agreement validity |
Example:
DiscPct discount =
MyPricingHelper::getLineDiscountPct(
"A0001",
"US-001",
today());
The method returns the matching trade agreement discount percentage.
Step 1: Retrieve Item Information
inventTable = InventTable::find(_itemId);
The first step retrieves the item record from InventTable.
if (!inventTable)
return 0;
If the item does not exist, there is no reason to continue searching for discounts.
Step 2: Handle Empty Date Values
if (_priceDate == dateNull())
{
_priceDate = today();
}
Trade agreements are date-sensitive.
Every trade agreement contains:
- From Date
- To Date
If the caller passes a blank date, the method defaults to today's date.
This ensures the pricing engine evaluates currently active agreements.
Step 3: Create PriceDiscParameters
Microsoft recommends using PriceDiscParameters to pass all pricing and discount criteria into the pricing engine. The PriceDisc class then performs the actual trade agreement search.
PriceDiscParameters priceDiscParameters =
PriceDiscParameters::construct();
This object acts as the search request.
Step 4: Specify Sales Module
priceDiscParameters.parmModuleType(
ModuleInventPurchSales::Sales);
This tells the framework:
Search Sales trade agreements.
Possible values include:
ModuleInventPurchSales::Sales
ModuleInventPurchSales::Purch
Because we are evaluating customer discounts, Sales is appropriate.
Step 5: Set Item
priceDiscParameters.parmItemId(
inventTable.ItemId);
The pricing engine needs to know which item is being evaluated.
The engine will check trade agreement records that match:
- Specific item
- Item group
- All items
depending on the setup.
Step 6: Set Inventory Dimensions
priceDiscParameters.parmInventDim(
InventDim::findOrCreate(inventDim));
This supplies inventory dimensions.
Examples include:
- Site
- Warehouse
- Color
- Size
- Style
In your code:
InventDim inventDim;
is blank.
Therefore:
InventDim::findOrCreate(inventDim)
creates an "empty" dimension combination.
This means discounts are searched without dimension-specific restrictions.
If your trade agreements depend on site or warehouse, you should populate these values.
Example:
inventDim.InventSiteId = "1";
inventDim.InventLocationId = "11";
Step 7: Set Sales Unit
priceDiscParameters.parmUnitID(
inventTable.inventTableModuleSales().UnitId);
Trade agreements can be defined per unit.
Example:
| Unit | Discount |
|---|---|
| EA | 10% |
| BOX | 15% |
The pricing engine therefore requires the sales unit.
Step 8: Set Pricing Date
priceDiscParameters.parmPriceDiscDate(
_priceDate);
This is one of the most important parameters.
The engine uses it to validate:
From Date <= Price Date <= To Date
Only active agreements are considered.
Step 9: Set Quantity
priceDiscParameters.parmQty(1);
Trade agreements often contain quantity breaks.
Example:
| Qty | Discount |
|---|---|
| 1+ | 5% |
| 10+ | 10% |
| 100+ | 20% |
By supplying:
parmQty(1)
you are asking:
What discount applies when purchasing one unit?
If quantity-based discounts are important, pass the actual quantity.
Step 10: Set Customer Account
priceDiscParameters.parmAccountNum(
_custAccount);
The pricing engine evaluates customer-specific agreements.
Possible trade agreement relations include:
- Specific Customer
- Customer Group
- All Customers
This parameter enables the framework to resolve the appropriate discount hierarchy.
Step 11: Set Currency
priceDiscParameters.parmCurrencyCode(
Ledger::reportingCurrency(
CompanyInfo::current()));
Currency can affect price and discount searches.
The framework expects a currency code during evaluation.
Step 12: Create the PriceDisc Engine
priceDisc =
PriceDisc::newFromPriceDiscParameters(
priceDiscParameters);
At this point all search criteria have been prepared.
The PriceDisc class now becomes the engine responsible for finding applicable prices and discounts. Microsoft describes PriceDisc as the core search engine used for price and discount determination.
Step 13: Search for Line Discount
discExist = priceDisc.findLineDisc('', '');
This is the most important statement in the method.
The framework searches trade agreements for a matching line discount.
Internally it evaluates:
- Customer
- Customer group
- Item
- Item group
- Date validity
- Quantity
- Unit
- Currency
and returns:
true
if a matching discount is found.
Step 14: Retrieve Discount Percentage
if (discExist)
{
lineDiscPct = priceDisc.lineDiscPct();
}
After a successful search:
priceDisc.lineDiscPct()
returns the actual discount percentage.
Example:
Trade Agreement = 15%
Result:
15.00
Step 15: Handle No Match
else
{
lineDiscPct = 0;
}
If no agreement exists:
0
is returned.
This prevents null or unexpected values.
No comments:
Post a Comment