ZuloOne
English
Sign in
Menu

Register — accumulation register

Updated

What it is#

A balances/turnovers view by accounting breakdowns. Each transactional posting writes a movement for a resource (resources) with concrete breakdown values. Breakdowns are almost always dynamic analytics from the global catalog (analytics; see Analytic), not physical dimensions: create physical dimensions only when you need to read raw movements by this breakdown. The totals driver sees only coordinates, not movement analytics; QueryMovementsAsync filters by dimension, but not by analytic. isDoubleEntry: false (the overwhelming majority of registers, including store and turnover registers — receipt and issue are the same one-sided posting, just with different signs) means the movement is written with transactions.Add. true means the movement is a transfer of a resource between two sets of breakdowns inside one register, and both sides must post atomically as one posting (transactionPairs.Add(outcome, income)); otherwise the whole movement is rejected (inter-store transfer, money register with two personal accounts). Do not set true only because a movement reduces the balance — a write-off/issue remains one-sided, isDoubleEntry: false (see zuloone-new-register; the live example is Stock, which people tried to make double-entry and then rolled back). totalDriverMetaId binds the totals calculation algorithm (driver; see below) — Standard/FIFO/Money/custom; registerEngineType: "Information" is a separate information-register engine (not accumulation, an IR_ table, slice of latest/earliest values by dimensions), see zuloone-new-register §9.

JSON envelope#

<Name>.object.json, "kind": "Register":

{
  "kind": "Register",
  "object": {
    "caption": { "en": "Stock", "ru": "Остатки" },
    "registerEngineType": "Standard",
    "totalDriverMetaId": "<GUID TotalDriver>",
    "isDoubleEntry": false,
    "allowNegativeBalance": true,
    "useBalanceTable": true,
    "metaId": "<GUID Register>", "name": "Stock", "modelId": "<GUID Model>"
  },
  "dimensions": [
    {
      "registerMetaId": "<GUID Register>",
      "fieldName": "Item",
      "edtMetaId": "<GUID EDT Reference>",
      "isOperational": true, "isInformational": false, "isPivotDimension": false,
      "displayOrder": 1,
      "caption": { "en": "Item", "ru": "Товар" },
      "metaId": "<GUID Dim>", "name": "Item", "modelId": "<GUID Model>"
    }
  ],
  "resources": [
    {
      "registerMetaId": "<GUID Register>",
      "fieldName": "Qty",
      "edtMetaId": "<GUID EDT Qty>",
      "isOperational": true, "displayOrder": 1,
      "caption": { "en": "Qty", "ru": "Кол-во" },
      "metaId": "<GUID Res>", "name": "Qty", "modelId": "<GUID Model>"
    }
  ]
}

Dimensions are either isOperational (participate in posting from the script) or isInformational (filled by the platform for reporting). Reusable dimensions are declared once as Analytic and attached by edtMetaId.

C# base#

Totals driver: public partial class <Name>TotalDriver — overrides calculation hooks; the class is generated and is not declared by hand. Important: the driver sees only transactions (TransactionBase — coordinates, with no access to movement analytics). If a hook must match by analytic, the analytic must get into the transaction itself at write time, not be read by the driver after the fact.

Events / Chain of Command#

A driver wrapper (not the calculation itself) is like a command and tx: a generated class; [Replace] swallows the whole chain, and a missing next in someone else's wrapper is a compilation error (see wiki/developer/chain-of-command.md). Movements are written from a document transaction script (transactions.Add(new <Register> {...}); double entry uses transactionPairs.Add(minus, plus)), not from dictionary/document events. Writing from an event to another table (including a register table) inside OnAfter*Insert escalates to a distributed transaction, which is unsupported on Linux.

Example#

Stock in Inventory — single-entry, dimensions Cell+Item, resource Qty, allowNegativeBalance: true (there is no "external counterparty" basket; the balance is always factual).

Was this page helpful?