feat: add invoicing system and subscription tier enforcement
Phase 1 OMS implementation: Invoicing: - Add Invoice and VendorInvoiceSettings database models - Full EU VAT support (27 countries, OSS, B2B reverse charge) - Invoice PDF generation with WeasyPrint + Jinja2 templates - Vendor invoice API endpoints for settings, creation, PDF download Subscription Tiers: - Add VendorSubscription model with 4 tiers (Essential/Professional/Business/Enterprise) - Tier limit enforcement for orders, products, team members - Feature gating based on subscription tier - Automatic trial subscription creation for new vendors - Integrate limit checks into order creation (direct and Letzshop sync) Marketing: - Update pricing documentation with 4-tier structure - Revise back-office positioning strategy - Update homepage with Veeqo-inspired Letzshop-focused messaging 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,10 @@ from app.exceptions import (
|
||||
ValidationException,
|
||||
)
|
||||
from app.services.order_item_exception_service import order_item_exception_service
|
||||
from app.services.subscription_service import (
|
||||
subscription_service,
|
||||
TierLimitExceededException,
|
||||
)
|
||||
from app.utils.money import Money, cents_to_euros, euros_to_cents
|
||||
from models.database.customer import Customer
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
@@ -271,7 +275,11 @@ class OrderService:
|
||||
Raises:
|
||||
ValidationException: If order data is invalid
|
||||
InsufficientInventoryException: If not enough inventory
|
||||
TierLimitExceededException: If vendor has reached order limit
|
||||
"""
|
||||
# Check tier limit before creating order
|
||||
subscription_service.check_order_limit(db, vendor_id)
|
||||
|
||||
try:
|
||||
# Get or create customer
|
||||
if order_data.customer_id:
|
||||
@@ -428,6 +436,9 @@ class OrderService:
|
||||
db.flush()
|
||||
db.refresh(order)
|
||||
|
||||
# Increment order count for subscription tracking
|
||||
subscription_service.increment_order_count(db, vendor_id)
|
||||
|
||||
logger.info(
|
||||
f"Order {order.order_number} created for vendor {vendor_id}, "
|
||||
f"total: EUR {cents_to_euros(total_amount_cents):.2f}"
|
||||
@@ -439,6 +450,7 @@ class OrderService:
|
||||
ValidationException,
|
||||
InsufficientInventoryException,
|
||||
CustomerNotFoundException,
|
||||
TierLimitExceededException,
|
||||
):
|
||||
raise
|
||||
except Exception as e:
|
||||
@@ -450,6 +462,7 @@ class OrderService:
|
||||
db: Session,
|
||||
vendor_id: int,
|
||||
shipment_data: dict[str, Any],
|
||||
skip_limit_check: bool = False,
|
||||
) -> Order:
|
||||
"""
|
||||
Create an order from Letzshop shipment data.
|
||||
@@ -462,13 +475,27 @@ class OrderService:
|
||||
db: Database session
|
||||
vendor_id: Vendor ID
|
||||
shipment_data: Raw shipment data from Letzshop API
|
||||
skip_limit_check: If True, skip tier limit check (for batch imports
|
||||
that check limit upfront)
|
||||
|
||||
Returns:
|
||||
Created Order object
|
||||
|
||||
Raises:
|
||||
ValidationException: If product not found by GTIN
|
||||
TierLimitExceededException: If vendor has reached order limit
|
||||
"""
|
||||
# Check tier limit before creating order (unless skipped for batch ops)
|
||||
if not skip_limit_check:
|
||||
can_create, message = subscription_service.can_create_order(db, vendor_id)
|
||||
if not can_create:
|
||||
raise TierLimitExceededException(
|
||||
message=message or "Order limit exceeded",
|
||||
limit_type="orders",
|
||||
current=0, # Will be filled by caller if needed
|
||||
limit=0,
|
||||
)
|
||||
|
||||
order_data = shipment_data.get("order", {})
|
||||
|
||||
# Generate order number using Letzshop order number
|
||||
@@ -777,6 +804,9 @@ class OrderService:
|
||||
f"order {order.order_number}"
|
||||
)
|
||||
|
||||
# Increment order count for subscription tracking
|
||||
subscription_service.increment_order_count(db, vendor_id)
|
||||
|
||||
logger.info(
|
||||
f"Letzshop order {order.order_number} created for vendor {vendor_id}, "
|
||||
f"status: {status}, items: {len(inventory_units)}"
|
||||
|
||||
Reference in New Issue
Block a user