Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -831,7 +831,7 @@ codeunit 5804 ItemCostManagement
end;

[IntegrationEvent(false, false)]
local procedure OnCalculatePreciseCostAmountsOnAfterFilterOpenInboundItemLedgerEntry(OpenInbndItemLedgerEntry: Record "Item Ledger Entry"; var Item: Record Item)
local procedure OnCalculatePreciseCostAmountsOnAfterFilterOpenInboundItemLedgerEntry(var OpenInbndItemLedgerEntry: Record "Item Ledger Entry"; var Item: Record Item)
begin
end;

Expand Down
60 changes: 60 additions & 0 deletions src/Layers/CZ/Tests/SCM/SCMAvgCostCalc.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ codeunit 137070 "SCM Avg. Cost Calc."
{
Subtype = Test;
TestPermissions = Disabled;
EventSubscriberInstance = Manual;

trigger OnRun()
begin
Expand All @@ -22,6 +23,7 @@ codeunit 137070 "SCM Avg. Cost Calc."
LibraryUtility: Codeunit "Library - Utility";
LibraryRandom: Codeunit "Library - Random";
IsInitialized: Boolean;
OpenInboundEntryNoFilter: Integer;
IncorrectAverageCostErr: Label 'Average Cost is incorrect.';
IncorrectAverageCostACYErr: Label 'Average Cost (ACY) is incorrect.';

Expand Down Expand Up @@ -662,6 +664,52 @@ codeunit 137070 "SCM Avg. Cost Calc."
Assert.AreNearlyEqual(24.01 / 8.003, AverageCost, LibraryERM.GetUnitAmountRoundingPrecision(), IncorrectAverageCostErr);
end;

[Test]
[Scope('OnPrem')]
procedure PreciseAvgCostRespectsSubscriberFilterOnOpenInboundEntries()
var
GeneralLedgerSetup: Record "General Ledger Setup";
Item: Record Item;
FilteredItemLedgerEntry: Record "Item Ledger Entry";
PosItemLedgerEntry: Record "Item Ledger Entry";
NegItemLedgerEntry: Record "Item Ledger Entry";
ItemCostManagement: Codeunit ItemCostManagement;
SCMAvgCostCalc: Codeunit "SCM Avg. Cost Calc.";
AverageCost: Decimal;
AverageCostACY: Decimal;
begin
// [FEATURE] [Unit Cost] [UT]
// [SCENARIO 7611] A subscriber to OnCalculatePreciseCostAmountsOnAfterFilterOpenInboundItemLedgerEntry can add filters on the open inbound item ledger entries included in the precise cost calculation.
Initialize();
GeneralLedgerSetup.Get();

LibraryInventory.CreateItem(Item);

// [GIVEN] Open positive item entry "E1". Quantity = 2; Remaining Quantity = 1; Cost Amount = 6.00.
// [GIVEN] Open positive item entry "E2". Quantity = 2; Remaining Quantity = 1; Cost Amount = 10.00 + rounding precision of the local currency.
// [GIVEN] Closed negative item entry "E3". Quantity = -2; Cost Amount = -16.00. "E3" is applied to "E1" and "E2".
// [GIVEN] Sum of Cost Amount for the item is thereby equal to the rounding precision, which triggers the precise cost calculation.
MockItemLedgerEntry(FilteredItemLedgerEntry, Item."No.", 2, 1, 6.0, 0);
MockItemLedgerEntry(PosItemLedgerEntry, Item."No.", 2, 1, 10.0 + GeneralLedgerSetup."Amount Rounding Precision", 0);
MockItemLedgerEntry(NegItemLedgerEntry, Item."No.", -2, 0, -16.0, 0);
MockItemApplicationEntry(NegItemLedgerEntry."Entry No.", FilteredItemLedgerEntry."Entry No.", NegItemLedgerEntry."Entry No.", -1);
MockItemApplicationEntry(NegItemLedgerEntry."Entry No.", PosItemLedgerEntry."Entry No.", NegItemLedgerEntry."Entry No.", -1);

// [GIVEN] A bound subscriber that filters the open inbound entries down to "E1".
SCMAvgCostCalc.SetOpenInboundEntryNoFilter(FilteredItemLedgerEntry."Entry No.");
BindSubscription(SCMAvgCostCalc);

// [WHEN] Calculate unit cost of the item.
ItemCostManagement.SetProperties(true, 0);
ItemCostManagement.CalculateAverageCost(Item, AverageCost, AverageCostACY);
UnbindSubscription(SCMAvgCostCalc);

// [THEN] The precise remaining cost is calculated from "E1" only.
// [THEN] Average Cost = 6.00 / 2 * 1 / 2 = 1.5, that is the unit cost of "E1" multiplied by its remaining quantity and divided by the remaining quantity on inventory.
Assert.AreNearlyEqual(
6.0 / 2 * 1 / 2, AverageCost, LibraryERM.GetUnitAmountRoundingPrecision(), IncorrectAverageCostErr);
end;

local procedure Initialize()
var
LibraryERMCountryData: Codeunit "Library - ERM Country Data";
Expand Down Expand Up @@ -1068,5 +1116,17 @@ codeunit 137070 "SCM Avg. Cost Calc."
begin
EnterQuantitytoCreate.OK().Invoke();
end;

procedure SetOpenInboundEntryNoFilter(EntryNo: Integer)
begin
OpenInboundEntryNoFilter := EntryNo;
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::ItemCostManagement, 'OnCalculatePreciseCostAmountsOnAfterFilterOpenInboundItemLedgerEntry', '', false, false)]
local procedure FilterOpenInboundEntriesOnCalculatePreciseCostAmounts(var OpenInbndItemLedgerEntry: Record "Item Ledger Entry")
begin
if OpenInboundEntryNoFilter <> 0 then
OpenInbndItemLedgerEntry.SetRange("Entry No.", OpenInboundEntryNoFilter);
end;
}

Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ codeunit 5804 ItemCostManagement
end;

[IntegrationEvent(false, false)]
local procedure OnCalculatePreciseCostAmountsOnAfterFilterOpenInboundItemLedgerEntry(OpenInbndItemLedgerEntry: Record "Item Ledger Entry"; var Item: Record Item)
local procedure OnCalculatePreciseCostAmountsOnAfterFilterOpenInboundItemLedgerEntry(var OpenInbndItemLedgerEntry: Record "Item Ledger Entry"; var Item: Record Item)
begin
end;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,7 @@ codeunit 5804 ItemCostManagement
end;

[IntegrationEvent(false, false)]
local procedure OnCalculatePreciseCostAmountsOnAfterFilterOpenInboundItemLedgerEntry(OpenInbndItemLedgerEntry: Record "Item Ledger Entry"; var Item: Record Item)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Agent} \quad \color{gray}{\texttt{\small Iteration\ 1}}$

OnCalculatePreciseCostAmountsOnAfterFilterOpenInboundItemLedgerEntry is a public [IntegrationEvent] on codeunit 5804 ItemCostManagement, and its 'OpenInbndItemLedgerEntry: Record "Item Ledger Entry"' parameter is changed from by-value to 'var' in place (identically in the W1, APAC, and RU layers).

AL requires an event subscriber's parameter modifiers to match the publisher's exactly, so any already-shipped extension subscribing to this event with the old (non-var) signature will fail to bind/compile once it recompiles against the updated base app symbols. Modifying a published event's signature in place is a breaking change for AppSource/partner extensions. Note: were this backed by a curated rule, the real-world impact would be major/blocker (extension breakage), but per agent-finding policy severity is capped at minor here.

Recommendation:

  • introduce a new event (e.g. ...WithFilterSupport, with the var parameter) and leave the existing event unchanged, or mark the existing event ObsoleteState=Pending with an ObsoleteReason/ObsoleteTag pointing subscribers at the replacement, rather than changing the parameter modifier of the existing published event.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why

local procedure OnCalculatePreciseCostAmountsOnAfterFilterOpenInboundItemLedgerEntry(var OpenInbndItemLedgerEntry: Record "Item Ledger Entry"; var Item: Record Item)
begin
end;

Expand Down
60 changes: 60 additions & 0 deletions src/Layers/W1/Tests/SCM/SCMAvgCostCalc.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ codeunit 137070 "SCM Avg. Cost Calc."
{
Subtype = Test;
TestPermissions = Disabled;
EventSubscriberInstance = Manual;

trigger OnRun()
begin
Expand All @@ -22,6 +23,7 @@ codeunit 137070 "SCM Avg. Cost Calc."
LibraryUtility: Codeunit "Library - Utility";
LibraryRandom: Codeunit "Library - Random";
IsInitialized: Boolean;
OpenInboundEntryNoFilter: Integer;
IncorrectAverageCostErr: Label 'Average Cost is incorrect.';
IncorrectAverageCostACYErr: Label 'Average Cost (ACY) is incorrect.';

Expand Down Expand Up @@ -662,6 +664,52 @@ codeunit 137070 "SCM Avg. Cost Calc."
Assert.AreNearlyEqual(24.01 / 8.003, AverageCost, LibraryERM.GetUnitAmountRoundingPrecision(), IncorrectAverageCostErr);
end;

[Test]
[Scope('OnPrem')]
procedure PreciseAvgCostRespectsSubscriberFilterOnOpenInboundEntries()
var
GeneralLedgerSetup: Record "General Ledger Setup";
Item: Record Item;
FilteredItemLedgerEntry: Record "Item Ledger Entry";
PosItemLedgerEntry: Record "Item Ledger Entry";
NegItemLedgerEntry: Record "Item Ledger Entry";
ItemCostManagement: Codeunit ItemCostManagement;
SCMAvgCostCalc: Codeunit "SCM Avg. Cost Calc.";
AverageCost: Decimal;
AverageCostACY: Decimal;
begin
// [FEATURE] [Unit Cost] [UT]
// [SCENARIO 7611] A subscriber to OnCalculatePreciseCostAmountsOnAfterFilterOpenInboundItemLedgerEntry can add filters on the open inbound item ledger entries included in the precise cost calculation.
Initialize();
GeneralLedgerSetup.Get();

LibraryInventory.CreateItem(Item);

// [GIVEN] Open positive item entry "E1". Quantity = 2; Remaining Quantity = 1; Cost Amount = 6.00.
// [GIVEN] Open positive item entry "E2". Quantity = 2; Remaining Quantity = 1; Cost Amount = 10.00 + rounding precision of the local currency.
// [GIVEN] Closed negative item entry "E3". Quantity = -2; Cost Amount = -16.00. "E3" is applied to "E1" and "E2".
// [GIVEN] Sum of Cost Amount for the item is thereby equal to the rounding precision, which triggers the precise cost calculation.
MockItemLedgerEntry(FilteredItemLedgerEntry, Item."No.", 2, 1, 6.0, 0);
MockItemLedgerEntry(PosItemLedgerEntry, Item."No.", 2, 1, 10.0 + GeneralLedgerSetup."Amount Rounding Precision", 0);
MockItemLedgerEntry(NegItemLedgerEntry, Item."No.", -2, 0, -16.0, 0);
MockItemApplicationEntry(NegItemLedgerEntry."Entry No.", FilteredItemLedgerEntry."Entry No.", NegItemLedgerEntry."Entry No.", -1);
MockItemApplicationEntry(NegItemLedgerEntry."Entry No.", PosItemLedgerEntry."Entry No.", NegItemLedgerEntry."Entry No.", -1);

// [GIVEN] A bound subscriber that filters the open inbound entries down to "E1".
SCMAvgCostCalc.SetOpenInboundEntryNoFilter(FilteredItemLedgerEntry."Entry No.");
BindSubscription(SCMAvgCostCalc);

// [WHEN] Calculate unit cost of the item.
ItemCostManagement.SetProperties(true, 0);
ItemCostManagement.CalculateAverageCost(Item, AverageCost, AverageCostACY);
UnbindSubscription(SCMAvgCostCalc);

// [THEN] The precise remaining cost is calculated from "E1" only.
// [THEN] Average Cost = 6.00 / 2 * 1 / 2 = 1.5, that is the unit cost of "E1" multiplied by its remaining quantity and divided by the remaining quantity on inventory.
Assert.AreNearlyEqual(
6.0 / 2 * 1 / 2, AverageCost, LibraryERM.GetUnitAmountRoundingPrecision(), IncorrectAverageCostErr);
end;

local procedure Initialize()
var
LibraryERMCountryData: Codeunit "Library - ERM Country Data";
Expand Down Expand Up @@ -1067,5 +1115,17 @@ codeunit 137070 "SCM Avg. Cost Calc."
begin
EnterQuantitytoCreate.OK().Invoke();
end;

procedure SetOpenInboundEntryNoFilter(EntryNo: Integer)
begin
OpenInboundEntryNoFilter := EntryNo;
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::ItemCostManagement, 'OnCalculatePreciseCostAmountsOnAfterFilterOpenInboundItemLedgerEntry', '', false, false)]
local procedure FilterOpenInboundEntriesOnCalculatePreciseCostAmounts(var OpenInbndItemLedgerEntry: Record "Item Ledger Entry")
begin
if OpenInboundEntryNoFilter <> 0 then
OpenInbndItemLedgerEntry.SetRange("Entry No.", OpenInboundEntryNoFilter);
end;
}

Loading