简介:制造企业数字化转型ERP与SRM系统集成方案课件,围绕制造企业数字化转型中的核心问题,帮助企业信息化负责人、ERP/SRM实施顾问及项目经理快速理解两大系统的集成路径。内容从项目背景与目标出发,逐层展开需求分析、业务流程梳理、模块划分与接口设计、数据整合与安全策略,再到技术架构中的分层设计、微服务、云计算、大数据与人工智能应用,并覆盖开发实施、集成测试、培训推广和效果评估等完整环节。资源包共1个PPTX演示文稿,大小6.23MB,单文件即可直接打开和编辑,适合在项目汇报、内部培训或方案评审时复用。目前已有83人学习。借助这份方案,读者可以获得一套可落地的ERP与SRM集成框架,包括跨部门协同机制、风险评估应对策略及上线部署要点,各阶段均配有关键实施动作与落地建议,对推进制造企业数字化项目具有较好的参考价值。1. ERP与SRM集成:制造企业数字化转型里最容易被低估的一环先说结论:在制造企业数字化转型项目里,ERP与SRM系统集成的难度通常被严重低估。很多企业以为这只是拉一根接口、把供应商主数据同步过去就完事,结果上线之后发现,一张采购订单在两边系统里状态对不上,仓库收货后财务看不到,供应商发货了计划却不知道。问题不在技术,而在两套系统对同一件事的理解不一样。这个标题指向的是一份完整的落地方案。它要解决的核心问题是:当ERP承担企业内部人财物资源的计划与核算、SRM承担企业与外部供应商的协作时,两者之间的主数据、业务单据和状态反馈,如何做到一致、及时、可追溯。适合正在做选型或即将启动集成的企业信息化负责人、实施顾问和开发人员。本文从架构选型、主数据同步、业务流联动、避坑和验证五个层面,把一套可复现的做法讲清楚。2. 集成前先定边界:ERP与SRM各自该管到哪一层2.1 为什么集成难在“边界”而不是“技术”做ERP与SRM集成,最容易犯的错是一上来就问“接口用REST还是WebService”。协议从来不是难点,难点在于两套系统各自的数据主权和流程责任没有划清。ERP里的物料、供应商、采购订单、收货凭证,天然是“企业内部唯一事实源”;SRM里的询价、报价、供应商协同、绩效评估,天然是“企业与外部协作的工作台”。这两者不是一个大系统拆成两半,而是两个不同生命周期阶段的管理工具。从数据生命周期看,物料档案在ERP中创建,但SRM需要引用;供应商资质在SRM中管理,但ERP需要用来开采购单;询价和报价过程发生在SRM,但最终生成的采购订单要回写ERP。如果边界不提前划清,就会出现“两边都建了物料”“供应商改了名称不知道以哪边为准”“询价结果没转成订单”等一连串问题。所以我一般会在项目启动阶段,让双方关键用户坐在一起,把“哪个字段由哪套系统负责维护、哪个流程在哪套系统发起、完成后通过什么机制通知对方”写成一个边界清单。这个清单比接口设计文档更重要,它决定了后续开发的方向。集成方案如果脱离业务边界去谈技术,后期必然返工。2.2 接口边界清单:主数据与业务单据的权责划分以下是我在项目里常用的一张边界划分表,能直接拿来当模板用,覆盖了主数据和业务单据两个层面。数据类型归属系统维护方同步方向同步触发时机物料主数据ERPERP物料组ERP → SRM创建/变更后实时或准实时供应商主数据SRMSRM供应商管理组SRM → ERP资质审核通过后采购申请ERP需求部门ERP → SRM审批完成后询价单SRMSRM采购员SRM内部生成采购申请转询价时报价单SRM供应商仅SRM内部采购员确认报价后采购订单ERPERP采购员ERP → SRMERP审批完成发货通知SRM供应商SRM → ERP供应商确认发货后收货凭证ERPERP仓库ERP → SRM仓库过账后强调一个原则:标准主数据只允许单边维护,业务单据通过状态来联动,而不是两边直接改对方的数据。这条原则能避免大量“覆盖”带来的脏数据。3. 主数据同步:把物料和供应商档案做成增量机制3.1 物料档案同步:增量拉取与幂等写入物料主数据从ERP同步到SRM,常见做法是ERP侧在物料创建或修改后,通过中间表或消息队列把增量数据推出去,SRM侧提供接收接口。如果企业没有消息中间件,用定时任务拉取ERP的增量表也很常见,关键是增量字段要可靠。下面是一个简化但可运行的SRM侧接收物料主数据的Python示例,逻辑和参数说明会放在代码后面。这套逻辑可以直接适配到任何类似场景,核心是增量 + 幂等。# erp_material_sync.py import requests import json import hashlib def get_erp_material_incremental(last_sync_time, page_num=1, page_size=100): """从ERP增量接口拉取物料数据""" url = "http://erp-internal:8080/api/material/incremental" params = { "last_sync_time": last_sync_time, "page_num": page_num, "page_size": page_size, "app_key": "SRM_SYSTEM" } resp = requests.get(url, params=params, timeout=10) resp.raise_for_status() return resp.json() # 预期返回 {"list": [...], "has_more": bool} def material_md5(material): """给物料关键字段生成指纹,用于判断是否真的变化""" key_fields = json.dumps({ "code": material.get("code"), "name": material.get("name"), "spec": material.get("spec"), "unit": material.get("unit"), "category": material.get("category") }, ensure_ascii=False, sort_keys=True) return hashlib.md5(key_fields.encode("utf-8")).hexdigest() def upsert_material_to_srm(material_list): """幂等写入SRM物料档案表""" for m in material_list: fingerprint = material_md5(m) # 先查本地指纹,相同则不更新 existing = db.query("SELECT fingerprint FROM t_material WHERE code=%s", m["code"]) if existing and existing[0] == fingerprint: continue if existing: db.execute("UPDATE t_material SET name=%s, spec=%s, unit=%s, fingerprint=%s WHERE code=%s", (m["name"], m["spec"], m["unit"], fingerprint, m["code"])) else: db.execute("INSERT INTO t_material(code, name, spec, unit, fingerprint) VALUES(%s,%s,%s,%s,%s)", (m["code"], m["name"], m["spec"], m["unit"], fingerprint)) db.commit() def sync_material_job(): """定时任务入口:处理增量拉取和翻页""" last_sync_time = get_last_sync_time_from_db() while True: data = get_erp_material_incremental(last_sync_time) material_list = data.get("list", []) if not material_list: break upsert_material_to_srm(material_list) if not data.get("has_more"): break # 翻页时用ERP返回的最新时间戳增量步进 last_sync_time = data.get("next_sync_time", last_sync_time) update_last_sync_time_in_db(last_sync_time)这段代码里有两个参数值得注意。last_sync_time是增量拉取的游标,要存在数据库里而不是内存里,否则任务重启后会重复拉全量数据。fingerprint字段是关键设计,它把物料名称、规格、单位等可变更字段算成哈希,只在内容真正变化时更新SRM,这个设计能避免“每次同步都把档案update一遍”造成的无用日志和锁竞争。如果ERP侧连增量查询接口都没有,退而求其次的常见做法是每天全量比对一次,但要把比对放在ERP库的只读副本上做,以免影响ERP白天业务。全量方案上线后要特别关注接口超时,数据量超过几万条时必须分页。3.2 供应商主数据:SRM审核通过后才能写到ERP供应商主数据的流向通常和物料相反,是在SRM里完成注册和资质审核后,由SRM把供应商编号、名称、税号、银行信息推送或由ERP拉取。这里最容易翻车的是“供应商在SRM改了名称或银行账户,ERP没收到变更”,导致开票和付款环节出问题。我的做法是在SRM侧做变更事件,而不是定时全量同步,因为供应商主数据总量通常只有几百到几千条,变更频率低,增量事件完全够用。SRM每次对供应商信息做修改后,把变更记录写入一张t_supplier_change_log,ERP侧通过定时任务增量消费这些变更记录并更新供应商档案。关于编码规则,很多项目在集成前就埋了雷:SRM和ERP各有一套供应商编码,集成时才会发现两边对不上。我建议在项目初始就定下“以SRM的供应商编号为准,或由ERP统一分配”,不要搞两套并存再映射。如果历史数据已经乱了,也要建一张映射表统一管理,而不是在接口里临时转换。4. 采购业务流联动:从申请到收货全链路的状态一致4.1 采购申请转采购订单:审批完成后才允许流动采购业务流联动是整个集成方案的骨架。在ERP里,需求部门提采购申请,走完内部审批后由采购员转换成采购订单。订单审批完成后,才允许把订单数据同步到SRM,供供应商确认。如果ERP的审批流程没走完就同步,供应商就会看到未生效的订单,后续改单、撤单都会非常难处理。ERP侧可执行的做法是:在审批通过的动作上挂一个回调,把已审批的采购订单写入中间表t_purchase_order_sync,SRM侧定时拉取并展示给供应商确认。中间表需要包含订单头、订单行状态,以及一个sync_status字段用来标记是否已成功推送,这样在失败时可以做补偿,而不是直接丢消息。# po_to_srm_sync.py import requests def fetch_approved_po_orders(): """从ERP中间表读取审批完成但未同步的采购订单""" return db.query("SELECT po_id, po_code, supplier_code, lines_json " "FROM t_purchase_order_sync WHERE sync_status=0 LIMIT 50") def push_po_to_srm(order): """推送订单到SRM的采购订单接收接口""" payload = { "po_code": order.po_code, "supplier_code": order.supplier_code, "order_lines": json.loads(order.lines_json), "source_system": "ERP" } resp = requests.post("http://srm-internal:8080/api/po/receive", json=payload, timeout=15) return resp.status_code == 200 def process_po_sync(): orders = fetch_approved_po_orders() for order in orders: try: ok = push_po_to_srm(order) except requests.exceptions.RequestException as e: log.error("push po failed: %s, po_code=%s", e, order.po_code) continue if ok: db.execute("UPDATE t_purchase_order_sync SET sync_status=1 WHERE po_id=%s", order.po_id) db.commit()这段代码里的sync_status=0表示待同步,1表示已同步,2表示ERP侧撤回或作废导致无需同步,最好再加一个-1表示多次重试仍然失败的“死信”。这样一张中间表就能把同步过程完整记录下来,排查问题时可以直接对着中间表看数据卡在哪一步。4.2 发货与收货:以ERP过账为最终依据供应商在SRM确认订单后,后续动作是供应商在SRM发起发货通知。这里要特别注意的是,SRM的发货通知只能作为“预通知”,不能代替ERP的收货。ERP仓库完成实物收货并过账后,才会产生正式的收货凭证,这个凭证要回传SRM,作为对账和绩效评价的数据来源。我见过一个典型案例:某企业把SRM的发货数量误认为入库数量,结果财务对账差了几百条,最后还是靠ERP的收货凭证反查才理顺。所以在设计状态机时,要明确一个原则:SRM能看到供应商“发了什么”,但“实际入库什么”只有ERP的收货过账说了算。在接口设计上,ERP的收货信息同步给SRM属于“单向通知”,不需要SRM返回确认之外的复杂逻辑。SRM侧收到收货凭证后,用po_code + po_line + delivery_no作为联合唯一键,保证一条发货行对应一条收货记录,重复推送时做幂等丢弃,避免频繁的重试消息把SRM的对账表搞出重复行。5. 集成避坑指南:五个高频问题的现象、原因与解决5.1 采购订单更新不回传SRM,导致供应商按旧版本发货现象:ERP里改了订单的交期或数量,SRM里供应商看到的还是旧数据,按旧交期排了生产计划。原因:同步方案只做了“创建”没做“变更推送”,或者变更状态标识没有触发增量捕获。解决:在同步逻辑里把“变更后推送”作为默认行为,并且用订单头版本号加一,SRM侧如果发现收到的版本号大于当前展示版本,直接覆盖更新并给供应商发提醒。5.2 物料编码两边不一致,仓库收货扫不出物料现象:SRM里的物料名称和ERP的物料描述看起来一样,但编码体系两套并存,供应商按SRM编码送货,仓库按ERP编码收不到货。原因:项目启动阶段没有统一编码规则,集成方案里还做了双向映射,结果映射表没人维护。解决:集成上线前统一编码,物料和供应商都以一方为准,另一方引用时完全采用对方编码;历史数据差异用映射表兜底,新增数据坚决不允许双编码。5.3 接口超时但实际成功,重推造成重复数据现象:ERP推送采购订单时,SRM处理完了但响应超时,ERP端触发了重试,SRM产生两条重复订单行。原因:缺失幂等设计。解决:SRM侧接收接口强制要求外部请求头携带message_id,用数据库唯一约束保证同一条message_id只能成功写入一次,已经处理过的直接返回“重复消息已忽略”。5.4 单据删除操作没有同步逻辑现象:ERP里删除了一张未审批的采购申请,SRM还留着记录,后续报表统计永远对不上。原因:只做了流程节点的状态同步,没有处理取消和删除场景。解决:在集成边界表里明确删除策略——ERP所有删除都改为逻辑删除,并把状态置为“取消”同步给SRM,SRM侧不允许物理删除历史记录,保证审计链路完整。5.5 测试环境一切正常,上线后数据量大内存溢出现象:联调时单条数据接口响应正常,正式上线后供应商集中确认订单,处理线程积压导致内存溢出。原因:没有做批量处理,每笔业务一个线程,数据库连接池被打满。解决:对接接口时按批量上限设计,ERP侧一次推50条或100条,SRM侧同步批量写入;同时把同步任务改成异步队列,接口响应只返回“已接收”,实际写入过程放到后台worker执行。6. 进阶验证:用幂等对账机制给集成链路做稳定性体检集成做完之后,验证不能只靠“页面能打开、数据能查到”。我习惯在方案上线时同时做一条独立于业务功能的对账通道,周期性比对两边的数据量级和关键单据状态,发现问题早于业务人员反馈。这个方法成本不高,但能非常有效地减少后续“半夜被电话叫醒”的概率。对账逻辑放在一个独立的定时任务里,每晚跑一次,对比维度固定在三个:物料主数据条数是否一致、采购订单状态是否一致、收货累计数量是否一致。不一致时直接在大盘上标红,不需要立即处理,但要有单据级别的明细差异清单。# daily_reconciliation.py def check_po_reconciliation(): """对比ERP和SRM当天有过变更的采购订单状态""" erp_orders = db_erp.query( "SELECT po_code, status, updated_at FROM t_po WHERE updated_at >= CURDATE()") srm_orders = db_srm.query( "SELECT po_code, status, updated_at FROM t_po_staging " "WHERE updated_at >= CURDATE()") erp_map = {o.po_code: o for o in erp_orders} srm_map = {o.po_code: o for o in srm_orders} diffs = [] for code in set(erp_map) | set(srm_map): erp_po = erp_map.get(code) srm_po = srm_map.get(code) if erp_po is None: diffs.append((code, "SRM独有", srm_po.status)) elif srm_po is None: diffs.append((code, "ERP独有", erp_po.status)) elif erp_po.status != srm_po.status: diffs.append((code, "状态不一致", f"ERP={erp_po.status} SRM={srm_po.status}")) if diffs: send_alert_email(f"采购订单对账差异{len(diffs)}条: {diffs[:20]}") else: log.info("采购订单对账一致")我平时还有一个习惯:每次版本迭代后,挑一个真实单据走完“申请→审批→订单→供应商确认→发货通知→收货过账→对账”全链路,然后把各节点的时间戳拉出来看耗时分布。某一段耗时异常涨高,就要趁早去看是不是接口或者队列堵了,而不是等业务部门来投诉。做集成最忌讳的就是“表面联调通过就不再管”,制造业的ERP与SRM链路长、参与角色多,只有日常对账和全链路抽查,才能保证这套方案不是上线即失效。这套集成的价值最终体现在一个地方:业务人员不再需要通过电话和微信群去核对“货发了没有、单到了没有”,系统状态本身就是答案。希望帮到你。本文还有配套的精品资源,点击获取
简介:制造企业数字化转型ERP与SRM系统集成方案课件,围绕制造企业数字化转型中的核心问题,帮助企业信息化负责人、ERP/SRM实施顾问及项目经理快速理解两大系统的集成路径。内容从项目背景与目标出发,逐层展开需求分析、业务流程梳理、模块划分与接口设计、数据整合与安全策略,再到技术架构中的分层设计、微服务、云计算、大数据与人工智能应用,并覆盖开发实施、集成测试、培训推广和效果评估等完整环节。资源包共1个PPTX演示文稿,大小6.23MB,单文件即可直接打开和编辑,适合在项目汇报、内部培训或方案评审时复用。目前已有83人学习。借助这份方案,读者可以获得一套可落地的ERP与SRM集成框架,包括跨部门协同机制、风险评估应对策略及上线部署要点,各阶段均配有关键实施动作与落地建议,对推进制造企业数字化项目具有较好的参考价值。1. ERP与SRM集成:制造企业数字化转型里最容易被低估的一环先说结论:在制造企业数字化转型项目里,ERP与SRM系统集成的难度通常被严重低估。很多企业以为这只是拉一根接口、把供应商主数据同步过去就完事,结果上线之后发现,一张采购订单在两边系统里状态对不上,仓库收货后财务看不到,供应商发货了计划却不知道。问题不在技术,而在两套系统对同一件事的理解不一样。这个标题指向的是一份完整的落地方案。它要解决的核心问题是:当ERP承担企业内部人财物资源的计划与核算、SRM承担企业与外部供应商的协作时,两者之间的主数据、业务单据和状态反馈,如何做到一致、及时、可追溯。适合正在做选型或即将启动集成的企业信息化负责人、实施顾问和开发人员。本文从架构选型、主数据同步、业务流联动、避坑和验证五个层面,把一套可复现的做法讲清楚。2. 集成前先定边界:ERP与SRM各自该管到哪一层2.1 为什么集成难在“边界”而不是“技术”做ERP与SRM集成,最容易犯的错是一上来就问“接口用REST还是WebService”。协议从来不是难点,难点在于两套系统各自的数据主权和流程责任没有划清。ERP里的物料、供应商、采购订单、收货凭证,天然是“企业内部唯一事实源”;SRM里的询价、报价、供应商协同、绩效评估,天然是“企业与外部协作的工作台”。这两者不是一个大系统拆成两半,而是两个不同生命周期阶段的管理工具。从数据生命周期看,物料档案在ERP中创建,但SRM需要引用;供应商资质在SRM中管理,但ERP需要用来开采购单;询价和报价过程发生在SRM,但最终生成的采购订单要回写ERP。如果边界不提前划清,就会出现“两边都建了物料”“供应商改了名称不知道以哪边为准”“询价结果没转成订单”等一连串问题。所以我一般会在项目启动阶段,让双方关键用户坐在一起,把“哪个字段由哪套系统负责维护、哪个流程在哪套系统发起、完成后通过什么机制通知对方”写成一个边界清单。这个清单比接口设计文档更重要,它决定了后续开发的方向。集成方案如果脱离业务边界去谈技术,后期必然返工。2.2 接口边界清单:主数据与业务单据的权责划分以下是我在项目里常用的一张边界划分表,能直接拿来当模板用,覆盖了主数据和业务单据两个层面。数据类型归属系统维护方同步方向同步触发时机物料主数据ERPERP物料组ERP → SRM创建/变更后实时或准实时供应商主数据SRMSRM供应商管理组SRM → ERP资质审核通过后采购申请ERP需求部门ERP → SRM审批完成后询价单SRMSRM采购员SRM内部生成采购申请转询价时报价单SRM供应商仅SRM内部采购员确认报价后采购订单ERPERP采购员ERP → SRMERP审批完成发货通知SRM供应商SRM → ERP供应商确认发货后收货凭证ERPERP仓库ERP → SRM仓库过账后强调一个原则:标准主数据只允许单边维护,业务单据通过状态来联动,而不是两边直接改对方的数据。这条原则能避免大量“覆盖”带来的脏数据。3. 主数据同步:把物料和供应商档案做成增量机制3.1 物料档案同步:增量拉取与幂等写入物料主数据从ERP同步到SRM,常见做法是ERP侧在物料创建或修改后,通过中间表或消息队列把增量数据推出去,SRM侧提供接收接口。如果企业没有消息中间件,用定时任务拉取ERP的增量表也很常见,关键是增量字段要可靠。下面是一个简化但可运行的SRM侧接收物料主数据的Python示例,逻辑和参数说明会放在代码后面。这套逻辑可以直接适配到任何类似场景,核心是增量 + 幂等。# erp_material_sync.py import requests import json import hashlib def get_erp_material_incremental(last_sync_time, page_num=1, page_size=100): """从ERP增量接口拉取物料数据""" url = "http://erp-internal:8080/api/material/incremental" params = { "last_sync_time": last_sync_time, "page_num": page_num, "page_size": page_size, "app_key": "SRM_SYSTEM" } resp = requests.get(url, params=params, timeout=10) resp.raise_for_status() return resp.json() # 预期返回 {"list": [...], "has_more": bool} def material_md5(material): """给物料关键字段生成指纹,用于判断是否真的变化""" key_fields = json.dumps({ "code": material.get("code"), "name": material.get("name"), "spec": material.get("spec"), "unit": material.get("unit"), "category": material.get("category") }, ensure_ascii=False, sort_keys=True) return hashlib.md5(key_fields.encode("utf-8")).hexdigest() def upsert_material_to_srm(material_list): """幂等写入SRM物料档案表""" for m in material_list: fingerprint = material_md5(m) # 先查本地指纹,相同则不更新 existing = db.query("SELECT fingerprint FROM t_material WHERE code=%s", m["code"]) if existing and existing[0] == fingerprint: continue if existing: db.execute("UPDATE t_material SET name=%s, spec=%s, unit=%s, fingerprint=%s WHERE code=%s", (m["name"], m["spec"], m["unit"], fingerprint, m["code"])) else: db.execute("INSERT INTO t_material(code, name, spec, unit, fingerprint) VALUES(%s,%s,%s,%s,%s)", (m["code"], m["name"], m["spec"], m["unit"], fingerprint)) db.commit() def sync_material_job(): """定时任务入口:处理增量拉取和翻页""" last_sync_time = get_last_sync_time_from_db() while True: data = get_erp_material_incremental(last_sync_time) material_list = data.get("list", []) if not material_list: break upsert_material_to_srm(material_list) if not data.get("has_more"): break # 翻页时用ERP返回的最新时间戳增量步进 last_sync_time = data.get("next_sync_time", last_sync_time) update_last_sync_time_in_db(last_sync_time)这段代码里有两个参数值得注意。last_sync_time是增量拉取的游标,要存在数据库里而不是内存里,否则任务重启后会重复拉全量数据。fingerprint字段是关键设计,它把物料名称、规格、单位等可变更字段算成哈希,只在内容真正变化时更新SRM,这个设计能避免“每次同步都把档案update一遍”造成的无用日志和锁竞争。如果ERP侧连增量查询接口都没有,退而求其次的常见做法是每天全量比对一次,但要把比对放在ERP库的只读副本上做,以免影响ERP白天业务。全量方案上线后要特别关注接口超时,数据量超过几万条时必须分页。3.2 供应商主数据:SRM审核通过后才能写到ERP供应商主数据的流向通常和物料相反,是在SRM里完成注册和资质审核后,由SRM把供应商编号、名称、税号、银行信息推送或由ERP拉取。这里最容易翻车的是“供应商在SRM改了名称或银行账户,ERP没收到变更”,导致开票和付款环节出问题。我的做法是在SRM侧做变更事件,而不是定时全量同步,因为供应商主数据总量通常只有几百到几千条,变更频率低,增量事件完全够用。SRM每次对供应商信息做修改后,把变更记录写入一张t_supplier_change_log,ERP侧通过定时任务增量消费这些变更记录并更新供应商档案。关于编码规则,很多项目在集成前就埋了雷:SRM和ERP各有一套供应商编码,集成时才会发现两边对不上。我建议在项目初始就定下“以SRM的供应商编号为准,或由ERP统一分配”,不要搞两套并存再映射。如果历史数据已经乱了,也要建一张映射表统一管理,而不是在接口里临时转换。4. 采购业务流联动:从申请到收货全链路的状态一致4.1 采购申请转采购订单:审批完成后才允许流动采购业务流联动是整个集成方案的骨架。在ERP里,需求部门提采购申请,走完内部审批后由采购员转换成采购订单。订单审批完成后,才允许把订单数据同步到SRM,供供应商确认。如果ERP的审批流程没走完就同步,供应商就会看到未生效的订单,后续改单、撤单都会非常难处理。ERP侧可执行的做法是:在审批通过的动作上挂一个回调,把已审批的采购订单写入中间表t_purchase_order_sync,SRM侧定时拉取并展示给供应商确认。中间表需要包含订单头、订单行状态,以及一个sync_status字段用来标记是否已成功推送,这样在失败时可以做补偿,而不是直接丢消息。# po_to_srm_sync.py import requests def fetch_approved_po_orders(): """从ERP中间表读取审批完成但未同步的采购订单""" return db.query("SELECT po_id, po_code, supplier_code, lines_json " "FROM t_purchase_order_sync WHERE sync_status=0 LIMIT 50") def push_po_to_srm(order): """推送订单到SRM的采购订单接收接口""" payload = { "po_code": order.po_code, "supplier_code": order.supplier_code, "order_lines": json.loads(order.lines_json), "source_system": "ERP" } resp = requests.post("http://srm-internal:8080/api/po/receive", json=payload, timeout=15) return resp.status_code == 200 def process_po_sync(): orders = fetch_approved_po_orders() for order in orders: try: ok = push_po_to_srm(order) except requests.exceptions.RequestException as e: log.error("push po failed: %s, po_code=%s", e, order.po_code) continue if ok: db.execute("UPDATE t_purchase_order_sync SET sync_status=1 WHERE po_id=%s", order.po_id) db.commit()这段代码里的sync_status=0表示待同步,1表示已同步,2表示ERP侧撤回或作废导致无需同步,最好再加一个-1表示多次重试仍然失败的“死信”。这样一张中间表就能把同步过程完整记录下来,排查问题时可以直接对着中间表看数据卡在哪一步。4.2 发货与收货:以ERP过账为最终依据供应商在SRM确认订单后,后续动作是供应商在SRM发起发货通知。这里要特别注意的是,SRM的发货通知只能作为“预通知”,不能代替ERP的收货。ERP仓库完成实物收货并过账后,才会产生正式的收货凭证,这个凭证要回传SRM,作为对账和绩效评价的数据来源。我见过一个典型案例:某企业把SRM的发货数量误认为入库数量,结果财务对账差了几百条,最后还是靠ERP的收货凭证反查才理顺。所以在设计状态机时,要明确一个原则:SRM能看到供应商“发了什么”,但“实际入库什么”只有ERP的收货过账说了算。在接口设计上,ERP的收货信息同步给SRM属于“单向通知”,不需要SRM返回确认之外的复杂逻辑。SRM侧收到收货凭证后,用po_code + po_line + delivery_no作为联合唯一键,保证一条发货行对应一条收货记录,重复推送时做幂等丢弃,避免频繁的重试消息把SRM的对账表搞出重复行。5. 集成避坑指南:五个高频问题的现象、原因与解决5.1 采购订单更新不回传SRM,导致供应商按旧版本发货现象:ERP里改了订单的交期或数量,SRM里供应商看到的还是旧数据,按旧交期排了生产计划。原因:同步方案只做了“创建”没做“变更推送”,或者变更状态标识没有触发增量捕获。解决:在同步逻辑里把“变更后推送”作为默认行为,并且用订单头版本号加一,SRM侧如果发现收到的版本号大于当前展示版本,直接覆盖更新并给供应商发提醒。5.2 物料编码两边不一致,仓库收货扫不出物料现象:SRM里的物料名称和ERP的物料描述看起来一样,但编码体系两套并存,供应商按SRM编码送货,仓库按ERP编码收不到货。原因:项目启动阶段没有统一编码规则,集成方案里还做了双向映射,结果映射表没人维护。解决:集成上线前统一编码,物料和供应商都以一方为准,另一方引用时完全采用对方编码;历史数据差异用映射表兜底,新增数据坚决不允许双编码。5.3 接口超时但实际成功,重推造成重复数据现象:ERP推送采购订单时,SRM处理完了但响应超时,ERP端触发了重试,SRM产生两条重复订单行。原因:缺失幂等设计。解决:SRM侧接收接口强制要求外部请求头携带message_id,用数据库唯一约束保证同一条message_id只能成功写入一次,已经处理过的直接返回“重复消息已忽略”。5.4 单据删除操作没有同步逻辑现象:ERP里删除了一张未审批的采购申请,SRM还留着记录,后续报表统计永远对不上。原因:只做了流程节点的状态同步,没有处理取消和删除场景。解决:在集成边界表里明确删除策略——ERP所有删除都改为逻辑删除,并把状态置为“取消”同步给SRM,SRM侧不允许物理删除历史记录,保证审计链路完整。5.5 测试环境一切正常,上线后数据量大内存溢出现象:联调时单条数据接口响应正常,正式上线后供应商集中确认订单,处理线程积压导致内存溢出。原因:没有做批量处理,每笔业务一个线程,数据库连接池被打满。解决:对接接口时按批量上限设计,ERP侧一次推50条或100条,SRM侧同步批量写入;同时把同步任务改成异步队列,接口响应只返回“已接收”,实际写入过程放到后台worker执行。6. 进阶验证:用幂等对账机制给集成链路做稳定性体检集成做完之后,验证不能只靠“页面能打开、数据能查到”。我习惯在方案上线时同时做一条独立于业务功能的对账通道,周期性比对两边的数据量级和关键单据状态,发现问题早于业务人员反馈。这个方法成本不高,但能非常有效地减少后续“半夜被电话叫醒”的概率。对账逻辑放在一个独立的定时任务里,每晚跑一次,对比维度固定在三个:物料主数据条数是否一致、采购订单状态是否一致、收货累计数量是否一致。不一致时直接在大盘上标红,不需要立即处理,但要有单据级别的明细差异清单。# daily_reconciliation.py def check_po_reconciliation(): """对比ERP和SRM当天有过变更的采购订单状态""" erp_orders = db_erp.query( "SELECT po_code, status, updated_at FROM t_po WHERE updated_at >= CURDATE()") srm_orders = db_srm.query( "SELECT po_code, status, updated_at FROM t_po_staging " "WHERE updated_at >= CURDATE()") erp_map = {o.po_code: o for o in erp_orders} srm_map = {o.po_code: o for o in srm_orders} diffs = [] for code in set(erp_map) | set(srm_map): erp_po = erp_map.get(code) srm_po = srm_map.get(code) if erp_po is None: diffs.append((code, "SRM独有", srm_po.status)) elif srm_po is None: diffs.append((code, "ERP独有", erp_po.status)) elif erp_po.status != srm_po.status: diffs.append((code, "状态不一致", f"ERP={erp_po.status} SRM={srm_po.status}")) if diffs: send_alert_email(f"采购订单对账差异{len(diffs)}条: {diffs[:20]}") else: log.info("采购订单对账一致")我平时还有一个习惯:每次版本迭代后,挑一个真实单据走完“申请→审批→订单→供应商确认→发货通知→收货过账→对账”全链路,然后把各节点的时间戳拉出来看耗时分布。某一段耗时异常涨高,就要趁早去看是不是接口或者队列堵了,而不是等业务部门来投诉。做集成最忌讳的就是“表面联调通过就不再管”,制造业的ERP与SRM链路长、参与角色多,只有日常对账和全链路抽查,才能保证这套方案不是上线即失效。这套集成的价值最终体现在一个地方:业务人员不再需要通过电话和微信群去核对“货发了没有、单到了没有”,系统状态本身就是答案。希望帮到你。本文还有配套的精品资源,点击获取