refactor: enforce strict architecture rules and add Pydantic response models
- Update architecture rules to be stricter (API-003 now blocks ALL exception raising in endpoints, not just HTTPException) - Update get_current_vendor_api dependency to guarantee token_vendor_id presence - Remove redundant _get_vendor_from_token helpers from all vendor API files - Move vendor access validation to service layer methods - Add Pydantic response models for media, notification, and payment endpoints - Add get_active_vendor_by_code service method for public vendor lookup - Add get_import_job_for_vendor service method with vendor validation - Update validation script to detect exception raising patterns in endpoints 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -275,7 +275,9 @@ def get_current_vendor_api(
|
||||
Get current vendor user from Authorization header ONLY.
|
||||
|
||||
Used for vendor API endpoints that should not accept cookies.
|
||||
Validates that user still has access to the vendor specified in the token.
|
||||
Validates that:
|
||||
1. Token contains vendor context (token_vendor_id)
|
||||
2. User still has access to the vendor specified in the token
|
||||
|
||||
Args:
|
||||
credentials: Bearer token from Authorization header
|
||||
@@ -285,7 +287,7 @@ def get_current_vendor_api(
|
||||
User: Authenticated vendor user (with token_vendor_id, token_vendor_code, token_vendor_role)
|
||||
|
||||
Raises:
|
||||
InvalidTokenException: If no token or invalid token
|
||||
InvalidTokenException: If no token, invalid token, or missing vendor context
|
||||
InsufficientPermissionsException: If user is not vendor or lost access to vendor
|
||||
"""
|
||||
if not credentials:
|
||||
@@ -302,23 +304,25 @@ def get_current_vendor_api(
|
||||
logger.warning(f"Non-vendor user {user.username} attempted vendor API")
|
||||
raise InsufficientPermissionsException("Vendor privileges required")
|
||||
|
||||
# Validate vendor access if token is vendor-scoped
|
||||
if hasattr(user, "token_vendor_id"):
|
||||
vendor_id = user.token_vendor_id
|
||||
# Require vendor context in token
|
||||
if not hasattr(user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
|
||||
# Verify user still has access to this vendor
|
||||
if not user.is_member_of(vendor_id):
|
||||
logger.warning(
|
||||
f"User {user.username} lost access to vendor_id={vendor_id}"
|
||||
)
|
||||
raise InsufficientPermissionsException(
|
||||
"Access to vendor has been revoked. Please login again."
|
||||
)
|
||||
vendor_id = user.token_vendor_id
|
||||
|
||||
logger.debug(
|
||||
f"Vendor API access: user={user.username}, vendor_id={vendor_id}, "
|
||||
f"vendor_code={getattr(user, 'token_vendor_code', 'N/A')}"
|
||||
# Verify user still has access to this vendor
|
||||
if not user.is_member_of(vendor_id):
|
||||
logger.warning(
|
||||
f"User {user.username} lost access to vendor_id={vendor_id}"
|
||||
)
|
||||
raise InsufficientPermissionsException(
|
||||
"Access to vendor has been revoked. Please login again."
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
f"Vendor API access: user={user.username}, vendor_id={vendor_id}, "
|
||||
f"vendor_code={getattr(user, 'token_vendor_code', 'N/A')}"
|
||||
)
|
||||
|
||||
return user
|
||||
|
||||
|
||||
@@ -21,7 +21,11 @@ from models.schema.admin import (
|
||||
PlatformAlertCreate,
|
||||
PlatformAlertListResponse,
|
||||
PlatformAlertResolve,
|
||||
PlatformAlertResponse,
|
||||
)
|
||||
from models.schema.notification import (
|
||||
AlertStatisticsResponse,
|
||||
MessageResponse,
|
||||
UnreadCountResponse,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/notifications")
|
||||
@@ -49,17 +53,17 @@ def get_notifications(
|
||||
)
|
||||
|
||||
|
||||
@router.get("/unread-count")
|
||||
@router.get("/unread-count", response_model=UnreadCountResponse)
|
||||
def get_unread_count(
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_api),
|
||||
):
|
||||
"""Get count of unread notifications."""
|
||||
# TODO: Implement
|
||||
return {"unread_count": 0}
|
||||
return UnreadCountResponse(unread_count=0)
|
||||
|
||||
|
||||
@router.put("/{notification_id}/read")
|
||||
@router.put("/{notification_id}/read", response_model=MessageResponse)
|
||||
def mark_as_read(
|
||||
notification_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
@@ -67,17 +71,17 @@ def mark_as_read(
|
||||
):
|
||||
"""Mark notification as read."""
|
||||
# TODO: Implement
|
||||
return {"message": "Notification marked as read"}
|
||||
return MessageResponse(message="Notification marked as read")
|
||||
|
||||
|
||||
@router.put("/mark-all-read")
|
||||
@router.put("/mark-all-read", response_model=MessageResponse)
|
||||
def mark_all_as_read(
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_api),
|
||||
):
|
||||
"""Mark all notifications as read."""
|
||||
# TODO: Implement
|
||||
return {"message": "All notifications marked as read"}
|
||||
return MessageResponse(message="All notifications marked as read")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
@@ -101,19 +105,19 @@ def get_platform_alerts(
|
||||
)
|
||||
|
||||
|
||||
@router.post("/alerts", response_model=PlatformAlertResponse)
|
||||
@router.post("/alerts", response_model=MessageResponse)
|
||||
def create_platform_alert(
|
||||
alert_data: PlatformAlertCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_api),
|
||||
):
|
||||
"""Create new platform alert (manual)."""
|
||||
# TODO: Implement
|
||||
# TODO: Implement - return PlatformAlertResponse when service is ready
|
||||
logger.info(f"Admin {current_admin.username} created alert: {alert_data.title}")
|
||||
return {}
|
||||
return MessageResponse(message="Platform alert creation coming soon")
|
||||
|
||||
|
||||
@router.put("/alerts/{alert_id}/resolve")
|
||||
@router.put("/alerts/{alert_id}/resolve", response_model=MessageResponse)
|
||||
def resolve_platform_alert(
|
||||
alert_id: int,
|
||||
resolve_data: PlatformAlertResolve,
|
||||
@@ -123,19 +127,19 @@ def resolve_platform_alert(
|
||||
"""Resolve platform alert."""
|
||||
# TODO: Implement
|
||||
logger.info(f"Admin {current_admin.username} resolved alert {alert_id}")
|
||||
return {"message": "Alert resolved successfully"}
|
||||
return MessageResponse(message="Alert resolved successfully")
|
||||
|
||||
|
||||
@router.get("/alerts/stats")
|
||||
@router.get("/alerts/stats", response_model=AlertStatisticsResponse)
|
||||
def get_alert_statistics(
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: User = Depends(get_current_admin_api),
|
||||
):
|
||||
"""Get alert statistics for dashboard."""
|
||||
# TODO: Implement
|
||||
return {
|
||||
"total_alerts": 0,
|
||||
"active_alerts": 0,
|
||||
"critical_alerts": 0,
|
||||
"resolved_today": 0,
|
||||
}
|
||||
return AlertStatisticsResponse(
|
||||
total_alerts=0,
|
||||
active_alerts=0,
|
||||
critical_alerts=0,
|
||||
resolved_today=0,
|
||||
)
|
||||
|
||||
14
app/api/v1/vendor/analytics.py
vendored
14
app/api/v1/vendor/analytics.py
vendored
@@ -2,7 +2,8 @@
|
||||
"""
|
||||
Vendor analytics and reporting endpoints.
|
||||
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern)
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern).
|
||||
The get_current_vendor_api dependency guarantees token_vendor_id is present.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -12,7 +13,6 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_vendor_api
|
||||
from app.core.database import get_db
|
||||
from app.exceptions import InvalidTokenException
|
||||
from app.services.stats_service import stats_service
|
||||
from models.database.user import User
|
||||
|
||||
@@ -20,13 +20,6 @@ router = APIRouter(prefix="/analytics")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_vendor_id_from_token(current_user: User) -> int:
|
||||
"""Helper to get vendor_id from JWT token."""
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
return current_user.token_vendor_id
|
||||
|
||||
|
||||
@router.get("")
|
||||
def get_vendor_analytics(
|
||||
period: str = Query("30d", description="Time period: 7d, 30d, 90d, 1y"),
|
||||
@@ -34,5 +27,4 @@ def get_vendor_analytics(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Get vendor analytics data for specified time period."""
|
||||
vendor_id = _get_vendor_id_from_token(current_user)
|
||||
return stats_service.get_vendor_analytics(db, vendor_id, period)
|
||||
return stats_service.get_vendor_analytics(db, current_user.token_vendor_id, period)
|
||||
|
||||
24
app/api/v1/vendor/customers.py
vendored
24
app/api/v1/vendor/customers.py
vendored
@@ -1,9 +1,9 @@
|
||||
# Vendor customer management
|
||||
# app/api/v1/vendor/customers.py
|
||||
"""
|
||||
Vendor customer management endpoints.
|
||||
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern)
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern).
|
||||
The get_current_vendor_api dependency guarantees token_vendor_id is present.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -13,7 +13,6 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_vendor_api
|
||||
from app.core.database import get_db
|
||||
from app.exceptions import InvalidTokenException
|
||||
from app.services.vendor_service import vendor_service
|
||||
from models.database.user import User
|
||||
|
||||
@@ -21,13 +20,6 @@ router = APIRouter(prefix="/customers")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_vendor_from_token(current_user: User, db: Session):
|
||||
"""Helper to get vendor from JWT token."""
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
return vendor_service.get_vendor_by_id(db, current_user.token_vendor_id)
|
||||
|
||||
|
||||
@router.get("")
|
||||
def get_vendor_customers(
|
||||
skip: int = Query(0, ge=0),
|
||||
@@ -46,7 +38,7 @@ def get_vendor_customers(
|
||||
- Support filtering by active status
|
||||
- Return paginated results
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return {
|
||||
"customers": [],
|
||||
"total": 0,
|
||||
@@ -71,7 +63,7 @@ def get_customer_details(
|
||||
- Include order history
|
||||
- Include total spent, etc.
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return {"message": "Customer details coming in Slice 4"}
|
||||
|
||||
|
||||
@@ -89,7 +81,7 @@ def get_customer_orders(
|
||||
- Filter by vendor_id
|
||||
- Return order details
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return {"orders": [], "message": "Customer orders coming in Slice 5"}
|
||||
|
||||
|
||||
@@ -108,7 +100,7 @@ def update_customer(
|
||||
- Verify customer belongs to vendor
|
||||
- Update customer preferences
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return {"message": "Customer update coming in Slice 4"}
|
||||
|
||||
|
||||
@@ -126,7 +118,7 @@ def toggle_customer_status(
|
||||
- Verify customer belongs to vendor
|
||||
- Log the change
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return {"message": "Customer status toggle coming in Slice 4"}
|
||||
|
||||
|
||||
@@ -145,7 +137,7 @@ def get_customer_statistics(
|
||||
- Average order value
|
||||
- Last order date
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return {
|
||||
"total_orders": 0,
|
||||
"total_spent": 0.0,
|
||||
|
||||
9
app/api/v1/vendor/dashboard.py
vendored
9
app/api/v1/vendor/dashboard.py
vendored
@@ -1,6 +1,9 @@
|
||||
# app/api/v1/vendor/dashboard.py
|
||||
"""
|
||||
Vendor dashboard and statistics endpoints.
|
||||
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern).
|
||||
The get_current_vendor_api dependency guarantees token_vendor_id is present.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -10,7 +13,7 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_vendor_api
|
||||
from app.core.database import get_db
|
||||
from app.exceptions import InvalidTokenException, VendorNotActiveException
|
||||
from app.exceptions import VendorNotActiveException
|
||||
from app.services.stats_service import stats_service
|
||||
from app.services.vendor_service import vendor_service
|
||||
from models.database.user import User
|
||||
@@ -37,10 +40,6 @@ def get_vendor_dashboard_stats(
|
||||
Vendor is determined from the JWT token (vendor_id claim).
|
||||
Requires Authorization header (API endpoint).
|
||||
"""
|
||||
# Get vendor ID from token (set by get_current_vendor_api)
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
|
||||
vendor_id = current_user.token_vendor_id
|
||||
|
||||
# Get vendor object (raises VendorNotFoundException if not found)
|
||||
|
||||
36
app/api/v1/vendor/info.py
vendored
36
app/api/v1/vendor/info.py
vendored
@@ -10,48 +10,16 @@ This module provides:
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, Depends, Path
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.exceptions import VendorNotFoundException
|
||||
from models.database.vendor import Vendor
|
||||
from app.services.vendor_service import vendor_service
|
||||
from models.schema.vendor import VendorDetailResponse
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_vendor_by_code(db: Session, vendor_code: str) -> Vendor:
|
||||
"""
|
||||
Helper to get active vendor by vendor_code.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
vendor_code: Vendor code (case-insensitive)
|
||||
|
||||
Returns:
|
||||
Vendor object
|
||||
|
||||
Raises:
|
||||
VendorNotFoundException: If vendor not found or inactive
|
||||
"""
|
||||
vendor = (
|
||||
db.query(Vendor)
|
||||
.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
|
||||
|
||||
|
||||
@router.get("/{vendor_code}", response_model=VendorDetailResponse)
|
||||
def get_vendor_info(
|
||||
vendor_code: str = Path(..., description="Vendor code"),
|
||||
@@ -81,7 +49,7 @@ def get_vendor_info(
|
||||
"""
|
||||
logger.info(f"Public vendor info request: {vendor_code}")
|
||||
|
||||
vendor = _get_vendor_by_code(db, vendor_code)
|
||||
vendor = vendor_service.get_active_vendor_by_code(db, vendor_code)
|
||||
|
||||
logger.info(f"Vendor info retrieved: {vendor.name} ({vendor.vendor_code})")
|
||||
|
||||
|
||||
38
app/api/v1/vendor/inventory.py
vendored
38
app/api/v1/vendor/inventory.py
vendored
@@ -2,7 +2,8 @@
|
||||
"""
|
||||
Vendor inventory management endpoints.
|
||||
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern)
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern).
|
||||
The get_current_vendor_api dependency guarantees token_vendor_id is present.
|
||||
"""
|
||||
import logging
|
||||
|
||||
@@ -11,7 +12,6 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_vendor_api
|
||||
from app.core.database import get_db
|
||||
from app.exceptions import InvalidTokenException
|
||||
from app.services.inventory_service import inventory_service
|
||||
from models.database.user import User
|
||||
from models.schema.inventory import (
|
||||
@@ -28,13 +28,6 @@ router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_vendor_id_from_token(current_user: User) -> int:
|
||||
"""Helper to get vendor_id from JWT token."""
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
return current_user.token_vendor_id
|
||||
|
||||
|
||||
@router.post("/inventory/set", response_model=InventoryResponse)
|
||||
def set_inventory(
|
||||
inventory: InventoryCreate,
|
||||
@@ -42,8 +35,7 @@ def set_inventory(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Set exact inventory quantity (replaces existing)."""
|
||||
vendor_id = _get_vendor_id_from_token(current_user)
|
||||
return inventory_service.set_inventory(db, vendor_id, inventory)
|
||||
return inventory_service.set_inventory(db, current_user.token_vendor_id, inventory)
|
||||
|
||||
|
||||
@router.post("/inventory/adjust", response_model=InventoryResponse)
|
||||
@@ -53,8 +45,7 @@ def adjust_inventory(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Adjust inventory (positive to add, negative to remove)."""
|
||||
vendor_id = _get_vendor_id_from_token(current_user)
|
||||
return inventory_service.adjust_inventory(db, vendor_id, adjustment)
|
||||
return inventory_service.adjust_inventory(db, current_user.token_vendor_id, adjustment)
|
||||
|
||||
|
||||
@router.post("/inventory/reserve", response_model=InventoryResponse)
|
||||
@@ -64,8 +55,7 @@ def reserve_inventory(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Reserve inventory for an order."""
|
||||
vendor_id = _get_vendor_id_from_token(current_user)
|
||||
return inventory_service.reserve_inventory(db, vendor_id, reservation)
|
||||
return inventory_service.reserve_inventory(db, current_user.token_vendor_id, reservation)
|
||||
|
||||
|
||||
@router.post("/inventory/release", response_model=InventoryResponse)
|
||||
@@ -75,8 +65,7 @@ def release_reservation(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Release reserved inventory (cancel order)."""
|
||||
vendor_id = _get_vendor_id_from_token(current_user)
|
||||
return inventory_service.release_reservation(db, vendor_id, reservation)
|
||||
return inventory_service.release_reservation(db, current_user.token_vendor_id, reservation)
|
||||
|
||||
|
||||
@router.post("/inventory/fulfill", response_model=InventoryResponse)
|
||||
@@ -86,8 +75,7 @@ def fulfill_reservation(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Fulfill reservation (complete order, remove from stock)."""
|
||||
vendor_id = _get_vendor_id_from_token(current_user)
|
||||
return inventory_service.fulfill_reservation(db, vendor_id, reservation)
|
||||
return inventory_service.fulfill_reservation(db, current_user.token_vendor_id, reservation)
|
||||
|
||||
|
||||
@router.get("/inventory/product/{product_id}", response_model=ProductInventorySummary)
|
||||
@@ -97,8 +85,7 @@ def get_product_inventory(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Get inventory summary for a product."""
|
||||
vendor_id = _get_vendor_id_from_token(current_user)
|
||||
return inventory_service.get_product_inventory(db, vendor_id, product_id)
|
||||
return inventory_service.get_product_inventory(db, current_user.token_vendor_id, product_id)
|
||||
|
||||
|
||||
@router.get("/inventory", response_model=InventoryListResponse)
|
||||
@@ -111,9 +98,8 @@ def get_vendor_inventory(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Get all inventory for vendor."""
|
||||
vendor_id = _get_vendor_id_from_token(current_user)
|
||||
inventories = inventory_service.get_vendor_inventory(
|
||||
db, vendor_id, skip, limit, location, low_stock
|
||||
db, current_user.token_vendor_id, skip, limit, location, low_stock
|
||||
)
|
||||
|
||||
# Get total count
|
||||
@@ -132,9 +118,8 @@ def update_inventory(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Update inventory entry."""
|
||||
vendor_id = _get_vendor_id_from_token(current_user)
|
||||
return inventory_service.update_inventory(
|
||||
db, vendor_id, inventory_id, inventory_update
|
||||
db, current_user.token_vendor_id, inventory_id, inventory_update
|
||||
)
|
||||
|
||||
|
||||
@@ -145,6 +130,5 @@ def delete_inventory(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Delete inventory entry."""
|
||||
vendor_id = _get_vendor_id_from_token(current_user)
|
||||
inventory_service.delete_inventory(db, vendor_id, inventory_id)
|
||||
inventory_service.delete_inventory(db, current_user.token_vendor_id, inventory_id)
|
||||
return {"message": "Inventory deleted successfully"}
|
||||
|
||||
26
app/api/v1/vendor/marketplace.py
vendored
26
app/api/v1/vendor/marketplace.py
vendored
@@ -2,7 +2,8 @@
|
||||
"""
|
||||
Marketplace import endpoints for vendors.
|
||||
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern)
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern).
|
||||
The get_current_vendor_api dependency guarantees token_vendor_id is present.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -12,7 +13,6 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_vendor_api
|
||||
from app.core.database import get_db
|
||||
from app.exceptions import InvalidTokenException, UnauthorizedVendorAccessException
|
||||
from app.services.marketplace_import_job_service import marketplace_import_job_service
|
||||
from app.services.vendor_service import vendor_service
|
||||
from app.tasks.background_tasks import process_marketplace_import
|
||||
@@ -27,13 +27,6 @@ router = APIRouter(prefix="/marketplace")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_vendor_from_token(current_user: User, db: Session):
|
||||
"""Helper to get vendor from JWT token."""
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
return vendor_service.get_vendor_by_id(db, current_user.token_vendor_id)
|
||||
|
||||
|
||||
@router.post("/import", response_model=MarketplaceImportJobResponse)
|
||||
@rate_limit(max_requests=10, window_seconds=3600)
|
||||
async def import_products_from_marketplace(
|
||||
@@ -43,7 +36,7 @@ async def import_products_from_marketplace(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Import products from marketplace CSV with background processing (Protected)."""
|
||||
vendor = _get_vendor_from_token(current_user, db)
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id)
|
||||
|
||||
logger.info(
|
||||
f"Starting marketplace import: {request.marketplace} for vendor {vendor.vendor_code} "
|
||||
@@ -90,13 +83,10 @@ def get_marketplace_import_status(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Get status of marketplace import job (Protected)."""
|
||||
vendor = _get_vendor_from_token(current_user, db)
|
||||
|
||||
job = marketplace_import_job_service.get_import_job_by_id(db, job_id, current_user)
|
||||
|
||||
# Verify job belongs to current vendor
|
||||
if job.vendor_id != vendor.id:
|
||||
raise UnauthorizedVendorAccessException(vendor.vendor_code, current_user.id)
|
||||
# Service validates that job belongs to vendor and raises UnauthorizedVendorAccessException if not
|
||||
job = marketplace_import_job_service.get_import_job_for_vendor(
|
||||
db, job_id, current_user.token_vendor_id
|
||||
)
|
||||
|
||||
return marketplace_import_job_service.convert_to_response_model(job)
|
||||
|
||||
@@ -110,7 +100,7 @@ def get_marketplace_import_jobs(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Get marketplace import jobs for current vendor (Protected)."""
|
||||
vendor = _get_vendor_from_token(current_user, db)
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id)
|
||||
|
||||
jobs = marketplace_import_job_service.get_import_jobs(
|
||||
db=db,
|
||||
|
||||
107
app/api/v1/vendor/media.py
vendored
107
app/api/v1/vendor/media.py
vendored
@@ -1,9 +1,9 @@
|
||||
# File and media management
|
||||
# app/api/v1/vendor/media.py
|
||||
"""
|
||||
Vendor media and file management endpoints.
|
||||
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern)
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern).
|
||||
The get_current_vendor_api dependency guarantees token_vendor_id is present.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -13,22 +13,23 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_vendor_api
|
||||
from app.core.database import get_db
|
||||
from app.exceptions import InvalidTokenException
|
||||
from app.services.vendor_service import vendor_service
|
||||
from models.database.user import User
|
||||
from models.schema.media import (
|
||||
MediaDetailResponse,
|
||||
MediaListResponse,
|
||||
MediaMetadataUpdate,
|
||||
MediaUploadResponse,
|
||||
MediaUsageResponse,
|
||||
MultipleUploadResponse,
|
||||
OptimizationResultResponse,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/media")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_vendor_from_token(current_user: User, db: Session):
|
||||
"""Helper to get vendor from JWT token."""
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
return vendor_service.get_vendor_by_id(db, current_user.token_vendor_id)
|
||||
|
||||
|
||||
@router.get("")
|
||||
@router.get("", response_model=MediaListResponse)
|
||||
def get_media_library(
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(100, ge=1, le=1000),
|
||||
@@ -47,17 +48,17 @@ def get_media_library(
|
||||
- Support pagination
|
||||
- Return file URLs, sizes, metadata
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {
|
||||
"media": [],
|
||||
"total": 0,
|
||||
"skip": skip,
|
||||
"limit": limit,
|
||||
"message": "Media library coming in Slice 3",
|
||||
}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return MediaListResponse(
|
||||
media=[],
|
||||
total=0,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
message="Media library coming in Slice 3",
|
||||
)
|
||||
|
||||
|
||||
@router.post("/upload")
|
||||
@router.post("/upload", response_model=MediaUploadResponse)
|
||||
async def upload_media(
|
||||
file: UploadFile = File(...),
|
||||
folder: str | None = Query(None, description="products, general, etc."),
|
||||
@@ -75,15 +76,15 @@ async def upload_media(
|
||||
- Save metadata to database
|
||||
- Return file URL
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {
|
||||
"file_url": None,
|
||||
"thumbnail_url": None,
|
||||
"message": "Media upload coming in Slice 3",
|
||||
}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return MediaUploadResponse(
|
||||
file_url=None,
|
||||
thumbnail_url=None,
|
||||
message="Media upload coming in Slice 3",
|
||||
)
|
||||
|
||||
|
||||
@router.post("/upload/multiple")
|
||||
@router.post("/upload/multiple", response_model=MultipleUploadResponse)
|
||||
async def upload_multiple_media(
|
||||
files: list[UploadFile] = File(...),
|
||||
folder: str | None = Query(None),
|
||||
@@ -99,15 +100,15 @@ async def upload_multiple_media(
|
||||
- Return list of uploaded file URLs
|
||||
- Handle errors gracefully
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {
|
||||
"uploaded_files": [],
|
||||
"failed_files": [],
|
||||
"message": "Multiple upload coming in Slice 3",
|
||||
}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return MultipleUploadResponse(
|
||||
uploaded_files=[],
|
||||
failed_files=[],
|
||||
message="Multiple upload coming in Slice 3",
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{media_id}")
|
||||
@router.get("/{media_id}", response_model=MediaDetailResponse)
|
||||
def get_media_details(
|
||||
media_id: int,
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
@@ -121,14 +122,14 @@ def get_media_details(
|
||||
- Return file URL
|
||||
- Return usage information (which products use this file)
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Media details coming in Slice 3"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return MediaDetailResponse(message="Media details coming in Slice 3")
|
||||
|
||||
|
||||
@router.put("/{media_id}")
|
||||
@router.put("/{media_id}", response_model=MediaDetailResponse)
|
||||
def update_media_metadata(
|
||||
media_id: int,
|
||||
metadata: dict,
|
||||
metadata: MediaMetadataUpdate,
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
@@ -141,11 +142,11 @@ def update_media_metadata(
|
||||
- Update tags/categories
|
||||
- Update description
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Media update coming in Slice 3"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return MediaDetailResponse(message="Media update coming in Slice 3")
|
||||
|
||||
|
||||
@router.delete("/{media_id}")
|
||||
@router.delete("/{media_id}", response_model=MediaDetailResponse)
|
||||
def delete_media(
|
||||
media_id: int,
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
@@ -161,11 +162,11 @@ def delete_media(
|
||||
- Delete database record
|
||||
- Return success/error
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Media deletion coming in Slice 3"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return MediaDetailResponse(message="Media deletion coming in Slice 3")
|
||||
|
||||
|
||||
@router.get("/{media_id}/usage")
|
||||
@router.get("/{media_id}/usage", response_model=MediaUsageResponse)
|
||||
def get_media_usage(
|
||||
media_id: int,
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
@@ -179,15 +180,15 @@ def get_media_usage(
|
||||
- Check other entities using this media
|
||||
- Return list of usage
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {
|
||||
"products": [],
|
||||
"other_usage": [],
|
||||
"message": "Media usage tracking coming in Slice 3",
|
||||
}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return MediaUsageResponse(
|
||||
products=[],
|
||||
other_usage=[],
|
||||
message="Media usage tracking coming in Slice 3",
|
||||
)
|
||||
|
||||
|
||||
@router.post("/optimize/{media_id}")
|
||||
@router.post("/optimize/{media_id}", response_model=OptimizationResultResponse)
|
||||
def optimize_media(
|
||||
media_id: int,
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
@@ -202,5 +203,5 @@ def optimize_media(
|
||||
- Keep original
|
||||
- Update database with new versions
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Media optimization coming in Slice 3"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return OptimizationResultResponse(message="Media optimization coming in Slice 3")
|
||||
|
||||
110
app/api/v1/vendor/notifications.py
vendored
110
app/api/v1/vendor/notifications.py
vendored
@@ -1,9 +1,9 @@
|
||||
# Notification management
|
||||
# app/api/v1/vendor/notifications.py
|
||||
"""
|
||||
Vendor notification management endpoints.
|
||||
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern)
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern).
|
||||
The get_current_vendor_api dependency guarantees token_vendor_id is present.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -13,22 +13,24 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_vendor_api
|
||||
from app.core.database import get_db
|
||||
from app.exceptions import InvalidTokenException
|
||||
from app.services.vendor_service import vendor_service
|
||||
from models.database.user import User
|
||||
from models.schema.notification import (
|
||||
MessageResponse,
|
||||
NotificationListResponse,
|
||||
NotificationSettingsResponse,
|
||||
NotificationSettingsUpdate,
|
||||
NotificationTemplateListResponse,
|
||||
NotificationTemplateUpdate,
|
||||
TestNotificationRequest,
|
||||
UnreadCountResponse,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/notifications")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_vendor_from_token(current_user: User, db: Session):
|
||||
"""Helper to get vendor from JWT token."""
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
return vendor_service.get_vendor_by_id(db, current_user.token_vendor_id)
|
||||
|
||||
|
||||
@router.get("")
|
||||
@router.get("", response_model=NotificationListResponse)
|
||||
def get_notifications(
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(50, ge=1, le=100),
|
||||
@@ -45,16 +47,16 @@ def get_notifications(
|
||||
- Support pagination
|
||||
- Return notification details
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {
|
||||
"notifications": [],
|
||||
"total": 0,
|
||||
"unread_count": 0,
|
||||
"message": "Notifications coming in Slice 5",
|
||||
}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return NotificationListResponse(
|
||||
notifications=[],
|
||||
total=0,
|
||||
unread_count=0,
|
||||
message="Notifications coming in Slice 5",
|
||||
)
|
||||
|
||||
|
||||
@router.get("/unread-count")
|
||||
@router.get("/unread-count", response_model=UnreadCountResponse)
|
||||
def get_unread_count(
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
@@ -66,11 +68,11 @@ def get_unread_count(
|
||||
- Count unread notifications for vendor
|
||||
- Used for notification badge
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"unread_count": 0, "message": "Unread count coming in Slice 5"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return UnreadCountResponse(unread_count=0, message="Unread count coming in Slice 5")
|
||||
|
||||
|
||||
@router.put("/{notification_id}/read")
|
||||
@router.put("/{notification_id}/read", response_model=MessageResponse)
|
||||
def mark_as_read(
|
||||
notification_id: int,
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
@@ -83,11 +85,11 @@ def mark_as_read(
|
||||
- Mark single notification as read
|
||||
- Update read timestamp
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Mark as read coming in Slice 5"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return MessageResponse(message="Mark as read coming in Slice 5")
|
||||
|
||||
|
||||
@router.put("/mark-all-read")
|
||||
@router.put("/mark-all-read", response_model=MessageResponse)
|
||||
def mark_all_as_read(
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
@@ -99,11 +101,11 @@ def mark_all_as_read(
|
||||
- Mark all vendor notifications as read
|
||||
- Update timestamps
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Mark all as read coming in Slice 5"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return MessageResponse(message="Mark all as read coming in Slice 5")
|
||||
|
||||
|
||||
@router.delete("/{notification_id}")
|
||||
@router.delete("/{notification_id}", response_model=MessageResponse)
|
||||
def delete_notification(
|
||||
notification_id: int,
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
@@ -116,11 +118,11 @@ def delete_notification(
|
||||
- Delete single notification
|
||||
- Verify notification belongs to vendor
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Notification deletion coming in Slice 5"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return MessageResponse(message="Notification deletion coming in Slice 5")
|
||||
|
||||
|
||||
@router.get("/settings")
|
||||
@router.get("/settings", response_model=NotificationSettingsResponse)
|
||||
def get_notification_settings(
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
@@ -133,18 +135,18 @@ def get_notification_settings(
|
||||
- Get in-app notification settings
|
||||
- Get notification types enabled/disabled
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {
|
||||
"email_notifications": True,
|
||||
"in_app_notifications": True,
|
||||
"notification_types": {},
|
||||
"message": "Notification settings coming in Slice 5",
|
||||
}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return NotificationSettingsResponse(
|
||||
email_notifications=True,
|
||||
in_app_notifications=True,
|
||||
notification_types={},
|
||||
message="Notification settings coming in Slice 5",
|
||||
)
|
||||
|
||||
|
||||
@router.put("/settings")
|
||||
@router.put("/settings", response_model=MessageResponse)
|
||||
def update_notification_settings(
|
||||
settings: dict,
|
||||
settings: NotificationSettingsUpdate,
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
@@ -156,11 +158,11 @@ def update_notification_settings(
|
||||
- Update in-app notification settings
|
||||
- Enable/disable specific notification types
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Notification settings update coming in Slice 5"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return MessageResponse(message="Notification settings update coming in Slice 5")
|
||||
|
||||
|
||||
@router.get("/templates")
|
||||
@router.get("/templates", response_model=NotificationTemplateListResponse)
|
||||
def get_notification_templates(
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
@@ -173,14 +175,16 @@ def get_notification_templates(
|
||||
- Include: order confirmation, shipping notification, etc.
|
||||
- Return template details
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"templates": [], "message": "Notification templates coming in Slice 5"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return NotificationTemplateListResponse(
|
||||
templates=[], message="Notification templates coming in Slice 5"
|
||||
)
|
||||
|
||||
|
||||
@router.put("/templates/{template_id}")
|
||||
@router.put("/templates/{template_id}", response_model=MessageResponse)
|
||||
def update_notification_template(
|
||||
template_id: int,
|
||||
template_data: dict,
|
||||
template_data: NotificationTemplateUpdate,
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
@@ -193,13 +197,13 @@ def update_notification_template(
|
||||
- Validate template variables
|
||||
- Preview template
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Template update coming in Slice 5"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return MessageResponse(message="Template update coming in Slice 5")
|
||||
|
||||
|
||||
@router.post("/test")
|
||||
@router.post("/test", response_model=MessageResponse)
|
||||
def send_test_notification(
|
||||
notification_data: dict,
|
||||
notification_data: TestNotificationRequest,
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
@@ -211,5 +215,5 @@ def send_test_notification(
|
||||
- Use specified template
|
||||
- Send to current user's email
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Test notification coming in Slice 5"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return MessageResponse(message="Test notification coming in Slice 5")
|
||||
|
||||
47
app/api/v1/vendor/orders.py
vendored
47
app/api/v1/vendor/orders.py
vendored
@@ -1,6 +1,9 @@
|
||||
# app/api/v1/vendor/orders.py
|
||||
"""
|
||||
Vendor order management endpoints.
|
||||
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern).
|
||||
The get_current_vendor_api dependency guarantees token_vendor_id is present.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -42,20 +45,9 @@ def get_vendor_orders(
|
||||
Vendor is determined from JWT token (vendor_id claim).
|
||||
Requires Authorization header (API endpoint).
|
||||
"""
|
||||
from fastapi import HTTPException
|
||||
|
||||
# Get vendor ID from token
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Token missing vendor information. Please login again.",
|
||||
)
|
||||
|
||||
vendor_id = current_user.token_vendor_id
|
||||
|
||||
orders, total = order_service.get_vendor_orders(
|
||||
db=db,
|
||||
vendor_id=vendor_id,
|
||||
vendor_id=current_user.token_vendor_id,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
status=status,
|
||||
@@ -81,18 +73,9 @@ def get_order_details(
|
||||
|
||||
Requires Authorization header (API endpoint).
|
||||
"""
|
||||
from fastapi import HTTPException
|
||||
|
||||
# Get vendor ID from token
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Token missing vendor information. Please login again.",
|
||||
)
|
||||
|
||||
vendor_id = current_user.token_vendor_id
|
||||
|
||||
order = order_service.get_order(db=db, vendor_id=vendor_id, order_id=order_id)
|
||||
order = order_service.get_order(
|
||||
db=db, vendor_id=current_user.token_vendor_id, order_id=order_id
|
||||
)
|
||||
|
||||
return OrderDetailResponse.model_validate(order)
|
||||
|
||||
@@ -117,19 +100,11 @@ def update_order_status(
|
||||
|
||||
Requires Authorization header (API endpoint).
|
||||
"""
|
||||
from fastapi import HTTPException
|
||||
|
||||
# Get vendor ID from token
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Token missing vendor information. Please login again.",
|
||||
)
|
||||
|
||||
vendor_id = current_user.token_vendor_id
|
||||
|
||||
order = order_service.update_order_status(
|
||||
db=db, vendor_id=vendor_id, order_id=order_id, order_update=order_update
|
||||
db=db,
|
||||
vendor_id=current_user.token_vendor_id,
|
||||
order_id=order_id,
|
||||
order_update=order_update,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
|
||||
116
app/api/v1/vendor/payments.py
vendored
116
app/api/v1/vendor/payments.py
vendored
@@ -1,9 +1,9 @@
|
||||
# Payment configuration and processing
|
||||
# app/api/v1/vendor/payments.py
|
||||
"""
|
||||
Vendor payment configuration and processing endpoints.
|
||||
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern)
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern).
|
||||
The get_current_vendor_api dependency guarantees token_vendor_id is present.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -13,22 +13,27 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_vendor_api
|
||||
from app.core.database import get_db
|
||||
from app.exceptions import InvalidTokenException
|
||||
from app.services.vendor_service import vendor_service
|
||||
from models.database.user import User
|
||||
from models.schema.payment import (
|
||||
PaymentBalanceResponse,
|
||||
PaymentConfigResponse,
|
||||
PaymentConfigUpdate,
|
||||
PaymentConfigUpdateResponse,
|
||||
PaymentMethodsResponse,
|
||||
RefundRequest,
|
||||
RefundResponse,
|
||||
StripeConnectRequest,
|
||||
StripeConnectResponse,
|
||||
StripeDisconnectResponse,
|
||||
TransactionsResponse,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/payments")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_vendor_from_token(current_user: User, db: Session):
|
||||
"""Helper to get vendor from JWT token."""
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
return vendor_service.get_vendor_by_id(db, current_user.token_vendor_id)
|
||||
|
||||
|
||||
@router.get("/config")
|
||||
@router.get("/config", response_model=PaymentConfigResponse)
|
||||
def get_payment_configuration(
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
@@ -42,19 +47,19 @@ def get_payment_configuration(
|
||||
- Get currency settings
|
||||
- Return masked/secure information only
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {
|
||||
"payment_gateway": None,
|
||||
"accepted_methods": [],
|
||||
"currency": "EUR",
|
||||
"stripe_connected": False,
|
||||
"message": "Payment configuration coming in Slice 5",
|
||||
}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return PaymentConfigResponse(
|
||||
payment_gateway=None,
|
||||
accepted_methods=[],
|
||||
currency="EUR",
|
||||
stripe_connected=False,
|
||||
message="Payment configuration coming in Slice 5",
|
||||
)
|
||||
|
||||
|
||||
@router.put("/config")
|
||||
@router.put("/config", response_model=PaymentConfigUpdateResponse)
|
||||
def update_payment_configuration(
|
||||
payment_config: dict,
|
||||
payment_config: PaymentConfigUpdate,
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
@@ -67,13 +72,15 @@ def update_payment_configuration(
|
||||
- Update accepted payment methods
|
||||
- Validate configuration before saving
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Payment configuration update coming in Slice 5"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return PaymentConfigUpdateResponse(
|
||||
message="Payment configuration update coming in Slice 5"
|
||||
)
|
||||
|
||||
|
||||
@router.post("/stripe/connect")
|
||||
@router.post("/stripe/connect", response_model=StripeConnectResponse)
|
||||
def connect_stripe_account(
|
||||
stripe_data: dict,
|
||||
stripe_data: StripeConnectRequest,
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
@@ -86,11 +93,11 @@ def connect_stripe_account(
|
||||
- Verify Stripe account is active
|
||||
- Enable payment processing
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Stripe connection coming in Slice 5"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return StripeConnectResponse(message="Stripe connection coming in Slice 5")
|
||||
|
||||
|
||||
@router.delete("/stripe/disconnect")
|
||||
@router.delete("/stripe/disconnect", response_model=StripeDisconnectResponse)
|
||||
def disconnect_stripe_account(
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
@@ -103,11 +110,11 @@ def disconnect_stripe_account(
|
||||
- Disable payment processing
|
||||
- Warn about pending payments
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Stripe disconnection coming in Slice 5"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return StripeDisconnectResponse(message="Stripe disconnection coming in Slice 5")
|
||||
|
||||
|
||||
@router.get("/methods")
|
||||
@router.get("/methods", response_model=PaymentMethodsResponse)
|
||||
def get_payment_methods(
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
@@ -119,11 +126,14 @@ def get_payment_methods(
|
||||
- Return list of enabled payment methods
|
||||
- Include: credit card, PayPal, bank transfer, etc.
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"methods": [], "message": "Payment methods coming in Slice 5"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return PaymentMethodsResponse(
|
||||
methods=[],
|
||||
message="Payment methods coming in Slice 5",
|
||||
)
|
||||
|
||||
|
||||
@router.get("/transactions")
|
||||
@router.get("/transactions", response_model=TransactionsResponse)
|
||||
def get_payment_transactions(
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
@@ -137,15 +147,15 @@ def get_payment_transactions(
|
||||
- Include payment details
|
||||
- Support pagination
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {
|
||||
"transactions": [],
|
||||
"total": 0,
|
||||
"message": "Payment transactions coming in Slice 5",
|
||||
}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return TransactionsResponse(
|
||||
transactions=[],
|
||||
total=0,
|
||||
message="Payment transactions coming in Slice 5",
|
||||
)
|
||||
|
||||
|
||||
@router.get("/balance")
|
||||
@router.get("/balance", response_model=PaymentBalanceResponse)
|
||||
def get_payment_balance(
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
@@ -159,20 +169,20 @@ def get_payment_balance(
|
||||
- Get next payout date
|
||||
- Get payout history
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {
|
||||
"available_balance": 0.0,
|
||||
"pending_balance": 0.0,
|
||||
"currency": "EUR",
|
||||
"next_payout_date": None,
|
||||
"message": "Payment balance coming in Slice 5",
|
||||
}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return PaymentBalanceResponse(
|
||||
available_balance=0.0,
|
||||
pending_balance=0.0,
|
||||
currency="EUR",
|
||||
next_payout_date=None,
|
||||
message="Payment balance coming in Slice 5",
|
||||
)
|
||||
|
||||
|
||||
@router.post("/refund/{payment_id}")
|
||||
@router.post("/refund/{payment_id}", response_model=RefundResponse)
|
||||
def refund_payment(
|
||||
payment_id: int,
|
||||
refund_data: dict,
|
||||
refund_data: RefundRequest,
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
@@ -185,5 +195,5 @@ def refund_payment(
|
||||
- Update order status
|
||||
- Send refund notification to customer
|
||||
"""
|
||||
vendor = _get_vendor_from_token(current_user, db) # noqa: F841 - vendor will be used when implemented
|
||||
return {"message": "Payment refund coming in Slice 5"}
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id) # noqa: F841
|
||||
return RefundResponse(message="Payment refund coming in Slice 5")
|
||||
|
||||
77
app/api/v1/vendor/products.py
vendored
77
app/api/v1/vendor/products.py
vendored
@@ -1,6 +1,9 @@
|
||||
# app/api/v1/vendor/products.py
|
||||
"""
|
||||
Vendor product catalog management endpoints.
|
||||
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern).
|
||||
The get_current_vendor_api dependency guarantees token_vendor_id is present.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -10,7 +13,6 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_vendor_api
|
||||
from app.core.database import get_db
|
||||
from app.exceptions import InvalidTokenException
|
||||
from app.services.product_service import product_service
|
||||
from models.database.user import User
|
||||
from models.schema.product import (
|
||||
@@ -45,15 +47,9 @@ def get_vendor_products(
|
||||
|
||||
Vendor is determined from JWT token (vendor_id claim).
|
||||
"""
|
||||
# Get vendor ID from token
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
|
||||
vendor_id = current_user.token_vendor_id
|
||||
|
||||
products, total = product_service.get_vendor_products(
|
||||
db=db,
|
||||
vendor_id=vendor_id,
|
||||
vendor_id=current_user.token_vendor_id,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
is_active=is_active,
|
||||
@@ -75,14 +71,8 @@ def get_product_details(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Get detailed product information including inventory."""
|
||||
# Get vendor ID from token
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
|
||||
vendor_id = current_user.token_vendor_id
|
||||
|
||||
product = product_service.get_product(
|
||||
db=db, vendor_id=vendor_id, product_id=product_id
|
||||
db=db, vendor_id=current_user.token_vendor_id, product_id=product_id
|
||||
)
|
||||
|
||||
return ProductDetailResponse.model_validate(product)
|
||||
@@ -99,14 +89,8 @@ def add_product_to_catalog(
|
||||
|
||||
This publishes a MarketplaceProduct to the vendor's public catalog.
|
||||
"""
|
||||
# Get vendor ID from token
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
|
||||
vendor_id = current_user.token_vendor_id
|
||||
|
||||
product = product_service.create_product(
|
||||
db=db, vendor_id=vendor_id, product_data=product_data
|
||||
db=db, vendor_id=current_user.token_vendor_id, product_data=product_data
|
||||
)
|
||||
|
||||
logger.info(
|
||||
@@ -125,14 +109,11 @@ def update_product(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Update product in vendor catalog."""
|
||||
# Get vendor ID from token
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
|
||||
vendor_id = current_user.token_vendor_id
|
||||
|
||||
product = product_service.update_product(
|
||||
db=db, vendor_id=vendor_id, product_id=product_id, product_update=product_data
|
||||
db=db,
|
||||
vendor_id=current_user.token_vendor_id,
|
||||
product_id=product_id,
|
||||
product_update=product_data,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
@@ -150,13 +131,9 @@ def remove_product_from_catalog(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Remove product from vendor catalog."""
|
||||
# Get vendor ID from token
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
|
||||
vendor_id = current_user.token_vendor_id
|
||||
|
||||
product_service.delete_product(db=db, vendor_id=vendor_id, product_id=product_id)
|
||||
product_service.delete_product(
|
||||
db=db, vendor_id=current_user.token_vendor_id, product_id=product_id
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Product {product_id} removed from catalog by user {current_user.username} "
|
||||
@@ -177,18 +154,12 @@ def publish_from_marketplace(
|
||||
|
||||
Shortcut endpoint for publishing directly from marketplace import.
|
||||
"""
|
||||
# Get vendor ID from token
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
|
||||
vendor_id = current_user.token_vendor_id
|
||||
|
||||
product_data = ProductCreate(
|
||||
marketplace_product_id=marketplace_product_id, is_active=True
|
||||
)
|
||||
|
||||
product = product_service.create_product(
|
||||
db=db, vendor_id=vendor_id, product_data=product_data
|
||||
db=db, vendor_id=current_user.token_vendor_id, product_data=product_data
|
||||
)
|
||||
|
||||
logger.info(
|
||||
@@ -206,13 +177,9 @@ def toggle_product_active(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Toggle product active status."""
|
||||
# Get vendor ID from token
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
|
||||
vendor_id = current_user.token_vendor_id
|
||||
|
||||
product = product_service.get_product(db, vendor_id, product_id)
|
||||
product = product_service.get_product(
|
||||
db, current_user.token_vendor_id, product_id
|
||||
)
|
||||
|
||||
product.is_active = not product.is_active
|
||||
db.commit()
|
||||
@@ -231,13 +198,9 @@ def toggle_product_featured(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Toggle product featured status."""
|
||||
# Get vendor ID from token
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
|
||||
vendor_id = current_user.token_vendor_id
|
||||
|
||||
product = product_service.get_product(db, vendor_id, product_id)
|
||||
product = product_service.get_product(
|
||||
db, current_user.token_vendor_id, product_id
|
||||
)
|
||||
|
||||
product.is_featured = not product.is_featured
|
||||
db.commit()
|
||||
|
||||
24
app/api/v1/vendor/profile.py
vendored
24
app/api/v1/vendor/profile.py
vendored
@@ -2,7 +2,8 @@
|
||||
"""
|
||||
Vendor profile management endpoints.
|
||||
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern)
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern).
|
||||
The get_current_vendor_api dependency guarantees token_vendor_id is present.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -12,7 +13,6 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_vendor_api
|
||||
from app.core.database import get_db
|
||||
from app.exceptions import InsufficientPermissionsException, InvalidTokenException
|
||||
from app.services.vendor_service import vendor_service
|
||||
from models.database.user import User
|
||||
from models.schema.vendor import VendorResponse, VendorUpdate
|
||||
@@ -21,20 +21,13 @@ router = APIRouter(prefix="/profile")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_vendor_from_token(current_user: User, db: Session):
|
||||
"""Helper to get vendor from JWT token."""
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
return vendor_service.get_vendor_by_id(db, current_user.token_vendor_id)
|
||||
|
||||
|
||||
@router.get("", response_model=VendorResponse)
|
||||
def get_vendor_profile(
|
||||
current_user: User = Depends(get_current_vendor_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Get current vendor profile information."""
|
||||
vendor = _get_vendor_from_token(current_user, db)
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id)
|
||||
return vendor
|
||||
|
||||
|
||||
@@ -45,10 +38,7 @@ def update_vendor_profile(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Update vendor profile information."""
|
||||
vendor = _get_vendor_from_token(current_user, db)
|
||||
|
||||
# Verify user has permission to update vendor
|
||||
if not vendor_service.can_update_vendor(vendor, current_user):
|
||||
raise InsufficientPermissionsException(required_permission="vendor:profile:update")
|
||||
|
||||
return vendor_service.update_vendor(db, vendor.id, vendor_update)
|
||||
# Service handles permission checking and raises InsufficientPermissionsException if needed
|
||||
return vendor_service.update_vendor(
|
||||
db, current_user.token_vendor_id, vendor_update, current_user
|
||||
)
|
||||
|
||||
41
app/api/v1/vendor/settings.py
vendored
41
app/api/v1/vendor/settings.py
vendored
@@ -2,7 +2,8 @@
|
||||
"""
|
||||
Vendor settings and configuration endpoints.
|
||||
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern)
|
||||
Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern).
|
||||
The get_current_vendor_api dependency guarantees token_vendor_id is present.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -12,7 +13,6 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_vendor_api
|
||||
from app.core.database import get_db
|
||||
from app.exceptions import InsufficientPermissionsException, InvalidTokenException
|
||||
from app.services.vendor_service import vendor_service
|
||||
from models.database.user import User
|
||||
|
||||
@@ -26,10 +26,6 @@ def get_vendor_settings(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Get vendor settings and configuration."""
|
||||
# Get vendor ID from JWT token
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id)
|
||||
|
||||
return {
|
||||
@@ -56,32 +52,7 @@ def update_marketplace_settings(
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""Update marketplace integration settings."""
|
||||
# Get vendor ID from JWT token
|
||||
if not hasattr(current_user, "token_vendor_id"):
|
||||
raise InvalidTokenException("Token missing vendor information. Please login again.")
|
||||
|
||||
vendor = vendor_service.get_vendor_by_id(db, current_user.token_vendor_id)
|
||||
|
||||
# Verify permissions
|
||||
if not vendor_service.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.commit()
|
||||
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,
|
||||
}
|
||||
# Service handles permission checking and raises InsufficientPermissionsException if needed
|
||||
return vendor_service.update_marketplace_settings(
|
||||
db, current_user.token_vendor_id, marketplace_config, current_user
|
||||
)
|
||||
|
||||
2
app/api/v1/vendor/team.py
vendored
2
app/api/v1/vendor/team.py
vendored
@@ -164,7 +164,7 @@ def invite_team_member(
|
||||
)
|
||||
|
||||
|
||||
@router.post("/accept-invitation", response_model=InvitationAcceptResponse)
|
||||
@router.post("/accept-invitation", response_model=InvitationAcceptResponse) # public
|
||||
def accept_invitation(acceptance: InvitationAccept, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Accept a team invitation and activate account.
|
||||
|
||||
Reference in New Issue
Block a user