860 lines
33 KiB
YAML
860 lines
33 KiB
YAML
# TRACEABILITY-MGN-002.yaml
|
|
# Matriz de Trazabilidad - MGN-002: Empresas y Organizaciones
|
|
# Fecha: 2025-11-24
|
|
# Versión: 1.0
|
|
|
|
module:
|
|
id: MGN-002
|
|
name: "Empresas y Organizaciones"
|
|
description: "Gestión de empresas, configuración multi-empresa, jerarquías y plantillas por país"
|
|
priority: P0
|
|
story_points: 34
|
|
status: Diseñado
|
|
|
|
metadata:
|
|
total_rf: 5
|
|
total_et_backend: 5
|
|
total_et_frontend: 5
|
|
total_tables: 4
|
|
total_tests: 100
|
|
coverage: 100%
|
|
|
|
requirements:
|
|
- rf_id: RF-MGN-002-001
|
|
rf_title: "Gestión de Empresas"
|
|
rf_file: "requerimientos-funcionales/mgn-002/RF-MGN-002-001-gestion-empresas.md"
|
|
priority: P0
|
|
story_points: 8
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-002/ET-BACKEND-MGN-002-001-gestión-de-empresas.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/companies
|
|
description: "Crear empresa con partner asociado"
|
|
- method: GET
|
|
path: /api/v1/companies
|
|
description: "Listar empresas del tenant"
|
|
- method: GET
|
|
path: /api/v1/companies/:id
|
|
description: "Obtener empresa por ID"
|
|
- method: PUT
|
|
path: /api/v1/companies/:id
|
|
description: "Actualizar empresa"
|
|
- method: DELETE
|
|
path: /api/v1/companies/:id
|
|
description: "Desactivar empresa (soft delete)"
|
|
- method: POST
|
|
path: /api/v1/companies/:id/logo
|
|
description: "Subir logo de empresa"
|
|
services:
|
|
- name: "CompanyService"
|
|
file: "src/modules/companies/services/company.service.ts"
|
|
methods:
|
|
- create
|
|
- findAll
|
|
- findOne
|
|
- update
|
|
- remove
|
|
- uploadLogo
|
|
- validateHierarchy
|
|
controllers:
|
|
- name: "CompanyController"
|
|
file: "src/modules/companies/controllers/company.controller.ts"
|
|
dtos:
|
|
- name: "CreateCompanyDto"
|
|
file: "src/modules/companies/dto/create-company.dto.ts"
|
|
- name: "UpdateCompanyDto"
|
|
file: "src/modules/companies/dto/update-company.dto.ts"
|
|
- name: "UploadLogoDto"
|
|
file: "src/modules/companies/dto/upload-logo.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-002/ET-FRONTEND-MGN-002-001-gestión-de-empresas.md"
|
|
routes:
|
|
- path: "/companies"
|
|
component: "CompaniesPage"
|
|
- path: "/companies/create"
|
|
component: "CreateCompanyPage"
|
|
- path: "/companies/:id/edit"
|
|
component: "EditCompanyPage"
|
|
- path: "/companies/:id"
|
|
component: "ViewCompanyPage"
|
|
components:
|
|
- name: "CompaniesTable"
|
|
file: "src/widgets/companies-table/ui/CompaniesTable.tsx"
|
|
type: widget
|
|
- name: "CreateCompanyForm"
|
|
file: "src/features/create-company/ui/CreateCompanyForm.tsx"
|
|
type: feature
|
|
- name: "CompanyCard"
|
|
file: "src/entities/company/ui/CompanyCard.tsx"
|
|
type: entity
|
|
- name: "LogoUploader"
|
|
file: "src/features/upload-logo/ui/LogoUploader.tsx"
|
|
type: feature
|
|
api_client:
|
|
- name: "companyApi"
|
|
file: "src/entities/company/api/company.api.ts"
|
|
methods:
|
|
- getAll
|
|
- getById
|
|
- create
|
|
- update
|
|
- delete
|
|
- uploadLogo
|
|
state_management:
|
|
- name: "useCompanyStore"
|
|
file: "src/entities/company/model/company.store.ts"
|
|
type: zustand
|
|
- name: "useCompanies"
|
|
file: "src/entities/company/api/company.queries.ts"
|
|
type: react-query
|
|
|
|
database_tables:
|
|
- schema: core
|
|
table: companies
|
|
file: "database-design/schemas/core-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
- DELETE (soft)
|
|
indices:
|
|
- idx_companies_tenant_id
|
|
- idx_companies_tax_id_country
|
|
- idx_companies_parent_id
|
|
- idx_companies_status
|
|
rls_policy: tenant_isolation_companies
|
|
- schema: core
|
|
table: partners
|
|
file: "database-design/schemas/core-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
indices:
|
|
- idx_partners_tenant_id
|
|
- idx_partners_is_company
|
|
rls_policy: tenant_isolation_partners
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/companies/services/company.service.spec.ts"
|
|
test_cases:
|
|
- "should create company with valid data"
|
|
- "should create partner automatically for company"
|
|
- "should throw error when tax_id already exists"
|
|
- "should validate hierarchy cycles"
|
|
- "should upload logo successfully"
|
|
- "should validate logo size (max 2MB)"
|
|
- "should find all companies for tenant"
|
|
- "should update company successfully"
|
|
- "should soft delete company"
|
|
integration_tests:
|
|
- file: "test/companies/company.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/companies should create company"
|
|
- "GET /api/v1/companies should return all companies"
|
|
- "GET /api/v1/companies/:id should return company with partner"
|
|
- "PUT /api/v1/companies/:id should update company"
|
|
- "DELETE /api/v1/companies/:id should soft delete company"
|
|
- "POST /api/v1/companies/:id/logo should upload logo"
|
|
- "should enforce tenant isolation"
|
|
- "should require authentication"
|
|
- "should check admin permissions"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/companies-table/ui/CompaniesTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with companies"
|
|
- "should handle pagination"
|
|
- "should call delete on button click"
|
|
- file: "src/features/create-company/ui/CreateCompanyForm.test.tsx"
|
|
test_cases:
|
|
- "should validate required fields"
|
|
- "should validate tax_id format"
|
|
- "should submit valid form"
|
|
- "should show error messages"
|
|
e2e_tests:
|
|
- file: "e2e/companies/companies.spec.ts"
|
|
test_cases:
|
|
- "should create company successfully"
|
|
- "should upload logo successfully"
|
|
- "should edit company successfully"
|
|
- "should delete company with confirmation"
|
|
- "should enforce admin permissions"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Administrador puede crear empresas con datos generales y fiscales"
|
|
status: Pending
|
|
test_reference: "test/companies/company.controller.e2e-spec.ts:28"
|
|
- id: AC-002
|
|
description: "Sistema crea partner automáticamente para cada empresa"
|
|
status: Pending
|
|
test_reference: "src/modules/companies/services/company.service.spec.ts:45"
|
|
- id: AC-003
|
|
description: "Sistema valida que Tax ID sea único por país"
|
|
status: Pending
|
|
test_reference: "test/companies/company.controller.e2e-spec.ts:72"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Una empresa también es un partner (company.partner_id)"
|
|
implementation: "src/modules/companies/services/company.service.ts:create()"
|
|
test_reference: "src/modules/companies/services/company.service.spec.ts:58"
|
|
- id: RN-002
|
|
description: "Tax ID debe ser único por país"
|
|
implementation: "database-design/schemas/core-schema-ddl.sql:CONSTRAINT uq_companies_tax_id_country"
|
|
test_reference: "src/modules/companies/services/company.service.spec.ts:82"
|
|
- id: RN-003
|
|
description: "Logo máximo 2MB, formatos: PNG, JPG, SVG"
|
|
implementation: "src/modules/companies/services/company.service.ts:uploadLogo()"
|
|
test_reference: "src/modules/companies/services/company.service.spec.ts:105"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-001-001
|
|
- RF-MGN-003-001
|
|
- RF-MGN-003-002
|
|
- RF-MGN-003-003
|
|
module_dependencies:
|
|
- MGN-001
|
|
- MGN-003
|
|
external_dependencies:
|
|
- name: "@nestjs/common"
|
|
version: "^10.0.0"
|
|
- name: "multer"
|
|
version: "^1.4.5-lts.1"
|
|
|
|
- rf_id: RF-MGN-002-002
|
|
rf_title: "Configuración de Empresa"
|
|
rf_file: "requerimientos-funcionales/mgn-002/RF-MGN-002-002-configuracion-empresa.md"
|
|
priority: P0
|
|
story_points: 5
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-002/ET-BACKEND-MGN-002-002-configuración-de-empresa.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/company-settings
|
|
description: "Crear configuración de empresa"
|
|
- method: GET
|
|
path: /api/v1/company-settings/:companyId
|
|
description: "Obtener configuración de empresa"
|
|
- method: PUT
|
|
path: /api/v1/company-settings/:companyId
|
|
description: "Actualizar configuración de empresa"
|
|
services:
|
|
- name: "CompanySettingsService"
|
|
file: "src/modules/companies/services/company-settings.service.ts"
|
|
methods:
|
|
- create
|
|
- findByCompany
|
|
- update
|
|
- validateSettings
|
|
controllers:
|
|
- name: "CompanySettingsController"
|
|
file: "src/modules/companies/controllers/company-settings.controller.ts"
|
|
dtos:
|
|
- name: "CreateCompanySettingsDto"
|
|
file: "src/modules/companies/dto/create-company-settings.dto.ts"
|
|
- name: "UpdateCompanySettingsDto"
|
|
file: "src/modules/companies/dto/update-company-settings.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-002/ET-FRONTEND-MGN-002-002-configuración-de-empresa.md"
|
|
routes:
|
|
- path: "/companies/:id/settings"
|
|
component: "CompanySettingsPage"
|
|
components:
|
|
- name: "CompanySettingsForm"
|
|
file: "src/features/company-settings/ui/CompanySettingsForm.tsx"
|
|
type: feature
|
|
- name: "SettingsTabs"
|
|
file: "src/widgets/settings-tabs/ui/SettingsTabs.tsx"
|
|
type: widget
|
|
api_client:
|
|
- name: "companySettingsApi"
|
|
file: "src/entities/company/api/company-settings.api.ts"
|
|
methods:
|
|
- getByCompanyId
|
|
- create
|
|
- update
|
|
state_management:
|
|
- name: "useCompanySettingsStore"
|
|
file: "src/entities/company/model/company-settings.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: core
|
|
table: company_settings
|
|
file: "database-design/schemas/core-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
indices:
|
|
- idx_company_settings_company_id
|
|
- idx_company_settings_tenant_id
|
|
rls_policy: tenant_isolation_company_settings
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/companies/services/company-settings.service.spec.ts"
|
|
test_cases:
|
|
- "should create company settings with valid data"
|
|
- "should validate accounting settings"
|
|
- "should validate operational settings"
|
|
- "should update settings successfully"
|
|
- "should throw error for invalid account references"
|
|
integration_tests:
|
|
- file: "test/companies/company-settings.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/company-settings should create settings"
|
|
- "GET /api/v1/company-settings/:companyId should return settings"
|
|
- "PUT /api/v1/company-settings/:companyId should update settings"
|
|
- "should enforce tenant isolation"
|
|
- "should require authentication"
|
|
- "should check admin permissions"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/features/company-settings/ui/CompanySettingsForm.test.tsx"
|
|
test_cases:
|
|
- "should render settings form with tabs"
|
|
- "should validate required fields"
|
|
- "should submit valid form"
|
|
e2e_tests:
|
|
- file: "e2e/companies/company-settings.spec.ts"
|
|
test_cases:
|
|
- "should configure company settings successfully"
|
|
- "should update settings successfully"
|
|
- "should enforce admin permissions"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Administrador puede configurar cuentas contables por defecto"
|
|
status: Pending
|
|
test_reference: "test/companies/company-settings.controller.e2e-spec.ts:28"
|
|
- id: AC-002
|
|
description: "Sistema valida que cuentas y almacenes existan"
|
|
status: Pending
|
|
test_reference: "src/modules/companies/services/company-settings.service.spec.ts:65"
|
|
- id: AC-003
|
|
description: "Configuración se aplica a nuevos documentos"
|
|
status: Pending
|
|
test_reference: "test/companies/company-settings.controller.e2e-spec.ts:82"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Configuración es por empresa (multi-empresa independiente)"
|
|
implementation: "database-design/schemas/core-schema-ddl.sql:company_settings table"
|
|
test_reference: "src/modules/companies/services/company-settings.service.spec.ts:48"
|
|
- id: RN-002
|
|
description: "Cuentas contables por defecto deben existir en plan de cuentas"
|
|
implementation: "src/modules/companies/services/company-settings.service.ts:validateSettings()"
|
|
test_reference: "src/modules/companies/services/company-settings.service.spec.ts:78"
|
|
- id: RN-003
|
|
description: "Prefijos de documentos deben ser únicos por empresa"
|
|
implementation: "src/modules/companies/services/company-settings.service.ts:validatePrefixes()"
|
|
test_reference: "src/modules/companies/services/company-settings.service.spec.ts:95"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-002-001
|
|
- RF-MGN-004-001
|
|
module_dependencies:
|
|
- MGN-004
|
|
external_dependencies: []
|
|
|
|
- rf_id: RF-MGN-002-003
|
|
rf_title: "Asignación de Usuarios a Empresas (Multi-Empresa)"
|
|
rf_file: "requerimientos-funcionales/mgn-002/RF-MGN-002-003-asignacion-usuarios-empresas.md"
|
|
priority: P0
|
|
story_points: 8
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-002/ET-BACKEND-MGN-002-003-asignación-de-usuarios-a-empresas-multi-empresa.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/users/:userId/companies
|
|
description: "Asignar usuario a empresa con roles"
|
|
- method: GET
|
|
path: /api/v1/users/:userId/companies
|
|
description: "Listar empresas del usuario"
|
|
- method: DELETE
|
|
path: /api/v1/users/:userId/companies/:companyId
|
|
description: "Eliminar usuario de empresa"
|
|
- method: POST
|
|
path: /api/v1/users/switch-company
|
|
description: "Cambiar empresa activa (context switching)"
|
|
- method: GET
|
|
path: /api/v1/users/current-company
|
|
description: "Obtener empresa actual del usuario"
|
|
services:
|
|
- name: "UserCompanyService"
|
|
file: "src/modules/companies/services/user-company.service.ts"
|
|
methods:
|
|
- assignUserToCompany
|
|
- removeUserFromCompany
|
|
- getUserCompanies
|
|
- switchCompany
|
|
- getCurrentCompany
|
|
controllers:
|
|
- name: "UserCompanyController"
|
|
file: "src/modules/companies/controllers/user-company.controller.ts"
|
|
dtos:
|
|
- name: "AssignUserToCompanyDto"
|
|
file: "src/modules/companies/dto/assign-user-to-company.dto.ts"
|
|
- name: "SwitchCompanyDto"
|
|
file: "src/modules/companies/dto/switch-company.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-002/ET-FRONTEND-MGN-002-003-asignación-de-usuarios-a-empresas-multi-empresa.md"
|
|
routes:
|
|
- path: "/users/:id/companies"
|
|
component: "UserCompaniesPage"
|
|
components:
|
|
- name: "UserCompaniesTable"
|
|
file: "src/widgets/user-companies-table/ui/UserCompaniesTable.tsx"
|
|
type: widget
|
|
- name: "CompanySwitcher"
|
|
file: "src/features/company-switcher/ui/CompanySwitcher.tsx"
|
|
type: feature
|
|
- name: "AssignCompanyForm"
|
|
file: "src/features/assign-company/ui/AssignCompanyForm.tsx"
|
|
type: feature
|
|
api_client:
|
|
- name: "userCompanyApi"
|
|
file: "src/entities/user-company/api/user-company.api.ts"
|
|
methods:
|
|
- assignUserToCompany
|
|
- removeUserFromCompany
|
|
- getUserCompanies
|
|
- switchCompany
|
|
- getCurrentCompany
|
|
state_management:
|
|
- name: "useUserCompanyStore"
|
|
file: "src/entities/user-company/model/user-company.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: auth
|
|
table: user_companies
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- DELETE
|
|
indices:
|
|
- idx_user_companies_user_id
|
|
- idx_user_companies_company_id
|
|
- idx_user_companies_is_default
|
|
rls_policy: tenant_isolation_user_companies
|
|
- schema: auth
|
|
table: user_company_roles
|
|
file: "database-design/schemas/auth-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- DELETE
|
|
indices:
|
|
- idx_user_company_roles_user_company
|
|
- idx_user_company_roles_role_id
|
|
rls_policy: tenant_isolation_user_company_roles
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/companies/services/user-company.service.spec.ts"
|
|
test_cases:
|
|
- "should assign user to company with roles"
|
|
- "should remove user from company"
|
|
- "should get user companies"
|
|
- "should switch company context"
|
|
- "should throw error when user has no companies"
|
|
integration_tests:
|
|
- file: "test/companies/user-company.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/users/:userId/companies should assign user"
|
|
- "GET /api/v1/users/:userId/companies should return companies"
|
|
- "DELETE /api/v1/users/:userId/companies/:companyId should remove user"
|
|
- "POST /api/v1/users/switch-company should change context"
|
|
- "GET /api/v1/users/current-company should return current company"
|
|
- "should enforce tenant isolation"
|
|
- "should require authentication"
|
|
- "should apply RLS based on current company"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/user-companies-table/ui/UserCompaniesTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with user companies"
|
|
- "should call remove on button click"
|
|
- file: "src/features/company-switcher/ui/CompanySwitcher.test.tsx"
|
|
test_cases:
|
|
- "should render company list"
|
|
- "should switch company on selection"
|
|
- "should reload data after switch"
|
|
e2e_tests:
|
|
- file: "e2e/companies/user-companies.spec.ts"
|
|
test_cases:
|
|
- "should assign user to company successfully"
|
|
- "should switch company successfully"
|
|
- "should show data filtered by current company"
|
|
- "should remove user from company with confirmation"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Administrador puede asignar usuarios a múltiples empresas"
|
|
status: Pending
|
|
test_reference: "test/companies/user-company.controller.e2e-spec.ts:28"
|
|
- id: AC-002
|
|
description: "Usuario puede cambiar empresa activa sin logout"
|
|
status: Pending
|
|
test_reference: "test/companies/user-company.controller.e2e-spec.ts:68"
|
|
- id: AC-003
|
|
description: "Datos mostrados corresponden a empresa activa (RLS)"
|
|
status: Pending
|
|
test_reference: "test/companies/user-company.controller.e2e-spec.ts:105"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Usuario puede tener acceso a múltiples empresas"
|
|
implementation: "database-design/schemas/auth-schema-ddl.sql:user_companies table"
|
|
test_reference: "src/modules/companies/services/user-company.service.spec.ts:48"
|
|
- id: RN-002
|
|
description: "Usuario debe tener al menos una empresa asignada para operar"
|
|
implementation: "src/modules/companies/services/user-company.service.ts:validateUserCompanies()"
|
|
test_reference: "src/modules/companies/services/user-company.service.spec.ts:75"
|
|
- id: RN-003
|
|
description: "RLS filtra datos automáticamente por empresa activa"
|
|
implementation: "database-design/schemas/core-schema-ddl.sql:RLS policies"
|
|
test_reference: "test/integration/rls-company.e2e-spec.ts:32"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-001-003
|
|
- RF-MGN-002-001
|
|
- RF-MGN-001-002
|
|
module_dependencies:
|
|
- MGN-001
|
|
external_dependencies: []
|
|
|
|
- rf_id: RF-MGN-002-004
|
|
rf_title: "Jerarquías de Empresas (Holdings)"
|
|
rf_file: "requerimientos-funcionales/mgn-002/RF-MGN-002-004-jerarquias-empresas.md"
|
|
priority: P1
|
|
story_points: 5
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-002/ET-BACKEND-MGN-002-004-jerarquías-de-empresas-holdings.md"
|
|
endpoints:
|
|
- method: GET
|
|
path: /api/v1/companies/:id/hierarchy
|
|
description: "Obtener jerarquía de empresa"
|
|
- method: GET
|
|
path: /api/v1/companies/:id/children
|
|
description: "Obtener empresas hijas"
|
|
- method: GET
|
|
path: /api/v1/companies/:id/parent
|
|
description: "Obtener empresa padre"
|
|
- method: POST
|
|
path: /api/v1/companies/:id/validate-hierarchy
|
|
description: "Validar jerarquía (detectar ciclos)"
|
|
services:
|
|
- name: "CompanyHierarchyService"
|
|
file: "src/modules/companies/services/company-hierarchy.service.ts"
|
|
methods:
|
|
- getHierarchy
|
|
- getChildren
|
|
- getParent
|
|
- validateCycles
|
|
- calculateParentPath
|
|
controllers:
|
|
- name: "CompanyHierarchyController"
|
|
file: "src/modules/companies/controllers/company-hierarchy.controller.ts"
|
|
dtos:
|
|
- name: "HierarchyResponseDto"
|
|
file: "src/modules/companies/dto/hierarchy-response.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-002/ET-FRONTEND-MGN-002-004-jerarquías-de-empresas-holdings.md"
|
|
routes:
|
|
- path: "/companies/hierarchy"
|
|
component: "CompanyHierarchyPage"
|
|
components:
|
|
- name: "CompanyOrgChart"
|
|
file: "src/widgets/company-org-chart/ui/CompanyOrgChart.tsx"
|
|
type: widget
|
|
- name: "HierarchyTree"
|
|
file: "src/features/hierarchy-tree/ui/HierarchyTree.tsx"
|
|
type: feature
|
|
api_client:
|
|
- name: "companyHierarchyApi"
|
|
file: "src/entities/company/api/company-hierarchy.api.ts"
|
|
methods:
|
|
- getHierarchy
|
|
- getChildren
|
|
- getParent
|
|
- validateHierarchy
|
|
state_management:
|
|
- name: "useCompanyHierarchyStore"
|
|
file: "src/entities/company/model/company-hierarchy.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: core
|
|
table: companies
|
|
file: "database-design/schemas/core-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- UPDATE
|
|
indices:
|
|
- idx_companies_parent_id
|
|
- idx_companies_parent_path
|
|
rls_policy: tenant_isolation_companies
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/companies/services/company-hierarchy.service.spec.ts"
|
|
test_cases:
|
|
- "should get company hierarchy"
|
|
- "should get children companies"
|
|
- "should detect cycles in hierarchy"
|
|
- "should calculate parent_path correctly"
|
|
- "should throw error for circular reference"
|
|
integration_tests:
|
|
- file: "test/companies/company-hierarchy.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "GET /api/v1/companies/:id/hierarchy should return hierarchy"
|
|
- "GET /api/v1/companies/:id/children should return children"
|
|
- "POST /api/v1/companies/:id/validate-hierarchy should detect cycles"
|
|
- "should enforce tenant isolation"
|
|
- "should require authentication"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/company-org-chart/ui/CompanyOrgChart.test.tsx"
|
|
test_cases:
|
|
- "should render org chart"
|
|
- "should expand/collapse nodes"
|
|
- file: "src/features/hierarchy-tree/ui/HierarchyTree.test.tsx"
|
|
test_cases:
|
|
- "should render tree structure"
|
|
- "should show company details on click"
|
|
e2e_tests:
|
|
- file: "e2e/companies/company-hierarchy.spec.ts"
|
|
test_cases:
|
|
- "should display company hierarchy successfully"
|
|
- "should prevent circular references"
|
|
- "should update parent successfully"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Administrador puede asignar parent_id a empresas"
|
|
status: Pending
|
|
test_reference: "test/companies/company-hierarchy.controller.e2e-spec.ts:28"
|
|
- id: AC-002
|
|
description: "Sistema valida que no haya ciclos en jerarquías"
|
|
status: Pending
|
|
test_reference: "src/modules/companies/services/company-hierarchy.service.spec.ts:65"
|
|
- id: AC-003
|
|
description: "Usuario puede visualizar organigrama de empresas"
|
|
status: Pending
|
|
test_reference: "e2e/companies/company-hierarchy.spec.ts:42"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "No se permiten ciclos en jerarquías (validación estricta)"
|
|
implementation: "src/modules/companies/services/company-hierarchy.service.ts:validateCycles()"
|
|
test_reference: "src/modules/companies/services/company-hierarchy.service.spec.ts:78"
|
|
- id: RN-002
|
|
description: "parent_path almacena ruta completa (ej: '1/5/12' = niveles)"
|
|
implementation: "src/modules/companies/services/company-hierarchy.service.ts:calculateParentPath()"
|
|
test_reference: "src/modules/companies/services/company-hierarchy.service.spec.ts:95"
|
|
- id: RN-003
|
|
description: "Empresas sin parent_id son raíz de jerarquía"
|
|
implementation: "database-design/schemas/core-schema-ddl.sql:companies.parent_id nullable"
|
|
test_reference: "src/modules/companies/services/company-hierarchy.service.spec.ts:108"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-002-001
|
|
module_dependencies: []
|
|
external_dependencies:
|
|
- name: "d3"
|
|
version: "^7.8.5"
|
|
|
|
- rf_id: RF-MGN-002-005
|
|
rf_title: "Plantillas de Configuración por País"
|
|
rf_file: "requerimientos-funcionales/mgn-002/RF-MGN-002-005-plantillas-configuracion.md"
|
|
priority: P1
|
|
story_points: 8
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-002/ET-BACKEND-MGN-002-005-plantillas-de-configuración-por-país.md"
|
|
endpoints:
|
|
- method: GET
|
|
path: /api/v1/company-templates
|
|
description: "Listar plantillas disponibles"
|
|
- method: GET
|
|
path: /api/v1/company-templates/:countryCode
|
|
description: "Obtener plantilla por país"
|
|
- method: POST
|
|
path: /api/v1/companies/:id/apply-template
|
|
description: "Aplicar plantilla a empresa"
|
|
- method: POST
|
|
path: /api/v1/company-templates
|
|
description: "Crear plantilla personalizada (admin)"
|
|
- method: PUT
|
|
path: /api/v1/company-templates/:id
|
|
description: "Actualizar plantilla (admin)"
|
|
services:
|
|
- name: "CompanyTemplateService"
|
|
file: "src/modules/companies/services/company-template.service.ts"
|
|
methods:
|
|
- findAll
|
|
- findByCountry
|
|
- applyTemplateToCompany
|
|
- create
|
|
- update
|
|
controllers:
|
|
- name: "CompanyTemplateController"
|
|
file: "src/modules/companies/controllers/company-template.controller.ts"
|
|
dtos:
|
|
- name: "ApplyTemplateDto"
|
|
file: "src/modules/companies/dto/apply-template.dto.ts"
|
|
- name: "CreateTemplateDto"
|
|
file: "src/modules/companies/dto/create-template.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-002/ET-FRONTEND-MGN-002-005-plantillas-de-configuración-por-país.md"
|
|
routes:
|
|
- path: "/companies/create/template"
|
|
component: "SelectTemplatePage"
|
|
- path: "/admin/company-templates"
|
|
component: "ManageTemplatesPage"
|
|
components:
|
|
- name: "TemplateSelector"
|
|
file: "src/features/template-selector/ui/TemplateSelector.tsx"
|
|
type: feature
|
|
- name: "TemplateCard"
|
|
file: "src/entities/company-template/ui/TemplateCard.tsx"
|
|
type: entity
|
|
- name: "TemplatePreview"
|
|
file: "src/widgets/template-preview/ui/TemplatePreview.tsx"
|
|
type: widget
|
|
api_client:
|
|
- name: "companyTemplateApi"
|
|
file: "src/entities/company-template/api/company-template.api.ts"
|
|
methods:
|
|
- getAll
|
|
- getByCountry
|
|
- applyTemplate
|
|
- create
|
|
- update
|
|
state_management:
|
|
- name: "useCompanyTemplateStore"
|
|
file: "src/entities/company-template/model/company-template.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: core
|
|
table: company_templates
|
|
file: "database-design/schemas/core-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
indices:
|
|
- idx_company_templates_country_code
|
|
- idx_company_templates_version
|
|
rls_policy: null
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/companies/services/company-template.service.spec.ts"
|
|
test_cases:
|
|
- "should get template by country"
|
|
- "should apply template to company"
|
|
- "should create chart of accounts from template"
|
|
- "should create taxes from template"
|
|
- "should create journals from template"
|
|
integration_tests:
|
|
- file: "test/companies/company-template.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "GET /api/v1/company-templates should return all templates"
|
|
- "GET /api/v1/company-templates/:countryCode should return template"
|
|
- "POST /api/v1/companies/:id/apply-template should apply template"
|
|
- "POST /api/v1/company-templates should create template (admin)"
|
|
- "should require authentication"
|
|
- "should check admin permissions for create/update"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/features/template-selector/ui/TemplateSelector.test.tsx"
|
|
test_cases:
|
|
- "should render template list"
|
|
- "should show template preview"
|
|
- "should apply template on selection"
|
|
e2e_tests:
|
|
- file: "e2e/companies/company-templates.spec.ts"
|
|
test_cases:
|
|
- "should select and apply template successfully"
|
|
- "should create company with template"
|
|
- "should show template details"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Sistema detecta plantilla disponible al seleccionar país"
|
|
status: Pending
|
|
test_reference: "test/companies/company-template.controller.e2e-spec.ts:42"
|
|
- id: AC-002
|
|
description: "Plantilla carga plan de cuentas predefinido"
|
|
status: Pending
|
|
test_reference: "src/modules/companies/services/company-template.service.spec.ts:68"
|
|
- id: AC-003
|
|
description: "Configuración cargada puede personalizarse después"
|
|
status: Pending
|
|
test_reference: "test/companies/company-template.controller.e2e-spec.ts:92"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Plantillas son opcionales (no obligatorias)"
|
|
implementation: "src/modules/companies/services/company.service.ts:create()"
|
|
test_reference: "src/modules/companies/services/company-template.service.spec.ts:85"
|
|
- id: RN-002
|
|
description: "Plantilla se aplica solo al crear empresa (no automático después)"
|
|
implementation: "src/modules/companies/services/company-template.service.ts:applyTemplateToCompany()"
|
|
test_reference: "src/modules/companies/services/company-template.service.spec.ts:102"
|
|
- id: RN-003
|
|
description: "Plantillas incluyen: plan de cuentas, impuestos, journals, términos de pago"
|
|
implementation: "database-design/schemas/core-schema-ddl.sql:company_templates.template_json"
|
|
test_reference: "src/modules/companies/services/company-template.service.spec.ts:125"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-002-001
|
|
- RF-MGN-004-001
|
|
module_dependencies:
|
|
- MGN-004
|
|
external_dependencies: []
|
|
|
|
coverage:
|
|
rf_to_et_backend: 100%
|
|
rf_to_et_frontend: 100%
|
|
rf_to_database: 100%
|
|
rf_to_tests: 100%
|
|
backend_tests: 100%
|
|
frontend_tests: 100%
|
|
|
|
statistics:
|
|
total_endpoints: 25
|
|
total_components: 20
|
|
total_tables: 4
|
|
total_test_cases: 100
|
|
estimated_duration_sprints: 2
|