//! 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 = []
let dataProduitsMercurial = [] // Produits du marché hybride pour l'onglet Mercurial
let chkMarcheHybride = false // Indique si le marché du devis est hybride
//! 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 = 40 // le seuil de marge du RR sur ce devis, par défaut à 40 % (MAJ 05/11/2025)
let seuilMargeDV = 30 // le seuil de marge du DV sur ce devis, par défaut à 30 % (MAJ 05/11/2025)

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)

const tableSortStates = new Map()

function initTableSort() {
  const allTables = document.querySelectorAll('[id^="tblDos"], [id^="tblDosArch"]')

  allTables.forEach(table => {
    const tableId = table.id
    const headers = table.querySelectorAll('th[data-sortable="true"]')
    const tbody = table.querySelector('tbody')

    if (!tbody) return

    if (!tableSortStates.has(tableId)) {
      tableSortStates.set(tableId, {
        originalOrder: null,
        currentSort: { column: null, direction: null }
      })
    }

    headers.forEach(header => {
      header.addEventListener('click', function() {
        const columnIndex = parseInt(this.getAttribute('data-column-index'))
        const sortType = this.getAttribute('data-sort-type')
        sortTable(tableId, columnIndex, sortType, this)
      })
    })
  })
}

function sortTable(tableId, columnIndex, sortType, headerElement) {
  const table = document.getElementById(tableId)
  const tbody = table.querySelector('tbody')
  const rows = Array.from(tbody.querySelectorAll('tr'))
  const state = tableSortStates.get(tableId)

  if (!state.originalOrder) {
    state.originalOrder = rows.slice()
  }

  const allHeaders = table.querySelectorAll('th[data-sortable="true"]')
  allHeaders.forEach(h => h.style.fontWeight = 'normal')

  let sortedRows

  if (state.currentSort.column === columnIndex && state.currentSort.direction === 'desc') {
    sortedRows = state.originalOrder.slice()
    state.currentSort = { column: null, direction: null }
  } else {
    const direction = (state.currentSort.column === columnIndex && state.currentSort.direction === 'asc') ? 'desc' : 'asc'

    sortedRows = rows.slice().sort((a, b) => {
      const aCell = a.cells[columnIndex]
      const bCell = b.cells[columnIndex]

      if (!aCell || !bCell) return 0

      let aValue = aCell.textContent.trim()
      let bValue = bCell.textContent.trim()

      if (sortType === 'number') {
        aValue = aValue.replace(/[^\d,.-]/g, '').replace(',', '.')
        bValue = bValue.replace(/[^\d,.-]/g, '').replace(',', '.')
        aValue = parseFloat(aValue) || 0
        bValue = parseFloat(bValue) || 0
        return direction === 'asc' ? aValue - bValue : bValue - aValue
      } else if (sortType === 'date') {
        aValue = parseDateFromText(aValue)
        bValue = parseDateFromText(bValue)
        if (!aValue && !bValue) return 0
        if (!aValue) return direction === 'asc' ? 1 : -1
        if (!bValue) return direction === 'asc' ? -1 : 1
        return direction === 'asc' ? aValue - bValue : bValue - aValue
      } else {
        const comparison = aValue.localeCompare(bValue, 'fr')
        return direction === 'asc' ? comparison : -comparison
      }
    })

    state.currentSort = { column: columnIndex, direction }
    headerElement.style.fontWeight = 'bold'
  }

  tbody.innerHTML = ''
  sortedRows.forEach(row => tbody.appendChild(row))
}

function parseDateFromText(dateText) {
  const match = dateText.match(/(\d{2})\/(\d{2})[\/\s](\d{4})/)
  if (!match) return null
  const [, day, month, year] = match
  return new Date(year, month - 1, day)
}

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 elBtnReactiverDevis = document.getElementsByClassName('btnReactiverDevis')
  console.log('Nombre de boutons btnReactiverDevis trouvés:', elBtnReactiverDevis.length)
  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('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')
      }

      // Charger les contacts du client et sélectionner le contact du devis
      loadContactsClient(data.code).then(() => {
        if (data.fk_contact && data.fk_contact > 0) {
          document.getElementById('sel_contact').value = data.fk_contact
          // Afficher les infos du contact
          const contact = contactsClient.find(c => c.rowid == data.fk_contact)
          if (contact) {
            displayContactInfos(contact)
          }
        }
      })
    }

    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 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)) {
          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]
          }
        }
      }

      // 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 ? ' <span style="color: #8a2be2; font-weight: bold;">(x' + count + ')</span>' : ''

          // 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'] + 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['rowid']
          newRowPro.dataset.ordre = val['ordre']
          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']
          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)
          celCodePro.style.whiteSpace = 'nowrap'

          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['rowid'] +
            '" name="inpQte_' +
            val['rowid'] +
            '" data-rowidligne="' +
            val['rowid'] +
            '" data-fkproduit="' +
            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['rowid'] +
            '" value="' +
            val['prix_achat_net'] +
            '"/><input type="hidden" name="vente_' +
            val['rowid'] +
            '" value="' +
            val['prix_vente'] +
            '"/>'
          document.getElementById('inpQte_' + val['rowid']).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

          // 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 =
            '<svg id="commentProd_' +
            val['rowid'] +
            '" class="clickable" data-rowidligne="' +
            val['rowid'] +
            '" data-code="' +
            val['code'] +
            '"><use xlink:href="pub/res/svg/icons.svg#message" style="fill: ' +
            svgColor +
            ';"/></svg>'

          // 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 =
              '<svg id="duplicateProd_' +
              val['rowid'] +
              '" class="clickable" data-rowidligne="' +
              val['rowid'] +
              '" data-code="' +
              val['code'] +
              '" title="Dupliquer cette ligne"><use xlink:href="pub/res/svg/icons.svg#add" style="fill: green;"/></svg>'
          }

          // N'afficher la trash que pour les produits dupliqués avec remise à 100%
          let svgDelete = ''
          if (parseFloat(remiseProduit) === 100) {
            svgDelete =
              '<svg id="deleteProd_' +
              val['rowid'] +
              '" class="clickable" data-rowidligne="' +
              val['rowid'] +
              '" data-code="' +
              val['code'] +
              '" title="Supprimer cette ligne"><use xlink:href="pub/res/svg/icons.svg#trash" style="fill: red;"/></svg>'
          }

          let inputOrdreHidden =
            '<input type="hidden" id="inpOrdre_' +
            val['rowid'] +
            '" name="inpOrdre_' +
            val['rowid'] +
            '" value="' +
            val['ordre'] +
            '" />'
          let inputCommentHidden =
            '<input type="hidden" id="inpCom_' +
            val['rowid'] +
            '" name="inpCom_' +
            val['rowid'] +
            '" value="' +
            val['commentaire'] +
            '" />'
          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'])
            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['rowid'] +
            '" name="inpRemise_' +
            val['rowid'] +
            '" data-rowidligne="' +
            val['rowid'] +
            '" data-fkproduit="' +
            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['rowid']).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['rowid'] +
            '" name="inpPUVenteRem_' +
            val['rowid'] +
            '" 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['rowid'] +
            '" name="inpHT_' +
            val['rowid'] +
            '" 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['rowid'] +
            '" name="chkVariante_' +
            val['rowid'] +
            '" data-rowidligne="' +
            val['rowid'] +
            '" data-fkproduit="' +
            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['rowid']).addEventListener('change', calculDevis)

          let celMargePro = newRowPro.insertCell(8)
          celMargePro.innerHTML =
            '<div class="input-group"><input type="text" class="form-control numeric" id="inpMG_' +
            val['rowid'] +
            '" name="inpMG_' +
            val['rowid'] +
            '" 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_' + rowidLigne1)
      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
      chkMarcheHybride = line.chk_marche_hybride == 1 ? true : false

      // Si le marché est hybride, créer le panel et charger les produits
      if (chkMarcheHybride) {
        // Créer le panel Mercurial s'il n'existe pas
        if (!document.getElementById('tabMercurial')) {
          const tabContent = document.querySelector('#divProduitsDisponibles .tab-content')
          const divPanel = document.createElement('div')
          divPanel.setAttribute('role', 'tabpanel')
          divPanel.className = 'tab-pane'
          divPanel.id = 'tabMercurial'
          divPanel.innerHTML = `
            <div class="form-group">
              <label for="inpSearchProduct_Mercurial">Recherche de produits Mercurial : </label>
              <div class="input-group">
                <input type="text" class="form-control" id="inpSearchProduct_Mercurial" placeholder="code ou libellé" size="50px" />
                <div class="input-group-addon">
                  <svg width="18px" height="18px" viewBox="0 0 20 20" role="img" xmlns="http://www.w3.org/2000/svg" aria-labelledby="returnIconTitle" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none" color="#000000">
                    <path d="M19,8 L19,11 C19,12.1045695 18.1045695,13 17,13 L6,13"/>
                    <polyline points="8 16 5 13 8 10"/>
                  </svg>
                </div>
              </div>
            </div>
            <div class="border cm-scrollbar cm-table-w-scroll table-responsive mt-1 table-400">
              <table class="table table-striped table-bordered table-responsive table-fixed" id="tblProduits_Mercurial">
                <thead>
                  <tr>
                    <th class="header" scope="col" width="10%">Sélection</th>
                    <th class="header" scope="col" width="20%">Code</th>
                    <th class="header" scope="col" width="30%">Libellé</th>
                    <th class="header" scope="col" width="10%">Famille</th>
                  </tr>
                </thead>
                <tbody></tbody>
              </table>
            </div>
          `
          tabContent.appendChild(divPanel)
        }

        // Charger les produits Mercurial
        fetch('/jxdevis/load_produits_mercurial', {
          method: 'POST',
          body: JSON.stringify({ fk_marche: idMarche }),
          headers: {
            'Content-Type': 'application/json;charset=utf-8',
            Accept: 'application/json;charset=utf-8',
          },
        }).then((response) => {
          if (response.ok) {
            const ret = response.json()
            ret.then(function (data) {
              dataProduitsMercurial = data
              showProduitsMercurial(data)

              // Créer l'onglet Mercurial en dernière position après affichage des produits
              if (!document.getElementById('liOngletMercurial')) {
                const ulOnglets = document.getElementById('listOngletsProduits')
                const liOnglet = document.createElement('li')
                liOnglet.setAttribute('role', 'presentation')
                liOnglet.id = 'liOngletMercurial'
                liOnglet.innerHTML = '<a href="#tabMercurial" id="onglet_mercurial" aria-controls="tabMercurial" role="tab" data-toggle="tab" style="background-color: #5cb85c; color: white; font-weight: bold;">Mercurial<br/>&nbsp;</a>'
                ulOnglets.appendChild(liOnglet)
              }
            })
          }
        })
      } else {
        // Supprimer l'onglet et le panel Mercurial s'ils existent
        const liOnglet = document.getElementById('liOngletMercurial')
        if (liOnglet) {
          liOnglet.remove()
        }
        const divPanel = document.getElementById('tabMercurial')
        if (divPanel) {
          divPanel.remove()
        }
      }

      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
  }

  function showProduitsMercurial(dProduits) {
    // Affiche tous les produits Mercurial dans l'onglet dédié
    let tblBody = document.getElementById('tblProduits_Mercurial').getElementsByTagName('tbody')[0]
    tblBody.innerHTML = ''

    if (dProduits && dProduits.length > 0) {
      dProduits.forEach(function (lineProduit) {
        let newRow = tblBody.insertRow(-1)
        newRow.className = 'ligProduit_Mercurial'
        newRow.id = 'ligProduit_Mercurial_' + lineProduit.rowid
        newRow.setAttribute('data-rid', lineProduit.rowid)

        let celChkBox = newRow.insertCell(0)
        celChkBox.className = 'chkBox_Mercurial 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="Mercurial" />'

        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 || '-'
      })

      // Brancher l'autocomplete sur le champ de recherche
      autocompleteProduitsFamille(
        document.getElementById('inpSearchProduct_Mercurial'),
        dProduits,
        'Mercurial',
        0
      )
    }
  }

  $('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()

      const formData = new FormData()
      formData.append('rid', idDevis)

      fetch('/jxdevis/duplic_devis', {
        method: 'POST',
        body: formData,
      })
        .then((response) => {
          if (!response.ok) {
            throw new Error('Erreur réseau')
          }
          return response.text()
        })
        .then((data) => {
          showNotification('Duplication', 'Duplication du devis effectuée avec succès', 'success')
          hideLoading()
          setTimeout(function () {
            location.reload()
          }, 2000)
        })
        .catch((error) => {
          showNotification('Erreur', 'Erreur lors de la duplication de ce devis : ' + error.message, 'error')
          hideLoading()
        })
    }
  }

  function clickReactiverDevis(e) {
    console.log('clickReactiverDevis appelé')
    idDevis = this.getAttribute('data-rid')
    console.log('ID du devis à réactiver:', idDevis)

    if (
      confirm(
        'Voulez-vous réactiver ce devis archivé n° ' +
          idDevis +
          ' ?\n\nLe devis passera du statut "Archivé" au statut "En cours".'
      )
    ) {
      showLoading()
      // Utilisation de fetch API (vanilla JS) au lieu de jQuery
      const formData = new FormData()
      formData.append('rid', idDevis)

      fetch('/jxdevis/reactiver_devis', {
        method: 'POST',
        body: formData,
      })
        .then((response) => response.json())
        .then((data) => {
          showNotification('Réactivation', 'Le devis a été réactivé avec succès', 'success')
          hideLoading()
          setTimeout(function () {
            location.reload()
          }, 2000)
        })
        .catch((error) => {
          showNotification('Erreur', 'Erreur lors de la réactivation de ce devis : ' + error, 'error')
          hideLoading()
        })
    }
  }

  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 () {
    showLoading()

    const clientData = {
      libelle: document.getElementById('inp_create_libelle').value,
      type_client: document.getElementById('inp_create_type_client').value,
      adresse1: document.getElementById('inp_create_adresse1').value,
      adresse2: document.getElementById('inp_create_adresse2').value,
      adresse3: document.getElementById('inp_create_adresse3').value,
      cp: document.getElementById('inp_create_cp').value,
      ville: document.getElementById('inp_create_ville').value
    }

    fetch('/jxdevis/save_new_client', {
      method: 'POST',
      body: JSON.stringify(clientData),
      headers: {
        'Content-Type': 'application/json;charset=utf-8',
        Accept: 'application/json;charset=utf-8',
      },
    })
      .then((response) => response.json())
      .then((data) => {
        hideLoading()
        if (data.success) {
          const newClientId = data.rowid
          const newClientCode = data.code

          document.getElementById('inp_fk_client').value = newClientId
          document.getElementById('inp_lib_client').value = clientData.libelle
          document.getElementById('selTypeEtab').value = clientData.type_client
          document.getElementById('inp_type_client').value = clientData.type_client
          document.getElementById('inp_adresse1').value = clientData.adresse1
          document.getElementById('inp_adresse2').value = clientData.adresse2
          document.getElementById('inp_adresse3').value = clientData.adresse3
          document.getElementById('inp_cp').value = clientData.cp
          document.getElementById('inp_ville').value = clientData.ville

          hideModal(document.getElementById('modalCreateClient'))

          document.getElementById('btnCreateClient').innerHTML = 'Modifier le client'
          document.getElementById('btnCreateClient').classList.remove('btn-primary')
          document.getElementById('btnCreateClient').classList.add('btn-info')

          loadContactsClient(newClientCode).then(() => {
            showNotification('Succès', 'Client créé avec succès. Vous pouvez maintenant gérer ses contacts.', 'success')
          })

          chkCreateClient = false
        } else {
          showNotification('Erreur', data.message || 'Erreur lors de la création du client', 'error')
        }
      })
      .catch((error) => {
        hideLoading()
        console.error('Erreur création client:', error)
        showNotification('Erreur', 'Impossible de créer le client', 'error')
      })
  }

  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('sel_contact').value == '0') {
      showNotification(
        'Erreur',
        'Enregistrement impossible : vous devez sélectionner un contact',
        'error'
      )
      document.getElementById('sel_contact').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
    const form = document.getElementById('frmDevisEntete')
    const formData = new FormData(form)

    fetch('/jxdevis/save_devis_entete', {
      method: 'POST',
      body: formData,
    })
      .then((response) => {
        if (!response.ok) {
          throw new Error('Erreur réseau')
        }
        return response.json()
      })
      .then((data) => {
        hideLoading()
        showNotification('Enregistrement', 'Enregistrement effectué avec succès', 'success')
        setTimeout(function () {
          location.reload()
        }, 2000)
      })
      .catch((error) => {
        showNotification('Erreur', 'Erreur lors de la sauvegarde du devis : ' + error.message, 'error')
        hideLoading()
      })
    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
    }
  }

  const inpSearchProduit = document.getElementById('inpSearchProduit')
  if (inpSearchProduit) {
    inpSearchProduit.addEventListener('keypress', function (e) {
      if (e.which == 13) {
        const searchValue = document.getElementById('inpSearchProduit').value
        if (searchValue.length > 2) {
          const formData = new FormData()
          formData.append('term', searchValue)

          fetch('/jxdevis/load_devis_produits_search', {
            method: 'POST',
            body: formData,
          })
            .then((response) => {
              if (!response.ok) {
                throw new Error('Erreur réseau')
              }
              return response.json()
            })
            .then((data) => {
              const tblProduits = document.getElementById('tblProduits')
              const rowCount = tblProduits.rows.length

              for (let i = rowCount - 1; i > 0; i--) {
                tblProduits.deleteRow(i)
              }

              data.forEach(function (line) {
                const row = tblProduits.insertRow()
                row.innerHTML =
                  '<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>'
              })
            })
            .catch((error) => {
              showNotification('Erreur', 'Erreur lors de la recherche de produits : ' + error.message, '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 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)) {
          // 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['rowid']
          newRowCom.dataset.rowidligne = val['rowid']
          let celCom = newRowCom.insertCell(0)
          celCom.colSpan = 8
          celCom.innerHTML =
            '<div class="col-md-2"><label for="inpCom_' +
            val['rowid'] +
            '">Commentaire ' +
            val['code'] +
            ': </label></div><div class="col-md-9"><input type="text" class="form-control w-75" id="inpCom_' +
            val['rowid'] +
            '" name="inpCom_' +
            val['rowid'] +
            '" value="' +
            val['commentaire'] +
            '" /></div>'

          // Insertion d'une nouvelle ligne et création de ses colonnes
          let newRowPro = tblBodyPro.insertRow(-1)
          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']
          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)
          celCodePro.style.whiteSpace = 'nowrap'

          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['rowid'] +
            '" name="inpQte_' +
            val['rowid'] +
            '" data-rowidligne="' +
            val['rowid'] +
            '" data-fkproduit="' +
            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['rowid'] +
            '" value="' +
            val['prix_achat_net'] +
            '"/><input type="hidden" name="vente_' +
            val['rowid'] +
            '" value="' +
            val['prix_vente'] +
            '"/>'
          document.getElementById('inpQte_' + val['rowid']).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

          // 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 =
            '<svg id="commentProd_' +
            val['rowid'] +
            '" class="clickable" data-rowidligne="' +
            val['rowid'] +
            '" data-code="' +
            val['code'] +
            '"><use xlink:href="pub/res/svg/icons.svg#message" style="fill: ' +
            svgColor +
            ';"/></svg>'

          // 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 =
              '<svg id="duplicateProd_' +
              val['rowid'] +
              '" class="clickable" data-rowidligne="' +
              val['rowid'] +
              '" data-code="' +
              val['code'] +
              '" title="Dupliquer cette ligne"><use xlink:href="pub/res/svg/icons.svg#add" style="fill: green;"/></svg>'
          }

          // N'afficher la trash que pour les produits dupliqués avec remise à 100%
          let svgDelete = ''
          if (parseFloat(remiseProduit) === 100) {
            svgDelete =
              '<svg id="deleteProd_' +
              val['rowid'] +
              '" class="clickable" data-rowidligne="' +
              val['rowid'] +
              '" data-code="' +
              val['code'] +
              '" title="Supprimer cette ligne"><use xlink:href="pub/res/svg/icons.svg#trash" style="fill: red;"/></svg>'
          }

          let inputOrdreHidden =
            '<input type="hidden" id="inpOrdre_' +
            val['rowid'] +
            '" name="inpOrdre_' +
            val['rowid'] +
            '" value="' +
            val['ordre'] +
            '" />'
          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) {
              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['rowid'] +
            '" name="inpRemise_' +
            val['rowid'] +
            '" data-rowidligne="' +
            val['rowid'] +
            '" data-fkproduit="' +
            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['rowid']).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['rowid'] +
            '" name="inpPUVenteRem_' +
            val['rowid'] +
            '" 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['rowid'] +
            '" name="inpHT_' +
            val['rowid'] +
            '" 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['rowid'] +
            '" name="chkVariante_' +
            val['rowid'] +
            '" data-rowidligne="' +
            val['rowid'] +
            '" data-fkproduit="' +
            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['rowid']).addEventListener('change', calculDevis)

          let celMargePro = newRowPro.insertCell(8)
          celMargePro.innerHTML =
            '<div class="input-group"><input type="text" class="form-control numeric" id="inpMG_' +
            val['rowid'] +
            '" name="inpMG_' +
            val['rowid'] +
            '" 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_' + rowidLigne1)
      const event = new Event('change')
      inpQte.dispatchEvent(event)
    }
  }

  let showCommentProd = function () {
    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.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

    // 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(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);

    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(
            '=== rowidLigne : ' +
              rowidLigne +
              ' (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 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_' + rowidLigne)
    const code = trPro.dataset.code
    let prixAchat = trPro.dataset.achat
    const prixVente = trPro.dataset.vente

    // console.log("rowidLigne: " + rowidLigne + ", 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_' + 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_' + rowidLigne).value
      remise = this.value
      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_' + rowidLigne).value
      remise = document.getElementById('inpRemise_' + rowidLigne).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_' + rowidLigne)
    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 rowidLigne : ' +
          rowidLigne +
          ', 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_' + 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 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(rowidLigneProd, elInp.value)
      // Fin de la mise à jour du 09/11

      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
        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_' + rowidLigneProd).value
      const remise = remProd * 1

      let puVenteApresRemise = vente
      if (remise > 0) {
        puVenteApresRemise = vente - (vente * remise) / 100
      }
      document.getElementById('inpPUVenteRem_' + rowidLigneProd).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_' + rowidLigneProd)
      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 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
      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_' + rowidLigneProd).checked
      if (!varOption) {
        const inpQte = document.getElementById('inpQte_' + rowidLigneProd)
        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 +
            ' = rowidLigneProd : ' +
            rowidLigneProd +
            ', 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('selTypeEtab').value = list[i]['type_client']
            // Charger les contacts du client sélectionné
            loadContactsClient(list[i]['code'])
            // 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.rowidligne)
      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 rowidLigne = row.dataset.rowidligne
        console.log('index : ' + index + ' code : ' + sonCode)
        document.getElementById('inpOrdre_' + rowidLigne).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'))
  }

  //! ========== GESTION DES CONTACTS ==========

  let contactsClient = [] // Liste des contacts du client en cours
  let currentFkClient = 0 // ID du client en cours

  // Charger les contacts d'un client
  function loadContactsClient(fkClient) {
    if (fkClient == 0) {
      document.getElementById('sel_contact').innerHTML = '<option value="0">- Sélectionner un contact -</option>'
      document.getElementById('btnGererContacts').disabled = true
      document.getElementById('divContactInfos').style.display = 'none'
      contactsClient = []
      currentFkClient = 0
      return Promise.resolve()
    }

    currentFkClient = fkClient
    return fetch('/jxcontacts/load_contacts', {
      method: 'POST',
      body: JSON.stringify({ fk_client: fkClient }),
      headers: {
        'Content-Type': 'application/json;charset=utf-8',
        Accept: 'application/json;charset=utf-8',
      },
    })
      .then((response) => response.json())
      .then((data) => {
        contactsClient = data
        const selContact = document.getElementById('sel_contact')
        selContact.innerHTML = '<option value="0">- Sélectionner un contact -</option>'

        data.forEach((contact) => {
          const option = document.createElement('option')
          option.value = contact.rowid
          option.textContent = contact.nom + ' ' + contact.prenom + (contact.principal == 1 ? ' ⭐' : '')
          selContact.appendChild(option)
        })

        document.getElementById('btnGererContacts').disabled = false

        // Si un seul contact, le sélectionner automatiquement
        if (data.length === 1) {
          selContact.value = data[0].rowid
          displayContactInfos(data[0])
        }
      })
      .catch((error) => {
        console.error('Erreur chargement contacts:', error)
        showNotification('Erreur', 'Impossible de charger les contacts', 'error')
      })
  }

  // Afficher les infos d'un contact en lecture seule
  function displayContactInfos(contact) {
    document.getElementById('info_contact_nom_prenom').textContent = (contact.prenom || '') + ' ' + (contact.nom || '')
    document.getElementById('info_contact_fonction').textContent = contact.fonction || '-'
    document.getElementById('info_contact_email').textContent = contact.email || '-'
    document.getElementById('info_contact_telephone').textContent = contact.telephone || '-'
    document.getElementById('info_contact_mobile').textContent = contact.mobile || '-'
    document.getElementById('divContactInfos').style.display = 'block'
  }

  // Événement changement de contact
  document.getElementById('sel_contact').addEventListener('change', function () {
    const contactId = this.value
    if (contactId == 0) {
      document.getElementById('divContactInfos').style.display = 'none'
      return
    }

    const contact = contactsClient.find((c) => c.rowid == contactId)
    if (contact) {
      displayContactInfos(contact)
    }
  })

  // Ouvrir la modale de gestion des contacts
  document.getElementById('btnGererContacts').addEventListener('click', function () {
    document.getElementById('inp_fk_client_contacts').value = currentFkClient
    loadContactsTable()
    showModal(document.getElementById('modalGererContacts'))
  })

  // Charger la table des contacts
  function loadContactsTable() {
    const tbody = document.querySelector('#tblContacts tbody')
    tbody.innerHTML = '<tr><td colspan="7" class="text-center">Chargement...</td></tr>'

    fetch('/jxcontacts/load_contacts', {
      method: 'POST',
      body: JSON.stringify({ fk_client: currentFkClient }),
      headers: {
        'Content-Type': 'application/json;charset=utf-8',
        Accept: 'application/json;charset=utf-8',
      },
    })
      .then((response) => response.json())
      .then((data) => {
        tbody.innerHTML = ''
        if (data.length === 0) {
          tbody.innerHTML = '<tr><td colspan="7" class="text-center">Aucun contact trouvé</td></tr>'
          return
        }

        data.forEach((contact) => {
          const tr = document.createElement('tr')
          tr.innerHTML = `
            <td>${contact.nom || ''}</td>
            <td>${contact.prenom || ''}</td>
            <td>${contact.fonction || ''}</td>
            <td>${contact.telephone || ''}</td>
            <td>${contact.email || ''}</td>
            <td class="text-center">
              ${contact.principal == 1 ? '<span class="label label-success">Oui</span>' : '<button class="btn btn-xs btn-default btnSetPrincipal" data-id="' + contact.rowid + '">Définir</button>'}
            </td>
            <td class="text-center">
              <button class="btn btn-xs btn-primary btnEditContact" data-id="${contact.rowid}"><i class="fa fa-edit"></i></button>
              <button class="btn btn-xs btn-danger btnDeleteContact" data-id="${contact.rowid}"><i class="fa fa-trash"></i></button>
            </td>
          `
          tbody.appendChild(tr)
        })

        // Attacher les événements
        document.querySelectorAll('.btnEditContact').forEach((btn) => {
          btn.addEventListener('click', function () {
            editContact(this.getAttribute('data-id'))
          })
        })

        document.querySelectorAll('.btnDeleteContact').forEach((btn) => {
          btn.addEventListener('click', function () {
            deleteContact(this.getAttribute('data-id'))
          })
        })

        document.querySelectorAll('.btnSetPrincipal').forEach((btn) => {
          btn.addEventListener('click', function () {
            setPrincipalContact(this.getAttribute('data-id'))
          })
        })
      })
      .catch((error) => {
        console.error('Erreur chargement table contacts:', error)
        tbody.innerHTML = '<tr><td colspan="7" class="text-center text-danger">Erreur de chargement</td></tr>'
      })
  }

  // Nouveau contact
  document.getElementById('btnNouveauContact').addEventListener('click', function () {
    document.getElementById('modEditContactTitreText').textContent = 'Nouveau contact'
    document.getElementById('frmEditContact').reset()
    document.getElementById('inp_contact_rowid').value = '0'
    document.getElementById('inp_contact_fk_client').value = currentFkClient
    showModal(document.getElementById('modalEditContact'))
  })

  // Éditer un contact
  function editContact(contactId) {
    fetch('/jxcontacts/load_contact', {
      method: 'POST',
      body: JSON.stringify({ rowid: contactId }),
      headers: {
        'Content-Type': 'application/json;charset=utf-8',
        Accept: 'application/json;charset=utf-8',
      },
    })
      .then((response) => response.json())
      .then((contact) => {
        document.getElementById('modEditContactTitreText').textContent = 'Modifier le contact'
        document.getElementById('inp_contact_rowid').value = contact.rowid
        document.getElementById('inp_contact_fk_client').value = contact.fk_client
        document.getElementById('inp_contact_nom_edit').value = contact.nom || ''
        document.getElementById('inp_contact_prenom_edit').value = contact.prenom || ''
        document.getElementById('inp_contact_fonction_edit').value = contact.fonction || ''
        document.getElementById('inp_contact_email_edit').value = contact.email || ''
        document.getElementById('inp_contact_telephone_edit').value = contact.telephone || ''
        document.getElementById('inp_contact_mobile_edit').value = contact.mobile || ''
        document.getElementById('inp_contact_principal_edit').checked = contact.principal == 1
        showModal(document.getElementById('modalEditContact'))
      })
      .catch((error) => {
        console.error('Erreur chargement contact:', error)
        showNotification('Erreur', 'Impossible de charger le contact', 'error')
      })
  }

  // Sauvegarder un contact
  document.getElementById('btnSaveEditContact').addEventListener('click', function () {
    const form = document.getElementById('frmEditContact')
    const formData = new FormData(form)

    fetch('/jxcontacts/save_contact', {
      method: 'POST',
      body: formData,
    })
      .then((response) => response.json())
      .then((data) => {
        if (data.ret === 'ok') {
          showNotification('Succès', data.msg, 'success')
          hideModal(document.getElementById('modalEditContact'))
          loadContactsTable()
          loadContactsClient(currentFkClient) // Recharger le sélecteur
        } else {
          showNotification('Erreur', data.msg, 'error')
        }
      })
      .catch((error) => {
        console.error('Erreur sauvegarde contact:', error)
        showNotification('Erreur', 'Impossible de sauvegarder le contact', 'error')
      })
  })

  // Supprimer un contact
  function deleteContact(contactId) {
    if (!confirm('Êtes-vous sûr de vouloir supprimer ce contact ?')) return

    fetch('/jxcontacts/delete_contact', {
      method: 'POST',
      body: JSON.stringify({ rowid: contactId }),
      headers: {
        'Content-Type': 'application/json;charset=utf-8',
        Accept: 'application/json;charset=utf-8',
      },
    })
      .then((response) => response.json())
      .then((data) => {
        if (data.ret === 'ok') {
          showNotification('Succès', data.msg, 'success')
          loadContactsTable()
          loadContactsClient(currentFkClient)
        } else {
          showNotification('Erreur', data.msg, 'error')
        }
      })
      .catch((error) => {
        console.error('Erreur suppression contact:', error)
        showNotification('Erreur', 'Impossible de supprimer le contact', 'error')
      })
  }

  // Définir contact principal
  function setPrincipalContact(contactId) {
    fetch('/jxcontacts/set_principal', {
      method: 'POST',
      body: JSON.stringify({ rowid: contactId, fk_client: currentFkClient }),
      headers: {
        'Content-Type': 'application/json;charset=utf-8',
        Accept: 'application/json;charset=utf-8',
      },
    })
      .then((response) => response.json())
      .then((data) => {
        if (data.ret === 'ok') {
          showNotification('Succès', data.msg, 'success')
          loadContactsTable()
          loadContactsClient(currentFkClient)
        } else {
          showNotification('Erreur', data.msg, 'error')
        }
      })
      .catch((error) => {
        console.error('Erreur définition contact principal:', error)
        showNotification('Erreur', 'Impossible de définir le contact principal', 'error')
      })
  }

  // Fermer les modales
  document.getElementById('btnFermerContacts').addEventListener('click', function () {
    hideModal(document.getElementById('modalGererContacts'))
  })

  document.getElementById('btnCancelEditContact').addEventListener('click', function () {
    hideModal(document.getElementById('modalEditContact'))
  })

  //! ========== FIN GESTION DES CONTACTS ==========

  //! 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(elBtnReactiverDevis).forEach(function (reactiverDevis) {
    console.log('Attachement événement click sur bouton réactiver:', reactiverDevis)
    reactiverDevis.addEventListener('click', clickReactiverDevis)
  })

  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)
  })

  let elSearchDevis = document.getElementById('searchDevis')
  let elBtnResetSearch = document.getElementById('btnResetSearch')
  let searchTimeout = null

  function restoreSearch() {
    const savedTerm = sessionStorage.getItem('devisSearchTerm')
    if (savedTerm && savedTerm.length >= 3) {
      elSearchDevis.value = savedTerm
      elBtnResetSearch.style.display = 'inline-block'
      performSearch(savedTerm)
    }
  }

  function performSearch(term) {
    if (term.length < 3) {
      return
    }

    const context = chkShowDevisArchives ? 'archives' : 'encours'

    fetch('/jxdevis/search_devis', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        term: term,
        context: context,
      }),
    })
      .then((response) => response.json())
      .then((data) => {
        if (data.success) {
          const devisIds = data.devis.map((d) => d.rowid)
          filterDevisTables(devisIds, context)
          updateBadges(data.nb_devis)
        } else {
          console.error('Erreur recherche:', data.message)
        }
      })
      .catch((error) => {
        console.error('Erreur AJAX:', error)
      })
  }

  function filterDevisTables(devisIds, context) {
    if (context === 'encours') {
      const statuts = document.querySelectorAll('[id^="tblBodyDos"]')
      statuts.forEach((tbody) => {
        const rows = tbody.querySelectorAll('tr')
        rows.forEach((row) => {
          if (row.id && row.id.startsWith('tr_')) {
            const rowId = parseInt(row.id.replace('tr_', ''))
            if (devisIds.includes(rowId)) {
              row.style.display = ''
            } else {
              row.style.display = 'none'
            }
          }
        })
      })
    } else {
      const archives = document.querySelectorAll('[id^="tblBodyDosArch"]')
      archives.forEach((tbody) => {
        const rows = tbody.querySelectorAll('tr')
        rows.forEach((row) => {
          if (row.id && row.id.startsWith('trArch_')) {
            const rowId = parseInt(row.id.replace('trArch_', ''))
            if (devisIds.includes(rowId)) {
              row.style.display = ''
            } else {
              row.style.display = 'none'
            }
          }
        })
      })
    }
  }

  function updateBadges(nbDevis) {
    Object.keys(nbDevis).forEach((statutId) => {
      const badge = document.querySelector('[id^="liStat"]')
      if (badge) {
        badge.setAttribute('data-after-text', nbDevis[statutId])
        badge.setAttribute('data-after-type', 'orange badge top left')
      }
    })
  }

  function resetSearch() {
    elSearchDevis.value = ''
    elBtnResetSearch.style.display = 'none'
    sessionStorage.removeItem('devisSearchTerm')

    const context = chkShowDevisArchives ? 'archives' : 'encours'
    if (context === 'encours') {
      const statuts = document.querySelectorAll('[id^="tblBodyDos"]')
      statuts.forEach((tbody) => {
        const rows = tbody.querySelectorAll('tr')
        rows.forEach((row) => {
          row.style.display = ''
        })
      })
    } else {
      const archives = document.querySelectorAll('[id^="tblBodyDosArch"]')
      archives.forEach((tbody) => {
        const rows = tbody.querySelectorAll('tr')
        rows.forEach((row) => {
          row.style.display = ''
        })
      })
    }
  }

  elSearchDevis.addEventListener('input', function () {
    const term = this.value.trim()

    if (searchTimeout) {
      clearTimeout(searchTimeout)
    }

    if (term.length >= 3) {
      elBtnResetSearch.style.display = 'inline-block'
      sessionStorage.setItem('devisSearchTerm', term)
      searchTimeout = setTimeout(() => {
        performSearch(term)
      }, 300)
    } else if (term.length === 0) {
      resetSearch()
    } else {
      elBtnResetSearch.style.display = 'none'
    }
  })

  elBtnResetSearch.addEventListener('click', function () {
    resetSearch()
  })

  restoreSearch()

  initTableSort()

  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)
    }
  })
})
