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>Txwithoverride GetTransactions(<Document> document, TransactionPairCollection transactionPairs, TransactionCollection transactions)— the class is NOT declared by hand; the platform generates it from the binding inobject.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).