Skip to content
Closed
Show file tree
Hide file tree
Changes from 84 commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
1bd93cd
init commit
Jul 17, 2026
61b2627
phase 2
Jul 17, 2026
ad43c02
new phase changes
Jul 17, 2026
ff48b53
update
Jul 17, 2026
4c75db9
more changes
Jul 17, 2026
9fe2fe0
new
Jul 17, 2026
830b06a
remaining lifecycle hardening
Jul 17, 2026
c9a68a1
improvements
Jul 17, 2026
92b361d
init
github-actions[bot] Jul 21, 2026
9d7abd8
use standard comment table
github-actions[bot] Jul 21, 2026
2aa69d6
removed tooltips from pages
github-actions[bot] Jul 21, 2026
1f46ee3
added test
github-actions[bot] Jul 21, 2026
17b02fb
removed copy comments as standard works
github-actions[bot] Jul 21, 2026
02cc439
revert 1
github-actions[bot] Jul 21, 2026
3d236c4
"FR Regulatory Comment Type" extended and added more options
Jul 22, 2026
d07ae2d
update permissions
Jul 22, 2026
8f3d925
Fix AL0129 in InjectExtendedCTCFranceElements: introduce local XmlEle…
Copilot Jul 22, 2026
f1b4252
new update
Jul 27, 2026
fb05a47
fix for node
Jul 27, 2026
f40c964
payment means
Jul 27, 2026
00f027f
Revert "payment means"
Jul 27, 2026
954d554
updated tooltip for prefix comment
Jul 27, 2026
75e9f23
Merge remote-tracking branch 'origin/main' into bugs/master_442216_Pa…
Jul 27, 2026
f6127a6
added missing using
Jul 27, 2026
99f9a7b
typo
Jul 27, 2026
2932ad5
Fix AA0477: sort using statements alphabetically
Jul 28, 2026
c2c9850
new test update
Jul 29, 2026
69783c8
new
Jul 29, 2026
558c555
Fix E-Reporting FR analyzer warnings
Jul 30, 2026
8c662c3
Address E-Reporting FR review feedback
Jul 30, 2026
1415fce
Test regulatory comments on credit memos
Jul 30, 2026
5616cf2
Fix French electronic address validation
Jul 30, 2026
bf3623e
corrections
Jul 30, 2026
2a0e1b7
fix for vat reg. no in tests
Jul 30, 2026
29038d0
peppol test fix
Jul 30, 2026
f537de9
new
Jul 30, 2026
ac3cc63
NEW
Jul 30, 2026
a472b8c
new changes for shipments
Jul 31, 2026
bbabfed
Apply suggestion from @github-actions[bot]
djukicmilica Aug 4, 2026
b35af92
new changes
Aug 4, 2026
3c049c4
Merge branch 'djukicmilica/fr-extended-ctc' of https://github.com/mic…
Aug 4, 2026
234f301
correction
Aug 4, 2026
121b808
update
Aug 4, 2026
129115e
extended billing mode
Aug 4, 2026
900be70
BillingMode
Aug 4, 2026
897bd33
added .xml file extension
Aug 4, 2026
b965f37
optimizations
Aug 4, 2026
bdb6afd
update tests
Aug 5, 2026
e92887b
PR comments changes
Aug 5, 2026
7978a24
Merge branch 'main' into bugs/master_442216_Payments
Aug 5, 2026
d7c0a5e
Merge branch 'djukicmilica/fr-extended-ctc' into bugs/master_442216_P…
Aug 5, 2026
3facb2a
new
Aug 5, 2026
f1a78dd
changed internal procedures
Aug 5, 2026
af0e365
add CreateMessage public
Aug 5, 2026
fde55a7
FrenchLifecycle visibility
Aug 5, 2026
4d309b7
testing changes
Aug 5, 2026
1b1854b
test update + remaing files
Aug 5, 2026
9980d73
tests update
Aug 6, 2026
ec0ed78
PR review changes
Aug 8, 2026
788f105
bb changes
Aug 8, 2026
b82be2c
fix tests
Aug 8, 2026
24a6fc0
fix tests
Aug 9, 2026
ed1f6e7
typo
Aug 9, 2026
d4437dc
new tests changes
Aug 10, 2026
8efbd16
tests updates
Aug 11, 2026
fad76f7
PR review comments
github-actions[bot] Aug 11, 2026
e61151f
PR comments fixes
github-actions[bot] Aug 11, 2026
7136c8e
compile errs
Aug 11, 2026
651c790
new tests
Aug 12, 2026
c3889fa
currency fix
Aug 12, 2026
f16546d
fix
Aug 12, 2026
1d6b65b
cleantag
Aug 13, 2026
785dbaf
fix
Aug 13, 2026
12e77fb
fixes
Aug 13, 2026
e3afb87
new PR comments
Aug 14, 2026
4a34336
new
Aug 14, 2026
991cdd7
more fixes
Aug 14, 2026
2e2409c
Merge branch 'main' into bugs/master_442216_Payments
Aug 14, 2026
6a2ead0
fix object ID
Aug 14, 2026
e53bb79
removed tablerelations
Aug 15, 2026
96b4383
currency changes
Aug 15, 2026
3d4bab7
error changed
Aug 15, 2026
ac12ca1
init commit
Aug 19, 2026
a96faae
revert unrelated changes
Aug 19, 2026
ff650ac
Add generic E-Document message transport
Aug 19, 2026
cd9ce42
Import French invoice lifecycle responses
Aug 19, 2026
0a91771
Add French buyer invoice refusal messages
Aug 19, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,69 @@
// ------------------------------------------------------------------------------------------------
namespace Microsoft.eServices.EDocument.Formats;

using Microsoft.eServices.EDocument;
using Microsoft.Foundation.Company;
using Microsoft.Sales.Customer;
using Microsoft.Utilities;

codeunit 10991 "EDoc. Helpers"
{
Access = Internal;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Data Classification Eval. Data", 'OnCreateEvaluationDataOnAfterClassifyTablesToNormal', '', false, false)]
local procedure ClassifyDataSensitivity()
var
DataClassificationEvalData: Codeunit "Data Classification Eval. Data";
begin
DataClassificationEvalData.SetTableFieldsToNormal(Database::"FR E-Invoice Lifecycle");
DataClassificationEvalData.SetTableFieldsToNormal(Database::"FR E-Invoice Lifecycle VAT");
end;

[EventSubscriber(ObjectType::Table, Database::"E-Document", 'OnBeforeModifyEvent', '', false, false)]
local procedure SetClearanceDateOnModify(var Rec: Record "E-Document"; var xRec: Record "E-Document"; RunTrigger: Boolean)
var
EDocumentServiceStatus: Record "E-Document Service Status";
EDocumentService: Record "E-Document Service";
begin
if not GetFrenchEDocumentService(Rec, EDocumentService, EDocumentServiceStatus) then
exit;

case EDocumentServiceStatus.Status of
EDocumentServiceStatus.Status::Approved,
EDocumentServiceStatus.Status::Cleared:
if Rec."Clearance Date" = 0DT then
Rec."Clearance Date" := CurrentDateTime();
EDocumentServiceStatus.Status::Rejected,
EDocumentServiceStatus.Status::"Not Cleared":
Rec."Clearance Date" := 0DT;
end;
end;

local procedure IsFrenchElectronicDocumentFormat(EDocumentFormat: Enum "E-Document Format"): Boolean
begin
exit(
EDocumentFormat in
[EDocumentFormat::"E-Reporting FR", EDocumentFormat::"Peppol BIS 3.0 FR", EDocumentFormat::"Factur-X FR"]);
end;

procedure GetFrenchEDocumentService(EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; var EDocumentServiceStatus: Record "E-Document Service Status"): Boolean
begin
EDocumentService.SetLoadFields("Document Format");
if (EDocument.Service <> '') and EDocumentService.Get(EDocument.Service) then
if IsFrenchElectronicDocumentFormat(EDocumentService."Document Format") then
exit(EDocumentServiceStatus.Get(EDocument."Entry No", EDocumentService.Code));

EDocumentServiceStatus.SetRange("E-Document Entry No", EDocument."Entry No");
if EDocumentServiceStatus.FindSet() then
repeat
if EDocumentService.Get(EDocumentServiceStatus."E-Document Service Code") then
if IsFrenchElectronicDocumentFormat(EDocumentService."Document Format") then
exit(true);
until EDocumentServiceStatus.Next() = 0;

exit(false);
end;

procedure FindFieldByName(RecRef: RecordRef; FieldName: Text; var FieldRefResult: FieldRef): Boolean
var
i: Integer;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
// ------------------------------------------------------------------------------------------------
// 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.Formats;

using Microsoft.eServices.EDocument;
using Microsoft.Finance.Currency;
using Microsoft.Sales.Receivables;

table 10970 "FR E-Invoice Lifecycle"
{
Caption = 'FR E-Invoice Lifecycle';
DataClassification = CustomerContent;
DrillDownPageId = "FR E-Invoice Lifecycles";
InherentPermissions = X;
LookupPageId = "FR E-Invoice Lifecycles";
ReplicateData = false;

fields
{
field(1; "Entry No."; Integer)
{
AutoIncrement = true;
Caption = 'Entry No.';
DataClassification = SystemMetadata;
ToolTip = 'Specifies the entry number of the lifecycle occurrence.';
}
field(2; "E-Document Entry No."; Integer)
{
Caption = 'E-Document Entry No.';
DataClassification = SystemMetadata;
TableRelation = "E-Document"."Entry No";
ToolTip = 'Specifies the related e-document entry.';
}
field(3; "Lifecycle Status"; Enum "FR E-Invoice Lifecycle Status")
{
Caption = 'Lifecycle Status';
DataClassification = SystemMetadata;
ToolTip = 'Specifies the French electronic invoice lifecycle status.';
}
field(4; "Source Occurrence ID"; Guid)
{
Caption = 'Source Occurrence ID';
DataClassification = SystemMetadata;
ToolTip = 'Specifies the unique identifier of the source event that triggered this occurrence.';
}
field(5; "Original Occurrence Entry No."; Integer)
{
Caption = 'Original Occurrence Entry No.';
DataClassification = SystemMetadata;
TableRelation = "FR E-Invoice Lifecycle"."Entry No.";
ToolTip = 'Specifies the entry number of the original Collected occurrence that this reversal references.';
}
field(6; "Reported Amount"; Decimal)
{
AutoFormatExpression = Rec."Currency Code";
AutoFormatType = 1;
Caption = 'Reported Amount';
DataClassification = CustomerContent;
ToolTip = 'Specifies the amount reported for this lifecycle occurrence.';
}
field(7; "Currency Code"; Code[10])
{
Caption = 'Currency Code';
DataClassification = CustomerContent;
TableRelation = Currency;
ToolTip = 'Specifies the currency of the reported amount. Blank represents the local currency.';
}
field(8; "Event Date"; Date)
{
Caption = 'Event Date';
DataClassification = CustomerContent;
ToolTip = 'Specifies the date of the lifecycle event.';
}
field(9; "Invoice Cust. Ledger Entry No."; Integer)
{
Caption = 'Invoice Customer Ledger Entry No.';
DataClassification = SystemMetadata;
TableRelation = "Cust. Ledger Entry"."Entry No.";
ToolTip = 'Specifies the customer ledger entry of the invoice related to this lifecycle occurrence.';
}
field(10; "Payment Cust. Ledger Entry No."; Integer)
{
Caption = 'Payment Customer Ledger Entry No.';
DataClassification = SystemMetadata;
TableRelation = "Cust. Ledger Entry"."Entry No.";
ToolTip = 'Specifies the customer ledger entry of the payment related to this lifecycle occurrence.';
}
field(11; "Detailed Ledger Entry No."; Integer)
{
Caption = 'Detailed Customer Ledger Entry No.';
DataClassification = SystemMetadata;
TableRelation = "Detailed Cust. Ledg. Entry"."Entry No.";
ToolTip = 'Specifies the detailed customer ledger entry that triggered this lifecycle occurrence.';
}
field(12; "E-Document Message Entry No."; Integer)
{
Caption = 'E-Document Message Entry No.';
DataClassification = SystemMetadata;
ToolTip = 'Specifies the related E-Document message entry.';
}
field(13; "Processing Status"; Enum "FR E-Invoice Lifecycle Proc.")
{
Caption = 'Processing Status';
DataClassification = SystemMetadata;
ToolTip = 'Specifies the processing status of the lifecycle occurrence.';
}
field(14; "Created At"; DateTime)
{
Caption = 'Created At';
DataClassification = SystemMetadata;
ToolTip = 'Specifies when the lifecycle occurrence was created.';
}
field(15; "Last Error"; Text[2048])
{
Caption = 'Last Error';
DataClassification = CustomerContent;
ToolTip = 'Specifies the last processing error for the lifecycle occurrence.';
}
field(16; "Invoice Issue Date"; Date)
{
Caption = 'Invoice Issue Date';
DataClassification = CustomerContent;
ToolTip = 'Specifies the issue date of the invoice frozen at the time of capture.';
}
field(17; "Invoice Receipt At"; DateTime)
{
Caption = 'Invoice Receipt At';
DataClassification = CustomerContent;
ToolTip = 'Specifies the date and time the invoice was received by the platform, frozen at capture.';
}
field(18; "Sender Platform ID"; Text[50])
{
Caption = 'Sender Platform ID';
DataClassification = CustomerContent;
ToolTip = 'Specifies the identifier of the approved platform that sent the invoice.';
}
field(19; "Sender Platform Scheme"; Code[4])
{
Caption = 'Sender Platform Scheme';
DataClassification = SystemMetadata;
ToolTip = 'Specifies the identifier scheme of the sender platform.';
}
field(20; "Sender Platform Name"; Text[100])
{
Caption = 'Sender Platform Name';
DataClassification = CustomerContent;
ToolTip = 'Specifies the name of the approved platform that sent the invoice.';
}
field(21; "Invoice Issuer ID"; Text[50])
{
Caption = 'Invoice Issuer ID';
DataClassification = CustomerContent;
ToolTip = 'Specifies the identifier (SIREN) of the invoice issuer.';
}
field(22; "Invoice Issuer Scheme"; Code[4])
{
Caption = 'Invoice Issuer Scheme';
DataClassification = SystemMetadata;
ToolTip = 'Specifies the identifier scheme of the invoice issuer.';
}
field(23; "Invoice Issuer Name"; Text[100])
{
Caption = 'Invoice Issuer Name';
DataClassification = CustomerContent;
ToolTip = 'Specifies the name of the invoice issuer.';
}
field(24; "E-Document Service Code"; Code[20])
{
Caption = 'E-Document Service Code';
DataClassification = SystemMetadata;
TableRelation = "E-Document Service";
ToolTip = 'Specifies the service selected for the lifecycle occurrence and its message.';
}
}

keys
{
key(PK; "Entry No.")
{
Clustered = true;
}
key(Occurrence; "E-Document Entry No.", "Source Occurrence ID", "Lifecycle Status")
{
Unique = true;
}
key(EDocument; "E-Document Entry No.", "Created At")
{
}
key(QueuedMessage; "E-Document Entry No.", "E-Document Message Entry No.", "Processing Status", "Created At")
{
}
key(DetailedLedgerEntry; "Lifecycle Status", "Detailed Ledger Entry No.")
{
}
}

trigger OnModify()
begin
TestImmutableFields();
end;

trigger OnDelete()
begin
Error(DeleteOccurrenceErr);
end;

trigger OnRename()
begin
Error(ImmutableOccurrenceErr);
end;

local procedure TestImmutableFields()
var
PersistedFREInvoiceLifecycle: Record "FR E-Invoice Lifecycle";
begin
PersistedFREInvoiceLifecycle.Get(Rec."Entry No.");
if (Rec."E-Document Entry No." <> PersistedFREInvoiceLifecycle."E-Document Entry No.") or
(Rec."Lifecycle Status" <> PersistedFREInvoiceLifecycle."Lifecycle Status") or
(Rec."Source Occurrence ID" <> PersistedFREInvoiceLifecycle."Source Occurrence ID") or
(Rec."Original Occurrence Entry No." <> PersistedFREInvoiceLifecycle."Original Occurrence Entry No.") or
(Rec."Reported Amount" <> PersistedFREInvoiceLifecycle."Reported Amount") or
(Rec."Currency Code" <> PersistedFREInvoiceLifecycle."Currency Code") or
(Rec."Event Date" <> PersistedFREInvoiceLifecycle."Event Date") or
(Rec."Invoice Cust. Ledger Entry No." <> PersistedFREInvoiceLifecycle."Invoice Cust. Ledger Entry No.") or
(Rec."Payment Cust. Ledger Entry No." <> PersistedFREInvoiceLifecycle."Payment Cust. Ledger Entry No.") or
(Rec."Detailed Ledger Entry No." <> PersistedFREInvoiceLifecycle."Detailed Ledger Entry No.") or
(Rec."Created At" <> PersistedFREInvoiceLifecycle."Created At") or
(Rec."Invoice Issue Date" <> PersistedFREInvoiceLifecycle."Invoice Issue Date") or
(Rec."Invoice Receipt At" <> PersistedFREInvoiceLifecycle."Invoice Receipt At") or
(Rec."Sender Platform ID" <> PersistedFREInvoiceLifecycle."Sender Platform ID") or
(Rec."Sender Platform Scheme" <> PersistedFREInvoiceLifecycle."Sender Platform Scheme") or
(Rec."Sender Platform Name" <> PersistedFREInvoiceLifecycle."Sender Platform Name") or
(Rec."Invoice Issuer ID" <> PersistedFREInvoiceLifecycle."Invoice Issuer ID") or
(Rec."Invoice Issuer Scheme" <> PersistedFREInvoiceLifecycle."Invoice Issuer Scheme") or
(Rec."Invoice Issuer Name" <> PersistedFREInvoiceLifecycle."Invoice Issuer Name") or
(Rec."E-Document Service Code" <> PersistedFREInvoiceLifecycle."E-Document Service Code")
then
Error(ImmutableOccurrenceErr);
end;

var
DeleteOccurrenceErr: Label 'A French electronic invoice lifecycle occurrence cannot be deleted.';
ImmutableOccurrenceErr: Label 'The regulatory identity and values of a French electronic invoice lifecycle occurrence cannot be changed.';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// ------------------------------------------------------------------------------------------------
// 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.Formats;

codeunit 10989 "FR E-Invoice Lifecycle Error"
{
Access = Internal;
InherentEntitlements = X;
Permissions = tabledata "FR E-Invoice Lifecycle" = m;
TableNo = "FR E-Invoice Lifecycle";

trigger OnRun()
begin
if Rec."Processing Status" = Rec."Processing Status"::"Message Sent" then
exit;
Session.LogMessage(
'0000TDQ', LifecycleWorkerFailedTelemetryMsg, Verbosity::Error,
DataClassification::SystemMetadata, TelemetryScope::All,
'Category', LifecycleTelemetryCategoryTok);
Session.LogMessage(
'0000TDT', LifecycleWorkerDiagnosticTelemetryMsg, Verbosity::Error,
DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher,
'Category', LifecycleTelemetryCategoryTok, 'ErrorCallStack', GetLastErrorCallStack());
Rec."Processing Status" := Rec."Processing Status"::Failed;
Rec."Last Error" := CopyStr(GetLastErrorText(true), 1, MaxStrLen(Rec."Last Error"));
Rec.Modify();
end;

var
LifecycleWorkerFailedTelemetryMsg: Label 'French e-invoice lifecycle message processing failed.', Locked = true;
LifecycleWorkerDiagnosticTelemetryMsg: Label 'French e-invoice lifecycle message processing failed with an internal error.', Locked = true;
LifecycleTelemetryCategoryTok: Label 'French E-Invoice Lifecycle', Locked = true;
}
Loading
Loading