feature/6-lab-requests #26
171
GEMINI.md
171
GEMINI.md
|
@ -240,4 +240,175 @@ 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.
|
||||
|
||||
---
|
||||
|
||||
## Creación de Datos de Demostración Complejos
|
||||
|
||||
Cuando los datos de demostración tienen dependencias complejas o requieren lógica de negocio (por ejemplo, cambiar el estado de un registro, o crear registros relacionados que dependen de otros), el uso de archivos XML puede ser limitado y propenso a errores de carga.
|
||||
|
||||
En estos casos, es preferible utilizar un script de Python para crear los datos de demostración.
|
||||
|
||||
### Procedimiento
|
||||
|
||||
1. **Crear un Script de Python:**
|
||||
Crea un script que utilice el ORM de Odoo para crear los registros de demostración. Esto permite utilizar la lógica de negocio de los modelos, como los métodos `create` y `write`, y buscar registros existentes con `search` y `ref`.
|
||||
|
||||
**Ejemplo (`create_lab_requests.py`):**
|
||||
```python
|
||||
import odoo
|
||||
|
||||
def create_lab_requests(cr):
|
||||
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
|
||||
|
||||
# Eliminar órdenes de venta de demostración no deseadas
|
||||
unwanted_orders = env['sale.order'].search([('name', 'in', ['S00001', ...])])
|
||||
for order in unwanted_orders:
|
||||
try:
|
||||
order.action_cancel()
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
unwanted_orders.unlink()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Crear solicitudes de laboratorio
|
||||
patient1 = env.ref('lims_management.demo_patient_1')
|
||||
doctor1 = env.ref('lims_management.demo_doctor_1')
|
||||
hemograma = env.ref('lims_management.analysis_hemograma')
|
||||
|
||||
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})
|
||||
]
|
||||
})
|
||||
|
||||
if __name__ == '__main__':
|
||||
db_name = 'lims_demo'
|
||||
registry = odoo.registry(db_name)
|
||||
with registry.cursor() as cr:
|
||||
create_lab_requests(cr)
|
||||
cr.commit()
|
||||
```
|
||||
|
||||
2. **Integrar el Script en la Inicialización:**
|
||||
Modifica el script `init_odoo.py` para que ejecute el script de creación de datos después de que Odoo haya terminado de instalar los módulos.
|
||||
|
||||
**En `docker-compose.yml`**, asegúrate de que el script esté disponible en el contenedor `odoo_init`:
|
||||
```yaml
|
||||
volumes:
|
||||
- ./create_lab_requests.py:/app/create_lab_requests.py
|
||||
```
|
||||
|
||||
**En `init_odoo.py`**, añade la lógica para ejecutar el script:
|
||||
```python
|
||||
# --- Lógica para crear datos de demostración personalizados ---
|
||||
print("Creando datos de demostración complejos...")
|
||||
sys.stdout.flush()
|
||||
|
||||
with open("/app/create_lab_requests.py", "r") as f:
|
||||
script_content = f.read()
|
||||
|
||||
create_requests_command = f"""
|
||||
odoo shell -c {ODOO_CONF} -d {DB_NAME} <<'EOF'
|
||||
{script_content}
|
||||
EOF
|
||||
"""
|
||||
|
||||
result = subprocess.run(
|
||||
create_requests_command,
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False
|
||||
)
|
||||
```
|
||||
Este enfoque proporciona un control total sobre la creación de datos de demostración y evita los problemas de dependencia y orden de carga de los archivos XML.
|
||||
|
|
26
README.md
Normal file
26
README.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Proyecto de Laboratorio Clínico (LIMS)
|
||||
|
||||
Este proyecto contiene el desarrollo de un módulo de gestión de laboratorios clínicos para Odoo 18.
|
||||
|
||||
## Desarrollo
|
||||
|
||||
### Hook de Pre-Commit
|
||||
|
||||
Para asegurar la integridad de los commits y evitar que se suban cambios incompletos, este repositorio incluye un hook de `pre-commit`.
|
||||
|
||||
**Propósito:**
|
||||
El hook revisa automáticamente si existen archivos modificados que no han sido agregados al "staging area" cada vez que se intenta realizar un commit. Si se detectan cambios sin agregar, el commit es abortado.
|
||||
|
||||
**Instalación (Obligatoria para todos los desarrolladores):**
|
||||
|
||||
Para activar el hook en tu copia local del repositorio, ejecuta los siguientes comandos desde la raíz del proyecto:
|
||||
|
||||
```bash
|
||||
# Copia el hook desde el directorio de scripts a tu directorio local de git
|
||||
cp scripts/hooks/pre-commit .git/hooks/
|
||||
|
||||
# Dale permisos de ejecución (necesario en macOS y Linux)
|
||||
chmod +x .git/hooks/pre-commit
|
||||
```
|
||||
|
||||
Una vez instalado, el hook se ejecutará en cada commit, ayudando a mantener un historial de cambios limpio y completo.
|
53
create_lab_requests.py
Normal file
53
create_lab_requests.py
Normal 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()
|
|
@ -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
|
||||
|
|
BIN
documents/logs/Quotation - S00021.pdf
Normal file
BIN
documents/logs/Quotation - S00021.pdf
Normal file
Binary file not shown.
BIN
documents/logs/Quotation - S00022.pdf
Normal file
BIN
documents/logs/Quotation - S00022.pdf
Normal file
Binary file not shown.
169
documents/logs/producto.json
Normal file
169
documents/logs/producto.json
Normal 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
158
documents/logs/s00021.json
Normal 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
650
documents/metadata.json
Normal 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"
|
||||
]
|
||||
]
|
||||
}
|
58
documents/plans/ISSUE6_PLAN.md
Normal file
58
documents/plans/ISSUE6_PLAN.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
# Plan de Desarrollo: Issue #6 - Solicitudes de Laboratorio
|
||||
|
||||
## Análisis
|
||||
|
||||
El objetivo de este issue es implementar la funcionalidad para que un recepcionista pueda registrar una **"Solicitud de Laboratorio"**. Esta solicitud debe estar vinculada a un paciente, a un médico remitente (opcional) y debe contener los análisis clínicos que se realizarán.
|
||||
|
||||
Basándose en los documentos de diseño (`ToBeDesing.md`) y los requerimientos, la estrategia principal es **reutilizar el modelo de Órdenes de Venta (`sale.order`) de Odoo** para representar las solicitudes de laboratorio. Esta decisión es clave porque aprovecha el flujo de facturación y contabilidad ya existente en Odoo, evitando desarrollar una lógica de cobro paralela y asegurando una integración nativa.
|
||||
|
||||
El trabajo realizado en el **Issue #5** ya nos proporciona el "Catálogo de Análisis Clínicos" como productos de tipo servicio, que serán los elementos que se añadirán a estas solicitudes.
|
||||
|
||||
Por lo tanto, el plan se centrará en adaptar y extender el modelo `sale.order` para que se comporte y se presente al usuario como una "Solicitud de Laboratorio".
|
||||
|
||||
---
|
||||
|
||||
## Plan de Actividades
|
||||
|
||||
- **1. Extender el Modelo `sale.order`:**
|
||||
- [x] Crear el archivo `lims_management/models/sale_order.py`.
|
||||
- [x] Heredar del modelo `sale.order` para añadir los siguientes campos:
|
||||
- `is_lab_request` (Booleano): Un campo técnico para identificar que la orden de venta es una solicitud de laboratorio. Será invisible en la interfaz y se usará para filtrar y aplicar lógica específica.
|
||||
- `doctor_id` (Many2one a `res.partner`): Para seleccionar al médico que remite la solicitud. Se debe aplicar un dominio para que solo muestre los contactos que estén marcados como doctores (`is_doctor = True`).
|
||||
- [x] Añadir el nuevo archivo `sale_order.py` al `__init__.py` de la carpeta `models`.
|
||||
|
||||
- **2. Crear Vistas para Solicitudes de Laboratorio:**
|
||||
- [x] Crear el archivo de vistas `lims_management/views/sale_order_views.xml`.
|
||||
- [x] **Heredar la vista de formulario de `sale.order`** para:
|
||||
- [x] Añadir el campo `doctor_id` cerca del campo del paciente.
|
||||
- [x] Cambiar la etiqueta (string) del campo `partner_id` de "Cliente" a "Paciente".
|
||||
- [x] **(Nuevo)** Aplicar un dominio al campo `partner_id` para que solo muestre contactos que sean pacientes (`is_patient = True`).
|
||||
- [x] **(Nuevo)** Corregir y asegurar que el dominio en el campo `product_template_id` de las líneas de la orden restrinja la selección únicamente a análisis clínicos (`is_analysis = True`).
|
||||
- [x] **Heredar la vista de lista (tree/list) de `sale.order`** para:
|
||||
- Añadir la columna "Médico Remitente" (`doctor_id`).
|
||||
|
||||
- **3. Crear Men<65><6E> y Acción de Ventana:**
|
||||
- [x] Modificar el archivo `lims_management/views/menus.xml`.
|
||||
- [x] Crear una nueva **Acción de Ventana** (`ir.actions.act_window`) para las solicitudes de laboratorio:
|
||||
- `name`: "Solicitudes de Laboratorio".
|
||||
- `res_model`: `sale.order`.
|
||||
- `view_mode`: `list,form`.
|
||||
- `domain`: `[('is_lab_request', '=', True)]` para mostrar solo las solicitudes de laboratorio.
|
||||
- `context`: `{'default_is_lab_request': True}` para que las nuevas solicitudes se marquen correctamente por defecto.
|
||||
- [x] Crear un nuevo `menuitem` llamado "Solicitudes de Laboratorio" bajo el menú principal de "Laboratorio", que dispare la acción anterior.
|
||||
|
||||
- **4. Actualizar Manifiesto y Seguridad:**
|
||||
- [x] Añadir la dependencia del módulo `sale` en el archivo `__manifest__.py`.
|
||||
- [x] Añadir el nuevo archivo de vistas `sale_order_views.xml` a la lista `data` en `__manifest__.py`.
|
||||
- [x] Asegurar que los grupos de usuarios del laboratorio (ej. Recepcionista) tengan los permisos adecuados para crear y modificar órdenes de venta (`sale.order`).
|
||||
|
||||
- **5. Crear Datos de Demostración:**
|
||||
- [x] Crear un nuevo archivo de datos de demostración para las solicitudes de laboratorio.
|
||||
- [x] Definir al menos dos solicitudes de ejemplo que incluyan diferentes análisis y pacientes.
|
||||
- [x] Añadir el nuevo archivo a la clave `demo` en `__manifest__.py`.
|
||||
|
||||
- **6. Verificación Final:**
|
||||
- [x] Reiniciar la instancia de Odoo para aplicar los cambios.
|
||||
- [x] Verificar en la interfaz que el nuevo menú "Solicitudes de Laboratorio" aparece y funciona.
|
||||
- [x] Comprobar que al crear una nueva solicitud, solo se puedan añadir análisis del catálogo y que se pueda seleccionar un médico remitente.
|
||||
- [x] Revisar los logs para asegurar que no haya errores.
|
21
get_metadata.py
Normal file
21
get_metadata.py
Normal 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))
|
14
get_view_arch.py
Normal file
14
get_view_arch.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
import odoo
|
||||
|
||||
def get_view_arch(cr, view_id):
|
||||
cr.execute("SELECT arch_db FROM ir_ui_view WHERE id = %s", (view_id,))
|
||||
return cr.fetchone()[0]
|
||||
|
||||
if __name__ == '__main__':
|
||||
db_name = 'lims_demo'
|
||||
registry = odoo.registry(db_name)
|
||||
with registry.cursor() as cr:
|
||||
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
|
||||
view = env.ref('sale.view_order_form')
|
||||
print(f"View ID: {view.id}")
|
||||
print(get_view_arch(cr, view.id))
|
41
init_odoo.py
41
init_odoo.py
|
@ -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.")
|
||||
|
|
|
@ -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,
|
||||
|
|
8
lims_management/data/sale_demo_cleanup.xml
Normal file
8
lims_management/data/sale_demo_cleanup.xml
Normal 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>
|
|
@ -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
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
lims_management/models/__pycache__/sale_order.cpython-312.pyc
Normal file
BIN
lims_management/models/__pycache__/sale_order.cpython-312.pyc
Normal file
Binary file not shown.
19
lims_management/models/sale_order.py
Normal file
19
lims_management/models/sale_order.py
Normal 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."
|
||||
)
|
|
@ -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
|
||||
|
|
|
|
@ -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"
|
||||
|
|
37
lims_management/views/sale_order_views.xml
Normal file
37
lims_management/views/sale_order_views.xml
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?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>
|
||||
<attribute name="domain">[('is_patient', '=', True)]</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//notebook/page[@name='order_lines']//field[@name='product_template_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>
|
13
scripts/hooks/pre-commit
Normal file
13
scripts/hooks/pre-commit
Normal file
|
@ -0,0 +1,13 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Pre-commit hook que verifica si hay cambios sin agregar al staging area.
|
||||
# Si se encuentran cambios sin agregar, el commit se aborta.
|
||||
|
||||
# Revisa si hay archivos modificados pero no agregados (unstaged)
|
||||
if ! git diff-index --quiet HEAD --; then
|
||||
echo "Error: Hay cambios sin agregar al commit."
|
||||
echo "Por favor, agrega todos los archivos relevantes con 'git add .' o 'git add <file>' antes de hacer commit."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
30
verify_products.py
Normal file
30
verify_products.py
Normal 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))
|
Loading…
Reference in New Issue
Block a user