Skip to content

Instantly share code, notes, and snippets.

@b23prodtm
Created May 30, 2026 15:54
Show Gist options
  • Select an option

  • Save b23prodtm/63b3efe43fa695c2c5a1304173cdee26 to your computer and use it in GitHub Desktop.

Select an option

Save b23prodtm/63b3efe43fa695c2c5a1304173cdee26 to your computer and use it in GitHub Desktop.
Transfert commandes GDO vers Webtel Station Chargeur Online
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>GDO ➜ Webtel Converter Ultimate</title>
<style>
:root { --webtel-blue: #0056b3; --webtel-gray: #f0f2f5; --webtel-border: #dcdfe3; --success: #28a745; }
body { font-family: 'Segoe UI', Arial, sans-serif; background-color: var(--webtel-gray); padding: 20px; margin: 0; }
.container { max-width: 1200px; margin: 20px auto; background: #fff; padding: 25px; border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); position: relative; }
.header { display: flex; justify-content: space-between; align-items: center; border-bottom: 2px solid var(--webtel-blue); margin-bottom: 20px; padding-bottom: 10px; }
h1 { color: var(--webtel-blue); font-size: 1.3rem; margin: 0; text-transform: uppercase; }
/* Onglet de Configuration */
.settings-panel { position: absolute; top: 70px; right: 25px; background: white; border: 1px solid var(--webtel-border); border-radius: 4px; padding: 15px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 100; display: none; width: 350px; }
.settings-btn { background: none; border: none; cursor: pointer; font-size: 1.5rem; color: var(--webtel-text); }
.settings-btn:hover { color: var(--webtel-blue); }
.filter-section { background: #f9f9f9; padding: 15px; border: 1px solid var(--webtel-border); border-radius: 4px; margin-bottom: 15px; }
.filter-section label { font-weight: bold; font-size: 0.85rem; display: block; margin-bottom: 8px; }
textarea { width: 100%; border: 1px solid var(--webtel-border); padding: 8px; font-family: monospace; font-size: 0.8rem; box-sizing: border-box; border-radius: 4px; }
.upload-area { border: 1px dashed var(--webtel-blue); padding: 30px; text-align: center; background: #fafafa; cursor: pointer; margin-bottom: 20px; border-radius: 4px; }
table { width: 100%; border-collapse: collapse; font-size: 0.8rem; }
th { background: #f8f9fa; padding: 10px; border-bottom: 2px solid var(--webtel-blue); text-align: left; }
td { padding: 8px 10px; border-bottom: 1px solid #eee; }
.pagination { display: flex; justify-content: center; align-items: center; gap: 20px; padding: 10px; background: #f8f9fa; border-top: 1px solid var(--webtel-border); }
.pagination button { padding: 4px 12px; cursor: pointer; border: 1px solid var(--webtel-blue); background: white; color: var(--webtel-blue); border-radius: 4px; font-weight: bold; }
.btn-action { width: 100%; padding: 12px; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; margin-top: 10px; display: none; }
#logArea { background: #fdfdfd; border: 1px solid var(--webtel-border); padding: 10px; height: 100px; overflow-y: auto; font-family: monospace; font-size: 0.75rem; margin-top: 15px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>GDO ➜ Webtel Converter</h1>
<button class="settings-btn" onclick="toggleSettings()"></button>
</div>
<!-- Panneau de Configuration JSON Masqué -->
<div id="settingsPanel" class="settings-panel">
<label style="font-weight: bold; display: block; margin-bottom: 10px;">Mappage JSON :</label>
<textarea id="mappingConfig" style="height: 200px;">
{
"ORDER": "NUM. ORD.",
"CLIENT_CODE": "COD. DEST",
"DEST": "DESTINATARIO",
"CITY": "LOCALITA",
"ZIP": "PR",
"WEIGHT": "LORDO",
"PARCELS": "COLLI",
"PALLETS": "PLT",
"DATE_SHIP": "DATA CARICO",
"DATE_DELIV": "DATA CONS.",
"TIME_DELIV": "ORA CONSEGNA",
"NOTE1": "NOTE DESTINATARI",
"NOTE2": "NOTE"
}
</textarea>
<button onclick="saveConfig()" style="margin-top:10px; width:100%; padding:8px; background:var(--webtel-blue); color:white; border:none; border-radius:4px; cursor:pointer;">Sauvegarder</button>
</div>
<div class="filter-section">
<label>Préchargement des commandes (Numéros séparés par espace ou ligne) :</label>
<textarea id="prechargeFilter" style="height: 60px;" placeholder="Ex: 2131290840 2131290841"></textarea>
</div>
<div id="validationResult"></div>
<div class="upload-area" id="dropZone">
Glissez le fichier <strong>BOLLE_ATTI_TESTATA.csv</strong> ici pour l'importer
</div>
<div id="tableWrapper" style="display:none;">
<input type="text" id="tableSearch" style="width:100%; padding:10px; margin-bottom:15px; border:1px solid var(--webtel-border); border-radius:4px;" placeholder="Rechercher un destinataire ou une ville...">
<div style="border: 1px solid var(--webtel-border);">
<table>
<thead>
<tr>
<th><input type="checkbox" id="selectAll"></th>
<th>Ordre</th>
<th>Destinataire</th>
<th>Ville</th>
<th>Poids</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
<div class="pagination">
<button id="prevBtn"> << </button>
<span id="pageInfo">Page 1 / 1</span>
<button id="nextBtn"> >> </button>
</div>
</div>
<button id="convertBtn" class="btn-action" style="background:var(--webtel-blue); color:white; display:block;">CONVERTIR POUR WEBTEL</button>
<button id="downloadBtn" class="btn-action" style="background:var(--success); color:white;">TÉLÉCHARGER LE CSV</button>
</div>
<div id="logArea"></div>
</div>
<script>
let allData = [];
let filteredData = [];
let selectedOrders = new Set();
let currentPage = 1;
const rowsPerPage = 15;
let fileHeaders = [];
let finalCSV = "";
function toggleSettings() {
const panel = document.getElementById('settingsPanel');
panel.style.display = panel.style.display === 'block' ? 'none' : 'block';
}
function saveConfig() {
localStorage.setItem('gdoMappingConfig', document.getElementById('mappingConfig').value);
toggleSettings();
addLog("Configuration JSON sauvegardée.");
}
window.onload = () => {
const saved = localStorage.getItem('gdoMappingConfig');
if (saved) document.getElementById('mappingConfig').value = saved;
};
const dropZone = document.getElementById('dropZone');
dropZone.onclick = () => { let i = document.createElement('input'); i.type='file'; i.onchange=e=>handleFile(e.target.files[0]); i.click(); };
dropZone.ondragover = e => { e.preventDefault(); dropZone.style.background="#f0f7ff"; };
dropZone.ondrop = e => { e.preventDefault(); handleFile(e.dataTransfer.files[0]); };
function addLog(msg) {
const div = document.createElement('div');
div.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`;
document.getElementById('logArea').prepend(div);
}
function handleFile(file) {
const precharge = document.getElementById('prechargeFilter').value.split(/[\s,\n]+/).filter(x => x.trim() !== "");
const reader = new FileReader();
reader.onload = e => {
const lines = e.target.result.split(/\r?\n/);
fileHeaders = lines[0].split(';');
const map = JSON.parse(document.getElementById('mappingConfig').value);
const idxOrder = fileHeaders.indexOf(map.ORDER);
allData = lines.slice(1).filter(l => l.trim()).map(l => l.split(';'))
.filter(row => precharge.length === 0 || precharge.includes(row[idxOrder]));
filteredData = [...allData];
selectedOrders.clear();
currentPage = 1;
renderTable();
document.getElementById('tableWrapper').style.display = "block";
addLog(`${allData.length} lignes filtrées par préchargement.`);
};
reader.readAsText(file);
}
function renderTable() {
const map = JSON.parse(document.getElementById('mappingConfig').value);
const tbody = document.getElementById('tableBody');
tbody.innerHTML = "";
const start = (currentPage - 1) * rowsPerPage;
const pageData = filteredData.slice(start, start + rowsPerPage);
const idx = { order: fileHeaders.indexOf(map.ORDER), dest: fileHeaders.indexOf(map.DEST), city: fileHeaders.indexOf(map.CITY), weight: fileHeaders.indexOf(map.WEIGHT) };
pageData.forEach(row => {
const id = row[idx.order];
const tr = document.createElement('tr');
const isChecked = selectedOrders.has(id) ? "checked" : "";
tr.innerHTML = `<td><input type="checkbox" ${isChecked} onchange="toggleOrderSelection('${id}', this.checked)"></td>
<td>${id}</td><td>${row[idx.dest]}</td><td>${row[idx.city]}</td><td>${row[idx.weight]}</td>`;
tbody.appendChild(tr);
});
const totalPages = Math.ceil(filteredData.length / rowsPerPage);
document.getElementById('pageInfo').textContent = `Page ${currentPage} / ${totalPages || 1}`;
}
window.toggleOrderSelection = (id, checked) => {
if(checked) selectedOrders.add(id); else selectedOrders.delete(id);
};
document.getElementById('selectAll').onchange = (e) => {
const map = JSON.parse(document.getElementById('mappingConfig').value);
const idxOrder = fileHeaders.indexOf(map.ORDER);
filteredData.forEach(row => {
if(e.target.checked) selectedOrders.add(row[idxOrder]);
else selectedOrders.delete(row[idxOrder]);
});
renderTable();
};
document.getElementById('tableSearch').oninput = (e) => {
const term = e.target.value.toLowerCase();
filteredData = allData.filter(row => row.some(col => col.toLowerCase().includes(term)));
currentPage = 1;
renderTable();
};
document.getElementById('convertBtn').onclick = () => {
if (selectedOrders.size === 0) return alert("Sélectionnez au moins une commande.");
const map = JSON.parse(document.getElementById('mappingConfig').value);
const header = "M_Separator;Date of shippment;M_Carrier code;M_Product code;M_Sender code;Sender label;Consignee code;M_Consignee name 1;Consignee name 2;Consignee name 3;M_Consignee address 1;Consignee address 2;Consignee address 3;Consignee country;M_Consignee postal code;M_Consignee city;Contact;Phone;Consignment number;Reference;M_Terms of delivery-Incoterm;M_Number of handlings units;NumberOfParcels;Number of Euro-pallet;Type of Euro-pallet;M_Gross weight;Taxable unit;Taxable unit type;Gross volume;Cash on Delivery;Currency1;Declared value;Currency2;not use1;not use2;Goods description;Delivery information1;Delivery information2;Mandatory delivery date;Printer code;Particular delivery;Announcement by email;Mobile phone;E-mail;meter floor;Start date desired delivery;Start hour desired delivery;End date desired delivery;End hour desired delivery;Prior delivery notice;Appointment taking before delivery;Availability date";
const rows = [header];
const idx = {};
for(let key in map) { idx[key] = fileHeaders.indexOf(map[key]); }
allData.filter(row => selectedOrders.has(row[idx.ORDER])).forEach(row => {
let r = new Array(52).fill("");
r[0] = "S"; r[2] = "01"; r[3] = "01"; r[4] = "01";
r[1] = row[idx.DATE_SHIP];
r[6] = row[idx.CLIENT_CODE];
r[7] = row[idx.DEST]; r[13] = "FR"; r[14] = row[idx.ZIP]; r[15] = row[idx.CITY];
r[19] = row[idx.ORDER]; r[20] = "P";
r[21] = row[idx.PARCELS]; r[22] = row[idx.PARCELS];
r[23] = row[idx.PALLETS];
r[24] = "PAL"; r[25] = row[idx.WEIGHT];
const notes = ((row[idx.NOTE1] || "") + " " + (row[idx.NOTE2] || "")).trim();
r[36] = notes.substring(0, 128); r[37] = notes.substring(128, 256);
r[38] = row[idx.DATE_DELIV];
r[46] = row[idx.TIME_DELIV];
r[50] = "O";
rows.push(r.join(';'));
});
finalCSV = rows.join('\n');
document.getElementById('downloadBtn').style.display = "block";
addLog(`Conversion terminée : ${selectedOrders.size} commandes sélectionnées.`);
};
document.getElementById('downloadBtn').onclick = () => {
const blob = new Blob([finalCSV], { type: 'text/csv' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `WEBTEL_EXPORT_${Date.now()}.csv`;
a.click();
};
document.getElementById('prevBtn').onclick = () => { if(currentPage > 1) { currentPage--; renderTable(); } };
document.getElementById('nextBtn').onclick = () => { if(currentPage * rowsPerPage < filteredData.length) { currentPage++; renderTable(); } };
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment