diff --git a/src/Apps/W1/Sustainability/app/src/Posting/SustainabilityPostMgt.Codeunit.al b/src/Apps/W1/Sustainability/app/src/Posting/SustainabilityPostMgt.Codeunit.al index 88e07e9c31..d69d2b0caa 100644 --- a/src/Apps/W1/Sustainability/app/src/Posting/SustainabilityPostMgt.Codeunit.al +++ b/src/Apps/W1/Sustainability/app/src/Posting/SustainabilityPostMgt.Codeunit.al @@ -281,12 +281,6 @@ codeunit 6212 "Sustainability Post Mgt" if TotalCO2e < 0 then IsNegativeEntry := true; - if ItemLedgerEntry."Entry Type" = ItemLedgerEntry."Entry Type"::Transfer then begin - TotalCO2e := Abs(CO2ePerUnit * ItemLedgerEntry.Quantity); - CorrectSign(TotalCO2e, IsNegativeEntry); - exit; - end; - ShowAppliedEntries.FindAppliedEntries(ItemLedgerEntry, TempItemLedgerEntry); if TempItemLedgerEntry.IsEmpty() then GetILEForAssemblyOutputs(ItemLedgerEntry, TempItemLedgerEntry); @@ -296,6 +290,15 @@ codeunit 6212 "Sustainability Post Mgt" GetCO2eAmountAndQuantity(TempItemLedgerEntry."Entry No.", AppliedAmount, AppliedQuantity); until TempItemLedgerEntry.Next() = 0; + if ItemLedgerEntry."Entry Type" = ItemLedgerEntry."Entry Type"::Transfer then begin + if AppliedQuantity <> 0 then + TotalCO2e := Abs((AppliedAmount / AppliedQuantity) * ItemLedgerEntry.Quantity) + else + TotalCO2e := Abs(CO2ePerUnit * ItemLedgerEntry.Quantity); + CorrectSign(TotalCO2e, IsNegativeEntry); + exit; + end; + if AppliedAmount = 0 then exit; diff --git a/src/Apps/W1/Sustainability/test/src/SustValueEntryTest.Codeunit.al b/src/Apps/W1/Sustainability/test/src/SustValueEntryTest.Codeunit.al index 43b2d1dc8e..2d0aadc5fe 100644 --- a/src/Apps/W1/Sustainability/test/src/SustValueEntryTest.Codeunit.al +++ b/src/Apps/W1/Sustainability/test/src/SustValueEntryTest.Codeunit.al @@ -4178,6 +4178,204 @@ codeunit 148190 "Sust. Value Entry Test" Navigate.Run(); end; + [Test] + procedure VerifyItemReclassUsesSpecificLotCO2eForSpecificCarbonTracking() + var + Item: Record Item; + FromLocation: Record Location; + ToLocation: Record Location; + ItemJournalBatch: Record "Item Journal Batch"; + ItemJournalLine: Record "Item Journal Line"; + ReservationEntry: Record "Reservation Entry"; + SustainabilityValueEntry: Record "Sustainability Value Entry"; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + AccountCode: Code[20]; + LotNo: array[2] of Code[50]; + LotTotalCO2e: array[2] of Decimal; + Quantity: Decimal; + begin + // [SCENARIO 641309] Item Reclassification (location move) of a Specific carbon tracking item must transfer the + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Update "Enable Value Chain Tracking" in Sustainability Setup. + LibrarySustainability.UpdateValueChainTrackingInSustainabilitySetup(true); + + // [GIVEN] Create source and destination Locations. + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(FromLocation); + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ToLocation); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + + // [GIVEN] Create a lot-tracked Item with Specific Carbon Tracking Method and a Default Sust. Account. + LibraryItemTracking.CreateLotItem(Item); + LibrarySustainability.UpdateCarbonTrackingMethod(Item, Item."Carbon Tracking Method"::Specific); + Item.Validate("Default Sust. Account", AccountCode); + Item.Modify(); + + // [GIVEN] Post inventory for two lots with clearly different tracked CO2e per unit (so specific <> average). + Quantity := LibraryRandom.RandIntInRange(10, 10); + PostTwoLotsWithSpecificCO2e(Item, FromLocation.Code, AccountCode, Quantity, LotNo, LotTotalCO2e); + + // [GIVEN] Create an Item Reclassification (Transfer) line moving the full quantity of the second lot to another location. + CreateItemReclassLine(ItemJournalBatch, ItemJournalLine, Item."No.", FromLocation.Code, ToLocation.Code, AccountCode, Quantity); + + // [GIVEN] Set an averaged (incorrect) Total CO2e on the line to satisfy the non-zero validation; posting must ignore it. + ItemJournalLine.Validate("Total CO2e", LotTotalCO2e[1]); + ItemJournalLine.Modify(true); + + // [GIVEN] Assign reclassification item tracking selecting the second lot (keeping the same lot on the new location). + LibraryItemTracking.CreateItemReclassJnLineItemTracking(ReservationEntry, ItemJournalLine, '', LotNo[2], Quantity); + ReservationEntry.Validate("New Lot No.", ReservationEntry."Lot No."); + ReservationEntry.Modify(); + + // [WHEN] Post the Item Reclassification Journal. + LibraryInventory.PostItemJournalBatch(ItemJournalBatch); + + // [THEN] The Sustainability Value Entry reflects the specific tracked CO2e of the moved lot, not the averaged value. + SustainabilityValueEntry.SetRange("Item No.", Item."No."); + SustainabilityValueEntry.SetRange("Item Ledger Entry Type", SustainabilityValueEntry."Item Ledger Entry Type"::Transfer); + SustainabilityValueEntry.FindFirst(); + Assert.AreEqual( + LotTotalCO2e[2], + SustainabilityValueEntry."CO2e Amount (Actual)", + StrSubstNo(ValueMustBeEqualErr, SustainabilityValueEntry.FieldCaption("CO2e Amount (Actual)"), LotTotalCO2e[2], SustainabilityValueEntry.TableCaption())); + end; + + [Test] + procedure VerifyItemReclassUsesFirstSelectedLotCO2eForSpecificCarbonTracking() + var + Item: Record Item; + FromLocation: Record Location; + ToLocation: Record Location; + ItemJournalBatch: Record "Item Journal Batch"; + ItemJournalLine: Record "Item Journal Line"; + ReservationEntry: Record "Reservation Entry"; + SustainabilityValueEntry: Record "Sustainability Value Entry"; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + AccountCode: Code[20]; + LotNo: array[2] of Code[50]; + LotTotalCO2e: array[2] of Decimal; + Quantity: Decimal; + begin + // [SCENARIO 641309] Item Reclassification must follow the CO2e of whichever specific lot is moved (here the first lot). + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Update "Enable Value Chain Tracking" in Sustainability Setup. + LibrarySustainability.UpdateValueChainTrackingInSustainabilitySetup(true); + + // [GIVEN] Create source and destination Locations. + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(FromLocation); + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ToLocation); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + + // [GIVEN] Create a lot-tracked Item with Specific Carbon Tracking Method and a Default Sust. Account. + LibraryItemTracking.CreateLotItem(Item); + LibrarySustainability.UpdateCarbonTrackingMethod(Item, Item."Carbon Tracking Method"::Specific); + Item.Validate("Default Sust. Account", AccountCode); + Item.Modify(); + + // [GIVEN] Post inventory for two lots with clearly different tracked CO2e per unit. + Quantity := LibraryRandom.RandIntInRange(10, 10); + PostTwoLotsWithSpecificCO2e(Item, FromLocation.Code, AccountCode, Quantity, LotNo, LotTotalCO2e); + + // [GIVEN] Create an Item Reclassification (Transfer) line moving the full quantity of the first lot to another location. + CreateItemReclassLine(ItemJournalBatch, ItemJournalLine, Item."No.", FromLocation.Code, ToLocation.Code, AccountCode, Quantity); + + // [GIVEN] Set an averaged (incorrect) Total CO2e on the line to satisfy the non-zero validation; posting must ignore it. + ItemJournalLine.Validate("Total CO2e", LotTotalCO2e[2]); + ItemJournalLine.Modify(true); + + // [GIVEN] Assign reclassification item tracking selecting the first lot (keeping the same lot on the new location). + LibraryItemTracking.CreateItemReclassJnLineItemTracking(ReservationEntry, ItemJournalLine, '', LotNo[1], Quantity); + ReservationEntry.Validate("New Lot No.", ReservationEntry."Lot No."); + ReservationEntry.Modify(); + + // [WHEN] Post the Item Reclassification Journal. + LibraryInventory.PostItemJournalBatch(ItemJournalBatch); + + // [THEN] The Sustainability Value Entry reflects the specific tracked CO2e of the first lot. + SustainabilityValueEntry.SetRange("Item No.", Item."No."); + SustainabilityValueEntry.SetRange("Item Ledger Entry Type", SustainabilityValueEntry."Item Ledger Entry Type"::Transfer); + SustainabilityValueEntry.FindFirst(); + Assert.AreEqual( + LotTotalCO2e[1], + SustainabilityValueEntry."CO2e Amount (Actual)", + StrSubstNo(ValueMustBeEqualErr, SustainabilityValueEntry.FieldCaption("CO2e Amount (Actual)"), LotTotalCO2e[1], SustainabilityValueEntry.TableCaption())); + end; + + [Test] + procedure VerifyItemReclassSumsSpecificLotCO2eWhenMovingMultipleLots() + var + Item: Record Item; + FromLocation: Record Location; + ToLocation: Record Location; + ItemJournalBatch: Record "Item Journal Batch"; + ItemJournalLine: Record "Item Journal Line"; + ReservationEntry: Record "Reservation Entry"; + SustainabilityValueEntry: Record "Sustainability Value Entry"; + CategoryCode: Code[20]; + SubcategoryCode: Code[20]; + AccountCode: Code[20]; + LotNo: array[2] of Code[50]; + LotTotalCO2e: array[2] of Decimal; + Quantity: Decimal; + Index: Integer; + begin + // [SCENARIO 641309] Moving two specific lots in one Item Reclassification must transfer the sum of the specific lot CO2e + LibrarySustainability.CleanUpBeforeTesting(); + + // [GIVEN] Update "Enable Value Chain Tracking" in Sustainability Setup. + LibrarySustainability.UpdateValueChainTrackingInSustainabilitySetup(true); + + // [GIVEN] Create source and destination Locations. + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(FromLocation); + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ToLocation); + + // [GIVEN] Create a Sustainability Account. + CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10)); + + // [GIVEN] Create a lot-tracked Item with Specific Carbon Tracking Method and a Default Sust. Account. + LibraryItemTracking.CreateLotItem(Item); + LibrarySustainability.UpdateCarbonTrackingMethod(Item, Item."Carbon Tracking Method"::Specific); + Item.Validate("Default Sust. Account", AccountCode); + Item.Modify(); + + // [GIVEN] Post inventory for two lots with clearly different tracked CO2e per unit. + Quantity := LibraryRandom.RandIntInRange(10, 10); + PostTwoLotsWithSpecificCO2e(Item, FromLocation.Code, AccountCode, Quantity, LotNo, LotTotalCO2e); + + // [GIVEN] Create an Item Reclassification (Transfer) line moving both lots (full quantity) to another location. + CreateItemReclassLine(ItemJournalBatch, ItemJournalLine, Item."No.", FromLocation.Code, ToLocation.Code, AccountCode, Quantity * ArrayLen(LotNo)); + + // [GIVEN] Set a non-zero Total CO2e on the line to satisfy the non-zero validation; posting must ignore it. + ItemJournalLine.Validate("Total CO2e", LotTotalCO2e[1]); + ItemJournalLine.Modify(true); + + // [GIVEN] Assign reclassification item tracking selecting both lots (keeping the same lot on the new location). + for Index := 1 to ArrayLen(LotNo) do begin + LibraryItemTracking.CreateItemReclassJnLineItemTracking(ReservationEntry, ItemJournalLine, '', LotNo[Index], Quantity); + ReservationEntry.Validate("New Lot No.", ReservationEntry."Lot No."); + ReservationEntry.Modify(); + end; + + // [WHEN] Post the Item Reclassification Journal. + LibraryInventory.PostItemJournalBatch(ItemJournalBatch); + + // [THEN] The total transferred CO2e equals the sum of the specific lot CO2e values. + SustainabilityValueEntry.SetRange("Item No.", Item."No."); + SustainabilityValueEntry.SetRange("Item Ledger Entry Type", SustainabilityValueEntry."Item Ledger Entry Type"::Transfer); + SustainabilityValueEntry.CalcSums("CO2e Amount (Actual)"); + Assert.AreEqual( + LotTotalCO2e[1] + LotTotalCO2e[2], + SustainabilityValueEntry."CO2e Amount (Actual)", + StrSubstNo(ValueMustBeEqualErr, SustainabilityValueEntry.FieldCaption("CO2e Amount (Actual)"), LotTotalCO2e[1] + LotTotalCO2e[2], SustainabilityValueEntry.TableCaption())); + end; + local procedure CreateSustainabilityAccount(var AccountCode: Code[20]; var CategoryCode: Code[20]; var SubcategoryCode: Code[20]; i: Integer): Record "Sustainability Account" begin CreateSustainabilitySubcategory(CategoryCode, SubcategoryCode, i); @@ -4344,6 +4542,32 @@ codeunit 148190 "Sust. Value Entry Test" exit(LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true)); end; + local procedure PostTwoLotsWithSpecificCO2e(Item: Record Item; LocationCode: Code[10]; AccountCode: Code[20]; Quantity: Decimal; var LotNo: array[2] of Code[50]; var LotTotalCO2e: array[2] of Decimal) + var + Index: Integer; + begin + for Index := 1 to ArrayLen(LotNo) do + LotNo[Index] := LibraryUtility.GenerateGUID(); + + LotTotalCO2e[1] := LibraryRandom.RandDecInRange(100, 200, 2); + LotTotalCO2e[2] := LibraryRandom.RandDecInRange(400, 600, 2); + + for Index := 1 to ArrayLen(LotNo) do + LibrarySustainability.PostPositiveAdjustmentWithItemTracking( + Item, LocationCode, AccountCode, '', Quantity, WorkDate(), '', LotNo[Index], LotTotalCO2e[Index]); + end; + + local procedure CreateItemReclassLine(var ItemJournalBatch: Record "Item Journal Batch"; var ItemJournalLine: Record "Item Journal Line"; ItemNo: Code[20]; FromLocationCode: Code[10]; ToLocationCode: Code[10]; AccountCode: Code[20]; Quantity: Decimal) + begin + SelectItemJournalTransferBatch(ItemJournalBatch); + LibraryInventory.CreateItemJournalLine( + ItemJournalLine, ItemJournalBatch."Journal Template Name", ItemJournalBatch.Name, + ItemJournalLine."Entry Type"::Transfer, ItemNo, Quantity); + ItemJournalLine.Validate("Location Code", FromLocationCode); + ItemJournalLine.Validate("New Location Code", ToLocationCode); + ItemJournalLine.Validate("Sust. Account No.", AccountCode); + end; + local procedure PostInventoryForItem(ItemNo: Code[20]) var ItemJournalBatch: Record "Item Journal Batch";