import logging from datetime import timedelta SL = env['poynt.settlement.line'].sudo() Invoice = env['account.move'].sudo() # Reset name-matched ones that don't have invoices — they might be wrong bad_name_matches = SL.search([ ('state', '=', 'matched'), ('match_method', '=', 'cardholder_name'), ('invoice_id', '=', False), ]) if bad_name_matches: bad_name_matches.write({'state': 'fetched', 'partner_id': False, 'match_method': False}) print(f'Reset {len(bad_name_matches)} name-only matches without invoices', flush=True) env.cr.commit() lines = SL.search([('state', '=', 'fetched'), ('action', '=', 'SALE')]) print(f'Unmatched SALE lines to process: {len(lines)}', flush=True) matched_invoice = 0 matched_card_history = 0 no_match = 0 # --- Build card history: which card_last4 has paid for which partner before? --- # From already-matched lines card_partner_map = {} matched_lines = SL.search([('state', '=', 'matched'), ('partner_id', '!=', False)]) for ml in matched_lines: if ml.card_last4: key = (ml.card_brand, ml.card_last4) card_partner_map.setdefault(key, set()).add(ml.partner_id.id) # Also from payment.transaction (Odoo-processed payments with card tokens) env.cr.execute(""" SELECT pt.payment_method_code, pt.provider_reference, pt.partner_id, tok.provider_ref FROM payment_transaction pt LEFT JOIN payment_token tok ON pt.token_id = tok.id WHERE pt.provider_code = 'poynt' AND pt.state = 'done' AND pt.partner_id IS NOT NULL """) for row in env.cr.fetchall(): pass # Token data doesn't reliably have last4 print(f'Card history: {len(card_partner_map)} unique cards mapped to partners', flush=True) for sl in lines: amount = sl.amount txn_date = sl.transaction_date.date() if sl.transaction_date else None if not txn_date: no_match += 1 continue partner = None invoice = None # --- Strategy 1: Match by exact amount against open invoices --- if amount > 20: invs = Invoice.search([ ('move_type', '=', 'out_invoice'), ('payment_state', 'in', ('not_paid', 'partial')), ('amount_residual', '>=', amount - 0.50), ('amount_residual', '<=', amount + 0.50), ('invoice_date', '>=', str(txn_date - timedelta(days=90))), ('invoice_date', '<=', str(txn_date + timedelta(days=5))), ], limit=5) if len(invs) == 1: # Unique match — confident partner = invs[0].partner_id invoice = invs[0] elif len(invs) > 1: # Multiple invoices with same amount — try card history to pick one card_key = (sl.card_brand, sl.card_last4) known_partners = card_partner_map.get(card_key, set()) for inv in invs: if inv.partner_id.id in known_partners: partner = inv.partner_id invoice = inv break # --- Strategy 2: Card history (same card paid before for same partner) --- if not partner and sl.card_last4: card_key = (sl.card_brand, sl.card_last4) known_partners = card_partner_map.get(card_key, set()) if len(known_partners) == 1: pid = list(known_partners)[0] # Check if this partner has any open invoice close to this amount invs = Invoice.search([ ('partner_id', '=', pid), ('move_type', '=', 'out_invoice'), ('payment_state', 'in', ('not_paid', 'partial')), ('amount_residual', '>=', amount - 5), ('amount_residual', '<=', amount + 5), ], limit=1) if invs: partner = invs[0].partner_id invoice = invs[0] matched_card_history += 1 if partner: vals = { 'partner_id': partner.id, 'state': 'matched', 'match_method': 'invoice_amount' if invoice else 'card_history', } if invoice: vals['invoice_id'] = invoice.id matched_invoice += 1 sl.write(vals) # Update card history if sl.card_last4: card_key = (sl.card_brand, sl.card_last4) card_partner_map.setdefault(card_key, set()).add(partner.id) else: no_match += 1 if (matched_invoice + matched_card_history + no_match) % 100 == 0: env.cr.commit() env.cr.commit() print(f'\nRESULTS:', flush=True) print(f' Matched by invoice amount: {matched_invoice}', flush=True) print(f' Matched by card history: {matched_card_history}', flush=True) print(f' No match: {no_match}', flush=True) # Final stats print(f'\nFINAL STATE:', flush=True) for state in ['matched', 'fetched']: cnt = SL.search_count([('state', '=', state)]) print(f' {state}: {cnt}', flush=True) inv_cnt = SL.search_count([('invoice_id', '!=', False)]) print(f' With invoice linked: {inv_cnt}', flush=True)