feat: add functional requirements for profitability, Monte Carlo simulation, and Capex/Opex management; enhance user guide with planners

This commit is contained in:
2025-11-13 09:20:10 +01:00
parent d3597bc8c9
commit fb6be6d84f
11 changed files with 526 additions and 24 deletions

View File

@@ -1,3 +1,8 @@
# User Guide
<!-- TODO: Create a user guide for Calminer. This should include instructions on how to use the various features of the application, along with screenshots and examples where applicable. -->
CalMiner user-facing documentation is organized by feature area. Start with the guides below and expand the repository as new workflows ship.
## Available Guides
- [Initial Capex Planner](initial_capex_planner.md) — capture upfront capital components, run calculations, and persist snapshots for projects and scenarios.
- [Processing Opex Planner](processing_opex_planner.md) — manage recurring operational costs, apply escalation/discount assumptions, and store calculation snapshots.

View File

@@ -0,0 +1,59 @@
# Initial Capex Planner
The Initial Capex Planner helps project teams capture upfront capital requirements, categorize spend, and produce a shareable breakdown that feeds downstream profitability analysis. The feature implements the acceptance criteria described in [FR-013](../requirements/FR-013.md) and persists calculation snapshots for both projects and scenarios when context is provided.
## Access Paths
- **Workspace sidebar**: Navigate to _Workspace → Initial Capex Planner_.
- **Scenario detail page**: Use the _Initial Capex Planner_ button in the scenario header to open the planner pre-loaded with the selected project and scenario.
## Prerequisites
- Authenticated CalMiner account with at least _viewer_ permissions on the target project/scenario.
- Baseline pricing settings seeded through the database initializer (the GET route resolves pricing metadata for defaults).
## Planner Workflow
1. **Open the planner** via the sidebar or scenario action button. Query parameters (`project_id`, `scenario_id`) preload metadata for the selected context.
2. **Review defaults** in the form header: currency code, contingency, discount rate, and evaluation horizon pull from the scenario, project, or system defaults.
3. **Capture capex components** by adding rows to the components table. Each row records:
- Category (equipment, infrastructure, land, miscellaneous, etc.)
- Component name and optional internal identifier
- Amount and currency (defaults to scenario or project currency)
- Planned spend year and notes
4. **Adjust global parameters** in the sidebar panel for contingency percentage, discount rate, and evaluation horizon years.
5. **Run the calculation** with _Save & Calculate_. The POST request validates the payload, aggregates totals, applies contingency, and produces categorized and time-phased results.
6. **Review results** in the Capex Summary cards, category table, and timeline table. Optional chart containers are rendered for future visualization enhancements.
## Persistence Behaviour
- When a `project_id` or `scenario_id` is supplied, the POST handler stores a snapshot via `ProjectCapexSnapshot` and/or `ScenarioCapexSnapshot` before rendering results.
- Snapshots include aggregate totals, contingency metrics, component counts, and the normalized payload, enabling auditing and downstream financial modelling.
- JSON clients can set `options[persist]=0` to skip persistence in future iterations (HTML form defaults to persist enabled).
## API Reference
| Route | Method | Description |
| --------------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `/calculations/capex` (`calculations.capex_form`) | GET | Renders the planner template with defaults for the provided project/scenario context. |
| `/calculations/capex` (`calculations.capex_submit`) | POST | Accepts form or JSON payload matching `CapexCalculationRequest`, returns HTML or JSON results, and persists snapshots when context is present. |
Key schemas and services:
- `schemas.calculations.CapexCalculationRequest`, `CapexComponentInput`, `CapexParameters`
- `services.calculations.calculate_initial_capex`
- `_persist_capex_snapshots` helper in `routes/calculations.py`
Refer to `tests/integration/test_capex_calculations.py` for example payloads and persistence assertions.
## Troubleshooting
- **Validation errors**: Field-level messages appear above the components table for row-specific issues and in the global error alert for general problems.
- **Currency mismatch**: The service enforces a single currency across all components; ensure component rows use the same ISO-4217 code or adjust the default currency.
- **Timeline gaps**: Rows without a `spend_year` are excluded from the timeline but still contribute to totals.
## Related Resources
- [Initial Capex Planner template](../../calminer/templates/scenarios/capex.html)
- [Capex snapshot models](../../calminer/models/capex_snapshot.py)
- [FR-013 Requirement](../requirements/FR-013.md)

View File

@@ -0,0 +1,114 @@
# Processing Opex Planner
The Processing Opex Planner captures recurring operational costs, applies escalation/discount assumptions, and persists calculation snapshots for projects and scenarios. It satisfies the Operational Expenditure tooling defined in [FR-014](../requirements/FR-014.md).
## Access Paths
- **Workspace sidebar**: Navigate to _Workspace → Processing Opex Planner_.
- **Scenario detail header**: Use the _Processing Opex Planner_ button to open the planner pre-loaded with the active project/scenario context.
## Prerequisites
- Authenticated CalMiner account with viewer or higher access to the target project or scenario.
- Baseline scenario/project metadata (currency, discount rate, primary resource) to seed form defaults when IDs are provided.
## Planner Workflow
1. **Open the planner** via the sidebar or scenario action. Query parameters (`project_id`, `scenario_id`) load project and scenario metadata plus the latest persisted snapshot, when available.
2. **Review defaults** in the global parameters panel. Currency, discount rate, evaluation horizon, and persist toggle derive from the scenario, project, or system defaults surfaced by `_prepare_opex_context` in `routes/calculations.py`.
3. **Capture opex components** in the components table. Each row records the details listed in the _Component Fields_ table below. Rows support dynamic add/remove interactions and inline validation messages for missing data.
4. **Adjust global parameters** such as escalation percentage, discount rate, evaluation horizon, and whether escalation applies to the timeline. Parameter defaults derive from the active scenario, project, or environment configuration.
5. **Add optional snapshot notes** that persist alongside stored results and appear in the snapshot history UI (planned) and API responses.
6. **Run the calculation** with _Save & Calculate_. The POST handler validates input via `ProcessingOpexCalculationRequest`, calls `services.calculations.calculate_processing_opex`, and repopulates the form with normalized data. When validation fails, the page returns a 422 status with component-level alerts.
7. **Review results** in the summary cards and detailed breakdowns: total annual opex, per-ton cost, category totals, escalated timeline table, and the normalized component listing. Alerts surface validation issues or component-level notices above the table.
### Component Fields
| Field | Description | Notes |
| -------------- | --------------------------------------------------------------------------- | ------------------------------------------------------ |
| `category` | Cost grouping (`labor`, `materials`, `energy`, `maintenance`, `other`). | Drives category totals and stacked charts. |
| `name` | Descriptive label for the component. | Displayed in breakdown tables and persisted snapshots. |
| `unit_cost` | Cost per unit in the selected currency. | Decimal, >= 0. |
| `quantity` | Units consumed per frequency period. | Decimal, >= 0. |
| `frequency` | Recurrence cadence (`daily`, `weekly`, `monthly`, `quarterly`, `annually`). | Controls timeline expansion scaling. |
| `currency` | ISO-4217 currency code. | Must match parameters currency when persisting. |
| `period_start` | First evaluation period (1-indexed) to include the component. | Optional; defaults to 1. |
| `period_end` | Final evaluation period to include the component. | Optional; defaults to evaluation horizon. |
| `notes` | Free-form text stored with the component. | Optional; hidden by default in HTML form. |
### Global Parameters
| Parameter | Purpose |
| -------------------------- | --------------------------------------------------------------------------------- |
| `currency_code` | Normalized currency for totals and timeline. |
| `escalation_pct` | Annual escalation applied to eligible components when `apply_escalation` is true. |
| `discount_rate_pct` | Discount rate surfaced for downstream profitability workflows. |
| `evaluation_horizon_years` | Number of years to expand the timeline. |
| `apply_escalation` | Boolean flag enabling escalation across the timeline. |
| `persist` (options) | Persists the calculation when project/scenario context is present. |
| `snapshot_notes` (options) | Optional metadata attached to stored snapshots. |
## Persistence Behaviour
- When `project_id` or `scenario_id` is supplied and `options[persist]` evaluates true (default for HTML form), snapshots are stored via `ProjectProcessingOpexSnapshot` and `ScenarioProcessingOpexSnapshot` repositories before rendering the response.
- Snapshot payloads capture normalized component entries, parameters, escalation settings, calculated totals, and optional notes, enabling historical comparison and downstream profitability inputs.
- JSON clients can disable persistence by sending `"options": {"persist": false}` or omit identifiers for ad hoc calculations.
## API Reference
| Route | Method | Description |
| ----------------------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/calculations/processing-opex` (`calculations.processing_opex_form`) | GET | Renders the planner template with defaults and any latest snapshot context for the supplied project/scenario IDs. |
| `/calculations/processing-opex` (`calculations.processing_opex_submit`) | POST | Accepts form or JSON payload matching `ProcessingOpexCalculationRequest`, returns HTML or JSON results, and persists snapshots when context is present. |
Key schemas and services:
- `schemas.calculations.ProcessingOpexComponentInput`, `ProcessingOpexParameters`, `ProcessingOpexCalculationRequest`, `ProcessingOpexCalculationResult`
- `services.calculations.calculate_processing_opex`
- `_prepare_opex_context`, `_persist_opex_snapshots`, and related helpers in `routes/calculations.py`
### Example JSON Request
```json
{
"components": [
{
"category": "energy",
"name": "Processing Plant Power",
"unit_cost": "480.00",
"quantity": "1.0",
"frequency": "monthly",
"currency": "USD",
"period_start": 1,
"period_end": 12
}
],
"parameters": {
"currency_code": "USD",
"escalation_pct": "3.0",
"discount_rate_pct": "8.0",
"evaluation_horizon_years": 10,
"apply_escalation": true
},
"options": {
"persist": true,
"snapshot_notes": "Baseline processing OPEX"
}
}
```
The response payload mirrors `ProcessingOpexCalculationResult`, returning normalized components, aggregated totals, timeline series, and snapshot metadata when persistence is enabled.
## Troubleshooting
- **Validation errors**: The planner surfaces field-level issues above the component table. JSON responses include `errors` and `message` keys mirroring Pydantic validation output.
- **Currency mismatch**: All component rows must share the same currency. Adjust row currencies or the default currency in the parameters panel to resolve mismatches enforced by the service layer.
- **Timeline coverage**: Ensure `period_start` and `period_end` fall within the evaluation horizon. Rows outside the horizon are ignored in the timeline though they still influence totals.
## Related Resources
- [Processing Opex planner template](../../calminer/templates/scenarios/opex.html)
- [Calculations route handlers](../../calminer/routes/calculations.py)
- [Opex schemas and results](../../calminer/schemas/calculations.py)
- [Service implementation and tests](../../calminer/services/calculations.py), [tests/services/test_calculations_processing_opex.py](../../calminer/tests/services/test_calculations_processing_opex.py)
- [Integration coverage](../../calminer/tests/integration/test_processing_opex_calculations.py)