83 lines
3.0 KiB
Python
Executable File
83 lines
3.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
|
||
import argparse
|
||
import json
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
|
||
|
||
def money(value: object) -> str:
|
||
return f"{float(value or 0):,.2f}"
|
||
|
||
|
||
def brief(result: dict) -> str:
|
||
status = str(result.get("status") or "failed")
|
||
month = str(result.get("report_month") or "")[:7]
|
||
if status in {"blocked", "failed"}:
|
||
return f"{month or '本月'} 未执行:{result.get('message') or '上游数据不完整'}"
|
||
|
||
weighted = result.get("weighted_average")
|
||
if isinstance(weighted, dict):
|
||
weighted_brief = brief(weighted)
|
||
cost = result.get("product_cost") or {}
|
||
cost_brief = (
|
||
f"正常匹配 {int(cost.get('normal_matched_rows') or 0):,} 行;"
|
||
f"排除占位 {int(cost.get('placeholder_excluded_rows') or 0):,} 行;"
|
||
f"排除组合装主行 {int(cost.get('bundle_parent_excluded_rows') or 0):,} 行;"
|
||
f"待核验成本 {int(cost.get('pending_cost_rows') or 0):,} 行 / "
|
||
f"{float(cost.get('pending_cost_quantity') or 0):,.2f} 件"
|
||
)
|
||
return f"{weighted_brief};商品成本:{cost_brief}"
|
||
|
||
totals = result.get("totals") or {}
|
||
inbound = result.get("inbound") or {}
|
||
anomalies = result.get("anomalies") or {}
|
||
reasons = "、".join(
|
||
str(item.get("label") or item.get("code") or "未知异常")
|
||
for item in anomalies.get("reasons") or []
|
||
)
|
||
suffix = f";待复核:{reasons}" if reasons else ""
|
||
if status == "already_computed":
|
||
prefix = "已完成,本次跳过重算"
|
||
elif status == "review_required":
|
||
prefix = "计算完成,需人工复核"
|
||
else:
|
||
prefix = "计算完成"
|
||
|
||
return (
|
||
f"{month} {prefix}:总行数 {int(totals.get('rows') or 0):,};"
|
||
f"期初 {float(totals.get('opening_quantity') or 0):,.2f} 件 / "
|
||
f"{money(totals.get('opening_amount'))};"
|
||
f"入库 {float(inbound.get('matched_quantity') or 0):,.2f} 件 / "
|
||
f"{money(inbound.get('matched_amount'))};"
|
||
f"加权单价 {int(totals.get('weighted_price_rows') or 0):,} 行;"
|
||
f"异常 {int(anomalies.get('affected_rows') or 0):,} 行{suffix}"
|
||
)
|
||
|
||
|
||
def main() -> int:
|
||
parser = argparse.ArgumentParser()
|
||
parser.add_argument("--input", required=True)
|
||
parser.add_argument("--result-json", required=True)
|
||
args = parser.parse_args()
|
||
|
||
result = json.loads(Path(args.input).read_text(encoding="utf-8"))
|
||
summary = brief(result)
|
||
payload = {
|
||
"finished_at": datetime.now().astimezone().isoformat(timespec="seconds"),
|
||
"brief": summary,
|
||
"result": result,
|
||
}
|
||
output = Path(args.result_json)
|
||
output.parent.mkdir(parents=True, exist_ok=True)
|
||
output.write_text(
|
||
json.dumps(payload, ensure_ascii=False, indent=2) + "\n",
|
||
encoding="utf-8",
|
||
)
|
||
print(summary)
|
||
return 0 if result.get("status") not in {"blocked", "failed", "missing"} else 1
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|