From 0b925cadecebcd10e5829e1888fa1e8b3e2ee063 Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Tue, 3 Feb 2026 22:39:28 +0700 Subject: [PATCH] fix: Post depreciation moves for newly imported assets and add a script to post missing depreciations for existing running assets. --- import_fixed_assets.py | 4 ++ post_missing_depreciations.py | 88 +++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 post_missing_depreciations.py diff --git a/import_fixed_assets.py b/import_fixed_assets.py index 7f9a9a7..1f6ba20 100644 --- a/import_fixed_assets.py +++ b/import_fixed_assets.py @@ -257,6 +257,10 @@ def process_import(env, excel_file): historical_moves.unlink() asset.write({'state': 'open'}) + + # FIX: Explicitly post the depreciation moves like Odoo's validate() method does + asset.depreciation_move_ids.filtered(lambda move: move.state != 'posted')._post() + print(f"Imported: {asset.name} | Val: {original_value} | Oct31: {accum_depr_oct31:,.2f}") count += 1 diff --git a/post_missing_depreciations.py b/post_missing_depreciations.py new file mode 100644 index 0000000..75be9c2 --- /dev/null +++ b/post_missing_depreciations.py @@ -0,0 +1,88 @@ +import sys +import os +import argparse + +def main(): + parser = argparse.ArgumentParser(description="Post Missing Depreciation Entries for Running Assets") + parser.add_argument("odoo_bin_path", help="Path to odoo-bin executable") + parser.add_argument("conf_path", help="Path to odoo.conf") + parser.add_argument("db_name", help="Database name") + + args = parser.parse_args() + + odoo_bin_path = os.path.abspath(args.odoo_bin_path) + conf_path = os.path.abspath(args.conf_path) + db_name = args.db_name + + # Add Odoo to sys.path + # The odoo package is located inside the directory containing odoo-bin + odoo_dir = os.path.dirname(odoo_bin_path) + if odoo_dir not in sys.path: + sys.path.insert(0, odoo_dir) + + # Change CWD to config directory to handle relative paths in config + os.chdir(os.path.dirname(conf_path)) + + try: + import odoo + from odoo import api, SUPERUSER_ID + except ImportError: + print(f"Error: Could not import 'odoo' module from {odoo_dir}. Make sure odoo-bin path is correct.") + sys.exit(1) + + print(f"Initializing Odoo Environment for database: {db_name}...") + try: + odoo.tools.config.parse_config(['-c', conf_path]) + registry = odoo.registry(db_name) + except Exception as e: + print(f"Error initializing Odoo: {e}") + return + + with registry.cursor() as cr: + env = api.Environment(cr, SUPERUSER_ID, {}) + print("Connected to Odoo.") + + process_assets(env) + + # Determine if we should commit + # (For safety, we could add a --dry-run flag, but for now we commit) + cr.commit() + print("Changes committed to database.") + +def process_assets(env): + # Find all OPEN (Running) assets + assets = env['account.asset'].search([('state', '=', 'open')]) + print(f"Found {len(assets)} running assets.") + + count_assets_fixed = 0 + count_moves_posted = 0 + + for asset in assets: + # Find draft moves for this asset + moves_to_post = asset.depreciation_move_ids.filtered(lambda m: m.state == 'draft') + + if not moves_to_post: + continue + + print(f"Asset '{asset.name}' has {len(moves_to_post)} draft moves. Posting...") + + # Post them + # We use _post() directly as it's the internal method used by validate() + # avoiding some higher level checks that might block us if dates are weird, + # though standard validate() uses _post(). + # Note: account.move.action_post() is the public method. + # validate() in account_asset.py calls: + # asset.depreciation_move_ids.filtered(lambda move: move.state != 'posted')._post() + # So we stick to that. + moves_to_post._post() + + count_assets_fixed += 1 + count_moves_posted += len(moves_to_post) + + print(f"\nSummary:") + print(f" Assets processed: {len(assets)}") + print(f" Assets updated: {count_assets_fixed}") + print(f" Moves posted: {count_moves_posted}") + +if __name__ == "__main__": + main()