import json from collections import defaultdict,Counter from read_links import P,encode def build(snapshot): data={t:{r['id']:dict(r) for r in v['rows']} for t,v in snapshot['tables'].items()} customers=data['crm_customer'];changes=[];conflicts=[] def get(t,id):return data.get(t,{}).get(id) def code(t,val,field='code'): return [r for r in data.get(t,{}).values() if val and r.get(field)==val] def candidates(table,row): evidence=[] def add(t,source,field='client_id'): if source and source.get(field) in customers:evidence.append({'table':t,'id':source['id'],'field':field,'customer_id':source[field]}) def one(t,id,field='client_id'):add(t,get(t,id),field) if table=='mes_pro_mps':one('erp_sale_order',row.get('sale_order_id'),'customer_id') elif table=='mes_pro_work_order': for m in data['mes_pro_mps'].values(): if m.get('work_order_id')==row['id']:add('mes_pro_mps',m) if row.get('order_source_type')==1: for o in code('erp_sale_order',row.get('order_source_code'),'no'):add('erp_sale_order',o,'customer_id') if row.get('parent_id'):one('mes_pro_work_order',row['parent_id']) elif table=='mes_pro_task':one('mes_pro_work_order',row.get('work_order_id')) elif table=='mes_wm_batch': one('mes_pro_work_order',row.get('work_order_id'));one('mes_pro_task',row.get('task_id')) for o in code('erp_sale_order',row.get('sales_order_code'),'no'):add('erp_sale_order',o,'customer_id') elif table in ('mes_wm_sales_notice','mes_wm_product_sales','mes_wm_return_sales'): one('erp_sale_order',row.get('sale_order_id'),'customer_id') if table=='mes_wm_product_sales':one('mes_wm_sales_notice',row.get('notice_id')) elif table=='mes_qc_oqc': if row.get('source_doc_type')==119:one('mes_wm_product_sales',row.get('source_doc_id')) for l in data.get('mes_wm_product_sales_line',{}).values(): if l.get('oqc_id')==row['id']:one('mes_wm_product_sales',l.get('sales_id')) elif table in ('crm_contract','crm_sale_quotation'): one('crm_business',row.get('business_id'),'customer_id') if table=='crm_sale_quotation':one('crm_contract',row.get('contract_id'),'customer_id') elif table in ('crm_invoice','crm_receivable','crm_receivable_plan','erp_sale_order'): one('crm_contract',row.get('contract_id'),'customer_id') return evidence targets=['crm_contract','crm_sale_quotation','erp_sale_order','crm_invoice','crm_receivable','crm_receivable_plan', 'mes_pro_mps','mes_pro_work_order','mes_pro_task','mes_wm_sales_notice','mes_wm_product_sales','mes_wm_return_sales','mes_wm_batch','mes_qc_oqc'] for table in targets: for r in data.get(table,{}).values(): f='client_id' if table.startswith('mes_') else 'customer_id' ev=candidates(table,r);ids={x['customer_id'] for x in ev} if len(ids)>1: conflicts.append({'table':table,'id':r['id'],'reason':'上下游存在多个客户','evidence':ev});continue if len(ids)==1: wanted=next(iter(ids)) if r.get(f) not in customers: changes.append({'table':table,'id':r['id'],'field':f,'before':r.get(f),'after':wanted, 'customer_name':customers[wanted]['name'],'evidence':ev,'source_before':dict(r)}) r[f]=wanted elif r[f]!=wanted: conflicts.append({'table':table,'id':r['id'],'reason':'既有客户与上下游不同','current':r[f],'evidence':ev}) unresolved=[];optional=[] for table in ['mes_pro_mps','mes_pro_work_order','mes_pro_task','mes_wm_batch','mes_qc_oqc']: for r in data.get(table,{}).values(): if r.get('client_id') not in customers: x={'table':table,'id':r['id'],'code':r.get('code'),'reason':'缺少可唯一确认客户的订单关联'} if table=='mes_wm_batch' and not (r.get('work_order_id') or r.get('task_id') or r.get('sales_order_code')):optional.append(x) else:unresolved.append(x) assert not conflicts,encode(conflicts) return {'state':snapshot['state'],'changes':changes,'conflicts':conflicts,'unresolved':unresolved,'unallocated_stock_batches':optional, 'summary':{'change_count':len(changes),'by_table':dict(Counter(r['table'] for r in changes)), 'by_customer':dict(Counter(r['customer_name'] for r in changes)),'unresolved':len(unresolved), 'stock_batches_without_customer_requirement':len(optional),'new_customers_required':0}} if __name__=='__main__': s=json.loads((P/'snapshot.json').read_text(encoding='utf8'));p=build(s) (P/'plan.json').write_text(encode(p),encoding='utf8');print(encode(p['summary']))