# 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