feat: backfill growth momo matches
All checks were successful
CD Pipeline / deploy (push) Successful in 1m9s

This commit is contained in:
OoO
2026-06-18 16:02:02 +08:00
parent 6d6f3b473f
commit 9ca8d4e43c
9 changed files with 421 additions and 4 deletions

View File

@@ -79,6 +79,7 @@ def _daily_sales_columns(conn) -> dict[str, str | None]:
"date": _first_available(columns, ["snapshot_date", "日期", "訂單日期", "交易日期", "Date"]),
"revenue": _first_available(columns, ["總業績", "銷售金額", "業績", "金額", "Amount", "Sales", "Total"]),
"qty": _first_available(columns, ["數量", "銷售數量", "銷量", "Qty", "Quantity"]),
"price": _first_available(columns, ["商品單位售價", "單價", "售價", "Price", "Unit Price"]),
"category": _first_available(columns, ["商品館", "館別", "分類", "Category"]),
"vendor": _first_available(columns, ["廠商名稱", "供應商", "Vendor"]),
}
@@ -110,6 +111,7 @@ def _fetch_sales_rows(conn, limit: int) -> tuple[list[dict[str, Any]], str | Non
date_col = _quote_identifier(cols["date"])
revenue_expr = _numeric_expr(cols["revenue"], dialect)
qty_expr = _numeric_expr(cols["qty"], dialect) if cols.get("qty") else "0"
price_expr = _numeric_expr(cols["price"], dialect) if cols.get("price") else "0"
category_text = _as_text_expr(cols["category"], dialect) if cols.get("category") else "NULL"
vendor_text = _as_text_expr(cols["vendor"], dialect) if cols.get("vendor") else "NULL"
sku_text = _as_text_expr(cols["sku"], dialect)
@@ -137,7 +139,8 @@ def _fetch_sales_rows(conn, limit: int) -> tuple[list[dict[str, Any]], str | Non
NULLIF(TRIM({_as_text_expr(vendor_text, dialect, raw=True)}), '') AS vendor,
{sale_date_expr} AS sale_date,
{revenue_expr} AS revenue,
{qty_expr} AS qty
{qty_expr} AS qty,
{price_expr} AS unit_price
FROM daily_sales_snapshot
WHERE {sku_col} IS NOT NULL
),
@@ -159,6 +162,12 @@ def _fetch_sales_rows(conn, limit: int) -> tuple[list[dict[str, Any]], str | Non
THEN sr.revenue ELSE 0 END) AS sales_prev_7d,
SUM(CASE WHEN sr.sale_date >= {curr_window}
THEN sr.qty ELSE 0 END) AS qty_7d,
CASE
WHEN SUM(CASE WHEN sr.sale_date >= {curr_window} THEN sr.qty ELSE 0 END) > 0
THEN SUM(CASE WHEN sr.sale_date >= {curr_window} THEN sr.revenue ELSE 0 END)
/ NULLIF(SUM(CASE WHEN sr.sale_date >= {curr_window} THEN sr.qty ELSE 0 END), 0)
ELSE NULLIF(MAX(CASE WHEN sr.sale_date >= {curr_window} THEN sr.unit_price ELSE 0 END), 0)
END AS pchome_price,
MAX(sr.sale_date) AS last_sale_date,
MAX(lw.latest_date) AS latest_sales_date
FROM sales_rows sr
@@ -663,6 +672,9 @@ def _score_opportunity(sales_row: dict[str, Any], external_row: dict[str, Any] |
"sales_prev_7d": round(sales_prev_7d, 2),
"sales_delta_pct": round(sales_delta_pct, 1) if sales_delta_pct is not None else None,
"qty_7d": round(qty_7d, 2),
"pchome_price": round(_to_float(sales_row.get("pchome_price")), 2)
if _to_float(sales_row.get("pchome_price")) > 0
else None,
"last_sale_date": str(sales_row.get("last_sale_date") or ""),
"external_price": external_payload,
"priority_score": round(priority_score, 1),