import logging _logger = logging.getLogger('fix_elavon') AML = env['account.move.line'].sudo() BSL = env['account.bank.statement.line'].sudo() # ============================================================ # PART 1: Fix 144 incoming Elavon payments (Bank Charges -> Outstanding Receipts) # ============================================================ print('=== PART 1: Fix incoming Elavon payments ===', flush=True) incoming_bad_amls = AML.search([ ('account_id', '=', 499), # Bank Charges ('statement_line_id', '!=', False), ('statement_line_id.payment_ref', 'ilike', 'elavon'), ('credit', '>', 0), # Credit to Bank Charges = incoming payment writeoff ]) # Filter to only those where the statement line amount > 0 (incoming) incoming_ids = [] for aml in incoming_bad_amls: if aml.statement_line_id.amount > 0: incoming_ids.append(aml.id) print(f'Found {len(incoming_ids)} incoming Elavon writeoff lines to fix', flush=True) if incoming_ids: # Direct SQL update - change account from 499 to 493 env.cr.execute(""" UPDATE account_move_line SET account_id = 493 WHERE id IN %s """, (tuple(incoming_ids),)) env.cr.commit() print(f'Changed {len(incoming_ids)} lines: Bank Charges (499) -> Outstanding Receipts (493)', flush=True) # ============================================================ # PART 2: Fix 6 round-number refund Business PADs # ============================================================ print('\n=== PART 2: Fix round-number customer refunds ===', flush=True) refund_bad_amls = AML.search([ ('account_id', '=', 499), # Bank Charges ('statement_line_id', '!=', False), ('statement_line_id.payment_ref', 'ilike', 'elavon'), ('debit', '>', 0), # Debit to Bank Charges = outgoing writeoff ]) refund_ids = [] for aml in refund_bad_amls: st_line = aml.statement_line_id if st_line.amount < 0 and st_line.amount == round(st_line.amount, 0): refund_ids.append(aml.id) print(f' Refund: line {st_line.id}, ${st_line.amount}, {st_line.move_id.date}', flush=True) print(f'Found {len(refund_ids)} round-number refund lines to fix', flush=True) if refund_ids: env.cr.execute(""" UPDATE account_move_line SET account_id = 493 WHERE id IN %s """, (tuple(refund_ids),)) env.cr.commit() print(f'Changed {len(refund_ids)} lines: Bank Charges (499) -> Outstanding Receipts (493)', flush=True) # ============================================================ # PART 3: Fix reconcile model 96 - should ONLY match fees (Business PAD) # and create new model for incoming Elavon payments # ============================================================ print('\n=== PART 3: Update reconcile models ===', flush=True) # Model 96 currently matches "Elavon Mrch Svc" which catches EVERYTHING # Change it to only match "Business PAD" (the fees) model96 = env['account.reconcile.model'].sudo().browse(96) print(f'Model 96 before: match="{model96.match_label_param}", account={model96.line_ids.account_id.name}', flush=True) model96.write({'match_label_param': 'Business PAD'}) # Keep account 499 (Bank Charges) for the fees - that's correct print(f'Model 96 after: match="{model96.match_label_param}" (now only matches fees)', flush=True) # Model 85 matches "MRCH" which also catches Elavon payments on RBC Chequing # Leave it for now - those are the RBC monthly MRCH fee lines, different pattern # Create new model for incoming Elavon payments -> Outstanding Receipts (493) existing = env['account.reconcile.model'].sudo().search([ ('match_label_param', '=', 'Elavon Mrch Svc : Miscellaneous'), ]) if not existing: new_model = env['account.reconcile.model'].sudo().create({ 'name': 'Elavon Customer Payment Deposit', 'sequence': 55, 'company_id': 1, 'trigger': 'auto_reconcile', 'match_label': 'contains', 'match_label_param': 'Elavon Mrch Svc : Miscellaneous', 'can_be_proposed': True, }) new_line = env['account.reconcile.model.line'].sudo().create({ 'model_id': new_model.id, 'company_id': 1, 'sequence': 10, 'account_id': 493, # Outstanding Receipts 'amount_type': 'percentage', 'amount': 100, 'amount_string': '100', 'label': 'Elavon Visa Terminal Customer Payment', 'partner_id': 1, # Westin Healthcare (company) }) # No tax on payment deposits env.cr.execute(""" INSERT INTO account_reconcile_model_line_account_tax_rel (account_reconcile_model_line_id, account_tax_id) VALUES (%s, 32) """, (new_line.id,)) print(f'Created new model: "Elavon Customer Payment Deposit" -> Outstanding Receipts (493)', flush=True) else: print(f'Model for Elavon incoming already exists: {existing.name}', flush=True) env.cr.commit() # ============================================================ # PART 4: Verify # ============================================================ print('\n=== VERIFICATION ===', flush=True) # Count remaining Elavon lines posted to Bank Charges remaining_499 = env.cr.execute(""" SELECT COUNT(*), ROUND(SUM(ABS(aml.balance))::numeric, 2) FROM account_move_line aml JOIN account_bank_statement_line bsl ON bsl.id = aml.statement_line_id WHERE aml.account_id = 499 AND bsl.payment_ref ILIKE '%%elavon%%' """) row = env.cr.fetchone() print(f'Elavon lines still on Bank Charges: {row[0]} lines, ${row[1]}', flush=True) print('(These should be the monthly processing fees only)', flush=True) # Count Elavon lines now on Outstanding Receipts env.cr.execute(""" SELECT COUNT(*), ROUND(SUM(ABS(aml.balance))::numeric, 2) FROM account_move_line aml JOIN account_bank_statement_line bsl ON bsl.id = aml.statement_line_id WHERE aml.account_id = 493 AND bsl.payment_ref ILIKE '%%elavon%%' """) row = env.cr.fetchone() print(f'Elavon lines now on Outstanding Receipts: {row[0]} lines, ${row[1]}', flush=True)