#!/usr/bin/env python3 """ 第三轮修复:内料继承主产品供应商 + CSV中有但供应商为空的补充 规则: 1. 内料(-NL/【内料】): 去掉 -NL 后缀,找到主产品,继承主产品的供应商 2. CSV中有记录但供应商为空的包材: 去掉后缀找主产品继承 3. CSV中有记录且有供应商的:不再处理(v2已完成) """ import csv import pymysql from config import DB_CONFIG CSV_FILE = "/home/gxd/.openclaw/workspace-dingtalk-assistant/media/inbound/采购订单-20260531-1780213945661.csv" def strip_suffix(code): """去掉已知后缀返回base_code""" SUFFIXES = [ '-FM/GZ', '-TMGJ-NL', '-ALJK-BT', '-ALJK-FM/GZ', '-TM-BT', '-TM-PZ', '-TM-CH', '-TM-BQ', '-TM-SSM', '-TM-HMT', '-TM-RG', '-TM-GZ', '-TM-FM/GZ', '-PDD-BT', '-PDD-PZ', '-PDD-CH', '-PDD-HMT', '-NL', '-BT', '-PZ', '-CH', '-BQ', '-SSM', '-HMT', '-RG', '-GZ', '-JD', '-PDD', '-ALJK', '-TM', '-WPH', '-DW', ] for s in SUFFIXES: if code.endswith(s): base = code[:-len(s)] if base: return base return code def main(): conn = pymysql.connect(**DB_CONFIG) cur = conn.cursor() # 1. 读取CSV——构建精确匹配索引 csv_best = {} with open(CSV_FILE, encoding="utf-8-sig") as f: reader = csv.DictReader(f) for row in reader: code = row["物料编码"].strip() supplier = row["供应商"].strip() price_str = row["含税单价"].strip() try: price = float(price_str) if price_str else None except ValueError: price = None if code not in csv_best: csv_best[code] = (supplier, price) else: old_sup, old_price = csv_best[code] if supplier and not old_sup: csv_best[code] = (supplier, old_price if old_price is not None else price) if old_price is None and price is not None: csv_best[code] = (old_sup if old_sup else supplier, price) print(f"CSV唯一物料编码: {len(csv_best)}") # 2. 找出所有包材类且supplier_name_match为空的产品 keywords = ['瓶子','泵头','彩盒','海绵头','瓶贴','标签','收缩膜','瓶体','瓶标','软管','盖子','膜袋','标贴','唇膏管','内料','海绵','瓶子+内塞','泵头','瓶标','海绵头'] clauses = " OR ".join(["product_name LIKE '%%{}%%'".format(k) for k in keywords]) cur.execute(""" SELECT id, product_code, product_name, cost_price FROM products WHERE ({}) AND (supplier_name_match IS NULL OR supplier_name_match = '') """.format(clauses)) need_fix = cur.fetchall() print(f"需要修复的包材(当前supplier为空): {len(need_fix)}") # 3. 加载所有产品的supplier_name_match(包括已有值的),用于继承 cur.execute("SELECT product_code, supplier_name_match, cost_price FROM products") all_prod = {} for code, sup, price in cur.fetchall(): all_prod[code.strip()] = { 'supplier': sup or '', 'cost_price': float(price) if price else 0 } # 4. 逐个处理 update_1 = [] # 内料继承主产品供应商 update_2 = [] # CSV中供应商为空但可继承主产品的 update_3 = [] # 通过找主产品获得供应商(其他后缀) for pid, product_code, product_name, old_price in need_fix: code = product_code.strip() is_nl = code.endswith('-NL') or '【内料】' in product_name new_supplier = None # 4a. 检查CSV是否有该编码且供应商不为空 if code in csv_best: csv_sup, csv_price = csv_best[code] if csv_sup: # CSV有供应商——v2应该已经覆盖了,但如果还没覆盖到 new_supplier = csv_sup new_price = csv_price if csv_price is not None else (float(old_price) if old_price else None) update_2.append((new_supplier, new_price, pid)) continue # 4b. 去掉后缀找主产品 base_code = strip_suffix(code) if base_code and base_code != code and base_code in all_prod: base = all_prod[base_code] if base['supplier']: new_supplier = base['supplier'] # 4c. 对于内料,特别处理:多剥一层后缀 if not new_supplier and is_nl: # 去-NL后缀后再去一次 stripped = code if stripped.endswith('-NL'): stripped = stripped[:-3] # 再去其他后缀 base_code2 = strip_suffix(stripped) if base_code2 and base_code2 in all_prod: base = all_prod[base_code2] if base['supplier']: new_supplier = base['supplier'] if new_supplier: new_price = float(old_price) if old_price else None if is_nl: update_1.append((new_supplier, new_price, pid)) else: update_2.append((new_supplier, new_price, pid)) print(f"\n内料继承主产品供应商: {len(update_1)}") print(f"其他后缀继承主产品供应商: {len(update_2)}") print(f"仍然无法匹配: {len(need_fix) - len(update_1) - len(update_2)}") # 5. 批量更新 def run_updates(label, updates_data): if not updates_data: return sql = "UPDATE products SET supplier_name_match=%s, cost_price=%s WHERE id=%s" n = 0 for i in range(0, len(updates_data), 500): batch = updates_data[i:i+500] cur.executemany(sql, batch) conn.commit() n += len(batch) print(f" {label}: {n} 条已更新") run_updates("内料继承", update_1) run_updates("其他继承", update_2) # 6. 最终统计 cur.execute("SELECT COUNT(*) FROM products WHERE ({}) AND (supplier_name_match IS NULL OR supplier_name_match = '')".format(clauses)) still_empty = cur.fetchone()[0] print(f"\n修复后,包材仍然为空: {still_empty}") # 按后缀分类查看剩余为空的情况 if still_empty > 0: cur.execute(""" SELECT product_code, product_name FROM products WHERE ({}) AND (supplier_name_match IS NULL OR supplier_name_match = '') """.format(clauses)) remain = cur.fetchall() nl_left = sum(1 for c, _ in remain if c.endswith('-NL')) print(f" 其中-NL内料: {nl_left}") print(f" 其他: {still_empty - nl_left}") cur.execute("SELECT COUNT(*) FROM products WHERE supplier_name_match != ''") total_sup = cur.fetchone()[0] print(f"\n全部产品有供应商: {total_sup}") conn.close() print("\n✅ 完成!") if __name__ == "__main__": main()