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
186 changes: 186 additions & 0 deletions src/Apps/W1/EDocument/App/.resources/AITools/ubl_example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
{
"customization_id": "urn:cen.eu:en16931:2017",
"profile_id": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0",
"id": "",
"issue_date": "",
"due_date": "",
"document_currency_code": "",
"order_reference": {
"id": ""
},
"accounting_supplier_party": {
"party": {
"party_name": {
"name": ""
},
"postal_address": {
"street_name": "",
"additional_street_name": "",
"city_name": "",
"postal_zone": "",
"country": {
"identification_code": ""
}
},
"party_tax_scheme": {
"company_id": "",
"tax_scheme": {
"id": ""
}
},
"contact": {
"name": "",
"telephone": "",
"electronic_mail": ""
}
}
},
"accounting_customer_party": {
"party": {
"party_name": {
"name": ""
},
"postal_address": {
"street_name": "",
"additional_street_name": "",
"city_name": "",
"postal_zone": "",
"country": {
"identification_code": ""
}
},
"party_tax_scheme": {
"company_id": "",
"tax_scheme": {
"id": ""
}
},
"contact": {
"name": "",
"telephone": "",
"electronic_mail": ""
}
}
},
"delivery": {
"actual_delivery_date": "",
"delivery_location": {
"id": {
"scheme_id": "",
"value": ""
},
"address": {
"street_name": "",
"additional_street_name": "",
"city_name": "",
"postal_zone": "",
"country": {
"identification_code": ""
}
}
},
"delivery_party": {
"party_name": {
"name": ""
}
}
},
"payment_means": {
"payment_means_code": "",
"payment_id": "",
"payee_financial_account": {
"id": "",
"name": "",
"financial_institution_branch": {
"id": ""
}
}
},
"payment_terms": {
"note": ""
},
"allowance_charge": [
{
"charge_indicator": false,
"allowance_charge_reason_code": 0,
"allowance_charge_reason": "",
"amount": {
"currency_id": "",
"value": "0"
},
"tax_category": {
"id": "",
"percent": "0",
"tax_scheme": {
"id": ""
}
}
}
],
"tax_total": {
"tax_amount": "0",
"tax_subtotal": [
{
"taxable_amount": "0",
"tax_amount": "0",
"tax_category": {
"id": "",
"percent": "0",
"tax_scheme": {
"id": ""
}
}
}
]
},
"legal_monetary_total": {
"line_extension_amount": "0",
"tax_exclusive_amount": "0",
"tax_inclusive_amount": "0",
"allowance_total_amount": "0",
"charge_total_amount": "0",
"payable_amount": "0"
},
"invoice_line": [
{
"id": "",
"invoiced_quantity": {
"unit_code": "",
"value": "0"
},
"line_extension_amount": "0",
"allowance_charge": {
"charge_indicator": false,
"allowance_charge_reason_code": 0,
"allowance_charge_reason": "",
"amount": {
"currency_id": "",
"value": "0"
},
"tax_category": {
"id": "",
"percent": "0",
"tax_scheme": {
"id": ""
}
}
},
"item": {
"name": "",
"sellers_item_identification": {
"id": ""
},
"classified_tax_category": {
"id": "",
"percent": "0",
"tax_scheme": {
"id": ""
}
}
},
"price": {
"price_amount": "0"
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
You are an data extraction system. Extract ONLY what is explicitly visible on the document into UBL (Universal Business Language) JSON format.

EXTRACTION RULES:
1. NEVER invent, calculate, or assume values - extract only what you see
2. Use "" for missing text fields
3. Dates: YYYY-MM-DD format
4. Extract ALL invoice lines with sequential IDs starting from "1"
5. Quantity: use "1" only if no quantity column exists on the document

CUSTOMER vs VENDOR IDENTIFICATION:
The JSON structure includes pre-filled accounting_customer_party data. This is OUR company — the buyer receiving the invoice. Use this to distinguish between customer and vendor on the document:
- The accounting_customer_party (buyer) is already filled in. Keep these values as provided unless the document clearly shows different buyer details.
- The accounting_supplier_party (vendor/seller) is the OTHER party on the invoice — the one sending the invoice and requesting payment. Extract their details from the document.

CRITICAL FORMAT RULES:
- Country codes: Use ISO 3166-1 alpha-2 (2 letters)
- VAT IDs: Extract only the number with country prefix, no labels (e.g., "DK29399700", NOT "SE. Nr. 31 89 26 86")
- Tax scheme ID: Always use "VAT"
- Tax category ID: Use standard codes: S=Standard rate, Z=Zero rate, E=Exempt, AE=Reverse charge
- Unit codes: Use UN/ECE codes
- Allowance Charge: Leave allowance_charge section empty if no discount/charge exists on the document


Output ONLY valid JSON. No markdown, no explanation.
8 changes: 8 additions & 0 deletions src/Apps/W1/EDocument/App/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@
{
"from": 6208,
"to": 6209
},
{
"from": 6231,
"to": 6232
},
{
"from": 6234,
"to": 6234
}
],
"resourceExposurePolicy": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ enumextension 6164 "E-Document Copilot Capability" extends "Copilot Capability"
{
Caption = 'E-Document analysis';
}
value(6166; "E-Document MLLM Analysis")
{
Caption = 'E-Document MLLM analysis';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// ------------------------------------------------------------------------------------------------
namespace Microsoft.eServices.EDocument;

using Microsoft.eServices.EDocument.Format;
using Microsoft.eServices.EDocument.IO.Peppol;
using Microsoft.eServices.EDocument.OrderMatch;
using Microsoft.EServices.EDocument.Processing;
Expand All @@ -30,6 +31,7 @@ using Microsoft.Service.Document;
using Microsoft.Service.History;
using Microsoft.Service.Posting;
using Microsoft.Utilities;
using System.AI;
using System.Automation;
using System.Reflection;
using System.Telemetry;
Expand All @@ -48,6 +50,14 @@ codeunit 6103 "E-Document Subscribers"
DeleteDocumentQst: Label 'This document is linked to E-Document %1. Do you want to continue?', Comment = '%1 - E-Document Entry No.';


[EventSubscriber(ObjectType::Page, Page::"Copilot AI Capabilities", OnRegisterCopilotCapability, '', false, false)]
local procedure HandleOnRegisterCopilotCapability()
var
EDocumentMLLMHandler: Codeunit "E-Document MLLM Handler";
begin
EDocumentMLLMHandler.RegisterCopilotCapabilityIfNeeded();
end;

#region Draft page user edits

[EventSubscriber(ObjectType::Page, Page::"E-Document Purchase Draft", OnAfterValidateEvent, "Vendor No.", false, false)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,9 @@ enum 6109 "E-Doc. Read into Draft" implements IStructuredFormatReader
Caption = 'PEPPOL';
Implementation = IStructuredFormatReader = "E-Document PEPPOL Handler";
}
value(4; "MLLM")
{
Caption = 'MLLM';
Implementation = IStructuredFormatReader = "E-Document MLLM Handler";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.EServices.EDocument.Format;

using Microsoft.eServices.EDocument.Processing.Import;
using Microsoft.eServices.EDocument.Processing.Interfaces;
using System.Config;
using System.Utilities;

codeunit 6191 "E-Doc. PDF File Format" implements IEDocFileFormat
Expand All @@ -20,12 +21,31 @@ codeunit 6191 "E-Doc. PDF File Format" implements IEDocFileFormat
end;

procedure PreferredStructureDataImplementation(): Enum "Structure Received E-Doc."
var
FeatureConfiguration: Codeunit "Feature Configuration";
Result: Enum "Structure Received E-Doc.";
IsExperiment: Boolean;
begin
IsExperiment := FeatureConfiguration.GetConfiguration(MLLMExperimentTok) = 'mllm';
Result := IsExperiment ? "Structure Received E-Doc."::MLLM : "Structure Received E-Doc."::ADI;
OnAfterSetIStructureReceivedEDocumentForPdf(Result);
exit(Result);
end;

/// <summary>
/// Allows subscribers to override which structure data implementation is used for PDF processing.
/// This is specifically used by the Payables Agent to force MLLM processing on, regardless of the experiment setting.
/// </summary>
[IntegrationEvent(false, false)]
local procedure OnAfterSetIStructureReceivedEDocumentForPdf(var Result: Enum "Structure Received E-Doc.")
begin
exit("Structure Received E-Doc."::ADI);
end;

procedure FileExtension(): Text
begin
exit('pdf');
end;

var
MLLMExperimentTok: Label 'EDocMLLMExtraction', Locked = true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,9 @@ enum 6103 "Structure Received E-Doc." implements IStructureReceivedEDocument
Caption = 'Azure Document Intelligence';
Implementation = IStructureReceivedEDocument = "E-Document ADI Handler";
}
value(3; "MLLM")
{
Caption = 'MLLM Extraction';
Implementation = IStructureReceivedEDocument = "E-Document MLLM Handler";
}
}
Loading
Loading