817 lines
28 KiB
JavaScript
817 lines
28 KiB
JavaScript
// public/js/historiqueParcours.js
|
|
|
|
document.addEventListener("DOMContentLoaded", async function () {
|
|
// Récupération du token
|
|
const token = localStorage.getItem("jwtToken");
|
|
|
|
if (!token) {
|
|
throw new Error("Aucun token trouvé dans le localStorage.");
|
|
}
|
|
|
|
const userData = parseJwt(token);
|
|
|
|
if (!userData) {
|
|
displayError("Erreur lors de l'extraction des données utilisateur à partir du token.");
|
|
return;
|
|
}
|
|
|
|
// Initialiser DataTables en mode server-side (obligé pour pagination)
|
|
const table = initServerSideDataTable();
|
|
|
|
// Variable pour suivre l'état des exports
|
|
let isExporting = false;
|
|
|
|
// Fonction pour désactiver/activer les boutons d'export
|
|
function setExportButtonsState(disabled) {
|
|
isExporting = disabled;
|
|
const buttons = ["#exportCSV", "#exportCSVFilter", "#exportXlxs", "#exportXlxsFilter"];
|
|
buttons.forEach(selector => {
|
|
const $btn = $(selector);
|
|
$btn.prop("disabled", disabled);
|
|
if (disabled) {
|
|
$btn.css("opacity", "0.5");
|
|
$btn.css("cursor", "not-allowed");
|
|
} else {
|
|
$btn.css("opacity", "1");
|
|
$btn.css("cursor", "pointer");
|
|
}
|
|
});
|
|
}
|
|
|
|
// Exports CSV/XLSX
|
|
$("#exportCSV").on("click", function () {
|
|
if (isExporting) return;
|
|
|
|
const dt = $("#historiqueParcours").DataTable();
|
|
if (!dt) {
|
|
displayError("Impossible d'accéder à la table de données.");
|
|
return;
|
|
}
|
|
|
|
const settings = dt.settings()[0];
|
|
if (!settings || !settings.aoColumns) {
|
|
displayError("Structure de données invalide.");
|
|
return;
|
|
}
|
|
|
|
setExportButtonsState(true);
|
|
|
|
const payload = {
|
|
mode: "full", // export total
|
|
search: { value: "" },
|
|
columns: settings.aoColumns.map((c, i) => ({
|
|
data: i,
|
|
search: { value: "" }
|
|
})),
|
|
// on garde l'ordre actuel pour la récuperation
|
|
order: (dt.order() || []).map(([col, dir]) => ({ column: col || 0, dir: dir || "asc" }))
|
|
};
|
|
|
|
fetch("/historiqueParcours/export/csv", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload),
|
|
})
|
|
.then(resp => {
|
|
if (!resp.ok) throw new Error("Export CSV (complet) impossible");
|
|
|
|
return resp.blob();
|
|
})
|
|
.then(blob => {
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = "historique_parcours_complet.csv";
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
a.remove();
|
|
setExportButtonsState(false);
|
|
})
|
|
.catch((err) => {
|
|
displayError("Export CSV (complet) impossible");
|
|
setExportButtonsState(false);
|
|
});
|
|
});
|
|
|
|
|
|
$("#exportCSVFilter").on("click", function () {
|
|
if (isExporting) return;
|
|
|
|
const dt = $("#historiqueParcours").DataTable();
|
|
if (!dt) {
|
|
displayError("Impossible d'accéder à la table de données.");
|
|
return;
|
|
}
|
|
|
|
const settings = dt.settings()[0];
|
|
if (!settings || !settings.aoColumns) {
|
|
displayError("Structure de données invalide.");
|
|
return;
|
|
}
|
|
|
|
setExportButtonsState(true);
|
|
|
|
const payload = {
|
|
mode: "filtered", // export avec les filtres/colonnes/tri actuels
|
|
search: { value: dt.search() || "" }, // recherche globale
|
|
columns: settings.aoColumns.map((c, i) => ({
|
|
data: i,
|
|
search: { value: dt.column(i).search() || "" } // filtres par colonne
|
|
})),
|
|
order: (dt.order() || []).map(([col, dir]) => ({ column: col || 0, dir: dir || "asc" }))
|
|
};
|
|
|
|
fetch("/historiqueParcours/export/csv", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload),
|
|
})
|
|
.then(resp => {
|
|
if (!resp.ok) throw new Error("Export CSV (filtré) impossible");
|
|
return resp.blob();
|
|
})
|
|
.then(blob => {
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = "historique_parcours_filtre.csv";
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
a.remove();
|
|
setExportButtonsState(false);
|
|
})
|
|
.catch((err) => {
|
|
displayError("Export CSV (filtré) impossible");
|
|
setExportButtonsState(false);
|
|
});
|
|
});
|
|
|
|
|
|
$("#exportXlxs").on("click", function () {
|
|
if (isExporting) return;
|
|
|
|
const dt = $("#historiqueParcours").DataTable();
|
|
if (!dt) {
|
|
displayError("Impossible d'accéder à la table de données.");
|
|
return;
|
|
}
|
|
|
|
const settings = dt.settings()[0];
|
|
if (!settings || !settings.aoColumns) {
|
|
displayError("Structure de données invalide.");
|
|
return;
|
|
}
|
|
|
|
setExportButtonsState(true);
|
|
|
|
const payload = {
|
|
mode: "full",
|
|
search: { value: "" },
|
|
columns: settings.aoColumns.map((c, i) => ({ data: i, search: { value: "" } })),
|
|
order: (dt.order() || []).map(([col, dir]) => ({ column: col || 0, dir: dir || "asc" }))
|
|
};
|
|
|
|
fetch("/historiqueParcours/export/xls", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload),
|
|
})
|
|
.then(resp => { if (!resp.ok) throw new Error(); return resp.blob(); })
|
|
.then(blob => {
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url; a.download = "historique_parcours_complet.xls";
|
|
document.body.appendChild(a); a.click();
|
|
URL.revokeObjectURL(url); a.remove();
|
|
setExportButtonsState(false);
|
|
})
|
|
.catch((err) => {
|
|
displayError("Export XLS (complet) impossible");
|
|
setExportButtonsState(false);
|
|
});
|
|
});
|
|
|
|
|
|
$("#exportXlxsFilter").on("click", function () {
|
|
if (isExporting) return;
|
|
|
|
const dt = $("#historiqueParcours").DataTable();
|
|
if (!dt) {
|
|
displayError("Impossible d'accéder à la table de données.");
|
|
return;
|
|
}
|
|
|
|
const settings = dt.settings()[0];
|
|
if (!settings || !settings.aoColumns) {
|
|
displayError("Structure de données invalide.");
|
|
return;
|
|
}
|
|
|
|
setExportButtonsState(true);
|
|
|
|
const payload = {
|
|
mode: "filtered", // export avec les filtres/colonnes/tri actuels
|
|
search: { value: dt.search() || "" }, // recherche globale
|
|
columns: settings.aoColumns.map((c, i) => ({
|
|
data: i,
|
|
search: { value: dt.column(i).search() || "" } // filtres par colonne
|
|
})),
|
|
order: (dt.order() || []).map(([col, dir]) => ({ column: col || 0, dir: dir || "asc" }))
|
|
};
|
|
|
|
fetch("/historiqueParcours/export/xls", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload),
|
|
})
|
|
.then(resp => {
|
|
if (!resp.ok) throw new Error("Export XLS (filtré) impossible");
|
|
return resp.blob();
|
|
})
|
|
.then(blob => {
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = "historique_parcours_filtre.xls";
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
a.remove();
|
|
setExportButtonsState(false);
|
|
})
|
|
.catch((err) => {
|
|
displayError("Export XLS (filtré) impossible");
|
|
setExportButtonsState(false);
|
|
});
|
|
});
|
|
|
|
|
|
// Délégation pour la génération de projet
|
|
$("#historiqueParcours").on("click", "button#btnGenerate", function () {
|
|
const numParcours = $(this).data("num-parcours");
|
|
const produit = $(this).data("produit");
|
|
generateProject(numParcours, produit);
|
|
});
|
|
});
|
|
|
|
/* =========================
|
|
* Helpers spécifiques server-side
|
|
* ========================= */
|
|
|
|
// Initialisation DataTables en server-side (recherche globale + par colonnes + tri + pagination)
|
|
function initServerSideDataTable() {
|
|
let inflightController = null;
|
|
|
|
const table = $("#historiqueParcours").DataTable({
|
|
processing: true,
|
|
serverSide: true,
|
|
searching: true,
|
|
paging: true,
|
|
orderCellsTop: true,
|
|
fixedHeader: true,
|
|
responsive: { details: false },
|
|
pageLength: 10,
|
|
retrieve: true,
|
|
order: [[0, "desc"]],
|
|
searchDelay: 350,
|
|
language: {
|
|
search: "Rechercher",
|
|
lengthMenu: "Afficher _MENU_ entrées par page",
|
|
info: "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
|
|
infoEmpty: "Affichage de 0 à 0 sur 0 entrée",
|
|
infoFiltered: "(filtré de _MAX_ entrées au total)",
|
|
paginate: { first: "Début", previous: "Précédent", next: "Suivant", last: "Fin" },
|
|
},
|
|
|
|
ajax: function (data, callback) {
|
|
const body = {
|
|
draw: data.draw || 1,
|
|
start: data.start || 0,
|
|
length: data.length || 10,
|
|
order: data.order || [],
|
|
columns: data.columns || [],
|
|
search: data.search || { value: "" },
|
|
};
|
|
|
|
if (inflightController) inflightController.abort(); // action en cours
|
|
inflightController = new AbortController();
|
|
const currentController = inflightController;
|
|
|
|
fetch("/historiqueParcours/datatable", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(body),
|
|
signal: currentController.signal,
|
|
})
|
|
.then(res => {
|
|
// Vérifier si la requête a été annulée
|
|
if (currentController.signal.aborted || inflightController !== currentController) {
|
|
return null;
|
|
}
|
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
return res.json();
|
|
})
|
|
.then(payload => {
|
|
// Vérifier si la requête a été annulée
|
|
if (currentController.signal.aborted || inflightController !== currentController) {
|
|
return;
|
|
}
|
|
if (!payload || typeof payload !== 'object') {
|
|
throw new Error("Réponse invalide du serveur");
|
|
}
|
|
callback({
|
|
draw: payload.draw || 0,
|
|
recordsTotal: payload.recordsTotal || 0,
|
|
recordsFiltered: payload.recordsFiltered || payload.recordsTotal || 0,
|
|
data: Array.isArray(payload.data) ? payload.data : []
|
|
});
|
|
})
|
|
.catch(err => {
|
|
// Ignorer silencieusement toutes les erreurs d'abort
|
|
if (err && (err.name === "AbortError" || err.name === "DOMException")) {
|
|
return;
|
|
}
|
|
// Vérifier aussi si le signal a été aborted
|
|
if (currentController.signal.aborted || inflightController !== currentController) {
|
|
return;
|
|
}
|
|
// Seulement afficher une erreur si ce n'est PAS un abort
|
|
displayError("Failed to fetch data. Please try again later.");
|
|
callback({ draw: 0, recordsTotal: 0, recordsFiltered: 0, data: [] });
|
|
});
|
|
},
|
|
|
|
initComplete: function () {
|
|
const api = this.api();
|
|
|
|
// Recherche globale : debounce y compris ENTER (plus de bypass immédiat)
|
|
const $globalInput = $('div.dataTables_filter input[type="search"]');
|
|
const $filterLabel = $globalInput.closest('label');
|
|
$globalInput.off('.DT'); // nettoie handlers datatables
|
|
const debouncedGlobal = debounce((v) => {
|
|
api.search(v);
|
|
api.ajax.reload();
|
|
}, 350);
|
|
|
|
// Fonction pour gérer l'affichage du texte "Rechercher"
|
|
function toggleSearchPlaceholder() {
|
|
if ($globalInput.val().trim() !== '') {
|
|
$filterLabel.addClass('has-value');
|
|
} else {
|
|
$filterLabel.removeClass('has-value');
|
|
}
|
|
}
|
|
|
|
$globalInput.on('input keyup keydown', function () {
|
|
debouncedGlobal(this.value);
|
|
toggleSearchPlaceholder();
|
|
});
|
|
|
|
// Vérifier l'état initial
|
|
toggleSearchPlaceholder();
|
|
|
|
// Recherche par colonne avec DEBOUNCE (ENTER inclus)
|
|
const debouncedColSearch = debounce((i, val) => {
|
|
api.column(i).search(val);
|
|
api.ajax.reload();
|
|
}, 350);
|
|
|
|
$("#historiqueParcours thead tr:eq(1) th").each(function (i) {
|
|
$("input", this).on("input keyup keydown change", function () {
|
|
debouncedColSearch(i, this.value);
|
|
});
|
|
});
|
|
|
|
api.on("responsive-resize", function (e, datatable, columns) {
|
|
for (let i = 0; i < columns.length; i++) {
|
|
if (columns[i]) {
|
|
$(api.column(i).header()).show();
|
|
$(api.column(i).footer()).show();
|
|
$($("#historiqueParcours thead tr:eq(1) th")[i]).show();
|
|
}
|
|
else {
|
|
$(api.column(i).header()).hide();
|
|
$(api.column(i).footer()).hide();
|
|
$($("#historiqueParcours thead tr:eq(1) th")[i]).hide();
|
|
}
|
|
}
|
|
});
|
|
|
|
$("#divToggleSearch").on("click", function () {
|
|
const $row = $("#historiqueParcours thead tr:eq(1)");
|
|
$row.toggle();
|
|
const $btn = $("#toggleSearch");
|
|
if ($row.is(":visible")) {
|
|
$btn.text("ENLEVER LA RECHERCHE PAR COLONNE");
|
|
} else {
|
|
$btn.text("ACTIVER LA RECHERCHE PAR COLONNE");
|
|
}
|
|
});
|
|
|
|
// Cacher la 2e ligne au départ
|
|
$("#historiqueParcours thead tr:eq(1)").hide();
|
|
},
|
|
|
|
// --- Ajout bouton détails en 1re colonne
|
|
columnDefs: [
|
|
{
|
|
targets: 0,
|
|
render: function (data, type, row) {
|
|
const np = String(data ?? "");
|
|
const numPacours = np.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
|
|
|
return '<span style="display:inline-flex; align-items:center; gap:8px;">'
|
|
+ '<button class="btn-row-details" title="Détails" aria-label="Afficher les détails"'
|
|
+ ' style="display:inline-block; width:22px; height:22px; border:none; border-radius:4px;'
|
|
+ ' background:#e74c3c; color:#fff; font-weight:700; cursor:pointer; line-height:22px;'
|
|
+ ' text-align:center; font-size:14px;">?</button>'
|
|
+ '<span class="np">' + numPacours + '</span>'
|
|
+ '</span>';
|
|
|
|
}
|
|
},
|
|
{ type: "date-eu", targets: 1 }, // Date de Création (colonne 1)
|
|
{
|
|
// Appliquer la classe nc-value aux cellules contenant "NC"
|
|
targets: "_all",
|
|
createdCell: function (td, cellData, rowData, row, col) {
|
|
// Exclure la première colonne (bouton détails) et les deux dernières (boutons)
|
|
if (col !== 0 && col < rowData.length - 2) {
|
|
const cellText = String(cellData || "").trim();
|
|
if (cellText === "NC") {
|
|
td.classList.add("nc-value");
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{ responsivePriority: 1, targets: -1 },
|
|
{ responsivePriority: 2, targets: -2 }
|
|
],
|
|
});
|
|
|
|
// --- Toggle des détails
|
|
$('#historiqueParcours tbody').on('click', '.btn-row-details', function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
const api = $('#historiqueParcours').DataTable();
|
|
const $tr = $(this).closest('tr');
|
|
|
|
// si c'est une ligne enfant (responsive), remonter à la parent
|
|
const row = api.row($tr.hasClass('child') ? $tr.prev() : $tr);
|
|
|
|
if (row.child.isShown()) {
|
|
row.child.hide();
|
|
// change icône
|
|
this.textContent = "?";
|
|
this.style.background = "#e74c3c";
|
|
return;
|
|
}
|
|
|
|
// récupérer le numParcours depuis la 1re cellule
|
|
const raw = row.data()?.[0] ?? "";
|
|
const tmp = document.createElement('div');
|
|
tmp.innerHTML = String(raw);
|
|
const numParcours = tmp.textContent.replace("?", "").trim();
|
|
if (!numParcours) return;
|
|
|
|
row.child('<div style="padding:12px 16px; font-style:italic;">Chargement des détails…</div>').show();
|
|
this.textContent = "?";
|
|
this.style.background = "#c0392b";
|
|
|
|
fetch(`/historiqueParcours/details/${encodeURIComponent(numParcours)}`)
|
|
.then(r => {
|
|
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
return r.json();
|
|
})
|
|
.then(payload => {
|
|
if (!payload || typeof payload !== 'object') {
|
|
throw new Error("Réponse invalide");
|
|
}
|
|
if (!payload.valid) {
|
|
row.child(createMessageBox('error', 'Impossible de charger les détails',
|
|
'Une erreur est survenue lors du chargement des détails du parcours.'));
|
|
this.textContent = "?";
|
|
this.style.background = "#e74c3c";
|
|
return;
|
|
}
|
|
row.child(formatDetailsPanel(payload));
|
|
})
|
|
.catch(() => {
|
|
row.child(createMessageBox('error', 'Erreur de chargement',
|
|
'Une erreur réseau est survenue lors du chargement des détails.'));
|
|
this.textContent = "?";
|
|
this.style.background = "#e74c3c";
|
|
});
|
|
});
|
|
|
|
return table;
|
|
}
|
|
|
|
/* =========================
|
|
* Fonctions annexes importantes
|
|
* ========================= */
|
|
|
|
async function fetchUserDetails(matriculeUser) {
|
|
try {
|
|
const response = await fetch(`/user/read/matricule/${matriculeUser}`);
|
|
const data = await response.json();
|
|
|
|
return data.valid ? data : null;
|
|
} catch (error) {
|
|
displayError(`Erreur lors de la récupération du contrat avec le matricule ${matriculeUser} :`, error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function generateProject(numParcours, produit) {
|
|
try {
|
|
if (!numParcours || !produit) {
|
|
displayError("Paramètres manquants pour la génération du projet.");
|
|
return;
|
|
}
|
|
|
|
const response = await fetch(`/generate/${produit}/projet/${encodeURIComponent(numParcours)}`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
});
|
|
|
|
if (!response.ok) throw new Error("Erreur réseau ou serveur");
|
|
|
|
const disposition = response.headers.get("content-disposition");
|
|
let filename = "projet.docx";
|
|
if (disposition) {
|
|
const parts = disposition.split(";");
|
|
if (parts.length > 1) {
|
|
const filenamePart = parts[1].trim();
|
|
if (filenamePart.startsWith("filename=")) {
|
|
filename = filenamePart.split("=")[1]?.replace(/"/g, "") || filename;
|
|
}
|
|
}
|
|
}
|
|
|
|
const blob = await response.blob();
|
|
if (!blob || blob.size === 0) {
|
|
throw new Error("Fichier vide reçu");
|
|
}
|
|
|
|
const url = window.URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = filename;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
window.URL.revokeObjectURL(url);
|
|
a.remove();
|
|
} catch (error) {
|
|
displayError("Erreur lors de la génération du projet : " + (error?.message || "Erreur inconnue"));
|
|
}
|
|
}
|
|
|
|
// ---------- Helpers rendu ----------
|
|
// Les fonctions kv, fmtDate, gridWrap2cols, debounce, parseJwt et displayError sont dans global.js
|
|
|
|
//formatDetailsPanel : rend l'HTML pour la récuperation des données par parcours
|
|
function formatDetailsPanel(payload) {
|
|
const prodKey = (payload.produit || "").toUpperCase();
|
|
const prod = payload.produitRecord || null;
|
|
const contrat = payload.contrat || null;
|
|
|
|
let body = "";
|
|
if (prodKey === "TPPC") body = sectionTPPC(prod, contrat);
|
|
else if (prodKey === "RC") body = sectionRC(prod, contrat);
|
|
else if (prodKey === "FAC") body = sectionFAC(prod, contrat);
|
|
else body = createMessageBox('warn', 'Produit non renseigné', 'Le type de produit n\'a pas été spécifié pour ce parcours.');
|
|
|
|
return `
|
|
<div class="parcours-details" style="padding:14px 16px; background:#fafafa; border-top:1px solid #e0e0e0;">
|
|
<div style="font-weight:600; margin:0 0 8px;">Détails ${prodKey || ""}</div>
|
|
${body}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// ---------- Sections produit ----------
|
|
|
|
//sectionTTPC : récupere les détails d'un parcours TPPC pour le mettre en forme (2 colonnes)
|
|
function sectionTPPC(produit, contrat) {
|
|
if (!produit) {
|
|
if (contrat) {
|
|
return createMessageBox('info', 'Informations non disponibles',
|
|
'Les informations sur ce Parcours TPPC ne sont pas encore disponibles.<br>La fiche TPPC n\'a pas encore été créée pour ce parcours.');
|
|
}
|
|
return createMessageBox('error', 'Fiche TPPC introuvable',
|
|
'Impossible de récupérer les informations de la fiche TPPC pour ce parcours.');
|
|
}
|
|
|
|
const tarif = produit?.["@expand"]?.tarif || null; // tppctarif
|
|
const projet = produit?.["@expand"]?.projet || null; // tppcprojet
|
|
|
|
const garant = Array.isArray(produit.garanties) ? produit.garanties.join(", ") : (produit.garanties || "NC");
|
|
|
|
// Extensions de garanties
|
|
const extensions = [];
|
|
if (produit.marCiternes) extensions.push("Citernes");
|
|
if (produit.marDenreesSousTemp) extensions.push("Denrées sous température");
|
|
if (produit.marAnimaux) extensions.push("Animaux vivants");
|
|
if (produit.marFranchise) extensions.push("Franchise");
|
|
const extensionsStr = extensions.length > 0 ? extensions.join(", ") : "Aucune";
|
|
|
|
// Type de cotisation et type de révision (couplés)
|
|
let typeCotStr = "NC";
|
|
let typeRevStr = "NC";
|
|
if (projet) {
|
|
if (projet.typeCot) {
|
|
typeCotStr = projet.typeCot === "revisable" ? "Revisable" : (projet.typeCot === "forfaitaire" ? "Forfaitaire" : projet.typeCot);
|
|
}
|
|
if (projet.typeRev) {
|
|
// Mapping des valeurs possibles du type de révision
|
|
if (projet.typeRev === "CotCA") {
|
|
typeRevStr = "Cotisation CA";
|
|
} else if (projet.typeRev === "CotFlotte" || projet.typeRev === "FlotteOuverte") {
|
|
typeRevStr = "Cotisation Flotte";
|
|
} else {
|
|
// Si autre valeur, on l'affiche telle quelle
|
|
typeRevStr = projet.typeRev;
|
|
}
|
|
} else if (projet.typeCot === "revisable") {
|
|
// Si revisable mais pas de typeRev, on regarde le tarif
|
|
if (tarif?.typeContrat === "detaillee") {
|
|
typeRevStr = "Cotisation Flotte";
|
|
}
|
|
}
|
|
}
|
|
|
|
const gauche = [
|
|
kv("Activité assurée", produit.actAssuree),
|
|
kv("Nombre de véhicules", produit.nbVehic),
|
|
kv("Garanties", garant),
|
|
kv("Extensions de garanties", extensionsStr),
|
|
kv("Type de cotisation", typeCotStr),
|
|
kv("Type de révision", typeRevStr),
|
|
].join("");
|
|
|
|
// Formatage des dates du projet
|
|
const dateEffet = projet?.dateEffet ? fmtDate(projet.dateEffet, false) : "NC";
|
|
const dateEcheance = projet?.dateEcheance ? fmtDate(projet.dateEcheance, false) : "NC";
|
|
const dateFin = projet?.dateFin ? fmtDate(projet.dateFin, false) : "NC";
|
|
|
|
const droite = [
|
|
kv("Prime HT", produit.primeHT),
|
|
kv("Tarif - Référence", tarif?.tarifRef),
|
|
kv("Projet - Dates", `Effet: ${dateEffet} / Échéance: ${dateEcheance} / Fin: ${dateFin}`),
|
|
].join("");
|
|
|
|
return gridWrap2cols(gauche, droite);
|
|
}
|
|
|
|
|
|
//sectionRC : récupere les détails d'un parcours RC pour le mettre en forme (2 colonnes)
|
|
function sectionRC(produit, contrat) {
|
|
// Si le produit n'existe pas mais qu'on a un contrat RC, c'est que la fiche n'est pas encore créée
|
|
if (!produit && contrat) {
|
|
return createMessageBox('info', 'Informations non disponibles',
|
|
'Les informations sur ce Parcours RC ne sont pas encore disponibles.<br>La fiche RC n\'a pas encore été créée pour ce parcours.');
|
|
}
|
|
|
|
// Si le produit n'existe pas et qu'on n'a pas de contrat, cas théorique (ne devrait jamais arriver)
|
|
if (!produit) {
|
|
return createMessageBox('dev', 'Fonctionnalité en cours de développement',
|
|
'L\'affichage des détails pour les parcours RC n\'est pas encore disponible.<br>Cette fonctionnalité sera bientôt implémentée.');
|
|
}
|
|
|
|
// Construction de l'activité assurée à partir des activités
|
|
const activites = [];
|
|
if (produit.actVoiturier) activites.push("Voiturier");
|
|
if (produit.actLoueur) activites.push("Loueur");
|
|
if (produit.actMultimodal) activites.push("Multimodal");
|
|
if (produit.actDouane) activites.push("Douane");
|
|
if (produit.actLevageur) activites.push("Levageur");
|
|
if (produit.actTransitaire) activites.push("Transitaire");
|
|
const activiteAssuree = activites.length > 0 ? activites.join(", ") : "NC";
|
|
|
|
// Construction des garanties à partir des marchandises
|
|
const garanties = [];
|
|
if (produit.marRoulant) garanties.push("Roulant");
|
|
if (produit.marEngins) garanties.push("Engins");
|
|
if (produit.marPerissable) garanties.push("Périssable");
|
|
if (produit.marOrdinaire) garanties.push("Ordinaire");
|
|
if (produit.marAnimaux) garanties.push("Animaux");
|
|
if (produit.marCiterne) garanties.push("Citerne");
|
|
if (produit.marBeton) garanties.push("Béton");
|
|
if (produit.marExceptionnels) garanties.push("Exceptionnels");
|
|
if (produit.marMobilerUsag) garanties.push("Mobilier usagé");
|
|
if (produit.marVrac) garanties.push("Vrac");
|
|
if (produit.marRoulantDem) garanties.push("Roulant déménagement");
|
|
const garantiesStr = garanties.length > 0 ? garanties.join(", ") : "NC";
|
|
|
|
// Zones
|
|
const zones = [];
|
|
if (produit.zone1) zones.push("1");
|
|
if (produit.zone2) zones.push("2");
|
|
if (produit.zone3) zones.push("3");
|
|
if (produit.zone4) zones.push("4");
|
|
if (produit.zone5) zones.push("5");
|
|
if (produit.zone6) zones.push("6");
|
|
const zonesStr = zones.length > 0 ? zones.join(", ") : "NC";
|
|
|
|
// Type de cotisation
|
|
let typeCotStr = "NC";
|
|
if (produit.typeCot) {
|
|
typeCotStr = produit.typeCot === "revisable" ? "Revisable" : (produit.typeCot === "forfaitaire" ? "Forfaitaire" : produit.typeCot);
|
|
}
|
|
|
|
// Formatage des dates
|
|
const dateEffet = produit.dateEffet ? fmtDate(produit.dateEffet, false) : "NC";
|
|
const dateEcheance = produit.dateEcheance ? fmtDate(produit.dateEcheance, false) : "NC";
|
|
const dateFin = produit.dateFin ? fmtDate(produit.dateFin, false) : "NC";
|
|
|
|
// Vérifier si on a un tarif (pas disponible pour RC actuellement)
|
|
const tarif = produit?.["@expand"]?.tarif || null;
|
|
const tarifRef = tarif?.tarifRef || "en cours de développement";
|
|
|
|
const gauche = [
|
|
kv("Activité assurée", activiteAssuree),
|
|
kv("Garanties", garantiesStr),
|
|
kv("Zones", zonesStr),
|
|
kv("Type de cotisation", typeCotStr),
|
|
kv("Tempo", produit.tempo || "NC"),
|
|
kv("Chiffre d'affaires", produit.ca || "NC"),
|
|
].join("");
|
|
|
|
const droite = [
|
|
kv("Tarif - Référence", tarifRef),
|
|
kv("Cot. totale HT / TTC", `${produit.cotTotalHT ?? "NC"} / ${produit.cotTotalTTC ?? "NC"}`),
|
|
kv("Dates", `Effet: ${dateEffet} / Échéance: ${dateEcheance} / Fin: ${dateFin}`),
|
|
].join("");
|
|
|
|
return gridWrap2cols(gauche, droite);
|
|
}
|
|
|
|
//sectionFAC : récupere les détails d'un parcours FAC pour le mettre en forme (2 colonnes)
|
|
function sectionFAC(produit, contrat) {
|
|
// Si le produit n'existe pas mais qu'on a un contrat FAC, c'est que la fiche n'est pas encore créée
|
|
if (!produit && contrat) {
|
|
return createMessageBox('info', 'Informations non disponibles',
|
|
'Les informations sur ce Parcours FAC ne sont pas encore disponibles.<br>La fiche FAC n\'a pas encore été créée pour ce parcours.');
|
|
}
|
|
|
|
// Si le produit n'existe pas et qu'on n'a pas de contrat, cas théorique (ne devrait jamais arriver)
|
|
if (!produit) {
|
|
return createMessageBox('dev', 'Fonctionnalité en cours de développement',
|
|
'L\'affichage des détails pour les parcours FAC n\'est pas encore disponible.<br>Cette fonctionnalité sera bientôt implémentée.');
|
|
}
|
|
|
|
// Construction des garanties à partir des modes de transport
|
|
const garanties = [];
|
|
if (produit.terrestre && produit.terrestre !== "NC" && produit.terrestre !== "") garanties.push("Terrestre");
|
|
if (produit.maritime && produit.maritime !== "NC" && produit.maritime !== "") garanties.push("Maritime");
|
|
if (produit.aerien && produit.aerien !== "NC" && produit.aerien !== "") garanties.push("Aérien");
|
|
if (produit.postal && produit.postal !== "NC" && produit.postal !== "") garanties.push("Postal");
|
|
if (produit.fluvial && produit.fluvial !== "NC" && produit.fluvial !== "") garanties.push("Fluvial");
|
|
const garantiesStr = garanties.length > 0 ? garanties.join(", ") : "NC";
|
|
|
|
// Zones
|
|
const zones = [];
|
|
if (produit.zone1) zones.push("1");
|
|
if (produit.zone2) zones.push("2");
|
|
if (produit.zone3) zones.push("3");
|
|
if (produit.zone4) zones.push("4");
|
|
if (produit.zone5) zones.push("5");
|
|
if (produit.zone6) zones.push("6");
|
|
const zonesStr = zones.length > 0 ? zones.join(", ") : "NC";
|
|
|
|
// Type de contrat
|
|
let typeContratStr = "NC";
|
|
if (produit.typeContrat) {
|
|
typeContratStr = produit.typeContrat;
|
|
}
|
|
|
|
// Formatage des dates
|
|
const dateEffet = produit.dateEffet ? fmtDate(produit.dateEffet, false) : "NC";
|
|
const dateEcheance = produit.dateEcheance ? fmtDate(produit.dateEcheance, false) : "NC";
|
|
const dateFin = produit.dateFin ? fmtDate(produit.dateFin, false) : "NC";
|
|
|
|
// Vérifier si on a un tarif (pas disponible pour FAC actuellement)
|
|
const tarif = produit?.["@expand"]?.tarif || null;
|
|
const tarifRef = tarif?.tarifRef || "en cours de développement";
|
|
|
|
const gauche = [
|
|
kv("Activité assurée", produit.actAssure || "NC"),
|
|
kv("Garanties", garantiesStr),
|
|
kv("Zones", zonesStr),
|
|
kv("Type de contrat", typeContratStr),
|
|
kv("Tempo", produit.tempo || "NC"),
|
|
kv("Chiffre d'affaires", produit.ca || "NC"),
|
|
].join("");
|
|
|
|
const droite = [
|
|
kv("Tarif - Référence", tarifRef),
|
|
kv("Cot. annuelle HT / TTC", `${produit.cotAnnuelleHT ?? "NC"} / ${produit.cotAnnuelleTTC ?? "NC"}`),
|
|
kv("Dates", `Effet: ${dateEffet} / Échéance: ${dateEcheance} / Fin: ${dateFin}`),
|
|
].join("");
|
|
|
|
return gridWrap2cols(gauche, droite);
|
|
} |