POST budget_cell_save
Save a single budget cell — upserts one period+layer row in a Budget Input document. Designed for EPMSAVE() immediate writes from Excel.
Endpoint
POST /api/method/konsol.api.budget_cell_save
Authentication: Required (Frappe session cookie)
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
scenario_id | string | Yes | Scenario identifier (e.g., BUDGET_2025) |
data_area_id | string | Yes | Entity code (e.g., USMF) |
fiscal_year | integer | Yes | Fiscal year |
main_account | string | Yes | Account code |
fiscal_period | integer | Yes | Period number (1–12) |
amount | number | Yes | Budget amount |
layer | string | Yes | Budget layer: base, challenge, management, board |
dim_cost_center | string | No | Cost center dimension |
dim_department | string | No | Department dimension |
base_modified | string | No | Optimistic-locking baseline — the document modified timestamp the client last read. See Optimistic Locking. |
Upsert Behavior
Finds or creates the Budget Input document by the budget grain, then upserts the specific period+layer row within it:
- If a row with the same
fiscal_period+layerexists, its amount is updated. - If no matching row exists, a new period row is appended.
The budget grain is the fixed keys (scenario_id, data_area_id, fiscal_year, main_account) plus every Dimension flagged in_budget (Published) — for example, a cost center on a shared Travel account. Two writers that differ only by a budget dimension resolve to different documents instead of silently clobbering each other.
The Budget Input document name is built from the readable grain components followed by a mandatory 8-character digest suffix (a SHA-1 of the exact key tuple), so two distinct keys never collide even when their readable parts sanitise to the same string — e.g. BUD-BUDGET_2025-USMF-2025-6100-1a2b3c4d.
Optimistic Locking
Pass the optional base_modified field (the document modified timestamp the client last read) to guard against lost updates. The behaviour is backward-compatible: omit it for last-write-wins.
- If the stored document's
modifiedno longer matchesbase_modified, the write is refused with HTTP status409and aconflictpayload (see Conflict below) so the client can refresh and re-prompt. - On a successful save, the response echoes the new
modifiedbaseline.
Example
curl -X POST http://localhost:8069/api/method/konsol.api.budget_cell_save \
-H "Content-Type: application/json" \
-b "cookies.txt" \
-d '{
"scenario_id": "BUDGET_2025",
"data_area_id": "USMF",
"fiscal_year": 2025,
"main_account": "6100",
"fiscal_period": 3,
"amount": 15000,
"layer": "base"
}'
Response
{
"message": {
"status": "ok",
"name": "BUD-BUDGET_2025-USMF-2025-6100-1a2b3c4d",
"value": 15000,
"modified": "2026-06-19 10:32:14.582931"
}
}
The modified field is the new optimistic-locking baseline; pass it back as base_modified on the next save to the same cell.
Error Responses
Conflict (409)
When base_modified is supplied and the stored document has been modified since, the write is refused with HTTP status 409 and the following payload:
{
"message": {
"status": "conflict",
"name": "BUD-BUDGET_2025-USMF-2025-6100-1a2b3c4d",
"fiscal_period": 3,
"layer": "base",
"your_amount": 15000,
"current_amount": 14000,
"current_modified": "2026-06-19 10:35:02.118764"
}
}
current_amount is null (not 0) when the cell has been cleared, so the client can distinguish a cleared cell from a genuine zero. Refresh from current_modified and re-prompt the user before retrying.
Invalid layer
{
"exc_type": "ValidationError",
"_server_messages": "[\"Invalid layer 'forecast'. Allowed: base, board, challenge, management\"]"
}
Invalid fiscal period
{
"exc_type": "ValidationError",
"_server_messages": "[\"fiscal_period must be 1-12\"]"
}