Commit c57b0806 by luoqi

feat(sync/friday): 消费/退费接入 — payment + 退费三轨(整单反向/行级/负额冲减)

结算源 arrail-settlement-server(全品牌覆盖,实测钉死语义):
- payment ← patient_settlement status∈{1,3} 且金额≥0(67,355 单):amount=receivable_this
  (应收=业务价值,jvs-dw 同款 LTV 决策);主导支付通道 lookup settlement_modes(金额最大者);
  encounterExternalId=registration_id。
- 退费三轨(比 jvs-dw 双轨多一轨,宿主表达差异):
  ① status=4 整单反向(99 单,全负额) paymentExternalId=ref_settlement_id 精确挂回原单
    (兑现 jvs-dw refund.yaml 预留的升级);
  ② spec is_refund=1 行级部分退费(581 行,全归属 status=3 头单,头单仍作 payment,LTV 自然对账);
  ③ status=3 负金额整单冲减(200 单,2ac96f6d 品牌不用 status=4 的表达)——被 amount_cents>=0
    校验正确拦截后归轨,导出侧 WHERE 按金额正负切分(transforms filter 不支持数值比较)。
- 其余 status(0/2/5/6/7/8)语义未明先不收。不从 settlement 反推治疗(财务≠临床)。

已验证:payment 67,155 + refund 99+581 facts 落库 failed=0。
待收尾:轨③ 200 单补摄等 FRIDAY 测试库恢复(宕机中,已挂探针;导出为空防护拦住了脏摄入)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
parent 04443ff8
# payment — FRIDAY 消费(patient_settlement 头单级,status∈{1,3})
# 消费 transforms 输出表 payment_rows(lookup settlement_modes 已附主导通道 method)
# status=3(含退费行的单)头单仍是原始消费 → 入 payment;其退费行走 refund_item.yaml,
# LTV = Σpayment − Σrefund 自然对账。
canonical: payment
emits:
action: payment_received
subjectType: payment
occurredAtField: paidAt
primary:
table: payment_rows
key: uuid
dedup_by: uuid
field_mapping:
externalId: uuid
updatedAt: updated_gmt_at
createdAt: created_gmt_at
patientExternalId: patient_id
clinicId: organization_id
paidAt: created_gmt_at # 结算入库时间(jvs-dw 同款决策:billing_date 有预付失真)
# amount = receivable_this(应收):LTV=患者带来的业务价值,非现金流(会员卡扣费单不清零)
# FieldMapper.normalize 按 amount_unit=yuan 转 cents
amount: receivable_this
method: method # lookup 主导支付通道(现金/微信/刷卡/医保…原样入 fact)
doctorId: doctor_id
encounterExternalId: registration_id # 关联接诊
settlementSerialNum: settlement_serial_num # 结算流水号(passthrough,对账用)
# refund_full — FRIDAY 整单冲减退费(status=4 反向单 + status=3 负金额单;导出侧 WHERE 切分)
# 实测:status=4 共 99 单全带 ref_settlement_id(挂原结算单)、金额全负;
# 2ac96f6d 品牌不用 status=4,整单冲减记 status=3 负金额(200 单,多无 ref → paymentExternalId=null)。
# ⭐ paymentExternalId = ref_settlement_id —— jvs-dw 缺此字段只能 registration_id 侧面对账,
# FRIDAY 直接兑现 refund.yaml 里预留的升级(退费精确挂回原 payment fact)。
# 金额负值由 refund parser Math.abs 归一为正 cents(语义=患者拿回的钱,host 无关)。
canonical: refund
emits:
action: refund_created
subjectType: refund
occurredAtField: refundedAt
kind: actual
primary:
table: patient_settlement_refund
key: uuid
dedup_by: uuid
field_mapping:
externalId: uuid
updatedAt: updated_gmt_at
createdAt: created_gmt_at
patientExternalId: patient_id
clinicId: organization_id
refundedAt: created_gmt_at
amount: net_receipts_this # 负值,parser abs 归一
paymentExternalId: ref_settlement_id # ⭐ 原结算单(= payment.externalId)
reason: reason
encounterExternalId: registration_id
# refund_item — FRIDAY 行级部分退费(patient_settlement_spec is_refund=1,导出已收窄)
# 实测:581 行全部归属 status=3 头单(部分退费轨道);金额正值 = 被退金额(同 jvs-dw ①语义)。
# 头单本身仍作 payment(原始消费),本资源扣减 → LTV 对账闭合。
# paymentExternalId = settlement_id(所属结算头单 = payment.externalId)。
canonical: refund
emits:
action: refund_created
subjectType: refund
occurredAtField: refundedAt
kind: actual
primary:
table: patient_settlement_spec_refund
key: id
dedup_by: id
field_mapping:
externalId: id
updatedAt: updated_gmt_at
createdAt: created_gmt_at
patientExternalId: patient_id
clinicId: organization_id
refundedAt: created_gmt_at
amount: net_receipts_this
paymentExternalId: settlement_id # 所属结算头单(部分退费挂回原 payment)
# 被退项目名(passthrough,UI 时间轴"退费 ¥X(项目 Y)")
refundProjectName: service_project_name
......@@ -56,6 +56,16 @@ mysql_csv "SELECT diag_code,diag_name,std_code FROM emr.std_diag" std_diag.csv
# 治疗计划(头:方案名/期望;行:牙位/术式/价格区间)→ treatment_planned
mysql_csv "SELECT id,tenant_id AS brand_id,organization_id,customer_id,plan_group_id,plan_name,desired_effect,emergency_urgency,expect_cost,created_gmt_at,updated_gmt_at FROM customer.customer_treat_plan" customer_treat_plan.csv
mysql_csv "SELECT id,tenant_id AS brand_id,customer_id,treat_plan_id,tooth_position,mode_name,charge_min,charge_max,created_gmt_at,updated_gmt_at FROM customer.customer_treat_plan_item" customer_treat_plan_item.csv
# 结算(消费/退费)。status 语义(2026-07-18 实测):1=已结算 3=含退费行的单 4=整单反向冲减(负额+ref 挂原单)。
# ⚠️ 2ac96f6d 品牌不用 status=4:整单冲减直接记 status=3 负金额(实测 200 行)→ 按金额正负切分:
# 消费 = status∈{1,3} 且金额≥0;整单退费 = status=4 或 status=3 负金额(退费第三表达)。
# 其余 status(0 草稿/2/5/6/7/8)语义未明,先不收 → WHERE 收窄(host 等价 dump 允许的过滤)
mysql_csv "SELECT uuid,tenant_id AS brand_id,organization_id,patient_id,doctor_id,status,receivable_this,net_receipts_this,billing_date,registration_id,ref_settlement_id,settlement_serial_num,reason,created_gmt_at,updated_gmt_at FROM \`arrail-settlement-server\`.patient_settlement WHERE status IN (1,3) AND receivable_this >= 0" patient_settlement.csv
mysql_csv "SELECT uuid,tenant_id AS brand_id,organization_id,patient_id,doctor_id,status,receivable_this,net_receipts_this,billing_date,registration_id,ref_settlement_id,settlement_serial_num,reason,created_gmt_at,updated_gmt_at FROM \`arrail-settlement-server\`.patient_settlement WHERE status=4 OR (status=3 AND receivable_this < 0)" patient_settlement_refund.csv
# 退费行级明细(部分退费轨道:is_refund=1 全部归属 status=3 头单)
mysql_csv "SELECT id,tenant_id AS brand_id,organization_id,patient_id,settlement_id,cure_name,service_project_name,receivable_this,net_receipts_this,is_refund,created_gmt_at,updated_gmt_at FROM \`arrail-settlement-server\`.patient_settlement_spec WHERE is_refund=1" patient_settlement_spec_refund.csv
# 支付通道长表(每单×通道一行;lookup 挑金额最大者为主导通道)
mysql_csv "SELECT id,tenant_id AS brand_id,settlement_id,modes_name,money,created_gmt_at,updated_gmt_at FROM \`arrail-settlement-server\`.settlement_modes" settlement_modes.csv
echo "── Mongo 导出(med_emr_info,status∈{3,4} 正式病历)──"
# 不用 mongoexport:Extended JSON 会把 Date/Long 包成 {$date}/{$numberLong} 对象,破坏平铺契约。
......
......@@ -40,6 +40,10 @@ tables:
- { table: std_diag, file: std_diag.csv } # 诊断字典(linkCode→std_code;K 码用)
- { table: customer_treat_plan, file: customer_treat_plan.csv } # 治疗计划头(方案名/期望)
- { table: customer_treat_plan_item, file: customer_treat_plan_item.csv } # 治疗计划行(牙位/术式/价格)
- { table: patient_settlement, file: patient_settlement.csv } # 消费头单(status 1/3 且金额≥0)
- { table: patient_settlement_refund, file: patient_settlement_refund.csv } # 整单退费(status=4 或 3 负额)
- { table: patient_settlement_spec_refund, file: patient_settlement_spec_refund.csv } # 行级退费明细(is_refund=1)
- { table: settlement_modes, file: settlement_modes.csv } # 支付通道长表
transforms:
# ── 患者:把 1:N 联系方式归约成 patient.phone 标量(无需源侧 join)──
......@@ -294,6 +298,31 @@ transforms:
op: normalize
from: mode_name
# ═══════════ 结算链(消费/退费;对齐 jvs-dw payment/refund 契约)═══════════
# status 语义(实测):1=已结算 3=含退费行的单(头单仍是原始消费) 4=整单反向冲减。
# 消费 = status∈{1,3};退费双轨 = ①status=4 整单(ref_settlement_id 挂原单)②spec is_refund=1 行级。
# LTV = Σpayment − Σrefund 自然对账;不从 settlement 反推治疗(财务≠临床,jvs-dw 已验证的教训)。
# ── S.1 消费:status 1/3 → 主导支付通道 lookup(金额最大者)──
- kind: filter
input: patient_settlement
output: _pay_hdr
where:
status: { in: ['1', '3'] }
- kind: lookup
input: _pay_hdr
output: payment_rows
from: settlement_modes
left_key: uuid
right_key: settlement_id
order_by:
- { field: money, dir: desc }
select:
method: modes_name # 主导支付通道(现金/微信/刷卡/医保…干净中文名,原样入 fact)
# ── S.2 退费①:整单冲减(status=4 或 status=3 负金额;导出侧 WHERE 已切分,assembler 直读
# patient_settlement_refund 表;金额负值由 refund parser Math.abs 归一)──
assemblers:
- { file: assemblers/patient.yaml }
- { file: assemblers/appointment.yaml }
......@@ -305,3 +334,6 @@ assemblers:
- { file: assemblers/image.yaml }
- { file: assemblers/treatment_review.yaml }
- { file: assemblers/recommendation.yaml }
- { file: assemblers/payment.yaml }
- { file: assemblers/refund_full.yaml }
- { file: assemblers/refund_item.yaml }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment