diff --git a/clone_extra_data.py b/clone_extra_data.py new file mode 100644 index 0000000..9709eea --- /dev/null +++ b/clone_extra_data.py @@ -0,0 +1,118 @@ +import sys + +def clone_extra_data(env): + 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 + + print(f"\n--- 1. Cloning Product BOMs ---") + boms = env['mrp.bom'].search([('company_id', '=', source_company.id)]) + count_bom = 0 + for bom in boms: + # Check if already exists in target (same product template & name-ish?) + # Let's match by product_tmpl_id + existing = env['mrp.bom'].search([ + ('product_tmpl_id', '=', bom.product_tmpl_id.id), + ('company_id', '=', target_company.id) + ], limit=1) + + if existing: + print(f" -> BOM for '{bom.product_tmpl_id.name}' already exists in {target_company.name}. Skipping.") + continue + + try: + with env.cr.savepoint(): + new_bom = bom.copy({'company_id': target_company.id}) + print(f" -> Cloned BOM for '{bom.product_tmpl_id.name}'") + count_bom += 1 + except Exception as e: + print(f" -> Failed to clone BOM '{bom.product_tmpl_id.name}': {e}") + + print(f"\n--- 2. Copying Product Category COA Setup ---") + categories = env['product.category'].with_company(source_company.id).search([]) + count_categ = 0 + for categ in categories: + # We need to read properties from source, and write to target + income_acc = categ.property_account_income_categ_id + expense_acc = categ.property_account_expense_categ_id + stock_val_acc = getattr(categ, 'property_stock_valuation_account_id', False) + stock_in_acc = getattr(categ, 'property_stock_account_input_categ_id', False) + stock_out_acc = getattr(categ, 'property_stock_account_output_categ_id', False) + stock_journal = getattr(categ, 'property_stock_journal', False) + + vals = {} + if income_acc: vals['property_account_income_categ_id'] = income_acc.id + if expense_acc: vals['property_account_expense_categ_id'] = expense_acc.id + if stock_val_acc: vals['property_stock_valuation_account_id'] = stock_val_acc.id + if stock_in_acc: vals['property_stock_account_input_categ_id'] = stock_in_acc.id + if stock_out_acc: vals['property_stock_account_output_categ_id'] = stock_out_acc.id + + # for journal it is tricky - the source journal might belong to Rungkut! + if stock_journal: + # check if it is explicitly rungkut's journal + if stock_journal.company_id.id == source_company.id: + # find equivalent for tenggilis + t_journal = env['account.journal'].search([ + ('name', '=', stock_journal.name), + ('company_id', '=', target_company.id) + ], limit=1) + if t_journal: vals['property_stock_journal'] = t_journal.id + else: + vals['property_stock_journal'] = stock_journal.id + + if vals: + # switch to target company context so properties are saved for target + categ.with_company(target_company.id).write(vals) + count_categ += 1 + print(f" -> Copied Accounting properties for {count_categ} product categories.") + + + print(f"\n--- 3. Fixing POS Payment Methods COA and Journals ---") + methods = env['pos.payment.method'].search([('company_id', '=', target_company.id)]) + count_pm = 0 + for method in methods: + # get original + orig_method = env['pos.payment.method'].search([ + ('name', '=', method.name), + ('company_id', '=', source_company.id) + ], limit=1) + + if not orig_method: + continue + + vals = {} + # Journals + orig_journal = orig_method.journal_id + if orig_journal: + if orig_journal.company_id.id == source_company.id: + t_j = env['account.journal'].search([ + ('name', '=', orig_journal.name), + ('company_id', '=', target_company.id) + ], limit=1) + vals['journal_id'] = t_j.id if t_j else False + else: + vals['journal_id'] = orig_journal.id + + # Custom Accounting fields (like income/discount accounts if they exist) + if hasattr(orig_method, 'income_account_id') and orig_method.income_account_id: + vals['income_account_id'] = orig_method.income_account_id.id + if hasattr(orig_method, 'discount_account_id') and orig_method.discount_account_id: + vals['discount_account_id'] = orig_method.discount_account_id.id + + if vals: + method.write(vals) + count_pm += 1 + print(f" -> Re-linked Journal & Accounts for Payment Method '{method.name}'") + + print(f"\nCommitting changes...") + env.cr.commit() + print("Done!") + +if __name__ == '__main__': + clone_extra_data(env) diff --git a/migrate_to_branch.py b/migrate_to_branch.py index 686deee..7d0a73c 100644 --- a/migrate_to_branch.py +++ b/migrate_to_branch.py @@ -25,6 +25,8 @@ def migrate_to_branch(env): 'pos_order_line', 'pos_payment', 'pos_payment_method', + # Accounting / Assets + 'account_asset', # Shop Floor / Inventory 'stock_warehouse', diff --git a/run_all_migrations.py b/run_all_migrations.py index 931033b..ec4d90d 100644 --- a/run_all_migrations.py +++ b/run_all_migrations.py @@ -25,7 +25,7 @@ def migrate_to_branch(env): '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', + 'account_journal', 'account_move', 'account_move_line', 'account_payment', 'account_bank_statement', 'account_bank_statement_line', 'account_partial_reconcile', 'account_payment_term', 'account_asset', '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' @@ -346,6 +346,112 @@ def fix_all_mismatched_journals(env): env.cr.commit() +# ========================================== +# 7. CLONE EXTRA DATA (BOM, CATEGORY COA, PM ACCOUNTS) +# ========================================== +def clone_extra_data(env): + print("\n==========================================") + print("7. CLONING BOMs, CATEGORY COA & PM ACCOUNTS") + 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: + return + + print(f"--- 1. Cloning Product BOMs ---") + boms = env['mrp.bom'].search([('company_id', '=', source_company.id)]) + count_bom = 0 + for bom in boms: + existing = env['mrp.bom'].search([ + ('product_tmpl_id', '=', bom.product_tmpl_id.id), + ('company_id', '=', target_company.id) + ], limit=1) + + if existing: + continue + + try: + with env.cr.savepoint(): + bom.copy({'company_id': target_company.id}) + count_bom += 1 + except Exception: + pass + + print(f" -> Cloned {count_bom} BOMs") + + print(f"\n--- 2. Copying Product Category COA Setup ---") + categories = env['product.category'].with_company(source_company.id).search([]) + count_categ = 0 + for categ in categories: + income_acc = categ.property_account_income_categ_id + expense_acc = categ.property_account_expense_categ_id + stock_val_acc = getattr(categ, 'property_stock_valuation_account_id', False) + stock_in_acc = getattr(categ, 'property_stock_account_input_categ_id', False) + stock_out_acc = getattr(categ, 'property_stock_account_output_categ_id', False) + stock_journal = getattr(categ, 'property_stock_journal', False) + + vals = {} + if income_acc: vals['property_account_income_categ_id'] = income_acc.id + if expense_acc: vals['property_account_expense_categ_id'] = expense_acc.id + if stock_val_acc: vals['property_stock_valuation_account_id'] = stock_val_acc.id + if stock_in_acc: vals['property_stock_account_input_categ_id'] = stock_in_acc.id + if stock_out_acc: vals['property_stock_account_output_categ_id'] = stock_out_acc.id + + if stock_journal: + if stock_journal.company_id.id == source_company.id: + t_journal = env['account.journal'].search([ + ('name', '=', stock_journal.name), + ('company_id', '=', target_company.id) + ], limit=1) + if t_journal: vals['property_stock_journal'] = t_journal.id + else: + vals['property_stock_journal'] = stock_journal.id + + if vals: + categ.with_company(target_company.id).write(vals) + count_categ += 1 + print(f" -> Copied Accounting properties for {count_categ} product categories.") + + print(f"\n--- 3. Fixing POS Payment Methods COA and Journals ---") + methods = env['pos.payment.method'].search([('company_id', '=', target_company.id)]) + count_pm = 0 + for method in methods: + orig_method = env['pos.payment.method'].search([ + ('name', '=', method.name), + ('company_id', '=', source_company.id) + ], limit=1) + + if not orig_method: + continue + + vals = {} + orig_journal = orig_method.journal_id + if orig_journal: + if orig_journal.company_id.id == source_company.id: + t_j = env['account.journal'].search([ + ('name', '=', orig_journal.name), + ('company_id', '=', target_company.id) + ], limit=1) + vals['journal_id'] = t_j.id if t_j else False + else: + vals['journal_id'] = orig_journal.id + + if hasattr(orig_method, 'income_account_id') and orig_method.income_account_id: + vals['income_account_id'] = orig_method.income_account_id.id + if hasattr(orig_method, 'discount_account_id') and orig_method.discount_account_id: + vals['discount_account_id'] = orig_method.discount_account_id.id + + if vals: + method.write(vals) + count_pm += 1 + print(f" -> Re-linked Journal & Accounts for {count_pm} Payment Methods") + + env.cr.commit() + if __name__ == '__main__': try: print("Starting full migration and cloning process...") @@ -355,6 +461,7 @@ if __name__ == '__main__': clone_rounding_and_pricelist(env) fix_company_journals(env) fix_all_mismatched_journals(env) + clone_extra_data(env) print("\n*** ALL MIGRATIONS COMPLETED SUCCESSFULLY! ***") except NameError: print("Please run this script using Odoo shell:")