OHO-12(Partial Sorted)

This commit is contained in:
lokeshramchand-ctrl 2025-08-20 10:37:44 +05:30
parent 594e8b6353
commit dc64841256
5 changed files with 155 additions and 38 deletions

View file

@ -30,11 +30,20 @@ const User = mongoose.model('User', userSchema);
const transactionSchema = new mongoose.Schema({
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
description: String,
amount: Number,
category: String,
description: { type: String, required: true },
amount: { type: Number, required: true },
category: { type: String, default: "Other" },
date: { type: Date, default: Date.now },
type: { type: String, default: "unknown" },
vendor: String, // Optional vendor/store name
source: { type: String, enum: ['manual', 'voice', 'email'], required: true },
referenceNumber: { type: String, unique: true, sparse: true }, // Unique transaction id for deduplication
});
const Transaction = mongoose.model('Transaction', transactionSchema);
/* ---------- SESSION ---------- */
@ -164,7 +173,8 @@ app.post('/api/transaction/add', async (req, res) => {
userId,
description,
amount,
category
category,
source: 'manual',
});
await newTransaction.save();
@ -230,6 +240,7 @@ app.post('/api/transactions/voice', async (req, res) => {
description,
amount,
category,
source: 'voice'
});
await newTransaction.save();
res.status(200).json({
@ -371,6 +382,14 @@ app.post('/api/sync-gmail', async (req, res) => {
console.warn('⚠️ Parsed email missing amount, skipping:', parsed);
continue;
}
// Extract unique transaction reference number from snippet
let referenceNumber = null;
const refMatch = email.snippet.match(/(?:reference number is|Ref\s*[:No]*\s*)([A-Za-z0-9]+)/i);
if (refMatch) {
referenceNumber = refMatch[1];
}
let category = 'Other';
try {
const predictRes = await axios.post('http://10.231.8.114:5000/api/predict', {
@ -395,6 +414,28 @@ app.post('/api/sync-gmail', async (req, res) => {
const dateValue = parseDateString(parsed.date) || new Date();
try {
if (referenceNumber) {
// Upsert transaction based on unique referenceNumber for emails, mark source as 'email'
const updatedTxn = await Transaction.findOneAndUpdate(
{ referenceNumber }, // search criteria for deduplication
{
userId,
description: parsed.vendor || "Unknown Vendor",
amount: parsed.amount,
type: parsed.type || "unknown",
date: dateValue,
vendor: parsed.vendor,
category,
source: "email", // mark source explicitly
bank: email.from,
referenceNumber,
},
{ upsert: true, new: true, setDefaultsOnInsert: true }
);
savedTxns.push(updatedTxn);
} else {
// No reference number available, just save normally; source still 'email'
const newTxn = new Transaction({
userId,
description: parsed.vendor || "Unknown Vendor",
@ -403,17 +444,16 @@ const newTxn = new Transaction({
date: dateValue,
vendor: parsed.vendor,
category,
source: "email",
source: "email", // mark source explicitly
bank: email.from,
});
try {
await newTxn.save();
savedTxns.push(newTxn);
} catch (saveError) {
console.error('❌ Error saving transaction to database:', saveError, 'Transaction:', newTxn);
}
} catch (saveError) {
console.error('❌ Error saving transaction to database:', saveError);
}
}
return res.json({
@ -421,14 +461,12 @@ const newTxn = new Transaction({
count: savedTxns.length,
transactions: savedTxns,
});
} catch (error) {
console.error('❌ Unexpected error in /api/sync-gmail:', error);
return res.status(500).json({ error: 'Failed to fetch and save Gmail messages' });
}
});
/* ---------- SERVER ---------- */
mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,

70
Backend/testing.js Normal file
View file

@ -0,0 +1,70 @@
const testEmails = [
`Dear Customer,
Rs.60.00 has been debited from account 5488 to VPA pavithramart.63434836@hdfcbank PAVITHRA MART on 24-07-25.
Your UPI transaction reference number is 108642900372.
If you did not authorize this transaction, please report it immediately by calling 18002586161 Or SMS BLOCK UPI to 7308080808.
Warm Regards,
HDFC Bank`,
`Rs.75.00 has been debited to VPA pavithramart.63434836@hdfcbank PAVITHRA MART on 01-08-25.
Your UPI transaction reference number is 109128631655.`,
`Rs.299.00 credited to account 1234 from AmazonMarketplace on 19-08-25.
UPI reference number is 123456789012.`,
`INR 150.00 debited at STARBUCKS on 20-08-25.
Ref No: 9876543210`,
];
function parseBankMessage(snippet) {
const result = {
amount: null,
vendor: null,
type: null,
date: null,
referenceNumber: null,
};
// Amount
const amtMatch = snippet.match(/(?:Rs\.?|INR)\s*([\d,]+\.?\d*)/i);
if (amtMatch) result.amount = parseFloat(amtMatch[1].replace(/,/g, ""));
// Debit / Credit
if (/debited/i.test(snippet)) result.type = "debit";
else if (/credited/i.test(snippet)) result.type = "credit";
// Date
const dateMatch = snippet.match(/on\s+(\d{2}[-/]\d{2}[-/]\d{2,4})/i);
if (dateMatch) result.date = dateMatch[1];
// Reference Number (flexible regex)
const refMatch = snippet.match(/(?:reference number is|UPI reference number is|Ref No:?)\s*([A-Za-z0-9]+)/i);
if (refMatch) result.referenceNumber = refMatch[1].trim();
// Vendor extraction - remove emails, pick last uppercase phrase
let vendorCandidate = snippet.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, '').trim();
const vendorWords = vendorCandidate.match(/(?:\b[A-Z]{2,}\b(?:\s)?)+/g);
if (vendorWords && vendorWords.length > 0) {
result.vendor = vendorWords[vendorWords.length - 1].trim();
} else {
// Fallback: extract after "to " or "at "
const toMatch = snippet.match(/to\s+([A-Za-z0-9\s&.-]+)/i);
if (toMatch) {
let v = toMatch[1].trim();
v = v.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, '').trim();
v = v.replace(/\s{2,}/g, ' ');
result.vendor = v;
}
}
return result;
}
console.log("=== Bank Email Regex Test ===");
testEmails.forEach((email, idx) => {
console.log(`\n-- Email ${idx + 1} --`);
console.log(email);
const parsed = parseBankMessage(email);
console.log("Parsed Result:", parsed);
});

View file

@ -2,6 +2,7 @@
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:monarch/main_pages/HomePage/homepage.dart';
import 'dart:convert';
import 'package:monarch/other_pages/enviroment.dart';
@ -60,7 +61,10 @@ class _AutoSyncEmailsScreenState extends State<AutoSyncEmailsScreen> {
// Optional: navigate back or to another screen after a delay
Future.delayed(const Duration(seconds: 2), () {
Navigator.pop(context, true);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => const FinTrackHomePage()),
);
});
} else {
setState(() {

View file

@ -36,7 +36,10 @@ class _EmailsScreenState extends State<EmailsScreen> {
final res = await http.post(
uri,
headers: {"Content-Type": "application/json"},
body: jsonEncode({"accessToken": widget.accessToken}),
body: jsonEncode({
"accessToken": widget.accessToken,
"userId": widget.userId, // Added userId here
}),
);
if (res.statusCode == 200) {

View file

@ -116,6 +116,7 @@ import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:http/http.dart' as http;
import 'package:monarch/emai_screen.dart';
import 'package:monarch/email.dart';
//import 'package:monarch/email.dart';
import 'package:monarch/other_pages/enviroment.dart';
import 'dart:convert';
@ -181,7 +182,8 @@ class _LoginScreenState extends State<LoginScreen> {
Navigator.pushReplacement(
context,
MaterialPageRoute(
// builder: (_) => EmailsScreen(
// builder:
// (_) => EmailsScreen(
// accessToken: accessToken,
// userId: data['user']['_id'],
// ),