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 =
'Commentaire ' +
val['code'] +
':
'
// 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(