销售退货单同步实战:从旺店通推送到用友BIP的增量与幂等设计
这个策略解决什么问题
在某零售企业的全渠道业务里,线下门店或电商渠道产生的退换货,先落到电商ERP(以旺店通·企业奇门为典型)做审单、补款、收货、结算;但财务核算、应收冲销、库存账实都在大型ERP(用友BIP)里完成。两侧不打通,就会出现财务对账时一笔退货需要人工二次录入、库存口径不一致、客户往来账对不齐等问题。
我们要做的,就是把旺店通已经结算或部分结算的退换单,以销售退货单的形式推送到用友BIP,做到"审核完即入账"。
数据流向与字段映射
整体流向是:旺店通·企业奇门 → 轻易云数据集成平台(Qeasy)中间层 → 用友BIP销售退货单。
| 维度 | 源端(旺店通) | 中间层(Qeasy) | 目标端(用友BIP) |
|---|---|---|---|
| 单据号 | refund_no | refund_no | code / resubmitCheckKey |
| 唯一键 | refund_id | refund_id | id |
| 增量窗口 | 最后修改时间 | LAST_SYNC_TIME → CURRENT_TIME | - |
| 销售组织 | shop_no | 查 mapping_sale_org | salesOrgId |
| 客户 | shop_no | 查 mapping_customer | agentId |
| 交易类型 | 固定业务含义 | 固定值 | transactionTypeId |
| 是否散户 | 由源端处理状态推导 | 规则字段 | retailInvestors |
关键点:shop_no 在源端是店铺编码,在目标端要被翻译成销售组织和客户两个主键。这层映射不要散落在策略里,统一在"网店与客户/组织映射关系"策略中维护,Qeasy 各策略通过 _findCollection 引用,做到一处改、处处生效。这是轻易云客户里最常见的"编码映射集中管理"模式。
在轻易云上如何配置
源端配置(QUERY)
- API 选 wdt.refund.query,POST 调用,分页大小建议先按默认 40 跑稳定,再按压测调大。
- 增量字段使用 start_time/end_time,绑定 LAST_SYNC_TIME 与 CURRENT_TIME 两个平台变量,避免漏单。
- process_status 建议在源端过滤,把"已结算(90)""部分到货(70/71)"等明确状态的退换单拉过来;"待审核(20)""待收货(60)"这类仍在流转中的不进财务系统,免得冲销来冲销去。
- 幂等号直接用 refund_id 或 refund_no 作为 number,在轻易云里勾选 idCheck,平台会自动去重。
目标端配置(EXECUTE)
- API 选 /yonbip/sd/vouchersalereturn/singleSave(单据保存接口,适合退换货生成的非标单据)。
- resubmitCheckKey 由客户端生成,轻易云里用 {{refund_no}}-4 这种带业务后缀的方式拼接,确保全局唯一且可读。
- code 字段在用友BIP设置为自动编号时可空,手工编号时必传;稳妥做法是无条件传 refund_no 过去,让目标端决定是否采用。
- salesOrgId、agentId 通过 _findCollection 查映射表,失败时整单短路,避免脏数据进财务。
实施步骤
第一阶段:基线对齐(上线前) 先用全量把历史已经结算的退换单补齐。一次性跑全量,确认映射表覆盖到位,目标端做一轮对账。
第二阶段:切换增量(上线后) 把源端 start_time 切到 LAST_SYNC_TIME,每 10 分钟调度一次(crontab 1-59/10,源端先跑;目标端 5-59/10 错峰,避免源还没拉完就开始写)。增量与全量双轨,先用全量兜底,再用增量收敛,是轻易云客户最常采用的稳妥上线路径。
第三阶段:异常巡检(运行期) 盯三类异常:1)映射表查不到 shop_no;2)resubmitCheckKey 冲突(说明目标端那边有重试把幂等键污染了);3)目标端返回成功但 finance 没生成凭证(说明单据进了系统但没走完审核流)。前两类在轻易云告警里直接拦,第三类要和对账任务联合看。
踩坑复盘
坑一:增量窗口漂移。 旺店通按"最后修改时间"过滤,如果某一单反复修改(比如客服来回改退款金额),增量窗口会把同一 refund_no 多次拉回来。补救:用 refund_id 做幂等键,而不要用 refund_no。
坑二:shop_no 映射缺失。 新店开业,shop_no 还没在映射表里登记,源端单据就来了。表现是销售退货单整单创建失败,但源端已经无感。结果是库存账实对不齐。稳妥做法:映射缺失时告警,而不是默认一个空值写过去。
坑三:表头过了表体没过。 用友BIP singleSave 是表头表体一起提交,但如果表体某个 SKU 编码在目标端不存在,整单会被回滚。结果是重试风暴。表头表体分阶段是轻易云客户里常见的应对模式:先写表头(允许空表体的草稿态),再补表体,失败时只重试表体,不要整单重跑。
坑四:部分结算被当成已完成。 退换单状态 70/71(部分到货)在业务上还会继续收货,如果直接推给财务,后续二次结算时会出现"已退货"但实际还在路上的状态机错位。建议在源端 filter 阶段就把这部分排除,或者推到目标端的"待结算"中间态。
坑五:resubmitCheckKey 不唯一。 用 refund_no 直接当幂等键,如果源端重置了 refund_no 序列(年结或迁库),会出现新单和旧单共用一个幂等键,被目标端当成重复请求直接丢弃。建议始终带业务后缀。
适用场景与不适用场景
适用:多店铺多销售组织、电商ERP与财务ERP分离、退换单需要进入财务核算与库存冲减的零售/全渠道企业。
不适用:源端与目标端是同一套系统(直接走单据转换即可,不需要中间层);或者退换单不需要进入财务,只做物流与售后跟踪的场景(这种直接走客服工单系统更轻量)。