jQuery(function($) { jQuery('form.load_simulator').each(function() { jQuery(this).attr('action', window.location.origin + '/credit-step1') }) /** * Système de sauvegarde localStorage */ const FORM_STORAGE_KEY = 'credit_direct_form_data'; const FORM_STORAGE_TIMESTAMP = 'credit_direct_form_timestamp'; const STORAGE_EXPIRY_HOURS = 24; // Les données expirent après 24h /** * Affiche l'indicateur de sauvegarde */ function showSaveIndicator(message, type = 'success') { let indicator = jQuery('.auto-save-indicator'); if (indicator.length === 0) { indicator = jQuery('
'); jQuery('body').append(indicator); } indicator.removeClass('show saving error').addClass('show ' + type); indicator.text(message); // Masquer après 3 secondes setTimeout(() => { indicator.removeClass('show'); }, 3000); } /** * Sauvegarde les données du formulaire dans localStorage */ function saveFormData() { // Afficher l'indicateur de sauvegarde showSaveIndicator('Sauvegarde en cours...', 'saving'); const formData = {}; const currentStep = getCurrentStep(); // Récupérer tous les champs du formulaire jQuery('form.form-submit').find('input, select, textarea').each(function() { const $field = jQuery(this); const name = $field.attr('name'); const type = $field.attr('type'); if (!name) return; let value = ''; switch (type) { case 'checkbox': value = $field.is(':checked') ? $field.val() : ''; break; case 'radio': if ($field.is(':checked')) { value = $field.val(); } break; case 'file': // Ne pas sauvegarder les fichiers return; default: value = $field.val() || ''; break; } // Ne sauvegarder que si la valeur n'est pas vide if (value !== '') { formData[name] = value; } }); // Sauvegarder avec l'étape actuelle et le timestamp const dataToSave = { step: currentStep, data: formData, timestamp: Date.now() }; try { localStorage.setItem(FORM_STORAGE_KEY, JSON.stringify(dataToSave)); localStorage.setItem(FORM_STORAGE_TIMESTAMP, Date.now().toString()); console.log('Données sauvegardées dans localStorage:', dataToSave); // Afficher le succès setTimeout(() => { showSaveIndicator('Données sauvegardées', 'success'); }, 500); } catch (e) { console.error('Erreur lors de la sauvegarde localStorage:', e); showSaveIndicator('Erreur de sauvegarde', 'error'); } } /** * Restaure les données du formulaire depuis localStorage */ function restoreFormData() { try { const savedData = localStorage.getItem(FORM_STORAGE_KEY); if (!savedData) return false; const parsedData = JSON.parse(savedData); const now = Date.now(); const savedTime = parsedData.timestamp || 0; console.log('parsedData', parsedData); // Vérifier si les données ne sont pas expirées if (now - savedTime > STORAGE_EXPIRY_HOURS * 60 * 60 * 1000) { console.log('Données localStorage expirées, suppression...'); clearFormData(); return false; } const formData = parsedData.data || {}; let restoredCount = 0; // Traiter d'abord les champs de crédits en cours const currentLoansData = {}; const coCurrentLoansData = {}; // Séparer les données des crédits en cours des autres champs Object.keys(formData).forEach(name => { if (name.startsWith('currentloans[')) { const match = name.match(/currentloans\[(\d+)\]\[([^\]]+)\]/); if (match) { const index = parseInt(match[1]); const field = match[2]; if (!currentLoansData[index]) currentLoansData[index] = {}; currentLoansData[index][field] = formData[name]; } } else if (name.startsWith('cocurrentloans[')) { const match = name.match(/cocurrentloans\[(\d+)\]\[([^\]]+)\]/); if (match) { const index = parseInt(match[1]); const field = match[2]; if (!coCurrentLoansData[index]) coCurrentLoansData[index] = {}; coCurrentLoansData[index][field] = formData[name]; } } }); // Restaurer les valeurs des champs simples Object.keys(formData).forEach(name => { // Ignorer les champs de crédits en cours (traités séparément) if (name.startsWith('currentloans[') || name.startsWith('cocurrentloans[')) { return; } const $field = jQuery('form.form-submit').find(`[name="${name}"]`); if ($field.length === 0) return; const value = formData[name]; const type = $field.attr('type'); switch (type) { case 'checkbox': if (value) { $field.prop('checked', true); } break; case 'radio': $field.filter(`[value="${value}"]`).prop('checked', true); break; default: $field.val(value); break; } // Ajouter un effet visuel temporaire $field.addClass('field-restored'); setTimeout(() => { $field.removeClass('field-restored'); }, 2000); restoredCount++; }); // Restaurer les crédits en cours de l'emprunteur Object.keys(currentLoansData).forEach(index => { const loanData = currentLoansData[index]; const blockIndex = parseInt(index); // Créer le bloc si nécessaire while (jQuery('.wpcf-currentloan--block').length <= blockIndex) { addCurrentLoan(); } // Remplir les champs du bloc const $block = jQuery('.wpcf-currentloan--block').eq(blockIndex); Object.keys(loanData).forEach(field => { const $field = $block.find(`[name="currentloans[${index}][${field}]"]`); if ($field.length > 0) { $field.val(loanData[field]); $field.addClass('field-restored'); setTimeout(() => { $field.removeClass('field-restored'); }, 2000); restoredCount++; } }); // Appliquer la logique de désactivation du champ durationmonth selon le type de prêt const loanType = loanData.loantype; if (loanType === '1') { const $durationField = $block.find('input[name*="durationmonth"]'); if ($durationField.length > 0) { $durationField.prop('readonly', true); $durationField.addClass('readonly-field'); } // Mettre à jour les labels selon le type de prêt const $borrowedCapitalField = $block.find('input[name*="borrowedcapital"]'); const $monthlyPaymentField = $block.find('input[name*="monthlypayment"]'); if ($borrowedCapitalField.length > 0) { const $borrowedCapitalLabel = $borrowedCapitalField.closest('.form-group').find('label'); if ($borrowedCapitalLabel.length > 0) { $borrowedCapitalLabel.text('Plafond de la carte'); } } if ($monthlyPaymentField.length > 0) { const $monthlyPaymentLabel = $monthlyPaymentField.closest('.form-group').find('label'); if ($monthlyPaymentLabel.length > 0) { $monthlyPaymentLabel.text('Remboursement mensuel'); } } } }); // Restaurer les crédits en cours du co-emprunteur Object.keys(coCurrentLoansData).forEach(index => { const loanData = coCurrentLoansData[index]; const blockIndex = parseInt(index); // Créer le bloc si nécessaire while (jQuery('.wpcf-cocurrentloan--block').length <= blockIndex) { addCoCurrentLoan(); } // Remplir les champs du bloc const $block = jQuery('.wpcf-cocurrentloan--block').eq(blockIndex); Object.keys(loanData).forEach(field => { const $field = $block.find(`[name="cocurrentloans[${index}][${field}]"]`); if ($field.length > 0) { $field.val(loanData[field]); $field.addClass('field-restored'); setTimeout(() => { $field.removeClass('field-restored'); }, 2000); restoredCount++; } }); // Appliquer la logique de désactivation du champ durationmonth selon le type de prêt const loanType = loanData.loantype; if (loanType === '1') { const $durationField = $block.find('input[name*="durationmonth"]'); if ($durationField.length > 0) { $durationField.prop('readonly', true); $durationField.addClass('readonly-field'); } // Mettre à jour les labels selon le type de prêt const $borrowedCapitalField = $block.find('input[name*="borrowedcapital"]'); const $monthlyPaymentField = $block.find('input[name*="monthlypayment"]'); if ($borrowedCapitalField.length > 0) { const $borrowedCapitalLabel = $borrowedCapitalField.closest('.form-group').find('label'); if ($borrowedCapitalLabel.length > 0) { $borrowedCapitalLabel.text('Plafond de la carte'); } } if ($monthlyPaymentField.length > 0) { const $monthlyPaymentLabel = $monthlyPaymentField.closest('.form-group').find('label'); if ($monthlyPaymentLabel.length > 0) { $monthlyPaymentLabel.text('Remboursement mensuel'); } } } }); console.log(`${restoredCount} champs restaurés depuis localStorage`); if (restoredCount > 0) { showSaveIndicator(`${restoredCount} champs restaurés`, 'success'); } return restoredCount > 0; } catch (e) { console.error('Erreur lors de la restauration localStorage:', e); return false; } } /** * Efface les données du formulaire du localStorage */ function clearFormData() { try { localStorage.removeItem(FORM_STORAGE_KEY); localStorage.removeItem(FORM_STORAGE_TIMESTAMP); console.log('Données localStorage effacées'); // Masquer le message d'information jQuery('.localstorage-info').fadeOut(); // Afficher un message de confirmation showSaveIndicator('Données effacées', 'success'); } catch (e) { console.error('Erreur lors de l\'effacement localStorage:', e); showSaveIndicator('Erreur lors de l\'effacement', 'error'); } } // Rendre la fonction accessible globalement window.clearFormData = clearFormData; /** * Détermine l'étape actuelle basée sur l'URL */ function getCurrentStep() { const path = window.location.pathname; if (path.includes('credit-step1')) return 'step1'; if (path.includes('credit-step2')) return 'step2'; if (path.includes('credit-step3')) return 'step3'; if (path.includes('credit-step4')) return 'step4'; if (path.includes('credit-step5')) return 'step5'; return 'unknown'; } /** * Sauvegarde automatique lors de la modification des champs */ function setupAutoSave() { // Sauvegarde sur clic du lien "previous-step" jQuery('a.previous-step').on('click', function(e) { /* e.preventDefault(); */ saveFormData(); }); // Sauvegarde sur changement de valeur (désactivé) // jQuery('form.form-submit').on('change input', 'input, select, textarea', function() { // // Debounce pour éviter trop de sauvegardes // clearTimeout(window.autoSaveTimeout); // window.autoSaveTimeout = setTimeout(saveFormData, 1000); // }); // Sauvegarde sur clic des boutons radio/checkbox (désactivé) // jQuery('form.form-submit').on('click', 'input[type="radio"], input[type="checkbox"]', function() { // setTimeout(saveFormData, 100); // }); // Sauvegarde avant soumission du formulaire (désactivé) // jQuery('form.form-submit').on('submit', function() { // saveFormData(); // }); // Sauvegarde avant navigation (bouton étape précédente) (désactivé) // jQuery('a[href*="credit-step"]').on('click', function() { // saveFormData(); // }); // Sauvegarde lors de l'ajout/suppression de crédits en cours (désactivé) // $(document).on('currentLoanAdded currentLoanRemoved', function() { // setTimeout(saveFormData, 500); // }); } /** * Affiche le message d'information localStorage */ function showLocalStorageInfo() { const savedData = localStorage.getItem(FORM_STORAGE_KEY); if (!savedData) return; // Vérifier si le message existe déjà if (jQuery('.localstorage-info').length > 0) return; const infoHtml = `
💾 Vos données sont sauvegardées automatiquement.
`; // Insérer au début du formulaire jQuery('form.form-submit').prepend(infoHtml); } /** * Initialise le système de sauvegarde */ function initFormStorage() { if(hasCreditDirectToken()) return; //check if localStorage is empty /* if(localStorage.getItem(FORM_STORAGE_KEY) === null) return; */ // Restaurer les données au chargement const restored = restoreFormData(); // Configurer la sauvegarde automatique setupAutoSave(); // Effacer les données après soumission réussie (optionnel) jQuery(document).on('formSubmitted', function() { clearFormData(); }); // Si des données ont été restaurées, initialiser l'affichage des zones conditionnelles if (restored) { setTimeout(function() { initializePreFilledValues(); }, 200); } // Afficher le message d'information showLocalStorageInfo(); console.log('Système de sauvegarde localStorage initialisé'); } function hasCreditDirectToken() { return window.location.search.includes('credit-direct-token'); } // Initialiser le système de sauvegarde UNIQUEMENT si le paramètre est présent dans l'URL // (déplacé après la définition des fonctions addCurrentLoan et addCoCurrentLoan) if (hasCreditDirectToken()) { } /** * General */ function empty(value) { return value == undefined || value == null || value == [] || value == ''; } function makeLabelRequired(containerSelector = 'body') { if (jQuery(containerSelector).length > 0) { jQuery(containerSelector).find('input:required, select:required, textarea:required').each(function() { var labelId = jQuery(this).attr('id'); var label = jQuery('label[for="' + labelId + '"]') if (label.length > 0 && label.html().charAt(label.html().length - 1) !== '*') { label.html(label.html() + '*') } }) } } // Marque les champs initialement requis pour pouvoir restaurer l'attribut required quand on ré-affiche une zone function markInitialRequired(containerSelector) { if (jQuery(containerSelector).length > 0) { jQuery(containerSelector).find('input, select, textarea').each(function() { var $el = jQuery(this); if ($el.is('[required]')) { $el.attr('data-initial-required', '1'); } var pattern = $el.attr('pattern'); if (typeof pattern !== 'undefined' && pattern !== null && pattern !== '') { $el.attr('data-initial-pattern', '1'); $el.attr('data-initial-pattern-value', pattern); } }); } } // Active/désactive l'attribut pattern pour les seuls champs date (input.input-date) du conteneur function togglePatternForContainer($container, isVisible) { if (!$container || $container.length === 0) { return; } $container.find('input.input-date').each(function() { var $el = jQuery(this); var defaultDatePattern = '\\d{1,2}/\\d{1,2}/\\d{4}'; // Capture paresseuse du pattern initial si non marqué mais présent var currentPattern = $el.attr('pattern'); if (!$el.attr('data-initial-pattern') && typeof currentPattern !== 'undefined' && currentPattern !== '') { $el.attr('data-initial-pattern', '1'); $el.attr('data-initial-pattern-value', currentPattern); } if (isVisible) { if ($el.attr('data-initial-pattern') === '1') { $el.attr('pattern', $el.attr('data-initial-pattern-value')); } else { // Si aucun pattern initial, appliquer le pattern date par défaut $el.attr('pattern', defaultDatePattern); } } else { $el.removeAttr('pattern'); } }); } // Retire l'attribut required de tous les champs dans les sections cachées // Cette fonction est appelée avant la soumission du formulaire pour éviter l'erreur // "An invalid form control with name='...' is not focusable" function removeRequiredFromHiddenFields() { // Retirer required uniquement des champs qui sont VRAIMENT cachés (display:none ou visibility:hidden) // Ne pas toucher aux champs visibles même s'ils ont la classe d-none (qui peut être retirée dynamiquement) jQuery('input, select, textarea').each(function() { var $field = jQuery(this); var $fieldElement = $field[0]; // Vérifier si le champ est vraiment caché (display:none ou dans un élément avec display:none) var isReallyHidden = false; // Vérifier d'abord si l'élément lui-même est caché if ($fieldElement.offsetParent === null && $field.css('display') === 'none') { isReallyHidden = true; } else { // Vérifier si un parent est caché avec display:none ou a la classe 'hidden' var $parent = $field.parent(); while ($parent.length > 0 && !$parent.is('form') && !$parent.is('body')) { // Ignorer les sections co-emprunteur qui pourraient être visibles if ($parent.hasClass('wpcf-coborrower') || $parent.hasClass('co-borrower-section') || $parent.hasClass('co-emprunteur')) { // Si la section co-emprunteur n'est pas vraiment cachée, ne pas retirer required if ($parent.css('display') !== 'none' && $parent.is(':visible') && !$parent.hasClass('hidden')) { break; } } // Vérifier aussi la classe 'hidden' pour les sections comme independent_section if ($parent.css('display') === 'none' || !$parent.is(':visible') || $parent.hasClass('hidden')) { isReallyHidden = true; break; } $parent = $parent.parent(); } } // Retirer required seulement si vraiment caché ET que ce n'est pas un champ du co-emprunteur visible if (isReallyHidden) { // Exception : ne pas retirer required des champs du co-emprunteur si celui-ci est activé // et que la section est visible (même si elle a la classe d-none) var hasCoBorrower = jQuery('input[name="hascoborrower"]:checked').length > 0 && jQuery('input[name="hascoborrower"]:checked').val() == '1'; if (hasCoBorrower) { var $coSection = $field.closest('.wpcf-coborrower, .co-borrower-section, .co-emprunteur'); if ($coSection.length > 0) { // Vérifier si la section co-emprunteur est visible malgré la classe d-none/hidden if ($coSection.is(':visible') && $coSection.css('display') !== 'none' && !$coSection.hasClass('hidden')) { // Vérifier aussi si le champ est dans une sous-section cachée (comme independent_section) var $hiddenSubSection = $field.closest('.hidden'); if ($hiddenSubSection.length === 0) { // Ne pas retirer required si la section est visible et pas dans une sous-section cachée return; } } } } $field.removeAttr('required'); } }); } if (jQuery('.wpcf-borrower').length > 0) { makeLabelRequired('.wpcf-borrower') } else { makeLabelRequired('.form-submit') } // Enregistrer l'état initial des champs requis/pattern (global + zones clés) markInitialRequired('.form-submit'); markInitialRequired('.co-borrower-section'); markInitialRequired('.wpcf-coborrower'); markInitialRequired('.isIndependant'); /** * Step 2 */ function changeJobLabels(value, container) { var jobLabels = { independent: { factory: 'de l\'entreprise', salary: 'Revenu imposable annuel', startDate: 'de début d\'activité' }, withoutEmployer: { employer: 'de l\'employeur/organisme de paiement', salary: 'Indemnités mensuelles', startDate: 'd\'engagement' }, withEmployer: { employer: 'de l\'employeur/organisme de paiement', salary: 'Salaire net mensuel', startDate: 'd\'engagement' } }, job = { independent: 5, withoutEmployer: ['1','6','9','12'] }; console.log('job', value, job.independent); var prefix = container.find('#IDcofirstname').length > 0 ? 'co' : '' var fields = ['emname', 'emaddress', 'emzip', 'emcity', 'emcountry', 'commitmentdate', 'salary'] fields.forEach(id => { var labelText = null switch (id) { case 'commitmentdate': if(value === job.independent) { labelText = jobLabels.independent.startDate } else if(job.withoutEmployer.includes(value)) { labelText = jobLabels.withoutEmployer.startDate } else { labelText = jobLabels.withEmployer.startDate } break; case 'salary': if(value === job.independent) { labelText = jobLabels.independent.salary } else if(job.withoutEmployer.includes(value)) { labelText = jobLabels.withoutEmployer.salary } else { labelText = jobLabels.withEmployer.salary } break; default: if(value === job.independent) { labelText = jobLabels.independent.factory } else if(job.withoutEmployer.includes(value)) { labelText = jobLabels.withoutEmployer.employer } else { labelText = jobLabels.withEmployer.employer } break; } container.find('label[for="ID' + prefix + id + '"] > span').text(labelText); console.log('labelText', labelText); }); /* console.log('container', container); console.log('prefix', container.find('.'+prefix+'independent_section')); */ console.log('value', value); excludedFields = ['IDnotice_of_assessment', 'IDpaycheck', 'IDcopaycheck', 'IDconotice_of_assessment']; if(value == job.independent) { container.find('.'+prefix+'independent_section').fadeIn().removeClass('hidden'); //remove required attributes container.find('.'+prefix+'employee_section').find('input, select').removeAttr('required'); container.find('.'+prefix+'employee_section').fadeOut().addClass('hidden'); container.find('.'+prefix+'independent_section').find('input, select').each(function() { var fieldId = jQuery(this).attr('id'); if (excludedFields.indexOf(fieldId) === -1) { jQuery(this).attr('required', true); } }); // Patterns: visible sur independent_section, masqué sur employee_section togglePatternForContainer(container.find('.'+prefix+'independent_section'), true); togglePatternForContainer(container.find('.'+prefix+'employee_section'), false); } else if(job.withoutEmployer.includes(value)) { container.find('.withemployer_section').fadeOut().addClass('hidden'); container.find('.withemployer_section').find('input, select').removeAttr('required'); togglePatternForContainer(container.find('.withemployer_section'), false); // Retirer aussi required des champs de la section indépendant (cas où on passe de indépendant à sans employeur) container.find('.'+prefix+'independent_section').fadeOut().addClass('hidden'); container.find('.'+prefix+'independent_section').find('input, select').removeAttr('required'); togglePatternForContainer(container.find('.'+prefix+'independent_section'), false); console.log((jobLabels.withoutEmployer.salary)); /* container.find('.'+prefix+'IDsalary').prev().text(jobLabels.withoutEmployer.salary); console.log(container.find('#'+prefix+'IDsalary').prev()); */ } else { container.find('.'+prefix+'independent_section').fadeOut().addClass('hidden'); //not input field required container.find('.'+prefix+'employee_section').find('input:not([type="file"]), select').attr('required', true); container.find('.'+prefix+'employee_section').fadeIn().removeClass('hidden'); //except file inputs container.find('.'+prefix+'independent_section').find('input, select').removeAttr('required'); if(jQuery('.withemployer_section').hasClass('hidden')) { jQuery('.withemployer_section').fadeIn().removeClass('hidden'); jQuery('.withemployer_section').find('input, select').attr('required', true); togglePatternForContainer(jQuery('.withemployer_section'), true); } // Patterns: visible sur employee_section, masqué sur independent_section togglePatternForContainer(container.find('.'+prefix+'employee_section'), true); togglePatternForContainer(container.find('.'+prefix+'independent_section'), false); } container .find('#ID' + prefix + 'emnumber') .attr('required', value === job.independent) .parent() .toggleClass('d-none', value !== job.independent) makeLabelRequired() } // Ajouter un gestionnaire sur les boutons submit pour retirer required AVANT la validation HTML5 jQuery('body').on('click', '.form-submit button[type="submit"]', function(e) { // Retirer l'attribut required de tous les champs dans les sections cachées // AVANT la validation HTML5 pour éviter l'erreur "not focusable" removeRequiredFromHiddenFields(); }); function displayCoBorrower() { // Éviter les exécutions multiples if (displayCoBorrower._isRunning) { return; } displayCoBorrower._isRunning = true; var excludedFieldsName = [ 'cocontract_type', 'coemname', 'coemaddress', 'coemzip', 'coemcity', 'coemcountry', 'cocommitmentdate', 'cooiamouthmealvoucher', 'cooiamouthrentalincome', 'cooiamouthunemployment', 'cooiamouthother', 'cooiothertext', 'co_secteur_activite', 'co_num_tva', 'coMtn_loyer', 'coHabitation_loyer' ], hasCoBorrower = jQuery('input[name="hascoborrower"]:checked').length > 0 && jQuery('input[name="hascoborrower"]:checked').val() == 1, key = '.wpcf-coborrower', cols_key = '.credit-cols'; console.log('hasCoBorrower', hasCoBorrower); // Gestion de l'affichage/masquage jQuery(key).toggleClass('d-none', !hasCoBorrower); jQuery('.co-borrower-section').toggleClass('d-none', !hasCoBorrower); // Note: .co-emprunteur est géré dans cd_main.js pour le formulaire one-step // Gérer l'attribut required/pattern en fonction de la visibilité var coContainers = jQuery(key).add('.co-borrower-section'); var cojobValue = jQuery('select[name="cojob"]').val(); var isCojobIndependent = cojobValue === '5'; if (hasCoBorrower) { coContainers.find('input, select, textarea').each(function() { var wasRequired = jQuery(this).attr('data-initial-required') === '1'; if (wasRequired) { jQuery(this).attr('required', true); } else { jQuery(this).removeAttr('required'); } }); // Gestion des patterns selon le statut indépendant du cojob // Activer les patterns pour tous les conteneurs sauf co-independent_section / coindependent_section togglePatternForContainer(coContainers.not('.co-independent_section, .coindependent_section'), true); // Pour co-independent_section / coindependent_section, activer le pattern uniquement si cojob = 5 if (isCojobIndependent) { togglePatternForContainer(jQuery('.co-independent_section, .coindependent_section'), true); } else { togglePatternForContainer(jQuery('.co-independent_section, .coindependent_section'), false); } } else { coContainers.find('input, select, textarea').removeAttr('required'); togglePatternForContainer(coContainers, false); } makeLabelRequired(); // Gestion des colonnes if (hasCoBorrower) { // Mode deux colonnes jQuery(cols_key).removeClass('col-md-12').addClass('col-md-6'); // Ajuster les colonnes internes jQuery('.credit-cols [class*="col-md-"]').each(function() { var classes = jQuery(this).attr('class').split(' '); classes.forEach(function(className) { if (className.startsWith('col-md-')) { var currentSize = parseInt(className.replace('col-md-', '')); console.log('currentSize', currentSize); // Sauvegarder la taille originale si ce n'est pas déjà fait if (!jQuery(this).data('original-size')) { jQuery(this).data('original-size', className); } // Définir la nouvelle taille jQuery(this).removeClass(className).addClass('col-md-12'); } }.bind(this)); }); // Ajuster spécifiquement les colonnes dans .other-credit-cols et ses sous-éléments jQuery('.other-credit-cols').find('[class*="col-md-"]').each(function() { var classes = jQuery(this).attr('class').split(' '); classes.forEach(function(className) { if (className.startsWith('col-md-')) { var currentSize = parseInt(className.replace('col-md-', '')); console.log('currentSize other-credit-cols', currentSize); // Sauvegarder la taille originale si ce n'est pas déjà fait if (!jQuery(this).data('original-size')) { jQuery(this).data('original-size', className); } // Définir la nouvelle taille pour .other-credit-cols et ses sous-éléments jQuery(this).removeClass(className).addClass('col-md-6'); } }.bind(this)); }); } else { // Mode une colonne jQuery('.credit-cols .wpcf-borrower').removeClass('col-md-6'); if(!jQuery(cols_key).hasClass('col-md-12')) jQuery(cols_key).addClass('col-md-12').removeClass('col-md-6'); // Restaurer les colonnes internes à leur taille d'origine jQuery('.credit-cols [class*="col-md-"]').each(function() { var originalSize = jQuery(this).data('original-size'); if (originalSize) { // Supprimer toutes les classes col-md-* actuelles var classes = jQuery(this).attr('class').split(' '); classes.forEach(function(className) { if (className.startsWith('col-md-')) { jQuery(this).removeClass(className); } }.bind(this)); // Restaurer la taille originale jQuery(this).addClass(originalSize); } }); // Pour .other-credit-cols, forcer une taille appropriée même sans co-emprunteur jQuery('.other-credit-cols').find('[class*="col-md-"]').each(function() { // Supprimer toutes les classes col-md-* actuelles var classes = jQuery(this).attr('class').split(' '); classes.forEach(function(className) { if (className.startsWith('col-md-')) { jQuery(this).removeClass(className); } }.bind(this)); // Toujours appliquer col-md-6 pour .other-credit-cols, même sans co-emprunteur jQuery(this).addClass('col-md-6'); }); } // Gestion des champs requis jQuery(key).find('input[type="text"], input[type="number"], input[type="date"], select, textarea').each(function() { if (excludedFieldsName.indexOf(jQuery(this).attr('name')) < 0) { jQuery(this).attr('required', hasCoBorrower) } }) if (!hasCoBorrower) { jQuery(key).find('input[type="checkbox"]:checked, input[type="radio"][value="0"]').each(function() { jQuery(this).prop('checked', jQuery(this).attr('type') === 'radio') }) jQuery(key).find('input:not([type="checkbox"]):not([type="radio"]), select, textarea').each(function() { jQuery(this).val('') }) } makeLabelRequired(key) // Afficher les autres revenus si nécessaire (même si cojob = 5) displayOtherIncome( hasCoBorrower && jQuery('input[name="cohasotherincome"]:checked').length > 0 && jQuery('input[name="cohasotherincome"]:checked').val() == 1, '.co' ); // Réinitialiser le flag après un court délai setTimeout(function() { displayCoBorrower._isRunning = false; }, 50); } function displayOtherIncome(show, key) { var target = jQuery('.wpcf-otherincome' + key) if (target.length > 0) { target.toggleClass('d-none', !show); if (!show) { target.find('input[type="checkbox"]:checked, input[type="radio"][value="0"]').each(function() { jQuery(this).prop('checked', jQuery(this).attr('type') === 'radio') }) } displayOtherIncomeSubFields(target.find('input[type="checkbox"], input[type="radio"]:checked')) } } function displayOtherIncomeSubFields(el) { el.each(function() { var subFields = jQuery(this).closest('.form-row').find('.form-group') if (subFields.length > 0) { subFields.toggleClass('d-none', !jQuery(this).is(':checked')) subFields.find('input').attr('required', jQuery(this).is(':checked')) } }) } function initializePreFilledValues() { // Gérer l'affichage des sections basé sur les valeurs pré-remplies setTimeout(function() { // Vérifier si hascoborrower est pré-rempli var hasCoBorrowerChecked = jQuery('input[name="hascoborrower"]:checked'); if (hasCoBorrowerChecked.length > 0) { displayCoBorrower(); } // Vérifier si hasotherincome est pré-rempli (emprunteur) var hasOtherIncomeChecked = jQuery('input[name="hasotherincome"]:checked'); if (hasOtherIncomeChecked.length > 0) { displayOtherIncome(hasOtherIncomeChecked.val() == 1, '.em'); } // Vérifier si cohasotherincome est pré-rempli (co-emprunteur) var coHasOtherIncomeChecked = jQuery('input[name="cohasotherincome"]:checked'); if (coHasOtherIncomeChecked.length > 0) { displayOtherIncome(coHasOtherIncomeChecked.val() == 1, '.co'); } // Vérifier si hascurrentloan est pré-rempli var hasCurrentLoanChecked = jQuery('input[name="hascurrentloan"]:checked'); if (hasCurrentLoanChecked.length > 0) { var isChecked = hasCurrentLoanChecked.val() == '1'; jQuery('.wpcf-currentloan').toggleClass('d-none', !isChecked); } // Vérifier si cohascurrentloan est pré-rempli var coHasCurrentLoanChecked = jQuery('input[name="cohascurrentloan"]:checked'); if (coHasCurrentLoanChecked.length > 0) { var isChecked = coHasCurrentLoanChecked.val() == '1'; jQuery('.wpcf-cocurrentloan').toggleClass('d-none', !isChecked); } // Gérer les champs autres revenus pré-remplis jQuery('.wpcf-otherincome input[type="checkbox"]:checked').each(function() { displayOtherIncomeSubFields(jQuery(this)); }); // Gérer les champs indépendant pré-remplis var jobSelect = jQuery('select[name="job"]'); if (jobSelect.length > 0 && jobSelect.val()) { changeJobLabels(jobSelect.val(), jobSelect.closest('fieldset')); } var coJobSelect = jQuery('select[name="cojob"]'); if (coJobSelect.length > 0 && coJobSelect.val()) { changeJobLabels(coJobSelect.val(), coJobSelect.closest('fieldset')); } }, 100); // Petit délai pour s'assurer que le DOM est chargé } var hasCoBorrowerField = jQuery('input[name="hascoborrower"]:checked') if (hasCoBorrowerField.length > 0) { displayCoBorrower() } if (jQuery('input[name="hasotherincome"]:checked').length > 0) { displayOtherIncome(jQuery('input[name="hasotherincome"]:checked').val() == 1, '.em') } // Initialisation pour les valeurs pré-remplies initializePreFilledValues(); // Flag pour éviter les déclenchements multiples var isProcessingJobChange = false; jQuery('body').on('change', 'input[name="hascoborrower"]', function() { // Éviter les déclenchements multiples quand cojob = 5 if (!isProcessingJobChange) { displayCoBorrower(); } }); jQuery('body').on('change', 'input[name="hasotherincome"]', function() { displayOtherIncome(jQuery(this).val() == 1, '.em') }); jQuery('body').on('change', 'input.wpcf-otherincome--oitype', function(e) { displayOtherIncomeSubFields(jQuery(e.target)) }); jQuery('body').on('change', 'input[name="cohasotherincome"]', function() { displayOtherIncome(jQuery(this).val() == 1, '.co') }); jQuery('body').on('change', 'select[name="job"], select[name="cojob"]', function() { var jobValue = jQuery(this).val(); var isCojob = jQuery(this).attr('name') === 'cojob'; var container = jQuery(this).closest('fieldset'); // Activer le flag pour éviter les déclenchements multiples isProcessingJobChange = true; // Appeler changeJobLabels changeJobLabels(jobValue, container); // Si c'est cojob, mettre à jour les patterns dans displayCoBorrower if (isCojob) { var hasCoBorrower = jQuery('input[name="hascoborrower"]:checked').length > 0 && jQuery('input[name="hascoborrower"]:checked').val() == 1; if (hasCoBorrower) { // Mettre à jour les patterns pour co-independent_section / coindependent_section selon la valeur if (jobValue == '5') { togglePatternForContainer(jQuery('.co-independent_section, .coindependent_section'), true); } else { togglePatternForContainer(jQuery('.co-independent_section, .coindependent_section'), false); } } } // Si c'est cojob et valeur = 5, éviter de déclencher displayCoBorrower en cascade if (isCojob && jobValue == '5') { // Attendre un peu avant de réactiver pour éviter les cascades setTimeout(function() { isProcessingJobChange = false; }, 100); } else { isProcessingJobChange = false; } }); /** * Step 3 */ jQuery('body').on('change', 'select[name="hometype"], select[name="cohometype"]', function(e) { var val = jQuery(this).val(), c = 'd-none', cname = 'wpcf-' + jQuery(this).attr('name') + '--complement-home' + val, elements = jQuery('.wpcf-' + jQuery(this).attr('name') + '--complement .form-group'); elements.addClass(c); elements.each(function() { if (jQuery(this).hasClass(cname)) { jQuery('.' + cname).removeClass(c); } jQuery(this).find('input, select').attr('required', !jQuery(this).hasClass(c)) // Synchroniser aussi le pattern avec la visibilité (comme required) togglePatternForContainer(jQuery(this), !jQuery(this).hasClass(c)) makeLabelRequired(jQuery(this).context) }) }); var currentLoanProto = jQuery('.wpcf-currentloan--block').length > 0 ? jQuery('.wpcf-currentloan--block') : null; var currentLoanCount = 0; if (currentLoanProto !== null) { currentLoanProto.removeClass('wpcf-prototype'); currentLoanProto = currentLoanProto[0].outerHTML jQuery('.wpcf-currentloan--block').remove() } var coCurrentLoanProto = jQuery('.wpcf-cocurrentloan--block').length > 0 ? jQuery('.wpcf-cocurrentloan--block') : null; var coCurrentLoanCount = 0; if (coCurrentLoanProto !== null) { coCurrentLoanProto.removeClass('wpcf-prototype'); coCurrentLoanProto = coCurrentLoanProto[0].outerHTML jQuery('.wpcf-cocurrentloan--block').remove() } jQuery('body').on('change', 'input[name="hascurrentloan"], input[name="cohascurrentloan"]', function(e) { var selector = jQuery(this).attr('name') === 'hascurrentloan' ? '.wpcf-currentloan' : '.wpcf-cocurrentloan'; var elem = jQuery(selector), c = 'd-none'; // Pour les radio, on vérifie la value sélectionnée var isChecked = jQuery('input[name="' + jQuery(this).attr('name') + '"]:checked').val() == '1'; elem.toggleClass(c, !isChecked); if (!isChecked) { jQuery(this).attr('name') === 'hascurrentloan' ? jQuery('.wpcf-currentloan--block').remove() : jQuery('.wpcf-cocurrentloan--block').remove(); } else if (elem.find(selector + '--block').length < 1) { jQuery(this).attr('name') === 'hascurrentloan' ? addCurrentLoan() : addCoCurrentLoan(); } }); // Affichage initial au chargement de la page function updateCurrentLoanVisibility() { var isChecked = jQuery('input[name="hascurrentloan"]:checked').val() == '1'; jQuery('.wpcf-currentloan').toggleClass('d-none', !isChecked); if (!isChecked) { jQuery('.wpcf-currentloan--block').remove(); } else if (jQuery('.wpcf-currentloan--block').length < 1) { addCurrentLoan(); } var isCoChecked = jQuery('input[name="cohascurrentloan"]:checked').val() == '1'; jQuery('.wpcf-cocurrentloan').toggleClass('d-none', !isCoChecked); if (!isCoChecked) { jQuery('.wpcf-cocurrentloan--block').remove(); } else if (jQuery('.wpcf-cocurrentloan--block').length < 1) { addCoCurrentLoan(); } } // Appel au chargement updateCurrentLoanVisibility(); // Initialiser l'état des champs durationmonth et labels selon les types de prêt existants function initializeDurationMonthFields() { jQuery('.wpcf-currentloan--block, .wpcf-cocurrentloan--block').each(function() { const $block = jQuery(this); const $loanTypeField = $block.find('select[name*="loantype"]'); const $durationField = $block.find('input[name*="durationmonth"]'); const $borrowedCapitalField = $block.find('input[name*="borrowedcapital"]'); const $monthlyPaymentField = $block.find('input[name*="monthlypayment"]'); if ($loanTypeField.length > 0) { const selectedValue = $loanTypeField.val(); // Gestion du champ durationmonth if ($durationField.length > 0) { if (selectedValue === '1') { $durationField.prop('readonly', true); $durationField.addClass('readonly-field'); } else { $durationField.prop('readonly', false); $durationField.removeClass('readonly-field'); } } // Gestion des labels borrowedcapital if ($borrowedCapitalField.length > 0) { const $borrowedCapitalLabel = $borrowedCapitalField.closest('.form-group').find('label'); if ($borrowedCapitalLabel.length > 0) { if (selectedValue === '1') { $borrowedCapitalLabel.text('Plafond de la carte'); } else { $borrowedCapitalLabel.text('Capital emprunté'); } } } // Gestion des labels monthlypayment if ($monthlyPaymentField.length > 0) { const $monthlyPaymentLabel = $monthlyPaymentField.closest('.form-group').find('label'); if ($monthlyPaymentLabel.length > 0) { if (selectedValue === '1') { $monthlyPaymentLabel.text('Remboursement mensuel'); } else { $monthlyPaymentLabel.text('Mensualité'); } } } } }); } // Initialiser au chargement de la page initializeDurationMonthFields(); // Initialiser le système de sauvegarde après la définition des fonctions addCurrentLoan et addCoCurrentLoan initFormStorage(); jQuery('body').on('click', 'button.wpcf-currentload--add, button.wpcf-cocurrentload--add', function(e) { e.preventDefault() jQuery(this).hasClass('wpcf-currentload--add') ? addCurrentLoan() : addCoCurrentLoan() }) function addCurrentLoan() { var newLoan = currentLoanProto.replaceAll('__number__', currentLoanCount) jQuery(newLoan).insertBefore(jQuery('.wpcf-currentload--add')) currentLoanCount++ // Déclencher l'événement personnalisé pour la sauvegarde $(document).trigger('currentLoanAdded'); } function addCoCurrentLoan() { var newLoan = coCurrentLoanProto.replaceAll('__number__', coCurrentLoanCount) jQuery(newLoan).insertBefore(jQuery('.wpcf-cocurrentload--add')) coCurrentLoanCount++ // Déclencher l'événement personnalisé pour la sauvegarde $(document).trigger('currentLoanAdded'); } jQuery('body').on('click', '.wpcf-currentloan--block--remove, .wpcf-cocurrentloan--block--remove', function(e) { e.preventDefault(); jQuery(this).parent().remove(); // Déclencher l'événement personnalisé pour la sauvegarde $(document).trigger('currentLoanRemoved'); }); // Gestionnaire pour les champs de type de prêt des crédits en cours jQuery('body').on('change', 'select[name*="loantype"]', function() { const selectedValue = jQuery(this).val(); const $currentBlock = jQuery(this).closest('.wpcf-currentloan--block, .wpcf-cocurrentloan--block'); console.log('Type de prêt sélectionné:', selectedValue, 'dans le bloc:', $currentBlock); // Gestion du champ durationmonth selon le type de prêt const $durationField = $currentBlock.find('input[name*="durationmonth"]'); if ($durationField.length > 0) { if (selectedValue === '1') { // Si le type de prêt vaut "1", rendre le champ durationmonth readonly $durationField.prop('readonly', true); $durationField.addClass('readonly-field'); console.log('Champ durationmonth mis en readonly'); } else { // Sinon, rendre le champ éditable $durationField.prop('readonly', false); $durationField.removeClass('readonly-field'); console.log('Champ durationmonth rendu éditable'); } } // Gestion des labels des champs borrowedcapital et monthlypayment const $borrowedCapitalField = $currentBlock.find('input[name*="borrowedcapital"]'); const $monthlyPaymentField = $currentBlock.find('input[name*="monthlypayment"]'); if ($borrowedCapitalField.length > 0) { const $borrowedCapitalLabel = $borrowedCapitalField.closest('.form-group').find('label'); if ($borrowedCapitalLabel.length > 0) { if (selectedValue === '1') { $borrowedCapitalLabel.text('Plafond de la carte'); } else { $borrowedCapitalLabel.text('Capital emprunté'); } } } if ($monthlyPaymentField.length > 0) { const $monthlyPaymentLabel = $monthlyPaymentField.closest('.form-group').find('label'); if ($monthlyPaymentLabel.length > 0) { if (selectedValue === '1') { $monthlyPaymentLabel.text('Remboursement mensuel'); } else { $monthlyPaymentLabel.text('Mensualité'); } } } // Ici vous pouvez ajouter la logique spécifique selon le type de prêt sélectionné // Par exemple, afficher/masquer d'autres champs selon le type // Exemple de logique conditionnelle selon le type de prêt switch(selectedValue) { case '1': // Hypothèque console.log('Prêt hypothécaire sélectionné'); // Logique spécifique pour les prêts hypothécaires break; case '2': // Prêt personnel console.log('Prêt personnel sélectionné'); // Logique spécifique pour les prêts personnels break; case '3': // Crédit auto console.log('Crédit auto sélectionné'); // Logique spécifique pour les crédits auto break; default: console.log('Autre type de prêt sélectionné:', selectedValue); break; } // Déclencher l'événement personnalisé pour la sauvegarde $(document).trigger('currentLoanTypeChanged', { value: selectedValue, block: $currentBlock }); }); // Fonction pour afficher/cacher la zone isFichedDisplay function displayIsFichedZone() { var hasFiched = jQuery('input[name="isFiched"]:checked').length > 0 && jQuery('input[name="isFiched"]:checked').val() == 1; jQuery('.isFichedDisplay').toggleClass('d-none', !hasFiched); } // Vérifier l'état initial var isFichedField = jQuery('input[name="isFiched"]:checked'); if (isFichedField.length > 0) { displayIsFichedZone(); } // Ajouter l'écouteur d'événement pour le changement jQuery('body').on('change', 'input[name="isFiched"]', displayIsFichedZone); // Fonction pour afficher/cacher la zone isIndependant function displayIsIndependantZone() { var isIndependant = jQuery('select[name="job"]').val() === '5'; var zone = jQuery('.isIndependant'); zone.toggleClass('d-none', !isIndependant); // Gérer l'attribut required/pattern en fonction de la visibilité if (isIndependant) { zone.find('input, select, textarea').each(function() { var wasRequired = jQuery(this).attr('data-initial-required') === '1'; if (wasRequired) { jQuery(this).attr('required', true); } else { jQuery(this).removeAttr('required'); } }); togglePatternForContainer(zone, true); } else { zone.find('input, select, textarea').removeAttr('required'); togglePatternForContainer(zone, false); } makeLabelRequired(); } // Vérifier l'état initial var jobSelect = jQuery('select[name="job"]'); if (jobSelect.length > 0) { displayIsIndependantZone(); } // Ajouter l'écouteur d'événement pour le changement jQuery('body').on('change', 'select[name="job"]', displayIsIndependantZone); // Fonction pour afficher/cacher la zone hasLoyer function displayHasLoyerZone() { var isLocataire = jQuery('select[name="habitation_type"]').val() === 'locataire'; jQuery('.hasLoyer').toggleClass('d-none', !isLocataire); } // Vérifier l'état initial var situationSelect = jQuery('select[name="habitation_type"]'); if (situationSelect.length > 0) { displayHasLoyerZone(); } // Ajouter l'écouteur d'événement pour le changement jQuery('body').on('change', 'select[name="habitation_type"]', displayHasLoyerZone); // Affichage de la taille totale des fichiers sélectionnés pour le champ IDpaycheck + vérification taille max 2 Mo jQuery('body').on('change', '#IDpaycheck, #IDcopaycheck', function(e) { var files = e.target.files; var totalSize = 0; var maxSize = 2 * 1024 * 1024; // 2 Mo en octets var errorFiles = []; for (var i = 0; i < files.length; i++) { totalSize += files[i].size; if (files[i].size > maxSize) { errorFiles.push(files[i].name); } } // Conversion en Mo, arrondi à 2 décimales var totalSizeMo = (totalSize / (1024 * 1024)).toFixed(2); // Création ou sélection de l'élément d'affichage var infoId = 'paycheck-size-info'; var infoElem = jQuery('#' + infoId); if (infoElem.length === 0) { infoElem = jQuery('
'); jQuery(this).after(infoElem); } // Création ou sélection de l'élément d'erreur var errorId = 'paycheck-size-error'; var errorElem = jQuery('#' + errorId); if (errorElem.length === 0) { errorElem = jQuery('
'); infoElem.after(errorElem); } var submitBtn = jQuery('.form-submit button[type="submit"]'); if (files.length > 0) { infoElem.text('Taille totale des fichiers sélectionnés : ' + totalSizeMo + ' Mo'); } else { infoElem.text(''); } if (errorFiles.length > 0) { errorElem.html('Fichier(s) trop volumineux (> 2 Mo) :
' + errorFiles.join(', ') + ''); submitBtn.prop('disabled', true); } else { errorElem.text(''); submitBtn.prop('disabled', false); } }); // Affichage conditionnel du champ "fonds propres" selon le choix Oui/Non avec effet slide const yesRadio = document.getElementById('estateequity_yes'); const noRadio = document.getElementById('estateequity_no'); if (yesRadio && noRadio) { $(yesRadio).on('change', function() { if (yesRadio.checked) { $('.fonds-propres').stop(true, true).slideDown(); $('[name="estateequity"]').prop('required', true); } }); $(noRadio).on('change', function() { if (noRadio.checked) { $('.fonds-propres').stop(true, true).slideUp(); $('[name="estateequity"]').prop('required', false); } }); // Initialisation à l'ouverture de la page if (yesRadio.checked) { $('.fonds-propres').show(); } else { $('.fonds-propres').hide(); } } // Variables pour le debouncing des inputs let capitalTimeout; let durationTimeout; let isCreditCalculationEnabled = true; if(jQuery('#creditCalcManualSwitch').length > 0) isCreditCalculationEnabled = true; // Liste des types de crédit qui utilisent des années const LOAN_TYPES_IN_YEARS = ['am', 'amr', 'cied', 'ph']; // Fonction pour mettre à jour l'affichage des valeurs calculées function updateCreditDisplay(result) { console.log('updateCreditDisplay - result:', result); // Calcul du TAEG const taeg = parseFloat(result.taux_nominal_annuel.replace('%', '')); const taux_debiteur = (taeg * 0.11 + taeg).toFixed(2); // Mise à jour des champs cachés jQuery('input[name="capital"]').val(result.capital); jQuery('input[name="duree"]').val(result.duree); jQuery('input[name="cout_total"]').val(result.cout_total); jQuery('input[name="mensualite"]').val(result.mensualite); jQuery('input[name="taux_nominal_annuel"]').val(result.taux_nominal_annuel); console.log('updateCreditDisplay - Champs cachés mis à jour:', { capital: result.capital, duree: result.duree, cout_total: result.cout_total, mensualite: result.mensualite, taux: result.taux_nominal_annuel }); // Mise à jour de l'affichage dans .credit-info jQuery('.credit-info .credit-item').each(function() { var $value = jQuery(this).find('.credit-value'); var $label = jQuery(this).find('.credit-label'); if($label.text().includes('Montant:')) { $value.text(result.capital + ' €'); } else if($label.text().includes('Durée:')) { $value.text(result.duree + ($('input[name="type_credit"]').val() === 'am' ? ' ans' : ' mois')); } else if($label.text().includes('Mensualité:')) { $value.text(result.mensualite + '€'); } else if($label.text().includes('Coût total:')) { $value.text(result.cout_total + '€'); } else if($label.text().includes('TAEG:')) { $value.text(taux_debiteur + '%'); } else if($label.text().includes('Taux débiteur:')) { $value.text(result.taux_nominal_annuel); } }); // Mise à jour du récapitulatif dans .info-credits-step jQuery('.info-credits-step .row > div').each(function() { $(this).find('p').each(function() { var $label = $(this); var $value = $label.next('span'); if($label.text().includes('Montant emprunté :')) { $value.text(result.capital + ' €'); } else if($label.text().includes('Durée :')) { $value.text(result.duree + ($('input[name="type_credit"]').val() === 'am' ? ' ans' : ' mois')); } else if($label.text().includes('Mensualité :')) { $value.text(result.mensualite + ' €'); } else if($label.text().includes('Coût Total :')) { $value.text(result.cout_total + ' €'); } else if($label.text().includes('Taux débiteur :')) { $value.text(result.taux_nominal_annuel); } else if($label.text().includes('Taeg :')) { $value.text(taux_debiteur + '%'); } }); }); console.log('updateCreditDisplay - Affichage mis à jour'); } // Fonction pour mettre à jour les limites et valeurs des champs selon le type de crédit function updateFieldLimits(selectedType) { const creditType = map_values[selectedType]; if (!creditType) { console.warn('Type de crédit non reconnu:', selectedType); return; } const $capitalField = jQuery('#IDbatiment_emprunt'); const $durationField = jQuery('#IDbatiment_duree'); let currentCapital = parseFloat($capitalField.val()); // Récupérer les limites depuis form_sliders pour les durées const sliderConfig = form_sliders[creditType]; if (!sliderConfig) { console.warn('Configuration slider non trouvée pour:', creditType); return; } // Récupérer les limites spécifiques du crédit depuis cd_js.groups const creditConfig = cd_js.groups[selectedType]; if (creditConfig && creditConfig.capital_max) { // Ne modifier le montant que s'il dépasse la limite du crédit choisi if (currentCapital > creditConfig.capital_max) { currentCapital = creditConfig.capital_selected || creditConfig.capital_max; $capitalField.val(currentCapital); } } // Mettre à jour les attributs min/max du champ capital $capitalField.attr('min', sliderConfig.capital_min); $capitalField.attr('max', sliderConfig.capital_max); $capitalField.attr('step', sliderConfig.capital_step || 100); // Ajuster la valeur du capital si nécessaire selon les limites du slider if (currentCapital > sliderConfig.capital_max) { currentCapital = sliderConfig.capital_max; $capitalField.val(currentCapital); } else if (currentCapital < sliderConfig.capital_min) { currentCapital = sliderConfig.capital_min; $capitalField.val(currentCapital); } // Déterminer la durée maximale disponible pour ce montant const isYearlyDuration = LOAN_TYPES_IN_YEARS.includes(creditType); let maxDurationForAmount = sliderConfig.durees[sliderConfig.durees.length - 1]; // Parcourir les durées disponibles dans l'ordre décroissant for (let i = sliderConfig.durees.length - 1; i >= 0; i--) { const duration = sliderConfig.durees[i]; // Vérifier si cette durée est valide pour le montant actuel if (currentCapital >= sliderConfig.capital_min && currentCapital <= sliderConfig.capital_max) { maxDurationForAmount = duration; break; } } // Convertir la durée maximale en années si nécessaire const displayDuration = isYearlyDuration ? Math.floor(maxDurationForAmount / 12) : maxDurationForAmount; const minDuration = isYearlyDuration ? Math.ceil(sliderConfig.duree_min / 12) : sliderConfig.duree_min; const maxDuration = isYearlyDuration ? Math.floor(sliderConfig.duree_max / 12) : sliderConfig.duree_max; // Mettre à jour les attributs min/max du champ durée $durationField.attr('min', minDuration); $durationField.attr('max', maxDuration); // Définir la durée maximale disponible $durationField.val(displayDuration); // Mettre à jour l'unité dans l'input-group-text jQuery('.input-group-text').text(isYearlyDuration ? 'Ans' : 'Mois'); // Mettre à jour l'affichage calculateCredit(); // Mettre à jour le champ caché type_credit jQuery('input[name="type_credit"]').val(creditType); } // Gestionnaire pour le changement de type de crédit jQuery('#IDestateloantype').on('change', function() { const creditType = jQuery(this).val(); if (creditType) { updateFieldLimits(creditType); calculateCredit(); } }); // Fonction pour arrondir le capital à la valeur acceptable la plus proche function snapCapitalValue(value, creditType) { if (!creditType || !form_sliders[creditType]) { return value; } const sliderConfig = form_sliders[creditType]; // Utiliser le step défini dans form_sliders const step = sliderConfig.capital_step || 100; // S'assurer que la valeur est dans les limites value = Math.max(sliderConfig.capital_min, Math.min(value, sliderConfig.capital_max)); // Arrondir à la valeur la plus proche selon le step return Math.round(value / step) * step; } // Fonction pour arrondir la durée à la valeur acceptable la plus proche function snapDurationValue(value, creditType) { if (!creditType || !form_sliders[creditType]) { return value; } const sliderConfig = form_sliders[creditType]; const isYearlyDuration = LOAN_TYPES_IN_YEARS.includes(creditType); console.log('snapDurationValue - value:', value); console.log('snapDurationValue - creditType:', creditType); console.log('snapDurationValue - isYearlyDuration:', isYearlyDuration); if (isYearlyDuration) { // Pour les crédits en années, on vérifie juste les limites min/max // Les limites sont déjà en années pour ces types de crédit const minYears = sliderConfig.duree_min; const maxYears = sliderConfig.duree_max; console.log('snapDurationValue - minYears:', minYears); console.log('snapDurationValue - maxYears:', maxYears); if (value > maxYears) return maxYears; if (value < minYears) return minYears; return value; } else { // Pour les autres crédits, on cherche la durée disponible la plus proche let closestDuration = sliderConfig.durees[0]; let minDiff = Math.abs(value - sliderConfig.durees[0]); for (let i = 1; i < sliderConfig.durees.length; i++) { const diff = Math.abs(value - sliderConfig.durees[i]); if (diff < minDiff) { minDiff = diff; closestDuration = sliderConfig.durees[i]; } } return closestDuration; } } // Fonction pour calculer la mensualité function calc_mens(mnt, dur, taeg) { const creditType = jQuery('input[name="type_credit"]').val(); // Pour le crédit pont, on ne calcule que les intérêts mensuels if (creditType === 'cdp') { const t100 = taeg / 100; // Calcul des intérêts mensuels uniquement : (capital * taux_annuel) / 12 const mens = (mnt * t100) / 12; return Math.floor(mens * 100) / 100; } // Pour les autres types de crédit, calcul normal const t100 = taeg / 100; const t101 = 1 + t100; const d12 = 1 / 12; const dur12 = dur / 12; const buf1 = Math.pow(t101, d12); const buf2 = Math.pow(1 / t101, dur12); const mens = (mnt * (buf1 - 1)) / (1 - buf2); return Math.floor(mens * 100) / 100; } // Notre propre fonction de calcul qui utilise les fonctions de base de cd_main.js function calculateCredit() { const capital = parseFloat(jQuery('#IDbatiment_emprunt').val()); const duration = parseInt(jQuery('#IDbatiment_duree').val()); const selectedType = jQuery('#IDestateloantype').val(); const creditType = map_values[selectedType]; console.log('=== Début calcul crédit ==='); console.log('Type sélectionné:', selectedType); console.log('Type de crédit mappé:', creditType); console.log('Capital:', capital); console.log('Durée:', duration); if (isNaN(capital) || isNaN(duration) || !creditType) { console.warn('Valeurs invalides pour le calcul:', { capital: isNaN(capital) ? 'invalide' : capital, duration: isNaN(duration) ? 'invalide' : duration, selectedType, creditType: !creditType ? 'non mappé' : creditType }); return; } // Convertir la durée en mois si nécessaire pour les calculs const isYearlyDuration = LOAN_TYPES_IN_YEARS.includes(creditType); const durationForCalculation = isYearlyDuration ? duration * 12 : duration; console.log('Durée pour calcul:', durationForCalculation, isYearlyDuration ? '(en années)' : '(en mois)'); let results; // Appeler la fonction de calcul appropriée switch(creditType) { case 'pat': results = calculate_pat(capital, duration); break; case 'am': results = calculate_am(capital, duration); break; case 'ph': results = calculate_ph(capital, duration); break; case 'amr': case 'cdp': case 'cied': results = calculate_mono_rate_bt_10_30(creditType, capital, duration); break; case 'fin_neuve': results = calculate_fin_neuve(capital, duration); break; case 'fin_occ_m3a': results = calculate_pao_m_3(capital, duration); break; case 'fin_occ_p3a': results = calculate_pao_p_3(capital, duration); break; case 'mobil_carav': results = calculate_mobilhome(capital, duration); break; case 'regroup_cred': results = calculate_regroupement_de_credit(capital, duration); break; case 'frais_notaire': results = calculate_frais_notaire(capital, duration); break; case 'but_immo': results = calculate_but_immo(capital, duration); break; default: console.warn('Type de crédit non reconnu:', creditType); results = calculate_pat(capital, duration); } if (!results) { console.warn('Pas de résultats retournés par la fonction de calcul'); return; } console.log('Résultats bruts:', results); // Extraire les valeurs des résultats const [min_duration, max_duration, selected_duration, duree_in_range, taux_nominal] = results; // Convertir le taux en nombre si c'est une chaîne const taux = parseFloat(String(taux_nominal).replace('%', '')); if (isNaN(taux)) { console.error('Taux nominal invalide après conversion:', { original: taux_nominal, converti: taux }); return; } console.log('Taux nominal (débiteur):', taux + '%'); console.log('Durée sélectionnée:', selected_duration, isYearlyDuration ? 'mois (convertie)' : 'mois'); // Pour les crédits en années, la durée retournée est déjà en mois const durationInMonths = isYearlyDuration ? durationForCalculation : selected_duration; // Calculer la mensualité avec notre fonction locale const mensualite = calc_mens(capital, durationInMonths, taux); if (isNaN(mensualite)) { console.error('Erreur dans le calcul de la mensualité:', { capital, durationInMonths, taux }); return; } const cout_total = (mensualite * durationInMonths).toFixed(2); console.log('Mensualité calculée:', mensualite.toFixed(2) + '€'); console.log('Coût total calculé:', cout_total + '€'); // Créer l'objet result pour updateCreditDisplay const displayResult = { capital: capital, duree: duration, mensualite: mensualite.toFixed(2), cout_total: cout_total, taux_nominal_annuel: taux.toFixed(2) + ' %', taux_debiteur: taux.toFixed(2) + ' %' }; console.log('Résultat final pour affichage:', displayResult); console.log('--- Fin calcul crédit ---'); // Mettre à jour l'affichage updateCreditDisplay(displayResult); // Mettre à jour le champ caché type_credit jQuery('input[name="type_credit"]').val(creditType); } if(isCreditCalculationEnabled) { // Remplacer tous les appels à calculate_mensualite par calculateCredit jQuery('#IDbatiment_emprunt') .on('input', function() { const $this = jQuery(this); clearTimeout(capitalTimeout); if ($this.val() !== '') { capitalTimeout = setTimeout(() => { let capital = parseFloat($this.val()); if (!isNaN(capital)) { const creditType = jQuery('input[name="type_credit"]').val(); const snappedValue = snapCapitalValue(capital, creditType); if (snappedValue !== capital) { $this.val(snappedValue); } calculateCredit(); } }, 500); // Délai un peu plus long pour laisser le temps de taper } }) .on('change', function() { // Validation finale quand on quitte le champ (onblur) const $this = jQuery(this); if ($this.val() !== '') { let capital = parseFloat($this.val()); if (!isNaN(capital)) { const selectedType = jQuery('#IDestateloantype').val(); const creditConfig = cd_js.groups[selectedType]; // Vérifier les limites du crédit if (creditConfig && creditConfig.capital_max && capital > creditConfig.capital_max) { capital = creditConfig.capital_selected || creditConfig.capital_max; $this.val(capital); } // Snap à la valeur la plus proche et recalcul const creditType = jQuery('input[name="type_credit"]').val(); const snappedValue = snapCapitalValue(capital, creditType); if (snappedValue !== capital) { $this.val(snappedValue); } calculateCredit(); } } }); jQuery('#IDbatiment_duree') .on('input', function() { const $this = jQuery(this); clearTimeout(durationTimeout); durationTimeout = setTimeout(() => { let duration = parseInt($this.val()); if (!isNaN(duration)) { const creditType = jQuery('input[name="type_credit"]').val(); console.log('Input duration:', duration); console.log('Credit type:', creditType); console.log('Is yearly:', LOAN_TYPES_IN_YEARS.includes(creditType)); // Ne pas snapper pendant la saisie pour les crédits en années if (LOAN_TYPES_IN_YEARS.includes(creditType)) { calculateCredit(); } else { const snappedValue = snapDurationValue(duration, creditType); if (snappedValue !== duration) { $this.val(snappedValue); } calculateCredit(); } } }, 300); }) .on('change', function() { let duration = parseInt($(this).val()); if (!isNaN(duration)) { const creditType = jQuery('input[name="type_credit"]').val(); console.log('onchange - creditType:', creditType); console.log('onchange - LOAN_TYPES_IN_YEARS:', LOAN_TYPES_IN_YEARS); console.log('onchange - includes?', LOAN_TYPES_IN_YEARS.includes(creditType)); if (LOAN_TYPES_IN_YEARS.includes(creditType)) { console.log('change', duration); const snappedValue = snapDurationValue(duration, creditType); console.log('snappedValue', snappedValue); if (snappedValue !== duration) { $(this).val(snappedValue); } } else { // Pour les autres crédits, comportement normal const snappedValue = snapDurationValue(duration, creditType); if (snappedValue !== duration) { $(this).val(snappedValue); } } calculateCredit(); } }); // Calcul initial au chargement /* $(document).ready(function() { const creditType = $('input[name="type_credit"]').val(); if (creditType) { updateFieldLimits(creditType); calculateCredit(); } }); */ // Gestionnaire pour la soumission du formulaire - s'exécute AVANT la validation HTML5 jQuery('.form-submit').on('submit', function(e) { // Retirer l'attribut required de tous les champs dans les sections cachées // AVANT la validation HTML5 pour éviter l'erreur "not focusable" removeRequiredFromHiddenFields(); // Recalculer une dernière fois avant l'envoi calculateCredit(); // Laisser le formulaire se soumettre normalement après avoir retiré les required des champs cachés }); } function findRate(type) { console.log('findRate - Type recherché:', type); let rate; switch(type) { case 'amr': rate = cd_js.groups.achat_maison_de_rapport.de_10_a_30_ans; break; case 'cdp': rate = cd_js.groups.credit_pont.de_10_a_30_ans; break; case 'cied': rate = cd_js.groups.independants_et_entreprises_en_difficultes.de_10_a_30_ans; break; default: console.warn('Type de crédit non reconnu pour le taux mono:', type); rate = null; } console.log('findRate - Taux trouvé:', rate); return rate; } function calculate_mono_rate_bt_10_30(type, selected_capital, selected_duration) { const annual_rate = findRate(type); if (!annual_rate) { console.error('Taux non trouvé pour le type:', type); return null; } const min_duration = 10; const max_duration = 30; let duree_in_range = true; const add_message = ''; if (min_duration <= parseInt(selected_duration) && parseInt(selected_duration) <= max_duration) { // La durée est dans la plage } else { duree_in_range = false; selected_duration = max_duration; } // Convertir la durée en mois pour le calcul selected_duration = selected_duration * 12; return [min_duration, max_duration, selected_duration, duree_in_range, annual_rate, add_message]; } }); // --- Changement dynamique du label Numéro de registre national selon la nationalité (emprunteur et co-emprunteur) --- jQuery(function($) { function setupNationalRegLabel(selectId, labelId) { var defaultLabel = 'Numéro de registre national'; var luxLabel = 'Numéro de sécurité sociale'; var select = $('#' + selectId); var labelSelector = 'label[for="' + labelId + '"]'; function updateLabel() { var val = select.val(); if (val === 'LU') { $(labelSelector).text(luxLabel); } else { $(labelSelector).text(defaultLabel); } } select.on('change', updateLabel); // Initialisation au chargement updateLabel(); } setupNationalRegLabel('IDnationality', 'IDnationalregistrationnumber'); setupNationalRegLabel('IDconationality', 'IDconationalregistrationnumber'); }); // --- Initialisation des Inputmasks pour les champs de date --- jQuery(function($) { // Initialisation des champs de date avec inputmask jQuery('input[data-inputmask]').inputmask(); // Configuration spécifique pour tous les champs de date jQuery('.input-date').inputmask({ mask: '99/99/9999', placeholder: 'JJ/MM/AAAA', clearMaskOnLostFocus: false, showMaskOnHover: true, showMaskOnFocus: true }); }); if(jQuery('.etat-civil-pat').length > 0) { // Variable pour tracker si le co-emprunteur a été forcé par l'état civil let coBorrowerForcedByCivilStatus = false; jQuery('#IDcivilstatus').on('change', function() { let etatCivil = jQuery('#IDcivilstatus').val(), switchValue = 4, hascoborrower_input = jQuery('[name="hascoborrower"]'), parent = hascoborrower_input.parents('fieldset'), info = cd_js.info_pat; if(etatCivil == switchValue) { // État civil = 4 : forcer l'activation du co-emprunteur hascoborrower_input[1].checked = true; coBorrowerForcedByCivilStatus = true; parent.append('
' + info + '
'); } else { // État civil ≠ 4 : ne désactiver que si c'était précédemment forcé if(coBorrowerForcedByCivilStatus) { hascoborrower_input[0].checked = true; coBorrowerForcedByCivilStatus = false; parent.find('.alert').remove(); } // Si ce n'était pas forcé, on laisse le choix de l'utilisateur } // Déclencher l'événement change sur le bouton radio pour activer displayCoBorrower // Mais éviter les déclenchements multiples si cojob = 5 var cojobValue = jQuery('select[name="cojob"]').val(); if (cojobValue !== '5') { jQuery('input[name="hascoborrower"]:checked').trigger('change'); } else { // Si cojob = 5, appeler directement sans trigger pour éviter la cascade displayCoBorrower(); } }); // Réinitialiser le flag quand l'utilisateur change manuellement le co-emprunteur jQuery('[name="hascoborrower"]').on('change', function() { if(!jQuery('#IDcivilstatus').val() == 4) { coBorrowerForcedByCivilStatus = false; } }); }