feat(advalo): integrate module in etv2 and restore v1 AXA scripts flow
This commit is contained in:
parent
7b484f34c9
commit
70dd59b03e
|
|
@ -240,6 +240,63 @@ hr.form {
|
||||||
background: #26a69a;
|
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 {
|
||||||
border-radius: 10px !important;
|
border-radius: 10px !important;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
});
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -21,6 +21,7 @@ const historiqueParcoursController = require('./controllers/historiqueParcoursCo
|
||||||
const userController = require('./controllers/userController');
|
const userController = require('./controllers/userController');
|
||||||
const regionController = require('./controllers/regionController');
|
const regionController = require('./controllers/regionController');
|
||||||
const downloadController = require('./controllers/utilsController');
|
const downloadController = require('./controllers/utilsController');
|
||||||
|
const advaloController = require('./controllers/advaloController');
|
||||||
|
|
||||||
// Association des contrôleurs aux routes
|
// Association des contrôleurs aux routes
|
||||||
router.use('/', rootController);
|
router.use('/', rootController);
|
||||||
|
|
@ -38,5 +39,6 @@ router.use('/historiqueParcours', historiqueParcoursController);
|
||||||
router.use('/user', userController);
|
router.use('/user', userController);
|
||||||
router.use('/region', regionController);
|
router.use('/region', regionController);
|
||||||
router.use('/download', downloadController);
|
router.use('/download', downloadController);
|
||||||
|
router.use('/advalo', advaloController);
|
||||||
|
|
||||||
module.exports = router;
|
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.
Binary file not shown.
|
|
@ -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.
|
|
@ -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
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||||
|
|
@ -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> 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> 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> 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> 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> 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 l’avenant</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>
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
<span class="white-text">EasyTransport 2.0.0</span>
|
<span class="white-text">EasyTransport 2.0.0</span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</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">Documentation</a></li>
|
||||||
<li><a href="/dev" class="white-text">Liens utiles</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>
|
<li id="reportingSidenavSelect"><a href="/dev" class="white-text">Reporting</a></li>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue