From add87ea10d5aa1303e6103e2b80ec7cc5632777e Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Tue, 3 Feb 2026 23:20:28 +0700 Subject: [PATCH] feat: Implement `auto_post='at_date'` for future depreciation entries in import and fix scripts, and add a `check_auto_post` utility. --- README_IMPORT.md | 45 +++++++++++++++++++++++- check_auto_post.py | 65 +++++++++++++++++++++++++++++++++++ import_fixed_assets.py | 6 +++- post_missing_depreciations.py | 19 +++++----- 4 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 check_auto_post.py diff --git a/README_IMPORT.md b/README_IMPORT.md index 45ed071..03d1324 100644 --- a/README_IMPORT.md +++ b/README_IMPORT.md @@ -7,6 +7,8 @@ Imports assets from an Excel file, handling depreciation calculation and creatin ### Features - **Avoids Double Posting**: Sets assets to 'Running' manually. +- **Auto-Posting**: Automatically posts depreciation entries for **past and current dates**. +- **Cron Ready**: Sets future depreciation entries to `auto_post='at_date'` so Odoo's standard cron picks them up. - **Asset Code**: Maps Column B ("Kode Barang") to `asset_code`. - **Historical Cleanup**: Deletes draft moves `<= Cutoff Date` to keep the books clean. - **Auto-Model**: Creates missing asset models (e.g. "PERALATAN DAPUR") on the fly. @@ -51,12 +53,53 @@ Posts all **Draft** depreciation entries found in the system up to a specific da /home/suherdy/Pythoncode/odoo17/odoo/odoo-bin \ /home/suherdy/Pythoncode/odoo17/odoo.conf \ kipasdbclone5 \ + kipasdbclone5 \ --date 2026-01-21 ``` --- -## 3. Delete All Assets (`delete_all_assets.py`) +## 3. Post Missing Depreciations (`post_missing_depreciations.py`) +Use this script to retroactive fix assets that were imported but have their depreciation entries stuck in **Draft** state. + +### Features +- Finds all **Running** assets. +- Posts `Draft` depreciation moves for **past and current dates**. +- Updates **future** `Draft` moves to have `auto_post='at_date'`, ensuring Odoo's cron processes them later. + +### Usage +```bash +/path/to/venv/bin/python scripts/post_missing_depreciations.py \ + \ + \ + +``` + +**Example:** +```bash +/home/suherdy/Pythoncode/odoo17/.venv/bin/python scripts/post_missing_depreciations.py \ + /home/suherdy/Pythoncode/odoo17/odoo/odoo-bin \ + /home/suherdy/Pythoncode/odoo17/odoo.conf \ + kipasdbclone5 +``` + +--- + +## 4. Check Auto Post Status (`check_auto_post.py`) +A simple diagnostic utility to check if the `auto_post` field is correctly set on draft depreciation moves. + +### Usage +```bash +/path/to/venv/bin/python scripts/check_auto_post.py \ + \ + \ + +``` + + +--- + +## 5. Delete All Assets (`delete_all_assets.py`) **⚠ WARNING**: This script deletes **ALL** fixed assets and their related journal entries (posted or draft). Use this only if you need to wipe clean and re-import. ### Usage diff --git a/check_auto_post.py b/check_auto_post.py new file mode 100644 index 0000000..8aaebd0 --- /dev/null +++ b/check_auto_post.py @@ -0,0 +1,65 @@ +import sys +import os +import argparse +from datetime import date + +def main(): + parser = argparse.ArgumentParser(description="Check Auto Post Status of Draft Depreciation Entries") + 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 + + odoo_dir = os.path.dirname(odoo_bin_path) + if odoo_dir not in sys.path: + sys.path.insert(0, odoo_dir) + + 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.") + 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.") + + check_assets(env) + +def check_assets(env): + assets = env['account.asset'].search([('state', '=', 'open')], limit=5) + print(f"Checking first {len(assets)} running assets...") + + for asset in assets: + print(f"\nAsset: {asset.name}") + draft_moves = asset.depreciation_move_ids.filtered(lambda m: m.state == 'draft') + if not draft_moves: + print(" No draft moves found.") + continue + + print(f" Found {len(draft_moves)} draft moves.") + # Check distinct auto_post values + auto_post_values = set(draft_moves.mapped('auto_post')) + print(f" 'auto_post' values in draft moves: {auto_post_values}") + + for move in draft_moves[:3]: + print(f" - Date: {move.date}, auto_post: {move.auto_post}") + +if __name__ == "__main__": + main() diff --git a/import_fixed_assets.py b/import_fixed_assets.py index 1f6ba20..5c3d175 100644 --- a/import_fixed_assets.py +++ b/import_fixed_assets.py @@ -259,7 +259,11 @@ def process_import(env, excel_file): 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() + # BUT only up to today. Future entries will be auto-posted by standard Odoo cron. + # We explicitly set auto_post to 'at_date' for future entries to ensure the cron picks them up. + today = date.today() + asset.depreciation_move_ids.filtered(lambda move: move.state != 'posted' and move.date <= today)._post() + asset.depreciation_move_ids.filtered(lambda move: move.state == 'draft' and move.date > today).write({'auto_post': 'at_date'}) print(f"Imported: {asset.name} | Val: {original_value} | Oct31: {accum_depr_oct31:,.2f}") diff --git a/post_missing_depreciations.py b/post_missing_depreciations.py index 75be9c2..fed5202 100644 --- a/post_missing_depreciations.py +++ b/post_missing_depreciations.py @@ -1,6 +1,7 @@ import sys import os import argparse +from datetime import date def main(): parser = argparse.ArgumentParser(description="Post Missing Depreciation Entries for Running Assets") @@ -58,8 +59,10 @@ def process_assets(env): 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') + # Find draft moves for this asset UP TO TODAY + # Future moves are handled by Odoo's auto_post cron + today = date.today() + moves_to_post = asset.depreciation_move_ids.filtered(lambda m: m.state == 'draft' and m.date <= today) if not moves_to_post: continue @@ -67,17 +70,15 @@ def process_assets(env): 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) + + # Also ensure FUTURE draft moves are set to auto_post='at_date' + future_moves = asset.depreciation_move_ids.filtered(lambda m: m.state == 'draft' and m.date > today) + if future_moves: + future_moves.write({'auto_post': 'at_date'}) print(f"\nSummary:") print(f" Assets processed: {len(assets)}")