- Add database migration to make vendor.owner_user_id nullable - Update Vendor model to support company-based ownership (DEPRECATED vendor.owner_user_id) - Implement company_service with singleton pattern (consistent with vendor_service) - Create Company model with proper relationships to vendors and users - Add company exception classes for proper error handling - Refactor companies API to use singleton service pattern Architecture Change: - OLD: Each vendor has its own owner (vendor.owner_user_id) - NEW: Vendors belong to a company, company has one owner (company.owner_user_id) - This allows one company owner to manage multiple vendor brands Technical Details: - Company service uses singleton pattern (not factory) - Company service accepts db: Session as parameter (follows SVC-003) - Uses AuthManager for password hashing (consistent with admin_service) - Added _generate_temp_password() helper method
129 lines
3.9 KiB
Python
129 lines
3.9 KiB
Python
# app/exceptions/company.py
|
|
"""
|
|
Company management specific exceptions.
|
|
"""
|
|
|
|
from typing import Any
|
|
|
|
from .base import (
|
|
AuthorizationException,
|
|
BusinessLogicException,
|
|
ConflictException,
|
|
ResourceNotFoundException,
|
|
ValidationException,
|
|
)
|
|
|
|
|
|
class CompanyNotFoundException(ResourceNotFoundException):
|
|
"""Raised when a company is not found."""
|
|
|
|
def __init__(self, company_identifier: str | int, identifier_type: str = "id"):
|
|
if identifier_type.lower() == "id":
|
|
message = f"Company with ID '{company_identifier}' not found"
|
|
else:
|
|
message = f"Company with name '{company_identifier}' not found"
|
|
|
|
super().__init__(
|
|
resource_type="Company",
|
|
identifier=str(company_identifier),
|
|
message=message,
|
|
error_code="COMPANY_NOT_FOUND",
|
|
)
|
|
|
|
|
|
class CompanyAlreadyExistsException(ConflictException):
|
|
"""Raised when trying to create a company that already exists."""
|
|
|
|
def __init__(self, company_name: str):
|
|
super().__init__(
|
|
message=f"Company with name '{company_name}' already exists",
|
|
error_code="COMPANY_ALREADY_EXISTS",
|
|
details={"company_name": company_name},
|
|
)
|
|
|
|
|
|
class CompanyNotActiveException(BusinessLogicException):
|
|
"""Raised when trying to perform operations on inactive company."""
|
|
|
|
def __init__(self, company_id: int):
|
|
super().__init__(
|
|
message=f"Company with ID '{company_id}' is not active",
|
|
error_code="COMPANY_NOT_ACTIVE",
|
|
details={"company_id": company_id},
|
|
)
|
|
|
|
|
|
class CompanyNotVerifiedException(BusinessLogicException):
|
|
"""Raised when trying to perform operations requiring verified company."""
|
|
|
|
def __init__(self, company_id: int):
|
|
super().__init__(
|
|
message=f"Company with ID '{company_id}' is not verified",
|
|
error_code="COMPANY_NOT_VERIFIED",
|
|
details={"company_id": company_id},
|
|
)
|
|
|
|
|
|
class UnauthorizedCompanyAccessException(AuthorizationException):
|
|
"""Raised when user tries to access company they don't own."""
|
|
|
|
def __init__(self, company_id: int, user_id: int | None = None):
|
|
details = {"company_id": company_id}
|
|
if user_id:
|
|
details["user_id"] = user_id
|
|
|
|
super().__init__(
|
|
message=f"Unauthorized access to company with ID '{company_id}'",
|
|
error_code="UNAUTHORIZED_COMPANY_ACCESS",
|
|
details=details,
|
|
)
|
|
|
|
|
|
class InvalidCompanyDataException(ValidationException):
|
|
"""Raised when company data is invalid or incomplete."""
|
|
|
|
def __init__(
|
|
self,
|
|
message: str = "Invalid company data",
|
|
field: str | None = None,
|
|
details: dict[str, Any] | None = None,
|
|
):
|
|
super().__init__(
|
|
message=message,
|
|
field=field,
|
|
details=details,
|
|
)
|
|
self.error_code = "INVALID_COMPANY_DATA"
|
|
|
|
|
|
class CompanyValidationException(ValidationException):
|
|
"""Raised when company validation fails."""
|
|
|
|
def __init__(
|
|
self,
|
|
message: str = "Company validation failed",
|
|
field: str | None = None,
|
|
validation_errors: dict[str, str] | None = None,
|
|
):
|
|
details = {}
|
|
if validation_errors:
|
|
details["validation_errors"] = validation_errors
|
|
|
|
super().__init__(
|
|
message=message,
|
|
field=field,
|
|
details=details,
|
|
)
|
|
self.error_code = "COMPANY_VALIDATION_FAILED"
|
|
|
|
|
|
class CompanyHasVendorsException(BusinessLogicException):
|
|
"""Raised when trying to delete a company that still has active vendors."""
|
|
|
|
def __init__(self, company_id: int, vendor_count: int):
|
|
super().__init__(
|
|
message=f"Cannot delete company with ID '{company_id}' because it has {vendor_count} associated vendor(s)",
|
|
error_code="COMPANY_HAS_VENDORS",
|
|
details={"company_id": company_id, "vendor_count": vendor_count},
|
|
)
|