erp-core/docs/04-modelado/trazabilidad/TRACEABILITY-MGN-003.yaml

999 lines
37 KiB
YAML

# TRACEABILITY-MGN-003.yaml
# Matriz de Trazabilidad - MGN-003: Catálogos Maestros
# Fecha: 2025-11-24
# Versión: 1.0
module:
id: MGN-003
name: "Catálogos Maestros"
description: "Partners universales, países, monedas, unidades de medida, categorías y términos de pago"
priority: P0
story_points: 29
status: Diseñado
metadata:
total_rf: 6
total_et_backend: 6
total_et_frontend: 6
total_tables: 9
total_tests: 120
coverage: 100%
requirements:
- rf_id: RF-MGN-003-001
rf_title: "Gestión de Partners Universales"
rf_file: "requerimientos-funcionales/mgn-003/RF-MGN-003-001-gestion-partners.md"
priority: P0
story_points: 8
et_backend:
file: "especificaciones-tecnicas/backend/mgn-003/ET-BACKEND-MGN-003-001-gestión-de-partners-universales.md"
endpoints:
- method: POST
path: /api/v1/partners
description: "Crear partner (cliente, proveedor o ambos)"
- method: GET
path: /api/v1/partners
description: "Listar partners con filtros"
- method: GET
path: /api/v1/partners/:id
description: "Obtener partner por ID"
- method: PUT
path: /api/v1/partners/:id
description: "Actualizar partner"
- method: DELETE
path: /api/v1/partners/:id
description: "Desactivar partner (soft delete)"
services:
- name: "PartnerService"
file: "src/modules/partners/services/partner.service.ts"
methods:
- create
- findAll
- findOne
- update
- remove
controllers:
- name: "PartnerController"
file: "src/modules/partners/controllers/partner.controller.ts"
dtos:
- name: "CreatePartnerDto"
file: "src/modules/partners/dto/create-partner.dto.ts"
- name: "UpdatePartnerDto"
file: "src/modules/partners/dto/update-partner.dto.ts"
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-003/ET-FRONTEND-MGN-003-001-gestión-de-partners-universales.md"
routes:
- path: "/partners"
component: "PartnersPage"
- path: "/partners/create"
component: "CreatePartnerPage"
- path: "/partners/:id/edit"
component: "EditPartnerPage"
- path: "/partners/:id"
component: "ViewPartnerPage"
components:
- name: "PartnersTable"
file: "src/widgets/partners-table/ui/PartnersTable.tsx"
type: widget
- name: "CreatePartnerForm"
file: "src/features/create-partner/ui/CreatePartnerForm.tsx"
type: feature
- name: "PartnerCard"
file: "src/entities/partner/ui/PartnerCard.tsx"
type: entity
api_client:
- name: "partnerApi"
file: "src/entities/partner/api/partner.api.ts"
methods:
- getAll
- getById
- create
- update
- delete
state_management:
- name: "usePartnerStore"
file: "src/entities/partner/model/partner.store.ts"
type: zustand
database_tables:
- schema: core
table: partners
file: "database-design/schemas/core-schema-ddl.sql"
operations:
- SELECT
- INSERT
- UPDATE
- DELETE (soft)
indices:
- idx_partners_tenant_id
- idx_partners_company_id
- idx_partners_tax_id
- idx_partners_type
rls_policy: tenant_isolation_partners
tests:
backend:
unit_tests:
- file: "src/modules/partners/services/partner.service.spec.ts"
test_cases:
- "should create partner with valid data"
- "should create customer partner"
- "should create supplier partner"
- "should create partner as both customer and supplier"
- "should throw error when tax_id already exists"
- "should find all partners for tenant"
- "should update partner successfully"
- "should soft delete partner"
integration_tests:
- file: "test/partners/partner.controller.e2e-spec.ts"
test_cases:
- "POST /api/v1/partners should create partner"
- "GET /api/v1/partners should return all partners"
- "GET /api/v1/partners/:id should return partner"
- "PUT /api/v1/partners/:id should update partner"
- "DELETE /api/v1/partners/:id should soft delete partner"
- "should filter partners by type (customer/supplier)"
- "should enforce tenant isolation"
- "should require authentication"
frontend:
component_tests:
- file: "src/widgets/partners-table/ui/PartnersTable.test.tsx"
test_cases:
- "should render table with partners"
- "should handle pagination"
- "should filter by type"
- file: "src/features/create-partner/ui/CreatePartnerForm.test.tsx"
test_cases:
- "should validate required fields"
- "should validate tax_id format"
- "should submit valid form"
e2e_tests:
- file: "e2e/partners/partners.spec.ts"
test_cases:
- "should create partner successfully"
- "should edit partner successfully"
- "should delete partner with confirmation"
- "should filter partners by type"
acceptance_criteria:
- id: AC-001
description: "Usuario puede crear partners como cliente, proveedor o ambos"
status: Pending
test_reference: "test/partners/partner.controller.e2e-spec.ts:28"
- id: AC-002
description: "Sistema valida que tax_id sea único por tenant"
status: Pending
test_reference: "src/modules/partners/services/partner.service.spec.ts:65"
- id: AC-003
description: "Partner puede tener múltiples direcciones y contactos"
status: Pending
test_reference: "test/partners/partner.controller.e2e-spec.ts:95"
business_rules:
- id: RN-001
description: "Partner puede ser cliente, proveedor o ambos (is_customer, is_supplier)"
implementation: "database-design/schemas/core-schema-ddl.sql:partners table"
test_reference: "src/modules/partners/services/partner.service.spec.ts:48"
- id: RN-002
description: "Tax ID debe ser único por tenant (no globalmente)"
implementation: "database-design/schemas/core-schema-ddl.sql:CONSTRAINT uq_partners_tax_id_tenant"
test_reference: "src/modules/partners/services/partner.service.spec.ts:78"
- id: RN-003
description: "Empresa también es partner (is_company=true)"
implementation: "src/modules/companies/services/company.service.ts:create()"
test_reference: "src/modules/partners/services/partner.service.spec.ts:105"
dependencies:
rf_dependencies:
- RF-MGN-001-001
- RF-MGN-003-002
module_dependencies:
- MGN-001
external_dependencies: []
- rf_id: RF-MGN-003-002
rf_title: "Gestión de Países y Regiones"
rf_file: "requerimientos-funcionales/mgn-003/RF-MGN-003-002-gestión-de-países-y-regiones.md"
priority: P0
story_points: 3
et_backend:
file: "especificaciones-tecnicas/backend/mgn-003/ET-BACKEND-MGN-003-002-gestión-de-países-y-regiones.md"
endpoints:
- method: GET
path: /api/v1/countries
description: "Listar países"
- method: GET
path: /api/v1/countries/:id
description: "Obtener país por ID"
- method: GET
path: /api/v1/countries/:id/states
description: "Obtener estados/provincias de país"
services:
- name: "CountryService"
file: "src/modules/catalogs/services/country.service.ts"
methods:
- findAll
- findOne
- findStates
controllers:
- name: "CountryController"
file: "src/modules/catalogs/controllers/country.controller.ts"
dtos: []
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-003/ET-FRONTEND-MGN-003-002-gestión-de-países-y-regiones.md"
routes:
- path: "/catalogs/countries"
component: "CountriesPage"
components:
- name: "CountriesTable"
file: "src/widgets/countries-table/ui/CountriesTable.tsx"
type: widget
- name: "CountrySelector"
file: "src/shared/ui/CountrySelector.tsx"
type: shared
api_client:
- name: "countryApi"
file: "src/entities/country/api/country.api.ts"
methods:
- getAll
- getById
- getStates
state_management:
- name: "useCountryStore"
file: "src/entities/country/model/country.store.ts"
type: zustand
database_tables:
- schema: core
table: countries
file: "database-design/schemas/core-schema-ddl.sql"
operations:
- SELECT
indices:
- idx_countries_code
rls_policy: null
- schema: core
table: states
file: "database-design/schemas/core-schema-ddl.sql"
operations:
- SELECT
indices:
- idx_states_country_id
rls_policy: null
tests:
backend:
unit_tests:
- file: "src/modules/catalogs/services/country.service.spec.ts"
test_cases:
- "should find all countries"
- "should find country by id"
- "should find states by country"
integration_tests:
- file: "test/catalogs/country.controller.e2e-spec.ts"
test_cases:
- "GET /api/v1/countries should return all countries"
- "GET /api/v1/countries/:id should return country"
- "GET /api/v1/countries/:id/states should return states"
- "should not require authentication for read"
frontend:
component_tests:
- file: "src/widgets/countries-table/ui/CountriesTable.test.tsx"
test_cases:
- "should render table with countries"
- "should show country details"
e2e_tests:
- file: "e2e/catalogs/countries.spec.ts"
test_cases:
- "should list countries successfully"
- "should select country in selector"
acceptance_criteria:
- id: AC-001
description: "Sistema provee catálogo de países ISO 3166"
status: Pending
test_reference: "test/catalogs/country.controller.e2e-spec.ts:22"
- id: AC-002
description: "Sistema provee estados/provincias por país"
status: Pending
test_reference: "test/catalogs/country.controller.e2e-spec.ts:48"
- id: AC-003
description: "Catálogo es precargado en base de datos"
status: Pending
test_reference: "database-design/schemas/core-schema-ddl.sql:countries seed"
business_rules:
- id: RN-001
description: "Catálogo de países es global (no por tenant)"
implementation: "database-design/schemas/core-schema-ddl.sql:countries table"
test_reference: "src/modules/catalogs/services/country.service.spec.ts:38"
- id: RN-002
description: "Código de país sigue ISO 3166-1 alpha-2"
implementation: "database-design/schemas/core-schema-ddl.sql:countries.code"
test_reference: "src/modules/catalogs/services/country.service.spec.ts:52"
- id: RN-003
description: "Estados son opcionales (no todos los países tienen estados)"
implementation: "database-design/schemas/core-schema-ddl.sql:states table"
test_reference: "src/modules/catalogs/services/country.service.spec.ts:68"
dependencies:
rf_dependencies: []
module_dependencies: []
external_dependencies: []
- rf_id: RF-MGN-003-003
rf_title: "Gestión de Monedas y Tasas de Cambio"
rf_file: "requerimientos-funcionales/mgn-003/RF-MGN-003-003-gestión-de-monedas-y-tasas-de-cambio.md"
priority: P0
story_points: 5
et_backend:
file: "especificaciones-tecnicas/backend/mgn-003/ET-BACKEND-MGN-003-003-gestión-de-monedas-y-tasas-de-cambio.md"
endpoints:
- method: GET
path: /api/v1/currencies
description: "Listar monedas"
- method: GET
path: /api/v1/currencies/:id
description: "Obtener moneda por ID"
- method: POST
path: /api/v1/exchange-rates
description: "Crear tasa de cambio"
- method: GET
path: /api/v1/exchange-rates
description: "Listar tasas de cambio"
- method: PUT
path: /api/v1/exchange-rates/:id
description: "Actualizar tasa de cambio"
services:
- name: "CurrencyService"
file: "src/modules/catalogs/services/currency.service.ts"
methods:
- findAll
- findOne
- name: "ExchangeRateService"
file: "src/modules/catalogs/services/exchange-rate.service.ts"
methods:
- create
- findAll
- update
- getRate
controllers:
- name: "CurrencyController"
file: "src/modules/catalogs/controllers/currency.controller.ts"
- name: "ExchangeRateController"
file: "src/modules/catalogs/controllers/exchange-rate.controller.ts"
dtos:
- name: "CreateExchangeRateDto"
file: "src/modules/catalogs/dto/create-exchange-rate.dto.ts"
- name: "UpdateExchangeRateDto"
file: "src/modules/catalogs/dto/update-exchange-rate.dto.ts"
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-003/ET-FRONTEND-MGN-003-003-gestión-de-monedas-y-tasas-de-cambio.md"
routes:
- path: "/catalogs/currencies"
component: "CurrenciesPage"
- path: "/catalogs/exchange-rates"
component: "ExchangeRatesPage"
components:
- name: "CurrenciesTable"
file: "src/widgets/currencies-table/ui/CurrenciesTable.tsx"
type: widget
- name: "ExchangeRatesTable"
file: "src/widgets/exchange-rates-table/ui/ExchangeRatesTable.tsx"
type: widget
- name: "CreateExchangeRateForm"
file: "src/features/create-exchange-rate/ui/CreateExchangeRateForm.tsx"
type: feature
api_client:
- name: "currencyApi"
file: "src/entities/currency/api/currency.api.ts"
methods:
- getAll
- getById
- name: "exchangeRateApi"
file: "src/entities/exchange-rate/api/exchange-rate.api.ts"
methods:
- getAll
- create
- update
state_management:
- name: "useCurrencyStore"
file: "src/entities/currency/model/currency.store.ts"
type: zustand
database_tables:
- schema: core
table: currencies
file: "database-design/schemas/core-schema-ddl.sql"
operations:
- SELECT
indices:
- idx_currencies_code
rls_policy: null
- schema: core
table: exchange_rates
file: "database-design/schemas/core-schema-ddl.sql"
operations:
- SELECT
- INSERT
- UPDATE
indices:
- idx_exchange_rates_from_to_date
- idx_exchange_rates_company_id
rls_policy: tenant_isolation_exchange_rates
tests:
backend:
unit_tests:
- file: "src/modules/catalogs/services/exchange-rate.service.spec.ts"
test_cases:
- "should create exchange rate"
- "should get rate for date"
- "should use latest rate if no date specified"
- "should calculate conversion"
- "should update exchange rate"
integration_tests:
- file: "test/catalogs/exchange-rate.controller.e2e-spec.ts"
test_cases:
- "POST /api/v1/exchange-rates should create rate"
- "GET /api/v1/exchange-rates should return all rates"
- "PUT /api/v1/exchange-rates/:id should update rate"
- "should enforce tenant isolation"
- "should require authentication"
frontend:
component_tests:
- file: "src/widgets/exchange-rates-table/ui/ExchangeRatesTable.test.tsx"
test_cases:
- "should render table with rates"
- "should show conversion preview"
- file: "src/features/create-exchange-rate/ui/CreateExchangeRateForm.test.tsx"
test_cases:
- "should validate required fields"
- "should validate rate > 0"
- "should submit valid form"
e2e_tests:
- file: "e2e/catalogs/exchange-rates.spec.ts"
test_cases:
- "should create exchange rate successfully"
- "should update rate successfully"
- "should show rate history"
acceptance_criteria:
- id: AC-001
description: "Usuario puede crear tasas de cambio por fecha"
status: Pending
test_reference: "test/catalogs/exchange-rate.controller.e2e-spec.ts:28"
- id: AC-002
description: "Sistema usa tasa más reciente si no hay tasa para fecha específica"
status: Pending
test_reference: "src/modules/catalogs/services/exchange-rate.service.spec.ts:68"
- id: AC-003
description: "Sistema convierte montos entre monedas automáticamente"
status: Pending
test_reference: "src/modules/catalogs/services/exchange-rate.service.spec.ts:95"
business_rules:
- id: RN-001
description: "Tasa de cambio es específica por empresa y fecha"
implementation: "database-design/schemas/core-schema-ddl.sql:exchange_rates table"
test_reference: "src/modules/catalogs/services/exchange-rate.service.spec.ts:48"
- id: RN-002
description: "Rate debe ser mayor a 0"
implementation: "src/modules/catalogs/dto/create-exchange-rate.dto.ts:@Min(0.000001)"
test_reference: "src/modules/catalogs/services/exchange-rate.service.spec.ts:78"
- id: RN-003
description: "Si no hay tasa para fecha, usar tasa más reciente"
implementation: "src/modules/catalogs/services/exchange-rate.service.ts:getRate()"
test_reference: "src/modules/catalogs/services/exchange-rate.service.spec.ts:108"
dependencies:
rf_dependencies:
- RF-MGN-002-001
module_dependencies:
- MGN-002
external_dependencies: []
- rf_id: RF-MGN-003-004
rf_title: "Gestión de Unidades de Medida (UoM)"
rf_file: "requerimientos-funcionales/mgn-003/RF-MGN-003-004-gestión-de-unidades-de-medida-uom.md"
priority: P0
story_points: 5
et_backend:
file: "especificaciones-tecnicas/backend/mgn-003/ET-BACKEND-MGN-003-004-gestión-de-unidades-de-medida-uom.md"
endpoints:
- method: POST
path: /api/v1/uom
description: "Crear unidad de medida"
- method: GET
path: /api/v1/uom
description: "Listar unidades de medida"
- method: GET
path: /api/v1/uom/:id
description: "Obtener UoM por ID"
- method: PUT
path: /api/v1/uom/:id
description: "Actualizar UoM"
- method: DELETE
path: /api/v1/uom/:id
description: "Eliminar UoM"
services:
- name: "UomService"
file: "src/modules/catalogs/services/uom.service.ts"
methods:
- create
- findAll
- findOne
- update
- remove
- convert
controllers:
- name: "UomController"
file: "src/modules/catalogs/controllers/uom.controller.ts"
dtos:
- name: "CreateUomDto"
file: "src/modules/catalogs/dto/create-uom.dto.ts"
- name: "UpdateUomDto"
file: "src/modules/catalogs/dto/update-uom.dto.ts"
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-003/ET-FRONTEND-MGN-003-004-gestión-de-unidades-de-medida-uom.md"
routes:
- path: "/catalogs/uom"
component: "UomPage"
- path: "/catalogs/uom/create"
component: "CreateUomPage"
components:
- name: "UomTable"
file: "src/widgets/uom-table/ui/UomTable.tsx"
type: widget
- name: "CreateUomForm"
file: "src/features/create-uom/ui/CreateUomForm.tsx"
type: feature
- name: "UomSelector"
file: "src/shared/ui/UomSelector.tsx"
type: shared
api_client:
- name: "uomApi"
file: "src/entities/uom/api/uom.api.ts"
methods:
- getAll
- getById
- create
- update
- delete
state_management:
- name: "useUomStore"
file: "src/entities/uom/model/uom.store.ts"
type: zustand
database_tables:
- schema: core
table: uom_categories
file: "database-design/schemas/core-schema-ddl.sql"
operations:
- SELECT
- INSERT
- UPDATE
indices:
- idx_uom_categories_tenant_id
rls_policy: tenant_isolation_uom_categories
- schema: core
table: uom
file: "database-design/schemas/core-schema-ddl.sql"
operations:
- SELECT
- INSERT
- UPDATE
- DELETE
indices:
- idx_uom_category_id
- idx_uom_tenant_id
rls_policy: tenant_isolation_uom
tests:
backend:
unit_tests:
- file: "src/modules/catalogs/services/uom.service.spec.ts"
test_cases:
- "should create UoM with valid data"
- "should create UoM category"
- "should convert between UoMs of same category"
- "should throw error for different categories"
- "should update UoM successfully"
integration_tests:
- file: "test/catalogs/uom.controller.e2e-spec.ts"
test_cases:
- "POST /api/v1/uom should create UoM"
- "GET /api/v1/uom should return all UoMs"
- "GET /api/v1/uom/:id should return UoM"
- "PUT /api/v1/uom/:id should update UoM"
- "DELETE /api/v1/uom/:id should delete UoM"
- "should enforce tenant isolation"
- "should require authentication"
frontend:
component_tests:
- file: "src/widgets/uom-table/ui/UomTable.test.tsx"
test_cases:
- "should render table with UoMs"
- "should group by category"
- file: "src/features/create-uom/ui/CreateUomForm.test.tsx"
test_cases:
- "should validate required fields"
- "should validate ratio > 0"
- "should submit valid form"
e2e_tests:
- file: "e2e/catalogs/uom.spec.ts"
test_cases:
- "should create UoM successfully"
- "should edit UoM successfully"
- "should delete UoM with confirmation"
acceptance_criteria:
- id: AC-001
description: "Usuario puede crear unidades de medida agrupadas por categoría"
status: Pending
test_reference: "test/catalogs/uom.controller.e2e-spec.ts:28"
- id: AC-002
description: "Sistema convierte cantidades entre UoMs de misma categoría"
status: Pending
test_reference: "src/modules/catalogs/services/uom.service.spec.ts:68"
- id: AC-003
description: "Sistema previene conversión entre categorías diferentes"
status: Pending
test_reference: "src/modules/catalogs/services/uom.service.spec.ts:95"
business_rules:
- id: RN-001
description: "UoMs se agrupan en categorías (Peso, Longitud, Volumen, Tiempo)"
implementation: "database-design/schemas/core-schema-ddl.sql:uom_categories table"
test_reference: "src/modules/catalogs/services/uom.service.spec.ts:48"
- id: RN-002
description: "Conversión solo entre UoMs de misma categoría"
implementation: "src/modules/catalogs/services/uom.service.ts:convert()"
test_reference: "src/modules/catalogs/services/uom.service.spec.ts:78"
- id: RN-003
description: "Ratio define conversión respecto a UoM base (ej: 1 kg = 1000 g)"
implementation: "database-design/schemas/core-schema-ddl.sql:uom.ratio"
test_reference: "src/modules/catalogs/services/uom.service.spec.ts:108"
dependencies:
rf_dependencies: []
module_dependencies: []
external_dependencies: []
- rf_id: RF-MGN-003-005
rf_title: "Gestión de Categorías de Productos"
rf_file: "requerimientos-funcionales/mgn-003/RF-MGN-003-005-gestión-de-categorías-de-productos.md"
priority: P0
story_points: 3
et_backend:
file: "especificaciones-tecnicas/backend/mgn-003/ET-BACKEND-MGN-003-005-gestión-de-categorías-de-productos.md"
endpoints:
- method: POST
path: /api/v1/product-categories
description: "Crear categoría de producto"
- method: GET
path: /api/v1/product-categories
description: "Listar categorías"
- method: GET
path: /api/v1/product-categories/:id
description: "Obtener categoría por ID"
- method: PUT
path: /api/v1/product-categories/:id
description: "Actualizar categoría"
- method: DELETE
path: /api/v1/product-categories/:id
description: "Eliminar categoría"
services:
- name: "ProductCategoryService"
file: "src/modules/catalogs/services/product-category.service.ts"
methods:
- create
- findAll
- findOne
- update
- remove
controllers:
- name: "ProductCategoryController"
file: "src/modules/catalogs/controllers/product-category.controller.ts"
dtos:
- name: "CreateProductCategoryDto"
file: "src/modules/catalogs/dto/create-product-category.dto.ts"
- name: "UpdateProductCategoryDto"
file: "src/modules/catalogs/dto/update-product-category.dto.ts"
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-003/ET-FRONTEND-MGN-003-005-gestión-de-categorías-de-productos.md"
routes:
- path: "/catalogs/product-categories"
component: "ProductCategoriesPage"
components:
- name: "ProductCategoriesTable"
file: "src/widgets/product-categories-table/ui/ProductCategoriesTable.tsx"
type: widget
- name: "CreateProductCategoryForm"
file: "src/features/create-product-category/ui/CreateProductCategoryForm.tsx"
type: feature
- name: "CategoryTree"
file: "src/widgets/category-tree/ui/CategoryTree.tsx"
type: widget
api_client:
- name: "productCategoryApi"
file: "src/entities/product-category/api/product-category.api.ts"
methods:
- getAll
- getById
- create
- update
- delete
state_management:
- name: "useProductCategoryStore"
file: "src/entities/product-category/model/product-category.store.ts"
type: zustand
database_tables:
- schema: inventory
table: product_categories
file: "database-design/schemas/inventory-schema-ddl.sql"
operations:
- SELECT
- INSERT
- UPDATE
- DELETE
indices:
- idx_product_categories_parent_id
- idx_product_categories_tenant_id
rls_policy: tenant_isolation_product_categories
tests:
backend:
unit_tests:
- file: "src/modules/catalogs/services/product-category.service.spec.ts"
test_cases:
- "should create product category"
- "should create nested categories"
- "should validate parent exists"
- "should update category successfully"
- "should delete category"
integration_tests:
- file: "test/catalogs/product-category.controller.e2e-spec.ts"
test_cases:
- "POST /api/v1/product-categories should create category"
- "GET /api/v1/product-categories should return all categories"
- "GET /api/v1/product-categories/:id should return category"
- "PUT /api/v1/product-categories/:id should update category"
- "DELETE /api/v1/product-categories/:id should delete category"
- "should enforce tenant isolation"
- "should require authentication"
frontend:
component_tests:
- file: "src/widgets/product-categories-table/ui/ProductCategoriesTable.test.tsx"
test_cases:
- "should render table with categories"
- "should show hierarchy"
- file: "src/features/create-product-category/ui/CreateProductCategoryForm.test.tsx"
test_cases:
- "should validate required fields"
- "should submit valid form"
e2e_tests:
- file: "e2e/catalogs/product-categories.spec.ts"
test_cases:
- "should create category successfully"
- "should create nested category"
- "should delete category with confirmation"
acceptance_criteria:
- id: AC-001
description: "Usuario puede crear categorías de productos jerárquicas"
status: Pending
test_reference: "test/catalogs/product-category.controller.e2e-spec.ts:28"
- id: AC-002
description: "Sistema muestra árbol de categorías"
status: Pending
test_reference: "e2e/catalogs/product-categories.spec.ts:48"
- id: AC-003
description: "Categorías pueden tener subcategorías ilimitadas"
status: Pending
test_reference: "src/modules/catalogs/services/product-category.service.spec.ts:68"
business_rules:
- id: RN-001
description: "Categorías soportan jerarquía con parent_id"
implementation: "database-design/schemas/inventory-schema-ddl.sql:product_categories.parent_id"
test_reference: "src/modules/catalogs/services/product-category.service.spec.ts:48"
- id: RN-002
description: "Productos heredan propiedades de categoría (cuentas contables)"
implementation: "src/modules/inventory/services/product.service.ts:create()"
test_reference: "src/modules/catalogs/services/product-category.service.spec.ts:78"
- id: RN-003
description: "No se puede eliminar categoría con productos asignados"
implementation: "src/modules/catalogs/services/product-category.service.ts:remove()"
test_reference: "src/modules/catalogs/services/product-category.service.spec.ts:95"
dependencies:
rf_dependencies: []
module_dependencies: []
external_dependencies: []
- rf_id: RF-MGN-003-006
rf_title: "Condiciones de Pago (Payment Terms)"
rf_file: "requerimientos-funcionales/mgn-003/RF-MGN-003-006-condiciones-de-pago-payment-terms.md"
priority: P0
story_points: 5
et_backend:
file: "especificaciones-tecnicas/backend/mgn-003/ET-BACKEND-MGN-003-006-condiciones-de-pago-payment-terms.md"
endpoints:
- method: POST
path: /api/v1/payment-terms
description: "Crear término de pago"
- method: GET
path: /api/v1/payment-terms
description: "Listar términos de pago"
- method: GET
path: /api/v1/payment-terms/:id
description: "Obtener término por ID"
- method: PUT
path: /api/v1/payment-terms/:id
description: "Actualizar término"
- method: DELETE
path: /api/v1/payment-terms/:id
description: "Eliminar término"
services:
- name: "PaymentTermService"
file: "src/modules/catalogs/services/payment-term.service.ts"
methods:
- create
- findAll
- findOne
- update
- remove
- calculateDueDate
controllers:
- name: "PaymentTermController"
file: "src/modules/catalogs/controllers/payment-term.controller.ts"
dtos:
- name: "CreatePaymentTermDto"
file: "src/modules/catalogs/dto/create-payment-term.dto.ts"
- name: "UpdatePaymentTermDto"
file: "src/modules/catalogs/dto/update-payment-term.dto.ts"
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-003/ET-FRONTEND-MGN-003-006-condiciones-de-pago-payment-terms.md"
routes:
- path: "/catalogs/payment-terms"
component: "PaymentTermsPage"
components:
- name: "PaymentTermsTable"
file: "src/widgets/payment-terms-table/ui/PaymentTermsTable.tsx"
type: widget
- name: "CreatePaymentTermForm"
file: "src/features/create-payment-term/ui/CreatePaymentTermForm.tsx"
type: feature
- name: "PaymentTermSelector"
file: "src/shared/ui/PaymentTermSelector.tsx"
type: shared
api_client:
- name: "paymentTermApi"
file: "src/entities/payment-term/api/payment-term.api.ts"
methods:
- getAll
- getById
- create
- update
- delete
state_management:
- name: "usePaymentTermStore"
file: "src/entities/payment-term/model/payment-term.store.ts"
type: zustand
database_tables:
- schema: core
table: payment_terms
file: "database-design/schemas/core-schema-ddl.sql"
operations:
- SELECT
- INSERT
- UPDATE
- DELETE
indices:
- idx_payment_terms_tenant_id
- idx_payment_terms_company_id
rls_policy: tenant_isolation_payment_terms
tests:
backend:
unit_tests:
- file: "src/modules/catalogs/services/payment-term.service.spec.ts"
test_cases:
- "should create payment term"
- "should calculate due date from invoice date"
- "should handle immediate payment"
- "should handle 30/60/90 days terms"
- "should update payment term"
integration_tests:
- file: "test/catalogs/payment-term.controller.e2e-spec.ts"
test_cases:
- "POST /api/v1/payment-terms should create term"
- "GET /api/v1/payment-terms should return all terms"
- "GET /api/v1/payment-terms/:id should return term"
- "PUT /api/v1/payment-terms/:id should update term"
- "DELETE /api/v1/payment-terms/:id should delete term"
- "should enforce tenant isolation"
- "should require authentication"
frontend:
component_tests:
- file: "src/widgets/payment-terms-table/ui/PaymentTermsTable.test.tsx"
test_cases:
- "should render table with terms"
- "should show days calculation"
- file: "src/features/create-payment-term/ui/CreatePaymentTermForm.test.tsx"
test_cases:
- "should validate required fields"
- "should validate days >= 0"
- "should submit valid form"
e2e_tests:
- file: "e2e/catalogs/payment-terms.spec.ts"
test_cases:
- "should create payment term successfully"
- "should edit payment term"
- "should delete payment term with confirmation"
acceptance_criteria:
- id: AC-001
description: "Usuario puede crear términos de pago con días personalizados"
status: Pending
test_reference: "test/catalogs/payment-term.controller.e2e-spec.ts:28"
- id: AC-002
description: "Sistema calcula fecha de vencimiento automáticamente"
status: Pending
test_reference: "src/modules/catalogs/services/payment-term.service.spec.ts:68"
- id: AC-003
description: "Términos comunes: Contado (0 días), 30, 60, 90 días"
status: Pending
test_reference: "test/catalogs/payment-term.controller.e2e-spec.ts:95"
business_rules:
- id: RN-001
description: "Payment term define días hasta vencimiento"
implementation: "database-design/schemas/core-schema-ddl.sql:payment_terms.days"
test_reference: "src/modules/catalogs/services/payment-term.service.spec.ts:48"
- id: RN-002
description: "Fecha vencimiento = fecha factura + días del término"
implementation: "src/modules/catalogs/services/payment-term.service.ts:calculateDueDate()"
test_reference: "src/modules/catalogs/services/payment-term.service.spec.ts:78"
- id: RN-003
description: "Partners y empresas tienen payment term por defecto"
implementation: "database-design/schemas/core-schema-ddl.sql:partners.payment_term_id"
test_reference: "src/modules/catalogs/services/payment-term.service.spec.ts:108"
dependencies:
rf_dependencies:
- RF-MGN-002-001
module_dependencies:
- MGN-002
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: 30
total_components: 24
total_tables: 9
total_test_cases: 120
estimated_duration_sprints: 2