refactor: migrate templates and static files to self-contained modules
Templates Migration: - Migrate admin templates to modules (tenancy, billing, monitoring, marketplace, etc.) - Migrate vendor templates to modules (tenancy, billing, orders, messaging, etc.) - Migrate storefront templates to modules (catalog, customers, orders, cart, checkout, cms) - Migrate public templates to modules (billing, marketplace, cms) - Keep shared templates in app/templates/ (base.html, errors/, partials/, macros/) - Migrate letzshop partials to marketplace module Static Files Migration: - Migrate admin JS to modules: tenancy (23 files), core (5 files), monitoring (1 file) - Migrate vendor JS to modules: tenancy (4 files), core (2 files) - Migrate shared JS: vendor-selector.js to core, media-picker.js to cms - Migrate storefront JS: storefront-layout.js to core - Keep framework JS in static/ (api-client, utils, money, icons, log-config, lib/) - Update all template references to use module_static paths Naming Consistency: - Rename static/platform/ to static/public/ - Rename app/templates/platform/ to app/templates/public/ - Update all extends and static references Documentation: - Update module-system.md with shared templates documentation - Update frontend-structure.md with new module JS organization Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
708
app/modules/tenancy/services/vendor_service.py
Normal file
708
app/modules/tenancy/services/vendor_service.py
Normal file
@@ -0,0 +1,708 @@
|
||||
# app/modules/tenancy/services/vendor_service.py
|
||||
"""
|
||||
Vendor service for managing vendor operations and product catalog.
|
||||
|
||||
This module provides classes and functions for:
|
||||
- Vendor creation and management
|
||||
- Vendor access control and validation
|
||||
- Vendor product catalog operations
|
||||
- Vendor filtering and search
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.exceptions import ValidationException
|
||||
from app.modules.catalog.exceptions import ProductAlreadyExistsException
|
||||
from app.modules.marketplace.exceptions import MarketplaceProductNotFoundException
|
||||
from app.modules.tenancy.exceptions import (
|
||||
InvalidVendorDataException,
|
||||
UnauthorizedVendorAccessException,
|
||||
VendorAlreadyExistsException,
|
||||
VendorNotFoundException,
|
||||
)
|
||||
from app.modules.marketplace.models import MarketplaceProduct
|
||||
from app.modules.catalog.models import Product
|
||||
from models.database.user import User
|
||||
from models.database.vendor import Vendor
|
||||
from app.modules.catalog.schemas import ProductCreate
|
||||
from models.schema.vendor import VendorCreate
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VendorService:
|
||||
"""Service class for vendor operations following the application's service pattern."""
|
||||
|
||||
def create_vendor(
|
||||
self, db: Session, vendor_data: VendorCreate, current_user: User
|
||||
) -> Vendor:
|
||||
"""
|
||||
Create a new vendor under a company.
|
||||
|
||||
DEPRECATED: This method is for self-service vendor creation by company owners.
|
||||
For admin operations, use admin_service.create_vendor() instead.
|
||||
|
||||
The new architecture:
|
||||
- Companies are the business entities with owners and contact info
|
||||
- Vendors are storefronts/brands under companies
|
||||
- The company_id is required in vendor_data
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_data: Vendor creation data (must include company_id)
|
||||
current_user: User creating the vendor (must be company owner or admin)
|
||||
|
||||
Returns:
|
||||
Created vendor object
|
||||
|
||||
Raises:
|
||||
VendorAlreadyExistsException: If vendor code already exists
|
||||
UnauthorizedVendorAccessException: If user is not company owner
|
||||
InvalidVendorDataException: If vendor data is invalid
|
||||
"""
|
||||
from models.database.company import Company
|
||||
|
||||
try:
|
||||
# Validate company_id is provided
|
||||
if not hasattr(vendor_data, "company_id") or not vendor_data.company_id:
|
||||
raise InvalidVendorDataException(
|
||||
"company_id is required to create a vendor", field="company_id"
|
||||
)
|
||||
|
||||
# Get company and verify ownership
|
||||
company = (
|
||||
db.query(Company).filter(Company.id == vendor_data.company_id).first()
|
||||
)
|
||||
if not company:
|
||||
raise InvalidVendorDataException(
|
||||
f"Company with ID {vendor_data.company_id} not found",
|
||||
field="company_id",
|
||||
)
|
||||
|
||||
# Check if user is company owner or admin
|
||||
if (
|
||||
current_user.role != "admin"
|
||||
and company.owner_user_id != current_user.id
|
||||
):
|
||||
raise UnauthorizedVendorAccessException(
|
||||
f"company-{vendor_data.company_id}", current_user.id
|
||||
)
|
||||
|
||||
# Normalize vendor code to uppercase
|
||||
normalized_vendor_code = vendor_data.vendor_code.upper()
|
||||
|
||||
# Check if vendor code already exists (case-insensitive check)
|
||||
if self._vendor_code_exists(db, normalized_vendor_code):
|
||||
raise VendorAlreadyExistsException(normalized_vendor_code)
|
||||
|
||||
# Create vendor linked to company
|
||||
new_vendor = Vendor(
|
||||
company_id=company.id,
|
||||
vendor_code=normalized_vendor_code,
|
||||
subdomain=vendor_data.subdomain.lower(),
|
||||
name=vendor_data.name,
|
||||
description=vendor_data.description,
|
||||
letzshop_csv_url_fr=vendor_data.letzshop_csv_url_fr,
|
||||
letzshop_csv_url_en=vendor_data.letzshop_csv_url_en,
|
||||
letzshop_csv_url_de=vendor_data.letzshop_csv_url_de,
|
||||
is_active=True,
|
||||
is_verified=(current_user.role == "admin"),
|
||||
)
|
||||
|
||||
db.add(new_vendor)
|
||||
db.flush() # Get ID without committing - endpoint handles commit
|
||||
|
||||
logger.info(
|
||||
f"New vendor created: {new_vendor.vendor_code} under company {company.name} by {current_user.username}"
|
||||
)
|
||||
return new_vendor
|
||||
|
||||
except (
|
||||
VendorAlreadyExistsException,
|
||||
UnauthorizedVendorAccessException,
|
||||
InvalidVendorDataException,
|
||||
):
|
||||
raise # Re-raise custom exceptions - endpoint handles rollback
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating vendor: {str(e)}")
|
||||
raise ValidationException("Failed to create vendor")
|
||||
|
||||
def get_vendors(
|
||||
self,
|
||||
db: Session,
|
||||
current_user: User,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
active_only: bool = True,
|
||||
verified_only: bool = False,
|
||||
) -> tuple[list[Vendor], int]:
|
||||
"""
|
||||
Get vendors with filtering.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
current_user: Current user requesting vendors
|
||||
skip: Number of records to skip
|
||||
limit: Maximum number of records to return
|
||||
active_only: Filter for active vendors only
|
||||
verified_only: Filter for verified vendors only
|
||||
|
||||
Returns:
|
||||
Tuple of (vendors_list, total_count)
|
||||
"""
|
||||
try:
|
||||
query = db.query(Vendor)
|
||||
|
||||
# Non-admin users can only see active and verified vendors, plus their own
|
||||
if current_user.role != "admin":
|
||||
# Get vendor IDs the user owns through companies
|
||||
from models.database.company import Company
|
||||
|
||||
owned_vendor_ids = (
|
||||
db.query(Vendor.id)
|
||||
.join(Company)
|
||||
.filter(Company.owner_user_id == current_user.id)
|
||||
.subquery()
|
||||
)
|
||||
query = query.filter(
|
||||
(Vendor.is_active == True)
|
||||
& ((Vendor.is_verified == True) | (Vendor.id.in_(owned_vendor_ids)))
|
||||
)
|
||||
else:
|
||||
# Admin can apply filters
|
||||
if active_only:
|
||||
query = query.filter(Vendor.is_active == True)
|
||||
if verified_only:
|
||||
query = query.filter(Vendor.is_verified == True)
|
||||
|
||||
total = query.count()
|
||||
vendors = query.offset(skip).limit(limit).all()
|
||||
|
||||
return vendors, total
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting vendors: {str(e)}")
|
||||
raise ValidationException("Failed to retrieve vendors")
|
||||
|
||||
def get_vendor_by_code(
|
||||
self, db: Session, vendor_code: str, current_user: User
|
||||
) -> Vendor:
|
||||
"""
|
||||
Get vendor by vendor code with access control.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_code: Vendor code to find
|
||||
current_user: Current user requesting the vendor
|
||||
|
||||
Returns:
|
||||
Vendor object
|
||||
|
||||
Raises:
|
||||
VendorNotFoundException: If vendor not found
|
||||
UnauthorizedVendorAccessException: If access denied
|
||||
"""
|
||||
try:
|
||||
vendor = (
|
||||
db.query(Vendor)
|
||||
.filter(func.upper(Vendor.vendor_code) == vendor_code.upper())
|
||||
.first()
|
||||
)
|
||||
|
||||
if not vendor:
|
||||
raise VendorNotFoundException(vendor_code)
|
||||
|
||||
# Check access permissions
|
||||
if not self._can_access_vendor(vendor, current_user):
|
||||
raise UnauthorizedVendorAccessException(vendor_code, current_user.id)
|
||||
|
||||
return vendor
|
||||
|
||||
except (VendorNotFoundException, UnauthorizedVendorAccessException):
|
||||
raise # Re-raise custom exceptions
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting vendor {vendor_code}: {str(e)}")
|
||||
raise ValidationException("Failed to retrieve vendor")
|
||||
|
||||
def get_vendor_by_id(self, db: Session, vendor_id: int) -> Vendor:
|
||||
"""
|
||||
Get vendor by ID (admin use - no access control).
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_id: Vendor ID to find
|
||||
|
||||
Returns:
|
||||
Vendor object with company and owner loaded
|
||||
|
||||
Raises:
|
||||
VendorNotFoundException: If vendor not found
|
||||
"""
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from models.database.company import Company
|
||||
|
||||
vendor = (
|
||||
db.query(Vendor)
|
||||
.options(joinedload(Vendor.company).joinedload(Company.owner))
|
||||
.filter(Vendor.id == vendor_id)
|
||||
.first()
|
||||
)
|
||||
|
||||
if not vendor:
|
||||
raise VendorNotFoundException(str(vendor_id), identifier_type="id")
|
||||
|
||||
return vendor
|
||||
|
||||
def get_vendor_by_id_optional(self, db: Session, vendor_id: int) -> Vendor | None:
|
||||
"""
|
||||
Get vendor by ID, returns None if not found.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_id: Vendor ID to find
|
||||
|
||||
Returns:
|
||||
Vendor object or None if not found
|
||||
"""
|
||||
return db.query(Vendor).filter(Vendor.id == vendor_id).first()
|
||||
|
||||
def get_active_vendor_by_code(self, db: Session, vendor_code: str) -> Vendor:
|
||||
"""
|
||||
Get active vendor by vendor_code for public access (no auth required).
|
||||
|
||||
This method is specifically designed for public endpoints where:
|
||||
- No authentication is required
|
||||
- Only active vendors should be returned
|
||||
- Inactive/disabled vendors are hidden
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_code: Vendor code (case-insensitive)
|
||||
|
||||
Returns:
|
||||
Vendor object with company and owner loaded
|
||||
|
||||
Raises:
|
||||
VendorNotFoundException: If vendor not found or inactive
|
||||
"""
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from models.database.company import Company
|
||||
|
||||
vendor = (
|
||||
db.query(Vendor)
|
||||
.options(joinedload(Vendor.company).joinedload(Company.owner))
|
||||
.filter(
|
||||
func.upper(Vendor.vendor_code) == vendor_code.upper(),
|
||||
Vendor.is_active == True,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
|
||||
if not vendor:
|
||||
logger.warning(f"Vendor not found or inactive: {vendor_code}")
|
||||
raise VendorNotFoundException(vendor_code, identifier_type="code")
|
||||
|
||||
return vendor
|
||||
|
||||
def get_vendor_by_identifier(self, db: Session, identifier: str) -> Vendor:
|
||||
"""
|
||||
Get vendor by ID or vendor_code (admin use - no access control).
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
identifier: Either vendor ID (int as string) or vendor_code (string)
|
||||
|
||||
Returns:
|
||||
Vendor object with company and owner loaded
|
||||
|
||||
Raises:
|
||||
VendorNotFoundException: If vendor not found
|
||||
"""
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from models.database.company import Company
|
||||
|
||||
# Try as integer ID first
|
||||
try:
|
||||
vendor_id = int(identifier)
|
||||
return self.get_vendor_by_id(db, vendor_id)
|
||||
except (ValueError, TypeError):
|
||||
pass # Not an integer, treat as vendor_code
|
||||
except VendorNotFoundException:
|
||||
pass # ID not found, try as vendor_code
|
||||
|
||||
# Try as vendor_code (case-insensitive)
|
||||
vendor = (
|
||||
db.query(Vendor)
|
||||
.options(joinedload(Vendor.company).joinedload(Company.owner))
|
||||
.filter(func.upper(Vendor.vendor_code) == identifier.upper())
|
||||
.first()
|
||||
)
|
||||
|
||||
if not vendor:
|
||||
raise VendorNotFoundException(identifier, identifier_type="code")
|
||||
|
||||
return vendor
|
||||
|
||||
def toggle_verification(self, db: Session, vendor_id: int) -> tuple[Vendor, str]:
|
||||
"""
|
||||
Toggle vendor verification status.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_id: Vendor ID
|
||||
|
||||
Returns:
|
||||
Tuple of (updated vendor, status message)
|
||||
|
||||
Raises:
|
||||
VendorNotFoundException: If vendor not found
|
||||
"""
|
||||
vendor = self.get_vendor_by_id(db, vendor_id)
|
||||
vendor.is_verified = not vendor.is_verified
|
||||
# No commit here - endpoint handles transaction
|
||||
|
||||
status = "verified" if vendor.is_verified else "unverified"
|
||||
logger.info(f"Vendor {vendor.vendor_code} {status}")
|
||||
return vendor, f"Vendor {vendor.vendor_code} is now {status}"
|
||||
|
||||
def set_verification(
|
||||
self, db: Session, vendor_id: int, is_verified: bool
|
||||
) -> tuple[Vendor, str]:
|
||||
"""
|
||||
Set vendor verification status to specific value.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_id: Vendor ID
|
||||
is_verified: Target verification status
|
||||
|
||||
Returns:
|
||||
Tuple of (updated vendor, status message)
|
||||
|
||||
Raises:
|
||||
VendorNotFoundException: If vendor not found
|
||||
"""
|
||||
vendor = self.get_vendor_by_id(db, vendor_id)
|
||||
vendor.is_verified = is_verified
|
||||
# No commit here - endpoint handles transaction
|
||||
|
||||
status = "verified" if is_verified else "unverified"
|
||||
logger.info(f"Vendor {vendor.vendor_code} set to {status}")
|
||||
return vendor, f"Vendor {vendor.vendor_code} is now {status}"
|
||||
|
||||
def toggle_status(self, db: Session, vendor_id: int) -> tuple[Vendor, str]:
|
||||
"""
|
||||
Toggle vendor active status.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_id: Vendor ID
|
||||
|
||||
Returns:
|
||||
Tuple of (updated vendor, status message)
|
||||
|
||||
Raises:
|
||||
VendorNotFoundException: If vendor not found
|
||||
"""
|
||||
vendor = self.get_vendor_by_id(db, vendor_id)
|
||||
vendor.is_active = not vendor.is_active
|
||||
# No commit here - endpoint handles transaction
|
||||
|
||||
status = "active" if vendor.is_active else "inactive"
|
||||
logger.info(f"Vendor {vendor.vendor_code} {status}")
|
||||
return vendor, f"Vendor {vendor.vendor_code} is now {status}"
|
||||
|
||||
def set_status(
|
||||
self, db: Session, vendor_id: int, is_active: bool
|
||||
) -> tuple[Vendor, str]:
|
||||
"""
|
||||
Set vendor active status to specific value.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_id: Vendor ID
|
||||
is_active: Target active status
|
||||
|
||||
Returns:
|
||||
Tuple of (updated vendor, status message)
|
||||
|
||||
Raises:
|
||||
VendorNotFoundException: If vendor not found
|
||||
"""
|
||||
vendor = self.get_vendor_by_id(db, vendor_id)
|
||||
vendor.is_active = is_active
|
||||
# No commit here - endpoint handles transaction
|
||||
|
||||
status = "active" if is_active else "inactive"
|
||||
logger.info(f"Vendor {vendor.vendor_code} set to {status}")
|
||||
return vendor, f"Vendor {vendor.vendor_code} is now {status}"
|
||||
|
||||
def add_product_to_catalog(
|
||||
self, db: Session, vendor: Vendor, product: ProductCreate
|
||||
) -> Product:
|
||||
"""
|
||||
Add existing product to vendor catalog with vendor -specific settings.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor : Vendor to add product to
|
||||
product: Vendor product data
|
||||
|
||||
Returns:
|
||||
Created Product object
|
||||
|
||||
Raises:
|
||||
MarketplaceProductNotFoundException: If product not found
|
||||
ProductAlreadyExistsException: If product already in vendor
|
||||
"""
|
||||
try:
|
||||
# Check if product exists
|
||||
marketplace_product = self._get_product_by_id_or_raise(
|
||||
db, product.marketplace_product_id
|
||||
)
|
||||
|
||||
# Check if product already in vendor
|
||||
if self._product_in_catalog(db, vendor.id, marketplace_product.id):
|
||||
raise ProductAlreadyExistsException(
|
||||
vendor.vendor_code, product.marketplace_product_id
|
||||
)
|
||||
|
||||
# Create vendor-product association
|
||||
new_product = Product(
|
||||
vendor_id=vendor.id,
|
||||
marketplace_product_id=marketplace_product.id,
|
||||
**product.model_dump(exclude={"marketplace_product_id"}),
|
||||
)
|
||||
|
||||
db.add(new_product)
|
||||
db.flush() # Get ID without committing - endpoint handles commit
|
||||
|
||||
logger.info(
|
||||
f"MarketplaceProduct {product.marketplace_product_id} added to vendor {vendor.vendor_code}"
|
||||
)
|
||||
return new_product
|
||||
|
||||
except (MarketplaceProductNotFoundException, ProductAlreadyExistsException):
|
||||
raise # Re-raise custom exceptions - endpoint handles rollback
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding product to vendor : {str(e)}")
|
||||
raise ValidationException("Failed to add product to vendor ")
|
||||
|
||||
def get_products(
|
||||
self,
|
||||
db: Session,
|
||||
vendor: Vendor,
|
||||
current_user: User,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
active_only: bool = True,
|
||||
featured_only: bool = False,
|
||||
) -> tuple[list[Product], int]:
|
||||
"""
|
||||
Get products in vendor catalog with filtering.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor : Vendor to get products from
|
||||
current_user: Current user requesting products
|
||||
skip: Number of records to skip
|
||||
limit: Maximum number of records to return
|
||||
active_only: Filter for active products only
|
||||
featured_only: Filter for featured products only
|
||||
|
||||
Returns:
|
||||
Tuple of (products_list, total_count)
|
||||
|
||||
Raises:
|
||||
UnauthorizedVendorAccessException: If vendor access denied
|
||||
"""
|
||||
try:
|
||||
# Check access permissions
|
||||
if not self._can_access_vendor(vendor, current_user):
|
||||
raise UnauthorizedVendorAccessException(
|
||||
vendor.vendor_code, current_user.id
|
||||
)
|
||||
|
||||
# Query vendor products
|
||||
query = db.query(Product).filter(Product.vendor_id == vendor.id)
|
||||
|
||||
if active_only:
|
||||
query = query.filter(Product.is_active == True)
|
||||
if featured_only:
|
||||
query = query.filter(Product.is_featured == True)
|
||||
|
||||
total = query.count()
|
||||
products = query.offset(skip).limit(limit).all()
|
||||
|
||||
return products, total
|
||||
|
||||
except UnauthorizedVendorAccessException:
|
||||
raise # Re-raise custom exceptions
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting vendor products: {str(e)}")
|
||||
raise ValidationException("Failed to retrieve vendor products")
|
||||
|
||||
# Private helper methods
|
||||
def _vendor_code_exists(self, db: Session, vendor_code: str) -> bool:
|
||||
"""Check if vendor code already exists (case-insensitive)."""
|
||||
return (
|
||||
db.query(Vendor)
|
||||
.filter(func.upper(Vendor.vendor_code) == vendor_code.upper())
|
||||
.first()
|
||||
is not None
|
||||
)
|
||||
|
||||
def _get_product_by_id_or_raise(
|
||||
self, db: Session, marketplace_product_id: int
|
||||
) -> MarketplaceProduct:
|
||||
"""Get marketplace product by database ID or raise exception."""
|
||||
product = (
|
||||
db.query(MarketplaceProduct)
|
||||
.filter(MarketplaceProduct.id == marketplace_product_id)
|
||||
.first()
|
||||
)
|
||||
if not product:
|
||||
raise MarketplaceProductNotFoundException(str(marketplace_product_id))
|
||||
return product
|
||||
|
||||
def _product_in_catalog(
|
||||
self, db: Session, vendor_id: int, marketplace_product_id: int
|
||||
) -> bool:
|
||||
"""Check if product is already in vendor."""
|
||||
return (
|
||||
db.query(Product)
|
||||
.filter(
|
||||
Product.vendor_id == vendor_id,
|
||||
Product.marketplace_product_id == marketplace_product_id,
|
||||
)
|
||||
.first()
|
||||
is not None
|
||||
)
|
||||
|
||||
def _can_access_vendor(self, vendor: Vendor, user: User) -> bool:
|
||||
"""Check if user can access vendor."""
|
||||
# Admins can always access
|
||||
if user.role == "admin":
|
||||
return True
|
||||
|
||||
# Company owners can access their vendors
|
||||
if vendor.company and vendor.company.owner_user_id == user.id:
|
||||
return True
|
||||
|
||||
# Others can only access active and verified vendors
|
||||
return vendor.is_active and vendor.is_verified
|
||||
|
||||
def _is_vendor_owner(self, vendor: Vendor, user: User) -> bool:
|
||||
"""Check if user is vendor owner (via company ownership)."""
|
||||
return vendor.company and vendor.company.owner_user_id == user.id
|
||||
|
||||
def can_update_vendor(self, vendor: Vendor, user: User) -> bool:
|
||||
"""
|
||||
Check if user has permission to update vendor settings.
|
||||
|
||||
Permission granted to:
|
||||
- Admins (always)
|
||||
- Vendor owners (company owner)
|
||||
- Team members with appropriate role (owner role in VendorUser)
|
||||
"""
|
||||
# Admins can always update
|
||||
if user.role == "admin":
|
||||
return True
|
||||
|
||||
# Check if user is vendor owner via company
|
||||
if self._is_vendor_owner(vendor, user):
|
||||
return True
|
||||
|
||||
# Check if user is owner via VendorUser relationship
|
||||
if user.is_owner_of(vendor.id):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def update_vendor(
|
||||
self,
|
||||
db: Session,
|
||||
vendor_id: int,
|
||||
vendor_update,
|
||||
current_user: User,
|
||||
) -> "Vendor":
|
||||
"""
|
||||
Update vendor profile with permission checking.
|
||||
|
||||
Raises:
|
||||
VendorNotFoundException: If vendor not found
|
||||
InsufficientPermissionsException: If user lacks permission
|
||||
"""
|
||||
from app.modules.tenancy.exceptions import InsufficientPermissionsException
|
||||
|
||||
vendor = self.get_vendor_by_id(db, vendor_id)
|
||||
|
||||
# Check permissions in service layer
|
||||
if not self.can_update_vendor(vendor, current_user):
|
||||
raise InsufficientPermissionsException(
|
||||
required_permission="vendor:profile:update"
|
||||
)
|
||||
|
||||
# Apply updates
|
||||
update_data = vendor_update.model_dump(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
if hasattr(vendor, field):
|
||||
setattr(vendor, field, value)
|
||||
|
||||
db.add(vendor)
|
||||
db.flush()
|
||||
db.refresh(vendor)
|
||||
return vendor
|
||||
|
||||
def update_marketplace_settings(
|
||||
self,
|
||||
db: Session,
|
||||
vendor_id: int,
|
||||
marketplace_config: dict,
|
||||
current_user: User,
|
||||
) -> dict:
|
||||
"""
|
||||
Update marketplace integration settings with permission checking.
|
||||
|
||||
Raises:
|
||||
VendorNotFoundException: If vendor not found
|
||||
InsufficientPermissionsException: If user lacks permission
|
||||
"""
|
||||
from app.modules.tenancy.exceptions import InsufficientPermissionsException
|
||||
|
||||
vendor = self.get_vendor_by_id(db, vendor_id)
|
||||
|
||||
# Check permissions in service layer
|
||||
if not self.can_update_vendor(vendor, current_user):
|
||||
raise InsufficientPermissionsException(
|
||||
required_permission="vendor:settings:update"
|
||||
)
|
||||
|
||||
# Update Letzshop URLs
|
||||
if "letzshop_csv_url_fr" in marketplace_config:
|
||||
vendor.letzshop_csv_url_fr = marketplace_config["letzshop_csv_url_fr"]
|
||||
if "letzshop_csv_url_en" in marketplace_config:
|
||||
vendor.letzshop_csv_url_en = marketplace_config["letzshop_csv_url_en"]
|
||||
if "letzshop_csv_url_de" in marketplace_config:
|
||||
vendor.letzshop_csv_url_de = marketplace_config["letzshop_csv_url_de"]
|
||||
|
||||
db.add(vendor)
|
||||
db.flush()
|
||||
db.refresh(vendor)
|
||||
|
||||
return {
|
||||
"message": "Marketplace settings updated successfully",
|
||||
"letzshop_csv_url_fr": vendor.letzshop_csv_url_fr,
|
||||
"letzshop_csv_url_en": vendor.letzshop_csv_url_en,
|
||||
"letzshop_csv_url_de": vendor.letzshop_csv_url_de,
|
||||
}
|
||||
|
||||
|
||||
# Create service instance following the same pattern as other services
|
||||
vendor_service = VendorService()
|
||||
Reference in New Issue
Block a user