Files
Cleo/pub/res/js/jdevis.js
Pierre 046c23f2d2 Initial commit - Application CLEO de gestion de devis
- Architecture MVC avec framework maison d6
- Modules : devis, clients, marchés, SAP
- Documentation initiale (README et TODO)
- Configuration Composer avec dépendances

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 18:26:07 +02:00

2975 lines
138 KiB
JavaScript

//! jdevis.js
let chkPageLoad = true; // indique que la page vient d'être chargée pour la première fois
let idDevis = 0;
let fkUser = 0;
let fkRole = 0;
let devIp = '0';
let fkUserDevis = 0;
let fkStatutDevis = 0;
let chkValidat = 0;
let oldColorLn;
let oldIdLn;
let chkChange = 0;
let idMarche = '';
let idNewMarche = ''; // dans le cas on l'utilisateur change de marché sur un devis déjà créé
let chkClientsSecteur;
let chkShowDevisArchives = false; // indique si on affiche les devis archivés ou non
let chkCreateClient = false;
// On charge les produits du marché du devis en cours dans un tableau pour ne pas avoir à les recharger à chaque fois
let dataProduitsMarche = [];
//! Pour ne charger les clients du secteur ou de toute la France qu'en cas de changement de la valeur du chkbox
let oldChkClientsSecteur = 2;
let clients = [];
let chkPrixNets = false; // le marché du devis en cours est-il en prix nets ou pas
let remiseMarcheDeBase = 0; // la remise de base du marché
let chkSaisieRemise = false;
let chkRemisesMarche = true; // indique si toutes les lignes du devis appliquent la remise du marché ou pas
let aRemisesMarches = [];
let devisTotalHT = 0;
let devisTotalRemHT = 0;
let devisTotalMarge = 0;
let chkRegleSeuilsMarge = false; // indique si le marché sélectionné prend en compte les seuils de marge fixés dans les familles de produits
let seuilMargeRR = 30; // le seuil de marge du RR sur ce devis, par défaut à 30 %
let seuilMargeDV = 20; // le seuil de marge du DV sur ce devis, par défaut à 20 %
let intervalRefresh;
let nbCommentChat = 0;
let draggedElement = null; // l'élément qui est en train d'être déplacé (la ligne du produit du devis lors d'un drag and drop)
window.addEventListener('DOMContentLoaded', (event) => {
console.log('#');
// Initialisation des éléments utilisés
let elCelDevis = document.getElementsByClassName('celDevis');
let elCelArchives = document.getElementsByClassName('celArchives');
let elBtnDupDevis = document.getElementsByClassName('btnDupDevis');
let elBtnSupprDevis = document.getElementsByClassName('btnSupprDevis');
let elBtnExpExcelDevis = document.getElementsByClassName('btnExpExcelDevis');
let elBtnValDevis = document.getElementsByClassName('btnValDevis');
let elBtnPdfDevis = document.getElementsByClassName('btnPdfDevis');
let elBtnValidationRR = document.getElementById('btnValidationRR');
let elBtnRefusRR = document.getElementById('btnRefusRR');
let elBtnCloseRR = document.getElementById('btnCloseRR');
let elBtnClosePDF = document.getElementById('btnClosePDF');
let elBtnDevisArchives = document.getElementById('btnDevisArchives');
let elBtnCreateDevis = document.getElementById('btnCreateDevis');
let elBtnCreateClient = document.getElementById('btnCreateClient');
let elBtnCancelCreateClient = document.getElementById('btnCancelCreateClient');
let elBtnSaveCreateClient = document.getElementById('btnSaveCreateClient');
let elBtnSpeciaux = document.getElementById('btnSpeciaux');
let elBtnCancelSpeciaux = document.getElementById('btnCancelSpeciaux');
let elBtnSaveSpeciaux = document.getElementById('btnSaveSpeciaux');
let elBtnSaveEnTete = document.getElementById('btnSaveEnTete');
let elBtnSaveSelProduits = document.getElementById('btnSaveSelProduits');
let elBtnSaveDevis = document.getElementById('btnSaveDevis');
let elBtnSaveDevisAndSend = document.getElementById('btnSaveDevisAndSend');
let elChkClientsSecteur = document.getElementById('inp_chk_clients_secteur');
let elInputSearchProducts = document.querySelectorAll("input[id^='inpSearchProduct_']");
let elInputQtes = document.querySelectorAll("input[name^='inpQte_']");
let elInputRemises = document.querySelectorAll("input[name^='inpRemise_']");
let elChkVariantes = document.querySelectorAll("input[type='checkbox'][name^='chkVariante_']");
let elInputDateDemande = document.getElementById('inp_date_demande');
let elInputDateRemise = document.getElementById('inp_date_remise');
let elListOngletsProduits = document.getElementById('listOngletsProduits');
let elOngletsProduits = document.querySelectorAll('[id^="onglet_"]');
let elProdSelect = document.querySelectorAll('input[type="checkbox"][name^="chkBoxProd_"]');
let elInpCommentGesteComm = document.getElementById('inpCommentGesteComm');
let elChatBtnSend = document.getElementById('chatBtnSend');
let elBtnCancelCommentProd = document.getElementById('btnCancelCommentProd');
let elBtnSaveCommentProd = document.getElementById('btnSaveCommentProd');
//! Au chargement de la page on affiche le menu vertical de choix du devis et on cache les 3 onglets
const elDivDevis = document.getElementById('divDevis');
const elDossStatuts = document.getElementById('vb-dossiers-statuts');
const elDossArchives = document.getElementById('vb-dossiers-archives');
// Par défaut on n'affiche pas le chat
document.getElementById('chat-container').style.display = 'none';
const elVerticalBar = document.getElementById('verticalBar');
const elBtnSideBarDevis = document.getElementById('btnSideBarDevis');
// par défaut on affiche les dossiers par statuts de devis
elDossStatuts.classList.remove('hidden');
elDossArchives.classList.add('hidden');
elVerticalBar.style.width = '1100px';
elDivDevis.style.display = 'none';
//! On récupère les données contextuelles propres à l'utilisateur
fetch('/jxpost/get_context', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
}).then((response) => {
const ret = response.json();
ret.then(function (data) {
const user = data.user;
fkUser = user.rowid;
fkRole = user.fk_role;
devIp = data.devip;
const session = data.session;
});
});
let clickDevisArchives = function () {
// click sur le bouton de la sidebar pour afficher les devis archivés ou revenir sur les devis en cours
elDossStatuts.classList.toggle('hidden');
idDevis = 0;
const archivesHidden = elDossArchives.classList.toggle('hidden');
if (archivesHidden) {
this.innerHTML = 'Mes devis archivés';
chkShowDevisArchives = false;
document.getElementById('chat-message-input').classList.remove('hidden');
// Il faut afficher tous les boutons d'enregistrement
elBtnSaveEnTete.classList.remove('hidden');
elBtnSaveSelProduits.classList.remove('hidden');
elBtnSaveDevis.classList.remove('hidden');
elBtnSaveDevisAndSend.classList.remove('hidden');
elBtnSaveSpeciaux.classList.remove('hidden');
} else {
this.innerHTML = 'Mes devis en cours';
chkShowDevisArchives = true;
document.getElementById('chat-message-input').classList.add('hidden');
// Il faut cacher tous les boutons d'enregistrement
elBtnSaveEnTete.classList.add('hidden');
elBtnSaveSelProduits.classList.add('hidden');
elBtnSaveDevis.classList.add('hidden');
elBtnSaveDevisAndSend.classList.add('hidden');
elBtnSaveSpeciaux.classList.add('hidden');
}
document.getElementById('chat-container').style.display = 'none';
elDivDevis.style.display = 'none';
return false;
};
let clickLigDevis = function () {
//! L'utilisateur vient de cliquer sur un devis dans la liste de gauche
//! On ne fait rien si l'utilisateur clique sur le même devis
if (this.getAttribute('data-rid') != idDevis) {
if (chkChange == 1) {
if (confirm('Attention, vous avez des modifications non enregistrées sur ce devis. Voulez-vous continuer ?')) {
chkChange = 0;
} else {
return false;
}
}
idDevis = this.getAttribute('data-rid');
showLoading();
// on met à jour l'input caché contenant l'id du devis sélectionné
document.getElementById('inpIdDevis').value = idDevis;
let dataFamilles;
//! on charge les familles de groupes de produits pour mettre à jour le tableau de chaque onglet
fetch('/jxdevis/load_familles', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
}).then((response) => {
if (!response.ok) {
showNotification('Erreur', "Le chargement des familles de produits n'a pas abouti", 'error');
} else {
const ret = response.json();
ret.then(function (data) {
dataFamilles = data;
});
}
});
// effectue la requête ajax fetch pour charger les produits du marché
fetch('/jxdevis/load_devis', {
method: 'POST',
body: JSON.stringify({ cid: idDevis }),
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
})
.then((response) => {
if (!response.ok) {
showNotification('Erreur', "Le chargement des infos de l'en-tête de ce devis n'a pas abouti", 'error');
} else {
const ret = response.json();
ret
.then(function (data) {
fkUserDevis = data[0].fk_user;
fkStatutDevis = data[0].fk_statut_devis;
chkValidat = data[0].chk_validat;
idMarche = data[0].fk_marche;
idNewMarche = data[0].fk_marche; // par défaut le nouveau marché est le même que le marché en cours sur ce devis
chkClientsSecteur = data[0].chk_clients_secteur;
showDevisEnTete(data);
updateBtnSpeciaux(data[0].chk_speciaux);
showDevisTotaux(data);
})
.then(function () {
//! Une fois le marché trouvé, on charge les infos du marché préchargé dans l'en-tête du devis
fetch('/jxdevis/load_devis_marche_infos', {
method: 'POST',
body: JSON.stringify({ cid: idMarche }),
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
}).then((response) => {
if (!response.ok) {
showNotification('Erreur', "Le chargement des infos du marché n'a pas abouti", 'error');
} else {
const ret = response.json();
//! Boucle sur le résultat de la requête ajax
ret.then(function (data) {
showDevisMarcheInfos(data);
chkRegleSeuilsMarge = data[0].chk_regle_seuils_marge;
//! On charge ensuite les produits du marché de ce devis
fetch('/jxdevis/load_devis_marche_produits', {
method: 'POST',
body: JSON.stringify({ cid: idMarche }),
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
}).then((response) => {
if (!response.ok) {
showNotification('Erreur', "Le chargement des produits du marché n'a pas abouti", 'error');
} else {
const ret = response.json();
ret.then(function (data) {
dataProduitsMarche = data;
showDevisMarcheProduits(dataFamilles, data);
// on charge les produits enregistrés pour ce devis dans 2 tableaux distincts tblProduitsSelect (2ème onglet) et tblDevisPro (3ème onglet)
fetch('/jxdevis/load_devis_produits', {
method: 'POST',
body: JSON.stringify({ cid: idDevis }),
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
}).then((response) => {
if (!response.ok) {
showNotification('Erreur', "Le chargement des produits de ce devis n'a pas abouti", 'error');
} else {
const ret = response.json();
//! Boucle sur le résultat de la requête ajax
ret.then(function (data) {
showDevisProduits(data);
});
}
});
});
}
});
});
}
});
});
}
})
.catch((error) => {
showNotification('Erreur', "Le chargement des infos de l'en-tête de ce devis n'a pas abouti", 'error');
});
hideLoading();
chkPageLoad = false;
elDivDevis.style.display = 'block';
refreshChat();
//! On met enfin en évidence la ligne cliquée
Array.from(elCelDevis).forEach(function (ligDevis) {
if (ligDevis.getAttribute('data-rid') == oldIdLn) {
ligDevis.style.backgroundColor = oldColorLn;
} else if (ligDevis.getAttribute('data-rid') == idDevis) {
oldColorLn = ligDevis.style.backgroundColor;
ligDevis.style.backgroundColor = '#9bbce7';
}
});
oldIdLn = idDevis;
chkChange = 0;
}
};
let clickLigArchives = function () {
//! L'utilisateur vient de cliquer sur un devis archivé dans la liste de gauche
//! On ne fait rien si l'utilisateur clique sur le même devis
if (this.getAttribute('data-rid') != idDevis) {
if (chkChange == 1) {
if (confirm('Attention, vous avez des modifications non enregistrées sur le devis en cours. Voulez-vous continuer ?')) {
chkChange = 0;
} else {
return false;
}
}
idDevis = this.getAttribute('data-rid');
refreshChat();
//! On met enfin en évidence la ligne cliquée
Array.from(elCelArchives).forEach(function (ligArchive) {
if (ligArchive.getAttribute('data-rid') == oldIdLn) {
ligArchive.style.backgroundColor = oldColorLn;
} else if (ligArchive.getAttribute('data-rid') == idDevis) {
oldColorLn = ligArchive.style.backgroundColor;
ligArchive.style.backgroundColor = '#9bbce7';
}
});
oldIdLn = idDevis;
chkChange = 0;
}
};
function showDevisEnTete(ret) {
// Affiche les données de l'en-tête du devis
const data = ret[0];
document.getElementById('inp_rowid').value = data.rowid;
document.getElementById('inp_num_opportunite').value = data.num_opportunite;
document.getElementById('inp_date_demande').value = data.date_demande;
document.getElementById('inp_date_remise').value = data.date_remise;
document.getElementById('inp_fk_user').value = data.fk_user;
document.getElementById('inp_fk_marche').value = data.fk_marche;
// On surveille un changement dans le champ fk_marche, ce qui peut provoquer la suppression des produits du devis s'il enregistre ce changement
document.getElementById('inp_fk_marche').addEventListener('change', function () {
idNewMarche = this.value;
console.log('idNewMarche :' + idNewMarche);
});
if (data.chk_clients_secteur == '1') {
document.getElementById('inp_chk_clients_secteur').checked = true;
} else {
document.getElementById('inp_chk_clients_secteur').checked = false;
}
if (data.chk_clients_secteur != oldChkClientsSecteur) {
// la valeur du chk_clients_secteur est différente de l'actuelle, on charge les clients du commercial sur son secteur ou sur toute la France
changeClientsSecteur();
oldChkClientsSecteur == 2 ? (chkChange = 0) : (chkChange = 1);
oldChkClientsSecteur = data.chk_clients_secteur;
}
document.getElementById('inp_fk_client').value = data.fk_client;
console.log('fk_type_new :' + data.type_new_client);
if (data.fk_client == 0) {
document.getElementById('inp_lib_client').value = data.lib_new_client;
document.getElementById('inp_adresse1').value = data.adresse1_new_client;
document.getElementById('inp_adresse2').value = data.adresse2_new_client;
document.getElementById('inp_adresse3').value = data.adresse3_new_client;
document.getElementById('inp_cp').value = data.cp_new_client;
document.getElementById('inp_ville').value = data.ville_new_client;
document.getElementById('inp_contact_nom').value = data.contact_new_nom;
document.getElementById('inp_contact_prenom').value = data.contact_new_prenom;
document.getElementById('inp_contact_fonction').value = data.contact_new_fonction;
document.getElementById('inp_email').value = data.new_email;
document.getElementById('inp_telephone').value = data.new_telephone;
document.getElementById('inp_mobile').value = data.new_mobile;
document.getElementById('selTypeEtab').value = data.type_new_client;
elBtnCreateClient.innerHTML = 'Modifier ce nouveau client';
if (elBtnCreateClient.classList.contains('btn-primary')) {
elBtnCreateClient.classList.remove('btn-primary');
elBtnCreateClient.classList.add('btn-info');
}
} else {
document.getElementById('inp_lib_client').value = data.libelle;
document.getElementById('inp_adresse1').value = data.adresse1;
document.getElementById('inp_adresse2').value = data.adresse2;
document.getElementById('inp_adresse3').value = data.adresse3;
document.getElementById('inp_cp').value = data.cp;
document.getElementById('inp_ville').value = data.ville;
document.getElementById('inp_contact_nom').value = data.contact_nom;
document.getElementById('inp_contact_prenom').value = data.contact_prenom;
document.getElementById('inp_contact_fonction').value = data.contact_fonction;
document.getElementById('inp_email').value = data.email;
document.getElementById('inp_telephone').value = data.telephone;
document.getElementById('inp_mobile').value = data.mobile;
document.getElementById('selTypeEtab').value = data.type_client;
elBtnCreateClient.innerHTML = 'Créer un nouveau client';
if (elBtnCreateClient.classList.contains('btn-info')) {
elBtnCreateClient.classList.remove('btn-info');
elBtnCreateClient.classList.add('btn-primary');
}
}
if (data.chk_devis_photos == '1') {
document.getElementById('inp_chk_devis_photos').checked = true;
} else {
document.getElementById('inp_chk_devis_photos').checked = false;
}
// Gestion et affichage des commentaires
document.getElementById('inp_commentaire').value = data.commentaire;
document.getElementById('inpCommentDevis').value = data.comment_devis;
elInpCommentGesteComm.value = data.comment_geste_comm;
// On supprime systématiquement la ligne de validation du devis
let rowCommentValidatDevis = document.getElementById('rowCommentValidatDevis');
if (rowCommentValidatDevis !== null) {
rowCommentValidatDevis.remove();
}
if (fkUserDevis != fkUser && fkRole < 3) {
// Le user actuel n'est pas le créateur du devis, et son rôle est le DIR-CO ou un DV
const tblBodyComment = document.getElementById('tblCommentDevis').getElementsByTagName('tbody')[0];
// Insertion d'une nouvelle ligne et création de ses colonnes : on prend ici le fk_produit
let newRowComment = tblBodyComment.insertRow(0);
newRowComment.id = 'rowCommentValidatDevis';
let celLabel = newRowComment.insertCell(0);
celLabel.innerHTML = '<label for >Validation du devis</label>';
let celComment = newRowComment.insertCell(1);
celComment.classList.add('w-60');
celComment.innerHTML = '<input type="txt" class="form-control w-100" id="inpCommentValidatDevis" name="commentValidatDevis" value="' + data.comment_validat + '" />';
let celBtnValid = newRowComment.insertCell(2);
celBtnValid.classList.add('w-40');
celBtnValid.innerHTML = '<div class="btn-group"><button type="button" class="btn btn-success mr-1" id="btnValidatDevis" name="btnValidatDevis">Valider ce devis</button>';
celBtnValid.innerHTML += '<button type="button" class="btn btn-danger" id="btnRefusDevis" name="btnRefusDevis">Refuser ce devis</button></div>';
document.getElementById('btnValidatDevis').addEventListener('click', clickValidatDevis);
document.getElementById('btnRefusDevis').addEventListener('click', clickRefusDevis);
}
}
function updateBtnSpeciaux(chkSpeciaux) {
// Met à jour en fonction le bouton btnSpeciaux
console.log('chkSpeciaux = ' + chkSpeciaux);
const btnSpeciaux = document.getElementById('btnSpeciaux');
if (chkSpeciaux == '1') {
btnSpeciaux.innerHTML = '<span data-after-text="Sp" data-after-type="blue circle">Modifier les produits spéciaux </span>';
btnSpeciaux.classList.remove('btn-warning');
btnSpeciaux.classList.add('btn-info');
} else {
btnSpeciaux.innerHTML = '<span data-after-text="Sp" data-after-type="orange circle">Ajouter des produits spéciaux </span>';
btnSpeciaux.classList.remove('btn-info');
btnSpeciaux.classList.add('btn-warning');
}
}
function showDevisTotaux(ret) {
// Affiche les totaux du devis
const data = ret[0];
document.getElementById('inpTotalHT').value = formatAmount(data.montant_total_ht);
document.getElementById('inpTotalRemHT').value = formatAmount(data.montant_total_ht_remise);
document.getElementById('inpTotalMarge').value = data.marge_totale;
// on renseigne les valeurs globales de ces 3 données
devisTotalHT = data.montant_total_ht;
devisTotalRemHT = data.montant_total_ht_remise;
devisTotalMarge = data.marge_totale;
// on met à jour le bouton de sauvegarde du devis
updateBtnSaveDevisAndSend();
}
function updateBtnSaveDevisAndSend() {
// Si la marge Totale est inférieure au seuil de latitude, on change le bouton en orange ou rouge
let btn = document.getElementById('btnSaveDevisAndSend');
let typRole = 'DV';
if (fkRole == 3 || fkRole > 19) {
typRole = 'RR';
}
console.log('updateBtnSaveDevisAndSend : chkRemisesMarche = ' + chkRemisesMarche + ' & typRole = ' + typRole);
// if ((chkPrixNets || chkRemisesMarche) && typRole == "RR") {
if (chkPrixNets || chkRemisesMarche) {
// Modif du 10/04/2024 : dans tous les rôles si le devis est en prix nets ou en remises marchés on passe directement le devis à l'ADV/SAP
// Si le marché est en prix nets, ou si les lignes produits sont en remises marchés,
// on ne peut pas modifier les remises donc on envoie le devis directement à l'ADV/SAP
if (elInpCommentGesteComm.value != '') {
btn.classList.add('btn-warning');
btn.classList.remove('btn-success');
btn.classList.remove('btn-danger');
btn.innerHTML = 'Demander Accord DV/DCG';
btn.dataset.statut = '3';
} else {
btn.classList.add('btn-primary');
btn.classList.remove('btn-danger');
btn.classList.remove('btn-warning');
btn.innerHTML = 'Demander Traitement SAP';
btn.dataset.statut = '4';
}
} else {
const margeTotale = parseFloat(document.getElementById('inpTotalMarge').value);
const latitudeRR = parseFloat(document.getElementById('inp_latitudeRR').value);
const latitudeDV = parseFloat(document.getElementById('inp_latitudeDV').value);
console.log('btnSaveDevisAndSend : Marge totale =' + margeTotale + ' vs DV ' + latitudeDV + ' & RR ' + latitudeRR);
if ((typRole == 'RR' && margeTotale < latitudeRR) || (fkRole > 2 && elInpCommentGesteComm.value != '')) {
// si on est un RR commercial et que la marge totale est inférieure à la latitude RR 30%, on demande l'accord DV
// ou si on est un DV ou RR commercial et qu'on a saisi un geste commercial
btn.classList.add('btn-warning');
btn.classList.remove('btn-success');
btn.classList.remove('btn-danger');
btn.innerHTML = 'Demander Accord DV/DCG';
btn.dataset.statut = '3';
} else {
if ((fkRole == 2 && margeTotale < latitudeDV) || (fkRole == 2 && fkUserDevis == fkUser && elInpCommentGesteComm.value != '')) {
// si on est un DV et que la marge totale est inférieure à la latitude DV 20%, on demande l'accord DIR-CO
// ou si on est un DV et que le devis est le sien et qu'on a saisi un geste commercial
btn.classList.add('btn-danger');
btn.classList.remove('btn-success');
btn.classList.remove('btn-warning');
btn.innerHTML = 'Demander Accord DIR-CO';
btn.dataset.statut = '2';
} else {
// sinon on envoie le devis directement à l'ADV/SAP
btn.classList.add('btn-primary');
btn.classList.remove('btn-danger');
btn.classList.remove('btn-warning');
btn.innerHTML = 'Demander Traitement SAP';
btn.dataset.statut = '4';
}
}
}
}
function showDevisProduits(ret) {
//! On affiche les produits du devis dans les 2 tableaux
// tblProduitsSelect (2ème onglet : le tableau des produits sélectionnés)
// tblDevisPro (3ème onglet : le tableau de saisie des qté)
// On vide le tableau tblProduitsSelect
let tblBodySelect = document.getElementById('tblProduitsSelect').getElementsByTagName('tbody')[0];
tblBodySelect.innerHTML = '';
// On vide le tableau tblDevisPro
let tblBodyPro = document.getElementById('tblDevisPro').getElementsByTagName('tbody')[0];
tblBodyPro.innerHTML = '';
// Si le marché est un marché de prix nets, on met les champs de saisie des remises en readonly
let readonlyRemise = chkPrixNets ? 'readonly="readonly"' : '';
// Ajout du 20/02/2025 : si marché hybride, les produits de ce marché sont en prix nets
let readonlyRemiseProduit = '';
// Ajout du 26 juin 2024 : si l'utilisateur est le DC ou DV ou DGC, ils peuvent modifier les remises dans tous les cas
if (fkRole < 3 || fkRole == 5) {
readonlyRemise = '';
}
// Fin de l'ajout du 26 juin 2024
if (ret.length > 0) {
// au moins un produit trouvé pour ce devis
let nbProduits = ret.length;
// on récupère le premier fk_produit, pour simuler un changement sur ce produit pour recalculer les totaux en fin de boucle
const fkProduit1 = ret[0]['fk_produit'];
for (let key in ret) {
if (ret.hasOwnProperty(key)) {
// Récupération des valeurs de la ligne
let val = ret[key];
// On initialise le readonlyremise par produit pour gérer les cas de marché hybride où leurs produits sont en Prix Nets
readonlyRemiseProduit = readonlyRemise;
// Insertion d'une nouvelle ligne et création de ses colonnes : on prend ici le fk_produit
let newRowSelect = tblBodySelect.insertRow(-1);
let celChkBox = newRowSelect.insertCell(0);
celChkBox.className = 'text-center';
celChkBox.innerHTML = '<input type="checkbox" class="chkBox" name="chkProdSelect_' + val['fk_produit'] + '" data-rid="' + val['fk_produit'] + '" data-code="' + val['code'] + '" data-libelle="' + val['libelle'] + '" value="' + val['fk_produit'] + '"/>';
let celCode = newRowSelect.insertCell(1);
celCode.innerHTML = val['code'];
let celLibelle = newRowSelect.insertCell(2);
celLibelle.innerHTML = val['libelle'];
// Sur le tableau tblBodyPro
// Insertion d'une nouvelle ligne et création de ses colonnes : on prend ici le rowid de devis_produits
let newRowPro = tblBodyPro.insertRow(-1);
newRowPro.id = 'trPro_' + val['fk_produit'];
newRowPro.dataset.ordre = val['ordre'];
newRowPro.dataset.rid = val['fk_produit'];
newRowPro.dataset.code = val['code'];
newRowPro.dataset.achat = val['prix_achat_net'];
newRowPro.dataset.achatdiscount = val['prix_achat_net'];
newRowPro.dataset.vente = val['prix_vente'];
newRowPro.dataset.discount1 = val['prc_discount_1'];
newRowPro.dataset.quantite1 = val['quantite_1'];
newRowPro.dataset.discount2 = val['prc_discount_2'];
newRowPro.dataset.quantite2 = val['quantite_2'];
newRowPro.dataset.discount3 = val['prc_discount_3'];
newRowPro.dataset.quantite3 = val['quantite_3'];
newRowPro.dataset.discount4 = val['prc_discount_4'];
newRowPro.dataset.quantite4 = val['quantite_4'];
newRowPro.dataset.discount5 = val['prc_discount_5'];
newRowPro.dataset.quantite5 = val['quantite_5'];
newRowPro.dataset.discount6 = val['prc_discount_6'];
newRowPro.dataset.quantite6 = val['quantite_6'];
newRowPro.setAttribute('draggable', 'true');
newRowPro.addEventListener('dragstart', handleDragStart);
newRowPro.addEventListener('dragover', handleDragOver);
newRowPro.addEventListener('drop', handleDrop);
let celCodePro = newRowPro.insertCell(-1);
const svgColor = val['commentaire'] == '' ? 'lightgray' : 'red';
const svgComment = '<svg id="commentProd_' + val['fk_produit'] + '" class="clickable" data-rid="' + val['fk_produit'] + '" data-code="' + val['code'] + '"><use xlink:href="pub/res/svg/icons.svg#message" style="fill: ' + svgColor + ';"/></svg>';
let inputOrdreHidden = '<input type="hidden" id="inpOrdre_' + val['fk_produit'] + '" name="inpOrdre_' + val['fk_produit'] + '" value="' + val['ordre'] + '" />';
let inputCommentHidden = '<input type="hidden" id="inpCom_' + val['fk_produit'] + '" name="inpCom_' + val['fk_produit'] + '" value="' + val['commentaire'] + '" />';
celCodePro.innerHTML = val['code'] + ' ' + svgComment + inputOrdreHidden + inputCommentHidden;
document.getElementById('commentProd_' + val['fk_produit']).addEventListener('click', showCommentProd);
let celLibellePro = newRowPro.insertCell(1);
celLibellePro.innerHTML = val['libelle'];
let celPrixVentePro = newRowPro.insertCell(2);
celPrixVentePro.className = 'text-right';
celPrixVentePro.innerHTML = formatAmount(val['prix_vente']) + ' €';
let celQtePro = newRowPro.insertCell(3);
celQtePro.innerHTML =
'<input type="number" class="form-control numeric" id="inpQte_' +
val['fk_produit'] +
'" name="inpQte_' +
val['fk_produit'] +
'" data-rid="' +
val['fk_produit'] +
'" data-achat="' +
val['prix_achat_net'] +
'" data-vente="' +
val['prix_vente'] +
'" value="' +
val['qte'] +
'" min="0" max="1000" step="1" style="width: 100px; text-align: right;"><input type="hidden" name="achat_' +
val['fk_produit'] +
'" value="' +
val['prix_achat_net'] +
'"/><input type="hidden" name="vente_' +
val['fk_produit'] +
'" value="' +
val['prix_vente'] +
'"/>';
document.getElementById('inpQte_' + val['fk_produit']).addEventListener('change', calculDevis);
let celRemisePro = newRowPro.insertCell(4);
// Nouveau code 21/09
// S'il y a une remise de base sur le marché, on vérifie chaque remise produit pour l'aligner à cette remise de base si elle est supérieure
let remiseProduit = val['remise'];
console.log('Remise de base : ' + remiseMarcheDeBase + ' vs remise sur le produit : ' + val['remise']);
if (remiseMarcheDeBase > 0) {
if (val['remise'] < remiseMarcheDeBase) {
console.log('La remise du produit est inférieure à la remise de base, on la force à la remise de base');
remiseProduit = remiseMarcheDeBase;
}
}
// Fin du nouveau code du 21/09
// AJOUT DU 20/02/25 : on regarde si ce produit a un chk_prix_net et s'il est à 1 (marché hybride)
if (val['chk_prix_net']) {
console.log('on a un chk_prix_net : ' + val['chk_prix_net']);
if (val['chk_prix_net'] == '1') {
console.log('Le produit ' + val['code'] + ' est sur un marché hybride donc chk_prix_net=1');
readonlyRemiseProduit = 'readonly="readonly"';
}
}
celRemisePro.innerHTML =
'<div class="input-group"><input type="number" class="form-control numeric" id="inpRemise_' +
val['fk_produit'] +
'" name="inpRemise_' +
val['fk_produit'] +
'" data-rid="' +
val['fk_produit'] +
'" data-achat="' +
val['prix_achat_net'] +
'" data-vente="' +
val['prix_vente'] +
'" value="' +
remiseProduit +
'" min="0" max="100" step="1" style="width: 80px; text-align: right;" ' +
readonlyRemiseProduit +
'/><div class="input-group-addon">%</div></div>';
if (readonlyRemiseProduit == '') {
document.getElementById('inpRemise_' + val['fk_produit']).addEventListener('change', calculDevis);
}
// nouvelle colonne PU vente avec remise
let celPUVenteRemPro = newRowPro.insertCell(5);
celPUVenteRemPro.innerHTML =
'<div class="input-group"><input type="text" class="form-control numeric" id="inpPUVenteRem_' + val['fk_produit'] + '" name="inpPUVenteRem_' + val['fk_produit'] + '" value="' + formatAmount(val['totalht']) + '" style="width: 100px; text-align: right;" readonly tabindex="-1" /><div class="input-group-addon">&euro;</div></div>';
// Fin nouvelle colonne
let celHTPro = newRowPro.insertCell(6);
celHTPro.innerHTML = '<div class="input-group"><input type="text" class="form-control numeric" id="inpHT_' + val['fk_produit'] + '" name="inpHT_' + val['fk_produit'] + '" value="' + formatAmount(val['totalht']) + '" style="width: 100px; text-align: right;" readonly tabindex="-1" /><div class="input-group-addon">&euro;</div></div>';
let celVariante = newRowPro.insertCell(7);
celVariante.className = 'text-center';
celVariante.innerHTML = '<input type="checkbox" id="chkVariante_' + val['fk_produit'] + '" name="chkVariante_' + val['fk_produit'] + '" data-rid="' + val['fk_produit'] + '" data-achat="' + val['prix_achat_net'] + '" data-vente="' + val['prix_vente'] + '" value="' + val['chk_variante'] + '" ' + (val['chk_variante'] == 1 ? 'checked' : '') + ' />';
document.getElementById('chkVariante_' + val['fk_produit']).addEventListener('change', calculDevis);
let celMargePro = newRowPro.insertCell(8);
celMargePro.innerHTML = '<div class="input-group"><input type="text" class="form-control numeric" id="inpMG_' + val['fk_produit'] + '" name="inpMG_' + val['fk_produit'] + '" value="' + val['marge'] + '" style="width: 80px; text-align: right;" readonly tabindex="-1" /><div class="input-group-addon">%</div></div>';
// on calcule et enregistre le prix d'achat discount du produit si on a un prc_discount
let chkDiscount = false;
if (val['prc_discount_6'] > 0 && val['quantite_6'] > 0) {
// il y a un prc_discount sur ce produit
if (parseInt(val['qte']) >= parseInt(val['quantite_6'])) {
// on applique le prc_discount
const prixAchat = (val['prix_achat_net'] * 1 * (val['prc_discount_6'] * 1)) / 100;
// On met à jour le nouveau prix achat de ce produit dans le dataset achatdiscount de sa ligne
newRowPro.dataset.achatdiscount = prixAchat;
chkDiscount = true;
console.log('showDevisPro prc_discount_6 appliqué pour la ref ' + val['code'] + ' : ' + prixAchat);
}
}
if (val['prc_discount_5'] > 0 && val['quantite_5'] > 0 && !chkDiscount) {
// il y a un prc_discount sur ce produit
if (parseInt(val['qte']) >= parseInt(val['quantite_5'])) {
// on applique le prc_discount
const prixAchat = (val['prix_achat_net'] * 1 * (val['prc_discount_5'] * 1)) / 100;
// On met à jour le nouveau prix achat de ce produit dans le dataset achatdiscount de sa ligne
newRowPro.dataset.achatdiscount = prixAchat;
chkDiscount = true;
console.log('showDevisPro prc_discount_5 appliqué pour la ref ' + val['code'] + ' : ' + prixAchat);
}
}
if (val['prc_discount_4'] > 0 && val['quantite_4'] > 0 && !chkDiscount) {
// il y a un prc_discount sur ce produit
if (parseInt(val['qte']) >= parseInt(val['quantite_4'])) {
// on applique le prc_discount
const prixAchat = (val['prix_achat_net'] * 1 * (val['prc_discount_4'] * 1)) / 100;
// On met à jour le nouveau prix achat de ce produit dans le dataset achatdiscount de sa ligne
newRowPro.dataset.achatdiscount = prixAchat;
chkDiscount = true;
console.log('showDevisPro prc_discount_4 appliqué pour la ref ' + val['code'] + ' : ' + prixAchat);
}
}
if (val['prc_discount_3'] > 0 && val['quantite_3'] > 0 && !chkDiscount) {
// il y a un prc_discount sur ce produit
if (parseInt(val['qte']) >= parseInt(val['quantite_3'])) {
// on applique le prc_discount
const prixAchat = (val['prix_achat_net'] * 1 * (val['prc_discount_3'] * 1)) / 100;
// On met à jour le nouveau prix achat de ce produit dans le dataset achatdiscount de sa ligne
newRowPro.dataset.achatdiscount = prixAchat;
chkDiscount = true;
console.log('showDevisPro prc_discount_3 appliqué pour la ref ' + val['code'] + ' : ' + prixAchat);
}
}
if (val['prc_discount_2'] > 0 && val['quantite_2'] > 0 && !chkDiscount) {
if (parseInt(val['qte']) >= parseInt(val['quantite_2'])) {
// on applique le prc_discount
const prixAchat = (val['prix_achat_net'] * 1 * (val['prc_discount_2'] * 1)) / 100;
// On met à jour le nouveau prix achat de ce produit dans le dataset achatdiscount de sa ligne
newRowPro.dataset.achatdiscount = prixAchat;
chkDiscount = true;
console.log('showDevisPro prc_discount_2 appliqué pour la ref ' + val['code'] + ' : ' + prixAchat);
}
}
if (val['prc_discount_1'] > 0 && val['quantite_1'] > 0 && !chkDiscount) {
if (parseInt(val['qte']) >= parseInt(val['quantite_1'])) {
// on applique le prc_discount
const prixAchat = (val['prix_achat_net'] * 1 * (val['prc_discount_1'] * 1)) / 100;
// On met à jour le nouveau prix achat de ce produit dans le dataset achatdiscount de sa ligne
newRowPro.dataset.achatdiscount = prixAchat;
chkDiscount = true;
console.log('showDevisPro prc_discount_1 appliqué pour la ref ' + val['code'] + ' : ' + prixAchat);
}
}
if (chkRegleSeuilsMarge == 1) {
// Le marché demande la prise en compte des seuils de marge RR et DV paramétrés dans la table produits_familles
if (val['marge_rr'] > seuilMargeRR) seuilMargeRR = val['marge_rr'];
if (val['marge_dv'] > seuilMargeDV) seuilMargeDV = val['marge_dv'];
}
}
}
// On met à jour les seuils de marge RR et DV en fonction du marché et des produits
document.getElementById('inp_latitudeRR').value = seuilMargeRR;
document.getElementById('inp_latitudeDV').value = seuilMargeDV;
// On simule le changement de quantité sur la première ligne pour recalculer les totaux
const inpQte = document.getElementById('inpQte_' + fkProduit1);
const event = new Event('change');
inpQte.dispatchEvent(event);
chkChange = 0;
}
}
function showDevisMarcheInfos(ret) {
// On affiche les infos du marché
if (ret.length == 1) {
let line = ret[0];
chkPrixNets = line.chk_prix_nets == 1 ? true : false;
document.getElementById('inp_latitudeRR').value = seuilMargeRR;
document.getElementById('inp_latitudeDV').value = seuilMargeDV;
document.getElementById('titleMarche').innerHTML = '<bolder>Informations du marché ' + line.libelle + '</bolder>'; // le titre du panel des infos marché dans l'onglet 3. Devis
$('#tdTxRemiseTrim').text(line.taux_remise_trimestrielle + ' %');
$('#tdTxRemiseSeme').text(line.taux_remise_semestrielle + ' %');
$('#tdTxRemiseAnnu').text(line.taux_remise_annuelle + ' %');
document.getElementById('tdDebutFin').innerHTML = convertMySQLDateToFrenchDate(line.date_debut) + ' - ' + convertMySQLDateToFrenchDate(line.date_fin);
// vérifie la date de validité du prix du marché est inférieure à la date du jour
const today = new Date();
if (line.date_validite_prix < today) {
document.getElementById('tdDateValiditePrix').style.color = 'red';
} else {
// vérifie que cette date est encore valable dans 2 mois
const today2mois = new Date();
today2mois.setMonth(today2mois.getMonth() + 2);
if (line.date_validite_prix < today2mois) {
document.getElementById('tdDateValiditePrix').style.color = 'orange';
} else {
document.getElementById('tdDateValiditePrix').style.color = 'green';
}
}
document.getElementById('tdDateValiditePrix').innerHTML = convertMySQLDateToFrenchDate(line.date_validite_prix);
$('#tdGarantie').text(line.garantie);
$('#tdRemisesCo').text(line.remises_commerciales);
// On affiche les remises de marché
if (line.remise_palier_1 == 0 && line.remise_taux_1 == 0 && line.remise_palier_2 == 0 && line.remise_palier_3 == 0 && !chkPrixNets) {
document.getElementById('trRemisesMarche').style.display = 'none';
} else {
let remisesMarche = '';
if (line.remise_palier_1 > 0) {
remisesMarche += line.remise_taux_1 + '% à partir de ' + line.remise_palier_1 + 'k€';
} else {
remisesMarche += line.remise_taux_1 + '% de base';
remiseMarcheDeBase = line.remise_taux_1;
}
if (line.remise_palier_2 > 0) {
remisesMarche += ', ' + line.remise_taux_2 + '% à partir de ' + line.remise_palier_2 + 'k€';
}
if (line.remise_palier_3 > 0) {
remisesMarche += ', ' + line.remise_taux_3 + '% à partir de ' + line.remise_palier_3 + 'k€';
}
if (line.remise_palier_4 > 0) {
remisesMarche += ', ' + line.remise_taux_4 + '% à partir de ' + line.remise_palier_4 + 'k€';
}
if (chkPrixNets) {
remisesMarche += ' <textalert>(PRIX NETS)</textalert>';
}
document.getElementById('tdRemisesMarche').innerHTML = remisesMarche;
document.getElementById('trRemisesMarche').style.display = 'block';
// on ajoute le readonly à tous les champs de saisie des remises de marché si le marché est en prix nets et que c'est un RR
if (chkPrixNets && (fkRole == 3 || fkRole == 4 || fkRole > 5)) {
// on boucle sur tous les inputs inpRemise_*
console.log('Prix Nets et RR : on boucle sur tous les inputs inpRemise_* pour les mettre en readonly');
let inputs = document.getElementsByTagName('input');
for (let i = 0; i < inputs.length; i++) {
if (inputs[i].id.substr(0, 10) == 'inpRemise_') {
inputs[i].readOnly = true;
}
}
}
}
// on sauvegarde les remises de marché dans le array aRemisesMarches
aRemisesMarches = [line.remise_palier_1, line.remise_taux_1, line.remise_palier_2, line.remise_taux_2, line.remise_palier_3, line.remise_taux_3, line.remise_palier_4, line.remise_taux_4];
// on met à jour le bouton de sauvegarde du devis
updateBtnSaveDevisAndSend();
} else {
$('#tdTxRemiseTrim').text('-');
$('#tdTxRemiseSeme').text('-');
$('#tdTxRemiseAnnu').text('-');
$('#tdDebutFin').text('-');
$('#tdDateValiditePrix').text('-');
$('#tdGarantie').text('-');
$('#tdRemisesCo').text('-');
}
}
function showDevisMarcheProduits(dFamilles, dProduits) {
// on met à jour les onglets de familles de groupes de produits
showLoading();
let famillesChargees = [];
dFamilles.forEach(function (lineFamille) {
let libIdFamille = lineFamille.libelle.replace(/ /g, '_');
let idFamille = lineFamille.rowid;
let dataProduitsFamille = [];
let tblBodyProduits = document.getElementById('tblProduits_' + libIdFamille).getElementsByTagName('tbody')[0];
tblBodyProduits.innerHTML = '';
// on charge les produits de cette famille
dProduits.forEach(function (lineProduit) {
if (lineProduit.fk_famille == idFamille) {
// on renseigne cet idFamille dans le tableau des familles chargées si ce n'est pas déjà fait (pour éviter les doublons)
if (famillesChargees.indexOf(libIdFamille) == -1) {
famillesChargees.push(libIdFamille);
}
// on enregistre les données de ce produit dans le tableau dataProduitsFamille
dataProduitsFamille.push(lineProduit);
}
});
// Une fois que tous les produits de cette famille sont chargés, on les affiche
showProduitsFamille(dataProduitsFamille, libIdFamille);
// puis on affecte les données de ce tableau dataProduitsFamille à l'autocomplete de l'input de recherche de produits de cette famille
autocompleteProduitsFamille(document.getElementById('inpSearchProduct_' + libIdFamille), dataProduitsFamille, libIdFamille, idFamille);
});
// Enfin, on affiche que les onglets des familles de produits chargées
// 1. On trie les onglets par ordre croissant de leur id_ordre
let sortedOnglets = Array.from(elOngletsProduits).sort(function (a, b) {
const aIndex = parseInt(a.id.split('_')[1]);
const bIndex = parseInt(b.id.split('_')[1]);
if (aIndex < bIndex) {
return -1;
} else if (aIndex > bIndex) {
return 1;
} else {
return 0;
}
});
// 2. On boucle sur ces onglets triés en ordre décroissant et on les affiche dans le bon ordre
sortedOnglets.forEach(function (onglet) {
elListOngletsProduits.appendChild(onglet.parentNode);
onglet.classList.remove('hidden');
});
// 3. On cache maintenant les onglets des familles de produits non chargées, et on les pousse à la fin de la liste
let nbOnglets = elOngletsProduits.length;
Array.from(elOngletsProduits).forEach(function (onglet) {
const libIdFamille = onglet.getAttribute('data-famille');
if (famillesChargees.indexOf(libIdFamille) == -1) {
onglet.classList.add('hidden');
nbOnglets--;
// et on pousse cet onglet à la fin de la liste
elListOngletsProduits.appendChild(onglet.parentNode);
}
});
console.log('nbOnglets : ' + nbOnglets);
Array.from(elOngletsProduits).forEach(function (onglet) {
const libIdFamille = onglet.getAttribute('data-famille');
if (famillesChargees.indexOf(libIdFamille) > -1) {
// on récupère le titre de l'onglet, on calcule sa longueur,
let titreOnglet = onglet.innerText.trim();
const espace = titreOnglet.indexOf(' ');
if (espace > 0) {
titreOnglet = titreOnglet.substring(0, espace) + '<br/>' + titreOnglet.substring(espace + 1);
onglet.innerHTML = titreOnglet;
} else {
const longueur = titreOnglet.length;
if (longueur < 10) {
onglet.innerHTML = titreOnglet + '<br><br>';
}
}
}
});
// 4. Enfin, on force le nav-justified à se réorganiser
elListOngletsProduits.classList.remove('nav-justified');
elListOngletsProduits.classList.add('nav-justified');
hideLoading();
}
function showProduitsFamille(dProduits, libIdFamille) {
// Affiche tous les produits d'une famille dans le 2ème onglet Produits
// dProduits ne contient que les produits de cette famille
let tblBody = document.getElementById('tblProduits_' + libIdFamille).getElementsByTagName('tbody')[0];
tblBody.innerHTML = '';
// on charge les produits de cette famille
dProduits.forEach(function (lineProduit) {
// Insertion d'une nouvelle ligne et création de ses colonnes
showLineProduitFamille(tblBody, lineProduit, libIdFamille);
});
}
function showLineProduitFamille(tblBody, lineProduit, libIdFamille) {
let newRow = tblBody.insertRow(0);
newRow.className = 'ligProduit_' + libIdFamille;
newRow.id = 'ligProduit_' + libIdFamille + '_' + lineProduit.rowid;
newRow.setAttribute('data-rid', lineProduit.rowid);
let celChkBox = newRow.insertCell(0);
celChkBox.className = 'chkBox_' + libIdFamille + ' text-center';
celChkBox.setAttribute('data-rid', lineProduit.rowid);
celChkBox.innerHTML = '<input type="checkbox" class="chkBox" id="chkBoxProd_' + lineProduit.rowid + '" name="chkBoxProd_' + lineProduit.rowid + '" data-rid="' + lineProduit.rowid + '" data-code="' + lineProduit.code + '" data-libelle="' + lineProduit.libelle + '" data-famille="' + libIdFamille + '" />';
let celCode = newRow.insertCell(1);
celCode.innerHTML = lineProduit.code;
let celLibelle = newRow.insertCell(2);
celLibelle.innerHTML = lineProduit.libelle;
let celFamille = newRow.insertCell(3);
celFamille.innerHTML = lineProduit.lib_famille;
}
$('a[data-toggle="tab"]').on('show.bs.tab', function (e) {
if (idDevis == 0) {
if ($(this).attr('href') == '#tabproduits' || $(this).attr('href') == '#tabdevis') {
showNotification('Erreur', "Vous devez d'abord sélectionner un devis dans la liste de vos devis à gauche", 'warning');
return false;
}
} else {
if (chkChange == 1) {
//! il y a un changement en cours...
if ($(this).attr('href') == '#tabentete' || $(this).attr('href') == '#tabproduits' || $(this).attr('href') == '#tabdevis') {
if (confirm("Attention, vous avez fait des modifications non enregistrées sur cette page du devis. Vous allez perdre d'éventuelles modifications importantes. Voulez-vous continuer ?")) {
chkChange = 0;
} else {
return false;
}
}
}
}
});
function changeClientsSecteur() {
// en cas de changement de secteur, on recharge les clients du commercial sur ce secteur ou sur toute la France
chkClientsSecteur = document.getElementById('inp_chk_clients_secteur').checked ? 1 : 0;
console.log('changement de secteur clients : ' + chkClientsSecteur);
if (fkUser > 0) {
fetch('/jxdevis/load_clients_devis', {
method: 'POST',
body: JSON.stringify({
user: fkUser,
secteur: chkClientsSecteur.toString(),
}),
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
}).then((response) => {
if (!response.ok) {
showNotification('Erreur', "Le chargement des clients n'a pas abouti", 'error');
} else {
const retClients = response.json();
retClients.then(function (dataClients) {
clients = dataClients;
// on charge les clients du commercial sur son secteur ou sur toute la France dans l'autocomplete
autocompleteClient(document.getElementById('inp_lib_client'), dataClients);
});
}
});
}
}
function clickDupDevis() {
idDevis = this.getAttribute('data-rid');
if (confirm('Confirmez-vous la duplication de ce devis n° ' + idDevis + ' ?')) {
showLoading();
$.ajax({
url: '/jxdevis/duplic_devis',
type: 'POST',
async: false,
cache: false,
data: 'rid=' + idDevis,
success: function (data) {
showNotification('Duplication', 'Duplication du devis effectuée avec succès', 'success');
hideLoading();
setTimeout(function () {
location.reload();
}, 2000); // 2000 millisecondes = 2 secondes
return false;
},
error: function (jqXHR, textStatus, errorThrown) {
showNotification('Erreur', 'Erreur lors de la duplication de ce devis : ' + textStatus, 'error');
hideLoading();
return false;
},
});
}
}
function clickExpExcelDevis(e) {
const idDevis = this.dataset.rid;
if (confirm("Confirmez l'exportation de ce devis #" + idDevis + ' en Excel ?')) {
const url = '/expxls/export_sap_devis/' + idDevis;
window.open(url);
return false;
}
}
let clickSupprDevis = function (e) {
const idDevis = this.dataset.rid;
if (confirm('Confirmez la suppression définitive de ce devis #' + idDevis + ' ?')) {
showLoading();
fetch('/jxdevis/delete_devis', {
method: 'POST',
body: JSON.stringify({ cid: idDevis }),
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
}).then((response) => {
if (!response.ok) {
showNotification('Erreur', "La suppression du devis n'a pas abouti", 'error');
} else {
const retDevis = response.json();
retDevis.then(function (dataDevis) {
// On supprime la ligne du tableau qui contenait ce bouton
let trDevis = document.getElementById('tr_' + idDevis);
trDevis.parentNode.removeChild(trDevis);
showNotification('Suppression', 'Suppression du devis effectuée avec succès', 'success');
setTimeout(function () {
location.reload();
}, 2000); // 2000 millisecondes = 2 secondes
});
}
});
hideLoading();
}
return false;
};
let clickCreateClient = function () {
if (this.innerHTML == 'Créer un nouveau client') {
if (confirm('Voulez-vous créer un nouveau client pour ce devis ?')) {
document.getElementById('frmCreateClient').reset();
showModal(document.getElementById('modalCreateClient'));
document.getElementById('inp_create_libelle').focus();
}
} else {
document.getElementById('inp_create_libelle').value = document.getElementById('inp_lib_client').value;
document.getElementById('inp_create_type_client').value = document.getElementById('selTypeEtab').value;
document.getElementById('inp_create_adresse1').value = document.getElementById('inp_adresse1').value;
document.getElementById('inp_create_adresse2').value = document.getElementById('inp_adresse2').value;
document.getElementById('inp_create_adresse3').value = document.getElementById('inp_adresse3').value;
document.getElementById('inp_create_cp').value = document.getElementById('inp_cp').value;
document.getElementById('inp_create_ville').value = document.getElementById('inp_ville').value;
showModal(document.getElementById('modalCreateClient'));
document.getElementById('inp_create_libelle').focus();
}
};
let clickCancelCreateClient = function () {
hideModal(document.getElementById('modalCreateClient'));
};
let clickSaveCreateClient = function () {
// on regarde si c'est une création de devis ou une modification
// on enregistre le fait que ça soit un nouveau client
// on met à jour les champs du devis avec les infos du nouveau client
// et quand on enregistre le devis on enregistre le nouveau client dans le devis
document.getElementById('inp_fk_client').value = '0';
document.getElementById('inp_lib_client').value = document.getElementById('inp_create_libelle').value;
document.getElementById('selTypeEtab').value = document.getElementById('inp_create_type_client').value;
document.getElementById('inp_type_client').value = document.getElementById('inp_create_type_client').value;
document.getElementById('inp_adresse1').value = document.getElementById('inp_create_adresse1').value;
document.getElementById('inp_adresse2').value = document.getElementById('inp_create_adresse2').value;
document.getElementById('inp_adresse3').value = document.getElementById('inp_create_adresse3').value;
document.getElementById('inp_cp').value = document.getElementById('inp_create_cp').value;
document.getElementById('inp_ville').value = document.getElementById('inp_create_ville').value;
console.log('nouveau client créé : ' + document.getElementById('inp_create_type_client').value + ' -> ' + document.getElementById('selTypeEtab').value);
hideModal(document.getElementById('modalCreateClient'));
// on change le texte et la couleur du bouton de nouveau client
document.getElementById('btnCreateClient').innerHTML = 'Modifier le nouveau client';
document.getElementById('btnCreateClient').classList.remove('btn-primary');
document.getElementById('btnCreateClient').classList.add('btn-info');
chkCreateClient = true;
};
let clickSpeciaux = function () {
showLoading();
// On cherche dans la table devis_speciaux s'il y a une ligne pour ce devis
fetch('/jxdevis/load_devis_speciaux', {
method: 'POST',
body: JSON.stringify({ cid: idDevis }),
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
}).then((response) => {
if (!response.ok) {
showNotification('Erreur', "Le chargement des produits spéciaux n'a pas abouti", 'error');
} else {
const retSpeciaux = response.json();
retSpeciaux.then(function (dataSpeciaux) {
// on vide les 5 lignes de produits spéciaux pour éviter de reprendre des données d'un autre devis
for (i = 1; i <= 5; i++) {
document.getElementById('inp_specialFkProduit_' + i).value = '';
document.getElementById('inp_specialCode_' + i).value = '';
document.getElementById('inp_specialLibe_' + i).value = '';
document.getElementById('inp_specialQte_' + i).value = '';
document.getElementById('inp_specialCout_' + i).value = '';
document.getElementById('inp_chk_specialEchantillon_' + i).checked = false;
document.getElementById('inp_specialDate_' + i).value = '';
document.getElementById('inp_specialConcurrent_' + i).value = '';
document.getElementById('inp_specialDescription_' + i).value = '';
}
if (dataSpeciaux.length > 0) {
// on a trouvé une ligne dans la table devis_speciaux
const data = dataSpeciaux[0];
// on charge les données dans le formulaire
document.getElementById('inp_idDevis_speciaux').value = data.fk_devis;
if (data.chk_livr_multi == '1') {
document.getElementById('inp_chk_livr_multi').checked = true;
} else {
document.getElementById('inp_chk_livr_multi').checked = false;
}
document.getElementById('inp_nb_livr').value = data.nb_livr;
document.getElementById('inp_date_livr_1').value = data.date_livr_1;
for (i = 1; i <= 5; i++) {
document.getElementById('inp_specialFkProduit_' + i).value = data[`fk_produit_${i}`];
document.getElementById('inp_specialCode_' + i).value = data[`code_produit_${i}`];
document.getElementById('inp_specialLibe_' + i).value = data[`lib_produit_${i}`];
document.getElementById('inp_specialQte_' + i).value = data[`qte_${i}`];
document.getElementById('inp_specialCout_' + i).value = data[`surcout_${i}`];
if (data[`chk_echantillon_${i}`] == '1') {
document.getElementById('inp_chk_specialEchantillon_' + i).checked = true;
} else {
document.getElementById('inp_chk_specialEchantillon_' + i).checked = false;
}
if (data[`date_echantillon_${i}`] != '0000-00-00') {
document.getElementById('inp_specialDate_' + i).value = data[`date_echantillon_${i}`];
}
document.getElementById('inp_specialConcurrent_' + i).value = data[`lib_concurrent_${i}`];
document.getElementById('inp_specialDescription_' + i).value = data[`description_${i}`];
}
document.getElementById('inp_specialEmail').value = data.email;
if (data.chk_email == 1) {
document.getElementById('inp_specialEmail').style.backgroundColor = 'lightgreen';
} else {
document.getElementById('inp_specialEmail').style.backgroundColor = 'white';
}
} else {
// on n'a pas trouvé de ligne dans la table devis_speciaux
// on vide les champs du formulaire
document.getElementById('inp_idDevis_speciaux').value = idDevis;
document.getElementById('frmSpeciaux').reset();
}
autocompleteProdSpecial(document.getElementById('inp_specialCode_1'), '1', dataProduitsMarche);
autocompleteProdSpecial(document.getElementById('inp_specialCode_2'), '2', dataProduitsMarche);
autocompleteProdSpecial(document.getElementById('inp_specialCode_3'), '3', dataProduitsMarche);
autocompleteProdSpecial(document.getElementById('inp_specialCode_4'), '4', dataProduitsMarche);
autocompleteProdSpecial(document.getElementById('inp_specialCode_5'), '5', dataProduitsMarche);
});
}
});
hideLoading();
showModal(document.getElementById('modalSpeciaux'));
};
let clickCancelSpeciaux = function () {
hideModal(document.getElementById('modalSpeciaux'));
};
let clickSaveSpeciaux = function () {
let frmData = new FormData(document.getElementById('frmSpeciaux'));
let objData = {};
frmData.forEach(function (value, key) {
objData[key] = value;
});
console.log(objData);
showLoading();
fetch('/jxdevis/save_devis_speciaux', {
method: 'POST',
body: JSON.stringify(objData),
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
}).then((response) => {
if (!response.ok) {
hideLoading();
showNotification('Erreur', "L'enregistrement des produits spéciaux de ce devis n'a pas abouti", 'error');
} else {
const ret = response.json();
ret.then(function (data) {
hideLoading();
showNotification('Succès', 'Enregistrement des produits spéciaux de ce devis effectué', 'success');
});
}
});
hideLoading();
hideModal(document.getElementById('modalSpeciaux'));
chkChange = 0;
return false;
};
let clickSaveEnTete = function () {
if (document.getElementById('inp_lib_client').value == '') {
showNotification('Erreur', 'Enregistrement impossible : vous devez sélectionner ou créer un client', 'error');
document.getElementById('inp_lib_client').focus();
return false;
}
if (document.getElementById('inp_num_opportunite').value == '') {
showNotification('Erreur', "Enregistrement impossible : vous devez saisir un N° d'opportunité", 'error');
document.getElementById('inp_num_opportunite').focus();
return false;
}
if (document.getElementById('inp_contact_nom').value == '') {
showNotification('Erreur', 'Enregistrement impossible : vous devez renseigner le nom et prénom du contact', 'error');
document.getElementById('inp_contact_nom').focus();
return false;
}
if (document.getElementById('inp_contact_prenom').value == '') {
showNotification('Erreur', 'Enregistrement impossible : vous devez renseigner le nom et prénom du contact', 'error');
document.getElementById('inp_contact_prenom').focus();
return false;
}
if (document.getElementById('inp_contact_fonction').value == '') {
showNotification('Erreur', 'Enregistrement impossible : vous devez renseigner la fonction du contact', 'error');
document.getElementById('inp_contact_fonction').focus();
return false;
}
if (document.getElementById('inp_email').value == '') {
showNotification('Erreur', "Enregistrement impossible : vous devez renseigner l'email du contact", 'error');
document.getElementById('inp_email').focus();
return false;
}
if (document.getElementById('inp_telephone').value == '' && document.getElementById('inp_mobile').value == '') {
showNotification('Erreur', 'Enregistrement impossible : vous devez renseigner au moins un numéro de téléphone du contact (fixe ou mobile)', 'error');
document.getElementById('inp_telephone').focus();
return false;
}
if (document.getElementById('inp_fk_marche').value == '0') {
showNotification('Erreur', 'Enregistrement impossible : vous devez sélectionner un marché', 'error');
document.getElementById('inp_fk_marche').focus();
return false;
}
const dateDemande = document.getElementById('inp_date_demande').value;
const dateRemise = document.getElementById('inp_date_remise').value;
// Vérification de la validité des dates saisies
if (isNaN(Date.parse(dateDemande))) {
showNotification('Erreur', "Enregistrement impossible : la date de la demande n'est pas saisie ou est incorrecte", 'error');
dateDemande.focus();
return false;
}
if (isNaN(Date.parse(dateRemise))) {
showNotification('Erreur', "Enregistrement impossible : la date de la remise n'est pas saisie ou est incorrecte", 'error');
dateRemise.focus();
return false;
}
if (dateDemande !== '' && dateRemise !== '') {
const dateDemandeObj = new Date(Date.parse(dateDemande));
const dateRemiseObj = new Date(Date.parse(dateRemise));
if (dateRemiseObj <= dateDemandeObj) {
showNotification('Erreur', 'Enregistrement impossible : la date de remise au client doit être supérieure à la date de la demande', 'error');
dateRemise.focus();
return false; // empêcher l'enregistrement du formulaire
}
}
// Vérification du non changement du marché
if (idDevis > 0) {
if (idMarche != document.getElementById('inp_fk_marche').value) {
if (!confirm('Vous avez changé le marché de ce devis. Confirmez-vous ce changement ? Cela va supprimer tous les produits enregistrés de ce devis.')) {
return false;
}
}
}
showLoading();
//! on récupère tous les input de la form dans dataform
let dataform = $('#frmDevisEntete').serialize();
//! on supprime les %2F des dates au lieu des /
dataform = decodeURIComponent(dataform.replace(/%2F/g, ' '));
$.ajax({
url: '/jxdevis/save_devis_entete',
type: 'POST',
dataType: 'json',
async: false,
cache: false,
data: dataform,
success: function (data) {
hideLoading();
showNotification('Enregistrement', 'Enregistrement effectué avec succès', 'success');
// On recharge la page en cours
setTimeout(function () {
location.reload();
}, 2000); // 2000 millisecondes = 2 secondes
},
error: function (jqXHR, textStatus, errorThrown) {
showNotification('Erreur', 'Erreur lors de la sauvegarde du devis : ' + textStatus, 'error');
},
});
chkChange = 0;
return false;
};
let clickCreateDevis = function () {
if (confirm('Voulez-vous créer un nouveau devis ?')) {
//! On vide tous les champs de la form frmDevisEntete
document.getElementById('frmDevisEntete').reset();
document.getElementById('inp_rowid').value = 0;
document.getElementById('inp_fk_user').value = fkUser;
document.getElementById('inp_fk_marche').value = 0;
document.getElementById('inp_fk_client').value = '';
document.getElementById('inp_chk_devis_photos').checked = false;
document.getElementById('inp_num_opportunite').focus();
//! On vérifie le bon chargement des clients
if (oldChkClientsSecteur == 2) {
// les clients n'ont pas encore été chargés
document.getElementById('inp_chk_clients_secteur').checked = true;
oldChkClientsSecteur = 1;
changeClientsSecteur();
}
chkChange = 1;
elDivDevis.style.display = 'block';
chkPageLoad = false;
idDevis = 0;
}
};
$('#inpSearchProduit').keypress(function (e) {
if (e.which == 13) {
if ($('#inpSearchProduit').val().length > 2) {
$.ajax({
url: '/jxdevis/load_devis_produits_search',
type: 'POST',
dataType: 'json',
async: false,
cache: false,
data: 'term=' + $('#inpSearchProduit').val(),
success: function (data) {
// on importe les produits sélectionnés dans le tableau tblProduits
let rowCount = $('#tblProduits tr').length;
// Pour ne garder que la 1ère ligne d'entête de la table
if (rowCount > 1) {
for (i = rowCount; i > 1; i--) {
$('#tblProduits tr:last').remove();
}
}
let nbProduits = data.length;
$.each(data, function (idx, line) {
$('#tblProduits').append('<tr><td class="text-center"><input type="checkbox" name="chkProdCat" data-rid="' + line.rowid + '" data-code="' + line.code + '" data-libelle="' + line.libelle + '" value="' + line.rowid + '"></td><td>' + line.code + '</td><td>' + line.libelle + '</td></tr>');
});
},
error: function (jqXHR, textStatus, errorThrown) {
showNotification('Erreur', 'Erreur lors de la recherche de produits : ' + textStatus, 'error');
},
});
return false;
} else {
showNotification('Saisie', 'Vous devez saisir au moins 3 caractères', 'warning');
return false;
}
}
});
$(document).on('change', 'input[name^="chkBoxProd_"]', function () {
// on ajoute un produit disponible dans la liste des produits sélectionnés
const rid = this.dataset.rid;
const code = this.dataset.code;
const libelle = this.dataset.libelle;
//! 1. on ajoute ce produit dans la liste des produits sélectionnés
$('#tblProduitsSelect').append('<tr><td class="text-center"><input type="checkbox" class="chkBox" name="chkProdSelect_' + rid + '" data-rid="' + rid + '" data-code="' + code + '" data-libelle="' + libelle + '" value="' + rid + '"></td><td>' + code + '</td><td>' + libelle + '</td></tr>');
//! 2. on cache la ligne de ce produit de la liste des produits disponibles à gauche
const trProd = this.parentNode.parentNode;
trProd.style.display = 'none';
chkChange = 1;
return false;
});
$(document).on('change', 'input[name^="chkProdSelect_"]', function () {
// on supprime un produit sélectionné pour le remettre dans la liste des produits disponibles
let rid = this.dataset.rid;
let code = this.dataset.code;
let libelle = this.dataset.libelle;
//! 1. on remet ce produit dans la liste des produits du catalogue
const chkBoxProd = document.querySelector('#chkBoxProd_' + rid);
if (chkBoxProd) {
// Si l'élément existe, on peut procéder
chkBoxProd.checked = false;
const trProd = chkBoxProd.parentNode.parentNode;
trProd.style.display = 'table-row';
} else {
console.warn(`L'élément #chkBoxProd_${rid} n'existe pas dans le DOM`);
}
//! 2. on supprime ce produit de la liste des produits sélectionnés
const trProdSelect = this.parentNode.parentNode;
trProdSelect.parentNode.removeChild(trProdSelect);
chkChange = 1;
return false;
});
let clickSaveSelProduits = function () {
//! Sauve la liste des produits sélectionnés d'un devis
const tblBodySelect = document.getElementById('tblProduitsSelect').getElementsByTagName('tbody')[0];
const nbProduits = tblBodySelect.rows.length;
showLoading();
let aProduits = new Array();
let lstProduits = '';
// if (nbProduits > 0) {
//! On parcourt la liste des produits sélectionnés
for (let i = 0, row; (row = tblBodySelect.rows[i]); i++) {
const rid = row.cells[0].firstElementChild.dataset.rid;
aProduits.push(rid);
//! On crée une chaîne liste des produits sélectionnés avec le "s" comme séparateur
lstProduits += ';' + rid;
}
let dataProduits = {};
dataProduits['cid'] = idDevis;
dataProduits['produits'] = lstProduits;
fetch('/jxdevis/save_devis_produits', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(dataProduits),
}).then((response) => {
if (!response.ok) {
hideLoading();
showNotification('Erreur', "L'enregistrement des produits de ce devis n'a pas abouti", 'error');
} else {
const ret = response.json();
ret.then(function (data) {
showDevisPro(data);
hideLoading();
showNotification('Succès', 'Enregistrement des ' + nbProduits + ' produits de ce devis effectué', 'success');
});
}
});
chkChange = 0;
return false;
};
function showDevisPro(data) {
//! Rafraîchit la liste des produits d'un devis dans le 3ème onglet Devis
const tblBodyPro = document.getElementById('tblDevisPro').getElementsByTagName('tbody')[0];
tblBodyPro.innerHTML = '';
if (data.length > 0) {
// au moins un produit trouvé pour ce devis
const nbProduits = data.length;
// Si le marché est un marché de prix nets, on met les champs de saisie des remises en readonly
let readonlyRemise = chkPrixNets ? 'readonly="readonly"' : '';
// Ajout du 20/02/2025 : si marché hybride, les produits de ce marché sont en prix nets
let readonlyRemiseProduit = '';
// Ajout du 26 juin 2024 : si l'utilisateur est le DC ou DV ou DGC, ils peuvent modifier les remises dans tous les cas
if (fkRole < 3 || fkRole == 5) {
readonlyRemise = '';
}
// Fin de l'ajout du 26 juin 2024
// on récupère le premier fk_produit, pour simuler un changement sur ce produit pour recalculer les totaux en fin de boucle
const fkProduit1 = data[0]['fk_produit'];
for (let key in data) {
if (data.hasOwnProperty(key)) {
// Récupération des valeurs de la ligne
let val = data[key];
// 20/02/2025 : On initialise le readonlyremise par produit pour gérer les cas de marché hybride où leurs produits sont en Prix Nets
readonlyRemiseProduit = readonlyRemise;
// on insère la ligne pour la saisie du commentaire au-dessus de la ligne du produit
let newRowCom = tblBodyPro.insertRow(-1);
newRowCom.className = 'hidden';
newRowCom.id = 'trCom_' + val['fk_produit'];
newRowCom.dataset.rid = val['fk_produit'];
let celCom = newRowCom.insertCell(0);
celCom.colSpan = 8;
celCom.innerHTML = '<div class="col-md-2"><label for="inpCom_' + val['fk_produit'] + '">Commentaire ' + val['code'] + ': </label></div><div class="col-md-9"><input type="text" class="form-control w-75" id="inpCom_' + val['fk_produit'] + '" name="inpCom_' + val['fk_produit'] + '" value="' + val['commentaire'] + '" /></div>';
// Insertion d'une nouvelle ligne et création de ses colonnes
let newRowPro = tblBodyPro.insertRow(-1);
newRowPro.id = 'trPro_' + val['fk_produit'];
newRowPro.dataset.rid = val['fk_produit'];
newRowPro.dataset.ordre = val['ordre'];
newRowPro.dataset.code = val['code'];
newRowPro.dataset.achat = val['prix_achat_net'];
newRowPro.dataset.achatdiscount = val['prix_achat_net'];
newRowPro.dataset.vente = val['prix_vente'];
newRowPro.dataset.discount1 = val['prc_discount_1'];
newRowPro.dataset.quantite1 = val['quantite_1'];
newRowPro.dataset.discount2 = val['prc_discount_2'];
newRowPro.dataset.quantite2 = val['quantite_2'];
newRowPro.dataset.discount3 = val['prc_discount_3'];
newRowPro.dataset.quantite3 = val['quantite_3'];
newRowPro.dataset.discount4 = val['prc_discount_4'];
newRowPro.dataset.quantite4 = val['quantite_4'];
newRowPro.dataset.discount5 = val['prc_discount_5'];
newRowPro.dataset.quantite5 = val['quantite_5'];
newRowPro.dataset.discount6 = val['prc_discount_6'];
newRowPro.dataset.quantite6 = val['quantite_6'];
let celCodePro = newRowPro.insertCell(0);
const svgColor = val['commentaire'] == '' ? 'lightgray' : 'red';
const svgComment = '<svg id="commentProd_' + val['fk_produit'] + '" class="clickable" data-rid="' + val['fk_produit'] + '" data-code="' + val['code'] + '"><use xlink:href="pub/res/svg/icons.svg#message" style="fill: ' + svgColor + ';"/></svg>';
let inputOrdreHidden = '<input type="hidden" id="inpOrdre_' + val['fk_produit'] + '" name="inpOrdre_' + val['fk_produit'] + '" value="' + val['ordre'] + '" />';
celCodePro.innerHTML = val['code'] + ' ' + svgComment + inputOrdreHidden;
document.getElementById('commentProd_' + val['fk_produit']).addEventListener('click', showCommentProd);
let celLibellePro = newRowPro.insertCell(1);
celLibellePro.innerHTML = val['libelle'];
let celPrixVentePro = newRowPro.insertCell(2);
celPrixVentePro.className = 'text-right';
celPrixVentePro.innerHTML = formatAmount(val['prix_vente']) + ' €';
let celQtePro = newRowPro.insertCell(3);
celQtePro.innerHTML =
'<input type="number" class="form-control numeric" id="inpQte_' +
val['fk_produit'] +
'" name="inpQte_' +
val['fk_produit'] +
'" data-rid="' +
val['fk_produit'] +
'" data-achat="' +
val['prix_achat_net'] +
'" data-vente="' +
val['prix_vente'] +
'" value="' +
val['qte'] +
'" min="0" max="1000" step="1" style="width: 100px; text-align: right;"/><input type="hidden" name="achat_' +
val['fk_produit'] +
'" value="' +
val['prix_achat_net'] +
'"/><input type="hidden" name="vente_' +
val['fk_produit'] +
'" value="' +
val['prix_vente'] +
'"/>';
document.getElementById('inpQte_' + val['fk_produit']).addEventListener('change', calculDevis);
let celRemisePro = newRowPro.insertCell(4);
// Nouveau code 21/09
// S'il y a une remise de base sur le marché, on vérifie chaque remise produit pour l'aligner à cette remise de base si elle est supérieure
let remiseProduit = val['remise'];
console.log('Remise de base : ' + remiseMarcheDeBase + ' vs remise sur le produit : ' + val['remise']);
if (remiseMarcheDeBase > 0) {
if (val['remise'] < remiseMarcheDeBase) {
console.log('La remise du produit est inférieure à la remise de base, on la force à la remise de base');
remiseProduit = remiseMarcheDeBase;
}
}
// Fin du nouveau code du 21/09
// 20/02/2025
if (val['chk_prix_net']) {
if (val['chk_prix_net'] == 1) {
console.log('Le produit ' + val['code'] + ' est sur un marché hybride donc chk_prix_net=1');
readonlyRemiseProduit = 'readonly=readonly';
}
}
celRemisePro.innerHTML =
'<div class="input-group"><input type="number" class="form-control numeric" id="inpRemise_' +
val['fk_produit'] +
'" name="inpRemise_' +
val['fk_produit'] +
'" data-rid="' +
val['fk_produit'] +
'" data-achat="' +
val['prix_achat_net'] +
'" data-vente="' +
val['prix_vente'] +
'" value="' +
remiseProduit +
'" min="0" max="100" step="1" style="width: 80px; text-align: right;" ' +
readonlyRemiseProduit +
' /><div class="input-group-addon">%</div></div>';
if (readonlyRemiseProduit == '') {
document.getElementById('inpRemise_' + val['fk_produit']).addEventListener('change', calculDevis);
}
// nouvelle colonne PU vente avec remise
let celPUVenteRemPro = newRowPro.insertCell(5);
celPUVenteRemPro.innerHTML =
'<div class="input-group"><input type="text" class="form-control numeric" id="inpPUVenteRem_' + val['fk_produit'] + '" name="inpPUVenteRem_' + val['fk_produit'] + '" value="' + formatAmount(val['totalht']) + '" style="width: 100px; text-align: right;" readonly tabindex="-1" /><div class="input-group-addon">&euro;</div></div>';
// Fin nouvelle colonne
let celHTPro = newRowPro.insertCell(6);
celHTPro.innerHTML = '<div class="input-group"><input type="text" class="form-control numeric" id="inpHT_' + val['fk_produit'] + '" name="inpHT_' + val['fk_produit'] + '" value="' + formatAmount(val['totalht']) + '" style="width: 100px; text-align: right;" readonly tabindex="-1" /><div class="input-group-addon">&euro;</div></div>';
let celVariante = newRowPro.insertCell(7);
celVariante.className = 'text-center';
celVariante.innerHTML = '<input type="checkbox" id="chkVariante_' + val['fk_produit'] + '" name="chkVariante_' + val['fk_produit'] + '" data-rid="' + val['fk_produit'] + '" data-achat="' + val['prix_achat_net'] + '" data-vente="' + val['prix_vente'] + '" value="' + val['chk_variante'] + '" ' + (val['chk_variante'] == 1 ? 'checked' : '') + ' />';
document.getElementById('chkVariante_' + val['fk_produit']).addEventListener('change', calculDevis);
let celMargePro = newRowPro.insertCell(8);
celMargePro.innerHTML = '<div class="input-group"><input type="text" class="form-control numeric" id="inpMG_' + val['fk_produit'] + '" name="inpMG_' + val['fk_produit'] + '" value="' + val['marge'] + '" style="width: 80px; text-align: right;" readonly tabindex="-1" /><div class="input-group-addon">%</div></div>';
// on calcule et enregistre le prix d'achat discount du produit si on a un prc_discount
let chkDiscount = false;
if (val['prc_discount_6'] > 0 && val['quantite_6'] > 0) {
// il y a un prc_discount sur ce produit
if (parseInt(val['qte']) >= parseInt(val['quantite_6'])) {
// on applique le prc_discount
const prixAchat = (val['prix_achat_net'] * 1 * (val['prc_discount_6'] * 1)) / 100;
// On met à jour le nouveau prix achat de ce produit dans le dataset achatdiscount de sa ligne
newRowPro.dataset.achatdiscount = prixAchat;
chkDiscount = true;
console.log('showDevisPro prc_discount_6 appliqué pour la ref ' + val['code'] + ' : ' + prixAchat);
}
}
if (val['prc_discount_5'] > 0 && val['quantite_5'] > 0 && !chkDiscount) {
// il y a un prc_discount sur ce produit
if (parseInt(val['qte']) >= parseInt(val['quantite_5'])) {
// on applique le prc_discount
const prixAchat = (val['prix_achat_net'] * 1 * (val['prc_discount_5'] * 1)) / 100;
// On met à jour le nouveau prix achat de ce produit dans le dataset achatdiscount de sa ligne
newRowPro.dataset.achatdiscount = prixAchat;
chkDiscount = true;
console.log('showDevisPro prc_discount_5 appliqué pour la ref ' + val['code'] + ' : ' + prixAchat);
}
}
if (val['prc_discount_4'] > 0 && val['quantite_4'] > 0 && !chkDiscount) {
// il y a un prc_discount sur ce produit
if (parseInt(val['qte']) >= parseInt(val['quantite_4'])) {
// on applique le prc_discount
const prixAchat = (val['prix_achat_net'] * 1 * (val['prc_discount_4'] * 1)) / 100;
// On met à jour le nouveau prix achat de ce produit dans le dataset achatdiscount de sa ligne
newRowPro.dataset.achatdiscount = prixAchat;
chkDiscount = true;
console.log('showDevisPro prc_discount_4 appliqué pour la ref ' + val['code'] + ' : ' + prixAchat);
}
}
if (val['prc_discount_3'] > 0 && val['quantite_3'] > 0 && !chkDiscount) {
// il y a un prc_discount sur ce produit
if (parseInt(val['qte']) >= parseInt(val['quantite_3'])) {
// on applique le prc_discount
const prixAchat = (val['prix_achat_net'] * 1 * (val['prc_discount_3'] * 1)) / 100;
// On met à jour le nouveau prix achat de ce produit dans le dataset achatdiscount de sa ligne
newRowPro.dataset.achatdiscount = prixAchat;
chkDiscount = true;
console.log('showDevisPro prc_discount_3 appliqué pour la ref ' + val['code'] + ' : ' + prixAchat);
}
}
if (val['prc_discount_2'] > 0 && val['quantite_2'] > 0 && !chkDiscount) {
if (parseInt(val['qte']) >= parseInt(val['quantite_2'])) {
// on applique le prc_discount
const prixAchat = (val['prix_achat_net'] * 1 * (val['prc_discount_2'] * 1)) / 100;
// On met à jour le nouveau prix achat de ce produit dans le dataset achatdiscount de sa ligne
newRowPro.dataset.achatdiscount = prixAchat;
chkDiscount = true;
console.log('showDevisPro prc_discount_2 appliqué pour la ref ' + val['code'] + ' : ' + prixAchat);
}
}
if (val['prc_discount_1'] > 0 && val['quantite_1'] > 0 && !chkDiscount) {
if (parseInt(val['qte']) >= parseInt(val['quantite_1'])) {
// on applique le prc_discount
const prixAchat = (val['prix_achat_net'] * 1 * (val['prc_discount_1'] * 1)) / 100;
// On met à jour le nouveau prix achat de ce produit dans le dataset achatdiscount de sa ligne
newRowPro.dataset.achatdiscount = prixAchat;
chkDiscount = true;
console.log('showDevisPro prc_discount_1 appliqué pour la ref ' + val['code'] + ' : ' + prixAchat);
}
}
if (chkRegleSeuilsMarge == 1) {
// Le marché demande la prise en compte des seuils de marge RR et DV paramétrés dans la table produits_familles
if (val['marge_rr'] != seuilMargeRR) seuilMargeRR = val['marge_rr'];
if (val['marge_dv'] != seuilMargeDV) seuilMargeDV = val['marge_dv'];
console.log("C'est un marché qui prend en compte les seuils de marge RR et DV : " + seuilMargeRR + ' / ' + seuilMargeDV);
}
}
}
// On met à jour les seuils de marge RR et DV en fonction du marché et des produits
document.getElementById('inp_latitudeRR').value = seuilMargeRR;
document.getElementById('inp_latitudeDV').value = seuilMargeDV;
// On simule le changement de quantité sur la première ligne pour recalculer les totaux
const inpQte = document.getElementById('inpQte_' + fkProduit1);
const event = new Event('change');
inpQte.dispatchEvent(event);
}
}
let showCommentProd = function () {
console.log('click sur le SVG commentProd de la ligne ' + this.dataset.rid);
document.getElementById('inp_commentProdId').value = this.dataset.rid;
document.getElementById('modCommentProdTitre').innerHTML = 'Commentaire sur le produit ' + this.dataset.code;
const inpComment = document.getElementById('inp_commentProd');
inpComment.value = document.getElementById('inpCom_' + this.dataset.rid).value;
showModal(document.getElementById('modalCommentProd'));
inpComment.focus();
return false;
};
function controlRemisesProduits(totalHT) {
// Contrôle des remises du marché en fonction du total HT du devis
// on arrondit le total HT à l'entier supérieur
const totHT = Math.ceil(totalHT);
// la remise calculée sur ce devis et à appliquer
let txRemiseAppliquee = 0.0;
// aRemisesMarches = [line.remise_palier_1, line.remise_taux_1, line.remise_palier_2, line.remise_taux_2, line.remise_palier_3, line.remise_taux_3, line.remise_palier_4, line.remise_taux_4];
const totPalier1 = aRemisesMarches[0] * 1000;
const txPalier1 = aRemisesMarches[1];
const totPalier2 = aRemisesMarches[2] * 1000;
const txPalier2 = aRemisesMarches[3];
const totPalier3 = aRemisesMarches[4] * 1000;
const txPalier3 = aRemisesMarches[5];
const totPalier4 = aRemisesMarches[6] * 1000;
const txPalier4 = aRemisesMarches[7];
console.log('controlRemisesProduits totHT : ' + totHT + ' totPalier1 : ' + totPalier1 + ' txPalier1 : ' + txPalier1);
if (txPalier1 == 0 && txPalier2 == 0 && txPalier3 == 0 && txPalier4 == 0) {
// pas de remise sur ce marché
return 0;
} else {
if (totPalier1 >= 0 && txPalier1 > 0) {
// il y a une remise sur ce marché à partir de ce total HT, si ce total est atteint on applique la remise
console.log('controlRemisesProduits totHT : ' + totHT + ' totPalier1 : ' + totPalier1 + ' txPalier1 : ' + txPalier1);
if (totHT >= totPalier1) txRemiseAppliquee = txPalier1;
}
if (totPalier2 > 0 && txPalier2 > 0) {
// il y a une remise sur ce marché à partir de ce total HT, si ce total est atteint on applique la remise
if (totHT >= totPalier2) txRemiseAppliquee = txPalier2;
}
if (totPalier3 > 0 && txPalier3 > 0) {
// il y a une remise sur ce marché à partir de ce total HT, si ce total est atteint on applique la remise
if (totHT >= totPalier3) txRemiseAppliquee = txPalier3;
}
if (totPalier4 > 0 && txPalier4 > 0) {
// il y a une remise sur ce marché à partir de ce total HT, si ce total est atteint on applique la remise
if (totHT >= totPalier4) txRemiseAppliquee = txPalier4;
}
console.log('controlRemisesProduits totalHT : ' + totHT + ' txRemiseAppliquee : ' + txRemiseAppliquee);
return txRemiseAppliquee;
}
}
function getPrixAchatAvecDiscount(idProduit, qte) {
let trPro = document.getElementById('trPro_' + idProduit);
const prixAchat = parseFloat(trPro.dataset.achat);
const qtt = parseInt(qte, 10);
// console.log("==== Début de getPrixAchatAvecDiscount pour la ref " + trPro.dataset.code + " : prix achat " + prixAchat + " et qte " + qtt);
let prixAchatDiscount = prixAchat;
let discount = [];
discount[1] = Array(parseFloat(trPro.dataset.discount1), parseInt(trPro.dataset.quantite1, 10));
discount[2] = Array(parseFloat(trPro.dataset.discount2), parseInt(trPro.dataset.quantite2, 10));
discount[3] = Array(parseFloat(trPro.dataset.discount3), parseInt(trPro.dataset.quantite3, 10));
discount[4] = Array(parseFloat(trPro.dataset.discount4), parseInt(trPro.dataset.quantite4, 10));
discount[5] = Array(parseFloat(trPro.dataset.discount5), parseInt(trPro.dataset.quantite5, 10));
discount[6] = Array(parseFloat(trPro.dataset.discount6), parseInt(trPro.dataset.quantite6, 10));
for (let inc = 6; inc > 0; inc--) {
const dscnt = discount[inc][0];
const qntt = discount[inc][1];
if (dscnt > 0 && qntt > 0) {
if (qtt >= qntt) {
// on applique le prc_discount
prixAchatDiscount = prixAchat - (prixAchat * dscnt) / 100;
console.log('=== idProduit : ' + idProduit + ' (prc_discount_' + inc + ' applique de ' + dscnt + '%, qte : ' + qtt + ' pour une quantité mini de ' + qntt + ') = ' + prixAchatDiscount);
break;
}
}
}
// On met à jour le nouveau prix achat de ce produit dans le dataset achatdiscount de sa ligne
trPro.dataset.achatdiscount = prixAchatDiscount.toFixed(2);
// console.log("==== Fin de getPrixAchatAvecDiscount pour la ref " + trPro.dataset.code);
return prixAchatDiscount;
}
let calculDevis = function () {
console.log('calculDevis...');
const idProduit = this.dataset.rid;
// On récupère toutes les infos de ce produit au niveau de sa ligne trPro_XX stockées en dataset
let trPro = document.getElementById('trPro_' + idProduit);
const code = trPro.dataset.code;
let prixAchat = trPro.dataset.achat;
const prixVente = trPro.dataset.vente;
// console.log("idProduit: " + idProduit + ", code : " + code + ", prixAchat: " + prixAchat + ", prixVente: " + prixVente);
let qte = 0;
let remise = 0;
let variante = 0;
let typeInput = ''; // qte, remise, variante
if (this.name.indexOf('inpQte') > -1) {
// c'est la quantité qui a changé
qte = this.value;
remise = document.getElementById('inpRemise_' + idProduit).value;
variante = document.getElementById('chkVariante_' + idProduit).checked;
typeInput = 'qte';
} else if (this.name.indexOf('inpRemise') > -1) {
// c'est la remise qui a changé
qte = document.getElementById('inpQte_' + idProduit).value;
remise = this.value;
variante = document.getElementById('chkVariante_' + idProduit).checked;
typeInput = 'remise';
chkSaisieRemise = true;
} else if (this.name.indexOf('chkVariante') > -1) {
// c'est la variante qui a changé
qte = document.getElementById('inpQte_' + idProduit).value;
remise = document.getElementById('inpRemise_' + idProduit).value;
variante = this.checked;
typeInput = 'variante';
}
let totalHt = 0;
let totalDevisHt = 0;
let totalDevisHtRemise = 0;
let txMarge = 0;
let coutTotalAchat = 0;
let margeTotale = 0;
// on calcule le total HT de cette ligne
let remiseProduit = 0;
if (variante) {
remiseProduit = 0;
totalHt = 0;
} else {
remiseProduit = (remise * 1 * (prixVente * 1)) / 100;
totalHt = (prixVente * 1 - remiseProduit * 1) * (qte * 1);
}
let inpHT = document.getElementById('inpHT_' + idProduit);
inpHT.value = parseFloat(totalHt).toFixed(2);
// Modif du 25/04 : on calcule la marge même si c'est une variante / option
//if (variante) {
txMarge = 0;
//} else {
if (prixAchat !== '0.00' && prixVente !== '0.00' && qte > 0) {
let prixVenteApresRemise = prixVente;
if (remise > 0) {
prixVenteApresRemise = prixVente - (prixVente * 1 * (remise * 1)) / 100;
}
console.log('Marge sur code : ' + code + ' - prixAchat : ' + prixAchat + ' - prixVente : ' + prixVente + ' - prixVenteApresRemise : ' + prixVenteApresRemise);
txMarge = ((prixVenteApresRemise * 1 - prixAchat * 1) / prixVenteApresRemise) * 100;
} else {
txMarge = 0;
console.log('ERREUR idProduit : ' + idProduit + ', code : ' + code + ' - prixAchat : ' + prixAchat + ' - prixVente : ' + prixVente);
if (qte > 0) showNotification('Info', "Le prix d'achat et/ou le prix de vente n'est pas renseigné pour ce produit, la marge ne peut pas être calculée.", 'info');
}
//}
let inpMG = document.getElementById('inpMG_' + idProduit);
inpMG.value = parseFloat(txMarge).toFixed(2);
console.log('Boucle 1 : calcul Total HT sans remise');
//! on boucle sur tous les éléments dont le name commence par inpQte_ pour calculer le total HT sans remise
for (let i = 0, elInp; (elInp = document.querySelectorAll("[name ^= 'inpQte_' ]")[i]); i++) {
const idProd = elInp.dataset.rid;
const ligne = document.getElementById('trPro_' + idProd);
const code = ligne.dataset.code;
const vente = ligne.dataset.vente * 1;
const qte = elInp.value * 1;
// Mise à jour du 09/11 : on calcule le prix d'achat du produit avec éventuel discount suivant sa qté
const achat = getPrixAchatAvecDiscount(idProd, elInp.value);
// Fin de la mise à jour du 09/11
const varOption = document.getElementById('chkVariante_' + idProd).checked;
if (!varOption) {
// calcul avec juste la quantité et le prix de vente
const vente = elInp.dataset.vente * 1;
totalDevisHt += qte * vente;
}
// Mise à jour du 03/11/2023 : nouvelle colonne du Prix de Vente Unitaire (avec remise)
// On met à jour le PUVenteRem sur la ligne
const remProd = document.getElementById('inpRemise_' + idProd).value;
const remise = remProd * 1;
let puVenteApresRemise = vente;
if (remise > 0) {
puVenteApresRemise = vente - (vente * remise) / 100;
}
document.getElementById('inpPUVenteRem_' + idProd).value = puVenteApresRemise.toFixed(2);
console.log('--- 1 Produit : ' + code + ' - PUVenteApresRemise : ' + puVenteApresRemise);
// Fin de la mise à jour du 03/11/2023
// Modif du 25/04 : on calcule la marge même si c'est une variante / option
//if (variante) {
txMarge = 0;
//} else {
if (achat > 0 && vente > 0 && qte > 0) {
let venteApresRemise = vente;
if (remise > 0) {
venteApresRemise = vente - (vente * remise) / 100;
}
console.log('--- 1 Marge sur : ' + code + ' - achat : ' + achat + ' - vente : ' + vente + ' - venteApresRemise : ' + venteApresRemise);
// Ajout du 0&/12/2023 pour éviter le -infinity
if (venteApresRemise > 0) txMarge = ((venteApresRemise - achat) / venteApresRemise) * 100;
} else {
txMarge = 0;
console.log('--- 1 ERREUR : ' + code + ' - achat : ' + achat + ' - vente : ' + vente);
if (qte > 0) showNotification('Info', "Le prix d'achat et/ou le prix de vente n'est pas renseigné pour ce produit " + code + ', la marge ne peut pas être calculée.', 'info');
}
//}
let inpMG = document.getElementById('inpMG_' + idProd);
inpMG.value = parseFloat(txMarge).toFixed(2);
}
// on met à jour le total HT du devis avant remise
let inpTotalHT = document.getElementById('inpTotalHT');
inpTotalHT.value = totalDevisHt.toFixed(2);
// le total HT du devis a été recalculé, on contrôle les remises sur les produits du devis
let txRemiseAppliquee = 0;
txRemiseAppliquee = controlRemisesProduits(totalDevisHt);
// On réinitialise le flag chkRemisesMarche
if (txRemiseAppliquee > 0) {
// on a une remise de base sur ce devis, par défaut toutes les lignes produits respectent cette remise
chkRemisesMarche = true;
} else {
chkRemisesMarche = false;
}
console.log('calculDevis txRemiseAppliquee : ' + txRemiseAppliquee + ' & chkRemisesMarche : ' + chkRemisesMarche);
console.log('Boucle 2 : calcul du total HT du devis apres remise');
//! on boucle sur tous les éléments dont le name commence par inpRemise_ pour calculer le total HT avec remise
for (let i = 0, elInp; (elInp = document.querySelectorAll("[name ^= 'inpRemise_' ]")[i]); i++) {
// calcul avec la quantité, le prix de vente et la remise
const idProd = elInp.dataset.rid;
const ligne = document.getElementById('trPro_' + idProd);
const vente = ligne.dataset.vente * 1;
const achat = ligne.dataset.achatdiscount * 1;
const rem = elInp.value * 1;
let remise = 0;
if (txRemiseAppliquee > 0) {
if (rem == txRemiseAppliquee) {
// cette ligne produit a un taux de remise identique à la remise de base du devis
remise = txRemiseAppliquee * 1;
} else {
// cette ligne produit a un taux de remise différent de la remise de base du devis, on remet la remise de base du devis
// elInp.value = txRemiseAppliquee;
// remise = txRemiseAppliquee * 1;
// cette remise est différente de la remise de base du devis, le devis ne respecte pas la remise de base,
// donc on peut envoyer le devis à validation si la marge n'est pas bonne
chkRemisesMarche = false;
remise = elInp.value * 1;
}
// elInp.readOnly = true;
} else {
remise = elInp.value * 1;
// elInp.readOnly = false;
}
const varOption = document.getElementById('chkVariante_' + idProd).checked;
if (!varOption) {
const inpQte = document.getElementById('inpQte_' + idProd);
const qte = inpQte.value;
const remiseProduit = (remise * vente) / 100;
console.log('--- 2 remiseProduit : ' + remise + ' * ' + vente + ' / 100 = ' + remiseProduit);
totalDevisHtRemise += (vente - remiseProduit) * (qte * 1);
coutTotalAchat += achat * 1 * (qte * 1);
console.log('--- 2 ligne code ' + ligne.dataset.code + ' = idProd : ' + idProd + ', vente : ' + vente + ', achat : ' + achat + ', qté : ' + qte + ', remise : ' + remise + ', remiseProduit : ' + remiseProduit);
}
}
let inpTotalRemHT = document.getElementById('inpTotalRemHT');
inpTotalRemHT.value = formatAmount(totalDevisHtRemise);
// on met à jour la marge totale
let totalRFA = 0;
// on prend le total HT après remise
// on recherche une RFA sur ce marché
// console.log("RFA TRIM : " + document.getElementById("tdTxRemiseTrim").textContent);
const RFAtrimestrielle = parseFloatFromPercentageString(document.getElementById('tdTxRemiseTrim').textContent);
if (RFAtrimestrielle > 0) {
totalRFA = totalDevisHtRemise * (RFAtrimestrielle / 100);
} else {
// console.log("RFA SEME : " + document.getElementById("tdTxRemiseSeme").textContent);
const RFAsemestrielle = parseFloatFromPercentageString(document.getElementById('tdTxRemiseSeme').textContent);
if (RFAsemestrielle > 0) {
totalRFA = totalDevisHtRemise * (RFAsemestrielle / 100);
} else {
// console.log("RFA ANNU : " + document.getElementById("tdTxRemiseAnnu").textContent);
const RFAannuelle = parseFloatFromPercentageString(document.getElementById('tdTxRemiseAnnu').textContent);
if (RFAannuelle > 0) {
// console.log("On prend en compte la RFAannuelle : " + RFAannuelle + " & totalDevisHtRemise : " + totalDevisHtRemise);
totalRFA = totalDevisHtRemise * (RFAannuelle / 100);
}
}
}
console.log('CoutTotalAchat affiché : ' + coutTotalAchat + ', totalRFA : ' + totalRFA);
// on ajoute le coût total de la RFA au total Achat
coutTotalAchat += totalRFA;
// et on calcule la marge totale
if (totalDevisHtRemise > 0) {
margeTotale = ((totalDevisHtRemise - coutTotalAchat) / totalDevisHtRemise) * 100;
} else {
margeTotale = 0;
}
let inpTotalMG = document.getElementById('inpTotalMarge');
inpTotalMG.value = margeTotale.toFixed(2);
console.log('margeTotale : ' + margeTotale + ', soit (totalDevisHtRemise : ' + totalDevisHtRemise + ' - coutTotalAchat : ' + coutTotalAchat + ') / totalDevisHtRemise = ' + (totalDevisHtRemise - coutTotalAchat) / totalDevisHtRemise);
if (devIp == '1') {
let inpCoutTotalAchat = document.getElementById('inpCoutTotalAchat');
inpCoutTotalAchat.value = coutTotalAchat.toFixed(2);
}
// on renseigne les valeurs globales de ces 3 données mises à jour
devisTotalHT = totalDevisHt;
devisTotalRemHT = totalDevisHtRemise;
devisTotalMarge = margeTotale;
// Si la marge Totale est inférieure au seuil de latitude, on change le bouton en orange ou rouge
const latitudeRR = document.getElementById('inp_latitudeRR').value;
const latitudeDV = document.getElementById('inp_latitudeDV').value;
let btn = document.getElementById('btnSaveDevisAndSend');
// enfin, on met à jour le bouton de sauvegarde du devis
updateBtnSaveDevisAndSend();
chkChange = 1;
};
let clickSaveDevis = function () {
showLoading();
let frmData = new FormData(document.getElementById('frmDevis'));
let objData = {};
frmData.forEach(function (value, key) {
objData[key] = value;
});
fetch('/jxdevis/save_devis', {
method: 'POST',
body: JSON.stringify(objData),
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
}).then(function (response) {
if (!response.ok) {
hideLoading();
showNotification('Erreur', "L'enregistrement du devis n'a pas abouti", 'error');
} else {
const ret = response.json();
ret.then(function (data) {
hideLoading();
// On met à jour la ligne du tableau des devis
let elLigDevis = document.getElementById('tr_' + data.rid);
elLigDevis.cells[7].innerHTML = data.totalremht + ' €';
elLigDevis.cells[8].innerHTML = data.totalmarge + ' %';
showNotification('Devis enregistré', 'Le devis a bien été enregistré', 'success');
});
}
});
chkChange = 0;
return false;
};
let clickSaveDevisAndSend = function () {
const btnLibelle = this.innerHTML;
// si le btnLibelle contient "SAP", on envoie le devis à SAP
let confLibelle = '';
if (btnLibelle.indexOf('SAP') > -1) {
confLibelle = 'traitement SAP ?';
//! On controle que ce devis ne soit pas en cours de validation et qu'il n'ait pas été validé !
if (fkRole < 3) {
// Uniquement pour le DIR-CO et les DV
if (fkStatutDevis == 2 || fkStatutDevis == 3) {
if (chkValidat == 0) {
showNotification('Erreur', "Ce devis est en cours de validation et n'a pas encore été validé. Vous devez d'abord saisir un commentaire de validation et cliquer sur le bouton 'Valider ce devis'", 'error');
return false;
}
}
}
} else if (btnLibelle.indexOf('DIR-CO') > -1) {
confLibelle = 'accord DIR-CO ?';
} else if (btnLibelle.indexOf('DV/DCG') > -1) {
confLibelle = 'accord DV/DCG ?';
}
if (confirm('Voulez-vous enregistrer et envoyer ce devis pour ' + confLibelle)) {
clickSaveDevis();
//! on modifie le statut du devis pour le passer à "2 : en cours de validation" ou en "3: validé"
let data = {};
data['cid'] = idDevis;
data['statut'] = this.getAttribute('data-statut');
data['commentaire'] = 'Devis enregistré et transmis';
fetch('/jxdevis/statut_devis', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
showNotification('Devis enregistré', 'Le devis a bien été enregistré et transmis', 'success');
setTimeout(function () {
location.reload();
}, 2000); // 2000 millisecondes = 2 secondes
}
return false;
};
let clickValDevis = function () {
// Le RR visualise le PDF SAP pour le valider ou non
idDevis = this.getAttribute('data-rid');
fetch('/jximport/get_files', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
body: JSON.stringify({
cid: idDevis,
sup: 'devis_pdf_sap',
}),
}).then(function (response) {
if (response.ok) {
const ret = response.json();
ret.then(function (data) {
if (data.length > 0) {
for (let key in data) {
if (data.hasOwnProperty(key)) {
// Récupération des valeurs de la ligne
let val = data[key];
// On ajoute le lien vers le fichier
const leFichier = val['dir0'] + val['fichier'];
const elLien = document.getElementById('embPdfSAP');
elLien.setAttribute('src', leFichier);
// On affiche le formulaire de validation frmValidationRR
document.getElementById('frmValidationRR').style.display = 'block';
document.getElementById('btnClosePDF').style.display = 'none';
showModal(document.getElementById('modalPDFSAP'));
break;
}
}
} else {
showNotification('Erreur', "Aucun fichier PDF SAP n'a été trouvé", 'error');
}
});
}
});
return false;
};
let clickPdfDevis = function () {
idDevis = this.getAttribute('data-rid');
fetch('/jximport/get_files', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
body: JSON.stringify({
cid: idDevis,
sup: 'devis_pdf_sap',
}),
}).then(function (response) {
if (response.ok) {
const ret = response.json();
ret.then(function (data) {
if (data.length > 0) {
for (let key in data) {
if (data.hasOwnProperty(key)) {
// Récupération des valeurs de la ligne
let val = data[key];
// On ajoute le lien vers le fichier
const leFichier = val['dir0'] + val['fichier'];
const elLien = document.getElementById('embPdfSAP');
elLien.setAttribute('src', leFichier);
// On cache le formulaire de validation frmValidationRR
document.getElementById('frmValidationRR').style.display = 'none';
document.getElementById('btnClosePDF').style.display = 'block';
showModal(document.getElementById('modalPDFSAP'));
break;
}
}
} else {
showNotification('Erreur', "Aucun fichier PDF SAP n'a été trouvé", 'error');
}
});
}
});
return false;
};
let clickValidationRR = function () {
const inpCommentPDFSAP = document.getElementById('inpCommentPDFSAP');
// if (inpCommentPDFSAP.value == "") {
// showNotification("Validation impossible", "Vous devez saisir un commentaire", "error");
// return false;
// }
if (confirm('Confirmez-vous la validation de ce document et du devis ?')) {
let data = {};
data['cid'] = idDevis;
data['statut'] = 7; // 7 = devis validé par le RR, il est à envoyer au client par SAP
data['commentaire'] = inpCommentPDFSAP.value ? inpCommentPDFSAP.value : 'Devis validé par le RR';
fetch('/jxdevis/statut_devis', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
showNotification('Devis validé', 'Le devis a bien été validé', 'success');
hideModal(document.getElementById('modalPDFSAP'));
setTimeout(function () {
location.reload();
}, 2000); // 2000 millisecondes = 2 secondes
}
};
let clickRefusRR = function () {
const inpCommentPDFSAP = document.getElementById('inpCommentPDFSAP');
if (inpCommentPDFSAP.value == '') {
showNotification('Refus impossible', 'Vous devez saisir un commentaire expliquant votre refus.', 'error');
return false;
}
if (confirm('Confirmez-vous le refus de ce document ?')) {
let data = {};
data['cid'] = idDevis;
data['statut'] = 4; // 4 = ddocument refusé par le RR, il revient à 4 à traiter par SAP
data['commentaire'] = inpCommentPDFSAP.value;
fetch('/jxdevis/statut_devis', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
showNotification('Document refusé', 'Refus enregistré, le devis revient à ADV', 'success');
hideModal(document.getElementById('modalPDFSAP'));
setTimeout(function () {
location.reload();
}, 2000); // 2000 millisecondes = 2 secondes
}
};
let clickCloseRR = function () {
if (confirm('Voulez-vous fermer ce document PDF sans y répondre ?')) {
hideModal(document.getElementById('modalPDFSAP'));
}
return false;
};
let clickClosePDF = function () {
hideModal(document.getElementById('modalPDFSAP'));
return false;
};
let clickValidatDevis = function () {
// Un DV ou le DIR-CO valide le devis
const inpCommentValidatDevis = document.getElementById('inpCommentValidatDevis');
// if (inpCommentValidatDevis.value == "") {
// showNotification("Validation impossible", "Vous devez saisir un commentaire", "error");
// return false;
// }
if (fkRole == 2) {
const libBtnSave = document.getElementById('btnSaveDevisAndSend').innerHTML;
if (libBtnSave.indexOf('DIR-CO') > -1) {
// Le DV veut valider un devis qui demande l'accord du DIR-CO
showNotification('Validation impossible', "Vous devez demander l'accord du DIR-CO", 'error');
return false;
}
}
if (confirm("Confirmez-vous la validation de ce devis ? Le devis sera transmis à l'ADV pour traitement SAP.")) {
let data = {};
data['cid'] = idDevis;
data['chk_validat'] = 1;
data['commentaire'] = inpCommentValidatDevis.value ? inpCommentValidatDevis.value : '-';
fetch('/jxdevis/validat_devis', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
showNotification('Devis validé', 'Le devis a bien été validé.', 'success');
setTimeout(function () {
location.reload();
}, 2000); // 2000 millisecondes = 2 secondes
}
};
let clickRefusDevis = function () {
// Un DV ou le DIR-CO refuse le devis
const inpCommentValidatDevis = document.getElementById('inpCommentValidatDevis');
if (inpCommentValidatDevis.value == '') {
showNotification('Refus impossible', 'Vous devez saisir un commentaire expliquant votre refus', 'error');
return false;
}
if (confirm('Confirmez-vous le refus de ce devis ?')) {
let data = {};
data['cid'] = idDevis;
data['chk_validat'] = 0;
data['commentaire'] = inpCommentValidatDevis.value;
fetch('/jxdevis/validat_devis', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
showNotification('Devis refusé', 'Le devis a bien été refusé.', 'success');
setTimeout(function () {
location.reload();
}, 2000); // 2000 millisecondes = 2 secondes
}
};
function autocompleteClient(input, list) {
//! Autocomplete pour la recherche de client
//Add an event listener to compare the input value with list items
input.addEventListener('input', function () {
//Close the existing list if it is open
closeList();
//If the input is empty, exit the function
if (!this.value) return false;
//Create a suggestions <div> and add it to the element containing the input field
suggestions = document.createElement('div');
suggestions.setAttribute('id', 'suggestionsClients');
suggestions.setAttribute('class', 'autocomplete-items');
this.parentNode.appendChild(suggestions);
//Iterate through all entries in the list and find matches (15 max)
let nbSuggestionsFound = 0;
for (let i = 0; i < list.length; i++) {
if (list[i]['rech'].toUpperCase().includes(this.value.toUpperCase())) {
//If a match is found, create a suggestion <div> and add it to the suggestions <div>
suggestion = document.createElement('div');
suggestion.innerHTML = list[i]['rech'];
suggestion.addEventListener('click', function () {
// on a cliqué sur une suggestion, on met à jour les champs du formulaire avec les infos du client
input.value = list[i]['libelle']; // this.innerHTML;
document.getElementById('inp_fk_client').value = list[i]['rowid'];
document.getElementById('inp_adresse1').value = list[i]['adresse1'];
document.getElementById('inp_adresse2').value = list[i]['adresse2'];
document.getElementById('inp_adresse3').value = list[i]['adresse3'];
document.getElementById('inp_cp').value = list[i]['cp'];
document.getElementById('inp_ville').value = list[i]['ville'];
document.getElementById('inp_contact_nom').value = list[i]['contact_nom'];
document.getElementById('inp_contact_prenom').value = list[i]['contact_prenom'];
document.getElementById('inp_contact_fonction').value = list[i]['contact_fonction'];
document.getElementById('inp_telephone').value = list[i]['telephone'];
document.getElementById('inp_email').value = list[i]['email'];
document.getElementById('inp_mobile').value = list[i]['mobile'];
document.getElementById('selTypeEtab').value = list[i]['type_client'];
// on ferme la liste des suggestions
closeList();
});
suggestion.style.cursor = 'pointer';
suggestion.style.backgroundColor = 'lightyellow';
suggestions.appendChild(suggestion);
nbSuggestionsFound++;
if (nbSuggestionsFound > 15) break;
}
}
});
function closeList() {
let suggestions = document.getElementById('suggestionsClients');
if (suggestions) suggestions.parentNode.removeChild(suggestions);
}
}
function autocompleteProdSpecial(input, num, list) {
//! Autocomplete pour la recherche de produit spécial
//Add an event listener to compare the input value with list items
input.addEventListener('input', function () {
//Close the existing list if it is open
closeList();
//If the input is empty, exit the function
if (!this.value) return false;
//Create a suggestions <div> and add it to the element containing the input field
suggestions = document.createElement('div');
suggestions.setAttribute('id', 'suggestionsProdSpecial');
suggestions.setAttribute('class', 'autocomplete-items');
this.parentNode.appendChild(suggestions);
//Iterate through all entries in the list and find matches (15 max)
let nbSuggestionsFound = 0;
for (let i = 0; i < list.length; i++) {
if (list[i]['code'].toUpperCase().includes(this.value.toUpperCase())) {
//If a match is found, create a suggestion <div> and add it to the suggestions <div>
suggestion = document.createElement('div');
suggestion.innerHTML = list[i]['rech'];
suggestion.addEventListener('click', function () {
// on a cliqué sur une suggestion, on met à jour les champs du formulaire avec les infos du client
input.value = list[i]['code']; // this.innerHTML;
document.getElementById('inp_specialFkProduit_' + num).value = list[i]['rowid'];
document.getElementById('inp_specialLibe_' + num).value = list[i]['libelle'];
// on ferme la liste des suggestions
closeList();
});
suggestion.style.cursor = 'pointer';
suggestion.style.backgroundColor = 'lightyellow';
suggestions.appendChild(suggestion);
nbSuggestionsFound++;
if (nbSuggestionsFound > 15) break;
}
}
});
function closeList() {
let suggestions = document.getElementById('suggestionsProdSpecial');
if (suggestions) suggestions.parentNode.removeChild(suggestions);
}
}
let searchProducts = function (el) {
//! L'utilisateur vient de taper au clavier dans un champ de recherche de produit
if (el.keyCode === 13) {
showLoading();
const searchTerm = this.value;
const libIdFamille = this.id.substring(this.id.indexOf('_') + 1);
const idFamille = this.getAttribute('data-idFamille');
fetchSearchProducts(searchTerm, libIdFamille, idFamille);
hideLoading();
return false;
}
};
let autocompleteProduitsFamille = function (input, list, libIdFamille, idFamille) {
//! Autocomplete pour la recherche de produits d'une famille
input.addEventListener('input', function () {
// si l'input est vide, on sort
if (!this.value) {
showProduitsFamille(list, libIdFamille);
return false;
}
// ou si sa longueur est inférieure à 2 caractères
if (this.value.length < 2) return false;
let tblBody = document.getElementById('tblProduits_' + libIdFamille).getElementsByTagName('tbody')[0];
tblBody.innerHTML = '';
let nbSuggestionsFound = 0;
for (let key in list) {
if (list.hasOwnProperty(key)) {
// Récupération des valeurs de la ligne
let val = list[key];
if (val['rech'].toUpperCase().includes(this.value.toUpperCase())) {
// On affiche la ligne
showLineProduitFamille(tblBody, val, libIdFamille);
}
}
}
});
return false;
};
// Use a MutationObserver to monitor the DOM for changes
let observerInputSearchProducts = new MutationObserver(function (mutationsList) {
for (var mutation of mutationsList) {
if (mutation.type === 'childList') {
// If new nodes have been added to the DOM, attach event listeners to any new input elements
var addedNodes = mutation.addedNodes;
for (var node of addedNodes) {
if (node instanceof HTMLElement) {
var newInputs = node.querySelectorAll("input[id^='inpSearchProduct_']");
newInputs.forEach(function (newInput) {
newInput.addEventListener('keyup', function () {
console.log('keyup sur input de recherche de produit');
if (event.keyCode === 13) {
let searchTerm = newInput.value;
let libIdFamille = newInput.id.split('_')[1];
let idFamille = newInput.getAttribute('data-idFamille');
fetchSearchProducts(searchTerm, libIdFamille, idFamille);
}
});
});
}
}
}
}
});
function fetchSearchProducts(searchTerm, libIdFamille, idFamille) {
// On cherche les produits correspondant au terme de recherche dans dataProduitsMarche qui contient la liste des produits du marché par famille
// On nettoie la liste des produits de la famille
let tblBodyProduits = document.getElementById('tblProduits_' + libIdFamille).getElementsByTagName('tbody')[0];
tblBodyProduits.innerHTML = '';
// on boucle sur la liste des produits du marché en cours sur ce devis, et on charge les produits de cette famille qui correspondent au terme de recherche
dataProduitsMarche.forEach(function (lineProduit) {
if (lineProduit.fk_famille == idFamille && (lineProduit.libelle.toUpperCase().includes(searchTerm.toUpperCase()) || lineProduit.code.toUpperCase().includes(searchTerm.toUpperCase()))) {
// TODO : il faut aussi vérifier que le produit n'est pas déjà dans la liste des produits sélectionnés
// on vérifie que le produit n'est pas déjà dans la liste des produits sélectionnés
let isProductAlreadySelected = false;
const tblBodyProductsSelect = document.getElementById('tblProduitsSelect').getElementsByTagName('tbody')[0];
const rowsProductsSelect = tblBodyProductsSelect.getElementsByTagName('tr');
for (let i = 0; i < rowsProductsSelect.length; i++) {
let rowProductSelect = rowsProductsSelect[i];
if (rowProductSelect.getAttribute('data-rid') == lineProduit.rowid) {
isProductAlreadySelected = true;
break;
}
}
if (!isProductAlreadySelected) {
// Insertion d'une nouvelle ligne et création de ses colonnes
let newRow = tblBodyProduits.insertRow(0);
newRow.className = 'ligProduit_' + libIdFamille;
newRow.id = 'ligProduit_' + libIdFamille + '_' + lineProduit.rowid;
newRow.setAttribute('data-rid', lineProduit.rowid);
let celChkBox = newRow.insertCell(0);
celChkBox.className = 'chkBox_' + libIdFamille + ' text-center';
celChkBox.setAttribute('data-rid', lineProduit.rowid);
celChkBox.innerHTML = '<input type="checkbox" class="chkBox" id="chkBoxProd_' + lineProduit.rowid + '" name="chkBoxProd_' + lineProduit.rowid + '" data-rid="' + lineProduit.rowid + '" data-code="' + lineProduit.code + '" data-libelle="' + lineProduit.libelle + '" data-famille="' + libIdFamille + '" />';
let celCode = newRow.insertCell(1);
celCode.innerHTML = lineProduit.code;
let celLibelle = newRow.insertCell(2);
celLibelle.innerHTML = lineProduit.libelle;
let celFamille = newRow.insertCell(3);
celFamille.innerHTML = lineProduit.lib_famille;
}
}
});
}
// Start observing the DOM for changes
observerInputSearchProducts.observe(document.body, {
childList: true,
subtree: true,
});
elInpCommentGesteComm.addEventListener('input', function () {
updateBtnSaveDevisAndSend();
});
function refreshChat() {
if (idDevis > 0) {
//! On récupère les données de devis_histo
fetch('/jxchat/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
body: JSON.stringify({
cid: idDevis,
}),
}).then((response) => {
const ret = response.json();
ret.then(function (data) {
// on vérifie si le nombre de commentaires a changé
if (nbCommentChat != data.length) {
// Si c'est le cas on rafraîchit tous les commentaires
// On supprime tous les commentaires
const chatContainer = document.getElementById('chat-bubbles');
const chatBubbles = chatContainer.getElementsByClassName('chat-bubble');
while (chatBubbles.length > 0) {
chatBubbles[0].parentNode.removeChild(chatBubbles[0]);
}
// On ajoute tous les commentaires
for (let i = 0; i < data.length; i++) {
const chatBubble = document.createElement('div');
chatBubble.classList.add('chat-bubble');
if (data[i].fk_user == fkUser) {
chatBubble.classList.add('right-chat-bubble');
} else {
chatBubble.classList.add('left-chat-bubble');
}
const initiales = data[i].prenom.substring(0, 1) + data[i].libelle.substring(0, 1);
const userInfo = document.createElement('div');
userInfo.classList.add('user-info');
const userInitials = document.createElement('div');
userInitials.classList.add('user-initials');
userInitials.innerHTML = initiales;
userInfo.appendChild(userInitials);
const usernameDate = document.createElement('div');
usernameDate.classList.add('username-date');
const username = document.createElement('span');
username.classList.add('username');
username.innerHTML = data[i].prenom + ' ' + data[i].libelle;
usernameDate.appendChild(username);
const date = document.createElement('span');
date.classList.add('date');
date.innerHTML = data[i].date_histo;
usernameDate.appendChild(date);
userInfo.appendChild(usernameDate);
chatBubble.appendChild(userInfo);
const message = document.createElement('div');
message.classList.add('message');
message.innerHTML = data[i].commentaire;
chatBubble.appendChild(message);
chatContainer.appendChild(chatBubble);
}
nbCommentChat = data.length;
// et on vide le champ de saisie s'il n'a pas le focus
if (!document.getElementById('chatInputMessage').hasFocus) {
document.getElementById('chatInputMessage').value = '';
}
}
const chatCont = document.getElementById('chat-container');
chatCont.style.display = 'block';
});
});
}
}
let chatSendMessage = function () {
// On récupère le message à envoyer
const message = document.getElementById('chatInputMessage').value;
// On récupère l'id du devis
// On envoie le message
fetch('/jxchat/save_message', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json;charset=utf-8',
},
body: JSON.stringify({
cid: idDevis,
message: message,
fkuser: fkUser,
}),
}).then((response) => {
const ret = response.json();
ret.then(function (data) {
refreshChat();
});
});
};
const elSelectTypeClient = document.getElementById('selTypeEtab');
elSelectTypeClient.addEventListener('change', function () {
document.getElementById('inp_type_client').value = this.value;
});
function handleDragStart(e) {
draggedElement = this; // on enregistre l'élément draggé
this.style.opacity = '0.4';
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', this.outerHTML);
console.log('DragStart : ' + this.dataset.code);
}
function handleDragOver(e) {
if (e.preventDefault) {
e.preventDefault();
}
e.dataTransfer.dropEffect = 'move';
console.log('DragOver au-dessus de : ' + this.dataset.code);
return false;
}
function handleDrop(e) {
if (e.stopPropagation) {
e.stopPropagation();
}
console.log('Drop sur : ' + this.dataset.code);
if (this !== e.target && this === e.target.parentNode) {
console.log('Drop dedans');
const dropHTML = e.dataTransfer.getData('text/html');
this.insertAdjacentHTML('beforebegin', dropHTML);
const dropElem = this.previousSibling;
// Supprimer l'élément original
draggedElement.remove();
// Update draggedElement to point to the new element
draggedElement = dropElem;
// Reset opacity and input states
draggedElement.style.opacity = '1.0';
const inputs = draggedElement.querySelectorAll('input');
inputs.forEach((input) => (input.disabled = false));
// Ajouter un événement click à l'élément svg
const svgElement = dropElem.querySelector('#commentProd_' + dropElem.dataset.rid);
svgElement.addEventListener('click', showCommentProd);
addDnDHandlers(dropElem);
updateOrder();
}
return false;
}
function addDnDHandlers(elem) {
elem.addEventListener('dragstart', handleDragStart);
elem.addEventListener('dragover', handleDragOver);
elem.addEventListener('drop', handleDrop);
}
function updateOrder() {
// Met à jour l'ordre des lignes dans le tableau des produits du devis
var rows = document.querySelectorAll('#tblDevisPro tr');
rows.forEach((row, index) => {
const sonCode = row.dataset.code;
if (sonCode) {
row.dataset.ordre = index + 1;
const fkProduit = row.dataset.rid;
console.log('index : ' + index + ' code : ' + sonCode);
document.getElementById('inpOrdre_' + fkProduit).value = index;
}
});
showNotification('Ordre', "Ordre des lignes produits mis à jour. Pensez à enregistrer le devis pour que l'ordre soit bien pris en compte.", 'success');
}
let clickCancelCommentProd = function () {
hideModal(document.getElementById('modalCommentProd'));
};
let clickSaveCommentProd = function () {
// Sauvegarde du commentaire produit
const fkProd = document.getElementById('inp_commentProdId').value;
const commentaire = document.getElementById('inp_commentProd').value;
document.getElementById('inpCom_' + fkProd).value = commentaire;
const svgElement = document.getElementById('commentProd_' + fkProd);
const svgNewColor = commentaire == '' ? 'lightgray' : 'red';
svgElement.querySelector('use').style.fill = svgNewColor;
showNotification('Commentaire', 'Votre commentaire a bien été enregistré dans le tableau. Enregistrez votre devis pour sauvegarder définitivement le commentaire.', 'success');
hideModal(document.getElementById('modalCommentProd'));
};
//! Configuration des événements
//! Sur chaque cellule du tableau des devis ayant la classe celDevis, on affecte un événement click qui appelle la fonction clickLigDevis()
Array.from(elCelDevis).forEach(function (lnDevis) {
lnDevis.addEventListener('click', clickLigDevis);
});
Array.from(elCelArchives).forEach(function (lnArchives) {
lnArchives.addEventListener('click', clickLigArchives);
});
//! 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(elBtnDupDevis).forEach(function (dupDevis) {
dupDevis.addEventListener('click', clickDupDevis);
});
Array.from(elBtnExpExcelDevis).forEach(function (expExcelDevis) {
expExcelDevis.addEventListener('click', clickExpExcelDevis);
});
Array.from(elBtnValDevis).forEach(function (valDevis) {
valDevis.addEventListener('click', clickValDevis);
});
Array.from(elBtnPdfDevis).forEach(function (pdfDevis) {
pdfDevis.addEventListener('click', clickPdfDevis);
});
Array.from(elBtnSupprDevis).forEach(function (supprDevis) {
supprDevis.addEventListener('click', clickSupprDevis);
});
elBtnDevisArchives.addEventListener('click', clickDevisArchives);
elBtnCreateDevis.addEventListener('click', clickCreateDevis);
elBtnValidationRR.addEventListener('click', clickValidationRR);
elBtnRefusRR.addEventListener('click', clickRefusRR);
elBtnCloseRR.addEventListener('click', clickCloseRR);
elBtnClosePDF.addEventListener('click', clickClosePDF);
elBtnCreateClient.addEventListener('click', clickCreateClient);
elBtnCancelCreateClient.addEventListener('click', clickCancelCreateClient);
elBtnSaveCreateClient.addEventListener('click', clickSaveCreateClient);
elBtnSaveEnTete.addEventListener('click', clickSaveEnTete);
elBtnSpeciaux.addEventListener('click', clickSpeciaux);
elBtnCancelSpeciaux.addEventListener('click', clickCancelSpeciaux);
elBtnSaveSpeciaux.addEventListener('click', clickSaveSpeciaux);
elBtnSaveSelProduits.addEventListener('click', clickSaveSelProduits);
elBtnSaveDevis.addEventListener('click', clickSaveDevis);
elBtnSaveDevisAndSend.addEventListener('click', clickSaveDevisAndSend);
elChkClientsSecteur.addEventListener('change', changeClientsSecteur);
elChatBtnSend.addEventListener('click', chatSendMessage);
elBtnCancelCommentProd.addEventListener('click', clickCancelCommentProd);
elBtnSaveCommentProd.addEventListener('click', clickSaveCommentProd);
Array.from(elInputSearchProducts).forEach(function (inpSearch) {
inpSearch.addEventListener('keyup', searchProducts);
});
Array.from(elInputQtes).forEach(function (inpQte) {
inpQte.addEventListener('change', calculDevis);
});
Array.from(elInputRemises).forEach(function (inpRemise) {
inpRemise.addEventListener('change', calculDevis);
});
Array.from(elChkVariantes).forEach(function (chkVariante) {
chkVariante.addEventListener('change', calculDevis);
});
elBtnSideBarDevis.addEventListener('click', function () {
if (elVerticalBar.style.width == '10px') {
elVerticalBar.style.width = '1100px'; // Largeur de la barre lorsqu'elle est ouverte
// et son contenu est affiché
document.getElementById('verticalBarContent').style.display = 'block';
intervalRefresh = setInterval(refreshChat, 6000); // Refresh every 6 seconds (1000 ms = 1 second)
} else {
elVerticalBar.style.width = '10px'; // Largeur de la barre lorsqu'elle est fermée
// et son contenu est caché
document.getElementById('verticalBarContent').style.display = 'none';
setTimeout(function () {
clearInterval(intervalRefresh);
}, 1000);
}
});
});