feat(v2.0.3): Marchés hybrides et améliorations multiples
Fonctionnalités principales : 1. Marchés hybrides - Onglet Mercurial - Ajout onglet Mercurial avec style distinct (vert, gras, blanc) - Affichage des produits mercuriaux pour marchés hybrides - Filtrage automatique des produits "Hors Marché 999" - Documentation Phase 2 avec CAS 1 et CAS 2 de marchés hybrides - Règles métier pour validation différenciée (devis 100% mercurial vs mixte) 2. Corrections bugs - Fix flag chkChange sur onglet "Sélection Produits" (callback asynchrone) - Plus d'alerte intempestive après sauvegarde des produits 3. Outils de déploiement - Nouveau script deploy-file.sh pour déploiement unitaire (DEV/PROD) - Amélioration deploy-cleo.sh 4. Gestion multi-contacts (v2.0.3) - Contrôleur AJAX cjxcontacts.php - Script migration clients_contacts - Documentation complète 5. Documentation - Mise à jour TODO.md avec Phase 2 marchés hybrides - Mise à jour README.md v2.0.3 - Ajout RULES.md - Ajout migration_clients_contacts.sql 6. Nettoyage - Suppression fichiers obsolètes (conf_new.php, conf_old.php, uof_linet_20250911.sql) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,8 @@ let chkShowDevisArchives = false // indique si on affiche les devis archivés ou
|
||||
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 = []
|
||||
@@ -407,18 +409,24 @@ window.addEventListener('DOMContentLoaded', (event) => {
|
||||
document.getElementById('inp_adresse3').value = data.adresse3
|
||||
document.getElementById('inp_cp').value = data.cp
|
||||
document.getElementById('inp_ville').value = data.ville
|
||||
document.getElementById('inp_contact_nom').value = data.contact_nom
|
||||
document.getElementById('inp_contact_prenom').value = data.contact_prenom
|
||||
document.getElementById('inp_contact_fonction').value = data.contact_fonction
|
||||
document.getElementById('inp_email').value = data.email
|
||||
document.getElementById('inp_telephone').value = data.telephone
|
||||
document.getElementById('inp_mobile').value = data.mobile
|
||||
document.getElementById('selTypeEtab').value = data.type_client
|
||||
elBtnCreateClient.innerHTML = 'Créer un nouveau client'
|
||||
if (elBtnCreateClient.classList.contains('btn-info')) {
|
||||
elBtnCreateClient.classList.remove('btn-info')
|
||||
elBtnCreateClient.classList.add('btn-primary')
|
||||
}
|
||||
|
||||
// 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') {
|
||||
@@ -908,6 +916,86 @@ window.addEventListener('DOMContentLoaded', (event) => {
|
||||
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/> </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
|
||||
@@ -1146,6 +1234,54 @@ window.addEventListener('DOMContentLoaded', (event) => {
|
||||
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') {
|
||||
@@ -1346,33 +1482,63 @@ window.addEventListener('DOMContentLoaded', (event) => {
|
||||
}
|
||||
|
||||
let clickSaveCreateClient = function () {
|
||||
// on regarde si c'est une création de devis ou une modification
|
||||
// on enregistre le fait que ça soit un nouveau client
|
||||
// on met à jour les champs du devis avec les infos du nouveau client
|
||||
// et quand on enregistre le devis on enregistre le nouveau client dans le devis
|
||||
document.getElementById('inp_fk_client').value = '0'
|
||||
document.getElementById('inp_lib_client').value = document.getElementById('inp_create_libelle').value
|
||||
document.getElementById('selTypeEtab').value = document.getElementById('inp_create_type_client').value
|
||||
document.getElementById('inp_type_client').value = document.getElementById('inp_create_type_client').value
|
||||
document.getElementById('inp_adresse1').value = document.getElementById('inp_create_adresse1').value
|
||||
document.getElementById('inp_adresse2').value = document.getElementById('inp_create_adresse2').value
|
||||
document.getElementById('inp_adresse3').value = document.getElementById('inp_create_adresse3').value
|
||||
document.getElementById('inp_cp').value = document.getElementById('inp_create_cp').value
|
||||
document.getElementById('inp_ville').value = document.getElementById('inp_create_ville').value
|
||||
showLoading()
|
||||
|
||||
console.log(
|
||||
'nouveau client créé : ' +
|
||||
document.getElementById('inp_create_type_client').value +
|
||||
' -> ' +
|
||||
document.getElementById('selTypeEtab').value
|
||||
)
|
||||
hideModal(document.getElementById('modalCreateClient'))
|
||||
// on change le texte et la couleur du bouton de nouveau client
|
||||
document.getElementById('btnCreateClient').innerHTML = 'Modifier le nouveau client'
|
||||
document.getElementById('btnCreateClient').classList.remove('btn-primary')
|
||||
document.getElementById('btnCreateClient').classList.add('btn-info')
|
||||
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
|
||||
}
|
||||
|
||||
chkCreateClient = true
|
||||
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 () {
|
||||
@@ -1510,45 +1676,13 @@ window.addEventListener('DOMContentLoaded', (event) => {
|
||||
return false
|
||||
}
|
||||
|
||||
if (document.getElementById('inp_contact_nom').value == '') {
|
||||
if (document.getElementById('sel_contact').value == '0') {
|
||||
showNotification(
|
||||
'Erreur',
|
||||
'Enregistrement impossible : vous devez renseigner le nom et prénom du contact',
|
||||
'Enregistrement impossible : vous devez sélectionner un contact',
|
||||
'error'
|
||||
)
|
||||
document.getElementById('inp_contact_nom').focus()
|
||||
return false
|
||||
}
|
||||
|
||||
if (document.getElementById('inp_contact_prenom').value == '') {
|
||||
showNotification(
|
||||
'Erreur',
|
||||
'Enregistrement impossible : vous devez renseigner le nom et prénom du contact',
|
||||
'error'
|
||||
)
|
||||
document.getElementById('inp_contact_prenom').focus()
|
||||
return false
|
||||
}
|
||||
|
||||
if (document.getElementById('inp_contact_fonction').value == '') {
|
||||
showNotification('Erreur', 'Enregistrement impossible : vous devez renseigner la fonction du contact', 'error')
|
||||
document.getElementById('inp_contact_fonction').focus()
|
||||
return false
|
||||
}
|
||||
|
||||
if (document.getElementById('inp_email').value == '') {
|
||||
showNotification('Erreur', "Enregistrement impossible : vous devez renseigner l'email du contact", 'error')
|
||||
document.getElementById('inp_email').focus()
|
||||
return false
|
||||
}
|
||||
|
||||
if (document.getElementById('inp_telephone').value == '' && document.getElementById('inp_mobile').value == '') {
|
||||
showNotification(
|
||||
'Erreur',
|
||||
'Enregistrement impossible : vous devez renseigner au moins un numéro de téléphone du contact (fixe ou mobile)',
|
||||
'error'
|
||||
)
|
||||
document.getElementById('inp_telephone').focus()
|
||||
document.getElementById('sel_contact').focus()
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1817,10 +1951,10 @@ window.addEventListener('DOMContentLoaded', (event) => {
|
||||
showDevisPro(data)
|
||||
hideLoading()
|
||||
showNotification('Succès', 'Enregistrement des ' + nbProduits + ' produits de ce devis effectué', 'success')
|
||||
chkChange = 0
|
||||
})
|
||||
}
|
||||
})
|
||||
chkChange = 0
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -2891,13 +3025,9 @@ window.addEventListener('DOMContentLoaded', (event) => {
|
||||
document.getElementById('inp_adresse3').value = list[i]['adresse3']
|
||||
document.getElementById('inp_cp').value = list[i]['cp']
|
||||
document.getElementById('inp_ville').value = list[i]['ville']
|
||||
document.getElementById('inp_contact_nom').value = list[i]['contact_nom']
|
||||
document.getElementById('inp_contact_prenom').value = list[i]['contact_prenom']
|
||||
document.getElementById('inp_contact_fonction').value = list[i]['contact_fonction']
|
||||
document.getElementById('inp_telephone').value = list[i]['telephone']
|
||||
document.getElementById('inp_email').value = list[i]['email']
|
||||
document.getElementById('inp_mobile').value = list[i]['mobile']
|
||||
document.getElementById('selTypeEtab').value = list[i]['type_client']
|
||||
// Charger les contacts du client sélectionné
|
||||
loadContactsClient(list[i]['code'])
|
||||
// on ferme la liste des suggestions
|
||||
closeList()
|
||||
})
|
||||
@@ -3305,6 +3435,284 @@ window.addEventListener('DOMContentLoaded', (event) => {
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user