gaoluyang
13 小时以前 e40a608ef9bf51db294d8eefada6a8e2e80cebca
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
"""Sales item need-production = yes; native draft MPS per item, idempotent links."""
import sys,json,csv,collections
from pathlib import Path
from decimal import Decimal
P=Path(__file__).parent
sys.path.insert(0,str(P.parent/'iqc-import-20261012/deps'))
import pymysql
s=json.loads((P/'before-sales-mps-snapshot.json').read_text(encoding='utf8'))
def norm(v):return json.loads(json.dumps(v,default=str))
def dump(n,v):(P/n).write_text(json.dumps(v,ensure_ascii=False,indent=2,default=str),encoding='utf8')
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,connect_timeout=10,read_timeout=60)
log=[]
try:
 with c.cursor() as q:
  q.execute('SELECT DATABASE() db');assert q.fetchone()['db']=='mom-hm'
  q.execute("SELECT GET_LOCK('sales-mps-20261012',10) ok");assert q.fetchone()['ok']==1
  c.begin();before={}
  for t in ['erp_sale_order','erp_sale_order_items','mes_pro_mps','mes_pro_mps_merge_item','mes_md_item','mdm_item','mdm_unit_measure']:
   q.execute('SELECT ENGINE FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name=%s',(t,));assert q.fetchone()['ENGINE']=='InnoDB'
   q.execute('SELECT * FROM '+t+' WHERE deleted=0 ORDER BY id FOR UPDATE');before[t]=list(q.fetchall())
   assert norm(before[t])==s[t],f'{t} changed; re-review required'
  dump('before-sales-mps-import.json',before)
  orders={o['id']:o for o in before['erp_sale_order']}
  assert all(o['status']==20 and o['production_status']==0 for o in orders.values())
  mes=collections.defaultdict(list)
  for i in before['mes_md_item']:mes[i['mdm_item_id']].append(i)
  active=collections.defaultdict(list)
  for m in before['mes_pro_mps']:
   if m['status']!=3:active[m['sale_order_item_id']].append(m)
  values=[];reused=[];codes={m['code'] for m in before['mes_pro_mps']};unit_issues=[]
  for item in before['erp_sale_order_items']:
   assert item['order_id'] in orders and Decimal(item['count'])>0
   assert len(mes[item['product_id']])==1
   product=mes[item['product_id']][0];order=orders[item['order_id']]
   assert product['status']==0
   if item['product_unit_id']!=product['unit_measure_id']:unit_issues.append(dict(sale_order_item_id=item['id'],sale_order_no=order['no'],product_code=product['code'],product_name=product['name'],specification=product['specification'],sale_unit_id=item['product_unit_id'],mes_unit_id=product['unit_measure_id'],quantity=str(item['count']),note='按原生MPS生成规则采用MES产品单位,数量为整个产品SKU数量;订单包装单位不同,后续下发前需核实主数据单位口径。'))
   existing=active[item['id']];assert len(existing)<=1
   if existing:
    old=existing[0];assert old['product_id']==product['id'] and old['quantity']==item['count'];reused.append(old);continue
   code='MPS20261012-SO-'+str(item['id']).zfill(6);assert code not in codes;codes.add(code)
   values.append(dict(code=code,status=0,sale_order_id=order['id'],sale_order_no=order['no'],sale_order_item_id=item['id'],product_id=product['id'],quantity=item['count'],quantity_produced=Decimal(0),unit_measure_id=product['unit_measure_id'],client_id=order['customer_id'],request_date=order['order_time'],merge_flag=0,remark='由销售订单生成:'+order['no']+';对应明细'+str(item['id'])+';需要生产改为是;保留订单数量,待计划下发。'))
  if values:
   for v in values:v['creator']='1';v['updater']='1'
   cols=list(values[0]);sql='INSERT INTO mes_pro_mps (`'+'`,`'.join(cols)+'`) VALUES ('+','.join(['%s']*len(cols))+')'
   args=[tuple(v[k] for k in cols) for v in values];log.extend(q.mogrify(sql,a)+';' for a in args);q.executemany(sql,args)
  # Scope to the reviewed order IDs; do not change approval, prices, quantities or shipment data.
  ids=list(orders);marks=','.join(['%s']*len(ids))
  sql="UPDATE erp_sale_order_items i JOIN mes_pro_mps m ON m.sale_order_item_id=i.id AND m.deleted=0 AND m.status<>3 SET i.need_production=1,i.mps_id=m.id,i.updater='1' WHERE i.deleted=0 AND i.order_id IN ("+marks+')'
  log.append(q.mogrify(sql,ids)+';');q.execute(sql,ids);changed=q.rowcount
  q.execute('SELECT * FROM mes_pro_mps WHERE deleted=0 ORDER BY id');after_mps=list(q.fetchall())
  q.execute('SELECT * FROM erp_sale_order_items WHERE deleted=0 ORDER BY id');after_items=list(q.fetchall())
  final={m['sale_order_item_id']:m for m in after_mps if m['status']!=3}
  assert len(final)==len(before['erp_sale_order_items']) and len(after_mps)==len(before['mes_pro_mps'])+len(values)
  for item,old in zip(after_items,before['erp_sale_order_items']):
   assert item['id']==old['id'] and item['need_production']==1
   m=final[item['id']];o=orders[item['order_id']];prd=mes[item['product_id']][0]
   assert item['mps_id']==m['id'] and m['sale_order_id']==o['id'] and m['sale_order_no']==o['no'] and m['product_id']==prd['id'] and m['quantity']==item['count'] and m['unit_measure_id']==prd['unit_measure_id'] and m['client_id']==o['customer_id'] and m['request_date']==o['order_time']
   if m['code'].startswith('MPS20261012-SO-'):assert m['status']==0 and m['quantity_produced']==0 and m['work_order_id'] is None and m['issue_date'] is None
   for k in old:
    if k not in ['need_production','mps_id','updater','update_time']:assert old[k]==item[k]
  q.execute('SELECT * FROM erp_sale_order WHERE deleted=0 ORDER BY id');assert list(q.fetchall())==before['erp_sale_order']
  for t in ['mes_pro_mps_merge_item','mes_md_item','mdm_item','mdm_unit_measure']:
   q.execute('SELECT * FROM '+t+' WHERE deleted=0 ORDER BY id');assert list(q.fetchall())==before[t]
  result={'database':'mom-hm','sale_orders':len(orders),'sales_items_need_production_yes':len(after_items),'new_draft_mps':len(values),'existing_mps_reused':len(reused),'sales_items_updated':changed,'unit_difference_items':len(unit_issues),'production_status':'未完成','mps_status':'草稿','duplicate_active_mps':0,'quantities_and_product_mapping_verified':True,'sales_financial_and_approval_fields_preserved':True,'verified':True}
  c.commit();dump('sales-mps-import-result.json',result);dump('sales-mps-generated.json',after_mps);dump('sales-mps-unit-differences.json',unit_issues)
  (P/'sales-mps-executed.sql').write_text('START TRANSACTION;\n'+'\n'.join(log)+'\nCOMMIT;\n',encoding='utf8')
  units={u['id']:u['name'].strip() for u in before['mdm_unit_measure']};prodids={p['id']:p for p in before['mes_md_item']}
  with (P/'销售订单生产主计划对应.csv').open('w',encoding='utf-8-sig',newline='') as f:
   w=csv.writer(f);w.writerow(['销售订单号','订单明细ID','主计划ID','主计划编码','产品编码','产品名称','规格','计划数量','计划单位','状态','需求日期','客户ID'])
   for m in after_mps:
    if m['sale_order_item_id'] not in final:continue
    prd=prodids[m['product_id']];w.writerow([m['sale_order_no'],m['sale_order_item_id'],m['id'],m['code'],prd['code'],prd['name'],prd['specification'],m['quantity'],units.get(m['unit_measure_id']),'草稿' if m['status']==0 else m['status'],m['request_date'],m['client_id']])
  with (P/'销售生产单位口径待核实.csv').open('w',encoding='utf-8-sig',newline='') as f:
   w=csv.writer(f);w.writerow(['销售订单号','明细ID','产品编码','产品名称','规格','销售单位','MES及MPS单位','计划数量','说明'])
   for a in unit_issues:w.writerow([a['sale_order_no'],a['sale_order_item_id'],a['product_code'],a['product_name'],a['specification'],units.get(a['sale_unit_id']),units.get(a['mes_unit_id']),a['quantity'],a['note']])
  print(json.dumps(result,ensure_ascii=False));q.execute("SELECT RELEASE_LOCK('sales-mps-20261012')")
except:c.rollback();raise
finally:c.close()