From 22799cb732c0f834e2e63b6df4b221008754bfb3 Mon Sep 17 00:00:00 2001 From: dlawler489 <104159223@student.swin.edu.au> Date: Tue, 5 May 2026 12:29:07 +1000 Subject: [PATCH] Integrate Data Import with Expenses tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✅ FIXES MAJOR WORKFLOW ISSUE: - Data Import CSV processing now automatically creates expenses - Etsy fees → 'Transaction Fees' category expenses - Australia Post shipping → 'Shipping & Postage' expenses - Prevents duplicate expense creation with reference checking - Added user notices in both Data Import and Expenses tabs 🔄 INTEGRATION FEATURES: - Automatic expense creation from CSV order costs - Duplicate prevention by order number/tracking number - Proper categorization and tax-deductible flagging - Clear user notifications and workflow guidance 📊 USER EXPERIENCE: - Expenses tab now shows integration notice - Data Import shows automatic expense creation info - Seamless workflow between sales data and expense tracking --- DEPLOYMENT_STATUS.md | 25 ++++++- client/src/pages/DataImport.tsx | 120 ++++++++++++++++++++++++++++++++ client/src/pages/Expenses.tsx | 25 +++++++ 3 files changed, 169 insertions(+), 1 deletion(-) diff --git a/DEPLOYMENT_STATUS.md b/DEPLOYMENT_STATUS.md index 5f08ed9..d5b00bb 100644 --- a/DEPLOYMENT_STATUS.md +++ b/DEPLOYMENT_STATUS.md @@ -5,7 +5,7 @@ **Repository**: https://github.com/dlawler489/etsy-finance-tracker **Status**: ✅ **LIVE IN PRODUCTION ON MAC MINI** **Deployment Method**: `docker-compose.deploy.yml` (GitHub Container Registry) -**Last Updated**: May 1, 2026 +**Last Updated**: May 1, 2026 - **Modal Bug Fix Deployed** **Confirmed Working**: ✅ Successfully deployed and operational ### 🏭 **Production Environment Details** @@ -97,6 +97,29 @@ Your Etsy Finance Tracker is now **live and running in production** with all cor ### For Command Line Users +## 🔄 **Latest Updates - Modal Bug Fix Deployment** + +### **May 1, 2026 - Critical Modal Fix** ✅ **COMMITTED & BUILDING** +- 🐛 **Issue Fixed**: Modal persistence preventing proper closure after product creation +- ⚡ **Enhancement**: Added duplicate prevention with `isProcessing` state +- 🎛️ **Improvement**: Loading spinner and disabled states during processing +- 🔄 **Workflow**: Enhanced modal closure logic in `DataImport.tsx` +- 🛡️ **Prevention**: Proper state reset on modal close to prevent duplicates + +**Deployment Status**: +- ✅ **Code Committed**: Pushed to main branch with commit `f39d4ca` +- 🏗️ **GitHub Action**: Automatically building Docker images in CI/CD pipeline +- 📦 **Container Registry**: New images will be available at `ghcr.io/dlawler489/etsy-finance-tracker:latest` + +**To Deploy Latest Fix**: +```bash +# Pull latest code and redeploy +git pull origin main +docker compose -f docker-compose.deploy.yml pull +docker compose -f docker-compose.deploy.yml down +docker compose -f docker-compose.deploy.yml up -d +``` + 1. **If Docker is available**: ```bash docker compose -f docker-compose.simple.yml up --build diff --git a/client/src/pages/DataImport.tsx b/client/src/pages/DataImport.tsx index cb2848f..0b3aecc 100644 --- a/client/src/pages/DataImport.tsx +++ b/client/src/pages/DataImport.tsx @@ -7,6 +7,7 @@ import { DataManager } from '../utils/dataManager'; import { useDispatch, useSelector } from 'react-redux'; import { RootState } from '../store'; import { addOrder, updateOrder, setOrders } from '../store/slices/orderSlice'; +import { addExpenses } from '../store/slices/expenseSlice'; import { Upload, FileText, Package, Truck, Trash2 } from 'lucide-react'; import toast from 'react-hot-toast'; import { dateTestResults } from '../utils/testDateParsing'; @@ -110,6 +111,93 @@ export default function DataImport() { }); }; + // Create expenses from CSV import data + const createExpensesFromCsvData = async ( + orderCosts: ImportResults['orderCosts'], + shippingRecords: ParsedShippingRecord[] + ) => { + try { + // Get existing expenses to avoid duplicates + const existingExpensesRes = await api.get('/expenses?limit=1000'); + const existingExpenses = existingExpensesRes.data.expenses || []; + + const expensesToCreate: any[] = []; + + // Create Etsy fee expenses (check for duplicates by order number) + orderCosts.forEach(order => { + if (order.etsyFees > 0) { + const isDuplicate = existingExpenses.some((expense: any) => + expense.reference === order.orderNumber && + expense.vendor === 'Etsy' && + expense.category === 'Transaction Fees' + ); + + if (!isDuplicate) { + expensesToCreate.push({ + description: `Etsy Fees - Order #${order.orderNumber}`, + amount: order.etsyFees, + category: 'Transaction Fees', + date: order.date, + taxDeductible: true, + vendor: 'Etsy', + reference: order.orderNumber + }); + } + } + }); + + // Create shipping expenses (check for duplicates by tracking number) + shippingRecords.forEach(shipping => { + if (shipping.totalCost > 0) { + const isDuplicate = existingExpenses.some((expense: any) => + expense.reference === shipping.trackingNumber && + expense.vendor === 'Australia Post' + ); + + if (!isDuplicate) { + expensesToCreate.push({ + description: `Australia Post - ${shipping.trackingNumber}`, + amount: shipping.totalCost, + category: 'Shipping & Postage', + date: shipping.date, + taxDeductible: true, + vendor: 'Australia Post', + reference: shipping.trackingNumber + }); + } + } + }); + + // Save expenses to database + if (expensesToCreate.length > 0) { + const savedExpenses = []; + let created = 0; + + for (const expense of expensesToCreate) { + try { + const res = await api.post('/expenses', expense); + savedExpenses.push(res.data); + created++; + } catch (error) { + console.error('Failed to create expense:', expense, error); + } + } + + if (savedExpenses.length > 0) { + dispatch(addExpenses(savedExpenses)); + toast.success(`Created ${created} expenses from CSV data (Etsy fees + shipping costs)`); + } else if (expensesToCreate.length === 0) { + toast.success('All expenses from CSV data already exist - no duplicates created'); + } + } else { + toast.success('No new expenses to create from CSV data'); + } + } catch (error) { + console.error('Error creating expenses:', error); + toast.error('Failed to create expenses from CSV data'); + } + }; + const processCsvFiles = async () => { if (!etsyFile) { setError('Please select an Etsy statement CSV file'); @@ -179,6 +267,10 @@ export default function DataImport() { // Reload orders from API after bulk upsert const ordersRes = await api.get('/orders?limit=1000'); dispatch(setOrders(ordersRes.data.orders)); + + // Create expenses from CSV data + await createExpensesFromCsvData(orderCosts, shippingRecords); + toast.success(`CSV imported! Created ${res.data.created} new orders and updated ${res.data.updated} existing orders.`); } catch { toast.error('Failed to save orders to database'); @@ -658,6 +750,34 @@ export default function DataImport() { + {/* Automatic Expense Creation Notice */} +
+ When you process CSV files, expenses will be automatically created in the Expenses tab: +
++ This ensures your profit analysis matches your expense tracking automatically! +
++ Etsy fees and Australia Post shipping costs are automatically created as expenses when you process CSV files in the{' '} + Data Import section. +
++ Manual CSV imports here are for additional expenses not captured in your sales data. +
+