import java.sql.*; import java.nio.file.*; import java.io.*; import java.util.*; class QuotationContractOrders { static final Path ROOT=Path.of("scripts/quotation-contract-orders"); static String esc(String s){return s.replace("&","&").replace("<","<").replace(">",">");} static int count(Statement s,String q)throws Exception{try(var r=s.executeQuery(q)){r.next();return r.getInt(1);}} static void check(Statement s,String q,String msg)throws Exception{if(count(s,q)!=0)throw new Exception(msg);} static void enableAutoApprove(Connection c)throws Exception{ c.setAutoCommit(false); try(var s=c.createStatement()){ if(count(s,"SELECT COUNT(*) FROM bpm_process_auto_approve_config WHERE process_definition_key='*'")>1)throw new Exception("Global switch ambiguous"); s.executeUpdate("INSERT INTO bpm_process_auto_approve_config(process_definition_key,auto_approve,remark,creator,updater) SELECT '*',1,'2026-10-12用户要求关闭所有人工审核,保留流程及通过回调','1','1' WHERE NOT EXISTS(SELECT 1 FROM bpm_process_auto_approve_config WHERE process_definition_key='*')"); s.executeUpdate("UPDATE bpm_process_auto_approve_config SET auto_approve=1,deleted=0,remark='2026-10-12用户要求关闭所有人工审核,保留流程及通过回调',updater='1',update_time=NOW() WHERE process_definition_key='*'"); int menus=s.executeUpdate("UPDATE system_menu SET visible=0,updater='1',update_time=NOW() WHERE deleted=0 AND type<>3 AND component IN('bpm/task/todo/index','bpm/task/done/index','bpm/task/manager/index') AND visible<>0"); // Remove the newly introduced documentary key; the engine uses the BPM global switch above. s.executeUpdate("DELETE FROM infra_config WHERE config_key='business.audit.enabled' AND creator='1' AND remark LIKE '%用户2026-10-12明确要求关闭所有审核%'"); if(count(s,"SELECT COUNT(*) FROM bpm_process_auto_approve_config WHERE process_definition_key='*' AND auto_approve=1 AND deleted=0")!=1)throw new Exception("Global switch failed"); c.commit();String result="Global BPM auto-approve enabled; review menus hidden="+menus+"; pending tasks="+count(s,"SELECT COUNT(*) FROM ACT_RU_TASK")+"\n";Files.writeString(ROOT.resolve("auto-approve-result.txt"),result);System.out.print(result); }catch(Exception e){c.rollback();throw e;}finally{c.setAutoCommit(true);} } static void ordersWithAuditDisabled(Connection c)throws Exception{ c.setAutoCommit(false); try(var s=c.createStatement()){ if(count(s,"SELECT GET_LOCK('quotation-contract-orders-20261012',10)")!=1)throw new Exception("Conversion running"); if(count(s,"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='mom-hm' AND table_name IN('infra_config','crm_contract','erp_sale_order','erp_sale_order_items') AND engine='InnoDB'")!=4)throw new Exception("Transactional tables required"); try(var r=s.executeQuery("SELECT c.id FROM crm_contract c JOIN crm_sale_quotation q ON q.contract_id=c.id WHERE q.deleted=0 AND q.no LIKE 'BJ20261012-%' FOR UPDATE")){while(r.next()){} } if(count(s,"SELECT COUNT(*) FROM crm_sale_quotation q JOIN crm_contract c ON c.id=q.contract_id WHERE q.deleted=0 AND c.deleted=0 AND q.no LIKE 'BJ20261012-%' AND q.status=10")!=345)throw new Exception("Expected 345 converted quotations"); check(s,"SELECT COUNT(*) FROM crm_sale_quotation q JOIN crm_contract c ON c.id=q.contract_id WHERE q.deleted=0 AND q.no LIKE 'BJ20261012-%' AND (c.audit_status NOT IN(0,20) OR c.process_instance_id IS NOT NULL OR c.total_price<>q.total_price OR c.customer_id<>q.customer_id OR c.owner_user_id<>q.owner_user_id)","Existing workflow or contract changed"); check(s,"SELECT COUNT(*) FROM infra_config WHERE config_key='business.audit.enabled' AND deleted=1","Deleted audit config conflicts"); s.executeUpdate("INSERT INTO infra_config(category,name,config_key,value,type,visible,remark,creator,updater) SELECT '业务流程','业务单据审核开关','business.audit.enabled','false',2,1,'false关闭业务单据审核,单据经业务校验后直接生效;用户2026-10-12明确要求关闭所有审核','1','1' WHERE NOT EXISTS(SELECT 1 FROM infra_config WHERE config_key='business.audit.enabled')"); s.executeUpdate("UPDATE infra_config SET value='false',visible=1,updater='1',update_time=NOW() WHERE config_key='business.audit.enabled' AND deleted=0"); if(count(s,"SELECT COUNT(*) FROM infra_config WHERE config_key='business.audit.enabled' AND deleted=0 AND value='false'")!=1)throw new Exception("Audit config ambiguity"); int active=s.executeUpdate("UPDATE crm_contract c JOIN crm_sale_quotation q ON q.contract_id=c.id SET c.audit_status=20,c.remark=CONCAT(COALESCE(c.remark,''),';业务审核关闭,按用户要求免审生效(2026-10-12)'),c.updater='1',c.update_time=NOW() WHERE q.deleted=0 AND q.no LIKE 'BJ20261012-%' AND c.deleted=0 AND c.audit_status=0 AND c.process_instance_id IS NULL"); String sql=Files.readString(ROOT.resolve("待审批后生成订单.sql")).replaceAll("(?m)^--[^\\r\\n]*", ""); int created=0,items=0; for(String statement:sql.split(";")){ if(statement.isBlank())continue; if(statement.trim().startsWith("INSERT INTO erp_sale_order(no,")){ statement=statement.replace("c.quotation_no),0,c.customer_id", "c.quotation_no),20,c.customer_id"); check(s,"SELECT COUNT(*) FROM stage_order_contract WHERE audit_status<>20","Contract not effective"); check(s,"SELECT COUNT(*) FROM stage_order_contract c JOIN erp_sale_order o ON o.no COLLATE utf8mb4_unicode_ci=CONCAT('XSDD-',c.quotation_no) COLLATE utf8mb4_unicode_ci WHERE o.deleted=1 OR o.contract_id<>c.id","Order number conflict"); created=s.executeUpdate(statement); }else if(statement.trim().startsWith("INSERT INTO erp_sale_order_items"))items=s.executeUpdate(statement); else s.execute(statement); } if(count(s,"SELECT COUNT(*) FROM stage_order_contract")!=345)throw new Exception("Order stage incomplete"); check(s,"SELECT COUNT(*) FROM stage_order_contract s JOIN crm_contract c ON c.id=s.id LEFT JOIN erp_sale_order o ON o.id=c.order_id AND o.deleted=0 WHERE o.id IS NULL OR o.contract_id<>c.id OR NOT(o.contract_no COLLATE utf8mb4_unicode_ci <=> c.no COLLATE utf8mb4_unicode_ci) OR NOT(o.no COLLATE utf8mb4_unicode_ci <=> c.order_no COLLATE utf8mb4_unicode_ci) OR o.customer_id<>c.customer_id OR o.sale_user_id<>c.owner_user_id OR o.total_price<>c.total_price OR o.total_product_price<>c.total_product_price OR o.status<>20 OR o.out_count<>0 OR o.out_status<>0 OR o.process_instance_id IS NOT NULL","Order header verification failed"); check(s,"SELECT COUNT(*) FROM stage_order_contract c JOIN erp_sale_order o ON o.contract_id=c.id AND o.deleted=0 JOIN crm_contract_product p ON p.contract_id=c.id AND p.deleted=0 LEFT JOIN erp_sale_order_items oi ON oi.order_id=o.id AND oi.product_id=p.item_id AND oi.deleted=0 WHERE oi.id IS NULL OR oi.product_unit_id<>p.item_unit_id OR NOT(oi.product_price <=> p.contract_price) OR NOT(oi.count <=> p.count) OR NOT(oi.total_price <=> p.total_price)","Order line verification failed"); check(s,"SELECT COUNT(*) FROM (SELECT o.id,o.total_price,o.total_count,SUM(i.total_price) actual,SUM(i.count) actual_count,COUNT(i.id) n FROM stage_order_contract c JOIN erp_sale_order o ON o.contract_id=c.id AND o.deleted=0 JOIN erp_sale_order_items i ON i.order_id=o.id AND i.deleted=0 GROUP BY o.id,o.total_price,o.total_count,c.id HAVING actual<>o.total_price OR actual_count<>o.total_count OR n<>(SELECT COUNT(*) FROM crm_contract_product p WHERE p.contract_id=MIN(c.id) AND p.deleted=0)) z","Order sums/counts failed"); if(count(s,"SELECT COUNT(*) FROM stage_order_contract c JOIN erp_sale_order o ON o.contract_id=c.id AND o.deleted=0")!=345)throw new Exception("Expected 345 orders"); c.commit();String result="audit disabled config=false; contracts made effective="+active+"; created sales orders="+created+", lines="+items+"; 345 quotation-contract-order links and prices/units/amounts verified; total=15392805.71; orders status=20, no shipments or production plans\n";Files.writeString(ROOT.resolve("orders-result.txt"),result);System.out.print(result); }catch(Exception e){c.rollback();throw e;}finally{try(var s=c.createStatement()){s.execute("SELECT RELEASE_LOCK('quotation-contract-orders-20261012')");}c.setAutoCommit(true);} } static void contracts(Connection c)throws Exception{ c.setAutoCommit(false); try(var s=c.createStatement()){ if(count(s,"SELECT GET_LOCK('quotation-contract-orders-20261012',10)")!=1)throw new Exception("Conversion running"); if(count(s,"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='mom-hm' AND table_name IN('crm_sale_quotation','crm_sale_quotation_product','crm_contract','crm_contract_product','crm_permission') AND engine='InnoDB'")!=5)throw new Exception("Transactional tables required"); try(var r=s.executeQuery("SELECT id FROM crm_sale_quotation WHERE deleted=0 FOR UPDATE")){while(r.next()){} } s.execute("CREATE TEMPORARY TABLE stage_convert_quote AS SELECT *,CONCAT('HT-',no) generated_contract_no FROM crm_sale_quotation WHERE deleted=0"); s.execute("CREATE TEMPORARY TABLE stage_convert_line AS SELECT p.* FROM crm_sale_quotation_product p JOIN stage_convert_quote q ON q.id=p.quotation_id WHERE p.deleted=0"); if(count(s,"SELECT COUNT(*) FROM stage_convert_quote")!=345 || count(s,"SELECT COUNT(*) FROM stage_convert_line")!=1702)throw new Exception("Source counts changed; review required"); check(s,"SELECT COUNT(*) FROM stage_convert_quote WHERE status<>0 AND NOT(status=10 AND contract_id IS NOT NULL)","Quotation not convertible"); check(s,"SELECT COUNT(*) FROM stage_convert_quote q LEFT JOIN crm_customer c ON c.id=q.customer_id AND c.deleted=0 LEFT JOIN system_users u ON u.id=q.owner_user_id AND u.deleted=0 AND u.status=0 WHERE c.id IS NULL OR u.id IS NULL","Customer/owner unavailable"); check(s,"SELECT COUNT(*) FROM stage_convert_line p LEFT JOIN mdm_item m ON m.id=p.item_id AND m.deleted=0 AND m.status=0 LEFT JOIN mdm_unit_measure u ON u.id=p.item_unit_id AND u.deleted=0 AND u.status=0 WHERE m.id IS NULL OR u.id IS NULL OR p.count<=0 OR p.quotation_price<0","Material/unit/quantity invalid"); check(s,"SELECT COUNT(*) FROM (SELECT q.id FROM stage_convert_quote q LEFT JOIN stage_convert_line p ON p.quotation_id=q.id GROUP BY q.id,q.total_product_price HAVING COUNT(p.id)=0 OR SUM(p.total_price)<>q.total_product_price) x","Quote header/lines do not balance"); check(s,"SELECT COUNT(*) FROM stage_convert_quote WHERE CHAR_LENGTH(CONCAT('由报价单[',no,']生成;',COALESCE(remark,'')))>500 OR CHAR_LENGTH(CONCAT(name,'-合同'))>255 OR tax_price IS NOT NULL OR discount_percent<>0","Unsupported length/tax/discount requires review"); check(s,"SELECT COUNT(*) FROM stage_convert_quote q JOIN crm_contract c ON c.no COLLATE utf8mb4_unicode_ci=q.generated_contract_no COLLATE utf8mb4_unicode_ci WHERE c.deleted<>0 OR (q.contract_id IS NULL OR q.contract_id<>c.id)","Contract number conflict"); check(s,"SELECT COUNT(*) FROM stage_convert_quote q LEFT JOIN crm_contract c ON c.id=q.contract_id AND c.deleted=0 WHERE q.contract_id IS NOT NULL AND (c.id IS NULL OR c.customer_id<>q.customer_id OR c.owner_user_id<>q.owner_user_id OR c.total_price<>q.total_price)","Existing contract link conflict"); int contracts=s.executeUpdate("INSERT INTO crm_contract(no,name,customer_id,business_id,owner_user_id,audit_status,order_date,total_product_price,discount_percent,total_price,deposit_price,remark,creator,updater) SELECT q.generated_contract_no,CONCAT(q.name,'-合同'),q.customer_id,q.business_id,q.owner_user_id,0,q.quotation_time,q.total_product_price,q.discount_percent,q.total_price,0,CONCAT('由报价单[',q.no,']生成;',COALESCE(q.remark,'')),q.owner_user_id,q.owner_user_id FROM stage_convert_quote q WHERE q.contract_id IS NULL AND q.status=0"); int lines=s.executeUpdate("INSERT INTO crm_contract_product(contract_id,item_id,item_unit_id,item_price,contract_price,count,total_price,creator,updater) SELECT c.id,p.item_id,p.item_unit_id,p.item_price,p.quotation_price,p.count,p.total_price,q.owner_user_id,q.owner_user_id FROM stage_convert_quote q JOIN crm_contract c ON c.no COLLATE utf8mb4_unicode_ci=q.generated_contract_no COLLATE utf8mb4_unicode_ci AND c.deleted=0 JOIN stage_convert_line p ON p.quotation_id=q.id WHERE q.contract_id IS NULL AND q.status=0"); int perms=s.executeUpdate("INSERT INTO crm_permission(biz_type,biz_id,user_id,level,creator,updater) SELECT 5,c.id,q.owner_user_id,1,q.owner_user_id,q.owner_user_id FROM stage_convert_quote q JOIN crm_contract c ON c.no COLLATE utf8mb4_unicode_ci=q.generated_contract_no COLLATE utf8mb4_unicode_ci AND c.deleted=0 WHERE NOT EXISTS(SELECT 1 FROM crm_permission p WHERE p.biz_type=5 AND p.biz_id=c.id AND p.user_id=q.owner_user_id AND p.level=1 AND p.deleted=0)"); int linked=s.executeUpdate("UPDATE crm_sale_quotation q JOIN stage_convert_quote t ON t.id=q.id JOIN crm_contract c ON c.no COLLATE utf8mb4_unicode_ci=t.generated_contract_no COLLATE utf8mb4_unicode_ci AND c.deleted=0 SET q.status=10,q.contract_id=c.id,q.contract_no=c.no,q.update_time=NOW(),q.updater=q.owner_user_id WHERE q.contract_id IS NULL AND q.status=0"); if(linked!=contracts)throw new Exception("Quote link count mismatch"); check(s,"SELECT COUNT(*) FROM stage_convert_quote t JOIN crm_sale_quotation q ON q.id=t.id LEFT JOIN crm_contract c ON c.id=q.contract_id AND c.deleted=0 WHERE q.status<>10 OR c.id IS NULL OR NOT(q.contract_no COLLATE utf8mb4_unicode_ci <=> c.no COLLATE utf8mb4_unicode_ci) OR c.total_price<>t.total_price OR c.total_product_price<>t.total_product_price OR c.customer_id<>t.customer_id OR c.owner_user_id<>t.owner_user_id","Contract header verification failed"); check(s,"SELECT COUNT(*) FROM stage_convert_line p JOIN crm_sale_quotation q ON q.id=p.quotation_id LEFT JOIN crm_contract_product cp ON cp.contract_id=q.contract_id AND cp.item_id=p.item_id AND cp.deleted=0 WHERE cp.id IS NULL OR NOT(cp.item_unit_id <=> p.item_unit_id) OR NOT(cp.item_price <=> p.item_price) OR NOT(cp.contract_price <=> p.quotation_price) OR NOT(cp.count <=> p.count) OR NOT(cp.total_price <=> p.total_price)","Contract line verification failed"); check(s,"SELECT COUNT(*) FROM (SELECT q.id,c.total_price,SUM(cp.total_price) actual,COUNT(cp.id) n FROM stage_convert_quote t JOIN crm_sale_quotation q ON q.id=t.id JOIN crm_contract c ON c.id=q.contract_id JOIN crm_contract_product cp ON cp.contract_id=c.id AND cp.deleted=0 GROUP BY q.id,c.total_price HAVING actual<>c.total_price OR n<>(SELECT COUNT(*) FROM stage_convert_line p WHERE p.quotation_id=q.id)) z","Contract total/line count mismatch"); c.commit();String result="345 quotation links verified; created contracts="+contracts+", lines="+lines+", owner permissions="+perms+"; contracts are DRAFT(0); exact quotation units/prices/amounts preserved; total=15392805.71\n";Files.writeString(ROOT.resolve("contracts-result.txt"),result);System.out.print(result); }catch(Exception e){c.rollback();throw e;}finally{try(var s=c.createStatement()){s.execute("SELECT RELEASE_LOCK('quotation-contract-orders-20261012')");}c.setAutoCommit(true);} } static void snapshot(Connection c,String name)throws Exception{ var qs=new LinkedHashMap(); qs.put("columns","SELECT table_name,column_name,column_type,is_nullable,column_default FROM information_schema.columns WHERE table_schema='mom-hm' AND table_name IN('crm_sale_quotation','crm_sale_quotation_product','crm_contract','crm_contract_product','crm_permission','erp_sale_order','erp_sale_order_items') ORDER BY table_name,ordinal_position"); qs.put("quotations","SELECT * FROM crm_sale_quotation WHERE deleted=0"); qs.put("quotationLines","SELECT p.* FROM crm_sale_quotation_product p JOIN crm_sale_quotation q ON q.id=p.quotation_id WHERE q.deleted=0 AND p.deleted=0"); qs.put("contracts","SELECT * FROM crm_contract"); qs.put("contractLines","SELECT * FROM crm_contract_product"); qs.put("orders","SELECT * FROM erp_sale_order"); qs.put("orderLines","SELECT * FROM erp_sale_order_items"); qs.put("permissions","SELECT * FROM crm_permission WHERE biz_type IN(5,9) AND deleted=0"); qs.put("materials","SELECT id,code,name,unit_measure_id,status,deleted FROM mdm_item WHERE id IN(SELECT item_id FROM crm_sale_quotation_product WHERE deleted=0)"); qs.put("customers","SELECT id,name,deleted FROM crm_customer WHERE id IN(SELECT customer_id FROM crm_sale_quotation WHERE deleted=0)"); qs.put("users","SELECT id,username,nickname,status,deleted FROM system_users WHERE id IN(SELECT owner_user_id FROM crm_sale_quotation WHERE deleted=0)"); try(var s=c.createStatement()){ qs.put("auditColumns","SELECT table_name,column_name,column_type,is_nullable,column_default FROM information_schema.columns WHERE table_schema='mom-hm' AND table_name IN('bpm_process_auto_approve_config','system_menu') ORDER BY table_name,ordinal_position"); if(count(s,"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='mom-hm' AND table_name='bpm_process_auto_approve_config'")>0)qs.put("autoApprove","SELECT * FROM bpm_process_auto_approve_config"); qs.put("bpmMenus","SELECT * FROM system_menu WHERE deleted=0 AND (path LIKE '%bpm%' OR component LIKE '%bpm%' OR permission LIKE 'bpm:%' OR name LIKE '%审批%' OR name LIKE '%待办%' OR name LIKE '%已办%' OR name LIKE '%协同%')"); qs.put("pendingTasks","SELECT ID_,NAME_,ASSIGNEE_,PROC_DEF_ID_,PROC_INST_ID_ FROM ACT_RU_TASK"); if(count(s,"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='mom-hm' AND table_name='ACT_RE_PROCDEF'")>0)qs.put("processDefinitions","SELECT ID_,KEY_,NAME_,CATEGORY_,SUSPENSION_STATE_ FROM ACT_RE_PROCDEF"); StringBuilder x=new StringBuilder(""); for(var e:qs.entrySet()){x.append('<').append(e.getKey()).append('>');try(var r=s.executeQuery(e.getValue())){var m=r.getMetaData();while(r.next()){x.append("");for(int i=1;i<=m.getColumnCount();i++){String k=m.getColumnLabel(i),v=r.getString(i);x.append('<').append(k).append('>');if(v!=null)x.append(esc(v));x.append("');}x.append("");}}x.append("');}x.append("");Files.writeString(ROOT.resolve(name+".xml"),x.toString()); }System.out.println(name+" snapshot saved"); } public static void main(String[] a)throws Exception{ Files.createDirectories(ROOT);String password=new BufferedReader(new InputStreamReader(System.in)).readLine(); try(var c=DriverManager.getConnection("jdbc:mysql://36.213.150.237:10000/mom-hm?characterEncoding=UTF-8&allowPublicKeyRetrieval=true&useSSL=false&connectTimeout=10000&socketTimeout=60000","root",password);var s=c.createStatement()){ try(var r=s.executeQuery("SELECT DATABASE()")){r.next();if(!r.getString(1).equals("mom-hm"))throw new Exception("Wrong database");} if(a[0].equals("contracts")){contracts(c);return;} if(a[0].equals("orders-no-audit")){ordersWithAuditDisabled(c);return;} if(a[0].equals("enable-auto-approve")){enableAutoApprove(c);return;} snapshot(c,a[0]); } } }