Files
2025-10-29 14:27:44 +00:00

352 lines
8.5 KiB
Python

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