feat: Add API endpoint for managing categories

This commit is contained in:
2026-07-03 08:33:48 +05:30
parent be6c95c497
commit 8488e3bdd0
15 changed files with 413 additions and 48 deletions

35
app/api/v1/categories.py Normal file
View File

@@ -0,0 +1,35 @@
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status
from app.core.dependencies import get_category_service
from app.core.exceptions import CategoryAlreadyExistsException
from app.schemas.category import CategoryCreate, CategoryResponse
from app.services.category_service import CategoryService
router = APIRouter(prefix="/categories", tags=["categories"])
@router.get("", response_model=List[CategoryResponse])
async def list_categories(
service: CategoryService = Depends(get_category_service),
):
"""
Returns the list of all available categories ordered alphabetically.
"""
return await service.list_categories()
@router.post("", response_model=CategoryResponse, status_code=status.HTTP_201_CREATED)
async def create_category(
category_in: CategoryCreate,
service: CategoryService = Depends(get_category_service),
):
"""
Creates a new custom category. The name must be unique.
"""
try:
return await service.create_category(name=category_in.name)
except CategoryAlreadyExistsException as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
)

View File

@@ -3,6 +3,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.db.session import get_db
from app.repositories.job_repository import JobRepository
from app.services.job_service import JobService
from app.services.category_service import CategoryService
def get_job_repository(db: AsyncSession = Depends(get_db)) -> JobRepository:
@@ -13,3 +14,8 @@ def get_job_repository(db: AsyncSession = Depends(get_db)) -> JobRepository:
def get_job_service(repo: JobRepository = Depends(get_job_repository)) -> JobService:
"""Dependency to retrieve JobService"""
return JobService(repo)
def get_category_service(db: AsyncSession = Depends(get_db)) -> CategoryService:
"""Dependency to retrieve CategoryService"""
return CategoryService(db)

View File

@@ -18,3 +18,11 @@ class InvalidCSVException(AlemnoException):
def __init__(self, detail: str):
self.detail = detail
super().__init__(detail)
class CategoryAlreadyExistsException(AlemnoException):
"""Raised when a category already exists"""
def __init__(self, name: str):
self.name = name
super().__init__(f"Category '{name}' already exists.")

View File

@@ -2,5 +2,6 @@ from app.db.base import Base
from app.db.models.job import Job
from app.db.models.transaction import Transaction
from app.db.models.job_summary import JobSummary
from app.db.models.category import Category
__all__ = ["Base", "Job", "Transaction", "JobSummary"]
__all__ = ["Base", "Job", "Transaction", "JobSummary", "Category"]

10
app/db/models/category.py Normal file
View File

@@ -0,0 +1,10 @@
from sqlalchemy import Integer, String
from sqlalchemy.orm import Mapped, mapped_column
from app.db.base import Base
class Category(Base):
__tablename__ = "categories"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(100), unique=True, nullable=False)

View File

@@ -2,6 +2,7 @@ from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api.v1.jobs import router as jobs_router
from app.api.v1.categories import router as categories_router
from app.core.config import settings
from app.db.base import Base
from app.db.session import engine
@@ -12,6 +13,28 @@ async def lifespan(app: FastAPI):
# Startup: Create tables if they don't exist
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
# Seed default categories if empty
from app.db.session import AsyncSessionLocal
from app.db.models.category import Category
from sqlalchemy import select
async with AsyncSessionLocal() as session:
result = await session.execute(select(Category))
if not result.scalars().first():
defaults = [
Category(name="Food"),
Category(name="Shopping"),
Category(name="Travel"),
Category(name="Transport"),
Category(name="Utilities"),
Category(name="Cash Withdrawal"),
Category(name="Entertainment"),
Category(name="Other"),
]
session.add_all(defaults)
await session.commit()
yield
# Shutdown: Clean up pool
await engine.dispose()
@@ -32,8 +55,9 @@ app.add_middleware(
allow_headers=["*"],
)
# Include API Router
# Include API Routers
app.include_router(jobs_router, prefix=settings.API_V1_STR)
app.include_router(categories_router, prefix=settings.API_V1_STR)
# Root healthcheck endpoint

View File

@@ -0,0 +1,28 @@
from typing import List, Optional
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.models.category import Category
class CategoryRepository:
def __init__(self, db: AsyncSession):
self.db = db
async def list_all(self) -> List[Category]:
"""List all category records ordered alphabetically by name"""
query = select(Category).order_by(Category.name.asc())
result = await self.db.execute(query)
return list(result.scalars().all())
async def create(self, name: str) -> Category:
"""Create a new category record"""
category = Category(name=name)
self.db.add(category)
await self.db.flush()
return category
async def get_by_name(self, name: str) -> Optional[Category]:
"""Fetch a category record by name (case-insensitive)"""
query = select(Category).where(Category.name.ilike(name))
result = await self.db.execute(query)
return result.scalar_one_or_none()

17
app/schemas/category.py Normal file
View File

@@ -0,0 +1,17 @@
from pydantic import BaseModel, Field, ConfigDict
class CategoryBase(BaseModel):
name: str = Field(
..., description="Unique category name", min_length=1, max_length=100
)
class CategoryCreate(CategoryBase):
pass
class CategoryResponse(CategoryBase):
id: int
model_config = ConfigDict(from_attributes=True)

View File

@@ -0,0 +1,26 @@
from typing import List
from sqlalchemy.ext.asyncio import AsyncSession
from app.repositories.category_repository import CategoryRepository
from app.db.models.category import Category
from app.core.exceptions import CategoryAlreadyExistsException
class CategoryService:
def __init__(self, db: AsyncSession):
self.db = db
self.repo = CategoryRepository(db)
async def list_categories(self) -> List[Category]:
"""Returns all categories alphabetical list"""
return await self.repo.list_all()
async def create_category(self, name: str) -> Category:
"""Creates a new category, validating uniqueness first"""
name_clean = name.strip()
existing = await self.repo.get_by_name(name_clean)
if existing:
raise CategoryAlreadyExistsException(name_clean)
category = await self.repo.create(name_clean)
await self.db.commit()
return category

View File

@@ -83,6 +83,7 @@ def parse_and_validate_csv(content: bytes) -> List[Dict[str, Any]]:
# Auto-generate txn_id if missing or empty
if not txn_id or not txn_id.strip():
import uuid
txn_id = f"GEN_{uuid.uuid4().hex[:8].upper()}"
else:
txn_id = txn_id.strip()

View File

@@ -24,7 +24,7 @@ class TransactionClassification(BaseModel):
description="The unique transaction ID (txn_id) from the input."
)
category: str = Field(
description="Assigned category, must be one of: Food, Shopping, Travel, Transport, Utilities, Cash Withdrawal, Entertainment, or Other."
description="Assigned category matching one of the allowed categories list."
)
@@ -35,6 +35,7 @@ class BatchClassificationResponse(BaseModel):
async def classify_transactions_batch(
client: genai.Client,
batch_txns: List[Dict[str, Any]],
allowed_categories: List[str],
) -> Dict[str, Dict[str, Any]]:
"""
Calls the Gemini model to classify a batch of transactions.
@@ -55,8 +56,7 @@ async def classify_transactions_batch(
input_prompt = (
"Classify the following financial transactions. For each transaction, match its txn_id "
"and assign one of the allowed categories: Food, Shopping, Travel, Transport, Utilities, "
"Cash Withdrawal, Entertainment, or Other based on the merchant name and details:\n\n"
f"and assign one of the allowed categories: {', '.join(allowed_categories)} based on the merchant name and details:\n\n"
f"{json.dumps(prompt_data, indent=2)}"
)
@@ -146,6 +146,9 @@ def process_transaction_job(job_id: str, transactions: List[Dict[str, Any]]):
async def _process_job_async(job_id: str, transactions: List[Dict[str, Any]]):
from app.db.session import engine
await engine.dispose()
async with AsyncSessionLocal() as db:
repo = JobRepository(db)
job = await repo.get_by_id(job_id)
@@ -215,6 +218,28 @@ async def _process_job_async(job_id: str, transactions: List[Dict[str, Any]]):
llm_results = {}
api_key = settings.GEMINI_API_KEY or os.environ.get("GEMINI_API_KEY")
if uncategorized_txns:
# Load allowed categories from the database categories table
from app.db.models.category import Category
try:
res_cats = await db.execute(select(Category.name))
allowed_categories = [r[0] for r in res_cats.all()]
except Exception as db_err:
logger.warning(f"Could not load categories from database: {db_err}")
allowed_categories = []
if not allowed_categories:
allowed_categories = [
"Food",
"Shopping",
"Travel",
"Transport",
"Utilities",
"Cash Withdrawal",
"Entertainment",
"Other",
]
if api_key:
logger.info(
f"Found {len(uncategorized_txns)} uncategorized transactions. Initializing GenAI client..."
@@ -226,7 +251,7 @@ async def _process_job_async(job_id: str, transactions: List[Dict[str, Any]]):
for i in range(0, len(uncategorized_txns), batch_size):
batch = uncategorized_txns[i : i + batch_size]
batch_results = await classify_transactions_batch(
client, batch
client, batch, allowed_categories
)
llm_results.update(batch_results)
except Exception as e:
@@ -247,58 +272,92 @@ async def _process_job_async(job_id: str, transactions: List[Dict[str, Any]]):
# Mock categorization mapping based on merchant name keywords
for t in uncategorized_txns:
merchant_lower = t["merchant"].lower()
cat = "Other"
if any(
kw in merchant_lower
for kw in ["swiggy", "zomato", "restaurant", "cafe", "food"]
cat = (
"Other"
if "Other" in allowed_categories
else (
allowed_categories[0] if allowed_categories else "Other"
)
)
if (
any(
kw in merchant_lower
for kw in [
"swiggy",
"zomato",
"restaurant",
"cafe",
"food",
]
)
and "Food" in allowed_categories
):
cat = "Food"
elif any(
kw in merchant_lower
for kw in ["ola", "uber", "taxi", "metro", "train"]
elif (
any(
kw in merchant_lower
for kw in ["ola", "uber", "taxi", "metro", "train"]
)
and "Transport" in allowed_categories
):
cat = "Transport"
elif any(
kw in merchant_lower
for kw in [
"amazon",
"flipkart",
"shopping",
"store",
"supermarket",
]
elif (
any(
kw in merchant_lower
for kw in [
"amazon",
"flipkart",
"shopping",
"store",
"supermarket",
]
)
and "Shopping" in allowed_categories
):
cat = "Shopping"
elif any(
kw in merchant_lower
for kw in ["air", "flight", "hotel", "travel", "irctc"]
elif (
any(
kw in merchant_lower
for kw in ["air", "flight", "hotel", "travel", "irctc"]
)
and "Travel" in allowed_categories
):
cat = "Travel"
elif any(
kw in merchant_lower
for kw in [
"electric",
"power",
"water",
"bill",
"utility",
"utilities",
]
elif (
any(
kw in merchant_lower
for kw in [
"electric",
"power",
"water",
"bill",
"utility",
"utilities",
]
)
and "Utilities" in allowed_categories
):
cat = "Utilities"
elif any(
kw in merchant_lower for kw in ["atm", "cash", "withdrawal"]
elif (
any(
kw in merchant_lower
for kw in ["atm", "cash", "withdrawal"]
)
and "Cash Withdrawal" in allowed_categories
):
cat = "Cash Withdrawal"
elif any(
kw in merchant_lower
for kw in [
"netflix",
"spotify",
"movie",
"show",
"entertainment",
]
elif (
any(
kw in merchant_lower
for kw in [
"netflix",
"spotify",
"movie",
"show",
"entertainment",
]
)
and "Entertainment" in allowed_categories
):
cat = "Entertainment"

View File

@@ -0,0 +1,32 @@
import pytest
from httpx import AsyncClient
pytestmark = pytest.mark.asyncio
async def test_get_categories_seeded_defaults(client: AsyncClient):
# Retrieve categories and verify the default list is present (since lifespan seeds them)
response = await client.get("/api/v1/categories")
assert response.status_code == 200
categories = response.json()
assert len(categories) >= 8
names = [c["name"] for c in categories]
assert "Food" in names
assert "Shopping" in names
assert "Travel" in names
assert "Other" in names
async def test_create_and_duplicate_category(client: AsyncClient):
# 1. Create a new category
response = await client.post("/api/v1/categories", json={"name": "Medical"})
assert response.status_code == 201
data = response.json()
assert "id" in data
assert data["name"] == "Medical"
# 2. Try creating the same category (duplicate, case-insensitive check)
dup_response = await client.post("/api/v1/categories", json={"name": "medical"})
assert dup_response.status_code == 400
assert "already exists" in dup_response.json()["detail"]

View File

@@ -46,6 +46,24 @@ def initialize_test_db():
await conn.run_sync(Base.metadata.drop_all)
await conn.run_sync(Base.metadata.create_all)
# Seed categories in test DB
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.models.category import Category
async with AsyncSession(test_engine) as session:
defaults = [
Category(name="Food"),
Category(name="Shopping"),
Category(name="Travel"),
Category(name="Transport"),
Category(name="Utilities"),
Category(name="Cash Withdrawal"),
Category(name="Entertainment"),
Category(name="Other"),
]
session.add_all(defaults)
await session.commit()
async def teardown_db():
async with test_engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)

View File

@@ -18,7 +18,9 @@ async def test_classify_transactions_batch_retry_success():
{"txn_id": "TX2", "merchant": "Amazon", "amount": 1200.0},
]
results = await classify_transactions_batch(mock_client, batch_txns)
results = await classify_transactions_batch(
mock_client, batch_txns, ["Food", "Shopping", "Other"]
)
assert len(results) == 2
assert results["TX1"]["category"] == "Food"
assert results["TX1"]["failed"] is False
@@ -37,7 +39,9 @@ async def test_classify_transactions_batch_retry_failure():
# Patch sleep to make retry test run instantly
with patch("asyncio.sleep", return_value=None):
results = await classify_transactions_batch(mock_client, batch_txns)
results = await classify_transactions_batch(
mock_client, batch_txns, ["Food", "Shopping", "Other"]
)
assert len(results) == 1
assert results["TX1"]["category"] == "Other"

96
transactions.csv Normal file
View File

@@ -0,0 +1,96 @@
txn_id,date,merchant,amount,currency,status,category,account_id,notes
TXN1065,04-09-2024,Flipkart,10882.55,INR,SUCCESS,Shopping,ACC003,Refund expected
TXN1054,2024/02/05,Swiggy,$11325.79,INR,success,Food,ACC004,
TXN1021,17-02-2024,Zomato,2536.35,USD,SUCCESS,Food,ACC001,Verified
TXN1045,07-05-2024,Amazon,6874.1,INR,failed,Shopping,ACC004,SUSPICIOUS
TXN1076,14-08-2024,Flipkart,2763.26,INR,SUCCESS,Shopping,ACC004,Duplicate?
TXN1029,19-05-2024,Flipkart,9092.21,INR,FAILED,Shopping,ACC003,
TXN1004,04-07-2024,Jio Recharge,2924.71,INR,SUCCESS,Utilities,ACC001,Verified
TXN1002,2024/01/04,HDFC ATM,10487.18,INR,FAILED,Cash Withdrawal,ACC004,
TXN1000,23-11-2024,Dentist Appointment Fee,423.91,INR,failed,,ACC004,
TXN1073,16-05-2024,HDFC ATM,1117.58,INR,failed,Cash Withdrawal,ACC004,
TXN1019,26-11-2024,Amazon,956.16,INR,success,Shopping,ACC004,Refund expected
TXN1006,24-03-2024,IRCTC,5722.86,INR,FAILED,Travel,ACC001,Refund expected
TXN1014,2024/09/05,Amazon,14670.87,INR,failed,Shopping,ACC005,
TXN1009,11-03-2024,MakeMyTrip,7428.06,USD,success,Travel,ACC004,SUSPICIOUS
TXN1066,2024/06/03,Swiggy,10634.88,INR,FAILED,Food,ACC002,
TXN1071,09-10-2024,Amazon,2390.16,INR,SUCCESS,Shopping,ACC003,
TXN1044,16-03-2024,Swiggy,13395.26,INR,success,Food,ACC005,Verified
TXN1047,2024/09/23,IRCTC,9419.89,INR,FAILED,Travel,ACC005,
TXN1068,03-02-2024,Ola,7401.61,inr,failed,Transport,ACC001,Refund expected
TXN1077,23-02-2024,HDFC ATM,4980.56,INR,failed,,ACC001,Duplicate?
TXN1060,22-05-2024,IRCTC,3695.76,INR,SUCCESS,Travel,ACC003,Refund expected
TXN1009,11-03-2024,MakeMyTrip,7428.06,USD,success,Travel,ACC004,SUSPICIOUS
TXN1033,13-10-2024,Ola,14608.86,inr,success,Transport,ACC002,Verified
TXN1059,29-11-2024,Flipkart,9257.89,INR,failed,Shopping,ACC002,
TXN1034,19-06-2024,Ola,12043.42,inr,SUCCESS,Transport,ACC001,
TXN1062,27-07-2024,IRCTC,8349.88,INR,PENDING,Travel,ACC001,Duplicate?
TXN1042,20-09-2024,Jio Recharge,5061.06,INR,success,Utilities,ACC002,
TXN1023,2024/10/23,MakeMyTrip,961.32,USD,failed,Travel,ACC005,
TXN1011,16-05-2024,BookMyShow,1717.7,INR,FAILED,Entertainment,ACC002,SUSPICIOUS
,25-06-2024,Jio Recharge,4004.59,INR,PENDING,,ACC003,
TXN1020,22-05-2024,IRCTC,3784.61,INR,failed,,ACC005,
TXN1064,20-08-2024,Flipkart,4634.01,INR,failed,Shopping,ACC002,Refund expected
TXN1025,02-08-2024,Jio Recharge,8500.14,INR,FAILED,Utilities,ACC004,Refund expected
TXN1069,01-08-2024,Jio Recharge,8962.1,INR,PENDING,Utilities,ACC005,Verified
TXN1024,05-02-2024,Jio Recharge,1066.01,INR,failed,Utilities,ACC005,
TXN1005,21-11-2024,BookMyShow,2481.68,INR,SUCCESS,Entertainment,ACC002,SUSPICIOUS
TXN1018,30-08-2024,Ola,2896.63,inr,PENDING,Transport,ACC001,
TXN1032,06-03-2024,Swiggy,4658.46,INR,FAILED,Food,ACC002,
TXN1028,30-11-2024,MakeMyTrip,166.96,USD,success,Travel,ACC001,
TXN1070,13-11-2024,Swiggy,9163.98,INR,success,Food,ACC001,Refund expected
TXN1043,31-08-2024,Jio Recharge,12753.58,INR,success,Utilities,ACC004,Verified
TXN1079,12-09-2024,IRCTC,11411.86,INR,PENDING,Travel,ACC004,
TXN1008,25-11-2024,IRCTC,2185.93,INR,failed,Travel,ACC005,Verified
TXN1035,12-04-2024,IRCTC,5277.39,INR,success,Travel,ACC003,
TXN1072,06-02-2024,Zomato,13862.47,USD,success,Food,ACC005,Refund expected
TXN1039,15-11-2024,BookMyShow,9967.64,INR,PENDING,Entertainment,ACC003,SUSPICIOUS
TXN1079,12-09-2024,IRCTC,11411.86,INR,PENDING,Travel,ACC004,
TXN1038,07-04-2024,Flipkart,713.58,INR,success,Shopping,ACC001,
TXN1026,2024/06/02,IRCTC,4776.85,INR,failed,Travel,ACC001,SUSPICIOUS
TXN1040,03-08-2024,Ola,10175.9,inr,failed,Transport,ACC003,Duplicate?
TXN1058,2024/06/23,Swiggy,12751.16,INR,PENDING,Food,ACC004,Refund expected
,2024/04/21,Zomato,7605.06,USD,failed,Food,ACC004,SUSPICIOUS
TXN1022,20-08-2024,Flipkart,6373.96,INR,PENDING,Shopping,ACC003,Refund expected
TXN1075,16-02-2024,Zomato,14430.57,USD,FAILED,Food,ACC002,Verified
TXN1012,2024/11/16,Flipkart,12632.96,INR,success,Shopping,ACC005,
TXN1074,04-06-2024,Ola,14143.01,inr,SUCCESS,Transport,ACC003,SUSPICIOUS
TXN1063,20-12-2024,Zomato,4627.78,USD,PENDING,Food,ACC005,Duplicate?
TXN1013,07-09-2024,Swiggy,1722.42,INR,success,,ACC005,
TXN1057,03-06-2024,Jio Recharge,$12092.64,INR,failed,Utilities,ACC003,
TXN1010,14-07-2024,Jio Recharge,14942.01,INR,SUCCESS,Utilities,ACC001,
TXN1046,15-09-2024,Ola,12467.01,inr,PENDING,Transport,ACC002,
TXN1017,17-01-2024,BookMyShow,1109.32,INR,success,Entertainment,ACC005,SUSPICIOUS
TXN1050,02-08-2024,MakeMyTrip,11225.36,USD,success,Travel,ACC004,
TXN1078,2024/02/29,Ola,12980.41,inr,PENDING,Transport,ACC002,SUSPICIOUS
TXN1041,23-10-2024,Jio Recharge,9837.85,INR,PENDING,Utilities,ACC002,
TXN1035,12-04-2024,IRCTC,5277.39,INR,success,Travel,ACC003,
TXN1037,15-05-2024,Swiggy,1670.62,INR,failed,Food,ACC001,Verified
TXN2003,2024-07-15,IRCTC,193647.29,INR,SUCCESS,,ACC002,
TXN1048,2024/04/28,Ola,10424.55,inr,failed,Transport,ACC001,Refund expected
TXN1016,25-04-2024,Amazon,5104.38,INR,SUCCESS,Shopping,ACC001,Refund expected
TXN1053,13-07-2024,Flipkart,11292.55,INR,SUCCESS,Shopping,ACC001,Duplicate?
TXN1033,13-10-2024,Ola,14608.86,inr,success,Transport,ACC002,Verified
TXN1031,14-10-2024,Swiggy,1722.51,INR,SUCCESS,Food,ACC005,SUSPICIOUS
TXN1000_DUP,23-11-2024,Amazon,423.91,INR,failed,,ACC004,
TXN2002,2024-07-15,Ola,91185.1,INR,SUCCESS,,ACC001,
TXN1055,2024/04/21,IRCTC,10507.0,INR,SUCCESS,Travel,ACC002,Duplicate?
TXN1036,22-05-2024,BookMyShow,9640.15,INR,SUCCESS,Entertainment,ACC003,Refund expected
TXN2001,2024-07-15,Flipkart,146100.68,INR,SUCCESS,,ACC005,
,20-11-2024,Ola,12448.75,inr,FAILED,Transport,ACC004,SUSPICIOUS
TXN1007,10-06-2024,Ola,4052.73,inr,FAILED,Transport,ACC004,Verified
TXN1015,18-04-2024,MakeMyTrip,11341.21,USD,FAILED,Travel,ACC005,Verified
TXN2004,2024-07-15,IRCTC,191918.37,INR,SUCCESS,,ACC003,
TXN1067,08-09-2024,Amazon,13097.16,INR,success,Shopping,ACC003,
TXN1020,22-05-2024,IRCTC,3784.61,INR,failed,,ACC005,
TXN1042,20-09-2024,Jio Recharge,5061.06,INR,success,Utilities,ACC002,
TXN1016,25-04-2024,Amazon,5104.38,INR,SUCCESS,Shopping,ACC001,Refund expected
TXN1061,30-06-2024,Flipkart,5138.07,INR,SUCCESS,Shopping,ACC002,
,18-07-2024,Jio Recharge,14193.63,INR,PENDING,,ACC003,Duplicate?
TXN1027,15-05-2024,HDFC ATM,14002.23,INR,success,Cash Withdrawal,ACC005,
TXN1051,08-12-2024,Ola,10876.51,inr,failed,,ACC004,SUSPICIOUS
TXN1052,14-01-2024,Amazon,9659.11,INR,PENDING,Shopping,ACC004,Duplicate?
TXN1044,16-03-2024,Swiggy,13395.26,INR,success,Food,ACC005,Verified
TXN1029,19-05-2024,Flipkart,9092.21,INR,FAILED,Shopping,ACC003,
TXN2000,2024-07-15,Jio Recharge,175917.65,INR,SUCCESS,,ACC002,
TXN1056,14-01-2024,Flipkart,8658.16,INR,success,,ACC002,
1 txn_id date merchant amount currency status category account_id notes
2 TXN1065 04-09-2024 Flipkart 10882.55 INR SUCCESS Shopping ACC003 Refund expected
3 TXN1054 2024/02/05 Swiggy $11325.79 INR success Food ACC004
4 TXN1021 17-02-2024 Zomato 2536.35 USD SUCCESS Food ACC001 Verified
5 TXN1045 07-05-2024 Amazon 6874.1 INR failed Shopping ACC004 SUSPICIOUS
6 TXN1076 14-08-2024 Flipkart 2763.26 INR SUCCESS Shopping ACC004 Duplicate?
7 TXN1029 19-05-2024 Flipkart 9092.21 INR FAILED Shopping ACC003
8 TXN1004 04-07-2024 Jio Recharge 2924.71 INR SUCCESS Utilities ACC001 Verified
9 TXN1002 2024/01/04 HDFC ATM 10487.18 INR FAILED Cash Withdrawal ACC004
10 TXN1000 23-11-2024 Dentist Appointment Fee 423.91 INR failed ACC004
11 TXN1073 16-05-2024 HDFC ATM 1117.58 INR failed Cash Withdrawal ACC004
12 TXN1019 26-11-2024 Amazon 956.16 INR success Shopping ACC004 Refund expected
13 TXN1006 24-03-2024 IRCTC 5722.86 INR FAILED Travel ACC001 Refund expected
14 TXN1014 2024/09/05 Amazon 14670.87 INR failed Shopping ACC005
15 TXN1009 11-03-2024 MakeMyTrip 7428.06 USD success Travel ACC004 SUSPICIOUS
16 TXN1066 2024/06/03 Swiggy 10634.88 INR FAILED Food ACC002
17 TXN1071 09-10-2024 Amazon 2390.16 INR SUCCESS Shopping ACC003
18 TXN1044 16-03-2024 Swiggy 13395.26 INR success Food ACC005 Verified
19 TXN1047 2024/09/23 IRCTC 9419.89 INR FAILED Travel ACC005
20 TXN1068 03-02-2024 Ola 7401.61 inr failed Transport ACC001 Refund expected
21 TXN1077 23-02-2024 HDFC ATM 4980.56 INR failed ACC001 Duplicate?
22 TXN1060 22-05-2024 IRCTC 3695.76 INR SUCCESS Travel ACC003 Refund expected
23 TXN1009 11-03-2024 MakeMyTrip 7428.06 USD success Travel ACC004 SUSPICIOUS
24 TXN1033 13-10-2024 Ola 14608.86 inr success Transport ACC002 Verified
25 TXN1059 29-11-2024 Flipkart 9257.89 INR failed Shopping ACC002
26 TXN1034 19-06-2024 Ola 12043.42 inr SUCCESS Transport ACC001
27 TXN1062 27-07-2024 IRCTC 8349.88 INR PENDING Travel ACC001 Duplicate?
28 TXN1042 20-09-2024 Jio Recharge 5061.06 INR success Utilities ACC002
29 TXN1023 2024/10/23 MakeMyTrip 961.32 USD failed Travel ACC005
30 TXN1011 16-05-2024 BookMyShow 1717.7 INR FAILED Entertainment ACC002 SUSPICIOUS
31 25-06-2024 Jio Recharge 4004.59 INR PENDING ACC003
32 TXN1020 22-05-2024 IRCTC 3784.61 INR failed ACC005
33 TXN1064 20-08-2024 Flipkart 4634.01 INR failed Shopping ACC002 Refund expected
34 TXN1025 02-08-2024 Jio Recharge 8500.14 INR FAILED Utilities ACC004 Refund expected
35 TXN1069 01-08-2024 Jio Recharge 8962.1 INR PENDING Utilities ACC005 Verified
36 TXN1024 05-02-2024 Jio Recharge 1066.01 INR failed Utilities ACC005
37 TXN1005 21-11-2024 BookMyShow 2481.68 INR SUCCESS Entertainment ACC002 SUSPICIOUS
38 TXN1018 30-08-2024 Ola 2896.63 inr PENDING Transport ACC001
39 TXN1032 06-03-2024 Swiggy 4658.46 INR FAILED Food ACC002
40 TXN1028 30-11-2024 MakeMyTrip 166.96 USD success Travel ACC001
41 TXN1070 13-11-2024 Swiggy 9163.98 INR success Food ACC001 Refund expected
42 TXN1043 31-08-2024 Jio Recharge 12753.58 INR success Utilities ACC004 Verified
43 TXN1079 12-09-2024 IRCTC 11411.86 INR PENDING Travel ACC004
44 TXN1008 25-11-2024 IRCTC 2185.93 INR failed Travel ACC005 Verified
45 TXN1035 12-04-2024 IRCTC 5277.39 INR success Travel ACC003
46 TXN1072 06-02-2024 Zomato 13862.47 USD success Food ACC005 Refund expected
47 TXN1039 15-11-2024 BookMyShow 9967.64 INR PENDING Entertainment ACC003 SUSPICIOUS
48 TXN1079 12-09-2024 IRCTC 11411.86 INR PENDING Travel ACC004
49 TXN1038 07-04-2024 Flipkart 713.58 INR success Shopping ACC001
50 TXN1026 2024/06/02 IRCTC 4776.85 INR failed Travel ACC001 SUSPICIOUS
51 TXN1040 03-08-2024 Ola 10175.9 inr failed Transport ACC003 Duplicate?
52 TXN1058 2024/06/23 Swiggy 12751.16 INR PENDING Food ACC004 Refund expected
53 2024/04/21 Zomato 7605.06 USD failed Food ACC004 SUSPICIOUS
54 TXN1022 20-08-2024 Flipkart 6373.96 INR PENDING Shopping ACC003 Refund expected
55 TXN1075 16-02-2024 Zomato 14430.57 USD FAILED Food ACC002 Verified
56 TXN1012 2024/11/16 Flipkart 12632.96 INR success Shopping ACC005
57 TXN1074 04-06-2024 Ola 14143.01 inr SUCCESS Transport ACC003 SUSPICIOUS
58 TXN1063 20-12-2024 Zomato 4627.78 USD PENDING Food ACC005 Duplicate?
59 TXN1013 07-09-2024 Swiggy 1722.42 INR success ACC005
60 TXN1057 03-06-2024 Jio Recharge $12092.64 INR failed Utilities ACC003
61 TXN1010 14-07-2024 Jio Recharge 14942.01 INR SUCCESS Utilities ACC001
62 TXN1046 15-09-2024 Ola 12467.01 inr PENDING Transport ACC002
63 TXN1017 17-01-2024 BookMyShow 1109.32 INR success Entertainment ACC005 SUSPICIOUS
64 TXN1050 02-08-2024 MakeMyTrip 11225.36 USD success Travel ACC004
65 TXN1078 2024/02/29 Ola 12980.41 inr PENDING Transport ACC002 SUSPICIOUS
66 TXN1041 23-10-2024 Jio Recharge 9837.85 INR PENDING Utilities ACC002
67 TXN1035 12-04-2024 IRCTC 5277.39 INR success Travel ACC003
68 TXN1037 15-05-2024 Swiggy 1670.62 INR failed Food ACC001 Verified
69 TXN2003 2024-07-15 IRCTC 193647.29 INR SUCCESS ACC002
70 TXN1048 2024/04/28 Ola 10424.55 inr failed Transport ACC001 Refund expected
71 TXN1016 25-04-2024 Amazon 5104.38 INR SUCCESS Shopping ACC001 Refund expected
72 TXN1053 13-07-2024 Flipkart 11292.55 INR SUCCESS Shopping ACC001 Duplicate?
73 TXN1033 13-10-2024 Ola 14608.86 inr success Transport ACC002 Verified
74 TXN1031 14-10-2024 Swiggy 1722.51 INR SUCCESS Food ACC005 SUSPICIOUS
75 TXN1000_DUP 23-11-2024 Amazon 423.91 INR failed ACC004
76 TXN2002 2024-07-15 Ola 91185.1 INR SUCCESS ACC001
77 TXN1055 2024/04/21 IRCTC 10507.0 INR SUCCESS Travel ACC002 Duplicate?
78 TXN1036 22-05-2024 BookMyShow 9640.15 INR SUCCESS Entertainment ACC003 Refund expected
79 TXN2001 2024-07-15 Flipkart 146100.68 INR SUCCESS ACC005
80 20-11-2024 Ola 12448.75 inr FAILED Transport ACC004 SUSPICIOUS
81 TXN1007 10-06-2024 Ola 4052.73 inr FAILED Transport ACC004 Verified
82 TXN1015 18-04-2024 MakeMyTrip 11341.21 USD FAILED Travel ACC005 Verified
83 TXN2004 2024-07-15 IRCTC 191918.37 INR SUCCESS ACC003
84 TXN1067 08-09-2024 Amazon 13097.16 INR success Shopping ACC003
85 TXN1020 22-05-2024 IRCTC 3784.61 INR failed ACC005
86 TXN1042 20-09-2024 Jio Recharge 5061.06 INR success Utilities ACC002
87 TXN1016 25-04-2024 Amazon 5104.38 INR SUCCESS Shopping ACC001 Refund expected
88 TXN1061 30-06-2024 Flipkart 5138.07 INR SUCCESS Shopping ACC002
89 18-07-2024 Jio Recharge 14193.63 INR PENDING ACC003 Duplicate?
90 TXN1027 15-05-2024 HDFC ATM 14002.23 INR success Cash Withdrawal ACC005
91 TXN1051 08-12-2024 Ola 10876.51 inr failed ACC004 SUSPICIOUS
92 TXN1052 14-01-2024 Amazon 9659.11 INR PENDING Shopping ACC004 Duplicate?
93 TXN1044 16-03-2024 Swiggy 13395.26 INR success Food ACC005 Verified
94 TXN1029 19-05-2024 Flipkart 9092.21 INR FAILED Shopping ACC003
95 TXN2000 2024-07-15 Jio Recharge 175917.65 INR SUCCESS ACC002
96 TXN1056 14-01-2024 Flipkart 8658.16 INR success ACC002