124 lines
4.7 KiB
Python
124 lines
4.7 KiB
Python
import logging
|
|
import re
|
|
_logger = logging.getLogger('poynt_match')
|
|
|
|
SL = env['poynt.settlement.line'].sudo()
|
|
Partner = env['res.partner'].sudo()
|
|
Invoice = env['account.move'].sudo()
|
|
|
|
lines = SL.search([('state', '=', 'fetched'), ('action', '=', 'SALE')])
|
|
print(f'Unmatched SALE lines: {len(lines)}', flush=True)
|
|
|
|
matched_by_name = 0
|
|
matched_by_amount = 0
|
|
no_match = 0
|
|
|
|
for sl in lines:
|
|
name = (sl.card_holder_name or '').strip()
|
|
amount = sl.amount
|
|
txn_date = sl.transaction_date.date() if sl.transaction_date else None
|
|
|
|
partner = None
|
|
|
|
# --- Strategy 1: Match by cardholder name ---
|
|
if name and name != '/':
|
|
# Parse LASTNAME/FIRSTNAME or FIRSTNAME LASTNAME
|
|
if '/' in name:
|
|
parts = name.split('/')
|
|
last_name = parts[0].strip().title()
|
|
first_name = parts[1].strip().title() if len(parts) > 1 else ''
|
|
else:
|
|
parts = name.split()
|
|
first_name = parts[0].title() if parts else ''
|
|
last_name = parts[-1].title() if len(parts) > 1 else ''
|
|
|
|
if last_name and first_name:
|
|
# Exact match: "Lastname, Firstname" or "Firstname Lastname"
|
|
candidates = Partner.search([
|
|
('customer_rank', '>', 0),
|
|
'|',
|
|
('name', 'ilike', f'{last_name}, {first_name}'),
|
|
('name', 'ilike', f'{first_name} {last_name}'),
|
|
], limit=5)
|
|
|
|
# If still ambiguous (>1), try to narrow by matching amount to open invoices
|
|
if len(candidates) == 1:
|
|
partner = candidates[0]
|
|
elif len(candidates) > 1:
|
|
# Pick the one with an open invoice matching the amount
|
|
for c in candidates:
|
|
inv = Invoice.search([
|
|
('partner_id', '=', c.id),
|
|
('move_type', '=', 'out_invoice'),
|
|
('payment_state', 'in', ('not_paid', 'partial')),
|
|
('amount_residual', '>=', amount - 1),
|
|
('amount_residual', '<=', amount + 1),
|
|
], limit=1)
|
|
if inv:
|
|
partner = c
|
|
break
|
|
if not partner:
|
|
partner = candidates[0] # Take first if no invoice match
|
|
|
|
# --- Strategy 2: Match by amount against open invoices (if no name match) ---
|
|
if not partner and txn_date and amount > 50:
|
|
# Look for open invoices with matching amount within ±30 days
|
|
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 - __import__('datetime').timedelta(days=60))),
|
|
('invoice_date', '<=', str(txn_date + __import__('datetime').timedelta(days=5))),
|
|
], limit=3)
|
|
|
|
if len(invs) == 1:
|
|
partner = invs[0].partner_id
|
|
sl.invoice_id = invs[0].id
|
|
|
|
# --- Write match ---
|
|
if partner:
|
|
vals = {'partner_id': partner.id, 'state': 'matched'}
|
|
if name and name != '/':
|
|
vals['match_method'] = 'cardholder_name'
|
|
matched_by_name += 1
|
|
else:
|
|
vals['match_method'] = 'invoice_amount'
|
|
matched_by_amount += 1
|
|
|
|
# Also try to find matching invoice if not already set
|
|
if not sl.invoice_id and txn_date:
|
|
inv = Invoice.search([
|
|
('partner_id', '=', partner.id),
|
|
('move_type', '=', 'out_invoice'),
|
|
('payment_state', 'in', ('not_paid', 'partial')),
|
|
('amount_residual', '>=', amount - 1),
|
|
('amount_residual', '<=', amount + 1),
|
|
], limit=1)
|
|
if inv:
|
|
vals['invoice_id'] = inv.id
|
|
|
|
sl.write(vals)
|
|
else:
|
|
no_match += 1
|
|
|
|
if (matched_by_name + matched_by_amount + no_match) % 100 == 0:
|
|
env.cr.commit()
|
|
|
|
env.cr.commit()
|
|
|
|
total_matched = matched_by_name + matched_by_amount
|
|
print(f'\nRESULTS:', flush=True)
|
|
print(f' Matched by name: {matched_by_name}', flush=True)
|
|
print(f' Matched by invoice amount: {matched_by_amount}', flush=True)
|
|
print(f' No match: {no_match}', flush=True)
|
|
print(f' Total matched: {total_matched}/{len(lines)} ({round(100*total_matched/len(lines),1) if lines else 0}%)', flush=True)
|
|
|
|
# Summary
|
|
all_lines = SL.search([])
|
|
print(f'\nOVERALL:', flush=True)
|
|
print(f' Total lines: {len(all_lines)}', flush=True)
|
|
print(f' Matched: {SL.search_count([("state", "=", "matched")])}', flush=True)
|
|
print(f' With invoice: {SL.search_count([("invoice_id", "!=", False)])}', flush=True)
|
|
print(f' Fetched (unmatched): {SL.search_count([("state", "=", "fetched")])}', flush=True)
|