RC: nouveaux formulaires et synchro
This commit is contained in:
parent
0125e2ae69
commit
4919940a26
|
|
@ -1,5 +1,8 @@
|
||||||
body {
|
body {
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: 'Roboto', sans-serif;
|
||||||
|
background-color: white;
|
||||||
|
color: black;
|
||||||
|
color-scheme: light;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
let produit = parcours["@expand"].contrat.produit
|
let produit = parcours["@expand"].contrat.produit
|
||||||
|
|
||||||
const produitObj = contrat?.["@expand"]?.enCours || null;
|
const produitObj = contrat?.["@expand"]?.enCours || null;
|
||||||
const tarif = produitObj?.["@expand"]?.tarif || null;
|
|
||||||
const projet = produitObj?.["@expand"]?.projet || null;
|
// RC utilise tarifRC/projetRC, les autres produits utilisent tarif/projet
|
||||||
|
let tarif, projet;
|
||||||
|
if (produit === "rc") {
|
||||||
|
tarif = produitObj?.["@expand"]?.tarifRC || null;
|
||||||
|
projet = produitObj?.["@expand"]?.projetRC || null;
|
||||||
|
} else {
|
||||||
|
tarif = produitObj?.["@expand"]?.tarif || null;
|
||||||
|
projet = produitObj?.["@expand"]?.projet || null;
|
||||||
|
}
|
||||||
|
|
||||||
let scriptSrc;
|
let scriptSrc;
|
||||||
// let newScriptModuloSrc;
|
// let newScriptModuloSrc;
|
||||||
|
|
@ -54,11 +62,21 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
scriptSrc = `/js/${submenu}-form.js`;
|
scriptSrc = `/js/${submenu}-form.js`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RC utilise tarifRC/projetRC, les autres produits utilisent tarif/projet
|
||||||
|
let tarifId, projetId;
|
||||||
|
if (produit === "rc") {
|
||||||
|
tarifId = contrat?.["@expand"]?.enCours?.["@expand"]?.tarifRC?.id || null;
|
||||||
|
projetId = contrat?.["@expand"]?.enCours?.["@expand"]?.projetRC?.id || null;
|
||||||
|
} else {
|
||||||
|
tarifId = contrat?.["@expand"]?.enCours?.["@expand"]?.tarif?.id || null;
|
||||||
|
projetId = contrat?.["@expand"]?.enCours?.["@expand"]?.projet?.id || null;
|
||||||
|
}
|
||||||
|
|
||||||
const etapes = {
|
const etapes = {
|
||||||
"client": contrat?.client || null,
|
"client": contrat?.client || null,
|
||||||
"intermediaire": contrat?.intermediaire || null,
|
"intermediaire": contrat?.intermediaire || null,
|
||||||
"tarif": contrat?.["@expand"]?.enCours?.["@expand"]?.tarif?.id || null,
|
"tarif": tarifId,
|
||||||
"projet": contrat?.["@expand"]?.enCours?.["@expand"]?.projet?.id || null,
|
"projet": projetId,
|
||||||
"contrat": null
|
"contrat": null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +97,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.getElementById('step-' + key).classList.add('line')
|
document.getElementById('step-' + key).classList.add('line')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
showLoader();
|
|
||||||
|
|
||||||
// Charger le formulaire associé
|
// Charger le formulaire associé
|
||||||
fetch(fetchUrl)
|
fetch(fetchUrl)
|
||||||
|
|
@ -89,6 +106,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
// Suppression de tout script précédemment chargé
|
// Suppression de tout script précédemment chargé
|
||||||
const oldScript = document.querySelector('script.dynamic-script');
|
const oldScript = document.querySelector('script.dynamic-script');
|
||||||
|
|
||||||
if (oldScript) {
|
if (oldScript) {
|
||||||
oldScript.remove();
|
oldScript.remove();
|
||||||
}
|
}
|
||||||
|
|
@ -120,26 +138,26 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
inputChanged = true
|
inputChanged = true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
hideLoader();
|
|
||||||
})
|
})
|
||||||
.catch(error => console.error('Error:', error));
|
.catch(error => console.error('Error:', error));
|
||||||
|
|
||||||
//A MODIFIER UNE FOIS QUE RC SERA ADAPTé AU PARCOURS
|
// Gestion des boutons de génération pour tous les produits
|
||||||
if (produit == "RC") {
|
const hasClient = parcours["@expand"]?.contrat?.client != '';
|
||||||
if (Object.keys(contrat?.["@expand"]?.enCours).length > 1) {
|
const hasIntermediaire = parcours["@expand"]?.contrat?.intermediaire != '';
|
||||||
document.getElementById('generateProject').disabled = false;
|
const hasProduit = produitObj != undefined;
|
||||||
}
|
|
||||||
|
// Bouton génération déclinaison tarifaire
|
||||||
|
if (hasClient && hasIntermediaire && hasProduit && tarif != null) {
|
||||||
|
document.getElementById('generateDeclinaison').disabled = false;
|
||||||
} else {
|
} else {
|
||||||
// Enable / disable bouton generate project
|
document.getElementById('generateDeclinaison').disabled = true;
|
||||||
if (parcours["@expand"].contrat.client != '' && parcours["@expand"].contrat.intermediaire != '' && produitObj != undefined && projet != null) {
|
}
|
||||||
document.getElementById('generateProject').disabled = false;
|
|
||||||
}
|
// Bouton génération projet
|
||||||
|
if (hasClient && hasIntermediaire && hasProduit && projet != null) {
|
||||||
// Enable / disable bouton generate déclinaison
|
document.getElementById('generateProject').disabled = false;
|
||||||
if (parcours["@expand"].contrat.client != '' && parcours["@expand"].contrat.intermediaire != '' && produitObj != undefined && tarif != null) {
|
} else {
|
||||||
document.getElementById('generateDeclinaison').disabled = false;
|
document.getElementById('generateProject').disabled = true;
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,9 +175,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
const parcours = JSON.parse(sessionStorage.getItem('parcours'));
|
const parcours = JSON.parse(sessionStorage.getItem('parcours'));
|
||||||
var produit = parcours["@expand"].contrat.produit
|
var produit = parcours["@expand"].contrat.produit
|
||||||
|
|
||||||
const btn = this // bouton "générer projet"
|
|
||||||
btn.disabled = true; // le desactiver le temps du téléchargement
|
|
||||||
|
|
||||||
var fileName
|
var fileName
|
||||||
switch (produit.toLowerCase()) {
|
switch (produit.toLowerCase()) {
|
||||||
case 'fac':
|
case 'fac':
|
||||||
|
|
@ -183,21 +198,26 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
link.download = fileName;
|
link.download = fileName;
|
||||||
link.click();
|
link.click();
|
||||||
})
|
})
|
||||||
.finally(() => {
|
|
||||||
btn.disabled = false; // réactiver le bouton a la fin du téléchargement
|
|
||||||
})
|
|
||||||
.catch(error => console.error('Error downloading file:', error));
|
.catch(error => console.error('Error downloading file:', error));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fonction de génération de projet
|
// Fonction de génération de projet
|
||||||
document.getElementById('generateProject').addEventListener('click', function() {
|
document.getElementById('generateProject').addEventListener('click', async function() {
|
||||||
const numParcours = getNumParcoursFromURL();
|
const numParcours = getNumParcoursFromURL();
|
||||||
let filename;
|
let filename;
|
||||||
const parcours = JSON.parse(sessionStorage.getItem('parcours'));
|
const parcours = JSON.parse(sessionStorage.getItem('parcours'));
|
||||||
let produit = parcours["@expand"].contrat.produit
|
let produit = parcours["@expand"].contrat.produit
|
||||||
|
|
||||||
const btn = this // bouton "générer projet"
|
// Sauvegarder les données du projet avant de générer le document (si RC)
|
||||||
btn.disabled = true; // le desactiver le temps du téléchargement
|
if (produit.toLowerCase() === 'rc' && typeof window.saveProjetRC === 'function') {
|
||||||
|
console.log('Sauvegarde des données projet RC avant génération...');
|
||||||
|
const saveResult = await window.saveProjetRC();
|
||||||
|
if (!saveResult || !saveResult.valid) {
|
||||||
|
console.error('Échec de la sauvegarde du projet RC');
|
||||||
|
M.toast({html: 'Erreur lors de la sauvegarde du projet. Veuillez réessayer.'});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Envoi de la requête POST au serveur pour générer le projet
|
// Envoi de la requête POST au serveur pour générer le projet
|
||||||
fetch(`/generate/${produit}/projet/${numParcours}`, {
|
fetch(`/generate/${produit}/projet/${numParcours}`, {
|
||||||
|
|
@ -228,23 +248,17 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
window.URL.revokeObjectURL(url); // Nettoie l'URL objet
|
window.URL.revokeObjectURL(url); // Nettoie l'URL objet
|
||||||
a.remove(); // Supprime l'élément a du document
|
a.remove(); // Supprime l'élément a du document
|
||||||
})
|
})
|
||||||
.finally(() => {
|
|
||||||
btn.disabled = false; // réactiver le bouton a la fin du téléchargement
|
|
||||||
})
|
|
||||||
.catch(error => console.error('Erreur lors de la génération du projet:', error));
|
.catch(error => console.error('Erreur lors de la génération du projet:', error));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
document.getElementById('generateDeclinaison').addEventListener('click', function() {
|
document.getElementById('generateDeclinaison').addEventListener('click', async function() {
|
||||||
const numParcours = getNumParcoursFromURL();
|
const numParcours = getNumParcoursFromURL();
|
||||||
let filename;
|
let filename;
|
||||||
const parcours = JSON.parse(sessionStorage.getItem('parcours'));
|
const parcours = JSON.parse(sessionStorage.getItem('parcours'));
|
||||||
let produit = parcours["@expand"].contrat.produit
|
let produit = parcours["@expand"].contrat.produit
|
||||||
|
|
||||||
const btn = this // bouton "générer déclinaison tarifaire"
|
// Envoi de la requête POST au serveur pour générer la déclinaison tarifaire
|
||||||
btn.disabled = true; // le desactiver le temps du téléchargement
|
|
||||||
|
|
||||||
// Envoi de la requête POST au serveur pour générer le projet
|
|
||||||
fetch(`/generate/${produit}/tarif/${numParcours}`, {
|
fetch(`/generate/${produit}/tarif/${numParcours}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
|
@ -274,9 +288,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
window.URL.revokeObjectURL(url); // Nettoie l'URL objet
|
window.URL.revokeObjectURL(url); // Nettoie l'URL objet
|
||||||
a.remove(); // Supprime l'élément a du document
|
a.remove(); // Supprime l'élément a du document
|
||||||
})
|
})
|
||||||
.finally(() => {
|
|
||||||
btn.disabled = false; // réactiver le bouton a la fin du téléchargement
|
|
||||||
})
|
|
||||||
.catch(error => console.error('Erreur lors de la génération du projet:', error));
|
.catch(error => console.error('Erreur lors de la génération du projet:', error));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -323,4 +334,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
// Exécutez init et gérez les erreurs potentielles
|
// Exécutez init et gérez les erreurs potentielles
|
||||||
init().catch(error => console.error('Error initializing the form:', error));
|
init().catch(error => console.error('Error initializing the form:', error));
|
||||||
});
|
});
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,647 @@
|
||||||
|
/**
|
||||||
|
* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
* RC DATA MANAGER
|
||||||
|
* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
*
|
||||||
|
* Ce module gère la collecte, la sauvegarde et le pré-remplissage des données
|
||||||
|
* RC entre les formulaires Tarif et Projet.
|
||||||
|
*
|
||||||
|
* @requires rc-sync-utils.js
|
||||||
|
* @author AXA Transport Team
|
||||||
|
* @version 2.0.0
|
||||||
|
* @since 2026-02-17
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(window) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { toNumber, getValue, setValue, getElementByIdFlexible } = window.RCSync;
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// MAPPING DES CHAMPS TARIF ↔ PROJET
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping complet des champs entre Tarif et Projet.
|
||||||
|
* Permet la synchronisation bidirectionnelle.
|
||||||
|
*
|
||||||
|
* Structure: { tarifFieldId: projetFieldId }
|
||||||
|
*/
|
||||||
|
const FIELD_MAPPING = {
|
||||||
|
// Informations générales
|
||||||
|
'CA': 'CA',
|
||||||
|
'chiffreAffaire': 'CA',
|
||||||
|
'nbVehicules': 'nombreVehicules',
|
||||||
|
'nbrVehicule': 'nombreVehicules',
|
||||||
|
|
||||||
|
// Type de cotisation
|
||||||
|
'cotisation': 'typeCot',
|
||||||
|
|
||||||
|
// Activités RCC - Voiturier
|
||||||
|
'checkVoiturier': 'actVoiturier',
|
||||||
|
'capitalVoiturier': 'valueActVoiturier',
|
||||||
|
|
||||||
|
// Activités RCC - Commissionnaire (Multimodal)
|
||||||
|
'checkCommissionnaire': 'actMultimodal',
|
||||||
|
'capitalCommissionnaire': 'valueActMultimodal',
|
||||||
|
|
||||||
|
// Activités RCC - Déménageur
|
||||||
|
'checkDemenageur': 'actDemEntr',
|
||||||
|
'capitalDemenageur': 'valueActDemEntr',
|
||||||
|
|
||||||
|
// Activités RCC - Logistique
|
||||||
|
'checkLogistique': 'actPrestaLog',
|
||||||
|
'capitalLogistique': 'valueActPrestaLog',
|
||||||
|
|
||||||
|
// RCE
|
||||||
|
'checkRCE': 'autresRC',
|
||||||
|
|
||||||
|
// Zones géographiques
|
||||||
|
'zone1': 'zone1',
|
||||||
|
'zone2': 'zone2',
|
||||||
|
'zone3': 'zone3',
|
||||||
|
'zone4': 'zone4',
|
||||||
|
'zone5': 'zone5',
|
||||||
|
'zone6': 'zone6',
|
||||||
|
|
||||||
|
// Protection Juridique
|
||||||
|
'checkPJ': 'pj',
|
||||||
|
|
||||||
|
// Garanties additionnelles - Engagements complémentaires
|
||||||
|
'checkDomImmat': 'extRCCConfie', // Simplifié
|
||||||
|
'checkContConf': 'extRCCConfie',
|
||||||
|
'checkTPPC': 'extRCCTPPC',
|
||||||
|
|
||||||
|
// Extensions RCC
|
||||||
|
'checkStationLavage': 'extRCCModifCalArrim',
|
||||||
|
|
||||||
|
// Extensions RCE
|
||||||
|
// (géré séparément car structure différente)
|
||||||
|
|
||||||
|
// Sinistralité
|
||||||
|
'sinistre': 'nbSinistres3ans'
|
||||||
|
};
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// COLLECTE DES DONNÉES COMPLÈTES
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collecte toutes les données du formulaire Tarif RC.
|
||||||
|
* Cette fonction est exhaustive et capture TOUS les champs nécessaires.
|
||||||
|
*
|
||||||
|
* @returns {Object} Objet contenant toutes les données du tarif
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const tarifData = collectAllTarifData();
|
||||||
|
* console.log(tarifData.ca, tarifData.zones, tarifData.marchandises);
|
||||||
|
*/
|
||||||
|
function collectAllTarifData() {
|
||||||
|
// Références flexibles aux éléments
|
||||||
|
const getEl = getElementByIdFlexible;
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
// ═══ INFORMATIONS GÉNÉRALES ═══
|
||||||
|
typeCotisation: document.querySelector('input[name="cotisation"]:checked')?.value || null,
|
||||||
|
ca: toNumber(getValue('CA') || getValue('chiffreAffaire')),
|
||||||
|
nombreVehicules: Math.max(0, Math.round(toNumber(getValue('nbVehicules') || getValue('nbrVehicule')))),
|
||||||
|
|
||||||
|
// ═══ ACTIVITÉS RCC ═══
|
||||||
|
activites: {
|
||||||
|
voiturier: {
|
||||||
|
checked: getValue('checkVoiturier') || false,
|
||||||
|
capital: toNumber(getValue('capitalVoiturier')),
|
||||||
|
pourcentage: toNumber(getValue('pourcent_voiturier') || getValue('pourcentVoiturier/Loueur')),
|
||||||
|
isSet: Boolean(getValue('pourcent_voiturier')?.trim())
|
||||||
|
},
|
||||||
|
commissionnaire: {
|
||||||
|
checked: getValue('checkCommissionnaire') || false,
|
||||||
|
capital: toNumber(getValue('capitalCommissionnaire')),
|
||||||
|
pourcentage: toNumber(getValue('pourcent_commissionnaire')),
|
||||||
|
isSet: Boolean(getValue('pourcent_commissionnaire')?.trim())
|
||||||
|
},
|
||||||
|
demenageur: {
|
||||||
|
checked: getValue('checkDemenageur') || false,
|
||||||
|
capital: toNumber(getValue('capitalDemenageur')),
|
||||||
|
pourcentage: toNumber(getValue('pourcent_demenageur')),
|
||||||
|
isSet: Boolean(getValue('pourcent_demenageur')?.trim())
|
||||||
|
},
|
||||||
|
logistique: {
|
||||||
|
checked: getValue('checkLogistique') || false,
|
||||||
|
capital: toNumber(getValue('capitalLogistique')),
|
||||||
|
pourcentage: toNumber(getValue('pourcent_logistique')),
|
||||||
|
isSet: Boolean(getValue('pourcent_logistique')?.trim())
|
||||||
|
},
|
||||||
|
autocariste: {
|
||||||
|
checked: getValue('checkAutocariste') || false,
|
||||||
|
capital: toNumber(getValue('capitalAutocariste')),
|
||||||
|
pourcentage: toNumber(getValue('pourcent_autocariste')),
|
||||||
|
isSet: Boolean(getValue('pourcent_autocariste')?.trim())
|
||||||
|
},
|
||||||
|
autres: {
|
||||||
|
checked: getValue('checkAutres') || false,
|
||||||
|
capital: toNumber(getValue('capitalAutres')),
|
||||||
|
pourcentage: toNumber(getValue('pourcent_autres')),
|
||||||
|
isSet: Boolean(getValue('pourcent_autres')?.trim())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// ═══ RCE ═══
|
||||||
|
rce: {
|
||||||
|
checked: getValue('checkRCE') || false
|
||||||
|
},
|
||||||
|
|
||||||
|
// ═══ ACTIVITÉS COMPLÉMENTAIRES (JSON) ═══
|
||||||
|
activitesComplementaires: {
|
||||||
|
voiturier: collectActivitesComplJSON('voiturier'),
|
||||||
|
commissionnaire: collectActivitesComplJSON('commissionnaire'),
|
||||||
|
demenageur: collectActivitesComplJSON('demenageur'),
|
||||||
|
logistique: collectActivitesComplJSON('logistique')
|
||||||
|
},
|
||||||
|
|
||||||
|
// ═══ MARCHANDISES (JSON) ═══
|
||||||
|
marchandises: {
|
||||||
|
voiturier: collectMarchandisesJSON('voiturier'),
|
||||||
|
commissionnaire: collectMarchandisesJSON('commissionnaire'),
|
||||||
|
demenageur: collectMarchandisesJSON('demenageur'),
|
||||||
|
logistique: collectMarchandisesJSON('logistique'),
|
||||||
|
autocariste: collectMarchandisesJSON('autocariste'),
|
||||||
|
autres: collectMarchandisesJSON('autres')
|
||||||
|
},
|
||||||
|
|
||||||
|
// ═══ ZONES GÉOGRAPHIQUES ═══
|
||||||
|
zones: {
|
||||||
|
zone1: getValue('zone1') || false,
|
||||||
|
zone2: getValue('zone2') || false,
|
||||||
|
zone3: getValue('zone3') || false,
|
||||||
|
zone4: getValue('zone4') || false,
|
||||||
|
zone5: getValue('zone5') || false,
|
||||||
|
zone6: getValue('zone6') || false
|
||||||
|
},
|
||||||
|
|
||||||
|
// ═══ ENGAGEMENTS COMPLÉMENTAIRES ═══
|
||||||
|
engagementsComplementaires: {
|
||||||
|
domicileImmatriculation: {
|
||||||
|
checked: getValue('checkDomImmat') || false,
|
||||||
|
capital: toNumber(getValue('inputDomImmat'))
|
||||||
|
},
|
||||||
|
contenantConfie: {
|
||||||
|
checked: getValue('checkContConf') || false,
|
||||||
|
capital: toNumber(getValue('inputContConf'))
|
||||||
|
},
|
||||||
|
differenceInventaire: {
|
||||||
|
checked: getValue('checkDiffInv') || false,
|
||||||
|
capital: toNumber(getValue('inputDiffInv'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// ═══ GARANTIES ADDITIONNELLES ═══
|
||||||
|
garantiesAdditionnelles: {
|
||||||
|
stationLavage: getValue('checkStationLavage') || false,
|
||||||
|
garageInterne: getValue('checkGarageInterne') || false,
|
||||||
|
cse: getValue('checkCSE') || false,
|
||||||
|
tppc: {
|
||||||
|
checked: getValue('checkTPPC') || false,
|
||||||
|
capital: toNumber(getValue('selTPPCcapital')),
|
||||||
|
vehicules: Math.max(0, Math.round(toNumber(getValue('selTPPCveh'))))
|
||||||
|
},
|
||||||
|
pj: getValue('checkPJ') || false
|
||||||
|
},
|
||||||
|
|
||||||
|
// ═══ SINISTRALITÉ ═══
|
||||||
|
sinistralite: {
|
||||||
|
nombre3ans: toNumber(getValue('sinistre')),
|
||||||
|
montant3ans: 0 // TODO: ajouter si champ existe
|
||||||
|
},
|
||||||
|
|
||||||
|
// ═══ RÉSULTATS DE CALCUL ═══
|
||||||
|
resultats: {
|
||||||
|
// Franchise 250
|
||||||
|
fr250: {
|
||||||
|
primeRCC: toNumber(getEl('rccFr250')?.textContent),
|
||||||
|
primeRCE: toNumber(getEl('rceFr250')?.textContent),
|
||||||
|
primePJ: toNumber(getEl('pjFr250')?.textContent),
|
||||||
|
primeTotal: toNumber(getEl('priceFr250')?.textContent),
|
||||||
|
tauxRCC: toNumber(getEl('tauxRccFr250')?.textContent),
|
||||||
|
tauxRCE: toNumber(getEl('tauxRceFr250')?.textContent),
|
||||||
|
tauxGlobal: toNumber(getEl('tauxGlobalFr250')?.textContent)
|
||||||
|
},
|
||||||
|
// Franchise 400
|
||||||
|
fr400: {
|
||||||
|
primeRCC: toNumber(getEl('rccFr400')?.textContent),
|
||||||
|
primeRCE: toNumber(getEl('rceFr400')?.textContent),
|
||||||
|
primePJ: toNumber(getEl('pjFr400')?.textContent),
|
||||||
|
primeTotal: toNumber(getEl('priceFr400')?.textContent),
|
||||||
|
tauxRCC: toNumber(getEl('tauxRccFr400')?.textContent),
|
||||||
|
tauxRCE: toNumber(getEl('tauxRceFr400')?.textContent),
|
||||||
|
tauxGlobal: toNumber(getEl('tauxGlobalFr400')?.textContent)
|
||||||
|
},
|
||||||
|
// Franchise 2000
|
||||||
|
fr2000: {
|
||||||
|
primeRCC: toNumber(getEl('rccFr2000')?.textContent),
|
||||||
|
primeRCE: toNumber(getEl('rceFr2000')?.textContent),
|
||||||
|
primePJ: toNumber(getEl('pjFr2000')?.textContent),
|
||||||
|
primeTotal: toNumber(getEl('priceFr2000')?.textContent),
|
||||||
|
tauxRCC: toNumber(getEl('tauxRccFr2000')?.textContent),
|
||||||
|
tauxRCE: toNumber(getEl('tauxRceFr2000')?.textContent),
|
||||||
|
tauxGlobal: toNumber(getEl('tauxGlobalFr2000')?.textContent)
|
||||||
|
},
|
||||||
|
franchiseChoisie: window.franchiseChoisie || null,
|
||||||
|
tarifCommercial: toNumber(getValue('tarifCom'))
|
||||||
|
},
|
||||||
|
|
||||||
|
// ═══ COMMENTAIRE ═══
|
||||||
|
commentaire: getValue('commentaire') || ''
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('📊 Données Tarif collectées:', data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction helper pour collecter les activités complémentaires depuis le formulaire.
|
||||||
|
*
|
||||||
|
* @param {string} typeActivite - Type d'activité ('voiturier', 'commissionnaire', etc.)
|
||||||
|
* @returns {string} JSON array des activités cochées
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
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 depuis le formulaire.
|
||||||
|
*
|
||||||
|
* @param {string} typeActivite - Type d'activité
|
||||||
|
* @returns {string} JSON array des marchandises cochées
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// PRÉ-REMPLISSAGE TARIF → PROJET
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pré-remplit le formulaire Projet avec les données du Tarif.
|
||||||
|
* Cette fonction est appelée quand l'utilisateur passe du Tarif au Projet.
|
||||||
|
*
|
||||||
|
* @param {Object} tarifData - Données complètes du tarif (de collectAllTarifData)
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const tarifData = collectAllTarifData();
|
||||||
|
* prefillProjetFromTarif(tarifData);
|
||||||
|
*/
|
||||||
|
function prefillProjetFromTarif(tarifData) {
|
||||||
|
if (!tarifData) {
|
||||||
|
console.warn('Pas de données tarif à pré-remplir');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('📝 Pré-remplissage Projet depuis Tarif...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// ═══ INFORMATIONS GÉNÉRALES ═══
|
||||||
|
|
||||||
|
// CA
|
||||||
|
if (tarifData.ca) {
|
||||||
|
setValue('CA', tarifData.ca);
|
||||||
|
console.log(' ✓ CA:', tarifData.ca);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type de cotisation
|
||||||
|
if (tarifData.typeCotisation) {
|
||||||
|
const radio = document.querySelector(`input[name="typeCot"][value="${tarifData.typeCotisation}"]`);
|
||||||
|
if (radio) {
|
||||||
|
radio.checked = true;
|
||||||
|
console.log(' ✓ Type cotisation:', tarifData.typeCotisation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nombre de véhicules
|
||||||
|
if (tarifData.nombreVehicules) {
|
||||||
|
setValue('nombreVehicules', tarifData.nombreVehicules);
|
||||||
|
console.log(' ✓ Véhicules:', tarifData.nombreVehicules);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══ ACTIVITÉS ═══
|
||||||
|
|
||||||
|
const activitySelector = document.getElementById('activity-selector');
|
||||||
|
if (activitySelector && tarifData.activites) {
|
||||||
|
const activitesToAdd = [];
|
||||||
|
|
||||||
|
if (tarifData.activites.voiturier?.checked) {
|
||||||
|
activitesToAdd.push('Voiturier/Loueur');
|
||||||
|
}
|
||||||
|
if (tarifData.activites.commissionnaire?.checked) {
|
||||||
|
activitesToAdd.push('Commissionnaire de Transport');
|
||||||
|
}
|
||||||
|
if (tarifData.activites.demenageur?.checked) {
|
||||||
|
activitesToAdd.push('Déménageur d\'entreprises');
|
||||||
|
}
|
||||||
|
if (tarifData.activites.logistique?.checked) {
|
||||||
|
activitesToAdd.push('Prestataire logistique');
|
||||||
|
}
|
||||||
|
if (tarifData.activites.autocariste?.checked) {
|
||||||
|
activitesToAdd.push('Autocariste');
|
||||||
|
}
|
||||||
|
if (tarifData.activites.autres?.checked) {
|
||||||
|
activitesToAdd.push('Autres activités');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sélectionner les options dans le select
|
||||||
|
Array.from(activitySelector.options).forEach(option => {
|
||||||
|
if (activitesToAdd.includes(option.value)) {
|
||||||
|
option.selected = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trigger change pour créer les chips Materialize
|
||||||
|
const event = new Event('change', { bubbles: true });
|
||||||
|
activitySelector.dispatchEvent(event);
|
||||||
|
|
||||||
|
console.log(' ✓ Activités:', activitesToAdd.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══ MARCHANDISES ═══
|
||||||
|
|
||||||
|
const marchandiseSelector = document.getElementById('marchandise-selector');
|
||||||
|
if (marchandiseSelector && tarifData.marchandises) {
|
||||||
|
const marchandisesToSelect = [];
|
||||||
|
|
||||||
|
// Parser les marchandises de chaque type
|
||||||
|
['voiturier', 'commissionnaire', 'demenageur', 'logistique', 'autocariste', 'autres'].forEach(type => {
|
||||||
|
const marchArray = tarifData.marchandises[type];
|
||||||
|
if (Array.isArray(marchArray)) {
|
||||||
|
marchArray.forEach(m => marchandisesToSelect.push(m));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sélectionner dans le select
|
||||||
|
Array.from(marchandiseSelector.options).forEach(option => {
|
||||||
|
if (marchandisesToSelect.includes(option.text) || marchandisesToSelect.includes(option.value)) {
|
||||||
|
option.selected = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const event = new Event('change', { bubbles: true });
|
||||||
|
marchandiseSelector.dispatchEvent(event);
|
||||||
|
|
||||||
|
console.log(' ✓ Marchandises:', marchandisesToSelect.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══ ZONES GÉOGRAPHIQUES ═══
|
||||||
|
|
||||||
|
if (tarifData.zones) {
|
||||||
|
let zonesCount = 0;
|
||||||
|
Object.keys(tarifData.zones).forEach(zoneKey => {
|
||||||
|
const checkbox = document.getElementById(zoneKey);
|
||||||
|
if (checkbox && tarifData.zones[zoneKey]) {
|
||||||
|
checkbox.checked = true;
|
||||||
|
zonesCount++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(' ✓ Zones:', zonesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══ PROTECTION JURIDIQUE ═══
|
||||||
|
|
||||||
|
if (tarifData.garantiesAdditionnelles?.pj) {
|
||||||
|
const switchPJ = document.getElementById('switchPJ');
|
||||||
|
if (switchPJ) {
|
||||||
|
switchPJ.checked = true;
|
||||||
|
console.log(' ✓ PJ activée');
|
||||||
|
|
||||||
|
// Afficher la section PJ
|
||||||
|
const pjSection = document.getElementById('pj-section');
|
||||||
|
if (pjSection) pjSection.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══ RCE ═══
|
||||||
|
|
||||||
|
if (tarifData.rce?.checked) {
|
||||||
|
const choixRCE = document.getElementById('choixRCE');
|
||||||
|
if (choixRCE) {
|
||||||
|
choixRCE.checked = true;
|
||||||
|
console.log(' ✓ RCE activée');
|
||||||
|
|
||||||
|
// Afficher la section RCE
|
||||||
|
const rceSection = document.getElementById('section-rce');
|
||||||
|
if (rceSection) rceSection.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══ TPPC ═══
|
||||||
|
|
||||||
|
if (tarifData.garantiesAdditionnelles?.tppc?.checked) {
|
||||||
|
const checkTPPC = document.getElementById('checkTPPC');
|
||||||
|
if (checkTPPC) {
|
||||||
|
checkTPPC.checked = true;
|
||||||
|
|
||||||
|
if (tarifData.garantiesAdditionnelles.tppc.capital) {
|
||||||
|
setValue('capitalTPPC', tarifData.garantiesAdditionnelles.tppc.capital);
|
||||||
|
}
|
||||||
|
if (tarifData.garantiesAdditionnelles.tppc.vehicules) {
|
||||||
|
setValue('vehiculesTPPC', tarifData.garantiesAdditionnelles.tppc.vehicules);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(' ✓ TPPC');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══ ENGAGEMENTS COMPLÉMENTAIRES ═══
|
||||||
|
|
||||||
|
const engagements = tarifData.engagementsComplementaires;
|
||||||
|
if (engagements) {
|
||||||
|
if (engagements.domicileImmatriculation?.checked) {
|
||||||
|
setValue('checkDomImmat', true);
|
||||||
|
console.log(' ✓ Domicile immatriculation');
|
||||||
|
}
|
||||||
|
if (engagements.contenantConfie?.checked) {
|
||||||
|
setValue('checkContConf', true);
|
||||||
|
console.log(' ✓ Contenant confié');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══ SINISTRALITÉ ═══
|
||||||
|
|
||||||
|
if (tarifData.sinistralite) {
|
||||||
|
if (tarifData.sinistralite.nombre3ans) {
|
||||||
|
setValue('nbSinistres3ans', tarifData.sinistralite.nombre3ans);
|
||||||
|
}
|
||||||
|
if (tarifData.sinistralite.montant3ans) {
|
||||||
|
setValue('montantSinistres3ans', tarifData.sinistralite.montant3ans);
|
||||||
|
}
|
||||||
|
console.log(' ✓ Sinistralité');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══ RÉSULTATS TARIFAIRES ═══
|
||||||
|
|
||||||
|
if (tarifData.resultats) {
|
||||||
|
const res = tarifData.resultats;
|
||||||
|
|
||||||
|
// Taux
|
||||||
|
if (res.tauxRCCHT) setValue('tauxRCCHT', res.tauxRCCHT);
|
||||||
|
if (res.tauxRCCTTC) setValue('tauxRCCTTC', res.tauxRCCTTC);
|
||||||
|
if (res.tauxRCEHT) setValue('tauxRCEHT', res.tauxRCEHT);
|
||||||
|
if (res.tauxRCETTC) setValue('tauxRCETTC', res.tauxRCETTC);
|
||||||
|
if (res.tauxTotalHT) setValue('tauxTotalHT', res.tauxTotalHT);
|
||||||
|
if (res.tauxTotalTTC) setValue('tauxTotalTTC', res.tauxTotalTTC);
|
||||||
|
|
||||||
|
// Cotisations
|
||||||
|
if (res.cotRCCHT) setValue('cotRCCHT', res.cotRCCHT);
|
||||||
|
if (res.cotRCCTTC) setValue('cotRCCTTC', res.cotRCCTTC);
|
||||||
|
if (res.cotRCEHT) setValue('cotRCEHT', res.cotRCEHT);
|
||||||
|
if (res.cotRCETTC) setValue('cotRCETTC', res.cotRCETTC);
|
||||||
|
if (res.cotPJHT) setValue('cotPJHT', res.cotPJHT);
|
||||||
|
if (res.cotPJTTC) setValue('cotPJTTC', res.cotPJTTC);
|
||||||
|
if (res.cotTotalHT) setValue('cotTotalHT', res.cotTotalHT);
|
||||||
|
if (res.cotTotalTTC) setValue('cotTotalTTC', res.cotTotalTTC);
|
||||||
|
|
||||||
|
console.log(' ✓ Résultats tarifaires');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forcer la mise à jour des éléments Materialize
|
||||||
|
if (window.M && window.M.FormSelect) {
|
||||||
|
const selects = document.querySelectorAll('select');
|
||||||
|
window.M.FormSelect.init(selects);
|
||||||
|
}
|
||||||
|
if (window.M && window.M.updateTextFields) {
|
||||||
|
window.M.updateTextFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ Pré-remplissage Projet terminé');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Erreur lors du pré-remplissage Projet:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// PRÉ-REMPLISSAGE PROJET → TARIF
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pré-remplit le formulaire Tarif avec les données du Projet.
|
||||||
|
* Cette fonction est appelée quand l'utilisateur passe du Projet au Tarif.
|
||||||
|
*
|
||||||
|
* @param {Object} projetData - Données complètes du projet
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* prefillTarifFromProjet(projetData);
|
||||||
|
*/
|
||||||
|
function prefillTarifFromProjet(projetData) {
|
||||||
|
if (!projetData) {
|
||||||
|
console.warn('Pas de données projet à pré-remplir');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('📝 Pré-remplissage Tarif depuis Projet...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// CA
|
||||||
|
if (projetData.ca) {
|
||||||
|
setValue('CA', projetData.ca);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type de cotisation
|
||||||
|
if (projetData.typeCot) {
|
||||||
|
const radio = document.querySelector(`input[name="cotisation"][value="${projetData.typeCot}"]`);
|
||||||
|
if (radio) radio.checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zones géographiques
|
||||||
|
['zone1', 'zone2', 'zone3', 'zone4', 'zone5', 'zone6'].forEach(zone => {
|
||||||
|
if (projetData[zone]) {
|
||||||
|
setValue(zone, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// PJ
|
||||||
|
if (projetData.pj) {
|
||||||
|
setValue('checkPJ', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RCE
|
||||||
|
if (projetData.autresRC) {
|
||||||
|
setValue('checkRCE', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ Pré-remplissage Tarif terminé');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Erreur lors du pré-remplissage Tarif:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// EXPORT PUBLIC
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
window.RCDataManager = {
|
||||||
|
collectAllTarifData,
|
||||||
|
prefillProjetFromTarif,
|
||||||
|
prefillTarifFromProjet,
|
||||||
|
FIELD_MAPPING
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('✅ RC Data Manager loaded');
|
||||||
|
|
||||||
|
})(window);
|
||||||
|
|
@ -0,0 +1,388 @@
|
||||||
|
/**
|
||||||
|
* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
* RC SYNC ORCHESTRATOR
|
||||||
|
* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
*
|
||||||
|
* Ce module orchestre la synchronisation entre Tarif RC et Projet RC.
|
||||||
|
* Il s'intègre avec les formulaires existants sans les modifier.
|
||||||
|
*
|
||||||
|
* @requires rc-sync-utils.js
|
||||||
|
* @requires rc-data-manager.js
|
||||||
|
* @author AXA Transport Team
|
||||||
|
* @version 2.0.0
|
||||||
|
* @since 2026-02-17
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(window) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Attendre que les dépendances soient chargées
|
||||||
|
if (!window.RCSync || !window.RCDataManager) {
|
||||||
|
console.error('❌ Dépendances RC Sync manquantes');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { isChangeImpactingTarif, showReturnToTarifModal } = window.RCSync;
|
||||||
|
const { collectAllTarifData, prefillProjetFromTarif, prefillTarifFromProjet } = window.RCDataManager;
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// CONFIGURATION
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
const SESSION_STORAGE_KEYS = {
|
||||||
|
TARIF_DATA: 'rc_tarif_validated_data',
|
||||||
|
PROJET_DATA: 'rc_projet_data',
|
||||||
|
TARIF_ORIGINAL: 'rc_tarif_original_for_comparison'
|
||||||
|
};
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// DÉTECTION DE LA PAGE ACTIVE
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Détecte la page active (tarif ou projet) depuis l'URL.
|
||||||
|
*
|
||||||
|
* @returns {'tarif'|'projet'|null} Page active ou null
|
||||||
|
*/
|
||||||
|
function detectActivePage() {
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const submenu = params.get('submenu');
|
||||||
|
|
||||||
|
if (submenu === 'tarif' || submenu === 'tarifrc') {
|
||||||
|
return 'tarif';
|
||||||
|
} else if (submenu === 'projet' || submenu === 'projetrc') {
|
||||||
|
return 'projet';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// GESTION SESSIONSTORAGE
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sauvegarde les données du tarif validé dans sessionStorage.
|
||||||
|
*
|
||||||
|
* @param {Object} tarifData - Données complètes du tarif
|
||||||
|
*/
|
||||||
|
function saveTarifDataToSession(tarifData) {
|
||||||
|
try {
|
||||||
|
sessionStorage.setItem(SESSION_STORAGE_KEYS.TARIF_DATA, JSON.stringify(tarifData));
|
||||||
|
sessionStorage.setItem(SESSION_STORAGE_KEYS.TARIF_ORIGINAL, JSON.stringify(tarifData));
|
||||||
|
console.log('✅ Données tarif sauvegardées en session');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Erreur sauvegarde session:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère les données du tarif depuis sessionStorage.
|
||||||
|
*
|
||||||
|
* @returns {Object|null} Données du tarif ou null
|
||||||
|
*/
|
||||||
|
function getTarifDataFromSession() {
|
||||||
|
try {
|
||||||
|
const data = sessionStorage.getItem(SESSION_STORAGE_KEYS.TARIF_DATA);
|
||||||
|
return data ? JSON.parse(data) : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Erreur lecture session:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère les données originales du tarif pour comparaison.
|
||||||
|
*
|
||||||
|
* @returns {Object|null} Données originales du tarif
|
||||||
|
*/
|
||||||
|
function getTarifOriginalDataFromSession() {
|
||||||
|
try {
|
||||||
|
const data = sessionStorage.getItem(SESSION_STORAGE_KEYS.TARIF_ORIGINAL);
|
||||||
|
return data ? JSON.parse(data) : null;
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// HOOK: APRÈS VALIDATION TARIF
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook appelé après la validation du tarif commercial.
|
||||||
|
* Collecte toutes les données et les sauvegarde en session.
|
||||||
|
*
|
||||||
|
* Cette fonction doit être appelée juste avant la redirection vers le projet.
|
||||||
|
*/
|
||||||
|
function onTarifValidated() {
|
||||||
|
console.log('🎯 Hook: Tarif validé, collecte des données...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Collecter toutes les données du tarif
|
||||||
|
const tarifData = collectAllTarifData();
|
||||||
|
|
||||||
|
// Sauvegarder en session pour le pré-remplissage projet
|
||||||
|
saveTarifDataToSession(tarifData);
|
||||||
|
|
||||||
|
console.log('✅ Données tarif prêtes pour le projet');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Erreur hook tarif validé:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// INITIALISATION PAGE PROJET
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise le formulaire projet au chargement.
|
||||||
|
* Configure UNIQUEMENT la détection des changements impactants.
|
||||||
|
* Le pré-remplissage est géré par prefillFromTarif() existant dans projet-form-RC.js
|
||||||
|
*/
|
||||||
|
function initProjetPage() {
|
||||||
|
console.log('🚀 Initialisation RC Orchestrator pour page Projet...');
|
||||||
|
|
||||||
|
// Les données rc/tarif/projet sont DÉJÀ chargées depuis la base
|
||||||
|
// par le code existant dans projet-form-RC.js
|
||||||
|
// On configure juste la détection des changements
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setupProjetChangeDetection();
|
||||||
|
}, 1000); // Attendre que prefillFromTarif() ait fini
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure la détection des changements impactants dans le projet.
|
||||||
|
* Affiche un modal si l'utilisateur modifie un champ qui impacte le tarif.
|
||||||
|
* Utilise les variables globales rc/tarif depuis projet-form-RC.js
|
||||||
|
*/
|
||||||
|
function setupProjetChangeDetection() {
|
||||||
|
// Les données originales sont dans les variables globales window.tarif et window.rc
|
||||||
|
// définies par projet-form-RC.js
|
||||||
|
const tarifOriginal = window.tarif;
|
||||||
|
const rcOriginal = window.rc;
|
||||||
|
|
||||||
|
if (!tarifOriginal && !rcOriginal) {
|
||||||
|
console.log('ℹ️ Pas de tarif/rc, pas de détection');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('👁️ Configuration détection changements...');
|
||||||
|
console.log('📋 Données originales:', { tarif: tarifOriginal, rc: rcOriginal });
|
||||||
|
|
||||||
|
// Liste COMPLÈTE des éléments à surveiller (tous les champs impactants)
|
||||||
|
const elementsToWatch = [
|
||||||
|
// CA et infos générales
|
||||||
|
'CA', 'chiffreAffaire', 'nombreVehicules', 'nbrVehicule',
|
||||||
|
|
||||||
|
// Zones géographiques
|
||||||
|
'zone1', 'zone2', 'zone3', 'zone4', 'zone5', 'zone6',
|
||||||
|
|
||||||
|
// Protection Juridique
|
||||||
|
'switchPJ', 'checkPJ',
|
||||||
|
|
||||||
|
// RCE
|
||||||
|
'choixRCE', 'checkRCE',
|
||||||
|
|
||||||
|
// TPPC
|
||||||
|
'checkTPPC', 'capitalTPPC', 'vehiculesTPPC',
|
||||||
|
|
||||||
|
// Engagements complémentaires
|
||||||
|
'checkDomImmat', 'checkContConf', 'checkDiffInv',
|
||||||
|
|
||||||
|
// Garanties additionnelles
|
||||||
|
'checkStationLavage', 'checkGarageInterne', 'checkCSE',
|
||||||
|
|
||||||
|
// Sinistralité
|
||||||
|
'nbSinistres3ans', 'montantSinistres3ans',
|
||||||
|
|
||||||
|
// Autres
|
||||||
|
'programmeInternationale', 'participationResultat'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Ajouter des listeners sur tous les éléments surveillés
|
||||||
|
elementsToWatch.forEach(elementId => {
|
||||||
|
const element = document.getElementById(elementId);
|
||||||
|
if (!element) return;
|
||||||
|
|
||||||
|
const eventType = element.type === 'checkbox' ? 'change' : 'blur';
|
||||||
|
|
||||||
|
element.addEventListener(eventType, function(e) {
|
||||||
|
const fieldName = this.id;
|
||||||
|
const newValue = this.type === 'checkbox' ? this.checked : this.value;
|
||||||
|
|
||||||
|
console.log(`🔍 Changement détecté: ${fieldName} = ${newValue}`);
|
||||||
|
|
||||||
|
// Vérifier si c'est un champ impactant
|
||||||
|
if (isFieldImpactingTarif(fieldName)) {
|
||||||
|
console.warn(`⚠️ "${fieldName}" impacte le tarif !`);
|
||||||
|
showReturnToTarifModal(fieldName);
|
||||||
|
} else {
|
||||||
|
console.log(`ℹ️ "${fieldName}" n'impacte pas le tarif`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Surveiller les radio buttons (type de cotisation)
|
||||||
|
const radioTypeCot = document.querySelectorAll('input[name="typeCot"]');
|
||||||
|
radioTypeCot.forEach(radio => {
|
||||||
|
radio.addEventListener('change', function() {
|
||||||
|
console.log(`🔍 Changement type cotisation: ${this.value}`);
|
||||||
|
console.warn(`⚠️ Type de cotisation impacte le tarif !`);
|
||||||
|
showReturnToTarifModal('Type de cotisation');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Surveiller le select activités
|
||||||
|
const activitySelector = document.getElementById('activity-selector');
|
||||||
|
if (activitySelector) {
|
||||||
|
activitySelector.addEventListener('change', function() {
|
||||||
|
const selectedValues = Array.from(this.selectedOptions).map(opt => opt.value);
|
||||||
|
console.log(`🔍 Changement activités:`, selectedValues);
|
||||||
|
console.warn(`⚠️ Activités impactent le tarif !`);
|
||||||
|
showReturnToTarifModal('Activités');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Surveiller le select marchandises
|
||||||
|
const marchandiseSelector = document.getElementById('marchandise-selector');
|
||||||
|
if (marchandiseSelector) {
|
||||||
|
marchandiseSelector.addEventListener('change', function() {
|
||||||
|
const selectedValues = Array.from(this.selectedOptions).map(opt => opt.value);
|
||||||
|
console.log(`🔍 Changement marchandises:`, selectedValues);
|
||||||
|
console.warn(`⚠️ Marchandises impactent le tarif !`);
|
||||||
|
showReturnToTarifModal('Marchandises');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Surveiller les boutons d'action sur les zones (Monde entier / Reset)
|
||||||
|
['btnMondeEntier', 'btnReset'].forEach(btnId => {
|
||||||
|
const btn = document.getElementById(btnId);
|
||||||
|
if (!btn) return;
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
console.log(`🔍 Changement zones via ${btnId}`);
|
||||||
|
console.warn('⚠️ Zones géographiques impactent le tarif !');
|
||||||
|
showReturnToTarifModal('Zones géographiques');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ Détection changements configurée sur tous les champs impactants');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// INITIALISATION PAGE TARIF
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise le formulaire tarif au chargement.
|
||||||
|
* Pré-remplit depuis le projet si l'utilisateur vient du projet.
|
||||||
|
*/
|
||||||
|
function initTarifPage() {
|
||||||
|
console.log('🚀 Initialisation page Tarif...');
|
||||||
|
|
||||||
|
// Vérifier si on vient du projet
|
||||||
|
const projetData = JSON.parse(sessionStorage.getItem(SESSION_STORAGE_KEYS.PROJET_DATA) || 'null');
|
||||||
|
|
||||||
|
if (projetData && !getTarifDataFromSession()) {
|
||||||
|
// On a des données projet mais pas de tarif validé
|
||||||
|
// = L'utilisateur a commencé par le projet
|
||||||
|
console.log('📥 Pré-remplissage depuis projet...');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
prefillTarifFromProjet(projetData);
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// INTERCEPTION DES FONCTIONS EXISTANTES
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intercepte la fonction de validation du tarif commercial existante.
|
||||||
|
* Ajoute notre hook avant la redirection.
|
||||||
|
*/
|
||||||
|
function interceptTarifValidation() {
|
||||||
|
// Attendre que la fonction window.saveTarifRC soit disponible
|
||||||
|
const checkInterval = setInterval(() => {
|
||||||
|
if (window.saveTarifRC) {
|
||||||
|
clearInterval(checkInterval);
|
||||||
|
|
||||||
|
// Sauvegarder la fonction originale
|
||||||
|
const originalSaveTarifRC = window.saveTarifRC;
|
||||||
|
|
||||||
|
// Remplacer par notre version wrappée
|
||||||
|
window.saveTarifRC = async function(...args) {
|
||||||
|
console.log('🎯 Interception saveTarifRC...');
|
||||||
|
|
||||||
|
// Appeler la fonction originale
|
||||||
|
const result = await originalSaveTarifRC.apply(this, args);
|
||||||
|
|
||||||
|
// Si succès, appeler notre hook
|
||||||
|
if (result && result.valid) {
|
||||||
|
onTarifValidated();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('✅ saveTarifRC intercepté');
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
// Timeout après 5 secondes
|
||||||
|
setTimeout(() => clearInterval(checkInterval), 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// DÉMARRAGE AUTOMATIQUE
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise l'orchestrateur au chargement de la page.
|
||||||
|
*/
|
||||||
|
function init() {
|
||||||
|
console.log('🎼 RC Sync Orchestrator: Démarrage...');
|
||||||
|
|
||||||
|
const activePage = detectActivePage();
|
||||||
|
console.log(`📄 Page active détectée: ${activePage || 'aucune'}`);
|
||||||
|
|
||||||
|
if (activePage === 'tarif') {
|
||||||
|
interceptTarifValidation();
|
||||||
|
|
||||||
|
// Attendre que le formulaire soit initialisé
|
||||||
|
setTimeout(() => {
|
||||||
|
initTarifPage();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
} else if (activePage === 'projet') {
|
||||||
|
// Attendre que le formulaire soit initialisé
|
||||||
|
setTimeout(() => {
|
||||||
|
initProjetPage();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Démarrage au chargement du DOM
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// EXPORT PUBLIC
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
window.RCOrchestrator = {
|
||||||
|
onTarifValidated,
|
||||||
|
initProjetPage,
|
||||||
|
initTarifPage,
|
||||||
|
saveTarifDataToSession,
|
||||||
|
getTarifDataFromSession
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('✅ RC Sync Orchestrator loaded');
|
||||||
|
|
||||||
|
})(window);
|
||||||
|
|
@ -0,0 +1,464 @@
|
||||||
|
/**
|
||||||
|
* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
* RC SYNCHRONIZATION UTILITIES
|
||||||
|
* ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
*
|
||||||
|
* Ce module contient toutes les fonctions utilitaires pour la synchronisation
|
||||||
|
* bidirectionnelle entre les formulaires Tarif RC et Projet RC.
|
||||||
|
*
|
||||||
|
* @author AXA Transport Team
|
||||||
|
* @version 2.0.0
|
||||||
|
* @since 2026-02-17
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(window) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// CONSTANTES
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liste exhaustive des champs qui impactent le calcul du tarif.
|
||||||
|
* Si l'un de ces champs est modifié dans le projet, un modal
|
||||||
|
* demandera à l'utilisateur de retourner au tarif.
|
||||||
|
*
|
||||||
|
* @constant {Array<string>}
|
||||||
|
*/
|
||||||
|
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 en nombre en gérant les formats français et internationaux.
|
||||||
|
* Gère les espaces, virgules, points, et valeurs nulles/undefined.
|
||||||
|
*
|
||||||
|
* @param {string|number|null|undefined} x - Valeur à convertir
|
||||||
|
* @returns {number} Nombre converti ou 0 si impossible
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* toNumber("1 234,56") // 1234.56
|
||||||
|
* toNumber("1.234,56") // 1234.56
|
||||||
|
* toNumber("1,234.56") // 1234.56
|
||||||
|
* toNumber(null) // 0
|
||||||
|
*/
|
||||||
|
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 la valeur d'un élément par son ID de manière flexible.
|
||||||
|
* Gère les différents types d'éléments (input, select, textarea, etc.)
|
||||||
|
* et les cas où l'ID contient des caractères spéciaux.
|
||||||
|
*
|
||||||
|
* @param {string} id - ID de l'élément
|
||||||
|
* @returns {HTMLElement|null} Élément trouvé ou null
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const element = getElementByIdFlexible("my-element");
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère la valeur d'un champ de formulaire de manière sécurisée.
|
||||||
|
* Gère les inputs, selects, textareas, checkboxes, et contenus textuels.
|
||||||
|
*
|
||||||
|
* @param {string} elementId - ID de l'élément
|
||||||
|
* @returns {string|number|boolean|null} Valeur du champ
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* getValue("ca") // "100000"
|
||||||
|
* getValue("checkPJ") // true
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Définit la valeur d'un champ de formulaire.
|
||||||
|
* Gère automatiquement le type de champ et met à jour l'interface.
|
||||||
|
*
|
||||||
|
* @param {string} elementId - ID de l'élément
|
||||||
|
* @param {any} value - Valeur à définir
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* setValue("ca", 100000);
|
||||||
|
* setValue("checkPJ", true);
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// COMPARAISON DE DONNÉES
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare deux tableaux pour vérifier leur égalité.
|
||||||
|
* Effectue une comparaison profonde élément par élément.
|
||||||
|
*
|
||||||
|
* @param {Array} arr1 - Premier tableau
|
||||||
|
* @param {Array} arr2 - Deuxième tableau
|
||||||
|
* @returns {boolean} true si les tableaux sont égaux
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* arraysEqual([1,2,3], [1,2,3]) // true
|
||||||
|
* arraysEqual([1,2], [1,2,3]) // false
|
||||||
|
*/
|
||||||
|
function arraysEqual(arr1, arr2) {
|
||||||
|
if (!Array.isArray(arr1) || !Array.isArray(arr2)) return false;
|
||||||
|
if (arr1.length !== arr2.length) return false;
|
||||||
|
|
||||||
|
const sorted1 = [...arr1].sort();
|
||||||
|
const sorted2 = [...arr2].sort();
|
||||||
|
|
||||||
|
return sorted1.every((val, idx) => val === sorted2[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare deux valeurs en tenant compte de leur type.
|
||||||
|
* Gère les tableaux, objets, null, undefined, et valeurs primitives.
|
||||||
|
*
|
||||||
|
* @param {any} value1 - Première valeur
|
||||||
|
* @param {any} value2 - Deuxième valeur
|
||||||
|
* @returns {boolean} true si les valeurs sont égales
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* valuesEqual([1,2], [2,1]) // true (ordre indépendant)
|
||||||
|
* valuesEqual(null, undefined) // true
|
||||||
|
* valuesEqual(100, "100") // true (conversion automatique)
|
||||||
|
*/
|
||||||
|
function valuesEqual(value1, value2) {
|
||||||
|
// Normaliser null et undefined
|
||||||
|
if (value1 == null && value2 == null) return true;
|
||||||
|
if (value1 == null || value2 == null) return false;
|
||||||
|
|
||||||
|
// Comparer les tableaux
|
||||||
|
if (Array.isArray(value1) && Array.isArray(value2)) {
|
||||||
|
return arraysEqual(value1, value2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparer les objets
|
||||||
|
if (typeof value1 === 'object' && typeof value2 === 'object') {
|
||||||
|
return JSON.stringify(value1) === JSON.stringify(value2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparer les nombres (avec conversion)
|
||||||
|
if (!isNaN(value1) && !isNaN(value2)) {
|
||||||
|
return toNumber(value1) === toNumber(value2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparaison standard
|
||||||
|
return value1 === value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// DÉTECTION DE CHANGEMENTS IMPACTANTS
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si un champ donné impacte le calcul du tarif.
|
||||||
|
* Se base sur la liste TARIF_IMPACTING_FIELDS.
|
||||||
|
*
|
||||||
|
* @param {string} fieldName - Nom du champ
|
||||||
|
* @returns {boolean} true si le champ impacte le tarif
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* isFieldImpactingTarif("ca") // true
|
||||||
|
* isFieldImpactingTarif("dateEffet") // false
|
||||||
|
*/
|
||||||
|
function isFieldImpactingTarif(fieldName) {
|
||||||
|
return TARIF_IMPACTING_FIELDS.some(field =>
|
||||||
|
fieldName.includes(field) || field.includes(fieldName)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si un changement de valeur impacte le tarif.
|
||||||
|
* Compare la nouvelle valeur avec les données originales du tarif.
|
||||||
|
*
|
||||||
|
* @param {string} fieldName - Nom du champ modifié
|
||||||
|
* @param {any} newValue - Nouvelle valeur
|
||||||
|
* @param {Object} tarifOriginalData - Données originales du tarif
|
||||||
|
* @returns {boolean} true si le changement impacte le tarif
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const impacted = isChangeImpactingTarif("ca", 200000, tarifData);
|
||||||
|
* if (impacted) showReturnToTarifModal();
|
||||||
|
*/
|
||||||
|
function isChangeImpactingTarif(fieldName, newValue, tarifOriginalData) {
|
||||||
|
// Vérifier si le champ est dans la liste des champs impactants
|
||||||
|
if (!isFieldImpactingTarif(fieldName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si pas de données originales, pas d'impact possible
|
||||||
|
if (!tarifOriginalData) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupérer la valeur originale
|
||||||
|
const originalValue = tarifOriginalData[fieldName];
|
||||||
|
|
||||||
|
// Comparer les valeurs
|
||||||
|
return !valuesEqual(newValue, originalValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
// MODAL DE RETOUR AU TARIF
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Affiche le modal demandant à l'utilisateur de retourner au tarif.
|
||||||
|
* Ce modal s'affiche quand une modification dans le projet impacte
|
||||||
|
* le calcul du tarif.
|
||||||
|
*
|
||||||
|
* @param {string} [fieldName] - Nom du champ modifié (optionnel, pour info)
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* showReturnToTarifModal("ca");
|
||||||
|
*/
|
||||||
|
function showReturnToTarifModal(fieldName) {
|
||||||
|
const modalId = 'modalRetourTarif';
|
||||||
|
let modal = document.getElementById(modalId);
|
||||||
|
|
||||||
|
// Créer le modal s'il n'existe pas
|
||||||
|
if (!modal) {
|
||||||
|
modal = createReturnToTarifModal();
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour le message si un champ est spécifié
|
||||||
|
if (fieldName) {
|
||||||
|
const messageEl = modal.querySelector('#modalRetourTarifMessage');
|
||||||
|
if (messageEl) {
|
||||||
|
messageEl.innerHTML = `
|
||||||
|
Vous avez modifié <strong>"${fieldName}"</strong> qui impacte le calcul du tarif.
|
||||||
|
<br><br>
|
||||||
|
Vous devez retourner sur le formulaire Tarif pour recalculer et valider le nouveau tarif.
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ouvrir le modal
|
||||||
|
if (window.M && window.M.Modal) {
|
||||||
|
const instance = window.M.Modal.getInstance(modal) || window.M.Modal.init(modal);
|
||||||
|
instance.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée l'élément DOM du modal de retour au tarif.
|
||||||
|
*
|
||||||
|
* @returns {HTMLElement} Élément modal créé
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function createReturnToTarifModal() {
|
||||||
|
const modal = document.createElement('div');
|
||||||
|
modal.id = 'modalRetourTarif';
|
||||||
|
modal.className = 'modal';
|
||||||
|
|
||||||
|
modal.innerHTML = `
|
||||||
|
<div class="modal-content">
|
||||||
|
<h5>⚠️ Modification impactant le tarif</h5>
|
||||||
|
<p id="modalRetourTarifMessage">
|
||||||
|
Vous avez modifié une donnée qui impacte le calcul du tarif.
|
||||||
|
<br><br>
|
||||||
|
<strong>Vous devez retourner sur le formulaire Tarif pour recalculer et valider le nouveau tarif.</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a href="#!" class="modal-close waves-effect waves-red btn-flat">Annuler</a>
|
||||||
|
<a href="#!" class="waves-effect waves-green btn" onclick="window.RCSync.navigateToTarif()">
|
||||||
|
Aller au Tarif
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return modal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate vers l'onglet Tarif depuis le Projet.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* navigateToTarif();
|
||||||
|
*/
|
||||||
|
function navigateToTarif() {
|
||||||
|
// Fermer le modal
|
||||||
|
const modal = document.getElementById('modalRetourTarif');
|
||||||
|
if (modal && window.M) {
|
||||||
|
const instance = window.M.Modal.getInstance(modal);
|
||||||
|
if (instance) instance.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
* Toutes les fonctions exportées ici sont accessibles via window.RCSync.
|
||||||
|
*/
|
||||||
|
window.RCSync = {
|
||||||
|
// Helpers
|
||||||
|
toNumber,
|
||||||
|
getValue,
|
||||||
|
setValue,
|
||||||
|
getElementByIdFlexible,
|
||||||
|
|
||||||
|
// Comparaison
|
||||||
|
arraysEqual,
|
||||||
|
valuesEqual,
|
||||||
|
|
||||||
|
// Détection changements
|
||||||
|
isFieldImpactingTarif,
|
||||||
|
isChangeImpactingTarif,
|
||||||
|
|
||||||
|
// Modal
|
||||||
|
showReturnToTarifModal,
|
||||||
|
navigateToTarif,
|
||||||
|
|
||||||
|
// Constantes
|
||||||
|
TARIF_IMPACTING_FIELDS
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('✅ RC Sync Utils loaded');
|
||||||
|
|
||||||
|
})(window);
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -8,157 +8,196 @@ const path = require("path");
|
||||||
const moment = require("moment");
|
const moment = require("moment");
|
||||||
const parcoursService = require("../services/parcoursService");
|
const parcoursService = require("../services/parcoursService");
|
||||||
const contratService = require("../services/contratService");
|
const contratService = require("../services/contratService");
|
||||||
|
const userService = require("../services/userService");
|
||||||
const globalService = require("../services/globalService");
|
const globalService = require("../services/globalService");
|
||||||
|
//const projetformrc = require("../../public/js/projet-form-rc");
|
||||||
|
//const moduloRC = require("../constantes/json-modulateur-rc"); useless pour le moment ?
|
||||||
|
|
||||||
require("moment/locale/fr");
|
require("moment/locale/fr");
|
||||||
moment.locale("fr");
|
moment.locale("fr");
|
||||||
|
|
||||||
|
// Fonctions helper pour récupérer les valeurs selon la franchise choisie
|
||||||
|
function getSelectedTarifReference(tarifRC) {
|
||||||
|
const franchise = tarifRC.franchiseChoisie;
|
||||||
|
if (franchise === '250') return tarifRC.primeTotal_250;
|
||||||
|
if (franchise === '400') return tarifRC.primeTotal_400;
|
||||||
|
if (franchise === 'mini300') return tarifRC.primeTotal_2000;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelectedPrime(tarifRC, type) {
|
||||||
|
const franchise = tarifRC.franchiseChoisie;
|
||||||
|
if (!franchise) return 0;
|
||||||
|
|
||||||
|
const suffix = franchise === 'mini300' ? '2000' : franchise;
|
||||||
|
const fieldName = `prime${type}_${suffix}`;
|
||||||
|
return tarifRC[fieldName] || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelectedTaux(tarifRC, type) {
|
||||||
|
const franchise = tarifRC.franchiseChoisie;
|
||||||
|
if (!franchise) return 0;
|
||||||
|
|
||||||
|
const suffix = franchise === 'mini300' ? '2000' : franchise;
|
||||||
|
const fieldName = `taux${type}_${suffix}`;
|
||||||
|
return tarifRC[fieldName] || 0;
|
||||||
|
}
|
||||||
|
|
||||||
router.post("/rc/projet/:numParcours", async (req, res) => {
|
router.post("/rc/projet/:numParcours", async (req, res) => {
|
||||||
const content = fs.readFileSync(
|
|
||||||
path.resolve("src/templates/template-projet-rc.docx"),
|
|
||||||
"binary"
|
|
||||||
);
|
|
||||||
|
|
||||||
const zip = new PizZip(content);
|
|
||||||
const doc = new Docxtemplater(zip, { paragraphLoop: true, linebreaks: true });
|
|
||||||
const numParcours = req.params.numParcours.toUpperCase();
|
|
||||||
|
|
||||||
const parcours = await parcoursService.getParcoursByNumParcours(numParcours);
|
|
||||||
const contrat = await contratService.getContratById(parcours.contrat);
|
|
||||||
const client = contrat?.["@expand"]?.client || {};
|
|
||||||
const intermediaire = contrat?.["@expand"]?.intermediaire || {};
|
|
||||||
const rc = contrat?.["@expand"]?.enCours || {};
|
|
||||||
|
|
||||||
const listAssAdd = [];
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rc.assureAdditionnel.forEach((objet) => {
|
const content = fs.readFileSync(
|
||||||
listAssAdd.push(
|
path.resolve("src/templates/template-projet-rc.docx"),
|
||||||
objet.nom +
|
"binary"
|
||||||
|
);
|
||||||
|
|
||||||
|
const zip = new PizZip(content);
|
||||||
|
const doc = new Docxtemplater(zip, { paragraphLoop: true, linebreaks: true });
|
||||||
|
const numParcours = req.params.numParcours.toUpperCase();
|
||||||
|
|
||||||
|
const parcours = await parcoursService.getParcoursByNumParcours(numParcours);
|
||||||
|
const contrat = await contratService.getContratById(parcours.contrat);
|
||||||
|
const client = contrat?.["@expand"]?.client || {};
|
||||||
|
const intermediaire = contrat?.["@expand"]?.intermediaire || {};
|
||||||
|
|
||||||
|
// Récupérer la collection rc avec ses relations (APRÈS placerDansEnCours, c'est dans enCours)
|
||||||
|
const rcFull = contrat?.["@expand"]?.enCours;
|
||||||
|
if (!rcFull) {
|
||||||
|
logger.log('error', 'No RC found for contrat:', contrat.id);
|
||||||
|
return res.status(404).send("Aucune donnée RC trouvée pour ce contrat");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Les données sont déjà expandées par contratService
|
||||||
|
const rc = rcFull?.["@expand"]?.projetRC || {};
|
||||||
|
|
||||||
|
const listAssAdd = [];
|
||||||
|
|
||||||
|
// Traiter les assurés additionnels s'ils existent
|
||||||
|
if (rc.assureAdditionnel && Array.isArray(rc.assureAdditionnel)) {
|
||||||
|
rc.assureAdditionnel.forEach((objet) => {
|
||||||
|
listAssAdd.push(
|
||||||
|
objet.nom +
|
||||||
" - Adresse : " +
|
" - Adresse : " +
|
||||||
objet.adresse +
|
objet.adresse +
|
||||||
" - Siret : " +
|
" - Siret : " +
|
||||||
objet.siret
|
objet.siret
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} catch (error) { }
|
|
||||||
|
|
||||||
// Conditions zone
|
|
||||||
hasMondeEntier = false;
|
|
||||||
hasZone1 = false;
|
|
||||||
hasZone2 = false;
|
|
||||||
hasZone3 = false;
|
|
||||||
hasZone4 = false;
|
|
||||||
hasZone5 = false;
|
|
||||||
hasZone6 = false;
|
|
||||||
hasActiviteButNotMultimodal = false; // Attention trick pour les zones géographiques dans le cas de multimodal
|
|
||||||
hasZone456 = false;
|
|
||||||
|
|
||||||
// Cas monde entier
|
|
||||||
if (
|
|
||||||
rc.zone1 &&
|
|
||||||
rc.zone2 &&
|
|
||||||
rc.zone3 &&
|
|
||||||
rc.zone4 &&
|
|
||||||
rc.zone5 &&
|
|
||||||
rc.zone6 &&
|
|
||||||
(rc.actVoiturier ||
|
|
||||||
rc.actLoueur ||
|
|
||||||
rc.actDouane ||
|
|
||||||
rc.actDemPar ||
|
|
||||||
rc.actDemParDom ||
|
|
||||||
rc.actDemParAdv ||
|
|
||||||
rc.actDemEntr ||
|
|
||||||
rc.actDemInterne ||
|
|
||||||
rc.actGardeMeuble ||
|
|
||||||
rc.actEntDep ||
|
|
||||||
rc.actPrestaLog ||
|
|
||||||
rc.actLevageur)
|
|
||||||
) {
|
|
||||||
hasMondeEntier = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cas date du jour d'édition
|
|
||||||
let dateNow;
|
|
||||||
|
|
||||||
dateNow = moment().format("DD MMMM YYYY");
|
|
||||||
|
|
||||||
// Cas une ou plusieurs zone(s) spécifique(s) + cas zone456
|
|
||||||
if (hasMondeEntier == false && (rc.actVoiturier || rc.actLoueur || rc.actDouane || rc.actDemPar || rc.actDemParDom || rc.actDemParAct || rc.actDemEntr || rc.actDemInterne || rc.actGardeMeuble || rc.actEntDep || rc.actPrestaLog || rc.actLevageur || rc.actDemParAdv)) {
|
|
||||||
if (rc.zone1) {
|
|
||||||
hasZone1 = true;
|
|
||||||
}
|
}
|
||||||
if (rc.zone2) {
|
|
||||||
hasZone2 = true;
|
// Conditions zone
|
||||||
|
let hasMondeEntier = false;
|
||||||
|
let hasZone1 = false;
|
||||||
|
let hasZone2 = false;
|
||||||
|
let hasZone3 = false;
|
||||||
|
let hasZone4 = false;
|
||||||
|
let hasZone5 = false;
|
||||||
|
let hasZone6 = false;
|
||||||
|
let hasActiviteButNotMultimodal = false; // Attention trick pour les zones géographiques dans le cas de multimodal
|
||||||
|
let hasZone456 = false;
|
||||||
|
|
||||||
|
// Cas monde entier
|
||||||
|
if (
|
||||||
|
rc.zone1 &&
|
||||||
|
rc.zone2 &&
|
||||||
|
rc.zone3 &&
|
||||||
|
rc.zone4 &&
|
||||||
|
rc.zone5 &&
|
||||||
|
rc.zone6 &&
|
||||||
|
(rc.actVoiturier ||
|
||||||
|
rc.actLoueur ||
|
||||||
|
rc.actDouane ||
|
||||||
|
rc.actDemPar ||
|
||||||
|
rc.actDemParDom ||
|
||||||
|
rc.actDemParAdv ||
|
||||||
|
rc.actDemEntr ||
|
||||||
|
rc.actDemInterne ||
|
||||||
|
rc.actGardeMeuble ||
|
||||||
|
rc.actEntDep ||
|
||||||
|
rc.actPrestaLog ||
|
||||||
|
rc.actLevageur)
|
||||||
|
) {
|
||||||
|
hasMondeEntier = true;
|
||||||
}
|
}
|
||||||
if (rc.zone3) {
|
|
||||||
hasZone3 = true;
|
// Cas date du jour d'édition
|
||||||
|
let dateNow = moment().format("DD MMMM YYYY");
|
||||||
|
|
||||||
|
// Cas une ou plusieurs zone(s) spécifique(s) + cas zone456
|
||||||
|
if (hasMondeEntier == false && (rc.actVoiturier || rc.actLoueur || rc.actDouane || rc.actDemPar || rc.actDemParDom || rc.actDemParAct || rc.actDemEntr || rc.actDemInterne || rc.actGardeMeuble || rc.actEntDep || rc.actPrestaLog || rc.actLevageur || rc.actDemParAdv)) {
|
||||||
|
if (rc.zone1) {
|
||||||
|
hasZone1 = true;
|
||||||
|
}
|
||||||
|
if (rc.zone2) {
|
||||||
|
hasZone2 = true;
|
||||||
|
}
|
||||||
|
if (rc.zone3) {
|
||||||
|
hasZone3 = true;
|
||||||
|
}
|
||||||
|
if (rc.zone4) {
|
||||||
|
hasZone4 = true;
|
||||||
|
hasZone456 = true;
|
||||||
|
}
|
||||||
|
if (rc.zone5) {
|
||||||
|
hasZone5 = true;
|
||||||
|
hasZone456 = true;
|
||||||
|
}
|
||||||
|
if (rc.zone6) {
|
||||||
|
hasZone6 = true;
|
||||||
|
hasZone456 = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (rc.zone4) {
|
|
||||||
hasZone4 = true;
|
// Cas Activité multimodal + Au minimum une zone
|
||||||
hasZone456 = true;
|
if (rc.actMultimodal) {
|
||||||
|
if (hasZone1 || hasZone2 || hasZone3 || hasZone456 || hasMondeEntier) {
|
||||||
|
hasActiviteButNotMultimodal = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (rc.zone5) {
|
|
||||||
hasZone5 = true;
|
let hasMondeEntierOrMultimodal, hasNotMondeEntierOrMultimodal;
|
||||||
hasZone456 = true;
|
if (rc.actMultimodal || hasMondeEntier) {
|
||||||
|
hasMondeEntierOrMultimodal = true;
|
||||||
|
hasNotMondeEntierOrMultimodal = false;
|
||||||
|
} else {
|
||||||
|
hasMondeEntierOrMultimodal = false;
|
||||||
|
hasNotMondeEntierOrMultimodal = true;
|
||||||
}
|
}
|
||||||
if (rc.zone6) {
|
|
||||||
hasZone6 = true;
|
// Conditions Extensions RCC
|
||||||
hasZone456 = true;
|
let extRCC = false;
|
||||||
|
|
||||||
|
if ( rc.extRCCModifCalArrim == true || rc.extRCCFerroutage == true || rc.extRCCFraisRecons == true || rc.extRCCConfie == true || rc.extRCCTPPC == true || rc.extRCCRegie == true || rc.extRCCSansMontageDemontage == true ) {
|
||||||
|
extRCC = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Cas Activité multimodal + Au minimum une zone
|
// Conditions Extensions RCE
|
||||||
if (rc.actMultimodal) {
|
let extRCE = false;
|
||||||
if (hasZone1 || hasZone2 || hasZone3 || hasZone456 || hasMondeEntier) {
|
|
||||||
hasActiviteButNotMultimodal = true;
|
if (rc.extRCEMontageDemontage == true || rc.extRCEBraDebra == true) {
|
||||||
|
extRCE = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (rc.actMultimodal || hasMondeEntier) {
|
// Conditions Activitées
|
||||||
hasMondeEntierOrMultimodal = true;
|
let hasActiviteDemenageurGardeMeuble = false;
|
||||||
hasNotMondeEntierOrMultimodal = false;
|
if ( rc.actDemPar == true || rc.actDemEntr == true || rc.actDemParDom == true || rc.actDemParAdv == true || rc.actDemInterne) {
|
||||||
} else {
|
hasActiviteDemenageurGardeMeuble = true;
|
||||||
hasMondeEntierOrMultimodal = false;
|
}
|
||||||
hasNotMondeEntierOrMultimodal = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conditions Extensions RCC
|
let oneOfActiviteDemenageurParticulier = false;
|
||||||
let extRCC = false;
|
if ( rc.actDemPar == true || rc.actDemParDom == true || rc.actDemParAdv == true ) {
|
||||||
|
oneOfActiviteDemenageurParticulier = true;
|
||||||
|
}
|
||||||
|
|
||||||
if ( rc.extRCCModifCalArrim == true || rc.extRCCFerroutage == true || rc.extRCCFraisRecons == true || rc.extRCCConfie == true || rc.extRCCTPPC == true || rc.extRCCRegie == true || rc.extRCCSansMontageDemontage == true ) {
|
let coorDem = "";
|
||||||
extRCC = true;
|
if ( (rc.actDemPar == true || rc.actDemParDom == true || rc.actDemParAdv == true) && (rc.actDemEntr == true || rc.actDemInterne == true) && rc.actGardeMeuble == true) {
|
||||||
}
|
coorDem = ",";
|
||||||
|
} else if ( (rc.actDemPar == true || rc.actDemParDom == true || rc.actDemParAdv == true) && (rc.actDemEntr == true || rc.actDemInterne == true) && rc.actGardeMeuble == false ) {
|
||||||
|
coorDem = " et";
|
||||||
|
}
|
||||||
|
|
||||||
// Conditions Extensions RCC
|
// Variables numériques
|
||||||
let extRCE = false;
|
const franchiseTarif = "250";
|
||||||
|
|
||||||
if ( rc.extRCEMontageDemontage == true || rc.extRCEBraDebra == true) {
|
|
||||||
extRCE = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conditions Activitées
|
|
||||||
let hasActiviteDemenageurGardeMeuble = false;
|
|
||||||
if ( rc.actDemPar == true || rc.actDemEntr == true || rc.actDemParDom == true || rc.actDemParAdv == true || rc.actDemInterne) {
|
|
||||||
hasActiviteDemenageurGardeMeuble = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let oneOfActiviteDemenageurParticulier = false;
|
|
||||||
if ( rc.actDemPar == true || rc.actDemParDom == true || rc.actDemParAdv == true ) {
|
|
||||||
oneOfActiviteDemenageurParticulier = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let coorDem = "";
|
|
||||||
if ( (rc.actDemPar == true || rc.actDemParDom == true || rc.actDemParAdv == true) && (rc.actDemEntr == true || rc.actDemInterne == true) && rc.actGardeMeuble == true) {
|
|
||||||
coorDem = ",";
|
|
||||||
} else if ( (rc.actDemPar == true || rc.actDemParDom == true || rc.actDemParAdv == true) && (rc.actDemEntr == true || rc.actDemInterne == true) && rc.actGardeMeuble == false ) {
|
|
||||||
coorDem = " et";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variables numériques
|
|
||||||
const franchiseTarif = "250";
|
|
||||||
|
|
||||||
try {
|
|
||||||
doc.render({
|
doc.render({
|
||||||
// Client
|
// Client
|
||||||
nomClient: client.nom,
|
nomClient: client.nom,
|
||||||
|
|
@ -303,51 +342,310 @@ router.post("/rc/projet/:numParcours", async (req, res) => {
|
||||||
fraisRepFraction: globalService.customFormatNumber(rc.cotFraisTTC, true),
|
fraisRepFraction: globalService.customFormatNumber(rc.cotFraisTTC, true),
|
||||||
ca: globalService.customFormatNumber(rc.ca, true),
|
ca: globalService.customFormatNumber(rc.ca, true),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const buf = doc.getZip().generate({ type: "nodebuffer" });
|
||||||
|
|
||||||
|
const currentDate = new Date();
|
||||||
|
|
||||||
|
// Formatage de la date au format "JJ-MM-AAAA-HH-MM-SS"
|
||||||
|
const day = String(currentDate.getDate()).padStart(2, "0");
|
||||||
|
const month = String(currentDate.getMonth() + 1).padStart(2, "0");
|
||||||
|
const year = currentDate.getFullYear();
|
||||||
|
const hours = String(currentDate.getHours()).padStart(2, "0");
|
||||||
|
const minutes = String(currentDate.getMinutes()).padStart(2, "0");
|
||||||
|
const seconds = String(currentDate.getSeconds()).padStart(2, "0");
|
||||||
|
const formattedDate = `${day}-${month}-${year}-${hours}-${minutes}-${seconds}`;
|
||||||
|
|
||||||
|
// Génération du nom de fichier
|
||||||
|
const sanitizedClientNom = client.nom
|
||||||
|
.replace(/[^\w\s.-]/gi, "")
|
||||||
|
.replace(/\s+/g, "-");
|
||||||
|
const filename = `Projet-${contrat.produit}-${parcours.numParcours}-${sanitizedClientNom}-${formattedDate}`;
|
||||||
|
|
||||||
|
// Définit le type de contenu et un nom de fichier par défaut pour le téléchargement
|
||||||
|
res.setHeader(
|
||||||
|
"Content-Type",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||||
|
);
|
||||||
|
|
||||||
|
res.setHeader(
|
||||||
|
"Content-Disposition",
|
||||||
|
"attachment; filename=" + filename + ".docx"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Envoie le buffer au client, déclenchant le téléchargement
|
||||||
|
res.send(buf);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const e = {
|
logger.log('error', 'Error in RC projet generation:', error);
|
||||||
message: error.message,
|
return res.status(500).send("Erreur lors de la génération du projet RC");
|
||||||
name: error.name,
|
|
||||||
stack: error.stack,
|
|
||||||
properties: error.properties,
|
|
||||||
};
|
|
||||||
logger.log('error', JSON.stringify({ error: e }));
|
|
||||||
|
|
||||||
// Envoyez une réponse d'erreur si le rendu échoue
|
|
||||||
return res.status(500).send("Erreur lors de la génération du document");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const buf = doc.getZip().generate({ type: "nodebuffer" });
|
|
||||||
|
|
||||||
const currentDate = new Date();
|
|
||||||
|
|
||||||
// Formatage de la date au format "JJ-MM-AAAA-HH-MM-SS"
|
|
||||||
const day = String(currentDate.getDate()).padStart(2, "0");
|
|
||||||
const month = String(currentDate.getMonth() + 1).padStart(2, "0");
|
|
||||||
const year = currentDate.getFullYear();
|
|
||||||
const hours = String(currentDate.getHours()).padStart(2, "0");
|
|
||||||
const minutes = String(currentDate.getMinutes()).padStart(2, "0");
|
|
||||||
const seconds = String(currentDate.getSeconds()).padStart(2, "0");
|
|
||||||
const formattedDate = `${day}-${month}-${year}-${hours}-${minutes}-${seconds}`;
|
|
||||||
|
|
||||||
// Génération du nom de fichier
|
|
||||||
const sanitizedClientNom = client.nom
|
|
||||||
.replace(/[^\w\s.-]/gi, "")
|
|
||||||
.replace(/\s+/g, "-");
|
|
||||||
const filename = `Projet-${contrat.produit}-${parcours.numParcours}-${sanitizedClientNom}-${formattedDate}`;
|
|
||||||
|
|
||||||
// Définit le type de contenu et un nom de fichier par défaut pour le téléchargement
|
|
||||||
res.setHeader(
|
|
||||||
"Content-Type",
|
|
||||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
|
||||||
);
|
|
||||||
|
|
||||||
res.setHeader(
|
|
||||||
"Content-Disposition",
|
|
||||||
"attachment; filename=" + filename + ".docx"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Envoie le buffer au client, déclenchant le téléchargement
|
|
||||||
res.send(buf);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
router.post("/rc/tarif/:numParcours", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync(
|
||||||
|
path.resolve("src/templates/template-declinaison-tarifaire-rc.docx"),
|
||||||
|
"binary"
|
||||||
|
);
|
||||||
|
|
||||||
|
const zip = new PizZip(content);
|
||||||
|
const doc = new Docxtemplater(zip, { paragraphLoop: true, linebreaks: true });
|
||||||
|
|
||||||
|
const numParcours = req.params.numParcours.toUpperCase();
|
||||||
|
const parcours = await parcoursService.getParcoursByNumParcours(numParcours);
|
||||||
|
const contrat = await contratService.getContratById(parcours.contrat);
|
||||||
|
const client = contrat?.["@expand"]?.client || {};
|
||||||
|
const user = await userService.getUserById(parcours.dernierUtilisateur);
|
||||||
|
|
||||||
|
// Récupérer la collection rc avec ses relations (APRÈS placerDansEnCours, c'est dans enCours)
|
||||||
|
const rcFull = contrat?.["@expand"]?.enCours;
|
||||||
|
if (!rcFull) {
|
||||||
|
logger.log('error', 'No RC found for contrat:', contrat.id);
|
||||||
|
return res.status(404).send("Aucune donnée RC trouvée pour ce contrat");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Les données sont déjà expandées par contratService
|
||||||
|
const rc = rcFull?.["@expand"]?.tarifRC || {};
|
||||||
|
const rcMain = rcFull || {};
|
||||||
|
|
||||||
|
// Formatage des taux avec maximum 2 décimales
|
||||||
|
function formatTaux(taux) {
|
||||||
|
if (!taux) return 0;
|
||||||
|
return parseFloat(taux).toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Préparer les données des activités avec leurs marchandises et activités complémentaires
|
||||||
|
const activites = [];
|
||||||
|
|
||||||
|
// Voiturier/Loueur
|
||||||
|
if (rcMain.checkVoiturier || rcMain.checkLoueur) {
|
||||||
|
activites.push({
|
||||||
|
type: 'Voiturier/Loueur',
|
||||||
|
capital: rcMain.capitalVoiturier || 0,
|
||||||
|
pourcentage: rc.pourcentageVoiturier || 0,
|
||||||
|
marchandises: rcMain.marchandisesVoiturier || [],
|
||||||
|
activitesCompl: rcMain.activitesVoiturier || []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commissionnaire de Transport
|
||||||
|
if (rcMain.checkCommissionnaire) {
|
||||||
|
activites.push({
|
||||||
|
type: 'Commissionnaire de Transport',
|
||||||
|
capital: rcMain.capitalCommissionnaire || 0,
|
||||||
|
pourcentage: rc.pourcentageCommissionnaire || 0,
|
||||||
|
marchandises: rcMain.marchandisesCommissionnaire || [],
|
||||||
|
activitesCompl: rcMain.activitesCommissionnaire || []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Déménageur
|
||||||
|
if (rcMain.checkDemenageur) {
|
||||||
|
activites.push({
|
||||||
|
type: 'Déménageur',
|
||||||
|
capital: rcMain.capitalDemenageur || 0,
|
||||||
|
pourcentage: rc.pourcentageDemenageur || 0,
|
||||||
|
marchandises: rcMain.marchandisesDemenageur || [],
|
||||||
|
activitesCompl: rcMain.activitesDemenageur || []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logistique
|
||||||
|
if (rcMain.checkLogistique) {
|
||||||
|
activites.push({
|
||||||
|
type: 'Logistique',
|
||||||
|
capital: rcMain.capitalLogistique || 0,
|
||||||
|
pourcentage: rc.pourcentageLogistique || 0,
|
||||||
|
marchandises: rcMain.marchandisesLogistique || [],
|
||||||
|
activitesCompl: rcMain.activitesLogistique || []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Autocariste
|
||||||
|
if (rcMain.checkAutocariste) {
|
||||||
|
activites.push({
|
||||||
|
type: 'Autocariste',
|
||||||
|
capital: rcMain.capitalAutocariste || 0,
|
||||||
|
pourcentage: rc.pourcentageAutocariste || 0,
|
||||||
|
marchandises: rcMain.marchandisesAutocariste || [],
|
||||||
|
activitesCompl: rcMain.activitesAutocariste || []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Autres activités
|
||||||
|
if (rcMain.checkAutres) {
|
||||||
|
activites.push({
|
||||||
|
type: 'Autres activités',
|
||||||
|
capital: rcMain.capitalAutres || 0,
|
||||||
|
pourcentage: rc.pourcentageAutres || 0,
|
||||||
|
marchandises: rcMain.marchandisesAutres || [],
|
||||||
|
activitesCompl: rcMain.activitesAutres || []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Préparer les zones
|
||||||
|
const zones = [];
|
||||||
|
if (rcMain.zone1) zones.push("Zone 1");
|
||||||
|
if (rcMain.zone2) zones.push("Zone 2");
|
||||||
|
if (rcMain.zone3) zones.push("Zone 3");
|
||||||
|
if (rcMain.zone4) zones.push("Zone 4");
|
||||||
|
if (rcMain.zone5) zones.push("Zone 5");
|
||||||
|
if (rcMain.zone6) zones.push("Zone 6");
|
||||||
|
|
||||||
|
// Préparer les garanties additionnelles
|
||||||
|
const garantiesAdditionnelles = [];
|
||||||
|
if (rc.checkStationLavage) garantiesAdditionnelles.push("Station de lavage");
|
||||||
|
if (rc.checkGarageInterne) garantiesAdditionnelles.push("Garage interne");
|
||||||
|
if (rc.checkCSE) garantiesAdditionnelles.push("CSE");
|
||||||
|
if (rc.checkTPPC) garantiesAdditionnelles.push(`TPPC (Capital: ${rc.capitalTPPC || 0}€, Véhicules: ${rc.vehiculesTPPC || 0})`);
|
||||||
|
if (rc.checkPJ) garantiesAdditionnelles.push("Protection Juridique");
|
||||||
|
|
||||||
|
// Préparer les engagements complémentaires
|
||||||
|
const engagements = [];
|
||||||
|
if (rc.checkDomImmat) engagements.push(`Dommages immatériels (Capital: ${rc.capitalDomImmat || 0}€)`);
|
||||||
|
if (rc.checkContConf) engagements.push(`Contenu confié (Capital: ${rc.capitalContConf || 0}€)`);
|
||||||
|
if (rc.checkDiffInv) engagements.push(`Différence d'inventaire (Capital: ${rc.capitalDiffInv || 0}€)`);
|
||||||
|
|
||||||
|
let dateNow = moment().format("DD MMMM YYYY");
|
||||||
|
|
||||||
|
doc.render({
|
||||||
|
// Infos générales
|
||||||
|
matricule: user.matricule,
|
||||||
|
date: dateNow,
|
||||||
|
typeCotisation: rcMain.typeCotisation || "Non défini",
|
||||||
|
hasforfaitaire: !(rcMain.typeCotisation === "forfaitaire" || (!rcMain.chiffreAffaires || rcMain.chiffreAffaires === 0)),
|
||||||
|
hasRCE: rcMain.checkRCE || false,
|
||||||
|
hasContrat: !!contrat.numContrat,
|
||||||
|
numContrat: contrat.numContrat,
|
||||||
|
hasSaisine: !!contrat.numSaisine,
|
||||||
|
numSaisine: contrat.numSaisine,
|
||||||
|
numeroCS: contrat.numSaisine || contrat.numContrat,
|
||||||
|
nomAssure: client.nom,
|
||||||
|
ca: rcMain.chiffreAffaires || 0,
|
||||||
|
hasNbVehicule: (rcMain.nombreVehicules || 0) > 0,
|
||||||
|
nbVehicule: rcMain.nombreVehicules || 0,
|
||||||
|
|
||||||
|
// Activités principales
|
||||||
|
hasVoiturierLoueur: rcMain.checkVoiturier || rcMain.checkLoueur,
|
||||||
|
capitalAssureVoiturierLoueur: rcMain.capitalVoiturier || 0,
|
||||||
|
pourcentageVoiturierLoueur: rc.pourcentageVoiturier || 0,
|
||||||
|
marchandiseVoiturierLoueur: rcMain.marchandisesVoiturier || [],
|
||||||
|
activiteVoiturierLoueur: rcMain.actComplVoiturier || [],
|
||||||
|
|
||||||
|
hasComTransport: rcMain.checkCommissionnaire || false,
|
||||||
|
capitalAssureComTransport: rcMain.capitalCommissionnaire || 0,
|
||||||
|
pourcentageComTransport: rc.pourcentageCommissionnaire || 0,
|
||||||
|
marchandiseComTransport: rcMain.marchandisesCommissionnaire || [],
|
||||||
|
activiteComTransport: rcMain.actComplCommissionnaire || [],
|
||||||
|
|
||||||
|
hasDemenageur: rcMain.checkDemenageur || false,
|
||||||
|
capitalAssureDemenageur: rcMain.capitalDemenageur || 0,
|
||||||
|
pourcentageDemenageur: rc.pourcentageDemenageur || 0,
|
||||||
|
marchandiseDemenageur: rcMain.marchandisesDemenageur || [],
|
||||||
|
activiteDemenageur: rcMain.actComplDemenageur || [],
|
||||||
|
|
||||||
|
hasLogistique: rcMain.checkLogistique || false,
|
||||||
|
capitalAssureLogistique: rcMain.capitalLogistique || 0,
|
||||||
|
pourcentageLogistique: rc.pourcentageLogistique || 0,
|
||||||
|
marchandiseLogistique: rcMain.marchandisesLogistique || [],
|
||||||
|
activiteLogistique: rcMain.actComplLogistique || [],
|
||||||
|
|
||||||
|
hasAutocariste: rcMain.checkAutocariste || false,
|
||||||
|
capitalAssureAutocariste: rcMain.capitalAutocariste || 0,
|
||||||
|
pourcentageAutocariste: rc.pourcentageAutocariste || 0,
|
||||||
|
marchandiseAutocariste: rcMain.marchandisesAutocariste || [],
|
||||||
|
activiteAutocariste: rcMain.activitesAutocariste || [],
|
||||||
|
|
||||||
|
hasAutre: rcMain.checkAutres || false,
|
||||||
|
capitalAssureAutre: rcMain.capitalAutres || 0,
|
||||||
|
pourcentageAutre: rc.pourcentageAutres || 0,
|
||||||
|
marchandiseAutre: rcMain.marchandisesAutres || [],
|
||||||
|
//activiteAutre: rcMain.activitesAutres || [], existe pas dans ejs
|
||||||
|
|
||||||
|
// Zones géographiques
|
||||||
|
zones: zones,
|
||||||
|
|
||||||
|
// Garanties additionnelles
|
||||||
|
hasGarantieAdd: garantiesAdditionnelles.length > 0,
|
||||||
|
garantieAdd: garantiesAdditionnelles,
|
||||||
|
|
||||||
|
// Engagements complémentaires
|
||||||
|
hasEngagement: engagements.length > 0,
|
||||||
|
engagement: engagements,
|
||||||
|
|
||||||
|
// Tarifs sélectionnés
|
||||||
|
franchise: rc.franchiseChoisie || rc.franchiseSelectionnee,
|
||||||
|
tarifReference: getSelectedTarifReference(rc),
|
||||||
|
tarifCommercial: rc.tarifcommercial,
|
||||||
|
primeRCC: getSelectedPrime(rc, 'RCC'),
|
||||||
|
tauxRCC: formatTaux(getSelectedTaux(rc, 'RCC')),
|
||||||
|
hasRCE: rcMain.checkRCE,
|
||||||
|
primeRCE: getSelectedPrime(rc, 'RCE'),
|
||||||
|
tauxRCE: formatTaux(getSelectedTaux(rc, 'RCE')),
|
||||||
|
hasPrimePJ: rc.checkPJ && getSelectedPrime(rc, 'PJ') > 0,
|
||||||
|
primePJ: getSelectedPrime(rc, 'PJ'),
|
||||||
|
tauxGlobal: formatTaux(getSelectedTaux(rc, 'Global')),
|
||||||
|
|
||||||
|
|
||||||
|
// Propositions franchise 250
|
||||||
|
tarifRefP1: rc.primeTotal_250 || 0,
|
||||||
|
pRCCP1: rc.primeRCC_250 || 0,
|
||||||
|
tRCCP1: formatTaux(rc.tauxRCC_250),
|
||||||
|
pRCEP1: rc.primeRCE_250 || 0,
|
||||||
|
tRCEP1: formatTaux(rc.tauxRCE_250),
|
||||||
|
primePJP1: rc.primePJ_250 || 0,
|
||||||
|
tauxGlobalP1: formatTaux(rc.tauxGlobal_250),
|
||||||
|
|
||||||
|
// Propositions franchise 400
|
||||||
|
tarifRefP2: rc.primeTotal_400 || 0,
|
||||||
|
pRCCP2: rc.primeRCC_400 || 0,
|
||||||
|
tRCCP2: formatTaux(rc.tauxRCC_400),
|
||||||
|
pRCEP2: rc.primeRCE_400 || 0,
|
||||||
|
tRCEP2: formatTaux(rc.tauxRCE_400),
|
||||||
|
primePJP2: rc.primePJ_400 || 0,
|
||||||
|
tauxGlobalP2: formatTaux(rc.tauxGlobal_400),
|
||||||
|
|
||||||
|
// Propositions franchise 2000
|
||||||
|
tarifRefP3: rc.primeTotal_2000 || 0,
|
||||||
|
pRCCP3: rc.primeRCC_2000 || 0,
|
||||||
|
tRCCP3: formatTaux(rc.tauxRCC_2000),
|
||||||
|
pRCEP3: rc.primeRCE_2000 || 0,
|
||||||
|
tRCEP3: formatTaux(rc.tauxRCE_2000),
|
||||||
|
primePJP3: rc.primePJ_2000 || 0,
|
||||||
|
tauxGlobalP3: formatTaux(rc.tauxGlobal_2000),
|
||||||
|
});
|
||||||
|
|
||||||
|
const buf = doc.getZip().generate({ type: "nodebuffer" });
|
||||||
|
|
||||||
|
const currentDate = new Date();
|
||||||
|
|
||||||
|
// Formatage de la date au format "JJ-MM-AAAA-HH-MM-SS"
|
||||||
|
const day = String(currentDate.getDate()).padStart(2, "0");
|
||||||
|
const month = String(currentDate.getMonth() + 1).padStart(2, "0");
|
||||||
|
const year = currentDate.getFullYear();
|
||||||
|
const hours = String(currentDate.getHours()).padStart(2, "0");
|
||||||
|
const minutes = String(currentDate.getMinutes()).padStart(2, "0");
|
||||||
|
const seconds = String(currentDate.getSeconds()).padStart(2, "0");
|
||||||
|
const formattedDate = `${day}-${month}-${year}-${hours}-${minutes}-${seconds}`;
|
||||||
|
|
||||||
|
const sanitizedClientNom = client.nom
|
||||||
|
.replace(/[^\w\s.-]/gi, "")
|
||||||
|
.replace(/\s+/g, "-");
|
||||||
|
|
||||||
|
const filename = `Tarif-RC-${parcours.numParcours}-${sanitizedClientNom}-${formattedDate}`;
|
||||||
|
|
||||||
|
res.setHeader(
|
||||||
|
"Content-Type",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||||
|
);
|
||||||
|
res.setHeader("Content-Disposition", "attachment; filename=" + filename + ".docx");
|
||||||
|
res.send(buf);
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("error", 'Error in RC tarif generation:', error);
|
||||||
|
return res.status(500).send("Erreur lors de la génération du tarif RC");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
|
@ -22,7 +22,7 @@ router.get('/contrat', async (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/tarifrc', async (req, res) => {
|
router.get('/tarifrc', async (req, res) => {
|
||||||
renderPage('dev.ejs', res, {}, true);
|
renderPage('tarifformrc.ejs', res, {}, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/tariffac', async (req, res) => {
|
router.get('/tariffac', async (req, res) => {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,152 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const rcService = require('../services/rcService');
|
const rcService = require('../services/rcService');
|
||||||
|
const constantesJSON = require("../constantes/json-modulateur-rc");
|
||||||
|
const logger = require('../utils/logger');
|
||||||
|
|
||||||
|
// ===== Routes RC principale =====
|
||||||
router.post('/create', async (req, res) => {
|
router.post('/create', async (req, res) => {
|
||||||
const data = req.body;
|
try {
|
||||||
const rc = await rcService.createRc(data);
|
const data = req.body;
|
||||||
|
const rc = await rcService.createRc(data);
|
||||||
|
res.json({ valid: Boolean(rc), rc });
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("error", "Error creating RC:", error);
|
||||||
|
res.status(500).json({ valid: false, error: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
res.json({ valid: Boolean(rc), rc });
|
router.get('/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const rc = await rcService.getRcById(req.params.id);
|
||||||
|
res.json({ valid: Boolean(rc), rc });
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("error", "Error getting RC:", error);
|
||||||
|
res.status(500).json({ valid: false, error: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/update/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const rc = await rcService.updateRc(req.params.id, req.body);
|
||||||
|
res.json({ valid: Boolean(rc), rc });
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("error", "Error updating RC:", error);
|
||||||
|
res.status(500).json({ valid: false, error: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ===== Routes TarifRC =====
|
||||||
|
router.post('/tarif/create', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const data = req.body;
|
||||||
|
const tarifRc = await rcService.createTarifRc(data);
|
||||||
|
res.json({ valid: Boolean(tarifRc), tarifRc });
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("error", "Error creating TarifRC:", error);
|
||||||
|
res.status(500).json({ valid: false, error: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/tarif/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const tarifRc = await rcService.getTarifRcById(req.params.id);
|
||||||
|
res.json({ valid: Boolean(tarifRc), tarifRc });
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("error", "Error getting TarifRC:", error);
|
||||||
|
res.status(500).json({ valid: false, error: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/tarif/update/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const tarifRc = await rcService.updateTarifRc(req.params.id, req.body);
|
||||||
|
res.json({ valid: Boolean(tarifRc), tarifRc });
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("error", "Error updating TarifRC:", error);
|
||||||
|
res.status(500).json({ valid: false, error: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ===== Routes ProjetRC =====
|
||||||
|
router.post('/projet/create', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const data = req.body;
|
||||||
|
const projetRc = await rcService.createProjetRc(data);
|
||||||
|
res.json({ valid: Boolean(projetRc), projetRc });
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("error", "Error creating ProjetRC:", error);
|
||||||
|
res.status(500).json({ valid: false, error: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/projet/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const projetRc = await rcService.getProjetRcById(req.params.id);
|
||||||
|
res.json({ valid: Boolean(projetRc), projetRc });
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("error", "Error getting ProjetRC:", error);
|
||||||
|
res.status(500).json({ valid: false, error: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/projet/update/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const projetRc = await rcService.updateProjetRc(req.params.id, req.body);
|
||||||
|
res.json({ valid: Boolean(projetRc), projetRc });
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("error", "Error updating ProjetRC:", error);
|
||||||
|
res.status(500).json({ valid: false, error: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ===== Routes Modulateurs =====
|
||||||
|
router.get("/modulo/:objDemande", async (req, res) => {
|
||||||
|
const objDemande = req.params.objDemande;
|
||||||
|
var objRetourne
|
||||||
|
|
||||||
|
switch (objDemande) {
|
||||||
|
case "CARC":
|
||||||
|
objRetourne = constantesJSON.modRCCA;
|
||||||
|
break;
|
||||||
|
case "activiteRCC":
|
||||||
|
objRetourne = constantesJSON.modRCActRCC;
|
||||||
|
break;
|
||||||
|
case "activiteRCE":
|
||||||
|
objRetourne = constantesJSON.modRCActRCE;
|
||||||
|
break;
|
||||||
|
case "activiteComplRC":
|
||||||
|
objRetourne = constantesJSON.modRCActCompl;
|
||||||
|
break;
|
||||||
|
case "marchandiseRC":
|
||||||
|
objRetourne = constantesJSON.modRCMar;
|
||||||
|
break;
|
||||||
|
case "zoneRC":
|
||||||
|
objRetourne = constantesJSON.modRCZone;
|
||||||
|
break;
|
||||||
|
case "engagComplRC":
|
||||||
|
objRetourne = constantesJSON.modRCEngagCompl;
|
||||||
|
break;
|
||||||
|
case "garAdditionelRC":
|
||||||
|
objRetourne = constantesJSON.modRCGarAdd ;
|
||||||
|
break;
|
||||||
|
case "sinistreRC":
|
||||||
|
objRetourne = constantesJSON.modRCSinistre;
|
||||||
|
break;
|
||||||
|
case "franchiseRC":
|
||||||
|
objRetourne = constantesJSON.modRCFranchise;
|
||||||
|
break;
|
||||||
|
case "primeMiniRC":
|
||||||
|
objRetourne = constantesJSON.modRCPrimeMini;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
res.json({valid: Boolean(objRetourne), objRetourne});
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("error", `Error finding constant ${objDemande}:`, error);
|
||||||
|
res.status(500).json({valid: false, error: "Internal Server Error"});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -45,14 +45,33 @@ async function createContrat() {
|
||||||
|
|
||||||
async function expandProjetTarif(resultat) {
|
async function expandProjetTarif(resultat) {
|
||||||
const produit = resultat?.['produit'].toLowerCase()
|
const produit = resultat?.['produit'].toLowerCase()
|
||||||
const idProjet = resultat?.['@expand']?.[produit]?.['projet'] || undefined
|
|
||||||
const idTarif = resultat?.['@expand']?.[produit]?.['tarif'] || undefined
|
|
||||||
|
|
||||||
const expand = {
|
|
||||||
projet: (idProjet) ? await db.records.getOne(produit + "projet", idProjet) : null,
|
|
||||||
tarif: (idTarif) ? await db.records.getOne(produit + "tarif", idTarif) : null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// RC utilise tarifRC/projetRC, les autres produits utilisent tarif/projet
|
||||||
|
let idProjet, idTarif, collectionProjet, collectionTarif;
|
||||||
|
if (produit === 'rc') {
|
||||||
|
idProjet = resultat?.['@expand']?.[produit]?.['projetRC'] || undefined
|
||||||
|
idTarif = resultat?.['@expand']?.[produit]?.['tarifRC'] || undefined
|
||||||
|
collectionProjet = 'projetRC'; // Nom de collection RC
|
||||||
|
collectionTarif = 'tarifRC'; // Nom de collection RC
|
||||||
|
} else {
|
||||||
|
idProjet = resultat?.['@expand']?.[produit]?.['projet'] || undefined
|
||||||
|
idTarif = resultat?.['@expand']?.[produit]?.['tarif'] || undefined
|
||||||
|
collectionProjet = produit + "projet"; // tppcprojet, facprojet
|
||||||
|
collectionTarif = produit + "tarif"; // tppctarif, factarif (n'existe pas mais bon)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pour RC, inclure à la fois les clés RC spécifiques (tarifRC/projetRC) et les clés génériques (tarif/projet)
|
||||||
|
// afin de rester compatible avec l'ancien front qui lisait `tarif`/`projet`.
|
||||||
|
const expand = {
|
||||||
|
projet: idProjet ? await db.records.getOne(collectionProjet, idProjet) : null,
|
||||||
|
tarif: idTarif ? await db.records.getOne(collectionTarif, idTarif) : null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (produit === 'rc') {
|
||||||
|
expand.projetRC = expand.projet;
|
||||||
|
expand.tarifRC = expand.tarif;
|
||||||
|
}
|
||||||
|
|
||||||
resultat['@expand'][produit] = {...resultat['@expand'][produit], '@expand': expand}
|
resultat['@expand'][produit] = {...resultat['@expand'][produit], '@expand': expand}
|
||||||
return resultat
|
return resultat
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,55 @@
|
||||||
const { db } = require('../db/db-connect');
|
const { db } = require('../db/db-connect');
|
||||||
const logger = require('../utils/logger');
|
const logger = require('../utils/logger');
|
||||||
|
|
||||||
|
// ===== Collection RC principale =====
|
||||||
async function createRc(data) {
|
async function createRc(data) {
|
||||||
return await db.records.create('rc', data);
|
return await db.records.create('rc', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getRcById(id) {
|
||||||
|
return await db.records.getOne('rc', id, {
|
||||||
|
expand: 'tarifRC,projetRC'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateRc(id, data) {
|
||||||
|
return await db.records.update('rc', id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Collection TarifRC =====
|
||||||
|
async function createTarifRc(data) {
|
||||||
|
return await db.records.create('tarifRC', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getTarifRcById(id) {
|
||||||
|
return await db.records.getOne('tarifRC', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateTarifRc(id, data) {
|
||||||
|
return await db.records.update('tarifRC', id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Collection ProjetRC =====
|
||||||
|
async function createProjetRc(data) {
|
||||||
|
return await db.records.create('projetRC', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getProjetRcById(id) {
|
||||||
|
return await db.records.getOne('projetRC', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateProjetRc(id, data) {
|
||||||
|
return await db.records.update('projetRC', id, data);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createRc
|
createRc,
|
||||||
|
getRcById,
|
||||||
|
updateRc,
|
||||||
|
createTarifRc,
|
||||||
|
getTarifRcById,
|
||||||
|
updateTarifRc,
|
||||||
|
createProjetRc,
|
||||||
|
getProjetRcById,
|
||||||
|
updateProjetRc
|
||||||
};
|
};
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -63,6 +63,12 @@
|
||||||
<script src="/js/loader.js"></script>
|
<script src="/js/loader.js"></script>
|
||||||
<!-- Script pour la navigation AJAX -->
|
<!-- Script pour la navigation AJAX -->
|
||||||
<script src="/js/navigation.js"></script>
|
<script src="/js/navigation.js"></script>
|
||||||
|
<!-- Utilitaires de synchronisation RC -->
|
||||||
|
<script src="/js/rc-sync-utils.js"></script>
|
||||||
|
<!-- Gestionnaire de données RC -->
|
||||||
|
<script src="/js/rc-data-manager.js"></script>
|
||||||
|
<!-- Orchestrateur de synchronisation RC -->
|
||||||
|
<script src="/js/rc-orchestrator.js"></script>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -931,7 +931,9 @@
|
||||||
<input type="text" name="cotRCEHT" id="cotRCEHT" placeholder="00.00" />
|
<input type="text" name="cotRCEHT" id="cotRCEHT" placeholder="00.00" />
|
||||||
<span id="cotRCEHT-error" class="helper-text red-text"></span>
|
<span id="cotRCEHT-error" class="helper-text red-text"></span>
|
||||||
</td>
|
</td>
|
||||||
<td><input type="text" name="cotRCETaux" id="cotRCETaux" value="9" disabled /></td>
|
<td>
|
||||||
|
<input type="text" name="cotRCETaux" id="cotRCETaux" value="9" disabled />
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="cotRCETTC" id="cotRCETTC" placeholder="00.00" />
|
<input type="text" name="cotRCETTC" id="cotRCETTC" placeholder="00.00" />
|
||||||
<span id="cotRCETTC-error" class="helper-text red-text"></span>
|
<span id="cotRCETTC-error" class="helper-text red-text"></span>
|
||||||
|
|
@ -943,7 +945,9 @@
|
||||||
<input type="text" name="cotPJHT" id="cotPJHT" placeholder="100.00" />
|
<input type="text" name="cotPJHT" id="cotPJHT" placeholder="100.00" />
|
||||||
<span id="cotPJHT-error" class="helper-text red-text"></span>
|
<span id="cotPJHT-error" class="helper-text red-text"></span>
|
||||||
</td>
|
</td>
|
||||||
<td><input type="text" name="cotPJTaux" id="cotPJTaux" value="13.4" disabled /></td>
|
<td>
|
||||||
|
<input type="text" name="cotPJTaux" id="cotPJTaux" value="13.4" disabled />
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="cotPJTTC" id="cotPJTTC" placeholder="113.40" />
|
<input type="text" name="cotPJTTC" id="cotPJTTC" placeholder="113.40" />
|
||||||
<span id="cotPJTTC-error" class="helper-text red-text"></span>
|
<span id="cotPJTTC-error" class="helper-text red-text"></span>
|
||||||
|
|
@ -1181,3 +1185,26 @@
|
||||||
<a class="modal-close waves-effect waves-green btn-flat">Fermer</a>
|
<a class="modal-close waves-effect waves-green btn-flat">Fermer</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- MODAL modification & liaison tarif -->
|
||||||
|
<div id="modalModif" class="modal modalAlert">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h4>Attention</h4>
|
||||||
|
<p>
|
||||||
|
Si vous modifiez cette information, la prime calculée à l'étape "Tarif" sera incorrecte, ce qui entraînera
|
||||||
|
la suppression du tarif de référence associé à ce contrat.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Cela s'applique aux informations suivantes :</p>
|
||||||
|
<ul class="modalRed">
|
||||||
|
<li>Activité assurée</li>
|
||||||
|
<li>Garanties</li>
|
||||||
|
<li>Extensions de Garanties impactant le tarif</li>
|
||||||
|
<li>Type de Cotisation (avec un type de contrat en cotisation forfaitaire) </li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a id="modif-OK" class="modal-close waves-effect btn-flat red darken-1">Modifier dans Tarif</a>
|
||||||
|
<a id="modif-NO" class="modal-close waves-effect btn-flat indigo darken-4">Annuler</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue