feat: Enhance currency handling and validation across scenarios
- Updated form template to prefill currency input with default value and added help text for clarity. - Modified integration tests to assert more descriptive error messages for invalid currency codes. - Introduced new tests for currency normalization and validation in various scenarios, including imports and exports. - Added comprehensive tests for pricing calculations, ensuring defaults are respected and overrides function correctly. - Implemented unit tests for pricing settings repository, ensuring CRUD operations and default settings are handled properly. - Enhanced scenario pricing evaluation tests to validate currency handling and metadata defaults. - Added simulation tests to ensure Monte Carlo runs are accurate and handle various distribution scenarios.
This commit is contained in:
166
templates/reports/scenario_comparison.html
Normal file
166
templates/reports/scenario_comparison.html
Normal file
@@ -0,0 +1,166 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Scenario Comparison | CalMiner{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include "partials/reports_header.html" with context %}
|
||||
|
||||
{% include "partials/reports/options_card.html" with options=include_options iterations=iterations percentiles=percentiles %}
|
||||
<section class="report-filters">
|
||||
<div class="report-card">
|
||||
<h2>Compared Scenarios</h2>
|
||||
<ul class="metric-list compact">
|
||||
{% for item in scenarios %}
|
||||
<li>
|
||||
<span>{{ item.scenario.name }}</span>
|
||||
<strong>#{{ item.scenario.id }}</strong>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="report-overview">
|
||||
<article class="report-card">
|
||||
<h2>Project Details</h2>
|
||||
<dl class="definition-list">
|
||||
<div>
|
||||
<dt>Name</dt>
|
||||
<dd>{{ project.name }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Location</dt>
|
||||
<dd>{{ project.location or "—" }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Operation Type</dt>
|
||||
<dd>{{ project.operation_type | replace("_", " ") | title }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Scenarios Compared</dt>
|
||||
<dd>{{ scenarios | length }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</article>
|
||||
|
||||
<article class="report-card">
|
||||
<h2>Comparison Summary</h2>
|
||||
{% if comparison %}
|
||||
<table class="metrics-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Metric</th>
|
||||
<th scope="col">Direction</th>
|
||||
<th scope="col">Best Performer</th>
|
||||
<th scope="col">Worst Performer</th>
|
||||
<th scope="col">Average</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, metric in comparison.items() %}
|
||||
<tr>
|
||||
<th scope="row">{{ key | replace("_", " ") | title }}</th>
|
||||
<td>{{ metric.direction | replace("_", " ") | title }}</td>
|
||||
<td>
|
||||
{% if metric.best %}
|
||||
<strong>{{ metric.best.name }}</strong>
|
||||
<span class="muted">({{ metric.best.value | format_metric(key, project.currency) }})</span>
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if metric.worst %}
|
||||
<strong>{{ metric.worst.name }}</strong>
|
||||
<span class="muted">({{ metric.worst.value | format_metric(key, project.currency) }})</span>
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ metric.average | format_metric(key, project.currency) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="muted">No deterministic metrics available for comparison.</p>
|
||||
{% endif %}
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="report-section">
|
||||
<header class="section-header">
|
||||
<h2>Scenario Details</h2>
|
||||
<p class="section-subtitle">Each scenario includes deterministic metrics and Monte Carlo summaries.</p>
|
||||
</header>
|
||||
|
||||
{% for item in scenarios %}
|
||||
<article class="scenario-card">
|
||||
<div class="scenario-card-header">
|
||||
<div>
|
||||
<h3>{{ item.scenario.name }}</h3>
|
||||
<p class="muted">{{ item.scenario.status | title }} · Currency: {{ item.scenario.currency or project.currency }}</p>
|
||||
</div>
|
||||
<div class="scenario-meta">
|
||||
<span class="meta-label">Primary Resource</span>
|
||||
<span class="meta-value">{{ item.scenario.primary_resource or "—" }}</span>
|
||||
</div>
|
||||
{% include "partials/reports/scenario_actions.html" with scenario=item.scenario %}
|
||||
</div>
|
||||
|
||||
<div class="scenario-grid">
|
||||
<section class="scenario-panel">
|
||||
<h4>Deterministic Metrics</h4>
|
||||
<table class="metrics-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">NPV</th>
|
||||
<td>{{ item.metrics.npv | currency_display(item.scenario.currency or project.currency) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">IRR</th>
|
||||
<td>{{ item.metrics.irr | percentage_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Payback Period</th>
|
||||
<td>{{ item.metrics.payback_period | period_display }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% if item.metrics.notes %}
|
||||
<ul class="note-list">
|
||||
{% for note in item.metrics.notes %}
|
||||
<li>{{ note }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="scenario-panel">
|
||||
<h4>Monte Carlo Summary</h4>
|
||||
{% if item.monte_carlo and item.monte_carlo.available %}
|
||||
<p class="muted">
|
||||
Iterations: {{ item.monte_carlo.iterations }}
|
||||
{% if percentiles %}
|
||||
· Percentiles:
|
||||
{% for percentile in percentiles %}
|
||||
{{ '%g' % percentile }}{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% include "partials/reports/monte_carlo_table.html" with metrics=item.monte_carlo.metrics currency=item.scenario.currency or project.currency percentiles=percentiles %}
|
||||
{% else %}
|
||||
<p class="muted">No Monte Carlo data available for this scenario.</p>
|
||||
{% if item.monte_carlo and item.monte_carlo.notes %}
|
||||
<ul class="note-list">
|
||||
{% for note in item.monte_carlo.notes %}
|
||||
<li>{{ note }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</section>
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user