From e56ff9e2c8de171f57206aafe25ffd4fd4dd592c Mon Sep 17 00:00:00 2001 From: abiandev Date: Mon, 13 Apr 2026 11:27:30 +0200 Subject: [PATCH] actualizar viaje padre --- src/controllers/tripsController.js | 94 +++++++++++++++++++++++++-- test/trips.start.integration.test.js | 4 +- test/trips.status.integration.test.js | 73 +++++++++++++++++++++ 3 files changed, 165 insertions(+), 6 deletions(-) diff --git a/src/controllers/tripsController.js b/src/controllers/tripsController.js index eb501bf..6801e53 100644 --- a/src/controllers/tripsController.js +++ b/src/controllers/tripsController.js @@ -476,6 +476,60 @@ const logTripIncidenceAudit = ({ tripId, userId, result, emailStatus, errorCode }); }; +const normalizeParentTripId = (value) => { + const parsed = Number.parseInt(value, 10); + return Number.isInteger(parsed) && parsed > 0 ? parsed : null; +}; + +const propagateTripStatusToParentTrips = async ( + queryExecutor, + { initialParentTripId, statusId, userId, requestId, flow } +) => { + let parentTripId = normalizeParentTripId(initialParentTripId); + const visitedParentTripIds = new Set(); + const updatedParentTripIds = []; + + while (parentTripId !== null) { + if (visitedParentTripIds.has(parentTripId)) { + throw new Error(`Detected c_viajes parent cycle at id_viaje ${parentTripId}`); + } + + visitedParentTripIds.add(parentTripId); + + const [updateParentTripResult] = await queryExecutor.query( + `UPDATE c_viajes + SET id_estado = ?, + ind_edi_app = 1, + fecha_ultima_edicion = NOW(), + id_usuarios_ultima_edicion = ? + WHERE id_viaje = ?`, + [statusId, userId, parentTripId] + ); + + updatedParentTripIds.push(parentTripId); + appendTripStatusDebugLog({ + stage: 'trip_status:parent_trip_updated', + request_id: requestId || null, + flow: flow || null, + parent_trip_id: parentTripId, + id_estado: statusId, + affected_rows: updateParentTripResult?.affectedRows ?? null + }); + + const [parentTripRows] = await queryExecutor.query( + `SELECT id_viaje_padre + FROM c_viajes + WHERE id_viaje = ? + LIMIT 1`, + [parentTripId] + ); + + parentTripId = normalizeParentTripId(parentTripRows[0]?.id_viaje_padre); + } + + return updatedParentTripIds; +}; + const startTrip = async (req, res) => { const dni = req.user?.dni; const userId = req.user?.id || null; @@ -509,7 +563,7 @@ const startTrip = async (req, res) => { ); const [tripRows] = await connection.query( - `SELECT id_viaje, cod_viaje, id_estado + `SELECT id_viaje, cod_viaje, id_estado, id_viaje_padre FROM c_viajes WHERE id_viaje = ? LIMIT 1 @@ -591,12 +645,20 @@ const startTrip = async (req, res) => { await connection.query( `UPDATE c_viajes SET id_estado = ?, + ind_edi_app = 1, fecha_inicio_real = ?, fecha_ultima_edicion = NOW(), id_usuarios_ultima_edicion = ? WHERE id_viaje = ?`, [START_TRIP_TARGET_STATE, now, userId, tripId] ); + await propagateTripStatusToParentTrips(connection, { + initialParentTripId: tripRows[0].id_viaje_padre, + statusId: START_TRIP_TARGET_STATE, + userId, + requestId, + flow: 'start_trip' + }); const [insertStartStatusResult] = await connection.query( `INSERT INTO c_cambios_estado @@ -1358,7 +1420,7 @@ const updateTripStatus = async (req, res) => { await connection.beginTransaction(); const [tripRows] = await connection.query( - `SELECT id_viaje, id_estado + `SELECT id_viaje, id_estado, id_viaje_padre FROM c_viajes WHERE id_viaje = ? LIMIT 1 @@ -1606,11 +1668,19 @@ const updateTripStatus = async (req, res) => { await connection.query( `UPDATE c_viajes SET id_estado = ?, + ind_edi_app = 1, fecha_ultima_edicion = NOW(), id_usuarios_ultima_edicion = ? WHERE id_viaje = ?`, [FAILED_TRIP_STATE, userId, tripId] ); + await propagateTripStatusToParentTrips(connection, { + initialParentTripId: tripRows[0].id_viaje_padre, + statusId: FAILED_TRIP_STATE, + userId, + requestId, + flow: 'failed_branch_manual' + }); await connection.commit(); @@ -1682,7 +1752,7 @@ const updateTripStatus = async (req, res) => { } const [tripRows] = await db.query( - `SELECT id_viaje + `SELECT id_viaje, id_viaje_padre FROM c_viajes WHERE id_viaje = ? LIMIT 1`, @@ -1761,6 +1831,7 @@ const updateTripStatus = async (req, res) => { await db.query( `UPDATE c_viajes SET id_estado = ?, + ind_edi_app = 1, fecha_ultima_edicion = NOW(), id_usuarios_ultima_edicion = ? WHERE id_viaje = ?`, @@ -1797,6 +1868,13 @@ const updateTripStatus = async (req, res) => { userId ] ); + await propagateTripStatusToParentTrips(db, { + initialParentTripId: tripRows[0].id_viaje_padre, + statusId: idEstado, + userId, + requestId, + flow: 'normal_manual' + }); appendTripStatusDebugLog({ stage: 'update_trip_status:c_cambios_estado_persisted', request_id: requestId, @@ -2330,7 +2408,7 @@ const clearTripStatus = async (req, res) => { } const [tripRows] = await db.query( - `SELECT id_viaje, id_estado + `SELECT id_viaje, id_estado, id_viaje_padre FROM c_viajes WHERE id_viaje = ? LIMIT 1`, @@ -2666,11 +2744,19 @@ const clearTripStatus = async (req, res) => { await connection.query( `UPDATE c_viajes SET id_estado = ?, + ind_edi_app = 1, fecha_ultima_edicion = NOW(), id_usuarios_ultima_edicion = ? WHERE id_viaje = ?`, [currentStatusId, userId, tripId] ); + await propagateTripStatusToParentTrips(connection, { + initialParentTripId: tripRows[0].id_viaje_padre, + statusId: currentStatusId, + userId, + requestId, + flow: 'clear_status_manual' + }); await connection.commit(); diff --git a/test/trips.start.integration.test.js b/test/trips.start.integration.test.js index 5c4fd94..eb21030 100644 --- a/test/trips.start.integration.test.js +++ b/test/trips.start.integration.test.js @@ -177,8 +177,8 @@ test('POST /api/trips/:tripId/start inicia viaje asignado sin otro activo => 200 assert.equal(params[1], 1); assert.equal(params[2], '58045340X'); assert.equal(params[3], 2); - assert.ok(params[7] instanceof Date); - assert.equal(params[9], 1); + assert.ok(params[8] instanceof Date); + assert.equal(params[10], 1); return [{ insertId: 10, affectedRows: 1 }]; } diff --git a/test/trips.status.integration.test.js b/test/trips.status.integration.test.js index 5b62015..8f0a2c1 100644 --- a/test/trips.status.integration.test.js +++ b/test/trips.status.integration.test.js @@ -1048,6 +1048,79 @@ test('POST /api/trips/:id/status estado 5 sin id_punto usa flujo global legacy = assert.equal(step, 6); }); +test('POST /api/trips/:id/status propaga estado global al viaje padre', async () => { + let step = 0; + + db.query = async (sql, params) => { + step += 1; + + if (step === 1) { + assert.match(sql, /FROM t_viaje_estados/); + assert.deepEqual(params, [6]); + return [[{ id_estado: 6 }]]; + } + + if (step === 2) { + assert.match(sql, /FROM c_viajes/); + assert.deepEqual(params, [248230]); + return [[{ id_viaje: 248230, id_viaje_padre: 196854 }]]; + } + + if (step === 3) { + assert.match(sql, /FROM c_viajes_proveedor/); + assert.deepEqual(params, [248230, '58045340X']); + return [[{ n_proveedor: 1, id_proveedor: 675 }]]; + } + + if (step === 4) { + assert.match(sql, /UPDATE c_viajes/); + assert.match(sql, /ind_edi_app = 1/); + assert.deepEqual(params, [6, 1, 248230]); + return [{ affectedRows: 1 }]; + } + + if (step === 5) { + assert.match(sql, /INSERT INTO c_cambios_estado/); + assert.equal(params[0], 248230); + assert.equal(params[3], 6); + return [{ insertId: 6, affectedRows: 1 }]; + } + + if (step === 6) { + assert.match(sql, /UPDATE c_viajes/); + assert.match(sql, /ind_edi_app = 1/); + assert.deepEqual(params, [6, 1, 196854]); + return [{ affectedRows: 1 }]; + } + + if (step === 7) { + assert.match(sql, /SELECT id_viaje_padre/); + assert.deepEqual(params, [196854]); + return [[{ id_viaje_padre: 0 }]]; + } + + throw new Error(`Unexpected query step ${step}: ${sql}`); + }; + + const response = await withServer(async (server) => + requestJson({ + port: server.address().port, + method: 'POST', + path: '/api/trips/248230/status', + authorization: `Bearer ${createToken()}`, + body: { + id_estado: 6 + } + }) + ); + + assert.equal(response.statusCode, 200); + assert.equal(response.body.success, true); + assert.equal(response.body.trip_id, 248230); + assert.equal(response.body.id_estado, 6); + assert.equal(step, 7); +}); + test('POST /api/trips/:id/status estado intermedio con id_punto inválido => 400', async () => { db.query = async () => { throw new Error('db.query should not run for invalid id_punto');