B-43: M3空参考号不再视为相等,单边intercompany需锁定才可计算

This commit is contained in:
腾讯WorkBuddy
2026-08-18 22:10:27 +08:00
parent df517d4a68
commit f99917321b
4 changed files with 151 additions and 14 deletions
+2 -1
View File
@@ -507,7 +507,8 @@ MIGRATIONS: tuple[Migration, ...] = (
ON payer.decision_id = d.id AND payer.role = 'payer'
JOIN transfer_decision_participants payee
ON payee.decision_id = d.id AND payee.role = 'payee'
WHERE e.lifecycle = 'active' AND d.classification = 'intercompany';
WHERE e.lifecycle = 'active' AND d.classification = 'intercompany'
AND (d.pairing = 'paired' OR d.locked = 1);
CREATE TRIGGER canonical_transfer_events_no_delete BEFORE DELETE ON canonical_transfer_events
BEGIN SELECT RAISE (ABORT, 'canonical_transfer_events rows are immutable'); END;
+27 -11
View File
@@ -570,6 +570,24 @@ def _mirror_level(
return None
def _reference_same(row: sqlite3.Row, other: sqlite3.Row) -> bool:
"""Positive reference evidence: both sides carry one equal reference.
``None == None`` must never count as reference agreement; only "both
reference numbers exist and are equal" satisfies the reference layer of the
M1/M3 evidence. A single-sided reference is not equality either.
"""
ref_row = normalize_reference(row["reference"])
ref_other = normalize_reference(other["reference"])
return bool(ref_row and ref_other and ref_row == ref_other)
def _reference_conflict(row: sqlite3.Row, other: sqlite3.Row) -> bool:
ref_row = normalize_reference(row["reference"])
ref_other = normalize_reference(other["reference"])
return bool(ref_row and ref_other and ref_row != ref_other)
def _classify_tier(
row: sqlite3.Row,
other: sqlite3.Row,
@@ -582,19 +600,19 @@ def _classify_tier(
row_date = _parse_datetime(row["transaction_at"]).date()
other_date = _parse_datetime(other["transaction_at"]).date()
date_diff = abs((row_date - other_date).days)
ref_row = normalize_reference(row["reference"])
ref_other = normalize_reference(other["reference"])
ref_conflict = bool(ref_row and ref_other and ref_row != ref_other)
summary_match = bool(ref_row and ref_row == ref_other) or _summary_equal(row, other)
ref_same = _reference_same(row, other)
ref_conflict = _reference_conflict(row, other)
if mirror == "exact":
if ref_row and ref_other and ref_row == ref_other and date_diff <= 3:
if ref_same and date_diff <= 3:
return "M1"
if date_diff <= 1 and not ref_conflict:
return "M2"
return "R1"
# alias / personal-mapping mirror
if date_diff <= 1 and not ref_conflict and (ref_row == ref_other or _summary_equal(row, other)):
# alias / personal-mapping mirror: only equal references (both present) or a
# deterministically equal summary is positive evidence; an absent reference
# is not.
if date_diff <= 1 and not ref_conflict and (ref_same or _summary_equal(row, other)):
return "M3"
return "R1"
@@ -602,11 +620,9 @@ def _classify_tier(
def _tier_evidence(
row: sqlite3.Row, other: sqlite3.Row
) -> tuple[str | None, bool]:
ref_row = normalize_reference(row["reference"])
ref_other = normalize_reference(other["reference"])
if ref_row and ref_other and ref_row == ref_other:
if _reference_same(row, other):
ref_match = "same"
elif ref_row and ref_other:
elif _reference_conflict(row, other):
ref_match = "conflict"
else:
ref_match = None