diff --git a/app/main.py b/app/main.py index c402d75..6010013 100644 --- a/app/main.py +++ b/app/main.py @@ -537,7 +537,7 @@ async def match_specific_receipts(request: MatchSpecificRequest, db: db_dependen ) try: - matching_results = matching_engine.process_matching(receipts, transactions) + matching_results = matching_engine.process_matching(receipts, transactions, user_location=request.user_location) logger.info(f"Matching completed, got {len(matching_results)} results") # Convert matching results to response format diff --git a/app/schemas.py b/app/schemas.py index b030e1d..5173552 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -208,3 +208,4 @@ class DepreciationResponse(BaseModel): class MatchSpecificRequest(BaseModel): file_ids: List[str] categorization_id: str + user_location: Optional[str] = "Canada" diff --git a/app/services/llm_tax_analyzer.py b/app/services/llm_tax_analyzer.py new file mode 100644 index 0000000..fee9cd0 --- /dev/null +++ b/app/services/llm_tax_analyzer.py @@ -0,0 +1,446 @@ +import json +import logging +from typing import Any, Dict + +import groq +from config import settings +from schemas import Receipt, Transaction + +logger = logging.getLogger(__name__) + + +class LLMTaxAnalyzer: + """ + Uses LLM to intelligently apply tax rules based on context. + + Implements four core tax rules: + 1. Sales Tax Rule - Based on receipt location (shipping/billing address) + 2. Foreign Exchange Rule - Handles currency mismatches + 3. Depreciation Rule - Capital assets (based on user location) + 4. Meals & Entertainment Rule - 50% tax deduction, 100% accounting deduction + """ + + # Provincial tax rates for reference + PROVINCIAL_TAX_RATES = { + "ON": {"rate": 0.13, "name": "HST", "type": "Harmonized"}, + "QC": {"rate": 0.14975, "name": "QST + GST", "type": "Combined"}, + "BC": {"rate": 0.12, "name": "PST + GST", "type": "Combined"}, + "AB": {"rate": 0.05, "name": "GST", "type": "Federal only"}, + "SK": {"rate": 0.11, "name": "PST + GST", "type": "Combined"}, + "MB": {"rate": 0.12, "name": "PST + GST", "type": "Combined"}, + "NS": {"rate": 0.15, "name": "HST", "type": "Harmonized"}, + "NB": {"rate": 0.15, "name": "HST", "type": "Harmonized"}, + "NL": {"rate": 0.15, "name": "HST", "type": "Harmonized"}, + "PE": {"rate": 0.15, "name": "HST", "type": "Harmonized"}, + "NT": {"rate": 0.05, "name": "GST", "type": "Federal only"}, + "NU": {"rate": 0.05, "name": "GST", "type": "Federal only"}, + "YT": {"rate": 0.05, "name": "GST", "type": "Federal only"}, + } + + # CCA rates by asset class (simplified) + CCA_RATES = { + "vehicles": 0.30, # Class 10 + "computer_equipment": 0.55, # Class 50 + "furniture": 0.20, # Class 8 + "buildings": 0.04, # Class 1 + "machinery": 0.20, # Class 8 + } + + def __init__(self): + self.client = groq.Groq(api_key=settings.GROQ_API_KEY) + self.model = "llama-3.1-8b-instant" + self.max_retries = 3 + + def analyze_and_apply_tax_rules( + self, + receipt: Receipt, + transaction: Transaction, + user_location: str = "ON", # Default to Ontario + ) -> Dict[str, Any]: + """ + Use LLM to intelligently analyze and apply all 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 deduction rules + """ + + # Prepare context for LLM + analysis_context = self._build_analysis_context( + receipt, transaction, user_location + ) + + # Get LLM analysis + llm_analysis = self._get_llm_tax_analysis(analysis_context) + + # Parse and structure the results + structured_results = self._structure_analysis_results( + llm_analysis, receipt, transaction, user_location + ) + + return structured_results + + def _build_analysis_context( + self, receipt: Receipt, transaction: Transaction, user_location: str + ) -> str: + """Build comprehensive context for LLM analysis""" + + # Extract location information + receipt_location = self._extract_receipt_location(receipt) + user_province = user_location.upper() + + # Build tax rates reference + tax_rates_info = json.dumps(self.PROVINCIAL_TAX_RATES, indent=2) + cca_rates_info = json.dumps(self.CCA_RATES, indent=2) + + context = f""" +RECEIPT DETAILS: +- Vendor: {receipt.vendor} +- Amount: ${receipt.amount:.2f} +- Tax: ${receipt.tax:.2f} +- Currency: {receipt.currency} +- Date: {receipt.receipt_date.strftime("%Y-%m-%d")} +- Category: {receipt.category} +- Description: {receipt.description} +- Billing Address: {self._format_address(receipt.billing_address)} +- Shipping Address: {self._format_address(receipt.shipping_address)} +- Is Meals & Entertainment: {receipt.is_meals_entertainment} + +TRANSACTION DETAILS: +- Vendor: {transaction.vendor} +- Amount: ${transaction.amount:.2f} +- Currency: {transaction.currency} +- Date: {transaction.transaction_date.strftime("%Y-%m-%d")} +- Notes: {transaction.notes} +- FX Rate: {transaction.fx_rate if transaction.fx_rate else "N/A"} + +USER CONTEXT: +- User Location (Province): {user_province} +- User Province Tax Rate: {self.PROVINCIAL_TAX_RATES.get(user_province, {}).get("rate", 0.13) * 100}% +- User Tax Type: {self.PROVINCIAL_TAX_RATES.get(user_province, {}).get("name", "HST")} + +RECEIPT LOCATION DETECTED: +{receipt_location} + +PROVINCIAL TAX RATES REFERENCE: +{tax_rates_info} + +CCA DEPRECIATION RATES BY ASSET CLASS: +{cca_rates_info} +""" + return context + + def _extract_receipt_location(self, receipt: Receipt) -> str: + """Extract and format receipt location information""" + + # Priority: Use shipping address if available, then billing + location = ( + receipt.shipping_address + if receipt.shipping_address + else receipt.billing_address + ) + + if location: + return f""" +- Province: {location.province} +- City: {location.city} +- Country: {location.country} +- Postal Code: {location.postal_code} +""" + else: + return "- No address information available (will use user location)" + + def _format_address(self, address) -> str: + """Format address for display""" + if address: + return f"{address.city}, {address.province}, {address.country} ({address.postal_code})" + return "Not provided" + + def _get_llm_tax_analysis(self, context: str) -> str: + """Get tax rule analysis from LLM""" + + prompt = f""" +You are a Canadian tax expert analyzing a receipt-transaction match. Apply the following tax rules intelligently: + +{context} + +=== FOUR CORE TAX RULES === + +### 1. SALES TAX RULE +**Purpose**: Calculate and apply correct sales tax based on shipping and billing addresses. + +**Key Principles**: +- When billing and shipping addresses are THE SAME: Apply sales tax based on that address location. +- When billing and shipping addresses are DIFFERENT: Apply sales tax based on the SHIPPING address. +- Tax rate is determined by the RECEIPT'S location, NOT the user's location (unless no receipt location). + +**Scenario Examples**: +a) User in Ontario, Receipt from Quebec: + - Apply Quebec's tax rate (14.975% QST+GST), not Ontario's 13% HST + - The user's location is only for depreciation purposes + +b) User in Ontario, Receipt from USA (New York): + - DO NOT apply Canadian sales tax + - This is an international transaction + - Flag for FX review instead + +c) User in USA (New York), Receipt from California: + - Apply California's sales tax rate (receipt location) + - Not New York's rate (user location) + +d) User in Ontario, Receipt has NO address information: + - DEFAULT to user's location (Ontario 13% HST) + - This is the fallback when receipt location is unknown + +**Tax Calculation**: +- Compare calculated tax vs stated tax on receipt +- Flag discrepancies for review + +### 2. FOREIGN EXCHANGE (FX) RULE +**Purpose**: Handle currency mismatches between receipts and transactions. + +**Actions**: +- Identify when receipt currency ≠ transaction currency (e.g., USD vs CAD) +- Calculate the absolute discrepancy: |receipt_amount - transaction_amount| +- ALWAYS flag for manual review - DO NOT fetch exchange rates automatically +- If FX rate is provided in transaction data, note it but still require manual review + +**Examples**: +- Transaction: USD $100, Receipt: CAD $125 → Discrepancy: $25, Flag for review +- The user must manually approve or adjust the FX difference + +### 3. DEPRECIATION RULE +**Purpose**: Calculate depreciation for assets using two methods. + +**Key Principle**: Depreciation is ALWAYS based on USER'S location, NOT receipt location. + +**Asset Identification**: +- Only applies to capital assets: vehicles, equipment, furniture, buildings, machinery +- Identify from receipt category and description +- Typical threshold: Assets generally > $500 + +**Two Methods Required**: +a) **Straight-Line Depreciation** (for accounting purposes): + Formula: (Cost - Residual Value) / Useful Life + Example: Asset $10,000, 5-year life, $1,000 residual = $1,800/year + +b) **CCA Depreciation** (for tax purposes - Canada): + Method: Declining Balance + Formula: Book Value × CCA Rate each year + Example: Truck $20,000, 30% CCA: + - Year 1: $20,000 × 30% = $6,000 + - Year 2: ($20,000 - $6,000) × 30% = $4,200 + - Continues declining each year + +**CCA Classes** (Canada): +- Vehicles: 30% (Class 10) +- Computer Equipment: 55% (Class 50) +- Furniture/Machinery: 20% (Class 8) +- Buildings: 4% (Class 1) + +### 4. MEALS & ENTERTAINMENT TAX DEDUCTION RULE +**Purpose**: Apply correct deductions for meals and entertainment expenses. + +**Deduction Rules**: +- **For Tax Purposes**: Only 50% of total receipt amount is deductible +- **For Accounting Purposes**: 100% of total receipt amount is deductible +- **Sales Tax**: Full sales tax amount is deductible for accounting + +**Example**: +- Receipt: $100 meal + $12 sales tax = $112 total +- **Tax Deduction**: $50 (50% of meal) + $12 (full tax) = $62 +- **Accounting Deduction**: $100 (full meal) + $12 (full tax) = $112 + +=== LOCATION-BASED SCENARIO HANDLING === + +**When Receipt Location ≠ User Location**: + +1. **Sales Tax**: Use RECEIPT's location for tax calculation + - Exception: If international (different country), no Canadian sales tax + flag FX + - Exception: If no location on receipt, use user's location as default + +2. **Depreciation**: ALWAYS use USER's location for depreciation rules + - Receipt location is irrelevant for depreciation + - Apply user's country/province depreciation methods + +3. **FX Handling**: + - If receipt currency ≠ transaction currency: Flag for manual review + - Do NOT automatically fetch or apply exchange rates + +4. **Missing Location**: + - If receipt has no address: Default to user's location for sales tax + - Still apply user's location for depreciation + +=== ANALYSIS REQUIRED === + +Provide a structured JSON response with the following format: + +{{ + "sales_tax": {{ + "applicable_province": "XX", + "applicable_rate": 0.XX, + "tax_name": "HST/GST/PST/QST", + "calculated_tax": XX.XX, + "stated_tax": XX.XX, + "discrepancy": XX.XX, + "reason": "Detailed explanation: which address used (billing/shipping), why this location, which scenario applies", + "requires_review": true/false + }}, + "foreign_exchange": {{ + "currency_mismatch": true/false, + "receipt_currency": "XXX", + "transaction_currency": "XXX", + "receipt_amount": XX.XX, + "transaction_amount": XX.XX, + "discrepancy": XX.XX, + "requires_manual_review": true/false, + "reason": "Explanation of FX situation" + }}, + "depreciation": {{ + "is_capital_asset": true/false, + "asset_class": "category name or N/A", + "suggested_cca_rate": 0.XX, + "straight_line_applicable": true/false, + "cca_applicable": true/false, + "straight_line_example": "Brief calculation example if applicable", + "cca_example": "Brief calculation example if applicable", + "reason": "Why this is/isn't a capital asset, which CCA class, and why depreciation based on user's location" + }}, + "meals_entertainment": {{ + "is_meals_entertainment": true/false, + "tax_deduction_amount": XX.XX, + "accounting_deduction_amount": XX.XX, + "sales_tax_included": XX.XX, + "reason": "Explanation of M&E rule application" + }}, + "confidence_adjustment": {{ + "boost": 0.XX, + "reduce": 0.XX, + "reason": "Why confidence should be adjusted based on tax analysis" + }}, + "overall_assessment": "Comprehensive summary: which rules applied, why, what location used for what purpose, and any required actions" +}} + +**Critical Reminders**: +- Sales tax uses RECEIPT location (or user location if receipt has none) +- Depreciation ALWAYS uses USER location +- For different addresses, use SHIPPING address for sales tax +- International transactions: no Canadian tax + FX flag +- Be precise with all calculations +- Always explain your reasoning clearly +""" + + try: + response = self.client.chat.completions.create( + model=self.model, + messages=[ + { + "role": "system", + "content": "You are a Canadian tax expert. Analyze transactions and apply tax rules accurately. Always return valid JSON.", + }, + {"role": "user", "content": prompt}, + ], + temperature=0.1, # Low temperature for consistent, factual responses + max_tokens=2000, + ) + + content = response.choices[0].message.content.strip() + logger.info(f"LLM tax analysis received: {len(content)} characters") + return content + + except Exception as e: + logger.error(f"Error getting LLM tax analysis: {str(e)}") + return self._get_fallback_analysis() + + def _get_fallback_analysis(self) -> str: + """Return fallback analysis if LLM fails""" + return json.dumps( + { + "sales_tax": { + "applicable_province": "ON", + "applicable_rate": 0.13, + "tax_name": "HST", + "calculated_tax": 0.0, + "stated_tax": 0.0, + "discrepancy": 0.0, + "reason": "LLM analysis failed - using defaults", + "requires_review": True, + }, + "foreign_exchange": { + "currency_mismatch": False, + "requires_manual_review": False, + "reason": "Analysis not available", + }, + "depreciation": { + "is_capital_asset": False, + "reason": "Analysis not available", + }, + "meals_entertainment": { + "is_meals_entertainment": False, + "reason": "Analysis not available", + }, + "confidence_adjustment": { + "boost": 0.0, + "reduce": 0.1, + "reason": "LLM analysis failed - recommend manual review", + }, + "overall_assessment": "Automatic analysis failed. Manual review recommended.", + } + ) + + def _structure_analysis_results( + self, + llm_response: str, + receipt: Receipt, + transaction: Transaction, + user_location: str, + ) -> Dict[str, Any]: + """Parse LLM response and structure it for application""" + + try: + # Extract JSON from LLM response (may have markdown code blocks) + json_str = llm_response + if "```json" in llm_response: + json_str = llm_response.split("```json")[1].split("```")[0].strip() + elif "```" in llm_response: + json_str = llm_response.split("```")[1].split("```")[0].strip() + + analysis = json.loads(json_str) + + # Add metadata + analysis["metadata"] = { + "user_location": user_location, + "receipt_id": receipt.id, + "transaction_id": transaction.id, + "analysis_method": "LLM-based", + "model": self.model, + } + + return analysis + + except json.JSONDecodeError as e: + logger.error(f"Failed to parse LLM response as JSON: {str(e)}") + logger.error(f"LLM response was: {llm_response}") + + # Return structured fallback + return { + "sales_tax": { + "requires_review": True, + "reason": "Failed to parse LLM response", + }, + "foreign_exchange": { + "requires_manual_review": receipt.currency != transaction.currency + }, + "depreciation": {"is_capital_asset": False}, + "confidence_adjustment": { + "boost": 0.0, + "reduce": 0.15, + "reason": "Analysis parsing failed", + }, + "overall_assessment": "Analysis failed. Manual review required.", + "error": str(e), + "metadata": { + "user_location": user_location, + "analysis_method": "fallback", + }, + } diff --git a/app/services/matching_engine.py b/app/services/matching_engine.py index ecf5d08..ac42118 100644 --- a/app/services/matching_engine.py +++ b/app/services/matching_engine.py @@ -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 diff --git a/app/services/rules.py b/app/services/rules.py new file mode 100644 index 0000000..cb3f3cf --- /dev/null +++ b/app/services/rules.py @@ -0,0 +1,106 @@ +rule = ''' +### Rule Scenarios +Impact of Signup Fields on Tax Calculation and Receipt Matching +Impact of Signup Fields (Country and Province/State) on Tax Calculation and Matching** + +**Scenario 1:** User Location (Canada, Ontario) but Receipt from Another Location (e.g., Quebec) +User's Location: Canada, Ontario (for tax and depreciation purposes). +Receipt Location: The receipt comes from Quebec (the tax rules in Quebec are different from Ontario). +What Happens: +The sales tax rate should be applied based on the location of the receipt, not the user's profile location. +**For example:** +The user in Ontario will have 13% HST applied to their purchases. +If the receipt is from Quebec, the QST (Quebec Sales Tax) of 9.975% applies instead. + +**Scenario 2:** User Location (Canada, Ontario) and Receipt Location is Different Country (e.g., USA) +User's Location: Canada, Ontario. +Receipt Location: The receipt is from a business in the USA (e.g., New York). +**What Happens:** +Sales Tax should not be applied for international transactions (USA in this case) unless the user is importing or there is a customs duty involved. +The system will not apply a Canadian sales tax to the receipt from the USA, but the foreign exchange (FX) rule will apply because there is a mismatch between currencies (USD vs. CAD). + +**Scenario 3:** User Location (USA, New York) but Receipt from Another Location in the Same Country (e.g., California) +User's Location: USA, New York (for tax purposes). +Receipt Location: The receipt is from California (still in the USA, but the sales tax rate is different). +**What Happens:** +Sales tax should be applied based on the location of the receipt, not the user’s location, since the receipt was issued in California. +California may have a different sales tax rate than New York. + +**Scenario 4:** User Location (Canada, Ontario) and Receipt Location with No Address Information +User's Location: Canada, Ontario. +Receipt Location: The receipt contains no clear shipping or billing address. +**What Happens:** +If the receipt does not have a clear location, the system will default to the user’s location for sales tax and depreciation. +Action: +Sales Tax: Apply the sales tax rate based on the user's location (Ontario). For example, 13% HST will be applied. +Depreciation: Apply the depreciation rules based on the user’s location (Ontario), even if the receipt doesn’t have address information. + +**Summary of Actions in These Scenarios:** +Sales Tax:If the receipt is from a different location (same country or foreign), use the location from the receipt for sales tax calculation. +If the receipt is from a different country, don’t apply sales tax from the user's country but flag the FX discrepancy. +If the location is missing, apply the user’s location sales tax by default. + +**Depreciation:** Always apply depreciation rules based on the user’s location, regardless of where the receipt is from. +**FX (Foreign Exchange):** If the receipt is in a different currency, flag the FX difference for manual review but don’t fetch exchange rates. + + +### Tax Rules: +Four Rules for Tax and Depreciation Handling +### 1. **Sales Tax Rule** +**Purpose**: To calculate and apply the correct sales tax based on the shipping and billing addresses. +- **When Billing and Shipping Address are the Same**: Apply the sales tax rate based on the billing address. +- **When Billing and Shipping Address are Different**: Apply the sales tax rate based on the shipping address. + +**Example**: +1. If the billing and shipping address are in Ontario, the system will apply the 13% HST tax rate based on Ontario's tax rate. +2. If the billing address is in Ontario but the shipping address is in Quebec, the system will apply the 14.975% QST tax rate based on the shipping address. + +### 2. **Foreign Exchange (FX) Rule** +**Purpose**: To handle discrepancies when transactions and receipts are in different currencies (e.g., USD vs. CAD). +- **Action**: Identify the currency mismatch, but do not automatically fetch the exchange rate. Flag the FX difference for manual review, allowing the user to approve or adjust the balance. +**Example**: +1. A transaction in USD for $100, matched to a receipt in CAD for $125, results in an FX discrepancy of $25. +2. The system flags the discrepancy for manual review by the user. The user can then approve the difference or adjust the amounts manually. + +### 3. **Depreciation Rule** +**Purpose**: To calculate the depreciation for assets based on the Straight-Line Method (for accounting) or CCA Depreciation (Declining Balance) for tax purposes. +**Action**: +- Apply Straight-Line Depreciation (for accounting) across the asset’s useful life. +- Apply CCA Depreciation (for tax purposes) using a declining balance method. +**Example**: +1. Straight-Line Depreciation: An asset purchased for $10,000, with a 5-year useful life and a residual value of $1,000, will have an annual depreciation of: +- (10,000 - 1,000)/5 = 1,800 per year for 5 years. +2. CCA Depreciation: A truck purchased for $20,000, eligible for 30% CCA per year. The depreciation will be: +- Year 1: 20,000 x 30% = $6,000 +- Year 2: (20,000 - 6,000) x 30% = $4,200 +- The depreciation will decline each year as the book value reduces. + +### 4. **Meals & Entertainment Tax Deduction Rule** +**Purpose**: To apply the correct tax deduction for Meals & Entertainment expenses. +**Action**: +- For Tax Purposes: Only 50% of the total receipt amount is deductible. +- For Accounting Purposes: 100% of the total receipt amount is deductible. +- Sales Tax: The full sales tax will be deducted for accounting purposes. +**Example**: +1. A $100 meal receipt for a business dinner: +- **Tax Purposes**: Only $50 of the total amount is deductible. +- **Accounting Purposes**: The full $100 is deductible. +2. If the sales tax on the meal is $12, the entire $12 is included in the accounting deduction, but for tax purposes, the $50 deduction will reflect the adjusted amount after the 50% rule is applied. + +### **When Location on Receipt is Different from User's Location** +**1. Sales Tax**: +- **Scenario 1**: If the **receipt's location** is different (e.g., receipt from Quebec for a user in Ontario), the **sales tax** is applied based on the **receipt's location** (Quebec sales tax). +- **Scenario 2**: If the **receipt** is from a different **country** (e.g., USA), the **system flags** the **currency mismatch** but does not apply **Canadian sales tax**. + +**2. Depreciation**: +- Depreciation is always calculated based on the **user's location**, not the receipt's location. +- **Depreciation Method** for **Canada (Ontario)**: **CCA method** will apply, regardless of where the receipt comes from. + +**3. FX Handling**: +- If the receipt is in a different **currency** (e.g., USD for a CAD-based user), the system will **flag FX differences** for manual review but won’t fetch exchange rates. + +**4. General Process**: +- When the **receipt location** is different from the **user's location**, ensure that the **tax and depreciation** are correctly applied based on the **receipt's data**. +- For **foreign transactions**, ensure that **FX differences** are flagged for user review. +- For **missing location information**, apply **user’s location** by default for tax and depreciation. +''' \ No newline at end of file