diff --git a/app/database.py b/app/database.py index 0e7abda..f4eb9e2 100644 --- a/app/database.py +++ b/app/database.py @@ -91,6 +91,7 @@ class DBReceipt(Base): receipt_location = Column(String, nullable=True) calculated_tax = Column(Float, nullable=True) is_depreciable = Column(String, nullable=True) # Store as string "True"/"False" + name_of_asset = Column(String, nullable=True) # Name/description of the asset cca_rate = Column(Float, nullable=True) useful_life = Column(Integer, nullable=True) residual_value = Column(Float, nullable=True) diff --git a/app/main.py b/app/main.py index 32761d5..92d2199 100644 --- a/app/main.py +++ b/app/main.py @@ -369,7 +369,9 @@ async def upload_multiple_documents( response_model=DocumentProcessResponse, tags=["Document Processing"], ) -async def process_document(file_id: str, request: DocumentProcessRequest, db: db_dependency): +async def process_document( + file_id: str, request: DocumentProcessRequest, db: db_dependency +): """ Process a previously uploaded document to extract receipt information. @@ -383,9 +385,7 @@ async def process_document(file_id: str, request: DocumentProcessRequest, db: db # Get file info from database db_uploaded_file = get_uploaded_file_from_db(db, file_id) if not db_uploaded_file: - raise HTTPException( - status_code=404, detail=f"File {file_id} not found" - ) + raise HTTPException(status_code=404, detail=f"File {file_id} not found") # Process the file using the stored file path receipt_data = await document_processor.process_file( @@ -423,6 +423,7 @@ async def process_document(file_id: str, request: DocumentProcessRequest, db: db is_depreciable=str(receipt_data.get("is_depreciable")) if receipt_data.get("is_depreciable") is not None else None, + name_of_asset=receipt_data.get("name_of_asset"), cca_rate=receipt_data.get("cca_rate"), useful_life=receipt_data.get("useful_life"), residual_value=receipt_data.get("residual_value"), @@ -448,6 +449,7 @@ async def process_document(file_id: str, request: DocumentProcessRequest, db: db receipt_location=receipt_data.get("location"), calculated_tax=receipt_data.get("calculated_tax"), is_depreciable=receipt_data.get("is_depreciable"), + name_of_asset=receipt_data.get("name_of_asset"), cca_rate=receipt_data.get("cca_rate"), useful_life=receipt_data.get("useful_life"), residual_value=receipt_data.get("residual_value"), diff --git a/app/schemas.py b/app/schemas.py index 61a438e..43003e8 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -185,6 +185,7 @@ class DocumentProcessResponse(BaseModel): ) 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%) ) diff --git a/app/services/document_processor.py b/app/services/document_processor.py index d6acd45..ac16e03 100644 --- a/app/services/document_processor.py +++ b/app/services/document_processor.py @@ -77,6 +77,7 @@ class DocumentProcessor: "location": "Province/State, Country", "calculated_tax": 0.00, "is_depreciable": false, + "name_of_asset": null, "cca_rate": null, "useful_life": null, "residual_value": null @@ -117,6 +118,7 @@ class DocumentProcessor: - Determine if item is a depreciable asset (vehicles, machinery, equipment, computers, furniture, buildings) - Set is_depreciable to true only for capital assets, false for consumables/services - If is_depreciable is true, provide: + * name_of_asset: Specific name/model of the asset (e.g., "2024 Honda Accord", "Dell Laptop XPS 15", "Office Desk") * cca_rate: CCA rate as decimal (e.g., 0.30 for 30%, 0.20 for 20%, 0.04 for 4%) - Class 10 (Vehicles): 30% - Class 8 (Furniture, equipment): 20% @@ -125,7 +127,7 @@ class DocumentProcessor: - Class 10.1 (Passenger vehicles >$30k): 30% * useful_life: Expected years of use (e.g., 5 for computers, 8 for vehicles, 10 for furniture) * residual_value: Estimated value at end of life (typically 10% of purchase price for equipment, 20% for vehicles) - - If is_depreciable is false, set cca_rate, useful_life, and residual_value to null + - If is_depreciable is false, set name_of_asset, cca_rate, useful_life, and residual_value to null Return only valid JSON. """ @@ -233,6 +235,7 @@ class DocumentProcessor: "location": "Province/State, Country", "calculated_tax": 0.00, "is_depreciable": false, + "name_of_asset": null, "cca_rate": null, "useful_life": null, "residual_value": null @@ -273,6 +276,7 @@ class DocumentProcessor: - Determine if item is a depreciable asset (vehicles, machinery, equipment, computers, furniture, buildings) - Set is_depreciable to true only for capital assets, false for consumables/services - If is_depreciable is true, provide: + * name_of_asset: Specific name/model of the asset (e.g., "2024 Honda Accord", "Dell Laptop XPS 15", "Office Desk") * cca_rate: CCA rate as decimal (e.g., 0.30 for 30%, 0.20 for 20%, 0.04 for 4%) - Class 10 (Vehicles): 30% - Class 8 (Furniture, equipment): 20% @@ -281,7 +285,7 @@ class DocumentProcessor: - Class 10.1 (Passenger vehicles >$30k): 30% * useful_life: Expected years of use (e.g., 5 for computers, 8 for vehicles, 10 for furniture) * residual_value: Estimated value at end of life (typically 10% of purchase price for equipment, 20% for vehicles) - - If is_depreciable is false, set cca_rate, useful_life, and residual_value to null + - If is_depreciable is false, set name_of_asset, cca_rate, useful_life, and residual_value to null Return only valid JSON. """ @@ -349,6 +353,9 @@ class DocumentProcessor: is_depreciable_match = re.search( r'"is_depreciable"\s*:\s*(true|false)', json_str ) + name_of_asset_match = re.search( + r'"name_of_asset"\s*:\s*"([^"]*)"', json_str + ) cca_rate_match = re.search( r'"cca_rate"\s*:\s*([0-9.]+|null)', json_str ) @@ -388,6 +395,9 @@ class DocumentProcessor: "is_depreciable": is_depreciable_match.group(1) == "true" if is_depreciable_match else None, + "name_of_asset": name_of_asset_match.group(1) + if name_of_asset_match + else None, "cca_rate": float(cca_rate_match.group(1)) if cca_rate_match and cca_rate_match.group(1) != "null" else None, @@ -414,6 +424,7 @@ class DocumentProcessor: "location": data.get("location"), "calculated_tax": data.get("calculated_tax"), "is_depreciable": data.get("is_depreciable"), + "name_of_asset": data.get("name_of_asset"), "cca_rate": data.get("cca_rate"), "useful_life": data.get("useful_life"), "residual_value": data.get("residual_value"), @@ -489,6 +500,7 @@ class DocumentProcessor: "location": None, "calculated_tax": None, "is_depreciable": None, + "name_of_asset": None, "cca_rate": None, "useful_life": None, "residual_value": None, @@ -508,6 +520,7 @@ class DocumentProcessor: "location": None, "calculated_tax": None, "is_depreciable": None, + "name_of_asset": None, "cca_rate": None, "useful_life": None, "residual_value": None,