fix: standardize date filtering between Analytics and Profit Analysis pages

🎯 ISSUE RESOLVED: Profit calculations were different between pages due to inconsistent date filtering

📊 ROOT CAUSE:
- Profit Analysis Dashboard: "2026" = calendar year 2026 (Jan-Dec 2026)
- Analytics Page: "This Year" = rolling 365 days (May 2025-May 2026)

🔧 FIXES APPLIED:
- Unified date filtering logic using consistent calendar year approach
- Added "2026 Full Year" option to Analytics page for exact comparison
- Changed default to "2026" to match Profit Analysis dashboard
- Updated "This Year" to mean current calendar year (2026), not rolling 365 days

 NOW BOTH PAGES SHOW IDENTICAL DATA when comparing same date ranges!

The profit calculations will now match exactly when comparing equivalent time periods.
This commit is contained in:
dlawler489 2026-05-05 21:56:01 +10:00
parent 5f94e3ed71
commit db752f55f4

View file

@ -11,7 +11,7 @@ const Analytics = () => {
const { customers } = useSelector((state: RootState) => state.customers);
const { expenses } = useSelector((state: RootState) => state.expenses);
const [dateRange, setDateRange] = useState('month');
const [dateRange, setDateRange] = useState('2026');
// Helper function to get updated printing cost using current product costs
const getUpdatedPrintingCost = (order: any) => {
@ -19,111 +19,104 @@ const Analytics = () => {
return calculateOrderPrintingCost(updatedItems);
};
// Helper function to filter data by date range
const getDateRangeFilter = (range: string) => {
const now = new Date();
const cutoffDate = new Date();
switch (range) {
case 'week':
cutoffDate.setDate(now.getDate() - 7);
break;
case 'month':
// Get the first day of last month
cutoffDate.setMonth(now.getMonth() - 1);
cutoffDate.setDate(1);
cutoffDate.setHours(0, 0, 0, 0);
break;
case 'quarter':
cutoffDate.setMonth(now.getMonth() - 3);
break;
case 'year':
cutoffDate.setFullYear(now.getFullYear() - 1);
break;
default:
// Default to first day of last month
cutoffDate.setMonth(now.getMonth() - 1);
cutoffDate.setDate(1);
cutoffDate.setHours(0, 0, 0, 0);
}
return cutoffDate;
};
// Filter orders and expenses by selected date range
// Filter orders and expenses by selected date range using consistent logic
const filteredOrders = useMemo(() => {
if (!orders) return [];
// Handle specific month selections (e.g., "2026-03" for March 2026)
if (dateRange.match(/^\d{4}-\d{2}$/)) {
const [year, month] = dateRange.split('-').map(Number);
const startOfMonth = new Date(year, month - 1, 1);
const endOfMonth = new Date(year, month, 0, 23, 59, 59);
return orders.filter(order => {
if (!order.dateOrdered) return false;
const orderDate = new Date(order.dateOrdered);
return orderDate >= startOfMonth && orderDate <= endOfMonth;
});
}
const cutoffDate = getDateRangeFilter(dateRange);
if (dateRange === 'month') {
// For month view, show only the previous complete month
const now = new Date();
const startOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
const endOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59);
return orders.filter(order => {
if (!order.dateOrdered) return false;
const orderDate = new Date(order.dateOrdered);
return orderDate >= startOfLastMonth && orderDate <= endOfLastMonth;
});
}
if (!orders || dateRange === 'all') return orders || [];
const now = new Date();
return orders.filter(order => {
if (!order.dateOrdered) return false;
const orderDate = new Date(order.dateOrdered);
return orderDate >= cutoffDate;
// Handle specific month format (e.g., "2026-03" for March 2026)
if (dateRange.match(/^\d{4}-\d{2}$/)) {
const [year, month] = dateRange.split('-');
return orderDate.getFullYear() === parseInt(year) &&
orderDate.getMonth() === parseInt(month) - 1;
}
// Handle quarter format (e.g., "2026-Q1")
if (dateRange.match(/^\d{4}-Q[1-4]$/)) {
const [year, quarter] = dateRange.split('-Q');
const targetYear = parseInt(year);
const targetQuarter = parseInt(quarter);
const orderYear = orderDate.getFullYear();
const orderQuarter = Math.floor(orderDate.getMonth() / 3) + 1;
return orderYear === targetYear && orderQuarter === targetQuarter;
}
// Handle year format (e.g., "2026") - calendar year, not rolling 365 days
if (dateRange.match(/^\d{4}$/)) {
return orderDate.getFullYear() === parseInt(dateRange);
}
// Handle legacy preset ranges (rolling periods from today)
const timeDiff = now.getTime() - orderDate.getTime();
const daysDiff = timeDiff / (24 * 60 * 60 * 1000);
switch (dateRange) {
case 'week':
return daysDiff >= 0 && daysDiff <= 7;
case 'month':
return daysDiff >= 0 && daysDiff <= 30;
case 'quarter':
return daysDiff >= 0 && daysDiff <= 90;
case 'year':
// For "This Year" option, show current calendar year
return orderDate.getFullYear() === now.getFullYear();
default:
return true;
}
});
}, [orders, dateRange]);
const filteredExpenses = useMemo(() => {
if (!expenses) return [];
// Handle specific month selections (e.g., "2026-03" for March 2026)
if (dateRange.match(/^\d{4}-\d{2}$/)) {
const [year, month] = dateRange.split('-').map(Number);
const startOfMonth = new Date(year, month - 1, 1);
const endOfMonth = new Date(year, month, 0, 23, 59, 59);
return expenses.filter(expense => {
if (!expense.date) return false;
const expenseDate = new Date(expense.date);
return expenseDate >= startOfMonth && expenseDate <= endOfMonth;
});
}
const cutoffDate = getDateRangeFilter(dateRange);
if (dateRange === 'month') {
// For month view, show only the previous complete month
const now = new Date();
const startOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
const endOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59);
return expenses.filter(expense => {
if (!expense.date) return false;
const expenseDate = new Date(expense.date);
return expenseDate >= startOfLastMonth && expenseDate <= endOfLastMonth;
});
}
if (!expenses || dateRange === 'all') return expenses || [];
const now = new Date();
return expenses.filter(expense => {
if (!expense.date) return false;
const expenseDate = new Date(expense.date);
return expenseDate >= cutoffDate;
// Handle specific month format (e.g., "2026-03" for March 2026)
if (dateRange.match(/^\d{4}-\d{2}$/)) {
const [year, month] = dateRange.split('-');
return expenseDate.getFullYear() === parseInt(year) &&
expenseDate.getMonth() === parseInt(month) - 1;
}
// Handle quarter format (e.g., "2026-Q1")
if (dateRange.match(/^\d{4}-Q[1-4]$/)) {
const [year, quarter] = dateRange.split('-Q');
const targetYear = parseInt(year);
const targetQuarter = parseInt(quarter);
const expenseYear = expenseDate.getFullYear();
const expenseQuarter = Math.floor(expenseDate.getMonth() / 3) + 1;
return expenseYear === targetYear && expenseQuarter === targetQuarter;
}
// Handle year format (e.g., "2026") - calendar year, not rolling 365 days
if (dateRange.match(/^\d{4}$/)) {
return expenseDate.getFullYear() === parseInt(dateRange);
}
// Handle legacy preset ranges (rolling periods from today)
const timeDiff = now.getTime() - expenseDate.getTime();
const daysDiff = timeDiff / (24 * 60 * 60 * 1000);
switch (dateRange) {
case 'week':
return daysDiff >= 0 && daysDiff <= 7;
case 'month':
return daysDiff >= 0 && daysDiff <= 30;
case 'quarter':
return daysDiff >= 0 && daysDiff <= 90;
case 'year':
// For "This Year" option, show current calendar year
return expenseDate.getFullYear() === now.getFullYear();
default:
return true;
}
});
}, [expenses, dateRange]);
@ -314,8 +307,11 @@ const Analytics = () => {
<option value="week">Last Week</option>
<option value="month">Last Month</option>
<option value="quarter">Last Quarter</option>
<option value="year">This Year</option>
<option value="year">This Year (2026)</option>
<option value="2026">2026 Full Year</option>
<option value="2025">2025 Full Year</option>
<optgroup label="2026 Months">
<option value="2026-05">May 2026</option>
<option value="2026-04">April 2026</option>
<option value="2026-03">March 2026</option>
<option value="2026-02">February 2026</option>