Flow · 20 steps

Open the books with a stocktake

Every set of books begins with the stock counted and valued. The organization's first stocktake is its opening one: every stock SKU at every live location, each SKU given its cost. Completing it is the canonical fact the general ledger opens at — the counted stock posted at its cost against opening balances, nothing before it the ledger's — and until it is done no accounting app can be activated. Every later stocktake counts what it names and posts the difference as a write-down or a gain.

Services involved

A filled dot is a read; a ring is a call that changes something.

  • Inventory Manager: https://inventory.getmanifold.io
  • Accounting Ledger: https://accounting.getmanifold.io

Every call carries Authorization: Bearer $MANIFOLD_TOKEN unless its step says otherwise (getting a token). Run a flow in your sandbox, not your live organization: see Before you start.

1 Bring two SKUs in

Twelve oak lamps on the shelf, as an import or a person left them — no cost yet.

POST /items Inventory Manager

Request · application/json
{
  "sku": "LAMP-OAK",
  "name": "Oak table lamp",
  "quantity": 12
}

Answers 201. What the answer holds: the operation's example.

Records inventory.item-created.

2 And a second, with nothing on hand

A SKU holding nothing still needs a cost: the opening stocktake values every stock SKU.

POST /items Inventory Manager

Request · application/json
{
  "sku": "SHADE-LINEN",
  "name": "Linen shade",
  "quantity": 0
}

Answers 201. What the answer holds: the operation's example.

Records inventory.item-created.

3 Start the opening stocktake

The first stocktake is the opening one, over every live location. The id is the caller's: a retry answers with the stocktake it started.

POST /stocktakes Inventory Manager

Request · application/json
{
  "stocktakeId": "stk_01a15870-d6a0-7c6d-8e7f-9a0b1c2d3e4f"
}

Answers 201. What the answer holds: the operation's example.

Records inventory.stocktake-started.

4 Only one at a time

A second stocktake while one is open is refused 409 StocktakeInProgress, naming the open one.

POST /stocktakes Inventory Manager

Request · application/json
{
  "stocktakeId": "stk_01a15885-51ee-7e8f-8a91-b2c3d4e5f607"
}
Refused · 409
{
  "_tag": "StocktakeInProgress",
  "stocktakeId": "stk_01a15870-d6a0-7c6d-8e7f-9a0b1c2d3e4f"
}

Records nothing.

5 Read the counting sheet

Every SKU of the scope, each at least once, with what on-hand says now and — where the stocktake gives none yet — the cost basis it suggests. Both SKUs still want a cost.

GET /stocktakes/stk_01a15870-d6a0-7c6d-8e7f-9a0b1c2d3e4f/sheet?missingCost=true Inventory Manager

What the answer holds: the operation's example.

6 Count the lamps and value them

Ten lamps on the shelf, not twelve, at $18.50 each. Saved as it is entered; nothing moves yet.

PUT /stocktakes/stk_01a15870-d6a0-7c6d-8e7f-9a0b1c2d3e4f/counts Inventory Manager

Request · application/json
{
  "counts": [
    {
      "sku": "LAMP-OAK",
      "counted": 10
    }
  ],
  "costs": [
    {
      "sku": "LAMP-OAK",
      "unitCostCents": 1850
    }
  ]
}

What the answer holds: the operation's example.

Records inventory.stocktake-counted.

7 Save the same figures again

Idempotent by content: what the stocktake already holds appends nothing.

PUT /stocktakes/stk_01a15870-d6a0-7c6d-8e7f-9a0b1c2d3e4f/counts Inventory Manager

Request · application/json
{
  "counts": [
    {
      "sku": "LAMP-OAK",
      "counted": 10
    }
  ],
  "costs": [
    {
      "sku": "LAMP-OAK",
      "unitCostCents": 1850
    }
  ]
}

What the answer holds: the operation's example.

Records nothing.

8 Try to complete it with a SKU unvalued

The shade has no cost: an opening stocktake values every stock SKU, so it is refused 409 StocktakeIncomplete, naming the SKU, and nothing moves.

POST /stocktakes/stk_01a15870-d6a0-7c6d-8e7f-9a0b1c2d3e4f/complete Inventory Manager

Refused · 409
{
  "_tag": "StocktakeIncomplete",
  "stocktakeId": "stk_01a15870-d6a0-7c6d-8e7f-9a0b1c2d3e4f",
  "missing": 1,
  "skus": [
    "SHADE-LINEN"
  ]
}

Records nothing.

9 Value the shade

Nothing on hand, but a cost all the same — what the next one received will be measured against.

PUT /stocktakes/stk_01a15870-d6a0-7c6d-8e7f-9a0b1c2d3e4f/counts Inventory Manager

Request · application/json
{
  "costs": [
    {
      "sku": "SHADE-LINEN",
      "unitCostCents": 640
    }
  ]
}

What the answer holds: the operation's example.

Records inventory.stocktake-counted.

10 Complete the opening stocktake

On-hand becomes the count, every SKU's cost basis the stocktake's, and the fact carries the whole opening balance. Accounting opens the general ledger here.

POST /stocktakes/stk_01a15870-d6a0-7c6d-8e7f-9a0b1c2d3e4f/complete Inventory Manager

What the answer holds: the operation's example.

Records inventory.stocktake-completed.

11 Complete it again

Idempotent by state: a stocktake completes once, so the retry answers with it and appends nothing.

POST /stocktakes/stk_01a15870-d6a0-7c6d-8e7f-9a0b1c2d3e4f/complete Inventory Manager

What the answer holds: the operation's example.

Records nothing.

12 The books open

The general ledger's first posting: ten lamps at $18.50 debited to inventory against opening balances. Accounting reads inventory's journal from its replica, so the poll waits for it. A poll claims nothing.

GET /postings?source=opening-stocktake Accounting Ledger

What the answer holds: the operation's example.

13 Read the opening one back

Its counts as completed: what it set on-hand to, and the cost it gave each SKU.

GET /stocktakes/stk_01a15870-d6a0-7c6d-8e7f-9a0b1c2d3e4f Inventory Manager

What the answer holds: the operation's example.

14 Start a recount, and abandon it

A later stocktake is periodic. Started by mistake, it is cancelled; nothing it counted moves.

POST /stocktakes Inventory Manager

Request · application/json
{
  "stocktakeId": "stk_01a15885-51ee-7e8f-8a91-b2c3d4e5f607"
}

Answers 201. What the answer holds: the operation's example.

From your answer, later steps send id as {stocktakeId}.

Records inventory.stocktake-started.

15 Cancel it

Idempotent by state: a stocktake is cancelled once.

POST /stocktakes/{stocktakeId}/cancel Inventory Manager

Request · application/json
{
  "reason": "started on the wrong day"
}

What the answer holds: the operation's example.

Records inventory.stocktake-cancelled.

16 Recount the lamps

A periodic stocktake over what it names: two lamps are missing since the opening.

POST /stocktakes Inventory Manager

Request · application/json
{
  "stocktakeId": "stk_01a15879-379d-7d7e-9f80-a1b2c3d4e5f6"
}

Answers 201. What the answer holds: the operation's example.

From your answer, later steps send id as {stocktakeId}.

Records inventory.stocktake-started.

17 Eight on the shelf

The count only; a periodic stocktake asks no cost of a SKU that came up short.

PUT /stocktakes/{stocktakeId}/counts Inventory Manager

Request · application/json
{
  "counts": [
    {
      "sku": "LAMP-OAK",
      "counted": 8
    }
  ]
}

What the answer holds: the operation's example.

Records inventory.stocktake-counted.

18 Complete the recount

On-hand drops to eight; the two lamps short are the variance.

POST /stocktakes/{stocktakeId}/complete Inventory Manager

What the answer holds: the operation's example.

Records inventory.stocktake-completed.

19 The variance posts

Two lamps at the cost they were counted at, written down: stock write-downs debited, inventory credited.

GET /postings?source=stocktake-variance Accounting Ledger

What the answer holds: the operation's example.

20 Every stocktake, newest first

The recount, the abandoned one and the opening one — the books begin at the first completed.

GET /stocktakes Inventory Manager

What the answer holds: the operation's example.

What a recorded fact is: Facts and the journal. Why a repeated step changes nothing: Idempotency and retries.