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:
parent
5f94e3ed71
commit
db752f55f4
1 changed files with 90 additions and 94 deletions
|
|
@ -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 [];
|
||||
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 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;
|
||||
});
|
||||
|
||||
// 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 => {
|
||||
if (!order.dateOrdered) return false;
|
||||
const orderDate = new Date(order.dateOrdered);
|
||||
return orderDate >= cutoffDate;
|
||||
// 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 [];
|
||||
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 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;
|
||||
});
|
||||
|
||||
// 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 => {
|
||||
if (!expense.date) return false;
|
||||
const expenseDate = new Date(expense.date);
|
||||
return expenseDate >= cutoffDate;
|
||||
// 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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue