diff --git a/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al index 650193f4dc..875c59838c 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al @@ -507,7 +507,6 @@ codeunit 6103 "E-Document Subscribers" DataClassificationEvalData.SetTableFieldsToNormal(Database::"E-Doc. Purchase Line History"); DataClassificationEvalData.SetTableFieldsToNormal(Database::"E-Document Line - Field"); DataClassificationEvalData.SetTableFieldsToNormal(Database::"ED Purchase Line Field Setup"); - DataClassificationEvalData.SetTableFieldsToNormal(Database::"E-Doc. PO Matching Setup"); #if not CLEAN28 #pragma warning disable AL0432 DataClassificationEvalData.SetTableFieldsToNormal(Database::"EDoc Historical Matching Setup"); @@ -516,7 +515,6 @@ codeunit 6103 "E-Document Subscribers" DataClassificationEvalData.SetTableFieldsToNormal(Database::"E-Doc. Vendor Assign. History"); DataClassificationEvalData.SetTableFieldsToNormal(Database::"E-Doc. Record Link"); DataClassificationEvalData.SetTableFieldsToNormal(Database::"E-Document Notification"); - DataClassificationEvalData.SetTableFieldsToNormal(Database::"E-Doc. PO Matching Setup"); #if not CLEAN26 #pragma warning disable AL0432 DataClassificationEvalData.SetTableFieldsToNormal(Database::"EDoc. Purch. Line Field Setup"); diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al index 37f6fea09e..0bcd71c7a1 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocCreatePurchaseInvoice.Codeunit.al @@ -35,20 +35,12 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, EDocImpSessionTelemetry: Codeunit "E-Doc. Imp. Session Telemetry"; EmptyRecordId: RecordId; IEDocumentFinishPurchaseDraft: Interface IEDocumentCreatePurchaseInvoice; - YourMatchedLinesAreNotValidErr: Label 'The purchase invoice cannot be created because one or more of its matched lines are not valid matches. Review if your configuration allows for receiving at invoice.'; - SomeLinesNotYetReceivedErr: Label 'Some of the matched purchase order lines have not yet been received, you need to either receive the lines or remove the matches.'; MissingInformationForMatchErr: Label 'Some of the draft lines that were matched to purchase order lines are missing unit of measure information. Please specify the unit of measure for those lines and try again.'; begin EDocumentPurchaseHeader.GetFromEDocument(EDocument); - if not EDocPOMatching.VerifyEDocumentMatchedLinesAreValidMatches(EDocumentPurchaseHeader) then - Error(YourMatchedLinesAreNotValidErr); - EDocPOMatching.SuggestReceiptsForMatchedOrderLines(EDocumentPurchaseHeader); EDocPOMatching.CalculatePOMatchWarnings(EDocumentPurchaseHeader, TempPOMatchWarnings); - TempPOMatchWarnings.SetRange("Warning Type", "E-Doc PO Match Warning"::ExceedsInvoiceableQty); - if not TempPOMatchWarnings.IsEmpty() then - Error(SomeLinesNotYetReceivedErr); TempPOMatchWarnings.SetRange("Warning Type", "E-Doc PO Match Warning"::MissingInformationForMatch); if not TempPOMatchWarnings.IsEmpty() then Error(MissingInformationForMatchErr); @@ -88,17 +80,12 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, VendorLedgerEntry: Record "Vendor Ledger Entry"; EDocumentPurchaseHeader: Record "E-Document Purchase Header"; EDocumentPurchaseLine: Record "E-Document Purchase Line"; - PurchaseLine: Record "Purchase Line"; EDocRecordLink: Record "E-Doc. Record Link"; EDocPurchaseDocumentHelper: Codeunit "E-Doc. Purch. Doc. Helper"; PurchCalcDiscByType: Codeunit "Purch - Calc Disc. By Type"; - EDocLineByReceipt: Query "E-Doc. Line by Receipt"; - LastReceiptNo: Code[20]; PurchaseLineNo: Integer; StopCreatingPurchaseInvoice: Boolean; VendorInvoiceNo: Code[35]; - ReceiptNoLbl: Label 'Receipt No. %1:', Comment = '%1 = Receipt No.'; - NullGuid: Guid; begin EDocumentPurchaseHeader.GetFromEDocument(EDocument); if not EDocPurchaseDocumentHelper.AllDraftLinesHaveTypeAndNumber(EDocumentPurchaseHeader) then begin @@ -142,40 +129,12 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, EDocRecordLink.InsertEDocumentHeaderLink(EDocumentPurchaseHeader, PurchaseHeader); PurchaseLineNo := EDocPurchaseDocumentHelper.GetLastPurchaseLineNo("Purchase Document Type"::Invoice, PurchaseHeader."No."); // We get the last line number, even if this is a new document since recurrent lines get inserted on the header's creation - // We create first the lines without any PO matches - EDocLineByReceipt.SetRange(EDocumentEntryNo, EDocument."Entry No"); - EDocLineByReceipt.SetRange(ReceiptNo, ''); - EDocLineByReceipt.SetRange(PurchaseLineSystemId, NullGuid); - EDocLineByReceipt.Open(); - while EDocLineByReceipt.Read() do begin - EDocumentPurchaseLine.GetBySystemId(EDocLineByReceipt.SystemId); - PurchaseLineNo += 10000; - EDocPurchaseDocumentHelper.CreatePurchaseLineFromDraft(PurchaseHeader, EDocumentPurchaseLine, EDocumentPurchaseHeader."Total Discount" > 0, PurchaseLineNo); - end; - EDocLineByReceipt.Close(); - - // Then we create the lines with receipt no., adding comment lines for each receipt no. - LastReceiptNo := ''; - EDocLineByReceipt.SetFilter(ReceiptNo, '<> %1', ''); - EDocLineByReceipt.SetRange(PurchaseLineSystemId); - EDocLineByReceipt.Open(); - while EDocLineByReceipt.Read() do begin - if LastReceiptNo <> EDocLineByReceipt.ReceiptNo then begin // A receipt no. for which we have not created a header comment line yet - Clear(PurchaseLine); - PurchaseLine."Document Type" := PurchaseHeader."Document Type"; - PurchaseLine."Document No." := PurchaseHeader."No."; + EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); + if EDocumentPurchaseLine.FindSet() then + repeat PurchaseLineNo += 10000; - PurchaseLine."Line No." := PurchaseLineNo; - PurchaseLine.Type := PurchaseLine.Type::" "; - PurchaseLine.Description := StrSubstNo(ReceiptNoLbl, EDocLineByReceipt.ReceiptNo); - PurchaseLine.Insert(); - end; - EDocumentPurchaseLine.GetBySystemId(EDocLineByReceipt.SystemId); - PurchaseLineNo += 10000; - EDocPurchaseDocumentHelper.CreatePurchaseLineFromDraft(PurchaseHeader, EDocumentPurchaseLine, EDocumentPurchaseHeader."Total Discount" > 0, PurchaseLineNo); - LastReceiptNo := EDocLineByReceipt.ReceiptNo; - end; - EDocLineByReceipt.Close(); + EDocPurchaseDocumentHelper.CreatePurchaseLineFromDraft(PurchaseHeader, EDocumentPurchaseLine, EDocumentPurchaseHeader."Total Discount" > 0, PurchaseLineNo); + until EDocumentPurchaseLine.Next() = 0; PurchaseHeader.Modify(); PurchCalcDiscByType.ApplyInvDiscBasedOnAmt(EDocumentPurchaseHeader."Total Discount", PurchaseHeader); EDocPurchaseDocumentHelper.ApplyVATDifferenceToLines(PurchaseHeader, EDocumentPurchaseHeader); 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 ee70da225f..8f46735905 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 @@ -51,7 +51,7 @@ page 6183 "E-Doc. Purchase Draft Subform" field(MatchWarnings; MatchWarningsCaption) { ApplicationArea = All; - Caption = 'Order match warnings'; + Caption = 'Warnings'; Editable = false; Visible = HasEDocumentOrderMatchWarnings; StyleExpr = MatchWarningsStyleExpr; @@ -188,9 +188,9 @@ page 6183 "E-Doc. Purchase Draft Subform" action(MatchToOrderLine) { ApplicationArea = All; - Caption = 'Match to order line'; + Caption = 'Match to order lines'; Image = LinkWithExisting; - ToolTip = 'Match this incoming invoice line to a purchase order line.'; + ToolTip = 'Match this incoming invoice line to purchase order lines.'; Scope = Repeater; trigger OnAction() @@ -211,9 +211,9 @@ page 6183 "E-Doc. Purchase Draft Subform" action(SpecifyReceiptLines) { ApplicationArea = All; - Caption = 'Specify receipt line'; + Caption = 'Specify receipt lines'; Image = ReceiptLines; - ToolTip = 'Specify the corresponding receipt line to the matched order line.'; + ToolTip = 'Specify the corresponding receipt lines to the matched order lines.'; Scope = Repeater; Enabled = IsLineMatchedToOrderLine; @@ -235,9 +235,9 @@ page 6183 "E-Doc. Purchase Draft Subform" action(OpenMatchedOrder) { ApplicationArea = All; - Caption = 'Open matched order'; + Caption = 'Open matched orders'; Image = ViewOrder; - ToolTip = 'Opens the matched purchase order.'; + ToolTip = 'Opens the matched purchase orders.'; Scope = Repeater; Enabled = IsLineMatchedToOrderLine; @@ -249,9 +249,9 @@ page 6183 "E-Doc. Purchase Draft Subform" action(OpenMatchedReceipt) { ApplicationArea = All; - Caption = 'Open matched receipt'; + Caption = 'Open matched receipts'; Image = PostedReceipt; - ToolTip = 'Opens the matched purchase receipt.'; + ToolTip = 'Opens the matched purchase receipts.'; Scope = Repeater; Enabled = IsLineMatchedToReceiptLine; diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocLineByReceipt.Query.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocLineByReceipt.Query.al deleted file mode 100644 index d3ed8bc1d6..0000000000 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocLineByReceipt.Query.al +++ /dev/null @@ -1,49 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ -namespace Microsoft.eServices.EDocument.Processing.Import.Purchase; - -using Microsoft.Purchases.History; - -/// -/// Query to get the e-document lines sorted by the receipt number that they have assigned -/// -query 6100 "E-Doc. Line by Receipt" -{ - Access = Internal; - OrderBy = ascending(ReceiptNo); - InherentEntitlements = X; - InherentPermissions = X; - - elements - { - dataitem(EDocumentPurchaseLine; "E-Document Purchase Line") - { - column(SystemId; SystemId) - { - } - column(EDocumentEntryNo; "E-Document Entry No.") - { - } - dataitem(EDocPurchaseLinePOMatch; "E-Doc. Purchase Line PO Match") - { - DataItemLink = "E-Doc. Purchase Line SystemId" = EDocumentPurchaseLine.SystemId; - SqlJoinType = LeftOuterJoin; - column(PurchaseLineSystemId; "Purchase Line SystemId") - { - } - column(ReceiptLineSystemId; "Receipt Line SystemId") - { - } - dataitem(PurchRcptLine; "Purch. Rcpt. Line") - { - DataItemLink = SystemId = EDocPurchaseLinePOMatch."Receipt Line SystemId"; - column(ReceiptNo; "Document No.") - { - } - } - } - } - } -} \ No newline at end of file diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocPOMConfigReceipt.Enum.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocPOMConfigReceipt.Enum.al deleted file mode 100644 index 0fa35f1770..0000000000 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocPOMConfigReceipt.Enum.al +++ /dev/null @@ -1,24 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ -namespace Microsoft.eServices.EDocument.Processing.Import.Purchase; - -enum 6112 "E-Doc. PO M. Config. Receipt" -{ - Access = Internal; - Extensible = false; - - value(0; "Always ask") - { - Caption = 'Always ask'; - } - value(1; "Always receive at posting") - { - Caption = 'Always receive at posting'; - } - value(2; "Never receive at posting") - { - Caption = 'Never receive at posting'; - } -} \ No newline at end of file diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocPOMConfiguration.Enum.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocPOMConfiguration.Enum.al deleted file mode 100644 index de257d3bf6..0000000000 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocPOMConfiguration.Enum.al +++ /dev/null @@ -1,31 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ -namespace Microsoft.eServices.EDocument.Processing.Import.Purchase; - -enum 6113 "E-Doc. PO M. Configuration" -{ - Access = Internal; - Extensible = false; - value(0; "Always ask") - { - Caption = 'Always ask'; - } - value(1; "Always receive at posting") - { - Caption = 'Always receive at posting'; - } - value(2; "Never receive at posting") - { - Caption = 'Never receive at posting'; - } - value(3; "Receive at posting only for certain vendors") - { - Caption = 'Receive at posting only for certain vendors'; - } - value(4; "Receive at posting except for certain vendors") - { - Caption = 'Receive at posting except for certain vendors'; - } -} \ No newline at end of file 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 1aae56fe46..e6330d92eb 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 @@ -6,6 +6,7 @@ namespace Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument; using Microsoft.Inventory.Item; +using Microsoft.Inventory.Tracking; using Microsoft.Purchases.Document; using Microsoft.Purchases.History; using Microsoft.Purchases.Vendor; @@ -431,13 +432,10 @@ codeunit 6196 "E-Doc. PO Matching" PurchaseLine: Record "Purchase Line"; Vendor: Record Vendor; EDocPurchaseLinePOMatch: Record "E-Doc. Purchase Line PO Match"; - TempMatchWarnings: Record "E-Doc PO Match Warning" temporary; - MatchesToMultiplePOLinesNotSupportedErr: Label 'Matching an e-document line to multiple purchase order lines is not currently supported.'; NotLinkedToVendorErr: Label 'The selected purchase order line is not linked to the same vendor as the e-document line.'; AlreadyMatchedErr: Label 'A selected purchase order line is already matched to another e-document line. E-Document: %1, Purchase document: %2 %3.', Comment = '%1 - E-Document No., %2 - Purchase Document Type, %3 - Purchase Document No.'; 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, MatchedUnitOfMeasure : Code[20]; @@ -445,8 +443,6 @@ codeunit 6196 "E-Doc. PO Matching" begin if SelectedPOLines.IsEmpty() then exit; - if SelectedPOLines.Count() > 1 then - Error(MatchesToMultiplePOLinesNotSupportedErr); RemoveAllMatchesForEDocumentLine(EDocumentPurchaseLine); FirstOfLinesBeingMatched := true; MatchedPOLineVendorNo := ''; @@ -498,10 +494,6 @@ codeunit 6196 "E-Doc. PO Matching" 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"::ExceedsInvoiceableQty); - if (not TempMatchWarnings.IsEmpty) and (not CanMatchInvoiceLineToPOLineWithoutReceipt(EDocumentPurchaseLine, PurchaseLine)) then - Error(NotYetReceivedErr); end; /// @@ -519,13 +511,10 @@ codeunit 6196 "E-Doc. PO Matching" NullGuid: Guid; ReceiptLineNotMatchedErr: Label 'A selected receipt line is not matched to any of the purchase order lines matched to the e-document line.'; ReceiptLinesDontCoverErr: Label 'The selected receipt lines do not cover the full quantity of the e-document line.'; - MatchesToMultipleReceiptLinesNotSupportedErr: Label 'Matching an e-document line to multiple receipt lines is not currently supported.'; QuantityCovered: Decimal; begin if SelectedReceiptLines.IsEmpty() then exit; - if SelectedReceiptLines.Count() > 1 then - Error(MatchesToMultipleReceiptLinesNotSupportedErr); // Remove existing receipt line matches EDocPurchaseLinePOMatch.SetRange("E-Doc. Purchase Line SystemId", EDocumentPurchaseLine.SystemId); @@ -551,66 +540,6 @@ codeunit 6196 "E-Doc. PO Matching" Error(ReceiptLinesDontCoverErr); end; - procedure ConfigureDefaultPOMatchingSettings() - var - DefaultSetup: Record "E-Doc. PO Matching Setup"; - DefaultConfiguration: Enum "E-Doc. PO M. Configuration"; - VendorNos: List of [Code[20]]; - begin - DefaultSetup."PO Matching Config. Receipt" := Enum::"E-Doc. PO M. Config. Receipt"::"Always ask"; - DefaultSetup."Receive G/L Account Lines" := false; - DefaultConfiguration := Enum::"E-Doc. PO M. Configuration"::"Always ask"; - ConfigurePOMatchingSettings(DefaultSetup, DefaultConfiguration, VendorNos); - end; - - /// - /// Stores the configuration selected by the user. - /// - /// The global setup, applicable if no vendor specific override has been configured. - /// The configuration selected by the user. - /// If the configuration is vendor specific, this will contain the vendor numbers affected by such. - procedure ConfigurePOMatchingSettings(DesiredGlobalSetup: Record "E-Doc. PO Matching Setup"; Configuration: Enum "E-Doc. PO M. Configuration"; VendorNos: List of [Code[20]]) - var - GlobalSetup: Record "E-Doc. PO Matching Setup"; - EDocPOMatchingSetup: Record "E-Doc. PO Matching Setup"; - VendorNo: Code[20]; - begin - // We delete all existing settings and recreate them based on the desired global setup, configuration and vendor list - EDocPOMatchingSetup.DeleteAll(); - // We first prepare the global setup record, used as fallback if there's no vendor-specific setting - EDocPOMatchingSetup.Copy(DesiredGlobalSetup); - Clear(EDocPOMatchingSetup.Id); - EDocPOMatchingSetup."Vendor No." := ''; - case Configuration of - Configuration::"Always ask": - EDocPOMatchingSetup."PO Matching Config. Receipt" := "E-Doc. PO M. Config. Receipt"::"Always ask"; - Configuration::"Never receive at posting": - EDocPOMatchingSetup."PO Matching Config. Receipt" := "E-Doc. PO M. Config. Receipt"::"Never receive at posting"; - Configuration::"Always receive at posting": - EDocPOMatchingSetup."PO Matching Config. Receipt" := "E-Doc. PO M. Config. Receipt"::"Always receive at posting"; - Configuration::"Receive at posting except for certain vendors": // The default for a vendor that is not specified is to always receive at posting - EDocPOMatchingSetup."PO Matching Config. Receipt" := "E-Doc. PO M. Config. Receipt"::"Always receive at posting"; - Configuration::"Receive at posting only for certain vendors": // The default for a vendor that is not specified is to always ask - EDocPOMatchingSetup."PO Matching Config. Receipt" := "E-Doc. PO M. Config. Receipt"::"Always ask"; - end; - EDocPOMatchingSetup.Insert(); - // Now we create vendor-specific settings, if any - // By default, we copy the global setup values to each vendor-specific setting, and then we adjust only the receipt configuration - if not (Configuration in [Configuration::"Receive at posting except for certain vendors", Configuration::"Receive at posting only for certain vendors"]) then - exit; - GlobalSetup.Copy(EDocPOMatchingSetup); - foreach VendorNo in VendorNos do begin - EDocPOMatchingSetup.Copy(GlobalSetup); - Clear(EDocPOMatchingSetup.Id); - EDocPOMatchingSetup."Vendor No." := VendorNo; - if Configuration = Configuration::"Receive at posting only for certain vendors" then - EDocPOMatchingSetup."PO Matching Config. Receipt" := "E-Doc. PO M. Config. Receipt"::"Always receive at posting" - else - EDocPOMatchingSetup."PO Matching Config. Receipt" := "E-Doc. PO M. Config. Receipt"::"Never receive at posting"; - EDocPOMatchingSetup.Insert(); - end; - end; - /// /// If the E-Document has been matched to an order line without specifying receipts, we match with receipt lines for that order line that can cover the E-Document line quantity. /// @@ -688,21 +617,54 @@ codeunit 6196 "E-Doc. PO Matching" procedure TransferPOMatchesFromEDocumentToInvoice(EDocument: Record "E-Document") var EDocumentPurchaseLine: Record "E-Document Purchase Line"; - PurchaseLine: Record "Purchase Line"; - TempPurchaseReceiptLine: Record "Purch. Rcpt. Line" temporary; + EDocPurchaseLinePOMatch: Record "E-Doc. Purchase Line PO Match"; + InvoicePurchaseLine: Record "Purchase Line"; + OrderPurchaseLine: Record "Purchase Line"; + PurchaseReceiptLine: Record "Purch. Rcpt. Line"; + MatchedOrderLineMgmt: Codeunit "Matched Order Line Mgmt."; + QtyToInvoice, QtyToInvoiceBase, EDocLineBaseQty : Decimal; + HasReceiptMatch: Boolean; + NullGuid: Guid; begin EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); if EDocumentPurchaseLine.FindSet() then repeat - LoadReceiptLinesMatchedToEDocumentLine(EDocumentPurchaseLine, TempPurchaseReceiptLine); - if not TempPurchaseReceiptLine.FindFirst() then // We only support a single receipt line match in BaseApp - continue; - PurchaseLine := EDocumentPurchaseLine.GetLinkedPurchaseLine(); - if IsNullGuid(PurchaseLine.SystemId) then - continue; - PurchaseLine."Receipt No." := TempPurchaseReceiptLine."Document No."; - PurchaseLine."Receipt Line No." := TempPurchaseReceiptLine."Line No."; - PurchaseLine.Modify(); + InvoicePurchaseLine := EDocumentPurchaseLine.GetLinkedPurchaseLine(); + if not IsNullGuid(InvoicePurchaseLine.SystemId) then begin + EDocPurchaseLinePOMatch.SetRange("E-Doc. Purchase Line SystemId", EDocumentPurchaseLine.SystemId); + + EDocPurchaseLinePOMatch.SetFilter("Receipt Line SystemId", '<>%1', NullGuid); + HasReceiptMatch := not EDocPurchaseLinePOMatch.IsEmpty(); + EDocPurchaseLinePOMatch.SetRange("Receipt Line SystemId"); + + if not GetEDocumentLineQuantityInBaseUoM(EDocumentPurchaseLine, EDocLineBaseQty) then + EDocLineBaseQty := EDocumentPurchaseLine.Quantity; + + if EDocPurchaseLinePOMatch.FindSet() then + repeat + if OrderPurchaseLine.GetBySystemId(EDocPurchaseLinePOMatch."Purchase Line SystemId") then + if not IsNullGuid(EDocPurchaseLinePOMatch."Receipt Line SystemId") then begin + // Matched to a posted receipt line: the quantity comes from that receipt line. + if PurchaseReceiptLine.GetBySystemId(EDocPurchaseLinePOMatch."Receipt Line SystemId") then begin + QtyToInvoice := PurchaseReceiptLine."Qty. Rcd. Not Invoiced"; + QtyToInvoiceBase := OrderPurchaseLine.CalcBaseQty(QtyToInvoice, OrderPurchaseLine.FieldCaption("Qty. to Invoice"), OrderPurchaseLine.FieldCaption("Qty. to Invoice (Base)")); + MatchedOrderLineMgmt.CreateMatchedOrderLine(InvoicePurchaseLine.SystemId, OrderPurchaseLine.SystemId, PurchaseReceiptLine.SystemId, QtyToInvoice, QtyToInvoiceBase, false); + end; + end else begin + // Order-only row. If the order line is already received we mirror BaseApp's remaining quantity; + // otherwise this is a receive-on-invoice row and the quantity to receive is what we are invoicing. + // TODO: when a single draft line is matched to several not-yet-received order lines, each row currently carries the full invoiced quantity. + if HasReceiptMatch or (OrderPurchaseLine."Qty. Rcd. Not Invoiced" <> 0) then begin + QtyToInvoice := OrderPurchaseLine."Qty. Rcd. Not Invoiced"; + QtyToInvoiceBase := OrderPurchaseLine."Qty. Rcd. Not Invoiced (Base)"; + end else begin + QtyToInvoice := EDocumentPurchaseLine.Quantity; + QtyToInvoiceBase := EDocLineBaseQty; + end; + MatchedOrderLineMgmt.CreateMatchedOrderLine(InvoicePurchaseLine.SystemId, OrderPurchaseLine.SystemId, NullGuid, QtyToInvoice, QtyToInvoiceBase, OrderPurchaseLine."Receipt on Invoice"); + end; + until EDocPurchaseLinePOMatch.Next() = 0; + end; RemoveAllMatchesForEDocumentLine(EDocumentPurchaseLine); until EDocumentPurchaseLine.Next() = 0; end; @@ -714,139 +676,63 @@ codeunit 6196 "E-Doc. PO Matching" procedure TransferPOMatchesFromInvoiceToEDocument(PurchaseHeader: Record "Purchase Header") var EDocumentPurchaseLine: Record "E-Document Purchase Line"; + MatchedOrderLine: Record "Matched Order Line"; PurchaseInvoiceLine: Record "Purchase Line"; PurchaseOrderLine: Record "Purchase Line"; PurchaseReceiptLine: Record "Purch. Rcpt. Line"; - TempPOLineToMatch: Record "Purchase Line" temporary; - TempReceiptLineToMatch: Record "Purch. Rcpt. Line" temporary; + TempPOLinesToMatch: Record "Purchase Line" temporary; + TempReceiptLinesToMatch: Record "Purch. Rcpt. Line" temporary; begin + // TODO: consider also the old "Receipt No." for this restoring PurchaseInvoiceLine.SetRange("Document Type", PurchaseHeader."Document Type"); PurchaseInvoiceLine.SetRange("Document No.", PurchaseHeader."No."); - PurchaseInvoiceLine.SetFilter("Receipt No.", '<>%1', ''); - PurchaseInvoiceLine.SetFilter("Receipt Line No.", '<>%1', 0); - if PurchaseInvoiceLine.IsEmpty() then + if not PurchaseInvoiceLine.FindSet() then exit; - PurchaseInvoiceLine.FindSet(); repeat - if not EDocumentPurchaseLine.GetFromLinkedPurchaseLine(PurchaseInvoiceLine) then - continue; - if not PurchaseReceiptLine.Get(PurchaseInvoiceLine."Receipt No.", PurchaseInvoiceLine."Receipt Line No.") then - continue; - if not PurchaseOrderLine.Get(Enum::"Purchase Document Type"::Order, PurchaseReceiptLine."Order No.", PurchaseReceiptLine."Order Line No.") then - continue; - TempPOLineToMatch.DeleteAll(); - TempPOLineToMatch.Copy(PurchaseOrderLine); - TempPOLineToMatch.Insert(); - MatchPOLinesToEDocumentLine(TempPOLineToMatch, EDocumentPurchaseLine); - - TempReceiptLineToMatch.DeleteAll(); - TempReceiptLineToMatch.Copy(PurchaseReceiptLine); - TempReceiptLineToMatch.Insert(); - MatchReceiptLinesToEDocumentLine(TempReceiptLineToMatch, EDocumentPurchaseLine); + if EDocumentPurchaseLine.GetFromLinkedPurchaseLine(PurchaseInvoiceLine) then begin + MatchedOrderLine.SetRange("Document Line SystemId", PurchaseInvoiceLine.SystemId); + if not MatchedOrderLine.IsEmpty() then begin + TempPOLinesToMatch.Reset(); + TempPOLinesToMatch.DeleteAll(); + TempReceiptLinesToMatch.Reset(); + TempReceiptLinesToMatch.DeleteAll(); + + MatchedOrderLine.FindSet(); + repeat + if PurchaseOrderLine.GetBySystemId(MatchedOrderLine."Matched Order Line SystemId") then + if not TempPOLinesToMatch.Get(PurchaseOrderLine."Document Type", PurchaseOrderLine."Document No.", PurchaseOrderLine."Line No.") then begin + TempPOLinesToMatch := PurchaseOrderLine; + TempPOLinesToMatch.Insert(); + end; + if not IsNullGuid(MatchedOrderLine."Matched Rcpt./Shpt. Line SysId") then + if PurchaseReceiptLine.GetBySystemId(MatchedOrderLine."Matched Rcpt./Shpt. Line SysId") then + if not TempReceiptLinesToMatch.Get(PurchaseReceiptLine."Document No.", PurchaseReceiptLine."Line No.") then begin + TempReceiptLinesToMatch := PurchaseReceiptLine; + TempReceiptLinesToMatch.Insert(); + end; + until MatchedOrderLine.Next() = 0; + + if not TempPOLinesToMatch.IsEmpty() then + MatchPOLinesToEDocumentLine(TempPOLinesToMatch, EDocumentPurchaseLine); + if not TempReceiptLinesToMatch.IsEmpty() then + MatchReceiptLinesToEDocumentLine(TempReceiptLinesToMatch, EDocumentPurchaseLine); + end; + end; until PurchaseInvoiceLine.Next() = 0; - PurchaseInvoiceLine.SetRange("Receipt No."); - PurchaseInvoiceLine.SetRange("Receipt Line No."); - PurchaseInvoiceLine.ModifyAll("Receipt No.", ''); - PurchaseInvoiceLine.ModifyAll("Receipt Line No.", 0); - end; - - /// - /// Loads the settings for purchase order matching. - /// - /// The global setup, applicable if no vendor specific override has been configured. - /// The configuration selected by the user. - /// If the configuration is vendor specific, this will contain the vendor numbers affected by such. - procedure GetPOMatchingSettings(var GlobalSetup: Record "E-Doc. PO Matching Setup"; var Configuration: Enum "E-Doc. PO M. Configuration"; var VendorNos: List of [Code[20]]) - var - EDocPOMatchingSetup: Record "E-Doc. PO Matching Setup"; - begin - GlobalSetup.GetSetup(); - Configuration := Enum::"E-Doc. PO M. Configuration".FromInteger(GlobalSetup."PO Matching Config. Receipt".AsInteger()); - EDocPOMatchingSetup.SetFilter("Vendor No.", '<> %1', ''); - if EDocPOMatchingSetup.FindSet() then begin - // If there are vendor-specific settings, we need to adjust the configuration value - if Configuration = Configuration::"Always ask" then // Always ask is the default for non specified vendors when "Receive at posting only for certain vendors" is selected - Configuration := Configuration::"Receive at posting only for certain vendors"; - if Configuration = Configuration::"Always receive at posting" then // Always receive at posting is the default for non specified vendors when "Receive at posting except for certain vendors" is selected - Configuration := Configuration::"Receive at posting except for certain vendors"; - repeat - VendorNos.Add(EDocPOMatchingSetup."Vendor No."); - until EDocPOMatchingSetup.Next() = 0; - end; end; /// - /// Verifies that all E-Document lines that have a Not Yet Received warning can be validly matched without receipt. - /// - /// - /// - procedure VerifyEDocumentMatchedLinesAreValidMatches(EDocumentPurchaseHeader: Record "E-Document Purchase Header"): Boolean - var - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - TempPurchaseLine: Record "Purchase Line" temporary; - TempPOMatchWarnings: Record "E-Doc PO Match Warning" temporary; - begin - CalculatePOMatchWarnings(EDocumentPurchaseHeader, TempPOMatchWarnings); - TempPOMatchWarnings.SetRange("Warning Type", "E-Doc PO Match Warning"::ExceedsInvoiceableQty); - // For each line that exceeds invoiceable qty, we check if it can be matched without receipt - if TempPOMatchWarnings.FindSet() then - repeat - EDocumentPurchaseLine.GetBySystemId(TempPOMatchWarnings."E-Doc. Purchase Line SystemId"); - LoadPOLinesMatchedToEDocumentLine(EDocumentPurchaseLine, TempPurchaseLine); - if not TempPurchaseLine.FindFirst() then - continue; - if not CanMatchInvoiceLineToPOLineWithoutReceipt(EDocumentPurchaseLine, TempPurchaseLine) then - exit(false); - until TempPOMatchWarnings.Next() = 0; - exit(true); - end; - - /// - /// Returns whether an invoice line can be matched against a PO line provided that the PO line has not yet been received. If it can be matched it does not imply that we will not warn the user, but if it cannot be matched we will warn the user. - /// - /// - /// - /// - local procedure CanMatchInvoiceLineToPOLineWithoutReceipt(EDocumentPurchaseLine: Record "E-Document Purchase Line"; PurchaseLine: Record "Purchase Line"): Boolean - var - EDocPOMatchingSetup: Record "E-Doc. PO Matching Setup"; - Vendor: Record Vendor; - begin - // Lines without a vendor assigned can't be matched in general - Vendor := EDocumentPurchaseLine.GetBCVendor(); - if Vendor."No." = '' then - exit(false); - - EDocPOMatchingSetup.GetSetup(Vendor."No."); - if EDocPOMatchingSetup."Receive G/L Account Lines" and (PurchaseLine.Type = "Purchase Line Type"::"G/L Account") then - exit(true); - - case EDocPOMatchingSetup."PO Matching Config. Receipt" of - "E-Doc. PO M. Config. Receipt"::"Always receive at posting", - "E-Doc. PO M. Config. Receipt"::"Always ask": - exit(true); - "E-Doc. PO M. Config. Receipt"::"Never receive at posting": - exit(false); - end; - end; - - /// - /// Returns whether we should warn the user if the specified vendor's purchase order lines are not yet received and they are matched to an invoice line. + /// Returns whether we should warn the user that the specified vendor's purchase order lines are not yet received when they are matched to an invoice line. /// /// /// procedure ShouldWarnIfNotYetReceived(VendorNo: Code[20]): Boolean var - EDocPOMatchingSetup: Record "E-Doc. PO Matching Setup"; + Vendor: Record Vendor; begin - EDocPOMatchingSetup.GetSetup(VendorNo); - case EDocPOMatchingSetup."PO Matching Config. Receipt" of - "E-Doc. PO M. Config. Receipt"::"Always receive at posting": - exit(false); - "E-Doc. PO M. Config. Receipt"::"Always ask", - "E-Doc. PO M. Config. Receipt"::"Never receive at posting": - exit(true); - end; + if not Vendor.Get(VendorNo) then + exit(true); + exit(Vendor."Receipt on Invoice Policy" <> Vendor."Receipt on Invoice Policy"::Always); end; } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocPOMatchingSetup.Table.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocPOMatchingSetup.Table.al deleted file mode 100644 index 01687ba88f..0000000000 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocPOMatchingSetup.Table.al +++ /dev/null @@ -1,74 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ -namespace Microsoft.eServices.EDocument.Processing.Import.Purchase; - -using Microsoft.Purchases.Vendor; - -table 6116 "E-Doc. PO Matching Setup" -{ - Access = Internal; - InherentEntitlements = RID; - InherentPermissions = RID; - ReplicateData = false; - - fields - { - field(1; Id; Integer) - { - AutoIncrement = true; - DataClassification = SystemMetadata; - } - field(2; "PO Matching Config. Receipt"; Enum "E-Doc. PO M. Config. Receipt") - { - DataClassification = CustomerContent; - } - field(3; "Vendor No."; Code[20]) - { - DataClassification = CustomerContent; - TableRelation = Vendor."No."; - } - field(4; "Receive G/L Account Lines"; Boolean) - { - DataClassification = CustomerContent; - } - } - keys - { - key(PK; Id) - { - Clustered = true; - } - } - - /// - /// Sets the current record to the setup applicable to the provided vendor - /// - /// - /// - procedure GetSetup(VendorNo: Code[20]) - begin - Clear(Rec); - Rec.SetRange("Vendor No.", VendorNo); - if Rec.FindFirst() then - exit; - // If there is no specific setup for the vendor, set to the global setup - GetSetup(); - end; - - /// - /// Sets the current record to the setup applicable if there is no specific override - /// - /// - procedure GetSetup() - begin - Clear(Rec); - Rec.SetFilter("Vendor No.", ''); - if Rec.FindFirst() then - exit; - Rec."PO Matching Config. Receipt" := "E-Doc. PO M. Config. Receipt"::"Always ask"; - Rec."Receive G/L Account Lines" := true; - end; - -} \ No newline at end of file diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocSelectPOLines.Page.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocSelectPOLines.Page.al index a91b2d447b..92588e4a14 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocSelectPOLines.Page.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/PurchaseOrderMatching/EDocSelectPOLines.Page.al @@ -37,7 +37,12 @@ page 6116 "E-Doc. Select PO Lines" Caption = 'Quantity'; ToolTip = 'Specifies the quantity of the e-document line.'; } - + field(EDocumentPurchaseLineSubTotal; EDocumentPurchaseLine."Sub Total") + { + ApplicationArea = All; + Caption = 'Amount'; + ToolTip = 'Specified the amount of the e-document line.'; + } } repeater(Lines) { @@ -84,6 +89,17 @@ page 6116 "E-Doc. Select PO Lines" ToolTip = 'Specifies the quantity that has already been invoiced.'; AutoFormatType = 0; } + field("Amount"; Rec.Amount) + { + ApplicationArea = All; + ToolTip = 'Specifies the amount on the purchase order line.'; + // AutoFormatType? + } + field("Expected Receipt Date"; Rec."Expected Receipt Date") + { + ApplicationArea = All; + ToolTip = 'Specifies the expected receipt date.'; + } } } } diff --git a/src/Apps/W1/EDocument/App/src/Processing/docs/business-logic.md b/src/Apps/W1/EDocument/App/src/Processing/docs/business-logic.md index c42bd1c89d..f4432dff3e 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/docs/business-logic.md +++ b/src/Apps/W1/EDocument/App/src/Processing/docs/business-logic.md @@ -53,9 +53,9 @@ The flow: 1. During Prepare Draft, `PreparePurchaseEDocDraft` calls `IPurchaseOrderProvider.GetPurchaseOrder()` to look up a PO by order number from the e-document header. 2. If found, `EDocPOMatching.MatchPOLinesToEDocumentLine()` matches e-document purchase lines to PO lines. 3. After matching, `CalculatePOMatchWarnings()` generates warnings for over-receipt, under-receipt, quantity mismatches, etc. -4. During Finish Draft, `SuggestReceiptsForMatchedOrderLines()` proposes receipt lines, and `TransferPOMatchesFromEDocumentToInvoice()` writes matches to the created purchase invoice. +4. During Finish Draft, `SuggestReceiptsForMatchedOrderLines()` proposes receipt lines, and `TransferPOMatchesFromEDocumentToInvoice()` transfers the matches onto the created purchase invoice as BaseApp `"Matched Order Line"` (table 5817) records via `"Matched Order Line Mgmt.".CreateMatchedOrderLine()`. -**Key data:** Matches are stored in `"E-Doc. Purchase Line PO Match"` (table 6114) -- a junction table linking e-document lines to PO lines and receipt lines via SystemIds. Warnings go in `"E-Doc. PO Match Warning"` (table 6115). Receipt behavior is configurable per vendor in `"E-Doc. PO Matching Setup"` (Always Ask / Always Receive / Never Receive). +**Key data:** Matches are staged in `"E-Doc. Purchase Line PO Match"` (table 6114) -- a junction table linking e-document lines to PO lines and receipt lines via SystemIds -- and transferred to BaseApp's `"Matched Order Line"` table when the invoice is created. Warnings go in `"E-Doc. PO Match Warning"` (table 6115). Receipt-on-invoice behavior is driven by the BaseApp vendor field `"Receipt on Invoice Policy"` (Never / Always), which is inherited onto the purchase order header and lines and can be overridden per order line; matching a not-yet-received line is allowed and only surfaced as a warning. **Main codeunit:** `EDocPOMatching.Codeunit.al` (codeunit 6196) in `Import/Purchase/PurchaseOrderMatching/`. 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 83f23def22..f32d05c9cc 100644 --- a/src/Apps/W1/EDocument/Test/src/Matching/EDocPOMatchingUnitTests.Codeunit.al +++ b/src/Apps/W1/EDocument/Test/src/Matching/EDocPOMatchingUnitTests.Codeunit.al @@ -11,6 +11,7 @@ using Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.Foundation.UOM; using Microsoft.Inventory.Item; +using Microsoft.Inventory.Tracking; using Microsoft.Purchases.Document; using Microsoft.Purchases.History; using Microsoft.Purchases.Setup; @@ -1981,7 +1982,7 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" end; [Test] - procedure POMatchingConfigurationAlwaysAskAllowsMatchingAndGeneratesWarnings() + procedure POMatchingPolicyNeverAllowsMatchingAndGeneratesWarning() var EDocument: Record "E-Document"; EDocumentPurchaseHeader: Record "E-Document Purchase Header"; @@ -1993,9 +1994,9 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" TempPurchaseLine: Record "Purchase Line" temporary; begin Initialize(); - // [SCENARIO] PO matching configuration "Always ask" allows matching and generates warnings for not yet received items - // [GIVEN] Configuration set to "Always ask" and a PO line not yet received - SetupPOMatchingConfiguration(Enum::"E-Doc. PO M. Configuration"::"Always ask", Vendor."No.", false); + // [SCENARIO] A vendor whose policy never receives on invoice allows matching a not-yet-received PO line and surfaces a warning + // [GIVEN] Vendor "Receipt on Invoice Policy" set to Never and a PO line not yet received + SetVendorReceiptOnInvoicePolicy(Vendor."No.", Enum::"Receipt on Invoice Policy"::Never); LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); @@ -2018,12 +2019,11 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" TempPurchaseLine.Insert(); // [WHEN] MatchPOLinesToEDocumentLine is called + // [THEN] Matching is allowed even though the line is not yet received (the gate has been removed) EDocPOMatching.MatchPOLinesToEDocumentLine(TempPurchaseLine, EDocumentPurchaseLine); - - // [THEN] Matching should succeed Assert.IsTrue(EDocPOMatching.IsPOLineMatchedToEDocumentLine(PurchaseLine, EDocumentPurchaseLine), 'PO line should be matched to E-Document line'); - // [THEN] ExceedsInvoiceableQty warning should be generated + // [THEN] An ExceedsInvoiceableQty warning is generated, because the line will not be received automatically at posting EDocPOMatching.CalculatePOMatchWarnings(EDocumentPurchaseHeader, TempPOMatchWarnings); TempPOMatchWarnings.SetRange("E-Doc. Purchase Line SystemId", EDocumentPurchaseLine.SystemId); TempPOMatchWarnings.SetRange("Warning Type", Enum::"E-Doc PO Match Warning"::ExceedsInvoiceableQty); @@ -2031,7 +2031,7 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" end; [Test] - procedure POMatchingConfigurationAlwaysReceiveAllowsMatchingWithoutWarnings() + procedure POMatchingPolicyAlwaysAllowsMatchingWithoutWarning() var EDocument: Record "E-Document"; EDocumentPurchaseHeader: Record "E-Document Purchase Header"; @@ -2043,9 +2043,9 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" TempPurchaseLine: Record "Purchase Line" temporary; begin Initialize(); - // [SCENARIO] PO matching configuration "Always receive at posting" allows matching without warnings for not yet received items - // [GIVEN] Configuration set to "Always receive at posting" and a PO line not yet received - SetupPOMatchingConfiguration(Enum::"E-Doc. PO M. Configuration"::"Always receive at posting", Vendor."No.", false); + // [SCENARIO] A vendor whose policy always receives on invoice allows matching a not-yet-received PO line without a warning + // [GIVEN] Vendor "Receipt on Invoice Policy" set to Always and a PO line not yet received + SetVendorReceiptOnInvoicePolicy(Vendor."No.", Enum::"Receipt on Invoice Policy"::Always); LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); @@ -2073,249 +2073,13 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" // [THEN] Matching should succeed Assert.IsTrue(EDocPOMatching.IsPOLineMatchedToEDocumentLine(PurchaseLine, EDocumentPurchaseLine), 'PO line should be matched to E-Document line'); - // [THEN] ExceedsInvoiceableQty warning should NOT be generated + // [THEN] No ExceedsInvoiceableQty warning is generated, because the line will be received automatically at posting EDocPOMatching.CalculatePOMatchWarnings(EDocumentPurchaseHeader, TempPOMatchWarnings); TempPOMatchWarnings.SetRange("E-Doc. Purchase Line SystemId", EDocumentPurchaseLine.SystemId); TempPOMatchWarnings.SetRange("Warning Type", Enum::"E-Doc PO Match Warning"::ExceedsInvoiceableQty); Assert.IsTrue(TempPOMatchWarnings.IsEmpty(), 'Expected no ExceedsInvoiceableQty warning to be generated'); end; - [Test] - procedure POMatchingConfigurationNeverReceiveBlocksMatchingForNotYetReceivedLines() - var - EDocument: Record "E-Document"; - EDocumentPurchaseHeader: Record "E-Document Purchase Header"; - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - Item: Record Item; - TempPurchaseLine: Record "Purchase Line" temporary; - begin - Initialize(); - // [SCENARIO] PO matching configuration "Never receive at posting" blocks matching for not yet received PO lines - // [GIVEN] Configuration set to "Never receive at posting" and a PO line not yet received - SetupPOMatchingConfiguration(Enum::"E-Doc. PO M. Configuration"::"Never receive at posting", Vendor."No.", false); - - LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); - EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); - EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; - EDocumentPurchaseHeader.Modify(); - EDocumentPurchaseLine := LibraryEDocument.InsertPurchaseDraftLine(EDocument); - - // Create PO line that is not yet received - LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Order, Vendor."No."); - LibraryEDocument.GetGenericItem(Item); - LibraryPurchase.CreatePurchaseLine(PurchaseLine, PurchaseHeader, PurchaseLine.Type::Item, Item."No.", 10); - PurchaseLine.Modify(); - - // Set up E-Document line to match the item - EDocumentPurchaseLine."[BC] Unit of Measure" := Item."Base Unit of Measure"; - EDocumentPurchaseLine.Quantity := 10; - EDocumentPurchaseLine.Modify(); - - TempPurchaseLine := PurchaseLine; - TempPurchaseLine.Insert(); - - // [WHEN] MatchPOLinesToEDocumentLine is called - // [THEN] An error should be raised indicating the lines are not yet received - asserterror EDocPOMatching.MatchPOLinesToEDocumentLine(TempPurchaseLine, EDocumentPurchaseLine); - end; - - [Test] - procedure POMatchingConfigurationReceiveOnlyForCertainVendorsAllowsMatchingForSpecifiedVendors() - var - EDocument: Record "E-Document"; - EDocumentPurchaseHeader: Record "E-Document Purchase Header"; - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - Item: Record Item; - TempPOMatchWarnings: Record "E-Doc PO Match Warning" temporary; - TempPurchaseLine: Record "Purchase Line" temporary; - begin - Initialize(); - // [SCENARIO] PO matching configuration "Receive at posting only for certain vendors" allows matching without warnings for specified vendors - // [GIVEN] Configuration set to "Receive at posting only for certain vendors" with current vendor specified - SetupPOMatchingConfiguration(Enum::"E-Doc. PO M. Configuration"::"Receive at posting only for certain vendors", Vendor."No.", true); - - LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); - EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); - EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; - EDocumentPurchaseHeader.Modify(); - EDocumentPurchaseLine := LibraryEDocument.InsertPurchaseDraftLine(EDocument); - - // Create PO line that is not yet received - LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Order, Vendor."No."); - LibraryEDocument.GetGenericItem(Item); - LibraryPurchase.CreatePurchaseLine(PurchaseLine, PurchaseHeader, PurchaseLine.Type::Item, Item."No.", 10); - PurchaseLine.Modify(); - - // Set up E-Document line to match the item - EDocumentPurchaseLine."[BC] Unit of Measure" := Item."Base Unit of Measure"; - EDocumentPurchaseLine.Quantity := 10; - EDocumentPurchaseLine.Modify(); - - TempPurchaseLine := PurchaseLine; - TempPurchaseLine.Insert(); - - // [WHEN] MatchPOLinesToEDocumentLine is called - EDocPOMatching.MatchPOLinesToEDocumentLine(TempPurchaseLine, EDocumentPurchaseLine); - - // [THEN] Matching should succeed - Assert.IsTrue(EDocPOMatching.IsPOLineMatchedToEDocumentLine(PurchaseLine, EDocumentPurchaseLine), 'PO line should be matched to E-Document line'); - - // [THEN] ExceedsInvoiceableQty warning should NOT be generated for specified vendor - EDocPOMatching.CalculatePOMatchWarnings(EDocumentPurchaseHeader, TempPOMatchWarnings); - TempPOMatchWarnings.SetRange("E-Doc. Purchase Line SystemId", EDocumentPurchaseLine.SystemId); - TempPOMatchWarnings.SetRange("Warning Type", Enum::"E-Doc PO Match Warning"::ExceedsInvoiceableQty); - Assert.IsTrue(TempPOMatchWarnings.IsEmpty(), 'Expected no ExceedsInvoiceableQty warning for specified vendor'); - end; - - [Test] - procedure POMatchingConfigurationReceiveOnlyForCertainVendorsGeneratesWarningsForNonSpecifiedVendors() - var - EDocument: Record "E-Document"; - EDocumentPurchaseHeader: Record "E-Document Purchase Header"; - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - Item: Record Item; - TempPOMatchWarnings: Record "E-Doc PO Match Warning" temporary; - TempPurchaseLine: Record "Purchase Line" temporary; - DifferentVendor: Record Vendor; - begin - Initialize(); - // [SCENARIO] PO matching configuration "Receive at posting only for certain vendors" generates warnings for non-specified vendors - // [GIVEN] Configuration set to "Receive at posting only for certain vendors" with a different vendor specified, not the current one - LibraryPurchase.CreateVendor(DifferentVendor); - SetupPOMatchingConfiguration(Enum::"E-Doc. PO M. Configuration"::"Receive at posting only for certain vendors", DifferentVendor."No.", true); - - LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); - EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); - EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; // Use the default vendor, not the one in configuration - EDocumentPurchaseHeader.Modify(); - EDocumentPurchaseLine := LibraryEDocument.InsertPurchaseDraftLine(EDocument); - - // Create PO line that is not yet received - LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Order, Vendor."No."); - LibraryEDocument.GetGenericItem(Item); - LibraryPurchase.CreatePurchaseLine(PurchaseLine, PurchaseHeader, PurchaseLine.Type::Item, Item."No.", 10); - PurchaseLine.Modify(); - - // Set up E-Document line to match the item - EDocumentPurchaseLine."[BC] Unit of Measure" := Item."Base Unit of Measure"; - EDocumentPurchaseLine.Quantity := 10; - EDocumentPurchaseLine.Modify(); - - TempPurchaseLine := PurchaseLine; - TempPurchaseLine.Insert(); - - // [WHEN] MatchPOLinesToEDocumentLine is called - EDocPOMatching.MatchPOLinesToEDocumentLine(TempPurchaseLine, EDocumentPurchaseLine); - - // [THEN] Matching should succeed - Assert.IsTrue(EDocPOMatching.IsPOLineMatchedToEDocumentLine(PurchaseLine, EDocumentPurchaseLine), 'PO line should be matched to E-Document line'); - - // [THEN] ExceedsInvoiceableQty warning should be generated for non-specified vendor (default behavior is "Always ask") - EDocPOMatching.CalculatePOMatchWarnings(EDocumentPurchaseHeader, TempPOMatchWarnings); - TempPOMatchWarnings.SetRange("E-Doc. Purchase Line SystemId", EDocumentPurchaseLine.SystemId); - TempPOMatchWarnings.SetRange("Warning Type", Enum::"E-Doc PO Match Warning"::ExceedsInvoiceableQty); - Assert.IsFalse(TempPOMatchWarnings.IsEmpty(), 'Expected ExceedsInvoiceableQty warning for non-specified vendor'); - end; - - [Test] - procedure POMatchingConfigurationReceiveExceptForCertainVendorsBlocksMatchingForSpecifiedVendors() - var - EDocument: Record "E-Document"; - EDocumentPurchaseHeader: Record "E-Document Purchase Header"; - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - Item: Record Item; - TempPurchaseLine: Record "Purchase Line" temporary; - begin - Initialize(); - // [SCENARIO] PO matching configuration "Receive at posting except for certain vendors" blocks matching for specified vendors - // [GIVEN] Configuration set to "Receive at posting except for certain vendors" with current vendor specified - SetupPOMatchingConfiguration(Enum::"E-Doc. PO M. Configuration"::"Receive at posting except for certain vendors", Vendor."No.", true); - - LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); - EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); - EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; - EDocumentPurchaseHeader.Modify(); - EDocumentPurchaseLine := LibraryEDocument.InsertPurchaseDraftLine(EDocument); - - // Create PO line that is not yet received - LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Order, Vendor."No."); - LibraryEDocument.GetGenericItem(Item); - LibraryPurchase.CreatePurchaseLine(PurchaseLine, PurchaseHeader, PurchaseLine.Type::Item, Item."No.", 10); - PurchaseLine.Modify(); - - // Set up E-Document line to match the item - EDocumentPurchaseLine."[BC] Unit of Measure" := Item."Base Unit of Measure"; - EDocumentPurchaseLine.Quantity := 10; - EDocumentPurchaseLine.Modify(); - - TempPurchaseLine := PurchaseLine; - TempPurchaseLine.Insert(); - - // [WHEN] MatchPOLinesToEDocumentLine is called - // [THEN] An error should be raised indicating the lines are not yet received for this vendor - asserterror EDocPOMatching.MatchPOLinesToEDocumentLine(TempPurchaseLine, EDocumentPurchaseLine); - end; - - [Test] - procedure POMatchingConfigurationReceiveExceptForCertainVendorsAllowsMatchingForNonSpecifiedVendors() - var - EDocument: Record "E-Document"; - EDocumentPurchaseHeader: Record "E-Document Purchase Header"; - EDocumentPurchaseLine: Record "E-Document Purchase Line"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - Item: Record Item; - TempPOMatchWarnings: Record "E-Doc PO Match Warning" temporary; - TempPurchaseLine: Record "Purchase Line" temporary; - DifferentVendor: Record Vendor; - begin - Initialize(); - // [SCENARIO] PO matching configuration "Receive at posting except for certain vendors" allows matching without warnings for non-specified vendors - // [GIVEN] Configuration set to "Receive at posting except for certain vendors" with a different vendor specified, not the current one - LibraryPurchase.CreateVendor(DifferentVendor); - SetupPOMatchingConfiguration(Enum::"E-Doc. PO M. Configuration"::"Receive at posting except for certain vendors", DifferentVendor."No.", true); - - LibraryEDocument.CreateInboundEDocument(EDocument, EDocumentService); - EDocumentPurchaseHeader := LibraryEDocument.MockPurchaseDraftPrepared(EDocument); - EDocumentPurchaseHeader."[BC] Vendor No." := Vendor."No."; // Use the default vendor, not the one in configuration - EDocumentPurchaseHeader.Modify(); - EDocumentPurchaseLine := LibraryEDocument.InsertPurchaseDraftLine(EDocument); - - // Create PO line that is not yet received - LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Order, Vendor."No."); - LibraryEDocument.GetGenericItem(Item); - LibraryPurchase.CreatePurchaseLine(PurchaseLine, PurchaseHeader, PurchaseLine.Type::Item, Item."No.", 10); - PurchaseLine.Modify(); - - // Set up E-Document line to match the item - EDocumentPurchaseLine."[BC] Unit of Measure" := Item."Base Unit of Measure"; - EDocumentPurchaseLine.Quantity := 10; - EDocumentPurchaseLine.Modify(); - - TempPurchaseLine := PurchaseLine; - TempPurchaseLine.Insert(); - - // [WHEN] MatchPOLinesToEDocumentLine is called - EDocPOMatching.MatchPOLinesToEDocumentLine(TempPurchaseLine, EDocumentPurchaseLine); - - // [THEN] Matching should succeed - Assert.IsTrue(EDocPOMatching.IsPOLineMatchedToEDocumentLine(PurchaseLine, EDocumentPurchaseLine), 'PO line should be matched to E-Document line'); - - // [THEN] ExceedsInvoiceableQty warning should NOT be generated for non-specified vendor (default behavior is "Always receive at posting") - EDocPOMatching.CalculatePOMatchWarnings(EDocumentPurchaseHeader, TempPOMatchWarnings); - TempPOMatchWarnings.SetRange("E-Doc. Purchase Line SystemId", EDocumentPurchaseLine.SystemId); - TempPOMatchWarnings.SetRange("Warning Type", Enum::"E-Doc PO Match Warning"::ExceedsInvoiceableQty); - Assert.IsTrue(TempPOMatchWarnings.IsEmpty(), 'Expected no ExceedsInvoiceableQty warning for non-specified vendor'); - end; - [Test] procedure SuggestReceiptsForMatchedOrderLinesDoesNotSuggestWhenEDocLineAlreadyHasReceiptMatch() var @@ -2482,6 +2246,7 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" PurchaseOrderLine: Record "Purchase Line"; PurchaseReceiptHeader: Record "Purch. Rcpt. Header"; PurchaseReceiptLine: Record "Purch. Rcpt. Line"; + MatchedOrderLine: Record "Matched Order Line"; Item: Record Item; begin Initialize(); @@ -2505,10 +2270,11 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" // [WHEN] TransferPOMatchesFromEDocumentToInvoice is called EDocPOMatching.TransferPOMatchesFromEDocumentToInvoice(EDocument); - // [THEN] The invoice line's Receipt No. and Receipt Line No. are set - PurchaseLine.Get(PurchaseLine."Document Type", PurchaseLine."Document No.", PurchaseLine."Line No."); - Assert.AreEqual(PurchaseReceiptLine."Document No.", PurchaseLine."Receipt No.", 'Expected invoice line Receipt No. to match receipt'); - Assert.AreEqual(PurchaseReceiptLine."Line No.", PurchaseLine."Receipt Line No.", 'Expected invoice line Receipt Line No. to match receipt line'); + // [THEN] A matched order line links the invoice line to the order line and the receipt line + MatchedOrderLine.SetRange("Document Line SystemId", PurchaseLine.SystemId); + MatchedOrderLine.SetRange("Matched Order Line SystemId", PurchaseOrderLine.SystemId); + MatchedOrderLine.SetRange("Matched Rcpt./Shpt. Line SysId", PurchaseReceiptLine.SystemId); + Assert.IsFalse(MatchedOrderLine.IsEmpty(), 'Expected a matched order line linking the invoice line to the order and receipt lines'); // [THEN] The E-Document line no longer has any PO or receipt matches Assert.IsFalse(EDocPOMatching.IsEDocumentLineMatchedToAnyPOLine(EDocumentPurchaseLine), 'Expected E-Document line to have no PO matches'); @@ -2527,6 +2293,7 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" PurchaseOrderLine1, PurchaseOrderLine2, PurchaseOrderLine3 : Record "Purchase Line"; PurchaseReceiptHeader: Record "Purch. Rcpt. Header"; PurchaseReceiptLine1, PurchaseReceiptLine2, PurchaseReceiptLine3 : Record "Purch. Rcpt. Line"; + MatchedOrderLine: Record "Matched Order Line"; Item1, Item2, Item3 : Record Item; begin Initialize(); @@ -2578,22 +2345,24 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" // [WHEN] TransferPOMatchesFromEDocumentToInvoice is called EDocPOMatching.TransferPOMatchesFromEDocumentToInvoice(EDocument); - // [THEN] Each invoice line has the correct Receipt No. and Receipt Line No. - PurchaseLine1.Get(PurchaseLine1."Document Type", PurchaseLine1."Document No.", PurchaseLine1."Line No."); - Assert.AreEqual(PurchaseReceiptLine1."Document No.", PurchaseLine1."Receipt No.", 'Expected first invoice line Receipt No. to match receipt'); - Assert.AreEqual(PurchaseReceiptLine1."Line No.", PurchaseLine1."Receipt Line No.", 'Expected first invoice line Receipt Line No. to match receipt line'); - - PurchaseLine2.Get(PurchaseLine2."Document Type", PurchaseLine2."Document No.", PurchaseLine2."Line No."); - Assert.AreEqual(PurchaseReceiptLine2."Document No.", PurchaseLine2."Receipt No.", 'Expected second invoice line Receipt No. to match receipt'); - Assert.AreEqual(PurchaseReceiptLine2."Line No.", PurchaseLine2."Receipt Line No.", 'Expected second invoice line Receipt Line No. to match receipt line'); + // [THEN] Each invoice line has a matched order line linking it to its order line and receipt line + AssertMatchedOrderLineExists(PurchaseLine1.SystemId, PurchaseOrderLine1.SystemId, PurchaseReceiptLine1.SystemId, 'first'); + AssertMatchedOrderLineExists(PurchaseLine2.SystemId, PurchaseOrderLine2.SystemId, PurchaseReceiptLine2.SystemId, 'second'); + AssertMatchedOrderLineExists(PurchaseLine3.SystemId, PurchaseOrderLine3.SystemId, PurchaseReceiptLine3.SystemId, 'third'); + end; - PurchaseLine3.Get(PurchaseLine3."Document Type", PurchaseLine3."Document No.", PurchaseLine3."Line No."); - Assert.AreEqual(PurchaseReceiptLine3."Document No.", PurchaseLine3."Receipt No.", 'Expected third invoice line Receipt No. to match receipt'); - Assert.AreEqual(PurchaseReceiptLine3."Line No.", PurchaseLine3."Receipt Line No.", 'Expected third invoice line Receipt Line No. to match receipt line'); + local procedure AssertMatchedOrderLineExists(InvoiceLineSystemId: Guid; OrderLineSystemId: Guid; ReceiptLineSystemId: Guid; LineLabel: Text) + var + MatchedOrderLine: Record "Matched Order Line"; + begin + MatchedOrderLine.SetRange("Document Line SystemId", InvoiceLineSystemId); + MatchedOrderLine.SetRange("Matched Order Line SystemId", OrderLineSystemId); + MatchedOrderLine.SetRange("Matched Rcpt./Shpt. Line SysId", ReceiptLineSystemId); + Assert.IsFalse(MatchedOrderLine.IsEmpty(), 'Expected a matched order line for the ' + LineLabel + ' invoice line'); end; [Test] - procedure TransferPOMatchesFromInvoiceToEDocumentCreatesMatchesAndClearsInvoiceReceiptInfo() + procedure TransferPOMatchesFromInvoiceToEDocumentReconstructsMatchesFromMatchedOrderLines() var EDocument: Record "E-Document"; EDocumentPurchaseHeader: Record "E-Document Purchase Header"; @@ -2604,11 +2373,12 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" PurchaseOrderLine: Record "Purchase Line"; PurchaseReceiptHeader: Record "Purch. Rcpt. Header"; PurchaseReceiptLine: Record "Purch. Rcpt. Line"; + MatchedOrderLineMgmt: Codeunit "Matched Order Line Mgmt."; Item: Record Item; begin Initialize(); - // [SCENARIO] TransferPOMatchesFromInvoiceToEDocument creates matches from invoice line receipt info and clears invoice receipt info - // [GIVEN] A purchase invoice line with Receipt No. and Receipt Line No. + // [SCENARIO] TransferPOMatchesFromInvoiceToEDocument reconstructs the e-document matches from the invoice's matched order lines + // [GIVEN] A purchase invoice line that carries a matched order line for an order line and a posted receipt line LibraryInventory.CreateItem(Item); LibraryPurchase.CreatePurchHeader(PurchaseOrderHeader, PurchaseOrderHeader."Document Type"::Order, Vendor."No."); LibraryPurchase.CreatePurchaseLine(PurchaseOrderLine, PurchaseOrderHeader, PurchaseOrderLine.Type::Item, Item."No.", 10); @@ -2618,9 +2388,7 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Invoice, Vendor."No."); LibraryPurchase.CreatePurchaseLine(PurchaseLine, PurchaseHeader, PurchaseLine.Type::Item, Item."No.", 10); - PurchaseLine."Receipt No." := PurchaseReceiptLine."Document No."; - PurchaseLine."Receipt Line No." := PurchaseReceiptLine."Line No."; - PurchaseLine.Modify(); + MatchedOrderLineMgmt.CreateMatchedOrderLine(PurchaseLine.SystemId, PurchaseOrderLine.SystemId, PurchaseReceiptLine.SystemId, 10, 10, false); // [GIVEN] The invoice line is linked to an E-Document line CreateMockEDocumentDraftWithLine(EDocument, EDocumentPurchaseHeader, EDocumentPurchaseLine, 10); @@ -2629,16 +2397,11 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" // [WHEN] TransferPOMatchesFromInvoiceToEDocument is called EDocPOMatching.TransferPOMatchesFromInvoiceToEDocument(PurchaseHeader); - // [THEN] The E-Document line is matched to the purchase order line + // [THEN] The E-Document line is matched back to the purchase order line Assert.IsTrue(EDocPOMatching.IsPOLineMatchedToEDocumentLine(PurchaseOrderLine, EDocumentPurchaseLine), 'Expected E-Document line to be matched to PO line'); - // [THEN] The E-Document line is matched to the receipt line + // [THEN] The E-Document line is matched back to the receipt line Assert.IsTrue(EDocPOMatching.IsReceiptLineMatchedToEDocumentLine(PurchaseReceiptLine, EDocumentPurchaseLine), 'Expected E-Document line to be matched to receipt line'); - - // [THEN] The invoice line's Receipt No. is empty and Receipt Line No. is 0 - PurchaseLine.Get(PurchaseLine."Document Type", PurchaseLine."Document No.", PurchaseLine."Line No."); - Assert.AreEqual('', PurchaseLine."Receipt No.", 'Expected invoice line Receipt No. to be cleared'); - Assert.AreEqual(0, PurchaseLine."Receipt Line No.", 'Expected invoice line Receipt Line No. to be cleared'); end; [Test] @@ -2653,11 +2416,12 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" PurchaseOrderLine1, PurchaseOrderLine2, PurchaseOrderLine3 : Record "Purchase Line"; PurchaseReceiptHeader: Record "Purch. Rcpt. Header"; PurchaseReceiptLine1, PurchaseReceiptLine2, PurchaseReceiptLine3 : Record "Purch. Rcpt. Line"; + MatchedOrderLineMgmt: Codeunit "Matched Order Line Mgmt."; Item1, Item2, Item3 : Record Item; begin Initialize(); // [SCENARIO] TransferPOMatchesFromInvoiceToEDocument processes multiple lines independently - // [GIVEN] A purchase invoice with three lines, each with different receipt information + // [GIVEN] A purchase invoice with three lines, each carrying a matched order line for its own order and receipt line LibraryPurchase.CreatePurchHeader(PurchaseOrderHeader, PurchaseOrderHeader."Document Type"::Order, Vendor."No."); LibraryInventory.CreateItem(Item1); LibraryPurchase.CreatePurchaseLine(PurchaseOrderLine1, PurchaseOrderHeader, PurchaseOrderLine1.Type::Item, Item1."No.", 10); @@ -2673,19 +2437,13 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Invoice, Vendor."No."); LibraryPurchase.CreatePurchaseLine(PurchaseLine1, PurchaseHeader, PurchaseLine1.Type::Item, Item1."No.", 10); - PurchaseLine1."Receipt No." := PurchaseReceiptLine1."Document No."; - PurchaseLine1."Receipt Line No." := PurchaseReceiptLine1."Line No."; - PurchaseLine1.Modify(); + MatchedOrderLineMgmt.CreateMatchedOrderLine(PurchaseLine1.SystemId, PurchaseOrderLine1.SystemId, PurchaseReceiptLine1.SystemId, 10, 10, false); LibraryPurchase.CreatePurchaseLine(PurchaseLine2, PurchaseHeader, PurchaseLine2.Type::Item, Item2."No.", 15); - PurchaseLine2."Receipt No." := PurchaseReceiptLine2."Document No."; - PurchaseLine2."Receipt Line No." := PurchaseReceiptLine2."Line No."; - PurchaseLine2.Modify(); + MatchedOrderLineMgmt.CreateMatchedOrderLine(PurchaseLine2.SystemId, PurchaseOrderLine2.SystemId, PurchaseReceiptLine2.SystemId, 15, 15, false); LibraryPurchase.CreatePurchaseLine(PurchaseLine3, PurchaseHeader, PurchaseLine3.Type::Item, Item3."No.", 20); - PurchaseLine3."Receipt No." := PurchaseReceiptLine3."Document No."; - PurchaseLine3."Receipt Line No." := PurchaseReceiptLine3."Line No."; - PurchaseLine3.Modify(); + MatchedOrderLineMgmt.CreateMatchedOrderLine(PurchaseLine3.SystemId, PurchaseOrderLine3.SystemId, PurchaseReceiptLine3.SystemId, 20, 20, false); // [GIVEN] An E-Document with three corresponding lines CreateMockEDocumentDraftWithLine(EDocument, EDocumentPurchaseHeader, EDocumentPurchaseLine1, 10); @@ -2713,19 +2471,6 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" Assert.IsTrue(EDocPOMatching.IsPOLineMatchedToEDocumentLine(PurchaseOrderLine3, EDocumentPurchaseLine3), 'Expected third E-Document line to be matched to third PO line'); Assert.IsTrue(EDocPOMatching.IsReceiptLineMatchedToEDocumentLine(PurchaseReceiptLine3, EDocumentPurchaseLine3), 'Expected third E-Document line to be matched to third receipt line'); - - // [THEN] All invoice lines have their receipt information cleared - PurchaseLine1.Get(PurchaseLine1."Document Type", PurchaseLine1."Document No.", PurchaseLine1."Line No."); - Assert.AreEqual('', PurchaseLine1."Receipt No.", 'Expected first invoice line Receipt No. to be cleared'); - Assert.AreEqual(0, PurchaseLine1."Receipt Line No.", 'Expected first invoice line Receipt Line No. to be cleared'); - - PurchaseLine2.Get(PurchaseLine2."Document Type", PurchaseLine2."Document No.", PurchaseLine2."Line No."); - Assert.AreEqual('', PurchaseLine2."Receipt No.", 'Expected second invoice line Receipt No. to be cleared'); - Assert.AreEqual(0, PurchaseLine2."Receipt Line No.", 'Expected second invoice line Receipt Line No. to be cleared'); - - PurchaseLine3.Get(PurchaseLine3."Document Type", PurchaseLine3."Document No.", PurchaseLine3."Line No."); - Assert.AreEqual('', PurchaseLine3."Receipt No.", 'Expected third invoice line Receipt No. to be cleared'); - Assert.AreEqual(0, PurchaseLine3."Receipt Line No.", 'Expected third invoice line Receipt Line No. to be cleared'); end; local procedure SetInvoiceNoSeriesInSetup() @@ -2737,21 +2482,13 @@ codeunit 133508 "E-Doc. PO Matching Unit Tests" PurchasesPayablesSetup.Modify(); end; - local procedure SetupPOMatchingConfiguration(Configuration: Enum "E-Doc. PO M. Configuration"; VendorNo: Code[20]; IncludeVendorInList: Boolean) + local procedure SetVendorReceiptOnInvoicePolicy(VendorNo: Code[20]; Policy: Enum "Receipt on Invoice Policy") var - EDocPOMatchingSetup: Record "E-Doc. PO Matching Setup"; - VendorNos: List of [Code[20]]; + VendorToConfigure: Record Vendor; begin - // Set default values for the setup - Clear(EDocPOMatchingSetup); - EDocPOMatchingSetup."Receive G/L Account Lines" := true; - - // Add vendor to list if requested - if IncludeVendorInList then - VendorNos.Add(VendorNo); - - // Configure the PO matching settings using the available procedure - EDocPOMatching.ConfigurePOMatchingSettings(EDocPOMatchingSetup, Configuration, VendorNos); + VendorToConfigure.Get(VendorNo); + VendorToConfigure."Receipt on Invoice Policy" := Policy; + VendorToConfigure.Modify(); end; local procedure ClearPurchaseDocumentsForVendor()