// Agrupa por empleado, ordena cronológicamente y arma pares 1-2, 3-4, ... permitiendo cruzar medianoche. export function buildIntervalsCrossDay(rows){ const byDoc = new Map(); for (const r of rows) { if (!byDoc.has(r.doc)) byDoc.set(r.doc, []); byDoc.get(r.doc).push({ ms: r.dt_ms, date: r.isoDate, name: r.name }); } const out = []; for (const [doc, arr] of byDoc.entries()){ arr.sort((a,b)=>a.ms-b.ms); for (let i=0;i x.doc.localeCompare(y.doc) || x.fecha.localeCompare(y.fecha) || (x.desde_ms - y.desde_ms)); return out; } // Alternativa por (doc, fecha) exacta (conservar nombre y firma) export function buildIntervals(rows) { const nameByDoc = new Map(); const byKey = new Map(); // doc|isoDate -> [ms] for (const r of rows) { nameByDoc.set(r.doc, r.name); const key = `${r.doc}|${r.isoDate}`; if (!byKey.has(key)) byKey.set(key, []); byKey.get(key).push(r.dt_ms); } const result = []; for (const [key, arr] of byKey.entries()) { arr.sort((a,b)=>a-b); const [doc, isoDate] = key.split('|'); const name = nameByDoc.get(doc) || ''; for (let i=0; i{ if (a.doc !== b.doc) return a.doc.localeCompare(b.doc); if (a.isoDate !== b.isoDate) return a.isoDate.localeCompare(b.isoDate); return (a.desde_ms||0) - (b.desde_ms||0); }); return result; }