"""Normalize only the isolated October supplement; retain source and measurement provenance.""" import sys,json,re,collections from pathlib import Path sys.stdout.reconfigure(encoding='utf-8') sys.path.insert(0,str(Path('scripts/iqc-import-20261012/deps').resolve())) import pymysql P=Path(__file__).parent;TAG='OCT0814-DEMO';NOTE='10月8日至14日业务链演示补充' apply='--apply' in sys.argv c=pymysql.connect(host='36.213.150.237',port=10000,user='root',password=sys.stdin.readline().strip(),database='mom-hm',charset='utf8mb4',cursorclass=pymysql.cursors.DictCursor,autocommit=False,read_timeout=90) schema=json.loads((P/'schema-snapshot.json').read_text(encoding='utf8')) rules=json.loads((P/'number-rules.json').read_text(encoding='utf8')) manifest=json.loads((P/'result-applied.json').read_text(encoding='utf8')) def save(name,obj): (P/name).write_text(json.dumps(obj,ensure_ascii=False,indent=2,default=str),encoding='utf8') def canon(obj):return json.loads(json.dumps(obj,default=str,ensure_ascii=False)) rows={};mapping={};reservations=[];changes=[];log=[] rule_ids={'mes_wm_arrival_notice':13,'mes_wm_item_receipt':14,'mes_wm_batch':19,'mes_wm_product_sales':20,'mes_wm_sales_notice':28,'mes_pro_work_order':30,'mes_pro_task':31,'mes_pro_feedback':33,'mes_qc_iqc':40,'mes_qc_ipqc':41,'mes_qc_oqc':42,'mes_qc_indicator_result':45,'mes_wm_product_issue':64,'mes_pro_mps':65,'mes_qc_template':67} q=c.cursor() try: q.execute('SELECT GET_LOCK(%s,10)',(TAG+'-normalize',));assert list(q.fetchone().values())==[1] for t,s in schema.items(): if not isinstance(s,dict) or 'columns' not in s:continue cols=[x['Field'] for x in s['columns'] if re.match(r'(?:var)?char|text',x['Type'])] if not cols:continue clause=' OR '.join('`'+k+'` LIKE %s' for k in cols) q.execute('SELECT * FROM `'+t+'` WHERE '+clause,tuple('%'+TAG+'%' for _ in cols));found={x['id']:x for x in q.fetchall()} if 'remark' in cols: q.execute('SELECT * FROM `'+t+'` WHERE remark LIKE %s',(NOTE+'%',));found.update({x['id']:x for x in q.fetchall()}) if found:rows[t]=list(found.values()) assert len(rows['erp_sale_order'])==4 and len(rows['mes_wm_batch'])==42 save('before-normalize-'+('apply' if apply else 'dry-run')+'.json',rows) q.execute('SELECT id,name FROM mdm_item');items={x['id']:x['name'] for x in q.fetchall()} q.execute('SELECT id,name FROM mes_pro_process');processes={x['id']:x['name'] for x in q.fetchall()} q.execute('SELECT id,name FROM crm_customer');customers={x['id']:x['name'] for x in q.fetchall()} def occupied(t,col,prefix): q.execute('SELECT `'+col+'` v FROM `'+t+'` WHERE `'+col+'` LIKE %s',(prefix+'%',));return [x['v'] for x in q.fetchall()] def next_code(t,col,prefix,width,start=1,extra=()): vals=occupied(t,col,prefix)+list(extra)+list(mapping.values()) nums=[int(v[len(prefix):]) for v in vals if v and v.startswith(prefix) and v[len(prefix):].isdigit()] n=max(nums+[start-1])+1;assert n<10**width return prefix+str(n).zfill(width),n # Marketing records follow the numbering conventions already used by the business data. for t,col,prefix,width in [('erp_sale_order','no','26',6),('crm_sale_quotation','no','BJ-202610-L',3),('crm_contract','no','HT-202610-L',3)]: for row in rows[t]: new,n=next_code(t,col,prefix,width);mapping[row[col]]=new for t,rid in rule_ids.items(): rule=next(x for x in rules['mes_md_auto_code_rule'] if x['id']==rid) parts=sorted([x for x in rules['mes_md_auto_code_part'] if x['rule_id']==rid],key=lambda x:x['sort']) for row in rows[t]: day=row['create_time'].strftime('%Y%m%d');prefix='';width=None;start=1 for part in parts: if part['type']==3:prefix+=part['fix_character'][:part['length']] elif part['type']==2:prefix+=day elif part['type']==4:width=part['length'];start=part['serial_start_no'] else:raise ValueError((t,part)) q.execute('SELECT result FROM mes_md_auto_code_record WHERE rule_id=%s',(rid,));used=[x['result'] for x in q.fetchall()] # IR_ is shared by receipts and indicator results: avoid cross-module string ambiguity. if prefix.startswith('IR_'):used+=occupied('mes_wm_item_receipt','code',prefix)+occupied('mes_qc_indicator_result','code',prefix) new,n=next_code(t,'code',prefix,width,start,used) if len(new)