Lattice - Process Customizations
What Lattice changes about the standard Dynamics 365 Finance and Operations processes you already run.
The other reference docs are organized by artifact - Page reference by page, API reference by class, Event catalog by business event. This one is organized by process: you pick the business process you care about - confirming a sales order, posting a journal, releasing a product - and read exactly where Lattice attaches to it and what changes as a result.
Use it when you are asking "we already do this in D365. What is different now that Lattice is installed?" - during an implementation review, a change-impact assessment, an upgrade regression plan, or a support call where a standard process behaved unexpectedly.
Scope. This document covers the existing D365 processes Lattice attaches to. It does not describe the pages and services Lattice adds alongside them - those are entirely new surface area and have their own docs.
The short answer
Lattice attaches to standard processes and pages at 225 points, but almost all of them belong to two additive features. The business event triggers are read-only observers: they let the standard process run untouched and then publish a business event describing what happened. The cross-reference keys add a button, a FactBox and a settings tab to standard pages and delete a record's keys together with the record. Neither can change a posting, block a save of a standard record, or alter a document.
| Count | What it means for you | |
|---|---|---|
| Observe only | 73 classes | The standard process is byte-for-byte unchanged. Lattice reads the result and publishes an event. |
| Observe and change | 2 classes | Publishes an event and writes something of its own: the e-invoice handlers stamp a Lattice UTC time on the standard submission log row, and the assortment hook records what it published. |
| Cross-reference keys | 9 classes | Shows the Cross references button and FactBox on the covered pages, runs the Cross references tab of the module parameters pages, and deletes a record's keys when the record is deleted (only in a legal entity that holds keys of that kind of record; batches are not covered, so their set-based deletion stays set-based). The standard process is unchanged. |
| Changes behavior | 5 classes | Genuinely alters what the standard process does. All are listed below and all are off by default or additive. |
| Infrastructure | 4 classes | Licensing, query range functions, a method that sets the options of the standard pending price activation for the inventory cost service, and a method that runs the production journal page's pre-posting step for the journal posting service. No effect on business logic. |
If you read only one section, read Processes whose behavior actually changes. Everything else in this document is observation.
Attachment points by mechanism
| Mechanism | Count | Notes |
|---|---|---|
Chain of Command ([ExtensionOf]) | 72 | Classes that wrap a standard class or table method (49 classes, 23 tables); always call next. (SysQueryRangeUtil only gains new functions, InventItemPriceActivationJob only a method that sets its options when the inventory cost service runs it without its dialog, and ProdJournalCheckPost only a method that lets the journal posting service run the step the production journal page runs before it posts.) |
| Event handler (delegate, data event, form event, post-handler) | 109 | Handler methods on a standard class, table or page: 22 for business events, guardrails, stamping and startup, and 87 for cross-reference keys (page opened, record deleted, and the Cross references tab and its buttons on the module parameters pages). The button handlers on Lattice's own pages are not counted. |
| Table extension | 4 | RetailAssortmentTable and RetailAssortmentStaging (the MGCLatticeCustAccount and MGCLatticeCustDataAreaId fields), CustomerParametersStaging, BusinessDocumentSubmission (the MGCLatticeSubmittedDateTimeUtc field). |
| Data entity extension | 2 | CustomerParametersEntity, RetailAssortmentEntity. |
| Form extension | 35 | DMFEntity - adds three buttons to the standard Data entities page; DataManagementWorkspace - adds two tiles to the Data management workspace; RetailAssortmentDetails - adds the customer account and customer legal entity fields; 25 record pages that get the cross-reference button and FactBox (AssetTable, BOMConsistOf, BOMTable, CustFreeInvoice, CustTable, EcoResProductDetailsExtended, EcoResProductVariantsPerCompany, InventBatch, InventTransferOrders, LedgerJournalTable, LedgerJournalTransDaily, ProdTable, ProdTableListPage, ProjInvoiceTable, ProjTable, PurchAgreement, PurchReqTable, PurchTable, ReturnTable, RouteInventProd, RouteTable, SalesAgreement, SalesQuotationTable, SalesTable, VendTable); 7 module parameters pages that get the Cross references tab (AssetParameters, CustParameters, InventParameters, LedgerParameters, ProdParameters, ProjParameters, SrmParameters). |
| Menu extension | 1 | SystemAdministration - adds the Lattice menu. |
Processes whose behavior actually changes
These are the only places where a standard D365 process does something different because Lattice is installed. Each one is gated - read the Gate row before assuming it is active in your environment.
1. Inbound sales order guardrails
Two handlers evaluate sales orders that arrive through an integration and can put them on hold or lock them against editing; an order hold extension releases an order when its price hold is cleared.
| Standard process | Sales order creation and line maintenance (SalesTable, SalesLine), and clearing order holds (MCRHoldCodeTrans). |
| Attaches at | MGCLatticeInboundOrderPolicy_SalesTable_EventHandler (Inserted and Deleted data events on SalesTable), MGCLatticeInboundOrderPolicy_SalesLine_EventHandler (ValidatingWrite / ValidatingDelete on SalesLine), and MGCLatticeMCRHoldCodeTrans_Table_Extension (chain of command on MCRHoldCodeTrans update and delete, the standard order hold rows). |
| What changes | On creation the order is flagged as pending policy evaluation: a lightweight state row only, removed again when the order is deleted. The heavy tolerance / hold / auto-confirm work runs separately. Once an order has been confirmed, a configurable edit-lock window makes validation of a line write or delete fail, so a user editing the order in the standard form is blocked. When the standard price hold the policy placed is cleared or removed, the order's Lattice status changes to Released in the same transaction; the standard hold processing itself is unchanged. |
| Who notices | A salesperson editing a recently confirmed integrated order sees a validation error instead of a successful save. An order can sit On hold without a user putting it there. |
| Gate | Master switch on System administration > Setup > Lattice > Inbound order policy parameters. Self-gating: with the policy off, both order handlers do nothing. They only read the switch, so creating an order never creates the parameters row, and a legal entity without one has the policy off. The hold extension is not gated, so an order held before the policy was switched off is still released when its hold is cleared; when any other hold is cleared or removed it only reads the order's policy state. The creation handler runs inside the order's own transaction and is written with guards rather than a catch, so it cannot fail the order; only the edit lock blocks a line change, on purpose. |
| Where to look | Page Inbound order policy state shows the evaluation of every inbound order, including holds and edit locks. The Inbound orders on hold tile on the Integration operations workspace counts them. |
2. Sales-origin financial dimension stamping
| Standard process | Sales order creation (SalesTable.insert). |
| Attaches at | MGCLatticeSalesOriginDimension_SalesTable_EventHandler, Inserting data event. |
| What changes | Writes the mapped financial dimensions onto the new order, based on its sales origin: every dimension mapped to the origin is stamped. It uses the Inserting event so the dimensions are written with the order rather than as a follow-up update, and so it does not depend on the SalesTable.insert() signature. A mapping whose dimension or value no longer exists is skipped, and the order is created without that dimension. |
| Who notices | New orders carry financial dimensions nobody typed. Downstream ledger postings inherit them. |
| Gate | A stamping switch plus the mapping rows on System administration > Setup > Lattice > Sales-origin financial dimension mapping. The switch (Sales-origin dimension stamping enabled, above the grid on the same page) is off by default and set per legal entity; opening the page creates the legal entity's parameters row. Creating an order only reads the switch and never creates the row, so a legal entity without one has stamping off. With no mapping row for an origin, nothing is stamped; deleting a sales origin deletes its mapping rows. |
3. Customer-specific retail assortments
| Standard process | Retail assortment maintenance (RetailAssortmentTable), assortment publishing (RetailAssortmentLookupTask). |
| Attaches at | MGCLatticeRetailAssortmentTable_Table_Extension (modifiedField, insert, update), plus a table extension adding the MGCLatticeCustAccount field and its legal entity MGCLatticeCustDataAreaId. |
| What changes | Adds an optional customer account field to the assortment. When it is set, the assortment name is defaulted to the customer name so the published assortment is recognizable. |
| Who notices | A merchandiser who sets the customer field sees the name change under them. |
| Gate | Additive - only fires when the optional field is populated. Leave it blank and the standard behavior is unchanged. |
4. Outbound business event logging
| Standard process | Business event publication (BusinessEventsPublisher.publish). |
| Attaches at | MGCLatticeBusinessEventsPublisher_Class_Extension, Chain of Command. |
| What changes | Durably logs every outbound business event that opts in via IMGCLatticeBusinessEventLoggable to MGCLatticeBusinessEventLog: a Success row with the serialized payload for each publish that reaches the business events commit log, and an Error row when the publish fails outside a database transaction. That failure is recorded, raises the Business event publish failed event, and is not passed on to the calling process. A failure inside a transaction is not caught (X++ runs no catch there): it propagates and rolls back the caller's transaction, and nothing is logged. This is what makes the Business event log page and the reconciliation sweep possible, with no per-event wiring. |
| Who notices | Extra rows in a Lattice table; a small write on every published opted-in event. Events that do not implement the interface pass straight through with the platform's own behavior: if one fails to publish, the process that raised it gets the error. |
| Gate | Opt-in per event class via the interface. In this release two events opt in: Retail assortment published, and Business event publish failed (which never alerts on itself). |
Also additive, no behavior change:
MGCLatticeSysQueryRangeUtil_Class_Extensionadds hour-granular relative-time query range functions (mgcLatticeAfterHoursAgo,mgcLatticeBeforeHoursAgo) that the standardSysQueryRangeUtilonly offers at day granularity. It adds new functions; it changes none of the existing ones. Lattice's own workspace count tiles use them, and so can your queries.
Also additive, no behavior change:
MGCLatticeProdJournalCheckPost_Class_Extensionadds one method that runs the step the production journal page's Post runs before the posting (for a report as finished journal, creating the quality orders that quality associations require), so the journal posting service posts an existing production journal as the page does. It wraps no standard method.
Also additive:
MGCLatticeEInvoiceSubmissionStatusChangedEventHandlersstamps the newMGCLatticeSubmittedDateTimeUtcfield (Submitted (UTC)) on the standardBusinessDocumentSubmissionrow when a submission is logged and when a re-submission records a new submitted time. No standard field changes. Because it subscribes to the table's data events, set-based statements on that table run row by row unless the caller skips data events.
Changes you can see on standard pages
| Standard page | What Lattice adds |
|---|---|
Data management > Data entities (DMFEntity) | A Lattice button group with three buttons: OData metadata (opens the Lattice explorer against the selected entity), Export data entity template and Export entity data to Excel (wide). |
Data management workspace (DataManagementWorkspace) | Two tiles, Export data entity template and OData entity metadata explorer, opened without a selected entity. |
Retail assortment details (RetailAssortmentDetails), entity and staging (RetailAssortmentEntity, RetailAssortmentStaging) | The optional customer account field (see §3 above). |
| The 25 record pages covered by cross-reference keys - customers, vendors, sales, purchase, transfer and production orders, products, assets, projects, journals and more | A Cross references button and FactBox (and a Line cross references button and FactBox on pages with lines), shown only when an external system is set up as visible on forms for that record kind. |
| The 7 module parameters pages (accounts receivable, procurement and sourcing, inventory and warehouse management, fixed assets, project management and accounting, general ledger, production control) | A Cross references tab with the per-system settings for that module's record kinds. |
Customer parameters entity and staging (CustomerParametersEntity, CustomerParametersStaging) | Additional fields exposed for data migration, prefixed so they cannot collide with a later Microsoft field: MGCLatticeMandatoryVATNum, MGCLatticeAdvanceInvoiceCreditCorrection_W, MGCLatticeAdvanceInvoiceReversalCorrection_W, MGCLatticeInvoiceIdEditing_W, MGCLatticePercentCustomersPerBatchTask (the CustParameters fields of the same names without the prefix). |
| System administration menu | The whole Lattice menu - Setup, Inquiries, Periodic tasks and the two workspace tiles. See Page reference. |
Processes Lattice observes
Everything below leaves the standard process untouched - apart from the submission time the
e-invoice handlers stamp (see Finance) - and publishes a business event. The pattern is
always the same: wrap the process method that causes the change (not the table insert() /
update(), except where noted), call next, compare before-and-after state, and publish only on a
genuine transition.
Read this section as "if I do X in D365, Lattice will emit an event". For each event's payload, exact hook and guard, see its page in Event catalog.
Sales
| Standard process | Hook | Event raised |
|---|---|---|
| Confirm a sales order | SalesConfirmJournalPost.postJournalPost | Sales order confirmed |
| Post a packing slip | SalesPackingSlipJournalPost.postJournalPost | Sales packing slip posted (not for a cancellation or a return order receipt) |
| Post a sales invoice or credit note | SalesInvoiceJournalPost.postJournalPost | Sales order invoice posted |
| Invoice a return order (post its credit note) | SalesInvoiceJournalPost.postJournalPost | Return credit note posted, after Sales order invoice posted |
| Confirm a sales quotation | SalesQuotationEditLinesForm_Sales_Confir.createJournal | Sales quotation confirmed |
| Cancel remaining quantity | SalesUpdateRemain.cancelOpenOrderLinesDeliveryRemainder; call-center and Commerce-payments orders through MCRSalesOrderCancellation::cancelOrder / cancelOrderFTC; under the Cancel action (cancelMultipleOrdersDeliveryRemainder) the event waits until the cancellation is final | Sales order canceled |
| Create a sales return order | SalesTableType_ReturnItem.inserted | Sales return order created |
| Change a sales order line | SalesLine Updated data event | Sales order line changed |
| Register an arrived return order line | SalesLine.returnLineRegisterUpdate() / SalesReturnNonStockedRegistrationHelper.updateOnRegistration() (ReturnStatus moves to Registered) | Return order received |
| Confirm a sales agreement | AgreementConfirm_Sales.createHeaderHistory | Sales agreement confirmed |
Pro forma documents raise nothing. A pro forma confirmation, packing slip or invoice runs through the same posting classes but does not raise Sales order confirmed, Sales packing slip posted or Sales order invoice posted. The same applies to free text and project invoices (see Finance).
Purchasing
| Standard process | Hook | Event raised |
|---|---|---|
| Approve a purchase requisition | PurchReqWFStatusTransitionHelper::setPurchReqTable2Approved | Purchase requisition approved |
| Create a purchase return order | PurchTableType_ReturnItem.inserted | Purchase return order created |
| Confirm a purchase agreement | AgreementConfirm_Purch.createHeaderHistory | Purchase agreement confirmed |
| Vendor submits an ASN | WHSASNItem.insert / update (purchase ASN lines only) | Vendor ASN line changed, once per new line and again when a line's quantity changes |
Inventory and warehouse
| Standard process | Hook | Event raised |
|---|---|---|
| Any on-hand update | InventUpdateOnhand.insertInventSum + onOnHandUpdated delegate; the events are sent after the transaction commits | On-hand changed; Item back in stock on a zero-to-positive transition |
| Ship a transfer order | InventTransferUpdShip.run | Transfer order shipped |
| Receive a transfer order | InventTransferUpdReceive.run | Transfer order received |
| Pick a transfer order | InventTransferUpdPick.run | Transfer order picking list created |
| Run inventory closing or recalculation | InventCostLedgerPostingTask.updateLedgerPosting | Inventory closing completed, once the close or recalculation has finished |
| Validate a quality order | InventQualityOrderValidate.run | Quality order passed / failed |
| Confirm a warehouse shipment, or ship-confirm a transport load | WHSShipConfirm.shipConfirm; WHSShipConfirmationActionTransportLoad.shipConfirm for transport loads | Warehouse load shipped, once per load that moves to Shipped |
| Complete warehouse work | WHSWorkTable.update, and insert for work created already closed | Warehouse work completed; Warehouse picking completed for picking work |
| Cancel a picking list, or a single picking line | WMSPickingRoute.cancel; post-handler on WMSOrderTransType_Output_OrderPick.cancel | Picking list canceled |
Production
| Standard process | Hook | Event raised |
|---|---|---|
| Schedule a production order | ProdUpdScheduling.run | Production order scheduled |
| Firm a planned production order | ReqTransPoMarkFirm.insertMovement | Production order scheduled (firming creates the order as Scheduled) |
| Estimate a production order | ProdUpdCostEstimation.run | Production order estimated |
| Start a production order | ProdUpdStartUp.run | Production order started |
| Report as finished | ProdUpdReportFinished.run | Production order reported as finished |
| Post a report-as-finished journal | ProdJournalCheckPostProd.run | Production order reported as finished |
| End a production order | ProdUpdHistoricalCost.run | Production order ended |
| Post a pick list | ProdJournalCheckPostBOM.run | Pick list posted |
| Post a route card | ProdJournalCheckPostRoute.run | Route card posted |
| Calculate a BOM | BomCalcItem.insertBOMCalcTable | BOM calculation completed |
| Approve a BOM version | BomVersionApprove.run | BOM version approved |
| Run MRP | ReqCalc.run (the main thread of a master planning run only) | MRP run completed (not for a canceled run) |
Master data
| Standard process | Hook | Event raised |
|---|---|---|
| Create or edit a customer | CustTable.insert / update | Customer created / updated; customer hold status changed |
| Create or edit a vendor | VendTable.insert / update; VendTable Updated data event | Vendor created / updated; vendor hold status changed (also for a vendor created on hold) |
| Release a product | EcoResProductReleaseManager.createInventTable, EcoResProductReleaseManagerItem.release | Released product created |
| Change an external item number | handler on CustVendExternalItem | External item changed |
Documented exception. Customer and vendor master data have no single orchestrating process method - creation and edit flow through the form data source, the DMF entity and several service paths - so these two hook the table methods deliberately. The class-level comment on each records the exception. They are not the only table-level triggers: where no process method carries the change, other events also hook a table method or a data event, as the Hook columns show.
Finance
| Standard process | Hook | Event raised |
|---|---|---|
| Post a general journal | LedgerJournalCheckPost.runInternalPostJournalTransactionEnded delegate | General journal posted |
| Post a vendor payment journal | same delegate | Vendor payment posted, once per vendor posting on the journal's lines |
| Post a free text invoice | CustInvoiceJour.insert | Free text invoice posted (not for a pro forma invoice) |
| Post a project invoice | ProjInvoiceJour.insert | Project invoice posted (not for a pro forma invoice) |
| Acquire or dispose a fixed asset | AssetTrans.insert | Fixed asset acquisition / disposal posted |
| Reverse an asset transaction, or reclassify an asset | TransactionReversal_Asset.createAssetTrans, AssetReclassification.run (they mark the rows they write) | Fixed asset acquisition / disposal posted, flagged isReversal / isReclassification |
| Complete a bank reconciliation | BankAccountReconcile.updateBankAccountStatementToReconciled | Bank reconciliation completed |
| Mark an Advanced bank reconciliation worksheet as reconciled | BankReconciliationHeader::reconcile / reconcileInBatch | Advanced bank reconciliation completed, after the reconcile transaction commits |
| Issue a vendor check | BankChequeTable.update | Vendor check issued |
| Exceed a customer credit limit | CustCreditLimit.check | Customer credit limit exceeded |
| Open or close a collections case | CustCollectionsCaseDetail::createCase / closeCase | Collection case changed |
| Post collection letters | CustCollectionLetterPost.writeCustCollectionLetterJour | Collection letter posted, when the letter is posted (creating a letter does not raise it) |
| A collection letter changes the level of open transactions (normally when it is posted) | CustCollectionLetterJour.updateCollectionLetterCodeCustTrans | Dunning level changed |
| Submit an electronic invoice | BusinessDocumentSubmission Inserted / Updated data events; Inserting / Updating handlers stamp the new MGCLatticeSubmittedDateTimeUtc field on the submission log row | E-invoice submission status changed, when a submission is logged and each time its status changes |
Pricing and trade agreements
| Standard process | Hook | Event raised |
|---|---|---|
| Post a trade agreement journal | PriceDiscAdmCheckPost.initJournalNum, createOrUpdatePriceDiscTableRecords | Sales price agreement created / modified / superseded |
| Post a trade agreement journal with lines marked for deletion | PriceDiscAdmTable::deleteMarked | Sales price agreement deleted |
| Delete a sales price agreement (by hand, through an entity or in code) | PriceDiscTable.delete | Sales price agreement deleted |
| Insert or update any sales price agreement row | PriceDiscTable Inserted / Updated data events | Sales price trade agreement changed (for journal postings, only when the "Skip price agreement insert alerts" / "Skip price agreement update alerts" parameters are No) |
| A price becomes effective or expires | batch sweep, not a hook | Sales price agreement effective / expired |
A record-based trigger only fires when a
PriceDiscTablerow is written. It cannot observe a price simply becoming effective as the clock crosses itsFromDate. That gap is closed by a batch sweep - Sales price agreement effectivity (effective/expired) events - which advances a watermark, kept per company and sweep in the newMGCLatticeTradeAgreementPriceEffectivityCursortable, and emits the effective/expired events. The first run with an active event only records its starting position; there is no historical backfill.
Platform and integration
| Standard process | Hook | Event raised |
|---|---|---|
| A batch job changes status | BatchJob.update | Batch job status changed (filterable - see Batch job event filters) |
| A DMF import/export finishes | DMFDefinitionGroupExecution.update; DMFEntityDbExporter::generateEntityDBDataV2 for bring-your-own-database exports | DMF execution completed (an import run without batch is mostly not reported) |
| An aggregate measurement refreshes | BIMeasurementProcessingStatisticsFull.update | Aggregate measurement refresh completed |
| A workflow action is recorded | WorkflowTrackingCommentTable.insert | Workflow lifecycle action |
| A workflow faults, or a work item waits past the threshold | scheduled scan (Workflow stalled/failed alert scan), not a hook | Workflow stalled or failed, once per condition until it changes (tracked in the new MGCLatticeWorkflowAlertState table) |
| Publish a retail assortment | RetailAssortmentLookupTask.run | Retail assortment published, on first publish in a legal entity or when its effective item list changes (tracked in the new MGCLatticeRetailAssortmentPublishState table, whose rows are deleted with the assortment) |
| A retail info code is answered | RetailTransactionInfocodeTrans.insert | Retail info-code response captured |
| Assign resources to a project task (save a published plan's task resources, or publish a draft plan) | ProjTask::reserveResourceActivity; ProjActivityAssignment Inserted / Updated data events for rows written by customizations and imports | Project task assigned, once per resource that holds hours on the task |
| Create a maintenance work order | EntAssetWorkOrderTable Inserted data event; the event is raised when the creating transaction commits (SysTransactionScope.ttsCommitting) | Maintenance work order created |
| A maintenance work order moves to another lifecycle state | EntAssetLifecycleStateUpdate_WorkOrderTable.updateStateRecord() | Maintenance work order scheduled / started / completed |
Documented exception. The workflow framework records every action - assignment, approval, rejection, delegation, escalation, completion, fault - as a tracking row followed by its comment row (
WorkflowTrackingCommentTable), so there is no single process method to wrap. The hook is on the comment row because it is written last, after the new assignee is recorded. The batch job hook is a documented exception for the same reason.
How to verify what is active in your environment
- Business events - open Business event catalog. An event that is not activated in the standard Business events catalog is never published, whatever the trigger does. Nothing is published until you activate and endpoint it.
- Guardrails - open Setup > Lattice > Inbound order policy parameters. With the master switch off, the hold and edit-lock behavior in §1 does not run.
- Dimension stamping - open Setup > Lattice > Sales-origin financial dimension mapping. With no rows, nothing is stamped. Rows stamp nothing until Sales-origin dimension stamping enabled, above the grid, is selected.
- What was actually emitted - Business event log records every outbound event that opts in to logging with its payload and outcome. In this release that is Retail assortment published and Business event publish failed; other events are not logged.
- What a process did - Service call log for service-driven work; Inbound message inquiry for file-driven work.
How to turn a customization off
| Customization | How to disable |
|---|---|
| Any business event | Deactivate it in the standard business events catalog. The trigger still runs but publishes nothing. |
| Inbound order guardrails | Master switch on the inbound order policy parameters page. |
| Sales-origin dimension stamping | Leave the stamping switch off (the default), or remove the mapping rows. |
| Retail customer assortments | Leave the optional customer account field blank. |
| Outbound event logging | Per-event, by whether the event class implements IMGCLatticeBusinessEventLoggable. |
See also
- Page reference - every page Lattice adds, with menu paths and buttons.
- Event catalog - per-event reference: trigger, guard and payload of all 81 events.
- Using Lattice end to end - end-to-end walkthroughs of a whole business outcome.
- API reference - every class, table and service.
- Security model - privileges, duties and roles.