personnal/ecole/public/js/rc-sync-utils.js

291 lines
11 KiB
JavaScript

/**
* Fonctions utilitaires partagées entre les écrans RC Tarif et Projet.
*/
(function(window) {
'use strict';
// ═══════════════════════════════════════════════════════════════════════
// CONSTANTES
// ═══════════════════════════════════════════════════════════════════════
/**
* Champs qui influencent le calcul du tarif.
* On s'en sert pour savoir quand proposer un retour vers l'onglet Tarif.
*/
const TARIF_IMPACTING_FIELDS = [
// Chiffre d'affaires et type de contrat
'ca', 'chiffreAffaires', 'CA',
'typeCotisation', 'cotisation',
'nombreVehicules', 'nbVehicules',
// Activités RCC
'checkVoiturier', 'capitalVoiturier', 'actVoiturier',
'checkCommissionnaire', 'capitalCommissionnaire', 'actMultimodal',
'checkDemenageur', 'capitalDemenageur',
'checkLogistique', 'capitalLogistique',
'checkAutocariste', 'capitalAutocariste',
'checkAutres', 'capitalAutres',
// RCE
'checkRCE', 'autresRC',
// Activités complémentaires
'actComplVoiturier', 'actComplCommissionnaire', 'actComplDemenageur', 'actComplLogistique',
'activitesVoiturier', 'activitesCommissionnaire', 'activitesDemenageur', 'activitesLogistique',
// Marchandises
'marchandisesVoiturier', 'marchandisesCommissionnaire', 'marchandisesDemenageur',
'marchandisesLogistique', 'marchandisesAutocariste', 'marchandisesAutres',
'marOrdinaire', 'marRoulant', 'marEngins', 'marRoulantDem', 'marMobilerUsag',
'marPerissable', 'marAnimaux', 'marCiterne', 'marBeton', 'marExceptionnels', 'marVrac',
// Zones géographiques
'zone1', 'zone2', 'zone3', 'zone4', 'zone5', 'zone6',
// Extensions de garantie RCC
'extRCCModifCalArrim', 'extRCCFerroutage', 'extRCCFraisRecons',
'extRCCConfie', 'typeExtConfies', 'extRCCTPPC', 'extRCCRegie', 'extRCCSansMontageDemontage',
'checkDomImmat', 'capitalDomImmat', 'checkContConf', 'capitalContConf',
'checkDiffInv', 'capitalDiffInv', 'checkTPPC', 'capitalTPPC', 'vehiculesTPPC',
// Extensions de garantie RCE
'extRCEBraDebra', 'extRCEMontageDemontage',
// Garanties additionnelles
'checkStationLavage', 'checkGarageInterne', 'checkCSE', 'checkPJ', 'pj',
// Sinistralité
'sinistre', 'nbSinistres3ans', 'montantSinistres3ans'
];
// ═══════════════════════════════════════════════════════════════════════
// HELPERS - MANIPULATION DE VALEURS
// ═══════════════════════════════════════════════════════════════════════
/**
* Convertit une valeur texte/nombre en nombre JS.
* Accepte les formats FR/EN (espaces, virgules, points).
*/
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;
}
/**
* Récupère un élément par id, avec fallback querySelector.
*/
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;
}
}
/**
* Lit la valeur d'un champ de formulaire selon son type.
*/
function getValue(elementId) {
const element = getElementByIdFlexible(elementId);
if (!element) return null;
if (element.type === 'checkbox') {
return element.checked;
} else if (element.type === 'radio') {
const checked = document.querySelector(`input[name="${element.name}"]:checked`);
return checked ? checked.value : null;
} else if (element.tagName === 'SELECT') {
return element.value;
} else if (element.value !== undefined) {
return element.value;
} else {
return element.textContent || element.innerText || null;
}
}
/**
* Ecrit une valeur dans un champ de formulaire selon son type.
*/
function setValue(elementId, value) {
const element = getElementByIdFlexible(elementId);
if (!element) {
console.warn(`Élément non trouvé: ${elementId}`);
return;
}
if (element.type === 'checkbox') {
element.checked = Boolean(value);
} else if (element.type === 'radio') {
const radio = document.querySelector(`input[name="${element.name}"][value="${value}"]`);
if (radio) radio.checked = true;
} else if (element.tagName === 'SELECT') {
element.value = value;
// Réinitialiser Materialize select si présent
if (window.M && window.M.FormSelect) {
const instance = window.M.FormSelect.getInstance(element);
if (instance) instance.destroy();
window.M.FormSelect.init(element);
}
} else if (element.value !== undefined) {
element.value = value;
} else {
element.textContent = value;
}
}
// ═══════════════════════════════════════════════════════════════════════
// DÉTECTION DE CHANGEMENTS IMPACTANTS
// ═══════════════════════════════════════════════════════════════════════
/**
* Dit si un nom de champ fait partie des champs qui influencent le tarif.
*/
function isFieldImpactingTarif(fieldName) {
return TARIF_IMPACTING_FIELDS.some(field =>
fieldName.includes(field) || field.includes(fieldName)
);
}
// ═══════════════════════════════════════════════════════════════════════
// MODAL DE RETOUR AU TARIF
// ═══════════════════════════════════════════════════════════════════════
/**
* Ouvre le modal "modif tarif" déjà présent dans la vue projet.
*/
function showReturnToTarifModal(fieldName) {
const modal = document.getElementById('modalModif');
if (!modal) {
console.warn('Modal "modalModif" introuvable');
return;
}
if (fieldName) {
console.warn(`Modification impactant le tarif: ${fieldName}`);
}
const okBtn = document.getElementById('modif-OK');
const noBtn = document.getElementById('modif-NO');
if (okBtn) {
okBtn.onclick = (event) => {
event?.preventDefault?.();
navigateToTarif();
};
}
if (noBtn) {
noBtn.onclick = (event) => {
event?.preventDefault?.();
if (!window.M || !window.M.Modal) return;
const instance = window.M.Modal.getInstance(modal);
if (instance) instance.close();
};
}
if (window.M && window.M.Modal) {
const instance = window.M.Modal.getInstance(modal) || window.M.Modal.init(modal);
instance.open();
}
}
/**
* Redirige vers l'onglet Tarif depuis le Projet.
*/
function navigateToTarif() {
// Fermer le modal
const modal = document.getElementById('modalModif');
if (modal && window.M) {
const instance = window.M.Modal.getInstance(modal);
if (instance) instance.close();
}
// Si on est sur le formulaire projet, on garde un snapshot local
// pour pré-remplir le tarif juste après la navigation.
try {
if (typeof window.collectProjetDataForTarifPrefill === 'function') {
const projetData = window.collectProjetDataForTarifPrefill();
if (projetData) {
sessionStorage.setItem('rc_projet_data', JSON.stringify(projetData));
}
}
} catch (error) {
console.error('Impossible de stocker les donnees projet avant redirection:', error);
}
// Naviguer vers le tarif
const numParcours = new URLSearchParams(window.location.search).get('numParcours');
if (numParcours) {
window.location.href = `/navParcours?numParcours=${numParcours}&submenu=tarif`;
}
}
// ═══════════════════════════════════════════════════════════════════════
// EXPORT PUBLIC
// ═══════════════════════════════════════════════════════════════════════
/**
* API publique du module RC Sync.
*/
window.RCSync = {
// Helpers
toNumber,
getValue,
setValue,
getElementByIdFlexible,
// Détection changements
isFieldImpactingTarif,
// Modal
showReturnToTarifModal,
navigateToTarif,
// Constantes
TARIF_IMPACTING_FIELDS
};
console.log('RC Sync Utils loaded');
})(window);