fix(analytics): harden period-linked sales queries

This commit is contained in:
ogt
2026-07-22 17:59:10 +08:00
parent 9b677ae616
commit f5df7bb1eb
8 changed files with 1529 additions and 909 deletions

View File

@@ -1,4 +1,4 @@
from datetime import date
from datetime import date, datetime as real_datetime
from pathlib import Path
import pandas as pd
@@ -297,6 +297,9 @@ def test_sales_frontend_uses_live_period_linked_api_routes():
assert "/api/sales_analysis/yoy_comparison" in script
assert "/api/sales_analysis/table_data" in script
assert "/api/sales_analysis/top_detail" in script
assert "/api/sales_analysis/export_vendor" in template
assert "/api/sales_analysis/export_marketing" in script
assert "/api/export/excel/marketing" not in script
assert "topDetailModal" in script
assert "window.open(url.toString()" not in script
assert "form.requestSubmit()" in script
@@ -304,6 +307,8 @@ def test_sales_frontend_uses_live_period_linked_api_routes():
assert "if (end) end.value = '';" in script
assert "d.growth_rate" in script
assert "d.monthly_breakdown" in script
assert "marketingMetric === 'qty' ? fmtNum : fmtMoney" in script
assert "order: []" in script
assert "columns," in script
assert 'data-field="product_id"' in template
assert 'data-field="amount"' in template
@@ -343,6 +348,30 @@ def test_yoy_period_accepts_the_year_month_value_sent_by_the_page_filter():
assert end == date(2025, 4, 30)
def test_yoy_period_intersects_month_with_the_selected_custom_range():
start, end = _project_yoy_period(
2025,
start_value="2026-03-15",
end_value="2026-05-10",
month_value="2026-04",
)
assert start == date(2025, 4, 1)
assert end == date(2025, 4, 30)
def test_yoy_period_intersects_month_with_a_cross_year_custom_range():
start, end = _project_yoy_period(
2025,
start_value="2025-11-15",
end_value="2026-02-10",
month_value="2025-12",
)
assert start == date(2025, 12, 1)
assert end == date(2025, 12, 31)
def test_yoy_api_uses_only_the_selected_period_for_totals_and_chart(monkeypatch):
engine = create_engine("sqlite:///:memory:")
with engine.begin() as conn:
@@ -477,3 +506,19 @@ def test_sales_page_builds_every_chart_from_the_same_custom_period(monkeypatch):
assert len(table_payload["data"]) == 1
assert table_payload["data"][0]["product_id"] == "P1"
assert table_payload["data"][0]["amount"] == 100
class FixedDateTime(real_datetime):
@classmethod
def now(cls, tz=None):
value = cls(2026, 7, 22, 12, 0, 0)
return value.replace(tzinfo=tz) if tz else value
monkeypatch.setattr(sales_routes, "datetime", FixedDateTime)
sales_routes._SALES_PROCESSED_CACHE.clear()
sales_routes._SALES_ANALYSIS_RESULT_CACHE.clear()
with app.test_request_context("/sales_analysis?metric=amount&data_range=6"):
rolling_context = sales_routes.sales_analysis.__wrapped__()
assert rolling_context["analysis_period"]["mode"] == "rolling_months"
assert rolling_context["analysis_period"]["label"] == "最近 6 個月"
assert rolling_context["total_records"] == 3