Update ARCHITECTURE.md with recent receipt management features
Updated documentation to reflect v1.2 changes: - Unmapped receipts with nullable TransactionId - Duplicate detection with blocking modal - Auto-mapping service with intelligent date range logic - Due date support for bills (bill date to due date + 5 days) - Enhanced manual mapping UI with transaction selector - Transactions page improvements (search, filters, ID column) - New Receipts page with comprehensive workflow - Updated service layer, database schema, and workflows sections 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
254
ARCHITECTURE.md
254
ARCHITECTURE.md
@@ -115,7 +115,7 @@ Stores uploaded receipt files (images/PDFs) linked to transactions.
|
|||||||
|
|
||||||
**Properties:**
|
**Properties:**
|
||||||
- `Id` (long) - Primary key
|
- `Id` (long) - Primary key
|
||||||
- `TransactionId` (long) - Foreign key to Transaction
|
- `TransactionId` (long?) - Foreign key to Transaction (nullable to support unmapped receipts)
|
||||||
- `FileName` (string, 260) - Original file name (sanitized)
|
- `FileName` (string, 260) - Original file name (sanitized)
|
||||||
- `ContentType` (string, 100) - MIME type
|
- `ContentType` (string, 100) - MIME type
|
||||||
- `StoragePath` (string, 1024) - Relative path in wwwroot
|
- `StoragePath` (string, 1024) - Relative path in wwwroot
|
||||||
@@ -126,6 +126,7 @@ Stores uploaded receipt files (images/PDFs) linked to transactions.
|
|||||||
**Parsed Fields (populated by AI parser):**
|
**Parsed Fields (populated by AI parser):**
|
||||||
- `Merchant` (string, 200) - Merchant name extracted from receipt
|
- `Merchant` (string, 200) - Merchant name extracted from receipt
|
||||||
- `ReceiptDate` (DateTime?) - Date on receipt
|
- `ReceiptDate` (DateTime?) - Date on receipt
|
||||||
|
- `DueDate` (DateTime?) - Payment due date (for bills only)
|
||||||
- `Subtotal`, `Tax`, `Total` (decimal?) - Monetary amounts
|
- `Subtotal`, `Tax`, `Total` (decimal?) - Monetary amounts
|
||||||
- `Currency` (string, 8) - Currency code
|
- `Currency` (string, 8) - Currency code
|
||||||
|
|
||||||
@@ -133,7 +134,7 @@ Stores uploaded receipt files (images/PDFs) linked to transactions.
|
|||||||
- `ParseLogs` - Collection of parse attempts
|
- `ParseLogs` - Collection of parse attempts
|
||||||
- `LineItems` - Collection of receipt line items
|
- `LineItems` - Collection of receipt line items
|
||||||
|
|
||||||
**Unique Index:** TransactionId + FileHashSha256 (prevents duplicate uploads)
|
**Unique Index:** TransactionId + FileHashSha256 (prevents duplicate uploads, filtered WHERE TransactionId IS NOT NULL)
|
||||||
|
|
||||||
### ReceiptLineItem (Models/ReceiptLineItem.cs)
|
### ReceiptLineItem (Models/ReceiptLineItem.cs)
|
||||||
Individual line items extracted from receipts.
|
Individual line items extracted from receipts.
|
||||||
@@ -286,16 +287,25 @@ Pattern-based rules for auto-categorization with merchant linking.
|
|||||||
### ReceiptManager (Services/ReceiptManager.cs)
|
### ReceiptManager (Services/ReceiptManager.cs)
|
||||||
**Interface:** `IReceiptManager`
|
**Interface:** `IReceiptManager`
|
||||||
|
|
||||||
**Responsibility:** Handle receipt file uploads and storage.
|
**Responsibility:** Handle receipt file uploads, storage, and transaction mapping.
|
||||||
|
|
||||||
**Key Methods:**
|
**Key Methods:**
|
||||||
- `UploadReceiptAsync(long transactionId, IFormFile file)`
|
- `UploadReceiptAsync(long transactionId, IFormFile file)`
|
||||||
- Validates file (10MB max, allowed extensions: jpg, jpeg, png, pdf, gif, heic)
|
- Validates file (10MB max, allowed extensions: jpg, jpeg, png, pdf, gif, heic)
|
||||||
- Computes SHA256 hash for deduplication
|
- Computes SHA256 hash for deduplication
|
||||||
|
- Checks for duplicates (same hash or same filename + size)
|
||||||
- Saves file to `wwwroot/receipts/{transactionId}_{guid}{ext}`
|
- Saves file to `wwwroot/receipts/{transactionId}_{guid}{ext}`
|
||||||
- Sanitizes filename (removes non-ASCII like ®, ™, ©)
|
- Sanitizes filename (removes non-ASCII like ®, ™, ©)
|
||||||
- Creates Receipt entity in database
|
- Creates Receipt entity in database
|
||||||
- Returns `ReceiptUploadResult`
|
- Returns `ReceiptUploadResult` with duplicate warnings
|
||||||
|
|
||||||
|
- `UploadUnmappedReceiptAsync(IFormFile file)`
|
||||||
|
- Same as UploadReceiptAsync but for receipts without initial transaction mapping
|
||||||
|
- TransactionId is null until later mapped
|
||||||
|
|
||||||
|
- `MapReceiptToTransactionAsync(long receiptId, long transactionId)`
|
||||||
|
- Links an unmapped receipt to a transaction
|
||||||
|
- Returns success boolean
|
||||||
|
|
||||||
- `DeleteReceiptAsync(long receiptId)`
|
- `DeleteReceiptAsync(long receiptId)`
|
||||||
- Deletes physical file from disk
|
- Deletes physical file from disk
|
||||||
@@ -304,6 +314,12 @@ Pattern-based rules for auto-categorization with merchant linking.
|
|||||||
- `GetReceiptPhysicalPath(Receipt receipt)` - Resolves storage path
|
- `GetReceiptPhysicalPath(Receipt receipt)` - Resolves storage path
|
||||||
- `GetReceiptAsync(long receiptId)` - Retrieves receipt with transaction
|
- `GetReceiptAsync(long receiptId)` - Retrieves receipt with transaction
|
||||||
|
|
||||||
|
**Duplicate Detection:**
|
||||||
|
- SHA256 hash matching (identical files)
|
||||||
|
- Filename + size matching (possible duplicates)
|
||||||
|
- Returns `DuplicateWarning` list with existing receipts
|
||||||
|
- User can confirm upload anyway via modal dialog
|
||||||
|
|
||||||
**Security Features:**
|
**Security Features:**
|
||||||
- File size validation (10MB limit)
|
- File size validation (10MB limit)
|
||||||
- Extension whitelist
|
- Extension whitelist
|
||||||
@@ -322,10 +338,11 @@ Pattern-based rules for auto-categorization with merchant linking.
|
|||||||
- Loads receipt file from disk
|
- Loads receipt file from disk
|
||||||
- Converts PDFs to PNG images using ImageMagick (220 DPI)
|
- Converts PDFs to PNG images using ImageMagick (220 DPI)
|
||||||
- Calls OpenAI Vision API with structured prompt
|
- Calls OpenAI Vision API with structured prompt
|
||||||
- Parses JSON response (merchant, date, amounts, line items)
|
- Parses JSON response (merchant, date, due date, amounts, line items)
|
||||||
- Updates Receipt entity with extracted data
|
- Updates Receipt entity with extracted data
|
||||||
- Replaces existing line items
|
- Replaces existing line items
|
||||||
- Logs parse attempt in ReceiptParseLog
|
- Logs parse attempt in ReceiptParseLog
|
||||||
|
- Attempts auto-mapping if receipt is unmapped
|
||||||
- Returns `ReceiptParseResult`
|
- Returns `ReceiptParseResult`
|
||||||
|
|
||||||
**API Configuration:**
|
**API Configuration:**
|
||||||
@@ -336,16 +353,69 @@ Pattern-based rules for auto-categorization with merchant linking.
|
|||||||
|
|
||||||
**Prompt Strategy:**
|
**Prompt Strategy:**
|
||||||
- Structured JSON request with schema example
|
- Structured JSON request with schema example
|
||||||
- Extracts: merchant, date, subtotal, tax, total, confidence
|
- Extracts: merchant, date, dueDate (for bills), subtotal, tax, total, confidence
|
||||||
- Line items with: description, quantity, unitPrice, lineTotal
|
- Line items with: description, quantity, unitPrice, lineTotal
|
||||||
- Special handling: Services/fees have null quantity (not products)
|
- Special handling: Services/fees have null quantity (not products)
|
||||||
|
- Due date extraction: For bills (utility, credit card, etc.), extracts payment due date
|
||||||
|
|
||||||
**PDF Handling:**
|
**PDF Handling:**
|
||||||
- ImageMagick converts first page to PNG at 220 DPI
|
- ImageMagick converts first page to PNG at 220 DPI
|
||||||
- Flattens alpha channel (white background)
|
- Flattens alpha channel (white background)
|
||||||
- TrueColor 8-bit RGB output
|
- TrueColor 8-bit RGB output
|
||||||
|
|
||||||
**Location:** Services/OpenAIReceiptParser.cs:23-302
|
**Auto-Mapping Integration:**
|
||||||
|
- After successful parse of unmapped receipts, triggers ReceiptAutoMapper
|
||||||
|
- Attempts to automatically link receipt to matching transaction
|
||||||
|
- Silently fails if auto-mapping unsuccessful (parsing still successful)
|
||||||
|
|
||||||
|
**Location:** Services/OpenAIReceiptParser.cs:23-342
|
||||||
|
|
||||||
|
### ReceiptAutoMapper (Services/ReceiptAutoMapper.cs)
|
||||||
|
**Interface:** `IReceiptAutoMapper`
|
||||||
|
|
||||||
|
**Responsibility:** Automatically map parsed receipts to matching transactions using smart matching rules.
|
||||||
|
|
||||||
|
**Key Methods:**
|
||||||
|
- `AutoMapReceiptAsync(long receiptId)`
|
||||||
|
- Attempts to match a single receipt to a transaction
|
||||||
|
- Uses date range, merchant name, and amount matching
|
||||||
|
- Returns `ReceiptAutoMapResult` with status (Success, NoMatch, MultipleMatches, AlreadyMapped, NotParsed, Failed)
|
||||||
|
|
||||||
|
- `AutoMapUnmappedReceiptsAsync()`
|
||||||
|
- Bulk processes all unmapped parsed receipts
|
||||||
|
- Returns `BulkAutoMapResult` with counts (total, mapped, no match, multiple matches)
|
||||||
|
|
||||||
|
**Matching Algorithm:**
|
||||||
|
1. **Date Range Filtering:**
|
||||||
|
- For bills with due dates: `billDate` to `dueDate + 5 days` (accounts for auto-pay delays, weekends, bank processing)
|
||||||
|
- For regular receipts: `receiptDate ± 3 days` (accounts for transaction posting delays)
|
||||||
|
- No date = no match (can't narrow down effectively)
|
||||||
|
|
||||||
|
2. **Merchant Filtering (optional):**
|
||||||
|
- Matches if merchant name or transaction name contains receipt merchant
|
||||||
|
- Case-insensitive contains matching
|
||||||
|
|
||||||
|
3. **Amount Matching (optional):**
|
||||||
|
- If receipt has total: matches transactions within ±$0.10 tolerance
|
||||||
|
- Accounts for rounding differences and tips
|
||||||
|
|
||||||
|
4. **Receipt Exclusion:**
|
||||||
|
- Excludes transactions that already have receipts attached
|
||||||
|
- Uses HashSet for performance
|
||||||
|
|
||||||
|
**Auto-Mapping Triggers:**
|
||||||
|
- Automatically triggered after successful receipt parsing (if receipt is unmapped)
|
||||||
|
- Can be manually triggered via "Auto-Map Unmapped Receipts" button on Receipts page
|
||||||
|
|
||||||
|
**Result Status Types:**
|
||||||
|
- `Success` - Single match found, receipt mapped automatically
|
||||||
|
- `NoMatch` - No matching transactions found
|
||||||
|
- `MultipleMatches` - Multiple potential matches found (requires manual selection)
|
||||||
|
- `AlreadyMapped` - Receipt already linked to a transaction
|
||||||
|
- `NotParsed` - Receipt has no parsed data (merchant, date, or total missing)
|
||||||
|
- `Failed` - Error during mapping process
|
||||||
|
|
||||||
|
**Location:** Services/ReceiptAutoMapper.cs
|
||||||
|
|
||||||
### TransactionAICategorizer (Services/TransactionAICategorizer.cs)
|
### TransactionAICategorizer (Services/TransactionAICategorizer.cs)
|
||||||
**Interface:** `ITransactionAICategorizer`
|
**Interface:** `ITransactionAICategorizer`
|
||||||
@@ -457,18 +527,69 @@ EF Core DbContext managing all database entities.
|
|||||||
### Transactions.cshtml / TransactionsModel
|
### Transactions.cshtml / TransactionsModel
|
||||||
**Route:** `/Transactions`
|
**Route:** `/Transactions`
|
||||||
|
|
||||||
**Purpose:** View and search all transactions.
|
**Purpose:** View and search all transactions with advanced filtering.
|
||||||
|
|
||||||
**Features:**
|
**Features:**
|
||||||
- Search by name or memo
|
- Search by name, memo, category, notes, or merchant
|
||||||
- Filter by card, category, date range
|
- Filter by card, category, merchant, date range
|
||||||
|
- Quick date range selectors (30/60/90 days, last year, this month, last month)
|
||||||
- Sort by date, amount, name, category
|
- Sort by date, amount, name, category
|
||||||
- Pagination
|
- Pagination with preserved filters
|
||||||
- Receipt count indicator
|
- Transaction ID display for easy reference
|
||||||
|
- Receipt count indicator with badge
|
||||||
|
- Upload CSV button in header
|
||||||
- Edit transaction link
|
- Edit transaction link
|
||||||
|
|
||||||
|
**Stats Display:**
|
||||||
|
- Total transaction count
|
||||||
|
- Current page showing count
|
||||||
|
- Total spent (debits sum)
|
||||||
|
- Total income (credits sum)
|
||||||
|
- Net amount with color coding
|
||||||
|
|
||||||
**Location:** Pages/Transactions.cshtml.cs
|
**Location:** Pages/Transactions.cshtml.cs
|
||||||
|
|
||||||
|
### Receipts.cshtml / ReceiptsModel
|
||||||
|
**Route:** `/Receipts`
|
||||||
|
|
||||||
|
**Purpose:** Manage receipts with upload, mapping, and auto-mapping capabilities.
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Upload receipts without initial transaction mapping (unmapped receipts)
|
||||||
|
- Duplicate detection with blocking modal dialog
|
||||||
|
- SHA256 hash matching (identical files)
|
||||||
|
- Filename + size matching (possible duplicates)
|
||||||
|
- User confirmation required to proceed
|
||||||
|
- View all receipts (mapped and unmapped) with status badges
|
||||||
|
- Manual receipt-to-transaction mapping via modal selector
|
||||||
|
- Auto-map unmapped receipts button (bulk operation)
|
||||||
|
- Smart transaction matching with filters
|
||||||
|
- Receipt info display: merchant, date, due date (for bills), total
|
||||||
|
- Delete receipts
|
||||||
|
- View receipt details
|
||||||
|
|
||||||
|
**Transaction Selector Modal:**
|
||||||
|
- Displays up to 50 matching transactions in scrollable table
|
||||||
|
- Date range filtering:
|
||||||
|
- Bills with due dates: bill date to due date + 5 days
|
||||||
|
- Regular receipts: receipt date ± 3 days
|
||||||
|
- Merchant-based relevance sorting (exact match > contains > no match)
|
||||||
|
- Amount matching with green highlighting (±$0.10 tolerance)
|
||||||
|
- Auto-scroll to first amount match when modal opens
|
||||||
|
- Manual transaction ID entry option
|
||||||
|
- Link to open Transactions page for searching
|
||||||
|
|
||||||
|
**Receipt List Columns:**
|
||||||
|
- Uploaded timestamp
|
||||||
|
- File name with icon (image/PDF)
|
||||||
|
- Receipt info (merchant, date, due date, total)
|
||||||
|
- Mapped transaction details or "Unmapped" badge
|
||||||
|
- Actions: View, Map (if unmapped), Delete
|
||||||
|
|
||||||
|
**Dependencies:** `IReceiptManager`, `IReceiptAutoMapper`
|
||||||
|
|
||||||
|
**Location:** Pages/Receipts.cshtml.cs
|
||||||
|
|
||||||
### EditTransaction.cshtml / EditTransactionModel
|
### EditTransaction.cshtml / EditTransactionModel
|
||||||
**Route:** `/EditTransaction/{id}`
|
**Route:** `/EditTransaction/{id}`
|
||||||
|
|
||||||
@@ -615,6 +736,8 @@ builder.Services.AddScoped<IRecentTransactionsProvider, RecentTransactionsProvid
|
|||||||
// Receipt Services
|
// Receipt Services
|
||||||
builder.Services.AddScoped<IReceiptManager, ReceiptManager>();
|
builder.Services.AddScoped<IReceiptManager, ReceiptManager>();
|
||||||
builder.Services.AddHttpClient<IReceiptParser, OpenAIReceiptParser>();
|
builder.Services.AddHttpClient<IReceiptParser, OpenAIReceiptParser>();
|
||||||
|
builder.Services.AddScoped<IReceiptAutoMapper, ReceiptAutoMapper>();
|
||||||
|
builder.Services.AddScoped<IMerchantService, MerchantService>();
|
||||||
```
|
```
|
||||||
|
|
||||||
**Location:** Program.cs:1-44
|
**Location:** Program.cs:1-44
|
||||||
@@ -668,7 +791,7 @@ INDEX: Date, Amount, Category
|
|||||||
### Receipts Table
|
### Receipts Table
|
||||||
```sql
|
```sql
|
||||||
Id (bigint, PK)
|
Id (bigint, PK)
|
||||||
TransactionId (bigint, FK → Transactions.Id, CASCADE)
|
TransactionId (bigint, FK → Transactions.Id, CASCADE, NULLABLE)
|
||||||
FileName (nvarchar(260), NOT NULL)
|
FileName (nvarchar(260), NOT NULL)
|
||||||
ContentType (nvarchar(100), DEFAULT 'application/octet-stream')
|
ContentType (nvarchar(100), DEFAULT 'application/octet-stream')
|
||||||
StoragePath (nvarchar(1024), NOT NULL)
|
StoragePath (nvarchar(1024), NOT NULL)
|
||||||
@@ -677,11 +800,12 @@ FileHashSha256 (nvarchar(64), NOT NULL)
|
|||||||
UploadedAtUtc (datetime2, NOT NULL)
|
UploadedAtUtc (datetime2, NOT NULL)
|
||||||
Merchant (nvarchar(200))
|
Merchant (nvarchar(200))
|
||||||
ReceiptDate (datetime2)
|
ReceiptDate (datetime2)
|
||||||
|
DueDate (datetime2) -- For bills: payment due date
|
||||||
Subtotal (decimal(18,2))
|
Subtotal (decimal(18,2))
|
||||||
Tax (decimal(18,2))
|
Tax (decimal(18,2))
|
||||||
Total (decimal(18,2))
|
Total (decimal(18,2))
|
||||||
Currency (nvarchar(8))
|
Currency (nvarchar(8))
|
||||||
UNIQUE INDEX: (TransactionId, FileHashSha256)
|
UNIQUE INDEX: (TransactionId, FileHashSha256) WHERE TransactionId IS NOT NULL
|
||||||
```
|
```
|
||||||
|
|
||||||
### ReceiptParseLogs Table
|
### ReceiptParseLogs Table
|
||||||
@@ -766,7 +890,7 @@ Iterate mappings:
|
|||||||
Update Transaction.Category and Transaction.MerchantId
|
Update Transaction.Category and Transaction.MerchantId
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Upload and Parse Receipt
|
### 3. Upload and Parse Receipt (Traditional Flow - From Transaction)
|
||||||
|
|
||||||
```
|
```
|
||||||
User attaches receipt to transaction
|
User attaches receipt to transaction
|
||||||
@@ -790,13 +914,83 @@ OpenAIReceiptParser.ParseReceiptAsync()
|
|||||||
- Encode as base64
|
- Encode as base64
|
||||||
- Call OpenAI Vision API with structured prompt
|
- Call OpenAI Vision API with structured prompt
|
||||||
- Parse JSON response
|
- Parse JSON response
|
||||||
- Update Receipt (merchant, date, amounts)
|
- Update Receipt (merchant, date, due date, amounts)
|
||||||
- Replace ReceiptLineItems
|
- Replace ReceiptLineItems
|
||||||
- Create ReceiptParseLog
|
- Create ReceiptParseLog
|
||||||
↓
|
↓
|
||||||
Display parsed data
|
Display parsed data
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 3a. Upload, Parse, and Auto-Map Receipt (New Unmapped Flow)
|
||||||
|
|
||||||
|
```
|
||||||
|
User visits /Receipts page
|
||||||
|
↓
|
||||||
|
User uploads receipt without transaction
|
||||||
|
↓
|
||||||
|
ReceiptsModel.OnPostUploadAsync()
|
||||||
|
↓
|
||||||
|
ReceiptManager.UploadUnmappedReceiptAsync()
|
||||||
|
- Validate file (size, extension)
|
||||||
|
- Compute SHA256 hash
|
||||||
|
- Check for duplicates (hash, filename+size)
|
||||||
|
↓
|
||||||
|
IF duplicates found:
|
||||||
|
- Delete uploaded file temporarily
|
||||||
|
- Store duplicate warnings in TempData
|
||||||
|
- Redirect to GET with modal
|
||||||
|
↓
|
||||||
|
User sees blocking modal:
|
||||||
|
Option A: Cancel (don't upload)
|
||||||
|
Option B: Upload Anyway (re-upload with confirmation)
|
||||||
|
↓
|
||||||
|
IF no duplicates OR confirmed:
|
||||||
|
- Save to wwwroot/receipts/
|
||||||
|
- Create Receipt entity (TransactionId = null)
|
||||||
|
↓
|
||||||
|
User clicks "Parse Receipt"
|
||||||
|
↓
|
||||||
|
OpenAIReceiptParser.ParseReceiptAsync()
|
||||||
|
- Parse receipt (merchant, date, due date, total, line items)
|
||||||
|
- Update Receipt entity
|
||||||
|
- Trigger ReceiptAutoMapper.AutoMapReceiptAsync()
|
||||||
|
↓
|
||||||
|
ReceiptAutoMapper.AutoMapReceiptAsync()
|
||||||
|
- Find matching transactions:
|
||||||
|
* Date filter (bill date to due date+5 OR receipt date ±3)
|
||||||
|
* Merchant filter (contains, case-insensitive)
|
||||||
|
* Amount filter (±$0.10 tolerance)
|
||||||
|
* Exclude transactions with existing receipts
|
||||||
|
↓
|
||||||
|
IF single match found:
|
||||||
|
- Auto-map receipt to transaction
|
||||||
|
- Return Success
|
||||||
|
ELSE IF multiple matches:
|
||||||
|
- Return MultipleMatches (requires manual selection)
|
||||||
|
ELSE:
|
||||||
|
- Return NoMatch
|
||||||
|
↓
|
||||||
|
IF auto-mapping failed (no match or multiple matches):
|
||||||
|
User clicks "Map" button on receipt row
|
||||||
|
↓
|
||||||
|
ReceiptsModel.FindMatchingTransactionsForReceipt()
|
||||||
|
- Apply same filtering logic
|
||||||
|
- Sort by merchant relevance (exact > contains > none)
|
||||||
|
- Return up to 50 matches
|
||||||
|
↓
|
||||||
|
Modal displays transactions in table:
|
||||||
|
- Green highlighting for amount matches
|
||||||
|
- Auto-scroll to first amount match
|
||||||
|
- Radio button selection
|
||||||
|
- Manual ID entry option
|
||||||
|
↓
|
||||||
|
User selects transaction
|
||||||
|
↓
|
||||||
|
ReceiptsModel.OnPostMapToTransactionAsync()
|
||||||
|
- Links receipt to transaction
|
||||||
|
- Redirect to receipts page
|
||||||
|
```
|
||||||
|
|
||||||
### 4. Dashboard Aggregation
|
### 4. Dashboard Aggregation
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -1044,5 +1238,31 @@ MoneyMap demonstrates a well-architected ASP.NET Core application with clear sep
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Last Updated:** 2025-10-12
|
**Last Updated:** 2025-10-12
|
||||||
**Version:** 1.1
|
**Version:** 1.2
|
||||||
**Framework:** ASP.NET Core 8.0 / EF Core 9.0
|
**Framework:** ASP.NET Core 8.0 / EF Core 9.0
|
||||||
|
|
||||||
|
## Recent Changes (v1.2)
|
||||||
|
|
||||||
|
### Receipt Management Enhancements
|
||||||
|
- **Unmapped Receipts**: Receipts can now be uploaded without initial transaction mapping
|
||||||
|
- **Duplicate Detection**: SHA256 hash and filename+size matching with blocking modal confirmation
|
||||||
|
- **Auto-Mapping**: Smart automatic receipt-to-transaction matching using date, merchant, and amount
|
||||||
|
- **Due Date Support**: Bills (utility, credit card) can have due dates for expanded matching windows
|
||||||
|
- **Manual Mapping UI**: Enhanced modal with transaction table, relevance sorting, amount highlighting, and auto-scroll
|
||||||
|
- **Date Range Intelligence**:
|
||||||
|
- Bills: bill date to due date + 5 days (accounts for auto-pay processing)
|
||||||
|
- Regular receipts: receipt date ± 3 days
|
||||||
|
|
||||||
|
### UI Improvements
|
||||||
|
- **Transactions Page**: Added search, quick date filters, transaction ID column, Upload CSV button in header
|
||||||
|
- **Navigation**: Moved Upload CSV from menu to Transactions page header
|
||||||
|
- **Receipts Page**: New dedicated page for receipt management with mapping workflow
|
||||||
|
- **Transaction Selector**: Scrollable table with green amount match highlighting and auto-scroll to best match
|
||||||
|
|
||||||
|
### Technical Improvements
|
||||||
|
- Made `Receipt.TransactionId` nullable with filtered unique index
|
||||||
|
- Added `Receipt.DueDate` column for bill payment tracking
|
||||||
|
- Enhanced OpenAI prompt to extract due dates from bills
|
||||||
|
- Implemented `ReceiptAutoMapper` service with intelligent matching algorithm
|
||||||
|
- Updated `ReceiptManager` with unmapped receipt support and duplicate detection
|
||||||
|
- Added `MerchantService` for merchant management
|
||||||
|
|||||||
Reference in New Issue
Block a user