diff --git a/migration/transform_transactions.py b/migration/transform_transactions.py index b810412..12f2e67 100644 --- a/migration/transform_transactions.py +++ b/migration/transform_transactions.py @@ -166,12 +166,24 @@ def main(): s(r["conepto"]), ) - def efectivo_like(src, name, domain, custmap, src_db, legacy_tbl, *, seen=None): + def efectivo_like(src, name, domain, custmap, src_db, legacy_tbl, *, seen=None, + type_label=None): """Load an EFECTIVO-shaped cash ledger. `seen` (a set) makes the load de-duplicating: keys are added to it as rows load, and a row whose key is already present is skipped. That is how EFECTIVO_BACKUP contributes only its genuinely-new rows. + + `type_label` names the transaction type for every row. These tables have + no type column at all — in Access the type is implied by which table the + row lives in — so unlike DATOS2 there is no string to map and typeId came + out NULL for all of them. + + That is not merely a blank label. handleGetAccountDetails in + my.jorgecuadros.com identifies payments by matching TYPEOFTRX against + ('PAYMENT THANK YOU', 'PAYPAL', 'CASH DEPOSIT', 'CHECK DEPOSIT') to reset + the running balance in mode=current; an unlabelled payment is not + recognised and the balance silently diverges from legacy. """ nonlocal skip_cust, skip_date, skip_dupe df = load(src, name) @@ -189,9 +201,20 @@ def main(): skip_date += 1; continue add(cid, domain, td, dec(r["monto"], Decimal(0)), cur(r["monedas"]), reference=s(r["folio"]), message=s(r["conepto"]), + typeid=type_id_for(type_label), src_db=src_db, src_tbl=legacy_tbl, legacy=str(int(r["_row_num"]))) def fm3(name, legacy_tbl, check_col=None): + """FM3 fee streams. Deliberately left unlabelled, unlike EFECTIVO. + + These rows (EFECTIVO FM3 627, CHEQUE FM3 157) also have no type column, + but every one of them predates the two periods the site exposes — it + allowlists only the current year and the prior year — so none can be + matched against a legacy label, and none can reach a customer. Inventing + a plausible name like "CHECK DEPOSIT" would feed the payment-detection + list in handleGetAccountDetails on nothing but a guess. Leave them NULL + until a real mapping is available. + """ nonlocal skip_cust, skip_date df = load("stg_utilities", name) for _, r in df.iterrows(): @@ -237,17 +260,25 @@ def main(): # order matters: EFECTIVO is the live table and loads first, so a collision # always resolves in its favour. cash_seen: set = set() + # "CASH DEPOSIT" is not a guess: matching these rows to the live site on + # (NUMid, date, amount) resolves to that label unanimously — 66/66 in the + # current-year `datosfreak` and 100/100 in the prior-year `2025` table, + # which are the only two periods the site exposes. efectivo_like("stg_utilities", "efectivo", "UTILITY", util_cust, "UTILITIES", - "EFECTIVO", seen=cash_seen) + "EFECTIVO", seen=cash_seen, type_label="CASH DEPOSIT") efectivo_like("stg_utilities", "efectivo_backup", "UTILITY", util_cust, "UTILITIES", - "EFECTIVO_BACKUP", seen=cash_seen) + "EFECTIVO_BACKUP", seen=cash_seen, type_label="CASH DEPOSIT") fm3("efectivo_fm3", "EFECTIVO FM3") fm3("cheque_fm3", "CHEQUE FM3", check_col="num_cheque") billing("datos2", "datos2") billing("fee_anual", "FEE ANUAL") billing("fee15", "fee15") iva() - efectivo_like("stg_seguros", "efectivo", "INSURANCE", ins_cust, "SEGUROS 16_be", "EFECTIVO") + # Same record shape in the seguros DB. Labelled for consistency in the + # platform's own UI; unverifiable against the site, which only ever reads + # domain='UTILITY', so no customer-facing behaviour depends on it. + efectivo_like("stg_seguros", "efectivo", "INSURANCE", ins_cust, "SEGUROS 16_be", + "EFECTIVO", type_label="CASH DEPOSIT") if sync_mode: # Transaction types are rebuilt with fresh uuids each run; resolve them