feat: Add NPV comparison and distribution charts to reporting
- Implemented NPV comparison chart generation using Plotly in ReportingService. - Added distribution histogram for Monte Carlo results. - Updated reporting templates to include new charts and improved layout. - Created new settings and currencies management pages. - Enhanced sidebar navigation with dynamic URL handling. - Improved CSS styles for chart containers and overall layout. - Added new simulation and theme settings pages with placeholders for future features.
This commit is contained in:
@@ -1,205 +1,248 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Project Summary | CalMiner{% endblock %}
|
||||
{% extends "base.html" %} {% block title %}Project Summary | CalMiner{% endblock
|
||||
%} {% block content %} {% include "partials/reports_header.html" %} {% include
|
||||
"partials/reports/options_card.html" %} {% include
|
||||
"partials/reports/filters_card.html" %}
|
||||
|
||||
{% block content %}
|
||||
{% include "partials/reports_header.html" %}
|
||||
<section class="report-overview">
|
||||
<div class="report-grid">
|
||||
<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</dt>
|
||||
<dd>{{ scenario_count }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Created</dt>
|
||||
<dd>{{ project.created_at | format_datetime }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Updated</dt>
|
||||
<dd>{{ project.updated_at | format_datetime }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</article>
|
||||
|
||||
{% include "partials/reports/options_card.html" %}
|
||||
{% include "partials/reports/filters_card.html" %}
|
||||
<article class="report-card">
|
||||
<h2>Financial Summary</h2>
|
||||
<ul class="metric-list">
|
||||
<li>
|
||||
<span>Total Inflows</span>
|
||||
<strong
|
||||
>{{ aggregates.financials.total_inflows |
|
||||
currency_display(project.currency) }}</strong
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span>Total Outflows</span>
|
||||
<strong
|
||||
>{{ aggregates.financials.total_outflows |
|
||||
currency_display(project.currency) }}</strong
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span>Net Cash Flow</span>
|
||||
<strong
|
||||
>{{ aggregates.financials.total_net |
|
||||
currency_display(project.currency) }}</strong
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<section class="report-overview">
|
||||
<div class="report-grid">
|
||||
<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</dt>
|
||||
<dd>{{ scenario_count }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Created</dt>
|
||||
<dd>{{ project.created_at | format_datetime }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Updated</dt>
|
||||
<dd>{{ project.updated_at | format_datetime }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</article>
|
||||
<article class="report-card">
|
||||
<h2>Deterministic Metrics</h2>
|
||||
{% if aggregates.deterministic_metrics %}
|
||||
<table class="metrics-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Metric</th>
|
||||
<th scope="col">Average</th>
|
||||
<th scope="col">Best</th>
|
||||
<th scope="col">Worst</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, metric in aggregates.deterministic_metrics.items() %}
|
||||
<tr>
|
||||
<th scope="row">{{ key | replace("_", " ") | title }}</th>
|
||||
<td>{{ metric.average | format_metric(key, project.currency) }}</td>
|
||||
<td>{{ metric.maximum | format_metric(key, project.currency) }}</td>
|
||||
<td>{{ metric.minimum | format_metric(key, project.currency) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="muted">
|
||||
Deterministic metrics are unavailable for the current filters.
|
||||
</p>
|
||||
{% endif %}
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<article class="report-card">
|
||||
<h2>Financial Summary</h2>
|
||||
<ul class="metric-list">
|
||||
<section class="report-section">
|
||||
<header class="section-header">
|
||||
<h2>NPV Comparison</h2>
|
||||
<p class="section-subtitle">
|
||||
Visual comparison of Net Present Value across scenarios.
|
||||
</p>
|
||||
</header>
|
||||
<div id="npv-chart" class="chart-container"></div>
|
||||
</section>
|
||||
|
||||
<section class="report-section">
|
||||
<header class="section-header">
|
||||
<h2>Scenario Breakdown</h2>
|
||||
<p class="section-subtitle">
|
||||
Deterministic metrics and Monte Carlo summaries for each scenario.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
{% if scenarios %} {% 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 }} · {{ item.scenario.primary_resource
|
||||
or "No primary resource" }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="scenario-meta">
|
||||
<span class="meta-label">Currency</span>
|
||||
<span class="meta-value"
|
||||
>{{ item.scenario.currency or project.currency or "—" }}</span
|
||||
>
|
||||
</div>
|
||||
{% include "partials/reports/scenario_actions.html" %}
|
||||
</div>
|
||||
|
||||
<div class="scenario-grid">
|
||||
<section class="scenario-panel">
|
||||
<h4>Financial Totals</h4>
|
||||
<ul class="metric-list compact">
|
||||
<li>
|
||||
<span>Total Inflows</span>
|
||||
<strong>{{ aggregates.financials.total_inflows | currency_display(project.currency) }}</strong>
|
||||
<span>Inflows</span>
|
||||
<strong
|
||||
>{{ item.financials.inflows |
|
||||
currency_display(item.scenario.currency or project.currency)
|
||||
}}</strong
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span>Total Outflows</span>
|
||||
<strong>{{ aggregates.financials.total_outflows | currency_display(project.currency) }}</strong>
|
||||
<span>Outflows</span>
|
||||
<strong
|
||||
>{{ item.financials.outflows |
|
||||
currency_display(item.scenario.currency or project.currency)
|
||||
}}</strong
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span>Net Cash Flow</span>
|
||||
<strong>{{ aggregates.financials.total_net | currency_display(project.currency) }}</strong>
|
||||
<span>Net</span>
|
||||
<strong
|
||||
>{{ item.financials.net | currency_display(item.scenario.currency
|
||||
or project.currency) }}</strong
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article class="report-card">
|
||||
<h2>Deterministic Metrics</h2>
|
||||
{% if aggregates.deterministic_metrics %}
|
||||
<table class="metrics-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Metric</th>
|
||||
<th scope="col">Average</th>
|
||||
<th scope="col">Best</th>
|
||||
<th scope="col">Worst</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, metric in aggregates.deterministic_metrics.items() %}
|
||||
<tr>
|
||||
<th scope="row">{{ key | replace("_", " ") | title }}</th>
|
||||
<td>{{ metric.average | format_metric(key, project.currency) }}</td>
|
||||
<td>{{ metric.maximum | format_metric(key, project.currency) }}</td>
|
||||
<td>{{ metric.minimum | format_metric(key, project.currency) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<h5>By Category</h5>
|
||||
{% if item.financials.by_category %}
|
||||
<ul class="metric-list compact">
|
||||
{% for label, value in item.financials.by_category.items() %}
|
||||
<li>
|
||||
<span>{{ label | replace("_", " ") | title }}</span>
|
||||
<strong
|
||||
>{{ value | currency_display(item.scenario.currency or
|
||||
project.currency) }}</strong
|
||||
>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="muted">Deterministic metrics are unavailable for the current filters.</p>
|
||||
<p class="muted">No financial inputs recorded.</p>
|
||||
{% endif %}
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="scenario-panel">
|
||||
<h4>Deterministic Metrics</h4>
|
||||
<table class="metrics-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">Discount Rate</th>
|
||||
<td>{{ item.metrics.discount_rate | percentage_display }}</td>
|
||||
</tr>
|
||||
<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" %} {% else %}
|
||||
<p class="muted">
|
||||
Monte Carlo metrics are unavailable 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>
|
||||
</section>
|
||||
|
||||
<section class="report-section">
|
||||
<header class="section-header">
|
||||
<h2>Scenario Breakdown</h2>
|
||||
<p class="section-subtitle">Deterministic metrics and Monte Carlo summaries for each scenario.</p>
|
||||
</header>
|
||||
|
||||
{% if scenarios %}
|
||||
{% 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 }} · {{ item.scenario.primary_resource or "No primary resource" }}</p>
|
||||
</div>
|
||||
<div class="scenario-meta">
|
||||
<span class="meta-label">Currency</span>
|
||||
<span class="meta-value">{{ item.scenario.currency or project.currency or "—" }}</span>
|
||||
</div>
|
||||
{% include "partials/reports/scenario_actions.html" %}
|
||||
</div>
|
||||
|
||||
<div class="scenario-grid">
|
||||
<section class="scenario-panel">
|
||||
<h4>Financial Totals</h4>
|
||||
<ul class="metric-list compact">
|
||||
<li>
|
||||
<span>Inflows</span>
|
||||
<strong>{{ item.financials.inflows | currency_display(item.scenario.currency or project.currency) }}</strong>
|
||||
</li>
|
||||
<li>
|
||||
<span>Outflows</span>
|
||||
<strong>{{ item.financials.outflows | currency_display(item.scenario.currency or project.currency) }}</strong>
|
||||
</li>
|
||||
<li>
|
||||
<span>Net</span>
|
||||
<strong>{{ item.financials.net | currency_display(item.scenario.currency or project.currency) }}</strong>
|
||||
</li>
|
||||
</ul>
|
||||
<h5>By Category</h5>
|
||||
{% if item.financials.by_category %}
|
||||
<ul class="metric-list compact">
|
||||
{% for label, value in item.financials.by_category.items() %}
|
||||
<li>
|
||||
<span>{{ label | replace("_", " ") | title }}</span>
|
||||
<strong>{{ value | currency_display(item.scenario.currency or project.currency) }}</strong>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="muted">No financial inputs recorded.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="scenario-panel">
|
||||
<h4>Deterministic Metrics</h4>
|
||||
<table class="metrics-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">Discount Rate</th>
|
||||
<td>{{ item.metrics.discount_rate | percentage_display }}</td>
|
||||
</tr>
|
||||
<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" %}
|
||||
{% else %}
|
||||
<p class="muted">Monte Carlo metrics are unavailable 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 %}
|
||||
{% else %}
|
||||
<p class="muted">No scenarios match the current filters.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
</article>
|
||||
{% endfor %} {% else %}
|
||||
<p class="muted">No scenarios match the current filters.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
||||
<script>
|
||||
const chartData = {{ chart_data | safe }};
|
||||
if (chartData && chartData.data) {
|
||||
Plotly.newPlot('npv-chart', chartData.data, chartData.layout);
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,149 +1,177 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Scenario Distribution | CalMiner{% endblock %}
|
||||
{% extends "base.html" %} {% block title %}Scenario Distribution | CalMiner{%
|
||||
endblock %} {% block content %} {% include "partials/reports_header.html" %}
|
||||
|
||||
{% block content %}
|
||||
{% include "partials/reports_header.html" %}
|
||||
<section class="report-overview">
|
||||
<div class="report-grid">
|
||||
<article class="report-card">
|
||||
<h2>Scenario Details</h2>
|
||||
<dl class="definition-list">
|
||||
<div>
|
||||
<dt>Name</dt>
|
||||
<dd>{{ scenario.name }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Project ID</dt>
|
||||
<dd>{{ scenario.project_id }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Status</dt>
|
||||
<dd>{{ scenario.status | title }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Currency</dt>
|
||||
<dd>{{ scenario.currency or "—" }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Discount Rate</dt>
|
||||
<dd>{{ metrics.discount_rate | percentage_display }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Updated</dt>
|
||||
<dd>{{ scenario.updated_at | format_datetime }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</article>
|
||||
|
||||
<section class="report-overview">
|
||||
<div class="report-grid">
|
||||
<article class="report-card">
|
||||
<h2>Scenario Details</h2>
|
||||
<dl class="definition-list">
|
||||
<div>
|
||||
<dt>Name</dt>
|
||||
<dd>{{ scenario.name }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Project ID</dt>
|
||||
<dd>{{ scenario.project_id }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Status</dt>
|
||||
<dd>{{ scenario.status | title }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Currency</dt>
|
||||
<dd>{{ scenario.currency or "—" }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Discount Rate</dt>
|
||||
<dd>{{ metrics.discount_rate | percentage_display }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Updated</dt>
|
||||
<dd>{{ scenario.updated_at | format_datetime }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</article>
|
||||
|
||||
<article class="report-card">
|
||||
<h2>Financial Totals</h2>
|
||||
<ul class="metric-list">
|
||||
<li>
|
||||
<span>Inflows</span>
|
||||
<strong>{{ summary.inflows | currency_display(scenario.currency) }}</strong>
|
||||
</li>
|
||||
<li>
|
||||
<span>Outflows</span>
|
||||
<strong>{{ summary.outflows | currency_display(scenario.currency) }}</strong>
|
||||
</li>
|
||||
<li>
|
||||
<span>Net Cash Flow</span>
|
||||
<strong>{{ summary.net | currency_display(scenario.currency) }}</strong>
|
||||
</li>
|
||||
</ul>
|
||||
{% if summary.by_category %}
|
||||
<h3>By Category</h3>
|
||||
<ul class="metric-list compact">
|
||||
{% for label, value in summary.by_category.items() %}
|
||||
<li>
|
||||
<span>{{ label | replace("_", " ") | title }}</span>
|
||||
<strong>{{ value | currency_display(scenario.currency) }}</strong>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="report-section">
|
||||
<header class="section-header">
|
||||
<h2>Deterministic Metrics</h2>
|
||||
<p class="section-subtitle">Key financial indicators calculated from deterministic cash flows.</p>
|
||||
</header>
|
||||
<table class="metrics-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">NPV</th>
|
||||
<td>{{ metrics.npv | currency_display(scenario.currency) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">IRR</th>
|
||||
<td>{{ metrics.irr | percentage_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Payback Period</th>
|
||||
<td>{{ metrics.payback_period | period_display }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% if metrics.notes %}
|
||||
<ul class="note-list">
|
||||
{% for note in metrics.notes %}
|
||||
<li>{{ note }}</li>
|
||||
<article class="report-card">
|
||||
<h2>Financial Totals</h2>
|
||||
<ul class="metric-list">
|
||||
<li>
|
||||
<span>Inflows</span>
|
||||
<strong
|
||||
>{{ summary.inflows | currency_display(scenario.currency) }}</strong
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span>Outflows</span>
|
||||
<strong
|
||||
>{{ summary.outflows | currency_display(scenario.currency)
|
||||
}}</strong
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span>Net Cash Flow</span>
|
||||
<strong
|
||||
>{{ summary.net | currency_display(scenario.currency) }}</strong
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
{% if summary.by_category %}
|
||||
<h3>By Category</h3>
|
||||
<ul class="metric-list compact">
|
||||
{% for label, value in summary.by_category.items() %}
|
||||
<li>
|
||||
<span>{{ label | replace("_", " ") | title }}</span>
|
||||
<strong>{{ value | currency_display(scenario.currency) }}</strong>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="report-section">
|
||||
<header class="section-header">
|
||||
<h2>Monte Carlo Distribution</h2>
|
||||
<p class="section-subtitle">Simulation-driven distributions contextualize stochastic variability.</p>
|
||||
</header>
|
||||
{% if monte_carlo and monte_carlo.available %}
|
||||
<div class="simulation-summary">
|
||||
<p>Iterations: {{ monte_carlo.iterations }} · Percentiles: {{ percentiles | join(", ") }}</p>
|
||||
<table class="metrics-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Metric</th>
|
||||
<th scope="col">Mean</th>
|
||||
<th scope="col">P5</th>
|
||||
<th scope="col">Median</th>
|
||||
<th scope="col">P95</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for metric, summary in monte_carlo.metrics.items() %}
|
||||
<tr>
|
||||
<th scope="row">{{ metric | replace("_", " ") | title }}</th>
|
||||
<td>{{ summary.mean | format_metric(metric, scenario.currency) }}</td>
|
||||
<td>{{ summary.percentiles['5'] | format_metric(metric, scenario.currency) }}</td>
|
||||
<td>{{ summary.percentiles['50'] | format_metric(metric, scenario.currency) }}</td>
|
||||
<td>{{ summary.percentiles['95'] | format_metric(metric, scenario.currency) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if monte_carlo.notes %}
|
||||
<ul class="note-list">
|
||||
{% for note in monte_carlo.notes %}
|
||||
<li>{{ note }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="muted">Monte Carlo output is unavailable for this scenario.</p>
|
||||
{% if monte_carlo and monte_carlo.notes %}
|
||||
<ul class="note-list">
|
||||
{% for note in monte_carlo.notes %}
|
||||
<li>{{ note }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="report-section">
|
||||
<header class="section-header">
|
||||
<h2>Deterministic Metrics</h2>
|
||||
<p class="section-subtitle">
|
||||
Key financial indicators calculated from deterministic cash flows.
|
||||
</p>
|
||||
</header>
|
||||
<table class="metrics-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">NPV</th>
|
||||
<td>{{ metrics.npv | currency_display(scenario.currency) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">IRR</th>
|
||||
<td>{{ metrics.irr | percentage_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Payback Period</th>
|
||||
<td>{{ metrics.payback_period | period_display }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% if metrics.notes %}
|
||||
<ul class="note-list">
|
||||
{% for note in metrics.notes %}
|
||||
<li>{{ note }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
<section class="report-section">
|
||||
<header class="section-header">
|
||||
<h2>Monte Carlo Distribution</h2>
|
||||
<p class="section-subtitle">
|
||||
Simulation-driven distributions contextualize stochastic variability.
|
||||
</p>
|
||||
</header>
|
||||
{% if monte_carlo and monte_carlo.available %}
|
||||
<div id="distribution-chart" class="chart-container"></div>
|
||||
<div class="simulation-summary">
|
||||
<p>
|
||||
Iterations: {{ monte_carlo.iterations }} · Percentiles: {{ percentiles |
|
||||
join(", ") }}
|
||||
</p>
|
||||
<table class="metrics-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Metric</th>
|
||||
<th scope="col">Mean</th>
|
||||
<th scope="col">P5</th>
|
||||
<th scope="col">Median</th>
|
||||
<th scope="col">P95</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for metric, summary in monte_carlo.metrics.items() %}
|
||||
<tr>
|
||||
<th scope="row">{{ metric | replace("_", " ") | title }}</th>
|
||||
<td>{{ summary.mean | format_metric(metric, scenario.currency) }}</td>
|
||||
<td>
|
||||
{{ summary.percentiles['5'] | format_metric(metric,
|
||||
scenario.currency) }}
|
||||
</td>
|
||||
<td>
|
||||
{{ summary.percentiles['50'] | format_metric(metric,
|
||||
scenario.currency) }}
|
||||
</td>
|
||||
<td>
|
||||
{{ summary.percentiles['95'] | format_metric(metric,
|
||||
scenario.currency) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if monte_carlo.notes %}
|
||||
<ul class="note-list">
|
||||
{% for note in monte_carlo.notes %}
|
||||
<li>{{ note }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</section>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="muted">Monte Carlo output is unavailable for this scenario.</p>
|
||||
{% if monte_carlo and monte_carlo.notes %}
|
||||
<ul class="note-list">
|
||||
{% for note in monte_carlo.notes %}
|
||||
<li>{{ note }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %} {% endif %}
|
||||
</section>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
||||
<script>
|
||||
const chartData = {{ chart_data | safe }};
|
||||
if (chartData && chartData.data) {
|
||||
Plotly.newPlot('distribution-chart', chartData.data, chartData.layout);
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user