diff --git a/README_IMPORT.md b/README_IMPORT.md index 7028663..664028b 100644 --- a/README_IMPORT.md +++ b/README_IMPORT.md @@ -1,31 +1,70 @@ -# Fixed Asset Import Script +# Fixed Asset Import Tools -This script imports fixed assets from `Fixed Asset Kipas.xlsx` into Odoo 17 database `kipasdbclone5`. +This folder contains scripts to manage the import and lifecycle of fixed assets in Odoo 17. -## Features -- **Avoids Double Posting**: Sets assets to 'Running' (Open) state manually, bypassing the Journal Entry creation for Asset Recognition. -- **Depreciation Calculation**: Adjusts the "Accumulated Depreciation Per Dec 31" from Excel to "Per Oct 31" (Opening Balance Date) by subtracting 2 months of depreciation. -- **Model Handling**: Automatically maps categories. Creates `Peralatan Dapur` model if missing (copying from `Peralatan Inventaris`). -- **Cutoff Date**: Skips assets acquired after Oct 31, 2025. +## 1. Import Script (`import_fixed_assets.py`) +Imports assets from an Excel file, handling depreciation calculation and creating the assets in "Running" state to avoid opening balance duplication. + +### Features +- **Avoids Double Posting**: Sets assets to 'Running' manually. +- **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. + +### Usage +Run the script providing the path to `odoo-bin`, `odoo.conf`, the Excel file, and the database name: -## Usage -Run the script using the Odoo virtual environment: ```bash -/home/suherdy/Pythoncode/odoo17/.venv/bin/python /home/suherdy/Pythoncode/odoo17/scripts/import_fixed_assets.py +/path/to/venv/bin/python scripts/import_fixed_assets.py \ + \ + \ + \ + ``` -## Logic Details -1. **Accumulated Depreciation**: - - Uses Excel column R (Accum Depr Dec 31). - - `Accum Oct 31 = Accum Dec 31 - (2 * Monthly Depreciation)`. - - If asset is fully depreciated (End Date <= Oct 31), uses `Accum Dec 31` as is. -2. **State**: - - Assets are created in `draft`. - - `compute_depreciation_board()` is called to generate Draft moves for future depreciation (subtracted by imported amount). - - `state` is manually Set to `open`. - - Result: No historical moves posted. Future moves are Draft (to be posted by cron/user). No Asset Recognition entry. +**Example:** +```bash +/home/suherdy/Pythoncode/odoo17/.venv/bin/python scripts/import_fixed_assets.py \ + /home/suherdy/Pythoncode/odoo17/odoo/odoo-bin \ + /home/suherdy/Pythoncode/odoo17/odoo.conf \ + "/home/suherdy/Pythoncode/odoo17/Fixed Asset Kipas.xlsx" \ + kipasdbclone5 +``` -## Prerequisites -- `openpyxl` -- `python-dateutil` -- Odoo configuration file at `../odoo.conf` +--- + +## 2. Post Depreciation (`post_depreciation.py`) +Posts all **Draft** depreciation entries found in the system up to a specific date (default: today). This is useful to "catch up" depreciation for Nov/Dec 2025 after import. + +### Usage +```bash +/home/suherdy/Pythoncode/odoo17/.venv/bin/python scripts/post_depreciation.py +``` +*Note: You may need to edit the `POST_UP_TO_DATE` variable inside the script to target a specific date.* + +--- + +## 3. 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 +Run the script providing the path to `odoo-bin`, `odoo.conf`, and the database name: + +```bash +/path/to/venv/bin/python scripts/delete_all_assets.py \ + \ + \ + +``` + +**Example:** +```bash +/home/suherdy/Pythoncode/odoo17/.venv/bin/python scripts/delete_all_assets.py \ + /home/suherdy/Pythoncode/odoo17/odoo/odoo-bin \ + /home/suherdy/Pythoncode/odoo17/odoo.conf \ + kipasdbclone5 +``` + +## Requirements +1. **Modules**: The `asset_code_field` custom module (in `customaddons/`) must be installed. +2. **Python Packages**: `openpyxl`, `python-dateutil`. diff --git a/delete_all_assets.py b/delete_all_assets.py index 5f0f464..516ccb1 100644 --- a/delete_all_assets.py +++ b/delete_all_assets.py @@ -1,24 +1,38 @@ import sys import os -# ---------------- CONFIGURATION ---------------- -PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) -os.chdir(PROJECT_ROOT) +import argparse -ODOO_PATH = os.path.join(PROJECT_ROOT, 'odoo') -CONF_FILE = os.path.join(PROJECT_ROOT, 'odoo.conf') -DB_NAME = 'kipasdbclone5' +def main(): + parser = argparse.ArgumentParser(description="Delete All Fixed Assets from Odoo") + 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") -if ODOO_PATH not in sys.path: - sys.path.append(ODOO_PATH) + args = parser.parse_args() -import odoo -from odoo import api, SUPERUSER_ID + 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 + odoo_root = os.path.dirname(odoo_bin_path) + if odoo_root not in sys.path: + sys.path.append(odoo_root) + + # Change CWD to config directory to handle relative paths in config + os.chdir(os.path.dirname(conf_path)) -def delete_assets(): - print(f"Initializing Odoo Environment for database: {DB_NAME}...") try: - odoo.tools.config.parse_config(['-c', CONF_FILE]) - registry = odoo.registry(DB_NAME) + import odoo + from odoo import api, SUPERUSER_ID + except ImportError: + print(f"Error: Could not import 'odoo' module from {odoo_root}. 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 @@ -49,12 +63,6 @@ def delete_assets(): moves.unlink() # Deleting moves first cleans up the relation # 3. Reset Assets to Draft - # Some assets might be 'open' or 'close' or 'cancelled'. - # To delete, they often need to be in draft or cancelled state depending on logic, - # but unlink() in Odoo 17 account_asset usually checks if they are NOT in open/paused/close. - # So we must write state = draft. - - # Check for assets that are not draft non_draft_assets = assets.filtered(lambda a: a.state != 'draft') if non_draft_assets: print(f"Setting {len(non_draft_assets)} assets to draft state...") @@ -74,4 +82,4 @@ def delete_assets(): traceback.print_exc() if __name__ == "__main__": - delete_assets() + main() diff --git a/import_fixed_assets.py b/import_fixed_assets.py index 5e5ee7e..a78da81 100644 --- a/import_fixed_assets.py +++ b/import_fixed_assets.py @@ -1,31 +1,46 @@ import sys import os +import argparse import openpyxl from datetime import datetime, date from dateutil.relativedelta import relativedelta -# ---------------- CONFIGURATION ---------------- -PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) -# Change CWD to Project Root so relative paths in odoo.conf work -os.chdir(PROJECT_ROOT) - -ODOO_PATH = os.path.join(PROJECT_ROOT, 'odoo') -CONF_FILE = os.path.join(PROJECT_ROOT, 'odoo.conf') -EXCEL_FILE = os.path.join(PROJECT_ROOT, 'Fixed Asset Kipas.xlsx') -DB_NAME = 'kipasdbclone5' +# Constants CUTOFF_DATE = date(2025, 10, 31) -if ODOO_PATH not in sys.path: - sys.path.append(ODOO_PATH) +def main(): + parser = argparse.ArgumentParser(description="Import Fixed Assets to Odoo") + 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("excel_path", help="Path to the Excel file") + parser.add_argument("db_name", help="Database name") -import odoo -from odoo import api, SUPERUSER_ID + args = parser.parse_args() + + odoo_bin_path = os.path.abspath(args.odoo_bin_path) + conf_path = os.path.abspath(args.conf_path) + excel_path = os.path.abspath(args.excel_path) + db_name = args.db_name + + # Add Odoo to sys.path + odoo_root = os.path.dirname(odoo_bin_path) + if odoo_root not in sys.path: + sys.path.append(odoo_root) + + # Change CWD to config directory to handle relative paths in config (like addons_path) + os.chdir(os.path.dirname(conf_path)) -def import_assets(): - print(f"Initializing Odoo Environment for database: {DB_NAME}...") try: - odoo.tools.config.parse_config(['-c', CONF_FILE]) - registry = odoo.registry(DB_NAME) + import odoo + from odoo import api, SUPERUSER_ID + except ImportError: + print(f"Error: Could not import 'odoo' module from {odoo_root}. 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 @@ -35,7 +50,7 @@ def import_assets(): print("Connected to Odoo.") try: ensure_models(env) - process_import(env) + process_import(env, excel_path) cr.commit() print("Changes committed to database.") except Exception as e: @@ -100,9 +115,9 @@ def ensure_models(env): env['account.asset'].create(vals) print(f"Created model '{config['name']}'.") -def process_import(env): - print(f"Reading Excel file: {EXCEL_FILE}...") - wb = openpyxl.load_workbook(EXCEL_FILE, data_only=True) +def process_import(env, excel_file): + print(f"Reading Excel file: {excel_file}...") + wb = openpyxl.load_workbook(excel_file, data_only=True) ws = wb.active current_category_name = None @@ -232,4 +247,4 @@ def process_import(env): print(f"\nTotal Assets Imported: {count}") if __name__ == "__main__": - import_assets() + main()