Free text invoice posted
Fires when a free text invoice (an invoice not tied to a sales order) is posted in Accounts receivable. At posting time a CustInvoiceJour record (the customer invoice journal header) is inserted; when that record has no associated sales order, this event publishes the invoice header details so downstream billing, collections and reporting systems can react to the new customer invoice. A free text credit note (a free text invoice with a negative total) is posted the same way and also raises this event, with a negative amount.
| Event class | MGCLatticeFreeTextInvoicePostedBusinessEvent |
| Contract class | MGCLatticeFreeTextInvoicePostedBusinessEventContract |
| Category | Accounts receivable |
| Module | ModuleAxapta::Customer |
| Label IDs | FreeTextInvoicePostedBusinessEventName / FreeTextInvoicePostedBusinessEventDescription |
| Trigger | CustInvoiceJour.insert() -> fires when !this.SalesId (no sales order, i.e. a free text invoice) and the header is not pro forma |
Trigger / Hook
- Extension class:
MGCLatticeCustInvoiceJour_Table_Extension,[ExtensionOf(tableStr(CustInvoiceJour))] - CoC method signature:
void insert() - Firing condition: after
next insert(), the event fires only when!this.SalesIdis true - the invoice journal header has no sales order, which distinguishes a free text invoice from a sales order invoice - and the header is not flagged pro forma. Apart from pro forma, the guard is only "no sales order": any otherCustInvoiceJourheader inserted without aSalesIdwould raise it too. Headers written withdoInsert(), or by set-based inserts run withskipDataMethods(true), do not. - Pro forma: printing a pro forma free text invoice (
CustPrintOutInvoice.createProforma()) inserts a real header flagged pro forma (with a temporary invoice number such as#5637171234) and deletes it after printing. That header is not a posted invoice and does not raise this event. - Amounts: standard free text invoice posting (
CustPostInvoice) sets the header totals from the invoice's calculated totals before it inserts the header, so the payload carries the final posted amount. - This event and Sales order invoice posted are mutually exclusive for any one invoice header (that event requires a
SalesId). - Timing:
send()writes the event to the business events commit log inside the posting transaction, and delivery happens asynchronously after commit, so a posting that fails and rolls back never delivers. - Enablement gate:
BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeFreeTextInvoicePostedBusinessEvent))must return true (the event is activated for the current legal entity) before the event is constructed and sent. - Trigger method body (verbatim from the extension source):
void insert()
{
next insert();
if (!this.SalesId && !this.isProforma())
{
if (BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeFreeTextInvoicePostedBusinessEvent)))
{
MGCLatticeFreeTextInvoicePostedBusinessEvent::newFromCustInvoiceJour(this).send();
}
}
}
Payload Contract
One row per [DataMember], in declaration order. All sources are fields on the inserted CustInvoiceJour record, mapped in initialize().
| JSON field | Display label | Type / EDT | Source | Notes |
|---|---|---|---|---|
legalEntity | Legal Entity | DataAreaId | CustInvoiceJour.DataAreaId | Company/legal entity of the invoice journal, in lower case (for example usmf). |
invoiceId | Invoice ID | InvoiceId | CustInvoiceJour.InvoiceId | Posted invoice number. |
invoiceAccount | Invoice Account | CustInvoiceAccount | CustInvoiceJour.InvoiceAccount | Invoice account (customer billed). |
invoiceDate | Invoice Date | InvoiceDate | CustInvoiceJour.InvoiceDate | Invoice date of the posted invoice. A date, serialized in the \/Date(milliseconds)\/ form (midnight UTC). |
invoiceAmount | Invoice Amount | AmountCur | CustInvoiceJour.InvoiceAmount | Total invoice amount in the invoice (transaction) currency named by currencyCode; negative for a credit note. |
invoiceAmountMST | Invoice Amount MST | AmountMST | CustInvoiceJour.InvoiceAmountMST | The same total in the legal entity's accounting currency. |
currencyCode | Currency Code | CurrencyCode | CustInvoiceJour.CurrencyCode | Transaction currency of the invoice and of invoiceAmount. |
crossReferences | Cross references | List of MGCLatticeCrossReferenceEventContract | MGCLatticeCrossReferenceEventHelper::listForFreeTextInvoice(CustInvoiceJour) | Keys external systems hold for the free text invoice the journal was posted from: one { externalSystem, externalKey } object per external system, ordered by external system, historical keys included; [] when it has none. See Cross-reference keys. The keys are the free text invoice's, not the journal's. |
Every Lattice event also carries the Lattice ordering and correlation envelope, inherited from MGCLatticeBusinessEventContractBase: eventSequence (Int64 - increasing across the whole catalog and all companies, allocated when the event is raised inside the business transaction, not when it commits; it orders the events of one business record, gaps are permanent and normal, and it must not be used to re-sequence the whole stream), correlationId (String, always Scope:Key - groups emissions that belong to one logical run; an event that joins no run carries Event: followed by a lower-case GUID unique to this emission) and schemaVersion (Int - the payload shape version; 1 for this contract). D365 F&O also adds its standard envelope fields to every business event payload: BusinessEventId, BusinessEventLegalEntity, ContextRecordSubject, ControlNumber, EventId, EventTime, EventTimeIso8601, InitiatingUserAADObjectId, MajorVersion, MinorVersion, and ParentContextRecordSubjects. Do not rely on the order of members in the delivered JSON.
Example Payload
{
"BusinessEventId": "MGCLatticeFreeTextInvoicePostedBusinessEvent",
"ControlNumber": 5637148612,
"EventId": "1F3D8C2A-6B94-4E21-9A77-2C0E5D8F4B10",
"EventTime": "\/Date(1790264919000)\/",
"EventTimeIso8601": "2026-09-24T15:48:39.4697237Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{8B95911F-6BBF-FFCE-7321-A0857390AE19}",
"ParentContextRecordSubjects": [],
"eventSequence": 48301,
"correlationId": "Event:7b6e0f3d-9a12-4c58-b7e4-21d8f5a3c690",
"schemaVersion": 1,
"legalEntity": "usmf",
"invoiceId": "FTI-00042",
"invoiceAccount": "US-004",
"invoiceDate": "\/Date(1790208000000)\/",
"invoiceAmount": 1250.00,
"invoiceAmountMST": 1250.00,
"currencyCode": "USD",
"crossReferences": [
{
"externalSystem": "BILLING",
"externalKey": "INV-55120"
}
]
}
Activation
In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find the event by name ("Free text invoice posted") or by filtering the Category column to Accounts receivable, select it, and use Activate to bind it to a configured endpoint (for example, an Azure Service Bus queue, Event Grid topic, or HTTPS webhook).
Related Events
- Sales order invoice posted - the sales-order counterpart, raised when an invoice journal header that has a
SalesIdis posted. - Return credit note posted - credit notes posted for sales return orders.
- E-invoice submission status changed - follows the invoice through Electronic invoicing submission.
- Customer credit limit exceeded - raised when a credit limit check (including the free text invoice check) finds the customer over the limit.
- Customer created - customer master event in the same module (ModuleAxapta::Customer).