erp-core/docs/01-fase-foundation/MGN-004-tenants/implementacion/TRACEABILITY.yml

554 lines
17 KiB
YAML

# TRACEABILITY.yml - MGN-004: Multi-tenant
# Matriz de trazabilidad: Documentacion -> Codigo
# Ubicacion: docs/01-fase-foundation/MGN-004-tenants/implementacion/
epic_code: MGN-004
epic_name: Multi-tenant
phase: 1
phase_name: Foundation
story_points: 35
status: documented
# =============================================================================
# DOCUMENTACION
# =============================================================================
documentation:
requirements:
- id: RF-TENANT-001
file: ../requerimientos/RF-TENANT-001.md
title: CRUD de Tenants
priority: P0
status: migrated
description: |
Crear, leer, actualizar y eliminar tenants (organizaciones).
Cada tenant tiene nombre, slug, dominio y configuracion.
- id: RF-TENANT-002
file: ../requerimientos/RF-TENANT-002.md
title: Configuracion por Tenant
priority: P0
status: migrated
description: |
Configuracion personalizada: moneda, zona horaria,
formato de fecha, configuracion fiscal, modulos habilitados.
- id: RF-TENANT-003
file: ../requerimientos/RF-TENANT-003.md
title: Planes y Suscripciones
priority: P1
status: migrated
description: |
Definicion de planes con features y limites.
Suscripciones de tenants a planes.
- id: RF-TENANT-004
file: ../requerimientos/RF-TENANT-004.md
title: Aislamiento RLS
priority: P0
status: migrated
description: |
Row Level Security en PostgreSQL para aislar datos.
Contexto de tenant en cada request.
requirements_index:
file: ../requerimientos/INDICE-RF-TENANT.md
status: migrated
specifications:
- id: ET-TENANTS-001
file: ../especificaciones/ET-tenants-backend.md
title: Backend Tenants
rf: [RF-TENANT-001, RF-TENANT-002, RF-TENANT-003, RF-TENANT-004]
status: migrated
- id: ET-TENANTS-002
file: ../especificaciones/ET-TENANT-database.md
title: Database Tenants
rf: [RF-TENANT-001, RF-TENANT-002, RF-TENANT-003, RF-TENANT-004]
status: migrated
user_stories:
- id: US-MGN004-001
file: ../historias-usuario/US-MGN004-001.md
title: Crear Tenant
rf: [RF-TENANT-001]
story_points: 8
status: migrated
- id: US-MGN004-002
file: ../historias-usuario/US-MGN004-002.md
title: Configurar Tenant
rf: [RF-TENANT-002]
story_points: 5
status: migrated
- id: US-MGN004-003
file: ../historias-usuario/US-MGN004-003.md
title: Gestionar Suscripcion
rf: [RF-TENANT-003]
story_points: 8
status: migrated
- id: US-MGN004-004
file: ../historias-usuario/US-MGN004-004.md
title: Cambiar Plan
rf: [RF-TENANT-003]
story_points: 5
status: migrated
backlog:
file: ../historias-usuario/BACKLOG-MGN004.md
status: migrated
# =============================================================================
# IMPLEMENTACION
# =============================================================================
implementation:
database:
schema: core_tenants
path: apps/database/ddl/schemas/core_tenants/
tables:
- name: tenants
file: apps/database/ddl/schemas/core_tenants/tables/tenants.sql
rf: RF-TENANTS-001
status: pending
columns:
- {name: id, type: UUID, pk: true}
- {name: name, type: VARCHAR(200)}
- {name: slug, type: VARCHAR(100), unique: true}
- {name: domain, type: VARCHAR(255)}
- {name: logo_url, type: TEXT}
- {name: settings, type: JSONB}
- {name: is_active, type: BOOLEAN}
- {name: trial_ends_at, type: TIMESTAMPTZ}
- {name: created_at, type: TIMESTAMPTZ}
- {name: updated_at, type: TIMESTAMPTZ}
- {name: deleted_at, type: TIMESTAMPTZ}
indexes:
- idx_tenants_slug
- idx_tenants_domain
- name: tenant_settings
file: apps/database/ddl/schemas/core_tenants/tables/tenant_settings.sql
rf: RF-TENANTS-002
status: pending
columns:
- {name: id, type: UUID, pk: true}
- {name: tenant_id, type: UUID, fk: tenants, unique: true}
- {name: currency, type: VARCHAR(3)}
- {name: timezone, type: VARCHAR(50)}
- {name: date_format, type: VARCHAR(20)}
- {name: fiscal_config, type: JSONB}
- {name: modules_enabled, type: JSONB}
- {name: created_at, type: TIMESTAMPTZ}
- {name: updated_at, type: TIMESTAMPTZ}
- name: plans
file: apps/database/ddl/schemas/core_tenants/tables/plans.sql
rf: RF-TENANTS-003
status: pending
columns:
- {name: id, type: UUID, pk: true}
- {name: name, type: VARCHAR(100)}
- {name: slug, type: VARCHAR(50), unique: true}
- {name: description, type: TEXT}
- {name: price_monthly, type: DECIMAL(10,2)}
- {name: price_yearly, type: DECIMAL(10,2)}
- {name: features, type: JSONB}
- {name: limits, type: JSONB}
- {name: is_active, type: BOOLEAN}
- {name: created_at, type: TIMESTAMPTZ}
- name: subscriptions
file: apps/database/ddl/schemas/core_tenants/tables/subscriptions.sql
rf: RF-TENANTS-003
status: pending
columns:
- {name: id, type: UUID, pk: true}
- {name: tenant_id, type: UUID, fk: tenants}
- {name: plan_id, type: UUID, fk: plans}
- {name: status, type: VARCHAR(20)}
- {name: starts_at, type: TIMESTAMPTZ}
- {name: ends_at, type: TIMESTAMPTZ}
- {name: canceled_at, type: TIMESTAMPTZ}
- {name: created_at, type: TIMESTAMPTZ}
indexes:
- idx_subscriptions_tenant
- idx_subscriptions_status
rls_policies:
- name: tenant_isolation
description: Politica base para aislamiento de datos
rf: RF-TENANTS-004
status: pending
tables: ALL
definition: |
CREATE POLICY tenant_isolation ON {table}
USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
functions:
- name: set_tenant_context
file: apps/database/ddl/schemas/core_tenants/functions/set_tenant_context.sql
rf: RF-TENANTS-004
status: pending
params: [p_tenant_id UUID]
returns: VOID
description: Establece contexto de tenant para RLS
- name: create_tenant_with_defaults
file: apps/database/ddl/schemas/core_tenants/functions/create_tenant_with_defaults.sql
rf: RF-TENANTS-005
status: pending
params: [p_name VARCHAR, p_slug VARCHAR, p_admin_user_id UUID]
returns: UUID
description: Crea tenant con datos iniciales y roles
backend:
module: tenants
path: apps/backend/src/modules/tenants/
framework: NestJS
entities:
- name: Tenant
file: apps/backend/src/modules/tenants/entities/tenant.entity.ts
rf: RF-TENANTS-001
status: pending
- name: TenantSettings
file: apps/backend/src/modules/tenants/entities/tenant-settings.entity.ts
rf: RF-TENANTS-002
status: pending
- name: Plan
file: apps/backend/src/modules/tenants/entities/plan.entity.ts
rf: RF-TENANTS-003
status: pending
- name: Subscription
file: apps/backend/src/modules/tenants/entities/subscription.entity.ts
rf: RF-TENANTS-003
status: pending
services:
- name: TenantsService
file: apps/backend/src/modules/tenants/tenants.service.ts
rf: [RF-TENANTS-001, RF-TENANTS-005]
status: pending
methods:
- {name: create, rf: RF-TENANTS-001}
- {name: findAll, rf: RF-TENANTS-001}
- {name: findOne, rf: RF-TENANTS-001}
- {name: findBySlug, rf: RF-TENANTS-001}
- {name: update, rf: RF-TENANTS-001}
- {name: remove, rf: RF-TENANTS-001}
- {name: onboard, rf: RF-TENANTS-005}
- name: TenantSettingsService
file: apps/backend/src/modules/tenants/tenant-settings.service.ts
rf: [RF-TENANTS-002]
status: pending
methods:
- {name: getSettings, rf: RF-TENANTS-002}
- {name: updateSettings, rf: RF-TENANTS-002}
- name: PlansService
file: apps/backend/src/modules/tenants/plans.service.ts
rf: [RF-TENANTS-003]
status: pending
methods:
- {name: findAll, rf: RF-TENANTS-003}
- {name: findOne, rf: RF-TENANTS-003}
- name: SubscriptionsService
file: apps/backend/src/modules/tenants/subscriptions.service.ts
rf: [RF-TENANTS-003]
status: pending
methods:
- {name: create, rf: RF-TENANTS-003}
- {name: getCurrentSubscription, rf: RF-TENANTS-003}
- {name: changePlan, rf: RF-TENANTS-003}
- {name: cancel, rf: RF-TENANTS-003}
middleware:
- name: TenantMiddleware
file: apps/backend/src/modules/tenants/middleware/tenant.middleware.ts
rf: RF-TENANTS-004
status: pending
description: |
Extrae tenant_id del JWT y establece contexto.
SET LOCAL app.current_tenant_id = '{uuid}'
guards:
- name: TenantGuard
file: apps/backend/src/modules/tenants/guards/tenant.guard.ts
rf: RF-TENANTS-004
status: pending
description: Verifica que tenant este activo
- name: SubscriptionGuard
file: apps/backend/src/modules/tenants/guards/subscription.guard.ts
rf: RF-TENANTS-003
status: pending
description: Verifica que suscripcion este activa
controllers:
- name: TenantsController
file: apps/backend/src/modules/tenants/tenants.controller.ts
status: pending
endpoints:
- method: GET
path: /api/v1/tenants
rf: RF-TENANTS-001
description: Listar tenants (super admin)
- method: POST
path: /api/v1/tenants
rf: RF-TENANTS-001
description: Crear tenant
- method: GET
path: /api/v1/tenants/:id
rf: RF-TENANTS-001
description: Obtener tenant
- method: PATCH
path: /api/v1/tenants/:id
rf: RF-TENANTS-001
description: Actualizar tenant
- method: GET
path: /api/v1/tenants/current
rf: RF-TENANTS-001
description: Tenant actual del usuario
- method: GET
path: /api/v1/tenants/current/settings
rf: RF-TENANTS-002
description: Configuracion del tenant
- method: PATCH
path: /api/v1/tenants/current/settings
rf: RF-TENANTS-002
description: Actualizar configuracion
- name: PlansController
file: apps/backend/src/modules/tenants/plans.controller.ts
status: pending
endpoints:
- method: GET
path: /api/v1/plans
rf: RF-TENANTS-003
description: Listar planes disponibles
- method: GET
path: /api/v1/plans/:id
rf: RF-TENANTS-003
description: Detalle de plan
- name: SubscriptionsController
file: apps/backend/src/modules/tenants/subscriptions.controller.ts
status: pending
endpoints:
- method: GET
path: /api/v1/subscriptions/current
rf: RF-TENANTS-003
description: Suscripcion actual
- method: POST
path: /api/v1/subscriptions
rf: RF-TENANTS-003
description: Crear suscripcion
- method: PATCH
path: /api/v1/subscriptions/current/plan
rf: RF-TENANTS-003
description: Cambiar plan
- method: POST
path: /api/v1/subscriptions/current/cancel
rf: RF-TENANTS-003
description: Cancelar suscripcion
decorators:
- name: CurrentTenant
file: apps/backend/src/modules/tenants/decorators/current-tenant.decorator.ts
rf: RF-TENANTS-004
status: pending
description: Inyecta tenant actual del request
frontend:
feature: tenants
path: apps/frontend/src/features/tenants/
framework: React
pages:
- name: TenantsPage
file: apps/frontend/src/features/tenants/pages/TenantsPage.tsx
rf: RF-TENANTS-001
status: pending
route: /admin/tenants
description: Lista de tenants (super admin)
- name: TenantSettingsPage
file: apps/frontend/src/features/tenants/pages/TenantSettingsPage.tsx
rf: RF-TENANTS-002
status: pending
route: /settings/organization
- name: OnboardingPage
file: apps/frontend/src/features/tenants/pages/OnboardingPage.tsx
rf: RF-TENANTS-005
status: pending
route: /onboarding
- name: PlansPage
file: apps/frontend/src/features/tenants/pages/PlansPage.tsx
rf: RF-TENANTS-003
status: pending
route: /settings/subscription
components:
- name: TenantSettingsForm
file: apps/frontend/src/features/tenants/components/TenantSettingsForm.tsx
rf: RF-TENANTS-002
status: pending
- name: OnboardingWizard
file: apps/frontend/src/features/tenants/components/OnboardingWizard.tsx
rf: RF-TENANTS-005
status: pending
- name: PlanCard
file: apps/frontend/src/features/tenants/components/PlanCard.tsx
rf: RF-TENANTS-003
status: pending
- name: SubscriptionStatus
file: apps/frontend/src/features/tenants/components/SubscriptionStatus.tsx
rf: RF-TENANTS-003
status: pending
stores:
- name: tenantStore
file: apps/frontend/src/features/tenants/stores/tenantStore.ts
rf: [RF-TENANTS-001, RF-TENANTS-002]
status: pending
state:
- {name: currentTenant, type: "Tenant | null"}
- {name: settings, type: "TenantSettings | null"}
- {name: subscription, type: "Subscription | null"}
actions:
- fetchCurrentTenant
- updateSettings
- changePlan
hooks:
- name: useTenant
file: apps/frontend/src/features/tenants/hooks/useTenant.ts
rf: RF-TENANTS-001
status: pending
description: Hook para acceder a tenant actual
- name: useSubscription
file: apps/frontend/src/features/tenants/hooks/useSubscription.ts
rf: RF-TENANTS-003
status: pending
description: Hook para verificar features del plan
# =============================================================================
# DEPENDENCIAS
# =============================================================================
dependencies:
depends_on:
- module: MGN-001
type: hard
reason: Tenant ID se incluye en JWT
- module: MGN-002
type: hard
reason: Usuarios pertenecen a tenants
- module: MGN-003
type: hard
reason: Roles son por tenant
required_by:
- module: MGN-005
type: hard
reason: Catalogos son por tenant
- module: MGN-006
type: hard
reason: Inventario por tenant
- module: MGN-007
type: hard
reason: Ventas por tenant
- module: ALL_BUSINESS
type: hard
reason: Todos los datos son por tenant
# =============================================================================
# TESTS
# =============================================================================
tests:
unit:
- name: TenantsService.spec.ts
file: apps/backend/src/modules/tenants/__tests__/tenants.service.spec.ts
status: pending
cases: 12
- name: SubscriptionsService.spec.ts
file: apps/backend/src/modules/tenants/__tests__/subscriptions.service.spec.ts
status: pending
cases: 8
- name: TenantMiddleware.spec.ts
file: apps/backend/src/modules/tenants/__tests__/tenant.middleware.spec.ts
status: pending
cases: 6
integration:
- name: tenants.controller.e2e.spec.ts
file: apps/backend/test/tenants/tenants.controller.e2e.spec.ts
status: pending
cases: 15
- name: rls-isolation.e2e.spec.ts
file: apps/backend/test/tenants/rls-isolation.e2e.spec.ts
status: pending
cases: 10
description: Tests de aislamiento RLS entre tenants
coverage:
target: 80%
current: 0%
# =============================================================================
# METRICAS
# =============================================================================
metrics:
story_points:
estimated: 35
actual: null
files:
database: 8
backend: 20
frontend: 12
tests: 8
total: 48
# =============================================================================
# HISTORIAL
# =============================================================================
history:
- date: "2025-12-05"
action: "Creacion de TRACEABILITY.yml"
author: Requirements-Analyst