# Budget

## Description

Budget is the financial planning entity of the accounting module, enabling organizations to create budgets with line items per cost element, manage approval workflows, track multiple budget versions, and compare budgeted amounts against actual postings from finance-ledger. Each budget carries a name, optional description, budget holder, fiscal year reference, company scope, cost object type and id (polymorphic reference to cost center, profit center, or internal order), plan version reference, total amount, and status. Budgets follow a five-stage lifecycle — DRAFT, SUBMITTED, APPROVED, ACTIVE, CLOSED — enforcing segregation of duties between budget creation, approval, and cost posting. When active, budgets enable budgetary control (funds check) that validates transactions against available budget using the formula: Available = Budget - (Actual + Commitments + Encumbrances).

## Domain Model Definitions

### Model type

Stateful

#### State Transitions

```mermaid
stateDiagram-v2
    [*] --> Draft: createBudget
    Draft --> Draft: updateBudget / createBudgetLineItem / deleteBudgetLineItem
    Draft --> Submitted: submitBudget
    Submitted --> Approved: approveBudget
    Submitted --> Draft: rejectBudget
    Approved --> Active: activateBudget
    Active --> Closed: closeBudget
    Draft --> [*]: deleteBudget
```

### Command Definitions

- [createBudget](../commands/CreateBudget.md) - Create a new budget in DRAFT status with name, budget holder, fiscal year, cost object, and plan version
- [updateBudget](../commands/UpdateBudget.md) - Update mutable fields of a DRAFT budget (name, description, budget holder)
- [submitBudget](../commands/SubmitBudget.md) - Submit a DRAFT budget for approval review
- [approveBudget](../commands/ApproveBudget.md) - Approve a SUBMITTED budget, advancing it toward activation
- [rejectBudget](../commands/RejectBudget.md) - Reject a SUBMITTED budget back to DRAFT with a rejection reason
- [activateBudget](../commands/ActivateBudget.md) - Activate an APPROVED budget, enabling budgetary control (funds check)
- [closeBudget](../commands/CloseBudget.md) - Close an ACTIVE budget, preventing further modifications
- [deleteBudget](../commands/DeleteBudget.md) - Delete a DRAFT budget and all associated line items
- [createBudgetLineItem](../commands/CreateBudgetLineItem.md) - Add a budget line item to a DRAFT budget specifying cost element, period, and planned amount
- [deleteBudgetLineItem](../commands/DeleteBudgetLineItem.md) - Remove a budget line item from a DRAFT budget

### Query Definitions

- [GetBudget](../queries/GetBudget.md) - Retrieve a budget by id with all line items
- [ListBudgets](../queries/ListBudgets.md) - List budgets with filters (status, company, fiscal year, cost object)
- [CalculateBudgetAvailability](../queries/CalculateBudgetAvailability.md) - Compute budget availability as Budget - (Actual + Commitments + Encumbrances) for a cost element, period, and cost object

### Models

- Budget
- BudgetLineItem

### Invariants

- Budget can only be created in DRAFT status
- Budget requires a valid companyId; the company must be in ACTIVE status
- Budget requires a valid fiscal year reference from finance-ledger
- Budget requires a name, budget holder, and cost object reference (cost center, profit center, or internal order)
- Budget must reference a valid PlanVersion (BUDGET, FORECAST, or SIMULATION)
- A budget with no line items cannot be submitted
- Only a user different from the budget creator can approve the budget (segregation of duties)
- Rejection requires a reason text
- Only one ACTIVE budget of type BUDGET can exist per cost object per fiscal year
- Multiple FORECAST or SIMULATION versions can coexist for the same cost object and fiscal year
- Line items can only be added to or removed from a DRAFT budget
- A CLOSED budget cannot be reopened; no further modifications are permitted
- Funds check computes BudgetAvailability as Budget - (Actual + Commitments + Encumbrances) for the target cost element, period, and cost object
- A transaction that exceeds BudgetAvailability is rejected with a budget exceeded error when budgetary control is active
- Budget periods align with finance-ledger AccountingPeriod boundaries; misaligned periods are rejected
- Locking a budget period prevents further modifications to planned amounts for that period
- Budgets are scoped to a company; budgets from different companies are fully isolated

### Relationships

- **References Company**: Each budget is scoped to a Company via companyId (organization module)
- **References FiscalYear**: Each budget references a FiscalYear via fiscalYearId (finance-ledger module)
- **References PlanVersion**: Each budget references a PlanVersion via planVersionId
- **Polymorphic Cost Object**: Each budget references a cost object via costObjectType and costObjectId, which can be a CostCenter, ProfitCenter, or InternalOrder
- **Has Many BudgetLineItem**: A budget contains one or more budget line items specifying planned amounts per cost element and period
