\"feat(#6): Implementar solicitudes de laboratorio y corregir datos de demostracion\"

This commit is contained in:
Luis Ernesto Portillo Zaldivar 2025-07-14 02:29:38 -06:00
parent 638d9b130a
commit f56b60ad15
25 changed files with 1297 additions and 5 deletions

View File

@ -240,4 +240,87 @@ Esto envía la cadena `"{'default_categ_id': ref(...)}"` al cliente, que no pued
}"/>
```
Al usar `eval`, Odoo ejecuta la expresión en el servidor, reemplaza `ref(...)` por el ID numérico correspondiente, y envía un diccionario JSON válido al cliente.
### Herencia de Vistas y XPath
Al heredar vistas para modificarlas, es crucial que las expresiones `XPath` sean precisas. Un error común es hacer referencia a campos o estructuras que han cambiado en la nueva versión de Odoo.
**Ejemplo de Error:**
Al intentar modificar las líneas de una orden de venta (`sale.order`), una expresión XPath que funcionaba en versiones anteriores puede fallar en Odoo 18.
**Expresión Incorrecta (para Odoo < 18):**
```xml
<xpath expr="//field[@name='order_line']/tree/field[@name='product_template_id']" position="attributes">
<attribute name="domain">[('is_analysis', '=', True)]</attribute>
</xpath>
```
Esta expresión falla por dos razones:
1. La vista de líneas ahora usa `<list>` en lugar de `<tree>`.
2. El campo del producto en las líneas de venta es `product_id`, no `product_template_id`.
**Expresión Correcta (para Odoo 18):**
Para hacer la expresión más robusta y compatible, se puede usar `//` para buscar en cualquier nivel descendiente.
```xml
<xpath expr="//field[@name='order_line']//field[@name='product_id']" position="attributes">
<attribute name="domain">[('is_analysis', '=', True)]</attribute>
</xpath>
```
Esta expresión busca el campo `product_id` dentro del campo `order_line`, sin importar si está dentro de una etiqueta `<list>` o `<tree>`, haciendo la herencia más resistente a cambios menores de estructura.
---
## Consultar la Base de Datos con un Script
Interactuar con la base de datos directamente usando `psql` a través de `docker-compose exec` puede ser complicado debido a la forma en que el shell maneja las comillas. Una alternativa más robusta y confiable es utilizar un script de Python que aproveche el ORM de Odoo.
### Procedimiento
1. **Crear un Script de Python:**
Crea un script que se conecte a la base de datos y ejecute la consulta deseada.
**Ejemplo (`verify_products.py`):**
```python
import odoo
import json
def verify_lab_order_products(cr):
cr.execute("""
SELECT
so.name AS order_name,
sol.id AS line_id,
pt.name->>'en_US' AS product_name,
pt.is_analysis
FROM
sale_order so
JOIN
sale_order_line sol ON so.id = sol.order_id
JOIN
product_product pp ON sol.product_id = pp.id
JOIN
product_template pt ON pp.product_tmpl_id = pt.id
WHERE
so.is_lab_request = TRUE;
""")
return cr.fetchall()
if __name__ == '__main__':
db_name = 'lims_demo'
registry = odoo.registry(db_name)
with registry.cursor() as cr:
results = verify_lab_order_products(cr)
print(json.dumps(results, indent=4))
```
2. **Copiar el Script al Contenedor:**
Usa el comando `docker cp` para copiar el script al contenedor de Odoo.
```bash
docker cp verify_products.py lims_odoo:/tmp/verify_products.py
```
3. **Ejecutar el Script:**
Ejecuta el script dentro del contenedor usando `docker-compose exec`.
```bash
docker-compose exec odoo python3 /tmp/verify_products.py
```
Este método evita los problemas de entrecomillado y permite ejecutar consultas complejas de manera confiable.

53
create_lab_requests.py Normal file
View File

@ -0,0 +1,53 @@
import odoo
import json
def create_lab_requests(cr):
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
# Delete unwanted demo sale orders
unwanted_orders = env['sale.order'].search([('name', 'in', ['S00001', 'S00002', 'S00003', 'S00004', 'S00005', 'S00006', 'S00007', 'S00008', 'S00009', 'S00010', 'S00011', 'S00012', 'S00013', 'S00014', 'S00015', 'S00016', 'S00017', 'S00018', 'S00019', 'S00020', 'S00021', 'S00022'])])
for order in unwanted_orders:
try:
order.action_cancel()
except Exception:
pass
try:
unwanted_orders.unlink()
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')
# 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})
]
})
if __name__ == '__main__':
db_name = 'lims_demo'
registry = odoo.registry(db_name)
with registry.cursor() as cr:
create_lab_requests(cr)
cr.commit()

View File

@ -24,6 +24,7 @@ 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
command: ["/usr/bin/python3", "/app/init_odoo.py"]
environment:
HOST: db

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,169 @@
{
"account_tag_ids": [],
"active": true,
"activity_date_deadline": false,
"activity_exception_decoration": false,
"activity_exception_icon": false,
"activity_ids": [],
"activity_state": false,
"activity_summary": false,
"activity_type_icon": false,
"activity_type_id": false,
"activity_user_id": false,
"analysis_type": false,
"attribute_line_ids": [],
"barcode": false,
"can_image_1024_be_zoomed": false,
"categ_id": [
10,
"All / Home Construction"
],
"color": 0,
"combo_ids": [],
"company_id": false,
"cost_currency_id": [
1,
"USD"
],
"cost_method": "standard",
"create_date": "2025-07-14 07:23:12",
"create_uid": [
1,
"OdooBot"
],
"currency_id": [
1,
"USD"
],
"default_code": false,
"description": false,
"description_picking": false,
"description_pickingin": false,
"description_pickingout": false,
"description_purchase": false,
"description_sale": false,
"display_name": "Furniture Assembly",
"expense_policy": "no",
"fiscal_country_codes": "US",
"has_available_route_ids": false,
"has_configurable_attributes": false,
"has_message": true,
"id": 29,
"image_1024": false,
"image_128": false,
"image_1920": false,
"image_256": false,
"image_512": false,
"incoming_qty": 0,
"invoice_policy": "order",
"is_analysis": false,
"is_favorite": false,
"is_product_variant": false,
"is_storable": false,
"list_price": 2000,
"location_id": false,
"lot_valuated": false,
"message_attachment_count": 0,
"message_follower_ids": [],
"message_has_error": false,
"message_has_error_counter": 0,
"message_has_sms_error": false,
"message_ids": [
188,
114
],
"message_is_follower": false,
"message_needaction": false,
"message_needaction_counter": 0,
"message_partner_ids": [],
"my_activity_date_deadline": false,
"name": "Furniture Assembly",
"nbr_moves_in": 0,
"nbr_moves_out": 0,
"nbr_reordering_rules": 0,
"optional_product_ids": [],
"outgoing_qty": 0,
"packaging_ids": [],
"pricelist_item_count": 0,
"product_document_count": 0,
"product_document_ids": [],
"product_properties": [],
"product_tag_ids": [],
"product_tooltip": "Invoice ordered quantities as soon as this service is sold.",
"product_variant_count": 1,
"product_variant_id": [
38,
"Furniture Assembly"
],
"product_variant_ids": [
38
],
"property_account_expense_id": false,
"property_account_income_id": false,
"property_stock_inventory": [
14,
"Virtual Locations/Inventory adjustment"
],
"property_stock_production": [
15,
"Virtual Locations/Production"
],
"purchase_ok": true,
"qty_available": 0,
"reordering_max_qty": 0,
"reordering_min_qty": 0,
"responsible_id": [
1,
"OdooBot"
],
"route_from_categ_ids": [],
"route_ids": [],
"sale_delay": 0,
"sale_line_warn": "no-message",
"sale_line_warn_msg": false,
"sale_ok": true,
"sales_count": 0,
"seller_ids": [],
"sequence": 1,
"service_tracking": "no",
"service_type": "manual",
"show_forecasted_qty_status_button": false,
"show_on_hand_qty_status_button": false,
"standard_price": 2500,
"supplier_taxes_id": [],
"tax_string": " ",
"taxes_id": [],
"technical_specifications": false,
"tracking": "none",
"type": "service",
"uom_category_id": [
3,
"Working Time"
],
"uom_id": [
4,
"Hours"
],
"uom_name": "Hours",
"uom_po_id": [
4,
"Hours"
],
"valid_product_template_attribute_line_ids": [],
"valuation": "manual_periodic",
"value_range_ids": [],
"variant_seller_ids": [],
"virtual_available": 0,
"visible_expense_policy": false,
"volume": 0,
"volume_uom_name": "m³",
"warehouse_id": false,
"website_message_ids": [],
"weight": 0,
"weight_uom_name": "kg",
"write_date": "2025-07-14 07:23:55",
"write_uid": [
1,
"OdooBot"
]
}

158
documents/logs/s00021.json Normal file
View File

@ -0,0 +1,158 @@
{
"access_token": false,
"access_url": "/my/orders/21",
"access_warning": "",
"activity_date_deadline": false,
"activity_exception_decoration": false,
"activity_exception_icon": false,
"activity_ids": [],
"activity_state": false,
"activity_summary": false,
"activity_type_icon": false,
"activity_type_id": false,
"activity_user_id": false,
"amount_invoiced": 0,
"amount_paid": 0,
"amount_tax": 0,
"amount_to_invoice": 2589,
"amount_total": 2589,
"amount_undiscounted": 2589,
"amount_untaxed": 2589,
"authorized_transaction_ids": [],
"available_product_document_ids": [
2
],
"campaign_id": false,
"client_order_ref": false,
"commitment_date": false,
"company_id": [
1,
"My Company (San Francisco)"
],
"company_price_include": "tax_excluded",
"country_code": "US",
"create_date": "2025-07-14 07:24:01",
"create_uid": [
1,
"OdooBot"
],
"currency_id": [
1,
"USD"
],
"currency_rate": 1,
"customizable_pdf_form_fields": false,
"date_order": "2025-07-14 07:24:02",
"delivery_count": 0,
"delivery_status": false,
"display_name": "S00021",
"doctor_id": [
46,
"Dr. Luis Herrera"
],
"duplicated_order_ids": [],
"effective_date": false,
"expected_date": "2025-07-14 07:26:23",
"fiscal_position_id": false,
"has_active_pricelist": false,
"has_archived_products": false,
"has_message": true,
"id": 21,
"incoterm": false,
"incoterm_location": false,
"invoice_count": 0,
"invoice_ids": [],
"invoice_status": "no",
"is_expired": false,
"is_lab_request": true,
"is_pdf_quote_builder_available": true,
"journal_id": false,
"json_popover": "{\"popoverTemplate\": \"sale_stock.DelayAlertWidget\", \"late_elements\": []}",
"locked": false,
"medium_id": false,
"message_attachment_count": 0,
"message_follower_ids": [],
"message_has_error": false,
"message_has_error_counter": 0,
"message_has_sms_error": false,
"message_ids": [
310
],
"message_is_follower": false,
"message_needaction": false,
"message_needaction_counter": 0,
"message_partner_ids": [],
"my_activity_date_deadline": false,
"name": "S00021",
"note": false,
"order_line": [
45,
46
],
"origin": false,
"partner_credit_warning": "",
"partner_id": [
44,
"Ana Torres"
],
"partner_invoice_id": [
44,
"Ana Torres"
],
"partner_shipping_id": [
44,
"Ana Torres"
],
"payment_term_id": false,
"pending_email_template_id": false,
"picking_ids": [],
"picking_policy": "direct",
"prepayment_percent": 1,
"pricelist_id": false,
"procurement_group_id": false,
"quotation_document_ids": [],
"reference": false,
"require_payment": true,
"require_signature": true,
"sale_order_option_ids": [],
"sale_order_template_id": false,
"show_json_popover": false,
"show_update_fpos": false,
"show_update_pricelist": false,
"signature": false,
"signed_by": false,
"signed_on": false,
"source_id": false,
"state": "draft",
"tag_ids": [],
"tax_calculation_rounding_method": "round_per_line",
"tax_country_id": [
233,
"United States"
],
"tax_totals": {
"currency_id": 1
},
"team_id": [
1,
"Sales"
],
"terms_type": "plain",
"transaction_ids": [],
"type_name": "Quotation",
"user_id": [
1,
"OdooBot"
],
"validity_date": "2025-08-13",
"warehouse_id": [
1,
"YourCompany"
],
"website_message_ids": [],
"write_date": "2025-07-14 07:24:04",
"write_uid": [
1,
"OdooBot"
]
}

650
documents/metadata.json Normal file
View File

@ -0,0 +1,650 @@
{
"sale_order": [
[
"id",
"integer"
],
[
"message_main_attachment_id",
"integer"
],
[
"access_token",
"character varying"
],
[
"name",
"character varying"
],
[
"origin",
"character varying"
],
[
"client_order_ref",
"character varying"
],
[
"reference",
"character varying"
],
[
"state",
"character varying"
],
[
"date_order",
"timestamp without time zone"
],
[
"validity_date",
"date"
],
[
"commitment_date",
"timestamp without time zone"
],
[
"expected_date",
"timestamp without time zone"
],
[
"user_id",
"integer"
],
[
"partner_id",
"integer"
],
[
"partner_invoice_id",
"integer"
],
[
"partner_shipping_id",
"integer"
],
[
"pricelist_id",
"integer"
],
[
"currency_id",
"integer"
],
[
"analytic_account_id",
"integer"
],
[
"order_line",
"integer"
],
[
"invoice_count",
"integer"
],
[
"invoice_status",
"character varying"
],
[
"note",
"text"
],
[
"amount_untaxed",
"numeric"
],
[
"amount_tax",
"numeric"
],
[
"amount_total",
"numeric"
],
[
"currency_rate",
"numeric"
],
[
"payment_term_id",
"integer"
],
[
"fiscal_position_id",
"integer"
],
[
"company_id",
"integer"
],
[
"team_id",
"integer"
],
[
"signature",
"text"
],
[
"signed_by",
"character varying"
],
[
"signed_on",
"timestamp without time zone"
],
[
"create_uid",
"integer"
],
[
"create_date",
"timestamp without time zone"
],
[
"write_uid",
"integer"
],
[
"write_date",
"timestamp without time zone"
],
[
"sale_order_template_id",
"integer"
],
[
"incoterm",
"integer"
],
[
"picking_policy",
"character varying"
],
[
"warehouse_id",
"integer"
],
[
"procurement_group_id",
"integer"
],
[
"campaign_id",
"integer"
],
[
"medium_id",
"integer"
],
[
"source_id",
"integer"
],
[
"delivery_count",
"integer"
],
[
"is_lab_request",
"boolean"
],
[
"doctor_id",
"integer"
]
],
"sale_order_line": [
[
"id",
"integer"
],
[
"order_id",
"integer"
],
[
"name",
"text"
],
[
"sequence",
"integer"
],
[
"invoice_status",
"character varying"
],
[
"price_unit",
"numeric"
],
[
"price_subtotal",
"numeric"
],
[
"price_tax",
"double precision"
],
[
"price_total",
"numeric"
],
[
"price_reduce",
"numeric"
],
[
"tax_id",
"integer"
],
[
"price_reduce_taxinc",
"numeric"
],
[
"price_reduce_taxexcl",
"numeric"
],
[
"discount",
"numeric"
],
[
"product_id",
"integer"
],
[
"product_template_id",
"integer"
],
[
"product_uom_category_id",
"integer"
],
[
"product_uom",
"integer"
],
[
"product_uom_qty",
"numeric"
],
[
"product_uom_readonly",
"boolean"
],
[
"qty_delivered_method",
"character varying"
],
[
"qty_delivered",
"numeric"
],
[
"qty_delivered_manual",
"numeric"
],
[
"qty_to_invoice",
"numeric"
],
[
"qty_invoiced",
"numeric"
],
[
"untaxed_amount_invoiced",
"numeric"
],
[
"untaxed_amount_to_invoice",
"numeric"
],
[
"salesman_id",
"integer"
],
[
"currency_id",
"integer"
],
[
"company_id",
"integer"
],
[
"order_partner_id",
"integer"
],
[
"is_expense",
"boolean"
],
[
"is_downpayment",
"boolean"
],
[
"state",
"character varying"
],
[
"customer_lead",
"double precision"
],
[
"display_type",
"character varying"
],
[
"create_uid",
"integer"
],
[
"create_date",
"timestamp without time zone"
],
[
"write_uid",
"integer"
],
[
"write_date",
"timestamp without time zone"
],
[
"analytic_distribution",
"jsonb"
],
[
"analytic_line_ids",
"integer"
],
[
"is_service",
"boolean"
],
[
"sale_order_option_ids",
"integer"
],
[
"linked_line_id",
"integer"
],
[
"product_packaging_id",
"integer"
],
[
"product_packaging_qty",
"numeric"
],
[
"product_packaging_description",
"text"
]
],
"product_template": [
[
"id",
"integer"
],
[
"message_main_attachment_id",
"integer"
],
[
"sequence",
"integer"
],
[
"name",
"jsonb"
],
[
"description",
"jsonb"
],
[
"description_purchase",
"text"
],
[
"description_sale",
"jsonb"
],
[
"type",
"character varying"
],
[
"categ_id",
"integer"
],
[
"currency_id",
"integer"
],
[
"cost_currency_id",
"integer"
],
[
"list_price",
"numeric"
],
[
"volume",
"double precision"
],
[
"weight",
"double precision"
],
[
"sale_ok",
"boolean"
],
[
"purchase_ok",
"boolean"
],
[
"uom_id",
"integer"
],
[
"uom_po_id",
"integer"
],
[
"company_id",
"integer"
],
[
"active",
"boolean"
],
[
"color",
"integer"
],
[
"default_code",
"character varying"
],
[
"can_image_1024_be_zoomed",
"boolean"
],
[
"create_uid",
"integer"
],
[
"create_date",
"timestamp without time zone"
],
[
"write_uid",
"integer"
],
[
"write_date",
"timestamp without time zone"
],
[
"service_type",
"character varying"
],
[
"sale_line_warn",
"character varying"
],
[
"sale_line_warn_msg",
"text"
],
[
"expense_policy",
"character varying"
],
[
"visible_expense_policy",
"boolean"
],
[
"invoice_policy",
"character varying"
],
[
"sale_delay",
"double precision"
],
[
"tracking",
"character varying"
],
[
"description_picking",
"text"
],
[
"description_pickingout",
"text"
],
[
"description_pickingin",
"text"
],
[
"responsible_id",
"integer"
],
[
"property_stock_production",
"integer"
],
[
"property_stock_inventory",
"integer"
],
[
"service_tracking",
"character varying"
],
[
"is_analysis",
"boolean"
],
[
"analysis_type",
"character varying"
],
[
"technical_specifications",
"text"
]
],
"product_product": [
[
"id",
"integer"
],
[
"message_main_attachment_id",
"integer"
],
[
"product_tmpl_id",
"integer"
],
[
"default_code",
"character varying"
],
[
"barcode",
"character varying"
],
[
"combination_indices",
"character varying"
],
[
"volume",
"double precision"
],
[
"weight",
"double precision"
],
[
"active",
"boolean"
],
[
"can_be_expensed",
"boolean"
],
[
"create_uid",
"integer"
],
[
"create_date",
"timestamp without time zone"
],
[
"write_uid",
"integer"
],
[
"write_date",
"timestamp without time zone"
],
[
"lst_price",
"numeric"
],
[
"standard_price",
"numeric"
],
[
"property_stock_production",
"integer"
],
[
"property_stock_inventory",
"integer"
]
]
}

21
get_metadata.py Normal file
View File

@ -0,0 +1,21 @@
import odoo
import json
def get_table_metadata(cr, table_name):
cr.execute("""
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = %s
""", (table_name,))
return cr.fetchall()
if __name__ == '__main__':
db_name = 'lims_demo'
registry = odoo.registry(db_name)
with registry.cursor() as cr:
metadata = {}
tables = ['sale_order', 'sale_order_line', 'product_template', 'product_product']
for table in tables:
metadata[table] = get_table_metadata(cr, table)
print(json.dumps(metadata, indent=4))

View File

@ -58,7 +58,46 @@ try:
sys.exit(result.returncode)
print("Inicialización de Odoo completada exitosamente.")
sys.exit(0)
# --- Lógica para crear datos de demostración personalizados ---
print("Creando solicitudes de laboratorio de demostración...")
sys.stdout.flush()
with open("/app/create_lab_requests.py", "r") as f:
script_content = f.read()
# Reutilizamos el entorno de Odoo para ejecutar un script
create_requests_command = f"""
odoo shell -c {ODOO_CONF} -d {DB_NAME} <<'EOF'
{script_content}
EOF
"""
try:
result = subprocess.run(
create_requests_command,
shell=True,
capture_output=True,
text=True,
check=False
)
print("--- Create Lab Requests stdout ---")
print(result.stdout)
print("--- Create Lab Requests stderr ---")
print(result.stderr)
sys.stdout.flush()
if result.returncode != 0:
print(f"Fallo al crear las solicitudes de laboratorio con código de salida {result.returncode}")
sys.exit(result.returncode)
print("Solicitudes de laboratorio de demostración creadas exitosamente.")
sys.exit(0)
except Exception as e:
print(f"Ocurrió un error inesperado al crear las solicitudes de laboratorio: {e}")
sys.exit(1)
except FileNotFoundError:
print("Error: El comando 'odoo' no se encontró. Asegúrate de que la imagen del contenedor es correcta y odoo está en el PATH.")

View File

@ -16,7 +16,7 @@
'website': "https://gitea.grupoconsiti.com/luis_portillo/clinical_laboratory",
'category': 'Industries',
'version': '18.0.1.0.0',
'depends': ['base', 'product'],
'depends': ['base', 'product', 'sale'],
'data': [
'security/lims_security.xml',
'security/ir.model.access.csv',
@ -24,11 +24,12 @@
'data/product_category.xml',
'views/partner_views.xml',
'views/analysis_views.xml',
'views/sale_order_views.xml',
'views/menus.xml',
],
'demo': [
'demo/lims_demo.xml',
'demo/analysis_demo.xml',
'demo/z_lims_demo.xml',
'demo/z_analysis_demo.xml',
],
'installable': True,
'application': True,

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<function model="sale.order" name="search" eval="[('name', 'in', ['S00001', 'S00002', 'S00003', 'S00004', 'S00005', 'S00006', 'S00007', 'S00008', 'S00009', 'S00010', 'S00011', 'S00012', 'S00013', 'S00014', 'S00015', 'S00016', 'S00017', 'S00018', 'S00019', 'S00020', 'S00021', 'S00022'])]"/>
<function model="sale.order" name="action_cancel"/>
<function model="sale.order" name="unlink"/>
</data>
</odoo>

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from . import partner
from . import product
from . import analysis_range
from . import analysis_range
from . import sale_order

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from odoo import models, fields
class SaleOrder(models.Model):
_inherit = 'sale.order'
is_lab_request = fields.Boolean(
string="Is a Laboratory Request",
default=False,
copy=False,
help="Technical field to identify if the sale order is a laboratory request."
)
doctor_id = fields.Many2one(
'res.partner',
string="Referring Doctor",
domain="[('is_doctor', '=', True)]",
help="The doctor who referred the patient for this laboratory request."
)

View File

@ -1,2 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_lims_analysis_range_user,lims.analysis.range.user,model_lims_analysis_range,base.group_user,1,1,1,1
access_sale_order_receptionist,sale.order.receptionist,sale.model_sale_order,group_lims_receptionist,1,1,1,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_lims_analysis_range_user lims.analysis.range.user model_lims_analysis_range base.group_user 1 1 1 1
3 access_sale_order_receptionist sale.order.receptionist sale.model_sale_order group_lims_receptionist 1 1 1 0

View File

@ -53,6 +53,28 @@
action="action_lims_doctor"
sequence="30"/>
<!-- Acción de Ventana para Solicitudes de Laboratorio -->
<record id="action_lims_lab_request" model="ir.actions.act_window">
<field name="name">Solicitudes de Laboratorio</field>
<field name="res_model">sale.order</field>
<field name="view_mode">list,form</field>
<field name="domain">[('is_lab_request', '=', True)]</field>
<field name="context">{'default_is_lab_request': True}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Crea una nueva solicitud de laboratorio
</p>
</field>
</record>
<!-- Menú para Solicitudes de Laboratorio -->
<menuitem
id="lims_menu_lab_requests"
name="Solicitudes de Laboratorio"
parent="lims_menu_root"
action="action_lims_lab_request"
sequence="15"/>
<!-- Submenú de Configuración -->
<menuitem
id="lims_menu_config"

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!-- Inherit Form View to Modify Sale Order for Lab Requests -->
<record id="view_order_form_inherit_lims" model="ir.ui.view">
<field name="name">sale.order.form.inherit.lims</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='partner_id']" position="after">
<field name="doctor_id" invisible="not is_lab_request"/>
</xpath>
<xpath expr="//field[@name='partner_id']" position="attributes">
<attribute name="string">Paciente</attribute>
</xpath>
<xpath expr="//field[@name='order_line']//field[@name='product_id']" position="attributes">
<attribute name="domain">[('is_analysis', '=', True)]</attribute>
</xpath>
</field>
</record>
<!-- Inherit List View to Add Referring Doctor -->
<record id="view_order_tree_inherit_lims" model="ir.ui.view">
<field name="name">sale.order.tree.inherit.lims</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='partner_id']" position="after">
<field name="doctor_id"/>
</xpath>
</field>
</record>
</data>
</odoo>

30
verify_products.py Normal file
View File

@ -0,0 +1,30 @@
import odoo
import json
def verify_lab_order_products(cr):
cr.execute("""
SELECT
so.name AS order_name,
sol.id AS line_id,
pt.name->>'en_US' AS product_name,
pt.is_analysis
FROM
sale_order so
JOIN
sale_order_line sol ON so.id = sol.order_id
JOIN
product_product pp ON sol.product_id = pp.id
JOIN
product_template pt ON pp.product_tmpl_id = pt.id
WHERE
so.is_lab_request = TRUE;
""")
return cr.fetchall()
if __name__ == '__main__':
db_name = 'lims_demo'
registry = odoo.registry(db_name)
with registry.cursor() as cr:
results = verify_lab_order_products(cr)
print(json.dumps(results, indent=4))