Extending Item Charge Distribution Methods
To ensure correct valuation, your inventory items must carry any added costs, such as freight, physical handling, insurance, and transportation that you incur when purchasing or selling the items.
Users can add these costs by adding a Charge (Item) line to the involved purchase or sales document. For more information, see Use Item Charges to Account for Additional Trade Costs in application help.
Item charges are distributed over other item lines in the document according to a distribution method. Dynamics 365 Business Central offers four distribution methods out of the box: Equally, By Amount, By Weight, and By Volume. This article explains how to remove or add item charge distribution methods. The article describes the method for purchases. The steps are similar for sales, except the events are located in codeunit 5807, Item Charge Assgnt. (Sales).
To enable extension of item charges distribution methods, two events can be found in codeunit 5805, Item Charge Assgnt. (Purch.). The work consists of the following two tasks:
In the OnBeforeShowSuggestItemChargeAssignStrMenu event, you manipulate the options that are presented to users. You can remove, add, and change the order of the options.
- Keep in mind that other extensions may also manipulate the options.
- You should not assume that an option will exist, nor should you write code that may remove an option added by another extension.
In the OnAssignItemCharges event, you distribute the item charge amount over the item lines according to your new distribution method.
- You must verify that the option selected by the user is your new option. If it is not, then exit without taking action.
- When you have distributed the amount over the lines, you must set the ItemChargesAssigned boolean to true. If you do not set this boolean to true, an error will occur.
The following procedures show how to extend the item charges distribution methods:
- Add a new option to the item charges distribution methods in the OnBeforeShowSuggestItemChargeAssignStrMenu event.
- Add a new distribution method for item charges.
- Call the new distribution method in the OnAssignItemCharges event.
The procedures are based on an example where the By Fairy Dust option is added to the string menu (STRMENU) and added to the CASE statement.
Note
To complete this example you will have to add a new field Fairy Dust to the Purchase Line table and other relevant tables and pages.
To add a new option to the item charges distribution methods
Create a new codeunit and add an event subscriber to the OnBeforeShowSuggestItemChargeAssignStrMenu event.
codeunit 50100 "Item Ch. Assign by Fairy Dust"
{
var
ByFairyDustTok: Label 'By Fairy Dust';
local procedure AssignByFairyDustMenuText(): Text
begin
exit(ByFairyDustTok)
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Charge Assgnt. (Purch.)", 'OnBeforeShowSuggestItemChargeAssignStrMenu', '', false, false)]
local procedure AddByFairyDustOnBeforeShowSuggestItemChargeAssignStrMenu(PurchLine: Record "Purchase Line"; var SuggestItemChargeMenuTxt: Text; var SuggestItemChargeMessageTxt: Text; var Selection: Integer)
begin
// if 'By Fairy Dust' is not in the menu options, add it at the end
if StrPos(SuggestItemChargeMenuTxt, AssignByFairyDustMenuText) = 0 then begin
SuggestItemChargeMenuTxt += ',' + AssignByFairyDustMenuText;
// make the last option ('By Fairy Dust') the default selection
Selection := StrLen(DelChr(SuggestItemChargeMenuTxt, '=', DelChr(SuggestItemChargeMenuTxt, '=', ','))) + 1;
end;
end;
}
To add a new distribution method for item charges
In the new codeunit, add functions to distribute the charges over the item lines.
local procedure AssignByFairyDust(var ItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"; Currency: Record Currency; TotalQtyToAssign: Decimal; TotalAmtToAssign: Decimal)
var
TempItemChargeAssgntPurch: Record "Item Charge Assignment (Purch)" temporary;
LineArray: array[2] OF Decimal;
TotalFairyDust: Decimal;
QtyRemaining: Decimal;
AmountRemaining: Decimal;
begin
// copy lines to temp variable and calculate total Fairy Dust
repeat
if not ItemChargeAssignmentPurch.PurchLineInvoiced then begin
TempItemChargeAssgntPurch.Init();
TempItemChargeAssgntPurch := ItemChargeAssignmentPurch;
TempItemChargeAssgntPurch.Insert(false);
GetItemValues(TempItemChargeAssgntPurch, LineArray);
TotalFairyDust += (LineArray[2] * LineArray[1]);
end;
until ItemChargeAssignmentPurch.Next = 0;
if TempItemChargeAssgntPurch.Findset(true) then
repeat
// Calculate Fairy Dust to assign to the line
GetItemValues(TempItemChargeAssgntPurch, LineArray);
if TotalFairyDust <> 0 then
TempItemChargeAssgntPurch."Qty. to Assign" :=
(TotalQtyToAssign * LineArray[2] * LineArray[1]) / TotalFairyDust + QtyRemaining
else
TempItemChargeAssgntPurch."Qty. to Assign" := 0;
// Assign Fairy Dust to the line and calculate the remaining Fairy Dust to assign
ItemChargeAssignmentPurch.Get(
TempItemChargeAssgntPurch."Document Type",
TempItemChargeAssgntPurch."Document No.",
TempItemChargeAssgntPurch."Document Line No.",
TempItemChargeAssgntPurch."Line No.");
ItemChargeAssignmentPurch."Qty. to Assign" := Round(TempItemChargeAssgntPurch."Qty. to Assign", 0.00001);
ItemChargeAssignmentPurch."Amount to Assign" :=
ItemChargeAssignmentPurch."Qty. to Assign" * ItemChargeAssignmentPurch."Unit Cost" + AmountRemaining;
AmountRemaining := ItemChargeAssignmentPurch."Amount to Assign" -
Round(ItemChargeAssignmentPurch."Amount to Assign", Currency."Amount Rounding Precision");
QtyRemaining := TempItemChargeAssgntPurch."Qty. to Assign" - ItemChargeAssignmentPurch."Qty. to Assign";
ItemChargeAssignmentPurch."Amount to Assign" :=
Round(ItemChargeAssignmentPurch."Amount to Assign", Currency."Amount Rounding Precision");
ItemChargeAssignmentPurch.Modify(false);
until TempItemChargeAssgntPurch.Next = 0;
TempItemChargeAssgntPurch.DeleteAll(false);
end;
procedure GetItemValues(TempItemChargeAssgntPurch: Record "Item Charge Assignment (Purch)" temporary; var DecimalArray: Array[2] of Decimal)
var
PurchaseLine: Record "Purchase Line";
PurchRcptLine: Record "Purch. Rcpt. Line";
ReturnShptLine: Record "Return Shipment Line";
TransferRcptLine: Record "Transfer Receipt Line";
SalesShptLine: Record "Sales Shipment Line";
ReturnRcptLine: Record "Return Receipt Line";
begin
// Get the Fairy Dust for the line
Clear(DecimalArray);
with TempItemChargeAssgntPurch do
case "Applies-to Doc. Type" of
"Applies-to Doc. Type"::Order,
"Applies-to Doc. Type"::Invoice,
"Applies-to Doc. Type"::"Return Order",
"Applies-to Doc. Type"::"Credit Memo":
begin
PurchaseLine.Get("Applies-to Doc. Type", "Applies-to Doc. No.", "Applies-to Doc. Line No.");
DecimalArray[1] := PurchaseLine.Quantity;
DecimalArray[2] := PurchaseLine."Fairy Dust";
end;
"Applies-to Doc. Type"::Receipt:
begin
PurchRcptLine.Get("Applies-to Doc. No.", "Applies-to Doc. Line No.");
DecimalArray[1] := PurchRcptLine.Quantity;
DecimalArray[2] := PurchRcptLine."Fairy Dust";
end;
"Applies-to Doc. Type"::"Return Receipt":
begin
ReturnRcptLine.Get("Applies-to Doc. No.", "Applies-to Doc. Line No.");
DecimalArray[1] := ReturnRcptLine.Quantity;
DecimalArray[2] := ReturnRcptLine."Fairy Dust";
end;
"Applies-to Doc. Type"::"Return Shipment":
begin
ReturnShptLine.Get("Applies-to Doc. No.", "Applies-to Doc. Line No.");
DecimalArray[1] := ReturnShptLine.Quantity;
DecimalArray[2] := ReturnShptLine."Fairy Dust";
end;
"Applies-to Doc. Type"::"Transfer Receipt":
begin
TransferRcptLine.Get("Applies-to Doc. No.", "Applies-to Doc. Line No.");
DecimalArray[1] := TransferRcptLine.Quantity;
DecimalArray[2] := TransferRcptLine."Fairy Dust";
end;
"Applies-to Doc. Type"::"Sales Shipment":
begin
SalesShptLine.Get("Applies-to Doc. No.", "Applies-to Doc. Line No.");
DecimalArray[1] := SalesShptLine.Quantity;
DecimalArray[2] := SalesShptLine."Fairy Dust";
end;
end;
end;
To call the new distribution method
In the new codeunit, add a subscriber to the OnAssignItemCharges event.
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Charge Assgnt. (Purch.)", 'OnAssignItemCharges', '', false, false)]
local procedure AssignByFairyDustOnAssignItemCharges(SelectionTxt: Text; var ItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"; Currency: Record Currency; PurchaseHeader: Record "Purchase Header"; TotalQtyToAssign: Decimal; TotalAmtToAssign: Decimal; var ItemChargesAssigned: Boolean)
begin
// if item charges are already assigned, exit
if ItemChargesAssigned then
exit;
// if the user did not choose 'By Fairy Dust', exit
if not (SelectionTxt = AssignByFairyDustMenuText) then
exit;
// assign item charges by fairy dust
AssignByFairyDust(ItemChargeAssignmentPurch, Currency, TotalQtyToAssign, TotalAmtToAssign);
// charges have been assigned
ItemChargesAssigned := true;
end;
Related information
Extending Application Areas
Module System Application
Module Base Application