Skip to content

Commit 418e73f

Browse files
author
Magnus Hartvig Grønbech
committed
Merge remote-tracking branch 'origin/main' into bugs/625438
# Conflicts: # src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al
2 parents 9546e20 + aebe219 commit 418e73f

67 files changed

Lines changed: 3156 additions & 500 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/AL-Go-Settings.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,16 @@
66
"runs-on": "windows-latest",
77
"cacheImageName": "",
88
"UsePsSession": false,
9-
"artifact": "bcinsider/Sandbox/29.0.47361.0//latest",
9+
"artifact": "bcinsider/Sandbox/29.0.47592.0//latest",
1010
"country": "base",
1111
"useProjectDependencies": true,
12+
"incrementalBuilds": {
13+
"onPush": false,
14+
"onPull_Request": true,
15+
"onSchedule": false,
16+
"retentionDays": 30,
17+
"mode": "modifiedApps"
18+
},
1219
"repoVersion": "29.0",
1320
"conditionalSettings": [
1421
{

build/Packages.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"Microsoft.Dynamics.BusinessCentral.Translations": {
3-
"Version": "29.0.26068.1",
3+
"Version": "29.0.26075.1",
44
"Source": "NuGet.org"
55
},
66
"AppBaselines-BCArtifacts": {
7-
"Version": "28.1.47357.0",
7+
"Version": "28.1.47700.0",
88
"Source": "BCArtifacts",
99
"_comment": "Used to fetch app baselines from BC artifacts"
1010
}
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
{
2+
"customization_id": "urn:cen.eu:en16931:2017",
3+
"profile_id": "urn:fdc:peppol.eu:2017:poacc:billing:01:1.0",
4+
"id": "",
5+
"issue_date": "",
6+
"due_date": "",
7+
"document_currency_code": "",
8+
"order_reference": {
9+
"id": ""
10+
},
11+
"accounting_supplier_party": {
12+
"party": {
13+
"party_name": {
14+
"name": ""
15+
},
16+
"postal_address": {
17+
"street_name": "",
18+
"additional_street_name": "",
19+
"city_name": "",
20+
"postal_zone": "",
21+
"country": {
22+
"identification_code": ""
23+
}
24+
},
25+
"party_tax_scheme": {
26+
"company_id": "",
27+
"tax_scheme": {
28+
"id": ""
29+
}
30+
},
31+
"contact": {
32+
"name": "",
33+
"telephone": "",
34+
"electronic_mail": ""
35+
}
36+
}
37+
},
38+
"accounting_customer_party": {
39+
"party": {
40+
"party_name": {
41+
"name": ""
42+
},
43+
"postal_address": {
44+
"street_name": "",
45+
"additional_street_name": "",
46+
"city_name": "",
47+
"postal_zone": "",
48+
"country": {
49+
"identification_code": ""
50+
}
51+
},
52+
"party_tax_scheme": {
53+
"company_id": "",
54+
"tax_scheme": {
55+
"id": ""
56+
}
57+
},
58+
"contact": {
59+
"name": "",
60+
"telephone": "",
61+
"electronic_mail": ""
62+
}
63+
}
64+
},
65+
"delivery": {
66+
"actual_delivery_date": "",
67+
"delivery_location": {
68+
"id": {
69+
"scheme_id": "",
70+
"value": ""
71+
},
72+
"address": {
73+
"street_name": "",
74+
"additional_street_name": "",
75+
"city_name": "",
76+
"postal_zone": "",
77+
"country": {
78+
"identification_code": ""
79+
}
80+
}
81+
},
82+
"delivery_party": {
83+
"party_name": {
84+
"name": ""
85+
}
86+
}
87+
},
88+
"payment_means": {
89+
"payment_means_code": "",
90+
"payment_id": "",
91+
"payee_financial_account": {
92+
"id": "",
93+
"name": "",
94+
"financial_institution_branch": {
95+
"id": ""
96+
}
97+
}
98+
},
99+
"payment_terms": {
100+
"note": ""
101+
},
102+
"allowance_charge": [
103+
{
104+
"charge_indicator": false,
105+
"allowance_charge_reason_code": 0,
106+
"allowance_charge_reason": "",
107+
"amount": {
108+
"currency_id": "",
109+
"value": "0"
110+
},
111+
"tax_category": {
112+
"id": "",
113+
"percent": "0",
114+
"tax_scheme": {
115+
"id": ""
116+
}
117+
}
118+
}
119+
],
120+
"tax_total": {
121+
"tax_amount": "0",
122+
"tax_subtotal": [
123+
{
124+
"taxable_amount": "0",
125+
"tax_amount": "0",
126+
"tax_category": {
127+
"id": "",
128+
"percent": "0",
129+
"tax_scheme": {
130+
"id": ""
131+
}
132+
}
133+
}
134+
]
135+
},
136+
"legal_monetary_total": {
137+
"line_extension_amount": "0",
138+
"tax_exclusive_amount": "0",
139+
"tax_inclusive_amount": "0",
140+
"allowance_total_amount": "0",
141+
"charge_total_amount": "0",
142+
"payable_amount": "0"
143+
},
144+
"invoice_line": [
145+
{
146+
"id": "",
147+
"invoiced_quantity": {
148+
"unit_code": "",
149+
"value": "0"
150+
},
151+
"line_extension_amount": "0",
152+
"allowance_charge": {
153+
"charge_indicator": false,
154+
"allowance_charge_reason_code": 0,
155+
"allowance_charge_reason": "",
156+
"amount": {
157+
"currency_id": "",
158+
"value": "0"
159+
},
160+
"tax_category": {
161+
"id": "",
162+
"percent": "0",
163+
"tax_scheme": {
164+
"id": ""
165+
}
166+
}
167+
},
168+
"item": {
169+
"name": "",
170+
"sellers_item_identification": {
171+
"id": ""
172+
},
173+
"classified_tax_category": {
174+
"id": "",
175+
"percent": "0",
176+
"tax_scheme": {
177+
"id": ""
178+
}
179+
}
180+
},
181+
"price": {
182+
"price_amount": "0"
183+
}
184+
}
185+
]
186+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
You are an data extraction system. Extract ONLY what is explicitly visible on the document into UBL (Universal Business Language) JSON format.
2+
3+
EXTRACTION RULES:
4+
1. NEVER invent, calculate, or assume values - extract only what you see
5+
2. Use "" for missing text fields
6+
3. Dates: YYYY-MM-DD format
7+
4. Extract ALL invoice lines with sequential IDs starting from "1"
8+
5. Quantity: use "1" only if no quantity column exists on the document
9+
10+
CUSTOMER vs VENDOR IDENTIFICATION:
11+
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:
12+
- The accounting_customer_party (buyer) is already filled in. Keep these values as provided unless the document clearly shows different buyer details.
13+
- 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.
14+
15+
CRITICAL FORMAT RULES:
16+
- Country codes: Use ISO 3166-1 alpha-2 (2 letters)
17+
- VAT IDs: Extract only the number with country prefix, no labels (e.g., "DK29399700", NOT "SE. Nr. 31 89 26 86")
18+
- Tax scheme ID: Always use "VAT"
19+
- Tax category ID: Use standard codes: S=Standard rate, Z=Zero rate, E=Exempt, AE=Reverse charge
20+
- Unit codes: Use UN/ECE codes
21+
- Allowance Charge: Leave allowance_charge section empty if no discount/charge exists on the document
22+
23+
24+
Output ONLY valid JSON. No markdown, no explanation.

src/Apps/W1/EDocument/App/app.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@
4949
{
5050
"from": 6208,
5151
"to": 6209
52+
},
53+
{
54+
"from": 6231,
55+
"to": 6232
56+
},
57+
{
58+
"from": 6234,
59+
"to": 6234
5260
}
5361
],
5462
"resourceExposurePolicy": {

src/Apps/W1/EDocument/App/src/Extensions/EDocCompanyInformation.PageExt.al

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,24 @@ pageextension 6165 "E-Doc. Company Information" extends "Company Information"
1818
{
1919
addafter(GLN)
2020
{
21-
field("E-Document Service Participation Ids"; ParticipantIdCount)
21+
group(ElectronicDocumentServiceGroup)
2222
{
23-
ApplicationArea = All;
24-
Caption = 'E-Document Service Participation';
25-
DrillDown = true;
26-
Editable = false;
27-
ToolTip = 'Specifies the company participation for the E-Document services.';
23+
ShowCaption = false;
2824
Visible = EDocumentServiceExists;
2925

30-
trigger OnDrillDown()
31-
begin
32-
ServiceParticipant.RunServiceParticipantPage(Enum::"E-Document Source Type"::Company, '');
33-
end;
26+
field("E-Document Service Participation Ids"; ParticipantIdCount)
27+
{
28+
ApplicationArea = All;
29+
Caption = 'E-Document Service Participation';
30+
DrillDown = true;
31+
Editable = false;
32+
ToolTip = 'Specifies the company participation for the E-Document services.';
33+
34+
trigger OnDrillDown()
35+
begin
36+
ServiceParticipant.RunServiceParticipantPage(Enum::"E-Document Source Type"::Company, '');
37+
end;
38+
}
3439
}
3540
}
3641
}

src/Apps/W1/EDocument/App/src/Processing/EDocumentCopilotCapability.EnumExt.al

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,8 @@ enumextension 6164 "E-Document Copilot Capability" extends "Copilot Capability"
1212
{
1313
Caption = 'E-Document analysis';
1414
}
15+
value(6166; "E-Document MLLM Analysis")
16+
{
17+
Caption = 'E-Document MLLM analysis';
18+
}
1519
}

src/Apps/W1/EDocument/App/src/Processing/EDocumentSubscribers.Codeunit.al

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// ------------------------------------------------------------------------------------------------
55
namespace Microsoft.eServices.EDocument;
66

7+
using Microsoft.eServices.EDocument.Format;
78
using Microsoft.eServices.EDocument.IO.Peppol;
89
using Microsoft.eServices.EDocument.OrderMatch;
910
using Microsoft.EServices.EDocument.Processing;
@@ -31,6 +32,7 @@ using Microsoft.Service.History;
3132
using Microsoft.Service.Posting;
3233
using Microsoft.Utilities;
3334
using Microsoft.Warehouse.Activity;
35+
using System.AI;
3436
using System.Automation;
3537
using System.Reflection;
3638
using System.Telemetry;
@@ -49,6 +51,14 @@ codeunit 6103 "E-Document Subscribers"
4951
DeleteDocumentQst: Label 'This document is linked to E-Document %1. Do you want to continue?', Comment = '%1 - E-Document Entry No.';
5052

5153

54+
[EventSubscriber(ObjectType::Page, Page::"Copilot AI Capabilities", OnRegisterCopilotCapability, '', false, false)]
55+
local procedure HandleOnRegisterCopilotCapability()
56+
var
57+
EDocumentMLLMHandler: Codeunit "E-Document MLLM Handler";
58+
begin
59+
EDocumentMLLMHandler.RegisterCopilotCapabilityIfNeeded();
60+
end;
61+
5262
#region Draft page user edits
5363

5464
[EventSubscriber(ObjectType::Page, Page::"E-Document Purchase Draft", OnAfterValidateEvent, "Vendor No.", false, false)]

src/Apps/W1/EDocument/App/src/Processing/Import/EDocReadIntoDraft.Enum.al

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,9 @@ enum 6109 "E-Doc. Read into Draft" implements IStructuredFormatReader
3535
Caption = 'PEPPOL';
3636
Implementation = IStructuredFormatReader = "E-Document PEPPOL Handler";
3737
}
38+
value(4; "MLLM")
39+
{
40+
Caption = 'MLLM';
41+
Implementation = IStructuredFormatReader = "E-Document MLLM Handler";
42+
}
3843
}

src/Apps/W1/EDocument/App/src/Processing/Import/FileFormat/EDocPDFFileFormat.Codeunit.al

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace Microsoft.EServices.EDocument.Format;
66

77
using Microsoft.eServices.EDocument.Processing.Import;
88
using Microsoft.eServices.EDocument.Processing.Interfaces;
9+
using System.Config;
910
using System.Utilities;
1011

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

2223
procedure PreferredStructureDataImplementation(): Enum "Structure Received E-Doc."
24+
var
25+
FeatureConfiguration: Codeunit "Feature Configuration";
26+
Result: Enum "Structure Received E-Doc.";
27+
IsExperiment: Boolean;
28+
begin
29+
IsExperiment := FeatureConfiguration.GetConfiguration(MLLMExperimentTok) = 'mllm';
30+
Result := IsExperiment ? "Structure Received E-Doc."::MLLM : "Structure Received E-Doc."::ADI;
31+
OnAfterSetIStructureReceivedEDocumentForPdf(Result);
32+
exit(Result);
33+
end;
34+
35+
/// <summary>
36+
/// Allows subscribers to override which structure data implementation is used for PDF processing.
37+
/// This is specifically used by the Payables Agent to force MLLM processing on, regardless of the experiment setting.
38+
/// </summary>
39+
[IntegrationEvent(false, false)]
40+
local procedure OnAfterSetIStructureReceivedEDocumentForPdf(var Result: Enum "Structure Received E-Doc.")
2341
begin
24-
exit("Structure Received E-Doc."::ADI);
2542
end;
2643

2744
procedure FileExtension(): Text
2845
begin
2946
exit('pdf');
3047
end;
48+
49+
var
50+
MLLMExperimentTok: Label 'EDocMLLMExtraction', Locked = true;
3151
}

0 commit comments

Comments
 (0)