= SPEC-1: Aplicación Web para Generación de Presupuestos de Muebles
:sectnums:
:toc:

== Input


Necesito planificar un proyecto web formado por un cliente web hecho en React y una REST API hecha en NodeJS, ExpressJS y Sequelize. La base de datos es MySQL.

El proyecto web consiste en una aplicación que sirve para generar presupuestos a partir de un catálogo de artículos. La aplicación es proporcionada por una fábrica de muebles a distribuidores ("dealers") donde venden esos muebles a clientes finales.

Cuando un cliente final llega a una tienda y pide un presupuesto, el distribuidor utiliza esta aplicación web para calcular una cotización ("quote") de lo que el cliente necesita.

Para realizar la cotización ("quote"), el distribuidor puede utilizar un catálogo de artículos ("catalog") que existe en la aplicación web ya de forma precargado y que no se puede modificar por parte del distribuidor. A la cotización también se le pueden añadir conceptos libres. Es importante que en los detalles/conceptos de la cotización se mantenga un orden determinado.

El fabricante es el único que puede modificar ese catálogo.

Si el cliente final acepta la cotización, el distribuidor "envia" la cotización a la fábrica. Junto con la cotización, el distribuidor también puede enviar documentos como ficheros PDF, fotos, imágenes, planos, etc. 

La fábrica estudia la cotización del distribuidor junto con los documentos enviados y genera un contrato ("contract") asociado que reune todo, junto el cálculo real de todos los importes. 

Ese contrato se vuelve a enviar al distribuidor para que sepa realmente lo que cuesta lo que ha pedido su cliente final.

Añade a "must have" que la fábrica debe poder gestionar los distribuidores en la aplicación, pudiente añadir, modificar, o dar de baja distribuidores.

Cada distribuidor tiene un usuario formado por un email y una contraseña para entrar en la aplicación web. La fábrica también tiene un email y contraseña para entrar en la aplicación.

Añade a "shoud have" la posibilidad, por parte de la fábrica, de poder bloquear un distribuidor de manera que cuando un distribuidor está bloqueado, este puede acceder al sistema y ver sus cotizaciones pero no puede ni añadir cotizaciones nuevas ni modificar las que hubiera ni eliminarlas.

Quiero que el sistema esté desarrollado siguiente los principios SOLID y según los principios de la Clean Architecture o arquitectura de puertos y adaptadores.



== Background

Desarrollo de una aplicación web destinada a la generación de presupuestos de muebles. Esta aplicación será utilizada por distribuidores de una fábrica de muebles para calcular cotizaciones para los clientes finales en el punto de venta. El proceso comienza cuando un cliente solicita un presupuesto en una tienda. El vendedor usa la aplicación web para seleccionar los artículos del catálogo predefinido y generar una cotización.

El catálogo de artículos está precargado en la aplicación web y solo puede ser modificado por el fabricante. Una vez que el cliente final acepta la cotización, el distribuidor puede enviarla a la fábrica junto con documentos adicionales, como PDFs, fotos, imágenes, y planos. La fábrica revisa la cotización y los documentos adjuntos, luego genera un contrato que incluye el cálculo final de los importes y lo envía de vuelta al distribuidor.

La aplicación consta de un cliente web desarrollado en React y una REST API implementada con NodeJS, ExpressJS y Sequelize, utilizando MySQL como base de datos.

== Requirements

=== Must Have
- La aplicación debe permitir a los distribuidores generar cotizaciones a partir de un catálogo de artículos precargado.
- Los distribuidores deben poder enviar cotizaciones a la fábrica junto con documentos adicionales (PDFs, fotos, imágenes, planos).
- La fábrica debe poder modificar el catálogo de artículos y actualizarlo en la aplicación web.
- La fábrica debe recibir y revisar las cotizaciones enviadas por los distribuidores, junto con los documentos adjuntos.
- La fábrica debe poder generar y enviar un contrato que incluya el cálculo final de los importes al distribuidor.
- La fábrica debe poder gestionar los distribuidores en la aplicación, pudiendo añadir, modificar, o dar de baja distribuidores.
- Cada distribuidor debe tener un usuario formado por un email y una contraseña para entrar en la aplicación web.
- La fábrica también debe tener un email y contraseña para entrar en la aplicación.

=== Should Have
- La aplicación debe ofrecer autenticación y autorización para distribuidores y empleados de la fábrica.
- La fábrica debe poder bloquear un distribuidor de manera que cuando un distribuidor está bloqueado, este pueda acceder al sistema y ver sus cotizaciones, pero no pueda ni añadir cotizaciones nuevas ni modificar las existentes ni eliminarlas.
- La aplicación debe tener una interfaz de usuario intuitiva y fácil de usar para la generación de cotizaciones.

=== Could Have
- La aplicación podría permitir a los distribuidores revisar el historial de cotizaciones enviadas.
- La aplicación podría ofrecer reportes y estadísticas sobre las cotizaciones generadas y enviadas.

=== Won't Have
- Los distribuidores no podrán modificar el catálogo de artículos.
- La aplicación no tendrá funcionalidad de chat en tiempo real entre distribuidores y la fábrica.

== Method

=== Arquitectura del Sistema

El sistema se compondrá de los siguientes componentes principales siguiendo los principios SOLID y Clean Architecture:

1. Cliente Web (React)
   - Interfaz de usuario para que los distribuidores generen cotizaciones.
   - Interfaz de usuario para que la fábrica gestione el catálogo y los distribuidores.
   - Interfaz de usuario para que la fábrica reciba y revise las cotizaciones enviadas por los distribuidores.
   - Interfaz de usuario para que la fábrica genere y envíe contratos asociados a las cotizaciones.

2. REST API (NodeJS, ExpressJS)
   - Controladores para manejar las solicitudes HTTP.
   - Casos de uso que representan la lógica de negocio.
   - Entidades que representan los objetos de dominio.
   - Repositorios para interactuar con la base de datos.

3. Base de Datos (MySQL)
   - Almacenamiento de usuarios, catálogos, cotizaciones, documentos adjuntos y contratos.

=== Diagrama de Componentes

[plantuml, components]
----
@startuml
package "Cliente Web" {
  [React App]
}

package "REST API" {
  [Express Server]
  package "Controllers" {
    [UserController]
    [ArticuloController]
    [CotizacionController]
    [DocumentoController]
    [ContratoController]
  }
  package "Use Cases" {
    [UserUseCases]
    [ArticuloUseCases]
    [CotizacionUseCases]
    [DocumentoUseCases]
    [ContratoUseCases]
  }
  package "Entities" {
    [User]
    [Articulo]
    [Cotizacion]
    [Documento]
    [Contrato]
  }
  package "Repositories" {
    [UserRepository]
    [ArticuloRepository]
    [CotizacionRepository]
    [DocumentoRepository]
    [ContratoRepository]
  }
}

package "Base de Datos" {
  [MySQL Database]
}

[React App] --> [Express Server] : HTTP Requests
[Express Server] --> [Controllers] : Route Handlers
[Controllers] --> [Use Cases] : Application Logic
[Use Cases] --> [Entities] : Domain Logic
[Use Cases] --> [Repositories] : Data Access
[Repositories] --> [MySQL Database] : CRUD Operations
@enduml
----

=== Esquema de Base de Datos

El esquema de la base de datos incluye las siguientes tablas:

1. **Usuarios**
   - `id` (INT, PK, AUTO_INCREMENT)
   - `email` (VARCHAR, UNIQUE)
   - `password` (VARCHAR)
   - `role` (ENUM: 'distribuidor', 'fabrica')
   - `status` (ENUM: 'activo', 'bloqueado')

2. **Articulos**
   - `id` (INT, PK, UUID)
   - `nombre` (VARCHAR)
   - `descripcion` (TEXT)
   - `precio` (DECIMAL)

3. **Cotizaciones**
   - `id` (INT, PK, AUTO_INCREMENT)
   - `distribuidor_id` (INT, FK -> Usuarios.id)
   - `fecha` (DATETIME)
   - `estado` (ENUM: 'pendiente', 'enviada', 'aceptada', 'rechazada')

4. **Detalles_Cotizacion**
   - `id` (INT, PK, AUTO_INCREMENT)
   - `cotizacion_id` (INT, FK -> Cotizaciones.id)
   - `articulo_id` (INT, FK -> Articulos.id)
   - `cantidad` (INT)
   - `precio_total` (DECIMAL)

5. **Documentos**
   - `id` (INT, PK, AUTO_INCREMENT)
   - `cotizacion_id` (INT, FK -> Cotizaciones.id)
   - `tipo` (VARCHAR)
   - `ruta` (VARCHAR)

6. **Contratos**
   - `id` (INT, PK, AUTO_INCREMENT)
   - `cotizacion_id` (INT, FK -> Cotizaciones.id)
   - `fabrica_id` (INT, FK -> Usuarios.id)
   - `distribuidor_id` (INT, FK -> Usuarios.id)
   - `fecha_envio` (DATETIME)
   - `contenido` (TEXT)
   - `ruta_documento` (VARCHAR)

=== Diagrama de Base de Datos

[plantuml, database_schema]
----
@startuml
entity "Usuarios" {
  + id: INT <<PK>>
  + email: VARCHAR <<UNIQUE>>
  + password: VARCHAR
  + role: ENUM('distribuidor', 'fabrica')
  + status: ENUM('activo', 'bloqueado')
}

entity "Articulos" {
  + id: INT <<PK>>
  + nombre: VARCHAR
  + descripcion: TEXT
  + precio: DECIMAL
}

entity "Cotizaciones" {
  + id: INT <<PK>>
  + distribuidor_id: INT <<FK>>
  + fecha: DATETIME
  + estado: ENUM('pendiente', 'enviada', 'aceptada', 'rechazada')
}

entity "Detalles_Cotizacion" {
  + id: INT <<PK>>
  + cotizacion_id: INT <<FK>>
  + articulo_id: INT <<FK>>
  + cantidad: INT
  + precio_total: DECIMAL
}

entity "Documentos" {
  + id: INT <<PK>>
  + cotizacion_id: INT <<FK>>
  + tipo: VARCHAR
  + ruta: VARCHAR
}

entity "Contratos" {
  + id: INT <<PK>>
  + cotizacion_id: INT <<FK>>
  + fabrica_id: INT <<FK>>
  + distribuidor_id: INT <<FK>>
  + fecha_envio: DATETIME
  + contenido: TEXT
  + ruta_documento: VARCHAR
}

Usuarios ||--o{ Cotizaciones : "distribuidor_id"
Cotizaciones ||--|{ Detalles_Cotizacion : "cotizacion_id"
Articulos ||--o{ Detalles_Cotizacion : "articulo_id"
Cotizaciones ||--o{ Documentos : "cotizacion_id"
Cotizaciones ||--|{ Contratos : "cotizacion_id"
Usuarios ||--o{ Contratos : "fabrica_id"
Usuarios ||--o{ Contratos : "distribuidor_id"
@enduml
----

=== Endpoints de la API

1. **Usuarios**
   - `POST /usuarios` : Crear un nuevo usuario.
   - `POST /login` : Iniciar sesión y obtener token de autenticación.
   - `GET /usuarios/:id` : Obtener información de un usuario.
   - `PUT /usuarios/:id` : Actualizar información de un usuario.
   - `DELETE /usuarios/:id` : Eliminar un usuario.

2. **Articulos**
   - `GET /articulos` : Obtener lista de artículos.
   - `GET /articulos/:id` : Obtener detalles de un artículo.
   - `POST /articulos` : Crear un nuevo artículo (solo fábrica).
   - `PUT /articulos/:id` : Actualizar un artículo (solo fábrica).
   - `DELETE /articulos/:id` : Eliminar un artículo (solo fábrica).

3. **Cotizaciones**
   - `GET /cotizaciones` : Obtener lista de cotizaciones (por distribuidor o fábrica).
   - `GET /cotizaciones/:id` : Obtener detalles de una cotización.
   - `POST /cotizaciones` : Crear una nueva cotización (solo distribuidor).
   - `PUT /cotizaciones/:id` : Actualizar una cotización (solo fábrica).
   - `DELETE /cotizaciones/:id` : Eliminar una cotización (solo fábrica).

4. **Documentos**
   - `POST /cotizaciones/:id/documentos` : Subir un documento para una cotización.
   - `GET /cotizaciones/:id/documentos` : Obtener lista de documentos de una cotización.
   - `DELETE /documentos/:id` : Eliminar un documento.

5. **Contratos**
   - `GET /contratos` : Obtener lista de contratos (por distribuidor o fábrica).
   - `GET /contratos/:id` : Obtener detalles de un contrato.
   - `POST /contratos` : Crear un nuevo contrato (solo fábrica).
   - `PUT /contratos/:id` : Actualizar un contrato (solo fábrica).
   - `DELETE /contratos/:id` : Eliminar un contrato.

=== Funcionalidades del Cliente Web

1. **Distribuidores**
   - Generar nuevas cotizaciones a partir del catálogo de artículos.
   - Enviar cotizaciones a la fábrica con documentos adjuntos.
   - Ver el historial de cotizaciones enviadas.
   - Acceder a contratos generados por la fábrica.

2. **Fábrica**
   - Gestionar el catálogo de artículos (añadir, modificar, eliminar artículos).
   - Gestionar distribuidores (añadir, modificar, eliminar distribuidores, bloquear/desbloquear distribuidores).
   - Revisar cotizaciones enviadas por distribuidores.
   - Generar y enviar contratos basados en cotizaciones revisadas.
   - Acceder al historial de contratos enviados a los distribuidores.
