From 6d6f59c99a867f9d27ebc620517fd05adbd82e6a Mon Sep 17 00:00:00 2001 From: AmandeepMandal1077 <2023kucp1077@iiitkota.ac.in> Date: Sat, 11 Oct 2025 01:20:54 +0530 Subject: [PATCH 1/3] added editing for ocr extracted receipt --- backend/controllers/receiptController.js | 66 ++++++++--- backend/routes/receiptRoutes.js | 3 +- frontend/src/pages/ReceiptsPage.jsx | 141 +++++++++++++++++++++-- 3 files changed, 188 insertions(+), 22 deletions(-) diff --git a/backend/controllers/receiptController.js b/backend/controllers/receiptController.js index 4ad7c54..21c4fb7 100644 --- a/backend/controllers/receiptController.js +++ b/backend/controllers/receiptController.js @@ -59,19 +59,6 @@ const uploadReceipt = async (req, res) => { const savedReceipt = await newReceipt.save(); - // Automatically create a corresponding expense transaction - if (savedReceipt) { - const newTransaction = new IncomeExpense({ - user: req.user.id, - name: savedReceipt.extractedData.merchant, - category: savedReceipt.extractedData.category, - cost: savedReceipt.extractedData.amount, - addedOn: savedReceipt.extractedData.date, - isIncome: false, - }); - await newTransaction.save(); - } - res.status(201).json(savedReceipt); } catch (error) { @@ -83,6 +70,59 @@ const uploadReceipt = async (req, res) => { } }; +// @desc Save transaction after user confirmation and edits +// @route POST /api/receipts/save-transaction +// @access Private +const saveTransactionFromReceipt = async (req, res) => { + try { + const { receiptId, transactionData } = req.body; + + // Validate required fields + if (!receiptId || !transactionData) { + return res.status(400).json({ message: 'Receipt ID and transaction data are required' }); + } + + // Verify the receipt belongs to the user + const receipt = await Receipt.findOne({ _id: receiptId, user: req.user.id }); + if (!receipt) { + return res.status(404).json({ message: 'Receipt not found' }); + } + + // Create the transaction with user-confirmed data + const newTransaction = new IncomeExpense({ + user: req.user.id, + name: transactionData.name, + category: transactionData.category, + cost: transactionData.cost, + addedOn: new Date(transactionData.addedOn), + isIncome: transactionData.isIncome || false, + }); + + const savedTransaction = await newTransaction.save(); + + // update the receipt with the final confirmed data + receipt.extractedData = { + merchant: transactionData.name, + amount: transactionData.cost, + category: transactionData.category, + date: new Date(transactionData.addedOn), + isIncome: transactionData.isIncome || false, + }; + await receipt.save(); + + res.status(201).json({ + message: 'Transaction saved successfully', + transaction: savedTransaction, + receipt: receipt + }); + + } catch (error) { + console.error('Error saving transaction:', error); + res.status(500).json({ message: 'Failed to save transaction', error: error.message }); + } +}; + module.exports = { uploadReceipt, + saveTransactionFromReceipt, }; \ No newline at end of file diff --git a/backend/routes/receiptRoutes.js b/backend/routes/receiptRoutes.js index 8e192d3..c9849b3 100644 --- a/backend/routes/receiptRoutes.js +++ b/backend/routes/receiptRoutes.js @@ -1,9 +1,10 @@ const express = require('express'); const router = express.Router(); -const { uploadReceipt } = require('../controllers/receiptController'); +const { uploadReceipt, saveTransactionFromReceipt } = require('../controllers/receiptController'); const { protect } = require('../middleware/authMiddleware'); const upload = require('../middleware/uploadMiddleware'); router.post('/upload', protect, upload, uploadReceipt); +router.post('/save-transaction', protect, saveTransactionFromReceipt); module.exports = router; \ No newline at end of file diff --git a/frontend/src/pages/ReceiptsPage.jsx b/frontend/src/pages/ReceiptsPage.jsx index 5b7e8b2..3d2ed5d 100644 --- a/frontend/src/pages/ReceiptsPage.jsx +++ b/frontend/src/pages/ReceiptsPage.jsx @@ -1,6 +1,7 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import api from '../api/axios'; +import TransactionModal from '../components/TransactionModal'; const ReceiptsPage = () => { const [file, setFile] = useState(null); @@ -9,6 +10,25 @@ const ReceiptsPage = () => { const [error, setError] = useState(''); const navigate = useNavigate(); + const [openEditReceiptResult, setOpenEditReceiptResult] = useState(false); + const [categories, setCategories] = useState([]); + const [isEditingResult, setIsEditingResult] = useState(false); + const [isSaving, setIsSaving] = useState(false); + + // Fetch categories when component mounts + useEffect(() => { + const fetchCategories = async () => { + try { + const response = await api.get('/transactions/categories'); + setCategories(response.data); + } catch (error) { + console.error('Failed to fetch categories:', error); + } + }; + + fetchCategories(); + }, []); + const handleFileChange = (e) => { setFile(e.target.files[0]); setReceiptResult(null); @@ -34,8 +54,9 @@ const ReceiptsPage = () => { }, }); setReceiptResult(response.data); - alert('Receipt processed successfully and transaction created! Redirecting to dashboard...'); - navigate('/dashboard'); + + // Open the modal to allow user to edit the extracted data + setOpenEditReceiptResult(true); } catch (err) { setError('Upload failed. Please try again.'); console.error(err); @@ -44,6 +65,66 @@ const ReceiptsPage = () => { } }; + const handleEditReceiptSubmit = async (formData, transactionId) => { + try { + // Update the receiptResult with the edited data + const updatedReceiptResult = { + ...receiptResult, + extractedData: { + merchant: formData.name, + amount: parseFloat(formData.cost), + category: formData.category, + date: formData.addedOn, + isIncome: formData.isIncome + } + }; + + setReceiptResult(updatedReceiptResult); + setOpenEditReceiptResult(false); + } catch (err) { + setError('Failed to update receipt data. Please try again.'); + console.error(err); + } + }; + + // Handle final save to database (second verification step) + const handleFinalSave = async () => { + try { + setIsSaving(true); + + const transactionData = { + name: receiptResult.extractedData.merchant, + category: receiptResult.extractedData.category, + cost: receiptResult.extractedData.amount, + addedOn: receiptResult.extractedData.date, + isIncome: receiptResult.extractedData.isIncome || false + }; + + const response = await api.post('/receipts/save-transaction', { + receiptId: receiptResult._id, + transactionData: transactionData + }); + + alert('Transaction saved successfully! Redirecting to dashboard...'); + navigate('/dashboard'); + } catch (err) { + setError('Failed to save transaction. Please try again.'); + console.error(err); + } finally { + setIsSaving(false); + } + }; + + // Handle edit button in result div + const handleEditResult = () => { + setIsEditingResult(true); + setOpenEditReceiptResult(true); + }; + + const handleNewCategory = (newCategory) => { + setCategories(prev => [...prev, newCategory].sort()); + }; + return ( <>

Upload Receipt

@@ -63,7 +144,7 @@ const ReceiptsPage = () => { disabled={uploading} className="mt-4 w-full px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-blue-300" > - {uploading ? 'Processing...' : 'Upload & Create Transaction'} + {uploading ? 'Processing...' : 'Upload & Extract Data'} {error &&

{error}

} @@ -73,10 +154,33 @@ const ReceiptsPage = () => {

Last Upload Result

{receiptResult ? (
-

Merchant: {receiptResult.extractedData.merchant}

-

Amount: {receiptResult.extractedData.amount.toFixed(2)}

-

Category: {receiptResult.extractedData.category}

-

Date: {new Date(receiptResult.extractedData.date).toLocaleDateString()}

+
+

Merchant: {receiptResult.extractedData.merchant}

+

Amount: {receiptResult.extractedData.amount.toFixed(2)}

+

Category: {receiptResult.extractedData.category}

+

Date: {new Date(receiptResult.extractedData.date).toLocaleDateString()}

+ {receiptResult.extractedData.isIncome && ( +

Income: Yes

+ )} +
+ +
+ + +
+ Uploaded Receipt
) : ( @@ -85,6 +189,27 @@ const ReceiptsPage = () => { + + {/* Transaction Modal for editing receipt data */} + {openEditReceiptResult && receiptResult && ( + { + setOpenEditReceiptResult(false); + setIsEditingResult(false); + }} + onSubmit={handleEditReceiptSubmit} + transaction={{ + name: receiptResult.extractedData.merchant || '', + category: receiptResult.extractedData.category || '', + cost: receiptResult.extractedData.amount || 0, + addedOn: receiptResult.extractedData.date || new Date().toISOString().split('T')[0], + isIncome: receiptResult.extractedData.isIncome || false + }} + categories={categories} + onNewCategory={handleNewCategory} + /> + )} ); }; From 3c2693bcd55d800166699449afbcdb16c88ad6fd Mon Sep 17 00:00:00 2001 From: AmandeepMandal1077 <2023kucp1077@iiitkota.ac.in> Date: Sat, 11 Oct 2025 01:33:09 +0530 Subject: [PATCH 2/3] removed unused parameter --- backend/controllers/receiptController.js | 2 +- frontend/src/pages/ReceiptsPage.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/controllers/receiptController.js b/backend/controllers/receiptController.js index 21c4fb7..7ccbedd 100644 --- a/backend/controllers/receiptController.js +++ b/backend/controllers/receiptController.js @@ -100,7 +100,7 @@ const saveTransactionFromReceipt = async (req, res) => { const savedTransaction = await newTransaction.save(); - // update the receipt with the final confirmed data + // Update the receipt with the final confirmed data receipt.extractedData = { merchant: transactionData.name, amount: transactionData.cost, diff --git a/frontend/src/pages/ReceiptsPage.jsx b/frontend/src/pages/ReceiptsPage.jsx index 3d2ed5d..f498479 100644 --- a/frontend/src/pages/ReceiptsPage.jsx +++ b/frontend/src/pages/ReceiptsPage.jsx @@ -65,7 +65,7 @@ const ReceiptsPage = () => { } }; - const handleEditReceiptSubmit = async (formData, transactionId) => { + const handleEditReceiptSubmit = async (formData) => { try { // Update the receiptResult with the edited data const updatedReceiptResult = { From d56bee115476a888a053344101a1000067398b16 Mon Sep 17 00:00:00 2001 From: AmandeepMandal1077 <2023kucp1077@iiitkota.ac.in> Date: Wed, 22 Oct 2025 22:02:53 +0530 Subject: [PATCH 3/3] fix: resolved issues --- backend/controllers/receiptController.js | 10 +++++-- frontend/src/pages/ReceiptsPage.jsx | 37 ++++++++++-------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/backend/controllers/receiptController.js b/backend/controllers/receiptController.js index 71f6139..42c63ae 100644 --- a/backend/controllers/receiptController.js +++ b/backend/controllers/receiptController.js @@ -108,13 +108,19 @@ const saveTransactionFromReceipt = async (req, res) => { return res.status(404).json({ message: 'Receipt not found' }); } + // Validate and parse the date + const transactionDate = new Date(transactionData.addedOn); + if (isNaN(transactionDate.getTime())) { + return res.status(400).json({ message: 'Invalid date format provided' }); + } + // Create the transaction with user-confirmed data const newTransaction = new IncomeExpense({ user: req.user.id, name: transactionData.name, category: transactionData.category, cost: transactionData.cost, - addedOn: new Date(transactionData.addedOn), + addedOn: transactionDate, isIncome: transactionData.isIncome || false, }); @@ -125,7 +131,7 @@ const saveTransactionFromReceipt = async (req, res) => { merchant: transactionData.name, amount: transactionData.cost, category: transactionData.category, - date: new Date(transactionData.addedOn), + date: transactionDate, isIncome: transactionData.isIncome || false, }; await receipt.save(); diff --git a/frontend/src/pages/ReceiptsPage.jsx b/frontend/src/pages/ReceiptsPage.jsx index f498479..c969a19 100644 --- a/frontend/src/pages/ReceiptsPage.jsx +++ b/frontend/src/pages/ReceiptsPage.jsx @@ -65,26 +65,21 @@ const ReceiptsPage = () => { } }; - const handleEditReceiptSubmit = async (formData) => { - try { - // Update the receiptResult with the edited data - const updatedReceiptResult = { - ...receiptResult, - extractedData: { - merchant: formData.name, - amount: parseFloat(formData.cost), - category: formData.category, - date: formData.addedOn, - isIncome: formData.isIncome - } - }; - - setReceiptResult(updatedReceiptResult); - setOpenEditReceiptResult(false); - } catch (err) { - setError('Failed to update receipt data. Please try again.'); - console.error(err); - } + const handleEditReceiptSubmit = (formData) => { + // Update the receiptResult with the edited data + const updatedReceiptResult = { + ...receiptResult, + extractedData: { + merchant: formData.name, + amount: parseFloat(formData.cost) || 0, + category: formData.category, + date: formData.addedOn, + isIncome: formData.isIncome + } + }; + + setReceiptResult(updatedReceiptResult); + setOpenEditReceiptResult(false); }; // Handle final save to database (second verification step) @@ -156,7 +151,7 @@ const ReceiptsPage = () => {

Merchant: {receiptResult.extractedData.merchant}

-

Amount: {receiptResult.extractedData.amount.toFixed(2)}

+

Amount: {(parseFloat(receiptResult.extractedData.amount) || 0).toFixed(2)}

Category: {receiptResult.extractedData.category}

Date: {new Date(receiptResult.extractedData.date).toLocaleDateString()}

{receiptResult.extractedData.isIncome && (