554 lines
17 KiB
YAML
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
|