- Introduced OpexValidationError for handling validation errors in processing opex calculations. - Implemented ProjectProcessingOpexRepository and ScenarioProcessingOpexRepository for managing project and scenario-level processing opex snapshots. - Enhanced UnitOfWork to include repositories for processing opex. - Updated sidebar navigation and scenario detail templates to include links to the new Processing Opex Planner. - Created a new template for the Processing Opex Planner with form handling for input components and parameters. - Developed integration tests for processing opex calculations, covering HTML and JSON flows, including validation for currency mismatches and unsupported frequencies. - Added unit tests for the calculation logic, ensuring correct handling of various scenarios and edge cases.
146 lines
5.3 KiB
HTML
146 lines
5.3 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}{{ scenario.name }} · Scenario · CalMiner{% endblock %}
|
|
|
|
{% block head_extra %}
|
|
<link rel="stylesheet" href="/static/css/scenarios.css" />
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<nav class="breadcrumb">
|
|
<a href="{{ url_for('projects.project_list_page') }}">Projects</a>
|
|
<a href="{{ url_for('projects.view_project', project_id=scenario.project_id) }}">{{ project.name }}</a>
|
|
<span aria-current="page">{{ scenario.name }}</span>
|
|
</nav>
|
|
|
|
<header class="page-header">
|
|
{% set profitability_href = url_for('calculations.profitability_form') %}
|
|
{% set processing_opex_href = url_for('calculations.processing_opex_form') %}
|
|
{% set capex_href = url_for('calculations.capex_form') %}
|
|
{% if project and scenario %}
|
|
{% set profitability_href = profitability_href ~ '?project_id=' ~ project.id ~ '&scenario_id=' ~ scenario.id %}
|
|
{% set processing_opex_href = processing_opex_href ~ '?project_id=' ~ project.id ~ '&scenario_id=' ~ scenario.id %}
|
|
{% set capex_href = capex_href ~ '?project_id=' ~ project.id ~ '&scenario_id=' ~ scenario.id %}
|
|
{% endif %}
|
|
<div>
|
|
<h1>{{ scenario.name }}</h1>
|
|
<p class="text-muted">Status: {{ scenario.status.value.title() }}</p>
|
|
</div>
|
|
<div class="header-actions">
|
|
<a class="btn" href="{{ url_for('projects.view_project', project_id=project.id) }}">Back to Project</a>
|
|
<a class="btn" href="{{ profitability_href }}">Profitability Calculator</a>
|
|
<a class="btn" href="{{ processing_opex_href }}">Processing Opex Planner</a>
|
|
<a class="btn" href="{{ capex_href }}">Initial Capex Planner</a>
|
|
<a class="btn primary" href="{{ url_for('scenarios.edit_scenario_form', scenario_id=scenario.id) }}">Edit Scenario</a>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="scenario-metrics">
|
|
<article class="metric-card">
|
|
<h2>Financial Inputs</h2>
|
|
<p class="metric-value">{{ scenario_metrics.financial_count }}</p>
|
|
<span class="metric-caption">Line items captured</span>
|
|
</article>
|
|
<article class="metric-card">
|
|
<h2>Simulation Parameters</h2>
|
|
<p class="metric-value">{{ scenario_metrics.parameter_count }}</p>
|
|
<span class="metric-caption">Inputs driving forecasts</span>
|
|
</article>
|
|
<article class="metric-card">
|
|
<h2>Currency</h2>
|
|
<p class="metric-value">{{ scenario_metrics.currency or '—' }}</p>
|
|
<span class="metric-caption">Financial reporting</span>
|
|
</article>
|
|
<article class="metric-card">
|
|
<h2>Primary Resource</h2>
|
|
<p class="metric-value">{{ scenario_metrics.primary_resource or '—' }}</p>
|
|
<span class="metric-caption">Scenario focus</span>
|
|
</article>
|
|
</section>
|
|
|
|
<div class="scenario-layout">
|
|
<section class="card">
|
|
<h2>Scenario Details</h2>
|
|
<dl class="definition-list">
|
|
<div>
|
|
<dt>Description</dt>
|
|
<dd>{{ scenario.description or 'No description provided.' }}</dd>
|
|
</div>
|
|
<div>
|
|
<dt>Timeline</dt>
|
|
<dd>
|
|
{{ scenario.start_date or '—' }} → {{ scenario.end_date or '—' }}
|
|
</dd>
|
|
</div>
|
|
<div>
|
|
<dt>Discount Rate</dt>
|
|
<dd>{{ scenario.discount_rate or '—' }}</dd>
|
|
</div>
|
|
<div>
|
|
<dt>Last Updated</dt>
|
|
<dd>{{ scenario.updated_at.strftime('%Y-%m-%d %H:%M') if scenario.updated_at else '—' }}</dd>
|
|
</div>
|
|
</dl>
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2>Financial Inputs</h2>
|
|
{% if financial_inputs %}
|
|
<div class="table-responsive">
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Category</th>
|
|
<th>Amount</th>
|
|
<th>Currency</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for item in financial_inputs %}
|
|
<tr>
|
|
<td>{{ item.name }}</td>
|
|
<td>{{ item.category.value.title() }}</td>
|
|
<td>{{ '{:,.2f}'.format(item.amount) }}</td>
|
|
<td>{{ item.currency or '—' }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<p class="empty-state">No financial inputs recorded yet.</p>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2>Simulation Parameters</h2>
|
|
{% if simulation_parameters %}
|
|
<div class="table-responsive">
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Distribution</th>
|
|
<th>Variable</th>
|
|
<th>Resource</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for param in simulation_parameters %}
|
|
<tr>
|
|
<td>{{ param.name }}</td>
|
|
<td>{{ param.distribution.value.title() }}</td>
|
|
<td>{{ param.variable.value.replace('_', ' ') | title if param.variable else '—' }}</td>
|
|
<td>{{ param.resource_type.value.replace('_', ' ') | title if param.resource_type else '—' }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<p class="empty-state">No simulation parameters defined.</p>
|
|
{% endif %}
|
|
</section>
|
|
</div>
|
|
{% endblock %}
|