186 lines
6.6 KiB
JavaScript
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;
|