This commit is contained in:
@@ -53,6 +53,37 @@ sys_log = SystemLogger("MonthlyRoutes").get_logger()
|
||||
monthly_bp = Blueprint('monthly', __name__)
|
||||
|
||||
|
||||
def _split_csv_param(value):
|
||||
if not value:
|
||||
return []
|
||||
return [item.strip() for item in value.split(',') if item.strip()]
|
||||
|
||||
|
||||
def _apply_monthly_filters(query, *, year=None, month=None, division=None, pm_name=None,
|
||||
brand_name=None, vendor_name=None, area_name=None,
|
||||
trade_type=None, ignore_year=False):
|
||||
if year and not ignore_year:
|
||||
query = query.filter(MonthlySummaryAnalysis.year == year)
|
||||
if month:
|
||||
query = query.filter(MonthlySummaryAnalysis.month == month)
|
||||
if division:
|
||||
query = query.filter(MonthlySummaryAnalysis.division == division)
|
||||
if pm_name:
|
||||
query = query.filter(MonthlySummaryAnalysis.pm_name == pm_name)
|
||||
if brand_name:
|
||||
query = query.filter(MonthlySummaryAnalysis.brand_name == brand_name)
|
||||
if vendor_name:
|
||||
query = query.filter(MonthlySummaryAnalysis.vendor_name == vendor_name)
|
||||
area_values = _split_csv_param(area_name)
|
||||
if len(area_values) > 1:
|
||||
query = query.filter(MonthlySummaryAnalysis.area_name.in_(area_values))
|
||||
elif len(area_values) == 1:
|
||||
query = query.filter(MonthlySummaryAnalysis.area_name == area_values[0])
|
||||
if trade_type:
|
||||
query = query.filter(MonthlySummaryAnalysis.trade_type == trade_type)
|
||||
return query
|
||||
|
||||
|
||||
# ==========================================
|
||||
# 頁面路由
|
||||
# ==========================================
|
||||
@@ -71,6 +102,57 @@ def monthly_summary_analysis_page():
|
||||
# API 路由
|
||||
# ==========================================
|
||||
|
||||
@monthly_bp.route('/api/monthly_summary_trend')
|
||||
@login_required
|
||||
def get_monthly_summary_trend():
|
||||
"""API: 取得月份總表輕量趨勢資料。"""
|
||||
year = request.args.get('year', type=int)
|
||||
month = request.args.get('month', type=int)
|
||||
division = request.args.get('division')
|
||||
pm_name = request.args.get('pm_name')
|
||||
brand_name = request.args.get('brand_name')
|
||||
vendor_name = request.args.get('vendor')
|
||||
area_name = request.args.get('area_name')
|
||||
trade_type = request.args.get('trade_type')
|
||||
|
||||
db = DatabaseManager()
|
||||
session = db.get_session()
|
||||
try:
|
||||
trend_query = session.query(
|
||||
MonthlySummaryAnalysis.year,
|
||||
MonthlySummaryAnalysis.month,
|
||||
func.sum(MonthlySummaryAnalysis.sales_amt_curr).label('sales')
|
||||
).group_by(
|
||||
MonthlySummaryAnalysis.year, MonthlySummaryAnalysis.month
|
||||
).order_by(MonthlySummaryAnalysis.year, MonthlySummaryAnalysis.month)
|
||||
|
||||
trend_query = _apply_monthly_filters(
|
||||
trend_query,
|
||||
year=year,
|
||||
month=month,
|
||||
division=division,
|
||||
pm_name=pm_name,
|
||||
brand_name=brand_name,
|
||||
vendor_name=vendor_name,
|
||||
area_name=area_name,
|
||||
trade_type=trade_type,
|
||||
)
|
||||
trend_results = trend_query.all()
|
||||
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'trend': [
|
||||
{'date': f"{r.year}/{r.month}", 'sales': int(r.sales or 0)}
|
||||
for r in trend_results
|
||||
],
|
||||
})
|
||||
except Exception as e:
|
||||
sys_log.error(f"取得月份總表趨勢資料失敗: {e}")
|
||||
return jsonify({'status': 'error', 'message': str(e)}), 500
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
@monthly_bp.route('/api/monthly_summary_data')
|
||||
@login_required
|
||||
def get_monthly_summary_data():
|
||||
|
||||
@@ -231,6 +231,7 @@ def test_ai_recommend_uses_v2_shell_and_runtime_category_data():
|
||||
def test_monthly_summary_analysis_uses_v2_shell_and_real_monthly_api():
|
||||
template = (ROOT / "templates/monthly_summary_analysis.html").read_text(encoding="utf-8")
|
||||
route_source = (ROOT / "routes/monthly_routes.py").read_text(encoding="utf-8")
|
||||
script = (ROOT / "web/static/js/page-monthly-summary.js").read_text(encoding="utf-8")
|
||||
|
||||
assert "{% extends 'ewoooc_base.html' %}" in template
|
||||
assert "{% block ewooo_content %}" in template
|
||||
@@ -241,6 +242,9 @@ def test_monthly_summary_analysis_uses_v2_shell_and_real_monthly_api():
|
||||
assert "monthly-analysis-hero" in template
|
||||
assert "monthly-analysis-page" in template
|
||||
assert "/api/monthly_summary_data" in template
|
||||
assert "/api/monthly_summary_trend" in route_source
|
||||
assert "/api/monthly_summary_trend" in script
|
||||
assert "area_name=${encodeURIComponent(area)}&limit=1" not in script
|
||||
assert "monthly_summary_analysis" in route_source
|
||||
assert "active_page='monthly'" in route_source
|
||||
assert "MonthlySummaryAnalysis" in route_source
|
||||
|
||||
@@ -79,9 +79,10 @@
|
||||
['私密保養,嬰幼洗沐', privacyInfantChart, 'privacyInfantChartTableBody']
|
||||
];
|
||||
pairs.forEach(([area, chart, tbody]) => {
|
||||
fetch(`/api/monthly_summary_data?area_name=${encodeURIComponent(area)}&limit=1`)
|
||||
fetch(`/api/monthly_summary_trend?area_name=${encodeURIComponent(area)}`)
|
||||
.then(r => r.json())
|
||||
.then(d => { if (d.status === 'success') renderExcelChart(chart, tbody, d.trend); });
|
||||
.then(d => { if (d.status === 'success') renderExcelChart(chart, tbody, d.trend); })
|
||||
.catch(err => console.error('monthly special trend failed', err));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user