workspace-v1/shared/knowledge-base/reference/odoo/docs/04-logica-negocio/FLUJO-base.md
rckrdmrd cb4c0681d3 feat(workspace): Add new projects and update architecture
New projects created:
- michangarrito (marketplace mobile)
- template-saas (SaaS template)
- clinica-dental (dental ERP)
- clinica-veterinaria (veterinary ERP)

Architecture updates:
- Move catalog from core/ to shared/
- Add MCP servers structure and templates
- Add git management scripts
- Update SUBREPOSITORIOS.md with 15 new repos
- Update .gitignore for new projects

Repository infrastructure:
- 4 main repositories
- 11 subrepositorios
- Gitea remotes configured

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 04:43:28 -06:00

27 KiB

Flujo de Negocio: Base (Kernel)

Modulo: base Aplica Workflow: Parcial (no tiene estados tradicionales, pero tiene flujos importantes)


1. Nota sobre Workflows en Base

El modulo base no implementa workflows de estados tradicionales como otros modulos (sale, purchase, stock). Sin embargo, tiene flujos de negocio criticos:

  1. Autenticacion de usuarios
  2. Control de acceso por reglas
  3. Ciclo de tareas programadas (cron)
  4. Jerarquia de permisos y grupos

2. Flujo de Autenticacion (res.users)

2.1 Diagrama de Autenticacion

    ┌─────────────────────────────────────────────────────────────────────┐
    │                    SOLICITUD DE AUTENTICACION                        │
    │                    (login + password + context)                      │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                                      ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    VERIFICAR COOLDOWN                                │
    │  - Consultar intentos fallidos recientes                            │
    │  - Si > 5 fallos en ultimos 60s: RECHAZAR                          │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                                      ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    BUSCAR USUARIO                                    │
    │  - Buscar por login                                                 │
    │  - Verificar usuario activo                                         │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                        ┌─────────────┴─────────────┐
                        │                           │
                        ▼                           ▼
              ┌─────────────────┐         ┌─────────────────┐
              │ USUARIO EXISTE  │         │ NO EXISTE       │
              │ Y ACTIVO        │         │ O INACTIVO      │
              └────────┬────────┘         └────────┬────────┘
                       │                           │
                       ▼                           ▼
    ┌─────────────────────────────────┐   ┌─────────────────┐
    │ VALIDAR CREDENCIALES            │   │  ACCESS DENIED  │
    │ - Comparar hash password        │   │  + Log intento  │
    │ - pbkdf2_sha512 (600k rounds)   │   └─────────────────┘
    └─────────────────────────────────┘
                       │
         ┌─────────────┴─────────────┐
         │                           │
         ▼                           ▼
┌─────────────────┐         ┌─────────────────┐
│ PASSWORD OK     │         │ PASSWORD FAIL   │
└────────┬────────┘         └────────┬────────┘
         │                           │
         ▼                           ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│ ACTUALIZAR SESSION      │ │ REGISTRAR FALLO         │
│ - Generar session_token │ │ - Incrementar contador  │
│ - Registrar login_date  │ │ - Verificar cooldown    │
│ - Actualizar log        │ └────────────┬────────────┘
└────────────┬────────────┘              │
             │                           ▼
             ▼                  ┌─────────────────┐
    ┌─────────────────┐         │  ACCESS DENIED  │
    │   AUTH SUCCESS  │         └─────────────────┘
    │   (uid, method) │
    └─────────────────┘

2.2 Configuracion Anti-Brute-Force

Parametro Valor Default Descripcion
Intentos maximos 5 Antes de activar cooldown
Tiempo cooldown 60 segundos Espera despues de exceder intentos
Configurable via ir.config.parameter auth_login_cooldown_*

3. Flujo de Control de Acceso (ir.rule)

3.1 Diagrama de Evaluacion de Reglas

    ┌─────────────────────────────────────────────────────────────────────┐
    │          SOLICITUD DE ACCESO A REGISTRO                              │
    │          (modelo, registro, modo: read/write/create/unlink)          │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                                      ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    BUSCAR REGLAS APLICABLES                          │
    │  - Filtrar por modelo                                               │
    │  - Filtrar por modo (perm_read, perm_write, etc.)                   │
    │  - Filtrar activas                                                  │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                                      ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    SEPARAR REGLAS                                    │
    │  - GLOBALES: sin grupos asignados                                   │
    │  - POR GRUPO: con grupos asignados                                  │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                        ┌─────────────┴─────────────┐
                        │                           │
                        ▼                           ▼
              ┌─────────────────┐         ┌─────────────────┐
              │ EVALUAR GLOBALES│         │ EVALUAR GRUPO   │
              │     (AND)       │         │     (OR)        │
              │                 │         │                 │
              │ Todas deben     │         │ Al menos una    │
              │ cumplirse       │         │ debe cumplirse  │
              └────────┬────────┘         └────────┬────────┘
                       │                           │
                       └───────────┬───────────────┘
                                   │
                                   ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    COMBINAR RESULTADOS                               │
    │                    FINAL = GLOBALES AND GRUPO                        │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                        ┌─────────────┴─────────────┐
                        │                           │
                        ▼                           ▼
              ┌─────────────────┐         ┌─────────────────┐
              │ ACCESO PERMITIDO│         │ ACCESO DENEGADO │
              └─────────────────┘         └────────┬────────┘
                                                   │
                                                   ▼
                                         ┌─────────────────────┐
                                         │ AccessError         │
                                         │ - Regla que falla   │
                                         │ - Registro afectado │
                                         └─────────────────────┘

3.2 Ejemplo de Evaluacion

Usuario: user_sales (grupo: sales_team.group_sale_salesman)

Reglas para model sale.order:
  - R1 (global): domain = [('company_id','in',company_ids)]
  - R2 (group: group_sale_salesman): domain = [('user_id','=',user.id)]
  - R3 (group: group_sale_manager): domain = []  (sin restriccion)

Evaluacion para user_sales:
  1. GLOBALES: R1 debe cumplirse
  2. GRUPO: R2 OR R3 - pero user_sales solo tiene group_sale_salesman
     Entonces solo R2 aplica
  3. FINAL: R1 AND R2
     = [('company_id','in',company_ids)] AND [('user_id','=',user.id)]

4. Flujo de Tareas Programadas (ir.cron)

4.1 Diagrama de Ciclo de Cron

    ┌─────────────────────────────────────────────────────────────────────┐
    │                    ESTADO INICIAL                                    │
    │  active=True, nextcall=DateTime, failure_count=0                     │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                                      ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    SISTEMA DETECTA                                   │
    │                    nextcall <= ahora                                 │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                                      ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    ADQUIRIR JOB                                      │
    │  - Lock en BD (FOR UPDATE SKIP LOCKED)                              │
    │  - Marcar como en ejecucion                                         │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                                      ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    EJECUTAR                                          │
    │  - Nuevo contexto con user_id                                       │
    │  - Ejecutar ir.actions.server                                       │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                        ┌─────────────┴─────────────┐
                        │                           │
                        ▼                           ▼
              ┌─────────────────┐         ┌─────────────────┐
              │ EXITO           │         │ FALLO           │
              │                 │         │                 │
              │ lastcall = ahora│         │ failure_count++ │
              │ failure_count=0 │         │ first_failure   │
              │ first_failure=  │         │   = ahora       │
              │   None          │         │   (si primero)  │
              └────────┬────────┘         └────────┬────────┘
                       │                           │
                       │                           ▼
                       │             ┌─────────────────────────────────┐
                       │             │ VERIFICAR DESACTIVACION         │
                       │             │ Si failure_count >= 5           │
                       │             │ AND (ahora - first_failure) >= 7d│
                       │             └─────────────┬───────────────────┘
                       │                           │
                       │             ┌─────────────┴─────────────┐
                       │             │                           │
                       │             ▼                           ▼
                       │   ┌─────────────────┐         ┌─────────────────┐
                       │   │ NO DESACTIVAR   │         │ DESACTIVAR      │
                       │   │ (< 5 fallos o   │         │ active = False  │
                       │   │ < 7 dias)       │         └────────┬────────┘
                       │   └────────┬────────┘                  │
                       │            │                           │
                       └────────────┼───────────────────────────┘
                                    │
                                    ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    CALCULAR SIGUIENTE EJECUCION                      │
    │  nextcall = lastcall + relativedelta(interval)                       │
    │  Minimo: 5 horas entre intentos si hay fallos                       │
    └─────────────────────────────────────────────────────────────────────┘

4.2 Tabla de Estados de Cron

Estado active failure_count first_failure_date Descripcion
Normal True 0 None Funcionando correctamente
Fallo temporal True 1-4 Set Reintentos pendientes
Desactivado False >= 5 >= 7 dias Requiere intervencion manual

4.3 Intervalo de Ejecucion

Tipo Valor
minutes Cada X minutos
hours Cada X horas
days Cada X dias
weeks Cada X semanas
months Cada X meses

5. Flujo de Jerarquia de Grupos (res.groups)

5.1 Diagrama de Implicaciones

    ┌─────────────────────────────────────────────────────────────────────┐
    │                    USUARIO ASIGNADO A GRUPO                          │
    │                    user.group_ids = [group_A]                        │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                                      ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    RESOLVER IMPLICACIONES                            │
    │  Si group_A.implied_ids = [group_B, group_C]                        │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                                      ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    RESOLVER TRANSITIVAMENTE                          │
    │  Si group_B.implied_ids = [group_D]                                 │
    │  Entonces: A → B → D  (A implica D)                                 │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                                      ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    GRUPOS EFECTIVOS                                  │
    │  user.all_group_ids = [group_A, group_B, group_C, group_D]          │
    └─────────────────────────────────────────────────────────────────────┘

    Ejemplo visual:

    group_sale_manager
           │
           ├── implied_ids ──► group_sale_salesman
           │                          │
           │                          └── implied_ids ──► group_sale_salesman_all_leads
           │
           └── implied_ids ──► group_user

    Usuario con group_sale_manager tiene:
    - group_sale_manager (explicito)
    - group_sale_salesman (implicado)
    - group_sale_salesman_all_leads (transitivo)
    - group_user (implicado)

5.2 Grupos Excluyentes (Disjoint)

    ┌──────────────────┐     DISJOINT     ┌──────────────────┐
    │   group_user     │◄────────────────►│   group_portal   │
    │   (Interno)      │                   │   (Portal)       │
    └────────┬─────────┘                   └─────────┬────────┘
             │              DISJOINT                  │
             └──────────────────┬─────────────────────┘
                                │
                                ▼
                       ┌──────────────────┐
                       │  group_public    │
                       │  (Publico)       │
                       └──────────────────┘

    REGLA: Un usuario solo puede pertenecer a UNO de estos grupos.
    - Si es group_user: usuario interno (empleado)
    - Si es group_portal: usuario externo (cliente/proveedor)
    - Si es group_public: usuario anonimo

6. Flujo de Sincronizacion Partner-Usuario

6.1 Diagrama de Sincronizacion

    ┌─────────────────────────────────────────────────────────────────────┐
    │                    CAMBIO EN RES.PARTNER                             │
    │  Campos: name, email, phone, active, etc.                           │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                                      ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    PROPAGAR A RES.USERS                              │
    │  Si partner tiene usuarios vinculados (user_ids)                    │
    │  Entonces actualizar campos relacionados en usuario                 │
    └─────────────────────────────────────────────────────────────────────┘

                                   Y

    ┌─────────────────────────────────────────────────────────────────────┐
    │                    CAMBIO EN RES.USERS                               │
    │  Campos: name, email, active, etc.                                  │
    └─────────────────────────────────┬───────────────────────────────────┘
                                      │
                                      ▼
    ┌─────────────────────────────────────────────────────────────────────┐
    │                    PROPAGAR A RES.PARTNER                            │
    │  Actualizar partner_id con nuevos valores                           │
    └─────────────────────────────────────────────────────────────────────┘

    RESTRICCION:
    - Si partner.active = False y tiene usuarios internos activos
      → Error: no se puede archivar

7. Reglas de Negocio

ID Regla Validacion Mensaje Error
R1 Usuario admin no eliminable res.users.unlink No se puede eliminar admin
R2 Partner con usuarios no archivable res.partner.write Partner tiene usuarios activos
R3 Grupos excluyentes res.users._check_disjoint Usuario en grupos disjuntos
R4 Empresa no puede cambiar padre res.company.write No se puede cambiar jerarquia
R5 Secuencia step != 0 ir.sequence.create Step no puede ser cero
R6 Cron requiere accion ir.cron.create Debe tener accion servidor

8. Acciones Automaticas

Trigger Accion Condicion
res.users.create Crear partner Si no existe partner_id
res.users.write(active=False) Archivar partner Si no tiene otros usuarios activos
res.company.create Crear partner Automatico
ir.cron.nextcall reached Ejecutar accion active=True
res.currency > 1 active Activar grupo multimoneda Automatico

9. Permisos por Modelo

Modelo Grupo Minimo Notas
res.partner group_user Lectura para portal
res.users group_system Solo admin
res.company group_user Lectura propia empresa
res.groups group_system Solo admin
ir.rule group_system Solo admin
ir.cron group_system Solo admin

Referencias:

  • Archivo principal: models/res_users.py, models/ir_rule.py, models/ir_cron.py
  • Seguridad: security/base_security.xml