965 lines
36 KiB
YAML
965 lines
36 KiB
YAML
# TRACEABILITY-MGN-010.yaml
|
|
# Matriz de Trazabilidad - MGN-010: RRHH Básico
|
|
# Fecha: 2025-11-24
|
|
# Versión: 1.0
|
|
|
|
module:
|
|
id: MGN-010
|
|
name: "RRHH Básico"
|
|
description: "Gestión de recursos humanos: empleados, departamentos, contratos, asistencias y ausencias"
|
|
priority: P1
|
|
story_points: 29
|
|
status: Diseñado
|
|
|
|
metadata:
|
|
total_rf: 5
|
|
total_et_backend: 5
|
|
total_et_frontend: 5
|
|
total_tables: 6
|
|
total_tests: 100
|
|
coverage: 100%
|
|
|
|
requirements:
|
|
- rf_id: RF-MGN-010-001
|
|
rf_title: "Gestión de Empleados"
|
|
rf_file: "requerimientos-funcionales/mgn-010/RF-MGN-010-001-gestión-de-empleados.md"
|
|
priority: P1
|
|
story_points: 8
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-010/ET-BACKEND-MGN-010-001-gestión-de-empleados.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/hr/employees
|
|
description: "Crear nuevo empleado"
|
|
- method: GET
|
|
path: /api/v1/hr/employees
|
|
description: "Listar todos los empleados"
|
|
- method: GET
|
|
path: /api/v1/hr/employees/:id
|
|
description: "Obtener empleado por ID"
|
|
- method: PUT
|
|
path: /api/v1/hr/employees/:id
|
|
description: "Actualizar empleado"
|
|
- method: DELETE
|
|
path: /api/v1/hr/employees/:id
|
|
description: "Eliminar empleado (soft delete)"
|
|
services:
|
|
- name: "EmployeeService"
|
|
file: "src/modules/hr/services/employee.service.ts"
|
|
methods:
|
|
- create
|
|
- findAll
|
|
- findOne
|
|
- update
|
|
- remove
|
|
- validateBusinessRules
|
|
controllers:
|
|
- name: "EmployeeController"
|
|
file: "src/modules/hr/controllers/employee.controller.ts"
|
|
dtos:
|
|
- name: "CreateEmployeeDto"
|
|
file: "src/modules/hr/dto/create-employee.dto.ts"
|
|
- name: "UpdateEmployeeDto"
|
|
file: "src/modules/hr/dto/update-employee.dto.ts"
|
|
- name: "EmployeeResponseDto"
|
|
file: "src/modules/hr/dto/employee-response.dto.ts"
|
|
- name: "FilterEmployeeDto"
|
|
file: "src/modules/hr/dto/filter-employee.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-010/ET-FRONTEND-MGN-010-001-gestión-de-empleados.md"
|
|
routes:
|
|
- path: "/hr/employees"
|
|
component: "EmployeesPage"
|
|
- path: "/hr/employees/create"
|
|
component: "CreateEmployeePage"
|
|
- path: "/hr/employees/:id/edit"
|
|
component: "EditEmployeePage"
|
|
- path: "/hr/employees/:id"
|
|
component: "ViewEmployeePage"
|
|
components:
|
|
- name: "EmployeesTable"
|
|
file: "src/widgets/employees-table/ui/EmployeesTable.tsx"
|
|
type: widget
|
|
- name: "CreateEmployeeForm"
|
|
file: "src/features/create-employee/ui/CreateEmployeeForm.tsx"
|
|
type: feature
|
|
- name: "EmployeeCard"
|
|
file: "src/entities/employee/ui/EmployeeCard.tsx"
|
|
type: entity
|
|
- name: "EmployeePage"
|
|
file: "src/pages/hr/EmployeePage.tsx"
|
|
type: page
|
|
api_client:
|
|
- name: "employeeApi"
|
|
file: "src/entities/employee/api/employee.api.ts"
|
|
methods:
|
|
- getAll
|
|
- getById
|
|
- create
|
|
- update
|
|
- delete
|
|
state_management:
|
|
- name: "useEmployeeStore"
|
|
file: "src/entities/employee/model/employee.store.ts"
|
|
type: zustand
|
|
- name: "useEmployees"
|
|
file: "src/entities/employee/api/employee.queries.ts"
|
|
type: react-query
|
|
|
|
database_tables:
|
|
- schema: hr
|
|
table: employees
|
|
file: "database-design/schemas/hr-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
- DELETE (soft)
|
|
indices:
|
|
- idx_employees_tenant_id
|
|
- idx_employees_user_id
|
|
- idx_employees_department_id
|
|
- idx_employees_status
|
|
rls_policy: tenant_isolation_employees
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/hr/services/employee.service.spec.ts"
|
|
test_cases:
|
|
- "should create employee with valid data"
|
|
- "should link employee to user account"
|
|
- "should validate unique employee number"
|
|
- "should find all employees for tenant"
|
|
- "should update employee successfully"
|
|
integration_tests:
|
|
- file: "test/hr/employee.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/hr/employees should create employee"
|
|
- "GET /api/v1/hr/employees should return all employees"
|
|
- "GET /api/v1/hr/employees/:id should return employee"
|
|
- "PUT /api/v1/hr/employees/:id should update employee"
|
|
- "DELETE /api/v1/hr/employees/:id should soft delete employee"
|
|
- "should enforce tenant isolation"
|
|
- "should require authentication"
|
|
- "should check permissions"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/employees-table/ui/EmployeesTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with employees"
|
|
- "should handle pagination"
|
|
- "should filter by department"
|
|
- file: "src/features/create-employee/ui/CreateEmployeeForm.test.tsx"
|
|
test_cases:
|
|
- "should validate required fields"
|
|
- "should submit valid form"
|
|
- "should show error messages"
|
|
e2e_tests:
|
|
- file: "e2e/hr/employees.spec.ts"
|
|
test_cases:
|
|
- "should create employee successfully"
|
|
- "should edit employee successfully"
|
|
- "should delete employee with confirmation"
|
|
- "should enforce permissions"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Usuario puede crear empleados con datos personales y laborales"
|
|
status: Pending
|
|
test_reference: "test/hr/employee.controller.e2e-spec.ts:35"
|
|
- id: AC-002
|
|
description: "Empleado puede vincularse a cuenta de usuario"
|
|
status: Pending
|
|
test_reference: "src/modules/hr/services/employee.service.spec.ts:72"
|
|
- id: AC-003
|
|
description: "Número de empleado es único por tenant"
|
|
status: Pending
|
|
test_reference: "test/hr/employee.controller.e2e-spec.ts:108"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Número de empleado debe ser único por tenant"
|
|
implementation: "database-design/schemas/hr-schema-ddl.sql:CONSTRAINT uq_employees_number_tenant"
|
|
test_reference: "src/modules/hr/services/employee.service.spec.ts:95"
|
|
- id: RN-002
|
|
description: "Empleado puede vincularse a un usuario del sistema"
|
|
implementation: "src/modules/hr/services/employee.service.ts:linkUser()"
|
|
test_reference: "src/modules/hr/services/employee.service.spec.ts:122"
|
|
- id: RN-003
|
|
description: "Fecha de ingreso no puede ser posterior a fecha actual"
|
|
implementation: "src/modules/hr/dto/create-employee.dto.ts:@MaxDate(today)"
|
|
test_reference: "src/modules/hr/services/employee.service.spec.ts:148"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-001-003
|
|
module_dependencies:
|
|
- MGN-001
|
|
external_dependencies:
|
|
- name: "@nestjs/common"
|
|
version: "^10.0.0"
|
|
- name: "ant-design"
|
|
version: "^5.0.0"
|
|
|
|
- rf_id: RF-MGN-010-002
|
|
rf_title: "Departamentos y Puestos"
|
|
rf_file: "requerimientos-funcionales/mgn-010/RF-MGN-010-002-departamentos-y-puestos.md"
|
|
priority: P1
|
|
story_points: 3
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-010/ET-BACKEND-MGN-010-002-departamentos-y-puestos.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/hr/departments
|
|
description: "Crear nuevo departamento"
|
|
- method: GET
|
|
path: /api/v1/hr/departments
|
|
description: "Listar todos los departamentos"
|
|
- method: GET
|
|
path: /api/v1/hr/departments/:id
|
|
description: "Obtener departamento por ID"
|
|
- method: PUT
|
|
path: /api/v1/hr/departments/:id
|
|
description: "Actualizar departamento"
|
|
- method: DELETE
|
|
path: /api/v1/hr/departments/:id
|
|
description: "Eliminar departamento (soft delete)"
|
|
services:
|
|
- name: "DepartmentService"
|
|
file: "src/modules/hr/services/department.service.ts"
|
|
methods:
|
|
- create
|
|
- findAll
|
|
- findOne
|
|
- update
|
|
- remove
|
|
- validateBusinessRules
|
|
controllers:
|
|
- name: "DepartmentController"
|
|
file: "src/modules/hr/controllers/department.controller.ts"
|
|
dtos:
|
|
- name: "CreateDepartmentDto"
|
|
file: "src/modules/hr/dto/create-department.dto.ts"
|
|
- name: "UpdateDepartmentDto"
|
|
file: "src/modules/hr/dto/update-department.dto.ts"
|
|
- name: "DepartmentResponseDto"
|
|
file: "src/modules/hr/dto/department-response.dto.ts"
|
|
- name: "FilterDepartmentDto"
|
|
file: "src/modules/hr/dto/filter-department.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-010/ET-FRONTEND-MGN-010-002-departamentos-y-puestos.md"
|
|
routes:
|
|
- path: "/hr/departments"
|
|
component: "DepartmentsPage"
|
|
- path: "/hr/departments/create"
|
|
component: "CreateDepartmentPage"
|
|
- path: "/hr/departments/:id/edit"
|
|
component: "EditDepartmentPage"
|
|
- path: "/hr/departments/:id"
|
|
component: "ViewDepartmentPage"
|
|
components:
|
|
- name: "DepartmentsTable"
|
|
file: "src/widgets/departments-table/ui/DepartmentsTable.tsx"
|
|
type: widget
|
|
- name: "CreateDepartmentForm"
|
|
file: "src/features/create-department/ui/CreateDepartmentForm.tsx"
|
|
type: feature
|
|
- name: "DepartmentCard"
|
|
file: "src/entities/department/ui/DepartmentCard.tsx"
|
|
type: entity
|
|
- name: "DepartmentPage"
|
|
file: "src/pages/hr/DepartmentPage.tsx"
|
|
type: page
|
|
api_client:
|
|
- name: "departmentApi"
|
|
file: "src/entities/department/api/department.api.ts"
|
|
methods:
|
|
- getAll
|
|
- getById
|
|
- create
|
|
- update
|
|
- delete
|
|
state_management:
|
|
- name: "useDepartmentStore"
|
|
file: "src/entities/department/model/department.store.ts"
|
|
type: zustand
|
|
- name: "useDepartments"
|
|
file: "src/entities/department/api/department.queries.ts"
|
|
type: react-query
|
|
|
|
database_tables:
|
|
- schema: hr
|
|
table: departments
|
|
file: "database-design/schemas/hr-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
- DELETE (soft)
|
|
indices:
|
|
- idx_departments_tenant_id
|
|
- idx_departments_parent_id
|
|
rls_policy: tenant_isolation_departments
|
|
- schema: hr
|
|
table: jobs
|
|
file: "database-design/schemas/hr-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
- DELETE (soft)
|
|
indices:
|
|
- idx_jobs_tenant_id
|
|
- idx_jobs_department_id
|
|
rls_policy: tenant_isolation_jobs
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/hr/services/department.service.spec.ts"
|
|
test_cases:
|
|
- "should create department with valid data"
|
|
- "should support hierarchical departments"
|
|
- "should create job positions in department"
|
|
- "should find all departments for tenant"
|
|
- "should update department successfully"
|
|
integration_tests:
|
|
- file: "test/hr/department.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/hr/departments should create department"
|
|
- "GET /api/v1/hr/departments should return all departments"
|
|
- "GET /api/v1/hr/departments/:id should return department"
|
|
- "PUT /api/v1/hr/departments/:id should update department"
|
|
- "DELETE /api/v1/hr/departments/:id should soft delete department"
|
|
- "should enforce tenant isolation"
|
|
- "should require authentication"
|
|
- "should check permissions"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/departments-table/ui/DepartmentsTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with departments"
|
|
- "should show hierarchy tree"
|
|
- "should call delete on button click"
|
|
- file: "src/features/create-department/ui/CreateDepartmentForm.test.tsx"
|
|
test_cases:
|
|
- "should validate required fields"
|
|
- "should submit valid form"
|
|
- "should show error messages"
|
|
e2e_tests:
|
|
- file: "e2e/hr/departments.spec.ts"
|
|
test_cases:
|
|
- "should create department successfully"
|
|
- "should create sub-department"
|
|
- "should delete department with confirmation"
|
|
- "should enforce permissions"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Usuario puede crear departamentos con estructura jerárquica"
|
|
status: Pending
|
|
test_reference: "test/hr/department.controller.e2e-spec.ts:38"
|
|
- id: AC-002
|
|
description: "Cada departamento puede tener múltiples puestos de trabajo"
|
|
status: Pending
|
|
test_reference: "src/modules/hr/services/department.service.spec.ts:75"
|
|
- id: AC-003
|
|
description: "Departamento con empleados no puede ser eliminado"
|
|
status: Pending
|
|
test_reference: "test/hr/department.controller.e2e-spec.ts:112"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Nombre de departamento debe ser único por tenant"
|
|
implementation: "database-design/schemas/hr-schema-ddl.sql:CONSTRAINT uq_departments_name_tenant"
|
|
test_reference: "src/modules/hr/services/department.service.spec.ts:98"
|
|
- id: RN-002
|
|
description: "Departamento puede tener departamentos hijos (jerarquía)"
|
|
implementation: "database-design/schemas/hr-schema-ddl.sql:parent_id column"
|
|
test_reference: "src/modules/hr/services/department.service.spec.ts:125"
|
|
- id: RN-003
|
|
description: "No se puede eliminar departamento con empleados asignados"
|
|
implementation: "src/modules/hr/services/department.service.ts:remove()"
|
|
test_reference: "src/modules/hr/services/department.service.spec.ts:152"
|
|
|
|
dependencies:
|
|
rf_dependencies: []
|
|
module_dependencies:
|
|
- MGN-001
|
|
external_dependencies:
|
|
- name: "@nestjs/common"
|
|
version: "^10.0.0"
|
|
|
|
- rf_id: RF-MGN-010-003
|
|
rf_title: "Contratos Laborales"
|
|
rf_file: "requerimientos-funcionales/mgn-010/RF-MGN-010-003-contratos-laborales.md"
|
|
priority: P1
|
|
story_points: 5
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-010/ET-BACKEND-MGN-010-003-contratos-laborales.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/hr/contracts
|
|
description: "Crear nuevo contrato"
|
|
- method: GET
|
|
path: /api/v1/hr/contracts
|
|
description: "Listar todos los contratos"
|
|
- method: GET
|
|
path: /api/v1/hr/contracts/:id
|
|
description: "Obtener contrato por ID"
|
|
- method: PUT
|
|
path: /api/v1/hr/contracts/:id
|
|
description: "Actualizar contrato"
|
|
- method: POST
|
|
path: /api/v1/hr/contracts/:id/terminate
|
|
description: "Terminar contrato"
|
|
services:
|
|
- name: "ContractService"
|
|
file: "src/modules/hr/services/contract.service.ts"
|
|
methods:
|
|
- create
|
|
- findAll
|
|
- findOne
|
|
- update
|
|
- terminate
|
|
- validateBusinessRules
|
|
controllers:
|
|
- name: "ContractController"
|
|
file: "src/modules/hr/controllers/contract.controller.ts"
|
|
dtos:
|
|
- name: "CreateContractDto"
|
|
file: "src/modules/hr/dto/create-contract.dto.ts"
|
|
- name: "UpdateContractDto"
|
|
file: "src/modules/hr/dto/update-contract.dto.ts"
|
|
- name: "ContractResponseDto"
|
|
file: "src/modules/hr/dto/contract-response.dto.ts"
|
|
- name: "TerminateContractDto"
|
|
file: "src/modules/hr/dto/terminate-contract.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-010/ET-FRONTEND-MGN-010-003-contratos-laborales.md"
|
|
routes:
|
|
- path: "/hr/contracts"
|
|
component: "ContractsPage"
|
|
- path: "/hr/contracts/create"
|
|
component: "CreateContractPage"
|
|
- path: "/hr/contracts/:id/edit"
|
|
component: "EditContractPage"
|
|
- path: "/hr/contracts/:id"
|
|
component: "ViewContractPage"
|
|
components:
|
|
- name: "ContractsTable"
|
|
file: "src/widgets/contracts-table/ui/ContractsTable.tsx"
|
|
type: widget
|
|
- name: "CreateContractForm"
|
|
file: "src/features/create-contract/ui/CreateContractForm.tsx"
|
|
type: feature
|
|
- name: "ContractCard"
|
|
file: "src/entities/contract/ui/ContractCard.tsx"
|
|
type: entity
|
|
- name: "ContractPage"
|
|
file: "src/pages/hr/ContractPage.tsx"
|
|
type: page
|
|
api_client:
|
|
- name: "contractApi"
|
|
file: "src/entities/contract/api/contract.api.ts"
|
|
methods:
|
|
- getAll
|
|
- getById
|
|
- create
|
|
- update
|
|
- terminate
|
|
state_management:
|
|
- name: "useContractStore"
|
|
file: "src/entities/contract/model/contract.store.ts"
|
|
type: zustand
|
|
- name: "useContracts"
|
|
file: "src/entities/contract/api/contract.queries.ts"
|
|
type: react-query
|
|
|
|
database_tables:
|
|
- schema: hr
|
|
table: contracts
|
|
file: "database-design/schemas/hr-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
- DELETE (soft)
|
|
indices:
|
|
- idx_contracts_tenant_id
|
|
- idx_contracts_employee_id
|
|
- idx_contracts_status
|
|
- idx_contracts_end_date
|
|
rls_policy: tenant_isolation_contracts
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/hr/services/contract.service.spec.ts"
|
|
test_cases:
|
|
- "should create contract with valid data"
|
|
- "should validate date range consistency"
|
|
- "should terminate contract with reason"
|
|
- "should find all contracts for tenant"
|
|
- "should update contract successfully"
|
|
integration_tests:
|
|
- file: "test/hr/contract.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/hr/contracts should create contract"
|
|
- "GET /api/v1/hr/contracts should return all contracts"
|
|
- "GET /api/v1/hr/contracts/:id should return contract"
|
|
- "PUT /api/v1/hr/contracts/:id should update contract"
|
|
- "POST /api/v1/hr/contracts/:id/terminate should terminate contract"
|
|
- "should enforce tenant isolation"
|
|
- "should require authentication"
|
|
- "should check permissions"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/contracts-table/ui/ContractsTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with contracts"
|
|
- "should filter by status"
|
|
- "should show expiring contracts"
|
|
- file: "src/features/create-contract/ui/CreateContractForm.test.tsx"
|
|
test_cases:
|
|
- "should validate required fields"
|
|
- "should submit valid form"
|
|
- "should show error messages"
|
|
e2e_tests:
|
|
- file: "e2e/hr/contracts.spec.ts"
|
|
test_cases:
|
|
- "should create contract successfully"
|
|
- "should terminate contract with reason"
|
|
- "should show expiring contract alerts"
|
|
- "should enforce permissions"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Usuario puede crear contratos con tipo, fecha inicio y fin"
|
|
status: Pending
|
|
test_reference: "test/hr/contract.controller.e2e-spec.ts:42"
|
|
- id: AC-002
|
|
description: "Sistema alerta sobre contratos próximos a vencer"
|
|
status: Pending
|
|
test_reference: "src/modules/hr/services/contract.service.spec.ts:78"
|
|
- id: AC-003
|
|
description: "Contrato terminado registra fecha y razón"
|
|
status: Pending
|
|
test_reference: "test/hr/contract.controller.e2e-spec.ts:115"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Fecha de fin debe ser posterior a fecha de inicio"
|
|
implementation: "src/modules/hr/dto/create-contract.dto.ts:@IsDateAfter('start_date')"
|
|
test_reference: "src/modules/hr/services/contract.service.spec.ts:102"
|
|
- id: RN-002
|
|
description: "Empleado solo puede tener un contrato activo a la vez"
|
|
implementation: "src/modules/hr/services/contract.service.ts:validateActiveContract()"
|
|
test_reference: "src/modules/hr/services/contract.service.spec.ts:128"
|
|
- id: RN-003
|
|
description: "Alerta si contrato vence en menos de 30 días"
|
|
implementation: "src/modules/hr/services/contract.service.ts:checkExpiringContracts()"
|
|
test_reference: "src/modules/hr/services/contract.service.spec.ts:155"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-010-001
|
|
module_dependencies:
|
|
- MGN-001
|
|
- MGN-014
|
|
external_dependencies:
|
|
- name: "@nestjs/schedule"
|
|
version: "^4.0.0"
|
|
|
|
- rf_id: RF-MGN-010-004
|
|
rf_title: "Asistencias (Check-in/Check-out)"
|
|
rf_file: "requerimientos-funcionales/mgn-010/RF-MGN-010-004-asistencias-check-in-check-out.md"
|
|
priority: P1
|
|
story_points: 5
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-010/ET-BACKEND-MGN-010-004-asistencias-check-in-check-out.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/hr/attendances/check-in
|
|
description: "Registrar entrada (check-in)"
|
|
- method: POST
|
|
path: /api/v1/hr/attendances/check-out
|
|
description: "Registrar salida (check-out)"
|
|
- method: GET
|
|
path: /api/v1/hr/attendances
|
|
description: "Listar asistencias"
|
|
- method: GET
|
|
path: /api/v1/hr/attendances/:id
|
|
description: "Obtener asistencia por ID"
|
|
- method: GET
|
|
path: /api/v1/hr/attendances/report
|
|
description: "Reporte de asistencias"
|
|
services:
|
|
- name: "AttendanceService"
|
|
file: "src/modules/hr/services/attendance.service.ts"
|
|
methods:
|
|
- checkIn
|
|
- checkOut
|
|
- findAll
|
|
- findOne
|
|
- getReport
|
|
- validateBusinessRules
|
|
controllers:
|
|
- name: "AttendanceController"
|
|
file: "src/modules/hr/controllers/attendance.controller.ts"
|
|
dtos:
|
|
- name: "CheckInDto"
|
|
file: "src/modules/hr/dto/check-in.dto.ts"
|
|
- name: "CheckOutDto"
|
|
file: "src/modules/hr/dto/check-out.dto.ts"
|
|
- name: "AttendanceResponseDto"
|
|
file: "src/modules/hr/dto/attendance-response.dto.ts"
|
|
- name: "AttendanceReportDto"
|
|
file: "src/modules/hr/dto/attendance-report.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-010/ET-FRONTEND-MGN-010-004-asistencias-check-in-check-out.md"
|
|
routes:
|
|
- path: "/hr/attendances"
|
|
component: "AttendancesPage"
|
|
- path: "/hr/attendances/report"
|
|
component: "AttendanceReportPage"
|
|
components:
|
|
- name: "AttendanceWidget"
|
|
file: "src/widgets/attendance-widget/ui/AttendanceWidget.tsx"
|
|
type: widget
|
|
- name: "CheckInButton"
|
|
file: "src/features/check-in/ui/CheckInButton.tsx"
|
|
type: feature
|
|
- name: "AttendanceTable"
|
|
file: "src/entities/attendance/ui/AttendanceTable.tsx"
|
|
type: entity
|
|
- name: "AttendancePage"
|
|
file: "src/pages/hr/AttendancePage.tsx"
|
|
type: page
|
|
api_client:
|
|
- name: "attendanceApi"
|
|
file: "src/entities/attendance/api/attendance.api.ts"
|
|
methods:
|
|
- checkIn
|
|
- checkOut
|
|
- getAll
|
|
- getById
|
|
- getReport
|
|
state_management:
|
|
- name: "useAttendanceStore"
|
|
file: "src/entities/attendance/model/attendance.store.ts"
|
|
type: zustand
|
|
- name: "useAttendances"
|
|
file: "src/entities/attendance/api/attendance.queries.ts"
|
|
type: react-query
|
|
|
|
database_tables:
|
|
- schema: hr
|
|
table: attendances
|
|
file: "database-design/schemas/hr-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
indices:
|
|
- idx_attendances_tenant_id
|
|
- idx_attendances_employee_id
|
|
- idx_attendances_check_in
|
|
- idx_attendances_check_out
|
|
rls_policy: tenant_isolation_attendances
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/hr/services/attendance.service.spec.ts"
|
|
test_cases:
|
|
- "should register check-in successfully"
|
|
- "should register check-out with duration calculation"
|
|
- "should prevent double check-in"
|
|
- "should generate attendance report"
|
|
- "should calculate total hours worked"
|
|
integration_tests:
|
|
- file: "test/hr/attendance.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/hr/attendances/check-in should check in"
|
|
- "POST /api/v1/hr/attendances/check-out should check out"
|
|
- "GET /api/v1/hr/attendances should return attendances"
|
|
- "GET /api/v1/hr/attendances/:id should return attendance"
|
|
- "GET /api/v1/hr/attendances/report should return report"
|
|
- "should enforce tenant isolation"
|
|
- "should require authentication"
|
|
- "should check permissions"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/attendance-widget/ui/AttendanceWidget.test.tsx"
|
|
test_cases:
|
|
- "should show check-in button when not checked in"
|
|
- "should show check-out button when checked in"
|
|
- "should display current session duration"
|
|
- file: "src/features/check-in/ui/CheckInButton.test.tsx"
|
|
test_cases:
|
|
- "should call check-in API"
|
|
- "should show success message"
|
|
- "should handle errors"
|
|
e2e_tests:
|
|
- file: "e2e/hr/attendances.spec.ts"
|
|
test_cases:
|
|
- "should check in successfully"
|
|
- "should check out successfully"
|
|
- "should view attendance report"
|
|
- "should enforce permissions"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Empleado puede registrar entrada y salida diaria"
|
|
status: Pending
|
|
test_reference: "test/hr/attendance.controller.e2e-spec.ts:45"
|
|
- id: AC-002
|
|
description: "Sistema calcula automáticamente horas trabajadas"
|
|
status: Pending
|
|
test_reference: "src/modules/hr/services/attendance.service.spec.ts:82"
|
|
- id: AC-003
|
|
description: "Reportes muestran asistencias por período y empleado"
|
|
status: Pending
|
|
test_reference: "test/hr/attendance.controller.e2e-spec.ts:118"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Empleado no puede hacer check-in si ya tiene uno activo"
|
|
implementation: "src/modules/hr/services/attendance.service.ts:validateCheckIn()"
|
|
test_reference: "src/modules/hr/services/attendance.service.spec.ts:108"
|
|
- id: RN-002
|
|
description: "Horas trabajadas = check-out - check-in"
|
|
implementation: "src/modules/hr/services/attendance.service.ts:calculateDuration()"
|
|
test_reference: "src/modules/hr/services/attendance.service.spec.ts:135"
|
|
- id: RN-003
|
|
description: "Check-out debe ser posterior a check-in del mismo día"
|
|
implementation: "src/modules/hr/services/attendance.service.ts:checkOut()"
|
|
test_reference: "src/modules/hr/services/attendance.service.spec.ts:162"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-010-001
|
|
module_dependencies:
|
|
- MGN-001
|
|
external_dependencies:
|
|
- name: "@nestjs/common"
|
|
version: "^10.0.0"
|
|
- name: "date-fns"
|
|
version: "^2.30.0"
|
|
|
|
- rf_id: RF-MGN-010-005
|
|
rf_title: "Ausencias y Permisos"
|
|
rf_file: "requerimientos-funcionales/mgn-010/RF-MGN-010-005-ausencias-y-permisos.md"
|
|
priority: P1
|
|
story_points: 8
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-010/ET-BACKEND-MGN-010-005-ausencias-y-permisos.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/hr/leaves
|
|
description: "Solicitar ausencia/permiso"
|
|
- method: GET
|
|
path: /api/v1/hr/leaves
|
|
description: "Listar ausencias"
|
|
- method: GET
|
|
path: /api/v1/hr/leaves/:id
|
|
description: "Obtener ausencia por ID"
|
|
- method: POST
|
|
path: /api/v1/hr/leaves/:id/approve
|
|
description: "Aprobar ausencia"
|
|
- method: POST
|
|
path: /api/v1/hr/leaves/:id/reject
|
|
description: "Rechazar ausencia"
|
|
services:
|
|
- name: "LeaveService"
|
|
file: "src/modules/hr/services/leave.service.ts"
|
|
methods:
|
|
- create
|
|
- findAll
|
|
- findOne
|
|
- approve
|
|
- reject
|
|
- validateBusinessRules
|
|
controllers:
|
|
- name: "LeaveController"
|
|
file: "src/modules/hr/controllers/leave.controller.ts"
|
|
dtos:
|
|
- name: "CreateLeaveDto"
|
|
file: "src/modules/hr/dto/create-leave.dto.ts"
|
|
- name: "LeaveResponseDto"
|
|
file: "src/modules/hr/dto/leave-response.dto.ts"
|
|
- name: "ApproveLeaveDto"
|
|
file: "src/modules/hr/dto/approve-leave.dto.ts"
|
|
- name: "RejectLeaveDto"
|
|
file: "src/modules/hr/dto/reject-leave.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-010/ET-FRONTEND-MGN-010-005-ausencias-y-permisos.md"
|
|
routes:
|
|
- path: "/hr/leaves"
|
|
component: "LeavesPage"
|
|
- path: "/hr/leaves/request"
|
|
component: "RequestLeavePage"
|
|
- path: "/hr/leaves/:id"
|
|
component: "ViewLeavePage"
|
|
components:
|
|
- name: "LeavesTable"
|
|
file: "src/widgets/leaves-table/ui/LeavesTable.tsx"
|
|
type: widget
|
|
- name: "RequestLeaveForm"
|
|
file: "src/features/request-leave/ui/RequestLeaveForm.tsx"
|
|
type: feature
|
|
- name: "LeaveCard"
|
|
file: "src/entities/leave/ui/LeaveCard.tsx"
|
|
type: entity
|
|
- name: "LeavePage"
|
|
file: "src/pages/hr/LeavePage.tsx"
|
|
type: page
|
|
api_client:
|
|
- name: "leaveApi"
|
|
file: "src/entities/leave/api/leave.api.ts"
|
|
methods:
|
|
- getAll
|
|
- getById
|
|
- request
|
|
- approve
|
|
- reject
|
|
state_management:
|
|
- name: "useLeaveStore"
|
|
file: "src/entities/leave/model/leave.store.ts"
|
|
type: zustand
|
|
- name: "useLeaves"
|
|
file: "src/entities/leave/api/leave.queries.ts"
|
|
type: react-query
|
|
|
|
database_tables:
|
|
- schema: hr
|
|
table: leaves
|
|
file: "database-design/schemas/hr-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
- INSERT
|
|
- UPDATE
|
|
- DELETE (soft)
|
|
indices:
|
|
- idx_leaves_tenant_id
|
|
- idx_leaves_employee_id
|
|
- idx_leaves_status
|
|
- idx_leaves_date_from
|
|
rls_policy: tenant_isolation_leaves
|
|
- schema: hr
|
|
table: leave_types
|
|
file: "database-design/schemas/hr-schema-ddl.sql"
|
|
operations:
|
|
- SELECT
|
|
indices:
|
|
- idx_leave_types_tenant_id
|
|
rls_policy: tenant_isolation_leave_types
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/hr/services/leave.service.spec.ts"
|
|
test_cases:
|
|
- "should create leave request successfully"
|
|
- "should validate leave balance before approval"
|
|
- "should approve leave request"
|
|
- "should reject leave request with reason"
|
|
- "should check overlapping leave requests"
|
|
integration_tests:
|
|
- file: "test/hr/leave.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/hr/leaves should request leave"
|
|
- "GET /api/v1/hr/leaves should return leaves"
|
|
- "GET /api/v1/hr/leaves/:id should return leave"
|
|
- "POST /api/v1/hr/leaves/:id/approve should approve"
|
|
- "POST /api/v1/hr/leaves/:id/reject should reject"
|
|
- "should enforce tenant isolation"
|
|
- "should require authentication"
|
|
- "should check permissions"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/leaves-table/ui/LeavesTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with leaves"
|
|
- "should filter by status"
|
|
- "should show pending approvals"
|
|
- file: "src/features/request-leave/ui/RequestLeaveForm.test.tsx"
|
|
test_cases:
|
|
- "should validate date range"
|
|
- "should submit valid form"
|
|
- "should show balance warning"
|
|
e2e_tests:
|
|
- file: "e2e/hr/leaves.spec.ts"
|
|
test_cases:
|
|
- "should request leave successfully"
|
|
- "should approve leave as manager"
|
|
- "should reject leave with reason"
|
|
- "should enforce permissions"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Empleado puede solicitar ausencias con fecha y tipo"
|
|
status: Pending
|
|
test_reference: "test/hr/leave.controller.e2e-spec.ts:48"
|
|
- id: AC-002
|
|
description: "Manager puede aprobar o rechazar solicitudes"
|
|
status: Pending
|
|
test_reference: "src/modules/hr/services/leave.service.spec.ts:85"
|
|
- id: AC-003
|
|
description: "Sistema valida balance de días disponibles"
|
|
status: Pending
|
|
test_reference: "test/hr/leave.controller.e2e-spec.ts:122"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Fecha de fin debe ser posterior o igual a fecha de inicio"
|
|
implementation: "src/modules/hr/dto/create-leave.dto.ts:@IsDateAfterOrEqual('date_from')"
|
|
test_reference: "src/modules/hr/services/leave.service.spec.ts:112"
|
|
- id: RN-002
|
|
description: "Sistema valida que empleado tenga días disponibles"
|
|
implementation: "src/modules/hr/services/leave.service.ts:validateBalance()"
|
|
test_reference: "src/modules/hr/services/leave.service.spec.ts:138"
|
|
- id: RN-003
|
|
description: "No se permiten solicitudes de ausencia superpuestas"
|
|
implementation: "src/modules/hr/services/leave.service.ts:checkOverlap()"
|
|
test_reference: "src/modules/hr/services/leave.service.spec.ts:165"
|
|
|
|
dependencies:
|
|
rf_dependencies:
|
|
- RF-MGN-010-001
|
|
module_dependencies:
|
|
- MGN-001
|
|
- MGN-014
|
|
external_dependencies:
|
|
- name: "@nestjs/common"
|
|
version: "^10.0.0"
|
|
- name: "date-fns"
|
|
version: "^2.30.0"
|
|
|
|
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: 6
|
|
total_test_cases: 100
|
|
estimated_duration_sprints: 2
|