Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
dbc9b7d
feat: Add support for Purchase Credit Memo in E-Document processing
AndriusAndrulevicius Jul 29, 2025
6207947
refactor: Simplify Purchase Credit Memo creation and enhance XML proc…
AndriusAndrulevicius Jul 30, 2025
a3f2ad7
Fixes purchase line numbering by using FindLast
AndriusAndrulevicius Jul 30, 2025
8fd6e5f
Adds XML documentation to E-Document helper methods
AndriusAndrulevicius Jul 30, 2025
45d806b
Update EDocumentXMLHelper.Codeunit.al
AndriusAndrulevicius Jul 31, 2025
e99adee
merge main into dev/aan/PEPPOL-v2
AndriusAndrulevicius Aug 4, 2025
6bac7c4
Reorganizes imports and updates codeunit ID
AndriusAndrulevicius Aug 4, 2025
9235e05
Adds whitespace for code readability
AndriusAndrulevicius Aug 4, 2025
23d1e89
Improves unit of measure handling in purchase documents
AndriusAndrulevicius Aug 9, 2025
78c5796
Apply suggestion from @GMatuleviciute
GMatuleviciute Aug 11, 2025
e13670c
Apply suggestion from @GMatuleviciute
GMatuleviciute Aug 11, 2025
5ad28aa
Enhances PEPPOL e-document testing with comprehensive validation
AndriusAndrulevicius Aug 11, 2025
9184777
Refactors purchase document creation logic
AndriusAndrulevicius Aug 13, 2025
7f48abd
Adds XML node value retrieval helper method
AndriusAndrulevicius Aug 13, 2025
bf2dabe
Refactors draft reset logic into reusable method
AndriusAndrulevicius Aug 13, 2025
c04a089
Fixes field mapping in PEPPOL credit note XML generation
AndriusAndrulevicius Aug 13, 2025
6a96f3e
Prevents errors when deleting empty record sets
AndriusAndrulevicius Aug 14, 2025
24fae99
Update Apps/W1/EDocument/test/src/Processing/PEPPOLStructuredValidati…
AndriusAndrulevicius Aug 14, 2025
f34e79e
Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests…
AndriusAndrulevicius Aug 14, 2025
5f300e0
Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests…
AndriusAndrulevicius Aug 14, 2025
6435c88
Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests…
AndriusAndrulevicius Aug 14, 2025
345a48c
Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests…
AndriusAndrulevicius Aug 14, 2025
ae9ff0e
Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests…
AndriusAndrulevicius Aug 14, 2025
1ed84e8
Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests…
AndriusAndrulevicius Aug 14, 2025
bc16422
Update Apps/W1/EDocument/test/src/Processing/EDocumentStructuredTests…
AndriusAndrulevicius Aug 14, 2025
1dbdc15
Makes PEPPOL validations stateless and consistent
AndriusAndrulevicius Aug 18, 2025
83ebedf
Merge branch 'main' into dev/aan/PEPPOL-v2
AndriusAndrulevicius Aug 18, 2025
7080ef0
Centralizes draft reset on purchase header
AndriusAndrulevicius Aug 18, 2025
acc02ab
Enhances PEPPOL validations with stateless and consistent testing fra…
AndriusAndrulevicius Aug 19, 2025
4071f21
Centralizes draft cleanup; adds invoice retrieval
AndriusAndrulevicius Aug 19, 2025
f5c9349
Merge branch 'main' into dev/aan/PEPPOL-v2
AndriusAndrulevicius Aug 21, 2025
f958008
Respects vendor target: order vs invoice in drafts
AndriusAndrulevicius Aug 21, 2025
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
1 change: 1 addition & 0 deletions Apps/W1/EDocument/app/src/Document/EDocument.Table.al
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@ table 6121 "E-Document"
exit(GetEDocumentServiceStatus()."Import Processing Status");
end;
#endif

internal procedure ToString(): Text
begin
exit(StrSubstNo(ToStringLbl, SystemId, "Document Record ID", "Workflow Step Instance ID", "Job Queue Entry ID"));
Expand Down
1 change: 1 addition & 0 deletions Apps/W1/EDocument/app/src/Document/EDocumentType.Enum.al
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ enum 6121 "E-Document Type" implements IEDocumentFinishDraft
value(10; "Purchase Credit Memo")
{
Caption = 'Purchase Credit Memo';
Implementation = IEDocumentFinishDraft = "E-Doc. Create Purch. Cr. Memo";
}
value(11; "Service Order")
{
Expand Down
126 changes: 126 additions & 0 deletions Apps/W1/EDocument/app/src/Helpers/EDocumentXMLHelper.Codeunit.al
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// ------------------------------------------------------------------------------------------------
// 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.Helpers;

using Microsoft.Finance.GeneralLedger.Setup;

codeunit 6177 "EDocument XML Helper"
{
Access = Internal;

/// <summary>
/// Extracts currency value from XML document and sets it in the currency field if it differs from LCY.
/// </summary>
/// <param name="XMLDocument">The XML document to search in.</param>
/// <param name="XMLNamespaces">The XML namespace manager for XPath queries.</param>
/// <param name="Path">The XPath expression to locate the currency value.</param>
/// <param name="MaxLength">The maximum length of the currency code.</param>
/// <param name="CurrencyField">The currency field to update with the extracted value.</param>
internal procedure SetCurrencyValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; MaxLength: Integer; var CurrencyField: Code[10])
var
GLSetup: Record "General Ledger Setup";
XMLNode: XmlNode;
CurrencyCode: Code[10];
begin
if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then
exit;

GLSetup.Get();

#pragma warning disable AA0139 // false positive
if XMLNode.IsXmlElement() then begin
CurrencyCode := CopyStr(XMLNode.AsXmlElement().InnerText(), 1, MaxLength);
if GLSetup."LCY Code" <> CurrencyCode then
CurrencyField := CurrencyCode;
exit;
end;

if XMLNode.IsXmlAttribute() then begin
CurrencyCode := CopyStr(XMLNode.AsXmlAttribute().Value, 1, MaxLength);
if GLSetup."LCY Code" <> CurrencyCode then
CurrencyField := CurrencyCode;
exit;
end;
#pragma warning restore AA0139
end;

/// <summary>
/// Extracts string value from XML document and sets it in the specified field.
/// </summary>
/// <param name="XMLDocument">The XML document to search in.</param>
/// <param name="XMLNamespaces">The XML namespace manager for XPath queries.</param>
/// <param name="Path">The XPath expression to locate the string value.</param>
/// <param name="MaxLength">The maximum length of the string value.</param>
/// <param name="Field">The text field to update with the extracted value.</param>
internal procedure SetStringValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; MaxLength: Integer; var Field: Text)
var
XMLNode: XmlNode;
begin
if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then
exit;

if XMLNode.IsXmlElement() then begin
Field := CopyStr(XMLNode.AsXmlElement().InnerText(), 1, MaxLength);
exit;
end;

if XMLNode.IsXmlAttribute() then begin
Field := CopyStr(XMLNode.AsXmlAttribute().Value(), 1, MaxLength);
exit;
end;
end;

/// <summary>
/// Extracts numeric value from XML document and sets it in the specified decimal field.
/// </summary>
/// <param name="XMLDocument">The XML document to search in.</param>
/// <param name="XMLNamespaces">The XML namespace manager for XPath queries.</param>
/// <param name="Path">The XPath expression to locate the numeric value.</param>
/// <param name="DecimalValue">The decimal field to update with the extracted value.</param>
internal procedure SetNumberValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; var DecimalValue: Decimal)
var
XMLNode: XmlNode;
begin
if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then
exit;

if XMLNode.AsXmlElement().InnerText() <> '' then
Evaluate(DecimalValue, XMLNode.AsXmlElement().InnerText(), 9);
end;

/// <summary>
/// Extracts date value from XML document and sets it in the specified date field.
/// </summary>
/// <param name="XMLDocument">The XML document to search in.</param>
/// <param name="XMLNamespaces">The XML namespace manager for XPath queries.</param>
/// <param name="Path">The XPath expression to locate the date value.</param>
/// <param name="DateValue">The date field to update with the extracted value.</param>
internal procedure SetDateValueInField(XMLDocument: XmlDocument; XMLNamespaces: XmlNamespaceManager; Path: Text; var DateValue: Date)
var
XMLNode: XmlNode;
begin
if not XMLDocument.SelectSingleNode(Path, XMLNamespaces, XMLNode) then
exit;

if XMLNode.AsXmlElement().InnerText() <> '' then
Evaluate(DateValue, XMLNode.AsXmlElement().InnerText(), 9);
end;

/// <summary>
/// Retrieves the inner text value of an XML node using XPath expression.
/// </summary>
/// <param name="XmlDoc">The XML document to search in.</param>
/// <param name="XmlNamespaces">The XML namespace manager for XPath queries.</param>
/// <param name="XPath">The XPath expression to locate the node.</param>
/// <returns>The inner text value of the found node, or empty string if node not found.</returns>
internal procedure GetNodeValue(XmlDoc: XmlDocument; XmlNamespaces: XmlNamespaceManager; XPath: Text): Text
var
XMLNode: XmlNode;
begin
if XmlDoc.SelectSingleNode(XPath, XmlNamespaces, XMLNode) then
exit(XMLNode.AsXmlElement().InnerText());
exit('');
end;
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ codeunit 6136 "E-Document Create Purch. Doc."
EDocumentImportHelper.ProcessField(EDocument, DocumentLine, PurchaseField, TempDocumentLine.Field(PurchaseField."No."));
until PurchaseField.Next() = 0;

// After processing all fields, set direct unit cost to be the same as in the import file
if Format(DocumentLine.Field(PurchaseLine.FieldNo(Type)).Value()) <> '0' then
EDocumentImportHelper.ProcessDecimalField(EDocument, DocumentLine, PurchaseLine.FieldNo("Direct Unit Cost"), TempDocumentLine.Field(PurchaseLine.FieldNo("Direct Unit Cost")).Value());

OnCreateNewPurchLineOnBeforeRecRefModify(EDocument, TempDocumentHeader, DocumentHeader, TempDocumentLine, DocumentLine);
DocumentLine.Modify(true);
until TempDocumentLine.Next() = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,12 @@ codeunit 6193 "E-Doc. Empty Draft" implements IStructureReceivedEDocument, IStru
begin
Error(NoDataErr);
end;

procedure ResetDraft(EDocument: Record "E-Document")
var
EDocPurchaseHeader: Record "E-Document Purchase Header";
begin
EDocPurchaseHeader.GetFromEDocument(EDocument);
EDocPurchaseHeader.Delete(true);
end;
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ codeunit 6116 "E-Doc. Unspecified Impl." implements IStructureReceivedEDocument,
begin
end;

procedure ResetDraft(EDocument: Record "E-Document")
begin
// No actions to undo
end;

var
EDocumentNoReadSpecifiedErr: Label 'No method to read the e-document has been provided.';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// ------------------------------------------------------------------------------------------------
// 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;

using Microsoft.eServices.EDocument;
using Microsoft.eServices.EDocument.Processing.Import.Purchase;
using Microsoft.eServices.EDocument.Processing.Interfaces;
using Microsoft.Finance.Dimension;
using Microsoft.Purchases.Document;

/// <summary>
/// Dealing with the creation of the purchase credit memo after the draft has been populated.
/// </summary>
codeunit 6105 "E-Doc. Create Purch. Cr. Memo" implements IEDocumentFinishDraft, IEDocumentCreatePurchaseCreditMemo
{
Access = Internal;
Permissions = tabledata "Dimension Set Tree Node" = im,
tabledata "Dimension Set Entry" = im;

var
EDocImpSessionTelemetry: Codeunit "E-Doc. Imp. Session Telemetry";

/// <summary>
/// Applies the draft E-Document to Business Central by creating a purchase credit memo from the draft data.
/// </summary>
/// <param name="EDocument">The E-Document record containing the draft data to be applied.</param>
/// <param name="EDocImportParameters">The import parameters containing processing customizations.</param>
/// <returns>The RecordId of the created purchase credit memo.</returns>
internal procedure ApplyDraftToBC(EDocument: Record "E-Document"; EDocImportParameters: Record "E-Doc. Import Parameters"): RecordId
var
EDocumentPurchaseHeader: Record "E-Document Purchase Header";
PurchaseHeader: Record "Purchase Header";
EDocPurchaseDocumentHelper: Codeunit "E-Doc Purchase Document Helper";
IEDocumentFinishPurchaseDraft: Interface IEDocumentCreatePurchaseCreditMemo;
begin
EDocumentPurchaseHeader.GetFromEDocument(EDocument);
IEDocumentFinishPurchaseDraft := EDocImportParameters."Processing Customizations";
PurchaseHeader := IEDocumentFinishPurchaseDraft.CreatePurchaseCreditMemo(EDocument);

// Post document validation - Silently emit telemetry
if not EDocPurchaseDocumentHelper.TryValidateDocumentTotals(PurchaseHeader) then
EDocImpSessionTelemetry.SetBool('Totals Validation Failed', true);

exit(PurchaseHeader.RecordId);
end;

/// <summary>
/// Reverts the draft actions by deleting the associated purchase credit memo document.
/// </summary>
/// <param name="EDocument">The E-Document record whose draft actions should be reverted.</param>
internal procedure RevertDraftActions(EDocument: Record "E-Document")
var
PurchaseHeader: Record "Purchase Header";
begin
PurchaseHeader.SetRange("E-Document Link", EDocument.SystemId);
if not PurchaseHeader.FindFirst() then
exit;

PurchaseHeader.TestField("Document Type", "Purchase Document Type"::"Credit Memo");
Clear(PurchaseHeader."E-Document Link");
PurchaseHeader.Delete(true);
end;

/// <summary>
/// Creates a purchase credit memo from E-Document draft data, including header and line information.
/// </summary>
/// <param name="EDocument">The E-Document record containing the draft data to create the credit memo from.</param>
/// <returns>The created purchase header record for the credit memo.</returns>
internal procedure CreatePurchaseCreditMemo(EDocument: Record "E-Document") PurchaseHeader: Record "Purchase Header"
var
EDocPurchaseDocumentHelper: Codeunit "E-Doc Purchase Document Helper";
begin
PurchaseHeader := EDocPurchaseDocumentHelper.CreatePurchaseDocumentHeader(EDocument, "Purchase Document Type"::"Credit Memo");
exit(PurchaseHeader);
end;
}
Loading
Loading