Enhance receipt matching by adding user location support and implementing LLM-based tax analysis rules
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from schemas import Match, Receipt, Transaction
|
||||
from services.ai_matcher import AIMatcher
|
||||
from services.ai_rules import AIRulesEngine
|
||||
from services.feedback_logger import FeedbackLogger
|
||||
from schemas import Match, Receipt, Transaction
|
||||
from services.llm_tax_analyzer import LLMTaxAnalyzer
|
||||
|
||||
|
||||
class MatchingEngine:
|
||||
@@ -11,9 +12,13 @@ class MatchingEngine:
|
||||
self.ai_matcher = AIMatcher()
|
||||
self.rules_engine = AIRulesEngine()
|
||||
self.feedback_logger = FeedbackLogger()
|
||||
self.llm_tax_analyzer = LLMTaxAnalyzer()
|
||||
|
||||
def process_matching(
|
||||
self, receipts: List[Receipt], transactions: List[Transaction]
|
||||
self,
|
||||
receipts: List[Receipt],
|
||||
transactions: List[Transaction],
|
||||
user_location: str = "ON",
|
||||
) -> List[Match]:
|
||||
# Get AI matches
|
||||
ai_matches = self.ai_matcher.match_receipts_to_transactions(
|
||||
@@ -23,15 +28,26 @@ class MatchingEngine:
|
||||
# Apply rules and enhance matches
|
||||
enhanced_matches = []
|
||||
for match in ai_matches:
|
||||
enhanced_match = self._enhance_match_with_rules(match)
|
||||
enhanced_match = self._enhance_match_with_rules(match, user_location)
|
||||
enhanced_matches.append(enhanced_match)
|
||||
|
||||
return enhanced_matches
|
||||
|
||||
def _enhance_match_with_rules(self, match: Match) -> Match:
|
||||
def _enhance_match_with_rules(
|
||||
self, match: Match, user_location: str = "ON"
|
||||
) -> Match:
|
||||
"""
|
||||
Enhanced version using LLM to intelligently apply tax rules:
|
||||
1. Sales tax based on receipt location (shipping/billing address priority)
|
||||
2. Foreign exchange rules for currency mismatches
|
||||
3. Depreciation rules for capital assets (based on user location)
|
||||
4. Meals & Entertainment tax deduction rules (50% for tax, 100% for accounting)
|
||||
"""
|
||||
|
||||
# First, apply traditional rule-based checks for basic matching quality
|
||||
rule_results = self.rules_engine.apply_rules(match.receipt, match.transaction)
|
||||
|
||||
# Apply confidence boost from rules
|
||||
# Apply confidence boost from traditional rules
|
||||
if rule_results["confidence_boost"] > 0:
|
||||
match.confidence_score = min(
|
||||
1.0, match.confidence_score + rule_results["confidence_boost"]
|
||||
@@ -42,9 +58,75 @@ class MatchingEngine:
|
||||
match.confidence_score = 1.0
|
||||
match.match_reason += " (Auto-approved by rules)"
|
||||
|
||||
# Add tax analysis to match
|
||||
if rule_results.get("tax_analysis"):
|
||||
match.tax_analysis = rule_results["tax_analysis"]
|
||||
# Now apply LLM-based tax analysis
|
||||
try:
|
||||
llm_tax_analysis = self.llm_tax_analyzer.analyze_and_apply_tax_rules(
|
||||
match.receipt, match.transaction, user_location
|
||||
)
|
||||
|
||||
# Store the complete tax analysis
|
||||
match.tax_analysis = llm_tax_analysis
|
||||
|
||||
# Apply confidence adjustments based on tax analysis
|
||||
confidence_adj = llm_tax_analysis.get("confidence_adjustment", {})
|
||||
|
||||
# Boost confidence if tax rules validate the match
|
||||
boost = confidence_adj.get("boost", 0.0)
|
||||
if boost > 0:
|
||||
match.confidence_score = min(1.0, match.confidence_score + boost)
|
||||
match.match_reason += f" (Tax analysis confidence boost: +{boost:.2f})"
|
||||
|
||||
# Reduce confidence if tax issues detected
|
||||
reduce = confidence_adj.get("reduce", 0.0)
|
||||
if reduce > 0:
|
||||
match.confidence_score = max(0.0, match.confidence_score - reduce)
|
||||
match.match_reason += f" (Tax issues detected: -{reduce:.2f})"
|
||||
|
||||
# Add flags for manual review if needed
|
||||
review_flags = []
|
||||
|
||||
# Check sales tax issues
|
||||
sales_tax = llm_tax_analysis.get("sales_tax", {})
|
||||
if sales_tax.get("requires_review", False):
|
||||
review_flags.append("Sales Tax Review Required")
|
||||
|
||||
# Check FX issues
|
||||
fx_analysis = llm_tax_analysis.get("foreign_exchange", {})
|
||||
if fx_analysis.get("requires_manual_review", False):
|
||||
review_flags.append(
|
||||
f"FX Review Required (Discrepancy: ${fx_analysis.get('discrepancy', 0):.2f})"
|
||||
)
|
||||
|
||||
# Check depreciation
|
||||
depreciation = llm_tax_analysis.get("depreciation", {})
|
||||
if depreciation.get("is_capital_asset", False):
|
||||
review_flags.append(
|
||||
f"Capital Asset - Depreciation Applicable ({depreciation.get('asset_class', 'Unknown')})"
|
||||
)
|
||||
|
||||
# Check meals & entertainment
|
||||
meals_ent = llm_tax_analysis.get("meals_entertainment", {})
|
||||
if meals_ent.get("is_meals_entertainment", False):
|
||||
tax_deduction = meals_ent.get("tax_deduction_amount", 0)
|
||||
accounting_deduction = meals_ent.get("accounting_deduction_amount", 0)
|
||||
review_flags.append(
|
||||
f"M&E Expense - Tax Deduction: ${tax_deduction:.2f} (50%), Accounting: ${accounting_deduction:.2f} (100%)"
|
||||
)
|
||||
|
||||
# Add review flags to match reason
|
||||
if review_flags:
|
||||
match.match_reason += " | REVIEW: " + "; ".join(review_flags)
|
||||
|
||||
except Exception as e:
|
||||
# If LLM analysis fails, log it and continue with traditional rules
|
||||
import logging
|
||||
|
||||
logging.error(f"LLM tax analysis failed: {str(e)}")
|
||||
match.match_reason += " (Note: Advanced tax analysis unavailable)"
|
||||
|
||||
# Fall back to traditional tax rules if available
|
||||
if rule_results.get("tax_analysis"):
|
||||
match.tax_analysis = rule_results["tax_analysis"]
|
||||
|
||||
return match
|
||||
|
||||
|
||||
Reference in New Issue
Block a user