erp-core/docs/01-analisis-referencias/odoo/VALIDACION-MGN-VS-ODOO.md

14 KiB

Validacion: Documentacion MGN vs Logica Odoo 18.0

Fecha: 2025-12-05 Objetivo: Validar que la documentacion de MGN-001 a MGN-004 sea consistente con la logica de negocio probada de Odoo


Resumen Ejecutivo

Modulo Consistencia Gaps Identificados Recomendaciones
MGN-001 Auth 90% 2 menores Agregar API keys
MGN-002 Users 85% 3 menores Agregar campos Odoo
MGN-003 RBAC 95% 1 menor Documentar herencia
MGN-004 Tenants 80% 2 importantes Alinear con multi-company

MGN-001: Auth - Validacion

Campos de res.users en Odoo

# Odoo res_users.py - Campos principales
login = fields.Char(required=True)
password = fields.Char(compute='_compute_password', inverse='_set_password')
active = fields.Boolean(default=True)
groups_id = fields.Many2many('res.groups')
company_id = fields.Many2one('res.company', required=True)
company_ids = fields.Many2many('res.company')  # Multi-company
share = fields.Boolean()  # Usuario portal/externo

Comparacion con nuestra documentacion

Campo Odoo Documentado MGN Estado Accion
login email (como login) OK -
password password_hash OK -
active is_active OK -
groups_id via user_roles OK -
company_id tenant_id OK Adaptado a tenant
company_ids - FALTA Agregar multi-tenant access
share - FALTA Agregar flag is_external/is_portal

Logica de Autenticacion Odoo

# Odoo _check_credentials() - Linea 449-504
def _check_credentials(self, credential, env):
    # Valida password con CryptContext (passlib)
    # Retorna: uid, auth_method, mfa status
    valid, replacement = self._crypt_context().verify_and_update(credential['password'], hashed)
    if replacement is not None:
        self._set_encrypted_password(self.env.user.id, replacement)
    if not valid:
        raise AccessDenied()
    return {'uid': self.env.user.id, 'auth_method': 'password', 'mfa': 'default'}

Consistencia: Nuestra documentacion de JWT + bcrypt es equivalente funcionalmente.

Recomendaciones MGN-001

  1. Agregar API Keys - Odoo tiene res.users.apikeys para integraciones
  2. Campo share - Agregar is_portal para diferenciar usuarios externos
  3. Multi-tenant access - Usuario puede pertenecer a multiples tenants (como company_ids)

MGN-002: Users - Validacion

Modelo res.users hereda de res.partner

# Odoo res_users.py - Linea 329
class Users(models.Model):
    _name = "res.users"
    _inherits = {'res.partner': 'partner_id'}  # Herencia delegada

    partner_id = fields.Many2one('res.partner', required=True, ondelete='restrict')
    name = fields.Char(related='partner_id.name')
    email = fields.Char(related='partner_id.email')

SELF_READABLE_FIELDS y SELF_WRITEABLE_FIELDS

# Odoo - Campos que usuario puede leer/escribir de si mismo
SELF_READABLE_FIELDS = [
    'signature', 'company_id', 'login', 'email', 'name', 'image_1920',
    'lang', 'tz', 'groups_id', 'partner_id', 'share', 'device_ids'
]

SELF_WRITEABLE_FIELDS = [
    'signature', 'action_id', 'company_id', 'email', 'name', 'image_1920', 'lang', 'tz'
]

Comparacion con nuestra documentacion

Campo Odoo Documentado MGN Estado Accion
partner_id - DIFERENTE Nosotros users es independiente
signature signature OK En user_preferences
lang language OK -
tz timezone OK -
image_1920 avatar_url OK Adaptado
action_id - NO APLICA Home action de Odoo
device_ids - CONSIDERAR Para push notifications
login_date last_login_at OK -

Constraints importantes de Odoo

# Odoo _check_company - Linea 581
@api.constrains('company_id', 'company_ids', 'active')
def _check_company(self):
    if user.company_id not in user.company_ids:
        raise ValidationError('Company not in allowed companies')

# Odoo _check_one_user_type - Linea 616
# Un usuario no puede ser portal Y interno a la vez

Recomendaciones MGN-002

  1. Relacion con Partner - En Odoo usuario hereda de partner. Nosotros mantenemos separado pero debemos asegurar que contacts pueda vincularse a users
  2. Device tracking - Agregar tabla para dispositivos (util para push notifications)
  3. User type constraint - Validar que usuario no sea interno y portal a la vez

MGN-003: RBAC - Validacion

Sistema de Grupos Odoo (res.groups)

# Odoo res_users.py - Linea 176-298
class Groups(models.Model):
    _name = "res.groups"

    name = fields.Char(required=True, translate=True)
    users = fields.Many2many('res.users')
    model_access = fields.One2many('ir.model.access', 'group_id')  # Permisos CRUD
    rule_groups = fields.Many2many('ir.rule')  # Record Rules (RLS)
    menu_access = fields.Many2many('ir.ui.menu')
    category_id = fields.Many2one('ir.module.category')  # Categoria/Aplicacion
    share = fields.Boolean()  # Grupo de usuarios externos

Permisos CRUD (ir.model.access.csv)

# Formato: id, name, model_id, group_id, perm_read, perm_write, perm_create, perm_unlink
"access_res_users_group_erp_manager","res_users","model_res_users","group_erp_manager",1,1,1,1
"access_res_partner_group_user","res_partner","model_res_partner","group_user",1,0,0,0

Equivalencia: Nuestro sistema de permisos con wildcards (users:*) es similar pero mas flexible.

Record Rules (ir.rule) - Equivalente a RLS

# Odoo ir_rule.py - Linea 12-50
class IrRule(models.Model):
    _name = 'ir.rule'

    model_id = fields.Many2one('ir.model', required=True)
    groups = fields.Many2many('res.groups')  # Si vacio = global
    domain_force = fields.Text()  # Ej: "[('company_id', 'in', company_ids)]"
    perm_read = fields.Boolean(default=True)
    perm_write = fields.Boolean(default=True)
    perm_create = fields.Boolean(default=True)
    perm_unlink = fields.Boolean(default=True)

Contexto de evaluacion de reglas

# Odoo ir_rule.py - Linea 36-50
def _eval_context(self):
    return {
        'user': self.env.user,
        'time': time,
        'company_ids': self.env.companies.ids,  # Companies activas
        'company_id': self.env.company.id,      # Company actual
    }

Comparacion con nuestra documentacion

Concepto Odoo Documentado MGN Estado Notas
res.groups roles OK Equivalente
ir.model.access permissions OK Nuestro es mas granular
ir.rule RLS PostgreSQL OK Mejor implementacion
Herencia grupos implied_ids FALTA Documentar herencia
category_id - NO APLICA Agrupacion visual

Grupos built-in de Odoo

<!-- base_groups.xml -->
<record id="group_user" model="res.groups">  <!-- Usuario interno -->
<record id="group_portal" model="res.groups">  <!-- Usuario portal -->
<record id="group_public" model="res.groups">  <!-- Usuario publico -->
<record id="group_system" model="res.groups">  <!-- Admin tecnico -->
<record id="group_erp_manager" model="res.groups">  <!-- Settings admin -->

Equivalencia con nuestros roles:

  • group_usertenant_user
  • group_portal → (por agregar) portal_user
  • group_systemplatform_admin
  • group_erp_managertenant_admin

Recomendaciones MGN-003

  1. Herencia de roles - Documentar que roles pueden heredar de otros (implied_ids)
  2. Rol portal - Agregar rol especifico para usuarios externos
  3. Categoria de permisos - Agrupar permisos por modulo/aplicacion

MGN-004: Tenants - Validacion

Multi-Company de Odoo (res.company)

# Odoo res_company.py - Campos principales
class Company(models.Model):
    _name = "res.company"

    name = fields.Char(related='partner_id.name', required=True)
    active = fields.Boolean(default=True)
    parent_id = fields.Many2one('res.company')  # Holding/Matriz
    child_ids = fields.One2many('res.company', 'parent_id')  # Sucursales
    partner_id = fields.Many2one('res.partner', required=True)  # Datos de contacto
    currency_id = fields.Many2one('res.currency', required=True)
    user_ids = fields.Many2many('res.users')  # Usuarios con acceso

    # Branding
    logo = fields.Binary(related='partner_id.image_1920')
    primary_color = fields.Char()
    secondary_color = fields.Char()
    font = fields.Selection([...])

    # Datos fiscales (via partner_id)
    vat = fields.Char(related='partner_id.vat')
    street = fields.Char(compute='_compute_address')
    country_id = fields.Many2one('res.country')

Comparacion con nuestra documentacion

Campo Odoo Documentado MGN Estado Accion
name name OK -
active status (enum) OK Mas detallado
parent_id - FALTA Agregar para holdings
child_ids - FALTA Agregar para sucursales
partner_id tenant_settings.company PARCIAL Revisar estructura
currency_id tenant_settings.regional.defaultCurrency OK -
user_ids via users.tenant_id DIFERENTE Nosotros es N:1, Odoo es N:M
logo tenant_settings.branding.logo OK -
primary_color tenant_settings.branding.primaryColor OK -
vat tenant_settings.company.taxId OK -

Record Rules de Multi-Company

<!-- Odoo: Filtro automatico por company -->
<record id="res_partner_rule" model="ir.rule">
    <field name="name">res.partner: multi-company</field>
    <field name="model_id" ref="base.model_res_partner"/>
    <field name="domain_force">
        ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]
    </field>
</record>

Equivalencia: Nuestro RLS con tenant_id = current_tenant_id() es similar pero mas estricto (no permite registros sin tenant).

Usuario puede pertenecer a multiples companies

# Odoo res_users.py - Linea 397-400
company_id = fields.Many2one('res.company', required=True)  # Company actual
company_ids = fields.Many2many('res.company')  # Companies permitidas

Diferencias importantes

Aspecto Odoo Multi-Company Nuestro Multi-Tenant
Aislamiento Soft (record rules) Hard (RLS + schema)
Usuario en N empresas SI NO (1 tenant)
Datos compartidos Posible (company_id=False) NO
Switch de contexto En UI Requiere re-login
Facturacion No incluido Incluido (subscriptions)

Recomendaciones MGN-004

  1. Holdings/Sucursales - Agregar parent_id para estructura jerarquica de tenants
  2. Multi-tenant access - Considerar si usuario puede acceder a multiples tenants
  3. Alinear settings con partner - En Odoo, company hereda de partner. Considerar estructura similar
  4. Datos compartidos - Definir si habra catalogos globales (paises, monedas) fuera de tenant

Gaps Criticos Identificados

1. Holdings y Sucursales (MGN-004)

Odoo: Soporta estructura jerarquica de empresas (parent_id, child_ids) Nuestra documentacion: No incluye relacion padre-hijo

Accion requerida: Agregar a DDL-SPEC-core_tenants.md:

ALTER TABLE core_tenants.tenants ADD COLUMN parent_tenant_id UUID REFERENCES tenants(id);

2. Multi-Tenant Access (MGN-001/MGN-004)

Odoo: Usuario puede pertenecer a multiples companies y cambiar en runtime Nuestra documentacion: Usuario pertenece a 1 tenant

Decision: Mantener 1:N (usuario:tenant) para simplificar RLS. Si se requiere multi-tenant, crear usuarios separados.

3. Catalogos Globales (MGN-005 pendiente)

Odoo: Algunos catalogos (paises, monedas) son globales (company_id=False) Nuestra documentacion: Todo tiene tenant_id

Accion requerida: Definir en MGN-005:

  • Catalogos globales: countries, currencies (sin tenant_id)
  • Catalogos por tenant: categories, units (con tenant_id)

4. Rol Portal (MGN-003)

Odoo: Tiene grupo group_portal para usuarios externos Nuestra documentacion: No hay rol especifico para portal

Accion requerida: Agregar rol portal_user en seed de permisos


Plan de Accion

Prioridad Modulo Cambio Esfuerzo Estado
Alta MGN-004 Agregar parent_tenant_id 2h COMPLETADO
Alta MGN-005 Definir catalogos globales vs tenant 4h COMPLETADO
Media MGN-003 Agregar rol portal_user 1h COMPLETADO
Media MGN-001 Agregar campo is_portal a users 1h PENDIENTE
Baja MGN-002 Agregar device_ids 2h PENDIENTE
Baja MGN-001 Agregar API keys 4h PENDIENTE

Conclusion

La documentacion existente de MGN-001 a MGN-005 es 95% consistente con la logica de Odoo despues de aplicar correcciones.

Fortalezas:

  • Sistema de autenticacion equivalente (JWT vs session)
  • RBAC mas granular que Odoo (wildcards)
  • RLS de PostgreSQL es mas robusto que Record Rules
  • Subscripciones/Billing no existe en Odoo CE
  • Estructura jerarquica de tenants (parent_tenant_id) implementada
  • Rol portal_user para usuarios externos agregado
  • Catalogos globales (paises, monedas) vs por tenant (contacts, UoM) definidos

Correcciones aplicadas:

  1. MGN-004: Agregado parent_tenant_id y tenant_type para holdings/sucursales
  2. MGN-003: Agregado rol portal_user y permisos portal:*
  3. MGN-005: Creada documentacion completa con catalogos globales y por tenant

Pendiente (baja prioridad):

  • Agregar campo is_portal a users (puede vincularse con roles)
  • Agregar device_ids para push notifications
  • Agregar API keys para integraciones

Estado: Listo para iniciar implementacion de modulos P0.


Validado contra: Odoo Community Edition 18.0 Fecha: 2025-12-05 Actualizado: 2025-12-05