function initSubmenuForm() { // Accéder aux informations stockées du parcours const parcours = JSON.parse(sessionStorage.getItem('parcours')); } function toNumber(x) { if (x == null) return 0; let value = String(x).trim(); if (!value) return 0; value = value .replace(/\s/g, '') .replace(/[^\d.,-]/g, ''); if (!value) return 0; const isNegative = value.startsWith('-'); value = value.replace(/-/g, ''); if (isNegative && value) { value = '-' + value; } const hasComma = value.includes(','); const hasDot = value.includes('.'); if (hasComma) { value = value.replace(/\./g, '').replace(/,/g, '.'); } else if (hasDot) { const dotMatches = value.match(/\./g); const dotCount = dotMatches ? dotMatches.length : 0; if (dotCount > 1) { const parts = value.split('.'); const lastSegment = parts[parts.length - 1]; if (lastSegment.length === 3) { value = parts.join(''); } else { value = parts.slice(0, -1).join('') + '.' + lastSegment; } } } const parsed = Number(value); return Number.isFinite(parsed) ? parsed : 0; } // Fonction pour formater un nombre avec X décimales max (sans décimales si entier) function formatNumber(num, decimals = 2) { if (!num || isNaN(num)) return '0.' + '0'.repeat(decimals); const factor = Math.pow(10, decimals); const rounded = Math.round(num * factor) / factor; // Si c'est un nombre entier, ne pas afficher les décimales if (Number.isInteger(rounded)) { return rounded.toString(); } return rounded.toFixed(decimals); } function getElementByIdFlexible(id) { if (!id) return null; const direct = document.getElementById(id); if (direct) return direct; try { return document.querySelector(`[id="${id.replace(/"/g, '\\"')}"]`); } catch (err) { return null; } } // Exposer initSubmenuForm globalement pour y accéder depuis l'extérieur window.initSubmenuForm = initSubmenuForm;// Module IIFE pour éviter la pollution de l'espace global (function () { // Variables globales du module let parcours, contrat, client, intermediaire, rc, projet, tarif; //Variables modulos et liste let modRCCA, modRCActRCC, modRCActRCE, modRCActCompl, modRCMar, modRCZone, modRCEngagCompl, modRCGarAdd, modRCSinistre, modRCFranchise, modRCPrimeMini let rcTarifGuard = null; let rcTarifHasErrors = false; function syncRCFloatingLabels() { if (window.RCValidationUtils && typeof window.RCValidationUtils.syncFloatingLabels === 'function') { window.RCValidationUtils.syncFloatingLabels(document); } } function updateTarifettesVisibility() { const tarifettesContainer = document.getElementById('tarifettesContainer'); if (!tarifettesContainer) return; tarifettesContainer.classList.toggle('rc-tarifettes-hidden', rcTarifHasErrors); } // ═══════════════════════════════════════════════════════════════ // FONCTIONS HELPERS // ═══════════════════════════════════════════════════════════════ // Fonction helper : trouver la tranche la plus proche function findClosestTranche(val, tranches) { if (val <= tranches[0]) return tranches[0]; if (val >= tranches[tranches.length - 1]) return tranches[tranches.length - 1]; for (let i = 0; i < tranches.length - 1; i++) { if (val >= tranches[i] && val < tranches[i + 1]) { return tranches[i]; } } return tranches[tranches.length - 1]; } function updateTarifChoiceButtonsState() { const buttons = document.querySelectorAll('.franchise-card button[name]'); buttons.forEach((button) => { const shouldDisable = rcTarifHasErrors; button.disabled = shouldDisable; if (!shouldDisable) { button.removeAttribute('title'); return; } button.title = 'Corrigez les erreurs de saisie avant de sélectionner un tarif.'; }); updateTarifettesVisibility(); } function setupRCSafeValidation() { if (!window.RCValidationUtils || typeof window.RCValidationUtils.createGuard !== 'function') { return; } rcTarifGuard = window.RCValidationUtils.createGuard({ summaryId: 'rcTarifBlockingSummary', summaryTitle: 'Impossible de choisir un tarif ou de continuer car :', blockTargets: ['#generateDeclinaison', '#generateProject', '#comm-OK'], onChange: function (messages) { rcTarifHasErrors = messages.length > 0; updateTarifChoiceButtonsState(); } }); rcTarifGuard.registerField('#chiffreAffaire', { profile: 'numeric', label: "Chiffre d'affaires", min: 0, positive: true, requiredWhen: function () { return document.querySelector('input[name="cotisation"]:checked')?.value === 'revisable'; } }); rcTarifGuard.registerField('#nbrVehicule', { profile: 'integer', label: 'Nombre de véhicule(s)', min: 1, max: 2, requiredWhen: function () { return document.querySelector('input[name="cotisation"]:checked')?.value === 'forfaitaire'; } }); rcTarifGuard.registerField('#sinistre', { profile: 'numeric', label: 'Montant de sinistres annuel', min: 0 }); rcTarifGuard.observe('.input-pourcent', { profile: 'numeric', label: "Pourcentage d'activité", min: 0, max: 100, requiredWhen: function (field) { return field && field.offsetParent !== null; }, activeWhen: function (field) { return field && field.offsetParent !== null; } }); const capitalFieldNames = [ 'selectActVoiturier/Loueur', 'selectActCommissionnaire de Transport', 'selectActDéménageur', 'selectActLogistique', 'selectActAutocariste', 'selectActAutres activites' ]; capitalFieldNames.forEach(function (fieldName) { const field = document.getElementsByName(fieldName)[0]; if (!field) return; rcTarifGuard.registerField(field, { profile: 'numeric', label: 'Capital à assurer', min: 0, positive: true, requiredWhen: function () { return field.offsetParent !== null; }, activeWhen: function () { return field.offsetParent !== null; } }); }); rcTarifGuard.registerField('#inputDomImmat', { profile: 'numeric', label: 'Capital dommages immatériels', min: 0, positive: true, requiredWhen: function () { return Boolean(document.getElementById('checkDomImmat')?.checked); }, activeWhen: function () { return Boolean(document.getElementById('checkDomImmat')?.checked); } }); rcTarifGuard.registerField('#inputContConf', { profile: 'numeric', label: 'Capital contenants confiés', min: 0, positive: true, requiredWhen: function () { return Boolean(document.getElementById('checkContConf')?.checked); }, activeWhen: function () { return Boolean(document.getElementById('checkContConf')?.checked); } }); rcTarifGuard.registerField('#inputDiffInv', { profile: 'numeric', label: "Capital différence inventaire", min: 0, positive: true, requiredWhen: function () { return Boolean(document.getElementById('checkDiffInv')?.checked); }, activeWhen: function () { return Boolean(document.getElementById('checkDiffInv')?.checked); } }); rcTarifGuard.registerField('#selTPPCcapital', { profile: 'numeric', label: 'Capital TPPC', min: 0, positive: true, requiredWhen: function () { return Boolean(document.getElementById('checkTPPC')?.checked); }, activeWhen: function () { return Boolean(document.getElementById('checkTPPC')?.checked); } }); rcTarifGuard.registerField('#selTPPCveh', { profile: 'integer', label: 'Nombre de véhicules TPPC', min: 1, max: 10, requiredWhen: function () { return Boolean(document.getElementById('checkTPPC')?.checked); }, activeWhen: function () { return Boolean(document.getElementById('checkTPPC')?.checked); } }); rcTarifGuard.registerField('#tarifCom', { profile: 'numeric', label: 'Tarif commercial', min: 0, positive: true, requiredWhen: function () { return Boolean(document.getElementById('modalTarifCom')?.classList.contains('open')); }, activeWhen: function () { return Boolean(document.getElementById('modalTarifCom')?.classList.contains('open')); } }); rcTarifGuard.registerField('#commentaire', { profile: 'text', label: 'Commentaire explicatif', requiredWhen: function () { return document.getElementById('tarifCom-error')?.style.display !== 'none'; }, activeWhen: function () { return document.getElementById('col-commentaire')?.style.display !== 'none'; } }); rcTarifGuard.observe('#tabAdvaloTerrestre input[type="text"], #tabAdvaloMultimodal input[type="text"], #tabAdvaloAerien input[type="text"]', { profile: 'number_or_consulter', label: 'Valeur ad valorem', min: 0, positive: true, decimals: 3, required: false }); rcTarifGuard.registerExternal('marchandises-valides', function () { const visibleContainers = Array.from(document.querySelectorAll('[name^="mar"]')).filter((container) => { return container && container.offsetParent !== null; }); if (!visibleContainers.length) { return { valid: true }; } const hasAtLeastOneChecked = visibleContainers.some((container) => { return Boolean(container.querySelector('input[type="checkbox"]:checked')); }); if (hasAtLeastOneChecked) { return { valid: true }; } return { valid: false, message: 'Marchandises invalides : sélectionnez au moins une marchandise.' }; }); rcTarifGuard.refresh(); updateTarifChoiceButtonsState(); } // Fonction pour afficher un avertissement visuel de dépassement % function showPercentageWarning(excess) { let warningDiv = document.getElementById('percentageWarning'); if (!warningDiv) { warningDiv = document.createElement('div'); warningDiv.id = 'percentageWarning'; warningDiv.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #f44336; color: white; padding: 15px 25px; border-radius: 5px; box-shadow: 0 4px 6px rgba(0,0,0,0.3); z-index: 9999; font-weight: bold; `; document.body.appendChild(warningDiv); } warningDiv.innerHTML = `Plafonnement à 100% (vous dépassiez de ${excess.toFixed(1)}%)`; warningDiv.style.display = 'block'; setTimeout(() => { warningDiv.style.display = 'none'; }, 3000); } // Fonction pour masquer toutes les primes (quand % invalide) function hideAllPrimes() { const primeElements = [ 'primeChapActRCC', 'primeChapActRCE', 'primeChapActComplRCC', 'primeChapActComplRCE', 'primeChapMarchRCC', 'primeChapMarchRCE', 'primeChapZonesRCC', 'primeChapZonesRCE', 'primeEngValue', 'primeChapGarAddRCC', 'primeChapGarAddRCE', 'priceFr250', 'priceFr400', 'priceFr2000' ]; primeElements.forEach(id => { const el = document.getElementById(id); if (el) { el.innerHTML = ' % invalide (> 100%)'; } }); } // Fonction pour mettre à jour l'indicateur visuel du total des pourcentages function updatePercentageIndicator(total) { const indicator = document.getElementById('pourcentageTotal'); if (!indicator) return; const displayTotal = total.toFixed(1); indicator.textContent = `Total : ${displayTotal}%`; if (total > 100) { indicator.style.background = '#f44336'; indicator.style.color = 'white'; indicator.style.border = '3px solid #c62828'; } else if (total === 100 || Math.abs(total - 100) < 0.1) { indicator.style.background = '#4caf50'; indicator.style.color = 'white'; indicator.style.border = '3px solid #2e7d32'; } else if (total >= 95) { indicator.style.background = '#ff9800'; indicator.style.color = 'white'; indicator.style.border = '3px solid #ef6c00'; } else { indicator.style.background = 'white'; indicator.style.color = 'darkblue'; indicator.style.border = '2px solid darkblue'; } } // ═══════════════════════════════════════════════════════════════ // Initialisation du formulaire et des données function init() { // Materialize init select var select = document.querySelectorAll('select'); M.FormSelect.init(select); // Materialize init Modal var modals = document.querySelectorAll('.modal'); M.Modal.init(modals); // Initialiser les modals spécifiques window.modalAnimauxVivants = M.Modal.getInstance(document.getElementById('modalAnimauxVivants')); window.modalTransportBeton = M.Modal.getInstance(document.getElementById('modalTransportBeton')); window.modalAutocaristeRCE = M.Modal.getInstance(document.getElementById('modalAutocaristeRCE')); window.modalTarifCom = M.Modal.getInstance(document.getElementById('modalTarifCom')); // Accéder aux informations stockées du parcours parcours = JSON.parse(sessionStorage.getItem('parcours')); contrat = JSON.parse(sessionStorage.getItem('contrat')); client = contrat?.["@expand"]?.client || null; intermediaire = contrat?.["@expand"]?.intermediaire || null; // Récupérer les données RC depuis la nouvelle structure (EXACTEMENT comme TPPC) rc = contrat?.["@expand"]?.enCours || null; // RC principal projet = rc?.["@expand"]?.projetRC || null; // Données projetRC tarif = rc?.["@expand"]?.tarifRC || null; // Données tarifRC console.log("Initialisation pour formulaire tarif :", parcours); constantsJSON().then(() => { //TODO à virer après les tests console.log("--- Initialisation modulateur RC ---" , "\nCA : ", modRCCA , "\nactRCC : ", modRCActRCC , "\nactRCE : ", modRCActRCE , "\nactCompl : ", modRCActCompl , "\nmar : ", modRCMar , "\nengagComple : ", modRCEngagCompl , "\nfranchise : ", modRCFranchise , "\ngarAdd : ", modRCGarAdd , "\nprimeMini : ", modRCPrimeMini , "\nzone : ", modRCZone , "\nantecedantSinistre: ", modRCSinistre ); // Appel des différentes fonctions d'initialisation setupEventListeners(); setupTarifetteButtons(); populateFormData(); setupRCSafeValidation(); syncRCFloatingLabels(); updatePercentageIndicator(100); // Initialiser à 100% calcGlobal(); setTimeout(() => { if (rcTarifGuard) rcTarifGuard.refresh(); updateTarifChoiceButtonsState(); syncRCFloatingLabels(); }, 300); }) } // Configuration des listeners d'événements function setupEventListeners() { // Empêcher la soumission du formulaire avec la touche Enter const form = document.getElementById('projetForm'); if (form) { form.addEventListener('keydown', function(e) { // Si Enter est pressée et que ce n'est pas sur un bouton submit if (e.key === 'Enter' && e.target.type !== 'submit' && e.target.tagName !== 'BUTTON') { // Si c'est un input de pourcentage, on passe au suivant if (e.target.classList.contains('input-pourcent')) { e.preventDefault(); // Déclencher l'événement input pour forcer le calcul e.target.dispatchEvent(new Event('input', { bubbles: true })); // Passer au champ suivant const inputs = Array.from(document.querySelectorAll('.input-pourcent')).filter(inp => inp.offsetParent !== null); const currentIndex = inputs.indexOf(e.target); if (currentIndex >= 0 && currentIndex < inputs.length - 1) { inputs[currentIndex + 1].focus(); inputs[currentIndex + 1].select(); } } else { // Pour les autres inputs, empêcher le submit mais permettre la navigation e.preventDefault(); } } }); } document.addEventListener('change', function (event) { const target = event.target; if (!target) return; if (!target.matches('[name^="mar"] input[type="checkbox"], [name^="actCompl"] input[type="checkbox"], #checkVoiturier, #checkCommissionnaire, #checkDemenageur, #checkLogistique, #checkAutocariste, #checkAutres, #zone1, #zone2, #zone3, #zone4, #zone5, #zone6')) { return; } if (rcTarifGuard) rcTarifGuard.refresh(); updateTarifChoiceButtonsState(); }); document.getElementById('loadHistoriqueBtn').addEventListener('click', function () { handleLoadHistoriqueBtn(); }); resetInputs(); var radioButtonsCot = document.getElementsByName('cotisation'); for (var i = 0; i < radioButtonsCot.length; i++) { radioButtonsCot[i].addEventListener('change', function () { if (this.value == "forfaitaire") { document.getElementById("rowNbrVehicule").style.display = "block"; document.getElementById("labelVoiturier").style.display = "block"; document.getElementById("labelCommissionnaire").style.display = "none"; document.getElementById("labelDemenageur").style.display = "block"; document.getElementById("labelLogistique").style.display = "none"; document.getElementById("labelAutocariste").style.display = "block"; document.getElementById("labelAutres").style.display = "none"; document.getElementById("nbrVehicule").value = null; // Lancement du calcul forfaitaire calcForfaitaire(); } else if (this.value == "revisable") { document.getElementById("rowNbrVehicule").style.display = "none"; document.getElementById("labelVoiturier").style.display = "block"; document.getElementById("labelCommissionnaire").style.display = "block"; document.getElementById("labelDemenageur").style.display = "block"; document.getElementById("labelLogistique").style.display = "block"; document.getElementById("labelAutocariste").style.display = "none"; document.getElementById("labelAutres").style.display = "block"; // Lancement du calcul revisable calcRevisable(); } }); }; document.getElementById('chiffreAffaire').addEventListener('input', function () { const cot = document.querySelector('input[name="cotisation"]:checked')?.value; if (document.getElementById('chiffreAffaire').value.trim() == '') { document.getElementById("modCA").style.display = "none"; } else { // Masquer modCA en forfaitaire car il n'est pas utilisé if (cot === 'forfaitaire') { document.getElementById("modCA").style.display = "none"; } else { document.getElementById("modCA").style.display = "block"; } } calcGlobal(); // Validation conditionnelle : CA obligatoire seulement en revisable if (typeof validateField === 'function') { if (cot === 'revisable') { validateField('chiffreAffaire', true); } else { // En forfaitaire, CA optionnel validateField('chiffreAffaire', false); } } if (document.getElementById('projetForm')) { updateSubmitButtonState('projetForm'); } }); document.getElementById('nbrVehicule').addEventListener('input', function () { // Validation : forcer entre 1 et 2 véhicules (comme ancienne plateforme) let nbVeh = parseInt(this.value); if (nbVeh < 1) { this.value = 1; } else if (nbVeh > 2) { this.value = 2; } else if (isNaN(nbVeh)) { this.value = 1; } calcGlobal(); validateField('nbrVehicule', true); if (document.getElementById('projetForm')) { updateSubmitButtonState('projetForm'); } }); document.getElementById('sinistre').addEventListener('input', function () { if (document.getElementById('sinistre').value.trim() == '') { document.getElementById("modSinistre").style.display = "none"; } else { document.getElementById("modSinistre").style.display = "block"; } }); document.getElementById('checkRCE').addEventListener('click', function () { var RCE = document.querySelectorAll('[name="RCE"]'); if (document.getElementById('checkRCE').checked == true) { for (var i = 0; i < RCE.length; i++) { RCE[i].style.display = "block"; } } else { for (var i = 0; i < RCE.length; i++) { RCE[i].style.display = "none"; } } calcGlobal(); }); // Ajouter des styles dynamiques pour améliorer le feedback visuel const style = document.createElement('style'); style.textContent = ` .input-pourcent:focus { border-color: #1976d2 !important; box-shadow: 0 0 8px rgba(25, 118, 210, 0.5) !important; outline: none; transform: scale(1.05); transition: all 0.2s ease; } .input-pourcent:hover { border-color: #5c6bc0 !important; transition: all 0.2s ease; } .input-pourcent.set { background-color: #e8f5e9 !important; border-color: #4caf50 !important; } `; document.head.appendChild(style); // Ajout d'un écouteur d'événement à chaque champ pourcentage document.querySelectorAll('.input-pourcent').forEach(input => { // Sélectionner automatiquement le contenu au focus pour faciliter la saisie input.addEventListener('focus', function() { this.select(); }); // Validation automatique lors de la perte de focus (clic ailleurs) input.addEventListener('blur', function() { // Déclencher le recalcul si la valeur a changé if (this.value !== this.defaultValue) { this.dispatchEvent(new Event('input', { bubbles: true })); } }); input.addEventListener('input', function() { // Récupérer l'ID de l'input modifié pour accéder à son isSet const isSetInputId = 'isSet' + input.id.replace('pourcent', ''); const isSetInput = document.getElementById(isSetInputId); // Mettre à jour isSet correspondant à true si l'input a une valeur if (input.value != '') { isSetInput.value = 'true'; input.classList.add('set'); // Changer le fond } else { isSetInput.value = 'false'; // Réinitialiser isSet si l'input est vide input.classList.remove('set'); // Remettre le fond d'origine } // Réinitialiser les inputs non set à vide avant de recalculer document.querySelectorAll('.input-pourcent').forEach(p => { const correspondingIsSetInput = document.getElementById('isSet' + p.id.replace('pourcent', '')); if (correspondingIsSetInput.value == 'false') { p.value = ''; // Réinitialiser la valeur } }); // Calcul de la somme des pourcentages des champs actuellement modifiés let totalPourcent = 0; document.querySelectorAll('.input-pourcent').forEach(pourcentInput => { if (pourcentInput.value != '' && pourcentInput.offsetParent != null) { totalPourcent += toNumber(pourcentInput.value); } }); // PLAFONNEMENT STRICT À 100% if (totalPourcent > 100) { const excess = totalPourcent - 100; // Plafonnement : empêcher de dépasser // Réduire la valeur qui vient d'être modifiée const currentInputVal = toNumber(input.value); const maxPossible = currentInputVal - excess; if (maxPossible >= 0) { // Plafonner la valeur actuelle input.value = maxPossible.toFixed(2); totalPourcent = 100; } else { // La valeur est trop grande, la limiter au restant const otherTotal = totalPourcent - currentInputVal; input.value = (100 - otherTotal).toFixed(2); totalPourcent = 100; } // Afficher message d'avertissement visuel showPercentageWarning(excess); // Masquer les primes pour montrer que c'est invalide const allInputs = document.querySelectorAll('.input-pourcent'); let finalTotal = 0; allInputs.forEach(p => { if (p.offsetParent != null) { finalTotal += toNumber(p.value); } }); if (finalTotal > 100.01) { // Tolérance de 0.01 pour les arrondis // BLOQUER les calculs - afficher erreur hideAllPrimes(); return; } } // Calcul de la différence à répartir const remainingPourcent = 100 - totalPourcent; // Répartition de la différence sur les champs restants non marqués isSet const nonModifiedInputs = Array.from(document.querySelectorAll('.input-pourcent')).filter(p => { const correspondingIsSetInput = document.getElementById('isSet' + p.id.replace('pourcent', '')); return p.value == '' && correspondingIsSetInput.value == 'false' && p.offsetParent != null; }); // Vérifier si nous avons des inputs non modifiés pour répartir le pourcentage restant if (remainingPourcent > 0 && nonModifiedInputs.length > 0) { const pourcentToAdd = remainingPourcent / nonModifiedInputs.length; nonModifiedInputs.forEach(p => { p.value = pourcentToAdd.toFixed(2); }); } else if (remainingPourcent > 0 && nonModifiedInputs.length === 0) { // Tous les champs sont remplis mais total < 100 // Trouver le dernier champ non-set et lui ajouter le restant const allInputs = Array.from(document.querySelectorAll('.input-pourcent')).filter(p => p.offsetParent != null); const lastNonSet = allInputs.filter(p => { const correspondingIsSetInput = document.getElementById('isSet' + p.id.replace('pourcent', '')); return correspondingIsSetInput.value == 'false'; }).pop(); if (lastNonSet) { const currentVal = toNumber(lastNonSet.value); lastNonSet.value = (currentVal + remainingPourcent).toFixed(2); } } // Mettre à jour l'indicateur visuel du total updatePercentageIndicator(totalPourcent); calcGlobal(); }); }); // Bouton reset de l'équilibrage pourcentage document.getElementById('resetPourcent').addEventListener('click', function() { resetInputs(); updatePercentageIndicator(100); // Après reset = 100% calcGlobal(); }); document.getElementById('checkVoiturier').addEventListener('click', function () { var actVoiturier = document.querySelectorAll('[name="actVoiturier/Loueur"]'); var actComplVoiturier = document.querySelectorAll('[name="actComplVoiturier/Loueur"]'); var marVoiturier = document.querySelectorAll('[name="marVoiturier/Loueur"]'); if (document.getElementById('checkVoiturier').checked == true) { for (var i = 0; i < actVoiturier.length; i++) { actVoiturier[i].style.display = "block"; actComplVoiturier[i].style.display = "block"; marVoiturier[i].style.display = "block"; } } else { for (var i = 0; i < actVoiturier.length; i++) { actVoiturier[i].style.display = "none"; actComplVoiturier[i].style.display = "none"; marVoiturier[i].style.display = "none"; } } // Reset de l'équilibrage pourcentage resetInputs(); updatePercentageIndicator(100); calcGlobal(); }); document.getElementById('checkCommissionnaire').addEventListener('click', function () { var actCommissionnaire = document.querySelectorAll('[name="actCommissionnaire de Transport"]'); var actComplCommissionnaire = document.querySelectorAll('[name="actComplCommissionnaire de Transport"]'); var marCommissionnaire = document.querySelectorAll('[name="marCommissionnaire de Transport"]'); if (document.getElementById('checkCommissionnaire').checked == true) { for (var i = 0; i < actCommissionnaire.length; i++) { actCommissionnaire[i].style.display = "block"; actComplCommissionnaire[i].style.display = "block"; marCommissionnaire[i].style.display = "block"; } } else { for (var i = 0; i < actCommissionnaire.length; i++) { actCommissionnaire[i].style.display = "none"; actComplCommissionnaire[i].style.display = "none"; marCommissionnaire[i].style.display = "none"; } } // Reset de l'équilibrage pourcentage resetInputs(); updatePercentageIndicator(100); calcGlobal(); }); document.getElementById('checkDemenageur').addEventListener('click', function () { var actDemenageur = document.querySelectorAll('[name="actDéménageur"]'); var actComplDemenageur = document.querySelectorAll('[name="actComplDéménageur"]'); var marDemenageur = document.querySelectorAll('[name="marDéménageur"]'); if (document.getElementById('checkDemenageur').checked == true) { for (var i = 0; i < actDemenageur.length; i++) { actDemenageur[i].style.display = "block"; actComplDemenageur[i].style.display = "block"; marDemenageur[i].style.display = "block"; } } else { for (var i = 0; i < actDemenageur.length; i++) { actDemenageur[i].style.display = "none"; actComplDemenageur[i].style.display = "none"; marDemenageur[i].style.display = "none"; } } // Reset de l'équilibrage pourcentage resetInputs(); updatePercentageIndicator(100); calcGlobal(); }); document.getElementById('checkLogistique').addEventListener('click', function () { var actLogistique = document.querySelectorAll('[name="actLogistique"]'); var actComplLogistique = document.querySelectorAll('[name="actComplLogistique"]'); var marLogistique = document.querySelectorAll('[name="marLogistique"]'); if (document.getElementById('checkLogistique').checked == true) { for (var i = 0; i < actLogistique.length; i++) { actLogistique[i].style.display = "block"; actComplLogistique[i].style.display = "block"; marLogistique[i].style.display = "block"; } } else { for (var i = 0; i < actLogistique.length; i++) { actLogistique[i].style.display = "none"; actComplLogistique[i].style.display = "none"; marLogistique[i].style.display = "none"; } } // Reset de l'équilibrage pourcentage resetInputs(); updatePercentageIndicator(100); calcGlobal(); }); document.getElementById('checkAutocariste').addEventListener('click', function () { var actAutocariste = document.querySelectorAll('[name="actAutocariste"]'); var marAutocariste = document.querySelectorAll('[name="marAutocariste"]'); if (document.getElementById('checkAutocariste').checked == true) { for (var i = 0; i < actAutocariste.length; i++) { actAutocariste[i].style.display = "block"; marAutocariste[i].style.display = "block"; } } else { for (var i = 0; i < actAutocariste.length; i++) { actAutocariste[i].style.display = "none"; marAutocariste[i].style.display = "none"; } } // Reset de l'équilibrage pourcentage resetInputs(); updatePercentageIndicator(100); calcGlobal(); }); document.getElementById('checkAutres').addEventListener('click', function () { var actAutres = document.querySelectorAll('[name="actAutres activites"]'); var marAutres = document.querySelectorAll('[name="marAutres activites"]'); if (document.getElementById('checkAutres').checked == true) { for (var i = 0; i < actAutres.length; i++) { actAutres[i].style.display = "block"; marAutres[i].style.display = "block"; } } else { for (var i = 0; i < actAutres.length; i++) { actAutres[i].style.display = "none"; marAutres[i].style.display = "none"; } } // Reset de l'équilibrage pourcentage resetInputs(); updatePercentageIndicator(100); calcGlobal(); }); // Event listeners pour les inputs capital (au lieu de selects) const capitalInputs = [ 'selectActVoiturier/Loueur', 'selectActCommissionnaire de Transport', 'selectActDéménageur', 'selectActLogistique', 'selectActAutocariste', 'selectActAutres activites' ]; capitalInputs.forEach(name => { const el = document.getElementsByName(name)[0]; if (el) { el.addEventListener('input', function() { calcGlobal(); }); } }); const checkboxesActComplVoiturier = document.querySelectorAll('div[name="actComplVoiturier/Loueur"] input[type="checkbox"]'); checkboxesActComplVoiturier.forEach(checkbox => { checkbox.addEventListener('change', function () { calcGlobal(); }); }); const checkboxesActCompltComDeTransport = document.querySelectorAll('div[name="actComplCommissionnaire de Transport"] input[type="checkbox"]'); checkboxesActCompltComDeTransport.forEach(checkbox => { checkbox.addEventListener('change', function () { calcGlobal(); }); }); const checkboxesActComplDem = document.querySelectorAll('div[name="actComplDéménageur"] input[type="checkbox"]'); checkboxesActComplDem.forEach(checkbox => { checkbox.addEventListener('change', function () { calcGlobal(); }); }); const checkboxesActComplLogistique = document.querySelectorAll('div[name="actComplLogistique"] input[type="checkbox"]'); checkboxesActComplLogistique.forEach(checkbox => { checkbox.addEventListener('change', function () { calcGlobal(); }); }); const checkboxesMarVoiturier = document.querySelectorAll('div[name="marVoiturier/Loueur"] input[type="checkbox"]'); checkboxesMarVoiturier.forEach(checkbox => { checkbox.addEventListener('change', function () { calcGlobal(); }); }); const checkboxesMarComDeTransport = document.querySelectorAll('div[name="marCommissionnaire de Transport"] input[type="checkbox"]'); checkboxesMarComDeTransport.forEach(checkbox => { checkbox.addEventListener('change', function () { calcGlobal(); }); }); const checkboxesMarDem = document.querySelectorAll('div[name="marDéménageur"] input[type="checkbox"]'); checkboxesMarDem.forEach(checkbox => { checkbox.addEventListener('change', function () { calcGlobal(); }); }); const checkboxesMarLogistique = document.querySelectorAll('div[name="marLogistique"] input[type="checkbox"]'); checkboxesMarLogistique.forEach(checkbox => { checkbox.addEventListener('change', function () { calcGlobal(); }); }); const checkboxesMarAutres = document.querySelectorAll('div[name="marAutres activites"] input[type="checkbox"]'); checkboxesMarAutres.forEach(checkbox => { checkbox.addEventListener('change', function () { calcGlobal(); }); }); document.getElementById('btnMondeEntier').addEventListener('click', function () { document.getElementById('zone1').checked = true; document.getElementById('zone1').disabled = true; document.getElementById('zone2').checked = true; document.getElementById('zone2').disabled = true; document.getElementById('zone3').checked = true; document.getElementById('zone4').checked = true; document.getElementById('zone5').checked = true; document.getElementById('zone6').checked = true; }); document.getElementById('btnReset').addEventListener('click', function () { document.getElementById('zone1').checked = false; document.getElementById('zone1').disabled = false; document.getElementById('zone2').checked = false; document.getElementById('zone2').disabled = false; document.getElementById('zone3').checked = false; document.getElementById('zone4').checked = false; document.getElementById('zone5').checked = false; document.getElementById('zone6').checked = false; }); document.getElementById('btnZone1').addEventListener('click', function () { const elem = document.getElementById('modalZone1'); const instance = M.Modal.getInstance(elem); instance.open(); }); document.getElementById('btnZone2').addEventListener('click', function () { const elem = document.getElementById('modalZone2'); const instance = M.Modal.getInstance(elem); instance.open(); }); document.getElementById('btnZone3').addEventListener('click', function () { const elem = document.getElementById('modalZone3'); const instance = M.Modal.getInstance(elem); instance.open(); }); document.getElementById('btnZone4').addEventListener('click', function () { const elem = document.getElementById('modalZone4'); const instance = M.Modal.getInstance(elem); instance.open(); }); document.getElementById('btnZone5').addEventListener('click', function () { const elem = document.getElementById('modalZone5'); const instance = M.Modal.getInstance(elem); instance.open(); }); document.getElementById('btnZone6').addEventListener('click', function () { const elem = document.getElementById('modalZone6'); const instance = M.Modal.getInstance(elem); instance.open(); }); document.getElementById('btnZone7').addEventListener('click', function () { const elem = document.getElementById('modalZoneExclus'); const instance = M.Modal.getInstance(elem); instance.open(); }); document.getElementById('zone2').addEventListener('click', function () { if (document.getElementById('zone2').checked == true) { document.getElementById('zone1').checked = true; document.getElementById('zone1').disabled = true; } else if (document.getElementById('zone2').checked == false) { document.getElementById('zone1').checked = true; document.getElementById('zone1').disabled = false; } }); document.getElementById('zone3').addEventListener('click', function () { if (document.getElementById('zone3').checked == true) { document.getElementById('zone2').checked = true; document.getElementById('zone1').checked = true; document.getElementById('zone2').disabled = true; document.getElementById('zone1').disabled = true; } else if (document.getElementById('zone3').checked == false) { document.getElementById('zone1').checked = true; document.getElementById('zone1').disabled = true; document.getElementById('zone2').checked = true; document.getElementById('zone2').disabled = false; } }); // === Brancher toutes les cases zones sur calcGlobal === document.querySelectorAll('input[id^="zone"]').forEach(cb => { cb.addEventListener('change', calcGlobal); }); // Dommages immatériels document.getElementById('checkDomImmat').addEventListener('click', function () { document.getElementById('selectDomImmat').style.display = this.checked ? "block" : "none"; calcGlobal(); }); const inputDomImmat = document.getElementById('inputDomImmat'); if (inputDomImmat) inputDomImmat.addEventListener('input', calcGlobal); // Contenants confiés document.getElementById('checkContConf').addEventListener('click', function () { document.getElementById('selectContConf').style.display = this.checked ? "block" : "none"; calcGlobal(); }); const inputContConf = document.getElementById('inputContConf'); if (inputContConf) inputContConf.addEventListener('input', calcGlobal); // Différence inventaire document.getElementById('checkDiffInv').addEventListener('click', function () { document.getElementById('selectDiffInv').style.display = this.checked ? "block" : "none"; calcGlobal(); }); const inputDiffInv = document.getElementById('inputDiffInv'); if (inputDiffInv) inputDiffInv.addEventListener('input', calcGlobal); // TPPC show/hide + recalc const checkTPPC = document.getElementById('checkTPPC'); const selectTPPC = document.getElementById('selectTPPC'); if (checkTPPC && selectTPPC) { checkTPPC.addEventListener('click', function () { console.log('TPPC checkbox clicked:', this.checked); selectTPPC.style.display = this.checked ? "block" : "none"; if (!this.checked) { // reset des valeurs si décoché const cap = document.getElementById('selTPPCcapital'); const veh = document.getElementById('selTPPCveh'); if (cap) cap.value = ""; if (veh) veh.value = ""; } calcGlobal(); }); } else { console.error('TPPC elements not found!', { checkTPPC, selectTPPC }); } const inputTPPCcap = document.getElementById('selTPPCcapital'); const inputTPPCveh = document.getElementById('selTPPCveh'); if (inputTPPCcap) { inputTPPCcap.addEventListener('input', calcGlobal); console.log('TPPC capital input listener attached'); } if (inputTPPCveh) { inputTPPCveh.addEventListener('input', calcGlobal); console.log('TPPC véhicules input listener attached'); } // Protection juridique document.getElementById('checkPJ').addEventListener('click', calcGlobal); // RCE document.getElementById('checkStationLavage').addEventListener('click', calcGlobal); document.getElementById('checkGarageInterne').addEventListener('click', calcGlobal); document.getElementById('checkCSE').addEventListener('click', calcGlobal); // Dès que l’utilisateur tape ou modifie le champ sinistre, on recalcule const elSin = document.getElementById('sinistre'); if (elSin) { elSin.addEventListener('input', () => { console.log('[event] valeur sinistre modifiée =', elSin.value); calcGlobal(); }); elSin.addEventListener('change', () => { console.log('[event] valeur sinistre validée =', elSin.value); calcGlobal(); }); } document.querySelectorAll('.franchise-card .btn').forEach(btn => { btn.addEventListener('click', () => { const selectedFr = btn.getAttribute('name'); // "250", "400", "mini300" window.franchiseChoisie = selectedFr; console.log('[tarifette] franchise choisie =', selectedFr); document.querySelectorAll('.franchise-card').forEach(c => c.classList.remove('selected')); btn.closest('.franchise-card').classList.add('selected'); }); }); // Event listeners pour les modals d'alerte setupMarchandiseAlerts(); setupActiviteAlerts(); // Event listener pour le bouton Valider du modal tarif commercial document.getElementById('comm-OK').addEventListener('click', handleValidateTarifCom); // Le bouton Annuler ferme automatiquement le modal grâce à la classe "modal-close" } function parsePrefillArray(value) { if (Array.isArray(value)) return value; if (typeof value === 'string') { try { const parsed = JSON.parse(value); return Array.isArray(parsed) ? parsed : []; } catch (error) { return []; } } return []; } function normalizePrefillValue(value) { if (Array.isArray(value)) { return value .map((item) => normalizePrefillValue(item)) .sort((a, b) => String(a).localeCompare(String(b), 'fr')); } if (value && typeof value === 'object') { const normalized = {}; Object.keys(value).sort().forEach((key) => { normalized[key] = normalizePrefillValue(value[key]); }); return normalized; } if (typeof value === 'string') { const trimmed = value.trim(); if (!trimmed) return ''; const numeric = toNumber(trimmed); if (!Number.isNaN(numeric) && trimmed.match(/^-?[\d\s,\.]+$/)) { return numeric; } return trimmed.toLowerCase(); } if (value == null) return null; return value; } function buildProjetPrefillDelta(snapshot, baseline) { if (!snapshot || typeof snapshot !== 'object') return null; if (!baseline || typeof baseline !== 'object') return snapshot; const delta = {}; Object.keys(snapshot).forEach((key) => { const currentValue = normalizePrefillValue(snapshot[key]); const baselineValue = normalizePrefillValue(baseline[key]); if (JSON.stringify(currentValue) !== JSON.stringify(baselineValue)) { delta[key] = snapshot[key]; } }); return Object.keys(delta).length > 0 ? delta : null; } function applyProjetDataToTarif(projetSource) { if (!projetSource || typeof projetSource !== 'object') return; const hasOwn = (key) => Object.prototype.hasOwnProperty.call(projetSource, key); const hasAny = (keys) => keys.some(hasOwn); const normalize = (value) => String(value || '') .normalize('NFD') .replace(/[\u0300-\u036f]/g, '') .toLowerCase() .trim(); const toProjetNumber = (value) => { if (value == null) return null; const raw = String(value).trim(); if (!raw || raw.toLowerCase() === 'nous consulter') return null; const parsed = toNumber(raw); return Number.isFinite(parsed) ? parsed : null; }; const pickNumber = (...keys) => { for (const key of keys) { const parsed = toProjetNumber(projetSource[key]); if (parsed != null) return parsed; } return null; }; const setCheckboxState = (id, checked, dispatchClick) => { const element = document.getElementById(id); if (!element) return; element.checked = Boolean(checked); if (dispatchClick) { element.dispatchEvent(new Event('click')); } }; const setFirstValue = (elements, value) => { if (value == null) return; for (const element of elements) { if (element) { element.value = value; return; } } }; const typeCotisation = projetSource.typeCot || projetSource.typeCotisation; if (typeCotisation) { const radio = document.getElementById(typeCotisation) || document.querySelector(`input[name="cotisation"][value="${typeCotisation}"]`); if (radio) { radio.checked = true; radio.dispatchEvent(new Event('change')); } } if (hasOwn('ca')) { const caField = getElementByIdFlexible('CA') || getElementByIdFlexible('chiffreAffaire'); if (caField) { const rawCA = String(projetSource.ca || '').trim(); caField.value = (!rawCA || rawCA.toLowerCase() === 'nous consulter') ? '' : rawCA; } } const nbVehiculeField = getElementByIdFlexible('nbVehicules') || getElementByIdFlexible('nbrVehicule'); const nbVehFromProjet = toProjetNumber(projetSource.nombreVehicules ?? projetSource.nbVehicules); const nbVehFromTable = Array.isArray(projetSource.designationVehicule) ? projetSource.designationVehicule.length : null; if (nbVehiculeField) { if (nbVehFromProjet != null) { nbVehiculeField.value = Math.max(0, Math.round(nbVehFromProjet)); } else if (nbVehFromTable != null && nbVehFromTable > 0) { nbVehiculeField.value = nbVehFromTable; } } const activityMappings = [ { keys: ['actVoiturier', 'actLoueur'], checkboxId: 'checkVoiturier', capitalKeys: ['valueActVoiturier', 'valueActLoueur'], capitalFields: [ getElementByIdFlexible('capitalVoiturier'), document.querySelector('input[name="selectActVoiturier/Loueur"]') ] }, { keys: ['actMultimodal'], checkboxId: 'checkCommissionnaire', capitalKeys: ['valueActMultimodal'], capitalFields: [ getElementByIdFlexible('capitalCommissionnaire'), document.querySelector('input[name="selectActCommissionnaire de Transport"]'), document.querySelector('input[name="selectActCommissionnaireDeTransport"]') ] }, { keys: ['actDemEntr', 'actDemInterne', 'actDemPar', 'actDemParDom', 'actDemParAdv', 'actGardeMeuble'], checkboxId: 'checkDemenageur', capitalKeys: ['valueActDemEntr', 'valueActDemInterne', 'valueActDemPar', 'valueActDemParDom', 'valueActDemParAdv'], capitalFields: [ getElementByIdFlexible('capitalDemenageur'), document.querySelector('input[name="selectActDéménageur"]'), document.querySelector('input[name="selectActDemenageur"]') ] }, { keys: ['actPrestaLog', 'actEntDep'], checkboxId: 'checkLogistique', capitalKeys: ['valueActPrestaLog', 'valueActEntDep'], capitalFields: [ getElementByIdFlexible('capitalLogistique'), document.querySelector('input[name="selectActLogistique"]') ] }, { keys: ['actAutocariste'], checkboxId: 'checkAutocariste', capitalKeys: ['valueActAutocariste'], capitalFields: [ getElementByIdFlexible('capitalAutocariste'), document.querySelector('input[name="selectActAutocariste"]') ] }, { keys: ['actAutres'], checkboxId: 'checkAutres', capitalKeys: ['valueActAutres'], capitalFields: [ getElementByIdFlexible('capitalAutres'), document.querySelector('input[name="selectActAutres activites"]'), document.querySelector('input[name="selectActAutresActivites"]') ] } ]; activityMappings.forEach((mapping) => { if (!hasAny(mapping.keys)) return; const selected = mapping.keys.some((key) => Boolean(projetSource[key])); setCheckboxState(mapping.checkboxId, selected, true); if (selected) { const capitalValue = pickNumber(...mapping.capitalKeys); setFirstValue(mapping.capitalFields, capitalValue); } }); if (hasAny(['zone1', 'zone2', 'zone3', 'zone4', 'zone5', 'zone6'])) { const zone1 = Boolean(projetSource.zone1); const zone2 = Boolean(projetSource.zone2); const zone3 = Boolean(projetSource.zone3); const zone4 = Boolean(projetSource.zone4); const zone5 = Boolean(projetSource.zone5); const zone6 = Boolean(projetSource.zone6); const zone1El = document.getElementById('zone1'); const zone2El = document.getElementById('zone2'); const zone3El = document.getElementById('zone3'); const zone4El = document.getElementById('zone4'); const zone5El = document.getElementById('zone5'); const zone6El = document.getElementById('zone6'); if (zone1El) { zone1El.checked = zone1 || zone2 || zone3; zone1El.disabled = zone2 || zone3; } if (zone2El) { zone2El.checked = zone2 || zone3; zone2El.disabled = zone3; } if (zone3El) zone3El.checked = zone3; if (zone4El) zone4El.checked = zone4; if (zone5El) zone5El.checked = zone5; if (zone6El) zone6El.checked = zone6; } if (hasOwn('autresRC')) { setCheckboxState('checkRCE', Boolean(projetSource.autresRC), true); } if (hasOwn('pj')) { setCheckboxState('checkPJ', Boolean(projetSource.pj), false); } const activitesComplementairesMap = [ { field: 'activitesVoiturier', container: 'actComplVoiturier/Loueur' }, { field: 'activitesCommissionnaire', container: 'actComplCommissionnaire de Transport' }, { field: 'activitesDemenageur', container: 'actComplDéménageur' }, { field: 'activitesLogistique', container: 'actComplLogistique' } ]; activitesComplementairesMap.forEach(({ field, container }) => { if (!hasOwn(field)) return; const wanted = parsePrefillArray(projetSource[field]); const wantedSet = new Set(wanted.map((item) => String(item).trim())); const checkboxes = document.querySelectorAll(`[name="${container}"] input[type="checkbox"]`); checkboxes.forEach((checkbox) => { const label = checkbox.nextElementSibling ? checkbox.nextElementSibling.textContent.trim() : checkbox.value; checkbox.checked = wantedSet.has(label); }); }); const marchandiseKeys = [ 'marOrdinaire', 'marRoulant', 'marEngins', 'marRoulantDem', 'marMobilerUsag', 'marPerissable', 'marAnimaux', 'marCiterne', 'marBeton', 'marExceptionnels', 'marVrac' ]; if (hasAny(marchandiseKeys)) { const flags = { marOrdinaire: Boolean(projetSource.marOrdinaire), marRoulant: Boolean(projetSource.marRoulant), marEngins: Boolean(projetSource.marEngins), marRoulantDem: Boolean(projetSource.marRoulantDem), marMobilerUsag: Boolean(projetSource.marMobilerUsag), marPerissable: Boolean(projetSource.marPerissable), marAnimaux: Boolean(projetSource.marAnimaux), marCiterne: Boolean(projetSource.marCiterne), marBeton: Boolean(projetSource.marBeton), marExceptionnels: Boolean(projetSource.marExceptionnels), marVrac: Boolean(projetSource.marVrac) }; const shouldSelectMarchandise = (label) => { const text = normalize(label); if (!text) return false; if (flags.marRoulantDem && text.includes('vehicules roulants') && text.includes('demenagement')) return true; if (flags.marRoulant && text.includes('vehicules roulants') && !text.includes('demenagement')) return true; if (flags.marOrdinaire && text.includes('ordinaires')) return true; if (flags.marEngins && text.includes('engins de chantier')) return true; if (flags.marMobilerUsag && text.includes('mobiliers')) return true; if (flags.marPerissable && text.includes('perissables')) return true; if (flags.marAnimaux && text.includes('animaux vivants')) return true; if (flags.marCiterne && text.includes('citerne')) return true; if (flags.marBeton && text.includes('beton')) return true; if (flags.marExceptionnels && text.includes('exceptionnels')) return true; if (flags.marVrac && (text.includes('benne') || text.includes('vrac'))) return true; return false; }; [ 'marVoiturier/Loueur', 'marCommissionnaire de Transport', 'marDéménageur', 'marLogistique', 'marAutocariste', 'marAutres activites' ].forEach((container) => { const checkboxes = document.querySelectorAll(`[name="${container}"] input[type="checkbox"]`); checkboxes.forEach((checkbox) => { const label = checkbox.nextElementSibling ? checkbox.nextElementSibling.textContent : checkbox.value; checkbox.checked = shouldSelectMarchandise(label); }); }); } } // Peupler le formulaire avec les données function populateFormData() { //Poupulate select historique if (!contrat.historique) { document.getElementById('historiqueDiv').style.display = "none"; } else { document.getElementById('historiqueDiv').style.display = "block"; const idSelect = document.getElementById('idSelect'); contrat.historique.forEach(function (item) { var option = document.createElement('option'); option.value = item.id; option.textContent = item.type + " " + item.produit + " - " + item.date + " - " + item.heure; if (item.nom != undefined && item.prenom != undefined) { option.textContent += " - " + item.nom + " " + item.prenom; } idSelect.appendChild(option); }); M.FormSelect.init(idSelect); } // Helpers défauts (au cas où aucune donnée côté RC) const ensureDefaultActComplVoiturier = () => { const container = document.querySelector('[name="actComplVoiturier/Loueur"]'); if (!container) return; const checkboxes = Array.from(container.querySelectorAll('input[type="checkbox"]')); const anyChecked = checkboxes.some(cb => cb.checked); if (!anyChecked) { checkboxes.forEach((cb, idx) => { if (idx <= 1) cb.checked = true; // Voiturier + Loueur }); } }; const ensureDefaultMarchandisesVoiturier = () => { const container = document.querySelector('[name="marVoiturier/Loueur"]'); if (!container) return; const checkboxes = Array.from(container.querySelectorAll('input[type="checkbox"]')); const anyChecked = checkboxes.some(cb => cb.checked); if (!anyChecked && checkboxes.length > 0) { checkboxes[0].checked = true; // Marchandises ordinaires par défaut } }; // ===== PRÉ-REMPLIR LE FORMULAIRE AVEC LES DONNÉES RC DE LA BASE ===== if (!rc) { console.log('Aucune donnée RC à pré-remplir'); ensureDefaultActComplVoiturier(); ensureDefaultMarchandisesVoiturier(); return; } console.log('Pre-remplissage du formulaire tarif avec les donnees RC:', rc); // Type de cotisation if (rc.typeCotisation) { const radioBtn = document.getElementById(rc.typeCotisation); if (radioBtn) { radioBtn.checked = true; radioBtn.dispatchEvent(new Event('change')); } } // Chiffre d'affaires et nombre de véhicules const caField = getElementByIdFlexible('CA') || getElementByIdFlexible('chiffreAffaire'); if (rc.chiffreAffaires && caField) caField.value = rc.chiffreAffaires; const nbVehiculeField = getElementByIdFlexible('nbVehicules') || getElementByIdFlexible('nbrVehicule'); if (rc.nombreVehicules && nbVehiculeField) nbVehiculeField.value = rc.nombreVehicules; // Sinistralité (depuis tarifRC) if (tarif && tarif.sinistre) { document.getElementById('sinistre').value = tarif.sinistre; } // Checkbox RCE if (rc.checkRCE !== undefined) { document.getElementById('checkRCE').checked = rc.checkRCE; document.getElementById('checkRCE').dispatchEvent(new Event('click')); } // Activité Voiturier/Loueur if (rc.checkVoiturier !== undefined) { const checkVoiturierEl = getElementByIdFlexible('checkVoiturier'); if (checkVoiturierEl) { checkVoiturierEl.checked = rc.checkVoiturier; const capitalVoiturierEl = getElementByIdFlexible('capitalVoiturier') || document.querySelector('input[name="selectActVoiturier/Loueur"]'); if (rc.capitalVoiturier && capitalVoiturierEl) capitalVoiturierEl.value = rc.capitalVoiturier; checkVoiturierEl.dispatchEvent(new Event('click')); } } // Activité Commissionnaire if (rc.checkCommissionnaire !== undefined) { const checkCommissionnaireEl = getElementByIdFlexible('checkCommissionnaire'); if (checkCommissionnaireEl) { checkCommissionnaireEl.checked = rc.checkCommissionnaire; const capitalCommissionnaireEl = getElementByIdFlexible('capitalCommissionnaire') || document.querySelector('input[name="selectActCommissionnaire de Transport"]') || document.querySelector('input[name="selectActCommissionnaireDeTransport"]'); if (rc.capitalCommissionnaire && capitalCommissionnaireEl) capitalCommissionnaireEl.value = rc.capitalCommissionnaire; checkCommissionnaireEl.dispatchEvent(new Event('click')); } } // Activité Déménageur if (rc.checkDemenageur !== undefined) { const checkDemenageurEl = getElementByIdFlexible('checkDemenageur'); if (checkDemenageurEl) { checkDemenageurEl.checked = rc.checkDemenageur; const capitalDemenageurEl = getElementByIdFlexible('capitalDemenageur') || document.querySelector('input[name="selectActDéménageur"]') || document.querySelector('input[name="selectActDemenageur"]'); if (rc.capitalDemenageur && capitalDemenageurEl) capitalDemenageurEl.value = rc.capitalDemenageur; checkDemenageurEl.dispatchEvent(new Event('click')); } } // Activité Logistique if (rc.checkLogistique !== undefined) { const checkLogistiqueEl = getElementByIdFlexible('checkLogistique'); if (checkLogistiqueEl) { checkLogistiqueEl.checked = rc.checkLogistique; const capitalLogistiqueEl = getElementByIdFlexible('capitalLogistique') || document.querySelector('input[name="selectActLogistique"]'); if (rc.capitalLogistique && capitalLogistiqueEl) capitalLogistiqueEl.value = rc.capitalLogistique; checkLogistiqueEl.dispatchEvent(new Event('click')); } } // Activité Autocariste if (rc.checkAutocariste !== undefined) { const checkAutocaristeEl = getElementByIdFlexible('checkAutocariste'); if (checkAutocaristeEl) { checkAutocaristeEl.checked = rc.checkAutocariste; const capitalAutocaristeEl = getElementByIdFlexible('capitalAutocariste') || document.querySelector('input[name="selectActAutocariste"]'); if (rc.capitalAutocariste && capitalAutocaristeEl) capitalAutocaristeEl.value = rc.capitalAutocariste; checkAutocaristeEl.dispatchEvent(new Event('click')); } } // Activité Autres if (rc.checkAutres !== undefined) { const checkAutresEl = getElementByIdFlexible('checkAutres'); if (checkAutresEl) { checkAutresEl.checked = rc.checkAutres; const capitalAutresEl = getElementByIdFlexible('capitalAutres') || document.querySelector('input[name="selectActAutres activites"]') || document.querySelector('input[name="selectActAutresActivites"]'); if (rc.capitalAutres && capitalAutresEl) capitalAutresEl.value = rc.capitalAutres; checkAutresEl.dispatchEvent(new Event('click')); } } // Zones géographiques if (rc.zone1) document.getElementById('zone1').checked = rc.zone1; if (rc.zone2) document.getElementById('zone2').checked = rc.zone2; if (rc.zone3) document.getElementById('zone3').checked = rc.zone3; if (rc.zone4) document.getElementById('zone4').checked = rc.zone4; if (rc.zone5) document.getElementById('zone5').checked = rc.zone5; if (rc.zone6) document.getElementById('zone6').checked = rc.zone6; // Pré-remplir les activités complémentaires (JSON) pour TOUTES les activités const activitiesTypes = [ { field: 'actComplVoiturier', name: 'actComplVoiturier/Loueur' }, { field: 'actComplCommissionnaire', name: 'actComplCommissionnaire de Transport' }, { field: 'actComplDemenageur', name: 'actComplDéménageur' }, { field: 'actComplLogistique', name: 'actComplLogistique' } ]; console.log('Pre-remplissage des activites complementaires...'); activitiesTypes.forEach(({ field, name }) => { // D'abord DÉCOCHER toutes les checkboxes de cette activité const allCheckboxes = document.querySelectorAll(`[name="${name}"] input[type="checkbox"]`); allCheckboxes.forEach(cb => cb.checked = false); if (rc[field]) { try { // PocketBase parse automatiquement les champs JSON, donc rc[field] est déjà un array const activites = Array.isArray(rc[field]) ? rc[field] : JSON.parse(rc[field]); console.log(` ${field}:`, activites); activites.forEach(actText => { // Chercher la checkbox dont le span adjacent contient ce texte allCheckboxes.forEach(cb => { const label = cb.nextElementSibling?.textContent.trim(); if (label === actText) { cb.checked = true; console.log(` Coche: ${actText}`); } }); }); } catch (e) { console.error(`Erreur parsing ${field}:`, e); } } }); // Pré-remplir les marchandises (JSON) pour TOUTES les activités const marchandisesTypes = [ { field: 'marchandisesVoiturier', name: 'marVoiturier/Loueur' }, { field: 'marchandisesCommissionnaire', name: 'marCommissionnaire de Transport' }, { field: 'marchandisesDemenageur', name: 'marDéménageur' }, { field: 'marchandisesLogistique', name: 'marLogistique' }, { field: 'marchandisesAutocariste', name: 'marAutocariste' }, { field: 'marchandisesAutres', name: 'marAutres activites' } ]; console.log('Pre-remplissage des marchandises...'); marchandisesTypes.forEach(({ field, name }) => { // D'abord DÉCOCHER toutes les checkboxes de cette marchandise const allCheckboxes = document.querySelectorAll(`[name="${name}"] input[type="checkbox"]`); allCheckboxes.forEach(cb => cb.checked = false); if (rc[field]) { try { // PocketBase parse automatiquement les champs JSON, donc rc[field] est déjà un array const marchandises = Array.isArray(rc[field]) ? rc[field] : JSON.parse(rc[field]); console.log(` ${field}:`, marchandises); marchandises.forEach(marText => { // Chercher la checkbox dont le span adjacent contient ce texte allCheckboxes.forEach(cb => { const label = cb.nextElementSibling?.textContent.trim(); if (label === marText) { cb.checked = true; console.log(` Coche: ${marText}`); } }); }); } catch (e) { console.error(`Erreur parsing ${field}:`, e); } } }); // Valeurs par défaut si aucune marchandise/activité compl. remontée ensureDefaultActComplVoiturier(); ensureDefaultMarchandisesVoiturier(); // Pré-remplir les pourcentages (depuis tarifRC) // Priorité au tarif persistant; ne pas retomber sur rc pour ces champs. const tarifSource = tarif || rc?.["@expand"]?.tarifRC || rc?.tarifRC || null; // Fallback ultime : données de session (hook RC orchestrator) si tout est vide let sessionTarifData = null; try { const stored = sessionStorage.getItem('rc_tarif_validated_data'); sessionTarifData = stored ? JSON.parse(stored) : null; } catch (e) { sessionTarifData = null; } if (tarifSource || sessionTarifData) { const pctVoiturierEl = getElementByIdFlexible('pourcent_voiturier') || getElementByIdFlexible('pourcentVoiturier/Loueur'); if (tarifSource?.pourcentageVoiturier && pctVoiturierEl) pctVoiturierEl.value = tarifSource.pourcentageVoiturier; const pctCommissionnaireEl = getElementByIdFlexible('pourcent_commissionnaire') || getElementByIdFlexible('pourcentCommissionnaire de Transport') || getElementByIdFlexible('pourcentCommissionnaireDeTransport'); if (tarifSource?.pourcentageCommissionnaire && pctCommissionnaireEl) pctCommissionnaireEl.value = tarifSource.pourcentageCommissionnaire; const pctDemenageurEl = getElementByIdFlexible('pourcent_demenageur') || getElementByIdFlexible('pourcentDéménageur') || getElementByIdFlexible('pourcentDemenageur'); if (tarifSource?.pourcentageDemenageur && pctDemenageurEl) pctDemenageurEl.value = tarifSource.pourcentageDemenageur; const pctLogistiqueEl = getElementByIdFlexible('pourcent_logistique') || getElementByIdFlexible('pourcentLogistique'); if (tarifSource?.pourcentageLogistique && pctLogistiqueEl) pctLogistiqueEl.value = tarifSource.pourcentageLogistique; const pctAutocaristeEl = getElementByIdFlexible('pourcent_autocariste') || getElementByIdFlexible('pourcentAutocariste'); if (tarifSource?.pourcentageAutocariste && pctAutocaristeEl) pctAutocaristeEl.value = tarifSource.pourcentageAutocariste; const pctAutresEl = getElementByIdFlexible('pourcent_autres') || getElementByIdFlexible('pourcentAutres activites') || getElementByIdFlexible('pourcentAutresActivites'); if (tarifSource?.pourcentageAutres && pctAutresEl) pctAutresEl.value = tarifSource.pourcentageAutres; // ===== ENGAGEMENTS COMPLÉMENTAIRES (depuis tarifRC) ===== // Dommages immatériels const engagementSrc = tarifSource || sessionTarifData?.engagementsComplementaires; if (tarifSource?.checkDomImmat || engagementSrc?.domicileImmatriculation?.checked) { document.getElementById('checkDomImmat').checked = true; document.getElementById('selectDomImmat').style.display = 'block'; const cap = tarifSource?.capitalDomImmat ?? engagementSrc?.domicileImmatriculation?.capital; if (cap) { document.getElementById('inputDomImmat').value = cap; } } // Contenants confiés if (tarifSource?.checkContConf || engagementSrc?.contenantConfie?.checked) { document.getElementById('checkContConf').checked = true; document.getElementById('selectContConf').style.display = 'block'; const cap = tarifSource?.capitalContConf ?? engagementSrc?.contenantConfie?.capital; if (cap) { document.getElementById('inputContConf').value = cap; } } // Différence inventaire if (tarifSource?.checkDiffInv || engagementSrc?.differenceInventaire?.checked) { document.getElementById('checkDiffInv').checked = true; document.getElementById('selectDiffInv').style.display = 'block'; const cap = tarifSource?.capitalDiffInv ?? engagementSrc?.differenceInventaire?.capital; if (cap) { document.getElementById('inputDiffInv').value = cap; } } // ===== GARANTIES ADDITIONNELLES (depuis tarifRC) ===== // TPPC const garantiesSrc = tarifSource || sessionTarifData?.garantiesAdditionnelles; if (tarifSource?.checkTPPC || garantiesSrc?.tppc?.checked) { document.getElementById('checkTPPC').checked = true; document.getElementById('selectTPPC').style.display = 'block'; const cap = tarifSource?.capitalTPPC ?? garantiesSrc?.tppc?.capital; if (cap) { document.getElementById('selTPPCcapital').value = cap; } const veh = tarifSource?.vehiculesTPPC ?? garantiesSrc?.tppc?.vehicules; if (veh) { document.getElementById('selTPPCveh').value = veh; } } // Protection juridique if (tarifSource?.checkPJ || garantiesSrc?.pj) { document.getElementById('checkPJ').checked = true; } // RCE - Station lavage if (tarifSource?.checkStationLavage || garantiesSrc?.stationLavage) { document.getElementById('checkStationLavage').checked = true; } // RCE - Garage interne if (tarifSource?.checkGarageInterne || garantiesSrc?.garageInterne) { document.getElementById('checkGarageInterne').checked = true; } // RCE - CSE if (tarifSource?.checkCSE || garantiesSrc?.cse) { document.getElementById('checkCSE').checked = true; } // ===== FRANCHISE CHOISIE ===== if (tarifSource?.franchiseChoisie) { window.franchiseChoisie = tarifSource.franchiseChoisie; // Mettre en surbrillance la carte sélectionnée const selectedButton = document.querySelector(`.franchise-card button[name="${tarifSource.franchiseChoisie}"]`); if (selectedButton) { selectedButton.closest('.franchise-card').classList.add('selected'); } } } // Pré-remplissage Projet -> Tarif // - si snapshot session: appliquer seulement les champs modifies sur Projet // - sinon: utiliser Projet uniquement si on n'a pas de tarif persistant let sessionProjetData = null; try { const storedProjetData = sessionStorage.getItem('rc_projet_data'); sessionProjetData = storedProjetData ? JSON.parse(storedProjetData) : null; } catch (error) { sessionProjetData = null; } let projetPrefillSource = null; if (sessionProjetData) { projetPrefillSource = buildProjetPrefillDelta(sessionProjetData, projet); } else if (!tarifSource && !sessionTarifData && projet) { projetPrefillSource = projet; } if (projetPrefillSource) { applyProjetDataToTarif(projetPrefillSource); } if (sessionProjetData) { sessionStorage.removeItem('rc_projet_data'); } console.log('Formulaire tarif pre-rempli avec succes'); syncRCFloatingLabels(); // Recalculer après pré-remplissage setTimeout(() => { calcGlobal(); syncRCFloatingLabels(); }, 500); } function calcModCA(caRaw){ const ca = Number(String(caRaw || '').replace(/\s/g,'').replace(',','.')) || 0; const thresholds = Object.keys(modRCCA).map(n => Number(n)).sort((a,b)=>a-b); for (const t of thresholds) if (ca < t) return modRCCA[t]; return modRCCA[thresholds.at(-1)] ?? 1; } function calcModMarchandises(data, activityName, type, cot) { let m = 1; // On cible le bloc correspondant à l'activité const container = document.querySelector(`[name="mar${activityName}"]`); if (!container) return 1; // On récupère toutes les cases à cocher de ce bloc const checkboxes = container.querySelectorAll('input[type="checkbox"]'); checkboxes.forEach(cb => { if (cb.checked) { // Le libellé affiché est dans le juste après l'input const label = cb.nextElementSibling?.innerText.trim(); // On simplifie le libellé pour matcher les clés du JSON let key = null; if (label.includes("ordinaires")) key = "Marchandises ordinaires"; else if (label.includes("Véhicules roulants")) key = "Véhicules roulants"; else if (label.includes("Engins de chantier")) key = "Engins de chantier"; else if (label.includes("Mobiliers")) key = "Mobiliers en déménagement"; else if (label.includes("périssables")) key = "Marchandises périssables"; else if (label.includes("citerne")) key = "Marchandises en citerne"; else if (label.includes("Animaux")) key = "Animaux vivants"; else if (label.includes("benne")) key = "Marchandises en benne"; else if (label.includes("béton")) key = "Transport de béton"; if (key) { const v = data?.[cot]?.[activityName]?.[key]?.[`mod${type}`]; if (typeof v === "number") { m *= v; } else { console.warn(`Pas de mod trouvé pour activité=${activityName}, clé=${key}, type=${type}, cot=${cot}`); } } else { console.warn(`Libellé non reconnu: "${label}"`); } } }); return m; } function getZoneMods(data) { let mRCC = 1; let mRCE = 1; // On parcourt toutes les cases à cocher de zones for (let i = 1; i <= 6; i++) { const cb = document.getElementById(`zone${i}`); if (cb && cb.checked) { // Récupérer le libellé affiché const label = document.getElementById(`zone${i}-text`).innerText.trim(); // Trouver la clé correspondante dans le JSON let key = null; if (label.includes("France Métropolitaine")) key = "France Métropolitaine et pays limitrophes"; else if (label.includes("Union Européenne")) key = "Union Européenne"; else if (label.includes("Autres pays européens")) key = "Autres pays européens sauf Russie et Ukraine (y compris UK et Norvège)"; else if (label.includes("Maghreb") || label.includes("Amérique du Nord")) key = "Pays du Maghreb et Amérique du Nord ( USA / Canada / Mexique )"; else if (label.includes("Amérique Centrale") || label.includes("Asie") || label.includes("Océanie")) key = "Amérique Centrale et Sud / Caraïbes, Asie et Océanie"; else if (label.includes("Afrique") || label.includes("Moyen Orient")) key = "Afrique Hors Maghreb / Proche Orient / Moyen Orient"; if (key && data[key]) { const zone = data[key]; if (typeof zone.modRCC === "number") mRCC = Math.max(mRCC, zone.modRCC); if (typeof zone.modRCE === "number") mRCE = Math.max(mRCE, zone.modRCE); } } } return { mRCC, mRCE }; } function calcModActCompl(data, activityName, type, cot) { let m = 1; const current = getSelectedActivities().find(a => a.typeActivite === activityName); if (!current) return 1; const table = data?.[cot]?.[activityName] || {}; (current.listActComplChecked||[]).forEach(name => { const v = table[name]?.[`mod${type}`]; if (typeof v === "number") m *= v; }); return m; } function calcEngagCompl(data, primeRCC) { let prime = primeRCC; let surPrime = 0; let modDommImmat = 1; let infos = []; function writeToCard(inputId, message) { const inputEl = document.getElementById(inputId); if (!inputEl) return; const cardContent = inputEl.closest('.card-content'); if (!cardContent) return; // Supprime l’ancien wrapper pour cet input cardContent.querySelectorAll(`div.chip[data-for="${inputId}"]`).forEach(el => el.remove()); if (!message) return; // Wrapper const wrapper = document.createElement("div"); wrapper.classList.add("chip"); // uniquement "chip" wrapper.dataset.for = inputId; // Message const p = document.createElement("p"); p.dataset.for = inputId; p.classList.add("helper-text", "red-text"); p.style.display = "block"; // override helper-text { display: none; } p.textContent = message; wrapper.appendChild(p); cardContent.appendChild(wrapper); } // === Contenants confiés === const checkContConf = document.getElementById('checkContConf'); const inputContConf = document.getElementById('inputContConf'); if (checkContConf?.checked) { const val = toNumber(inputContConf?.value); if (val > 0) { const tranches = Object.keys(data.modRCC["Contenants confiés"]).map(Number).sort((a,b)=>a-b); const tranche = findClosestTranche(val, tranches); const add = data.modRCC["Contenants confiés"][tranche]; if (typeof add === "number") { surPrime += add; const msg = `Prime RCC : +${add.toLocaleString('fr-FR',{style:'currency',currency:'EUR'})}`; //infos.push(msg); pour ajouter le message dans la div "Prime RCC" writeToCard('inputContConf', msg); } } } else { writeToCard('inputContConf', null); // supprime si décoché } // === Différence inventaire === const checkDiffInv = document.getElementById('checkDiffInv'); const inputDiffInv = document.getElementById('inputDiffInv'); if (checkDiffInv?.checked) { const val = toNumber(inputDiffInv?.value); if (val > 0) { const tranches = Object.keys(data.modRCC["Différence inventaire"]).map(Number).sort((a,b)=>a-b); const tranche = findClosestTranche(val, tranches); const add = data.modRCC["Différence inventaire"][tranche]; if (typeof add === "number") { surPrime += add; const msg = `Prime RCC : +${add.toLocaleString('fr-FR',{style:'currency',currency:'EUR'})}`; //infos.push(msg); pour ajouter le message dans la div "Prime RCC" writeToCard('inputDiffInv', msg); } } } else { writeToCard('inputDiffInv', null); } // === Dommages immatériels === const checkDomImmat = document.getElementById('checkDomImmat'); const inputDomImmat = document.getElementById('inputDomImmat'); if (checkDomImmat?.checked) { const val = toNumber(inputDomImmat?.value); if (val > 0) { const tranches = Object.keys(data.modRCC["Dommages immatériels"]).map(Number).sort((a,b)=>a-b); const tranche = findClosestTranche(val, tranches); const mod = data.modRCC["Dommages immatériels"][tranche]; if (typeof mod === "number") { modDommImmat = mod; const msg = `Mod Taux RCC : ×${mod}`; //infos.push(msg); pour ajouter le message dans la div "Prime RCC" writeToCard('inputDomImmat', msg); } } } else { writeToCard('inputDomImmat', null); } // === Totaux === prime += surPrime; prime *= modDommImmat; return { prime, surPrime, infos }; } // Fonction helper : trouver la tranche la plus proche function findClosestTranche(val, tranches) { if (val <= tranches[0]) return tranches[0]; if (val >= tranches[tranches.length - 1]) return tranches[tranches.length - 1]; for (let i = 0; i < tranches.length - 1; i++) { if (val >= tranches[i] && val < tranches[i + 1]) { // Prendre la tranche inférieure ou égale return tranches[i]; } } return tranches[tranches.length - 1]; } // Fonction pour afficher un avertissement visuel de dépassement % function showPercentageWarning(excess) { // Créer ou récupérer le div d'avertissement let warningDiv = document.getElementById('percentageWarning'); if (!warningDiv) { warningDiv = document.createElement('div'); warningDiv.id = 'percentageWarning'; warningDiv.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #f44336; color: white; padding: 15px 25px; border-radius: 5px; box-shadow: 0 4px 6px rgba(0,0,0,0.3); z-index: 9999; font-weight: bold; animation: slideIn 0.3s ease; `; document.body.appendChild(warningDiv); } warningDiv.innerHTML = `Vous dépassez 100% (vous dépassiez de ${excess.toFixed(1)}%)`; warningDiv.style.display = 'block'; // Masquer après 3 secondes setTimeout(() => { warningDiv.style.display = 'none'; }, 3000); } // Fonction pour masquer toutes les primes (quand % invalide) function hideAllPrimes() { const primeElements = [ 'primeChapActRCC', 'primeChapActRCE', 'primeChapActComplRCC', 'primeChapActComplRCE', 'primeChapMarchRCC', 'primeChapMarchRCE', 'primeChapZonesRCC', 'primeChapZonesRCE', 'primeEngValue', 'primeChapGarAddRCC', 'primeChapGarAddRCE', 'priceFr250', 'priceFr400', 'priceFr2000' ]; primeElements.forEach(id => { const el = document.getElementById(id); if (el) { el.innerHTML = ' % invalide (> 100%)'; } }); } // Fonction pour afficher toutes les primes (quand % valide) function showAllPrimes() { } // Fonction pour mettre à jour l'indicateur visuel du total des pourcentages function updatePercentageIndicator(total) { const indicator = document.getElementById('pourcentageTotal'); if (!indicator) return; const displayTotal = total.toFixed(1); indicator.textContent = `Total : ${displayTotal}%`; // Changer la couleur selon le total if (total > 100) { // ROUGE : Dépassement - INVALIDE indicator.style.background = '#f44336'; indicator.style.color = 'white'; indicator.style.border = '3px solid #c62828'; indicator.style.animation = 'pulse 1s infinite'; } else if (total === 100 || Math.abs(total - 100) < 0.1) { // VERT : Parfait - 100% indicator.style.background = '#4caf50'; indicator.style.color = 'white'; indicator.style.border = '3px solid #2e7d32'; indicator.style.animation = 'none'; } else if (total >= 95) { // ORANGE : Proche de 100% indicator.style.background = '#ff9800'; indicator.style.color = 'white'; indicator.style.border = '3px solid #ef6c00'; indicator.style.animation = 'none'; } else { // BLEU : En cours de saisie indicator.style.background = 'white'; indicator.style.color = 'darkblue'; indicator.style.border = '2px solid darkblue'; indicator.style.animation = 'none'; } } function calcGarAdd(data, primeRCC, primeRCE) { let primeRCCres = Number(primeRCC) || 0; let primeRCEres = Number(primeRCE) || 0; let surPrimeRCE = 0; const infosRCC = []; const infosRCE = []; function writeToCard(inputId, message) { const inputEl = document.getElementById(inputId); if (!inputEl) return; const cardContent = inputEl.closest('.card-content'); if (!cardContent) return; // Supprime l’ancien wrapper pour cet input cardContent.querySelectorAll(`div.chip[data-for="${inputId}"]`).forEach(el => el.remove()); if (!message) return; // Wrapper const wrapper = document.createElement("div"); wrapper.classList.add("chip"); wrapper.dataset.for = inputId; // Style : occupe sa propre ligne mais largeur adaptée au contenu wrapper.style.display = "table"; // se comporte comme un bloc centré sur son contenu wrapper.style.margin = "8px auto"; // centre horizontalement // Message const p = document.createElement("p"); p.dataset.for = inputId; p.classList.add("helper-text", "red-text"); p.style.display = "block"; // override helper-text { display: none; } p.textContent = message; wrapper.appendChild(p); cardContent.appendChild(wrapper); } // === Station de lavage (RCE forfait) === const checkStation = document.getElementById('checkStationLavage'); if (checkStation?.checked) { const add = data.modRCE["Station de lavage"]; if (typeof add === "number") { surPrimeRCE += add; const msg = `Prime RCE : +${add.toLocaleString('fr-FR', { style:'currency', currency:'EUR' })}`; //infosRCE.push(msg); writeToCard('checkStationLavage', msg); } } else { writeToCard('checkStationLavage', null); } // === Garage interne (RCE forfait) === const checkGarage = document.getElementById('checkGarageInterne'); if (checkGarage?.checked) { const add = data.modRCE["Garage interne"]; if (typeof add === "number") { surPrimeRCE += add; const msg = `Prime RCE : +${add.toLocaleString('fr-FR', { style:'currency', currency:'EUR' })}`; //infosRCE.push(msg); writeToCard('checkGarageInterne', msg); } } else { writeToCard('checkGarageInterne', null); } // === Comité Social et Economique (RCE forfait) === const checkCSE = document.getElementById('checkCSE'); if (checkCSE?.checked) { const add = data.modRCE["Comité Social et Economique"]; if (typeof add === "number") { surPrimeRCE += add; const msg = `Prime RCE : +${add.toLocaleString('fr-FR', { style:'currency', currency:'EUR' })}`; //infosRCE.push(msg); writeToCard('checkCSE', msg); } } else { writeToCard('checkCSE', null); } // === Addition des garanties RCE === primeRCEres += surPrimeRCE; return { primeRCC: primeRCCres, primeRCE: primeRCEres, surPrimeRCE, infosRCC, infosRCE }; } function calcSinistre(data, primeRCC, primeRCE) { console.log('[calcSinistre] start', { primeRCC, primeRCE }); let primeRCCres = Number(primeRCC) || 0; let primeRCEres = Number(primeRCE) || 0; let mod = 1; const elSin = document.getElementById('sinistre'); const elCA = document.getElementById('chiffreAffaire'); const chip = document.getElementById('modSinistre'); if (!elSin || !elCA) { console.warn('[calcSinistre] Missing #sinistre or #chiffreAffaire'); } else { const sinistreVal = toNumber(elSin.value); const CA = toNumber(elCA.value); console.log('[calcSinistre] inputs', { sinistreVal, CA }); if (CA > 0 && sinistreVal >= 0) { const ratio = sinistreVal / CA; let key = 0.4; if (ratio <= 0.4) key = 0.4; else if (ratio <= 0.7) key = 0.7; else key = 1; mod = data[key]; console.log('[calcSinistre] ratio=', ratio.toFixed(3), 'key=', key, 'mod=', mod); } else { console.log('[calcSinistre] CA <= 0 or sinistre < 0 -> mod=1'); } } // Apply modulation to both RCC and RCE primeRCCres *= mod; primeRCEres *= mod; if (chip) { chip.style.display = 'block'; chip.innerHTML = 'Modulation sinistre :
x ' + mod.toLocaleString('fr-FR', { minimumFractionDigits: 2 }); } else { console.warn('[calcSinistre] #modSinistre not found'); } console.log('[calcSinistre] result', { primeRCC: primeRCCres, primeRCE: primeRCEres, mod }); return { primeRCC: primeRCCres, primeRCE: primeRCEres, mod }; } // --- Fonctions utilitaires --- function getMaxMiniRCC(activites, cot) { let maxMini = 0; activites.forEach(act => { // Récupérer l'input/select pour cette activité const inputEl = document.querySelector(`input[name="selectAct${act}"], select[name="selectAct${act}"]`); if (!inputEl) return; const capitalSaisi = toNumber(inputEl.value); if (capitalSaisi <= 0) return; // Déterminer le type de cotisation const cotType = cot === 'revisable' ? 'revisable' : 'forfaitaire'; // Trouver la tranche la plus proche const grilleAct = modRCPrimeMini?.[cotType]?.[act]; if (grilleAct) { const tranches = Object.keys(grilleAct).map(Number).sort((a,b)=>a-b); const tranche = findClosestTranche(capitalSaisi, tranches); const miniData = grilleAct[tranche]; if (miniData && typeof miniData.miniRCC === 'number') { maxMini = Math.max(maxMini, miniData.miniRCC); } } }); return maxMini; } function getMaxMiniRCE(activites, cot) { let maxMini = 0; activites.forEach(act => { // Récupérer l'input/select pour cette activité const inputEl = document.querySelector(`input[name="selectAct${act}"], select[name="selectAct${act}"]`); if (!inputEl) return; const capitalSaisi = toNumber(inputEl.value); if (capitalSaisi <= 0) return; // Déterminer le type de cotisation const cotType = cot === 'revisable' ? 'revisable' : 'forfaitaire'; // Trouver la tranche la plus proche const grilleAct = modRCPrimeMini?.[cotType]?.[act]; if (grilleAct) { const tranches = Object.keys(grilleAct).map(Number).sort((a,b)=>a-b); const tranche = findClosestTranche(capitalSaisi, tranches); const miniData = grilleAct[tranche]; if (miniData && typeof miniData.miniRCE === 'number') { maxMini = Math.max(maxMini, miniData.miniRCE); } } }); return maxMini; } function calcTarifRCC({ CA, primeRCCbase, primeRCEbase, capitalTPPC, nbVehicules, coefTPPC, franchiseCoef, primeMini }) { // --- Prime RCC avec franchise --- let primeFranchiseRCC = Number(primeRCCbase) * Number(franchiseCoef.modRCC || 1); let primeFranchiseRCE = Number(primeRCEbase) * Number(franchiseCoef.modRCE || 1); // --- Ajout TPPC APRÈS franchise --- const primeTPPC = capitalTPPC * nbVehicules * coefTPPC; primeFranchiseRCC = primeFranchiseRCC + primeTPPC; // --- Calcul taux AVANT minima --- let tauxRCC = CA > 0 ? (primeFranchiseRCC * 100 / CA).toFixed(2) : "0.00"; let tauxRCE = CA > 0 ? (primeFranchiseRCE * 100 / CA).toFixed(2) : "0.00"; // --- Application des minima --- let isMiniRCC = false; let isMiniRCE = false; if (primeFranchiseRCC < primeMini[0]) { tauxRCC = (primeFranchiseRCC * 100 / CA).toFixed(2); // Garder le taux avant mini primeFranchiseRCC = primeMini[0]; isMiniRCC = true; } if (primeFranchiseRCE < primeMini[1]) { tauxRCE = (primeFranchiseRCE * 100 / CA).toFixed(2); // Garder le taux avant mini primeFranchiseRCE = primeMini[1]; isMiniRCE = true; } // --- Recalculer taux si PAS mini --- if (!isMiniRCC && CA > 0) { tauxRCC = (primeFranchiseRCC * 100 / CA).toFixed(2); } if (!isMiniRCE && CA > 0) { tauxRCE = (primeFranchiseRCE * 100 / CA).toFixed(2); } // --- Prime totale --- const primeTotale = primeFranchiseRCC + primeFranchiseRCE; // --- Taux global --- const tauxGlobal = CA > 0 ? ((primeTotale * 100 / CA).toFixed(2)) : "0.00"; return { primeRCC: primeFranchiseRCC.toString(), tauxRCC, primeRCE: primeFranchiseRCE.toString(), tauxRCE, primeTotale: primeTotale.toString(), tauxGlobal, isMiniRCC, isMiniRCE }; } function calcTarifettes(primePJ, CA, activites, capitalTPPC, nbVehicules, coefTPPC, primeRCCbase, primeRCEbase) { const cards = [ { key: "250", priceId: "priceFr250", rccId: "rccFr250", rceId: "rceFr250", pjId: "pjFr250", txRccId: "tauxRccFr250", txRceId: "tauxRceFr250", txGlobId: "tauxGlobalFr250" }, { key: "400", priceId: "priceFr400", rccId: "rccFr400", rceId: "rceFr400", pjId: "pjFr400", txRccId: "tauxRccFr400", txRceId: "tauxRceFr400", txGlobId: "tauxGlobalFr400" }, { key: "10% avec mini 300 € et maxi 2000", priceId: "priceFr2000", rccId: "rccFr2000", rceId: "rceFr2000", pjId: "pjFr2000", txRccId: "tauxRccFr2000", txRceId: "tauxRceFr2000", txGlobId: "tauxGlobalFr2000" }, ]; // Déterminer le type de cotisation const cot = document.querySelector('input[name="cotisation"]:checked')?.value || 'revisable'; const checkRCE = document.getElementById('checkRCE')?.checked || false; const checkPJ = document.getElementById('checkPJ')?.checked || false; const primeMini = [ getMaxMiniRCC(activites, cot), getMaxMiniRCE(activites, cot) ]; cards.forEach(card => { const mods = modRCFranchise[card.key] || { modRCC: 1, modRCE: 1 }; const result = calcTarifRCC({ CA, primeRCCbase, primeRCEbase, capitalTPPC, nbVehicules, coefTPPC, franchiseCoef: mods, primeMini }); // Calculer la PJ en fonction de la prime totale RCC+RCE let primePJcalc = 0; if (checkPJ) { const primeTotaleAvantPJ = Number(result.primeRCC) + (checkRCE ? Number(result.primeRCE) : 0); primePJcalc = calcPJ(primeTotaleAvantPJ); } // Prime totale finale avec PJ const primeTotaleFinale = Number(result.primeRCC) + (checkRCE ? Number(result.primeRCE) : 0) + primePJcalc; const tauxGlobalFinal = CA > 0 ? formatNumber((Number(result.primeRCC) + (checkRCE ? Number(result.primeRCE) : 0)) * 100 / CA, 2) : "0.000"; // Récupérer les éléments du DOM const elPrice = document.getElementById(card.priceId); const elRCC = document.getElementById(card.rccId); const elRCE = document.getElementById(card.rceId); const elPJ = document.getElementById(card.pjId); const elTxRCC = document.getElementById(card.txRccId); const elTxRCE = document.getElementById(card.txRceId); const elTxGlob = document.getElementById(card.txGlobId); // Récupérer les lignes parent pour masquer/afficher const rowRCE = elRCE?.closest('p'); const rowTxRCE = elTxRCE?.closest('p'); const rowPJ = elPJ?.closest('p'); const rowTxGlob = elTxGlob?.closest('p'); // Afficher le prix total if (elPrice) elPrice.innerText = formatNumber(primeTotaleFinale, 2) + ' €'; // Afficher Prime RCC (toujours visible) if (elRCC) { elRCC.innerText = formatNumber(result.primeRCC, 2) + " €" + (result.isMiniRCC ? " (Mini)" : ""); } // Afficher Prime RCE uniquement si cochée ET > 0 if (rowRCE) { if (checkRCE && result.primeRCE > 0) { rowRCE.style.display = 'block'; if (elRCE) { elRCE.innerText = formatNumber(result.primeRCE, 2) + " €" + (result.isMiniRCE ? " (Mini)" : ""); } } else { rowRCE.style.display = 'none'; } } // Afficher Prime PJ uniquement si cochée ET > 0 if (rowPJ) { if (checkPJ && primePJcalc > 0) { rowPJ.style.display = 'block'; if (elPJ) { elPJ.innerText = formatNumber(primePJcalc, 2) + " €"; } } else { rowPJ.style.display = 'none'; } } // En forfaitaire, ne pas afficher les taux (uniquement des primes fixes) if (cot === 'forfaitaire') { // Masquer tous les taux en forfaitaire if (elTxRCC?.closest('p')) elTxRCC.closest('p').style.display = 'none'; if (rowTxRCE) rowTxRCE.style.display = 'none'; if (rowTxGlob) rowTxGlob.style.display = 'none'; } else { // En revisable, afficher les taux if (elTxRCC?.closest('p')) { elTxRCC.closest('p').style.display = 'block'; elTxRCC.innerText = formatNumber(result.tauxRCC, 2) + " %"; } if (rowTxRCE) { if (checkRCE && result.primeRCE > 0) { rowTxRCE.style.display = 'block'; if (elTxRCE) { elTxRCE.innerText = formatNumber(result.tauxRCE, 2) + " %"; } } else { rowTxRCE.style.display = 'none'; } } if (rowTxGlob && CA > 0) { rowTxGlob.style.display = 'block'; if (elTxGlob) { elTxGlob.innerText = tauxGlobalFinal + " %"; } } } }); } // Fonction pour calculer la PJ selon la prime totale function calcPJ(primeTotale) { let primePJ = 0; const tablePJ = modRCGarAdd?.modRCC?.["Protection juridique"]; if (!tablePJ) return 0; // Parcourir les tranches de PJ et prendre la plus grande tranche inférieure à la prime totale const tranches = Object.keys(tablePJ).map(Number).sort((a, b) => a - b); for (const tranche of tranches) { if (primeTotale >= tranche) { primePJ = tablePJ[tranche]; } else { break; } } return primePJ; } function calcGlobal() { // Vérification des pourcentages AVANT calcul const allPourcentInputs = Array.from(document.querySelectorAll('.input-pourcent')).filter(p => p.offsetParent != null); let totalPourcentCheck = 0; allPourcentInputs.forEach(p => { totalPourcentCheck += toNumber(p.value); }); if (totalPourcentCheck > 100.01) { // Total > 100% : BLOQUER tous les calculs hideAllPrimes(); if (rcTarifGuard) rcTarifGuard.refresh(); updateTarifChoiceButtonsState(); return; } const cot = document.querySelector('input[name="cotisation"]:checked')?.value; let result = (cot === 'revisable') ? calcRevisable() : calcForfaitaire(); if (result) { const capitalTPPCsaisi = toNumber(document.getElementById("selTPPCcapital")?.value); const nbVehicules = toNumber(document.getElementById("selTPPCveh")?.value); // Trouver la tranche TPPC la plus proche let capitalTPPC = 0; let coefTPPC = 0; if (capitalTPPCsaisi > 0 && modRCGarAdd?.modRCC?.["TPPC"]) { const tranches = Object.keys(modRCGarAdd.modRCC.TPPC).map(Number).sort((a,b)=>a-b); capitalTPPC = findClosestTranche(capitalTPPCsaisi, tranches); coefTPPC = modRCGarAdd.modRCC.TPPC[capitalTPPC] || 0; } calcTarifettes(result.primePJ, result.CA, result.activites, capitalTPPC, nbVehicules, coefTPPC, result.primeRCCbase, result.primeRCEbase); } if (rcTarifGuard) rcTarifGuard.refresh(); updateTarifChoiceButtonsState(); } function calcRevisable() { // ========= Variables centrales (déclarées au début) ========= const capitalTPPCEl = document.getElementById("capital_TPPC"); const vehiculeTPPCEl = document.getElementById("vehicule_TPPC"); const coefTPPCEl = document.getElementById("garantie_prime_TPPC"); const capitalTPPC = toNumber(capitalTPPCEl?.value); const nbVehicules = toNumber(vehiculeTPPCEl?.value); const coefTPPC = toNumber(coefTPPCEl?.value) || 0.01; let primeBaseRCC = 0; let tauxBaseRCC = 0.000; let primeRCC = 0; let tauxRCC = 0.000; let primeBaseRCE = 0; let tauxBaseRCE = 0.000; let primeRCE = 0; let tauxRCE = 0.000; let primePJ = 0; let primeGlobal = 0; let tauxGlobal = 0; // Tableaux de travail par activité let primeBaseRCCparAct = []; let primeBaseRCEparAct = []; // ========= Étape CA ========= const modCA = calcModCA(document.getElementById('chiffreAffaire').value); document.getElementById('modCA').innerHTML = 'Mod. CA test :
x ' + modCA.toLocaleString('fr-FR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); // ========= Étape Activités (taux base) ========= tauxBaseRCC = get_taux_base_RCC(modRCActRCC, "revisable"); if (document.getElementById('checkRCE').checked === true) { tauxBaseRCE = get_taux_base_RCE(modRCActRCE, "revisable"); } const CA = toNumber(document.getElementById("chiffreAffaire").value); // ========= Calcul primes de base RCC ========= for (let i = 0; i < tauxBaseRCC.length; i++) { const pourcentInput = toNumber(document.getElementById("pourcent" + tauxBaseRCC[i].typeActivite)?.value); const primeBase = (tauxBaseRCC[i].tauxBase / 100 * pourcentInput / 100 * CA); primeBaseRCCparAct.push({ typeActivite: tauxBaseRCC[i].typeActivite, primeBase: primeBase }); } // ========= Calcul primes de base RCE si coché ========= if (document.getElementById('checkRCE').checked) { for (let i = 0; i < tauxBaseRCE.length; i++) { const pourcentInput = toNumber(document.getElementById("pourcent" + tauxBaseRCE[i].typeActivite)?.value); const primeBase = (tauxBaseRCE[i].tauxBase / 100 * pourcentInput / 100 * CA); primeBaseRCEparAct.push({ typeActivite: tauxBaseRCE[i].typeActivite, primeBase: primeBase }); } } // Sommes des primes de base (SANS mod CA) primeBaseRCC = primeBaseRCCparAct.reduce((total, item) => total + item.primeBase, 0); primeBaseRCE = primeBaseRCEparAct.reduce((total, item) => total + item.primeBase, 0); // UI (affichage sans mod CA) document.getElementById('primeChapActRCC').innerHTML = 'Prime de base RCC : ' + primeBaseRCC.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }) + ''; document.getElementById('primeChapActRCE').innerHTML = 'Prime de base RCE : ' + primeBaseRCE.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }) + ''; // ========= Étape Activités complémentaires ========= for (let i = 0; i < primeBaseRCCparAct.length; i++) { primeBaseRCCparAct[i].primeBase *= calcModActCompl(modRCActCompl, primeBaseRCCparAct[i].typeActivite, "RCC", "revisable"); } if (document.getElementById('checkRCE').checked) { for (let i = 0; i < primeBaseRCEparAct.length; i++) { primeBaseRCEparAct[i].primeBase *= calcModActCompl( modRCActCompl, primeBaseRCEparAct[i].typeActivite, "RCE", "revisable" ); } } // Re-sommes (toujours sans mod CA) primeBaseRCC = primeBaseRCCparAct.reduce((total, item) => total + item.primeBase, 0); primeBaseRCE = primeBaseRCEparAct.reduce((total, item) => total + item.primeBase, 0); document.getElementById('primeChapActComplRCC').innerHTML = 'Prime RCC : ' + primeBaseRCC.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); document.getElementById('primeChapActComplRCE').innerHTML = 'Prime RCE : ' + primeBaseRCE.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); // ========= Étape Marchandises (× par activité) + Mod CA ========= const selections = getSelectedActivities(); for (let i = 0; i < primeBaseRCCparAct.length; i++) { const actName = primeBaseRCCparAct[i].typeActivite; const modMar = calcModMarchandises(modRCMar, actName, "RCC", "revisable"); // Appliquer marchandises ET CA ensemble primeBaseRCCparAct[i].primeBase = primeBaseRCCparAct[i].primeBase * modMar * modCA; } if (document.getElementById('checkRCE').checked) { for (let i = 0; i < primeBaseRCEparAct.length; i++) { const actName = primeBaseRCEparAct[i].typeActivite; const modMar = calcModMarchandises(modRCMar, actName, "RCE", "revisable"); // Appliquer marchandises ET CA ensemble primeBaseRCEparAct[i].primeBase = primeBaseRCEparAct[i].primeBase * modMar * modCA; } } // Recalcule totaux apres mods marchandises + CA primeBaseRCC = primeBaseRCCparAct.reduce((total, item) => total + item.primeBase, 0); primeBaseRCE = primeBaseRCEparAct.reduce((total, item) => total + item.primeBase, 0); // Maintenant primeRCC et primeRCE incluent déjà le mod CA primeRCC = primeBaseRCC; primeRCE = primeBaseRCE; // UI marchandises document.getElementById('primeChapMarchRCC').innerHTML = 'Prime RCC : ' + primeRCC.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); document.getElementById('primeChapMarchRCE').innerHTML = 'Prime RCE : ' + primeRCE.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); // ========= Étape Zones (× max RCC / × max RCE) ========= const { mRCC, mRCE } = getZoneMods(modRCZone); primeRCC *= mRCC; primeRCE *= mRCE; // UI zones document.getElementById('primeChapZonesRCC').innerHTML = 'Prime RCC : ' + primeRCC.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); document.getElementById('primeChapZonesRCE').innerHTML = 'Prime RCE : ' + primeRCE.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); // ========= Étape Engagements complémentaires (×) ========= const result = calcEngagCompl(modRCEngagCompl, primeRCC); primeRCC = result.prime; // UI engagements document.getElementById('primeEngValue').innerText = 'Prime RCC : ' + primeRCC.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); const infoContainer = document.getElementById('primeEngInfos'); infoContainer.innerHTML = ""; result.infos.forEach(msg => { const p = document.createElement("p"); p.style.color = "#e53935"; p.style.fontWeight = "300"; p.style.fontSize = "0.85em"; p.style.marginTop = "5px"; p.style.fontStyle = "italic"; p.innerText = msg; infoContainer.appendChild(p); }); // non necessaire, mais garder si utile, car fonctionne a 100% // ========= Étape Garanties additionnelles (RCE uniquement) ========= const garAdd = calcGarAdd(modRCGarAdd, primeRCC, primeRCE); // primeRCC reste inchangée (TPPC appliquée plus tard) primeRCE = garAdd.primeRCE; // RCE avec Station lavage, Garage, CSE // UI RCC const blocRCC = document.getElementById('primeChapGarAddRCC'); blocRCC.innerHTML = 'Prime RCC : ' + primeRCC.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }) + ''; garAdd.infosRCC.forEach(msg => { const p = document.createElement("p"); p.style.color = "#e53935"; p.style.fontWeight = "300"; p.style.fontSize = "0.85em"; p.style.marginTop = "5px"; p.style.fontStyle = "italic"; p.innerText = msg; blocRCC.appendChild(p); });// toujours inutile, pas necessaire // UI RCE const blocRCE = document.getElementById('primeChapGarAddRCE'); blocRCE.innerHTML = 'Prime RCE : ' + primeRCE.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }) + ''; garAdd.infosRCE.forEach(msg => { const p = document.createElement("p"); p.style.color = "#e53935"; p.style.fontWeight = "300"; p.style.fontSize = "0.85em"; p.style.marginTop = "5px"; p.style.fontStyle = "italic"; p.innerText = msg; blocRCE.appendChild(p); }); // toujours inutile, pas necessaire // ========= Étape Sinistres ========= const sin = calcSinistre(modRCSinistre, primeRCC, primeRCE); primeRCC = sin.primeRCC; primeRCE = sin.primeRCE; // ========= Sauvegarder les primes BASE pour les tarifettes (après sinistre, avant franchise) ========= const primeRCCavantFranchise = primeRCC; const primeRCEavantFranchise = primeRCE; // Les totaux RCC et RCE sont maintenant prêts pour les franchises const totalRCC = primeRCC; const totalRCE = primeRCE; // PJ (calculée dans calcTarifettes selon prime totale) primePJ = 0; primeGlobal = totalRCC + totalRCE + primePJ; // UI document.getElementById('modCA').innerHTML = 'Modulation CA :
x ' + modCA.toLocaleString('fr-FR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); const activiteMap = { "Voiturier / Loueur": "Voiturier/Loueur", "Commissionnaire de Transport": "Commissionnaire de Transport", "Déménageur": "Déménageur", "Logistique": "Logistique", "Autocariste": "Autocariste", "Autres activites": "Autres activites" }; function getSelectedActivites() { const activites = []; document.querySelectorAll('label input[type="checkbox"]:checked').forEach(cb => { const span = cb.parentElement.querySelector('span'); if (span) { const labelText = span.textContent.trim(); if (activiteMap[labelText]) { activites.push(activiteMap[labelText]); } } }); return activites; } const activites = getSelectedActivites(); return { CA, activites, primeRCC: totalRCC, primeRCE: totalRCE, primeRCCbase: primeRCCavantFranchise, primeRCEbase: primeRCEavantFranchise, primePJ, primeGlobal }; } function calcForfaitaire() { // ========= Variables centrales ========= let primeBaseRCC = 0; let primeBaseRCE = 0; let primeRCC = 0; let primeRCE = 0; let primePJ = 0; let primeGlobal = 0; // Tableaux de travail par activité let primeBaseRCCparAct = []; let primeBaseRCEparAct = []; // ========= Récupération du nombre de véhicules ========= const nbrVehicule = toNumber(document.getElementById('nbrVehicule')?.value); // Déterminer si c'est 1 ou 2 véhicules (par défaut 1) const nbVehiculeKey = (nbrVehicule === 2) ? "deuxVehicules" : "unVehicule"; // ========= Étape Activités (primes forfaitaires de base) ========= const selections = getSelectedActivities(); selections.forEach(activity => { const typeActivite = activity.typeActivite; const inputElement = document.querySelector( `input[name="selectAct${typeActivite}"], select[name="selectAct${typeActivite}"]` ); if (inputElement) { const capitalSaisi = toNumber(inputElement.value); if (capitalSaisi <= 0) { return; // Skip cette activité } // Récupération de la prime forfaitaire RCC const grilleActRCC = modRCActRCC.forfaitaire[typeActivite]; if (grilleActRCC) { const tranches = Object.keys(grilleActRCC).map(Number).sort((a, b) => a - b); const tranche = findClosestTranche(capitalSaisi, tranches); const primeData = grilleActRCC[tranche]; if (primeData && typeof primeData[nbVehiculeKey] === 'number') { const primeBase = primeData[nbVehiculeKey]; primeBaseRCCparAct.push({ typeActivite: typeActivite, primeBase: primeBase }); // Affichage du taux (ici c'est une prime fixe) const rccDiv = document.getElementById("tauxBaseRCCact" + typeActivite); if (rccDiv) { rccDiv.innerText = "RCC : " + primeBase.toFixed(2) + " €"; } // Info tranche si différente if (capitalSaisi !== tranche) { const rowDiv = inputElement.closest('.row'); if (rowDiv) { const oldInfo = rowDiv.querySelector('.info-tranche'); if (oldInfo) oldInfo.remove(); const infoSpan = document.createElement('span'); infoSpan.className = 'info-tranche'; infoSpan.style.color = '#e53935'; infoSpan.style.fontSize = '0.8em'; infoSpan.style.fontStyle = 'italic'; infoSpan.innerText = `(tranche ${tranche.toLocaleString('fr-FR')} €)`; rowDiv.appendChild(infoSpan); } } } } // Récupération de la prime forfaitaire RCE si coché if (document.getElementById('checkRCE')?.checked) { const grilleActRCE = modRCActRCE.forfaitaire[typeActivite]; if (grilleActRCE && grilleActRCE[nbVehiculeKey]) { const primeBaseRCE_val = grilleActRCE[nbVehiculeKey].optimale || 0; primeBaseRCEparAct.push({ typeActivite: typeActivite, primeBase: primeBaseRCE_val }); // Affichage const rceDiv = document.getElementById("tauxBaseRCEact" + typeActivite); if (rceDiv) { rceDiv.innerText = "RCE : " + primeBaseRCE_val.toFixed(2) + " €"; } } } } }); // Sommes des primes de base primeBaseRCC = primeBaseRCCparAct.reduce((total, item) => total + item.primeBase, 0); primeBaseRCE = primeBaseRCEparAct.reduce((total, item) => total + item.primeBase, 0); // UI (affichage) document.getElementById('primeChapActRCC').innerHTML = 'Prime de base RCC : ' + primeBaseRCC.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }) + ''; document.getElementById('primeChapActRCE').innerHTML = 'Prime de base RCE : ' + primeBaseRCE.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }) + ''; // ========= Étape Activités complémentaires ========= for (let i = 0; i < primeBaseRCCparAct.length; i++) { primeBaseRCCparAct[i].primeBase *= calcModActCompl(modRCActCompl, primeBaseRCCparAct[i].typeActivite, "RCC", "forfaitaire"); } if (document.getElementById('checkRCE')?.checked) { for (let i = 0; i < primeBaseRCEparAct.length; i++) { primeBaseRCEparAct[i].primeBase *= calcModActCompl( modRCActCompl, primeBaseRCEparAct[i].typeActivite, "RCE", "forfaitaire" ); } } // Re-sommes primeBaseRCC = primeBaseRCCparAct.reduce((total, item) => total + item.primeBase, 0); primeBaseRCE = primeBaseRCEparAct.reduce((total, item) => total + item.primeBase, 0); document.getElementById('primeChapActComplRCC').innerHTML = 'Prime RCC : ' + primeBaseRCC.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); document.getElementById('primeChapActComplRCE').innerHTML = 'Prime RCE : ' + primeBaseRCE.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); // ========= Étape Marchandises (× par activité) ========= for (let i = 0; i < primeBaseRCCparAct.length; i++) { const actName = primeBaseRCCparAct[i].typeActivite; const modMar = calcModMarchandises(modRCMar, actName, "RCC", "forfaitaire"); primeBaseRCCparAct[i].primeBase = primeBaseRCCparAct[i].primeBase * modMar; } if (document.getElementById('checkRCE')?.checked) { for (let i = 0; i < primeBaseRCEparAct.length; i++) { const actName = primeBaseRCEparAct[i].typeActivite; const modMar = calcModMarchandises(modRCMar, actName, "RCE", "forfaitaire"); primeBaseRCEparAct[i].primeBase = primeBaseRCEparAct[i].primeBase * modMar; } } // Recalcule totaux après mods marchandises primeBaseRCC = primeBaseRCCparAct.reduce((total, item) => total + item.primeBase, 0); primeBaseRCE = primeBaseRCEparAct.reduce((total, item) => total + item.primeBase, 0); primeRCC = primeBaseRCC; primeRCE = primeBaseRCE; // UI marchandises document.getElementById('primeChapMarchRCC').innerHTML = 'Prime RCC : ' + primeRCC.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); document.getElementById('primeChapMarchRCE').innerHTML = 'Prime RCE : ' + primeRCE.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); // ========= Étape Zones (× max RCC / × max RCE) ========= const { mRCC, mRCE } = getZoneMods(modRCZone); primeRCC *= mRCC; primeRCE *= mRCE; // UI zones document.getElementById('primeChapZonesRCC').innerHTML = 'Prime RCC : ' + primeRCC.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); document.getElementById('primeChapZonesRCE').innerHTML = 'Prime RCE : ' + primeRCE.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); // ========= Étape Engagements complémentaires (×) ========= const result = calcEngagCompl(modRCEngagCompl, primeRCC); primeRCC = result.prime; // UI engagements document.getElementById('primeEngValue').innerText = 'Prime RCC : ' + primeRCC.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }); const infoContainer = document.getElementById('primeEngInfos'); infoContainer.innerHTML = ""; result.infos.forEach(msg => { const p = document.createElement("p"); p.style.color = "#e53935"; p.style.fontWeight = "300"; p.style.fontSize = "0.85em"; p.style.marginTop = "5px"; p.style.fontStyle = "italic"; p.innerText = msg; infoContainer.appendChild(p); }); // ========= Étape Garanties additionnelles (RCE uniquement) ========= const garAdd = calcGarAdd(modRCGarAdd, primeRCC, primeRCE); primeRCE = garAdd.primeRCE; // UI RCC const blocRCC = document.getElementById('primeChapGarAddRCC'); blocRCC.innerHTML = 'Prime RCC : ' + primeRCC.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }) + ''; garAdd.infosRCC.forEach(msg => { const p = document.createElement("p"); p.style.color = "#e53935"; p.style.fontWeight = "300"; p.style.fontSize = "0.85em"; p.style.marginTop = "5px"; p.style.fontStyle = "italic"; p.innerText = msg; blocRCC.appendChild(p); }); // UI RCE const blocRCE = document.getElementById('primeChapGarAddRCE'); blocRCE.innerHTML = 'Prime RCE : ' + primeRCE.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' }) + ''; garAdd.infosRCE.forEach(msg => { const p = document.createElement("p"); p.style.color = "#e53935"; p.style.fontWeight = "300"; p.style.fontSize = "0.85em"; p.style.marginTop = "5px"; p.style.fontStyle = "italic"; p.innerText = msg; blocRCE.appendChild(p); }); // ========= Étape Sinistres (OPTIONNEL en forfaitaire - basé sur CA) ========= // Note: En forfaitaire, normalement pas de sinistre car pas de CA // Mais on garde la logique au cas où il y aurait un CA saisi const CA = toNumber(document.getElementById("chiffreAffaire")?.value); if (CA > 0) { const sin = calcSinistre(modRCSinistre, primeRCC, primeRCE); primeRCC = sin.primeRCC; primeRCE = sin.primeRCE; } // ========= Sauvegarder les primes BASE pour les tarifettes ========= const primeRCCavantFranchise = primeRCC; const primeRCEavantFranchise = primeRCE; // Les totaux RCC et RCE sont maintenant prêts const totalRCC = primeRCC; const totalRCE = primeRCE; // PJ (calculée dans calcTarifettes selon prime totale) primePJ = 0; primeGlobal = totalRCC + totalRCE + primePJ; const activiteMap = { "Voiturier / Loueur": "Voiturier/Loueur", "Commissionnaire de Transport": "Commissionnaire de Transport", "Déménageur": "Déménageur", "Logistique": "Logistique", "Autocariste": "Autocariste", "Autres activites": "Autres activites" }; function getSelectedActivites() { const activites = []; document.querySelectorAll('label input[type="checkbox"]:checked').forEach(cb => { const span = cb.parentElement.querySelector('span'); if (span) { const labelText = span.textContent.trim(); if (activiteMap[labelText]) { activites.push(activiteMap[labelText]); } } }); return activites; } const activites = getSelectedActivites(); return { CA: CA || 0, activites, primeRCC: totalRCC, primeRCE: totalRCE, primeRCCbase: primeRCCavantFranchise, primeRCEbase: primeRCEavantFranchise, primePJ, primeGlobal }; } function getZoneMods(modRCZone) { const zones = [ { id: 'zone1', label: 'France Métropolitaine et pays limitrophes' }, { id: 'zone2', label: 'Union Européenne' }, { id: 'zone3', label: 'Autres pays européens sauf Russie et Ukraine (y compris UK et Norvège)' }, { id: 'zone4', label: 'Pays du Maghreb et Amérique du Nord ( USA / Canada / Mexique )' }, { id: 'zone5', label: 'Amérique Centrale et Sud / Caraïbes, Asie et Océanie' }, { id: 'zone6', label: 'Afrique Hors Maghreb / Proche Orient / Moyen Orient' } ]; let mRCC = 1, mRCE = 1; zones.forEach(z => { const cb = document.getElementById(z.id); if (cb && cb.checked) { const m = modRCZone[z.label]; if (m) { if (typeof m.modRCC === 'number') mRCC = Math.max(mRCC, m.modRCC); if (typeof m.modRCE === 'number') mRCE = Math.max(mRCE, m.modRCE); } } }); return { mRCC, mRCE }; } function handleLoadHistoriqueBtn() { var selectedId = document.getElementById('idSelect').value; if (selectedId != "") { fetch(`/contrat/update/${contrat.produit}/${contrat.id}/${selectedId}`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, }) .then(response => response.json()) .then(data => { if (data.valid) { window.location.href = `/navParcours?numParcours=${getNumParcoursFromURL()}&submenu=projet`; } else { console.log('Echec lors de la mise à jour de la relation id contrat - id client :', data); } }); } } //Appel pour recevoir les constantes async function constantsJSON() { try { const responsesJSON = await Promise.all([ fetch('/rc/modulo/CARC'), fetch('/rc/modulo/activiteRCC'), fetch('/rc/modulo/activiteRCE'), fetch('/rc/modulo/activiteComplRC'), fetch('/rc/modulo/marchandiseRC'), fetch('/rc/modulo/zoneRC'), fetch('/rc/modulo/engagComplRC'), fetch('/rc/modulo/garAdditionelRC'), fetch('/rc/modulo/sinistreRC'), fetch('/rc/modulo/franchiseRC'), fetch('/rc/modulo/primeMiniRC') ]); const jsonResponses = await Promise.all(responsesJSON.map(r => r.json())); // Récupérer toutes les réponses JSON // Extraire la valeur objRetourne de chaque réponse [ modRCCA, modRCActRCC, modRCActRCE, modRCActCompl, modRCMar, modRCZone, modRCEngagCompl, modRCGarAdd, modRCSinistre, modRCFranchise, modRCPrimeMini ] = jsonResponses.map(response => response.objRetourne); // Adapter pour extraire objRetourne } catch (err) { throw err; } } function resetInputs() { // Sélection des éléments input pourcentage const pourcentInputs = document.querySelectorAll('.input-pourcent'); // Calculer le nombre d'inputs visibles const visibleInputs = Array.from(pourcentInputs).filter(input => input.offsetParent != null); // Vérifier s'il y a des inputs visibles if (visibleInputs.length > 0) { // Réinitialiser les valeurs visibleInputs.forEach(p => { p.value = ''; // Réinitialiser la valeur const correspondingIsSetInput = document.getElementById('isSet' + p.id.replace('pourcent', '')); correspondingIsSetInput.value = 'false'; // Réinitialiser isSet p.classList.remove('set'); // Remettre le fond d'origine }); // Calculer le pourcentage à répartir const pourcentToAdd = (100 / visibleInputs.length).toFixed(2); // Répartir le pourcentage entre les inputs visibles visibleInputs.forEach(p => { p.value = pourcentToAdd; // Assigner la valeur calculée // Ne pas changer isSet à true ici // correspondingIsSetInput.value reste 'false' }); // Mettre à jour l'indicateur updatePercentageIndicator(100); } } function get_taux_base_RCC(grille_mod_RCC, cot) { const tauxOptimaux = []; const selections = getSelectedActivities(); selections.forEach(activity => { const typeActivite = activity.typeActivite; const inputElement = document.querySelector( `input[name="selectAct${typeActivite}"], select[name="selectAct${typeActivite}"]` ); if (inputElement) { const capitalSaisi = toNumber(inputElement.value); if (capitalSaisi <= 0) { return; } if (cot === 'revisable') { const grilleAct = grille_mod_RCC.revisable[typeActivite]; if (grilleAct) { const tranches = Object.keys(grilleAct).map(Number).sort((a, b) => a - b); const tranche = findClosestTranche(capitalSaisi, tranches); const tauxBase = grilleAct[tranche]; if (typeof tauxBase === 'number') { // 1. Mettre à jour la div RCC avec uniquement le texte principal const rccDiv = document.getElementById("tauxBaseRCCact" + typeActivite); if (rccDiv) { rccDiv.innerText = "RCC : " + tauxBase.toFixed(3) + " %"; } if (capitalSaisi !== tranche) { const rowDiv = inputElement.closest('.row'); if (rowDiv) { const oldInfo = rowDiv.querySelector('.info-tranche'); if (oldInfo) oldInfo.remove(); const infoSpan = document.createElement('span'); infoSpan.className = 'info-tranche'; infoSpan.style.color = '#e53935'; infoSpan.style.fontSize = '0.8em'; infoSpan.style.fontStyle = 'italic'; infoSpan.innerText = `(tranche ${tranche.toLocaleString('fr-FR')} €)`; rowDiv.appendChild(infoSpan); } } tauxOptimaux.push({ typeActivite, capital: tranche, tauxBase }); } } } else if (cot === 'forfaitaire') { console.log("TODO FORFAITAIRE"); } } }); return tauxOptimaux; } function get_taux_base_RCE(grille_mod_RCE, cot) { const tauxOptimaux = []; const selections = getSelectedActivities(); selections.forEach(activity => { const typeActivite = activity.typeActivite; const inputElement = document.querySelector(`input[name="selectAct${typeActivite}"], select[name="selectAct${typeActivite}"]`); if (inputElement) { const capitalSaisi = toNumber(inputElement.value); // Si rien n'est saisi ou invalide, ignorer if (capitalSaisi <= 0) { return; } // Traitement selon le type de cotisation if (cot == 'revisable') { if (grille_mod_RCE.revisable[typeActivite]) { const tauxBase = grille_mod_RCE.revisable[typeActivite]["optimale"]; document.getElementById("tauxBaseRCEact" + typeActivite).innerHTML = "RCE : " + tauxBase.toFixed(3) + " %" tauxOptimaux.push({ typeActivite, tauxBase }); } } else if (cot == 'forfaitaire') { console.log("TODO FORFAITAIRE"); // ehhh } } }); return tauxOptimaux; } function getSelectedActivities() { const selections = []; // Récupérer le nombre de véhicules depuis l'input const nombreVehicules = document.getElementById('nbrVehicule').value || null; // Valeur par défaut à null si vide if (document.getElementById('checkVoiturier').checked) { const listActComplChecked = []; const listMarChecked = []; // Récupérer les activités complémentaires const actComplCheckboxes = document.querySelectorAll('[name="actComplVoiturier/Loueur"] input[type="checkbox"]:checked'); actComplCheckboxes.forEach(checkbox => { listActComplChecked.push(checkbox.nextElementSibling.textContent.trim()); }); // Récupérer les marchandises const marComplCheckboxes = document.querySelectorAll('[name="marVoiturier/Loueur"] input[type="checkbox"]:checked'); marComplCheckboxes.forEach(checkbox => { listMarChecked.push(checkbox.nextElementSibling.textContent.trim()); }); selections.push({ typeActivite: "Voiturier/Loueur", nombreVehicules: nombreVehicules, listActComplChecked: listActComplChecked, listMarChecked: listMarChecked }); } if (document.getElementById('checkCommissionnaire').checked) { const listActComplChecked = []; const listMarChecked = []; // Récupérer les activités complémentaires const actComplCheckboxes = document.querySelectorAll('[name="actComplCommissionnaire de Transport"] input[type="checkbox"]:checked'); actComplCheckboxes.forEach(checkbox => { listActComplChecked.push(checkbox.nextElementSibling.textContent.trim()); }); // Récupérer les marchandises const marComplCheckboxes = document.querySelectorAll('[name="marCommissionnaire de Transport"] input[type="checkbox"]:checked'); marComplCheckboxes.forEach(checkbox => { listMarChecked.push(checkbox.nextElementSibling.textContent.trim()); }); selections.push({ typeActivite: "Commissionnaire de Transport", nombreVehicules: null, listActComplChecked: listActComplChecked, listMarChecked: listMarChecked }); } if (document.getElementById('checkDemenageur').checked) { const listActComplChecked = []; const listMarChecked = []; // Récupérer les activités complémentaires const actComplCheckboxes = document.querySelectorAll('[name="actComplDéménageur"] input[type="checkbox"]:checked'); actComplCheckboxes.forEach(checkbox => { listActComplChecked.push(checkbox.nextElementSibling.textContent.trim()); }); // Récupérer les marchandises const marComplCheckboxes = document.querySelectorAll('[name="marDéménageur"] input[type="checkbox"]:checked'); marComplCheckboxes.forEach(checkbox => { listMarChecked.push(checkbox.nextElementSibling.textContent.trim()); }); selections.push({ typeActivite: "Déménageur", nombreVehicules: nombreVehicules, listActComplChecked: listActComplChecked, listMarChecked: listMarChecked }); } if (document.getElementById('checkLogistique').checked) { const listActComplChecked = []; const listMarChecked = []; // Récupérer les activités complémentaires const actComplCheckboxes = document.querySelectorAll('[name="actComplLogistique"] input[type="checkbox"]:checked'); actComplCheckboxes.forEach(checkbox => { listActComplChecked.push(checkbox.nextElementSibling.textContent.trim()); }); // Récupérer les marchandises const marComplCheckboxes = document.querySelectorAll('[name="marLogistique"] input[type="checkbox"]:checked'); marComplCheckboxes.forEach(checkbox => { listMarChecked.push(checkbox.nextElementSibling.textContent.trim()); }); selections.push({ typeActivite: "Logistique", nombreVehicules: null, listActComplChecked: listActComplChecked, listMarChecked: listMarChecked }); } if (document.getElementById('checkAutres').checked) { const listActComplChecked = []; const listMarChecked = []; // Récupérer les activités complémentaires const actComplCheckboxes = document.querySelectorAll('[name="actComplAutres activites"] input[type="checkbox"]:checked'); actComplCheckboxes.forEach(checkbox => { listActComplChecked.push(checkbox.nextElementSibling.textContent.trim()); }); // Récupérer les marchandises const marComplCheckboxes = document.querySelectorAll('[name="marAutres activites"] input[type="checkbox"]:checked'); marComplCheckboxes.forEach(checkbox => { listMarChecked.push(checkbox.nextElementSibling.textContent.trim()); }); selections.push({ typeActivite: "Autres activites", nombreVehicules: nombreVehicules, listActComplChecked: listActComplChecked, listMarChecked: listMarChecked }); } // Ajoutez d'autres cas si nécessaire return selections; } // Fonction pour sauvegarder les données tarifRC dans la base async function saveTarifRC() { if (!rc || !contrat) { console.error('Données manquantes pour sauvegarder le tarif RC'); return { valid: false, message: 'Données manquantes' }; } // Préparer les références aux champs selon les différents templates possibles const caInput = getElementByIdFlexible('CA') || getElementByIdFlexible('chiffreAffaire'); const nbVehInput = getElementByIdFlexible('nbVehicules') || getElementByIdFlexible('nbrVehicule'); const capitalVoiturierInput = getElementByIdFlexible('capitalVoiturier') || document.querySelector('input[name="selectActVoiturier/Loueur"]'); const capitalCommissionnaireInput = getElementByIdFlexible('capitalCommissionnaire') || document.querySelector('input[name="selectActCommissionnaire de Transport"]') || document.querySelector('input[name="selectActCommissionnaireDeTransport"]'); const capitalDemenageurInput = getElementByIdFlexible('capitalDemenageur') || document.querySelector('input[name="selectActDéménageur"]') || document.querySelector('input[name="selectActDemenageur"]'); const capitalLogistiqueInput = getElementByIdFlexible('capitalLogistique') || document.querySelector('input[name="selectActLogistique"]'); const capitalAutocaristeInput = getElementByIdFlexible('capitalAutocariste') || document.querySelector('input[name="selectActAutocariste"]'); const capitalAutresInput = getElementByIdFlexible('capitalAutres') || document.querySelector('input[name="selectActAutres activites"]') || document.querySelector('input[name="selectActAutresActivites"]'); const pctVoiturierInput = getElementByIdFlexible('pourcent_voiturier') || getElementByIdFlexible('pourcentVoiturier/Loueur'); const pctCommissionnaireInput = getElementByIdFlexible('pourcent_commissionnaire') || getElementByIdFlexible('pourcentCommissionnaire de Transport') || getElementByIdFlexible('pourcentCommissionnaireDeTransport'); const pctDemenageurInput = getElementByIdFlexible('pourcent_demenageur') || getElementByIdFlexible('pourcentDéménageur') || getElementByIdFlexible('pourcentDemenageur'); const pctLogistiqueInput = getElementByIdFlexible('pourcent_logistique') || getElementByIdFlexible('pourcentLogistique'); const pctAutocaristeInput = getElementByIdFlexible('pourcent_autocariste') || getElementByIdFlexible('pourcentAutocariste'); const pctAutresInput = getElementByIdFlexible('pourcent_autres') || getElementByIdFlexible('pourcentAutres activites') || getElementByIdFlexible('pourcentAutresActivites'); // ===== ÉTAPE 1: Collecter les données communes à sauvegarder dans RC principal ===== const rcMainData = { typeCotisation: document.querySelector('input[name="cotisation"]:checked')?.value, chiffreAffaires: toNumber(caInput?.value ?? caInput?.textContent), nombreVehicules: Math.max(0, Math.round(toNumber(nbVehInput?.value ?? nbVehInput?.textContent))), checkRCE: document.getElementById('checkRCE')?.checked || false, checkVoiturier: document.getElementById('checkVoiturier')?.checked || false, capitalVoiturier: toNumber(capitalVoiturierInput?.value ?? capitalVoiturierInput?.textContent), checkCommissionnaire: document.getElementById('checkCommissionnaire')?.checked || false, capitalCommissionnaire: toNumber(capitalCommissionnaireInput?.value ?? capitalCommissionnaireInput?.textContent), checkDemenageur: document.getElementById('checkDemenageur')?.checked || false, capitalDemenageur: toNumber(capitalDemenageurInput?.value ?? capitalDemenageurInput?.textContent), checkLogistique: document.getElementById('checkLogistique')?.checked || false, capitalLogistique: toNumber(capitalLogistiqueInput?.value ?? capitalLogistiqueInput?.textContent), checkAutocariste: document.getElementById('checkAutocariste')?.checked || false, capitalAutocariste: toNumber(capitalAutocaristeInput?.value ?? capitalAutocaristeInput?.textContent), checkAutres: document.getElementById('checkAutres')?.checked || false, capitalAutres: toNumber(capitalAutresInput?.value ?? capitalAutresInput?.textContent), // Activités complémentaires (JSON) - collectées avec les bons noms actComplVoiturier: collectActivitesComplJSON('voiturier'), actComplCommissionnaire: collectActivitesComplJSON('commissionnaire'), actComplDemenageur: collectActivitesComplJSON('demenageur'), actComplLogistique: collectActivitesComplJSON('logistique'), // Marchandises (JSON) - collectées avec les bons noms marchandisesVoiturier: collectMarchandisesJSON('voiturier'), marchandisesCommissionnaire: collectMarchandisesJSON('commissionnaire'), marchandisesDemenageur: collectMarchandisesJSON('demenageur'), marchandisesLogistique: collectMarchandisesJSON('logistique'), marchandisesAutocariste: collectMarchandisesJSON('autocariste'), marchandisesAutres: collectMarchandisesJSON('autres'), // Zones zone1: document.getElementById('zone1')?.checked || false, zone2: document.getElementById('zone2')?.checked || false, zone3: document.getElementById('zone3')?.checked || false, zone4: document.getElementById('zone4')?.checked || false, zone5: document.getElementById('zone5')?.checked || false, zone6: document.getElementById('zone6')?.checked || false, //commentaire commentaire: document.getElementById('commentaire')?.value, }; // ===== LOGS DE DÉBOGAGE ===== console.log('=== DEBUT SAUVEGARDE TARIF RC ==='); console.log('rcMainData:', rcMainData); // ===== ÉTAPE 2: Collecter les résultats de calculs pour tarifRC ===== const tarifRCData = { sinistre: toNumber(document.getElementById('sinistre')?.value), pourcentageVoiturier: toNumber(pctVoiturierInput?.value ?? pctVoiturierInput?.textContent), isSetVoiturier: Boolean(pctVoiturierInput?.value?.trim()), pourcentageCommissionnaire: toNumber(pctCommissionnaireInput?.value ?? pctCommissionnaireInput?.textContent), isSetCommissionnaire: Boolean(pctCommissionnaireInput?.value?.trim()), pourcentageDemenageur: toNumber(pctDemenageurInput?.value ?? pctDemenageurInput?.textContent), isSetDemenageur: Boolean(pctDemenageurInput?.value?.trim()), pourcentageLogistique: toNumber(pctLogistiqueInput?.value ?? pctLogistiqueInput?.textContent), isSetLogistique: Boolean(pctLogistiqueInput?.value?.trim()), pourcentageAutocariste: toNumber(pctAutocaristeInput?.value ?? pctAutocaristeInput?.textContent), isSetAutocariste: Boolean(pctAutocaristeInput?.value?.trim()), pourcentageAutres: toNumber(pctAutresInput?.value ?? pctAutresInput?.textContent), isSetAutres: Boolean(pctAutresInput?.value?.trim()), // Tarifs pour franchise 250 primeRCC_250: toNumber(document.getElementById('rccFr250')?.textContent), primeRCE_250: toNumber(document.getElementById('rceFr250')?.textContent), primePJ_250: toNumber(document.getElementById('pjFr250')?.textContent), primeTotal_250: toNumber(document.getElementById('priceFr250')?.textContent), tauxRCC_250: toNumber(document.getElementById('tauxRccFr250')?.textContent), tauxRCE_250: toNumber(document.getElementById('tauxRceFr250')?.textContent), tauxGlobal_250: toNumber(document.getElementById('tauxGlobalFr250')?.textContent), // Tarifs pour franchise 400 primeRCC_400: toNumber(document.getElementById('rccFr400')?.textContent), primeRCE_400: toNumber(document.getElementById('rceFr400')?.textContent), primePJ_400: toNumber(document.getElementById('pjFr400')?.textContent), primeTotal_400: toNumber(document.getElementById('priceFr400')?.textContent), tauxRCC_400: toNumber(document.getElementById('tauxRccFr400')?.textContent), tauxRCE_400: toNumber(document.getElementById('tauxRceFr400')?.textContent), tauxGlobal_400: toNumber(document.getElementById('tauxGlobalFr400')?.textContent), // Tarifs pour franchise 2000 primeRCC_2000: toNumber(document.getElementById('rccFr2000')?.textContent), primeRCE_2000: toNumber(document.getElementById('rceFr2000')?.textContent), primePJ_2000: toNumber(document.getElementById('pjFr2000')?.textContent), primeTotal_2000: toNumber(document.getElementById('priceFr2000')?.textContent), tauxRCC_2000: toNumber(document.getElementById('tauxRccFr2000')?.textContent), tauxRCE_2000: toNumber(document.getElementById('tauxRceFr2000')?.textContent), tauxGlobal_2000: toNumber(document.getElementById('tauxGlobalFr2000')?.textContent), // Franchise choisie franchiseChoisie: window.franchiseChoisie || null, // Engagements complémentaires (dans tarifRC, pas dans rc!) checkDomImmat: document.getElementById('checkDomImmat')?.checked || false, capitalDomImmat: toNumber(document.getElementById('inputDomImmat')?.value), checkContConf: document.getElementById('checkContConf')?.checked || false, capitalContConf: toNumber(document.getElementById('inputContConf')?.value), checkDiffInv: document.getElementById('checkDiffInv')?.checked || false, capitalDiffInv: toNumber(document.getElementById('inputDiffInv')?.value), // Garanties additionnelles (dans tarifRC, pas dans rc!) checkStationLavage: document.getElementById('checkStationLavage')?.checked || false, checkGarageInterne: document.getElementById('checkGarageInterne')?.checked || false, checkCSE: document.getElementById('checkCSE')?.checked || false, checkTPPC: document.getElementById('checkTPPC')?.checked || false, capitalTPPC: toNumber(document.getElementById('selTPPCcapital')?.value), vehiculesTPPC: Math.max(0, Math.round(toNumber(document.getElementById('selTPPCveh')?.value))), checkPJ: document.getElementById('checkPJ')?.checked || false, tarifcommercial: toNumber(document.getElementById('tarifCom')?.value), }; console.log('tarifRCData:', tarifRCData); try { let idTarifRC; let rcId = rc?.id; // ===== VÉRIFIER SI RC EXISTE, SINON LE CRÉER ===== if (!rcId) { console.log('RC n\'existe pas encore, création en cours...'); const createRCResponse = await fetch(`/rc/create`, { method: 'POST', body: JSON.stringify({ typeCotisation: rcMainData.typeCotisation }), headers: { 'Content-Type': 'application/json' }, }); const createRCData = await createRCResponse.json(); if (createRCData.valid) { rcId = createRCData.rc.id; // Mettre à jour le contrat avec le nouvel ID RC await fetch(`/contrat/update/${contrat.produit}/${contrat.id}/${rcId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, }); } else { console.error('Échec de la création de RC:', createRCData.message); return { valid: false, message: 'Échec création RC' }; } } // ===== ÉTAPE 3: Sauvegarder/Mettre à jour tarifRC ===== if (tarif && tarif.id) { // Mettre à jour un enregistrement tarifRC existant console.log(`Mise a jour tarifRC existant (ID: ${tarif.id})`); const response = await fetch(`/rc/tarif/update/${tarif.id}`, { method: 'POST', body: JSON.stringify(tarifRCData), headers: { 'Content-Type': 'application/json', }, }); const data = await response.json(); console.log('Reponse serveur tarifRC update:', data); if (data.valid) { idTarifRC = data.tarifRc.id; } else { console.error('Echec de la mise a jour de tarifRC:', data.message); return { valid: false, message: data.message }; } } else { // Créer un nouvel enregistrement tarifRC console.log('Creation nouveau tarifRC'); const response = await fetch(`/rc/tarif/create`, { method: 'POST', body: JSON.stringify(tarifRCData), headers: { 'Content-Type': 'application/json', }, }); const data = await response.json(); console.log('Reponse serveur tarifRC create:', data); if (data.valid) { idTarifRC = data.tarifRc.id; } else { console.error('Echec de la creation de tarifRC:', data.message); return { valid: false, message: data.message }; } } // ===== ÉTAPE 4: Mettre à jour RC principal avec les données communes ET la référence tarifRC ===== rcMainData.tarifRC = idTarifRC; // Ajouter la référence à tarifRC console.log(`Mise a jour RC principal (ID: ${rcId}) avec reference tarifRC: ${idTarifRC}`); console.log('Donnees envoyees pour RC:', rcMainData); const updateRCResponse = await fetch(`/rc/update/${rcId}`, { method: 'POST', body: JSON.stringify(rcMainData), headers: { 'Content-Type': 'application/json', }, }); const updateRCData = await updateRCResponse.json(); console.log('Reponse serveur RC update:', updateRCData); if (!updateRCData.valid) { console.error('Echec de la mise a jour de RC principal:', updateRCData.message); return { valid: false, message: 'Échec mise à jour RC principal' }; } console.log('=== TARIF RC SAUVEGARDE AVEC SUCCES ==='); console.log('RC ID:', rcId, '| TarifRC ID:', idTarifRC); return { valid: true, idTarifRC, idRC: rcId }; } catch (error) { console.error('Erreur lors de la sauvegarde de tarifRC:', error); return { valid: false, message: error.message }; } } // Fonction helper pour collecter les activités complémentaires function collectActivitesComplJSON(typeActivite) { let name; switch(typeActivite.toLowerCase()) { case 'voiturier': name = 'actComplVoiturier/Loueur'; break; case 'commissionnaire': name = 'actComplCommissionnaire de Transport'; break; case 'demenageur': name = 'actComplDéménageur'; break; case 'logistique': name = 'actComplLogistique'; break; default: return JSON.stringify([]); } const checkboxes = document.querySelectorAll(`[name="${name}"] input[type="checkbox"]:checked`); const activites = []; checkboxes.forEach(cb => { const text = cb.nextElementSibling ? cb.nextElementSibling.textContent.trim() : cb.value; activites.push(text); }); return JSON.stringify(activites); } // Fonction helper pour collecter les marchandises function collectMarchandisesJSON(typeActivite) { let name; switch(typeActivite.toLowerCase()) { case 'voiturier': name = 'marVoiturier/Loueur'; break; case 'commissionnaire': name = 'marCommissionnaire de Transport'; break; case 'demenageur': name = 'marDéménageur'; break; case 'logistique': name = 'marLogistique'; break; case 'autocariste': name = 'marAutocariste'; break; case 'autres': name = 'marAutres activites'; break; default: return JSON.stringify([]); } const checkboxes = document.querySelectorAll(`[name="${name}"] input[type="checkbox"]:checked`); const marchandises = []; checkboxes.forEach(cb => { const text = cb.nextElementSibling ? cb.nextElementSibling.textContent.trim() : cb.value; marchandises.push(text); }); return JSON.stringify(marchandises); } // Fonction pour gérer les clics sur les boutons de tariffettes function setupTarifetteButtons() { const tarifetteButtons = document.querySelectorAll('.franchise-card button[name]'); tarifetteButtons.forEach(button => { button.addEventListener('click', async function(e) { e.preventDefault(); const franchiseValue = this.getAttribute('name'); // Mettre à jour la variable globale et l'UI window.franchiseChoisie = franchiseValue; console.log(`Tariffette sélectionnée: ${franchiseValue}€`); // Mettre en surbrillance la carte sélectionnée document.querySelectorAll('.franchise-card').forEach(c => c.classList.remove('selected')); this.closest('.franchise-card').classList.add('selected'); // Ouvrir le modal de tarif commercial openModalTarifCom(franchiseValue); }); }); } // Fonction pour configurer les alertes de marchandises function setupMarchandiseAlerts() { // Alerte pour Animaux Vivants document.querySelectorAll('[name^="mar"] input[type="checkbox"]').forEach(cb => { cb.addEventListener('change', function() { const label = this.nextElementSibling?.textContent.trim(); if (label && label.includes('Animaux vivants') && this.checked) { window.modalAnimauxVivants.open(); } if (label && label.includes('Transport de béton') && this.checked) { window.modalTransportBeton.open(); } }); }); } // Fonction pour configurer les alertes d'activités function setupActiviteAlerts() { // Alerte pour Autocariste + RCE const checkAutocariste = document.getElementById('checkAutocariste'); const checkRCE = document.getElementById('checkRCE'); const checkBothAutocariste = () => { if (checkAutocariste?.checked && checkRCE?.checked) { window.modalAutocaristeRCE.open(); } }; if (checkAutocariste) checkAutocariste.addEventListener('change', checkBothAutocariste); if (checkRCE) checkRCE.addEventListener('change', checkBothAutocariste); } // Seuil de différence acceptable (5%) const seuil = 5; // Fonction pour ouvrir le modal de tarif commercial function openModalTarifCom(franchiseValue) { // Récupérer le tarif de référence selon la franchise let tarifRef = 0; if (franchiseValue === '250') { tarifRef = toNumber(document.getElementById('priceFr250')?.textContent); } else if (franchiseValue === '400') { tarifRef = toNumber(document.getElementById('priceFr400')?.textContent); } else if (franchiseValue === 'mini300') { tarifRef = toNumber(document.getElementById('priceFr2000')?.textContent); } // Réinitialiser le modal document.getElementById('tarifRefText').innerText = `Tarif de Référence : ${formatNumber(tarifRef, 2)} €`; document.getElementById('tarifCom').value = ''; document.getElementById('commentaire').value = ''; document.getElementById('comm-OK').disabled = false; document.getElementById('tarifCom-error').style.display = 'none'; document.getElementById('col-commentaire').style.display = 'none'; document.getElementById('qualiteDiv').style.display = 'none'; document.getElementById('comm-OK').setAttribute('data-franchise', franchiseValue); document.getElementById('comm-OK').setAttribute('data-tarif-ref', tarifRef); // Event listener pour le champ tarif commercial const tarifComInput = document.getElementById('tarifCom'); tarifComInput.removeEventListener('input', handleTarifComInput); tarifComInput.addEventListener('input', handleTarifComInput); // Event listener pour le commentaire const commentaireInput = document.getElementById('commentaire'); commentaireInput.removeEventListener('input', handleCommentaireInput); commentaireInput.addEventListener('input', handleCommentaireInput); // Ouvrir le modal window.modalTarifCom.open(); } // Fonction pour gérer l'input du tarif commercial function handleTarifComInput(e) { const tarifCom = parseFloat(e.target.value); const tarifRef = parseFloat(document.getElementById('comm-OK').getAttribute('data-tarif-ref')); if (!tarifCom || tarifCom <= 0) { document.getElementById('qualiteDiv').style.display = 'none'; return; } const diff = 100 * (tarifCom / tarifRef); let qualitePrime = ''; let emoji = ''; let showComment = false; if (diff < (100 + seuil) && diff > (100 - seuil)) { emoji = 'mood'; showComment = false; if (diff > 100) { qualitePrime = `Tarif correct (+${(diff - 100).toFixed(2)}%)`; } else if (diff < 100) { qualitePrime = `Tarif correct (-${(100 - diff).toFixed(2)}%)`; } else { qualitePrime = 'Le juste prix'; emoji = 'thumb_up_alt'; } } else { emoji = 'mood_bad'; showComment = true; if (diff > (100 + seuil)) { qualitePrime = `Tarif trop élevé (+${(diff - 100).toFixed(2)}%)`; } else if (diff < (100 - seuil)) { qualitePrime = `Tarif trop bas (-${(100 - diff).toFixed(2)}%)`; } } document.getElementById('qualiteDiv').style.display = 'block'; document.getElementById('qualitePrime').innerText = qualitePrime; document.getElementById('modalTarifCom-icon').innerText = emoji; document.getElementById('modalTarifCom-icon').style.color = (emoji === 'mood_bad') ? 'red' : 'green'; if (showComment) { document.getElementById('tarifCom-error').style.display = 'flex'; document.getElementById('col-commentaire').style.display = 'flex'; if (!document.getElementById('commentaire').value.trim()) { document.getElementById('comm-OK').disabled = true; } } else { document.getElementById('comm-OK').disabled = false; document.getElementById('tarifCom-error').style.display = 'none'; document.getElementById('col-commentaire').style.display = 'none'; } } // Fonction pour gérer l'input du commentaire function handleCommentaireInput(e) { const commentaire = e.target.value.trim(); const tarifComError = document.getElementById('tarifCom-error').style.display !== 'none'; if (tarifComError) { document.getElementById('comm-OK').disabled = !commentaire; } } // Fonction pour gérer la validation du tarif commercial async function handleValidateTarifCom() { const tarifCom = parseFloat(document.getElementById('tarifCom').value); const commentaire = document.getElementById('commentaire').value.trim(); const franchiseValue = document.getElementById('comm-OK').getAttribute('data-franchise'); if (!tarifCom || tarifCom <= 0) { M.toast({html: 'Veuillez saisir un tarif commercial valide', classes: 'red'}); return; } if (rcTarifGuard && rcTarifGuard.refresh().length > 0) { M.toast({html: 'Corrigez les erreurs du formulaire avant de valider ce tarif.', classes: 'red'}); return; } // Fermer le modal window.modalTarifCom.close(); // Afficher un loader M.toast({html: 'Sauvegarde en cours...', classes: 'blue'}); // Sauvegarder le tarif const saveResult = await saveTarifRC(); if (saveResult && saveResult.valid) { M.toast({html: `Tarif sauvegardé avec succès !`, classes: 'green'}); // Rediriger vers la page projet après un court délai setTimeout(() => { console.log('Redirection vers projet...'); // Construire l'URL de redirection vers projet const numParcours = parcours?.numParcours; if (numParcours) { window.location.href = `navParcours?numParcours=${numParcours}&submenu=projet`; } else { M.toast({html: 'Erreur: impossible de rediriger', classes: 'red'}); } }, 1000); } else { M.toast({html: 'Erreur lors de la sauvegarde du tarif', classes: 'red'}); } } // Exposer les fonctions globalement pour y accéder depuis l'extérieur window.initSubmenuForm = init; window.saveTarifRC = saveTarifRC; })();