feat(#10): Implementar etiquetas de muestras con código de barras

- Crear plantilla QWeb para etiquetas de 100x50mm
- Incluir datos del paciente, orden y tipo de muestra
- Generar código de barras Code128 usando campo existente
- Agregar botón 'Imprimir Etiquetas' en órdenes confirmadas
- Formato optimizado para impresoras de etiquetas

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Luis Ernesto Portillo Zaldivar 2025-07-15 19:57:25 -06:00
parent 4d3206f3a1
commit c74b17554e
7 changed files with 149 additions and 1 deletions

View File

@ -23,7 +23,8 @@
"Bash(find:*)",
"Bash(true)",
"Bash(bash:*)",
"Bash(grep:*)"
"Bash(grep:*)",
"Bash(gh pr merge:*)"
],
"deny": []
}

View File

@ -43,6 +43,7 @@
'views/product_template_parameter_config_views.xml',
'views/parameter_dashboard_views.xml',
'views/menus.xml',
'report/sample_label_report.xml',
],
'demo': [
'demo/z_lims_demo.xml',

View File

@ -293,3 +293,16 @@ class SaleOrder(models.Model):
_logger.info(f"Cancelled {len(samples_to_cancel)} samples and {len(tests_to_cancel)} tests for order {self.name}")
return res
def action_print_sample_labels(self):
"""Imprimir etiquetas de todas las muestras generadas para esta orden"""
self.ensure_one()
if not self.generated_sample_ids:
raise UserError(_('No hay muestras generadas para esta orden. Por favor, confirme la orden primero.'))
# Obtener el reporte
report = self.env.ref('lims_management.action_report_sample_label')
# Retornar la acción de imprimir el reporte para todas las muestras
return report.report_action(self.generated_sample_ids)

View File

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!-- Definir el reporte -->
<record id="action_report_sample_label" model="ir.actions.report">
<field name="name">Etiquetas de Muestras</field>
<field name="model">stock.lot</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">lims_management.report_sample_label</field>
<field name="report_file">lims_management.report_sample_label</field>
<field name="print_report_name">'Etiquetas - ' + object.name</field>
<field name="binding_type">report</field>
<field name="paperformat_id" ref="lims_management.paperformat_sample_label"/>
</record>
<!-- Formato de papel para etiquetas -->
<record id="paperformat_sample_label" model="report.paperformat">
<field name="name">Formato Etiqueta Muestra</field>
<field name="default" eval="False"/>
<field name="format">custom</field>
<field name="page_height">50</field>
<field name="page_width">100</field>
<field name="orientation">Landscape</field>
<field name="margin_top">2</field>
<field name="margin_bottom">2</field>
<field name="margin_left">2</field>
<field name="margin_right">2</field>
<field name="header_line" eval="False"/>
<field name="header_spacing">0</field>
<field name="dpi">200</field>
</record>
<!-- Template del reporte -->
<template id="report_sample_label">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<div class="page" style="page-break-after: always;">
<div style="width: 96mm; height: 46mm; border: 1px solid #ccc; padding: 2mm; font-family: Arial, sans-serif;">
<!-- Encabezado -->
<div style="text-align: center; margin-bottom: 2mm;">
<h4 style="margin: 0; font-size: 14px;">LABORATORIO CLÍNICO</h4>
</div>
<!-- Información del paciente -->
<div style="font-size: 11px; margin-bottom: 2mm;">
<div><strong>Paciente:</strong> <span t-field="o.patient_id.name"/></div>
<div><strong>ID:</strong> <span t-field="o.patient_id.vat" t-if="o.patient_id.vat"/>
<span t-else="">Sin ID</span>
</div>
</div>
<!-- Información de la muestra -->
<div style="font-size: 10px; margin-bottom: 3mm;">
<div><strong>Orden:</strong> <span t-field="o.origin"/></div>
<div><strong>Tipo:</strong> <span t-esc="o.get_container_name()"/></div>
<div><strong>Fecha:</strong> <span t-field="o.collection_date" t-options='{"widget": "date"}'/></div>
</div>
<!-- Código de barras -->
<div style="text-align: center; margin-top: 3mm;">
<img t-att-src="'/report/barcode/?barcode_type=%s&amp;value=%s&amp;width=%s&amp;height=%s&amp;humanreadable=%s' % ('Code128', o.barcode, 250, 60, 1)"
style="width: 250px; height: 60px;"/>
</div>
<!-- Análisis a realizar (si caben) -->
<div style="font-size: 9px; margin-top: 2mm;" t-if="o.analysis_names">
<div><strong>Análisis:</strong> <span t-field="o.analysis_names"/></div>
</div>
</div>
</div>
</t>
</t>
</template>
</data>
</odoo>

View File

@ -8,6 +8,15 @@
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<!-- Agregar botón de imprimir etiquetas en el header -->
<xpath expr="//header" position="inside">
<button name="action_print_sample_labels"
string="Imprimir Etiquetas"
type="object"
class="btn-primary"
invisible="not is_lab_request or state != 'sale' or not generated_sample_ids"
icon="fa-print"/>
</xpath>
<xpath expr="//field[@name='partner_id']" position="after">
<field name="doctor_id" invisible="not is_lab_request"/>
</xpath>

48
pr_body_9.txt Normal file
View File

@ -0,0 +1,48 @@
## Implementación del flujo de validación y seguridad
### Cambios realizados
#### 1. Ajuste de permisos base (ir.model.access.csv)
- Recepcionista: Solo lectura en lims.test y lims.result
- Técnico: Lectura/escritura pero sin crear/eliminar
- Administrador: Permisos completos
#### 2. Reglas de registro implementadas (lims_security.xml)
- Recepcionistas no pueden editar pruebas
- Técnicos solo pueden editar pruebas no validadas
- Administradores tienen acceso completo
#### 3. Validación de permisos en transiciones (lims_test.py)
- `action_start_process()`: Solo técnicos y administradores
- `action_enter_results()`: Solo técnicos y administradores
- `action_validate()`: Solo administradores
- `action_cancel()`: Técnicos (excepto validadas) y administradores
- `action_draft()`: Solo administradores
#### 4. Trazabilidad mejorada
- stock.lot ahora hereda de mail.thread
- Todos los cambios de estado se registran en el chatter
- Mensajes más descriptivos con contexto
#### 5. Validaciones adicionales
- Control de transiciones de estado válidas
- Verificación del estado de la muestra
- Validación de resultados críticos fuera de rango
- No se puede crear pruebas en estado != draft sin ser admin
#### 6. Vistas actualizadas
- Botones visibles solo para roles apropiados
- Campos de resultados editables solo por técnicos/admin
#### 7. Usuarios demo para pruebas
- Usuario: `recepcionista` / Contraseña: `demo`
- Usuario: `tecnico` / Contraseña: `demo`
- Usuario: `administrador` / Contraseña: `demo`
### Pruebas realizadas
- Verificación de permisos por rol
- Validación de transiciones de estado
- Trazabilidad en chatter
- Restricciones visuales en formularios
Closes #9