import csv,json,collections,re,xml.etree.ElementTree as ET from pathlib import Path from decimal import Decimal root=Path('scripts/stock-import-202609') source=json.loads((root/'source.json').read_text(encoding='utf-8'),parse_float=Decimal) ledger=next(s for s in source if s['sheet']=='Sheet1') merged_names={} for merged in ledger['merged']: match=re.fullmatch(r'D(\d+):D(\d+)',merged) if match: first,last=map(int,match.groups()) for number in range(first,last+1):merged_names[number]=ledger['rows'][first-1][3] rows=[] for index,r in enumerate(ledger['rows'],1): if len(r)>20 and isinstance(r[4],(int,str)) and re.fullmatch(r'[A-Za-z0-9]+',str(r[4]).strip()): name=r[3] or merged_names.get(index) assert name,('Missing material name',index,r[4]) rows.append(dict(row=index,code=str(r[4]).strip(),name=name,unit=r[5],quantity=str(r[20]),stock_unit=r[6],stock_quantity=str(r[21]))) assert len(rows)==len({r['code'] for r in rows}) for target in ('local','remote'): db=ET.parse(root/f'{target}-snapshot.xml').getroot() materials=collections.defaultdict(list) for m in db.find('materials'): materials[m.findtext('code')].append({c.tag:c.text for c in m}) units={u.findtext('id'):u.findtext('name').strip() for u in db.find('units')} names=collections.defaultdict(list) for entries in materials.values(): for m in entries:names[m['name']].append(m) plan=[] for r in rows: out=dict(r) matches=materials[r['code']] out['material_id']=matches[0]['id'] if len(matches)==1 else '' out['system_name']=matches[0]['name'] if len(matches)==1 else '' out['system_unit']=units.get(matches[0]['unit_measure_id'],'') if len(matches)==1 else '' out['state']='编码匹配' if len(matches)==1 else ('编码重复' if matches else '缺少物料') if not matches and names[r['name']]:out['state']='同名不同编码,待核对';out['candidates']=';'.join(m['code'] for m in names[r['name']]) if len(matches)==1 and out['system_unit']!=r['unit']:out['state']='单位待换算' plan.append(out) (root/f'{target}-plan.json').write_text(json.dumps(plan,ensure_ascii=False,indent=2),encoding='utf-8') with (root/f'{target}-库存核对清单.csv').open('w',encoding='utf-8-sig',newline='') as f: keys=['row','code','name','unit','quantity','stock_unit','stock_quantity','material_id','system_name','system_unit','state','candidates'] writer=csv.DictWriter(f,fieldnames=keys);writer.writeheader();writer.writerows(plan) print(target,len(plan),dict(collections.Counter(r['state'] for r in plan)))