diff --git a/pos/static/js/dashboard.js b/pos/static/js/dashboard.js index 0c0b872..f5e8205 100644 --- a/pos/static/js/dashboard.js +++ b/pos/static/js/dashboard.js @@ -84,13 +84,15 @@ const Dashboard = (() => { }); // ------------------------------------------------------------------------- - // Period selector (placeholder for future use) + // Period selector // ------------------------------------------------------------------------- function setPeriod(btn) { btn.closest('.period-selector').querySelectorAll('.period-btn').forEach(function(b) { b.classList.remove('active'); }); btn.classList.add('active'); + const period = btn.textContent.trim().toLowerCase(); + loadChart(period); } window.setPeriod = setPeriod; @@ -440,50 +442,168 @@ const Dashboard = (() => { } // ------------------------------------------------------------------------- - // 5. Weekly bar chart (last 7 days) + // Helpers for chart grouping // ------------------------------------------------------------------------- - async function loadWeeklyChart() { + function isoWeek(date) { + const tmp = new Date(date.valueOf()); + const dayNum = (date.getDay() + 6) % 7; + tmp.setDate(tmp.getDate() - dayNum + 3); + const firstThursday = tmp.valueOf(); + tmp.setMonth(0, 1); + if (tmp.getDay() !== 4) { + tmp.setMonth(0, 1 + ((4 - tmp.getDay()) + 7) % 7); + } + return 1 + Math.ceil((firstThursday - tmp) / 604800000); + } + + function weekLabel(date) { + return `Sem ${isoWeek(date)}`; + } + + function monthLabel(date) { + return MONTH_NAMES[date.getMonth()].slice(0, 3); + } + + // ------------------------------------------------------------------------- + // 5. Sales chart (today / week / month / year) + // ------------------------------------------------------------------------- + async function loadChart(period) { const chartEl = document.getElementById('bar-chart'); const totalEl = document.getElementById('chart-week-total'); + const legendEl = document.getElementById('chart-legend'); + const titleEl = document.querySelector('.chart-header .section-title'); if (!chartEl) return; - // Fetch daily summary for each of last 7 days - const days = []; - for (let i = 6; i >= 0; i--) { - days.push(daysAgo(i)); + period = period || 'semana'; + + let dateFrom, dateTo, labels = [], buckets = {}, labelOrder = []; + const now = new Date(); + + if (period === 'hoy') { + dateFrom = dateTo = todayStr(); + labelOrder = ['Hoy']; + buckets['Hoy'] = 0; + } else if (period === 'semana') { + const days = []; + for (let i = 6; i >= 0; i--) { days.push(daysAgo(i)); } + dateFrom = days[0]; + dateTo = days[6]; + days.forEach(d => { + const date = new Date(d + 'T12:00:00'); + const label = DAY_NAMES_SHORT[date.getDay()]; + labelOrder.push(label); + buckets[label] = { total: 0, date: d }; + }); + } else if (period === 'mes') { + const start = new Date(); + start.setDate(start.getDate() - 27); + dateFrom = start.toISOString().slice(0, 10); + dateTo = todayStr(); + for (let i = 3; i >= 0; i--) { + const d = new Date(); + d.setDate(d.getDate() - (i * 7 + 6)); + const label = weekLabel(d); + labelOrder.push(label); + buckets[label] = 0; + } + } else if (period === 'año') { + for (let i = 11; i >= 0; i--) { + const d = new Date(now.getFullYear(), now.getMonth() - i, 1); + const label = monthLabel(d); + labelOrder.push(label); + buckets[label] = 0; + } + dateFrom = new Date(now.getFullYear(), now.getMonth() - 11, 1).toISOString().slice(0, 10); + dateTo = todayStr(); } - const [summaries, histData] = await Promise.all([ - Promise.all(days.map(d => apiFetch(`/pos/api/register/daily-summary?date=${d}`))), - apiFetch(`/pos/api/historical-sales?date_from=${days[0]}&date_to=${days[6]}&per_page=200`).catch(() => ({ data: [] })) - ]); + // Fetch normal sales for short periods + let normalByKey = {}; + if (period === 'hoy' || period === 'semana') { + const days = period === 'hoy' ? [todayStr()] : (function() { + const arr = []; + for (let i = 6; i >= 0; i--) arr.push(daysAgo(i)); + return arr; + })(); + const summaries = await Promise.all( + days.map(d => apiFetch(`/pos/api/register/daily-summary?date=${d}`)) + ); + days.forEach((d, i) => { + const date = new Date(d + 'T12:00:00'); + const key = period === 'hoy' ? 'Hoy' : DAY_NAMES_SHORT[date.getDay()]; + normalByKey[key] = summaries[i] ? (summaries[i].total_sales || 0) : 0; + }); + } - // Group historical sales by day - const histRows = histData.data || []; - const histByDay = {}; + // Fetch historical sales for the range + let histRows = []; + try { + const perPage = period === 'año' ? 2000 : 1000; + const histData = await apiFetch(`/pos/api/historical-sales?date_from=${dateFrom}&date_to=${dateTo}&per_page=${perPage}`); + histRows = histData.data || []; + const totalPages = histData.pagination ? histData.pagination.total_pages : 1; + for (let p = 2; p <= totalPages && p <= 20; p++) { + const more = await apiFetch(`/pos/api/historical-sales?date_from=${dateFrom}&date_to=${dateTo}&per_page=${perPage}&page=${p}`); + histRows = histRows.concat(more.data || []); + } + } catch (e) { + histRows = []; + } + + // Group historical sales histRows.forEach(r => { if (!r.sale_date) return; - histByDay[r.sale_date] = (histByDay[r.sale_date] || 0) + (r.total || 0); + const date = new Date(r.sale_date + 'T12:00:00'); + let key; + if (period === 'hoy') key = 'Hoy'; + else if (period === 'semana') key = DAY_NAMES_SHORT[date.getDay()]; + else if (period === 'mes') { + // Find which week bucket this date belongs to + for (let i = 0; i < 4; i++) { + const bucketStart = new Date(); + bucketStart.setDate(bucketStart.getDate() - ((3 - i) * 7 + 6)); + const bucketEnd = new Date(bucketStart); + bucketEnd.setDate(bucketEnd.getDate() + 6); + if (date >= bucketStart && date <= bucketEnd) { + key = labelOrder[i]; + break; + } + } + } else { + key = monthLabel(date); + } + if (key) { + if (typeof buckets[key] === 'object') buckets[key].total += (r.total || 0); + else buckets[key] = (buckets[key] || 0) + (r.total || 0); + } }); - let weekTotal = 0; - const dayData = days.map((dateStr, i) => { - const s = summaries[i]; - const normalTotal = s ? (s.total_sales || 0) : 0; - const histTotal = histByDay[dateStr] || 0; + // Build chart data + let chartTotal = 0; + const dayData = labelOrder.map(label => { + let normalTotal = normalByKey[label] || 0; + let histTotal = 0; + if (typeof buckets[label] === 'object') { + histTotal = buckets[label].total; + } else { + histTotal = buckets[label] || 0; + } const total = normalTotal + histTotal; - weekTotal += total; - const d = new Date(dateStr + 'T12:00:00'); - return { - label: DAY_NAMES_SHORT[d.getDay()], - total: total, - isToday: dateStr === todayStr(), - }; + chartTotal += total; + const isToday = period === 'hoy' || (typeof buckets[label] === 'object' && buckets[label].date === todayStr()); + return { label, total, isToday }; }); - // Update week total + // Update labels + const titles = { hoy: 'Ventas de Hoy', semana: 'Ventas Semanales', mes: 'Ventas del Mes', año: 'Ventas del Año' }; + const legends = { hoy: 'Total del día', semana: 'Ventas brutas (7 días)', mes: 'Ventas brutas (4 semanas)', año: 'Ventas brutas (12 meses)' }; + if (titleEl) titleEl.textContent = titles[period] || 'Ventas'; if (totalEl) { - totalEl.innerHTML = `Total semana: ${fmt(weekTotal)}`; + const periodLabel = period === 'hoy' ? 'Total día' : period === 'semana' ? 'Total semana' : period === 'mes' ? 'Total mes' : 'Total año'; + totalEl.innerHTML = `${periodLabel}: ${fmt(chartTotal)}`; + } + if (legendEl) { + legendEl.innerHTML = `