Fix header finder
This commit is contained in:
@@ -515,6 +515,10 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
||||
});
|
||||
let sheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
|
||||
// Read sheet range to determine column offset
|
||||
const sheetRange = XLSX.utils.decode_range(sheet['!ref'] || 'A1');
|
||||
const colOffset = sheetRange.s.c; // first column index in sheet (0-based)
|
||||
|
||||
let sheetData = XLSX.utils.sheet_to_json(sheet, {
|
||||
header: 1,
|
||||
defval: "",
|
||||
@@ -522,6 +526,13 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
||||
range: 0
|
||||
});
|
||||
|
||||
// Track merged cell ranges — adjust for column offset
|
||||
const merges = (sheet['!merges'] || []).map(m => ({
|
||||
s: { r: m.s.r, c: m.s.c - colOffset },
|
||||
e: { r: m.e.r, c: m.e.c - colOffset }
|
||||
}));
|
||||
console.log('Sheet column offset:', colOffset, '(first col:', String.fromCharCode(65 + colOffset) + ')');
|
||||
|
||||
const useAutoDetect = document.getElementById('autoDetectHeader').checked;
|
||||
|
||||
if (!useAutoDetect) {
|
||||
@@ -537,7 +548,33 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
||||
return;
|
||||
}
|
||||
|
||||
let headers = sheetData[rowIndex - 1].slice(startColumn - 1).map(header => header === undefined ? "" : String(header).trim());
|
||||
// Build logical headers, collapsing merged cells
|
||||
const mergeStartMapManual = {};
|
||||
merges.forEach(m => {
|
||||
if ((rowIndex - 1) >= m.s.r && (rowIndex - 1) <= m.e.r) {
|
||||
for (let c = m.s.c; c <= m.e.c; c++) {
|
||||
mergeStartMapManual[c] = m.s.c;
|
||||
}
|
||||
}
|
||||
});
|
||||
let headers = [];
|
||||
const rawRowManual = sheetData[rowIndex - 1] || [];
|
||||
const seenManual = new Set();
|
||||
for (let c = startColumn - 1; c < rawRowManual.length; c++) {
|
||||
const ms = mergeStartMapManual[c];
|
||||
if (ms !== undefined) {
|
||||
if (seenManual.has(ms)) continue;
|
||||
seenManual.add(ms);
|
||||
const v = rawRowManual[ms];
|
||||
headers.push(v === undefined ? "" : String(v).replace(/[\r\n\t]+/g, ' ').trim());
|
||||
} else {
|
||||
const v = rawRowManual[c];
|
||||
headers.push(v === undefined ? "" : String(v).replace(/[\r\n\t]+/g, ' ').trim());
|
||||
}
|
||||
}
|
||||
while (headers.length > 0 && headers[headers.length - 1] === '') {
|
||||
headers.pop();
|
||||
}
|
||||
console.log("Intestazioni estratte (manual):", headers);
|
||||
availableXlsColumns = [...headers];
|
||||
usedColumnsFromDB = [];
|
||||
@@ -639,8 +676,40 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
||||
return;
|
||||
}
|
||||
|
||||
let headers = sheetData[bestRow].slice(bestStartCol).map(header => header === undefined ? "" : String(header).trim());
|
||||
console.log("Intestazioni estratte:", headers);
|
||||
// Build logical columns: each merge = one column, each non-merged cell = one column
|
||||
let headers = [];
|
||||
const rawRow = sheetData[bestRow] || [];
|
||||
|
||||
// Map each physical column to its merge start (or itself if not merged)
|
||||
const mergeStartMap = {}; // physCol -> startCol of its merge
|
||||
merges.forEach(m => {
|
||||
if (bestRow >= m.s.r && bestRow <= m.e.r) {
|
||||
for (let c = m.s.c; c <= m.e.c; c++) {
|
||||
mergeStartMap[c] = m.s.c;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const seen = new Set();
|
||||
for (let c = bestStartCol; c < rawRow.length; c++) {
|
||||
const mergeStart = mergeStartMap[c];
|
||||
const cleanVal = (v) => (v === undefined ? "" : String(v).replace(/[\r\n\t]+/g, ' ').trim());
|
||||
if (mergeStart !== undefined) {
|
||||
// Part of a merge — only take the first occurrence
|
||||
if (seen.has(mergeStart)) continue;
|
||||
seen.add(mergeStart);
|
||||
headers.push(cleanVal(rawRow[mergeStart]));
|
||||
} else {
|
||||
headers.push(cleanVal(rawRow[c]));
|
||||
}
|
||||
}
|
||||
// Trim trailing empty columns
|
||||
while (headers.length > 0 && headers[headers.length - 1] === '') {
|
||||
headers.pop();
|
||||
}
|
||||
// Final clean: ensure no whitespace-only entries sneak through
|
||||
headers = headers.map(h => h.replace(/[\r\n\t]+/g, ' ').trim());
|
||||
console.log("Logical headers:", headers, `(${headers.length} columns from ${rawRow.length} physical)`);
|
||||
availableXlsColumns = [...headers];
|
||||
usedColumnsFromDB = [];
|
||||
saveXlsHeaders(headers, bestRow + 1, bestStartCol + 1);
|
||||
@@ -680,8 +749,17 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
||||
document.querySelectorAll('select.xls-columns').forEach(select => {
|
||||
let currentValue = select.value || select.dataset.currentXls || '';
|
||||
let options = availableXlsColumns
|
||||
.filter(col => !usedColumns.includes(col) || col === currentValue)
|
||||
.map(col => `<option value="${col}" ${col === currentValue ? 'selected' : ''}>${col}</option>`)
|
||||
.map((col, origIdx) => ({ col, origIdx }))
|
||||
.filter(({ col }) => !usedColumns.includes(col) || col === currentValue)
|
||||
.map(({ col, origIdx }) => {
|
||||
const clean = col.replace(/[\r\n\t]+/g, ' ').trim();
|
||||
const isEmpty = clean === '';
|
||||
const colNum = origIdx + 1;
|
||||
const val = isEmpty ? `__empty_${colNum}__` : clean;
|
||||
const label = isEmpty ? `(empty column ${colNum})` : clean;
|
||||
const isSelected = (isEmpty ? val === currentValue : col === currentValue) ? 'selected' : '';
|
||||
return `<option value="${val}" ${isSelected}>${label}</option>`;
|
||||
})
|
||||
.join('');
|
||||
select.innerHTML = '<option value="">Select XLS Column</option>' + options;
|
||||
select.dataset.currentXls = currentValue;
|
||||
|
||||
Reference in New Issue
Block a user