Skip to main content

Scenario: Post a journal that already exists in D365

Goal. A journal is prepared somewhere other than your integration: a person enters it on its journal page, a data management import creates it, or another system builds it through the standard journal entities. Your system decides when it posts. Lattice posts the journal the way Post on its journal page does, with the standard posting of the journal type, completely or not at all, and tells you what it posted.

When to use. Journals reviewed or approved in D365 before they post, journals created in bulk by set-based imports or by the standard entities, and a scheduler that posts what is ready at the end of the day. When your system has the lines and wants one journal created and posted in one call, use the posting operation of the journal type instead (General and intercompany journals, Inventory journals, Project journals and invoicing, Production order lifecycle and the others).

OperationJournalsRequest member
postLedgerJournalGeneral journal, customer payment journal, vendor payment journal, invoice register, vendor invoice journal, invoice approval journal, project expense journal, fixed asset journalJournalNum (or JournalNumExternalKey with ExternalSystem)
postInventoryJournalMovement, profit/loss (adjustment), counting, transfer, BOM, tag counting, ownership change, project item and fixed asset inventory journalsJournalId
postProjectJournalProject hour and fee journalsJournalId
postProductionJournalPicking list, route card, job card and report as finished journalsJournalId
postTradeAgreementJournalTrade agreement journals (sales and purchase prices and discounts)JournalNum

Prerequisites​

  1. Security. MGCLatticeJournalPostingMaintain ("Journal posting - post existing journals") grants the five operations and their batch variants. It is in duty MGCLatticeIntegrationServiceOperationsDuty (integration operator and administrator roles). The privilege alone posts nothing: the caller must also hold every Lattice operation that would post the journal's content, so posting an existing journal never lets a caller do more than the operations it holds would. When one is missing, the request is refused with AccessDenied and the message names all the missing operations.

    Journal typeOperation the caller must also hold
    General journalMGCLatticeGeneralLedgerJournalService.postGeneralJournal
    Customer payment journalMGCLatticePaymentJournalService.postCustomerPaymentJournal
    Vendor payment journalMGCLatticePaymentJournalService.postVendorPaymentJournal
    Invoice register, vendor invoice journalMGCLatticeVendorInvoiceJournalService.postInvoiceRegisterJournal
    Invoice approval journalMGCLatticeVendorInvoiceJournalService.postInvoiceApprovalJournal
    Project expense journalMGCLatticeProjectJournalService.postExpenseJournal
    Fixed asset journalDecided by its lines (below)
    Inventory movement, profit/loss, counting, transfer, BOM, tag counting, ownership change journalMGCLatticeInventoryJournalPostingService.postMovementJournal, postAdjustmentJournal, postCountingJournal, postTransferJournal, postBOMJournal, postTagCountingJournal, postOwnershipChangeJournal
    Project item journal (inventory)MGCLatticeProjectJournalService.postItemJournal
    Fixed asset inventory journalMGCLatticeFixedAssetService.postAcquisition
    Project hour journal, fee journalMGCLatticeProjectJournalService.postHourJournal, postFeeJournal
    Picking list; route card and job card; report as finished journalMGCLatticeProductionOrderPostingService.postPickList, postRouteCard, reportAsFinished
    Trade agreement journalMGCLatticeSalesTradeAgreementService.setSalesPrice

    In a ledger journal of any type, these lines need one more operation each:

    LineOperation the caller must also hold
    An intercompany line: its account company or offset company is another legal entityMGCLatticeGeneralLedgerJournalService.postIntercompanyJournal
    A fixed asset line (every line of a fixed asset journal, and in any other journal a line whose account or offset type is Fixed assets) with transaction type AcquisitionMGCLatticeFixedAssetService.postAcquisition
    A fixed asset line with transaction type Disposal - sale or Disposal - scrapMGCLatticeFixedAssetService.postDisposal
    A fixed asset line with any other transaction type (acquisition adjustment, depreciation, revaluation, write-up or write-down adjustment...)MGCLatticeFixedAssetService.postAssetTransaction
    A project line (account or offset type Project)MGCLatticeProjectJournalService.postExpenseJournal

    Customer, vendor and bank lines of a general journal are covered by postGeneralJournal. For example, a general journal with an intercompany line and a fixed asset disposal line needs postGeneralJournal, postIntercompanyJournal and postDisposal; the last two are high-risk operations of the integration administrator role. Creating the journal through the standard entities needs D365's own privileges for them. See Security model.

  2. D365 configuration. The journal name of the journal, with its number sequences (USMF GenJrn for general journals, IMov for inventory movements, Hour for project hours, Pick for picking lists, S_Price for sales price agreements), and whatever the standard posting of the journal type needs: accounts, open periods, on-hand inventory, a started production order, and so on. A journal under an approval workflow, or whose journal name requires approval, must be approved first.

  3. Lattice. A valid license (an expired or invalid one refuses the call with LicenseNotValid). To name a ledger journal by your own key, register it as a cross-reference and send JournalNumExternalKey with ExternalSystem (Use your own keys).

Steps​

The operations are on MGCLatticeJournalPostingService in service group MGCLatticeJournalPostingServiceGroup. Send a POST with an OAuth 2.0 bearer token and the request in _request (see Trial and day-one guide).

  1. Create the journal. Skip this step when the journal was entered in D365 or imported. Otherwise create it through its standard entities; for a general journal, the header:

    POST https://<your-environment>/data/LedgerJournalHeaders
    { "dataAreaId": "usmf", "JournalName": "GenJrn", "Description": "October accruals" }

    The answer carries the new journal's JournalBatchNumber. Then the lines; the voucher comes from the journal name:

    POST https://<your-environment>/data/LedgerJournalLines
    {
    "dataAreaId": "usmf",
    "JournalBatchNumber": "<JournalBatchNumber>",
    "LineNumber": 1,
    "TransDate": "2026-10-23T12:00:00Z",
    "AccountType": "Ledger",
    "AccountDisplayValue": "110110",
    "OffsetAccountType": "Ledger",
    "OffsetAccountDisplayValue": "110180",
    "DebitAmount": 1000.00,
    "CurrencyCode": "USD",
    "Text": "Accrual"
    }
  2. Post it when it is complete. Name the journal by its number:

    POST https://<your-environment>/api/services/MGCLatticeJournalPostingServiceGroup/MGCLatticeJournalPostingService/postLedgerJournal
    {
    "_request": {
    "RequestId": "c2a4e6f8-1b3d-4f5a-9c7e-0d2f4a6c8e1b",
    "LegalEntityId": "USMF",
    "UserContext": "close-scheduler",
    "JournalNum": "<JournalBatchNumber>"
    }
    }

    A ledger journal posts as the page's Post posts it, not Post and transfer: one voucher that fails cancels the whole posting and the journal stays as it was. Lattice checks the journal before it posts: its type, your rights for it (Prerequisites, step 1), that it is not posted, locked or in use, that its approval is complete, and that it has lines.

  3. Read the result. DocumentId is the journal. For a ledger journal, PostedDocumentId is its first voucher in voucher order and the message counts the vouchers and lists the first 25: Ledger journal 000912 (Daily) was posted with 1 vouchers: GNJL001240. Warnings and information from the posting are in Warnings and InformationalMessages.

  4. The other journal families take the same request with JournalId (JournalNum for trade agreements), and answer with the journal in both DocumentId and PostedDocumentId:

    JournalOperationCreate it withPosted in D365
    Inventory journalspostInventoryJournalThe journal page, the set-based Inventory journal lines (set-based), or InventoryMovementJournalHeaders + InventoryMovementJournalEntriesV2Inventory management > Journal entries; the item transactions
    Project hour and fee journalspostProjectJournalThe journal page, Project journal lines (set-based), or JournalTables + JournalTransProject management and accounting > Journals; the posted project transactions
    Production journalspostProductionJournalThe production order's journals, the set-based production entities, or ProductionPickingListJournalEntriesThe production order's Production journals; the consumption and the finished goods
    Trade agreement journalspostTradeAgreementJournalThe journal page, Trade agreement journal lines (set-based), or TradeAgreementJournalHeaders + OpenSalesPriceJournalLinesSales and marketing > Prices and discounts > Trade agreement journals; the agreement on the item
    • A tag counting journal posts as the standard posts it: it creates a counting journal with the counted quantities and leaves it open for review; the message names that counting journal.
    • A report as finished journal first creates the quality orders that quality associations require before a report as finished, as the page does; they stay when the posting is then refused.
    • A trade agreement journal validates every line before it posts. When the accounts receivable parameter Check for overlaps and gaps is on, a journal whose quantity or amount ranges overlap or leave gaps is not posted. Trade agreements marked for deletion (the journal page's Delete trade agreements) are deleted by the posting, as on the page.

The bulk variants (postLedgerJournalBatch, postInventoryJournalBatch, postProjectJournalBatch, postProductionJournalBatch, postTradeAgreementJournalBatch) take { "_requests": [ ... ] }, each item a full request with its own RequestId, and answer one result per item. All five operations can also be queued with submitAsyncOperation, which is the way to post a large journal: the standard posts a ledger journal above the ledger journal line limit only in batch. See Batch and async operations. None of the operations has a preview.

How to verify​

  • The response: Success true, Status 3, DocumentId the journal, PostedDocumentId the first voucher (ledger) or the journal (every other family).
  • A ledger journal: LedgerJournalHeaders with $filter=JournalBatchNumber eq '<journal>' and dataAreaId eq 'usmf'&cross-company=true shows IsPosted Yes; LatticeGeneralLedgerEntries with $filter=JournalNumber eq '<journal>' and LegalEntityId eq 'USMF' shows the posted account entries, the returned voucher in SubledgerVoucher.
  • The other families: the header entity shows the journal posted (InventoryMovementJournalHeaders, JournalTables, LatticeProductionJournalHeaders IsPosted, TradeAgreementJournalHeaders), and the transactions exist: item transactions, LatticeProjectPostedTransactions for the journal's lines, the production order's consumption, the agreement in SalesPriceAgreements.
  • In D365: the journal page of the type, with its filter set to show posted journals.
  • The call: System administration > Inquiries > Lattice > Services > Service call log, or LatticeServiceCallLogs filtered on your RequestId.
  • A general journal fires the General journal posted business event, as when it is posted from its page.

When it goes wrong​

A failed call answers Success false, Status 4, an ErrorCode and the message "The journal was not posted: the request was refused or the standard posting failed."; the reasons are in Errors. The journal stays as it was.

ErrorCodeTypical cause (text in Errors)What to do
ValidationFailed (1)No RequestId or unknown LegalEntityId; no JournalNum / JournalId, or a journal key that does not resolve; "Journal ... is a Periodic journal, which postLedgerJournal does not post. It posts these journal types: ..." (allocation, elimination, periodic, budget, project beginning balance and other types no Lattice operation posts); "Journal ... has no lines to post."Correct the request. Post other journal types in D365.
DocumentNotFound (2)"Ledger journal ... was not found in legal entity ..." (the same for inventory, project, production and trade agreement journals)Check the number, and that the request's LegalEntityId is the journal's.
AccessDenied (14)"You do not have access to MGCLatticeGeneralLedgerJournalService.postIntercompanyJournal, which posting journal ... requires, so the journal was not posted."Grant the caller the operations named (Prerequisites, step 1), or post the journal in D365.
InvalidStatus (3)"Journal ... is already posted."; locked by the system while another process validates or posts it; private to another user; locked by a user group the caller is not in; in use by another user; "it uses workflow approval, and its approval status is ..., not Approved."; "journal name ... requires approval, and the journal has not been approved."; a project journal under journal approval that is not ready for posting (this applies to every caller, including system administrators)Close, release or approve the journal in D365, then resend. A posted journal needs nothing more.
BusinessRule (4)D365 refused the posting: an unbalanced voucher, accounts, dimensions, a closed period, on-hand inventory, an item on hold, the production order's status, overlapping or missing trade agreement ranges; its messages are in Errors and Warnings. Also a ledger journal above the ledger journal line limit posted outside batchFix the journal in D365, then resend. Queue a very large ledger journal with submitAsyncOperation.
PreviewUnsupported (10)PreviewMode trueSend without PreviewMode. Nothing was run.
TransientRuntime (6)A runtime fault; IsRetryable trueResend with the same RequestId.
LicenseNotValid (12)The Lattice license is expired or invalidRenew the license, then resend.

Retries are safe. Resending a successful request with the same RequestId posts nothing again: the answer repeats the original result with the message Duplicate request detected. Original request processed at .... A new RequestId for a journal that is already posted is refused with InvalidStatus. See Post a journal exactly-once.

Reference​