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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Apps/DE/EDocumentDE/app/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
"name": "E-Document Core",
"publisher": "Microsoft",
"version": "29.0.0.0"
},
{
"id": "e1966889-b5fb-4fda-a84c-ea71b590e1a9",
"name": "PEPPOL",
"publisher": "Microsoft",
"version": "29.0.0.0"
}
],
"screenshots": [],
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ using Microsoft.Foundation.PaymentTerms;
using Microsoft.Foundation.Reporting;
using Microsoft.Foundation.UOM;
using Microsoft.Inventory.Location;
using Microsoft.Peppol;
using Microsoft.Sales.Customer;
using Microsoft.Sales.History;
using Microsoft.Sales.Peppol;
Expand All @@ -37,12 +38,14 @@ codeunit 13917 "Export ZUGFeRD Document"
EDocumentService: Record "E-Document Service";
FeatureTelemetry: Codeunit "Feature Telemetry";
PEPPOLMgt: Codeunit "PEPPOL Management";
PeppolVATHelper: Codeunit "PEPPOL VAT Helper";
FeatureNameTok: Label 'E-document ZUGFeRD Format', Locked = true;
StartEventNameTok: Label 'E-document ZUGFeRD export started', Locked = true;
EndEventNameTok: Label 'E-document ZUGFeRD export completed', Locked = true;
XmlNamespaceRSM: Text;
XmlNamespaceRAM: Text;
XmlNamespaceUDT: Text;
DocumentLanguageCode: Code[10];

trigger OnRun()
var
Expand Down Expand Up @@ -284,6 +287,7 @@ codeunit 13917 "Export ZUGFeRD Document"
if not DocumentLinesExist(SalesInvoiceHeader, SalesInvLine) then
exit;

DocumentLanguageCode := SalesInvoiceHeader."Language Code";
XmlDocument.ReadFrom(GetInvoiceXMLHeader(), XMLDoc);
XmlDoc.GetRoot(RootXMLNode);

Expand Down Expand Up @@ -317,6 +321,7 @@ codeunit 13917 "Export ZUGFeRD Document"
if not DocumentLinesExist(SalesCrMemoHeader, SalesCrMemoLine) then
exit;

DocumentLanguageCode := SalesCrMemoHeader."Language Code";
XmlDocument.ReadFrom(GetInvoiceXMLHeader(), XMLDoc);
XmlDoc.GetRoot(RootXMLNode);

Expand Down Expand Up @@ -360,6 +365,7 @@ codeunit 13917 "Export ZUGFeRD Document"
if not DocumentLinesExist(SalesInvoiceHeader, TempSalesInvLine) then
exit;

DocumentLanguageCode := SalesInvoiceHeader."Language Code";
XmlDocument.ReadFrom(GetInvoiceXMLHeader(), XMLDoc);
XmlDoc.GetRoot(RootXMLNode);

Expand Down Expand Up @@ -403,6 +409,7 @@ codeunit 13917 "Export ZUGFeRD Document"
if not DocumentLinesExist(SalesCrMemoHeader, TempSalesCrMemoLine) then
exit;

DocumentLanguageCode := SalesCrMemoHeader."Language Code";
XmlDocument.ReadFrom(GetInvoiceXMLHeader(), XMLDoc);
XmlDoc.GetRoot(RootXMLNode);

Expand Down Expand Up @@ -793,13 +800,17 @@ codeunit 13917 "Export ZUGFeRD Document"
end;

local procedure InsertTradeTax(var SettlementElement: XmlElement; var SalesInvLine: Record "Sales Invoice Line"; var LineAmount: Dictionary of [Decimal, Decimal]; var LineVATAmount: Dictionary of [Decimal, Decimal])
var
VATEXCode: Text;
VATClauseDescription: Text;
begin
if SalesInvLine.FindSet() then
repeat
if LineVATAmount.ContainsKey(SalesInvLine."VAT %") and LineAmount.ContainsKey(SalesInvLine."VAT %") then begin
PeppolVATHelper.GetVATClauseInfo(SalesInvLine."VAT Bus. Posting Group", SalesInvLine."VAT Prod. Posting Group", DocumentLanguageCode, VATEXCode, VATClauseDescription);
InsertTaxElement(SettlementElement, FormatDecimal(LineVATAmount.Get(SalesInvLine."VAT %")), FormatDecimal(LineAmount.Get(SalesInvLine."VAT %")),
GetTaxCategoryID(SalesInvLine."Tax Category", SalesInvLine."VAT Bus. Posting Group", SalesInvLine."VAT Prod. Posting Group"), FormatFiveDecimal(SalesInvLine."VAT %"),
SalesInvLine."VAT %" = 0);
VATEXCode, VATClauseDescription);
LineAmount.Remove(SalesInvLine."VAT %");
LineVATAmount.Remove(SalesInvLine."VAT %");
end;
Expand All @@ -810,13 +821,17 @@ codeunit 13917 "Export ZUGFeRD Document"
end;

local procedure InsertTradeTax(var SettlementElement: XmlElement; var SalesCrMemoLine: Record "Sales Cr.Memo Line"; var LineAmount: Dictionary of [Decimal, Decimal]; var LineVATAmount: Dictionary of [Decimal, Decimal])
var
VATEXCode: Text;
VATClauseDescription: Text;
begin
if SalesCrMemoLine.FindSet() then
repeat
if LineVATAmount.ContainsKey(SalesCrMemoLine."VAT %") and LineAmount.ContainsKey(SalesCrMemoLine."VAT %") then begin
PeppolVATHelper.GetVATClauseInfo(SalesCrMemoLine."VAT Bus. Posting Group", SalesCrMemoLine."VAT Prod. Posting Group", DocumentLanguageCode, VATEXCode, VATClauseDescription);
InsertTaxElement(SettlementElement, FormatDecimal(LineVATAmount.Get(SalesCrMemoLine."VAT %")), FormatDecimal(LineAmount.Get(SalesCrMemoLine."VAT %")),
GetTaxCategoryID(SalesCrMemoLine."Tax Category", SalesCrMemoLine."VAT Bus. Posting Group", SalesCrMemoLine."VAT Prod. Posting Group"), FormatFiveDecimal(SalesCrMemoLine."VAT %"),
SalesCrMemoLine."VAT %" = 0);
VATEXCode, VATClauseDescription);

LineAmount.Remove(SalesCrMemoLine."VAT %");
LineVATAmount.Remove(SalesCrMemoLine."VAT %");
Expand All @@ -827,17 +842,19 @@ codeunit 13917 "Export ZUGFeRD Document"
SalesCrMemoLine.SetRange("VAT Calculation Type");
end;

local procedure InsertTaxElement(var SettlementElement: XmlElement; CalculatedAmount: Text; BasisAmount: Text; CategoryCode: Text; RateApplicablePercent: Text; ZeroVAT: Boolean)
local procedure InsertTaxElement(var SettlementElement: XmlElement; CalculatedAmount: Text; BasisAmount: Text; CategoryCode: Text; RateApplicablePercent: Text; VATEXCode: Text; VATClauseDescription: Text)
var
TaxElement: XmlElement;
begin
TaxElement := XmlElement.Create('ApplicableTradeTax', XmlNamespaceRAM);
TaxElement.Add(XmlElement.Create('CalculatedAmount', XmlNamespaceRAM, CalculatedAmount));
TaxElement.Add(XmlElement.Create('TypeCode', XmlNamespaceRAM, 'VAT'));
if ZeroVAT then
TaxElement.Add(XmlElement.Create('ExemptionReason', XmlNamespaceRAM, 'VATEX-EU-O'));
if VATClauseDescription <> '' then
TaxElement.Add(XmlElement.Create('ExemptionReason', XmlNamespaceRAM, VATClauseDescription));
TaxElement.Add(XmlElement.Create('BasisAmount', XmlNamespaceRAM, BasisAmount));
TaxElement.Add(XmlElement.Create('CategoryCode', XmlNamespaceRAM, CategoryCode));
if VATEXCode <> '' then
TaxElement.Add(XmlElement.Create('ExemptionReasonCode', XmlNamespaceRAM, VATEXCode));
TaxElement.Add(XmlElement.Create('RateApplicablePercent', XmlNamespaceRAM, RateApplicablePercent));
SettlementElement.Add(TaxElement);
end;
Expand Down
6 changes: 6 additions & 0 deletions Apps/DE/EDocumentDE/test/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
"publisher": "Microsoft",
"version": "29.0.0.0"
},
{
"id": "e1966889-b5fb-4fda-a84c-ea71b590e1a9",
"name": "PEPPOL",
"publisher": "Microsoft",
"version": "29.0.0.0"
},
{
"id": "e1d97edc-c239-46b4-8d84-6368bdf67c8b",
"name": "E-Document Core",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ using Microsoft.eServices.EDocument;
using Microsoft.eServices.EDocument.Integration;
using Microsoft.Finance.Currency;
using Microsoft.Finance.GeneralLedger.Setup;
using Microsoft.Finance.VAT.Clause;
using Microsoft.Finance.VAT.Setup;
using Microsoft.Foundation.Address;
using Microsoft.Foundation.Attachment;
using Microsoft.Foundation.Company;
using Microsoft.Foundation.PaymentTerms;
using Microsoft.Foundation.UOM;
using Microsoft.Inventory.Location;
using Microsoft.Peppol;
using Microsoft.Purchases.Document;
using Microsoft.Purchases.Vendor;
using Microsoft.Sales.Customer;
Expand Down Expand Up @@ -388,6 +391,36 @@ codeunit 13918 "XRechnung XML Document Tests"
// [THEN] XRechnung Electronic Document is created with company data as accounting supplier party
VerifyAccountingSupplierParty(TempXMLBuffer, '/ubl:Invoice/cac:AccountingSupplierParty/cac:Party', ResponsibilityCenter);
end;

[Test]
procedure ExportPostedSalesInvoiceInXRechnungFormatVerifyVATEXCodeAndExemptionReason();
var
SalesInvoiceHeader: Record "Sales Invoice Header";
SalesInvoiceLine: Record "Sales Invoice Line";
TempXMLBuffer: Record "XML Buffer" temporary;
InvoiceTaxCategoryTok: Label '/ubl:Invoice/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory', Locked = true;
Path: Text;
begin
// [SCENARIO] Export posted sales invoice creates electronic document in XRechnung format with VATEX code and exemption reason from VAT Clause
Initialize();

// [GIVEN] Create and Post Sales Invoice.
SalesInvoiceHeader.Get(CreateAndPostSalesDocument("Sales Document Type"::Invoice, Enum::"Sales Line Type"::Item, false));

// [GIVEN] VAT Clause with VATEX Code 'VATEX-EU-O' and Description 'Not subject to VAT' linked to the VAT Posting Setup
SalesInvoiceLine.SetRange("Document No.", SalesInvoiceHeader."No.");
SalesInvoiceLine.FindFirst();
CreateVATClauseWithVATEXCode(SalesInvoiceLine."VAT Bus. Posting Group", SalesInvoiceLine."VAT Prod. Posting Group");

// [WHEN] Export XRechnung Electronic Document.
ExportInvoice(SalesInvoiceHeader, TempXMLBuffer);

// [THEN] TaxExemptionReasonCode and TaxExemptionReason are exported with correct values
Path := InvoiceTaxCategoryTok + '/cbc:TaxExemptionReasonCode';
Assert.AreEqual('VATEX-EU-O', GetNodeByPathWithError(TempXMLBuffer, Path), StrSubstNo(IncorrectValueErr, Path));
Path := InvoiceTaxCategoryTok + '/cbc:TaxExemptionReason';
Assert.AreEqual('Not subject to VAT', GetNodeByPathWithError(TempXMLBuffer, Path), StrSubstNo(IncorrectValueErr, Path));
end;
#endregion

#region ServiceInvoice
Expand Down Expand Up @@ -992,6 +1025,36 @@ codeunit 13918 "XRechnung XML Document Tests"
// [THEN] XRechnung Electronic Document is created with company data as accounting supplier party
VerifyAccountingSupplierParty(TempXMLBuffer, '/ns0:CreditNote/cac:AccountingSupplierParty/cac:Party', ResponsibilityCenter);
end;

[Test]
procedure ExportPostedSalesCrMemoInXRechnungFormatVerifyVATEXCodeAndExemptionReason();
var
SalesCrMemoHeader: Record "Sales Cr.Memo Header";
SalesCrMemoLine: Record "Sales Cr.Memo Line";
TempXMLBuffer: Record "XML Buffer" temporary;
CrMemoTaxCategoryTok: Label '/ns0:CreditNote/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory', Locked = true;
Path: Text;
begin
// [SCENARIO] Export posted sales cr. memo creates electronic document in XRechnung format with VATEX code and exemption reason from VAT Clause
Initialize();

// [GIVEN] Create and Post Sales Credit Memo.
SalesCrMemoHeader.Get(CreateAndPostSalesDocument("Sales Document Type"::"Credit Memo", Enum::"Sales Line Type"::Item, false));

// [GIVEN] VAT Clause with VATEX Code 'VATEX-EU-O' and Description 'Not subject to VAT' linked to the VAT Posting Setup
SalesCrMemoLine.SetRange("Document No.", SalesCrMemoHeader."No.");
SalesCrMemoLine.FindFirst();
CreateVATClauseWithVATEXCode(SalesCrMemoLine."VAT Bus. Posting Group", SalesCrMemoLine."VAT Prod. Posting Group");

// [WHEN] Export XRechnung Electronic Document.
ExportCreditMemo(SalesCrMemoHeader, TempXMLBuffer);

// [THEN] TaxExemptionReasonCode and TaxExemptionReason are exported with correct values
Path := CrMemoTaxCategoryTok + '/cbc:TaxExemptionReasonCode';
Assert.AreEqual('VATEX-EU-O', GetNodeByPathWithError(TempXMLBuffer, Path), StrSubstNo(IncorrectValueErr, Path));
Path := CrMemoTaxCategoryTok + '/cbc:TaxExemptionReason';
Assert.AreEqual('Not subject to VAT', GetNodeByPathWithError(TempXMLBuffer, Path), StrSubstNo(IncorrectValueErr, Path));
end;
#endregion

#region ServiceCreditMemo
Expand Down Expand Up @@ -2893,6 +2956,23 @@ codeunit 13918 "XRechnung XML Document Tests"
exit(Format(VarDate, 0, '<Year4>-<Month,2>-<Day,2>'));
end;

local procedure CreateVATClauseWithVATEXCode(VATBusPostingGroup: Code[20]; VATProductPostingGroup: Code[20])
var
VATClause: Record "VAT Clause";
VATPostingSetup: Record "VAT Posting Setup";
VATClauseCode: Code[10];
begin
VATClauseCode := LibraryUtility.GenerateRandomCode(VATClause.FieldNo(Code), Database::"VAT Clause");
VATClause.Init();
VATClause.Validate(Code, VATClauseCode);
VATClause.Validate(Description, 'Not subject to VAT');
VATClause.Validate("VATEX Code", 'VATEX-EU-O');
VATClause.Insert(true);
VATPostingSetup.Get(VATBusPostingGroup, VATProductPostingGroup);
VATPostingSetup.Validate("VAT Clause Code", VATClauseCode);
VATPostingSetup.Modify(true);
end;

local procedure Initialize();
begin
LibraryTestInitialize.OnTestInitialize(Codeunit::"XRechnung XML Document Tests");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ using Microsoft.eServices.EDocument;
using Microsoft.eServices.EDocument.Integration;
using Microsoft.Finance.Currency;
using Microsoft.Finance.GeneralLedger.Setup;
using Microsoft.Finance.VAT.Clause;
using Microsoft.Finance.VAT.Setup;
using Microsoft.Foundation.Address;
using Microsoft.Foundation.Company;
using Microsoft.Foundation.PaymentTerms;
using Microsoft.Foundation.Reporting;
using Microsoft.Foundation.UOM;
using Microsoft.Inventory.Location;
using Microsoft.Peppol;
using Microsoft.Purchases.Document;
using Microsoft.Purchases.Vendor;
using Microsoft.Sales.Customer;
Expand Down Expand Up @@ -468,6 +471,36 @@ codeunit 13922 "ZUGFeRD XML Document Tests"
VerifyHeaderData(SalesInvoiceHeader, TempXMLBuffer);
end;

[Test]
procedure ExportPostedSalesInvoiceInZUGFeRDFormatVerifyVATEXCodeAndExemptionReason();
var
SalesInvoiceHeader: Record "Sales Invoice Header";
SalesInvoiceLine: Record "Sales Invoice Line";
TempXMLBuffer: Record "XML Buffer" temporary;
TradeTaxTok: Label '/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax', Locked = true;
Path: Text;
begin
// [SCENARIO] Export posted sales invoice creates electronic document in ZUGFeRD format with VATEX code and exemption reason from VAT Clause
Initialize();

// [GIVEN] Create and Post Sales Invoice.
SalesInvoiceHeader.Get(CreateAndPostSalesDocument("Sales Document Type"::Invoice, Enum::"Sales Line Type"::Item, false));

// [GIVEN] VAT Clause with VATEX Code 'VATEX-EU-O' and Description 'Not subject to VAT' linked to the VAT Posting Setup
SalesInvoiceLine.SetRange("Document No.", SalesInvoiceHeader."No.");
SalesInvoiceLine.FindFirst();
CreateVATClauseWithVATEXCode(SalesInvoiceLine."VAT Bus. Posting Group", SalesInvoiceLine."VAT Prod. Posting Group");

// [WHEN] Export ZUGFeRD Electronic Document.
ExportInvoice(SalesInvoiceHeader, TempXMLBuffer);

// [THEN] ExemptionReasonCode and ExemptionReason are exported with correct values
Path := TradeTaxTok + '/ram:ExemptionReasonCode';
Assert.AreEqual('VATEX-EU-O', GetNodeByPathWithError(TempXMLBuffer, Path), StrSubstNo(IncorrectValueErr, Path));
Path := TradeTaxTok + '/ram:ExemptionReason';
Assert.AreEqual('Not subject to VAT', GetNodeByPathWithError(TempXMLBuffer, Path), StrSubstNo(IncorrectValueErr, Path));
end;

[Test]
procedure PrintPostedSalesInvoiceWithCustomReportLayout();
var
Expand Down Expand Up @@ -792,6 +825,36 @@ codeunit 13922 "ZUGFeRD XML Document Tests"
// [THEN] ZUGFeRD Electronic Document is created with 2 cr.memo lines
VerifyCrMemoLine(SalesCrMemoHeader, TempXMLBuffer);
end;

[Test]
procedure ExportPostedSalesCrMemoInZUGFeRDFormatVerifyVATEXCodeAndExemptionReason();
var
SalesCrMemoHeader: Record "Sales Cr.Memo Header";
SalesCrMemoLine: Record "Sales Cr.Memo Line";
TempXMLBuffer: Record "XML Buffer" temporary;
TradeTaxTok: Label '/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax', Locked = true;
Path: Text;
begin
// [SCENARIO] Export posted sales cr. memo creates electronic document in ZUGFeRD format with VATEX code and exemption reason from VAT Clause
Initialize();

// [GIVEN] Create and Post Sales Credit Memo.
SalesCrMemoHeader.Get(CreateAndPostSalesDocument("Sales Document Type"::"Credit Memo", Enum::"Sales Line Type"::Item, false));

// [GIVEN] VAT Clause with VATEX Code 'VATEX-EU-O' and Description 'Not subject to VAT' linked to the VAT Posting Setup
SalesCrMemoLine.SetRange("Document No.", SalesCrMemoHeader."No.");
SalesCrMemoLine.FindFirst();
CreateVATClauseWithVATEXCode(SalesCrMemoLine."VAT Bus. Posting Group", SalesCrMemoLine."VAT Prod. Posting Group");

// [WHEN] Export ZUGFeRD Electronic Document.
ExportCreditMemo(SalesCrMemoHeader, TempXMLBuffer);

// [THEN] ExemptionReasonCode and ExemptionReason are exported with correct values
Path := TradeTaxTok + '/ram:ExemptionReasonCode';
Assert.AreEqual('VATEX-EU-O', GetNodeByPathWithError(TempXMLBuffer, Path), StrSubstNo(IncorrectValueErr, Path));
Path := TradeTaxTok + '/ram:ExemptionReason';
Assert.AreEqual('Not subject to VAT', GetNodeByPathWithError(TempXMLBuffer, Path), StrSubstNo(IncorrectValueErr, Path));
end;
#endregion

#region ServiceInvoice
Expand Down Expand Up @@ -2762,6 +2825,23 @@ codeunit 13922 "ZUGFeRD XML Document Tests"
exit(Format(VarDate, 0, '<Year4><Month,2><Day,2>'));
end;

local procedure CreateVATClauseWithVATEXCode(VATBusPostingGroup: Code[20]; VATProductPostingGroup: Code[20])
var
VATClause: Record "VAT Clause";
VATPostingSetup: Record "VAT Posting Setup";
VATClauseCode: Code[10];
begin
VATClauseCode := LibraryUtility.GenerateRandomCode(VATClause.FieldNo(Code), Database::"VAT Clause");
VATClause.Init();
VATClause.Validate(Code, VATClauseCode);
VATClause.Validate(Description, 'Not subject to VAT');
VATClause.Validate("VATEX Code", 'VATEX-EU-O');
VATClause.Insert(true);
VATPostingSetup.Get(VATBusPostingGroup, VATProductPostingGroup);
VATPostingSetup.Validate("VAT Clause Code", VATClauseCode);
VATPostingSetup.Modify(true);
end;

local procedure Initialize();
begin
LibraryTestInitialize.OnTestInitialize(Codeunit::"ZUGFeRD XML Document Tests");
Expand Down
Loading