diff --git a/controllers/cjxdevis.php b/controllers/cjxdevis.php old mode 100644 new mode 100755 index 1240b2d..1e53429 --- a/controllers/cjxdevis.php +++ b/controllers/cjxdevis.php @@ -158,6 +158,131 @@ switch ($Route->_action) { } break; + case "duplicate_ligne_produit": + $data = json_decode(file_get_contents("php://input")); + if (isset($data->rowid_ligne)) { + $rowidLigneSafe = intval(nettoie_input($data->rowid_ligne)); + + $sql = 'SELECT * FROM devis_produits WHERE rowid = ' . $rowidLigneSafe . ';'; + $ligne = getinfos($sql, 'gen'); + + if (count($ligne) > 0) { + $l = $ligne[0]; + $fk_devis = $l['fk_devis']; + + // Récupérer toutes les lignes du devis pour recalculer les ordres + $sql = 'SELECT rowid, ordre FROM devis_produits WHERE fk_devis = ' . $fk_devis . ' ORDER BY rowid;'; + $lignes_devis = getinfos($sql, 'gen'); + + // Recalculer tous les ordres si nécessaire (au cas où il y a des doublons à 0) + $ordre_actuel = 0; + $position_source = -1; + foreach ($lignes_devis as $idx => $ligne_devis) { + if ($ligne_devis['rowid'] == $rowidLigneSafe) { + $position_source = $ordre_actuel; + } + if ($ligne_devis['ordre'] != $ordre_actuel) { + $sql = 'UPDATE devis_produits SET ordre = ' . $ordre_actuel . ' WHERE rowid = ' . $ligne_devis['rowid'] . ';'; + qSQL($sql, 'gen'); + } + $ordre_actuel++; + } + + // Le nouvel ordre sera juste après la ligne source + $nouvel_ordre = $position_source + 1; + + // Décaler toutes les lignes après la position d'insertion + $sql = 'UPDATE devis_produits SET ordre = ordre + 1 WHERE fk_devis = ' . $fk_devis . ' AND ordre >= ' . $nouvel_ordre . ';'; + qSQL($sql, 'gen'); + + $gratuite = false; + if (isset($data->gratuite) && $data->gratuite === true) { + $gratuite = true; + } + + $sql = 'INSERT INTO devis_produits SET '; + $sql .= 'fk_devis = ' . $l['fk_devis'] . ', '; + $sql .= 'fk_produit = ' . $l['fk_produit'] . ', '; + $sql .= 'ordre = ' . $nouvel_ordre . ', '; + $sql .= 'code = "' . $l['code'] . '", '; + $sql .= 'libelle = "' . $l['libelle'] . '", '; + $sql .= 'qte = ' . $l['qte'] . ', '; + + if ($gratuite) { + $sql .= 'prix_vente = 0, '; + $sql .= 'pu_vente_remise = 0, '; + $sql .= 'totalht = 0, '; + $sql .= 'marge = ' . (-floatval($l['prix_achat_net']) * intval($l['qte'])) . ', '; + $sql .= 'remise = 100, '; + } else { + $sql .= 'totalht = ' . $l['totalht'] . ', '; + $sql .= 'remise = ' . $l['remise'] . ', '; + $sql .= 'marge = ' . $l['marge'] . ', '; + $sql .= 'prix_vente = ' . $l['prix_vente'] . ', '; + $sql .= 'pu_vente_remise = ' . $l['pu_vente_remise'] . ', '; + } + + $sql .= 'prix_achat_net = ' . $l['prix_achat_net'] . ', '; + $sql .= 'prc_discount_1 = ' . $l['prc_discount_1'] . ', '; + $sql .= 'quantite_1 = ' . $l['quantite_1'] . ', '; + $sql .= 'prc_discount_2 = ' . $l['prc_discount_2'] . ', '; + $sql .= 'quantite_2 = ' . $l['quantite_2'] . ', '; + $sql .= 'prc_discount_3 = ' . $l['prc_discount_3'] . ', '; + $sql .= 'quantite_3 = ' . $l['quantite_3'] . ', '; + $sql .= 'prc_discount_4 = ' . $l['prc_discount_4'] . ', '; + $sql .= 'quantite_4 = ' . $l['quantite_4'] . ', '; + $sql .= 'prc_discount_5 = ' . $l['prc_discount_5'] . ', '; + $sql .= 'quantite_5 = ' . $l['quantite_5'] . ', '; + $sql .= 'prc_discount_6 = ' . $l['prc_discount_6'] . ', '; + $sql .= 'quantite_6 = ' . $l['quantite_6'] . ', '; + $sql .= 'chk_variante = ' . $l['chk_variante'] . ', '; + $sql .= 'chk_prix_net = ' . $l['chk_prix_net'] . ', '; + $sql .= 'commentaire = "' . $l['commentaire'] . '";'; + + eLog($sql); + qSQL($sql, 'gen'); + + $sql = 'SELECT dp.*, pf.marge_rr, pf.marge_dv FROM devis_produits dp '; + $sql .= 'LEFT JOIN produits p ON dp.fk_produit = p.rowid '; + $sql .= 'LEFT JOIN produits_familles pf ON p.groupe = pf.groupe '; + $sql .= 'WHERE dp.fk_devis = ' . $fk_devis . ' ORDER BY dp.ordre;'; + echo getinfos($sql, 'gen', 'json'); + } else { + echo json_encode(['success' => false, 'msg' => 'Ligne non trouvée']); + } + } else { + echo json_encode(['success' => false, 'msg' => 'rowid_ligne manquant']); + } + break; + + case "delete_ligne_produit": + $data = json_decode(file_get_contents("php://input")); + if (isset($data->rowid_ligne)) { + $rowidLigneSafe = intval(nettoie_input($data->rowid_ligne)); + + $sql = 'SELECT fk_devis FROM devis_produits WHERE rowid = ' . $rowidLigneSafe . ';'; + $ligne = getinfos($sql, 'gen'); + + if (count($ligne) > 0) { + $fk_devis = $ligne[0]['fk_devis']; + + $sql = 'DELETE FROM devis_produits WHERE rowid = ' . $rowidLigneSafe . ';'; + eLog($sql); + qSQL($sql, 'gen'); + + $sql = 'SELECT dp.*, pf.marge_rr, pf.marge_dv FROM devis_produits dp '; + $sql .= 'LEFT JOIN produits p ON dp.fk_produit = p.rowid '; + $sql .= 'LEFT JOIN produits_familles pf ON p.groupe = pf.groupe '; + $sql .= 'WHERE dp.fk_devis = ' . $fk_devis . ' ORDER BY dp.ordre;'; + echo getinfos($sql, 'gen', 'json'); + } else { + echo json_encode(['success' => false, 'msg' => 'Ligne non trouvée']); + } + } else { + echo json_encode(['success' => false, 'msg' => 'rowid_ligne manquant']); + } + break; + case "load_clients_devis": //! On récupère les infos des clients de son secteur ou de toute la France, suivant le devis chk_clients_secteur, utilisé aussi pour l'autocomplete de la recherche de clients $data = json_decode(file_get_contents("php://input")); @@ -843,53 +968,54 @@ switch ($Route->_action) { $idDevis = nettoie_input($data->inpIdDevis); eLog("save_devis final : idDevis = " . $idDevis); - // TODO: enregistrer le prix d'achat et de vente de chaque produit au moment du devis + $sql = 'SELECT rowid FROM devis_produits WHERE fk_devis = ' . $idDevis . ';'; + $lignesProduits = getinfos($sql, 'gen'); - //! loop sur les datas commençant par inpQte_ - foreach ($data as $key => $value) { - if (substr($key, 0, 7) == "inpQte_") { - //! on a une ligne de produit - $idProd = substr($key, 7); - $qte = nettoie_input($value); - $qte = "" ? 0 : $qte; - $rem = nettoie_input($data->{"inpRemise_" . $idProd}); - $rem = "" ? 0 : $rem; + if (count($lignesProduits) > 0) { + foreach ($lignesProduits as $ligne) { + $rowidLigne = $ligne['rowid']; - $ht = nettoie_input($data->{"inpHT_" . $idProd}); - $ht = "" ? 0 : $ht; - // si $ht contient un espace (délimiteur millier), on le supprime - $ht = str_replace(" ", "", $ht); + if (isset($data->{"inpQte_" . $rowidLigne})) { + $qte = nettoie_input($data->{"inpQte_" . $rowidLigne}); + $qte = $qte == "" ? 0 : $qte; + $rem = nettoie_input($data->{"inpRemise_" . $rowidLigne}); + $rem = $rem == "" ? 0 : $rem; - $mg = nettoie_input($data->{"inpMG_" . $idProd}); - $mg = "" ? 0 : $mg; + $ht = nettoie_input($data->{"inpHT_" . $rowidLigne}); + $ht = $ht == "" ? 0 : $ht; + $ht = str_replace(" ", "", $ht); - $ha = nettoie_input($data->{"achat_" . $idProd}); - $ha = "" ? 0 : $ha; - $ha = str_replace(" ", "", $ha); + $mg = nettoie_input($data->{"inpMG_" . $rowidLigne}); + $mg = $mg == "" ? 0 : $mg; - $pv = nettoie_input($data->{"vente_" . $idProd}); - $pv = "" ? 0 : $pv; - $pv = str_replace(" ", "", $pv); + $ha = nettoie_input($data->{"achat_" . $rowidLigne}); + $ha = $ha == "" ? 0 : $ha; + $ha = str_replace(" ", "", $ha); - $pu = nettoie_input($data->{"inpPUVenteRem_" . $idProd}); - $pu = "" ? 0 : $pu; - $pu = str_replace(" ", "", $pu); + $pv = nettoie_input($data->{"vente_" . $rowidLigne}); + $pv = $pv == "" ? 0 : $pv; + $pv = str_replace(" ", "", $pv); - $varOpt = 0; - if (isset($data->{"chkVariante_" . $idProd})) { - $varOpt = 1; + $pu = nettoie_input($data->{"inpPUVenteRem_" . $rowidLigne}); + $pu = $pu == "" ? 0 : $pu; + $pu = str_replace(" ", "", $pu); + + $varOpt = 0; + if (isset($data->{"chkVariante_" . $rowidLigne})) { + $varOpt = 1; + } + + $comment = nettoie_input($data->{"inpCom_" . $rowidLigne}); + $ordre = nettoie_input($data->{"inpOrdre_" . $rowidLigne}); + if ($ordre == "") { + $ordre = 0; + } + $sql = 'UPDATE devis_produits SET qte=' . $qte . ', remise=' . $rem . ', totalht=' . $ht . ', marge=' . $mg . ', prix_achat_net=' . $ha . ', '; + $sql .= 'prix_vente=' . $pv . ', pu_vente_remise=' . $pu . ', chk_variante=' . $varOpt . ', commentaire="' . $comment . '", ordre=' . $ordre . ' '; + $sql .= 'WHERE rowid = ' . $rowidLigne . ';'; + eLog($sql); + qSQL($sql, "gen"); } - - $comment = nettoie_input($data->{"inpCom_" . $idProd}); - $ordre = nettoie_input($data->{"inpOrdre_" . $idProd}); - if ($ordre == "") { - $ordre = 0; - } - $sql = 'UPDATE devis_produits SET qte=' . $qte . ', remise=' . $rem . ', totalht=' . $ht . ', marge=' . $mg . ', prix_achat_net=' . $ha . ', '; - $sql .= 'prix_vente=' . $pv . ', pu_vente_remise=' . $pu . ', chk_variante=' . $varOpt . ', commentaire="' . $comment . '", ordre=' . $ordre . ' '; - $sql .= 'WHERE fk_devis=' . $idDevis . ' AND fk_produit=' . $idProd . ';'; - eLog($sql); - qSQL($sql, "gen"); } } diff --git a/pub/res/js/jdevis.js b/pub/res/js/jdevis.js old mode 100644 new mode 100755 index 421d378..cfcb41b --- a/pub/res/js/jdevis.js +++ b/pub/res/js/jdevis.js @@ -693,16 +693,29 @@ window.addEventListener('DOMContentLoaded', (event) => { // 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'] + // on récupère le premier rowid, pour simuler un changement sur ce produit pour recalculer les totaux en fin de boucle + const rowidLigne1 = ret[0]['rowid'] + // Compter les occurrences de chaque produit pour masquer le + si >= 2 + const produitsCount = {} + const produitsUniques = {} for (let key in ret) { if (ret.hasOwnProperty(key)) { - // Récupération des valeurs de la ligne - let val = ret[key] + const fkProduit = ret[key]['fk_produit'] + produitsCount[fkProduit] = (produitsCount[fkProduit] || 0) + 1 + // Garder la première occurrence de chaque produit + if (!produitsUniques[fkProduit]) { + produitsUniques[fkProduit] = 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 + // Remplir le tableau tblProduitsSelect avec des produits uniques + for (let fkProduit in produitsUniques) { + if (produitsUniques.hasOwnProperty(fkProduit)) { + let val = produitsUniques[fkProduit] + const count = produitsCount[fkProduit] + const badgeCount = count > 1 ? ' (x' + count + ')' : '' // Insertion d'une nouvelle ligne et création de ses colonnes : on prend ici le fk_produit let newRowSelect = tblBodySelect.insertRow(-1) @@ -723,18 +736,29 @@ window.addEventListener('DOMContentLoaded', (event) => { '"/>' let celCode = newRowSelect.insertCell(1) - celCode.innerHTML = val['code'] + celCode.innerHTML = val['code'] + badgeCount let celLibelle = newRowSelect.insertCell(2) celLibelle.innerHTML = val['libelle'] + } + } + + 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 // 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.id = 'trPro_' + val['rowid'] newRowPro.dataset.ordre = val['ordre'] - newRowPro.dataset.rid = val['fk_produit'] + newRowPro.dataset.rowidligne = val['rowid'] + newRowPro.dataset.fkproduit = val['fk_produit'] newRowPro.dataset.code = val['code'] newRowPro.dataset.achat = val['prix_achat_net'] newRowPro.dataset.achatdiscount = val['prix_achat_net'] @@ -758,36 +782,7 @@ window.addEventListener('DOMContentLoaded', (event) => { newRowPro.addEventListener('drop', handleDrop) let celCodePro = newRowPro.insertCell(-1) - const svgColor = val['commentaire'] == '' ? 'lightgray' : 'red' - const svgComment = - '' - let inputOrdreHidden = - '' - let inputCommentHidden = - '' - celCodePro.innerHTML = val['code'] + ' ' + svgComment + inputOrdreHidden + inputCommentHidden - - document.getElementById('commentProd_' + val['fk_produit']).addEventListener('click', showCommentProd) + celCodePro.style.whiteSpace = 'nowrap' let celLibellePro = newRowPro.insertCell(1) celLibellePro.innerHTML = val['libelle'] @@ -799,10 +794,12 @@ window.addEventListener('DOMContentLoaded', (event) => { let celQtePro = newRowPro.insertCell(3) celQtePro.innerHTML = '' - document.getElementById('inpQte_' + val['fk_produit']).addEventListener('change', calculDevis) + document.getElementById('inpQte_' + val['rowid']).addEventListener('change', calculDevis) let celRemisePro = newRowPro.insertCell(4) // Nouveau code 21/09 @@ -834,6 +831,76 @@ window.addEventListener('DOMContentLoaded', (event) => { } // Fin du nouveau code du 21/09 + // Colorisation si remise à 100% (gratuité) + if (parseFloat(remiseProduit) === 100) { + newRowPro.style.backgroundColor = 'rgba(138, 43, 226, 0.2)' + } + + // Remplissage de la cellule code produit (on le fait ici car on a besoin de remiseProduit) + const svgColor = val['commentaire'] == '' ? 'lightgray' : 'red' + const svgComment = + '' + + // N'afficher le + que si le produit apparaît moins de 2 fois dans le devis + let svgDuplicate = '' + if (produitsCount[val['fk_produit']] < 2) { + svgDuplicate = + '' + } + + // N'afficher la trash que pour les produits dupliqués avec remise à 100% + let svgDelete = '' + if (parseFloat(remiseProduit) === 100) { + svgDelete = + '' + } + + let inputOrdreHidden = + '' + let inputCommentHidden = + '' + celCodePro.innerHTML = val['code'] + ' ' + svgComment + ' ' + svgDuplicate + ' ' + svgDelete + inputOrdreHidden + inputCommentHidden + + document.getElementById('commentProd_' + val['rowid']).addEventListener('click', showCommentProd) + if (produitsCount[val['fk_produit']] < 2) { + document.getElementById('duplicateProd_' + val['rowid']).addEventListener('click', duplicateLigneProduit) + } + if (parseFloat(remiseProduit) === 100) { + document.getElementById('deleteProd_' + val['rowid']).addEventListener('click', deleteLigneProduit) + } + // 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']) @@ -845,10 +912,12 @@ window.addEventListener('DOMContentLoaded', (event) => { celRemisePro.innerHTML = '
%
' if (readonlyRemiseProduit == '') { - document.getElementById('inpRemise_' + val['fk_produit']).addEventListener('change', calculDevis) + document.getElementById('inpRemise_' + val['rowid']).addEventListener('change', calculDevis) } // nouvelle colonne PU vente avec remise let celPUVenteRemPro = newRowPro.insertCell(5) celPUVenteRemPro.innerHTML = '
' @@ -878,9 +947,9 @@ window.addEventListener('DOMContentLoaded', (event) => { let celHTPro = newRowPro.insertCell(6) celHTPro.innerHTML = '
' @@ -889,10 +958,12 @@ window.addEventListener('DOMContentLoaded', (event) => { celVariante.className = 'text-center' celVariante.innerHTML = '' - document.getElementById('chkVariante_' + val['fk_produit']).addEventListener('change', calculDevis) + document.getElementById('chkVariante_' + val['rowid']).addEventListener('change', calculDevis) let celMargePro = newRowPro.insertCell(8) celMargePro.innerHTML = '
%
' @@ -996,7 +1067,7 @@ window.addEventListener('DOMContentLoaded', (event) => { 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 inpQte = document.getElementById('inpQte_' + rowidLigne1) const event = new Event('change') inpQte.dispatchEvent(event) @@ -2070,8 +2141,17 @@ window.addEventListener('DOMContentLoaded', (event) => { } // 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'] + // on récupère le premier rowid, pour simuler un changement sur ce produit pour recalculer les totaux en fin de boucle + const rowidLigne1 = data[0]['rowid'] + + // Compter les occurrences de chaque produit pour masquer le + si >= 2 + const produitsCount = {} + for (let key in data) { + if (data.hasOwnProperty(key)) { + const fkProduit = data[key]['fk_produit'] + produitsCount[fkProduit] = (produitsCount[fkProduit] || 0) + 1 + } + } for (let key in data) { if (data.hasOwnProperty(key)) { @@ -2084,27 +2164,28 @@ window.addEventListener('DOMContentLoaded', (event) => { // 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'] + newRowCom.id = 'trCom_' + val['rowid'] + newRowCom.dataset.rowidligne = val['rowid'] let celCom = newRowCom.insertCell(0) celCom.colSpan = 8 celCom.innerHTML = '
' // 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.id = 'trPro_' + val['rowid'] + newRowPro.dataset.rowidligne = val['rowid'] + newRowPro.dataset.fkproduit = val['fk_produit'] newRowPro.dataset.ordre = val['ordre'] newRowPro.dataset.code = val['code'] newRowPro.dataset.achat = val['prix_achat_net'] @@ -2124,27 +2205,7 @@ window.addEventListener('DOMContentLoaded', (event) => { newRowPro.dataset.quantite6 = val['quantite_6'] let celCodePro = newRowPro.insertCell(0) - const svgColor = val['commentaire'] == '' ? 'lightgray' : 'red' - const svgComment = - '' - let inputOrdreHidden = - '' - celCodePro.innerHTML = val['code'] + ' ' + svgComment + inputOrdreHidden - document.getElementById('commentProd_' + val['fk_produit']).addEventListener('click', showCommentProd) + celCodePro.style.whiteSpace = 'nowrap' let celLibellePro = newRowPro.insertCell(1) celLibellePro.innerHTML = val['libelle'] @@ -2156,10 +2217,12 @@ window.addEventListener('DOMContentLoaded', (event) => { let celQtePro = newRowPro.insertCell(3) celQtePro.innerHTML = '' - document.getElementById('inpQte_' + val['fk_produit']).addEventListener('change', calculDevis) + document.getElementById('inpQte_' + val['rowid']).addEventListener('change', calculDevis) let celRemisePro = newRowPro.insertCell(4) // Nouveau code 21/09 @@ -2191,6 +2254,68 @@ window.addEventListener('DOMContentLoaded', (event) => { } // Fin du nouveau code du 21/09 + // Colorisation si remise à 100% (gratuité) + if (parseFloat(remiseProduit) === 100) { + newRowPro.style.backgroundColor = 'rgba(138, 43, 226, 0.2)' + } + + // Remplissage de la cellule code produit (on le fait ici car on a besoin de remiseProduit) + const svgColor = val['commentaire'] == '' ? 'lightgray' : 'red' + const svgComment = + '' + + // N'afficher le + que si le produit apparaît moins de 2 fois dans le devis + let svgDuplicate = '' + if (produitsCount[val['fk_produit']] < 2) { + svgDuplicate = + '' + } + + // N'afficher la trash que pour les produits dupliqués avec remise à 100% + let svgDelete = '' + if (parseFloat(remiseProduit) === 100) { + svgDelete = + '' + } + + let inputOrdreHidden = + '' + celCodePro.innerHTML = val['code'] + ' ' + svgComment + ' ' + svgDuplicate + ' ' + svgDelete + inputOrdreHidden + + document.getElementById('commentProd_' + val['rowid']).addEventListener('click', showCommentProd) + if (produitsCount[val['fk_produit']] < 2) { + document.getElementById('duplicateProd_' + val['rowid']).addEventListener('click', duplicateLigneProduit) + } + if (parseFloat(remiseProduit) === 100) { + document.getElementById('deleteProd_' + val['rowid']).addEventListener('click', deleteLigneProduit) + } + // 20/02/2025 if (val['chk_prix_net']) { if (val['chk_prix_net'] == 1) { @@ -2201,10 +2326,12 @@ window.addEventListener('DOMContentLoaded', (event) => { celRemisePro.innerHTML = '
%
' if (readonlyRemiseProduit == '') { - document.getElementById('inpRemise_' + val['fk_produit']).addEventListener('change', calculDevis) + document.getElementById('inpRemise_' + val['rowid']).addEventListener('change', calculDevis) } // nouvelle colonne PU vente avec remise let celPUVenteRemPro = newRowPro.insertCell(5) celPUVenteRemPro.innerHTML = '
' @@ -2234,9 +2361,9 @@ window.addEventListener('DOMContentLoaded', (event) => { let celHTPro = newRowPro.insertCell(6) celHTPro.innerHTML = '
' @@ -2245,10 +2372,12 @@ window.addEventListener('DOMContentLoaded', (event) => { celVariante.className = 'text-center' celVariante.innerHTML = '' - document.getElementById('chkVariante_' + val['fk_produit']).addEventListener('change', calculDevis) + document.getElementById('chkVariante_' + val['rowid']).addEventListener('change', calculDevis) let celMargePro = newRowPro.insertCell(8) celMargePro.innerHTML = '
%
' @@ -2358,23 +2487,115 @@ window.addEventListener('DOMContentLoaded', (event) => { 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 inpQte = document.getElementById('inpQte_' + rowidLigne1) 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 + console.log('click sur le SVG commentProd de la ligne ' + this.dataset.rowidligne) + document.getElementById('inp_commentProdId').value = this.dataset.rowidligne 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 + inpComment.value = document.getElementById('inpCom_' + this.dataset.rowidligne).value showModal(document.getElementById('modalCommentProd')) inpComment.focus() return false } + let duplicateLigneProduit = function () { + const rowidLigne = this.dataset.rowidligne + const codeProduit = this.dataset.code + console.log('Duplication de la ligne ' + rowidLigne + ' - produit ' + codeProduit) + + // Duplication directe en gratuité (remise 100%) + duplicateLigne(rowidLigne, true) + return false + } + + function duplicateLigne(rowidLigne, gratuite) { + showLoading() + + const data = { + rowid_ligne: rowidLigne, + gratuite: gratuite + } + + fetch('/jxdevis/duplicate_ligne_produit', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }) + .then((response) => { + if (!response.ok) { + hideLoading() + showNotification('Erreur', 'La duplication de la ligne a échoué', 'error') + throw new Error('Erreur lors de la duplication') + } + return response.json() + }) + .then((ret) => { + hideLoading() + const message = gratuite ? 'Ligne dupliquée en GRATUITÉ' : 'Ligne dupliquée avec succès' + showNotification('Succès', message, 'success') + showDevisProduits(ret) + chkChange = 1 + }) + .catch((error) => { + hideLoading() + console.error('Erreur:', error) + showNotification('Erreur', 'Une erreur est survenue lors de la duplication', 'error') + }) + } + + let deleteLigneProduit = function () { + const rowidLigne = this.dataset.rowidligne + const codeProduit = this.dataset.code + console.log('Suppression de la ligne ' + rowidLigne + ' - produit ' + codeProduit) + + if (!confirm('Êtes-vous sûr de vouloir supprimer cette ligne ?\n\nProduit : ' + codeProduit)) { + return false + } + + showLoading() + + const data = { + rowid_ligne: rowidLigne + } + + fetch('/jxdevis/delete_ligne_produit', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }) + .then((response) => { + if (!response.ok) { + hideLoading() + showNotification('Erreur', 'La suppression de la ligne a échoué', 'error') + throw new Error('Erreur lors de la suppression') + } + return response.json() + }) + .then((ret) => { + hideLoading() + showNotification('Succès', 'Ligne supprimée avec succès', 'success') + showDevisProduits(ret) + chkChange = 1 + }) + .catch((error) => { + hideLoading() + console.error('Erreur:', error) + showNotification('Erreur', 'Une erreur est survenue lors de la suppression', 'error') + }) + + return false + } + function controlRemisesProduits(totalHT) { // Contrôle des remises du marché en fonction du total HT du devis @@ -2424,8 +2645,8 @@ window.addEventListener('DOMContentLoaded', (event) => { } } - function getPrixAchatAvecDiscount(idProduit, qte) { - let trPro = document.getElementById('trPro_' + idProduit) + function getPrixAchatAvecDiscount(rowidLigne, qte) { + let trPro = document.getElementById('trPro_' + rowidLigne) 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); @@ -2448,8 +2669,8 @@ window.addEventListener('DOMContentLoaded', (event) => { // on applique le prc_discount prixAchatDiscount = prixAchat - (prixAchat * dscnt) / 100 console.log( - '=== idProduit : ' + - idProduit + + '=== rowidLigne : ' + + rowidLigne + ' (prc_discount_' + inc + ' applique de ' + @@ -2474,15 +2695,15 @@ window.addEventListener('DOMContentLoaded', (event) => { let calculDevis = function () { console.log('calculDevis...') - const idProduit = this.dataset.rid + const rowidLigne = this.dataset.rowidligne // 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) + let trPro = document.getElementById('trPro_' + rowidLigne) 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); + // console.log("rowidLigne: " + rowidLigne + ", code : " + code + ", prixAchat: " + prixAchat + ", prixVente: " + prixVente); let qte = 0 let remise = 0 @@ -2491,20 +2712,20 @@ window.addEventListener('DOMContentLoaded', (event) => { 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 + remise = document.getElementById('inpRemise_' + rowidLigne).value + variante = document.getElementById('chkVariante_' + rowidLigne).checked typeInput = 'qte' } else if (this.name.indexOf('inpRemise') > -1) { // c'est la remise qui a changé - qte = document.getElementById('inpQte_' + idProduit).value + qte = document.getElementById('inpQte_' + rowidLigne).value remise = this.value - variante = document.getElementById('chkVariante_' + idProduit).checked + variante = document.getElementById('chkVariante_' + rowidLigne).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 + qte = document.getElementById('inpQte_' + rowidLigne).value + remise = document.getElementById('inpRemise_' + rowidLigne).value variante = this.checked typeInput = 'variante' } @@ -2526,7 +2747,7 @@ window.addEventListener('DOMContentLoaded', (event) => { totalHt = (prixVente * 1 - remiseProduit * 1) * (qte * 1) } - let inpHT = document.getElementById('inpHT_' + idProduit) + let inpHT = document.getElementById('inpHT_' + rowidLigne) inpHT.value = parseFloat(totalHt).toFixed(2) // Modif du 25/04 : on calcule la marge même si c'est une variante / option @@ -2552,8 +2773,8 @@ window.addEventListener('DOMContentLoaded', (event) => { } else { txMarge = 0 console.log( - 'ERREUR idProduit : ' + - idProduit + + 'ERREUR rowidLigne : ' + + rowidLigne + ', code : ' + code + ' - prixAchat : ' + @@ -2569,22 +2790,22 @@ window.addEventListener('DOMContentLoaded', (event) => { ) } //} - let inpMG = document.getElementById('inpMG_' + idProduit) + let inpMG = document.getElementById('inpMG_' + rowidLigne) 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 rowidLigneProd = elInp.dataset.rowidligne + const ligne = document.getElementById('trPro_' + rowidLigneProd) 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) + const achat = getPrixAchatAvecDiscount(rowidLigneProd, elInp.value) // Fin de la mise à jour du 09/11 - const varOption = document.getElementById('chkVariante_' + idProd).checked + const varOption = document.getElementById('chkVariante_' + rowidLigneProd).checked if (!varOption) { // calcul avec juste la quantité et le prix de vente const vente = elInp.dataset.vente * 1 @@ -2593,14 +2814,14 @@ window.addEventListener('DOMContentLoaded', (event) => { // 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 remProd = document.getElementById('inpRemise_' + rowidLigneProd).value const remise = remProd * 1 let puVenteApresRemise = vente if (remise > 0) { puVenteApresRemise = vente - (vente * remise) / 100 } - document.getElementById('inpPUVenteRem_' + idProd).value = puVenteApresRemise.toFixed(2) + document.getElementById('inpPUVenteRem_' + rowidLigneProd).value = puVenteApresRemise.toFixed(2) console.log('--- 1 Produit : ' + code + ' - PUVenteApresRemise : ' + puVenteApresRemise) // Fin de la mise à jour du 03/11/2023 @@ -2638,7 +2859,7 @@ window.addEventListener('DOMContentLoaded', (event) => { ) } //} - let inpMG = document.getElementById('inpMG_' + idProd) + let inpMG = document.getElementById('inpMG_' + rowidLigneProd) inpMG.value = parseFloat(txMarge).toFixed(2) } @@ -2663,8 +2884,8 @@ window.addEventListener('DOMContentLoaded', (event) => { //! 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 rowidLigneProd = elInp.dataset.rowidligne + const ligne = document.getElementById('trPro_' + rowidLigneProd) const vente = ligne.dataset.vente * 1 const achat = ligne.dataset.achatdiscount * 1 const rem = elInp.value * 1 @@ -2689,9 +2910,9 @@ window.addEventListener('DOMContentLoaded', (event) => { // elInp.readOnly = false; } - const varOption = document.getElementById('chkVariante_' + idProd).checked + const varOption = document.getElementById('chkVariante_' + rowidLigneProd).checked if (!varOption) { - const inpQte = document.getElementById('inpQte_' + idProd) + const inpQte = document.getElementById('inpQte_' + rowidLigneProd) const qte = inpQte.value const remiseProduit = (remise * vente) / 100 @@ -2701,8 +2922,8 @@ window.addEventListener('DOMContentLoaded', (event) => { console.log( '--- 2 ligne code ' + ligne.dataset.code + - ' = idProd : ' + - idProd + + ' = rowidLigneProd : ' + + rowidLigneProd + ', vente : ' + vente + ', achat : ' + @@ -3473,7 +3694,7 @@ window.addEventListener('DOMContentLoaded', (event) => { inputs.forEach((input) => (input.disabled = false)) // Ajouter un événement click à l'élément svg - const svgElement = dropElem.querySelector('#commentProd_' + dropElem.dataset.rid) + const svgElement = dropElem.querySelector('#commentProd_' + dropElem.dataset.rowidligne) svgElement.addEventListener('click', showCommentProd) addDnDHandlers(dropElem) @@ -3495,9 +3716,9 @@ window.addEventListener('DOMContentLoaded', (event) => { const sonCode = row.dataset.code if (sonCode) { row.dataset.ordre = index + 1 - const fkProduit = row.dataset.rid + const rowidLigne = row.dataset.rowidligne console.log('index : ' + index + ' code : ' + sonCode) - document.getElementById('inpOrdre_' + fkProduit).value = index + document.getElementById('inpOrdre_' + rowidLigne).value = index } }) showNotification(