| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- #!/usr/bin/env python3
- """Create tenant-scoped UAT sales shipment business facts.
- Facts are written to the normal ASN business tables. A subsequent S1 rebuild
- must pull them through mdp_stg_ship_trans -> mdp_std_ship_trans -> DWD -> KPI.
- """
- from __future__ import annotations
- import argparse
- import json
- from collections import defaultdict
- from datetime import datetime, timedelta
- from decimal import Decimal
- from typing import Any
- from apply_sql_file import connect
- TENANTS = {
- 838257186181189: "A",
- 838257212780613: "B",
- 838257237606469: "DEMO",
- }
- PREFIX = "UAT-SHIP-"
- def shipment_ratio(code: str, order_no: str, index: int) -> Decimal:
- if code == "A":
- scenario = next((x for x in ("A01", "A02", "A03", "A04", "A05") if x in order_no), "")
- return {
- "A01": Decimal("1.00"),
- "A02": Decimal("0.60"),
- "A03": Decimal("0.20"),
- "A04": Decimal("0.80"),
- "A05": Decimal("1.00"),
- }.get(scenario, Decimal("0.75"))
- if code == "B":
- return Decimal("0.65")
- return (Decimal("1.00"), Decimal("0.82"), Decimal("0.55"), Decimal("0.25"))[index % 4]
- def load_order_lines(cursor: Any) -> dict[int, list[dict[str, Any]]]:
- tenant_ids = ",".join(str(x) for x in TENANTS)
- cursor.execute(
- f"""
- SELECT tenant_id,order_no,order_line,item_code,item_name,order_qty
- FROM mdp_std_so
- WHERE tenant_id IN ({tenant_ids})
- AND IFNULL(order_no,'')<>''
- AND IFNULL(item_code,'')<>''
- AND IFNULL(order_qty,0)>0
- ORDER BY tenant_id,order_no,CAST(order_line AS UNSIGNED),item_code
- """
- )
- result: dict[int, list[dict[str, Any]]] = defaultdict(list)
- for row in cursor.fetchall():
- result[int(row["tenant_id"])].append(row)
- return result
- def apply() -> dict[str, Any]:
- conn = connect()
- summary: dict[str, Any] = {"masters": 0, "details": 0, "tenants": {}}
- try:
- conn.begin()
- with conn.cursor() as cursor:
- lines_by_tenant = load_order_lines(cursor)
- tenant_ids = tuple(TENANTS)
- placeholders = ",".join(["%s"] * len(tenant_ids))
- cursor.execute(
- f"DELETE FROM ASNBOLShipperDetail WHERE tenant_id IN ({placeholders}) AND Id LIKE %s",
- (*tenant_ids, f"{PREFIX}%"),
- )
- cursor.execute(
- f"DELETE FROM ASNBOLShipperMaster WHERE tenant_id IN ({placeholders}) AND Id LIKE %s",
- (*tenant_ids, f"{PREFIX}%"),
- )
- now = datetime.now().replace(microsecond=0)
- for tenant_id, code in TENANTS.items():
- grouped: dict[str, list[dict[str, Any]]] = defaultdict(list)
- for row in lines_by_tenant.get(tenant_id, []):
- grouped[str(row["order_no"])].append(row)
- tenant_summary = {"orders": 0, "lines": 0}
- for order_index, (order_no, order_lines) in enumerate(grouped.items()):
- shipper_id = f"{PREFIX}{code}-{order_index + 1:03d}"
- if code == "DEMO":
- ship_date = now - timedelta(days=(order_index * 2) % 90)
- else:
- ship_date = now - timedelta(days=order_index % 10)
- ratio = shipment_ratio(code, order_no, order_index)
- total_qty = sum(Decimal(str(row["order_qty"])) for row in order_lines)
- real_total = (total_qty * ratio).quantize(Decimal("0.0001"))
- cursor.execute(
- """
- INSERT INTO ASNBOLShipperMaster
- (Id,OrdNbr,QtyToShip,QtyShipped,ShipDate,Status,IsConfirm,
- SoldTo,Remark,CreateTime,UpdateTime,IsActive,tenant_id)
- VALUES
- (%s,%s,%s,%s,%s,'SHIPPED',1,'UAT-CUSTOMER',
- 'UAT_GENERATOR:销售发运事实',NOW(),NOW(),1,%s)
- """,
- (shipper_id, order_no, total_qty, real_total, ship_date, tenant_id),
- )
- master_rec_id = cursor.lastrowid
- summary["masters"] += 1
- tenant_summary["orders"] += 1
- for line_index, row in enumerate(order_lines, 1):
- qty = Decimal(str(row["order_qty"]))
- real_qty = (qty * ratio).quantize(Decimal("0.0001"))
- cursor.execute(
- """
- INSERT INTO ASNBOLShipperDetail
- (ASNBOLShipperRecID,Id,Line,OrdNbr,OrdLine,ContainerItem,
- Descr,QtyToShip,PickingQty,RealQty,QtyShipped,ShipDate,
- Status,IsConfirm,Remark,CreateTime,UpdateTime,IsActive,tenant_id)
- VALUES
- (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,
- 'SHIPPED',1,'UAT_GENERATOR:销售发运明细',NOW(),NOW(),1,%s)
- """,
- (
- master_rec_id,
- f"{shipper_id}-L{line_index:03d}",
- line_index,
- order_no,
- int(row["order_line"] or line_index),
- row["item_code"],
- row["item_name"] or row["item_code"],
- qty,
- real_qty,
- real_qty,
- real_qty,
- ship_date,
- tenant_id,
- ),
- )
- summary["details"] += 1
- tenant_summary["lines"] += 1
- summary["tenants"][code] = tenant_summary
- conn.commit()
- except Exception:
- conn.rollback()
- raise
- finally:
- conn.close()
- return summary
- def main() -> int:
- parser = argparse.ArgumentParser()
- parser.add_argument("--apply", action="store_true")
- args = parser.parse_args()
- if not args.apply:
- print(json.dumps({"mode": "dry-run", "tenants": TENANTS, "prefix": PREFIX}, ensure_ascii=False, indent=2))
- return 0
- result = apply()
- result["mode"] = "apply"
- print(json.dumps(result, ensure_ascii=False, indent=2, default=str))
- return 0
- if __name__ == "__main__":
- raise SystemExit(main())
|