diff --git a/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchPayablesSetup.PageExt.al b/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchPayablesSetup.PageExt.al index 556fbad792..6e67ed6537 100644 --- a/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchPayablesSetup.PageExt.al +++ b/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchPayablesSetup.PageExt.al @@ -26,6 +26,16 @@ pageextension 6162 "E-Doc. Purch. Payables Setup" extends "Purchases & Payables ApplicationArea = All; ToolTip = 'Specifies how the posting date is set on purchase invoices created from e-documents. Work Date uses the current work date. Document Date uses the document date from the e-document.'; } + field("Apply VAT Diff. For Purch EDoc"; Rec."Apply VAT Diff. For Purch EDoc") + { + ApplicationArea = All; + ToolTip = 'Specifies whether VAT difference should be applied when matching incoming E-Document line with Purchase Order line'; + } + field("Resolve VAT Group Purch EDoc"; Rec."Resolve VAT Group Purch EDoc") + { + ApplicationArea = All; + ToolTip = 'Specifies whether to resolve VAT Product Group for purchase lines created from e-documents based on the VAT rate and the vendor''s VAT posting setup. If disabled, the VAT Product Group will not be identified based on the purchase e-document.'; + } } } } \ No newline at end of file diff --git a/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchPayablesSetup.TableExt.al b/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchPayablesSetup.TableExt.al index 2dc0c9ccec..6a433efeaf 100644 --- a/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchPayablesSetup.TableExt.al +++ b/src/Apps/W1/EDocument/App/src/Extensions/EDocPurchPayablesSetup.TableExt.al @@ -26,5 +26,16 @@ tableextension 6162 "E-Doc. Purch. Payables Setup" extends "Purchases & Payables Caption = 'E-Document Default Posting Date'; DataClassification = CustomerContent; } + field(6103; "Apply VAT Diff. For Purch EDoc"; Boolean) + { + Caption = 'Apply VAT Diff. For Purch. E-Doc.'; + DataClassification = CustomerContent; + InitValue = true; + } + field(6104; "Resolve VAT Group Purch EDoc"; Boolean) + { + Caption = 'Resolve VAT Product Group for Purch. E-Doc.'; + DataClassification = CustomerContent; + } } } 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 2eceac35ed..b81ef08ce5 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 @@ -176,6 +176,7 @@ codeunit 6117 "E-Doc. Create Purchase Invoice" implements IEDocumentFinishDraft, LastReceiptNo := EDocLineByReceipt.ReceiptNo; end; EDocLineByReceipt.Close(); + EDocPurchaseDocumentHelper.ApplyVATDifferenceToLines(PurchaseHeader, EDocumentPurchaseHeader); PurchaseHeader.Modify(); PurchCalcDiscByType.ApplyInvDiscBasedOnAmt(EDocumentPurchaseHeader."Total Discount", PurchaseHeader); exit(PurchaseHeader); diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocPurchDocHelper.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocPurchDocHelper.Codeunit.al index 857e73d309..3c2c8398d1 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocPurchDocHelper.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/FinishDraft/EDocPurchDocHelper.Codeunit.al @@ -7,7 +7,9 @@ namespace Microsoft.eServices.EDocument.Processing.Import; using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing; using Microsoft.eServices.EDocument.Processing.Import.Purchase; +using Microsoft.Finance.Currency; using Microsoft.Finance.Dimension; +using Microsoft.Finance.VAT.Setup; using Microsoft.Foundation.Attachment; using Microsoft.Purchases.Document; using Microsoft.Purchases.Posting; @@ -40,6 +42,9 @@ codeunit 6402 "E-Doc. Purch. Doc. Helper" PurchaseLine."Variant Code" := EDocumentPurchaseLine."[BC] Variant Code"; PurchaseLine.Type := EDocumentPurchaseLine."[BC] Purchase Line Type"; ValidateFieldWithContext(PurchaseLine, PurchaseLine.FieldNo("No."), EDocumentPurchaseLine."[BC] Purchase Type No."); + if EDocumentPurchaseLine."[BC] VAT Prod. Posting Group" <> '' then + ValidateFieldWithContext( + PurchaseLine, PurchaseLine.FieldNo("VAT Prod. Posting Group"), EDocumentPurchaseLine."[BC] VAT Prod. Posting Group"); if (PurchaseLine.Type = PurchaseLine.Type::"G/L Account") and HasTotalDiscount then ValidateFieldWithContext(PurchaseLine, PurchaseLine.FieldNo("Allow Invoice Disc."), true); PurchaseLine.Description := EDocumentPurchaseLine.Description; @@ -154,6 +159,43 @@ codeunit 6402 "E-Doc. Purch. Doc. Helper" EDocImpSessionTelemetry.SetBool('Totals Validation', TryValidateDocumentTotals(PurchaseHeader)); end; + procedure ApplyVATDifferenceToLines(PurchaseHeader: Record "Purchase Header"; EDocumentPurchaseHeader: Record "E-Document Purchase Header") + var + PurchaseLine: Record "Purchase Line"; + Currency: Record Currency; + LineAmount: Decimal; + TotalLineAmount, VATDiffRemainder, VATDiffForLine : Decimal; + begin + if EDocumentPurchaseHeader."Applied VAT Amount Diff." = 0 then + exit; + + TotalLineAmount := ComputeTotalLineAmount(EDocumentPurchaseHeader."E-Document Entry No."); + if TotalLineAmount = 0 then + exit; + + if PurchaseHeader."Currency Code" = '' then + Currency.InitRoundingPrecision() + else + Currency.Get(PurchaseHeader."Currency Code"); + + PurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type"); + PurchaseLine.SetRange("Document No.", PurchaseHeader."No."); + PurchaseLine.SetFilter(Type, '<>%1', PurchaseLine.Type::" "); + if not PurchaseLine.FindSet() then + exit; + + VATDiffRemainder := 0; + repeat + LineAmount := PurchaseLine."Line Amount" - PurchaseLine."Inv. Discount Amount"; + if LineAmount <> 0 then begin + VATDiffForLine := VATDiffRemainder + EDocumentPurchaseHeader."Applied VAT Amount Diff." * LineAmount / TotalLineAmount; + PurchaseLine."VAT Difference" := Round(VATDiffForLine, Currency."Amount Rounding Precision"); + VATDiffRemainder := VATDiffForLine - PurchaseLine."VAT Difference"; + PurchaseLine.Modify(); + end; + until PurchaseLine.Next() = 0; + end; + procedure RevertCreatedDocument(EDocument: Record "E-Document") var PurchaseHeader: Record "Purchase Header"; @@ -181,4 +223,25 @@ codeunit 6402 "E-Doc. Purch. Doc. Helper" exit; PurchaseHeader.Validate("Posting Date", EDocumentPurchaseHeader."Document Date"); end; + + procedure SetNormalReverseChargeFilter(var VATPostingSetup: Record "VAT Posting Setup"; VATBusPostingGroup: Code[20]) + begin + VATPostingSetup.SetRange("VAT Bus. Posting Group", VATBusPostingGroup); + VATPostingSetup.SetFilter("VAT Calculation Type", '%1|%2', + VATPostingSetup."VAT Calculation Type"::"Normal VAT", + VATPostingSetup."VAT Calculation Type"::"Reverse Charge VAT"); + end; + + local procedure ComputeTotalLineAmount(EDocEntryNo: Integer): Decimal + var + EDocPurchLine: Record "E-Document Purchase Line"; + TotalLineAmount: Decimal; + begin + EDocPurchLine.SetRange("E-Document Entry No.", EDocEntryNo); + if EDocPurchLine.FindSet() then + repeat + TotalLineAmount += Round(EDocPurchLine.Quantity * EDocPurchLine."Unit Price" - EDocPurchLine."Total Discount"); + until EDocPurchLine.Next() = 0; + exit(TotalLineAmount); + end; } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/PrepareDraft/EDocPreparePurchDraft.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/PrepareDraft/EDocPreparePurchDraft.Codeunit.al index 23612228b1..89e7d6a358 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/PrepareDraft/EDocPreparePurchDraft.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/PrepareDraft/EDocPreparePurchDraft.Codeunit.al @@ -8,8 +8,11 @@ using Microsoft.eServices.EDocument; using Microsoft.eServices.EDocument.Processing.AI; using Microsoft.eServices.EDocument.Processing.Import.Purchase; using Microsoft.eServices.EDocument.Processing.Interfaces; +using Microsoft.Finance.GeneralLedger.Setup; +using Microsoft.Finance.VAT.Setup; using Microsoft.Foundation.UOM; using Microsoft.Purchases.Document; +using Microsoft.Purchases.Setup; using Microsoft.Purchases.Vendor; using System.Log; @@ -36,6 +39,9 @@ codeunit 6406 "EDoc Prepare Purch. Draft" IUnitOfMeasureProvider: Interface IUnitOfMeasureProvider; IPurchaseLineProvider: Interface IPurchaseLineProvider; IPurchaseOrderProvider: Interface IPurchaseOrderProvider; + LineAmount: Decimal; + LineVATAmount: Decimal; + TotalLineVATAmount: Decimal; begin IUnitOfMeasureProvider := EDocImportParameters."Processing Customizations"; IPurchaseLineProvider := EDocImportParameters."Processing Customizations"; @@ -72,16 +78,26 @@ codeunit 6406 "EDoc Prepare Purch. Draft" EDocumentPurchaseLine.Modify(); until EDocumentPurchaseLine.Next() = 0; + // Resolve VAT Product Posting Groups from extracted VAT rates + ResolveVATProductPostingGroups(EDocument."Entry No", EDocumentPurchaseHeader); + CopilotLineMatching(EDocument."Entry No"); end; Clear(EDocumentPurchaseLine); EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocument."Entry No"); + TotalLineVATAmount := 0; if EDocumentPurchaseLine.FindSet() then repeat + LineAmount := Round(EDocumentPurchaseLine.Quantity * EDocumentPurchaseLine."Unit Price" - EDocumentPurchaseLine."Total Discount"); + LineVATAmount := Round(LineAmount * EDocumentPurchaseLine."VAT Rate" / 100); + TotalLineVATAmount += LineVATAmount; EDocImpSessionTelemetry.SetLine(EDocumentPurchaseLine.SystemId); until EDocumentPurchaseLine.Next() = 0; + ComputeAndApplyVATAmountDifference(EDocumentPurchaseHeader, TotalLineVATAmount); + EDocumentPurchaseHeader.Modify(); + LogAllActivitySessionChanges(EDocActivityLogSession); if EDocActivityLogSession.EndSession() then; @@ -138,6 +154,67 @@ codeunit 6406 "EDoc Prepare Purch. Draft" ActivityLog.Log(); end; + local procedure ResolveVATProductPostingGroups(EDocumentEntryNo: Integer; EDocumentPurchaseHeader: Record "E-Document Purchase Header") + var + PurchasesPayablesSetup: Record "Purchases & Payables Setup"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + Vendor: Record Vendor; + VATRate: Decimal; + LineCount: Integer; + begin + if not PurchasesPayablesSetup.Get() then + exit; + if not PurchasesPayablesSetup."Resolve VAT Group Purch EDoc" then + exit; + Vendor := EDocumentPurchaseHeader.GetBCVendor(); + if Vendor."No." = '' then + exit; + if Vendor."VAT Bus. Posting Group" = '' then + exit; + + EDocumentPurchaseLine.SetRange("E-Document Entry No.", EDocumentEntryNo); + LineCount := EDocumentPurchaseLine.Count(); + if LineCount = 0 then + exit; + + if EDocumentPurchaseLine.FindSet() then + repeat + VATRate := EDocumentPurchaseLine."VAT Rate"; + + // Single-line fallback: compute from header Total VAT + if (VATRate = 0) and (LineCount = 1) and + (EDocumentPurchaseHeader."Total VAT" > 0) and (EDocumentPurchaseHeader."Sub Total" > 0) + then + VATRate := Round((EDocumentPurchaseHeader."Total VAT" / EDocumentPurchaseHeader."Sub Total") * 100, 0.01); + + if VATRate > 0 then begin + EDocumentPurchaseLine."[BC] VAT Prod. Posting Group" := + FindVATProductPostingGroup(Vendor."VAT Bus. Posting Group", VATRate); + EDocumentPurchaseLine."[BC] VAT Rate Mismatch" := + EDocumentPurchaseLine."[BC] VAT Prod. Posting Group" = ''; + EDocumentPurchaseLine.Modify(); + if EDocumentPurchaseLine."[BC] VAT Rate Mismatch" then + EDocumentPurchaseLine.LogVATRateMismatch(Vendor."VAT Bus. Posting Group", VATRate) + else + EDocumentPurchaseLine.LogVATRateResolved(Vendor."VAT Bus. Posting Group", VATRate); + end; + until EDocumentPurchaseLine.Next() = 0; + end; + + local procedure FindVATProductPostingGroup(VATBusPostingGroup: Code[20]; VATRate: Decimal): Code[20] + var + VATPostingSetup: Record "VAT Posting Setup"; + EDocPurchDocHelper: Codeunit "E-Doc. Purch. Doc. Helper"; + begin + EDocPurchDocHelper.SetNormalReverseChargeFilter(VATPostingSetup, VATBusPostingGroup); + VATPostingSetup.SetRange("VAT %", VATRate); + if VATPostingSetup.Count() = 1 then begin + VATPostingSetup.FindFirst(); + exit(VATPostingSetup."VAT Prod. Posting Group"); + end; + exit(''); + end; + local procedure CopilotLineMatching(EDocumentEntryNo: Integer) var EDocumentPurchaseLine: Record "E-Document Purchase Line"; @@ -172,4 +249,68 @@ codeunit 6406 "EDoc Prepare Purch. Draft" if Codeunit.Run(Codeunit::"E-Doc. Deferral Matching", EDocumentPurchaseLine) then; end; end; + + local procedure ComputeAndApplyVATAmountDifference(var EDocumentPurchaseHeader: Record "E-Document Purchase Header"; TotalLineVATAmount: Decimal) + var + PurchasesPayablesSetup: Record "Purchases & Payables Setup"; + GeneralLedgerSetup: Record "General Ledger Setup"; + ActivityLog: Codeunit "Activity Log Builder"; + VATAmountDiff: Decimal; + Reasoning: Text[250]; + VATDiffAppliedLbl: Label 'Applied VAT amount difference of %1 to reconcile document Total VAT %2 with computed Total Line VAT Amount %3.', Comment = '%1 = VAT difference, %2 = Total VAT, %3 = Total Line VAT Amount'; + VATDiffSkippedSetupLbl: Label 'VAT amount difference of %1 was not applied because Apply VAT Diff. For Purch. E-Doc. is disabled in Purchases & Payables Setup.', Comment = '%1 = VAT difference'; + VATDiffSkippedAllowLbl: Label 'VAT amount difference of %1 was not applied because Allow VAT Difference is disabled in Purchases & Payables Setup.', Comment = '%1 = VAT difference'; + VATDiffSkippedMaxLbl: Label 'VAT amount difference of %1 was not applied because it exceeds the Max. VAT Difference Allowed of %2 in General Ledger Setup.', Comment = '%1 = VAT difference, %2 = Max. VAT Difference Allowed'; + begin + EDocumentPurchaseHeader."Applied VAT Amount Diff." := 0; + + if (EDocumentPurchaseHeader."Total VAT" = 0) or (TotalLineVATAmount = EDocumentPurchaseHeader."Total VAT") then + exit; + + VATAmountDiff := EDocumentPurchaseHeader."Total VAT" - TotalLineVATAmount; + + if not PurchasesPayablesSetup.Get() then + exit; + + if not PurchasesPayablesSetup."Apply VAT Diff. For Purch EDoc" then begin + Reasoning := CopyStr(StrSubstNo(VATDiffSkippedSetupLbl, VATAmountDiff), 1, MaxStrLen(Reasoning)); + ActivityLog + .Init(Database::"E-Document Purchase Header", EDocumentPurchaseHeader.FieldNo("Applied VAT Amount Diff."), EDocumentPurchaseHeader.SystemId) + .SetExplanation(Reasoning) + .SetType(Enum::"Activity Log Type"::"AL") + .Log(); + exit; + end; + + if not PurchasesPayablesSetup."Allow VAT Difference" then begin + Reasoning := CopyStr(StrSubstNo(VATDiffSkippedAllowLbl, VATAmountDiff), 1, MaxStrLen(Reasoning)); + ActivityLog + .Init(Database::"E-Document Purchase Header", EDocumentPurchaseHeader.FieldNo("Applied VAT Amount Diff."), EDocumentPurchaseHeader.SystemId) + .SetExplanation(Reasoning) + .SetType(Enum::"Activity Log Type"::"AL") + .Log(); + exit; + end; + + if not GeneralLedgerSetup.Get() then + exit; + if Abs(VATAmountDiff) > GeneralLedgerSetup."Max. VAT Difference Allowed" then begin + Reasoning := CopyStr(StrSubstNo(VATDiffSkippedMaxLbl, VATAmountDiff, GeneralLedgerSetup."Max. VAT Difference Allowed"), 1, MaxStrLen(Reasoning)); + ActivityLog + .Init(Database::"E-Document Purchase Header", EDocumentPurchaseHeader.FieldNo("Applied VAT Amount Diff."), EDocumentPurchaseHeader.SystemId) + .SetExplanation(Reasoning) + .SetType(Enum::"Activity Log Type"::"AL") + .Log(); + exit; + end; + + EDocumentPurchaseHeader."Applied VAT Amount Diff." := VATAmountDiff; + + Reasoning := CopyStr(StrSubstNo(VATDiffAppliedLbl, VATAmountDiff, EDocumentPurchaseHeader."Total VAT", TotalLineVATAmount), 1, MaxStrLen(Reasoning)); + ActivityLog + .Init(Database::"E-Document Purchase Header", EDocumentPurchaseHeader.FieldNo("Applied VAT Amount Diff."), EDocumentPurchaseHeader.SystemId) + .SetExplanation(Reasoning) + .SetType(Enum::"Activity Log Type"::"AL") + .Log(); + end; } 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 a49e0ab6aa..62abbe3835 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 @@ -71,6 +71,11 @@ page 6183 "E-Doc. Purchase Draft Subform" Lookup = true; ShowMandatory = true; } + field("VAT Prod. Posting Group"; Rec."[BC] VAT Prod. Posting Group") + { + ApplicationArea = All; + Lookup = true; + } field("Item Reference No."; Rec."[BC] Item Reference No.") { ApplicationArea = All; @@ -564,5 +569,4 @@ page 6183 "E-Doc. Purchase Draft Subform" if WarningDetails.Length() > 0 then Message(WarningDetails.ToText()); end; - } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al index 167ad97160..24de204354 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseDraft.Page.al @@ -200,6 +200,14 @@ page 6181 "E-Document Purchase Draft" group("E-Document Details") { ShowCaption = false; + field("Applied VAT Amount Diff."; EDocumentPurchaseHeader."Applied VAT Amount Diff.") + { + Caption = 'Applied VAT Amount Diff.'; + ToolTip = 'Specifies the VAT amount difference that was automatically applied to reconcile the document total VAT with the computed line VAT amounts.'; + Importance = Additional; + Editable = false; + Visible = EDocumentPurchaseHeader."Applied VAT Amount Diff." <> 0; + } field("Amount Excl. VAT"; EDocumentPurchaseHeader."Sub Total") { Caption = 'Amount Excl. VAT'; diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al index 67dcfbdc46..9a05d6ddd2 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseHeader.Table.al @@ -234,6 +234,14 @@ table 6100 "E-Document Purchase Header" Caption = 'Vendor Invoice No.'; DataClassification = CustomerContent; } + field(44; "Applied VAT Amount Diff."; Decimal) + { + Caption = 'Applied VAT Amount Diff.'; + DataClassification = CustomerContent; + AutoFormatType = 1; + AutoFormatExpression = Rec."Currency Code"; + Editable = false; + } #endregion Purchase fields #region Business Central Data - Validated fields [101-200] diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseLine.Table.al b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseLine.Table.al index df4c126db9..417a15c735 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseLine.Table.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/Purchase/EDocumentPurchaseLine.Table.al @@ -11,6 +11,7 @@ using Microsoft.Finance.AllocationAccount; using Microsoft.Finance.Deferral; using Microsoft.Finance.Dimension; using Microsoft.Finance.GeneralLedger.Account; +using Microsoft.Finance.VAT.Setup; using Microsoft.FixedAssets.FixedAsset; using Microsoft.Foundation.UOM; using Microsoft.Inventory.Item; @@ -20,6 +21,7 @@ using Microsoft.Purchases.Document; using Microsoft.Purchases.History; using Microsoft.Purchases.Vendor; using Microsoft.Utilities; +using System.Log; using System.Reflection; using System.Utilities; @@ -208,6 +210,54 @@ table 6101 "E-Document Purchase Line" DimMgt.UpdateGlobalDimFromDimSetID("[BC] Dimension Set ID", "[BC] Shortcut Dimension 1 Code", "[BC] Shortcut Dimension 2 Code"); end; } + field(110; "[BC] VAT Prod. Posting Group"; Code[20]) + { + Caption = 'VAT Prod. Posting Group'; + ToolTip = 'Specifies the VAT product posting group resolved from the extracted VAT rate.'; + TableRelation = "VAT Product Posting Group"; + + trigger OnValidate() + var + Vendor: Record Vendor; + VATPostingSetup: Record "VAT Posting Setup"; + begin + if "[BC] VAT Prod. Posting Group" = '' then begin + "[BC] VAT Rate Mismatch" := true; + exit; + end; + Vendor := Rec.GetBCVendor(); + if Vendor."No." = '' then + exit; + if VATPostingSetup.Get(Vendor."VAT Bus. Posting Group", "[BC] VAT Prod. Posting Group") then begin + if not (VATPostingSetup."VAT Calculation Type" in + [VATPostingSetup."VAT Calculation Type"::"Normal VAT", + VATPostingSetup."VAT Calculation Type"::"Reverse Charge VAT"]) + then + exit; + "[BC] VAT Rate Mismatch" := VATPostingSetup."VAT %" <> "VAT Rate"; + end else + "[BC] VAT Rate Mismatch" := true; + end; + + trigger OnLookup() + var + Vendor: Record Vendor; + VATPostingSetup: Record "VAT Posting Setup"; + EDocPurchDocHelper: Codeunit "E-Doc. Purch. Doc. Helper"; + begin + Vendor := Rec.GetBCVendor(); + if Vendor."No." = '' then + exit; + EDocPurchDocHelper.SetNormalReverseChargeFilter(VATPostingSetup, Vendor."VAT Bus. Posting Group"); + if Page.RunModal(Page::"VAT Posting Setup", VATPostingSetup) = Action::LookupOK then + Validate("[BC] VAT Prod. Posting Group", VATPostingSetup."VAT Prod. Posting Group"); + end; + } + field(111; "[BC] VAT Rate Mismatch"; Boolean) + { + Caption = 'VAT Rate Mismatch'; + ToolTip = 'Specifies whether the VAT Product Posting Group could not be resolved from the extracted VAT rate.'; + } #endregion Validated fields #region Metadata fields [201-300] @@ -374,4 +424,56 @@ table 6101 "E-Document Purchase Line" exit(GetEDocumentPurchaseHeader().GetBCVendor()); end; + internal procedure LogVATRateMismatch(VendVATBusPostingGroupCode: Code[20]; VATRate: Decimal) + var + VATPostingSetup: Record "VAT Posting Setup"; + EDocPurchDocHelper: Codeunit "E-Doc. Purch. Doc. Helper"; + ActivityLog: Codeunit "Activity Log Builder"; + VATPostingSetupRef: RecordRef; + Reasoning: Text[250]; + VATRateMismatchReasonLbl: Label 'VAT rate %1% extracted from the document could not be matched to a VAT Posting Setup for vendor''s VAT Business Posting Group %2.', Comment = '%1 = extracted VAT rate %, %2 = VAT Bus. Posting Group code'; + VATRateMismatchTitleLbl: Label 'VAT Posting Setup for %1', Comment = '%1 = VAT Bus. Posting Group code'; + begin + if not Rec."[BC] VAT Rate Mismatch" then + exit; + EDocPurchDocHelper.SetNormalReverseChargeFilter(VATPostingSetup, VendVATBusPostingGroupCode); + VATPostingSetupRef.GetTable(VATPostingSetup); + + Reasoning := CopyStr(StrSubstNo(VATRateMismatchReasonLbl, Rec."VAT Rate", VendVATBusPostingGroupCode), 1, MaxStrLen(Reasoning)); + + ActivityLog + .Init(Database::"E-Document Purchase Line", Rec.FieldNo("[BC] VAT Prod. Posting Group"), Rec.SystemId) + .SetExplanation(Reasoning) + .SetType(Enum::"Activity Log Type"::AL) + .SetReferenceSource(Page::"VAT Posting Setup", VATPostingSetupRef) + .SetReferenceTitle(StrSubstNo(VATRateMismatchTitleLbl, VendVATBusPostingGroupCode)) + .Log(); + end; + + internal procedure LogVATRateResolved(VendVATBusPostingGroupCode: Code[20]; VATRate: Decimal) + var + VATPostingSetup: Record "VAT Posting Setup"; + EDocPurchDocHelper: Codeunit "E-Doc. Purch. Doc. Helper"; + ActivityLog: Codeunit "Activity Log Builder"; + VATPostingSetupRef: RecordRef; + Reasoning: Text[250]; + VATRateResolvedReasonLbl: Label 'VAT rate %1% extracted from the document was matched to VAT Product Posting Group %2 for vendor''s VAT Business Posting Group %3.', Comment = '%1 = extracted VAT rate %, %2 = resolved VAT Prod. Posting Group code, %3 = VAT Bus. Posting Group code'; + VATRateResolvedTitleLbl: Label 'VAT Posting Setup for %1', Comment = '%1 = VAT Bus. Posting Group code'; + begin + if Rec."[BC] VAT Rate Mismatch" then + exit; + EDocPurchDocHelper.SetNormalReverseChargeFilter(VATPostingSetup, VendVATBusPostingGroupCode); + VATPostingSetupRef.GetTable(VATPostingSetup); + + Reasoning := CopyStr(StrSubstNo(VATRateResolvedReasonLbl, VATRate, Rec."[BC] VAT Prod. Posting Group", VendVATBusPostingGroupCode), 1, MaxStrLen(Reasoning)); + + ActivityLog + .Init(Database::"E-Document Purchase Line", Rec.FieldNo("[BC] VAT Prod. Posting Group"), Rec.SystemId) + .SetExplanation(Reasoning) + .SetType(Enum::"Activity Log Type"::AL) + .SetReferenceSource(Page::"VAT Posting Setup", VATPostingSetupRef) + .SetReferenceTitle(StrSubstNo(VATRateResolvedTitleLbl, VendVATBusPostingGroupCode)) + .Log(); + end; + } diff --git a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al index 52ca55a954..ddc548d7f5 100644 --- a/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al +++ b/src/Apps/W1/EDocument/App/src/Processing/Import/StructureReceivedEDocument/EDocumentADIHandler.Codeunit.al @@ -182,9 +182,60 @@ codeunit 6174 "E-Document ADI Handler" implements IStructureReceivedEDocument, I EDocumentJsonHelper.SetStringValueInField('productCode', MaxStrLen(TempEDocPurchaseLine."Product Code"), FieldsJsonObject, TempEDocPurchaseLine."Product Code"); EDocumentJsonHelper.SetStringValueInField('unit', MaxStrLen(TempEDocPurchaseLine."Unit of Measure"), FieldsJsonObject, TempEDocPurchaseLine."Unit of Measure"); EDocumentJsonHelper.SetDateValueInField('date', FieldsJsonObject, TempEDocPurchaseLine.Date); - EDocumentJsonHelper.SetCurrencyValueInField('tax', FieldsJsonObject, TempEDocPurchaseLine."VAT Rate", TempEDocPurchaseLine."Currency Code"); + ResolveVATRateFromADI(FieldsJsonObject, TempEDocPurchaseLine); if TempEDocPurchaseLine."Unit Price" <> 0 then TempEDocPurchaseLine."Total Discount" := (TempEDocPurchaseLine."Unit Price" * TempEDocPurchaseLine.Quantity) - TempEDocPurchaseLine."Sub Total"; end; + + local procedure ResolveVATRateFromADI(FieldsJsonObject: JsonObject; var TempEDocPurchaseLine: Record "E-Document Purchase Line" temporary) + var + TaxRateText: Text; + TaxAmount: Decimal; + TaxValueText: Text; + ParsedRate: Decimal; + UnusedCurrencyCode: Code[10]; + begin + // 1. Prefer TaxRate (string) — unambiguous percentage field from ADI + EDocumentJsonHelper.SetStringValueInField('taxRate', MaxStrLen(TaxRateText), FieldsJsonObject, TaxRateText); + if TaxRateText <> '' then begin + ParsedRate := ParsePercentageFromText(TaxRateText); + if ParsedRate >= 0 then begin + TempEDocPurchaseLine."VAT Rate" := ParsedRate; + exit; + end; + end; + + // 2. Fallback to Tax field — but it's ambiguous (can be amount, %, or Y/N) + EDocumentJsonHelper.SetCurrencyValueInField('tax', FieldsJsonObject, TaxAmount, UnusedCurrencyCode); + if TaxAmount = 0 then + exit; + + // Check value_text to disambiguate + EDocumentJsonHelper.SetStringValueInField('tax', MaxStrLen(TaxValueText), FieldsJsonObject, TaxValueText); + if TaxValueText.Contains('%') then + // Tax field contains a percentage (e.g., "20%") + TempEDocPurchaseLine."VAT Rate" := TaxAmount + else + // Tax field contains a monetary amount (e.g., "$6.00") — compute percentage + if TempEDocPurchaseLine."Sub Total" > 0 then + TempEDocPurchaseLine."VAT Rate" := Round((TaxAmount / TempEDocPurchaseLine."Sub Total") * 100, 0.01); + end; + + local procedure ParsePercentageFromText(TaxRateText: Text): Decimal + var + CleanedText: Text; + ParsedValue: Decimal; + begin + // Strip common non-numeric prefixes/suffixes: "VAT 20%", "20%", "20.0%", "20" + CleanedText := TaxRateText.Replace('%', '').Trim(); + // Remove common prefixes like "VAT ", "Tax ", etc. + if CleanedText.StartsWith('VAT ') then + CleanedText := CopyStr(CleanedText, 5).Trim(); + if CleanedText.StartsWith('Tax ') then + CleanedText := CopyStr(CleanedText, 5).Trim(); + if Evaluate(ParsedValue, CleanedText) then + exit(ParsedValue); + exit(-1); // Signal parse failure + end; #pragma warning restore AA0139 } diff --git a/src/Apps/W1/EDocument/Test/src/Processing/EDocPurchVATTests.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Processing/EDocPurchVATTests.Codeunit.al new file mode 100644 index 0000000000..490c7db126 --- /dev/null +++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocPurchVATTests.Codeunit.al @@ -0,0 +1,690 @@ +// ------------------------------------------------------------------------------------------------ +// 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.Test; + +using Microsoft.eServices.EDocument; +using Microsoft.eServices.EDocument.Integration; +using Microsoft.eServices.EDocument.Processing.Import; +using Microsoft.eServices.EDocument.Processing.Import.Purchase; +using Microsoft.Finance.Currency; +using Microsoft.Finance.GeneralLedger.Setup; +using Microsoft.Finance.VAT.Setup; +using Microsoft.Foundation.Company; +using Microsoft.Purchases.History; +using Microsoft.Purchases.Payables; +using Microsoft.Purchases.Setup; +using Microsoft.Purchases.Vendor; +using Microsoft.Sales.Customer; +using System.IO; +using System.TestLibraries.Utilities; + +codeunit 139897 "E-Doc Purch. VAT Tests" +{ + Subtype = Test; + TestType = IntegrationTest; + + var + Vendor: Record Vendor; + Customer: Record Customer; + EDocumentService: Record "E-Document Service"; + Assert: Codeunit Assert; + LibraryVariableStorage: Codeunit "Library - Variable Storage"; + LibraryEDoc: Codeunit "Library - E-Document"; + EDocImplState: Codeunit "E-Doc. Impl. State"; + LibraryLowerPermission: Codeunit "Library - Lower Permissions"; + LibrarySetupStorage: Codeunit "Library - Setup Storage"; + IsInitialized: Boolean; + + [Test] + procedure PreparingPurchaseDraftResolvesVATProductPostingGroupFromLineVATRate() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + TempEDocImportParameters: Record "E-Doc. Import Parameters"; + Vendor2: Record Vendor; + CompanyInformation: Record "Company Information"; + VATPostingSetup2: Record "VAT Posting Setup"; + VATProductPostingGroup: Record "VAT Product Posting Group"; + EDocumentProcessing: Codeunit "E-Document Processing"; + EDocImport: Codeunit "E-Doc. Import"; + LibraryERM: Codeunit "Library - ERM"; + begin + // [SCENARIO] When a draft line has a VAT Rate and a matching VAT Posting Setup exists, Prepare Draft resolves the VAT Prod. Posting Group + Initialize(Enum::"Service Integration"::"Mock"); + SetResolveVATProductGroupInPurchSetup(true); + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + // [GIVEN] A vendor with a known VAT Bus. Posting Group + CompanyInformation.GetRecordOnce(); + Vendor2."Country/Region Code" := CompanyInformation."Country/Region Code"; + Vendor2."No." := 'EDOC001'; + Vendor2."VAT Registration No." := 'XXXXXXX001'; + Vendor2."VAT Bus. Posting Group" := Vendor."VAT Bus. Posting Group"; + Vendor2.Insert(); + + // [GIVEN] A VAT Posting Setup with VAT % = 10 for the vendor's bus posting group + LibraryERM.CreateVATProductPostingGroup(VATProductPostingGroup); + VATPostingSetup2."VAT Bus. Posting Group" := Vendor2."VAT Bus. Posting Group"; + VATPostingSetup2."VAT Prod. Posting Group" := VATProductPostingGroup.Code; + VATPostingSetup2."VAT Calculation Type" := VATPostingSetup2."VAT Calculation Type"::"Normal VAT"; + VATPostingSetup2."VAT %" := 10; + VATPostingSetup2."Sales VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2."Purchase VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2.Insert(); + + // [GIVEN] E-Document purchase header and line with VAT Rate = 10 + EDocumentPurchaseHeader."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseHeader."Vendor VAT Id" := Vendor2."VAT Registration No."; + EDocumentPurchaseHeader.Insert(); + EDocumentPurchaseLine."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseLine.Description := 'Test VAT resolution'; + EDocumentPurchaseLine."VAT Rate" := 10; + EDocumentPurchaseLine.Insert(); + + // [WHEN] Prepare Draft is run + EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Ready for draft"); + TempEDocImportParameters."Step to Run" := "Import E-Document Steps"::"Prepare draft"; + EDocImport.ProcessIncomingEDocument(EDocument, TempEDocImportParameters); + + // [THEN] The VAT Prod. Posting Group is resolved from the matching setup + EDocumentPurchaseLine.SetRecFilter(); + EDocumentPurchaseLine.FindFirst(); + Assert.AreEqual(VATProductPostingGroup.Code, EDocumentPurchaseLine."[BC] VAT Prod. Posting Group", 'The VAT Prod. Posting Group should be resolved from the matching VAT Posting Setup.'); + Assert.IsFalse(EDocumentPurchaseLine."[BC] VAT Rate Mismatch", 'VAT Rate Mismatch should be false when resolution succeeds.'); + + // Cleanup + Vendor2.SetRecFilter(); + Vendor2.Delete(); + VATPostingSetup2.SetRecFilter(); + VATPostingSetup2.Delete(); + VATProductPostingGroup.SetRecFilter(); + VATProductPostingGroup.Delete(); + end; + + [Test] + procedure PreparingPurchaseDraftSetsVATRateMismatchWhenNoMatchingVATSetup() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + TempEDocImportParameters: Record "E-Doc. Import Parameters"; + Vendor2: Record Vendor; + CompanyInformation: Record "Company Information"; + EDocumentProcessing: Codeunit "E-Document Processing"; + EDocImport: Codeunit "E-Doc. Import"; + begin + // [SCENARIO] When a draft line has a VAT Rate but no matching VAT Posting Setup exists, Prepare Draft leaves the field blank and sets the mismatch flag + Initialize(Enum::"Service Integration"::"Mock"); + SetResolveVATProductGroupInPurchSetup(true); + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + // [GIVEN] A vendor with a known VAT Bus. Posting Group + CompanyInformation.GetRecordOnce(); + Vendor2."Country/Region Code" := CompanyInformation."Country/Region Code"; + Vendor2."No." := 'EDOC001'; + Vendor2."VAT Registration No." := 'XXXXXXX001'; + Vendor2."VAT Bus. Posting Group" := Vendor."VAT Bus. Posting Group"; + Vendor2.Insert(); + + // [GIVEN] E-Document purchase header and line with VAT Rate = 99 (no matching setup) + EDocumentPurchaseHeader."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseHeader."Vendor VAT Id" := Vendor2."VAT Registration No."; + EDocumentPurchaseHeader.Insert(); + EDocumentPurchaseLine."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseLine.Description := 'Test VAT mismatch'; + EDocumentPurchaseLine."VAT Rate" := 99; + EDocumentPurchaseLine.Insert(); + + // [WHEN] Prepare Draft is run + EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Ready for draft"); + TempEDocImportParameters."Step to Run" := "Import E-Document Steps"::"Prepare draft"; + EDocImport.ProcessIncomingEDocument(EDocument, TempEDocImportParameters); + + // [THEN] The VAT Prod. Posting Group is blank and mismatch flag is set + EDocumentPurchaseLine.SetRecFilter(); + EDocumentPurchaseLine.FindFirst(); + Assert.AreEqual('', EDocumentPurchaseLine."[BC] VAT Prod. Posting Group", 'The VAT Prod. Posting Group should be blank when no matching VAT Posting Setup exists.'); + Assert.IsTrue(EDocumentPurchaseLine."[BC] VAT Rate Mismatch", 'VAT Rate Mismatch should be true when resolution fails.'); + + // Cleanup + Vendor2.SetRecFilter(); + Vendor2.Delete(); + end; + + [Test] + procedure PreparingDraftIgnoresFullVATSetupWhenResolvingPostingGroup() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + TempEDocImportParameters: Record "E-Doc. Import Parameters"; + Vendor2: Record Vendor; + CompanyInformation: Record "Company Information"; + VATPostingSetup2: Record "VAT Posting Setup"; + VATProductPostingGroup: Record "VAT Product Posting Group"; + EDocumentProcessing: Codeunit "E-Document Processing"; + EDocImport: Codeunit "E-Doc. Import"; + LibraryERM: Codeunit "Library - ERM"; + begin + // [SCENARIO] Full VAT setups must not be matched during VAT Posting Group resolution + Initialize(Enum::"Service Integration"::"Mock"); + SetResolveVATProductGroupInPurchSetup(true); + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + // [GIVEN] A vendor + CompanyInformation.GetRecordOnce(); + Vendor2."Country/Region Code" := CompanyInformation."Country/Region Code"; + Vendor2."No." := 'EDOC001'; + Vendor2."VAT Registration No." := 'XXXXXXX001'; + Vendor2."VAT Bus. Posting Group" := Vendor."VAT Bus. Posting Group"; + Vendor2.Insert(); + + // [GIVEN] A Full VAT Posting Setup with VAT % = 10 + LibraryERM.CreateVATProductPostingGroup(VATProductPostingGroup); + VATPostingSetup2."VAT Bus. Posting Group" := Vendor2."VAT Bus. Posting Group"; + VATPostingSetup2."VAT Prod. Posting Group" := VATProductPostingGroup.Code; + VATPostingSetup2."VAT Calculation Type" := VATPostingSetup2."VAT Calculation Type"::"Full VAT"; + VATPostingSetup2."VAT %" := 10; + VATPostingSetup2."Sales VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2."Purchase VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2.Insert(); + + // [GIVEN] E-Document line with VAT Rate = 10 + EDocumentPurchaseHeader."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseHeader."Vendor VAT Id" := Vendor2."VAT Registration No."; + EDocumentPurchaseHeader.Insert(); + EDocumentPurchaseLine."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseLine.Description := 'Test Full VAT ignored'; + EDocumentPurchaseLine."VAT Rate" := 10; + EDocumentPurchaseLine.Insert(); + + // [WHEN] Prepare Draft is run + EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Ready for draft"); + TempEDocImportParameters."Step to Run" := "Import E-Document Steps"::"Prepare draft"; + EDocImport.ProcessIncomingEDocument(EDocument, TempEDocImportParameters); + + // [THEN] Full VAT setup is not matched + EDocumentPurchaseLine.SetRecFilter(); + EDocumentPurchaseLine.FindFirst(); + Assert.AreEqual('', EDocumentPurchaseLine."[BC] VAT Prod. Posting Group", 'Full VAT setups must not be matched.'); + Assert.IsTrue(EDocumentPurchaseLine."[BC] VAT Rate Mismatch", 'VAT Rate Mismatch should be true when only Full VAT setups exist.'); + + // Cleanup + Vendor2.SetRecFilter(); + Vendor2.Delete(); + VATPostingSetup2.SetRecFilter(); + VATPostingSetup2.Delete(); + VATProductPostingGroup.SetRecFilter(); + VATProductPostingGroup.Delete(); + end; + + [Test] + procedure PreparingDraftIgnoresSalesTaxSetupWhenResolvingPostingGroup() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + TempEDocImportParameters: Record "E-Doc. Import Parameters"; + Vendor2: Record Vendor; + CompanyInformation: Record "Company Information"; + VATPostingSetup2: Record "VAT Posting Setup"; + VATProductPostingGroup: Record "VAT Product Posting Group"; + EDocumentProcessing: Codeunit "E-Document Processing"; + EDocImport: Codeunit "E-Doc. Import"; + LibraryERM: Codeunit "Library - ERM"; + begin + // [SCENARIO] Sales Tax setups must not be matched during VAT Posting Group resolution + Initialize(Enum::"Service Integration"::"Mock"); + SetResolveVATProductGroupInPurchSetup(true); + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + // [GIVEN] A vendor + CompanyInformation.GetRecordOnce(); + Vendor2."Country/Region Code" := CompanyInformation."Country/Region Code"; + Vendor2."No." := 'EDOC001'; + Vendor2."VAT Registration No." := 'XXXXXXX001'; + Vendor2."VAT Bus. Posting Group" := Vendor."VAT Bus. Posting Group"; + Vendor2.Insert(); + + // [GIVEN] A Sales Tax Posting Setup with VAT % = 10 + LibraryERM.CreateVATProductPostingGroup(VATProductPostingGroup); + VATPostingSetup2."VAT Bus. Posting Group" := Vendor2."VAT Bus. Posting Group"; + VATPostingSetup2."VAT Prod. Posting Group" := VATProductPostingGroup.Code; + VATPostingSetup2."VAT Calculation Type" := VATPostingSetup2."VAT Calculation Type"::"Sales Tax"; + VATPostingSetup2."VAT %" := 10; + VATPostingSetup2."Sales VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2."Purchase VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2.Insert(); + + // [GIVEN] E-Document line with VAT Rate = 10 + EDocumentPurchaseHeader."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseHeader."Vendor VAT Id" := Vendor2."VAT Registration No."; + EDocumentPurchaseHeader.Insert(); + EDocumentPurchaseLine."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseLine.Description := 'Test Sales Tax ignored'; + EDocumentPurchaseLine."VAT Rate" := 10; + EDocumentPurchaseLine.Insert(); + + // [WHEN] Prepare Draft is run + EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Ready for draft"); + TempEDocImportParameters."Step to Run" := "Import E-Document Steps"::"Prepare draft"; + EDocImport.ProcessIncomingEDocument(EDocument, TempEDocImportParameters); + + // [THEN] Sales Tax setup is not matched + EDocumentPurchaseLine.SetRecFilter(); + EDocumentPurchaseLine.FindFirst(); + Assert.AreEqual('', EDocumentPurchaseLine."[BC] VAT Prod. Posting Group", 'Sales Tax setups must not be matched.'); + Assert.IsTrue(EDocumentPurchaseLine."[BC] VAT Rate Mismatch", 'VAT Rate Mismatch should be true when only Sales Tax setups exist.'); + + // Cleanup + Vendor2.SetRecFilter(); + Vendor2.Delete(); + VATPostingSetup2.SetRecFilter(); + VATPostingSetup2.Delete(); + VATProductPostingGroup.SetRecFilter(); + VATProductPostingGroup.Delete(); + end; + + [Test] + procedure PreparingDraftResolvesReverseChargeVATPostingGroup() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + TempEDocImportParameters: Record "E-Doc. Import Parameters"; + Vendor2: Record Vendor; + CompanyInformation: Record "Company Information"; + VATPostingSetup2: Record "VAT Posting Setup"; + VATProductPostingGroup: Record "VAT Product Posting Group"; + EDocumentProcessing: Codeunit "E-Document Processing"; + EDocImport: Codeunit "E-Doc. Import"; + LibraryERM: Codeunit "Library - ERM"; + begin + // [SCENARIO] Reverse Charge VAT setups should be matched during VAT Posting Group resolution + Initialize(Enum::"Service Integration"::"Mock"); + SetResolveVATProductGroupInPurchSetup(true); + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + // [GIVEN] A vendor + CompanyInformation.GetRecordOnce(); + Vendor2."Country/Region Code" := CompanyInformation."Country/Region Code"; + Vendor2."No." := 'EDOC001'; + Vendor2."VAT Registration No." := 'XXXXXXX001'; + Vendor2."VAT Bus. Posting Group" := Vendor."VAT Bus. Posting Group"; + Vendor2.Insert(); + + // [GIVEN] A Reverse Charge VAT Posting Setup with VAT % = 20 + LibraryERM.CreateVATProductPostingGroup(VATProductPostingGroup); + VATPostingSetup2."VAT Bus. Posting Group" := Vendor2."VAT Bus. Posting Group"; + VATPostingSetup2."VAT Prod. Posting Group" := VATProductPostingGroup.Code; + VATPostingSetup2."VAT Calculation Type" := VATPostingSetup2."VAT Calculation Type"::"Reverse Charge VAT"; + VATPostingSetup2."VAT %" := 20; + VATPostingSetup2."Sales VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2."Purchase VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2."Reverse Chrg. VAT Acc." := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2.Insert(); + + // [GIVEN] E-Document line with VAT Rate = 20 + EDocumentPurchaseHeader."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseHeader."Vendor VAT Id" := Vendor2."VAT Registration No."; + EDocumentPurchaseHeader.Insert(); + EDocumentPurchaseLine."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseLine.Description := 'Test Reverse Charge resolved'; + EDocumentPurchaseLine."VAT Rate" := 20; + EDocumentPurchaseLine.Insert(); + + // [WHEN] Prepare Draft is run + EDocumentProcessing.ModifyEDocumentProcessingStatus(EDocument, "Import E-Doc. Proc. Status"::"Ready for draft"); + TempEDocImportParameters."Step to Run" := "Import E-Document Steps"::"Prepare draft"; + EDocImport.ProcessIncomingEDocument(EDocument, TempEDocImportParameters); + + // [THEN] Reverse Charge VAT setup is matched + EDocumentPurchaseLine.SetRecFilter(); + EDocumentPurchaseLine.FindFirst(); + Assert.AreEqual(VATProductPostingGroup.Code, EDocumentPurchaseLine."[BC] VAT Prod. Posting Group", 'Reverse Charge VAT setups should be matched.'); + Assert.IsFalse(EDocumentPurchaseLine."[BC] VAT Rate Mismatch", 'VAT Rate Mismatch should be false when Reverse Charge VAT matches.'); + + // Cleanup + Vendor2.SetRecFilter(); + Vendor2.Delete(); + VATPostingSetup2.SetRecFilter(); + VATPostingSetup2.Delete(); + VATProductPostingGroup.SetRecFilter(); + VATProductPostingGroup.Delete(); + end; + + [Test] + procedure ValidatingVATProdPostingGroupClearsMismatchWhenRateMatches() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + Vendor2: Record Vendor; + CompanyInformation: Record "Company Information"; + VATPostingSetup2: Record "VAT Posting Setup"; + VATProductPostingGroup: Record "VAT Product Posting Group"; + LibraryERM: Codeunit "Library - ERM"; + begin + // [SCENARIO] OnValidate clears mismatch when selected posting group's VAT % matches the line's VAT Rate + Initialize(Enum::"Service Integration"::"Mock"); + SetResolveVATProductGroupInPurchSetup(true); + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + // [GIVEN] A vendor + CompanyInformation.GetRecordOnce(); + Vendor2."Country/Region Code" := CompanyInformation."Country/Region Code"; + Vendor2."No." := 'EDOC001'; + Vendor2."VAT Registration No." := 'XXXXXXX001'; + Vendor2."VAT Bus. Posting Group" := Vendor."VAT Bus. Posting Group"; + Vendor2.Insert(); + + // [GIVEN] A Normal VAT setup with VAT % = 20 + LibraryERM.CreateVATProductPostingGroup(VATProductPostingGroup); + VATPostingSetup2."VAT Bus. Posting Group" := Vendor2."VAT Bus. Posting Group"; + VATPostingSetup2."VAT Prod. Posting Group" := VATProductPostingGroup.Code; + VATPostingSetup2."VAT Calculation Type" := VATPostingSetup2."VAT Calculation Type"::"Normal VAT"; + VATPostingSetup2."VAT %" := 20; + VATPostingSetup2."Sales VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2."Purchase VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2.Insert(); + + // [GIVEN] A line with VAT Rate = 20 and mismatch = true + EDocumentPurchaseHeader."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor2."No."; + EDocumentPurchaseHeader.Insert(); + EDocumentPurchaseLine."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseLine."VAT Rate" := 20; + EDocumentPurchaseLine."[BC] VAT Rate Mismatch" := true; + EDocumentPurchaseLine.Insert(); + + // [WHEN] User validates the posting group to the matching setup + EDocumentPurchaseLine.Validate("[BC] VAT Prod. Posting Group", VATProductPostingGroup.Code); + EDocumentPurchaseLine.Modify(); + + // [THEN] Mismatch is cleared + Assert.IsFalse(EDocumentPurchaseLine."[BC] VAT Rate Mismatch", 'Mismatch should be false when VAT % matches VAT Rate.'); + + // Cleanup + Vendor2.SetRecFilter(); + Vendor2.Delete(); + VATPostingSetup2.SetRecFilter(); + VATPostingSetup2.Delete(); + VATProductPostingGroup.SetRecFilter(); + VATProductPostingGroup.Delete(); + end; + + [Test] + procedure ValidatingVATProdPostingGroupKeepsMismatchWhenRateDiffers() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + Vendor2: Record Vendor; + CompanyInformation: Record "Company Information"; + VATPostingSetup2: Record "VAT Posting Setup"; + VATProductPostingGroup: Record "VAT Product Posting Group"; + LibraryERM: Codeunit "Library - ERM"; + begin + // [SCENARIO] OnValidate keeps mismatch when selected posting group's VAT % differs from VAT Rate + Initialize(Enum::"Service Integration"::"Mock"); + SetResolveVATProductGroupInPurchSetup(true); + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + // [GIVEN] A vendor + CompanyInformation.GetRecordOnce(); + Vendor2."Country/Region Code" := CompanyInformation."Country/Region Code"; + Vendor2."No." := 'EDOC001'; + Vendor2."VAT Registration No." := 'XXXXXXX001'; + Vendor2."VAT Bus. Posting Group" := Vendor."VAT Bus. Posting Group"; + Vendor2.Insert(); + + // [GIVEN] A Normal VAT setup with VAT % = 10 (different from line's 20) + LibraryERM.CreateVATProductPostingGroup(VATProductPostingGroup); + VATPostingSetup2."VAT Bus. Posting Group" := Vendor2."VAT Bus. Posting Group"; + VATPostingSetup2."VAT Prod. Posting Group" := VATProductPostingGroup.Code; + VATPostingSetup2."VAT Calculation Type" := VATPostingSetup2."VAT Calculation Type"::"Normal VAT"; + VATPostingSetup2."VAT %" := 10; + VATPostingSetup2."Sales VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2."Purchase VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2.Insert(); + + // [GIVEN] A line with VAT Rate = 20 and mismatch = true + EDocumentPurchaseHeader."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor2."No."; + EDocumentPurchaseHeader.Insert(); + EDocumentPurchaseLine."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseLine."VAT Rate" := 20; + EDocumentPurchaseLine."[BC] VAT Rate Mismatch" := true; + EDocumentPurchaseLine.Insert(); + + // [WHEN] User validates the posting group to a non-matching setup + EDocumentPurchaseLine.Validate("[BC] VAT Prod. Posting Group", VATProductPostingGroup.Code); + EDocumentPurchaseLine.Modify(); + + // [THEN] Mismatch remains true + Assert.IsTrue(EDocumentPurchaseLine."[BC] VAT Rate Mismatch", 'Mismatch should remain true when VAT % does not match VAT Rate.'); + + // Cleanup + Vendor2.SetRecFilter(); + Vendor2.Delete(); + VATPostingSetup2.SetRecFilter(); + VATPostingSetup2.Delete(); + VATProductPostingGroup.SetRecFilter(); + VATProductPostingGroup.Delete(); + end; + + [Test] + procedure ValidatingVATProdPostingGroupSetsMismatchWhenCleared() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + begin + // [SCENARIO] OnValidate sets mismatch when posting group is cleared + Initialize(Enum::"Service Integration"::"Mock"); + SetResolveVATProductGroupInPurchSetup(true); + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + // [GIVEN] A line with VAT Rate = 20, a posting group, and no mismatch + EDocumentPurchaseHeader."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseHeader.Insert(); + EDocumentPurchaseLine."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseLine."VAT Rate" := 20; + EDocumentPurchaseLine."[BC] VAT Prod. Posting Group" := 'STANDARD'; + EDocumentPurchaseLine."[BC] VAT Rate Mismatch" := false; + EDocumentPurchaseLine.Insert(); + + // [WHEN] User clears the posting group + EDocumentPurchaseLine.Validate("[BC] VAT Prod. Posting Group", ''); + EDocumentPurchaseLine.Modify(); + + // [THEN] Mismatch is set + Assert.IsTrue(EDocumentPurchaseLine."[BC] VAT Rate Mismatch", 'Mismatch should be true when posting group is cleared.'); + end; + + [Test] + procedure ValidatingVATProdPostingGroupSkipsMismatchForFullVAT() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + Vendor2: Record Vendor; + CompanyInformation: Record "Company Information"; + VATPostingSetup2: Record "VAT Posting Setup"; + VATProductPostingGroup: Record "VAT Product Posting Group"; + LibraryERM: Codeunit "Library - ERM"; + begin + // [SCENARIO] OnValidate skips mismatch evaluation for Full VAT — flag stays unchanged + Initialize(Enum::"Service Integration"::"Mock"); + SetResolveVATProductGroupInPurchSetup(true); + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + // [GIVEN] A vendor + CompanyInformation.GetRecordOnce(); + Vendor2."Country/Region Code" := CompanyInformation."Country/Region Code"; + Vendor2."No." := 'EDOC001'; + Vendor2."VAT Registration No." := 'XXXXXXX001'; + Vendor2."VAT Bus. Posting Group" := Vendor."VAT Bus. Posting Group"; + Vendor2.Insert(); + + // [GIVEN] A Full VAT setup with VAT % = 0 + LibraryERM.CreateVATProductPostingGroup(VATProductPostingGroup); + VATPostingSetup2."VAT Bus. Posting Group" := Vendor2."VAT Bus. Posting Group"; + VATPostingSetup2."VAT Prod. Posting Group" := VATProductPostingGroup.Code; + VATPostingSetup2."VAT Calculation Type" := VATPostingSetup2."VAT Calculation Type"::"Full VAT"; + VATPostingSetup2."VAT %" := 0; + VATPostingSetup2."Sales VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2."Purchase VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2.Insert(); + + // [GIVEN] A line with VAT Rate = 5 and mismatch = false + EDocumentPurchaseHeader."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor2."No."; + EDocumentPurchaseHeader.Insert(); + EDocumentPurchaseLine."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseLine."VAT Rate" := 5; + EDocumentPurchaseLine."[BC] VAT Rate Mismatch" := false; + EDocumentPurchaseLine.Insert(); + + // [WHEN] User validates the posting group to the Full VAT setup + EDocumentPurchaseLine.Validate("[BC] VAT Prod. Posting Group", VATProductPostingGroup.Code); + EDocumentPurchaseLine.Modify(); + + // [THEN] Mismatch flag is unchanged (still false) — Full VAT skips comparison + Assert.IsFalse(EDocumentPurchaseLine."[BC] VAT Rate Mismatch", 'Mismatch should remain unchanged for Full VAT calculation type.'); + + // Cleanup + Vendor2.SetRecFilter(); + Vendor2.Delete(); + VATPostingSetup2.SetRecFilter(); + VATPostingSetup2.Delete(); + VATProductPostingGroup.SetRecFilter(); + VATProductPostingGroup.Delete(); + end; + + [Test] + procedure ValidatingVATProdPostingGroupMatchesZeroRate() + var + EDocument: Record "E-Document"; + EDocumentPurchaseHeader: Record "E-Document Purchase Header"; + EDocumentPurchaseLine: Record "E-Document Purchase Line"; + Vendor2: Record Vendor; + CompanyInformation: Record "Company Information"; + VATPostingSetup2: Record "VAT Posting Setup"; + VATProductPostingGroup: Record "VAT Product Posting Group"; + LibraryERM: Codeunit "Library - ERM"; + begin + // [SCENARIO] OnValidate clears mismatch when both VAT Rate and VAT % are 0 + Initialize(Enum::"Service Integration"::"Mock"); + SetResolveVATProductGroupInPurchSetup(true); + LibraryEDoc.CreateInboundEDocument(EDocument, EDocumentService); + + // [GIVEN] A vendor + CompanyInformation.GetRecordOnce(); + Vendor2."Country/Region Code" := CompanyInformation."Country/Region Code"; + Vendor2."No." := 'EDOC001'; + Vendor2."VAT Registration No." := 'XXXXXXX001'; + Vendor2."VAT Bus. Posting Group" := Vendor."VAT Bus. Posting Group"; + Vendor2.Insert(); + + // [GIVEN] A Normal VAT setup with VAT % = 0 (zero-rated) + LibraryERM.CreateVATProductPostingGroup(VATProductPostingGroup); + VATPostingSetup2."VAT Bus. Posting Group" := Vendor2."VAT Bus. Posting Group"; + VATPostingSetup2."VAT Prod. Posting Group" := VATProductPostingGroup.Code; + VATPostingSetup2."VAT Calculation Type" := VATPostingSetup2."VAT Calculation Type"::"Normal VAT"; + VATPostingSetup2."VAT %" := 0; + VATPostingSetup2."Sales VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2."Purchase VAT Account" := LibraryERM.CreateGLAccountNo(); + VATPostingSetup2.Insert(); + + // [GIVEN] A line with VAT Rate = 0 + EDocumentPurchaseHeader."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseHeader."[BC] Vendor No." := Vendor2."No."; + EDocumentPurchaseHeader.Insert(); + EDocumentPurchaseLine."E-Document Entry No." := EDocument."Entry No"; + EDocumentPurchaseLine."VAT Rate" := 0; + EDocumentPurchaseLine."[BC] VAT Rate Mismatch" := true; + EDocumentPurchaseLine.Insert(); + + // [WHEN] User validates the posting group to the zero-rated setup + EDocumentPurchaseLine.Validate("[BC] VAT Prod. Posting Group", VATProductPostingGroup.Code); + EDocumentPurchaseLine.Modify(); + + // [THEN] Mismatch is cleared — both rates are 0 + Assert.IsFalse(EDocumentPurchaseLine."[BC] VAT Rate Mismatch", 'Mismatch should be false when both VAT Rate and VAT % are 0.'); + + // Cleanup + Vendor2.SetRecFilter(); + Vendor2.Delete(); + VATPostingSetup2.SetRecFilter(); + VATPostingSetup2.Delete(); + VATProductPostingGroup.SetRecFilter(); + VATProductPostingGroup.Delete(); + end; + + local procedure Initialize(Integration: Enum "Service Integration") + var + TransformationRule: Record "Transformation Rule"; + EDocument: Record "E-Document"; + EDocDataStorage: Record "E-Doc. Data Storage"; + EDocumentServiceStatus: Record "E-Document Service Status"; + EDocPurchLineFieldSetup: Record "ED Purchase Line Field Setup"; + PurchInvHeader: Record "Purch. Inv. Header"; + VendorLedgerEntry: Record "Vendor Ledger Entry"; + GLSetup: Record "General Ledger Setup"; + Currency: Record Currency; + LibraryERM: Codeunit "Library - ERM"; + begin + LibrarySetupStorage.Restore(); + LibraryLowerPermission.SetOutsideO365Scope(); + LibraryVariableStorage.Clear(); + Clear(EDocImplState); + EDocPurchLineFieldSetup.DeleteAll(); + + PurchInvHeader.DeleteAll(); + VendorLedgerEntry.DeleteAll(); + + if IsInitialized then + exit; + + GLSetup.GetRecordOnce(); + GLSetup."VAT Reporting Date Usage" := GLSetup."VAT Reporting Date Usage"::Disabled; + GLSetup.Modify(); + + // Set a currency that can be used across all localizations + Currency.Init(); + Currency.Validate(Code, 'XYZ'); + if Currency.Insert(true) then + LibraryERM.CreateExchangeRate(Currency.Code, WorkDate(), 1.0, 1.0); + + EDocument.DeleteAll(); + EDocumentServiceStatus.DeleteAll(); + EDocumentService.DeleteAll(); + EDocDataStorage.DeleteAll(); + + LibraryEDoc.SetupStandardVAT(); + LibraryEDoc.SetupStandardSalesScenario(Customer, EDocumentService, Enum::"E-Document Format"::Mock, Integration); + LibraryEDoc.SetupStandardPurchaseScenario(Vendor, EDocumentService, Enum::"E-Document Format"::Mock, Integration); + EDocumentService."Import Process" := "E-Document Import Process"::"Version 2.0"; + EDocumentService."Read into Draft Impl." := "E-Doc. Read into Draft"::PEPPOL; + EDocumentService.Modify(); + + TransformationRule.DeleteAll(); + TransformationRule.CreateDefaultTransformations(); + LibrarySetupStorage.SavePurchasesSetup(); + + IsInitialized := true; + end; + + local procedure SetResolveVATProductGroupInPurchSetup(NewResolveVATProductGroup: Boolean) + var + PurchasesPayablesSetup: Record "Purchases & Payables Setup"; + begin + PurchasesPayablesSetup.Get(); + PurchasesPayablesSetup.Validate("Resolve VAT Group Purch EDoc", NewResolveVATProductGroup); + PurchasesPayablesSetup.Modify(); + end; +} diff --git a/src/Apps/W1/EDocument/Test/src/Processing/EDocStructuredValidations.Codeunit.al b/src/Apps/W1/EDocument/Test/src/Processing/EDocStructuredValidations.Codeunit.al index 2fef0f05fb..74fc95dcc9 100644 --- a/src/Apps/W1/EDocument/Test/src/Processing/EDocStructuredValidations.Codeunit.al +++ b/src/Apps/W1/EDocument/Test/src/Processing/EDocStructuredValidations.Codeunit.al @@ -56,7 +56,7 @@ codeunit 139894 "EDoc Structured Validations" Assert.AreEqual('A123', EDocumentPurchaseLine."Product Code", 'The product code in the purchase line does not allign with the mock data.'); Assert.AreEqual('hours', EDocumentPurchaseLine."Unit of Measure", 'The unit of measure in the purchase line does not allign with the mock data.'); Assert.AreEqual(DMY2Date(4, 3, 2021), EDocumentPurchaseLine.Date, 'The date in the purchase line does not allign with the mock data.'); - Assert.AreEqual(6, EDocumentPurchaseLine."VAT Rate", 'The amount in the purchase line does not allign with the mock data.'); + Assert.AreEqual(10, EDocumentPurchaseLine."VAT Rate", 'The VAT rate in the purchase line does not match the expected percentage.'); EDocumentPurchaseLine.Next(); Assert.AreEqual(30, EDocumentPurchaseLine."Sub Total", 'The amount in the purchase line does not allign with the mock data.'); @@ -67,7 +67,7 @@ codeunit 139894 "EDoc Structured Validations" Assert.AreEqual('B456', EDocumentPurchaseLine."Product Code", 'The product code in the purchase line does not allign with the mock data.'); Assert.AreEqual('', EDocumentPurchaseLine."Unit of Measure", 'The unit of measure in the purchase line does not allign with the mock data.'); Assert.AreEqual(DMY2Date(5, 3, 2021), EDocumentPurchaseLine.Date, 'The date in the purchase line does not allign with the mock data.'); - Assert.AreEqual(3, EDocumentPurchaseLine."VAT Rate", 'The amount in the purchase line does not allign with the mock data.'); + Assert.AreEqual(10, EDocumentPurchaseLine."VAT Rate", 'The VAT rate in the purchase line does not match the expected percentage.'); EDocumentPurchaseLine.Next(); Assert.AreEqual(10, EDocumentPurchaseLine."Sub Total", 'The amount does not allign with the mock data.'); @@ -77,7 +77,7 @@ codeunit 139894 "EDoc Structured Validations" Assert.AreEqual('C789', EDocumentPurchaseLine."Product Code", 'The product code does not allign with the mock data.'); Assert.AreEqual('pages', EDocumentPurchaseLine."Unit of Measure", 'The unit of measure does not allign with the mock data.'); Assert.AreEqual(DMY2Date(6, 3, 2021), EDocumentPurchaseLine.Date, 'The date does not allign with the mock data.'); - Assert.AreEqual(1, EDocumentPurchaseLine."VAT Rate", 'The amount does not allign with the mock data.'); + Assert.AreEqual(10, EDocumentPurchaseLine."VAT Rate", 'The VAT rate in the purchase line does not match the expected percentage.'); end; internal procedure AssertMinimalCAPIDocumentParsed(EDocumentEntryNo: Integer)