asset_depreciation_run_create
Create a draft depreciation run for a period, calculating proposed depreciation for every eligible asset so a bookkeeper can review amounts before anything is posted.
Instructions
Create a DRAFT depreciation run for a period (H04): calculate the depreciation for every eligible asset via the H03 engine and persist a run header (status=draft) plus one line per asset that returned amount > 0, so a bookkeeper can review the proposed expense before anything hits the books. Eligible = status active/fully_depreciated, method != none, accumulated depreciation below cost minus residual, last_depreciation_period null or before the target period, not disposed. Optional filters narrow the set: assetIds (an explicit subset), categoryId, costCenterId. unitsByAsset maps assetId to the WHOLE units produced this period and is what makes a units_of_production asset depreciable at all (TILL captures no production data of its own, so the run carries it; same shape as asset_depreciation_preview, and values must be non-negative safe integers up to 1e12 or the create is invalid_input). skipped[] reports assets that produced NO line, as {assetId, assetNumber, reason}: on an EXPLICIT assetIds list every named asset that produced no line is reported (non_depreciable | asset_terminal | already_at_residual | period_already_processed | filtered_out | missing_production_data | missing_units_estimate | zero_amount), because naming an asset is an instruction about that asset; on a SWEEP the eligible register IS the selection, so only assets that reached the calculation are reported (missing_production_data | missing_units_estimate | zero_amount). A units_of_production asset named EXPLICITLY in assetIds is refused, not merely reported, when the input can be corrected: missing_production_data (no figure supplied) or missing_units_estimate (the asset carries no totalEstimatedUnits, so there is no denominator to allocate over). Nothing is written in either case. postingGranularity is detailed (one expense + one accum line per asset, the default) or summarised (grouped by account + cost centre); the per-asset lines exist either way for the sub-ledger audit. period is YYYY-MM. Refused with invalid_period, period_locked (the period is hard-locked in A03) or run_already_exists (a draft for the same period and selection already exists, and the response names it). Writes NO journal (the post verb does). Idempotent on idempotencyKey and on the (period, selection) signature, where the signature is built from the LINES the run would write (asset, amount and production figure), so two creates over the same eligible set yield ONE draft however the filters or the units map were spelled. When there is nothing to charge, NO run is persisted at all: the answer is {ok:true, run:null, lines:[], empty:true, skipped:[...]}, computed fresh, so it is identical on every call however many times it is repeated and whatever key each call carries. A run header exists only where at least one asset is charged, which is why a finished period never leaves an outstanding draft in run_list for a period-close checklist to trip over. Once a period is POSTED its assets carry it as their last depreciation period, so nothing is eligible and a later create for that period returns exactly that empty answer, carrying alreadyPostedRunId: the id of the most recently posted run for the period (two disjoint selections can both post for one period). A foreign assetId is not_found (§H-TENANT).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| period | Yes | ||
| assetIds | No | ||
| categoryId | No | ||
| workspaceId | Yes | ||
| costCenterId | No | ||
| unitsByAsset | No | ||
| idempotencyKey | Yes | ||
| postingGranularity | No |