gaoluyang
4 天以前 e71d71bf43c2307c89d3592adf08c1b32fcd0907
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
import sys,json,re,hashlib,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
before=json.loads((P/'before-apply.json').read_text(encoding='utf8'))
r=json.loads((P/'result-applied.json').read_text(encoding='utf8'));assert r['applied']
changes={(x['table'],x['id']):x['after'] for x in r['changes']};deleted=set(before['delete_ids'])
canon=lambda v:json.loads(json.dumps(v,ensure_ascii=False,default=str))
def fingerprint(v):return hashlib.sha256(json.dumps(v,ensure_ascii=False,sort_keys=True,default=str).encode()).hexdigest()
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)
checked=0
with c.cursor() as q:
 for t,rows in before['tables'].items():
  q.execute('SELECT * FROM '+t+' ORDER BY id');actual=q.fetchall();assert len(actual)==len(rows)
  for old,new in zip(rows,actual):assert canon(new)=={**old,**changes.get((t,old['id']),{})},(t,old['id'])
  checked+=len(rows)
 for t,h in r['protected_hashes'].items():q.execute('SELECT * FROM `'+t+'` ORDER BY id');assert fingerprint(q.fetchall())==h,(t,'Protected business data changed')
 q.execute('SELECT id,name FROM crm_business_status_type WHERE deleted=0');flows={x['name']:x['id'] for x in q.fetchall()}
 q.execute('SELECT b.id,b.name,b.status_type_id,b.end_status,s.type_id,c.name customer_name FROM crm_business b LEFT JOIN crm_business_status s ON s.id=b.status_id AND s.deleted=0 LEFT JOIN crm_customer c ON c.id=b.customer_id WHERE b.deleted=0');businesses=q.fetchall();counts=collections.Counter()
 for row in businesses:
  text=row['name']+' '+(row['customer_name'] or '');assert '北京同仁堂' not in text
  flow=flows['电销流程'] if re.search(r'电商|电子商务|399\s*零号店',text) else flows['标准直销流程']
  assert row['status_type_id']==row['type_id']==flow;counts[flow]+=1
 for t in ['crm_sale_quotation','crm_contract','crm_business_product','crm_contact_business']:
  q.execute('SELECT x.business_id FROM '+t+' x WHERE x.deleted=0 AND x.business_id IS NOT NULL');assert not any(x['business_id'] in deleted for x in q.fetchall())
 for t in ['crm_permission','crm_follow_up_record']:
  q.execute('SELECT * FROM '+t+' WHERE deleted=0');rows=q.fetchall()
  for row in rows:
   assert not (row['biz_type']==4 and row['biz_id'] in deleted)
   if t=='crm_follow_up_record' and row['business_ids']:
    value=row['business_ids'];ids=json.loads(value) if value.startswith('[') else [int(x) for x in value.split(',') if x.strip()]
    assert not deleted.intersection(ids)
 # Sales -> contract -> quotation stays intact; optional business associations may be absent.
 q.execute('SELECT s.id,s.contract_id,s.contract_no,c.no FROM erp_sale_order s JOIN crm_contract c ON c.id=s.contract_id WHERE s.deleted=0 AND c.deleted=0');sales=q.fetchall()
 for row in sales:assert row['contract_no']==row['no'],('Sales-contract number mismatch',row['id'])
result={'verified':True,'rows_checked':checked,'deleted_businesses':len(deleted),'remaining_businesses':len(businesses),'tele_businesses':counts[flows['电销流程']],'direct_businesses':counts[flows['标准直销流程']],'unbound_quotations':r['change_counts'].get('crm_sale_quotation',0),'unbound_contracts':r['change_counts'].get('crm_contract',0),'stage_flow_consistent':True,'no_references_to_removed_businesses':True,'all_other_document_fields_unchanged':True,'protected_tables_unchanged':len(r['protected_hashes'])}
(P/'verification.json').write_text(json.dumps(result,ensure_ascii=False,indent=2),encoding='utf8')
(P/'商机调整核对清单.md').write_text('# 商机流程和关联调整\n\n已同步远程 mom-hm。\n\n- 电商、电子商务及399零号店:10条,电销流程。\n- 其余商机:687条,标准直销流程。\n- 名称或客户含北京同仁堂的商机:463条,逻辑删除。\n- 解除4张报价、10张合同的商机绑定,保留业务单据及其销售、生产、质检、库存关联。\n- 商品明细、联系人关联、跟进关联及商机权限同时清理。\n- 原有结束状态、编号、客户、单价、金额、数量与库存保持不变。\n- 提交前事务核验与提交后独立连接复核通过;报价与合同的商机字段在现有保存接口中为可选。\n',encoding='utf8')
print(json.dumps(result,ensure_ascii=False));c.close()