feat(v2.0.6): Fonctionnalité de duplication de lignes produits avec gratuité
Implémentation complète de la duplication de lignes produits dans les devis : Backend (controllers/cjxdevis.php): - Ajout endpoint duplicate_ligne_produit avec paramètre gratuite - Recalcul automatique des ordres lors de la duplication - Suppression du warning "Undefined array key user" - Gestion correcte de l'ordre des lignes (fix ordre=0) Frontend (pub/res/js/jdevis.js): - Bouton ➕ pour dupliquer une ligne produit - Duplication directe en gratuité (remise 100%) sans confirmation - Bouton 🗑️ pour supprimer les lignes à 100% de remise - Colorisation violet clair (rgba(138, 43, 226, 0.2)) des lignes gratuites - Limitation à 2 occurrences max par produit (➕ disparaît après) - Badge (x2) dans l'onglet Sélection pour les produits dupliqués - Déduplication de la liste de sélection (1 produit = 1 ligne) - Première colonne sans retour à la ligne (white-space: nowrap) - Fix: loadProduitsDevis n'existe pas → showDevisProduits - Fix: ReferenceError remiseProduit avant initialisation Bugs corrigés: - Bug #1: Warning PHP "Undefined array key user" (ligne 165) - Bug #2: Ligne dupliquée ne s'affiche pas (ordre=0) - Bug #3: ReferenceError loadProduitsDevis non définie - Bug #4: ReferenceError remiseProduit avant initialisation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
204
controllers/cjxdevis.php
Normal file → Executable file
204
controllers/cjxdevis.php
Normal file → Executable file
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user