B-43: M3空参考号不再视为相等,单边intercompany需锁定才可计算
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user