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 ( <>
{error}
} @@ -73,10 +154,33 @@ const ReceiptsPage = () => {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
+ )} +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 && (