diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 34b60b5..a0eff50 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -15,7 +15,10 @@ "Bash(mkdir:*)", "Bash(mv:*)", "Bash(rm:*)", - "Bash(ls:*)" + "Bash(ls:*)", + "Bash(move lab_logo.png lims_management/static/img/lab_logo.png)", + "WebFetch(domain:github.com)", + "WebFetch(domain:apps.odoo.com)" ], "deny": [] } diff --git a/CLAUDE.md b/CLAUDE.md index 48df957..f126516 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -62,7 +62,7 @@ docker exec -it lims_db psql -U odoo -d odoo #### Python Script Method (Recommended) For complex queries, use Python scripts with Odoo ORM: -1. Create script (e.g., `verify_products.py`): +1. Create script (e.g., `test/verify_products.py`): ```python import odoo import json @@ -81,7 +81,7 @@ if __name__ == '__main__': 2. Copy to container: ```bash -docker cp verify_products.py lims_odoo:/tmp/verify_products.py +docker cp test/verify_products.py lims_odoo:/tmp/verify_products.py ``` 3. Execute: @@ -92,25 +92,25 @@ docker-compose exec odoo python3 /tmp/verify_products.py ### Gitea Integration ```bash # Create issue -python gitea_cli_helper.py create-issue --title "Title" --body "Description\nSupports multiple lines" +python utils/gitea_cli_helper.py create-issue --title "Title" --body "Description\nSupports multiple lines" # Create PR with inline description -python gitea_cli_helper.py create-pr --head "feature-branch" --base "dev" --title "Title" --body "Description" +python utils/gitea_cli_helper.py create-pr --head "feature-branch" --base "dev" --title "Title" --body "Description" # Create PR with description from file -python gitea_cli_helper.py create-pr dev --title "feat(#31): Sample lifecycle" --description-file pr_description.txt +python utils/gitea_cli_helper.py create-pr dev --title "feat(#31): Sample lifecycle" --description-file pr_description.txt # Comment on issue -python gitea_cli_helper.py comment-issue --issue-number 123 --body "Comment text" +python utils/gitea_cli_helper.py comment-issue --issue-number 123 --body "Comment text" # Close issue -python gitea_cli_helper.py close-issue --issue-number 123 +python utils/gitea_cli_helper.py close-issue --issue-number 123 # Get issue details and comments -python gitea_cli_helper.py get-issue --issue-number 8 +python utils/gitea_cli_helper.py get-issue --issue-number 8 # List all open issues -python gitea_cli_helper.py list-open-issues +python utils/gitea_cli_helper.py list-open-issues ``` ## Mandatory Reading @@ -169,7 +169,7 @@ At the start of each work session, read these documents to understand requiremen - **Initial Data**: `lims_management/data/` - Sequences, categories, basic configuration - **Demo Data**: - XML files in `lims_management/demo/` - - Python scripts in root directory for complex demo data creation + - Python scripts in `test/` directory for complex demo data creation - Use `noupdate="1"` for demo data to prevent reloading ### Security Model diff --git a/docker-compose.yml b/docker-compose.yml index 87c8b34..09de5d7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,8 +24,9 @@ services: - ./lims_management:/mnt/extra-addons/lims_management - ./odoo.conf:/etc/odoo/odoo.conf - ./init_odoo.py:/app/init_odoo.py - - ./create_lab_requests.py:/app/create_lab_requests.py + - ./test/create_lab_requests.py:/app/create_lab_requests.py - ./test:/app/test + - ./scripts:/app/scripts command: ["/usr/bin/python3", "/app/init_odoo.py"] environment: HOST: db diff --git a/init_odoo.py b/init_odoo.py index 923571a..fdff0c5 100644 --- a/init_odoo.py +++ b/init_odoo.py @@ -128,6 +128,64 @@ EOF else: print(f"Advertencia: Fallo al crear datos de demostración de pruebas (código {result.returncode})") + # --- Actualizar logo de la empresa --- + print("\nActualizando logo de la empresa...") + sys.stdout.flush() + + if os.path.exists("/app/scripts/update_company_logo_odoo18.py"): + with open("/app/scripts/update_company_logo_odoo18.py", "r") as f: + logo_script_content = f.read() + + update_logo_command = f""" + odoo shell -c {ODOO_CONF} -d {DB_NAME} <<'EOF' +{logo_script_content} +EOF + """ + + result = subprocess.run( + update_logo_command, + shell=True, + capture_output=True, + text=True, + check=False + ) + + print("--- Update Company Logo stdout ---") + print(result.stdout) + print("--- Update Company Logo stderr ---") + print(result.stderr) + sys.stdout.flush() + + if result.returncode == 0: + print("Logo de empresa actualizado exitosamente.") + else: + print(f"Advertencia: Fallo al actualizar logo de empresa (código {result.returncode})") + + # --- Validación final del logo --- + print("\nValidando estado final del logo y nombre...") + sys.stdout.flush() + + if os.path.exists("/app/test/verify_company_logo.py"): + with open("/app/test/verify_company_logo.py", "r") as f: + verify_script_content = f.read() + + verify_command = f""" + odoo shell -c {ODOO_CONF} -d {DB_NAME} <<'EOF' +{verify_script_content} +EOF + """ + + result = subprocess.run( + verify_command, + shell=True, + capture_output=True, + text=True, + check=False + ) + + print("--- Verify Company Logo stdout ---") + print(result.stdout) + sys.exit(0) except Exception as e: diff --git a/lims_management/models/__pycache__/__init__.cpython-312.pyc b/lims_management/models/__pycache__/__init__.cpython-312.pyc index 8a440fc..a1e9030 100644 Binary files a/lims_management/models/__pycache__/__init__.cpython-312.pyc and b/lims_management/models/__pycache__/__init__.cpython-312.pyc differ diff --git a/lims_management/models/__pycache__/sale_order.cpython-312.pyc b/lims_management/models/__pycache__/sale_order.cpython-312.pyc index b4b6816..fa60ec8 100644 Binary files a/lims_management/models/__pycache__/sale_order.cpython-312.pyc and b/lims_management/models/__pycache__/sale_order.cpython-312.pyc differ diff --git a/lims_management/static/img/lab_logo.png b/lims_management/static/img/lab_logo.png new file mode 100644 index 0000000..5edae71 Binary files /dev/null and b/lims_management/static/img/lab_logo.png differ diff --git a/scripts/simple_logo_update.py b/scripts/simple_logo_update.py new file mode 100644 index 0000000..38d686b --- /dev/null +++ b/scripts/simple_logo_update.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +import odoo +import base64 +import os + +def update_logo(): + db_name = 'lims_demo' + registry = odoo.registry(db_name) + + with registry.cursor() as cr: + env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) + + # Buscar la empresa principal + company = env['res.company'].browse(1) + + # Leer el logo + logo_path = '/mnt/extra-addons/lims_management/static/img/lab_logo.png' + with open(logo_path, 'rb') as f: + logo_base64 = base64.b64encode(f.read()).decode('utf-8') + + # Actualizar + company.write({ + 'logo': logo_base64, + 'name': 'Laboratorio Clínico LIMS' + }) + + cr.commit() + print("Logo actualizado exitosamente") + +if __name__ == '__main__': + update_logo() \ No newline at end of file diff --git a/scripts/update_company_logo.py b/scripts/update_company_logo.py new file mode 100644 index 0000000..6e9c3e0 --- /dev/null +++ b/scripts/update_company_logo.py @@ -0,0 +1,56 @@ +import base64 +import os +import sys + +try: + # El script se ejecuta dentro del shell de Odoo + # env ya está disponible en el contexto + + print("Iniciando actualización de logo...") + + # Buscar la empresa principal + company = env['res.company'].search([('id', '=', 1)], limit=1) + if not company: + print("ERROR: No se encontró la empresa principal") + sys.exit(1) + + print(f"Empresa encontrada: {company.name}") + + # Leer el archivo de logo + logo_path = '/mnt/extra-addons/lims_management/static/img/lab_logo.png' + if not os.path.exists(logo_path): + print(f"ERROR: No se encontró el archivo de logo en: {logo_path}") + sys.exit(1) + + print(f"Archivo de logo encontrado en: {logo_path}") + + # Leer y codificar la imagen + with open(logo_path, 'rb') as logo_file: + logo_data = logo_file.read() + logo_base64 = base64.b64encode(logo_data).decode('utf-8') + + print(f"Logo leído correctamente, tamaño: {len(logo_data)} bytes") + + # Actualizar el logo y nombre de la empresa + company.write({ + 'logo': logo_base64, + 'name': 'Laboratorio Clínico LIMS' + }) + + print(f"Logo actualizado exitosamente para la empresa: {company.name}") + + # También actualizar el partner asociado + if company.partner_id: + company.partner_id.write({ + 'image_1920': logo_base64 + }) + print(f"Logo del partner también actualizado (ID: {company.partner_id.id})") + + # El commit se hace automáticamente al salir del shell + print("\nLogo de la empresa actualizado exitosamente en la base de datos.") + +except Exception as e: + print(f"ERROR al actualizar el logo: {str(e)}") + import traceback + traceback.print_exc() + sys.exit(1) \ No newline at end of file diff --git a/scripts/update_company_logo_fixed.py b/scripts/update_company_logo_fixed.py new file mode 100644 index 0000000..5256f89 --- /dev/null +++ b/scripts/update_company_logo_fixed.py @@ -0,0 +1,69 @@ +import base64 +import os +import sys + +try: + # El script se ejecuta dentro del shell de Odoo + # env ya está disponible en el contexto + + print("Iniciando actualización de logo...") + + # Buscar TODAS las empresas (puede haber más de una) + companies = env['res.company'].search([]) + print(f"Empresas encontradas: {len(companies)}") + + # Leer el archivo de logo + logo_path = '/mnt/extra-addons/lims_management/static/img/lab_logo.png' + if not os.path.exists(logo_path): + print(f"ERROR: No se encontró el archivo de logo en: {logo_path}") + sys.exit(1) + + print(f"Archivo de logo encontrado en: {logo_path}") + + # Leer y codificar la imagen + with open(logo_path, 'rb') as logo_file: + logo_data = logo_file.read() + logo_base64 = base64.b64encode(logo_data).decode('utf-8') + + print(f"Logo leído correctamente, tamaño: {len(logo_data)} bytes") + + # Actualizar TODAS las empresas + for company in companies: + print(f"\nActualizando empresa ID {company.id}: {company.name}") + + # Actualizar el logo y nombre de la empresa + company.write({ + 'logo': logo_base64, + 'name': 'Laboratorio Clínico LIMS' + }) + + print(f"Logo actualizado para la empresa ID {company.id}") + + # También actualizar el partner asociado + if company.partner_id: + company.partner_id.write({ + 'image_1920': logo_base64, + 'name': 'Laboratorio Clínico LIMS' + }) + print(f"Logo y nombre del partner también actualizados (ID: {company.partner_id.id})") + + # IMPORTANTE: Hacer commit explícito + env.cr.commit() + print("\nCommit realizado - cambios guardados en la base de datos.") + + # Verificar que los cambios se guardaron + companies_check = env['res.company'].search([]) + for company in companies_check: + has_logo = bool(company.logo) + correct_name = company.name == 'Laboratorio Clínico LIMS' + print(f"\nVerificación empresa ID {company.id}:") + print(f" - Nombre: {company.name} ({'✓' if correct_name else '✗'})") + print(f" - Logo: {'✓ Presente' if has_logo else '✗ Ausente'}") + + print("\nLogo de la empresa actualizado exitosamente en la base de datos.") + +except Exception as e: + print(f"ERROR al actualizar el logo: {str(e)}") + import traceback + traceback.print_exc() + sys.exit(1) \ No newline at end of file diff --git a/scripts/update_company_logo_fixed2.py b/scripts/update_company_logo_fixed2.py new file mode 100644 index 0000000..7c3092f --- /dev/null +++ b/scripts/update_company_logo_fixed2.py @@ -0,0 +1,86 @@ +import base64 +import os +import sys + +try: + # El script se ejecuta dentro del shell de Odoo + # env ya está disponible en el contexto + + print("Iniciando actualización de logo...") + + # Buscar TODAS las empresas + companies = env['res.company'].search([]) + print(f"Empresas encontradas: {len(companies)}") + + # Leer el archivo de logo + logo_path = '/mnt/extra-addons/lims_management/static/img/lab_logo.png' + if not os.path.exists(logo_path): + print(f"ERROR: No se encontró el archivo de logo en: {logo_path}") + sys.exit(1) + + print(f"Archivo de logo encontrado en: {logo_path}") + + # Leer y codificar la imagen + with open(logo_path, 'rb') as logo_file: + logo_data = logo_file.read() + logo_base64 = base64.b64encode(logo_data).decode('utf-8') + + print(f"Logo leído correctamente, tamaño: {len(logo_data)} bytes") + + # Actualizar las empresas con nombres únicos + for idx, company in enumerate(companies): + print(f"\nActualizando empresa ID {company.id}: {company.name}") + + # Generar nombre único para cada empresa + if idx == 0: + new_name = 'Laboratorio Clínico LIMS' + else: + new_name = f'Laboratorio Clínico LIMS - Sucursal {idx}' + + try: + # Actualizar el logo y nombre de la empresa + company.write({ + 'logo': logo_base64, + 'name': new_name + }) + + print(f"Logo actualizado para la empresa ID {company.id}") + print(f"Nuevo nombre: {new_name}") + + # También actualizar el partner asociado + if company.partner_id: + company.partner_id.write({ + 'image_1920': logo_base64, + 'name': new_name + }) + print(f"Logo y nombre del partner también actualizados (ID: {company.partner_id.id})") + + except Exception as e: + print(f"Error al actualizar empresa ID {company.id}: {str(e)}") + continue + + # IMPORTANTE: Hacer commit explícito + env.cr.commit() + print("\nCommit realizado - cambios guardados en la base de datos.") + + # Verificar que los cambios se guardaron + companies_check = env['res.company'].search([]) + print("\n" + "="*60) + print("VERIFICACIÓN FINAL:") + print("="*60) + for company in companies_check: + has_logo = bool(company.logo) + print(f"\nEmpresa ID {company.id}:") + print(f" - Nombre: {company.name}") + print(f" - Logo: {'✓ Presente' if has_logo else '✗ Ausente'}") + if has_logo: + logo_size = len(base64.b64decode(company.logo)) + print(f" - Tamaño del logo: {logo_size:,} bytes") + + print("\nLogo de la empresa actualizado exitosamente en la base de datos.") + +except Exception as e: + print(f"ERROR al actualizar el logo: {str(e)}") + import traceback + traceback.print_exc() + sys.exit(1) \ No newline at end of file diff --git a/scripts/update_company_logo_odoo18.py b/scripts/update_company_logo_odoo18.py new file mode 100644 index 0000000..bf7bbfc --- /dev/null +++ b/scripts/update_company_logo_odoo18.py @@ -0,0 +1,98 @@ +import base64 +import os +import sys + +try: + # El script se ejecuta dentro del shell de Odoo + print("Iniciando actualización de logo para Odoo 18...") + + # Buscar TODAS las empresas + companies = env['res.company'].search([]) + print(f"Empresas encontradas: {len(companies)}") + + # Leer el archivo de logo + logo_path = '/mnt/extra-addons/lims_management/static/img/lab_logo.png' + if not os.path.exists(logo_path): + print(f"ERROR: No se encontró el archivo de logo en: {logo_path}") + sys.exit(1) + + print(f"Archivo de logo encontrado en: {logo_path}") + + # Leer y codificar la imagen + with open(logo_path, 'rb') as logo_file: + logo_data = logo_file.read() + logo_base64 = base64.b64encode(logo_data) # No decodificar a string + + print(f"Logo leído correctamente, tamaño: {len(logo_data)} bytes") + + # Actualizar las empresas + for idx, company in enumerate(companies): + print(f"\nActualizando empresa ID {company.id}: {company.name}") + + # Generar nombre único para cada empresa + if idx == 0: + new_name = 'Laboratorio Clínico LIMS' + else: + new_name = f'Laboratorio Clínico LIMS - Sucursal {idx}' + + try: + # En Odoo 18, actualizar por separado para evitar problemas + # Primero el nombre + company.name = new_name + env.cr.commit() + print(f"Nombre actualizado: {new_name}") + + # Luego el logo usando sudo para evitar problemas de permisos + company.sudo().write({ + 'logo': logo_base64, + }) + env.cr.commit() + print(f"Logo actualizado para la empresa ID {company.id}") + + # También actualizar el partner asociado + if company.partner_id: + company.partner_id.name = new_name + company.partner_id.sudo().write({ + 'image_1920': logo_base64, + }) + env.cr.commit() + print(f"Partner actualizado (ID: {company.partner_id.id})") + + except Exception as e: + print(f"Error al actualizar empresa ID {company.id}: {str(e)}") + env.cr.rollback() + continue + + # Verificación final con consulta directa a la BD + print("\n" + "="*60) + print("VERIFICACIÓN FINAL (consulta directa):") + print("="*60) + + env.cr.execute(""" + SELECT id, name, + CASE WHEN logo IS NOT NULL THEN 'SI' ELSE 'NO' END as tiene_logo, + CASE WHEN logo IS NOT NULL THEN length(logo) ELSE 0 END as logo_size + FROM res_company + ORDER BY id + """) + + for row in env.cr.fetchall(): + print(f"\nEmpresa ID {row[0]}:") + print(f" - Nombre: {row[1]}") + print(f" - Logo presente: {row[2]}") + if row[2] == 'SI': + print(f" - Tamaño del logo (base64): {row[3]:,} caracteres") + + # Forzar actualización de caché + env['res.company'].invalidate_cache() + + print("\nLogo de la empresa actualizado exitosamente.") + print("NOTA: Si el logo no aparece en la interfaz, puede ser necesario:") + print(" 1. Limpiar la caché del navegador (Ctrl+F5)") + print(" 2. Reiniciar el servicio de Odoo") + +except Exception as e: + print(f"ERROR al actualizar el logo: {str(e)}") + import traceback + traceback.print_exc() + sys.exit(1) \ No newline at end of file diff --git a/check_stock_lot_fields.py b/test/check_stock_lot_fields.py similarity index 100% rename from check_stock_lot_fields.py rename to test/check_stock_lot_fields.py diff --git a/create_lab_requests.py b/test/create_lab_requests.py similarity index 100% rename from create_lab_requests.py rename to test/create_lab_requests.py diff --git a/verify_automatic_sample_generation.py b/test/verify_automatic_sample_generation.py similarity index 100% rename from verify_automatic_sample_generation.py rename to test/verify_automatic_sample_generation.py diff --git a/test/verify_company_logo.py b/test/verify_company_logo.py new file mode 100644 index 0000000..ac05d13 --- /dev/null +++ b/test/verify_company_logo.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +""" +Script de validación para verificar el estado del logo y nombre de la empresa +""" +import odoo +import base64 + +def verify_company_logo_status(): + db_name = 'lims_demo' + registry = odoo.registry(db_name) + + with registry.cursor() as cr: + env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) + + print("=" * 60) + print("VERIFICACIÓN DE LOGO Y NOMBRE DE EMPRESA") + print("=" * 60) + + # Buscar todas las empresas + companies = env['res.company'].search([]) + + for company in companies: + print(f"\nEmpresa ID: {company.id}") + print(f"Nombre actual: '{company.name}'") + print(f"Nombre esperado: 'Laboratorio Clínico LIMS'") + print(f"¿Nombre correcto?: {'✓ SÍ' if company.name == 'Laboratorio Clínico LIMS' else '✗ NO'}") + + # Verificar logo + if company.logo: + logo_size = len(base64.b64decode(company.logo)) + print(f"Logo presente: ✓ SÍ (tamaño: {logo_size:,} bytes)") + + # Verificar si es el logo correcto comparando tamaño + expected_size = 1428899 # Tamaño del lab_logo.png + if abs(logo_size - expected_size) < 1000: # Margen de error pequeño + print(f"¿Logo correcto?: ✓ PROBABLEMENTE SÍ (tamaño coincide)") + else: + print(f"¿Logo correcto?: ✗ PROBABLEMENTE NO (tamaño esperado: {expected_size:,} bytes)") + else: + print("Logo presente: ✗ NO") + + # Verificar partner asociado + if company.partner_id: + print(f"\nPartner asociado ID: {company.partner_id.id}") + print(f"Nombre del partner: '{company.partner_id.name}'") + if company.partner_id.image_1920: + partner_img_size = len(base64.b64decode(company.partner_id.image_1920)) + print(f"Imagen del partner: ✓ SÍ (tamaño: {partner_img_size:,} bytes)") + else: + print("Imagen del partner: ✗ NO") + + print("-" * 60) + + # Verificar configuración del sistema + print("\nCONFIGURACIÓN DEL SISTEMA:") + print("-" * 30) + + # Verificar parámetros del sistema + param_obj = env['ir.config_parameter'].sudo() + web_base_url = param_obj.get_param('web.base.url') + print(f"URL base: {web_base_url}") + + # Verificar usuarios admin + admin_users = env['res.users'].search([('id', '=', 2)]) # Usuario admin + for user in admin_users: + print(f"\nUsuario admin: {user.login}") + print(f"Empresa del usuario: {user.company_id.name}") + print(f"Empresas permitidas: {', '.join([c.name for c in user.company_ids])}") + + print("\n" + "=" * 60) + + # Resumen final + company_ok = any(c.name == 'Laboratorio Clínico LIMS' for c in companies) + logo_ok = any(c.logo for c in companies) + + print("\nRESUMEN:") + print(f"Estado del nombre: {'✓ CORRECTO' if company_ok else '✗ INCORRECTO'}") + print(f"Estado del logo: {'✓ PRESENTE' if logo_ok else '✗ AUSENTE'}") + + if not company_ok or not logo_ok: + print("\n⚠️ ATENCIÓN: El logo o nombre no están configurados correctamente.") + print(" Es posible que los cambios no se estén guardando en la base de datos.") + else: + print("\n✓ Todo parece estar configurado correctamente.") + +if __name__ == '__main__': + verify_company_logo_status() \ No newline at end of file diff --git a/verify_products.py b/test/verify_products.py similarity index 100% rename from verify_products.py rename to test/verify_products.py diff --git a/verify_sample_relationships.py b/test/verify_sample_relationships.py similarity index 100% rename from verify_sample_relationships.py rename to test/verify_sample_relationships.py diff --git a/get_metadata.py b/utils/get_metadata.py similarity index 100% rename from get_metadata.py rename to utils/get_metadata.py diff --git a/get_view_arch.py b/utils/get_view_arch.py similarity index 100% rename from get_view_arch.py rename to utils/get_view_arch.py diff --git a/gitea_cli_helper.py b/utils/gitea_cli_helper.py similarity index 100% rename from gitea_cli_helper.py rename to utils/gitea_cli_helper.py