Initial commit: CLEO ERP avec améliorations debug

- Configuration du debug conditionnel pour dev/recette
- Fonction debug() globale avec niveaux
- Logging des requêtes SQL
- Handlers d'exceptions et d'erreurs globaux

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-04 10:24:52 +02:00
commit 99021b4f42
7348 changed files with 11423897 additions and 0 deletions

361
pub/res/ts/jmarches.ts Normal file
View File

@@ -0,0 +1,361 @@
//! jmarches.js
let idMarche;
let libMarche;
let oldColorLn;
let oldIdLn;
window.addEventListener('DOMContentLoaded', (event) => {
console.log('DOM chargé');
// Initialisation des éléments utilisés
let elLigMarches = document.getElementsByClassName("ligMarche");
let elCelMarches = document.getElementsByClassName("celMarche");
let elBtnModMarches = document.getElementsByClassName("btnModMarche");
let elBtnCancelModMarche = document.getElementById("btnCancelModMarche");
let elBtnSubmitModMarche = document.getElementById("btnSubmitModMarche");
let elBtnCreateMarche = document.getElementById("btnCreateMarche");
let elBtnImportProduits = document.getElementById("btnImportProduits");
let elBtnCancelImportMarche = document.getElementById("btnCancelImportMarche");
let elBtnSubmitImportMarche = document.getElementById("btnSubmitImportMarche");
// ██████╗██╗ ██╗ ██████╗██╗ ██╗██╗ ██╗ ██████╗ ███╗ ███╗ █████╗ ██████╗ ██████╗██╗ ██╗███████╗
// ██╔════╝██║ ██║██╔════╝██║ ██╔╝██║ ██║██╔════╝ ████╗ ████║██╔══██╗██╔══██╗██╔════╝██║ ██║██╔════╝
// ██║ ██║ ██║██║ █████╔╝ ██║ ██║██║ ███╗██╔████╔██║███████║██████╔╝██║ ███████║█████╗
// ██║ ██║ ██║██║ ██╔═██╗ ██║ ██║██║ ██║██║╚██╔╝██║██╔══██║██╔══██╗██║ ██╔══██║██╔══╝
// ╚██████╗███████╗██║╚██████╗██║ ██╗███████╗██║╚██████╔╝██║ ╚═╝ ██║██║ ██║██║ ██║╚██████╗██║ ██║███████╗
// ╚═════╝╚══════╝╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝
let clickLigMarche = function () {
//! Event Click sur une cellule d'une ligne d'un marché qui a la classe celMarches
idMarche = this.getAttribute("data-rowid");
libMarche = this.getAttribute("data-libelle");
let thisColor;
console.log("idMarche : " + idMarche);
Array.from(elCelMarches).forEach(function (lngMarche) {
if (lngMarche.getAttribute("data-rowid") == oldIdLn) {
lngMarche.style.backgroundColor = oldColorLn;
} else if (lngMarche.getAttribute("data-rowid") == idMarche) {
thisColor = lngMarche.style.backgroundColor;
lngMarche.style.backgroundColor = "#9bbce7";
}
});
oldColorLn = thisColor;
oldIdLn = idMarche;
// Chargement du tableau des produits au click d'un marché
document.getElementById("loadingDiv").style.display = "block";
let xhttp = new XMLHttpRequest();
xhttp.open("POST", "/jxpost/load_marche_produits", true);
xhttp.setRequestHeader("Content-Type", "application/json");
xhttp.onreadystatechange = function () {
if (this.readyState == XMLHttpRequest.DONE && this.status == 200) {
showNotification("Chargement", "Chargement des produits du marché " + libMarche + " terminé", "success");
let tblBody = document.getElementById("tblMarchesProduits").getElementsByTagName("tbody")[0];
// on vide le body de cette table
tblBody.innerHTML = "";
let ret = JSON.parse(this.responseText);
if (ret.length > 0) {
// au moins un produit trouvé pour ce marché
let nbProduits = ret.length;
let nbLignes = 0;
for (let key in ret) {
if (ret.hasOwnProperty(key)) {
// Récupération des valeurs de la ligne
let val = ret[key];
// Insertion d'une nouvelle ligne et création de ses colonnes
let newRow = tblBody.insertRow(0);
let celCode = newRow.insertCell(0);
celCode.innerHTML = val['code'];
let celLibelle = newRow.insertCell(1);
celLibelle.innerHTML = val['libelle'];
let celGroupe = newRow.insertCell(2);
celGroupe.innerHTML = val['groupe'];
let celAchatNet = newRow.insertCell(3);
celAchatNet.className = "text-right";
celAchatNet.innerHTML = val['prix_achat_net'] + " €";
let celVentePub = newRow.insertCell(4);
celVentePub.className = "text-right";
celVentePub.innerHTML = val['prix_vente_public'] + " €";
nbLignes++;
if (nbLignes > 600) {
// TODO: mettre en place un chargement à la volée avec une pagination sur le tableau des produits
break;
}
}
}
document.getElementById("pnlProduitsTitre").innerHTML = '<i class="fa fa-cubes"></i> Liste des ' + nbProduits + ' produits du marché ' + libMarche;
} else {
// Aucun produit trouvé
document.getElementById("pnlProduitsTitre").innerHTML = '<i class="fa fa-cubes"></i> Liste des produits du marché ' + libMarche;
let newRow = tblBody.insertRow(0);
let celCode = newRow.insertCell(0);
celCode.colSpan = 5;
celCode.innerHTML = "<strong>Aucun produit trouvé pour ce marché</strong>";
}
}
};
let data = {cid: idMarche};
xhttp.send(JSON.stringify(data));
document.getElementById("loadingDiv").style.display = "none";
return false;
};
// ██████╗██╗ ██╗ ██████╗██╗ ██╗███╗ ███╗ ██████╗ ██████╗ ███╗ ███╗ █████╗ ██████╗ ██████╗██╗ ██╗███████╗
// ██╔════╝██║ ██║██╔════╝██║ ██╔╝████╗ ████║██╔═══██╗██╔══██╗████╗ ████║██╔══██╗██╔══██╗██╔════╝██║ ██║██╔════╝
// ██║ ██║ ██║██║ █████╔╝ ██╔████╔██║██║ ██║██║ ██║██╔████╔██║███████║██████╔╝██║ ███████║█████╗
// ██║ ██║ ██║██║ ██╔═██╗ ██║╚██╔╝██║██║ ██║██║ ██║██║╚██╔╝██║██╔══██║██╔══██╗██║ ██╔══██║██╔══╝
// ╚██████╗███████╗██║╚██████╗██║ ██╗██║ ╚═╝ ██║╚██████╔╝██████╔╝██║ ╚═╝ ██║██║ ██║██║ ██║╚██████╗██║ ██║███████╗
// ╚═════╝╚══════╝╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝
let clickModMarche = function () {
//! Event Click sur un bouton de modification d'une ligne d'un marché
idMarche = this.getAttribute("data-rowid");
libMarche = this.getAttribute("data-libelle");
// Va chercher la 1ère cellule de la ligne pour lancer la function click() => clickLigMarche
let firstCellOfRow = this.parentNode.parentNode.cells[0];
firstCellOfRow.click();
let xhttp = new XMLHttpRequest();
xhttp.open("POST", "/jxpost/load_marche", true);
xhttp.setRequestHeader("Content-Type", "application/json");
xhttp.onreadystatechange = function () {
if (this.readyState == XMLHttpRequest.DONE && this.status == 200) {
let ret = JSON.parse(this.responseText);
if (ret.length == 1) {
// 1 marché récupéré
let marche = ret[0];
document.getElementById("marcheAct").value = "M";
document.getElementById("marcheRowid").value = marche.rowid;
document.getElementById("marcheLibelle").value = marche.libelle;
if (marche.chk_remise_sur_tg == '1') {
document.getElementById("marcheChk_remise_sur_tg").checked = true;
} else {
document.getElementById("marcheChk_remise_sur_tg").checked = false;
}
if (marche.chk_prix_nets == '1') {
document.getElementById("marcheChk_prix_nets").checked = true;
} else {
document.getElementById("marcheChk_prix_nets").checked = false;
}
if (marche.chk_marche_public == '1') {
document.getElementById("marcheChk_marche_public").checked = true;
} else {
document.getElementById("marcheChk_marche_public").checked = false;
}
document.getElementById("marcheTaux_remise_trimestrielle").value = marche.taux_remise_trimestrielle;
document.getElementById("marcheTaux_remise_semestrielle").value = marche.taux_remise_semestrielle;
document.getElementById("marcheTaux_remise_annuelle").value = marche.taux_remise_annuelle;
document.getElementById("marcheDate_debut").value = new Date(marche.date_debut).toLocaleDateString("fr");
document.getElementById("marcheDate_fin").value = new Date(marche.date_fin).toLocaleDateString("fr");
document.getElementById("marcheDate_validite_prix").value = new Date(marche.date_validite_prix).toLocaleDateString("fr");
document.getElementById("marcheFranco_de_port").value = marche.franco_de_port;
document.getElementById("marcheGarantie").value = marche.garantie;
document.getElementById("marcheDelai_de_livraison").value = marche.delai_de_livraison;
document.getElementById("marcheRemises_commerciales").value = marche.remises_commerciales;
document.getElementById("marcheRemise_palier_1").value = marche.remise_palier_1;
document.getElementById("marcheRemise_taux_1").value = marche.remise_taux_1;
document.getElementById("marcheRemise_palier_2").value = marche.remise_palier_2;
document.getElementById("marcheRemise_taux_2").value = marche.remise_taux_2;
document.getElementById("marcheRemise_palier_3").value = marche.remise_palier_3;
document.getElementById("marcheRemise_taux_3").value = marche.remise_taux_3;
document.getElementById("marcheRemise_palier_4").value = marche.remise_palier_4;
document.getElementById("marcheRemise_taux_4").value = marche.remise_taux_4;
document.getElementById("marcheCommentaire").value = marche.commentaire;
if (marche.active == '1') {
document.getElementById("marcheActive").checked = true;
} else {
document.getElementById("marcheActive").checked = false;
}
if (marche.chk_cache_commerciaux == '1') {
document.getElementById("marcheChk_cache_commerciaux").checked = true;
} else {
document.getElementById("marcheChk_cache_commerciaux").checked = false;
}
document.getElementById("modMarcheTitre").innerHTML = "Modification de la fiche du marché " + libMarche;
showModal(document.getElementById("modalEditMarche"));
document.getElementById("marcheLibelle").focus();
}
}
};
let data = {cid: idMarche};
xhttp.send(JSON.stringify(data));
return false;
}
let clickCancelModMarche = function () {
//! Event Click sur le bouton Annuler de la modale d'un marché
hideModal(document.getElementById("modalEditMarche"));
}
// section submitModMarche
let clickSubmitModMarche = function () {
//! Event Click sur le bouton Enregistrer de la modale d'un marché
if (!document.getElementById("marcheLibelle").value) {
showNotification("Enregistrement impossible", "Le marché doit au moins avoir un titre", "warning");
document.getElementById("marcheLibelle").focus();
return false;
}
let xhttp = new XMLHttpRequest();
xhttp.open("POST", "/jxpost/save_marche", true);
xhttp.setRequestHeader("Content-Type", "application/json");
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
let retour = JSON.parse(this.responseText);
if (retour.ret == "ok") {
showNotification("Enregistrement", "L'enregistrement de cette fiche marché a été effectué avec succès", "success");
} else {
showNotification("Enregistrement", "Erreur lors de l'enregistrement de cette fiche marché", "error");
}
}
};
let frmData = new FormData(document.getElementById('frmMarche'));
let objData = {};
frmData.forEach(function (value, key) {
objData[key] = value;
});
xhttp.send(JSON.stringify(objData));
// TODO: on rafraîchit la ligne mise à jour dans le tableau
if (document.getElementById("marcheRowid").value == "0") {
//! si c'était une création, on recharge totalement la page
window.location.reload();
} else {
//! on recherche la ligne du tableau à mettre à jour
let cell;
Array.from(elLigMarches).forEach(function (lngMarche) {
cell = lngMarche.cells[0];
if (cell.getAttribute("data-rowid") == idMarche) {
lngMarche.cells[0].innerHTML = document.getElementById("marcheLibelle").value;
if (document.getElementById("marcheActive").checked) {
lngMarche.cells[1].innerHTML = "Oui";
} else {
lngMarche.cells[1].innerHTML = "Non";
}
lngMarche.cells[2].innerHTML = document.getElementById("marcheDate_debut").value;
lngMarche.cells[3].innerHTML = document.getElementById("marcheDate_fin").value;
lngMarche.cells[4].innerHTML = document.getElementById("marcheDate_validite_prix").value;
}
})
hideModal(document.getElementById("modalEditMarche"));
}
return false;
}
let clickImportProduitsMarche = function () {
//! Event Click sur le bouton d'importation des produits d'un marché
// ██╗███╗ ███╗██████╗ ██████╗ ██████╗ ████████╗
// ██║████╗ ████║██╔══██╗██╔═══██╗██╔══██╗╚══██╔══╝
// ██║██╔████╔██║██████╔╝██║ ██║██████╔╝ ██║
// ██║██║╚██╔╝██║██╔═══╝ ██║ ██║██╔══██╗ ██║
// ██║██║ ╚═╝ ██║██║ ╚██████╔╝██║ ██║ ██║
// ╚═╝╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
if (idMarche) {
//! un marché a été sélectionné
document.getElementById("modImportTitre").innerHTML = "Importation des produits du marché " + libMarche;
document.getElementById("importIdMarche").value = idMarche;
showModal(document.getElementById("modalImportMarche"));
} else {
showNotification("Importation", "Vous devez d'abord sélectionner un marché avant de pouvoir importer ses produits", "warning");
}
}
let clickCancelImportMarche = function () {
//! Event Click sur le bouton Annuler de la modale d'importation des produits d'un marché
hideModal(document.getElementById("modalImportMarche"));
}
// section submitImportProduits
let clickSubmitImportProduitsMarche = function () {
//! Click Importation du CSV des produits d'un marché
if (document.getElementById('importTypeFile').value == "0") {
showNotification("Importation", "Vous devez sélectionner un type de fichier (Achat / Vente)", "warning");
return false;
}
let xhttp = new XMLHttpRequest();
xhttp.open("POST", "/jximport/upload_marche_produits", true);
// add progress event to find the progress of file upload
xhttp.upload.addEventListener("progress", jxProgress);
xhttp.onreadystatechange = function () {
if (this.readyState == XMLHttpRequest.DONE && this.status == 200) {
let retour = JSON.parse(this.responseText);
let msg = "";
if (retour.ret == "ok") {
msg = (retour.message) ? retour.message : "L'importation des produits de ce marché a été effectuée avec succès";
showNotification("Importation", msg, "success");
} else {
msg = (retour.message) ? retour.message : "Erreur lors de l'importation des produits de ce marché";
showNotification("Importation", msg, "error");
}
}
};
let frmData = new FormData();
frmData.append("file1", document.getElementById("importFileMarche").files[0]);
frmData.append("MAX_FILE_SIZE", document.getElementById("MAX_FILE_SIZE").value)
frmData.append("importIdMarche", document.getElementById("importIdMarche").value);
xhttp.send(frmData);
hideModal(document.getElementById("modalImportMarche"));
return false;
}
// section createMarche
let clickCreateMarche = function () {
//! Event Click sur le bouton de création d'un marché
$("form#frmMarche").find("input[type=text], textarea").val("");
$("form#frmMarche").find("input[type=number]").val("");
document.getElementById("marcheRowid").value = "0";
document.getElementById("modMarcheTitre").innerHTML = "Création d'un nouveau marché";
showModal(document.getElementById("modalEditMarche"));
document.getElementById("marcheLibelle").focus();
}
// section Config functions Click
//! Sur chaque cellule du tableau des marchés ayant la classe celMarches, on affecte un événement click qui appelle la fonction clickLnMarche()
Array.from(elCelMarches).forEach(function (lnMarche) {
lnMarche.addEventListener('click', clickLigMarche);
});
//! Sur chaque bouton de modification du tableau des marchés ayant la classe btnModMarche, on affecte un événement click qui appelle la fonction clickModMarche()
Array.from(elBtnModMarches).forEach(function (modMarche) {
modMarche.addEventListener('click', clickModMarche);
});
elBtnCancelModMarche.addEventListener('click', clickCancelModMarche);
elBtnSubmitModMarche.addEventListener('click', clickSubmitModMarche);
elBtnCreateMarche.addEventListener('click', clickCreateMarche);
elBtnImportProduits.addEventListener('click', clickImportProduitsMarche);
elBtnCancelImportMarche.addEventListener('click', clickCancelImportMarche);
elBtnSubmitImportMarche.addEventListener('click', clickSubmitImportProduitsMarche);
});