"""Reclassify all 200 lines on legacy 411000 to proper Nexa accounts based on keyword rules. Run on prod via odoo-shell.""" acct_4030 = env['account.account'].search([('code', '=', '4030')], limit=1).id # Support & Maintenance acct_4230 = env['account.account'].search([('code', '=', '4230')], limit=1).id # Tech Support hourly acct_4320 = env['account.account'].search([('code', '=', '4320')], limit=1).id # Hardware Resale legacy_acct = env['account.account'].with_context(active_test=False).search([('code', '=', '411000')], limit=1).id # (priority, regex pattern (case-insensitive), target_account_id, label) import re RULES = [ ('Computer & Server Maintenance', acct_4030, 'Support & Maintenance'), ('Server Backup & Monitoring', acct_4030, 'Support & Maintenance'), ('Membership Fee', acct_4030, 'Support & Maintenance (membership)'), ('[CUSTCOMP]', acct_4320, 'Hardware Resale (custom PC)'), ('Custom Computer', acct_4320, 'Hardware Resale (custom PC)'), ('ustom Computer', acct_4320, 'Hardware Resale (typo'), # the 'ustom Computer' typo entry ('HP Desk Computer', acct_4320, 'Hardware Resale (HP desktop)'), ('Server 2019', acct_4320, 'Hardware Resale (Windows Server license)'), ('Server Rack', acct_4320, 'Hardware Resale (rack)'), ('16 Port POE', acct_4320, 'Hardware Resale (switch)'), ('CPU:', acct_4320, 'Hardware Resale (custom build)'), ('Cleaning Supplies', acct_4320, 'Hardware Resale (consumables)'), ('ONSITE-', acct_4230, 'Tech Support — onsite'), ('OFFSITE-', acct_4230, 'Tech Support — offsite'), ('Onsite Sever Setup', acct_4230, 'Tech Support — setup'), ('Server Setup', acct_4230, 'Tech Support — setup'), ('Wiring for', acct_4230, 'Tech Support — installation'), ] # Find all lines on 411000 env.cr.execute(""" SELECT aml.id, aml.name, aml.credit FROM account_move_line aml JOIN account_move m ON m.id = aml.move_id WHERE aml.account_id = %s AND m.move_type = 'out_invoice' """, (legacy_acct,)) lines = env.cr.fetchall() bucket = {acct_4030: 0, acct_4230: 0, acct_4320: 0} unmatched = [] for line_id, line_name, credit in lines: name = (line_name or '').strip() matched = False for pattern, target, label in RULES: if pattern.lower() in name.lower(): env.cr.execute("UPDATE account_move_line SET account_id = %s WHERE id = %s", (target, line_id)) bucket[target] += 1 matched = True break if not matched: unmatched.append((line_id, name[:60], credit)) print(f"Reclassified {bucket[acct_4030]} lines -> 4030 Support & Maintenance") print(f"Reclassified {bucket[acct_4230]} lines -> 4230 Tech Support — Hourly") print(f"Reclassified {bucket[acct_4320]} lines -> 4320 Hardware Resale") print(f"Unmatched: {len(unmatched)}") for u in unmatched[:20]: print(f" id={u[0]} amount={u[2]} name={u[1]!r}") env.cr.commit() print(">>> done <<<")