From e9a764936f10c166d38999dfa2ab057343a36318 Mon Sep 17 00:00:00 2001 From: Luis Ernesto Portillo Zaldivar Date: Mon, 14 Jul 2025 15:34:46 -0600 Subject: [PATCH 1/6] \"docs(#31): Marcar tareas completadas en ISSUE31_PLAN.md\" --- documents/plans/ISSUE31_PLAN.md | 36 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/documents/plans/ISSUE31_PLAN.md b/documents/plans/ISSUE31_PLAN.md index 85965e5..59a35db 100644 --- a/documents/plans/ISSUE31_PLAN.md +++ b/documents/plans/ISSUE31_PLAN.md @@ -12,7 +12,7 @@ Implementar una máquina de estados completa para el modelo `stock.lot` con el f - **Archivo:** `lims_management/models/stock_lot.py` - **Tareas:** - - [ ] **Añadir campo `state`:** + - [x] **Añadir campo `state`:** - Tipo: `Selection` - Nombre técnico: `state` - String: "Estado" @@ -24,7 +24,7 @@ Implementar una máquina de estados completa para el modelo `stock.lot` con el f - `stored`: 'Almacenada' - `disposed`: 'Desechada' - Atributos: `tracking=True` para registrar cambios en el chatter. - - [ ] **Definir métodos para transiciones:** + - [x] **Definir métodos para transiciones:** - `action_receive()`: Cambia el estado a `received`. - `action_start_analysis()`: Cambia el estado a `in_process`. - `action_complete_analysis()`: Cambia el estado a `analyzed`. @@ -36,35 +36,35 @@ Implementar una máquina de estados completa para el modelo `stock.lot` con el f - **Archivo:** `lims_management/views/stock_lot_views.xml` - **Tareas:** - - [ ] **Vista de Formulario:** - - [ ] **Añadir `header`:** + - [x] **Vista de Formulario:** + - [x] **Añadir `header`:** - Incorporar botones para las acciones (`action_receive`, `action_start_analysis`, etc.). - Controlar la visibilidad de los botones según el estado actual (ej. el botón "Recibir" solo debe ser visible si el estado es 'Recolectada'). - - [ ] **Añadir `statusbar`:** + - [x] **Añadir `statusbar`:** - Visualizar el campo `state` usando el widget `statusbar`. - Definir el `statusbar_visible` para mostrar los estados clave del flujo principal. - - [ ] **Hacer campos `readonly`:** + - [x] **Hacer campos `readonly`:** - Campos como `patient_id`, `request_id`, `collection_date` deben volverse de solo lectura después de que la muestra es recibida para asegurar la integridad de los datos. Se usará el atributo `attrs` con el nuevo formato `invisible` o `readonly` basado en el campo `state`. - - [ ] **Vista de Lista:** - - [ ] Añadir el campo `state` para que sea visible. - - [ ] Añadir el campo `state` a los filtros por defecto en el `search` para poder agrupar por estado fácilmente. + - [x] **Vista de Lista:** + - [x] Añadir el campo `state` para que sea visible. + - [x] Añadir el campo `state` a los filtros por defecto en el `search` para poder agrupar por estado fácilmente. ### 3. Seguridad (Opcional, si es necesario) - **Archivo:** `lims_management/security/lims_security.xml` o `ir.model.access.csv` - **Tareas:** - - [ ] Evaluar si se necesitan reglas de seguridad específicas para controlar quién puede ejecutar las transiciones de estado. Por ahora, se asumirá que los grupos existentes (`group_lims_technician`, `group_lims_admin`) tienen los permisos. + - [x] Evaluar si se necesitan reglas de seguridad específicas para controlar quién puede ejecutar las transiciones de estado. Por ahora, se asumirá que los grupos existentes (`group_lims_technician`, `group_lims_admin`) tienen los permisos. ### 4. Verificación y Pruebas - **Pasos:** - - [ ] Reiniciar la instancia de Odoo con el módulo actualizado. - - [ ] Crear una nueva muestra de laboratorio manualmente. - - [ ] Verificar que el estado por defecto sea 'Recolectada'. - - [ ] Probar cada uno de los botones de transición de estado en la vista de formulario. - - [ ] Confirmar que el `statusbar` se actualiza correctamente. - - [ ] Revisar el chatter para asegurarse de que los cambios de estado se están registrando. - - [ ] Verificar la visibilidad condicional de los botones y el modo de solo lectura de los campos. - - [ ] Filtrar y agrupar por estado en la vista de lista. + - [x] Reiniciar la instancia de Odoo con el módulo actualizado. + - [x] Crear una nueva muestra de laboratorio manualmente. + - [x] Verificar que el estado por defecto sea 'Recolectada'. + - [x] Probar cada uno de los botones de transición de estado en la vista de formulario. + - [x] Confirmar que el `statusbar` se actualiza correctamente. + - [x] Revisar el chatter para asegurarse de que los cambios de estado se están registrando. + - [x] Verificar la visibilidad condicional de los botones y el modo de solo lectura de los campos. + - [x] Filtrar y agrupar por estado en la vista de lista. --- From 654f79dbfcf0e19f79103e4ab648f249caf4befe Mon Sep 17 00:00:00 2001 From: Luis Ernesto Portillo Zaldivar Date: Mon, 14 Jul 2025 15:40:21 -0600 Subject: [PATCH 2/6] \"chore: Add __pycache__/ to .gitignore\" --- .gitignore | Bin 44 -> 14 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/.gitignore b/.gitignore index 12829c01a33a63f07077d85ce9d9be3692ae3f3b..1b0574052da476dbbda5f136edecf7199a34628f 100644 GIT binary patch literal 14 Vcmd;zk1wc9PE5{7jgQyo0stfp1f2i? literal 44 pcmezWFPcf&6#|eFk0zE(R?IJ*XUr2LSfG2v`6B From d81e76a5f5d0282c2cabec11c12276d8b84b3631 Mon Sep 17 00:00:00 2001 From: Luis Ernesto Portillo Zaldivar Date: Mon, 14 Jul 2025 16:00:16 -0600 Subject: [PATCH 3/6] =?UTF-8?q?\"docs:=20Actualizar=20GEMINI.md=20con=20ma?= =?UTF-8?q?nejo=20de=20contenido=20multil=C2=A1nea=20para=20tea\"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GEMINI.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/GEMINI.md b/GEMINI.md index c496a6a..344baf8 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -11,7 +11,7 @@ tea issue create --title "Título del Issue" --description "Descripción detalla ``` - `--title`: Especifica el título del issue. -- `--description`: Especifica la descripción o cuerpo del issue. +- `--description`: Especifica la descripción o cuerpo del issue. Para contenido multilínea, ver la sección "Manejo de Contenido Multilínea". - `--labels`: Especifica una o más etiquetas separadas por comas. --- @@ -34,6 +34,26 @@ tea comment 3 "Comentario de prueba" **Nota:** No se deben utilizar flags como `-i` o `--message`. El formato es directo. +### Manejo de Contenido Multilínea + +Para comentarios o descripciones que contengan múltiples líneas, es crucial escapar los saltos de línea (`\n`) dentro de la cadena. Esto asegura que el shell interprete todo el contenido como un único argumento. + +**Ejemplo para `tea comment`:** + +```bash +tea comment "Línea 1 del comentario.\nLínea 2 del comentario.\nLínea 3 del comentario." +``` + +**Ejemplo para `tea issue create` (en `--description`):** + +```bash +tea issue create --title "Título del Issue" --description "Descripción detallada del issue.\nEsta es la segunda línea de la descripción.\nY esta es la tercera." --labels "etiqueta1" +``` + +**Nota sobre compatibilidad de Shell:** +En entornos Windows, el uso de `printf` para mensajes multilínea puede no estar disponible. En su lugar, se puede intentar usar `echo` con saltos de línea escapados (`\n`), aunque su comportamiento puede variar. La forma más robusta es asegurar que toda la cadena se pase como un único argumento al comando `tea`. + + --- ## Realizar Commits From 27462bf68354f83caac42ef27bf9240e2e8fb1d7 Mon Sep 17 00:00:00 2001 From: Luis Ernesto Portillo Zaldivar Date: Mon, 14 Jul 2025 16:23:27 -0600 Subject: [PATCH 4/6] \"docs: Actualizar GEMINI.md con gitea_cli_helper.py\" --- GEMINI.md | 160 ++++++++++++++++-------------------------------------- 1 file changed, 48 insertions(+), 112 deletions(-) diff --git a/GEMINI.md b/GEMINI.md index 344baf8..7db976e 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -2,126 +2,62 @@ Este proyecto utiliza `tea` para interactuar con el repositorio de Gitea. -## Crear un Issue (Modo no Interactivo) +## Gestión de Gitea con `gitea_cli_helper.py` -Para crear un nuevo issue de forma no interactiva, se utiliza el siguiente comando, proporcionando todos los datos necesarios mediante flags: +Para interactuar con el repositorio de Gitea (crear issues, pull requests, comentar y cerrar issues) de forma robusta y con soporte para contenido multilínea, se recomienda utilizar el script de Python `gitea_cli_helper.py`. Este script lee la configuración sensible directamente desde el archivo `.env`. + +### Configuración + +Asegúrate de que las siguientes variables estén definidas en tu archivo `.env`: + +- `GITEA_API_KEY`: Tu Token de Acceso Personal (PAT) de Gitea. +- `GITEA_API_KEY_URL`: La URL base de la API de tu instancia de Gitea (ej. `https://gitea.grupoconsiti.com/api/v1/`). +- `GITEA_USERNAME`: Tu nombre de usuario de Gitea (propietario del repositorio). +- `GITEA_REPO_NAME`: El nombre del repositorio (ej. `clinical_laboratory`). + +### Uso + +El script `gitea_cli_helper.py` utiliza `argparse` para diferentes comandos: + +#### 1. Crear un Issue ```bash -tea issue create --title "Título del Issue" --description "Descripción detallada del issue." --labels "etiqueta1,etiqueta2" +python gitea_cli_helper.py create-issue --title "Título del Issue" --body "Descripción detallada del issue.\nSoporta múltiples líneas." ``` -- `--title`: Especifica el título del issue. -- `--description`: Especifica la descripción o cuerpo del issue. Para contenido multilínea, ver la sección "Manejo de Contenido Multilínea". -- `--labels`: Especifica una o más etiquetas separadas por comas. +- `--title`: Título del issue. +- `--body`: Cuerpo o descripción del issue. Los saltos de línea (`\n`) se interpretarán correctamente. + +#### 2. Crear un Pull Request + +```bash +python gitea_cli_helper.py create-pr --head "tu-rama" --base "rama-destino" --title "Título del PR" --body "Descripción del Pull Request.\nSoporta múltiples líneas." +``` + +- `--head`: Rama de origen (tu rama actual). +- `--base`: Rama de destino (ej. `dev`, `main`). +- `--title`: Título del Pull Request. +- `--body`: Cuerpo o descripción del Pull Request. + +#### 3. Comentar en un Issue + +```bash +python gitea_cli_helper.py comment-issue --issue-number 123 --body "Este es un nuevo comentario.\nTambién soporta múltiples líneas." +``` + +- `--issue-number`: Número del issue al que se desea añadir el comentario. +- `--body`: Contenido del comentario. + +#### 4. Cerrar un Issue + +```bash +python gitea_cli_helper.py close-issue --issue-number 123 +``` + +- `--issue-number`: Número del issue a cerrar. --- -## Comentar en un Issue - -Para agregar un comentario a un issue existente, se utiliza el comando `comment` seguido del número del issue y el texto del comentario entre comillas. - -**Formato correcto:** - -```bash -tea comment "Tu comentario aquí" -``` - -**Ejemplo:** - -```bash -tea comment 3 "Comentario de prueba" -``` - -**Nota:** No se deben utilizar flags como `-i` o `--message`. El formato es directo. - -### Manejo de Contenido Multilínea - -Para comentarios o descripciones que contengan múltiples líneas, es crucial escapar los saltos de línea (`\n`) dentro de la cadena. Esto asegura que el shell interprete todo el contenido como un único argumento. - -**Ejemplo para `tea comment`:** - -```bash -tea comment "Línea 1 del comentario.\nLínea 2 del comentario.\nLínea 3 del comentario." -``` - -**Ejemplo para `tea issue create` (en `--description`):** - -```bash -tea issue create --title "Título del Issue" --description "Descripción detallada del issue.\nEsta es la segunda línea de la descripción.\nY esta es la tercera." --labels "etiqueta1" -``` - -**Nota sobre compatibilidad de Shell:** -En entornos Windows, el uso de `printf` para mensajes multilínea puede no estar disponible. En su lugar, se puede intentar usar `echo` con saltos de línea escapados (`\n`), aunque su comportamiento puede variar. La forma más robusta es asegurar que toda la cadena se pase como un único argumento al comando `tea`. - - ---- - -## Realizar Commits - -Debido a problemas de interpretación de comillas en el shell de ejecución, el uso de `git commit -m "mensaje"` puede fallar. Para evitar estos problemas, se debe pasar el mensaje del commit a través de la entrada estándar (`stdin`). - -### Política de Mensajes de Commit - -**Es mandatorio que el título de cada commit referencie el número del issue que resuelve.** Esto se hace para mantener una trazabilidad clara entre el código y las tareas. - -**Formato del Título:** -``` -(#): -``` -- **``:** `feat` (nueva funcionalidad), `fix` (corrección de bug), `docs` (cambios en documentación), `style` (formato), `refactor`, `test`, `chore` (otras tareas). -- **`()`:** El número del issue entre paréntesis y precedido de `#`. - -**Ejemplo:** -``` -feat(#4): Agregar campos de género y fecha de nacimiento al paciente -``` - -### Método Recomendado - -Utiliza el comando `echo` y una tubería (`|`) para enviar el mensaje a `git commit -F -`. - -**Commit de una sola línea:** - -```bash -echo "feat(#4): Tu mensaje de commit conciso" | git commit -F - -``` - -**Commit multilínea:** -Para mensajes de commit multilínea, la forma más segura es usar `printf` que maneja mejor los saltos de línea (` -`): - -```bash -printf "feat(#4): Título del commit - -Cuerpo del mensaje con descripción detallada." | git commit -F - -``` - -Esto asegura que el formato del mensaje del commit se preserve correctamente. - - ---- - -## Crear un Pull Request - -Para crear un pull request (PR), se utiliza el comando `tea pulls create`. Debes especificar la rama base (hacia donde van los cambios) y la rama `head` (tu rama actual), junto con un título que referencie el issue que resuelve. - -**Formato del comando:** - -```bash -tea pulls create --base "" --head "" --title "(#issue): Título descriptivo" -``` - -**Ejemplo:** - -```bash -tea pulls create --base "dev" --head "feature/3-core-setup" --title "feat(#3): Actualiza instrucciones en GEMINI.md" -``` - -- `--base`: La rama de destino (ej. `dev`, `main`). -- `--head`: Tu rama de trabajo actual. -- `--title`: Un título claro que incluya el tipo de cambio (`feat`, `fix`, `docs`) y el número de issue. - ---- ## Contexto del Proyecto From 40123969b1f3b2f176c90443984488480ab12603 Mon Sep 17 00:00:00 2001 From: Luis Ernesto Portillo Zaldivar Date: Mon, 14 Jul 2025 16:23:49 -0600 Subject: [PATCH 5/6] =?UTF-8?q?\"feat:=20A=C2=A4adir=20gitea=5Fcli=5Fhelpe?= =?UTF-8?q?r.py=20y=20actualizar=20.env\"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 5 ++ gitea_cli_helper.py | 133 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 gitea_cli_helper.py diff --git a/.env b/.env index ca51609..18c2278 100644 --- a/.env +++ b/.env @@ -9,3 +9,8 @@ POSTGRES_PASSWORD=supersegura ODOO_DB_NAME=lims_demo ODOO_MASTER_PASSWORD=admin ODOO_WEB_PORT=8069 + +GITEA_API_KEY=1ad57b1e553ee0b092d51f061e78c5c3df9f8107 +GITEA_API_KEY_URL=https://gitea.grupoconsiti.com/api/v1/ +GITEA_USERNAME=luis_portillo +GITEA_REPO_NAME=clinical_laboratory \ No newline at end of file diff --git a/gitea_cli_helper.py b/gitea_cli_helper.py new file mode 100644 index 0000000..bf1ac9e --- /dev/null +++ b/gitea_cli_helper.py @@ -0,0 +1,133 @@ +import requests +import json +import os +import argparse +from dotenv import load_dotenv + +# Cargar variables del archivo .env +load_dotenv() + +# --- Configuración (obtenida de variables de entorno) --- +GITEA_API_KEY = os.getenv("GITEA_API_KEY") +GITEA_API_KEY_URL = os.getenv("GITEA_API_KEY_URL") +GITEA_USERNAME = os.getenv("GITEA_USERNAME") +GITEA_REPO_NAME = os.getenv("GITEA_REPO_NAME") + +# Extraer la URL base de Gitea de GITEA_API_KEY_URL +GITEA_BASE_URL = GITEA_API_KEY_URL.split('/api/v1/')[0] + +def _make_gitea_request(method, endpoint, payload=None): + """Helper function to make authenticated requests to Gitea API.""" + api_url = f"{GITEA_BASE_URL}/api/v1/{endpoint}" + headers = { + "Accept": "application/json", + "Authorization": f"token {GITEA_API_KEY}", + "Content-Type": "application/json" + } + try: + if method == "POST": + response = requests.post(api_url, headers=headers, data=json.dumps(payload)) + elif method == "PATCH": + response = requests.patch(api_url, headers=headers, data=json.dumps(payload)) + else: + raise ValueError(f"Unsupported HTTP method: {method}") + + response.raise_for_status() + return response.json() + except requests.exceptions.RequestException as e: + print(f"Error en la solicitud {method} a {api_url}: {e}") + if hasattr(e, 'response') and e.response is not None: + print(f"Respuesta del servidor: {e.response.text}") + raise + +def create_issue(title, body): + """Creates a new issue in the Gitea repository.""" + endpoint = f"repos/{GITEA_USERNAME}/{GITEA_REPO_NAME}/issues" + payload = { + "title": title, + "body": body, + "closed": False + } + print(f"Creando issue: '{title}'...") + issue_data = _make_gitea_request("POST", endpoint, payload) + print(f"Issue creado exitosamente:") + print(f" Número: {issue_data['number']}") + print(f" Título: {issue_data['title']}") + print(f" URL: {issue_data['html_url']}") + +def create_pull_request(head_branch, base_branch, title, body): + """Creates a new pull request in the Gitea repository.""" + endpoint = f"repos/{GITEA_USERNAME}/{GITEA_REPO_NAME}/pulls" + payload = { + "head": head_branch, + "base": base_branch, + "title": title, + "body": body + } + print(f"Creando Pull Request: '{title}' de '{head_branch}' a '{base_branch}'...") + pr_data = _make_gitea_request("POST", endpoint, payload) + print(f"Pull Request creado exitosamente:") + print(f" Número: {pr_data['number']}") + print(f" Título: {pr_data['title']}") + print(f" URL: {pr_data['html_url']}") + +def comment_issue(issue_number, body): + """Adds a comment to an existing issue.""" + endpoint = f"repos/{GITEA_USERNAME}/{GITEA_REPO_NAME}/issues/{issue_number}/comments" + payload = { + "body": body + } + print(f"Añadiendo comentario al issue #{issue_number}...") + _make_gitea_request("POST", endpoint, payload) + print(f"Comentario añadido al issue #{issue_number} exitosamente.") + +def close_issue(issue_number): + """Closes an existing issue.""" + endpoint = f"repos/{GITEA_USERNAME}/{GITEA_REPO_NAME}/issues/{issue_number}" + payload = { + "state": "closed" + } + print(f"Cerrando issue #{issue_number}...") + _make_gitea_request("PATCH", endpoint, payload) + print(f"Issue #{issue_number} cerrado exitosamente.") + +def main(): + parser = argparse.ArgumentParser(description="Helper CLI para interactuar con la API de Gitea.") + subparsers = parser.add_subparsers(dest="command", help="Comandos disponibles") + + # Subparser para crear issue + create_issue_parser = subparsers.add_parser("create-issue", help="Crea un nuevo issue.") + create_issue_parser.add_argument("--title", required=True, help="Título del issue.") + create_issue_parser.add_argument("--body", required=True, help="Cuerpo/descripción del issue (soporta multilínea con \n).") + + # Subparser para crear pull request + create_pr_parser = subparsers.add_parser("create-pr", help="Crea un nuevo pull request.") + create_pr_parser.add_argument("--head", required=True, help="Rama de origen (head branch).") + create_pr_parser.add_argument("--base", required=True, help="Rama de destino (base branch).") + create_pr_parser.add_argument("--title", required=True, help="Título del pull request.") + create_pr_parser.add_argument("--body", required=True, help="Cuerpo/descripción del pull request (soporta multilínea con \n).") + + # Subparser para comentar issue + comment_issue_parser = subparsers.add_parser("comment-issue", help="Añade un comentario a un issue existente.") + comment_issue_parser.add_argument("--issue-number", type=int, required=True, help="Número del issue.") + comment_issue_parser.add_argument("--body", required=True, help="Cuerpo del comentario (soporta multilínea con \n).") + + # Subparser para cerrar issue + close_issue_parser = subparsers.add_parser("close-issue", help="Cierra un issue existente.") + close_issue_parser.add_argument("--issue-number", type=int, required=True, help="Número del issue a cerrar.") + + args = parser.parse_args() + + if args.command == "create-issue": + create_issue(args.title, args.body) + elif args.command == "create-pr": + create_pull_request(args.head, args.base, args.title, args.body) + elif args.command == "comment-issue": + comment_issue(args.issue_number, args.body) + elif args.command == "close-issue": + close_issue(args.issue_number) + else: + parser.print_help() + +if __name__ == "__main__": + main() From 472f88a47748403cd30f6ba7f0b78999c9af9dc2 Mon Sep 17 00:00:00 2001 From: Luis Ernesto Portillo Zaldivar Date: Mon, 14 Jul 2025 20:22:31 -0600 Subject: [PATCH 6/6] feat: Update gitea_cli_helper.py to use file input and add CLAUDE.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Modified gitea_cli_helper.py to read issue/PR body from files instead of inline text - Added CLAUDE.md with comprehensive development guidelines for Claude Code - CLAUDE.md includes Odoo 18 specific conventions, Docker commands, and project structure 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- CLAUDE.md | 227 ++++++++++++++++++++++++++++++++++++++++++++ gitea_cli_helper.py | 30 ++++-- 2 files changed, 249 insertions(+), 8 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..92228a6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,227 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is a Laboratory Information Management System (LIMS) module for Odoo 18 ERP, specifically designed for clinical laboratories. The module manages patients, samples, analyses, and test results. + +## Key Technologies + +- **Odoo 18**: ERP framework (Python-based) +- **PostgreSQL 15**: Database +- **Docker & Docker Compose**: Containerization +- **Gitea**: Version control and issue tracking + +## Development Commands + +### Starting the Environment +```bash +# Start all services +docker-compose up -d + +# MANDATORY: View initialization logs to check for errors +docker-compose logs odoo_init + +# Stop and clean everything (removes volumes) +docker-compose down -v +``` + +### 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. + +### Database Operations + +#### Direct PostgreSQL Access +```bash +# Connect to PostgreSQL +docker exec -it lims_db psql -U odoo -d odoo +``` + +#### Python Script Method (Recommended) +For complex queries, use Python scripts with Odoo ORM: + +1. Create script (e.g., `verify_products.py`): +```python +import odoo +import json + +def verify_lab_order_products(cr): + cr.execute("""SELECT ... FROM sale_order ...""") + 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. Copy to container: +```bash +docker cp verify_products.py lims_odoo:/tmp/verify_products.py +``` + +3. Execute: +```bash +docker-compose exec odoo python3 /tmp/verify_products.py +``` + +### Gitea Integration +```bash +# Create issue +python gitea_cli_helper.py create-issue --title "Title" --body "Description\nSupports multiple lines" + +# Create PR with inline description +python gitea_cli_helper.py create-pr --head "feature-branch" --base "dev" --title "Title" --body "Description" + +# Create PR with description from file +python gitea_cli_helper.py create-pr dev --title "feat(#31): Sample lifecycle" --description-file pr_description.txt + +# Comment on issue +python gitea_cli_helper.py comment-issue --issue-number 123 --body "Comment text" + +# Close issue +python gitea_cli_helper.py close-issue --issue-number 123 +``` + +## Mandatory Reading + +At the start of each work session, read these documents to understand requirements and technical design: +- `documents/requirements/RequerimientoInicial.md` +- `documents/requirements/ToBeDesing.md` + +## Code Architecture + +### Module Structure +- **lims_management/models/**: Core business logic + - `partner.py`: Patient and healthcare provider management + - `product.py`: Analysis types and categories + - `sale_order.py`: Analysis orders and sample management + - `stock_lot.py`: Sample tracking and lifecycle + - `analysis_range.py`: Normal ranges for test results + +### Odoo 18 Specific Conventions + +#### View Definitions +- **CRITICAL**: Use `` instead of `` - using `` causes `ValueError: Wrong value for ir.ui.view.type: 'tree'` +- View mode in actions must be `list,form` not `tree,form` + +#### Visibility Attributes +- Use `invisible` attribute directly instead of `attrs`: + ```xml + + + + + + + ``` + +#### Context with ref() +- Use `eval` attribute when using `ref()` in action contexts: + ```xml + + {'default_categ_id': ref('module.xml_id')} + + + + ``` + +#### XPath in View Inheritance +- Use flexible XPath expressions for robustness: + ```xml + + + [('is_analysis', '=', True)] + + ``` + +### Data Management +- **Initial Data**: `lims_management/data/` - Sequences, categories, basic configuration +- **Demo Data**: + - XML files in `lims_management/demo/` + - Python scripts in root directory for complex demo data creation + - Use `noupdate="1"` for demo data to prevent reloading + +### Security Model +- Access rights defined in `security/ir.model.access.csv` +- Field-level security in `security/security.xml` +- Group-based permissions: Laboratory Technician, Manager, etc. + +## Environment Variables + +Required in `.env` file: +- `GITEA_API_KEY`: Personal Access Token for Gitea +- `GITEA_API_KEY_URL`: Gitea API base URL (e.g., `https://gitea.grupoconsiti.com/api/v1/`) +- `GITEA_USERNAME`: Gitea username (repository owner) +- `GITEA_REPO_NAME`: Repository name (e.g., `clinical_laboratory`) + +## Important Patterns + +### Sample Lifecycle States +```python +STATE_PENDING_COLLECTION = 'pending_collection' +STATE_COLLECTED = 'collected' +STATE_IN_ANALYSIS = 'in_analysis' +STATE_COMPLETED = 'completed' +STATE_CANCELLED = 'cancelled' +``` + +### Barcode Generation +- 13-digit format: YYMMDDNNNNNNC +- Uses `barcode` Python library for Code-128 generation +- Stored as PDF with human-readable text + +### Demo Data Creation + +#### XML Files (Simple Data) +- Use for basic records without complex dependencies +- Place in `lims_management/demo/` +- Use `noupdate="1"` to prevent reloading + +#### Python Scripts (Complex Data) +For data with dependencies or business logic: + +1. Create script: +```python +import odoo + +def create_lab_requests(cr): + env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) + + # Use ref() to get existing records + patient1 = env.ref('lims_management.demo_patient_1') + hemograma = env.ref('lims_management.analysis_hemograma') + + # Create records with business logic + env['sale.order'].create({ + 'partner_id': patient1.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. Integrate in initialization or run separately + +## Git Workflow + +### Pre-commit Hook +Automatically installed via `scripts/install_hooks.sh`: +- Prevents commits to 'main' or 'dev' branches +- Enforces feature branch workflow + +### Branch Naming +- Feature branches: `feature/XX-description` (where XX is issue number) +- Always create PRs to 'dev' branch, not 'main' \ No newline at end of file diff --git a/gitea_cli_helper.py b/gitea_cli_helper.py index bf1ac9e..2a2bb6c 100644 --- a/gitea_cli_helper.py +++ b/gitea_cli_helper.py @@ -40,23 +40,37 @@ def _make_gitea_request(method, endpoint, payload=None): print(f"Respuesta del servidor: {e.response.text}") raise -def create_issue(title, body): +def create_issue(title, body_file): """Creates a new issue in the Gitea repository.""" + try: + with open(body_file, 'r', encoding='utf-8') as f: + body = f.read() + except FileNotFoundError: + print(f"Error: El archivo de cuerpo '{body_file}' no fue encontrado.") + return + endpoint = f"repos/{GITEA_USERNAME}/{GITEA_REPO_NAME}/issues" payload = { "title": title, "body": body, "closed": False } - print(f"Creando issue: '{title}'...") + print(f"Creando issue: '{title}' desde archivo '{body_file}'...") issue_data = _make_gitea_request("POST", endpoint, payload) print(f"Issue creado exitosamente:") print(f" Número: {issue_data['number']}") print(f" Título: {issue_data['title']}") print(f" URL: {issue_data['html_url']}") -def create_pull_request(head_branch, base_branch, title, body): +def create_pull_request(head_branch, base_branch, title, body_file): """Creates a new pull request in the Gitea repository.""" + try: + with open(body_file, 'r', encoding='utf-8') as f: + body = f.read() + except FileNotFoundError: + print(f"Error: El archivo de cuerpo '{body_file}' no fue encontrado.") + return + endpoint = f"repos/{GITEA_USERNAME}/{GITEA_REPO_NAME}/pulls" payload = { "head": head_branch, @@ -64,7 +78,7 @@ def create_pull_request(head_branch, base_branch, title, body): "title": title, "body": body } - print(f"Creando Pull Request: '{title}' de '{head_branch}' a '{base_branch}'...") + print(f"Creando Pull Request: '{title}' de '{head_branch}' a '{base_branch}' desde archivo '{body_file}'...") pr_data = _make_gitea_request("POST", endpoint, payload) print(f"Pull Request creado exitosamente:") print(f" Número: {pr_data['number']}") @@ -98,14 +112,14 @@ def main(): # Subparser para crear issue create_issue_parser = subparsers.add_parser("create-issue", help="Crea un nuevo issue.") create_issue_parser.add_argument("--title", required=True, help="Título del issue.") - create_issue_parser.add_argument("--body", required=True, help="Cuerpo/descripción del issue (soporta multilínea con \n).") + create_issue_parser.add_argument("--body-file", required=True, help="Ruta al archivo de texto con el cuerpo/descripción del issue.") # Subparser para crear pull request create_pr_parser = subparsers.add_parser("create-pr", help="Crea un nuevo pull request.") create_pr_parser.add_argument("--head", required=True, help="Rama de origen (head branch).") create_pr_parser.add_argument("--base", required=True, help="Rama de destino (base branch).") create_pr_parser.add_argument("--title", required=True, help="Título del pull request.") - create_pr_parser.add_argument("--body", required=True, help="Cuerpo/descripción del pull request (soporta multilínea con \n).") + create_pr_parser.add_argument("--body-file", required=True, help="Ruta al archivo de texto con el cuerpo/descripción del pull request.") # Subparser para comentar issue comment_issue_parser = subparsers.add_parser("comment-issue", help="Añade un comentario a un issue existente.") @@ -119,9 +133,9 @@ def main(): args = parser.parse_args() if args.command == "create-issue": - create_issue(args.title, args.body) + create_issue(args.title, args.body_file) elif args.command == "create-pr": - create_pull_request(args.head, args.base, args.title, args.body) + create_pull_request(args.head, args.base, args.title, args.body_file) elif args.command == "comment-issue": comment_issue(args.issue_number, args.body) elif args.command == "close-issue":