feat(advalo): integrate module in etv2 and restore v1 AXA scripts flow

This commit is contained in:
Alexis Burnaz 2026-04-27 12:08:22 +02:00
parent 7b484f34c9
commit 70dd59b03e
22 changed files with 3343 additions and 3 deletions

View File

@ -240,6 +240,63 @@ hr.form {
background: #26a69a;
}
#advaloNavSelect .active a {
background: #26a69a;
}
#advaloNavSelect li a {
font-weight: 500;
letter-spacing: 0.2px;
}
.advalo-panel .input-field > label {
color: #1a237e !important;
transition: transform .18s ease, color .18s ease, font-size .18s ease;
}
.advalo-panel .input-field > label.active {
transform: translateY(-24px) scale(0.82) !important;
transform-origin: 0 0;
}
.advalo-loader-wrap {
min-height: 46px;
}
.advalo-ring-loader {
width: 34px;
height: 34px;
border: 3px solid rgba(0, 0, 139, 0.18);
border-top-color: #00008b;
border-right-color: #26a69a;
border-radius: 50%;
margin: 0 auto;
animation: advalo-spin 0.9s cubic-bezier(0.5, 0.1, 0.5, 0.9) infinite;
}
.advalo-cumul-hist,
.advalo-cumul-fact {
margin-right: 6px;
margin-bottom: 6px;
}
.advalo-panel .btn,
.advalo-panel .btn-flat {
color: #fff !important;
}
@keyframes advalo-spin {
0% {
transform: rotate(0deg) scale(1);
}
40% {
transform: rotate(180deg) scale(1.04);
}
100% {
transform: rotate(360deg) scale(1);
}
}
.border {
border-radius: 10px !important;
}

View File

@ -0,0 +1,955 @@
document.addEventListener('DOMContentLoaded', function () {
if (window.location.pathname !== '/advalo') return;
const token = localStorage.getItem('jwtToken');
const pageSize = 20;
const state = {
historique: { page: 1, totalPages: 1 },
reporting: { page: 1, totalPages: 1 },
cumul: { page: 1, totalPages: 1 },
loaded: { historique: false, reporting: false, cumul: false },
confirmation: { demandId: '', summary: '' },
batchConfirmation: { batchId: '', summary: '' },
facturationRows: [],
removedFacturationIds: new Set(),
openHistoriqueId: null
};
const requestControllers = {};
const loadingState = {};
const TRANSPORT_MODES = ['Terrestre', 'Aérien', 'Fluvial', 'Maritime', 'Postal'];
const toast = (message, classes = 'red') => M.toast({ html: message, classes });
const authHeaders = () => {
if (!token) throw new Error('Session expirée.');
return { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` };
};
const authOnlyHeaders = () => {
if (!token) throw new Error('Session expirée.');
return { Authorization: `Bearer ${token}` };
};
const isAbortError = (error) => error && (error.name === 'AbortError' || String(error.message || '').toLowerCase().includes('aborted'));
const api = async (url, options = {}) => {
const res = await fetch(url, { ...options, headers: options.headers || authHeaders() });
const contentType = res.headers.get('content-type') || '';
if (contentType.includes('text/csv')) {
if (!res.ok) throw new Error('Erreur export CSV');
return res.text();
}
const data = await res.json().catch(() => ({}));
if (!res.ok || data.valid === false) throw new Error(data.message || 'Erreur serveur');
return data;
};
const apiLatest = async (key, url, options = {}) => {
if (requestControllers[key]) requestControllers[key].abort();
const controller = new AbortController();
requestControllers[key] = controller;
try {
return await api(url, { ...options, signal: controller.signal });
} finally {
if (requestControllers[key] === controller) delete requestControllers[key];
}
};
const abortDataRequests = () => {
['historique', 'reporting', 'cumul', 'facturation'].forEach((k) => {
if (requestControllers[k]) requestControllers[k].abort();
});
};
const parseAmount = (value) => {
const n = Number(String(value || '').replace(',', '.').replace(/\s/g, ''));
return Number.isFinite(n) ? n : 0;
};
const fmt = (n) => Number(n || 0).toLocaleString('fr-FR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const sourceLabel = (source) => (source === 'deleguee' ? 'Grille déléguée' : 'Hors grille');
const setIndicator = (id, page, totalPages, totalRows) => {
const el = document.getElementById(id);
if (!el) return;
const pages = Math.max(1, Number(totalPages || 1));
const current = Math.min(Math.max(1, Number(page || 1)), pages);
el.textContent = `Page ${current} / ${pages} - ${Number(totalRows || 0)} lignes`;
};
const setLoading = (section, isLoading) => {
const el = document.getElementById(`advalo-loading-${section}`);
if (!el) return;
if (isLoading) {
const marker = Symbol(section);
loadingState[section] = { marker, startedAt: Date.now() };
el.style.display = 'block';
return;
}
const current = loadingState[section];
if (!current) {
el.style.display = 'none';
return;
}
const elapsed = Date.now() - current.startedAt;
setTimeout(() => {
const latest = loadingState[section];
if (latest && latest.marker === current.marker) {
el.style.display = 'none';
delete loadingState[section];
}
}, Math.max(0, 280 - elapsed));
};
const syncTextFields = (scope = document) => {
if (window.M && typeof window.M.updateTextFields === 'function') window.M.updateTextFields();
const root = scope && scope.querySelectorAll ? scope : document;
const fields = root.querySelectorAll('.advalo-panel .input-field input:not([type="hidden"]), .advalo-panel .input-field textarea');
fields.forEach((field) => {
if (!field.id) return;
const label = document.querySelector(`label[for="${field.id}"]`);
if (!label) return;
const shouldFloat = String(field.value || '').trim().length > 0 || document.activeElement === field;
label.classList.toggle('active', shouldFloat);
});
};
const refreshTextFields = (scope = document) => {
syncTextFields(scope);
requestAnimationFrame(() => syncTextFields(scope));
setTimeout(() => syncTextFields(scope), 60);
};
const initSelects = (scope = document) => {
const selects = scope.querySelectorAll('select');
selects.forEach((select) => {
const instance = M.FormSelect.getInstance(select);
if (instance) instance.destroy();
M.FormSelect.init(select);
});
};
const formatDateInput = (raw) => {
const digits = String(raw || '').replace(/\D/g, '').slice(0, 8);
if (digits.length <= 2) return digits;
if (digits.length <= 4) return `${digits.slice(0, 2)}/${digits.slice(2)}`;
return `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4)}`;
};
const initDateFields = () => {
document.querySelectorAll('input.advalo-date').forEach((input) => {
if (input.dataset.dateMaskBound === '1') return;
input.addEventListener('input', () => {
const formatted = formatDateInput(input.value);
if (formatted !== input.value) input.value = formatted;
refreshTextFields(input.closest('.advalo-panel') || document);
});
input.dataset.dateMaskBound = '1';
});
};
const downloadDocx = async (url, options = {}, fallbackName = 'document_advalo.docx') => {
const res = await fetch(url, { ...options, headers: { ...authOnlyHeaders(), ...(options.headers || {}) } });
if (!res.ok) {
const payload = await res.json().catch(() => ({}));
throw new Error(payload.message || 'Erreur génération document');
}
const disposition = res.headers.get('content-disposition') || '';
const filenameMatch = disposition.match(/filename=\"?([^\";]+)\"?/i);
const filename = filenameMatch ? filenameMatch[1] : fallbackName;
const blob = await res.blob();
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
URL.revokeObjectURL(link.href);
};
const ensureContract16 = (value, { required = false } = {}) => {
const digits = String(value || '').replace(/\D/g, '');
if (!digits) return required ? null : '';
if (digits.length !== 16) return null;
return digits;
};
const validateContractField = (id, required = false) => {
const input = document.getElementById(id);
if (!input) return true;
const v = ensureContract16(input.value, { required });
if (v === null) {
input.classList.add('invalid');
return false;
}
if (v) input.value = v;
input.classList.remove('invalid');
return true;
};
const requireFields = (ids) => {
let ok = true;
ids.forEach((id) => {
const input = document.getElementById(id);
if (!input) return;
if (!String(input.value || '').trim()) {
input.classList.add('invalid');
ok = false;
} else {
input.classList.remove('invalid');
}
});
return ok;
};
const applyNumericGuards = () => {
const integerOnlyIds = ['p-numContrat', 'p-numClient', 'p-numAgent', 'h-numClient', 'h-numContrat', 'c-numContrat', 'c-numClient', 'f-numContrat', 'r-numClient', 'r-numContrat', 'r-actorMatricule'];
integerOnlyIds.forEach((id) => {
const input = document.getElementById(id);
if (!input) return;
input.addEventListener('input', () => {
const cleaned = String(input.value || '').replace(/\D/g, '');
if (cleaned !== input.value) input.value = cleaned;
});
});
const decimalIds = ['p-capital', 'p-taux', 'p-primeMin', 'p-coutActe', 'p-cotisationHT', 'p-cotisationTTC'];
decimalIds.forEach((id) => {
const input = document.getElementById(id);
if (!input) return;
input.addEventListener('input', () => {
let cleaned = String(input.value || '').replace(/[^0-9.,]/g, '');
const comma = cleaned.indexOf(',');
const dot = cleaned.indexOf('.');
const splitAt = comma >= 0 ? comma : dot;
if (splitAt >= 0) cleaned = cleaned.slice(0, splitAt + 1) + cleaned.slice(splitAt + 1).replace(/[.,]/g, '');
if (cleaned !== input.value) input.value = cleaned;
});
});
};
const bindFloatingLabels = () => {
const inputs = document.querySelectorAll('#advalo-tab-ponctuel input, #advalo-tab-facturation input, #advalo-tab-historique input, #advalo-tab-cumul input, #advalo-tab-reporting input');
inputs.forEach((input) => {
if (input.dataset.floatBound === '1') return;
const resync = () => requestAnimationFrame(() => refreshTextFields(input.closest('.advalo-panel') || document));
input.addEventListener('focus', resync);
input.addEventListener('input', resync);
input.addEventListener('change', resync);
input.addEventListener('blur', resync);
input.dataset.floatBound = '1';
});
};
const navLinks = document.querySelectorAll('#advaloNavSelect a[data-target]');
const panels = document.querySelectorAll('.advalo-panel');
const confirmModal = M.Modal.init(document.getElementById('advalo-confirm-modal'), { dismissible: true });
const batchModal = M.Modal.init(document.getElementById('advalo-batch-modal'), { dismissible: true });
const getSelectedModes = () => [...document.querySelectorAll('.p-mode-check:checked')].map((el) => el.value).filter((v) => TRANSPORT_MODES.includes(v));
const syncModes = () => {
const modes = getSelectedModes();
document.getElementById('p-mode').value = modes.join(', ');
};
const recalcPonctuelPricing = () => {
const capital = parseAmount(document.getElementById('p-capital').value);
const taux = parseAmount(document.getElementById('p-taux').value);
const primeMin = parseAmount(document.getElementById('p-primeMin').value);
const coutActe = parseAmount(document.getElementById('p-coutActe').value);
if (!capital && !taux && !primeMin) return;
const cotisationHT = Math.max((capital * taux) / 100, primeMin);
const cotisationTTC = cotisationHT + coutActe;
document.getElementById('p-cotisationHT').value = cotisationHT.toFixed(2);
document.getElementById('p-cotisationTTC').value = cotisationTTC.toFixed(2);
document.getElementById('p-tarif').value = cotisationTTC.toFixed(2);
refreshTextFields();
};
const fillContractInfo = (info) => {
document.getElementById('p-numContrat').value = info.numContrat || document.getElementById('p-numContrat').value;
document.getElementById('p-numClient').value = info.numClient || '';
document.getElementById('p-nomClient').value = info.nomClient || '';
document.getElementById('p-numAgent').value = info.numAgent || '';
document.getElementById('p-nomAgent').value = info.nomAgent || '';
refreshTextFields();
};
const lookupContract = async () => {
if (!validateContractField('p-numContrat', true)) {
toast('N° contrat invalide: 16 chiffres requis.');
return;
}
const contract = document.getElementById('p-numContrat').value.trim();
const params = new URLSearchParams({ numContrat: contract });
const data = await api(`/advalo/lookup-contract?${params.toString()}`, { method: 'GET' });
fillContractInfo(data.info || {});
if (data.info?.source && data.info.source !== 'none') toast(`Informations chargées (${data.info.source}).`, 'green');
else toast('Aucune donnée trouvée pour ce contrat. Saisie manuelle possible.', 'orange');
};
const renderHistoriqueDetail = (typedId, row, details) => {
const detailId = `h-detail-${typedId.replace(/[^a-zA-Z0-9_-]/g, '-')}`;
return `
<tr id="${detailId}" class="advalo-history-detail-row">
<td colspan="10">
<div class="card-panel" style="margin:6px 0;">
<div class="row" style="margin-bottom:0;">
<div class="col s12 m3"><b>Marchandise:</b> ${details.marchandise || '-'}</div>
<div class="col s12 m3"><b>Modes:</b> ${details.mode || '-'}</div>
<div class="col s12 m3"><b>Départ:</b> ${details.depart || '-'}</div>
<div class="col s12 m3"><b>Arrivée:</b> ${details.arrivee || '-'}</div>
</div>
<div class="row" style="margin-bottom:0;">
<div class="col s12 m2"><b>Valeur assurée:</b> ${details.valeurAssuree || '-'}</div>
<div class="col s12 m2"><b>Taux:</b> ${details.taux || '-'}</div>
<div class="col s12 m2"><b>Prime mini:</b> ${details.primeMinimum || '-'}</div>
<div class="col s12 m2"><b>Cotisation HT:</b> ${details.cotisationHT || '-'}</div>
<div class="col s12 m2"><b>Coût acte:</b> ${details.coutActe || '-'}</div>
<div class="col s12 m2"><b>Cotisation TTC:</b> ${details.cotisationTTC || '-'}</div>
</div>
<div class="row" style="margin-bottom:0;">
<div class="col s12 m4"><b>Acteur:</b> ${(details.actorPrenom || '')} ${(details.actorNom || '')} ${details.actorMatricule ? `(${details.actorMatricule})` : ''}</div>
<div class="col s12 m8 right-align">
${row.source === 'hors_grille' && String(row.statutFacturation || '').toLowerCase().includes('non') ? `<button class="btn red darken-3 advalo-h-delete" data-id="${typedId}">Supprimer</button>` : ''}
</div>
</div>
</div>
</td>
</tr>`;
};
const loadHistorique = async () => {
setLoading('historique', true);
try {
const statut = document.getElementById('h-statut').value || 'all';
const params = new URLSearchParams({
numClient: document.getElementById('h-numClient').value || '',
numContrat: document.getElementById('h-numContrat').value || '',
dateDebut: document.getElementById('h-dateDebut').value || '',
dateFin: document.getElementById('h-dateFin').value || '',
sourceType: document.getElementById('h-sourceType').value || 'all',
statutFacturation: statut === 'all' ? '' : statut,
page: String(state.historique.page),
pageSize: String(pageSize),
sort: 'dateDebutIso',
order: 'desc'
});
const data = await apiLatest('historique', `/advalo/historique?${params.toString()}`, { method: 'GET' });
const body = document.getElementById('historique-body');
body.innerHTML = '';
(data.rows || []).forEach((row) => {
const typedId = `${row.source === 'deleguee' ? 'g' : 'd'}:${row.id}`;
body.insertAdjacentHTML('beforeend', `
<tr data-typed-id="${typedId}">
<td><button class="btn-flat advalo-h-expand" data-id="${typedId}" style="color:#1a237e;"></button></td>
<td>${sourceLabel(row.source)}</td>
<td>${row.numDemande || ''}</td>
<td>${row.numClient || ''}</td>
<td>${row.numContrat || ''}</td>
<td>${row.dateDebut || ''}</td>
<td>${row.dateFin || ''}</td>
<td>${row.tarif || ''}</td>
<td>${row.statutFacturation || ''}</td>
<td>
<button class="btn indigo darken-4 white-text advalo-doc-avenant" data-id="${typedId}">Avenant</button>
<button class="btn teal darken-1 white-text advalo-doc-attestation" data-id="${typedId}">Attestation</button>
</td>
</tr>
`);
});
document.querySelectorAll('.advalo-h-expand').forEach((btn) => {
btn.addEventListener('click', async () => {
const typedId = btn.dataset.id;
const existing = document.getElementById(`h-detail-${typedId.replace(/[^a-zA-Z0-9_-]/g, '-')}`);
if (existing) {
existing.remove();
state.openHistoriqueId = null;
btn.textContent = '▸';
return;
}
if (state.openHistoriqueId) {
const old = document.getElementById(`h-detail-${state.openHistoriqueId.replace(/[^a-zA-Z0-9_-]/g, '-')}`);
if (old) old.remove();
const oldBtn = document.querySelector(`.advalo-h-expand[data-id="${state.openHistoriqueId}"]`);
if (oldBtn) oldBtn.textContent = '▸';
}
try {
const detailData = await api(`/advalo/historique/${encodeURIComponent(typedId)}`, { method: 'GET' });
const row = (data.rows || []).find((r) => `${r.source === 'deleguee' ? 'g' : 'd'}:${r.id}` === typedId);
const details = detailData.row?.details || row?.details || {};
const currentRow = btn.closest('tr');
currentRow.insertAdjacentHTML('afterend', renderHistoriqueDetail(typedId, row || {}, details));
state.openHistoriqueId = typedId;
btn.textContent = '▾';
document.querySelectorAll('.advalo-h-delete').forEach((deleteBtn) => {
deleteBtn.addEventListener('click', async () => {
if (!confirm('Supprimer cette demande hors grille ?')) return;
try {
await api(`/advalo/demande/${encodeURIComponent(deleteBtn.dataset.id)}`, { method: 'DELETE' });
toast('Demande supprimée.', 'green');
await loadHistorique();
} catch (error) {
toast(error.message);
}
});
});
} catch (error) {
toast(error.message);
}
});
});
document.querySelectorAll('.advalo-doc-avenant').forEach((button) => {
button.addEventListener('click', async () => {
try {
await downloadDocx(`/advalo/demande/${encodeURIComponent(button.dataset.id)}/avenant`, { method: 'POST' }, 'Avenant_Advalo.docx');
toast('Avenant généré.', 'green');
} catch (error) {
toast(error.message);
}
});
});
document.querySelectorAll('.advalo-doc-attestation').forEach((button) => {
button.addEventListener('click', async () => {
try {
await downloadDocx(`/advalo/demande/${encodeURIComponent(button.dataset.id)}/attestation`, { method: 'POST' }, 'Attestation_Advalo.docx');
toast('Attestation générée.', 'green');
} catch (error) {
toast(error.message);
}
});
});
state.historique.totalPages = Number(data.meta?.totalPages || 1);
state.historique.page = Number(data.meta?.page || 1);
setIndicator('h-page-indicator', state.historique.page, state.historique.totalPages, data.meta?.totalRows || 0);
refreshTextFields();
} catch (error) {
if (isAbortError(error)) return;
throw error;
} finally {
setLoading('historique', false);
}
};
const updateFacturationInfo = () => {
const selected = [...document.querySelectorAll('.advalo-fact-check')].filter((input) => input.checked).length;
const total = [...document.querySelectorAll('.advalo-fact-check')].length;
document.getElementById('f-selection-info').textContent = `${selected} / ${total} ligne(s) sélectionnée(s)`;
};
const renderFacturationRows = () => {
const body = document.getElementById('facturation-body');
body.innerHTML = '';
state.facturationRows.forEach((row) => {
const typedId = `${row.source === 'deleguee' ? 'g' : 'd'}:${row.id}`;
body.insertAdjacentHTML('beforeend', `
<tr>
<td><label><input type="checkbox" class="filled-in advalo-fact-check" value="${typedId}" checked><span></span></label></td>
<td>${sourceLabel(row.source)}</td>
<td>${row.numDemande || ''}</td>
<td>${row.numClient || ''}</td>
<td>${row.numContrat || ''}</td>
<td>${row.dateDebut || ''}</td>
<td>${row.tarif || ''}</td>
<td>${row.statutFacturation || ''}</td>
</tr>
`);
});
document.querySelectorAll('.advalo-fact-check').forEach((input) => input.addEventListener('change', updateFacturationInfo));
updateFacturationInfo();
const first = state.facturationRows[0];
document.getElementById('facturation-client-agent-row').style.display = first ? 'block' : 'none';
document.getElementById('f-client-recap').textContent = first ? `${first.nomClient || '-'} (${first.numClient || '-'})` : '-';
document.getElementById('f-agent-recap').textContent = first ? (first.souscripteur || '-') : '-';
};
const loadFacturationCandidates = async () => {
if (!validateContractField('f-numContrat', false)) {
toast('N° contrat invalide: 16 chiffres requis.');
return;
}
setLoading('facturation', true);
try {
const mode = document.getElementById('f-sourceMode').value || 'hors_grille';
const params = new URLSearchParams({
numContrat: document.getElementById('f-numContrat').value || '',
dateDebut: document.getElementById('f-dateDebut').value || '',
dateFin: document.getElementById('f-dateFin').value || '',
facture: 'false',
nonFacture: 'true',
deleguee: mode === 'mixte' ? 'true' : 'false',
nonDeleguee: 'true',
sourceType: mode === 'mixte' ? 'all' : 'hors_grille',
page: '1',
pageSize: '200',
sort: 'dateDebutIso',
order: 'desc'
});
const data = await apiLatest('facturation', `/advalo/historique?${params.toString()}`, { method: 'GET' });
state.facturationRows = (data.rows || []);
state.removedFacturationIds = new Set();
renderFacturationRows();
if ((data.meta?.totalRows || 0) === 0) toast('Aucune ligne non facturée sur la période/contrat.', 'orange');
else toast(`${data.meta?.totalRows || 0} ligne(s) facturable(s).`, 'green');
} catch (error) {
if (isAbortError(error)) return;
throw error;
} finally {
setLoading('facturation', false);
}
};
const loadCumul = async () => {
setLoading('cumul', true);
try {
if (!validateContractField('c-numContrat', false)) {
toast('N° contrat invalide: 16 chiffres requis.');
return;
}
const params = new URLSearchParams({
numContrat: document.getElementById('c-numContrat').value || '',
numClient: document.getElementById('c-numClient').value || '',
dateDebut: document.getElementById('c-dateDebut').value || '',
dateFin: document.getElementById('c-dateFin').value || '',
page: String(state.cumul.page),
pageSize: String(pageSize),
sort: 'totalNonFacture',
order: 'desc'
});
const data = await apiLatest('cumul', `/advalo/cumul?${params.toString()}`, { method: 'GET' });
document.getElementById('k-total-advalo').textContent = fmt(data.totalAdvalo);
document.getElementById('k-total-facture').textContent = fmt(data.totalFacture);
document.getElementById('k-total-nonfacture').textContent = fmt(data.totalNonFacture);
document.getElementById('k-total-lignes').textContent = String(data.totalLignes || 0);
const body = document.getElementById('cumul-body');
body.innerHTML = '';
(data.rows || []).forEach((row) => {
body.insertAdjacentHTML('beforeend', `
<tr>
<td>${row.numContrat || ''}</td>
<td>${row.nomClient || ''}</td>
<td>${row.region || ''}</td>
<td>${row.dpt || ''}</td>
<td>${row.souscripteur || ''}</td>
<td>${fmt(row.totalAdvalo)}</td>
<td>${fmt(row.totalFacture)}</td>
<td>${fmt(row.totalNonFacture)}</td>
<td>
<button class="btn indigo darken-4 white-text advalo-cumul-hist" data-contrat="${row.numContrat || ''}">Voir historique filtré</button>
<button class="btn teal darken-1 white-text advalo-cumul-fact" data-contrat="${row.numContrat || ''}">Aller facturation</button>
</td>
</tr>
`);
});
state.cumul.totalPages = Number(data.meta?.totalPages || 1);
state.cumul.page = Number(data.meta?.page || 1);
setIndicator('c-page-indicator', state.cumul.page, state.cumul.totalPages, data.meta?.totalRows || 0);
document.querySelectorAll('.advalo-cumul-hist').forEach((btn) => {
btn.addEventListener('click', async () => {
document.getElementById('h-numContrat').value = btn.dataset.contrat || '';
state.historique.page = 1;
state.loaded.historique = true;
await activatePanel('advalo-tab-historique');
await loadHistorique();
});
});
document.querySelectorAll('.advalo-cumul-fact').forEach((btn) => {
btn.addEventListener('click', async () => {
document.getElementById('f-numContrat').value = btn.dataset.contrat || '';
await activatePanel('advalo-tab-facturation');
refreshTextFields();
});
});
refreshTextFields();
} catch (error) {
if (isAbortError(error)) return;
throw error;
} finally {
setLoading('cumul', false);
}
};
const loadReporting = async () => {
setLoading('reporting', true);
try {
if (!validateContractField('r-numContrat', false)) {
toast('N° contrat invalide: 16 chiffres requis.');
return;
}
const statut = document.getElementById('r-statut').value || 'all';
const params = new URLSearchParams({
numClient: document.getElementById('r-numClient').value || '',
numContrat: document.getElementById('r-numContrat').value || '',
souscripteur: document.getElementById('r-souscripteur').value || '',
region: document.getElementById('r-region').value || '',
dateDebut: document.getElementById('r-dateDebut').value || '',
dateFin: document.getElementById('r-dateFin').value || '',
actorMatricule: document.getElementById('r-actorMatricule').value || '',
actorNom: document.getElementById('r-actorNom').value || '',
actionType: document.getElementById('r-actionType').value || '',
sourceType: document.getElementById('r-sourceType').value || 'all',
statutFacturation: statut === 'all' ? '' : statut,
sort: document.getElementById('r-sort').value || 'totalAdvalo',
order: document.getElementById('r-order').value || 'desc',
page: String(state.reporting.page),
pageSize: String(pageSize)
});
const data = await apiLatest('reporting', `/advalo/reporting?${params.toString()}`, { method: 'GET' });
document.getElementById('r-total-advalo').textContent = fmt(data.totaux?.totalAdvalo || 0);
document.getElementById('r-total-facture').textContent = fmt(data.totaux?.totalFacture || 0);
document.getElementById('r-total-nonfacture').textContent = fmt(data.totaux?.totalNonFacture || 0);
document.getElementById('r-total-lignes').textContent = String(data.totaux?.totalLignes || 0);
const body = document.getElementById('reporting-body');
body.innerHTML = '';
(data.rows || []).forEach((row) => {
body.insertAdjacentHTML('beforeend', `
<tr>
<td>${row.numContrat || ''}</td>
<td>${row.nomClient || ''}</td>
<td>${row.region || ''}</td>
<td>${row.souscripteur || ''}</td>
<td>${fmt(row.totalAdvalo)}</td>
<td>${fmt(row.totalFacture)}</td>
<td>${fmt(row.totalNonFacture)}</td>
<td>${row.totalLignes || 0}</td>
</tr>`);
});
const actorBody = document.getElementById('reporting-actors-body');
actorBody.innerHTML = '';
(data.actorStats || []).forEach((a) => {
actorBody.insertAdjacentHTML('beforeend', `
<tr>
<td>${a.actorMatricule || ''}</td>
<td>${a.actorName || ''}</td>
<td>${a.actionType || ''}</td>
<td>${a.actionsCount || 0}</td>
</tr>
`);
});
state.reporting.totalPages = Number(data.meta?.totalPages || 1);
state.reporting.page = Number(data.meta?.page || 1);
setIndicator('r-page-indicator', state.reporting.page, state.reporting.totalPages, data.meta?.totalRows || 0);
refreshTextFields();
} catch (error) {
if (isAbortError(error)) return;
throw error;
} finally {
setLoading('reporting', false);
}
};
const activatePanel = async (targetId) => {
abortDataRequests();
panels.forEach((panel) => { panel.style.display = panel.id === targetId ? 'block' : 'none'; });
navLinks.forEach((link) => {
const li = link.parentElement;
if (!li) return;
if (link.dataset.target === targetId) li.classList.add('active');
else li.classList.remove('active');
});
initDateFields();
refreshTextFields();
if (targetId === 'advalo-tab-historique' && !state.loaded.historique) {
state.historique.page = 1;
await loadHistorique();
state.loaded.historique = true;
}
if (targetId === 'advalo-tab-reporting' && !state.loaded.reporting) {
state.reporting.page = 1;
await loadReporting();
state.loaded.reporting = true;
}
if (targetId === 'advalo-tab-cumul' && !state.loaded.cumul) {
state.cumul.page = 1;
await loadCumul();
state.loaded.cumul = true;
}
};
navLinks.forEach((link) => {
link.addEventListener('click', async (event) => {
event.preventDefault();
try { await activatePanel(link.dataset.target); } catch (error) { toast(error.message); }
});
});
document.querySelectorAll('.p-mode-check').forEach((input) => input.addEventListener('change', () => { syncModes(); refreshTextFields(); }));
['p-capital', 'p-taux', 'p-primeMin', 'p-coutActe'].forEach((id) => {
const input = document.getElementById(id);
if (input) input.addEventListener('input', recalcPonctuelPricing);
});
document.getElementById('p-numContrat').addEventListener('blur', async () => {
const value = String(document.getElementById('p-numContrat').value || '').trim();
if (value.length === 16) {
try { await lookupContract(); } catch (error) { toast(error.message); }
}
});
document.getElementById('p-numContrat').addEventListener('keydown', async (e) => {
if (e.key !== 'Enter') return;
e.preventDefault();
try { await lookupContract(); } catch (error) { toast(error.message); }
});
document.getElementById('btn-load-contract').addEventListener('click', async (event) => {
event.preventDefault();
try { await lookupContract(); } catch (error) { toast(error.message); }
});
document.getElementById('advalo-ponctuel-form').addEventListener('submit', async (event) => {
event.preventDefault();
const errorSlot = document.getElementById('p-form-error');
if (errorSlot) errorSlot.textContent = '';
syncModes();
const required = ['p-numContrat', 'p-marchandise', 'p-depart', 'p-arrivee', 'p-dateDebut', 'p-dateFin', 'p-capital', 'p-taux', 'p-primeMin', 'p-cotisationHT', 'p-cotisationTTC'];
if (!validateContractField('p-numContrat', true) || !requireFields(required)) {
if (errorSlot) errorSlot.textContent = 'Complète les champs obligatoires (contrat 16 chiffres).';
toast('Complète les champs obligatoires (contrat 16 chiffres).');
return;
}
if (!document.getElementById('p-mode').value.trim()) {
if (errorSlot) errorSlot.textContent = 'Sélectionne au moins un mode de transport.';
toast('Sélectionne au moins un mode de transport.');
return;
}
try {
const payload = {
typeFacturation: document.querySelector('input[name="p-typeFacturation"]:checked')?.value || 'ponctuel',
numClient: document.getElementById('p-numClient').value.trim(),
nomClient: document.getElementById('p-nomClient').value.trim(),
numContrat: document.getElementById('p-numContrat').value.trim(),
marchandise: document.getElementById('p-marchandise').value.trim(),
mode: document.getElementById('p-mode').value.trim(),
capital: document.getElementById('p-capital').value.trim(),
depart: document.getElementById('p-depart').value.trim(),
arrivee: document.getElementById('p-arrivee').value.trim(),
dateDebut: document.getElementById('p-dateDebut').value.trim(),
dateFin: document.getElementById('p-dateFin').value.trim(),
taux: document.getElementById('p-taux').value.trim(),
primeMinimum: document.getElementById('p-primeMin').value.trim(),
coutActe: document.getElementById('p-coutActe').value.trim(),
cotisationHT: document.getElementById('p-cotisationHT').value.trim(),
cotisationTTC: document.getElementById('p-cotisationTTC').value.trim(),
tarif: document.getElementById('p-cotisationTTC').value.trim(),
facturer: document.getElementById('p-facturer').checked
};
const data = await api('/advalo/ponctuel', { method: 'POST', body: JSON.stringify(payload) });
toast(`Demande créée: ${data.row.numDemande || data.row.id}`, 'green');
state.confirmation.demandId = `d:${data.row.id}`;
state.confirmation.summary = `Demande ${data.row.numDemande || data.row.id} - Contrat ${data.row.numContrat || ''}`;
const summary = document.getElementById('advalo-confirm-summary');
if (summary) summary.textContent = state.confirmation.summary;
confirmModal.open();
event.target.reset();
document.querySelectorAll('.p-mode-check').forEach((i) => { i.checked = false; });
document.getElementById('p-coutActe').value = '36';
document.getElementById('p-taux').value = '0.3';
document.getElementById('p-primeMin').value = '15';
document.querySelector('input[name="p-typeFacturation"][value="ponctuel"]').checked = true;
recalcPonctuelPricing();
refreshTextFields();
state.loaded.historique = false;
state.loaded.reporting = false;
state.loaded.cumul = false;
} catch (error) {
if (errorSlot) errorSlot.textContent = error.message;
toast(error.message);
}
});
document.getElementById('btn-f-load').addEventListener('click', async (event) => {
event.preventDefault();
try { await loadFacturationCandidates(); } catch (error) { toast(error.message); }
});
document.getElementById('btn-f-remove').addEventListener('click', (event) => {
event.preventDefault();
const selected = [...document.querySelectorAll('.advalo-fact-check:checked')].map((i) => i.value);
if (!selected.length) {
toast('Sélectionne au moins une ligne à retirer.');
return;
}
selected.forEach((id) => state.removedFacturationIds.add(id));
state.facturationRows = state.facturationRows.filter((row) => {
const typedId = `${row.source === 'deleguee' ? 'g' : 'd'}:${row.id}`;
return !state.removedFacturationIds.has(typedId);
});
renderFacturationRows();
toast('Lignes retirées de la liste.', 'green');
});
document.getElementById('btn-facturer').addEventListener('click', async (event) => {
event.preventDefault();
const ids = [...document.querySelectorAll('.advalo-fact-check:checked')].map((i) => i.value).filter(Boolean);
if (!ids.length) {
toast('Sélectionne au moins une ligne.');
return;
}
try {
const sourceMode = document.getElementById('f-sourceMode').value || 'hors_grille';
const includeTransportDetails = document.querySelector('input[name="f-include-details"]:checked')?.value !== 'false';
const data = await api('/advalo/facturation/batch', {
method: 'POST',
body: JSON.stringify({
demandeIds: ids,
sourceMode,
includeTransportDetails,
removedDemandeIds: [...state.removedFacturationIds]
})
});
if (data.idempotent) toast(`Lot déjà traité: ${data.batch.id}`, 'blue');
else toast(`Facturation OK: lot ${data.batch.id}`, 'green');
state.batchConfirmation.batchId = data.batch.id;
state.batchConfirmation.summary = `Lot ${data.batch.id} - Contrat ${data.batch.numContrat || ''} - ${data.batch.dateDebut || ''} / ${data.batch.dateFin || ''}`;
const batchSummary = document.getElementById('advalo-batch-summary');
if (batchSummary) batchSummary.textContent = state.batchConfirmation.summary;
batchModal.open();
await loadFacturationCandidates();
state.loaded.reporting = false;
state.loaded.cumul = false;
state.loaded.historique = false;
} catch (error) {
toast(error.message);
}
});
document.getElementById('btn-h-search').addEventListener('click', async () => {
if (!validateContractField('h-numContrat', false)) {
toast('N° contrat invalide: 16 chiffres requis.');
return;
}
state.historique.page = 1;
try { await loadHistorique(); toast('Historique chargé.', 'green'); } catch (error) { toast(error.message); }
});
document.getElementById('btn-h-export').addEventListener('click', async () => {
if (!validateContractField('h-numContrat', false)) {
toast('N° contrat invalide: 16 chiffres requis.');
return;
}
try {
const statut = document.getElementById('h-statut').value || 'all';
const params = new URLSearchParams({
numClient: document.getElementById('h-numClient').value || '',
numContrat: document.getElementById('h-numContrat').value || '',
dateDebut: document.getElementById('h-dateDebut').value || '',
dateFin: document.getElementById('h-dateFin').value || '',
sourceType: document.getElementById('h-sourceType').value || 'all',
statutFacturation: statut === 'all' ? '' : statut,
sort: 'dateDebutIso',
order: 'desc'
});
const csv = await api(`/advalo/export?${params.toString()}`, { method: 'GET', headers: { Authorization: `Bearer ${token}` } });
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'advalo_export.csv';
link.click();
URL.revokeObjectURL(url);
toast('Export CSV généré.', 'green');
} catch (error) {
toast(error.message);
}
});
document.getElementById('btn-h-prev').addEventListener('click', async () => {
if (state.historique.page <= 1) return;
state.historique.page -= 1;
try { await loadHistorique(); } catch (error) { toast(error.message); }
});
document.getElementById('btn-h-next').addEventListener('click', async () => {
if (state.historique.page >= state.historique.totalPages) return;
state.historique.page += 1;
try { await loadHistorique(); } catch (error) { toast(error.message); }
});
document.getElementById('btn-cumul').addEventListener('click', async (event) => {
event.preventDefault();
state.cumul.page = 1;
try { await loadCumul(); toast('Cumul calculé.', 'green'); } catch (error) { toast(error.message); }
});
document.getElementById('btn-c-prev').addEventListener('click', async () => {
if (state.cumul.page <= 1) return;
state.cumul.page -= 1;
try { await loadCumul(); } catch (error) { toast(error.message); }
});
document.getElementById('btn-c-next').addEventListener('click', async () => {
if (state.cumul.page >= state.cumul.totalPages) return;
state.cumul.page += 1;
try { await loadCumul(); } catch (error) { toast(error.message); }
});
document.getElementById('btn-reporting').addEventListener('click', async (event) => {
event.preventDefault();
state.reporting.page = 1;
try { await loadReporting(); toast('Reporting chargé.', 'green'); } catch (error) { toast(error.message); }
});
document.getElementById('btn-r-prev').addEventListener('click', async () => {
if (state.reporting.page <= 1) return;
state.reporting.page -= 1;
try { await loadReporting(); } catch (error) { toast(error.message); }
});
document.getElementById('btn-r-next').addEventListener('click', async () => {
if (state.reporting.page >= state.reporting.totalPages) return;
state.reporting.page += 1;
try { await loadReporting(); } catch (error) { toast(error.message); }
});
document.getElementById('advalo-confirm-avenant').addEventListener('click', async () => {
if (!state.confirmation.demandId) return;
try {
await downloadDocx(`/advalo/demande/${encodeURIComponent(state.confirmation.demandId)}/avenant`, { method: 'POST' }, 'Avenant_Advalo.docx');
toast('Avenant généré.', 'green');
} catch (error) {
toast(error.message);
}
});
document.getElementById('advalo-confirm-attestation').addEventListener('click', async () => {
if (!state.confirmation.demandId) return;
try {
await downloadDocx(`/advalo/demande/${encodeURIComponent(state.confirmation.demandId)}/attestation`, { method: 'POST' }, 'Attestation_Advalo.docx');
toast('Attestation générée.', 'green');
} catch (error) {
toast(error.message);
}
});
document.getElementById('advalo-batch-avenant').addEventListener('click', async () => {
if (!state.batchConfirmation.batchId) return;
try {
await downloadDocx(`/advalo/facturation/batch/${encodeURIComponent(state.batchConfirmation.batchId)}/avenant`, { method: 'POST' }, 'Avenant_Periodique_Advalo.docx');
toast('Avenant périodique généré.', 'green');
} catch (error) {
toast(error.message);
}
});
applyNumericGuards();
bindFloatingLabels();
initDateFields();
initSelects(document);
syncModes();
recalcPonctuelPricing();
refreshTextFields();
activatePanel('advalo-tab-accueil').catch((error) => toast(error.message));
});

View File

@ -0,0 +1,174 @@
const express = require('express');
const jwt = require('jsonwebtoken');
const renderPage = require('../utils/renderHelper');
const advaloService = require('../services/advaloService');
const logger = require('../utils/logger');
const router = express.Router();
function getActor(req) {
const authHeader = req.headers.authorization || '';
const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : null;
if (!token) {
const err = new Error('Session invalide, reconnectez-vous.');
err.status = 401;
throw err;
}
return jwt.verify(token, 'no-mdp');
}
function handleError(res, error) {
logger.log('error', `Advalo error: ${error.message}`, {
status: error.status || 500,
stack: error.stack,
data: error.data,
originalError: error.originalError ? String(error.originalError) : undefined
});
return res.status(error.status || 500).json({
valid: false,
message: error.message || 'Erreur serveur Advalorem'
});
}
router.get('/', (_req, res) => {
renderPage('advalo.ejs', res);
});
router.get('/lookup-contract', async (req, res) => {
try {
const actor = getActor(req);
const info = await advaloService.lookupContract(req.query.numContrat, actor);
return res.json({ valid: true, info });
} catch (error) {
return handleError(res, error);
}
});
router.get('/historique', async (req, res) => {
try {
const actor = getActor(req);
const data = await advaloService.getHistorique(req.query, actor);
return res.json({
valid: true,
rows: data.rows,
meta: {
totalRows: data.totalRows,
totalPages: data.totalPages,
page: data.page,
pageSize: data.pageSize
}
});
} catch (error) {
return handleError(res, error);
}
});
router.get('/historique/:id', async (req, res) => {
try {
const actor = getActor(req);
const data = await advaloService.getHistoriqueDetail(req.params.id, actor);
return res.json({ valid: true, row: data });
} catch (error) {
return handleError(res, error);
}
});
router.get('/cumul', async (req, res) => {
try {
const actor = getActor(req);
const cumul = await advaloService.getCumul(req.query, actor);
return res.json({ valid: true, ...cumul });
} catch (error) {
return handleError(res, error);
}
});
router.post('/ponctuel', async (req, res) => {
try {
const actor = getActor(req);
const row = await advaloService.createPonctuel(req.body, actor);
return res.status(201).json({ valid: true, row });
} catch (error) {
return handleError(res, error);
}
});
router.delete('/demande/:id', async (req, res) => {
try {
const actor = getActor(req);
const row = await advaloService.softDeleteDemande(req.params.id, actor);
return res.json({ valid: true, row });
} catch (error) {
return handleError(res, error);
}
});
router.post('/facturation/batch', async (req, res) => {
try {
const actor = getActor(req);
const result = await advaloService.facturerBatch(req.body, actor);
return res.json({ valid: true, ...result });
} catch (error) {
return handleError(res, error);
}
});
router.post('/demande/:id/avenant', async (req, res) => {
try {
const actor = getActor(req);
const doc = await advaloService.generateDemandeDocument(req.params.id, 'avenant', actor);
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
res.setHeader('Content-Disposition', `attachment; filename="${doc.filename}"`);
return res.send(doc.buffer);
} catch (error) {
return handleError(res, error);
}
});
router.post('/demande/:id/attestation', async (req, res) => {
try {
const actor = getActor(req);
const doc = await advaloService.generateDemandeDocument(req.params.id, 'attestation', actor);
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
res.setHeader('Content-Disposition', `attachment; filename="${doc.filename}"`);
return res.send(doc.buffer);
} catch (error) {
return handleError(res, error);
}
});
router.post('/facturation/batch/:id/avenant', async (req, res) => {
try {
const actor = getActor(req);
const doc = await advaloService.generateBatchAvenant(req.params.id, actor);
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
res.setHeader('Content-Disposition', `attachment; filename="${doc.filename}"`);
return res.send(doc.buffer);
} catch (error) {
return handleError(res, error);
}
});
router.get('/reporting', async (req, res) => {
try {
const actor = getActor(req);
const data = await advaloService.getReporting(req.query, actor);
return res.json({ valid: true, ...data });
} catch (error) {
return handleError(res, error);
}
});
router.get('/export', async (req, res) => {
try {
const actor = getActor(req);
const csv = await advaloService.exportHistorique(req.query, actor);
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', 'attachment; filename="advalo_export.csv"');
return res.send(csv);
} catch (error) {
return handleError(res, error);
}
});
module.exports = router;

View File

@ -21,6 +21,7 @@ const historiqueParcoursController = require('./controllers/historiqueParcoursCo
const userController = require('./controllers/userController');
const regionController = require('./controllers/regionController');
const downloadController = require('./controllers/utilsController');
const advaloController = require('./controllers/advaloController');
// Association des contrôleurs aux routes
router.use('/', rootController);
@ -38,5 +39,6 @@ router.use('/historiqueParcours', historiqueParcoursController);
router.use('/user', userController);
router.use('/region', regionController);
router.use('/download', downloadController);
router.use('/advalo', advaloController);
module.exports = router;

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

View File

@ -0,0 +1,24 @@
Option Explicit
On Error Resume Next
ExempleMacroExcel
Sub ExempleMacroExcel()
Dim ApplicationExcel
Dim ClasseurExcel
Set ApplicationExcel = CreateObject("Excel.Application")
Dim WshShell, strCurDir
Set WshShell = CreateObject("WScript.Shell")
strCurDir = WshShell.CurrentDirectory
Set ClasseurExcel = ApplicationExcel.Workbooks.Open( strCurDir & "\vbs\script_cl063\Connexion.xlsm")
ApplicationExcel.Visible = False
ApplicationExcel.Run "CL063_AC800_sub" 'va lancer la macro
ApplicationExcel.Quit
Set ClasseurExcel = Nothing
Set ApplicationExcel = Nothing
End Sub

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,24 @@
Option Explicit
On Error Resume Next
ExempleMacroExcel
Sub ExempleMacroExcel()
Dim ApplicationExcel
Dim ClasseurExcel
Set ApplicationExcel = CreateObject("Excel.Application")
Dim WshShell, strCurDir
Set WshShell = CreateObject("WScript.Shell")
strCurDir = WshShell.CurrentDirectory
Set ClasseurExcel = ApplicationExcel.Workbooks.Open( strCurDir & "\vbs\script_pa025\attestation.xlsm")
ApplicationExcel.Visible = False
ApplicationExcel.Run "PA025_AC800_sub" 'va lancer la macro
ApplicationExcel.Quit
Set ClasseurExcel = Nothing
Set ApplicationExcel = Nothing
End Sub

View File

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,24 @@
Option Explicit
On Error Resume Next
ExempleMacroExcel
Sub ExempleMacroExcel()
Dim ApplicationExcel
Dim ClasseurExcel
Set ApplicationExcel = CreateObject("Excel.Application")
Dim WshShell, strCurDir
Set WshShell = CreateObject("WScript.Shell")
strCurDir = WshShell.CurrentDirectory
Set ClasseurExcel = ApplicationExcel.Workbooks.Open( strCurDir & "\vbs\script_qt550\Bordereau.xlsm")
ApplicationExcel.Visible = False
ApplicationExcel.Run "QT550_sub" 'va lancer la macro
ApplicationExcel.Quit
Set ClasseurExcel = Nothing
Set ApplicationExcel = Nothing
End Sub

View File

326
ecole/views/advalo.ejs Normal file
View File

@ -0,0 +1,326 @@
<div class="section">
<h4 class="center-align">Advalorem</h4>
</div>
<div class="divider"></div>
<div class="section">
<nav>
<div class="nav-wrapper">
<ul class="left" id="advaloNavSelect">
<li class="active"><a href="#" data-target="advalo-tab-accueil">Accueil</a></li>
<li><a href="#" data-target="advalo-tab-ponctuel">Hors grille</a></li>
<li><a href="#" data-target="advalo-tab-facturation">Facturation</a></li>
<li><a href="#" data-target="advalo-tab-historique">Historique</a></li>
<li><a href="#" data-target="advalo-tab-cumul">Cumul</a></li>
<li><a href="#" data-target="advalo-tab-reporting">Reporting</a></li>
</ul>
</div>
</nav>
</div>
<div id="advalo-tab-accueil" class="section advalo-panel">
<div class="card-panel">
<h6>Advalorem est intégré à EasyTransport.</h6>
<p>Parité V1: Hors grille (ponctuel/périodique), Facturation, Historique, Cumul, Reporting et documents.</p>
</div>
</div>
<div id="advalo-tab-ponctuel" class="section advalo-panel" style="display:none;">
<form id="advalo-ponctuel-form" class="col s12">
<div class="chapter"><i class="material-icons">settings</i>&nbsp;Type de facturation</div>
<div class="row" style="margin-top:12px;">
<div class="col s12 m6">
<label><input name="p-typeFacturation" type="radio" value="ponctuel" checked /><span>Transport ponctuel</span></label>
</div>
<div class="col s12 m6">
<label><input name="p-typeFacturation" type="radio" value="periodique" /><span>Bordereau périodique (sans ligne à ligne)</span></label>
</div>
</div>
<div class="chapter"><i class="material-icons">business</i>&nbsp;Infos client / intermédiaire</div>
<div class="row" style="margin-top:12px;">
<div class="input-field col s12 m4">
<input id="p-numContrat" maxlength="16" required>
<label for="p-numContrat">N° Contrat (16 chiffres)</label>
</div>
<div class="col s12 m2" style="padding-top:16px;">
<button id="btn-load-contract" type="button" class="btn waves-effect waves-light">Charger</button>
</div>
<div class="input-field col s12 m2"><input id="p-numClient"><label for="p-numClient">N° Client</label></div>
<div class="input-field col s12 m4"><input id="p-nomClient"><label for="p-nomClient">Nom Client</label></div>
</div>
<div class="row">
<div class="input-field col s12 m3"><input id="p-numAgent"><label for="p-numAgent">N° Portefeuille</label></div>
<div class="input-field col s12 m3"><input id="p-nomAgent"><label for="p-nomAgent">Intermédiaire</label></div>
</div>
<div class="chapter"><i class="material-icons">local_shipping</i>&nbsp;Infos transport</div>
<div class="row" style="margin-top:12px;">
<div class="input-field col s12 m4"><input id="p-marchandise" required><label for="p-marchandise">Marchandise *</label></div>
<div class="input-field col s12 m4"><input id="p-depart" required><label for="p-depart">Lieu de départ *</label></div>
<div class="input-field col s12 m4"><input id="p-arrivee" required><label for="p-arrivee">Lieu d'arrivée *</label></div>
</div>
<div class="row">
<div class="input-field col s12 m3"><input id="p-dateDebut" class="advalo-date" autocomplete="off" required><label for="p-dateDebut">Date début</label></div>
<div class="input-field col s12 m3"><input id="p-dateFin" class="advalo-date" autocomplete="off" required><label for="p-dateFin">Date fin</label></div>
<div class="col s12 m6">
<label class="rc-field-label">Mode(s) de transport *</label>
<p>
<label><input type="checkbox" class="filled-in p-mode-check" value="Terrestre"><span>Terrestre</span></label>
<label style="margin-left:16px;"><input type="checkbox" class="filled-in p-mode-check" value="Aérien"><span>Aérien</span></label>
<label style="margin-left:16px;"><input type="checkbox" class="filled-in p-mode-check" value="Fluvial"><span>Fluvial</span></label>
<label style="margin-left:16px;"><input type="checkbox" class="filled-in p-mode-check" value="Maritime"><span>Maritime</span></label>
<label style="margin-left:16px;"><input type="checkbox" class="filled-in p-mode-check" value="Postal"><span>Postal</span></label>
</p>
<input id="p-mode" type="hidden">
</div>
</div>
<div class="chapter"><i class="material-icons">euro</i>&nbsp;Calcul cotisation</div>
<div class="row" style="margin-top:12px;">
<div class="input-field col s12 m2"><input id="p-capital" required><label for="p-capital">Valeur assurée *</label></div>
<div class="input-field col s12 m2"><input id="p-taux" value="0.3" required><label for="p-taux">Taux (%) *</label></div>
<div class="input-field col s12 m2"><input id="p-primeMin" value="15" required><label for="p-primeMin">Prime mini *</label></div>
<div class="input-field col s12 m2"><input id="p-coutActe" value="36"><label for="p-coutActe">Coût acte</label></div>
<div class="input-field col s12 m2"><input id="p-cotisationHT" required><label for="p-cotisationHT">Cotisation HT *</label></div>
<div class="input-field col s12 m2"><input id="p-cotisationTTC" required><label for="p-cotisationTTC">Cotisation TTC *</label></div>
<input id="p-tarif" type="hidden">
</div>
<div class="chapter"><i class="material-icons">check_circle</i>&nbsp;Enregistrement / facturation</div>
<div class="row" style="margin-top:12px;">
<div class="col s12 m4">
<label><input type="checkbox" id="p-facturer" /><span>Facturer immédiatement (pont AXA)</span></label>
</div>
<div class="col s12 m4"><span id="p-form-error" class="helper-text error" style="display:block;"></span></div>
<div class="col s12 m4 right-align">
<button class="btn waves-effect waves-light" type="submit">Enregistrer</button>
</div>
</div>
</form>
</div>
<div id="advalo-tab-facturation" class="section advalo-panel" style="display:none;">
<div class="card-panel">
<div class="row">
<div class="input-field col s12 m2"><input id="f-numContrat" maxlength="16"><label for="f-numContrat">N° Contrat (16)</label></div>
<div class="input-field col s12 m2"><input id="f-dateDebut" class="advalo-date" autocomplete="off"><label for="f-dateDebut">Date début</label></div>
<div class="input-field col s12 m2"><input id="f-dateFin" class="advalo-date" autocomplete="off"><label for="f-dateFin">Date fin</label></div>
<div class="input-field col s12 m2">
<select id="f-sourceMode">
<option value="hors_grille" selected>Hors grille</option>
<option value="mixte">Mixte</option>
</select>
<label>Source facturation</label>
</div>
<div class="col s12 m2" style="padding-top:20px;"><button id="btn-f-load" class="btn waves-effect waves-light">Charger lignes</button></div>
<div class="col s12 m2" style="padding-top:20px;"><button id="btn-facturer" class="btn waves-effect waves-light">Facturer</button></div>
</div>
<div class="row" id="facturation-client-agent-row" style="display:none; margin-bottom:0;">
<div class="col s12 m6"><b>Client:</b> <span id="f-client-recap">-</span></div>
<div class="col s12 m6"><b>Intermédiaire:</b> <span id="f-agent-recap">-</span></div>
</div>
<div class="row" style="margin-top:8px; margin-bottom:0;">
<div class="col s12 m6">
<label><input class="with-gap" name="f-include-details" type="radio" value="true" checked /><span>Inclure le détail des transports dans lavenant</span></label>
</div>
<div class="col s12 m6">
<label><input class="with-gap" name="f-include-details" type="radio" value="false" /><span>Ne pas inclure le détail des transports</span></label>
</div>
</div>
<div class="row" style="margin-top:10px; margin-bottom:0;">
<div class="col s12 m6"><button id="btn-f-remove" class="btn red darken-3 waves-effect waves-light">Retirer de la liste</button></div>
<div class="col s12 m6 right-align"><span id="f-selection-info" class="grey-text text-darken-1"></span></div>
</div>
</div>
<table class="striped responsive-table">
<thead>
<tr><th></th><th>Source</th><th>Demande</th><th>Client</th><th>Contrat</th><th>Date début</th><th>Tarif</th><th>Facturation</th></tr>
</thead>
<tbody id="facturation-body"></tbody>
</table>
<div id="advalo-loading-facturation" class="center-align advalo-loader-wrap" style="display:none; margin-top:16px;"><div class="advalo-ring-loader"></div></div>
</div>
<div id="advalo-tab-historique" class="section advalo-panel" style="display:none;">
<div class="card-panel">
<div class="row">
<div class="input-field col s12 m2"><input id="h-numClient"><label for="h-numClient">N° Client</label></div>
<div class="input-field col s12 m2"><input id="h-numContrat" maxlength="16"><label for="h-numContrat">N° Contrat</label></div>
<div class="input-field col s12 m2"><input id="h-dateDebut" class="advalo-date" autocomplete="off"><label for="h-dateDebut">Date début</label></div>
<div class="input-field col s12 m2"><input id="h-dateFin" class="advalo-date" autocomplete="off"><label for="h-dateFin">Date fin</label></div>
<div class="input-field col s12 m2">
<select id="h-sourceType">
<option value="all" selected>Toutes</option>
<option value="deleguee">Grille déléguée</option>
<option value="hors_grille">Hors grille</option>
</select>
<label>Source</label>
</div>
<div class="input-field col s12 m2">
<select id="h-statut">
<option value="all" selected>Tous</option>
<option value="facture">Facturé</option>
<option value="non_facture">Non facturé</option>
</select>
<label>Facturé / Non facturé</label>
</div>
</div>
<div class="row" style="margin-bottom:0;">
<div class="col s12 m6">
<button id="btn-h-search" class="btn waves-effect waves-light">Rechercher</button>
<button id="btn-h-export" class="btn waves-effect waves-light" style="margin-left:8px;">Exporter CSV</button>
</div>
<div class="col s12 m6 right-align" style="padding-top:8px;">
<button id="btn-h-prev" class="btn indigo darken-4 white-text">Précédent</button>
<span id="h-page-indicator">Page 1 / 1</span>
<button id="btn-h-next" class="btn indigo darken-4 white-text">Suivant</button>
</div>
</div>
</div>
<table class="striped responsive-table">
<thead>
<tr><th></th><th>Source</th><th>Demande</th><th>Client</th><th>Contrat</th><th>Date début</th><th>Date fin</th><th>Tarif</th><th>Statut</th><th>Actions</th></tr>
</thead>
<tbody id="historique-body"></tbody>
</table>
<div id="advalo-loading-historique" class="center-align advalo-loader-wrap" style="display:none; margin-top:16px;"><div class="advalo-ring-loader"></div></div>
</div>
<div id="advalo-tab-cumul" class="section advalo-panel" style="display:none;">
<div class="card-panel">
<div class="row">
<div class="input-field col s12 m3"><input id="c-numContrat" maxlength="16"><label for="c-numContrat">N° Contrat</label></div>
<div class="input-field col s12 m2"><input id="c-numClient"><label for="c-numClient">N° Client</label></div>
<div class="input-field col s12 m2"><input id="c-dateDebut" class="advalo-date" autocomplete="off"><label for="c-dateDebut">Date début</label></div>
<div class="input-field col s12 m2"><input id="c-dateFin" class="advalo-date" autocomplete="off"><label for="c-dateFin">Date fin</label></div>
<div class="col s12 m3 right-align" style="padding-top:20px;"><button id="btn-cumul" class="btn waves-effect waves-light">Calculer</button></div>
</div>
</div>
<div class="row">
<div class="col s12 m3"><div class="card-panel">Total Advalo: <b id="k-total-advalo">0</b></div></div>
<div class="col s12 m3"><div class="card-panel">Total Facturé: <b id="k-total-facture">0</b></div></div>
<div class="col s12 m3"><div class="card-panel">Total Non facturé: <b id="k-total-nonfacture">0</b></div></div>
<div class="col s12 m3"><div class="card-panel">Lignes: <b id="k-total-lignes">0</b></div></div>
</div>
<div class="row" style="margin-bottom:8px;"><div class="col s12 right-align"><button id="btn-c-prev" class="btn indigo darken-4 white-text">Précédent</button> <span id="c-page-indicator">Page 1 / 1</span> <button id="btn-c-next" class="btn indigo darken-4 white-text">Suivant</button></div></div>
<table class="striped responsive-table">
<thead>
<tr><th>Contrat</th><th>Client</th><th>Région</th><th>DPT</th><th>Souscripteur</th><th>Total Advalo</th><th>Total Facturé</th><th>Total Non facturé</th><th>Actions</th></tr>
</thead>
<tbody id="cumul-body"></tbody>
</table>
<div id="advalo-loading-cumul" class="center-align advalo-loader-wrap" style="display:none; margin-top:16px;"><div class="advalo-ring-loader"></div></div>
</div>
<div id="advalo-tab-reporting" class="section advalo-panel" style="display:none;">
<div class="card-panel">
<div class="row">
<div class="input-field col s12 m2"><input id="r-numClient"><label for="r-numClient">N° Client</label></div>
<div class="input-field col s12 m2"><input id="r-numContrat" maxlength="16"><label for="r-numContrat">N° Contrat</label></div>
<div class="input-field col s12 m2"><input id="r-souscripteur"><label for="r-souscripteur">Souscripteur</label></div>
<div class="input-field col s12 m2"><input id="r-region"><label for="r-region">Région</label></div>
<div class="input-field col s12 m2"><input id="r-dateDebut" class="advalo-date" autocomplete="off"><label for="r-dateDebut">Date début</label></div>
<div class="input-field col s12 m2"><input id="r-dateFin" class="advalo-date" autocomplete="off"><label for="r-dateFin">Date fin</label></div>
</div>
<div class="row" style="margin-bottom:0;">
<div class="input-field col s12 m2"><input id="r-actorMatricule"><label for="r-actorMatricule">Matricule acteur</label></div>
<div class="input-field col s12 m2"><input id="r-actorNom"><label for="r-actorNom">Nom acteur</label></div>
<div class="input-field col s12 m2">
<select id="r-actionType">
<option value="" selected>Toutes actions</option>
<option value="create">Création</option>
<option value="update">Modification</option>
<option value="delete_soft">Suppression</option>
<option value="facturation_batch">Facturation</option>
<option value="doc_avenant">Doc avenant</option>
<option value="doc_attestation">Doc attestation</option>
<option value="lookup_contrat">Lookup contrat</option>
</select>
<label>Action</label>
</div>
<div class="input-field col s12 m2">
<select id="r-sourceType">
<option value="all" selected>Toutes</option>
<option value="deleguee">Grille déléguée</option>
<option value="hors_grille">Hors grille</option>
</select>
<label>Source</label>
</div>
<div class="input-field col s12 m2">
<select id="r-statut">
<option value="all" selected>Tous</option>
<option value="facture">Facturé</option>
<option value="non_facture">Non facturé</option>
</select>
<label>Facturé / Non facturé</label>
</div>
<div class="input-field col s12 m2">
<select id="r-sort">
<option value="totalAdvalo" selected>Total Advalo</option>
<option value="totalFacture">Total Facturé</option>
<option value="totalNonFacture">Total Non facturé</option>
<option value="numContrat">Contrat</option>
</select>
<label>Trier par</label>
</div>
</div>
<div class="row" style="margin-bottom:0;">
<div class="input-field col s12 m2">
<select id="r-order"><option value="desc" selected>Décroissant</option><option value="asc">Croissant</option></select>
<label>Ordre</label>
</div>
<div class="col s12 m10 right-align" style="padding-top:20px;"><button id="btn-reporting" class="btn waves-effect waves-light">Rafraîchir</button></div>
</div>
</div>
<div class="row">
<div class="col s12 m3"><div class="card-panel">Total Advalo: <b id="r-total-advalo">0</b></div></div>
<div class="col s12 m3"><div class="card-panel">Total Facturé: <b id="r-total-facture">0</b></div></div>
<div class="col s12 m3"><div class="card-panel">Total Non facturé: <b id="r-total-nonfacture">0</b></div></div>
<div class="col s12 m3"><div class="card-panel">Lignes source: <b id="r-total-lignes">0</b></div></div>
</div>
<div class="row" style="margin-bottom:8px;"><div class="col s12 right-align"><button id="btn-r-prev" class="btn indigo darken-4 white-text">Précédent</button> <span id="r-page-indicator">Page 1 / 1</span> <button id="btn-r-next" class="btn indigo darken-4 white-text">Suivant</button></div></div>
<table class="striped responsive-table">
<thead>
<tr><th>Contrat</th><th>Client</th><th>Région</th><th>Souscripteur</th><th>Total Advalo</th><th>Total Facturé</th><th>Total Non facturé</th><th>Lignes</th></tr>
</thead>
<tbody id="reporting-body"></tbody>
</table>
<div class="card-panel" style="margin-top:20px;">
<h6>Qui a fait quoi</h6>
<table class="striped responsive-table">
<thead><tr><th>Matricule</th><th>Acteur</th><th>Action</th><th>Occurrences</th></tr></thead>
<tbody id="reporting-actors-body"></tbody>
</table>
</div>
<div id="advalo-loading-reporting" class="center-align advalo-loader-wrap" style="display:none; margin-top:16px;"><div class="advalo-ring-loader"></div></div>
</div>
<script src="/js/advalo-module.js"></script>
<div id="advalo-confirm-modal" class="modal">
<div class="modal-content"><h5>Confirmation ponctuel</h5><p id="advalo-confirm-summary" class="grey-text text-darken-2"></p></div>
<div class="modal-footer">
<a href="#!" id="advalo-confirm-avenant" class="modal-close waves-effect waves-green btn">Avenant</a>
<a href="#!" id="advalo-confirm-attestation" class="modal-close waves-effect waves-light btn">Attestation</a>
<a href="#!" id="advalo-confirm-close" class="modal-close waves-effect btn-flat">Fermer</a>
</div>
</div>
<div id="advalo-batch-modal" class="modal">
<div class="modal-content"><h5>Confirmation facturation</h5><p id="advalo-batch-summary" class="grey-text text-darken-2"></p></div>
<div class="modal-footer">
<a href="#!" id="advalo-batch-avenant" class="modal-close waves-effect waves-green btn">Avenant périodique</a>
<a href="#!" id="advalo-batch-close" class="modal-close waves-effect btn-flat">Fermer</a>
</div>
</div>

View File

@ -17,7 +17,7 @@
<span class="white-text">EasyTransport 2.0.0</span>
</div>
</li>
<li><a href="/dev" class="white-text">Advalorem</a></li>
<li><a href="/advalo" class="white-text">Advalorem</a></li>
<li><a href="/dev" class="white-text">Documentation</a></li>
<li><a href="/dev" class="white-text">Liens utiles</a></li>
<li id="reportingSidenavSelect"><a href="/dev" class="white-text">Reporting</a></li>