diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al index bd8d9f7504..581ed8ed08 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocPurchaseDraftSubform.Page.al @@ -451,6 +451,7 @@ page 6183 "E-Doc. Purchase Draft Subform" local procedure OpenMatchedPurchaseOrder(SelectedEDocumentPurchaseLine: Record "E-Document Purchase Line") var TempPurchaseOrders: Record "Purchase Header" temporary; + PurchaseOrder: Record "Purchase Header"; CountPOs: Integer; begin EDocPOMatching.LoadPOsMatchedToEDocumentLine(SelectedEDocumentPurchaseLine, TempPurchaseOrders); @@ -459,7 +460,8 @@ page 6183 "E-Doc. Purchase Draft Subform" exit; if CountPOs = 1 then begin TempPurchaseOrders.FindFirst(); - Page.Run(Page::"Purchase Order", TempPurchaseOrders); + PurchaseOrder.Get(TempPurchaseOrders."Document Type", TempPurchaseOrders."No."); + Page.Run(Page::"Purchase Order", PurchaseOrder); exit; end; Page.Run(Page::"Purchase Orders", TempPurchaseOrders); diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocPOMatching.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocPOMatching.Codeunit.al index f754f047c8..250f44236d 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocPOMatching.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocPOMatching.Codeunit.al @@ -39,6 +39,8 @@ codeunit 6196 "E-Doc. PO Matching" exit; PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); PurchaseLine.SetRange("Pay-to Vendor No.", Vendor."No."); + if EDocumentPurchaseLine."[BC] Unit of Measure" <> '' then + PurchaseLine.SetRange("Unit of Measure Code", EDocumentPurchaseLine."[BC] Unit of Measure"); PurchaseLine.SetLoadFields("Document No.", "Line No.", Description, Quantity, "Qty. Invoiced (Base)", "Qty. Received (Base)", Type, "No.", "Quantity Received", "Quantity Invoiced"); if PurchaseLine.FindSet() then repeat @@ -412,17 +414,20 @@ codeunit 6196 "E-Doc. PO Matching" OrderLineAndEDocFromDifferentVendorsErr: Label 'All selected purchase order lines must belong to orders for the same vendor as the e-document line.'; OrderLinesMustBeOfSameTypeAndNoErr: Label 'All selected purchase order lines must be of the same type and number.'; NotYetReceivedErr: Label 'The selected purchase order lines are not yet received with the quantity of the invoice. You must first receive them before matching them.'; + OrderLinesMustHaveSameUoMErr: Label 'All selected purchase order lines must have the same unit of measure.'; MatchedPOLineType: Enum "Purchase Line Type"; - MatchedPOLineVendorNo, MatchedPOLineTypeNo : Code[20]; - POLineTypeCollected: Boolean; + MatchedPOLineVendorNo, MatchedPOLineTypeNo, MatchedUnitOfMeasure : Code[20]; + FirstOfLinesBeingMatched: Boolean; begin if SelectedPOLines.IsEmpty() then exit; if SelectedPOLines.Count() > 1 then Error(MatchesToMultiplePOLinesNotSupportedErr); RemoveAllMatchesForEDocumentLine(EDocumentPurchaseLine); + FirstOfLinesBeingMatched := true; MatchedPOLineVendorNo := ''; MatchedPOLineTypeNo := ''; + MatchedUnitOfMeasure := ''; if SelectedPOLines.FindSet() then repeat // Create new matches, if each line being matched is valid @@ -438,28 +443,26 @@ codeunit 6196 "E-Doc. PO Matching" EDocPurchaseLinePOMatch.SetRange("Purchase Line SystemId", PurchaseLine.SystemId); // The PO Line must not already be matched to another E-Document line if not EDocPurchaseLinePOMatch.IsEmpty() then Error(AlreadyMatchedErr, EDocumentPurchaseLine."E-Document Entry No.", SelectedPOLines."Document Type", SelectedPOLines."Document No."); + if EDocumentPurchaseLine."[BC] Unit of Measure" <> '' then + PurchaseLine.TestField("Unit of Measure Code", EDocumentPurchaseLine."[BC] Unit of Measure"); - // We ensure that all matched lines have the same Vendor, Type and No. - if MatchedPOLineVendorNo = '' then - MatchedPOLineVendorNo := PurchaseLine."Pay-to Vendor No." - else - if PurchaseLine."Pay-to Vendor No." <> MatchedPOLineVendorNo then - Error(OrderLineAndEDocFromDifferentVendorsErr); - - if MatchedPOLineTypeNo = '' then - MatchedPOLineTypeNo := PurchaseLine."No." - else - if PurchaseLine."No." <> MatchedPOLineTypeNo then - Error(OrderLinesMustBeOfSameTypeAndNoErr); - - if not POLineTypeCollected then begin - POLineTypeCollected := true; + // We ensure that all matched lines have the same Vendor, Type, No. and Unit of Measure + if FirstOfLinesBeingMatched then begin MatchedPOLineType := PurchaseLine.Type; - end - else + MatchedPOLineTypeNo := PurchaseLine."No."; + MatchedPOLineVendorNo := PurchaseLine."Pay-to Vendor No."; + MatchedUnitOfMeasure := PurchaseLine."Unit of Measure Code"; + FirstOfLinesBeingMatched := false; + end else begin if PurchaseLine.Type <> MatchedPOLineType then Error(OrderLinesMustBeOfSameTypeAndNoErr); - + if PurchaseLine."No." <> MatchedPOLineTypeNo then + Error(OrderLinesMustBeOfSameTypeAndNoErr); + if PurchaseLine."Pay-to Vendor No." <> MatchedPOLineVendorNo then + Error(OrderLineAndEDocFromDifferentVendorsErr); + if PurchaseLine."Unit of Measure Code" <> MatchedUnitOfMeasure then + Error(OrderLinesMustHaveSameUoMErr); + end; Clear(EDocPurchaseLinePOMatch); EDocPurchaseLinePOMatch."E-Doc. Purchase Line SystemId" := EDocumentPurchaseLine.SystemId; EDocPurchaseLinePOMatch."Purchase Line SystemId" := PurchaseLine.SystemId; @@ -469,6 +472,7 @@ codeunit 6196 "E-Doc. PO Matching" // Set the E-Document Purchase Line properties to match the matched Purchase Line properties EDocumentPurchaseLine."[BC] Purchase Line Type" := MatchedPOLineType; EDocumentPurchaseLine."[BC] Purchase Type No." := MatchedPOLineTypeNo; + EDocumentPurchaseLine."[BC] Unit of Measure" := MatchedUnitOfMeasure; EDocumentPurchaseLine.Modify(); AppendPOMatchWarnings(EDocumentPurchaseLine, TempMatchWarnings); TempMatchWarnings.SetRange("Warning Type", "E-Doc PO Match Warning"::NotYetReceived); diff --git a/src/Apps/W1/EDocument/Test/src/Matching/EDocPOMatchingUnitTests.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Matching/EDocPOMatchingUnitTests.Codeunit.al index f9f1268e79..4133a2fb53 100644 --- a/src/Apps/W1/EDocument/Test/src/Matching/EDocPOMatchingUnitTests.Codeunit.al +++ b/src/Apps/W1/EDocument/Test/src/Matching/EDocPOMatchingUnitTests.Codeunit.al @@ -225,6 +225,161 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" Assert.IsFalse(TempPurchaseLine.IsEmpty(), 'Unmatched purchase line should be included'); end; + [Test] + procedure LoadAvailablePOLinesFiltersByUoMWhenEDocLineHasUoMSpecified() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine1, PurchaseLine2 : Record "Purchase Line"; + TempPurchaseLine: Record "Purchase Line" temporary; + Item: Record Item; + UnitOfMeasure1, UnitOfMeasure2 : Record "Unit of Measure"; + ItemUnitOfMeasure1, ItemUnitOfMeasure2 : Record "Item Unit of Measure"; + begin + // [SCENARIO 619582] Loading available PO lines filters by UoM when E-Document line has UoM specified + Initialize(); + ClearPurchaseDocumentsForVendor(); + + // [GIVEN] Item "I" with two units of measure "UOM1" and "UOM2" + LibraryEDocument.GetGenericItem(Item); + LibraryInventory.CreateUnitOfMeasureCode(UnitOfMeasure1); + LibraryInventory.CreateUnitOfMeasureCode(UnitOfMeasure2); + LibraryInventory.CreateItemUnitOfMeasure(ItemUnitOfMeasure1, Item."No.", UnitOfMeasure1.Code, 1); + LibraryInventory.CreateItemUnitOfMeasure(ItemUnitOfMeasure2, Item."No.", UnitOfMeasure2.Code, 1); + + // [GIVEN] Purchase order with line "PL1" having UoM "UOM1" and line "PL2" having UoM "UOM2" + LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Order, Vendor."No."); + LibraryPurchase.CreatePurchaseLine(PurchaseLine1, PurchaseHeader, PurchaseLine1.Type::Item, Item."No.", 10); + PurchaseLine1.Validate("Unit of Measure Code", UnitOfMeasure1.Code); + PurchaseLine1.Modify(true); + LibraryPurchase.CreatePurchaseLine(PurchaseLine2, PurchaseHeader, PurchaseLine2.Type::Item, Item."No.", 10); + PurchaseLine2.Validate("Unit of Measure Code", UnitOfMeasure2.Code); + PurchaseLine2.Modify(true); + + // [GIVEN] E-Document line with UoM "UOM1" specified + LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); + EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; + EDocumentPurchaseHeader.Modify(); + EDocumentPurchaseLine := LibraryEDocument.InsertPurchaseDraftLine(EDocument); + EDocumentPurchaseLine."[BC] Unit of Measure" := UnitOfMeasure1.Code; + EDocumentPurchaseLine.Modify(); + + // [WHEN] LoadAvailablePOLinesForEDocumentLine is called + EDocPOMatching.LoadAvailablePOLinesForEDocumentLine(EDocumentPurchaseLine, TempPurchaseLine); + + // [THEN] Only PO line "PL1" with matching UoM "UOM1" is returned + Assert.AreEqual(1, TempPurchaseLine.Count(), 'Expected only 1 purchase line with matching UoM'); + TempPurchaseLine.FindFirst(); + Assert.AreEqual(PurchaseLine1.SystemId, TempPurchaseLine.SystemId, 'Expected purchase line with matching UoM to be returned'); + end; + + [Test] + procedure LoadAvailablePOLinesReturnsAllLinesWhenEDocLineHasNoUoMSpecified() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine1, PurchaseLine2 : Record "Purchase Line"; + TempPurchaseLine: Record "Purchase Line" temporary; + Item: Record Item; + UnitOfMeasure1, UnitOfMeasure2 : Record "Unit of Measure"; + ItemUnitOfMeasure1, ItemUnitOfMeasure2 : Record "Item Unit of Measure"; + begin + // [SCENARIO 619582] Loading available PO lines returns all lines when E-Document line has no UoM specified + Initialize(); + ClearPurchaseDocumentsForVendor(); + + // [GIVEN] Item "I" with two units of measure "UOM1" and "UOM2" + LibraryEDocument.GetGenericItem(Item); + LibraryInventory.CreateUnitOfMeasureCode(UnitOfMeasure1); + LibraryInventory.CreateUnitOfMeasureCode(UnitOfMeasure2); + LibraryInventory.CreateItemUnitOfMeasure(ItemUnitOfMeasure1, Item."No.", UnitOfMeasure1.Code, 1); + LibraryInventory.CreateItemUnitOfMeasure(ItemUnitOfMeasure2, Item."No.", UnitOfMeasure2.Code, 1); + + // [GIVEN] Purchase order with line "PL1" having UoM "UOM1" and line "PL2" having UoM "UOM2" + LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Order, Vendor."No."); + LibraryPurchase.CreatePurchaseLine(PurchaseLine1, PurchaseHeader, PurchaseLine1.Type::Item, Item."No.", 10); + PurchaseLine1.Validate("Unit of Measure Code", UnitOfMeasure1.Code); + PurchaseLine1.Modify(true); + LibraryPurchase.CreatePurchaseLine(PurchaseLine2, PurchaseHeader, PurchaseLine2.Type::Item, Item."No.", 10); + PurchaseLine2.Validate("Unit of Measure Code", UnitOfMeasure2.Code); + PurchaseLine2.Modify(true); + + // [GIVEN] E-Document line with no UoM specified + LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); + EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; + EDocumentPurchaseHeader.Modify(); + EDocumentPurchaseLine := LibraryEDocument.InsertPurchaseDraftLine(EDocument); + EDocumentPurchaseLine."[BC] Unit of Measure" := ''; + EDocumentPurchaseLine.Modify(); + + // [WHEN] LoadAvailablePOLinesForEDocumentLine is called + EDocPOMatching.LoadAvailablePOLinesForEDocumentLine(EDocumentPurchaseLine, TempPurchaseLine); + + // [THEN] Both PO lines "PL1" and "PL2" are returned + Assert.AreEqual(2, TempPurchaseLine.Count(), 'Expected 2 purchase lines when E-Document line has no UoM specified'); + TempPurchaseLine.SetRange(SystemId, PurchaseLine1.SystemId); + Assert.IsFalse(TempPurchaseLine.IsEmpty(), 'First purchase line should be included'); + TempPurchaseLine.SetRange(SystemId, PurchaseLine2.SystemId); + Assert.IsFalse(TempPurchaseLine.IsEmpty(), 'Second purchase line should be included'); + end; + + [Test] + procedure LoadAvailablePOLinesReturnsNoLinesWhenNoMatchingUoMExists() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine1, PurchaseLine2 : Record "Purchase Line"; + TempPurchaseLine: Record "Purchase Line" temporary; + Item: Record Item; + UnitOfMeasure1, UnitOfMeasure2, UnitOfMeasure3 : Record "Unit of Measure"; + ItemUnitOfMeasure1, ItemUnitOfMeasure2, ItemUnitOfMeasure3 : Record "Item Unit of Measure"; + begin + // [SCENARIO 619582] Loading available PO lines returns no lines when E-Document line has UoM specified but no PO lines have matching UoM + Initialize(); + ClearPurchaseDocumentsForVendor(); + + // [GIVEN] Item "I" with three units of measure "UOM1", "UOM2", and "UOM3" + LibraryEDocument.GetGenericItem(Item); + LibraryInventory.CreateUnitOfMeasureCode(UnitOfMeasure1); + LibraryInventory.CreateUnitOfMeasureCode(UnitOfMeasure2); + LibraryInventory.CreateUnitOfMeasureCode(UnitOfMeasure3); + LibraryInventory.CreateItemUnitOfMeasure(ItemUnitOfMeasure1, Item."No.", UnitOfMeasure1.Code, 1); + LibraryInventory.CreateItemUnitOfMeasure(ItemUnitOfMeasure2, Item."No.", UnitOfMeasure2.Code, 1); + LibraryInventory.CreateItemUnitOfMeasure(ItemUnitOfMeasure3, Item."No.", UnitOfMeasure3.Code, 1); + + // [GIVEN] Purchase order with line "PL1" having UoM "UOM1" and line "PL2" having UoM "UOM2" + LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Order, Vendor."No."); + LibraryPurchase.CreatePurchaseLine(PurchaseLine1, PurchaseHeader, PurchaseLine1.Type::Item, Item."No.", 10); + PurchaseLine1.Validate("Unit of Measure Code", UnitOfMeasure1.Code); + PurchaseLine1.Modify(true); + LibraryPurchase.CreatePurchaseLine(PurchaseLine2, PurchaseHeader, PurchaseLine2.Type::Item, Item."No.", 10); + PurchaseLine2.Validate("Unit of Measure Code", UnitOfMeasure2.Code); + PurchaseLine2.Modify(true); + + // [GIVEN] E-Document line with UoM "UOM3" specified (not matching any PO line) + LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); + EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; + EDocumentPurchaseHeader.Modify(); + EDocumentPurchaseLine := LibraryEDocument.InsertPurchaseDraftLine(EDocument); + EDocumentPurchaseLine."[BC] Unit of Measure" := UnitOfMeasure3.Code; + EDocumentPurchaseLine.Modify(); + + // [WHEN] LoadAvailablePOLinesForEDocumentLine is called + EDocPOMatching.LoadAvailablePOLinesForEDocumentLine(EDocumentPurchaseLine, TempPurchaseLine); + + // [THEN] No PO lines are returned + Assert.IsTrue(TempPurchaseLine.IsEmpty(), 'Expected no purchase lines when no PO lines have matching UoM'); + end; + [Test] procedure LoadPOLinesMatchedToEDocLineWithNoMatches() var @@ -377,6 +532,10 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Order, Vendor."No."); LibraryPurchase.CreatePurchaseLine(PurchaseLine1, PurchaseHeader, PurchaseLine1.Type::Item, Item."No.", 5); LibraryPurchase.CreatePurchaseLine(PurchaseLine2, PurchaseHeader, PurchaseLine2.Type::Item, Item."No.", 10); + PurchaseLine1."Unit of Measure Code" := ItemUnitOfMeasure.Code; + PurchaseLine1.Modify(); + PurchaseLine2."Unit of Measure Code" := ItemUnitOfMeasure.Code; + PurchaseLine2.Modify(); // Match E-Document lines to purchase order lines MatchEDocumentLineToPOLine(EDocumentPurchaseLine1, PurchaseLine1); @@ -411,7 +570,6 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" PurchaseHeader: Record "Purchase Header"; PurchaseLine: Record "Purchase Line"; Item: Record Item; - ItemUnitOfMeasure: Record "Item Unit of Measure"; TempPOMatchWarnings: Record "E-Doc PO Match Warning" temporary; begin Initialize(); @@ -424,27 +582,18 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Order, Vendor."No."); LibraryPurchase.CreatePurchaseLine(PurchaseLine, PurchaseHeader, PurchaseLine.Type::Item, Item."No.", 10); - // Create item with UOM that has different qty per unit of measure - ItemUnitOfMeasure.Init(); - ItemUnitOfMeasure."Item No." := Item."No."; - ItemUnitOfMeasure.Code := 'BIGBOX'; - ItemUnitOfMeasure."Qty. per Unit of Measure" := 10; - ItemUnitOfMeasure.Insert(); - // Create E-Document Purchase Header EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; EDocumentPurchaseHeader.Modify(); - // Set up E-Document line to create quantity mismatch also with 10 units but different UOM + // Set up E-Document line to create quantity mismatch EDocumentPurchaseLine := LibraryEDocument.InsertPurchaseDraftLine(EDocument); EDocumentPurchaseLine."[BC] Purchase Line Type" := Enum::"Purchase Line Type"::Item; EDocumentPurchaseLine."[BC] Purchase Type No." := Item."No."; - EDocumentPurchaseLine."[BC] Unit of Measure" := 'BIGBOX'; - EDocumentPurchaseLine.Quantity := 10; + EDocumentPurchaseLine.Quantity := 100; EDocumentPurchaseLine.Modify(); - MatchEDocumentLineToPOLine(EDocumentPurchaseLine, PurchaseLine); // [WHEN] CalculatePOMatchWarnings is called