diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index b8988b6..e9b9f67 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -10,7 +10,11 @@
"Bash(git stash:*)",
"Bash(git commit:*)",
"Bash(docker-compose up:*)",
- "Bash(docker:*)"
+ "Bash(docker:*)",
+ "Bash(curl:*)",
+ "Bash(mkdir:*)",
+ "Bash(mv:*)",
+ "Bash(rm:*)"
],
"deny": []
}
diff --git a/CLAUDE.md b/CLAUDE.md
index c735b10..064b638 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -27,6 +27,8 @@ docker-compose logs odoo_init
docker-compose down -v
```
+**IMPORTANT**: Odoo initialization takes approximately 5 minutes. When using docker-compose commands, set timeout to 5 minutes (300000ms) to avoid premature timeouts.
+
### Instance Persistence Policy
After successful installation/update, the instance must remain active for user validation. Do NOT stop the instance until user explicitly confirms testing is complete.
@@ -188,10 +190,16 @@ STATE_CANCELLED = 'cancelled'
- Use for basic records without complex dependencies
- Place in `lims_management/demo/`
- Use `noupdate="1"` to prevent reloading
+- **IMPORTANT**: Do NOT create sale.order records in XML demo files - use Python scripts instead
#### Python Scripts (Complex Data)
For data with dependencies or business logic:
+#### Test Scripts
+- **IMPORTANT**: Always create test scripts inside the `test/` folder within the project directory
+- Example: `test/test_sample_generation.py`
+- This ensures scripts are properly organized and accessible
+
1. Create script:
```python
import odoo
diff --git a/create_lab_requests.py b/create_lab_requests.py
index 227645e..226bc25 100644
--- a/create_lab_requests.py
+++ b/create_lab_requests.py
@@ -16,34 +16,59 @@ def create_lab_requests(cr):
except Exception:
pass
- # Get patient and doctor
- patient1 = env.ref('lims_management.demo_patient_1')
- doctor1 = env.ref('lims_management.demo_doctor_1')
- patient2 = env.ref('lims_management.demo_patient_2')
+ try:
+ # Get patients and doctors - using search instead of ref to be more robust
+ patient1 = env['res.partner'].search([('patient_identifier', '=', 'P-A87B01'), ('is_patient', '=', True)], limit=1)
+ patient2 = env['res.partner'].search([('patient_identifier', '=', 'P-C45D02'), ('is_patient', '=', True)], limit=1)
+ doctor1 = env['res.partner'].search([('doctor_license', '=', 'L-98765'), ('is_doctor', '=', True)], limit=1)
+
+ if not patient1:
+ print("Warning: Patient 1 not found, skipping lab requests creation")
+ return
+
+ # Get analysis products - using search instead of ref
+ hemograma = env['product.template'].search([('name', '=', 'Hemograma Completo'), ('is_analysis', '=', True)], limit=1)
+ perfil_lipidico = env['product.template'].search([('name', '=', 'Perfil Lipídico'), ('is_analysis', '=', True)], limit=1)
+ glucosa = env['product.template'].search([('name', '=', 'Glucosa en Sangre'), ('is_analysis', '=', True)], limit=1)
+ urocultivo = env['product.template'].search([('name', '=', 'Urocultivo'), ('is_analysis', '=', True)], limit=1)
+
+ # Create Lab Request 1 - Multiple analyses with same sample type
+ if patient1 and hemograma and perfil_lipidico:
+ order1 = env['sale.order'].create({
+ 'partner_id': patient1.id,
+ 'doctor_id': doctor1.id if doctor1 else False,
+ 'is_lab_request': True,
+ 'order_line': [
+ (0, 0, {'product_id': hemograma.product_variant_id.id, 'product_uom_qty': 1}),
+ (0, 0, {'product_id': perfil_lipidico.product_variant_id.id, 'product_uom_qty': 1})
+ ]
+ })
+ print(f"Created Lab Order 1: {order1.name}")
+
+ # Confirm the order to test automatic sample generation
+ order1.action_confirm()
+ print(f"Confirmed Lab Order 1. Generated samples: {len(order1.generated_sample_ids)}")
- # Get analysis products
- hemograma = env.ref('lims_management.analysis_hemograma')
- perfil_lipidico = env.ref('lims_management.analysis_perfil_lipidico')
-
- # Create Lab Request 1
- env['sale.order'].create({
- 'partner_id': patient1.id,
- 'doctor_id': doctor1.id,
- 'is_lab_request': True,
- 'order_line': [
- (0, 0, {'product_id': hemograma.product_variant_id.id, 'product_uom_qty': 1}),
- (0, 0, {'product_id': perfil_lipidico.product_variant_id.id, 'product_uom_qty': 1})
- ]
- })
-
- # Create Lab Request 2
- env['sale.order'].create({
- 'partner_id': patient2.id,
- 'is_lab_request': True,
- 'order_line': [
- (0, 0, {'product_id': hemograma.product_variant_id.id, 'product_uom_qty': 1})
- ]
- })
+ # Create Lab Request 2 - Different sample types
+ if patient2 and glucosa and urocultivo:
+ order2 = env['sale.order'].create({
+ 'partner_id': patient2.id,
+ 'is_lab_request': True,
+ 'order_line': [
+ (0, 0, {'product_id': glucosa.product_variant_id.id, 'product_uom_qty': 1}),
+ (0, 0, {'product_id': urocultivo.product_variant_id.id, 'product_uom_qty': 1})
+ ]
+ })
+ print(f"Created Lab Order 2: {order2.name}")
+
+ # Confirm to test automatic sample generation with different types
+ order2.action_confirm()
+ print(f"Confirmed Lab Order 2. Generated samples: {len(order2.generated_sample_ids)}")
+
+ except Exception as e:
+ print(f"Error creating lab requests: {str(e)}")
+ import traceback
+ traceback.print_exc()
if __name__ == '__main__':
db_name = 'lims_demo'
diff --git a/documents/plans/ISSUE32_PLAN.md b/documents/plans/ISSUE32_PLAN.md
index a3a1e03..dbf1845 100644
--- a/documents/plans/ISSUE32_PLAN.md
+++ b/documents/plans/ISSUE32_PLAN.md
@@ -153,14 +153,14 @@ graph TD
## Criterios de Aceptación
-1. [ ] Al confirmar una orden de laboratorio, se generan automáticamente las muestras necesarias
-2. [ ] Los análisis que requieren el mismo tipo de muestra se agrupan en un solo contenedor
-3. [ ] Cada muestra tiene un código de barras único
-4. [ ] Se muestra claramente qué muestras fueron generadas para cada orden
-5. [ ] Se manejan adecuadamente los análisis sin tipo de muestra definido
-6. [ ] El sistema registra un log de la generación para auditoría
-7. [ ] La funcionalidad se puede deshabilitar si es necesario
-8. [ ] No afecta el rendimiento de confirmación de órdenes regulares
+1. [x] Al confirmar una orden de laboratorio, se generan automáticamente las muestras necesarias
+2. [x] Los análisis que requieren el mismo tipo de muestra se agrupan en un solo contenedor
+3. [x] Cada muestra tiene un código de barras único
+4. [x] Se muestra claramente qué muestras fueron generadas para cada orden
+5. [x] Se manejan adecuadamente los análisis sin tipo de muestra definido
+6. [x] El sistema registra un log de la generación para auditoría
+7. [ ] La funcionalidad se puede deshabilitar si es necesario (opcional - no implementado)
+8. [x] No afecta el rendimiento de confirmación de órdenes regulares
## Estimación de Tiempo
diff --git a/init_odoo.py b/init_odoo.py
index d1c5a08..7ab37ef 100644
--- a/init_odoo.py
+++ b/init_odoo.py
@@ -35,6 +35,7 @@ odoo_command = [
"-c", ODOO_CONF,
"-d", DB_NAME,
"-i", MODULES_TO_INSTALL,
+ "--load-language", "es_ES",
"--stop-after-init"
]
diff --git a/lims_management/demo/z_automatic_generation_demo.xml b/lims_management/demo/z_automatic_generation_demo.xml
index bba7732..dc5fdf6 100644
--- a/lims_management/demo/z_automatic_generation_demo.xml
+++ b/lims_management/demo/z_automatic_generation_demo.xml
@@ -1,91 +1,7 @@
-
-
-
-
-
-
- draft
-
-
-
-
-
-
- 1
-
-
-
-
-
- 1
-
-
-
-
-
-
-
- draft
-
-
-
-
-
-
- 1
-
-
-
-
-
- 1
-
-
-
-
-
- 1
-
-
-
-
-
-
- draft
-
-
-
-
-
- 1
-
-
-
-
-
- 1
-
-
-
-
-
-
-
- draft
-
-
-
-
-
- 1
-
-
-
-
-
- 1
-
-
+
\ No newline at end of file
diff --git a/lims_management/models/__pycache__/product.cpython-312.pyc b/lims_management/models/__pycache__/product.cpython-312.pyc
index 7c7c822..59f71a2 100644
Binary files a/lims_management/models/__pycache__/product.cpython-312.pyc and b/lims_management/models/__pycache__/product.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 560f98c..8e54302 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/models/__pycache__/stock_lot.cpython-312.pyc b/lims_management/models/__pycache__/stock_lot.cpython-312.pyc
index ad21e4d..051c734 100644
Binary files a/lims_management/models/__pycache__/stock_lot.cpython-312.pyc and b/lims_management/models/__pycache__/stock_lot.cpython-312.pyc differ
diff --git a/lims_management/models/product.py b/lims_management/models/product.py
index c3eb347..0f23f1c 100644
--- a/lims_management/models/product.py
+++ b/lims_management/models/product.py
@@ -29,8 +29,8 @@ class ProductTemplate(models.Model):
)
is_sample_type = fields.Boolean(
- string="Is a Sample Type",
- help="Check if this product represents a type of laboratory sample container."
+ string="Es Tipo de Muestra",
+ help="Marcar si este producto representa un tipo de contenedor de muestra de laboratorio."
)
required_sample_type_id = fields.Many2one(
diff --git a/lims_management/models/sale_order.py b/lims_management/models/sale_order.py
index e3cc54b..4f2227f 100644
--- a/lims_management/models/sale_order.py
+++ b/lims_management/models/sale_order.py
@@ -9,17 +9,17 @@ class SaleOrder(models.Model):
_inherit = 'sale.order'
is_lab_request = fields.Boolean(
- string="Is a Laboratory Request",
+ string="Es Orden de Laboratorio",
default=False,
copy=False,
- help="Technical field to identify if the sale order is a laboratory request."
+ help="Campo técnico para identificar si la orden de venta es una solicitud de laboratorio."
)
doctor_id = fields.Many2one(
'res.partner',
- string="Referring Doctor",
+ string="Médico Referente",
domain="[('is_doctor', '=', True)]",
- help="The doctor who referred the patient for this laboratory request."
+ help="El médico que refirió al paciente para esta solicitud de laboratorio."
)
generated_sample_ids = fields.Many2many(
@@ -30,7 +30,7 @@ class SaleOrder(models.Model):
string='Muestras Generadas',
domain="[('is_lab_sample', '=', True)]",
readonly=True,
- help="Laboratory samples automatically generated when this order was confirmed"
+ help="Muestras de laboratorio generadas automáticamente cuando se confirmó esta orden"
)
def action_confirm(self):
diff --git a/lims_management/models/stock_lot.py b/lims_management/models/stock_lot.py
index 5de18a4..9ec953e 100644
--- a/lims_management/models/stock_lot.py
+++ b/lims_management/models/stock_lot.py
@@ -6,7 +6,7 @@ import random
class StockLot(models.Model):
_inherit = 'stock.lot'
- is_lab_sample = fields.Boolean(string='Is a Laboratory Sample')
+ is_lab_sample = fields.Boolean(string='Es Muestra de Laboratorio')
barcode = fields.Char(
string='Código de Barras',
@@ -18,25 +18,25 @@ class StockLot(models.Model):
patient_id = fields.Many2one(
'res.partner',
- string='Patient',
+ string='Paciente',
domain="[('is_patient', '=', True)]"
)
request_id = fields.Many2one(
'sale.order',
- string='Lab Request',
+ string='Orden de Laboratorio',
domain="[('is_lab_request', '=', True)]"
)
- collection_date = fields.Datetime(string='Collection Date')
+ collection_date = fields.Datetime(string='Fecha de Recolección')
container_type = fields.Selection([
- ('serum_tube', 'Serum Tube'),
- ('edta_tube', 'EDTA Tube'),
- ('swab', 'Swab'),
- ('urine', 'Urine Container'),
- ('other', 'Other')
- ], string='Container Type (Legacy)', help='Deprecated field, use sample_type_product_id instead')
+ ('serum_tube', 'Tubo de Suero'),
+ ('edta_tube', 'Tubo EDTA'),
+ ('swab', 'Hisopo'),
+ ('urine', 'Contenedor de Orina'),
+ ('other', 'Otro')
+ ], string='Tipo de Contenedor (Obsoleto)', help='Campo obsoleto, use sample_type_product_id en su lugar')
sample_type_product_id = fields.Many2one(
'product.template',
@@ -47,7 +47,7 @@ class StockLot(models.Model):
collector_id = fields.Many2one(
'res.users',
- string='Collected by',
+ string='Recolectado por',
default=lambda self: self.env.user
)
@@ -83,19 +83,28 @@ class StockLot(models.Model):
('disposed', 'Desechada')
], string='Estado', default='collected', tracking=True)
+ def action_collect(self):
+ """Mark sample as collected"""
+ self.write({'state': 'collected', 'collection_date': fields.Datetime.now()})
+
def action_receive(self):
+ """Mark sample as received in laboratory"""
self.write({'state': 'received'})
def action_start_analysis(self):
+ """Start analysis process"""
self.write({'state': 'in_process'})
def action_complete_analysis(self):
+ """Mark analysis as completed"""
self.write({'state': 'analyzed'})
def action_store(self):
+ """Store the sample"""
self.write({'state': 'stored'})
def action_dispose(self):
+ """Dispose of the sample"""
self.write({'state': 'disposed'})
@api.onchange('sample_type_product_id')
diff --git a/lims_management/views/sale_order_views.xml b/lims_management/views/sale_order_views.xml
index 9ae923f..9c0945f 100644
--- a/lims_management/views/sale_order_views.xml
+++ b/lims_management/views/sale_order_views.xml
@@ -30,7 +30,7 @@
-
@@ -56,7 +56,7 @@
-
+
diff --git a/lims_management/views/stock_lot_views.xml b/lims_management/views/stock_lot_views.xml
index 6f5975c..ec3f47d 100644
--- a/lims_management/views/stock_lot_views.xml
+++ b/lims_management/views/stock_lot_views.xml
@@ -7,15 +7,15 @@
lab.sample.list
stock.lot
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
@@ -25,14 +25,15 @@
lab.sample.form
stock.lot
-