Traslada los cambios de 7fbfd300b4ea17212c04528145d366a3f00b91e5 conservando la configuracion de entorno de produccion.
5.8 KiB
Consulta y firma eCMR desde la app
GET /api/trips/:id/ecmr
Enviar Authorization: Bearer <token de login> con la sesión de la app.
La respuesta contiene únicamente documentos activos con PDF generado de los
tramos cuyo DNI coincide con el conductor autenticado.
{
"success": true,
"data": [
{
"id_documento": 7,
"n_proveedor": 2,
"numero": "ECMR-123",
"version": 1,
"estado": "emitido",
"fecha_creacion": "2026-09-16 10:00:00",
"document_url": "https://<erp>/adm/pages/logic/ecmr/documento.php?t=<token-publico>",
"firma_carga_completada": false,
"firma_descarga_completada": false,
"firma_carga_url": "https://<erp>/adm/pages/logic/ecmr/firmar.php?t=<token-carga>",
"firma_descarga_url": "https://<erp>/adm/pages/logic/ecmr/firmar.php?t=<token-descarga>",
"firma_expira_en_segundos": 900
}
]
}
Integración React / React Native
En el detalle del viaje, consultar esta ruta y mostrar:
- «Ver eCMR»: abrir
document_urlmediante el visor PDF de la app. - «Firmar carga»: abrir
firma_carga_urlen el navegador seguro o WebView con soporte HTTPS y WebCrypto. Si esnull, mostrar «Carga firmada». - «Firmar descarga»: abrir
firma_descarga_urlde la misma manera. Si esnull, mostrar «Descarga firmada».
Los enlaces son independientes por documento y rol. Después de volver de la
pantalla de firma, consultar de nuevo la ruta para refrescar el estado. Los
campos firma_*_completada son booleanos; no inferir las firmas a partir del
estado general del viaje.
Las URLs de firma duran 15 minutos desde su emisión. Volver a consultar cuando
caduquen. Cada consulta crea invitaciones temporales para los roles pendientes:
no usar sondeo periódico ni precarga masiva. La respuesta lleva no-store.
No persistir ni registrar enlaces, y no enviar el Bearer de la app a las URLs
PHP. El token del enlace es la credencial temporal, no un parámetro skip_otp.
La pantalla PHP valida el acceso emitido por Node, comprueba que el conductor continúa asignado al tramo y omite el código de seis cifras. El DNI procede de la sesión y no se puede cambiar. Se mantienen nombre, consentimiento y firma criptográfica. Las invitaciones de correo/WhatsApp conservan su OTP. Tras firmar carga desde la app, se consulta la app para acceder a la firma de descarga; no se envía un correo o WhatsApp automático en este canal.
Si data está vacío, mostrar «Este viaje todavía no tiene eCMR disponible».
El PDF se entrega como attachment; abrirlo en un navegador puede descargarlo.
Errores: 401 sin sesión/DNI, 404 identificador inválido o viaje inexistente, 403 conductor no asignado, 500 error de base de datos/invitaciones. No mostrar los errores como ausencia de documentos. Un enlace PHP caducado o cuyo tramo ya no está asignado devuelve 403 y pide volver a abrirlo desde la app.
Requisitos
- Node y PHP deben usar las mismas tablas
ecmr_documentos,ecmr_invitaciones,ecmr_firmasyc_viajes_proveedor. - Desplegar también
firma_app.php, los cambios defirma_api.php,firmar.php,common.phpy las traducciones ES/EN del ERP. public_urldebe apuntar al ERP actualizado y ser accesible mediante HTTPS.- No requiere nuevas columnas ni migraciones para los enlaces de firma.
- No se ha facilitado el código React; queda pendiente conectar sus botones.
Validación
- 40 pruebas Node: consulta eCMR, autorización, enlaces por rol, rollback y regresión de listado, viaje activo y puntos intermedios.
- 27 comprobaciones PHP: DNI, caducidad, asignación, OTP y traducciones.
- Prueba del JavaScript de firma para sesiones app y enlaces con OTP.
- Consultas SQL reales e invitación temporal verificadas en desarrollo mediante una transacción revertida. No se ha firmado ningún documento real.
QR del PDF DeCA / eCMR
GET /api/trips/:id/ecmr/:documentId/qr
Usar el mismo Authorization: Bearer <token de login> y el id_documento
obtenido en la consulta eCMR del viaje. Se especifica el documento porque un
viaje puede tener varios tramos y versiones.
{
"success": true,
"data": {
"id_documento": 7,
"n_proveedor": 2,
"numero": "ECMR-123",
"version": 1,
"document_url": "https://<erp>/adm/pages/logic/ecmr/documento.php?t=<token-publico>",
"qr_data_url": "data:image/png;base64,..."
}
}
La imagen PNG es de 320 × 320 píxeles y codifica exactamente document_url,
la URL del PDF usada por el QR del documento. No codifica un enlace de firma.
En React Native puede mostrarse con:
<Image source={{ uri: data.qr_data_url }} style={{ width: 240, height: 240 }} />
Solo devuelve documentos activos con PDF generado y del tramo asignado al DNI
de la sesión. Responde 401 sin sesión/DNI y 404 si los identificadores no son
válidos, el documento no existe, pertenece a otro viaje/conductor, está sustituido
o no tiene PDF generado. El 404 evita revelar documentos ajenos. Los fallos de
BD o generación del QR devuelven 500 con un mensaje genérico.
La respuesta lleva Cache-Control: no-store. Esta consulta no crea invitaciones,
no modifica la BD ni envía mensajes; se puede usar para actualizar la imagen sin
renovar enlaces de firma. Al sustituirse una versión, volver a consultar los
documentos del viaje y utilizar el nuevo identificador. El QR contiene un enlace
público al PDF: no registrarlo en logs ni analítica.
Despliegue: instalar las dependencias del lockfile (npm ci --omit=dev) y
reiniciar el proceso Node según el procedimiento habitual. Se añade qrcode
1.5.4. No requiere migraciones de BD ni cambios en PHP.
Pruebas: node --test test/trips.ecmr.integration.test.js cubre autenticación,
identificadores, filtrado por tramo/conductor, ausencia de documentos, PNG real,
URL codificada, errores de BD/generador y regresión del flujo de firma.