Merge pull request 'feat(#11): Implementar informe PDF de resultados de laboratorio' (#66) from feature/11-informe-resultados-pdf into dev
Reviewed-on: #66
This commit is contained in:
commit
ac427ff778
|
@ -25,7 +25,9 @@
|
||||||
"Bash(bash:*)",
|
"Bash(bash:*)",
|
||||||
"Bash(grep:*)",
|
"Bash(grep:*)",
|
||||||
"Bash(gh pr merge:*)",
|
"Bash(gh pr merge:*)",
|
||||||
"Bash(git cherry-pick:*)"
|
"Bash(git cherry-pick:*)",
|
||||||
|
"Bash(del comment_issue_15.txt)",
|
||||||
|
"Bash(cat:*)"
|
||||||
],
|
],
|
||||||
"deny": []
|
"deny": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,8 @@
|
||||||
'views/menus.xml',
|
'views/menus.xml',
|
||||||
'views/lims_config_views.xml',
|
'views/lims_config_views.xml',
|
||||||
'report/sample_label_report.xml',
|
'report/sample_label_report.xml',
|
||||||
|
'reports/lab_results_report_data.xml',
|
||||||
|
'reports/lab_results_report.xml',
|
||||||
],
|
],
|
||||||
'demo': [
|
'demo': [
|
||||||
'demo/demo_users.xml',
|
'demo/demo_users.xml',
|
||||||
|
|
Binary file not shown.
|
@ -28,6 +28,14 @@ class LimsTest(models.Model):
|
||||||
ondelete='restrict'
|
ondelete='restrict'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sale_order_id = fields.Many2one(
|
||||||
|
'sale.order',
|
||||||
|
string='Orden de Venta',
|
||||||
|
related='sale_order_line_id.order_id',
|
||||||
|
store=True,
|
||||||
|
readonly=True
|
||||||
|
)
|
||||||
|
|
||||||
patient_id = fields.Many2one(
|
patient_id = fields.Many2one(
|
||||||
'res.partner',
|
'res.partner',
|
||||||
string='Paciente',
|
string='Paciente',
|
||||||
|
|
|
@ -102,6 +102,26 @@ class LimsParameterRange(models.Model):
|
||||||
readonly=True
|
readonly=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
reference_text = fields.Char(
|
||||||
|
string='Texto de Referencia',
|
||||||
|
compute='_compute_reference_text',
|
||||||
|
store=False,
|
||||||
|
help='Texto formateado del rango de referencia'
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends('normal_min', 'normal_max', 'parameter_unit')
|
||||||
|
def _compute_reference_text(self):
|
||||||
|
"""Computa el texto de referencia basado en los valores min/max y unidad"""
|
||||||
|
for record in self:
|
||||||
|
if record.normal_min is not False and record.normal_max is not False:
|
||||||
|
unit = record.parameter_unit or ''
|
||||||
|
# Formatear los números para evitar decimales innecesarios
|
||||||
|
min_val = f"{record.normal_min:.2f}".rstrip('0').rstrip('.')
|
||||||
|
max_val = f"{record.normal_max:.2f}".rstrip('0').rstrip('.')
|
||||||
|
record.reference_text = f"{min_val} - {max_val} {unit}".strip()
|
||||||
|
else:
|
||||||
|
record.reference_text = "N/A"
|
||||||
|
|
||||||
@api.depends('parameter_id', 'gender', 'age_min', 'age_max', 'pregnant')
|
@api.depends('parameter_id', 'gender', 'age_min', 'age_max', 'pregnant')
|
||||||
def _compute_name(self):
|
def _compute_name(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
|
|
|
@ -339,3 +339,56 @@ class SaleOrder(models.Model):
|
||||||
|
|
||||||
# Retornar la acción de imprimir el reporte para las muestras activas
|
# Retornar la acción de imprimir el reporte para las muestras activas
|
||||||
return report.report_action(active_samples)
|
return report.report_action(active_samples)
|
||||||
|
|
||||||
|
# Fields for lab results report
|
||||||
|
can_print_results = fields.Boolean(
|
||||||
|
string="Puede Imprimir Resultados",
|
||||||
|
compute='_compute_can_print_results',
|
||||||
|
help="Indica si todas las pruebas están validadas y se puede imprimir el informe"
|
||||||
|
)
|
||||||
|
|
||||||
|
lab_test_ids = fields.One2many(
|
||||||
|
'lims.test',
|
||||||
|
'sale_order_id',
|
||||||
|
string="Pruebas de Laboratorio",
|
||||||
|
readonly=True,
|
||||||
|
help="Todas las pruebas de laboratorio asociadas a esta orden"
|
||||||
|
)
|
||||||
|
|
||||||
|
referring_doctor_id = fields.Many2one(
|
||||||
|
'res.partner',
|
||||||
|
string="Médico Solicitante",
|
||||||
|
related='doctor_id',
|
||||||
|
readonly=True,
|
||||||
|
help="Médico que solicitó los análisis"
|
||||||
|
)
|
||||||
|
|
||||||
|
lab_notes = fields.Text(
|
||||||
|
string="Observaciones del Laboratorio",
|
||||||
|
help="Observaciones generales sobre la orden o los resultados"
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends('lab_test_ids.state')
|
||||||
|
def _compute_can_print_results(self):
|
||||||
|
"""Compute if results can be printed (all tests validated)"""
|
||||||
|
for order in self:
|
||||||
|
tests = order.lab_test_ids
|
||||||
|
order.can_print_results = (
|
||||||
|
tests and
|
||||||
|
all(test.state == 'validated' for test in tests)
|
||||||
|
)
|
||||||
|
|
||||||
|
def action_print_lab_results(self):
|
||||||
|
"""Generate and print lab results report"""
|
||||||
|
self.ensure_one()
|
||||||
|
|
||||||
|
# Verify all tests are validated
|
||||||
|
if not self.can_print_results:
|
||||||
|
raise UserError(_("No se puede imprimir el informe: hay pruebas sin validar"))
|
||||||
|
|
||||||
|
# Ensure this is a lab request
|
||||||
|
if not self.is_lab_request:
|
||||||
|
raise UserError(_("Esta no es una orden de laboratorio"))
|
||||||
|
|
||||||
|
# Generate the report
|
||||||
|
return self.env.ref('lims_management.action_report_lab_results').report_action(self)
|
||||||
|
|
274
lims_management/reports/lab_results_report.xml
Normal file
274
lims_management/reports/lab_results_report.xml
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- Template principal del reporte -->
|
||||||
|
<template id="report_lab_results">
|
||||||
|
<t t-call="web.html_container">
|
||||||
|
<t t-foreach="docs" t-as="o">
|
||||||
|
<t t-if="o.is_lab_request">
|
||||||
|
<t t-call="lims_management.report_lab_results_document" t-lang="o.partner_id.lang"/>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Documento individual -->
|
||||||
|
<template id="report_lab_results_document">
|
||||||
|
<t t-call="web.external_layout">
|
||||||
|
<div class="page">
|
||||||
|
<!-- Estilos CSS -->
|
||||||
|
<style>
|
||||||
|
.lab-header {
|
||||||
|
border-bottom: 2px solid #337ab7;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
.patient-info {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.results-table {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.results-table th {
|
||||||
|
background-color: #e9ecef;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
.results-table td {
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
.result-out-of-range {
|
||||||
|
color: #d9534f;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.result-critical {
|
||||||
|
background-color: #f2dede;
|
||||||
|
color: #a94442;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 2px 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.result-normal {
|
||||||
|
color: #5cb85c;
|
||||||
|
}
|
||||||
|
.test-header {
|
||||||
|
background-color: #337ab7;
|
||||||
|
color: white;
|
||||||
|
padding: 10px;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.observations {
|
||||||
|
background-color: #fcf8e3;
|
||||||
|
padding: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
border-left: 4px solid #faebcc;
|
||||||
|
}
|
||||||
|
.validation-info {
|
||||||
|
margin-top: 40px;
|
||||||
|
border-top: 1px solid #dee2e6;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
.signature-line {
|
||||||
|
border-bottom: 1px solid #000;
|
||||||
|
width: 250px;
|
||||||
|
margin-top: 50px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- Encabezado del laboratorio -->
|
||||||
|
<div class="lab-header">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8">
|
||||||
|
<h2>LABORATORIO CLÍNICO</h2>
|
||||||
|
<h3><t t-esc="o.company_id.name"/></h3>
|
||||||
|
<p>
|
||||||
|
<t t-if="o.company_id.street"><t t-esc="o.company_id.street"/><br/></t>
|
||||||
|
<t t-if="o.company_id.city"><t t-esc="o.company_id.city"/>, </t>
|
||||||
|
<t t-if="o.company_id.state_id"><t t-esc="o.company_id.state_id.name"/><br/></t>
|
||||||
|
<t t-if="o.company_id.phone">Tel: <t t-esc="o.company_id.phone"/></t>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 text-right">
|
||||||
|
<img t-if="o.company_id.logo" t-att-src="image_data_uri(o.company_id.logo)"
|
||||||
|
style="max-height: 100px; max-width: 200px;"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Información del paciente y orden -->
|
||||||
|
<div class="patient-info">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<h4>DATOS DEL PACIENTE</h4>
|
||||||
|
<table class="table table-sm">
|
||||||
|
<tr>
|
||||||
|
<td><strong>Nombre:</strong></td>
|
||||||
|
<td><t t-esc="o.partner_id.name"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Identificación:</strong></td>
|
||||||
|
<td><t t-esc="o.partner_id.vat or 'N/A'"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Edad:</strong></td>
|
||||||
|
<td>
|
||||||
|
<t t-if="o.partner_id.birthdate_date">
|
||||||
|
<t t-esc="o.partner_id.age"/> años
|
||||||
|
</t>
|
||||||
|
<t t-else="">N/A</t>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Sexo:</strong></td>
|
||||||
|
<td>
|
||||||
|
<t t-if="o.partner_id.gender == 'male'">Masculino</t>
|
||||||
|
<t t-elif="o.partner_id.gender == 'female'">Femenino</t>
|
||||||
|
<t t-else="">No especificado</t>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<h4>DATOS DE LA ORDEN</h4>
|
||||||
|
<table class="table table-sm">
|
||||||
|
<tr>
|
||||||
|
<td><strong>Número de Orden:</strong></td>
|
||||||
|
<td><t t-esc="o.name"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Fecha de Solicitud:</strong></td>
|
||||||
|
<td><t t-esc="o.date_order" t-options='{"widget": "date"}'/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Médico Solicitante:</strong></td>
|
||||||
|
<td><t t-esc="o.referring_doctor_id.name or 'N/A'"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Estado:</strong></td>
|
||||||
|
<td>Resultados Validados</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Resultados de análisis -->
|
||||||
|
<h3 class="text-center" style="margin: 30px 0;">INFORME DE RESULTADOS</h3>
|
||||||
|
|
||||||
|
<!-- Iterar por cada prueba validada -->
|
||||||
|
<t t-set="validated_tests" t-value="o.lab_test_ids.filtered(lambda t: t.state == 'validated')"/>
|
||||||
|
<t t-foreach="validated_tests" t-as="test">
|
||||||
|
<div class="test-section">
|
||||||
|
<!-- Encabezado del análisis -->
|
||||||
|
<h4 class="test-header">
|
||||||
|
<t t-esc="test.product_id.name"/>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<!-- Tabla de resultados -->
|
||||||
|
<table class="table results-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="30%">PARÁMETRO</th>
|
||||||
|
<th width="20%" class="text-center">RESULTADO</th>
|
||||||
|
<th width="15%" class="text-center">UNIDAD</th>
|
||||||
|
<th width="35%" class="text-center">VALOR DE REFERENCIA</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<t t-foreach="test.result_ids" t-as="result">
|
||||||
|
<tr>
|
||||||
|
<td><t t-esc="result.parameter_id.name"/></td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span t-attf-class="#{result.is_critical and 'result-critical' or result.is_out_of_range and 'result-out-of-range' or 'result-normal'}">
|
||||||
|
<t t-esc="result.value_display"/>
|
||||||
|
<t t-if="result.is_critical"> **</t>
|
||||||
|
<t t-elif="result.is_out_of_range"> *</t>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<t t-esc="result.parameter_id.unit or '-'"/>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<t t-if="result.applicable_range_id">
|
||||||
|
<t t-if="result.parameter_id.value_type == 'numeric'">
|
||||||
|
<t t-esc="result.applicable_range_id.normal_min"/> - <t t-esc="result.applicable_range_id.normal_max"/>
|
||||||
|
</t>
|
||||||
|
<t t-else="">
|
||||||
|
<t t-esc="result.applicable_range_id.reference_text or 'N/A'"/>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
<t t-else="">N/A</t>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!-- Mostrar notas si existen -->
|
||||||
|
<t t-if="result.notes">
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" style="padding-left: 30px; font-style: italic;">
|
||||||
|
<strong>Nota:</strong> <t t-esc="result.notes"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Comentarios de la prueba -->
|
||||||
|
<t t-if="test.notes">
|
||||||
|
<div class="observations">
|
||||||
|
<strong>Observaciones:</strong> <t t-esc="test.notes"/>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<!-- Leyenda de símbolos -->
|
||||||
|
<div style="margin-top: 30px; font-size: 12px;">
|
||||||
|
<p><strong>*</strong> Valor fuera del rango normal</p>
|
||||||
|
<p><strong>**</strong> Valor crítico que requiere atención inmediata</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Comentarios generales de la orden -->
|
||||||
|
<t t-if="o.lab_notes">
|
||||||
|
<div class="observations" style="margin-top: 30px;">
|
||||||
|
<h5>OBSERVACIONES GENERALES</h5>
|
||||||
|
<p><t t-esc="o.lab_notes"/></p>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<!-- Información de validación -->
|
||||||
|
<div class="validation-info">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<p><strong>Fecha de Validación:</strong>
|
||||||
|
<t t-if="validated_tests">
|
||||||
|
<t t-esc="validated_tests[0].validation_date" t-options='{"widget": "datetime"}'/>
|
||||||
|
</t>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 text-center">
|
||||||
|
<t t-if="validated_tests and validated_tests[0].validator_id">
|
||||||
|
<div class="signature-line"></div>
|
||||||
|
<p style="margin-top: 5px;">
|
||||||
|
<strong><t t-esc="validated_tests[0].validator_id.name"/></strong><br/>
|
||||||
|
Responsable del Laboratorio
|
||||||
|
</p>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Nota al pie -->
|
||||||
|
<div style="margin-top: 50px; font-size: 10px; text-align: center; color: #666;">
|
||||||
|
<p>Este informe es confidencial y está dirigido exclusivamente al paciente y/o médico tratante.</p>
|
||||||
|
<p>Los resultados se relacionan únicamente con las muestras analizadas.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
</odoo>
|
30
lims_management/reports/lab_results_report_data.xml
Normal file
30
lims_management/reports/lab_results_report_data.xml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- Paper Format para el reporte de resultados -->
|
||||||
|
<record id="paperformat_lab_results" model="report.paperformat">
|
||||||
|
<field name="name">Formato Resultados de Laboratorio</field>
|
||||||
|
<field name="format">A4</field>
|
||||||
|
<field name="orientation">Portrait</field>
|
||||||
|
<field name="margin_top">40</field>
|
||||||
|
<field name="margin_bottom">25</field>
|
||||||
|
<field name="margin_left">10</field>
|
||||||
|
<field name="margin_right">10</field>
|
||||||
|
<field name="header_spacing">35</field>
|
||||||
|
<field name="dpi">90</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Acción del reporte -->
|
||||||
|
<record id="action_report_lab_results" model="ir.actions.report">
|
||||||
|
<field name="name">Informe de Resultados</field>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="report_type">qweb-pdf</field>
|
||||||
|
<field name="report_name">lims_management.report_lab_results</field>
|
||||||
|
<field name="report_file">lims_management.report_lab_results</field>
|
||||||
|
<field name="print_report_name">'Resultados_Lab_' + object.name + '.pdf'</field>
|
||||||
|
<field name="paperformat_id" ref="paperformat_lab_results"/>
|
||||||
|
<field name="attachment">'Resultados_Lab_' + object.name + '.pdf'</field>
|
||||||
|
<field name="attachment_use">True</field>
|
||||||
|
<field name="binding_model_id" ref="sale.model_sale_order"/>
|
||||||
|
<field name="binding_type">report</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
|
@ -64,7 +64,7 @@
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<notebook>
|
<notebook>
|
||||||
<page string="Resultados">
|
<page string="Resultados" name="results">
|
||||||
<field name="result_ids"
|
<field name="result_ids"
|
||||||
readonly="state in ['validated', 'cancelled']"
|
readonly="state in ['validated', 'cancelled']"
|
||||||
context="{'default_test_id': id, 'default_patient_id': patient_id, 'default_test_date': create_date}"
|
context="{'default_test_id': id, 'default_patient_id': patient_id, 'default_test_date': create_date}"
|
||||||
|
@ -118,16 +118,19 @@
|
||||||
</list>
|
</list>
|
||||||
</field>
|
</field>
|
||||||
</page>
|
</page>
|
||||||
<page string="Observaciones">
|
<page string="Observaciones" name="observations">
|
||||||
<field name="notes" placeholder="Agregar observaciones generales de la prueba..."/>
|
<group>
|
||||||
|
<field name="notes" nolabel="1" placeholder="Agregar observaciones generales de la prueba..."/>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
<page string="Actividades" name="activities">
|
||||||
|
<field name="activity_ids"/>
|
||||||
|
</page>
|
||||||
|
<page string="Historial" name="history">
|
||||||
|
<field name="message_ids" options="{'no_create': True}"/>
|
||||||
</page>
|
</page>
|
||||||
</notebook>
|
</notebook>
|
||||||
</sheet>
|
</sheet>
|
||||||
<div class="oe_chatter">
|
|
||||||
<field name="message_follower_ids"/>
|
|
||||||
<field name="activity_ids"/>
|
|
||||||
<field name="message_ids"/>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
|
@ -16,6 +16,12 @@
|
||||||
class="btn-primary"
|
class="btn-primary"
|
||||||
invisible="not is_lab_request or state != 'sale' or not all_sample_ids"
|
invisible="not is_lab_request or state != 'sale' or not all_sample_ids"
|
||||||
icon="fa-print"/>
|
icon="fa-print"/>
|
||||||
|
<button name="action_print_lab_results"
|
||||||
|
string="Imprimir Informe de Resultados"
|
||||||
|
type="object"
|
||||||
|
class="btn-success"
|
||||||
|
invisible="not can_print_results or not is_lab_request"
|
||||||
|
icon="fa-file-pdf-o"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='partner_id']" position="after">
|
<xpath expr="//field[@name='partner_id']" position="after">
|
||||||
<field name="doctor_id" invisible="not is_lab_request"/>
|
<field name="doctor_id" invisible="not is_lab_request"/>
|
||||||
|
@ -69,6 +75,11 @@
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</page>
|
</page>
|
||||||
|
<page string="Observaciones Lab" name="lab_notes" invisible="not is_lab_request">
|
||||||
|
<group>
|
||||||
|
<field name="lab_notes" nolabel="1" placeholder="Ingrese observaciones generales sobre la orden o los resultados..."/>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
127
test/add_results_to_tests.py
Normal file
127
test/add_results_to_tests.py
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Script para agregar resultados a las pruebas ya validadas
|
||||||
|
"""
|
||||||
|
|
||||||
|
import odoo
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def add_results_to_validated_tests(env):
|
||||||
|
"""Agregar resultados a pruebas ya validadas"""
|
||||||
|
|
||||||
|
# Buscar las órdenes S00029 y S00030
|
||||||
|
orders = env['sale.order'].search([('name', 'in', ['S00029', 'S00030'])], order='name')
|
||||||
|
|
||||||
|
if not orders:
|
||||||
|
print("No se encontraron las órdenes S00029 o S00030")
|
||||||
|
return
|
||||||
|
|
||||||
|
for order in orders:
|
||||||
|
print(f"\n=== Procesando orden {order.name} ===")
|
||||||
|
|
||||||
|
for test in order.lab_test_ids:
|
||||||
|
print(f"\nPrueba: {test.product_id.name}")
|
||||||
|
|
||||||
|
# Cambiar temporalmente a estado draft para poder modificar
|
||||||
|
test.sudo().write({'state': 'draft'})
|
||||||
|
|
||||||
|
# Generar resultados si no existen
|
||||||
|
if not test.result_ids:
|
||||||
|
test.sudo()._generate_test_results()
|
||||||
|
print(f" Generados {len(test.result_ids)} resultados")
|
||||||
|
|
||||||
|
# Asignar valores a los resultados
|
||||||
|
for result in test.result_ids:
|
||||||
|
parameter = result.parameter_id
|
||||||
|
vals = {}
|
||||||
|
|
||||||
|
if parameter.value_type == 'numeric':
|
||||||
|
# Valores específicos por código
|
||||||
|
if parameter.code == 'HGB': # Hemoglobina
|
||||||
|
vals['value_numeric'] = random.uniform(12.0, 16.0)
|
||||||
|
elif parameter.code == 'HCT': # Hematocrito
|
||||||
|
vals['value_numeric'] = random.uniform(36.0, 46.0)
|
||||||
|
elif parameter.code == 'WBC': # Leucocitos
|
||||||
|
vals['value_numeric'] = random.uniform(4.5, 10.0)
|
||||||
|
elif parameter.code == 'PLT': # Plaquetas
|
||||||
|
vals['value_numeric'] = random.uniform(150, 400)
|
||||||
|
elif parameter.code == 'RBC': # Eritrocitos
|
||||||
|
vals['value_numeric'] = random.uniform(4.0, 5.5)
|
||||||
|
elif parameter.code == 'GLU': # Glucosa
|
||||||
|
vals['value_numeric'] = random.uniform(70, 110)
|
||||||
|
elif parameter.code == 'CHOL': # Colesterol
|
||||||
|
vals['value_numeric'] = random.uniform(160, 220)
|
||||||
|
elif parameter.code == 'TRIG': # Triglicéridos
|
||||||
|
vals['value_numeric'] = random.uniform(50, 150)
|
||||||
|
elif parameter.code == 'HDL': # HDL
|
||||||
|
vals['value_numeric'] = random.uniform(40, 60)
|
||||||
|
elif parameter.code == 'LDL': # LDL
|
||||||
|
vals['value_numeric'] = random.uniform(80, 130)
|
||||||
|
else:
|
||||||
|
# Valor genérico
|
||||||
|
vals['value_numeric'] = random.uniform(10, 100)
|
||||||
|
|
||||||
|
print(f" - {parameter.name}: {vals['value_numeric']:.2f}")
|
||||||
|
|
||||||
|
elif parameter.value_type == 'text':
|
||||||
|
vals['value_text'] = "Normal"
|
||||||
|
elif parameter.value_type == 'selection':
|
||||||
|
vals['value_selection'] = "normal"
|
||||||
|
elif parameter.value_type == 'boolean':
|
||||||
|
vals['value_boolean'] = False
|
||||||
|
|
||||||
|
# Escribir con sudo para evitar restricciones
|
||||||
|
result.sudo().write(vals)
|
||||||
|
|
||||||
|
# Volver a estado validated
|
||||||
|
test.sudo().write({
|
||||||
|
'state': 'validated',
|
||||||
|
'validator_id': env.ref('base.user_admin').id,
|
||||||
|
'validation_date': fields.Datetime.now()
|
||||||
|
})
|
||||||
|
|
||||||
|
# Agregar notas a algunas pruebas
|
||||||
|
if 'Hemograma' in test.product_id.name:
|
||||||
|
test.sudo().write({'notes': 'Todos los parámetros dentro de rangos normales.'})
|
||||||
|
elif 'Lipídico' in test.product_id.name:
|
||||||
|
test.sudo().write({'notes': 'Perfil lipídico normal. Se recomienda mantener dieta balanceada.'})
|
||||||
|
|
||||||
|
print("\n✅ Resultados agregados exitosamente a todas las pruebas")
|
||||||
|
return orders
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Importar fields después de configurar Odoo
|
||||||
|
from odoo import fields
|
||||||
|
|
||||||
|
# Configuración
|
||||||
|
db_name = 'lims_demo'
|
||||||
|
|
||||||
|
# Conectar a Odoo
|
||||||
|
odoo.tools.config.parse_config(['--database', db_name])
|
||||||
|
|
||||||
|
# Obtener el registro de la base de datos
|
||||||
|
registry = odoo.registry(db_name)
|
||||||
|
|
||||||
|
# Crear cursor y environment
|
||||||
|
with registry.cursor() as cr:
|
||||||
|
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Agregar resultados a las pruebas
|
||||||
|
orders = add_results_to_validated_tests(env)
|
||||||
|
|
||||||
|
# Confirmar cambios
|
||||||
|
cr.commit()
|
||||||
|
|
||||||
|
print("\n📋 Ahora puedes probar el botón 'Imprimir Informe de Resultados' en las órdenes S00029 y S00030.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
cr.rollback()
|
||||||
|
print(f"\n❌ Error: {str(e)}")
|
||||||
|
_logger.error(f"Error agregando resultados: {str(e)}", exc_info=True)
|
67
test/check_order_s00025.py
Normal file
67
test/check_order_s00025.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Script para verificar los datos de la orden S00025
|
||||||
|
"""
|
||||||
|
|
||||||
|
import odoo
|
||||||
|
|
||||||
|
def check_order_s00025(env):
|
||||||
|
"""Verificar datos específicos de la orden S00025"""
|
||||||
|
|
||||||
|
# Buscar la orden S00025
|
||||||
|
order = env['sale.order'].search([
|
||||||
|
('name', '=', 'S00025')
|
||||||
|
], limit=1)
|
||||||
|
|
||||||
|
if not order:
|
||||||
|
print("❌ No se encontró la orden S00025")
|
||||||
|
return
|
||||||
|
|
||||||
|
patient = order.partner_id
|
||||||
|
print(f"Orden: {order.name}")
|
||||||
|
print(f" Es orden de laboratorio: {order.is_lab_request}")
|
||||||
|
print(f" Paciente: {patient.name}")
|
||||||
|
print(f" ID Paciente: {patient.id}")
|
||||||
|
print(f" Fecha de nacimiento: {patient.birthdate_date}")
|
||||||
|
print(f" Edad: {patient.age if patient.birthdate_date else 'N/A'}")
|
||||||
|
print(f" Género: {patient.gender or 'No especificado'}")
|
||||||
|
|
||||||
|
# Verificar estado de las pruebas
|
||||||
|
print(f"\nPruebas de laboratorio ({len(order.lab_test_ids)}):")
|
||||||
|
for test in order.lab_test_ids:
|
||||||
|
print(f" - {test.product_id.name}: {test.state}")
|
||||||
|
|
||||||
|
# Verificar si puede imprimir resultados
|
||||||
|
print(f"\n¿Puede imprimir resultados?: {order.can_print_results}")
|
||||||
|
|
||||||
|
# Si el paciente no tiene fecha de nacimiento, actualizarla
|
||||||
|
if not patient.birthdate_date:
|
||||||
|
print("\n⚠️ El paciente no tiene fecha de nacimiento. Actualizando...")
|
||||||
|
patient.write({'birthdate_date': '1985-01-15'})
|
||||||
|
print(f" Fecha de nacimiento actualizada a: {patient.birthdate_date}")
|
||||||
|
print(f" Edad calculada: {patient.age} años")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Configuración
|
||||||
|
db_name = 'lims_demo'
|
||||||
|
|
||||||
|
# Conectar a Odoo
|
||||||
|
odoo.tools.config.parse_config(['--database', db_name])
|
||||||
|
|
||||||
|
# Obtener el registro de la base de datos
|
||||||
|
registry = odoo.registry(db_name)
|
||||||
|
|
||||||
|
# Crear cursor y environment
|
||||||
|
with registry.cursor() as cr:
|
||||||
|
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Verificar datos
|
||||||
|
check_order_s00025(env)
|
||||||
|
cr.commit()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n❌ Error: {str(e)}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
62
test/check_patient_data.py
Normal file
62
test/check_patient_data.py
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Script para verificar los datos de pacientes en las órdenes de laboratorio
|
||||||
|
"""
|
||||||
|
|
||||||
|
import odoo
|
||||||
|
|
||||||
|
def check_patient_data(env):
|
||||||
|
"""Verificar datos de pacientes en órdenes recientes"""
|
||||||
|
|
||||||
|
# Buscar órdenes de laboratorio recientes
|
||||||
|
orders = env['sale.order'].search([
|
||||||
|
('is_lab_request', '=', True),
|
||||||
|
('name', 'in', ['S00032', 'S00033'])
|
||||||
|
], order='id desc', limit=5)
|
||||||
|
|
||||||
|
if not orders:
|
||||||
|
orders = env['sale.order'].search([
|
||||||
|
('is_lab_request', '=', True)
|
||||||
|
], order='id desc', limit=5)
|
||||||
|
|
||||||
|
print(f"Verificando {len(orders)} órdenes de laboratorio...")
|
||||||
|
|
||||||
|
for order in orders:
|
||||||
|
patient = order.partner_id
|
||||||
|
print(f"\nOrden: {order.name}")
|
||||||
|
print(f" Paciente: {patient.name}")
|
||||||
|
print(f" ID Paciente: {patient.id}")
|
||||||
|
print(f" Fecha de nacimiento: {patient.birthdate_date}")
|
||||||
|
print(f" Edad: {patient.age if patient.birthdate_date else 'N/A'}")
|
||||||
|
print(f" Género: {patient.gender or 'No especificado'}")
|
||||||
|
print(f" ¿Tiene campo age?: {hasattr(patient, 'age')}")
|
||||||
|
print(f" ¿Tiene campo birthdate_date?: {hasattr(patient, 'birthdate_date')}")
|
||||||
|
|
||||||
|
# Verificar si podemos acceder a los campos
|
||||||
|
try:
|
||||||
|
age_value = patient.age
|
||||||
|
print(f" Valor de age: {age_value}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Error al acceder a age: {str(e)}")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Configuración
|
||||||
|
db_name = 'lims_demo'
|
||||||
|
|
||||||
|
# Conectar a Odoo
|
||||||
|
odoo.tools.config.parse_config(['--database', db_name])
|
||||||
|
|
||||||
|
# Obtener el registro de la base de datos
|
||||||
|
registry = odoo.registry(db_name)
|
||||||
|
|
||||||
|
# Crear cursor y environment
|
||||||
|
with registry.cursor() as cr:
|
||||||
|
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Verificar datos
|
||||||
|
check_patient_data(env)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n❌ Error: {str(e)}")
|
226
test/create_validated_tests.py
Normal file
226
test/create_validated_tests.py
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Script para crear órdenes de laboratorio con resultados validados para probar el reporte PDF
|
||||||
|
"""
|
||||||
|
|
||||||
|
import odoo
|
||||||
|
import logging
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import random
|
||||||
|
from odoo import fields
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def create_validated_lab_order(env):
|
||||||
|
"""Crear una orden de laboratorio con resultados completos y validados"""
|
||||||
|
|
||||||
|
# Obtener o crear paciente y doctor demo
|
||||||
|
patient = env['res.partner'].search([('is_patient', '=', True)], limit=1)
|
||||||
|
if not patient:
|
||||||
|
patient = env['res.partner'].create({
|
||||||
|
'name': 'Juan Pérez (Demo)',
|
||||||
|
'is_patient': True,
|
||||||
|
'birthdate_date': '1980-05-15',
|
||||||
|
'gender': 'male',
|
||||||
|
'vat': '12345678',
|
||||||
|
})
|
||||||
|
|
||||||
|
doctor = env['res.partner'].search([('is_doctor', '=', True)], limit=1)
|
||||||
|
if not doctor:
|
||||||
|
doctor = env['res.partner'].create({
|
||||||
|
'name': 'Dr. María García (Demo)',
|
||||||
|
'is_doctor': True,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Usar usuario admin como técnico y validador
|
||||||
|
admin_user = env.ref('base.user_admin')
|
||||||
|
technician = admin_user
|
||||||
|
validator = admin_user
|
||||||
|
|
||||||
|
# Obtener análisis disponibles
|
||||||
|
hemograma = env.ref('lims_management.analysis_hemograma')
|
||||||
|
glucosa = env.ref('lims_management.analysis_glucosa')
|
||||||
|
perfil_lipidico = env.ref('lims_management.analysis_perfil_lipidico')
|
||||||
|
|
||||||
|
# Crear orden de laboratorio
|
||||||
|
order = env['sale.order'].create({
|
||||||
|
'partner_id': patient.id,
|
||||||
|
'doctor_id': doctor.id,
|
||||||
|
'is_lab_request': True,
|
||||||
|
'lab_notes': 'Paciente en ayunas de 12 horas. Control de rutina anual.',
|
||||||
|
'order_line': [
|
||||||
|
(0, 0, {
|
||||||
|
'product_id': hemograma.product_variant_id.id,
|
||||||
|
'product_uom_qty': 1,
|
||||||
|
'price_unit': hemograma.list_price,
|
||||||
|
}),
|
||||||
|
(0, 0, {
|
||||||
|
'product_id': glucosa.product_variant_id.id,
|
||||||
|
'product_uom_qty': 1,
|
||||||
|
'price_unit': glucosa.list_price,
|
||||||
|
}),
|
||||||
|
(0, 0, {
|
||||||
|
'product_id': perfil_lipidico.product_variant_id.id,
|
||||||
|
'product_uom_qty': 1,
|
||||||
|
'price_unit': perfil_lipidico.list_price,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
# Confirmar orden (genera muestras y pruebas automáticamente)
|
||||||
|
order.action_confirm()
|
||||||
|
|
||||||
|
_logger.info(f"Orden creada: {order.name}")
|
||||||
|
|
||||||
|
# Primero, marcar todas las muestras como recolectadas
|
||||||
|
for sample in order.generated_sample_ids:
|
||||||
|
sample.write({'state': 'collected'})
|
||||||
|
|
||||||
|
# Procesar cada prueba
|
||||||
|
for test in order.lab_test_ids:
|
||||||
|
# Asignar técnico
|
||||||
|
test.write({
|
||||||
|
'technician_id': technician.id
|
||||||
|
})
|
||||||
|
|
||||||
|
# Generar resultados si no existen
|
||||||
|
if not test.result_ids:
|
||||||
|
# Usar sudo para evitar restricciones de permisos y llamar método interno
|
||||||
|
test.sudo()._generate_test_results()
|
||||||
|
_logger.info(f"Generados {len(test.result_ids)} resultados para prueba {test.name}")
|
||||||
|
|
||||||
|
# Cambiar estado a in_process
|
||||||
|
test.write({'state': 'in_process'})
|
||||||
|
|
||||||
|
# Ingresar resultados según el tipo de análisis
|
||||||
|
for result in test.result_ids:
|
||||||
|
parameter = result.parameter_id
|
||||||
|
vals = {}
|
||||||
|
|
||||||
|
# Solo procesar parámetros numéricos
|
||||||
|
if parameter.value_type == 'numeric':
|
||||||
|
# Generar valores basados en el parámetro
|
||||||
|
if parameter.code == 'HGB': # Hemoglobina
|
||||||
|
vals['value_numeric'] = random.uniform(11.0, 16.5) # Algunos fuera de rango
|
||||||
|
elif parameter.code == 'HCT': # Hematocrito
|
||||||
|
vals['value_numeric'] = random.uniform(35.0, 48.0)
|
||||||
|
elif parameter.code == 'WBC': # Leucocitos
|
||||||
|
vals['value_numeric'] = random.uniform(3.5, 11.0) # Algunos fuera de rango
|
||||||
|
elif parameter.code == 'PLT': # Plaquetas
|
||||||
|
vals['value_numeric'] = random.uniform(140, 450)
|
||||||
|
elif parameter.code == 'RBC': # Eritrocitos
|
||||||
|
vals['value_numeric'] = random.uniform(3.8, 5.8)
|
||||||
|
elif parameter.code == 'GLU': # Glucosa
|
||||||
|
vals['value_numeric'] = random.uniform(65, 125) # Algunos elevados
|
||||||
|
elif parameter.code == 'CHOL': # Colesterol
|
||||||
|
vals['value_numeric'] = random.uniform(150, 240) # Algunos elevados
|
||||||
|
elif parameter.code == 'TRIG': # Triglicéridos
|
||||||
|
vals['value_numeric'] = random.uniform(40, 200) # Algunos elevados
|
||||||
|
elif parameter.code == 'HDL': # HDL
|
||||||
|
vals['value_numeric'] = random.uniform(35, 65)
|
||||||
|
elif parameter.code == 'LDL': # LDL
|
||||||
|
vals['value_numeric'] = random.uniform(70, 160)
|
||||||
|
else:
|
||||||
|
# Valor genérico para otros parámetros numéricos
|
||||||
|
if result.applicable_range_id:
|
||||||
|
# Generar valor cercano al rango normal
|
||||||
|
min_val = result.applicable_range_id.normal_min or 0
|
||||||
|
max_val = result.applicable_range_id.normal_max or 100
|
||||||
|
vals['value_numeric'] = random.uniform(min_val * 0.8, max_val * 1.2)
|
||||||
|
else:
|
||||||
|
# Valor por defecto si no hay rango
|
||||||
|
vals['value_numeric'] = random.uniform(1, 100)
|
||||||
|
|
||||||
|
_logger.info(f"Asignando valor numérico {vals.get('value_numeric', 0):.2f} a {parameter.name} ({parameter.code})")
|
||||||
|
elif parameter.value_type == 'text':
|
||||||
|
vals['value_text'] = "Normal"
|
||||||
|
elif parameter.value_type == 'selection':
|
||||||
|
vals['value_selection'] = "normal"
|
||||||
|
elif parameter.value_type == 'boolean':
|
||||||
|
vals['value_boolean'] = False
|
||||||
|
|
||||||
|
# Escribir valores
|
||||||
|
if vals:
|
||||||
|
result.write(vals)
|
||||||
|
|
||||||
|
# Agregar notas si está fuera de rango
|
||||||
|
if result.is_out_of_range:
|
||||||
|
if result.is_critical:
|
||||||
|
result.notes = "Valor crítico. Se recomienda repetir el análisis y consultar con el médico de inmediato."
|
||||||
|
else:
|
||||||
|
result.notes = "Valor ligeramente alterado. Se sugiere control en 3 meses."
|
||||||
|
|
||||||
|
# Marcar resultados como ingresados
|
||||||
|
test.write({'state': 'result_entered'})
|
||||||
|
|
||||||
|
# Agregar comentarios a algunas pruebas
|
||||||
|
if test.product_id == hemograma:
|
||||||
|
test.notes = "Serie roja dentro de parámetros normales. Serie blanca con ligera leucocitosis."
|
||||||
|
elif test.product_id == perfil_lipidico:
|
||||||
|
test.notes = "Se recomienda dieta baja en grasas y control en 3 meses."
|
||||||
|
|
||||||
|
# Validar todas las pruebas
|
||||||
|
for test in order.lab_test_ids:
|
||||||
|
test.write({
|
||||||
|
'state': 'validated',
|
||||||
|
'validator_id': validator.id,
|
||||||
|
'validation_date': fields.Datetime.now()
|
||||||
|
})
|
||||||
|
|
||||||
|
_logger.info(f"Todas las pruebas validadas para orden {order.name}")
|
||||||
|
|
||||||
|
return order
|
||||||
|
|
||||||
|
|
||||||
|
def create_multiple_test_orders(env, count=3):
|
||||||
|
"""Crear múltiples órdenes con diferentes escenarios"""
|
||||||
|
orders = env['sale.order']
|
||||||
|
|
||||||
|
for i in range(count):
|
||||||
|
_logger.info(f"Creando orden {i+1} de {count}")
|
||||||
|
order = create_validated_lab_order(env)
|
||||||
|
orders |= order
|
||||||
|
|
||||||
|
# Variar las observaciones
|
||||||
|
if i == 1:
|
||||||
|
order.lab_notes = "Paciente diabético tipo 2. Control mensual de glucemia."
|
||||||
|
elif i == 2:
|
||||||
|
order.lab_notes = "Control post-operatorio. Paciente con antecedentes de anemia."
|
||||||
|
|
||||||
|
return orders
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Configuración
|
||||||
|
db_name = 'lims_demo'
|
||||||
|
|
||||||
|
# Conectar a Odoo
|
||||||
|
odoo.tools.config.parse_config(['--database', db_name])
|
||||||
|
|
||||||
|
# Obtener el registro de la base de datos
|
||||||
|
registry = odoo.registry(db_name)
|
||||||
|
|
||||||
|
# Crear cursor y environment
|
||||||
|
with registry.cursor() as cr:
|
||||||
|
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Crear órdenes con resultados validados
|
||||||
|
orders = create_multiple_test_orders(env, count=2)
|
||||||
|
|
||||||
|
# Confirmar cambios
|
||||||
|
cr.commit()
|
||||||
|
|
||||||
|
print(f"\n✅ Se crearon {len(orders)} órdenes de laboratorio con resultados validados:")
|
||||||
|
for order in orders:
|
||||||
|
print(f" - {order.name}: {order.partner_id.name}")
|
||||||
|
print(f" Pruebas: {', '.join(test.product_id.name for test in order.lab_test_ids)}")
|
||||||
|
|
||||||
|
print("\n📋 Ahora puedes probar el botón 'Imprimir Informe de Resultados' en estas órdenes.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
cr.rollback()
|
||||||
|
print(f"\n❌ Error: {str(e)}")
|
||||||
|
_logger.error(f"Error creando datos demo: {str(e)}", exc_info=True)
|
65
test/update_patient_birthdate.py
Normal file
65
test/update_patient_birthdate.py
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Script para actualizar la fecha de nacimiento de los pacientes existentes
|
||||||
|
"""
|
||||||
|
|
||||||
|
import odoo
|
||||||
|
from datetime import date, timedelta
|
||||||
|
import random
|
||||||
|
|
||||||
|
def update_patient_birthdates(env):
|
||||||
|
"""Actualizar fechas de nacimiento de pacientes existentes"""
|
||||||
|
|
||||||
|
# Buscar pacientes sin fecha de nacimiento
|
||||||
|
patients = env['res.partner'].search([
|
||||||
|
('is_patient', '=', True),
|
||||||
|
('birthdate_date', '=', False)
|
||||||
|
])
|
||||||
|
|
||||||
|
if patients:
|
||||||
|
print(f"Actualizando {len(patients)} pacientes sin fecha de nacimiento...")
|
||||||
|
|
||||||
|
for patient in patients:
|
||||||
|
# Generar una edad aleatoria entre 20 y 70 años
|
||||||
|
age_years = random.randint(20, 70)
|
||||||
|
birthdate = date.today() - timedelta(days=age_years * 365 + random.randint(0, 364))
|
||||||
|
|
||||||
|
# Actualizar fecha de nacimiento
|
||||||
|
patient.write({
|
||||||
|
'birthdate_date': birthdate.strftime('%Y-%m-%d')
|
||||||
|
})
|
||||||
|
|
||||||
|
print(f" - {patient.name}: {birthdate.strftime('%Y-%m-%d')} ({age_years} años)")
|
||||||
|
else:
|
||||||
|
print("Todos los pacientes ya tienen fecha de nacimiento.")
|
||||||
|
|
||||||
|
return patients
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Configuración
|
||||||
|
db_name = 'lims_demo'
|
||||||
|
|
||||||
|
# Conectar a Odoo
|
||||||
|
odoo.tools.config.parse_config(['--database', db_name])
|
||||||
|
|
||||||
|
# Obtener el registro de la base de datos
|
||||||
|
registry = odoo.registry(db_name)
|
||||||
|
|
||||||
|
# Crear cursor y environment
|
||||||
|
with registry.cursor() as cr:
|
||||||
|
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Actualizar pacientes
|
||||||
|
patients = update_patient_birthdates(env)
|
||||||
|
|
||||||
|
# Confirmar cambios
|
||||||
|
cr.commit()
|
||||||
|
|
||||||
|
if patients:
|
||||||
|
print(f"\n✅ Se actualizaron {len(patients)} pacientes con fecha de nacimiento.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
cr.rollback()
|
||||||
|
print(f"\n❌ Error: {str(e)}")
|
Loading…
Reference in New Issue
Block a user