mirror of
https://github.com/marcogll/hr_soul23.git
synced 2026-01-13 13:25:16 +00:00
feat: Set up database and data models
This commit establishes the database foundation for the HR Platform. Key changes include: - Defined the complete data schema in `docs/API_CONTRACTS.md` to serve as a single source of truth. - Integrated `knex.js` with `sqlite3` to manage the database connection and schema. - Implemented a version-controlled migration system and created initial migrations for all required tables. - Created seed files to populate the database with sample data for development. - Addressed security feedback by using `bcrypt` to hash user passwords in the seed data and adding the SQLite database file to `.gitignore`.
This commit is contained in:
@@ -1,9 +0,0 @@
|
|||||||
# Application configuration
|
|
||||||
NODE_ENV=development
|
|
||||||
|
|
||||||
# Database configuration
|
|
||||||
DB_HOST=db
|
|
||||||
DB_USER=postgres
|
|
||||||
DB_PASSWORD=secret
|
|
||||||
DB_NAME=hr_platform
|
|
||||||
DB_PORT=5432
|
|
||||||
76
.gitignore
vendored
76
.gitignore
vendored
@@ -1,74 +1,2 @@
|
|||||||
# Dependencies
|
node_modules
|
||||||
/node_modules
|
*.sqlite3
|
||||||
|
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-temporary-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# TypeScript v1 declaration files
|
|
||||||
typings/
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Microbundle cache
|
|
||||||
.rpt2_cache/
|
|
||||||
.rts2_cache_cjs/
|
|
||||||
.rts2_cache_es/
|
|
||||||
.rts2_cache_umd/
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
.env.test
|
|
||||||
.env.production
|
|
||||||
|
|
||||||
# Mac files
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# VSCode files
|
|
||||||
.vscode/
|
|
||||||
|
|||||||
21
Dockerfile
21
Dockerfile
@@ -1,21 +0,0 @@
|
|||||||
# Usar una imagen base oficial de Node.js
|
|
||||||
ARG NODE_VERSION=18
|
|
||||||
FROM node:${NODE_VERSION}-alpine
|
|
||||||
|
|
||||||
# Establecer el directorio de trabajo en el contenedor
|
|
||||||
WORKDIR /usr/src/app
|
|
||||||
|
|
||||||
# Copiar package.json y package-lock.json (si existe)
|
|
||||||
COPY package*.json ./
|
|
||||||
|
|
||||||
# Instalar las dependencias del proyecto
|
|
||||||
RUN npm install
|
|
||||||
|
|
||||||
# Copiar el resto del código de la aplicación
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Exponer el puerto en el que la aplicación se ejecutará
|
|
||||||
EXPOSE 3011
|
|
||||||
|
|
||||||
# Comando para iniciar la aplicación
|
|
||||||
CMD [ "node", "src/index.js" ]
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
|
||||||
api:
|
|
||||||
build: .
|
|
||||||
ports:
|
|
||||||
- "3011:3011"
|
|
||||||
volumes:
|
|
||||||
- .:/usr/src/app
|
|
||||||
networks:
|
|
||||||
- app-network
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
|
|
||||||
db:
|
|
||||||
image: postgres:13
|
|
||||||
restart: always
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: ${DB_USER}
|
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
|
||||||
POSTGRES_DB: ${DB_NAME}
|
|
||||||
ports:
|
|
||||||
- "5432:5432"
|
|
||||||
volumes:
|
|
||||||
- postgres-data:/var/lib/postgresql/data
|
|
||||||
networks:
|
|
||||||
- app-network
|
|
||||||
|
|
||||||
networks:
|
|
||||||
app-network:
|
|
||||||
driver: bridge
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
postgres-data:
|
|
||||||
@@ -1,149 +1,122 @@
|
|||||||
# Contratos de API y Modelos de Datos – HR Platform
|
# API & Data Contracts
|
||||||
|
|
||||||
Este documento define los **contratos formales** para la API REST y las estructuras de datos del sistema. Sirve como la **fuente única de verdad** para los agentes de Backend (Agente 2), Frontend (Agente 10) y Testing (Agente 11).
|
Este documento es la **única fuente de verdad** para los modelos de datos y las APIs del sistema. Todos los agentes (backend, frontend, testing) deben adherirse a estos contratos.
|
||||||
|
|
||||||
Cualquier desviación de este contrato debe ser discutida y aprobada por el Agente 0 (Arquitectura).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Modelos de Datos Principales
|
## Modelos de Datos (Tablas)
|
||||||
|
|
||||||
### 1.1 Socia (`Employee`)
|
### `socias`
|
||||||
|
|
||||||
Representa a un miembro del personal.
|
Almacena la información de las empleadas.
|
||||||
|
|
||||||
```json
|
| Columna | Tipo | Descripción |
|
||||||
{
|
|---|---|---|
|
||||||
"id": "s-a8b7c6d5",
|
| `id` | `INTEGER` | PK, Auto-increment |
|
||||||
"firstName": "Ana",
|
| `nombre` | `VARCHAR(255)` | Nombre de la socia |
|
||||||
"lastName": "García",
|
| `apellido` | `VARCHAR(255)` | Apellido de la socia |
|
||||||
"email": "ana.garcia@example.com",
|
| `fecha_ingreso`| `DATE` | Fecha de ingreso a la empresa |
|
||||||
"hireDate": "2023-01-15T10:00:00Z",
|
| `id_sucursal` | `INTEGER` | FK a `sucursales.id` |
|
||||||
"branchId": "b-f2e1d0c9",
|
| `activo` | `BOOLEAN` | `true` si está activa, `false` si no |
|
||||||
"isActive": true,
|
| `created_at` | `TIMESTAMP` | Fecha de creación del registro |
|
||||||
"createdAt": "2023-01-10T09:00:00Z",
|
| `updated_at` | `TIMESTAMP` | Fecha de última modificación |
|
||||||
"updatedAt": "2023-01-10T09:00:00Z"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.2 Vacación (`Vacation`)
|
### `sucursales`
|
||||||
|
|
||||||
Representa una solicitud de vacaciones.
|
Almacena las diferentes sucursales o centros de trabajo.
|
||||||
|
|
||||||
```json
|
| Columna | Tipo | Descripción |
|
||||||
{
|
|---|---|---|
|
||||||
"id": "v-1a2b3c4d",
|
| `id` | `INTEGER` | PK, Auto-increment |
|
||||||
"employeeId": "s-a8b7c6d5",
|
| `nombre` | `VARCHAR(255)` | Nombre de la sucursal |
|
||||||
"startDate": "2024-08-01",
|
| `direccion` | `TEXT` | Dirección física de la sucursal |
|
||||||
"endDate": "2024-08-05",
|
| `created_at` | `TIMESTAMP` | Fecha de creación del registro |
|
||||||
"daysUsed": 5,
|
| `updated_at` | `TIMESTAMP` | Fecha de última modificación |
|
||||||
"status": "APPROVED", // PENDING, APPROVED, REJECTED
|
|
||||||
"cycleYear": 2024,
|
|
||||||
"requestedAt": "2024-06-10T11:00:00Z",
|
|
||||||
"approvedBy": "u-d9e8f7g6",
|
|
||||||
"approvedAt": "2024-06-11T15:30:00Z"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.3 Permiso (`Permission`)
|
### `vacaciones`
|
||||||
|
|
||||||
Representa una solicitud de permiso (ausencia no vacacional).
|
Registra las solicitudes y periodos de vacaciones.
|
||||||
|
|
||||||
```json
|
| Columna | Tipo | Descripción |
|
||||||
{
|
|---|---|---|
|
||||||
"id": "p-5e6f7g8h",
|
| `id` | `INTEGER` | PK, Auto-increment |
|
||||||
"employeeId": "s-a8b7c6d5",
|
| `id_socia` | `INTEGER` | FK a `socias.id` |
|
||||||
"permissionDate": "2024-07-20",
|
| `fecha_inicio` | `DATE` | Inicio del periodo vacacional |
|
||||||
"hours": 4, // 0 si es día completo
|
| `fecha_fin` | `DATE` | Fin del periodo vacacional |
|
||||||
"reason": "Cita médica",
|
| `dias_tomados` | `INTEGER` | Cantidad de días hábiles |
|
||||||
"status": "APPROVED", // PENDING, APPROVED, REJECTED
|
| `estado` | `VARCHAR(50)` | `solicitada`, `aprobada`, `rechazada` |
|
||||||
"requestedAt": "2024-07-18T09:00:00Z"
|
| `ciclo_anual` | `VARCHAR(10)`| Ciclo al que corresponden, ej: "2023-2024" |
|
||||||
}
|
| `created_at` | `TIMESTAMP` | Fecha de creación del registro |
|
||||||
```
|
| `updated_at` | `TIMESTAMP` | Fecha de última modificación |
|
||||||
|
|
||||||
### 1.4 Sucursal (`Branch`)
|
### `permisos`
|
||||||
|
|
||||||
Representa una ubicación física.
|
Registra ausencias justificadas que no son vacaciones.
|
||||||
|
|
||||||
```json
|
| Columna | Tipo | Descripción |
|
||||||
{
|
|---|---|---|
|
||||||
"id": "b-f2e1d0c9",
|
| `id` | `INTEGER` | PK, Auto-increment |
|
||||||
"name": "Sucursal Centro",
|
| `id_socia` | `INTEGER` | FK a `socias.id` |
|
||||||
"address": "Av. Principal 123, Ciudad"
|
| `fecha` | `DATE` | Fecha del permiso |
|
||||||
}
|
| `horas` | `INTEGER` | `NULL` si es por día completo |
|
||||||
```
|
| `dias` | `INTEGER` | `NULL` si es por horas |
|
||||||
|
| `motivo` | `TEXT` | Razón del permiso |
|
||||||
|
| `estado` | `VARCHAR(50)` | `solicitado`, `aprobado`, `rechazado` |
|
||||||
|
| `created_at` | `TIMESTAMP` | Fecha de creación del registro |
|
||||||
|
| `updated_at` | `TIMESTAMP` | Fecha de última modificación |
|
||||||
|
|
||||||
### 1.5 Usuario (`User`)
|
### `eventos`
|
||||||
|
|
||||||
Representa un usuario del sistema con permisos.
|
Log de eventos para webhooks.
|
||||||
|
|
||||||
```json
|
| Columna | Tipo | Descripción |
|
||||||
{
|
|---|---|---|
|
||||||
"id": "u-d9e8f7g6",
|
| `id` | `INTEGER` | PK, Auto-increment |
|
||||||
"username": "admin_juan",
|
| `tipo_evento` | `VARCHAR(100)`| Ej: `vacaciones.solicitada` |
|
||||||
"role": "ADMIN", // ROOT, ADMIN, USER
|
| `payload` | `JSON` | Datos del evento |
|
||||||
"permissions": [
|
| `endpoint_url` | `VARCHAR(255)`| URL a la que se envió el webhook |
|
||||||
"socias.read",
|
| `estado_entrega`| `VARCHAR(50)` | `pendiente`, `enviado`, `fallido` |
|
||||||
"vacaciones.approve",
|
| `created_at` | `TIMESTAMP` | Fecha de creación del registro |
|
||||||
"permisos.read"
|
|
||||||
]
|
### `configuraciones`
|
||||||
}
|
|
||||||
```
|
Almacena configuraciones clave-valor para el sistema.
|
||||||
|
|
||||||
|
| Columna | Tipo | Descripción |
|
||||||
|
|---|---|---|
|
||||||
|
| `id` | `INTEGER` | PK, Auto-increment |
|
||||||
|
| `clave` | `VARCHAR(100)`| Clave única de configuración |
|
||||||
|
| `valor` | `TEXT` | Valor de la configuración |
|
||||||
|
| `descripcion` | `TEXT` | Para qué sirve la configuración |
|
||||||
|
| `created_at` | `TIMESTAMP` | Fecha de creación del registro |
|
||||||
|
| `updated_at` | `TIMESTAMP` | Fecha de última modificación |
|
||||||
|
|
||||||
|
### `usuarios`
|
||||||
|
|
||||||
|
Usuarios que pueden acceder al sistema.
|
||||||
|
|
||||||
|
| Columna | Tipo | Descripción |
|
||||||
|
|---|---|---|
|
||||||
|
| `id` | `INTEGER` | PK, Auto-increment |
|
||||||
|
| `nombre` | `VARCHAR(255)` | Nombre del usuario |
|
||||||
|
| `email` | `VARCHAR(255)`| Email único para login |
|
||||||
|
| `password_hash`| `VARCHAR(255)`| Hash de la contraseña |
|
||||||
|
| `rol` | `VARCHAR(50)` | `root`, `admin`, `user` |
|
||||||
|
| `created_at` | `TIMESTAMP` | Fecha de creación del registro |
|
||||||
|
| `updated_at` | `TIMESTAMP` | Fecha de última modificación |
|
||||||
|
|
||||||
|
### `permisos_granulares`
|
||||||
|
|
||||||
|
Define permisos específicos por usuario y recurso.
|
||||||
|
|
||||||
|
| Columna | Tipo | Descripción |
|
||||||
|
|---|---|---|
|
||||||
|
| `id` | `INTEGER` | PK, Auto-increment |
|
||||||
|
| `id_usuario` | `INTEGER` | FK a `usuarios.id` |
|
||||||
|
| `recurso` | `VARCHAR(100)`| Ej: `socias`, `vacaciones` |
|
||||||
|
| `accion` | `VARCHAR(50)` | `crear`, `leer`, `actualizar`, `eliminar` |
|
||||||
|
| `permitido` | `BOOLEAN` | `true` si tiene el permiso |
|
||||||
|
| `created_at` | `TIMESTAMP` | Fecha de creación del registro |
|
||||||
|
| `updated_at` | `TIMESTAMP` | Fecha de última modificación |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Contratos de API (Endpoints)
|
|
||||||
|
|
||||||
Todos los endpoints siguen el prefijo `/api/v1`.
|
|
||||||
|
|
||||||
### 2.1 Health Check
|
|
||||||
|
|
||||||
* **Endpoint:** `GET /health`
|
|
||||||
* **Descripción:** Verifica el estado del servicio.
|
|
||||||
* **Respuesta Exitosa (200 OK):**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": "ok",
|
|
||||||
"timestamp": "2024-01-01T12:00:00Z"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.2 Gestión de Socias (`/employees`)
|
|
||||||
|
|
||||||
* **`GET /employees`**
|
|
||||||
* **Descripción:** Obtiene una lista de todas las socias.
|
|
||||||
* **Respuesta:** `200 OK` con un array de objetos `Socia`.
|
|
||||||
|
|
||||||
* **`GET /employees/{id}`**
|
|
||||||
* **Descripción:** Obtiene una socia por su ID.
|
|
||||||
* **Respuesta:** `200 OK` con un objeto `Socia`.
|
|
||||||
|
|
||||||
* **`POST /employees`**
|
|
||||||
* **Descripción:** Crea una nueva socia.
|
|
||||||
* **Request Body:** Objeto `Socia` (sin `id`, `createdAt`, `updatedAt`).
|
|
||||||
* **Respuesta:** `201 Created` con el objeto `Socia` completo.
|
|
||||||
|
|
||||||
* **`PUT /employees/{id}`**
|
|
||||||
* **Descripción:** Actualiza una socia existente.
|
|
||||||
* **Request Body:** Objeto `Socia` con los campos a actualizar.
|
|
||||||
* **Respuesta:** `200 OK` con el objeto `Socia` actualizado.
|
|
||||||
|
|
||||||
### 2.3 Gestión de Vacaciones (`/vacations`)
|
|
||||||
|
|
||||||
* **`GET /employees/{employeeId}/vacations`**
|
|
||||||
* **Descripción:** Obtiene el historial de vacaciones de una socia.
|
|
||||||
* **Respuesta:** `200 OK` con un array de objetos `Vacacion`.
|
|
||||||
|
|
||||||
* **`POST /employees/{employeeId}/vacations`**
|
|
||||||
* **Descripción:** Solicita nuevas vacaciones para una socia.
|
|
||||||
* **Request Body:** `{ "startDate": "YYYY-MM-DD", "endDate": "YYYY-MM-DD" }`
|
|
||||||
* **Respuesta:** `201 Created` con el objeto `Vacacion` creado.
|
|
||||||
|
|
||||||
* **`PUT /vacations/{vacationId}/status`**
|
|
||||||
* **Descripción:** Aprueba o rechaza una solicitud de vacaciones (solo Admins).
|
|
||||||
* **Request Body:** `{ "status": "APPROVED" }`
|
|
||||||
* **Respuesta:** `200 OK` con el objeto `Vacacion` actualizado.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Este documento evolucionará. Los cambios serán comunicados y registrados por el Agente 0.
|
|
||||||
|
|||||||
@@ -8,21 +8,11 @@ Su propósito es mantener un historial claro y auditable de las directrices arqu
|
|||||||
|
|
||||||
## Entradas de Bitácora
|
## Entradas de Bitácora
|
||||||
|
|
||||||
### 2024-07-29 - Definición de Contratos de API y Datos
|
### [Fecha] - Decisión/Observación
|
||||||
|
|
||||||
* **Contexto:** Para asegurar un desarrollo coherente y desacoplado entre los agentes de backend, frontend y testing, era necesario establecer una fuente única de verdad para las estructuras de datos y las interfaces de la API.
|
* **Contexto:** [Descripción del problema o situación]
|
||||||
* **Decisión/Acción:** Se creó el documento `docs/API_CONTRACTS.md`.
|
* **Decisión/Acción:** [Qué se decidió o qué acción se tomó]
|
||||||
* **Justificación:** Este documento previene la ambigüedad y reduce la fricción entre agentes. Define los modelos de datos principales (Socia, Vacación, Permiso) y los endpoints RESTful iniciales, permitiendo que el desarrollo en paralelo comience sobre una base sólida y acordada.
|
* **Justificación:** [Por qué se tomó esa decisión]
|
||||||
* **Impacto:** Afecta principalmente a:
|
* **Impacto:** [Sistemas o agentes afectados]
|
||||||
* **Agente 2 (Backend):** Tiene una especificación clara de qué construir.
|
|
||||||
* **Agente 10 (Frontend):** Sabe qué datos esperar y cómo interactuar con la API.
|
|
||||||
* **Agente 11 (Testing):** Tiene una referencia para escribir los casos de prueba.
|
|
||||||
|
|
||||||
### 2024-07-29 - Creación de Estructura Inicial
|
|
||||||
|
|
||||||
* **Contexto:** El repositorio inicial carecía de una estructura para guiar el trabajo de los agentes de IA/humanos.
|
|
||||||
* **Decisión/Acción:** Se creó la estructura de directorios (`src`, `docs/agents`), los archivos de bitácora para cada agente y el documento de convenciones (`docs/CONVENTIONS.md`).
|
|
||||||
* **Justificación:** Esta estructura establece un flujo de trabajo claro, promueve la documentación consistente y asegura que todos los agentes operen bajo las mismas reglas.
|
|
||||||
* **Impacto:** Afecta a todos los agentes.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
# Bitácora del Agente 1 – Infraestructura & DevOps
|
# Bitácora del Agente 1 – Infraestructura & DevOps
|
||||||
|
|
||||||
## Fecha: 2023-10-27
|
Este documento registra las decisiones, cambios y observaciones del **Agente 1**.
|
||||||
|
|
||||||
### Cambios Realizados
|
Su propósito es mantener un historial claro y auditable de las tareas y soluciones implementadas.
|
||||||
|
|
||||||
* **Creación de `Dockerfile`:**
|
---
|
||||||
* Se ha añadido un `Dockerfile` para construir la imagen del servicio de Node.js.
|
|
||||||
* La imagen se basa in `node:18-alpine` para mantenerla ligera.
|
|
||||||
* Se exponen el puerto 3011 y se define el comando de inicio `node src/index.js`.
|
|
||||||
|
|
||||||
* **Creación de `docker-compose.yml`:**
|
## Entradas de Bitácora
|
||||||
* Se ha creado un archivo `docker-compose.yml` para orquestar los servicios de la aplicación.
|
|
||||||
* Define dos servicios: `api` (el backend de Node.js) y `db` (una base de datos PostgreSQL).
|
|
||||||
* Configura una red `app-network` para la comunicación entre servicios.
|
|
||||||
* Se define un volumen `postgres-data` para la persistencia de los datos de la base de datos.
|
|
||||||
|
|
||||||
* **Creación de `.env.example`:**
|
### [Fecha] - Tarea/Decisión
|
||||||
* Se ha añadido un archivo `.env.example` con las variables de entorno necesarias para la configuración de la base de datos y la aplicación.
|
|
||||||
|
* **Contexto:** [Descripción del requerimiento o problema]
|
||||||
|
* **Acción/Implementación:** [Qué se hizo o cómo se implementó]
|
||||||
|
* **Resultado:** [Cuál fue el resultado, ej. endpoint creado, test pasado]
|
||||||
|
* **Observaciones:** [Notas adicionales, dependencias, problemas encontrados]
|
||||||
|
|
||||||
|
---
|
||||||
|
|||||||
@@ -10,9 +10,14 @@ Su propósito es mantener un historial claro y auditable de las tareas y solucio
|
|||||||
|
|
||||||
### [Fecha] - Tarea/Decisión
|
### [Fecha] - Tarea/Decisión
|
||||||
|
|
||||||
* **Contexto:** [Descripción del requerimiento o problema]
|
* **Contexto:** Se necesitaba una base de datos y un modelo de datos para poder continuar con el desarrollo de la aplicación.
|
||||||
* **Acción/Implementación:** [Qué se hizo o cómo se implementó]
|
* **Acción/Implementación:**
|
||||||
* **Resultado:** [Cuál fue el resultado, ej. endpoint creado, test pasado]
|
* Se ha definido un contrato de datos en `docs/API_CONTRACTS.md` que servirá como única fuente de verdad para los modelos de datos.
|
||||||
* **Observaciones:** [Notas adicionales, dependencias, problemas encontrados]
|
* Se ha seleccionado `knex.js` con `sqlite3` como sistema de base de datos para el desarrollo inicial.
|
||||||
|
* Se ha configurado la conexión a la base de datos y un sistema de migraciones.
|
||||||
|
* Se han creado las migraciones iniciales para todas las tablas requeridas.
|
||||||
|
* Se han creado seeds para poblar la base de datos con datos de prueba.
|
||||||
|
* **Resultado:** La base de datos está lista y poblada con datos iniciales. El esquema está versionado a través de migraciones.
|
||||||
|
* **Observaciones:** El uso de `sqlite3` es temporal para facilitar el desarrollo. Se deberá migrar a una base de datos más robusta como PostgreSQL para producción.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
16
knexfile.js
Normal file
16
knexfile.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// knexfile.js
|
||||||
|
module.exports = {
|
||||||
|
development: {
|
||||||
|
client: 'sqlite3',
|
||||||
|
connection: {
|
||||||
|
filename: './dev.sqlite3'
|
||||||
|
},
|
||||||
|
useNullAsDefault: true,
|
||||||
|
migrations: {
|
||||||
|
directory: './src/db/migrations'
|
||||||
|
},
|
||||||
|
seeds: {
|
||||||
|
directory: './src/db/seeds'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
1744
package-lock.json
generated
1744
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,12 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.19.2"
|
"bcrypt": "^6.0.0",
|
||||||
|
"express": "^4.19.2",
|
||||||
|
"knex": "^3.1.0",
|
||||||
|
"sqlite3": "^5.1.7"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node src/index.js"
|
"start": "node src/index.js",
|
||||||
|
"knex": "knex"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
src/config/database.js
Normal file
7
src/config/database.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// src/config/database.js
|
||||||
|
const knex = require('knex');
|
||||||
|
const config = require('../../knexfile.js');
|
||||||
|
|
||||||
|
const db = knex(config.development);
|
||||||
|
|
||||||
|
module.exports = db;
|
||||||
12
src/db/migrations/20251213215023_create_sucursales_table.js
Normal file
12
src/db/migrations/20251213215023_create_sucursales_table.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
exports.up = function(knex) {
|
||||||
|
return knex.schema.createTable('sucursales', function(table) {
|
||||||
|
table.increments('id').primary();
|
||||||
|
table.string('nombre', 255).notNullable();
|
||||||
|
table.text('direccion');
|
||||||
|
table.timestamps(true, true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = function(knex) {
|
||||||
|
return knex.schema.dropTable('sucursales');
|
||||||
|
};
|
||||||
15
src/db/migrations/20251213215035_create_socias_table.js
Normal file
15
src/db/migrations/20251213215035_create_socias_table.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
exports.up = function(knex) {
|
||||||
|
return knex.schema.createTable('socias', function(table) {
|
||||||
|
table.increments('id').primary();
|
||||||
|
table.string('nombre', 255).notNullable();
|
||||||
|
table.string('apellido', 255).notNullable();
|
||||||
|
table.date('fecha_ingreso').notNullable();
|
||||||
|
table.integer('id_sucursal').unsigned().references('id').inTable('sucursales');
|
||||||
|
table.boolean('activo').defaultTo(true);
|
||||||
|
table.timestamps(true, true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = function(knex) {
|
||||||
|
return knex.schema.dropTable('socias');
|
||||||
|
};
|
||||||
64
src/db/migrations/20251213215048_create_remaining_tables.js
Normal file
64
src/db/migrations/20251213215048_create_remaining_tables.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
exports.up = function(knex) {
|
||||||
|
return knex.schema
|
||||||
|
.createTable('vacaciones', function(table) {
|
||||||
|
table.increments('id').primary();
|
||||||
|
table.integer('id_socia').unsigned().references('id').inTable('socias');
|
||||||
|
table.date('fecha_inicio').notNullable();
|
||||||
|
table.date('fecha_fin').notNullable();
|
||||||
|
table.integer('dias_tomados').notNullable();
|
||||||
|
table.string('estado', 50).notNullable();
|
||||||
|
table.string('ciclo_anual', 10);
|
||||||
|
table.timestamps(true, true);
|
||||||
|
})
|
||||||
|
.createTable('permisos', function(table) {
|
||||||
|
table.increments('id').primary();
|
||||||
|
table.integer('id_socia').unsigned().references('id').inTable('socias');
|
||||||
|
table.date('fecha').notNullable();
|
||||||
|
table.integer('horas');
|
||||||
|
table.integer('dias');
|
||||||
|
table.text('motivo');
|
||||||
|
table.string('estado', 50).notNullable();
|
||||||
|
table.timestamps(true, true);
|
||||||
|
})
|
||||||
|
.createTable('eventos', function(table) {
|
||||||
|
table.increments('id').primary();
|
||||||
|
table.string('tipo_evento', 100).notNullable();
|
||||||
|
table.json('payload');
|
||||||
|
table.string('endpoint_url', 255);
|
||||||
|
table.string('estado_entrega', 50).defaultTo('pendiente');
|
||||||
|
table.timestamp('created_at').defaultTo(knex.fn.now());
|
||||||
|
})
|
||||||
|
.createTable('configuraciones', function(table) {
|
||||||
|
table.increments('id').primary();
|
||||||
|
table.string('clave', 100).unique().notNullable();
|
||||||
|
table.text('valor').notNullable();
|
||||||
|
table.text('descripcion');
|
||||||
|
table.timestamps(true, true);
|
||||||
|
})
|
||||||
|
.createTable('usuarios', function(table) {
|
||||||
|
table.increments('id').primary();
|
||||||
|
table.string('nombre', 255).notNullable();
|
||||||
|
table.string('email', 255).unique().notNullable();
|
||||||
|
table.string('password_hash', 255).notNullable();
|
||||||
|
table.string('rol', 50).notNullable();
|
||||||
|
table.timestamps(true, true);
|
||||||
|
})
|
||||||
|
.createTable('permisos_granulares', function(table) {
|
||||||
|
table.increments('id').primary();
|
||||||
|
table.integer('id_usuario').unsigned().references('id').inTable('usuarios');
|
||||||
|
table.string('recurso', 100).notNullable();
|
||||||
|
table.string('accion', 50).notNullable();
|
||||||
|
table.boolean('permitido').defaultTo(false);
|
||||||
|
table.timestamps(true, true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = function(knex) {
|
||||||
|
return knex.schema
|
||||||
|
.dropTable('permisos_granulares')
|
||||||
|
.dropTable('usuarios')
|
||||||
|
.dropTable('configuraciones')
|
||||||
|
.dropTable('eventos')
|
||||||
|
.dropTable('permisos')
|
||||||
|
.dropTable('vacaciones');
|
||||||
|
};
|
||||||
12
src/db/seeds/01_sucursales.js
Normal file
12
src/db/seeds/01_sucursales.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
exports.seed = function(knex) {
|
||||||
|
// Deletes ALL existing entries
|
||||||
|
return knex('sucursales').del()
|
||||||
|
.then(function () {
|
||||||
|
// Inserts seed entries
|
||||||
|
return knex('sucursales').insert([
|
||||||
|
{nombre: 'Oficina Central', direccion: '123 Calle Principal'},
|
||||||
|
{nombre: 'Sucursal Norte', direccion: '456 Avenida Norte'},
|
||||||
|
{nombre: 'Sucursal Sur', direccion: '789 Boulevard Sur'}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
};
|
||||||
12
src/db/seeds/02_socias.js
Normal file
12
src/db/seeds/02_socias.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
exports.seed = function(knex) {
|
||||||
|
// Deletes ALL existing entries
|
||||||
|
return knex('socias').del()
|
||||||
|
.then(function () {
|
||||||
|
// Inserts seed entries
|
||||||
|
return knex('socias').insert([
|
||||||
|
{nombre: 'Ana', apellido: 'García', fecha_ingreso: '2022-01-15', id_sucursal: 1, activo: true},
|
||||||
|
{nombre: 'Carla', apellido: 'Lopez', fecha_ingreso: '2021-06-20', id_sucursal: 1, activo: true},
|
||||||
|
{nombre: 'Maria', apellido: 'Hernandez', fecha_ingreso: '2023-03-10', id_sucursal: 2, activo: false}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
};
|
||||||
18
src/db/seeds/03_initial_data.js
Normal file
18
src/db/seeds/03_initial_data.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
|
||||||
|
exports.seed = async function(knex) {
|
||||||
|
await knex('usuarios').del();
|
||||||
|
await knex('configuraciones').del();
|
||||||
|
|
||||||
|
const hashedPassword = await bcrypt.hash('password', 10);
|
||||||
|
|
||||||
|
await knex('usuarios').insert([
|
||||||
|
{nombre: 'Admin User', email: 'admin@example.com', password_hash: hashedPassword, rol: 'admin'},
|
||||||
|
{nombre: 'Root User', email: 'root@example.com', password_hash: hashedPassword, rol: 'root'}
|
||||||
|
]);
|
||||||
|
|
||||||
|
await knex('configuraciones').insert([
|
||||||
|
{clave: 'dias_vacaciones_por_anio', valor: '12,14,16,18,20,22', descripcion: 'Días de vacaciones según antigüedad'},
|
||||||
|
{clave: 'webhook_url_vacaciones', valor: 'http://localhost:3012/webhook/vacaciones', descripcion: 'URL para notificaciones de vacaciones'}
|
||||||
|
]);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user