Compare commits

...

2 Commits

Author SHA1 Message Date
5f555bb242 . 2025-01-29 17:01:25 +01:00
9d2a620302 . 2025-01-29 17:01:17 +01:00
11 changed files with 3840 additions and 5951 deletions

165
REQUISITOS CLIENTES.md Normal file
View File

@ -0,0 +1,165 @@
# **Especificaciones del Módulo de Clientes - ERP**
El **módulo de clientes** del ERP permite gestionar la información de clientes, asegurando compatibilidad con la gestión multiempresa, multiidioma y multi-moneda. A continuación, se detallan las especificaciones, incluyendo los campos clave y su función.
---
## **1. Funcionalidades del Módulo de Clientes**
El módulo de clientes debe permitir:
- **Crear, leer, actualizar y eliminar clientes** (CRUD).
- **Asociar clientes a una empresa** y, opcionalmente, a una o más sucursales.
- **Registrar clientes como personas físicas o empresas**.
- **Gestionar datos fiscales, comerciales y de contacto**.
- **Definir configuraciones de facturación y pago personalizadas**.
- **Aplicar descuentos en distintos niveles**.
- **Manejar aspectos financieros como retenciones y recargos**.
- **Identificar clientes con riesgo financiero**.
- **Asignar comerciales o delegados**.
- **Controlar el estado del cliente** (activo/inactivo).
- **Registrar auditoría de cambios**.
---
## **2. Estructura del Cliente y sus Campos**
Cada cliente tiene los siguientes atributos:
### **Datos Generales**
| Campo | Tipo de Dato | Descripción |
|-----------------------|-----------------|-------------|
| `id` | `INT` (PK) | Identificador único del cliente. |
| `company_id` | `INT` (FK) | Empresa a la que pertenece el cliente. |
| `is_company` | `BOOLEAN` | Indica si el cliente es una empresa (`true`) o una persona física (`false`). |
| `fiscal_name` | `VARCHAR(255)` | Nombre fiscal del cliente (solo si es empresa). |
| `commercial_name` | `VARCHAR(255)` | Nombre comercial del cliente (si aplica). |
| `name` | `VARCHAR(255)` | Nombre de la persona o empresa. |
| `email` | `VARCHAR(100)` | Correo electrónico único del cliente. |
| `phone` | `VARCHAR(20)` | Número de teléfono de contacto. |
| `address` | `TEXT` | Dirección del cliente. |
| `country_code` | `CHAR(2)` | Código del país del cliente (ISO 3166-1 alpha-2). |
| `origin` | `VARCHAR(100)` | Origen del cliente (ej. publicidad, redes sociales, feria, escaparate). |
### **Configuración de Moneda e Idioma**
| Campo | Tipo de Dato | Descripción |
|---------------------|-----------------|-------------|
| `currency_code` | `CHAR(3)` | Código de la divisa con la que trabaja el cliente (ISO 4217). |
| `language_code` | `CHAR(5)` | Idioma preferido del cliente para documentos y comunicación (ej. `es-ES`, `en-US`). |
### **Información Fiscal y Financiera**
| Campo | Tipo de Dato | Descripción |
|------------------------|----------------|-------------|
| `vat_percentage` | `DECIMAL(5,2)` | Porcentaje de IVA aplicable al cliente. |
| `equivalence_charge` | `BOOLEAN` | Indica si el cliente aplica recargo de equivalencia. |
| `withholding_percentage` | `DECIMAL(5,2)` | Porcentaje de retención en facturas del cliente. |
| `payment_method` | `VARCHAR(100)` | Forma de pago habitual del cliente (ej. transferencia, tarjeta, efectivo). |
| `payment_day` | `INT` | Día del mes en que el cliente realiza pagos (1-31). |
| `risk` | `BOOLEAN` | Indica si el cliente tiene riesgo financiero o historial de morosidad. |
### **Descuentos y Tarifas Especiales**
| Campo | Tipo de Dato | Descripción |
|---------------------|----------------|-------------|
| `discount_line` | `DECIMAL(5,2)` | Descuento aplicado a nivel de línea en presupuestos. |
| `discount_chapter` | `DECIMAL(5,2)` | Descuento aplicado a nivel de capítulo en presupuestos. |
| `discount_global` | `DECIMAL(5,2)` | Descuento global aplicado al presupuesto. |
| `price_point` | `DECIMAL(10,2)` | Valor de "precio punto" del cliente, usado para calcular costos personalizados en catálogo. |
### **Clasificación y Estado**
| Campo | Tipo de Dato | Descripción |
|---------------|----------------|-------------|
| `client_type` | `VARCHAR(100)` | Tipo de cliente (ej. profesionales, constructoras, distribuidores, particulares). |
| `is_active` | `BOOLEAN` | Indica si el cliente está activo (`true`) o dado de baja (`false`). |
### **Delegados y Comerciales**
| Campo | Tipo de Dato | Descripción |
|-----------------|----------------|-------------|
| `Client_Sales_Rep` | `Tabla Relacional` | Relación con los comerciales asignados a este cliente. |
---
## **3. Auditoría y Seguridad**
Cada cambio en los datos de un cliente debe registrarse en una tabla de auditoría:
```sql
CREATE TABLE Client_Audit (
id SERIAL PRIMARY KEY,
client_id INT NOT NULL REFERENCES Clients(id) ON DELETE CASCADE,
user_id INT NOT NULL REFERENCES Users(id),
change_description TEXT,
change_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
Esto permitirá rastrear modificaciones y garantizar transparencia en la gestión de clientes.
---
## **4. Endpoints Principales de la API**
### **4.1. Listar Clientes**
```http
GET /api/companies/{company_id}/clients
Authorization: Bearer <token>
```
### **4.2. Crear Cliente**
```http
POST /api/companies/{company_id}/clients
Authorization: Bearer <token>
```
**Solicitud:**
```json
{
"is_company": true,
"fiscal_name": "Empresa XYZ SL",
"commercial_name": "XYZ Comercial",
"name": "Empresa XYZ",
"email": "contacto@xyz.com",
"phone": "+34912345678",
"country_code": "ES",
"origin": "Publicidad",
"currency_code": "EUR",
"language_code": "es-ES",
"vat_percentage": 21.00,
"payment_method": "Transferencia bancaria",
"discount_line": 5.00,
"discount_chapter": 2.50,
"discount_global": 10.00,
"price_point": 1.20,
"payment_day": 15,
"risk": false,
"equivalence_charge": false,
"withholding_percentage": 10.00,
"client_type": "Distribuidor",
"is_active": true
}
```
### **4.3. Obtener Cliente por ID**
```http
GET /api/clients/{client_id}
Authorization: Bearer <token>
```
### **4.4. Actualizar Cliente**
```http
PUT /api/clients/{client_id}
Authorization: Bearer <token>
```
### **4.5. Baja Lógica de Cliente**
```http
PATCH /api/clients/{client_id}/deactivate
Authorization: Bearer <token>
```
### **4.6. Asignar Comercial a Cliente**
```http
POST /api/clients/{client_id}/sales-reps
Authorization: Bearer <token>
```
---
## **5. Seguridad y Control de Acceso**
- **Middleware de autenticación** para validar que solo usuarios autorizados gestionen clientes.
- **Permisos basados en roles**, como `Clientes: leer`, `Clientes: escribir`, `Clientes: eliminar`.
- **Reglas de validación** para evitar datos erróneos o incompletos.
- **Registro de auditoría** para cada modificación.
---

View File

@ -0,0 +1,9 @@
DB_HOST=localhost
DB_USER=rodax
DB_PASSWORD=rodax
DB_NAME=uecko_erp
DB_DIALECT=mariadb
DB_PORT=3306
PORT=3002
JWT_SECRET=clave_secreta_para_tokens

View File

@ -18,12 +18,26 @@
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/express": "^5.0.0",
"@types/node": "^22.10.7",
"@types/response-time": "^2.3.8",
"@typescript-eslint/eslint-plugin": "^8.22.0",
"@typescript-eslint/parser": "^8.22.0",
"eslint": "^9.19.0",
"nodemon": "^3.1.9",
"ts-node-dev": "^2.0.0",
"tsx": "^4.19.2",
"typescript": "^5.6.3"
"typescript": "^5.7.3"
},
"dependencies": {
"@types/express": "^5.0.0",
"dotenv": "^16.4.7",
"esbuild": "^0.24.0",
"express": "^4.21.1"
"express": "^4.21.2",
"helmet": "^8.0.0",
"mariadb": "^3.4.0",
"mysql2": "^3.12.0",
"reflect-metadata": "^0.2.2",
"response-time": "^2.3.3",
"sequelize": "^6.37.5"
}
}

View File

@ -0,0 +1,31 @@
import express, { Application } from "express";
import responseTime from "response-time";
import helmet from "helmet";
import dotenv from "dotenv";
dotenv.config();
export function createApp(): Application {
const app = express();
// secure apps by setting various HTTP headers
app.use(helmet());
app.disable("x-powered-by");
// Middlewares
app.use(express.json());
app.use(express.text());
app.use(express.urlencoded({ extended: true }));
// set up the response-time middleware
app.use(responseTime());
app.set("port", process.env.PORT ?? 3002);
// Rutas (placeholder para bounded contexts)
app.get("/", (req, res) => {
res.json({ message: "¡Servidor funcionando!" });
});
return app;
}

View File

@ -0,0 +1,26 @@
import { Sequelize } from "sequelize";
import dotenv from "dotenv";
dotenv.config();
export const sequelize = new Sequelize(
process.env.DB_NAME as string,
process.env.DB_USER as string,
process.env.DB_PASSWORD as string,
{
host: process.env.DB_HOST as string,
dialect: "mariadb",
port: parseInt(process.env.DB_PORT || "3306", 10),
logging: false,
},
);
export async function connectToDatabase(): Promise<void> {
try {
await sequelize.authenticate();
console.log("Conexión a MariaDB establecida correctamente.");
} catch (error) {
console.error("Error al conectar a la base de datos:", error);
process.exit(1);
}
}

View File

@ -1,11 +1,16 @@
import express, { Request, Response } from "express";
import { createApp } from "./config/app"
import { connectToDatabase } from "./config/database";
const app = express();
const PORT = process.env.PORT || 3000;
app.get("/", (req: Request, res: Response) => {
res.send("Hello World");
});
(async () => {
// Conexión a la base de datos
await connectToDatabase();
app.listen(3000, () => {
console.log("Server is running on port 3000");
});
// Inicializar la aplicación
const app = createApp();
app.listen(PORT, () => {
console.log(`Servidor escuchando en http://localhost:${PORT}`);
});
})();

View File

@ -20,7 +20,7 @@
"volta": {
"node": "22.2.0"
},
"packageManager": "yarn@1.22.22",
"packageManager": "pnpm@9.15.4",
"workspaces": [
"apps/*",
"packages/*"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2018",
"module": "commonjs",
"moduleResolution": "node",
"strict": true,
"importHelpers": false,
"noEmitHelpers": false,
"skipLibCheck": true,
"outDir": "./dist",
"baseUrl": "./packages",
"paths": {
"@vercel/webpack-nft": ["webpack-nmt/src/index.ts"]
}
},
"exclude": ["node_modules", "target"]
}

3595
yarn.lock

File diff suppressed because it is too large Load Diff