feat: Reorganización de scripts y actualización automática de logo

- Movidos scripts Python a carpetas organizadas (test/ y utils/)
- Agregado logo del laboratorio (lab_logo.png)
- Implementado sistema de actualización automática de logo y nombre de empresa
- Creado script de validación para verificar estado del logo
- Actualizada documentación CLAUDE.md con nuevas rutas
- Eliminados intentos fallidos de cambio de tema CSS
- Ajustado docker-compose.yml para mapear carpetas de scripts

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Luis Ernesto Portillo Zaldivar 2025-07-15 03:50:46 -06:00
parent beaed060eb
commit ddbc829103
21 changed files with 501 additions and 12 deletions

View File

@ -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": []
}

View File

@ -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

View File

@ -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

View File

@ -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:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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()