feat: Add new scripts for Odoo MO/QC cleanup, custom addon Git operations, and module upgrades, and improve module path resolution in existing scripts.
This commit is contained in:
parent
8bc083b84e
commit
b35ad10c51
129
delete_old_mo_qc.py
Normal file
129
delete_old_mo_qc.py
Normal file
@ -0,0 +1,129 @@
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
sys.path.append(os.getcwd())
|
||||
|
||||
# Try to import odoo, if not found, assume we are in odoo-bin shell or need path
|
||||
try:
|
||||
import odoo
|
||||
from odoo import api, SUPERUSER_ID
|
||||
from odoo.tools import config
|
||||
except ImportError as e:
|
||||
# Check if due to missing dependency like passlib
|
||||
if "No module named" in str(e):
|
||||
print("!" * 80)
|
||||
print(f"ERROR: Missing dependency: {e}")
|
||||
print("Please run this script using the Python executable that runs Odoo.")
|
||||
print("Example: /path/to/venv/bin/python delete_old_mo_qc.py")
|
||||
print("!" * 80)
|
||||
sys.exit(1)
|
||||
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# Try adding current_dir/odoo to path if not already covered by os.getcwd() fix above
|
||||
# But usually os.getcwd() should cover it if running from community/ folder.
|
||||
if os.path.exists(os.path.join(current_dir, 'odoo')):
|
||||
sys.path.append(os.path.join(current_dir, 'odoo'))
|
||||
else:
|
||||
sys.path.append(current_dir)
|
||||
|
||||
import odoo
|
||||
from odoo import api, SUPERUSER_ID
|
||||
from odoo.tools import config
|
||||
|
||||
def delete_old_records():
|
||||
print("Starting Delete Old MO/QC Script...")
|
||||
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description='Delete MOs and Quality Checks older than 1 day.')
|
||||
parser.add_argument('db_name', help='Database name')
|
||||
parser.add_argument('-c', '--config', help='Path to odoo.conf', default=None)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
db_name = args.db_name
|
||||
|
||||
# Load config if provided or default exists
|
||||
conf_path = args.config
|
||||
if conf_path:
|
||||
if os.path.exists(conf_path):
|
||||
config.parse_config(['-c', conf_path])
|
||||
else:
|
||||
print(f"WARNING: Config file '{conf_path}' not found.")
|
||||
elif os.path.exists('odoo.conf'):
|
||||
config.parse_config(['-c', 'odoo.conf'])
|
||||
|
||||
print(f"Connecting to database: {db_name}")
|
||||
|
||||
try:
|
||||
registry = odoo.registry(db_name)
|
||||
except AttributeError:
|
||||
from odoo.modules.registry import Registry
|
||||
registry = Registry.new(db_name)
|
||||
|
||||
with registry.cursor() as cr:
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
|
||||
# Calculate Cutoff Date
|
||||
cutoff_date = datetime.now() - timedelta(days=1)
|
||||
print(f"Cutoff Date: {cutoff_date}")
|
||||
|
||||
# 1. Find Old Quality Checks (NOT passed/failed)
|
||||
print("\nSearching for old Quality Checks...")
|
||||
# quality_state can be 'none', 'pass', 'fail'
|
||||
domain_qc = [('create_date', '<', cutoff_date), ('quality_state', '=', 'none')]
|
||||
qcs = env['quality.check'].search(domain_qc)
|
||||
print(f"Found {len(qcs)} Quality Checks strictly older than 1 day (and not done).")
|
||||
|
||||
# 2. Find Old Manufacturing Orders (NOT done)
|
||||
print("\nSearching for old Manufacturing Orders...")
|
||||
# state can be 'draft', 'confirmed', 'progress', 'to_close', 'done', 'cancel'
|
||||
# We delete anything NOT done (and maybe not cancel if user wants clean up, but user said 'skip done')
|
||||
# Safer to skip 'cancel' too if we want pure cleanup of abandoned work? User said "skip done".
|
||||
# But 'cancel' is safe to delete usually. Let's stick to != 'done'.
|
||||
start_domain_mo = [('create_date', '<', cutoff_date), ('state', '!=', 'done')]
|
||||
mos = env['mrp.production'].search(start_domain_mo)
|
||||
print(f"Found {len(mos)} Manufacturing Orders strictly older than 1 day (and not done).")
|
||||
|
||||
if not qcs and not mos:
|
||||
print("\nNothing to delete.")
|
||||
return
|
||||
|
||||
print("\n---------------------------------------------------")
|
||||
print("Dry run complete. No changes have been committed yet.")
|
||||
print("WARNING: This will PERMANENTLY DELETE the following records:")
|
||||
print(f" - {len(qcs)} Quality Checks")
|
||||
print(f" - {len(mos)} Manufacturing Orders")
|
||||
|
||||
user_input = input("Do you want to PROCEED with DELETION and COMMIT changes? (yes/no): ")
|
||||
|
||||
if user_input.lower() == 'yes':
|
||||
try:
|
||||
if qcs:
|
||||
print(f"Deleting {len(qcs)} Quality Checks...")
|
||||
qcs.unlink()
|
||||
print(" Success.")
|
||||
|
||||
if mos:
|
||||
print(f"Deleting {len(mos)} Manufacturing Orders...")
|
||||
# MO deletions might be blocked if state is 'done'.
|
||||
# We attempt unlink, if it fails, Odoo raises UserError.
|
||||
mos.unlink()
|
||||
print(" Success.")
|
||||
|
||||
cr.commit()
|
||||
print("Changes COMMITTED.")
|
||||
|
||||
except Exception as e:
|
||||
cr.rollback()
|
||||
print(f"ERROR during deletion: {e}")
|
||||
print("Transaction ROLLED BACK. No changes persisted.")
|
||||
else:
|
||||
cr.rollback()
|
||||
print("Operation CANCELLED (Rolled back).")
|
||||
print("Done.")
|
||||
|
||||
if __name__ == '__main__':
|
||||
delete_old_records()
|
||||
@ -2,6 +2,8 @@ import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
sys.path.append(os.getcwd())
|
||||
|
||||
# Try to import odoo, if not found, assume we are in odoo-bin shell or need path
|
||||
try:
|
||||
import odoo
|
||||
|
||||
30
pull_all_addons.sh
Normal file
30
pull_all_addons.sh
Normal file
@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Define the customaddons directory relative to the script location
|
||||
CUSTOM_ADDONS_DIR="$(dirname "$0")/customaddons"
|
||||
|
||||
if [ ! -d "$CUSTOM_ADDONS_DIR" ]; then
|
||||
echo "Error: customaddons directory not found at $CUSTOM_ADDONS_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Starting git pull for all modules in $CUSTOM_ADDONS_DIR..."
|
||||
|
||||
# Iterate over each subdirectory in customaddons
|
||||
for dir in "$CUSTOM_ADDONS_DIR"/*; do
|
||||
if [ -d "$dir" ]; then
|
||||
# Check if it's a git repository
|
||||
if [ -d "$dir/.git" ]; then
|
||||
echo "--------------------------------------------------"
|
||||
echo "Pulling updates for $(basename "$dir")..."
|
||||
(cd "$dir" && git pull)
|
||||
else
|
||||
# Optional: Uncomment the line below if you want to be notified about non-git folders
|
||||
# echo "Skipping $(basename "$dir") - Not a git repository"
|
||||
:
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo "--------------------------------------------------"
|
||||
echo "All done."
|
||||
@ -2,6 +2,8 @@ import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
sys.path.append(os.getcwd())
|
||||
|
||||
# Try to import odoo, if not found, assume we are in odoo-bin shell or need path
|
||||
try:
|
||||
import odoo
|
||||
|
||||
160
upgrade_custom_addons.py
Normal file
160
upgrade_custom_addons.py
Normal file
@ -0,0 +1,160 @@
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
import configparser
|
||||
import psycopg2
|
||||
|
||||
def get_custom_addons_modules(addons_path):
|
||||
"""
|
||||
Scans the given addons path and returns a list of directory names
|
||||
that contain a __manifest__.py file.
|
||||
"""
|
||||
modules = []
|
||||
if not os.path.exists(addons_path):
|
||||
print(f"Error: Addons path '{addons_path}' does not exist.")
|
||||
return []
|
||||
|
||||
for item in os.listdir(addons_path):
|
||||
item_path = os.path.join(addons_path, item)
|
||||
if os.path.isdir(item_path):
|
||||
manifest_path = os.path.join(item_path, '__manifest__.py')
|
||||
if os.path.exists(manifest_path):
|
||||
modules.append(item)
|
||||
return modules
|
||||
|
||||
def get_db_params(config_path):
|
||||
"""
|
||||
Parses the Odoo config file to extract database connection parameters.
|
||||
"""
|
||||
config = configparser.ConfigParser()
|
||||
try:
|
||||
config.read(config_path)
|
||||
except Exception as e:
|
||||
print(f"Error reading config file: {e}")
|
||||
return {}
|
||||
|
||||
db_params = {}
|
||||
if 'options' in config:
|
||||
params = config['options']
|
||||
db_params['host'] = params.get('db_host', 'localhost')
|
||||
db_params['port'] = params.get('db_port', '5432')
|
||||
db_params['user'] = params.get('db_user', 'odoo')
|
||||
db_params['password'] = params.get('db_password', '')
|
||||
|
||||
return db_params
|
||||
|
||||
def get_installed_modules(modules_list, db_name, db_params):
|
||||
"""
|
||||
Connects to the database and filters the given list of modules specifically
|
||||
for those that are installed (state='installed').
|
||||
"""
|
||||
installed_modules = []
|
||||
|
||||
conn_params = {
|
||||
'dbname': db_name,
|
||||
'user': db_params.get('user', 'odoo'),
|
||||
'password': db_params.get('password', ''),
|
||||
'host': db_params.get('host', 'localhost'),
|
||||
'port': db_params.get('port', '5432')
|
||||
}
|
||||
|
||||
# Filter out empty or None values to use defaults or avoid errors
|
||||
conn_params = {k: v for k, v in conn_params.items() if v}
|
||||
|
||||
try:
|
||||
conn = psycopg2.connect(**conn_params)
|
||||
cur = conn.cursor()
|
||||
|
||||
# Check if modules are installed
|
||||
if not modules_list:
|
||||
return []
|
||||
|
||||
query = "SELECT name FROM ir_module_module WHERE state = 'installed' AND name IN %s"
|
||||
cur.execute(query, (tuple(modules_list),))
|
||||
rows = cur.fetchall()
|
||||
|
||||
installed_modules = [row[0] for row in rows]
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
except psycopg2.Error as e:
|
||||
print(f"Database error: {e}")
|
||||
print("Ensure psycopg2 matches the server configuration.")
|
||||
return []
|
||||
except Exception as e:
|
||||
print(f"Error checking installed modules: {e}")
|
||||
return []
|
||||
|
||||
return installed_modules
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Upgrade installed modules in customaddons for a specific database.")
|
||||
parser.add_argument('-d', '--database', required=True, help="Database name to upgrade modules on.")
|
||||
parser.add_argument('-c', '--config', required=True, help="Path to Odoo configuration file.")
|
||||
parser.add_argument('--odoo-bin', default='./odoo/odoo-bin', help="Path to odoo-bin executable (default: ./odoo/odoo-bin)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Determine customaddons path relative to this script
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
custom_addons_path = os.path.join(script_dir, 'customaddons')
|
||||
|
||||
print(f"Scanning for modules in {custom_addons_path}...")
|
||||
modules = get_custom_addons_modules(custom_addons_path)
|
||||
|
||||
if not modules:
|
||||
print("No modules found in customaddons.")
|
||||
sys.exit(0)
|
||||
|
||||
print(f"Found {len(modules)} local modules.")
|
||||
|
||||
# Get DB params from config
|
||||
db_params = get_db_params(args.config)
|
||||
|
||||
print(f"Checking installed status in database '{args.database}'...")
|
||||
installed_modules = get_installed_modules(modules, args.database, db_params)
|
||||
|
||||
if not installed_modules:
|
||||
print("None of the local modules are installed in the specified database.")
|
||||
sys.exit(0)
|
||||
|
||||
print(f"Found {len(installed_modules)} installed modules to upgrade.")
|
||||
|
||||
# Construct the Odoo command
|
||||
modules_str = ','.join(installed_modules)
|
||||
|
||||
# Check if odoo-bin exists
|
||||
if not os.path.exists(args.odoo_bin) and not os.path.exists(os.path.abspath(args.odoo_bin)):
|
||||
print(f"Error: odoo-bin not found at {args.odoo_bin}")
|
||||
sys.exit(1)
|
||||
|
||||
cmd = [
|
||||
sys.executable,
|
||||
args.odoo_bin,
|
||||
'-c', args.config,
|
||||
'-d', args.database,
|
||||
'-u', modules_str,
|
||||
'--stop-after-init'
|
||||
]
|
||||
|
||||
print("--------------------------------------------------")
|
||||
print(f"Starting upgrade for database '{args.database}'...")
|
||||
print(f"Modules to upgrade: {modules_str}")
|
||||
print(f"Command: {' '.join(cmd)}")
|
||||
print("--------------------------------------------------")
|
||||
|
||||
try:
|
||||
subprocess.run(cmd, check=True)
|
||||
print("--------------------------------------------------")
|
||||
print("Upgrade completed successfully.")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print("--------------------------------------------------")
|
||||
print(f"Error: Upgrade failed with exit code {e.returncode}")
|
||||
sys.exit(e.returncode)
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user