Files
EM2_ERP/em2rp/functions/rollbackEventStep.js
T

186 lines
6.6 KiB
JavaScript

const {onCall} = require("firebase-functions/v2/https");
const admin = require("firebase-admin");
const logger = require("firebase-functions/logger");
/**
* Reverts the validation progress of an event to a target step.
* Resets subsequent step statuses and validation flags of assigned equipment.
* If rolling back from a completed return, it decrements the consumable stock quantities
* that were restored during return validation.
*/
const handler = async (request) => {
try {
const {auth, data} = request;
if (!auth) {
throw new Error("L'utilisateur doit être authentifié");
}
const {eventId, targetStep} = data;
if (!eventId || !targetStep) {
throw new Error("eventId et targetStep sont requis");
}
const db = admin.firestore();
const eventRef = db.collection("events").doc(eventId);
await db.runTransaction(async (transaction) => {
const eventDoc = await transaction.get(eventRef);
if (!eventDoc.exists) {
throw new Error("Événement introuvable");
}
const event = eventDoc.data();
const assignedEquipment = event.assignedEquipment || [];
// Si le retour était complété et qu'on revient en arrière, on doit annuler la restauration des stocks
const shouldRevertStocks = event.stocksRestored === true;
if (shouldRevertStocks) {
// Charger tous les équipements uniques de l'événement pour ajuster leur stock
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();
}
}
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";
if (hasQuantity) {
// C'est un consommable, on doit déduire la quantité qui avait été restaurée
const qtyAtRet = Number(eq.quantityAtReturn) || 0;
if (qtyAtRet > 0) {
const eqRef = db.collection("equipments").doc(eqId);
const currentAvailable = Number(equipmentData.availableQuantity) || 0;
// S'assurer de ne pas descendre en dessous de 0 (ou autoriser le négatif si stock virtuel)
const newAvailable = Math.max(0, currentAvailable - qtyAtRet);
transaction.update(eqRef, {
availableQuantity: newAvailable,
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
});
logger.info(`[rollbackEventStep] Annulé la restauration de ${qtyAtRet} pour ${eqId}. Ancien stock: ${currentAvailable}, Nouveau stock: ${newAvailable}`);
}
}
}
}
// Préparer les nouvelles valeurs des étapes
let prepStatus = event.preparationStatus;
let loadStatus = event.loadingStatus;
let unloadStatus = event.unloadingStatus;
let retStatus = event.returnStatus;
if (targetStep === 'PREPARATION') {
prepStatus = 'IN_PROGRESS';
loadStatus = 'NOT_STARTED';
unloadStatus = 'NOT_STARTED';
retStatus = 'NOT_STARTED';
} else if (targetStep === 'LOADING') {
loadStatus = 'IN_PROGRESS';
unloadStatus = 'NOT_STARTED';
retStatus = 'NOT_STARTED';
} else if (targetStep === 'UNLOADING') {
unloadStatus = 'IN_PROGRESS';
retStatus = 'NOT_STARTED';
} else if (targetStep === 'RETURN') {
retStatus = 'IN_PROGRESS';
} else {
throw new Error("targetStep invalide. Doit être PREPARATION, LOADING, UNLOADING ou RETURN");
}
// Nettoyer les champs de validation des équipements pour les étapes annulées
const updatedEquipment = assignedEquipment.map((eq) => {
let isPrepared = eq.isPrepared;
let isMissingAtPreparation = eq.isMissingAtPreparation;
let quantityAtPreparation = eq.quantityAtPreparation;
let isLoaded = eq.isLoaded;
let isMissingAtLoading = eq.isMissingAtLoading;
let quantityAtLoading = eq.quantityAtLoading;
let isUnloaded = eq.isUnloaded;
let isMissingAtUnloading = eq.isMissingAtUnloading;
let quantityAtUnloading = eq.quantityAtUnloading;
let isReturned = eq.isReturned;
let isMissingAtReturn = eq.isMissingAtReturn;
let quantityAtReturn = eq.quantityAtReturn;
if (targetStep === 'PREPARATION') {
isLoaded = false;
isMissingAtLoading = false;
quantityAtLoading = null;
isUnloaded = false;
isMissingAtUnloading = false;
quantityAtUnloading = null;
isReturned = false;
isMissingAtReturn = false;
quantityAtReturn = null;
} else if (targetStep === 'LOADING') {
isUnloaded = false;
isMissingAtUnloading = false;
quantityAtUnloading = null;
isReturned = false;
isMissingAtReturn = false;
quantityAtReturn = null;
} else if (targetStep === 'UNLOADING') {
isReturned = false;
isMissingAtReturn = false;
quantityAtReturn = null;
}
return {
...eq,
isPrepared,
isMissingAtPreparation,
quantityAtPreparation,
isLoaded,
isMissingAtLoading,
quantityAtLoading,
isUnloaded,
isMissingAtUnloading,
quantityAtUnloading,
isReturned,
isMissingAtReturn,
quantityAtReturn,
};
});
// Mettre à jour le document de l'événement
transaction.update(eventRef, {
preparationStatus: prepStatus,
loadingStatus: loadStatus,
unloadingStatus: unloadStatus,
returnStatus: retStatus,
assignedEquipment: updatedEquipment,
stocksRestored: false,
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
});
});
logger.info(`[rollbackEventStep] Événement ${eventId} réinitialisé avec succès à l'étape ${targetStep}`);
return {success: true};
} catch (error) {
logger.error("[rollbackEventStep] Erreur:", error);
throw error;
}
};
exports.rollbackEventStep = onCall({
cors: true,
region: "europe-west9",
}, handler);
exports.handler = handler;