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 { customers } = useSelector((state: RootState) => state.customers);
const { expenses } = useSelector((state: RootState) => state.expenses); 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 // Helper function to get updated printing cost using current product costs
const getUpdatedPrintingCost = (order: any) => { const getUpdatedPrintingCost = (order: any) => {
@ -19,111 +19,104 @@ const Analytics = () => {
return calculateOrderPrintingCost(updatedItems); return calculateOrderPrintingCost(updatedItems);
}; };
// Helper function to filter data by date range // Filter orders and expenses by selected date range using consistent logic
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
const filteredOrders = useMemo(() => { const filteredOrders = useMemo(() => {
if (!orders) return []; if (!orders || dateRange === 'all') return orders || [];
// 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 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 => { return orders.filter(order => {
if (!order.dateOrdered) return false; if (!order.dateOrdered) return false;
const orderDate = new Date(order.dateOrdered); const orderDate = new Date(order.dateOrdered);
return orderDate >= startOfLastMonth && orderDate <= endOfLastMonth;
}); // 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;
} }
return orders.filter(order => { // Handle quarter format (e.g., "2026-Q1")
if (!order.dateOrdered) return false; if (dateRange.match(/^\d{4}-Q[1-4]$/)) {
const orderDate = new Date(order.dateOrdered); const [year, quarter] = dateRange.split('-Q');
return orderDate >= cutoffDate; 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]); }, [orders, dateRange]);
const filteredExpenses = useMemo(() => { const filteredExpenses = useMemo(() => {
if (!expenses) return []; if (!expenses || dateRange === 'all') return expenses || [];
// 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 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 => { return expenses.filter(expense => {
if (!expense.date) return false; if (!expense.date) return false;
const expenseDate = new Date(expense.date); const expenseDate = new Date(expense.date);
return expenseDate >= startOfLastMonth && expenseDate <= endOfLastMonth;
}); // 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;
} }
return expenses.filter(expense => { // Handle quarter format (e.g., "2026-Q1")
if (!expense.date) return false; if (dateRange.match(/^\d{4}-Q[1-4]$/)) {
const expenseDate = new Date(expense.date); const [year, quarter] = dateRange.split('-Q');
return expenseDate >= cutoffDate; 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]); }, [expenses, dateRange]);
@ -314,8 +307,11 @@ const Analytics = () => {
<option value="week">Last Week</option> <option value="week">Last Week</option>
<option value="month">Last Month</option> <option value="month">Last Month</option>
<option value="quarter">Last Quarter</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"> <optgroup label="2026 Months">
<option value="2026-05">May 2026</option>
<option value="2026-04">April 2026</option> <option value="2026-04">April 2026</option>
<option value="2026-03">March 2026</option> <option value="2026-03">March 2026</option>
<option value="2026-02">February 2026</option> <option value="2026-02">February 2026</option>