feat: implement equipment and container loading rollback functionality with corresponding backend cloud functions

This commit is contained in:
ElPoyo
2026-05-27 22:04:46 +02:00
parent 64a9fe382a
commit faff06e4df
15 changed files with 660 additions and 514 deletions
+101 -19
View File
@@ -190,14 +190,6 @@ exports.getEventWithDetails = onRequest(httpOptions, withCors((req, res) => {
return require("./src/events").getEventWithDetails(req, res);
}));
exports.validateEquipmentPreparation = onRequest(httpOptions, withCors((req, res) => {
return require("./src/events").validateEquipmentPreparation(req, res);
}));
exports.validateAllPreparation = onRequest(httpOptions, withCors((req, res) => {
return require("./src/events").validateAllPreparation(req, res);
}));
exports.validateEquipmentLoading = onRequest(httpOptions, withCors((req, res) => {
return require("./src/events").validateEquipmentLoading(req, res);
}));
@@ -214,14 +206,6 @@ exports.validateAllUnloading = onRequest(httpOptions, withCors((req, res) => {
return require("./src/events").validateAllUnloading(req, res);
}));
exports.validateEquipmentReturn = onRequest(httpOptions, withCors((req, res) => {
return require("./src/events").validateEquipmentReturn(req, res);
}));
exports.validateAllReturn = onRequest(httpOptions, withCors((req, res) => {
return require("./src/events").validateAllReturn(req, res);
}));
// ============================================================================
// MAINTENANCES
// ============================================================================
@@ -379,15 +363,19 @@ exports.aiEquipmentProposal = onRequest(aiHttpOptions, withCors((req, res) => {
// CALLABLE EMAIL & VALIDATION (LEGACY LAZY WRAPPERS)
// ============================================================================
exports.sendAlertEmail = onCall({region: "europe-west9", cors: true}, (request) => {
return require("./sendAlertEmail").sendAlertEmail(request);
return require("./sendAlertEmail").handler(request);
});
exports.createAlert = onCall({region: "europe-west9", cors: true}, (request) => {
return require("./createAlert").createAlert(request);
return require("./createAlert").handler(request);
});
exports.processEquipmentValidation = onCall({region: "europe-west9", cors: true}, (request) => {
return require("./processEquipmentValidation").processEquipmentValidation(request);
return require("./processEquipmentValidation").handler(request);
});
exports.rollbackEventStep = onCall({region: "europe-west9", cors: true}, (request) => {
return require("./rollbackEventStep").handler(request);
});
// ============================================================================
@@ -515,3 +503,97 @@ exports.onAlertCreated = onDocumentCreated({
logger.error("[onAlertCreated] Erreur:", error);
}
});
exports.onEventReturnCompleted = onDocumentUpdated({
document: "events/{eventId}",
region: "europe-west9",
}, async (event) => {
const before = event.data.before.data();
const after = event.data.after.data();
const eventId = event.params.eventId;
try {
const beforeReturnStatus = (before.returnStatus || "").toString().toUpperCase();
const afterReturnStatus = (after.returnStatus || "").toString().toUpperCase();
if (afterReturnStatus === "COMPLETED" && beforeReturnStatus !== "COMPLETED") {
logger.info(`[onEventReturnCompleted] Event ${eventId} returnStatus completed. Resetting assigned equipments...`);
const eventRef = db.collection("events").doc(eventId);
await db.runTransaction(async (transaction) => {
const currentEventDoc = await transaction.get(eventRef);
if (!currentEventDoc.exists) {
logger.warn(`[onEventReturnCompleted] Event doc ${eventId} not found during transaction.`);
return;
}
const currentEventData = currentEventDoc.data();
if (currentEventData.stocksRestored === true) {
logger.info(`[onEventReturnCompleted] Stocks already restored for event ${eventId}, skipping.`);
return;
}
const assignedEquipment = currentEventData.assignedEquipment || [];
if (assignedEquipment.length === 0) {
logger.info(`[onEventReturnCompleted] No assigned equipment for event ${eventId}.`);
transaction.update(eventRef, {
stocksRestored: true,
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
});
return;
}
// Fetch all unique equipment docs in the transaction
const equipmentIds = Array.from(new Set(assignedEquipment.map((eq) => eq.equipmentId).filter(Boolean)));
const equipmentDocsMap = {};
for (const eqId of equipmentIds) {
const eqRef = db.collection("equipments").doc(eqId);
const eqDoc = await transaction.get(eqRef);
if (eqDoc.exists) {
equipmentDocsMap[eqId] = eqDoc.data();
}
}
// Update equipment statuses and quantities
for (const eq of assignedEquipment) {
const eqId = eq.equipmentId;
const equipmentData = equipmentDocsMap[eqId];
if (!equipmentData) continue;
const hasQuantity = equipmentData.hasQuantity === true ||
equipmentData.category === "CABLE" ||
equipmentData.category === "CONSUMABLE";
const eqRef = db.collection("equipments").doc(eqId);
if (!hasQuantity) {
// Non-consumable: reset to AVAILABLE
transaction.update(eqRef, {
status: "AVAILABLE",
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
});
logger.info(`[onEventReturnCompleted] Set status to AVAILABLE for equipment ${eqId}`);
} else if (hasQuantity && eq.quantityAtReturn !== undefined && eq.quantityAtReturn !== null) {
// Consumable: increment availableQuantity
const currentAvailable = Number(equipmentData.availableQuantity) || 0;
const returnedQty = Number(eq.quantityAtReturn) || 0;
transaction.update(eqRef, {
availableQuantity: currentAvailable + returnedQty,
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
});
logger.info(`[onEventReturnCompleted] Restored ${returnedQty} items for consumable ${eqId} (new available: ${currentAvailable + returnedQty})`);
}
}
// Mark event as stocksRestored
transaction.update(eventRef, {
stocksRestored: true,
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
});
});
logger.info(`[onEventReturnCompleted] Transaction completed successfully for event ${eventId}`);
}
} catch (error) {
logger.error(`[onEventReturnCompleted] Error resetting equipment statuses for event ${eventId}:`, error);
}
});