from dataclasses import dataclass from datetime import datetime from typing import List, Optional from pydantic import BaseModel @dataclass class Address: """Address information for tax calculations""" province: str city: str postal_code: str country: str = "Canada" @dataclass class Receipt: id: str file_name: str upload_date: datetime receipt_date: datetime amount: float tax: float vendor: str category: str description: str # Tax rule fields billing_address: Optional[Address] = None shipping_address: Optional[Address] = None currency: str = "CAD" is_meals_entertainment: bool = False @dataclass class Transaction: id: str transaction_date: datetime amount: float vendor: str notes: str # Tax rule fields currency: str = "CAD" fx_rate: Optional[float] = None source: Optional[str] = None # e.g., "csv", "image", "manual", "api" # QuickBooks CSV fields TxnId: Optional[str] = None AccountType: Optional[str] = None AccountNumber: Optional[str] = None TransactionDate: Optional[str] = None TransactionType: Optional[str] = None ChequeNumber: Optional[str] = None Description1: Optional[str] = None Description2: Optional[str] = None VendorId: Optional[str] = None VendorName: Optional[str] = None AccountId: Optional[str] = None AccountName: Optional[str] = None @dataclass class Asset: """Asset for depreciation calculations""" id: str name: str purchase_date: datetime purchase_amount: float useful_life_years: int residual_value: float cca_rate: float # Capital Cost Allowance rate asset_class: str @dataclass class Match: receipt: Receipt transaction: Transaction confidence_score: float match_reason: str tax_analysis: Optional[dict] = None class AddressRequest(BaseModel): province: str city: str postal_code: str country: str = "Canada" class ReceiptRequest(BaseModel): id: str file_name: str upload_date: datetime receipt_date: datetime amount: float tax: float vendor: str category: str description: str # Tax rule fields billing_address: Optional[AddressRequest] = None shipping_address: Optional[AddressRequest] = None currency: str = "CAD" is_meals_entertainment: bool = False class TransactionRequest(BaseModel): id: str transaction_date: datetime amount: float vendor: str notes: str # Tax rule fields currency: str = "CAD" fx_rate: Optional[float] = None source: Optional[str] = None # e.g., "csv", "image", "manual", "api" # QuickBooks CSV fields TxnId: Optional[str] = None AccountType: Optional[str] = None AccountNumber: Optional[str] = None TransactionDate: Optional[str] = None TransactionType: Optional[str] = None ChequeNumber: Optional[str] = None Description1: Optional[str] = None Description2: Optional[str] = None VendorId: Optional[str] = None VendorName: Optional[str] = None AccountId: Optional[str] = None AccountName: Optional[str] = None class AssetRequest(BaseModel): id: str name: str purchase_date: datetime purchase_amount: float useful_life_years: int residual_value: float cca_rate: float asset_class: str class MatchingRequest(BaseModel): receipt_ids: List[str] transaction_ids: List[str] class MatchResponse(BaseModel): receipt_id: str transaction_id: str confidence_score: float match_reason: str receipt_vendor: str receipt_amount: float receipt_description: str receipt_category: str receipt_tax_amount: float transaction_vendor: str transaction_amount: float tax_analysis: Optional[dict] = None flag_for_review: Optional[bool] = None auto_approve: Optional[bool] = None # Transaction metadata transaction_source: Optional[str] = None # Source of the transaction # QuickBooks CSV fields from transaction TxnId: Optional[str] = None AccountType: Optional[str] = None AccountNumber: Optional[str] = None TransactionDate: Optional[str] = None TransactionType: Optional[str] = None ChequeNumber: Optional[str] = None Description1: Optional[str] = None Description2: Optional[str] = None VendorId: Optional[str] = None VendorName: Optional[str] = None AccountId: Optional[str] = None AccountName: Optional[str] = None Source: Optional[str] = None class MatchingResponse(BaseModel): matches: List[MatchResponse] stats: dict class ApprovalRequest(BaseModel): match_id: str approved: bool reason: Optional[str] = None class RuleRequest(BaseModel): name: str condition: str action: str source: str = "user" class DocumentUploadResponse(BaseModel): file_id: str filename: str file_type: str upload_date: datetime status: str class AIRules(BaseModel): condition: str action: str class DocumentProcessRequest(BaseModel): file_id: Optional[str] = None user_location: Optional[str] = ( None # Format: "State/Province, Country" (e.g., "Ontario, Canada") ) ai_rules: Optional[List[AIRules]] = None class DocumentProcessResponse(BaseModel): file_id: str receipt_id: str extraction_success: bool vendor: Optional[str] = None description: Optional[str] = None total_amount: Optional[float] = None tax_amount: Optional[float] = None date: Optional[str] = None category: Optional[str] = None confidence: Optional[float] = None error: Optional[str] = None receipt_currency: Optional[str] = "CAD" receipt_location: Optional[str] = ( None # Location from receipt (e.g., "Ontario, Canada" or "California, USA") ) calculated_tax: Optional[float] = None # Calculated sales tax if not clearly shown is_depreciable: Optional[bool] = None # Whether item is a depreciable asset name_of_asset: Optional[str] = None # Name/description of the asset if depreciable cca_rate: Optional[float] = ( None # CCA rate for tax depreciation (e.g., 0.30 for 30%) ) useful_life: Optional[int] = ( None # Useful life in years for straight-line depreciation ) residual_value: Optional[float] = ( None # Residual value for straight-line depreciation ) # New tax-related models class TaxCalculationRequest(BaseModel): receipt_id: str transaction_id: Optional[str] = None class TaxCalculationResponse(BaseModel): receipt_id: str rules_applied: List[str] sales_tax: dict fx_analysis: Optional[dict] = None meals_entertainment: dict class DepreciationRequest(BaseModel): asset: AssetRequest year: int method: str # "straight_line" or "cca" class DepreciationResponse(BaseModel): asset_id: str year: int method: str depreciation: float book_value: float total_depreciation: Optional[float] = None success: bool error: Optional[str] = None class CityInfo(BaseModel): """City information from user tax info""" id: int name: str state_id: int state_code: str country_id: int country_code: str latitude: Optional[str] = None longitude: Optional[str] = None class StateInfo(BaseModel): """State/Province information from user tax info""" id: int name: str country_id: int country_code: str state_code: str class CountryInfo(BaseModel): """Country information from user tax info""" id: int name: str iso3: str iso2: str phone_code: str capital: str currency: str native: Optional[str] = None region: Optional[str] = None subregion: Optional[str] = None emoji: Optional[str] = None emojiU: Optional[str] = None class UserTaxInfo(BaseModel): """User tax information for location-based tax calculations""" id: int user_id: int company_name: str tax_id: Optional[str] = "" tax_id_type: Optional[str] = "EIN" address_line_1: Optional[str] = "" address_line_2: Optional[str] = "" city: CityInfo state: StateInfo zip_postal_code: Optional[str] = "" country: CountryInfo include_on_invoices: Optional[int] = 1 created_at: Optional[str] = None updated_at: Optional[str] = None class MatchSpecificRequest(BaseModel): file_ids: List[str] categorization_id: str user_location: Optional[str] = "Canada" # Kept for backward compatibility user_tax_info: Optional[UserTaxInfo] = None ai_rules: Optional[List[AIRules]] = None