185 lines
7.3 KiB
HTML
185 lines
7.3 KiB
HTML
{% extends "ewoooc_base.html" %}
|
|
{% block title %}ABC 分析詳情 - {{ info.title if info else '資料準備中' }} - EwoooC{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<link rel="stylesheet" href="https://cdn.datatables.net/1.11.5/css/dataTables.bootstrap5.min.css">
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/page-abc-analysis-detail.css') }}">
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<section class="abc-detail-page">
|
|
{% if loading_state|default(false) %}
|
|
<div class="abc-loading-card">
|
|
<div class="abc-loading-spinner" aria-hidden="true"></div>
|
|
<div>
|
|
<h1 class="h4 mb-2">數據準備中</h1>
|
|
<p class="text-muted mb-0">正在重新載入業績分析快取,稍後會自動重試。</p>
|
|
</div>
|
|
<a class="btn btn-outline-secondary" href="/sales_analysis">回業績分析</a>
|
|
</div>
|
|
{% else %}
|
|
<header class="abc-detail-header">
|
|
<div>
|
|
<span class="abc-detail-kicker">
|
|
<i class="fas fa-layer-group" aria-hidden="true"></i>
|
|
ABC Analysis
|
|
</span>
|
|
<h1>{{ info.title }}</h1>
|
|
<p>{{ info.desc }}</p>
|
|
<div class="abc-detail-statline">
|
|
<span class="abc-detail-pill">
|
|
<i class="fas fa-coins" aria-hidden="true"></i>
|
|
總營收 ${{ "{:,.0f}".format(total_revenue|default(0)) }}
|
|
</span>
|
|
<span class="abc-detail-pill">
|
|
<i class="fas fa-cubes" aria-hidden="true"></i>
|
|
{{ items|length }} 個商品
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="abc-detail-actions">
|
|
<div class="abc-factor-control input-group input-group-sm">
|
|
<span class="input-group-text">補貨 x</span>
|
|
<input type="number" id="restockFactor" class="form-control text-center" value="{{ current_factor }}" step="0.1" min="0" aria-label="補貨係數">
|
|
<button class="btn btn-outline-primary" type="button" onclick="applyFactor()">套用</button>
|
|
</div>
|
|
<a href="/api/export/excel/abc?class={{ target_class }}&factor={{ current_factor }}&{{ query_string }}" class="btn btn-primary btn-sm">
|
|
<i class="fas fa-file-excel me-2" aria-hidden="true"></i>匯出報表
|
|
</a>
|
|
<a href="/sales_analysis" class="btn btn-outline-secondary btn-sm">
|
|
<i class="fas fa-arrow-left me-2" aria-hidden="true"></i>回分析頁
|
|
</a>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="abc-detail-card">
|
|
<div class="abc-detail-card__head">
|
|
<h2>商品明細</h2>
|
|
<span class="abc-detail-pill">{{ target_class }} 類排序</span>
|
|
</div>
|
|
<div class="abc-detail-table">
|
|
<div class="table-responsive">
|
|
<table id="dataTable" class="table table-hover align-middle mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th class="text-center">排名</th>
|
|
{% if cols.pid %}<th>商品 ID</th>{% endif %}
|
|
<th>商品名稱</th>
|
|
{% if cols.brand %}<th>品牌</th>{% endif %}
|
|
{% if cols.vendor %}<th>廠商名稱</th>{% endif %}
|
|
{% if cols.cat %}<th>分類</th>{% endif %}
|
|
{% if cols.cost or cols.profit %}<th>毛利率</th>{% endif %}
|
|
{% if cols.qty %}<th>平均單價</th>{% endif %}
|
|
{% if cols.qty %}<th class="text-end">銷售數量</th>{% endif %}
|
|
{% if cols.qty %}<th class="text-end">建議補貨</th>{% endif %}
|
|
<th class="text-end">銷售金額</th>
|
|
<th class="text-end">累積營收佔比</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for item in items %}
|
|
<tr>
|
|
<td class="text-center fw-bold text-muted">{{ loop.index }}</td>
|
|
{% if cols.pid %}
|
|
<td class="small text-muted">{{ item[cols.pid] }}</td>
|
|
{% endif %}
|
|
<td>
|
|
<div class="abc-product-name" title="{{ item[cols.name] | e }}">
|
|
{{ item[cols.name] | e }}
|
|
</div>
|
|
</td>
|
|
{% if cols.brand %}
|
|
<td class="small text-muted">{{ item[cols.brand] }}</td>
|
|
{% endif %}
|
|
{% if cols.vendor %}
|
|
<td class="small text-muted">{{ item[cols.vendor] }}</td>
|
|
{% endif %}
|
|
{% if cols.cat %}
|
|
<td><span class="badge bg-light text-dark border">{{ item[cols.cat] }}</span></td>
|
|
{% endif %}
|
|
{% if cols.cost or cols.profit %}
|
|
<td>
|
|
{% set margin = item['calculated_margin_rate'] %}
|
|
<span class="{{ 'text-success' if margin >= 30 else ('text-danger' if margin < 10 else 'text-dark') }} fw-bold">
|
|
{{ "{:.1f}%".format(margin) }}
|
|
</span>
|
|
</td>
|
|
{% endif %}
|
|
{% if cols.qty %}
|
|
<td class="small">${{ "{:,.0f}".format(item['avg_unit_price']) }}</td>
|
|
<td class="text-end">{{ "{:,.0f}".format(item[cols.qty]) }}</td>
|
|
<td class="text-end fw-bold">
|
|
{% if item['suggested_restock'] > 0 %}
|
|
{{ "{:,.0f}".format(item['suggested_restock']) }}
|
|
{% else %}
|
|
<span class="text-muted small">建議清倉</span>
|
|
{% endif %}
|
|
</td>
|
|
{% endif %}
|
|
<td class="text-end fw-bold text-danger">
|
|
${{ "{:,.0f}".format(item[cols.amount]) }}
|
|
</td>
|
|
<td class="text-end text-muted small">
|
|
{{ "{:.2f}%".format(item['cumulative_pct']) }}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</section>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
{% if loading_state|default(false) %}
|
|
<script>
|
|
setTimeout(function () {
|
|
window.location.reload();
|
|
}, 1500);
|
|
|
|
const retryCount = parseInt(sessionStorage.getItem('abc_retry') || '0', 10);
|
|
if (retryCount > 3) {
|
|
sessionStorage.removeItem('abc_retry');
|
|
window.location.href = '/sales_analysis';
|
|
} else {
|
|
sessionStorage.setItem('abc_retry', String(retryCount + 1));
|
|
}
|
|
</script>
|
|
{% else %}
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
<script src="https://cdn.datatables.net/1.11.5/js/jquery.dataTables.min.js"></script>
|
|
<script src="https://cdn.datatables.net/1.11.5/js/dataTables.bootstrap5.min.js"></script>
|
|
<script>
|
|
const sortColIndex = parseInt("{{ sort_col_index }}", 10);
|
|
|
|
$(document).ready(function () {
|
|
$('#dataTable').DataTable({
|
|
language: {
|
|
url: '//cdn.datatables.net/plug-ins/1.11.5/i18n/zh-HANT.json',
|
|
paginate: {
|
|
previous: '上一頁',
|
|
next: '下一頁'
|
|
}
|
|
},
|
|
pageLength: 25,
|
|
lengthMenu: [[10, 25, 50, 100, -1], [10, 25, 50, 100, '全部']],
|
|
order: [[sortColIndex, 'desc']],
|
|
paging: true,
|
|
info: true
|
|
});
|
|
});
|
|
|
|
function applyFactor() {
|
|
const factor = document.getElementById('restockFactor').value;
|
|
const url = new URL(window.location.href);
|
|
url.searchParams.set('factor', factor);
|
|
window.location.href = url.toString();
|
|
}
|
|
</script>
|
|
{% endif %}
|
|
{% endblock %}
|