feat: incorporar eCMR y mercancias desde 7fbfd30
Traslada los cambios de 7fbfd300b4ea17212c04528145d366a3f00b91e5 conservando la configuracion de entorno de produccion.
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
# Mercancía del viaje desde la app
|
||||
|
||||
Base: la misma URL de API usada por el login y los viajes.
|
||||
Todas las peticiones requieren `Authorization: Bearer <token de sesión>`.
|
||||
POST y PUT requieren `Content-Type: application/json`.
|
||||
|
||||
Solo el conductor asignado al viaje puede consultar, añadir o editar su
|
||||
mercancía. La mercancía pertenece al viaje completo y la comparten sus tramos.
|
||||
Las operaciones se limitan al identificador de viaje solicitado.
|
||||
|
||||
## Consultar mercancía y tipos de embalaje
|
||||
|
||||
`GET /api/trips/:id/cargo`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"mercancias": [
|
||||
{
|
||||
"id_mercancia": 123,
|
||||
"id_tipo_embalaje": 1,
|
||||
"cantidad": 2,
|
||||
"peso": 400,
|
||||
"largo": 120,
|
||||
"ancho": 80,
|
||||
"alto": 150,
|
||||
"remontable": true,
|
||||
"tipo_embalaje": "Palet",
|
||||
"tipo_embalaje_en": "Pallet"
|
||||
}
|
||||
],
|
||||
"embalajes": [
|
||||
{ "id_tipo_embalaje": 1, "tipo_embalaje": "Palet", "tipo_embalaje_en": "Pallet" }
|
||||
],
|
||||
"observaciones_mercancia": "Material",
|
||||
"ecmr_emitido": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Los nombres de embalaje del ejemplo son ilustrativos: usar los valores e IDs
|
||||
que devuelva el servidor. `embalajes` contiene las opciones activas.
|
||||
`observaciones_mercancia` es información del viaje, de solo lectura en estos
|
||||
endpoints. Una lista `mercancias: []` permite añadir la primera línea.
|
||||
|
||||
## Añadir una línea
|
||||
|
||||
`POST /api/trips/:id/cargo`
|
||||
|
||||
```json
|
||||
{
|
||||
"id_tipo_embalaje": 1,
|
||||
"cantidad": 2,
|
||||
"peso": 400,
|
||||
"largo": 120,
|
||||
"ancho": 80,
|
||||
"alto": 150,
|
||||
"remontable": true
|
||||
}
|
||||
```
|
||||
|
||||
## Editar una línea
|
||||
|
||||
`PUT /api/trips/:id/cargo/:cargoId`
|
||||
|
||||
Mismo cuerpo que POST. `cargoId` es el `id_mercancia` devuelto por GET.
|
||||
Enviar la línea completa; no es una actualización parcial. La línea debe
|
||||
pertenecer al viaje indicado. Las demás líneas se conservan.
|
||||
|
||||
| Campo | Regla |
|
||||
|---|---|
|
||||
| `id_tipo_embalaje` | Entero obligatorio; debe existir y estar activo. En edición se permite conservar el embalaje anterior aunque esté inactivo. |
|
||||
| `cantidad` | Entero obligatorio mayor que cero; número de bultos. |
|
||||
| `peso` | Entero obligatorio, de 0 a 9999999999; **peso total de la línea en kg**, no peso por bulto. |
|
||||
| `largo`, `ancho`, `alto` | Enteros en centímetros, entre 0 y 2147483647. Si se omiten se guardan a cero. |
|
||||
| `remontable` | Booleano; también se acepta 0/1. Si se omite se guarda como falso. |
|
||||
|
||||
Los números deben enviarse como números JSON, no cadenas. El almacenamiento
|
||||
actual no admite decimales en peso o medidas; no redondear silenciosamente.
|
||||
Para emitir un eCMR el peso total del viaje debe ser mayor que cero.
|
||||
|
||||
## Respuesta de guardado
|
||||
|
||||
POST devuelve HTTP 201; PUT devuelve HTTP 200.
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id_mercancia": 123,
|
||||
"id_tipo_embalaje": 1,
|
||||
"cantidad": 2,
|
||||
"peso": 400,
|
||||
"largo": 120,
|
||||
"ancho": 80,
|
||||
"alto": 150,
|
||||
"remontable": true,
|
||||
"tipo_embalaje": "Palet",
|
||||
"tipo_embalaje_en": "Pallet"
|
||||
},
|
||||
"ecmr_documentos_actualizados": 1,
|
||||
"ecmr_versiones": [
|
||||
{
|
||||
"id_documento": 45,
|
||||
"numero": "ECMR-VIAJE-P1-V3",
|
||||
"version": 3,
|
||||
"public_url": "https://<erp>/adm/pages/logic/ecmr/documento.php?t=<token>",
|
||||
"n_proveedor": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Al añadir o editar se genera una nueva versión y un nuevo PDF de **cada eCMR
|
||||
vigente del viaje**. Se conservan los extras documentales (remolque, ADR,
|
||||
aduanas, autorización especial e Incoterm). Las versiones y firmas antiguas
|
||||
permanecen en el historial. Las nuevas versiones requieren nuevas firmas;
|
||||
los enlaces de firma de versiones sustituidas dejan de ser válidos.
|
||||
|
||||
`ecmr_documentos_actualizados` cuenta todas las revisiones del viaje.
|
||||
`ecmr_versiones` solo expone los documentos de los tramos asignados al conductor.
|
||||
Si todavía no hay eCMR, la mercancía se guarda y se devuelve 0 y `[]`; la
|
||||
primera emisión continúa haciéndose desde el ERP.
|
||||
|
||||
El guardado y las revisiones se realizan en una misma transacción. Si faltan
|
||||
datos para emitir o falla un PDF, no se confirma el cambio de mercancía.
|
||||
Se marca el viaje como editado desde la app. No se envían invitaciones por
|
||||
correo o WhatsApp automáticamente en esta operación.
|
||||
|
||||
## Flujo recomendado del frontend
|
||||
|
||||
1. Abrir «Mercancía» desde el detalle del viaje y cargar GET.
|
||||
2. Mostrar líneas y botones «Añadir mercancía» / «Editar».
|
||||
3. Usar los embalajes recibidos y convertir los campos numéricos antes de enviar.
|
||||
4. Si `ecmr_emitido` es verdadero, avisar antes de guardar: «Se generará una
|
||||
nueva versión del eCMR y será necesario firmarla de nuevo».
|
||||
5. Deshabilitar Guardar durante la petición; no reenviar automáticamente POST
|
||||
ante un timeout, para evitar duplicar líneas. Consultar el estado primero.
|
||||
6. Tras el éxito, refrescar mercancía y `GET /api/trips/:id/ecmr` para obtener
|
||||
los nuevos PDFs y enlaces de firma. Desechar enlaces antiguos.
|
||||
7. Mostrar los errores conservando los datos del formulario para corregirlos.
|
||||
|
||||
## Errores
|
||||
|
||||
Formato: `{ "success": false, "error": "..." }`. Una validación de campo
|
||||
puede añadir `field`, por ejemplo `"field": "peso"`.
|
||||
|
||||
- 401: sesión ausente, inválida o sin DNI. Usar el flujo de login existente.
|
||||
- 403: el conductor no tiene asignado el viaje.
|
||||
- 404: viaje o línea inexistente, o línea de otro viaje.
|
||||
- 422: datos inválidos, embalaje no permitido o datos insuficientes para emitir
|
||||
el eCMR. El cambio no se ha guardado.
|
||||
- 500: fallo al guardar o generar documentos. No presentar éxito ni suponer
|
||||
que no existen mercancías.
|
||||
|
||||
No existe operación de eliminación en esta integración.
|
||||
|
||||
## Operación del backend
|
||||
|
||||
Node invoca un proceso PHP local, sin shell y con JSON por stdin, para reutilizar
|
||||
el generador del ERP y mantener una única transacción de mercancía/documentos.
|
||||
El script solo funciona en CLI, vuelve a comprobar la asignación y exige que
|
||||
la base de datos coincida con `DB_NAME` de Node. No admite acceso HTTP.
|
||||
La ruta del generador está en `src/services/tripCargoWriter.js`; esta instalación
|
||||
usa `/var/www/desarrollogestion/desarrollo/adm/pages/logic/ecmr/mercancia_app_cli.php`.
|
||||
|
||||
No se necesitan migraciones. El usuario del proceso Node debe poder ejecutar
|
||||
PHP, leer el ERP y escribir los PDFs en su repositorio eCMR.
|
||||
|
||||
Validación: 73 pruebas Node y prueba de integración PHP sobre desarrollo con
|
||||
altas, ediciones, PDF real, versiones, permisos, fallo de emisión y rollback.
|
||||
Los datos y PDFs de prueba se revierten/eliminan.
|
||||
Reference in New Issue
Block a user