ZuloOne
English
Sign in
Menu

Document — document

Updated

What it is#

A business operation with a finite state machine (subtypes): a transition into the target subtype may require posting (requiresPosting) and runs the WHOLE chain of the subtype's transaction scripts by executionOrder, not one owner script (see [zuloone-new-document §3]). A user transition is only through a document command (button); after the first save, the subtype selector in the card is frozen.

JSON envelope#

<Name>.object.json, "kind": "Document" — four sections in one file:

{
  "kind": "Document",
  "object": {
    "caption": { "en": "Goods issue", "ru": "Списание" },
    "requiresPosting": true,
    "postOnSave": true,
    "numberSequenceMetaId": "<GUID NumberSequence>",
    "metaId": "<GUID Document>", "name": "GoodsIssue", "modelId": "<GUID Model>"
  },
  "headerFields": [
    {
      "documentTypeMetaId": "<GUID Document>",
      "fieldName": "Customer",
      "edtMetaId": "<GUID EDT Reference>",
      "isRequired": false, "displayOrder": 2, "isVisible": true,
      "caption": { "en": "Customer", "ru": "Клиент" },
      "metaId": "<GUID Field>", "name": "Customer", "modelId": "<GUID Model>"
    }
  ],
  "subtypes": [
    {
      "documentTypeMetaId": "<GUID Document>",
      "name": "Draft", "subtypeValue": "Draft",
      "subtypeCaption": { "en": "Draft", "ru": "Черновик" },
      "isInitial": true, "isReadOnly": false, "displayOrder": 1,
      "metaId": "<GUID Subtype Draft>", "modelId": "<GUID Model>"
    },
    {
      "documentTypeMetaId": "<GUID Document>",
      "name": "Posted", "subtypeValue": "Posted",
      "subtypeCaption": { "en": "Posted", "ru": "Проведено" },
      "isInitial": false, "isReadOnly": true, "displayOrder": 2,
      "metaId": "<GUID Subtype Posted>", "modelId": "<GUID Model>"
    }
  ],
  "tableParts": [
    {
      "documentTypeMetaId": "<GUID Document>",
      "tablePartTypeMetaId": "<GUID TablePartType>",
      "isCloneable": true,
      "metaId": "<GUID TablePartLink>", "name": "Lines", "modelId": "<GUID Model>"
    }
  ],
  "subtypeTransactionScripts": [
    {
      "subtypeMetaId": "<GUID Subtype Posted>",
      "scriptMetaId": "<GUID Tx Script>",
      "executionOrder": 1,
      "metaId": "<GUID Binding>", "modelId": "<GUID Model>"
    }
  ]
}

lookupFilter on a reference field narrows its picker by other fields of the same form: comma-separated target field = form field pairs, where the target field belongs to the referenced dictionary and the form field to this document. "lookupFilter": "Customer=Customer" on Outlet offers only outlets whose Customer equals the document's Customer; "Customer=Customer,Outlet=Outlet" on Contract narrows by both. A pair whose form field is empty does not narrow. It is a choice aid, not a rule: a stored value outside the filter still shows, and the save rules stay with the document's events. The same property exists on dictionary fields and table-part columns (a column narrows by the HEADER's fields).

isReadOnly: true on a subtype means the server blocks edits to document lines in this state (see [zuloone-subtype-guards]); subtypeTransactionScripts is horizontal: the list of tx scripts ON ONE subtype, linked by subtypeMetaId+scriptMetaId; if orders are equal, creation time decides. Allowed transitions between subtypes are limited by the separate subtypeTransitions list (pairs fromSubtypeMetaId→toSubtypeMetaId). An empty set means the transition is not limited in either direction. Usually only "forward" needs a limit (for example, forbid Posted→Draft directly), while rollback remains a separate explicit pair.

C# base#

  • Events: <Name>EventHandler : TypedDocumentEventHandler<Name> — the base is manual.
  • Subtype transaction script: public partial class <Doc><Subtype><Target>Tx with override GetTransactions(<Document> document, TransactionPairCollection transactionPairs, TransactionCollection transactions) — the class is NOT declared by hand; the platform generates it from the binding in object.json.
  • Transition command: <Name>Command : DocumentCommandBase<Document> + override ExecuteAsync(<Document> document, CommandContext context) — also generated, not declared.

Events / Chain of Command#

The same life-cycle hooks as for Dictionary (see dictionary.md), plus the subtype field is written only by a subtype update (SaveDocumentAsync never touches it). The only point where a calculated header field is visible to transaction scripts is the before event of EXACTLY this update; after the transition, the document must be reread. The owner is the handler in the document's own model (link 0, does not call next); an extension from another model uses [ExtensionOf("GoodsIssue")] + next in every override.

Example#

GoodsIssue in Inventory — two subtypes (Draft→Posted), one table part Lines, one transaction script on Posted (writing off Stock).

Was this page helpful?