# 📦 Documentación del Backend — API Hotel Hacienda

## Índice
1. [Introducción](#introducción)
2. [Estructura de Carpetas](#estructura-de-carpetas)
3. [Flujo de una Petición](#flujo-de-una-petición)
4. [Configuración de Express (`app.js`)](#configuración-de-express-appjs)
5. [Conexión a Base de Datos](#conexión-a-base-de-datos)
6. [Controllers](#controllers)
7. [Rutas](#rutas)
8. [Middlewares](#middlewares)
9. [Servicios](#servicios)
10. [Variables de Entorno](#variables-de-entorno)
11. [Patrones y Convenciones](#patrones-y-convenciones)
12. [Guía para Agregar un Nuevo Endpoint](#guía-para-agregar-un-nuevo-endpoint)

---

## Introducción

El backend es una API REST construida con **Node.js** y **Express.js**. No utiliza ORM; la lógica de negocio reside en **funciones SQL de PostgreSQL** que son invocadas desde los controllers.

**Entry point:** `index.js` → `src/server.js` → `src/app.js`

---

## Estructura de Carpetas

```
hotel_hacienda/
├── index.js                      # Solo requiere src/server.js
├── src/
│   ├── server.js                 # Levanta el servidor
│   ├── app.js                    # Configura Express, CORS, rutas
│   ├── db/
│   │   └── connection.js         # Pool de PostgreSQL
│   ├── middlewares/
│   │   ├── handleValidation.js   # Manejo de errores de express-validator
│   │   └── validators.js         # Validadores (login)
│   ├── routes/                   # 16 archivos de rutas
│   │   ├── auth.routes.js
│   │   ├── employee.routes.js
│   │   ├── contract.routes.js
│   │   ├── product.routes.js
│   │   ├── expense.routes.js
│   │   ├── status.routes.js
│   │   ├── payment.routes.js
│   │   ├── settings.routes.js
│   │   ├── mail.routes.js
│   │   ├── incomes.routes.js
│   │   ├── purchase.routes.js
│   │   ├── exchange.routes.js
│   │   ├── hotelpl.routes.js
│   │   ├── restaurantpl.routes.js
│   │   ├── incomehrx.routes.js
│   │   └── reportcontract.routes.js
│   ├── controllers/              # 18 archivos de controllers
│   │   ├── auth.controller.js
│   │   ├── employee.controller.js
│   │   ├── contract.controller.js
│   │   ├── product.controller.js
│   │   ├── expense.controller.js
│   │   ├── status.controller.js
│   │   ├── payment.controller.js
│   │   ├── settings.controller.js
│   │   ├── mail.controller.js
│   │   ├── incomes.controller.js
│   │   ├── purchase.controller.js
│   │   ├── exchange.controller.js
│   │   ├── hotelp.controller.js
│   │   ├── restaurantpl.controller.js
│   │   ├── incomehrx.controller.js
│   │   ├── reportcontract.controller.js
│   │   └── reporter.controller.js
│   └── services/
│       └── mailService.js        # Transporte de Nodemailer
```

---

## Flujo de una Petición

```
HTTP Request
     │
     ▼
┌─────────────┐
│   Express   │  ← CORS, JSON parser
│   (app.js)  │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│   Routes    │  ← Prefijo /api/xxx
│  (.routes)  │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ Controllers │  ← Extrae req.params/body
│ (.controller)│
└──────┬──────┘
       │
       ▼
┌─────────────┐
│    Pool     │  ← pg (node-postgres)
│ (connection)│
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ PostgreSQL  │  ← Funciones SQL
│  (funciones)│
└─────────────┘
```

---

## Configuración de Express (`app.js`)

```javascript
const express = require('express');
const app = express();
const cors = require('cors');
require("dotenv").config();

const corsOptions = {
  origin: process.env.URL_CORS,
  methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
  allowedHeaders: ["Content-Type", "Authorization"]
};
app.use(cors(corsOptions));
app.use(express.json());
```

**Observaciones:**
- `URL_CORS` debe definirse sin slash al final.
- No hay middleware de autenticación global.
- Todas las rutas usan prefijo `/api/`.

---

## Conexión a Base de Datos

```javascript
// src/db/connection.js
const { Pool } = require('pg');
require('dotenv').config();

const pool = new Pool({
  host: process.env.DB_HOST,
  port: process.env.DB_PORT,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME
});

pool.connect()
  .then(() => console.log('Conectado a PostgreSQL'))
  .catch(err => console.error('Error de conexión:', err));

module.exports = pool;
```

**Nota:** Se usa un `Pool` reutilizable. Cada controller lo importa directamente.

---

## Controllers

### Patrón General

```javascript
const pool = require('../db/connection');

const nombreFuncion = async (req, res) => {
  try {
    const { param1, param2 } = req.body;   // o req.params / req.query
    
    const result = await pool.query(
      'SELECT nombrefuncionsql($1, $2) AS status',
      [param1, param2]
    );
    
    const status = result.rows[0].status;
    
    res.json({ message: 'Éxito', status });
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: 'Error interno' });
  }
};

module.exports = { nombreFuncion };
```

### Paginación Manual

Algunos endpoints implementan paginación manual:

```javascript
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 500;
const offset = (page - 1) * limit;

const result = await pool.query(
  'SELECT * FROM getemployees() LIMIT $1 OFFSET $2',
  [limit, offset]
);

const totalResult = await pool.query('SELECT COUNT(*) FROM employees');
const total = parseInt(totalResult.rows[0].count);
const totalPages = Math.ceil(total / limit);

res.json({ page, limit, total, totalPages, data: result.rows });
```

### Manejo de JSONB

Para pasar arrays/objetos a PostgreSQL:

```javascript
const categoriesJson = categories ? JSON.stringify(categories) : '[]';
await pool.query(
  'SELECT newincome($1,$2,$3,$4,$5,$6::jsonb) AS status',
  [param1, param2, param3, param4, param5, categoriesJson]
);
```

---

## Rutas

### Convención de Nombres
- Archivos: `nombreplural.routes.js`
- Prefijo en `app.js`: `/api/nombreplural`

### Ejemplo

```javascript
// src/routes/expense.routes.js
const express = require('express');
const router = express.Router();
const expenseController = require('../controllers/expense.controller');

router.get('/pendingapproval', expenseController.getPendingAppExpenses);
router.post('/newexpense', expenseController.newExpense);
router.put('/updateexpense/:id', expenseController.updateExpense);

module.exports = router;
```

### Registro en `app.js`

```javascript
const expenseRoutes = require('./routes/expense.routes');
app.use('/api/expenses', expenseRoutes);
```

---

## Middlewares

### Validación de Login

```javascript
// src/middlewares/validators.js
const { body } = require('express-validator');

const loginValidator = [
  body('name_mail_user').notEmpty().withMessage('Usuario requerido'),
  body('user_pass').notEmpty().withMessage('Contraseña requerida')
];

module.exports = { loginValidator };
```

### Manejo de Errores de Validación

```javascript
// src/middlewares/handleValidation.js
const { validationResult } = require('express-validator');

const handleValidation = (req, res, next) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }
  next();
};

module.exports = handleValidation;
```

**Nota:** Actualmente solo el login tiene validación. Los demás endpoints no validan inputs.

---

## Servicios

### Mail Service (`src/services/mailService.js`)

Configura un transporte SMTP reutilizable con Nodemailer:

```javascript
const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
  host: process.env.EMAIL_HOST,
  port: process.env.EMAIL_PORT,
  auth: {
    user: process.env.EMAIL_USER,
    pass: process.env.EMAIL_PASS
  }
});

module.exports = transporter;
```

**Uso:** Importado en controllers que envían correos (`auth.controller.js`, `mail.controller.js`).

---

## Variables de Entorno

| Variable | Descripción | Requerida |
|----------|-------------|-----------|
| `PORT` | Puerto del servidor | Sí (default 3000) |
| `URL_CORS` | Origen permitido para CORS | Sí |
| `DB_HOST` | Host de PostgreSQL | Sí |
| `DB_PORT` | Puerto de PostgreSQL | Sí |
| `DB_USER` | Usuario de PostgreSQL | Sí |
| `DB_PASSWORD` | Contraseña de PostgreSQL | Sí |
| `DB_NAME` | Nombre de la base de datos | Sí |
| `EMAIL_HOST` | Servidor SMTP | Sí |
| `EMAIL_PORT` | Puerto SMTP | Sí |
| `EMAIL_USER` | Usuario SMTP | Sí |
| `EMAIL_PASS` | Contraseña SMTP | Sí |
| `STRIPE_SECRET_KEY` | Clave secreta de Stripe | Para integración Stripe |
| `FACTURAS_API_URL` | URL API de facturas | Para integración facturas |
| `FACTURAS_ISSUER_RFC` | RFC del emisor | Para integración facturas |
| `FACTURAS_TYPE` | Tipo de factura (I/E) | Para integración facturas |
| `FACTURAS_API_TOKEN` | Token de API facturas | Para integración facturas |
| `BANXICO_TOKEN` | Token de Banxico | Para tipo de cambio |

---

## Patrones y Convenciones

1. **Un controller por dominio:** Todos los endpoints relacionados con un recurso viven en el mismo controller.
2. **Parámetros posicionales:** Las funciones SQL usan `$1, $2, $3...` en orden.
3. **Respuesta consistente:** Siempre retornar JSON con al menos `{ message, ...data }`.
4. **Errores 500:** Cualquier excepción se captura y responde con status 500.
5. **Sin ORM:** Las consultas son SQL crudo o llamadas a funciones almacenadas.

---

## Guía para Agregar un Nuevo Endpoint

1. **Crear/actualizar la función SQL** en PostgreSQL.
2. **Crear el método** en el controller correspondiente.
3. **Agregar la ruta** en el archivo `.routes.js` apropiado.
4. **Registrar la ruta** en `src/app.js` con un prefijo `/api/`.
5. **Probar** la función SQL directamente en PostgreSQL antes de probar el endpoint.
6. **Actualizar** `docs/API.md` con el nuevo endpoint.
