dy-dashboard/scripts/backfill_backup_post_packaging.py
2026-10-28 11:14:17 +08:00

92 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""One-time sync of post-order packaging balances into backup BOM snapshots."""
import argparse
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
def post_pack_for_item(item, product_qty, bom_usage):
"""Mirror the value that pending detail displays as 下单后包材."""
pre_pack = item.get('pre_pack_qty')
usage = item.get('quantity')
if usage in (None, ''):
usage = bom_usage.get(str(item.get('material_code') or ''))
if pre_pack in (None, '') or usage in (None, ''):
stored = item.get('post_pack_qty')
return float(stored) if stored not in (None, '') else 0
try:
return round(float(pre_pack) + float(item.get('actual_quantity') or 0)
- float(product_qty) * float(usage), 2)
except (TypeError, ValueError):
return 0
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--local', action='store_true', help='Use the local test database')
parser.add_argument('--dry-run', action='store_true')
args = parser.parse_args()
if args.local:
import test_wsgi # noqa: F401 - installs local DB configuration
from routes.purchase import _extract_product_actual_qty
from services.db import get_db, query
rows = query(
"SELECT id, product_code, formal_order_qty, actual_order_qty, bom_items "
"FROM pending_orders WHERE status IN ('ordered','rolled_back') ORDER BY id"
)
usage_cache = {}
updates = []
changed_items = 0
for row in rows:
try:
items = json.loads(row['bom_items']) if row.get('bom_items') else []
except (TypeError, ValueError):
continue
if not isinstance(items, list) or not items:
continue
product_code = str(row.get('product_code') or '')
product_qty = _extract_product_actual_qty(items, product_code)
if product_qty is None:
product_qty = int(row.get('formal_order_qty') or row.get('actual_order_qty') or 0)
if product_code not in usage_cache:
usage_cache[product_code] = {
str(r.get('material_code') or ''): float(r.get('usage_qty') or 0)
for r in query("SELECT material_code, quantity AS usage_qty FROM bom WHERE product_code=%s",
(product_code,))
}
changed = 0
for item in items:
if not isinstance(item, dict) or str(item.get('material_code') or '').startswith('成品_'):
continue
value = post_pack_for_item(item, product_qty, usage_cache[product_code])
if item.get('post_pack_qty') != value:
item['post_pack_qty'] = value
changed += 1
if changed:
updates.append((json.dumps(items, ensure_ascii=False), row['id']))
changed_items += changed
if not args.dry_run and updates:
conn = get_db()
try:
with conn.cursor() as cur:
cur.executemany("UPDATE pending_orders SET bom_items=%s WHERE id=%s", updates)
conn.commit()
except Exception:
conn.rollback()
raise
finally:
conn.close()
print(json.dumps({'records': len(rows), 'updated_records': len(updates),
'updated_items': changed_items, 'dry_run': args.dry_run},
ensure_ascii=False))
if __name__ == '__main__':
main()