fix: resolve architecture validation violations
- Add invoice exceptions module with proper exception hierarchy - Replace HTTPException with service-layer exceptions in invoice API - Add InvoicePDFGeneratedResponse and InvoiceStatsResponse Pydantic models - Replace db.commit() with db.flush() in services for proper transaction control - Update invoice service to use exceptions from app/exceptions/invoice.py All 14 errors and 14 warnings are now resolved. Validation passes with only INFO-level findings. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -90,6 +90,18 @@ from .customer import (
|
||||
InvalidCustomerCredentialsException,
|
||||
)
|
||||
|
||||
# Invoice exceptions
|
||||
from .invoice import (
|
||||
InvoiceNotFoundException,
|
||||
InvoicePDFGenerationException,
|
||||
InvoicePDFNotFoundException,
|
||||
InvoiceSettingsAlreadyExistException,
|
||||
InvoiceSettingsNotFoundException,
|
||||
InvoiceValidationException,
|
||||
InvalidInvoiceStatusTransitionException,
|
||||
OrderNotFoundException as OrderNotFoundForInvoiceException,
|
||||
)
|
||||
|
||||
# Inventory exceptions
|
||||
from .inventory import (
|
||||
InsufficientInventoryException,
|
||||
@@ -272,6 +284,15 @@ __all__ = [
|
||||
"TeamValidationException",
|
||||
"InvalidInvitationDataException",
|
||||
"InvalidInvitationTokenException",
|
||||
# Invoice exceptions
|
||||
"InvoiceNotFoundException",
|
||||
"InvoiceSettingsNotFoundException",
|
||||
"InvoiceSettingsAlreadyExistException",
|
||||
"InvoiceValidationException",
|
||||
"InvoicePDFGenerationException",
|
||||
"InvoicePDFNotFoundException",
|
||||
"InvalidInvoiceStatusTransitionException",
|
||||
"OrderNotFoundForInvoiceException",
|
||||
# Inventory exceptions
|
||||
"InventoryNotFoundException",
|
||||
"InsufficientInventoryException",
|
||||
|
||||
118
app/exceptions/invoice.py
Normal file
118
app/exceptions/invoice.py
Normal file
@@ -0,0 +1,118 @@
|
||||
# app/exceptions/invoice.py
|
||||
"""
|
||||
Invoice-related exceptions.
|
||||
|
||||
This module provides exception classes for invoice operations including:
|
||||
- Invoice not found errors
|
||||
- Invoice settings validation
|
||||
- PDF generation errors
|
||||
- Invoice status transitions
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .base import BusinessLogicException, ResourceNotFoundException, WizamartException
|
||||
|
||||
|
||||
class InvoiceNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when an invoice is not found."""
|
||||
|
||||
def __init__(self, invoice_id: int | str):
|
||||
super().__init__(
|
||||
resource_type="Invoice",
|
||||
identifier=str(invoice_id),
|
||||
error_code="INVOICE_NOT_FOUND",
|
||||
)
|
||||
|
||||
|
||||
class InvoiceSettingsNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when invoice settings are not found for a vendor."""
|
||||
|
||||
def __init__(self, vendor_id: int):
|
||||
super().__init__(
|
||||
resource_type="InvoiceSettings",
|
||||
identifier=str(vendor_id),
|
||||
message="Invoice settings not found. Create settings first.",
|
||||
error_code="INVOICE_SETTINGS_NOT_FOUND",
|
||||
)
|
||||
|
||||
|
||||
class InvoiceSettingsAlreadyExistException(WizamartException):
|
||||
"""Raised when trying to create invoice settings that already exist."""
|
||||
|
||||
def __init__(self, vendor_id: int):
|
||||
super().__init__(
|
||||
message=f"Invoice settings already exist for vendor {vendor_id}",
|
||||
error_code="INVOICE_SETTINGS_ALREADY_EXIST",
|
||||
status_code=409,
|
||||
details={"vendor_id": vendor_id},
|
||||
)
|
||||
|
||||
|
||||
class InvoiceValidationException(BusinessLogicException):
|
||||
"""Raised when invoice data validation fails."""
|
||||
|
||||
def __init__(self, message: str, details: dict[str, Any] | None = None):
|
||||
super().__init__(
|
||||
message=message,
|
||||
error_code="INVOICE_VALIDATION_ERROR",
|
||||
details=details,
|
||||
)
|
||||
|
||||
|
||||
class InvoicePDFGenerationException(WizamartException):
|
||||
"""Raised when PDF generation fails."""
|
||||
|
||||
def __init__(self, invoice_id: int, reason: str):
|
||||
super().__init__(
|
||||
message=f"Failed to generate PDF for invoice {invoice_id}: {reason}",
|
||||
error_code="INVOICE_PDF_GENERATION_FAILED",
|
||||
status_code=500,
|
||||
details={"invoice_id": invoice_id, "reason": reason},
|
||||
)
|
||||
|
||||
|
||||
class InvoicePDFNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when invoice PDF file is not found."""
|
||||
|
||||
def __init__(self, invoice_id: int):
|
||||
super().__init__(
|
||||
resource_type="InvoicePDF",
|
||||
identifier=str(invoice_id),
|
||||
message="PDF file not found. Generate the PDF first.",
|
||||
error_code="INVOICE_PDF_NOT_FOUND",
|
||||
)
|
||||
|
||||
|
||||
class InvalidInvoiceStatusTransitionException(BusinessLogicException):
|
||||
"""Raised when an invalid invoice status transition is attempted."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
current_status: str,
|
||||
new_status: str,
|
||||
reason: str | None = None,
|
||||
):
|
||||
message = f"Cannot change invoice status from '{current_status}' to '{new_status}'"
|
||||
if reason:
|
||||
message += f": {reason}"
|
||||
|
||||
super().__init__(
|
||||
message=message,
|
||||
error_code="INVALID_INVOICE_STATUS_TRANSITION",
|
||||
details={
|
||||
"current_status": current_status,
|
||||
"new_status": new_status,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class OrderNotFoundException(ResourceNotFoundException):
|
||||
"""Raised when an order for invoice creation is not found."""
|
||||
|
||||
def __init__(self, order_id: int):
|
||||
super().__init__(
|
||||
resource_type="Order",
|
||||
identifier=str(order_id),
|
||||
error_code="ORDER_NOT_FOUND_FOR_INVOICE",
|
||||
)
|
||||
Reference in New Issue
Block a user