import sys # ========================================== # 1. MIGRATE TO BRANCH # ========================================== def migrate_to_branch(env): print("\n==========================================") print("1. RUNNING CORE DATA MIGRATION") print("==========================================") source_name = 'PT Kipas Lima Delapan' target_name = 'Kedai Kipas 58 Rungkut' source_company = env['res.company'].search([('name', '=', source_name)], limit=1) target_company = env['res.company'].search([('name', '=', target_name)], limit=1) if not source_company or not target_company: print(f"Could not find one or both companies: '{source_name}', '{target_name}'") return print(f"Source Company: {source_company.name} (ID: {source_company.id})") print(f"Target Company: {target_company.name} (ID: {target_company.id})") tables_to_update = [ 'pos_config', 'pos_session', 'pos_order', 'pos_order_line', 'pos_payment', 'pos_payment_method', 'stock_warehouse', 'stock_location', 'stock_picking_type', 'stock_picking', 'stock_move', 'stock_move_line', 'stock_quant', 'stock_valuation_layer', 'stock_scrap', 'stock_inventory', 'stock_rule', 'stock_route', 'stock_putaway_rule', 'mrp_production', 'mrp_workorder', 'mrp_workcenter', 'mrp_routing_workcenter', 'mrp_bom', 'mrp_bom_line', 'mrp_unbuild', 'mrp_consumption_warning', 'account_journal', 'account_move', 'account_move_line', 'account_payment', 'account_bank_statement', 'account_bank_statement_line', 'account_partial_reconcile', 'account_payment_term', 'sale_order', 'sale_order_line', 'purchase_order', 'purchase_order_line', 'purchase_requisition', 'purchase_requisition_line', 'hr_employee', 'hr_contract', 'hr_attendance', 'hr_payslip', 'hr_expense', 'hr_expense_sheet', 'product_pricelist', 'ir_sequence' ] total_updated = 0 for table in tables_to_update: env.cr.execute(""" SELECT column_name FROM information_schema.columns WHERE table_name=%s AND column_name='company_id' """, (table,)) if env.cr.fetchone(): env.cr.execute(f""" UPDATE {table} SET company_id = %s WHERE company_id = %s """, (target_company.id, source_company.id)) rowcount = env.cr.rowcount if rowcount > 0: print(f"Updated {rowcount} records in table '{table}'") total_updated += rowcount print(f"Committing changes... (Total {total_updated} rows updated)") env.cr.commit() env.invalidate_all() print("Migration complete!") # ========================================== # 2. CLONE ACCOUNTING JOURNALS # ========================================== def clone_journals(env): print("\n==========================================") print("2. CLONING ACCOUNTING JOURNALS") print("==========================================") source_name = 'Kedai Kipas 58 Rungkut' target_name = 'Kedai Kipas 58 Tenggilis' source_company = env['res.company'].search([('name', 'ilike', source_name)], limit=1) target_company = env['res.company'].search([('name', 'ilike', target_name)], limit=1) if not source_company or not target_company: print(f"Could not find one or both companies: '{source_name}', '{target_name}'") return journals = env['account.journal'].with_context(active_test=False).search([ ('company_id', '=', source_company.id) ]) print(f"Found {len(journals)} journals in {source_company.name} to clone.") count = 0 for journal in journals: existing = env['account.journal'].with_context(active_test=False).search([ ('code', '=', journal.code), ('company_id', '=', target_company.id) ], limit=1) if existing: print(f" -> '{journal.name}' (Code: {journal.code}) already exists in {target_company.name}. Skipping.") continue try: with env.cr.savepoint(): copy_defaults = { 'company_id': target_company.id, 'default_account_id': journal.default_account_id.id if journal.default_account_id else False, 'suspense_account_id': journal.suspense_account_id.id if journal.suspense_account_id else False, 'profit_account_id': journal.profit_account_id.id if journal.profit_account_id else False, 'loss_account_id': journal.loss_account_id.id if journal.loss_account_id else False, } new_journal = journal.copy(copy_defaults) new_journal.write({ 'name': journal.name, 'code': journal.code, }) print(f" -> Cloned '{journal.name}' [{journal.code}] (New ID: {new_journal.id})") count += 1 except Exception as e: print(f" -> Failed to clone '{journal.name}' [{journal.code}]: {e}") print(f"Committing changes... (Cloned {count} journals)") env.cr.commit() # ========================================== # 3. CLONE POS PAYMENT METHODS # ========================================== def clone_payment_methods(env): print("\n==========================================") print("3. CLONING POS PAYMENT METHODS") print("==========================================") source_name = 'Kedai Kipas 58 Rungkut' target_name = 'Kedai Kipas 58 Tenggilis' source_company = env['res.company'].search([('name', 'ilike', source_name)], limit=1) target_company = env['res.company'].search([('name', 'ilike', target_name)], limit=1) methods = env['pos.payment.method'].with_context(active_test=False).search([ ('company_id', '=', source_company.id) ]) print(f"Found {len(methods)} payment methods to clone.") count = 0 for method in methods: existing = env['pos.payment.method'].with_context(active_test=False).search([ ('name', '=', method.name), ('company_id', '=', target_company.id) ], limit=1) if existing: print(f" -> '{method.name}' already exists in target. Skipping.") continue new_method = method.copy({ 'company_id': target_company.id, 'receivable_account_id': method.receivable_account_id.id if method.receivable_account_id else False, 'outstanding_account_id': method.outstanding_account_id.id if method.outstanding_account_id else False, 'journal_id': method.journal_id.id if method.journal_id else False, }) print(f" -> Cloned '{method.name}' (New ID: {new_method.id})") count += 1 print(f"Committing changes... (Cloned {count} methods)") env.cr.commit() # ========================================== # 4. CLONE CASH ROUNDING & PRICELISTS # ========================================== def clone_rounding_and_pricelist(env): print("\n==========================================") print("4. CLONING CASH ROUNDING & PRICELISTS") print("==========================================") source_name = 'Kedai Kipas 58 Rungkut' target_name = 'Kedai Kipas 58 Tenggilis' source_company = env['res.company'].search([('name', 'ilike', source_name)], limit=1) target_company = env['res.company'].search([('name', 'ilike', target_name)], limit=1) print("--- Applying Cash Rounding Setup from Rungkut to Tenggilis POS ---") source_configs = env['pos.config'].with_context(active_test=False).search([ ('company_id', '=', source_company.id), ('cash_rounding', '=', True) ], limit=1) target_configs = env['pos.config'].with_context(active_test=False).search([ ('company_id', '=', target_company.id) ]) if source_configs and target_configs: template_config = source_configs[0] for t_config in target_configs: t_config.write({ 'cash_rounding': template_config.cash_rounding, 'rounding_method': template_config.rounding_method.id if template_config.rounding_method else False, 'only_round_cash_method': template_config.only_round_cash_method, }) print(f" -> Applied Cash Rounding setup to POS '{t_config.name}'") print("\n--- Cloning Pricelists ---") pricelists = env['product.pricelist'].with_context(active_test=False).search([ ('company_id', '=', source_company.id) ]) count_pricelist = 0 for pricelist in pricelists: existing = env['product.pricelist'].with_context(active_test=False).search([ ('name', '=', pricelist.name), ('company_id', '=', target_company.id) ], limit=1) if existing: print(f" -> '{pricelist.name}' already exists. Skipping.") continue try: with env.cr.savepoint(): new_pricelist = pricelist.copy({ 'company_id': target_company.id, 'website_id': False, }) new_pricelist.write({'name': pricelist.name}) print(f" -> Cloned Pricelist '{pricelist.name}'") count_pricelist += 1 except Exception as e: print(f" -> Failed to clone Pricelist: {e}") if source_configs and target_configs: print("\n--- Updating POS Pricelist configuration ---") for s_config, t_config in zip(source_configs, target_configs): if s_config.pricelist_id: matching_pl = env['product.pricelist'].with_context(active_test=False).search([ ('name', '=', s_config.pricelist_id.name), ('company_id', '=', target_company.id) ], limit=1) if matching_pl: t_config.write({'pricelist_id': matching_pl.id}) print(f" -> Set Pricelist '{matching_pl.name}' on POS '{t_config.name}'") if s_config.available_pricelist_ids: mapped_ids = [] for pl in s_config.available_pricelist_ids: match = env['product.pricelist'].with_context(active_test=False).search([ ('name', '=', pl.name), ('company_id', '=', target_company.id) ], limit=1) if match: mapped_ids.append(match.id) if mapped_ids: t_config.write({'available_pricelist_ids': [(6, 0, mapped_ids)]}) print(f" -> Set Available Pricelists on POS '{t_config.name}'") print(f"Committing changes... (Cloned setup, {count_pricelist} pricelists)") env.cr.commit() # ========================================== # 5. FIX COMPANY JOURNALS # ========================================== def fix_company_journals(env): print("\n==========================================") print("5. FIXING RES.COMPANY JOURNALS") print("==========================================") company_name = 'Kedai Kipas 58 Tenggilis' company = env['res.company'].search([('name', 'ilike', company_name)], limit=1) if not company: return cash_basis_journal = env['account.journal'].search([ ('company_id', '=', company.id), ('code', '=', 'CABA') ], limit=1) if not cash_basis_journal: cash_basis_journal = env['account.journal'].search([ ('company_id', '=', company.id), ('name', 'ilike', 'Cash Basis') ], limit=1) vals = {} if cash_basis_journal: vals['tax_cash_basis_journal_id'] = cash_basis_journal.id else: vals['tax_cash_basis_journal_id'] = False tax_journal = env['account.journal'].search([ ('company_id', '=', company.id), ('name', 'ilike', 'Kas Kecil Operasional Tenggilis') ], limit=1) if tax_journal: vals['account_tax_periodicity_journal_id'] = tax_journal.id else: vals['account_tax_periodicity_journal_id'] = False company.write(vals) print(f"Fixed company journal settings.") env.cr.commit() # ========================================== # 6. FIX ALL MISMATCHED JOURNALS # ========================================== def fix_all_mismatched_journals(env): print("\n==========================================") print("6. FIXING ALL MISMATCHED JOURNALS") print("==========================================") company_name = 'Kedai Kipas 58 Tenggilis' company = env['res.company'].search([('name', 'ilike', company_name)], limit=1) if not company: return fields_to_check = ['account_tax_periodicity_journal_id', 'tax_cash_basis_journal_id', 'currency_exdiff_journal_id'] vals = {} for field in fields_to_check: try: journal_id = getattr(company, field, False) if journal_id and journal_id.company_id and journal_id.company_id.id != company.id: vals[field] = False except Exception: pass pos_configs = env['pos.config'].search([('company_id', '=', company.id)]) for config in pos_configs: config_vals = {} if config.journal_id and config.journal_id.company_id and config.journal_id.company_id.id != company.id: config_vals['journal_id'] = False if config.invoice_journal_id and config.invoice_journal_id.company_id and config.invoice_journal_id.company_id.id != company.id: config_vals['invoice_journal_id'] = False if config_vals: config.write(config_vals) if vals: company.write(vals) methods = env['pos.payment.method'].with_context(active_test=False).search([ ('company_id', '=', company.id), ('journal_id.company_id', '!=', company.id), ('journal_id', '!=', False) ]) for method in methods: method.journal_id = False banks = env['res.partner.bank'].with_context(active_test=False).search([ ('company_id', '=', company.id), ('journal_id.company_id', '!=', company.id), ('journal_id', '!=', False) ]) for bank in banks: bank.journal_id = False print("Fixed leftover mismatched journals.") env.cr.commit() if __name__ == '__main__': try: print("Starting full migration and cloning process...") migrate_to_branch(env) clone_journals(env) clone_payment_methods(env) clone_rounding_and_pricelist(env) fix_company_journals(env) fix_all_mismatched_journals(env) print("\n*** ALL MIGRATIONS COMPLETED SUCCESSFULLY! ***") except NameError: print("Please run this script using Odoo shell:") print("Example: ./.venv/bin/python ./odoo/odoo-bin shell -d -c odoo.conf < scripts/run_all_migrations.py")