diff --git a/app/core/celery_config.py b/app/core/celery_config.py index 956f2768..8db35e9e 100644 --- a/app/core/celery_config.py +++ b/app/core/celery_config.py @@ -9,13 +9,6 @@ It includes: - Task retry policies - Sentry integration for error tracking - Module-based task discovery (discovers tasks from app/modules/*/tasks/) - -Task Discovery: -- Legacy tasks: Explicitly listed in the 'include' parameter -- Module tasks: Auto-discovered via discover_module_tasks() - -As modules are migrated, their tasks will move from the legacy include list -to automatic discovery from the module's tasks/ directory. """ import logging @@ -48,38 +41,27 @@ if SENTRY_DSN: # ============================================================================= # TASK DISCOVERY # ============================================================================= -# Legacy tasks (will be migrated to modules over time) -# MIGRATION STATUS: -# - subscription: MIGRATED to billing module (kept for capture_capacity_snapshot -> monitoring) -# - marketplace, letzshop, export: MIGRATED to marketplace module -# - code_quality, test_runner: Will migrate to dev-tools module -LEGACY_TASK_MODULES: list[str] = [ - # All legacy tasks have been migrated to their respective modules. - # Task discovery now happens via app.modules.tasks.discover_module_tasks() -] def get_all_task_modules() -> list[str]: """ - Get all task modules (legacy + module-based). + Get all task modules via module-based discovery. Returns: - Combined list of legacy task modules and discovered module tasks + List of discovered module task packages """ - all_modules = list(LEGACY_TASK_MODULES) - try: from app.modules.tasks import discover_module_tasks module_tasks = discover_module_tasks() - all_modules.extend(module_tasks) logger.info(f"Discovered {len(module_tasks)} module task packages") + return module_tasks except ImportError as e: logger.warning(f"Could not import module task discovery: {e}") except Exception as e: logger.error(f"Error discovering module tasks: {e}") - return all_modules + return [] # Create Celery application diff --git a/app/modules/billing/exceptions.py b/app/modules/billing/exceptions.py index 61f78bbd..9dcf2a7f 100644 --- a/app/modules/billing/exceptions.py +++ b/app/modules/billing/exceptions.py @@ -19,24 +19,18 @@ from app.exceptions.base import ( __all__ = [ # Base billing exception "BillingException", - "BillingServiceError", # Alias for backwards compatibility # Subscription exceptions "SubscriptionNotFoundException", "NoActiveSubscriptionException", - "NoActiveSubscriptionError", # Alias for backwards compatibility "SubscriptionNotCancelledException", - "SubscriptionNotCancelledError", # Alias for backwards compatibility "SubscriptionAlreadyCancelledException", # Tier exceptions "TierNotFoundException", - "TierNotFoundError", "TierLimitExceededException", # Payment exceptions "PaymentSystemNotConfiguredException", - "PaymentSystemNotConfiguredError", # Alias for backwards compatibility "StripeNotConfiguredException", "StripePriceNotConfiguredException", - "StripePriceNotConfiguredError", # Alias for backwards compatibility "PaymentFailedException", # Webhook exceptions "InvalidWebhookSignatureException", @@ -44,7 +38,6 @@ __all__ = [ "WebhookVerificationException", # Feature exceptions "FeatureNotFoundException", - "FeatureNotFoundError", "FeatureNotAvailableException", "InvalidFeatureCodesError", ] @@ -62,10 +55,6 @@ class BillingException(BusinessLogicException): super().__init__(message=message, error_code=error_code, details=details) -# Alias for backwards compatibility with billing_service.py -BillingServiceError = BillingException - - # ============================================================================= # Subscription Exceptions # ============================================================================= @@ -92,10 +81,6 @@ class NoActiveSubscriptionException(BusinessLogicException): ) -# Alias for backwards compatibility with billing_service.py -NoActiveSubscriptionError = NoActiveSubscriptionException - - class SubscriptionNotCancelledException(BusinessLogicException): """Raised when trying to reactivate a subscription that is not cancelled.""" @@ -106,10 +91,6 @@ class SubscriptionNotCancelledException(BusinessLogicException): ) -# Alias for backwards compatibility with billing_service.py -SubscriptionNotCancelledError = SubscriptionNotCancelledException - - class SubscriptionAlreadyCancelledException(BusinessLogicException): """Raised when trying to cancel an already cancelled subscription.""" @@ -138,18 +119,6 @@ class TierNotFoundException(ResourceNotFoundException): self.tier_code = tier_code -class TierNotFoundError(ResourceNotFoundException): - """Subscription tier not found (alternate naming).""" - - def __init__(self, tier_code: str): - super().__init__( - resource_type="SubscriptionTier", - identifier=tier_code, - message=f"Tier '{tier_code}' not found", - ) - self.tier_code = tier_code - - class TierLimitExceededException(BillingException): """Raised when a tier limit is exceeded.""" @@ -180,10 +149,6 @@ class PaymentSystemNotConfiguredException(ServiceUnavailableException): super().__init__(message="Payment system not configured") -# Alias for backwards compatibility with billing_service.py -PaymentSystemNotConfiguredError = PaymentSystemNotConfiguredException - - class StripeNotConfiguredException(BillingException): """Raised when Stripe is not configured.""" @@ -206,10 +171,6 @@ class StripePriceNotConfiguredException(BusinessLogicException): self.tier_code = tier_code -# Alias for backwards compatibility with billing_service.py -StripePriceNotConfiguredError = StripePriceNotConfiguredException - - class PaymentFailedException(BillingException): """Raised when a payment fails.""" @@ -277,18 +238,6 @@ class FeatureNotFoundException(ResourceNotFoundException): self.feature_code = feature_code -class FeatureNotFoundError(ResourceNotFoundException): - """Feature not found (alternate naming).""" - - def __init__(self, feature_code: str): - super().__init__( - resource_type="Feature", - identifier=feature_code, - message=f"Feature '{feature_code}' not found", - ) - self.feature_code = feature_code - - class FeatureNotAvailableException(BillingException): """Raised when a feature is not available in current tier.""" diff --git a/app/modules/billing/routes/api/store_features.py b/app/modules/billing/routes/api/store_features.py index ef5c9f31..e2d9da22 100644 --- a/app/modules/billing/routes/api/store_features.py +++ b/app/modules/billing/routes/api/store_features.py @@ -24,7 +24,7 @@ from sqlalchemy.orm import Session from app.api.deps import get_current_store_api, require_module_access from app.core.database import get_db -from app.modules.billing.exceptions import FeatureNotFoundError +from app.modules.billing.exceptions import FeatureNotFoundException from app.modules.billing.schemas.billing import ( CategoryListResponse, FeatureCodeListResponse, @@ -275,7 +275,7 @@ def get_feature_detail( # Get feature declaration decl = feature_aggregator.get_declaration(feature_code) if not decl: - raise FeatureNotFoundError(feature_code) + raise FeatureNotFoundException(feature_code) # Check availability is_available = feature_service.has_feature(db, merchant_id, platform_id, feature_code) diff --git a/app/modules/billing/services/__init__.py b/app/modules/billing/services/__init__.py index 281d4fe5..5bf6a6d5 100644 --- a/app/modules/billing/services/__init__.py +++ b/app/modules/billing/services/__init__.py @@ -5,14 +5,6 @@ Billing module services. Provides subscription management, Stripe integration, and admin operations. """ -from app.modules.billing.exceptions import ( - BillingServiceError, - NoActiveSubscriptionError, - PaymentSystemNotConfiguredError, - StripePriceNotConfiguredError, - SubscriptionNotCancelledError, - TierNotFoundError, -) from app.modules.billing.services.admin_subscription_service import ( AdminSubscriptionService, admin_subscription_service, @@ -56,12 +48,6 @@ __all__ = [ "admin_subscription_service", "BillingService", "billing_service", - "BillingServiceError", - "PaymentSystemNotConfiguredError", - "TierNotFoundError", - "StripePriceNotConfiguredError", - "NoActiveSubscriptionError", - "SubscriptionNotCancelledError", "FeatureService", "feature_service", "PlatformPricingService", diff --git a/app/modules/billing/services/billing_service.py b/app/modules/billing/services/billing_service.py index f7db848e..51d65e18 100644 --- a/app/modules/billing/services/billing_service.py +++ b/app/modules/billing/services/billing_service.py @@ -16,12 +16,12 @@ from datetime import datetime from sqlalchemy.orm import Session from app.modules.billing.exceptions import ( - BillingServiceError, - NoActiveSubscriptionError, - PaymentSystemNotConfiguredError, - StripePriceNotConfiguredError, - SubscriptionNotCancelledError, - TierNotFoundError, + BillingException, + NoActiveSubscriptionException, + PaymentSystemNotConfiguredException, + StripePriceNotConfiguredException, + SubscriptionNotCancelledException, + TierNotFoundException, ) from app.modules.billing.models import ( AddOnProduct, @@ -93,7 +93,7 @@ class BillingService: Get a tier by its code. Raises: - TierNotFoundError: If tier doesn't exist + TierNotFoundException: If tier doesn't exist """ tier = ( db.query(SubscriptionTier) @@ -105,7 +105,7 @@ class BillingService: ) if not tier: - raise TierNotFoundError(tier_code) + raise TierNotFoundException(tier_code) return tier @@ -126,12 +126,12 @@ class BillingService: Dict with checkout_url and session_id Raises: - PaymentSystemNotConfiguredError: If Stripe not configured - TierNotFoundError: If tier doesn't exist - StripePriceNotConfiguredError: If price not configured + PaymentSystemNotConfiguredException: If Stripe not configured + TierNotFoundException: If tier doesn't exist + StripePriceNotConfiguredException: If price not configured """ if not stripe_service.is_configured: - raise PaymentSystemNotConfiguredError() + raise PaymentSystemNotConfiguredException() tier = self.get_tier_by_code(db, tier_code) @@ -142,7 +142,7 @@ class BillingService: ) if not price_id: - raise StripePriceNotConfiguredError(tier_code) + raise StripePriceNotConfiguredException(tier_code) # Check if this is a new subscription (for trial) existing_sub = subscription_service.get_merchant_subscription( @@ -188,18 +188,18 @@ class BillingService: Dict with portal_url Raises: - PaymentSystemNotConfiguredError: If Stripe not configured - NoActiveSubscriptionError: If no subscription with customer ID + PaymentSystemNotConfiguredException: If Stripe not configured + NoActiveSubscriptionException: If no subscription with customer ID """ if not stripe_service.is_configured: - raise PaymentSystemNotConfiguredError() + raise PaymentSystemNotConfiguredException() subscription = subscription_service.get_merchant_subscription( db, merchant_id, platform_id ) if not subscription or not subscription.stripe_customer_id: - raise NoActiveSubscriptionError() + raise NoActiveSubscriptionException() session = stripe_service.create_portal_session( customer_id=subscription.stripe_customer_id, @@ -266,14 +266,14 @@ class BillingService: Dict with message and effective_date Raises: - NoActiveSubscriptionError: If no subscription to cancel + NoActiveSubscriptionException: If no subscription to cancel """ subscription = subscription_service.get_merchant_subscription( db, merchant_id, platform_id ) if not subscription or not subscription.stripe_subscription_id: - raise NoActiveSubscriptionError() + raise NoActiveSubscriptionException() if stripe_service.is_configured: stripe_service.cancel_subscription( @@ -308,18 +308,18 @@ class BillingService: Dict with success message Raises: - NoActiveSubscriptionError: If no subscription - SubscriptionNotCancelledError: If not cancelled + NoActiveSubscriptionException: If no subscription + SubscriptionNotCancelledException: If not cancelled """ subscription = subscription_service.get_merchant_subscription( db, merchant_id, platform_id ) if not subscription or not subscription.stripe_subscription_id: - raise NoActiveSubscriptionError() + raise NoActiveSubscriptionException() if not subscription.cancelled_at: - raise SubscriptionNotCancelledError() + raise SubscriptionNotCancelledException() if stripe_service.is_configured: stripe_service.reactivate_subscription(subscription.stripe_subscription_id) @@ -339,14 +339,14 @@ class BillingService: Dict with amount_due_cents, currency, next_payment_date, line_items Raises: - NoActiveSubscriptionError: If no subscription with customer ID + NoActiveSubscriptionException: If no subscription with customer ID """ subscription = subscription_service.get_merchant_subscription( db, merchant_id, platform_id ) if not subscription or not subscription.stripe_customer_id: - raise NoActiveSubscriptionError() + raise NoActiveSubscriptionException() if not stripe_service.is_configured: return { @@ -399,16 +399,16 @@ class BillingService: Dict with message, new_tier, effective_immediately Raises: - TierNotFoundError: If tier doesn't exist - NoActiveSubscriptionError: If no subscription - StripePriceNotConfiguredError: If price not configured + TierNotFoundException: If tier doesn't exist + NoActiveSubscriptionException: If no subscription + StripePriceNotConfiguredException: If price not configured """ subscription = subscription_service.get_merchant_subscription( db, merchant_id, platform_id ) if not subscription or not subscription.stripe_subscription_id: - raise NoActiveSubscriptionError() + raise NoActiveSubscriptionException() tier = self.get_tier_by_code(db, new_tier_code) @@ -419,7 +419,7 @@ class BillingService: ) if not price_id: - raise StripePriceNotConfiguredError(new_tier_code) + raise StripePriceNotConfiguredException(new_tier_code) # Update in Stripe if stripe_service.is_configured: @@ -472,11 +472,11 @@ class BillingService: Dict with checkout_url and session_id Raises: - PaymentSystemNotConfiguredError: If Stripe not configured - BillingServiceError: If addon doesn't exist + PaymentSystemNotConfiguredException: If Stripe not configured + BillingException: If addon doesn't exist """ if not stripe_service.is_configured: - raise PaymentSystemNotConfiguredError() + raise PaymentSystemNotConfiguredException() addon = ( db.query(AddOnProduct) @@ -488,10 +488,10 @@ class BillingService: ) if not addon: - raise BillingServiceError(f"Add-on '{addon_code}' not found") + raise BillingException(f"Add-on '{addon_code}' not found") if not addon.stripe_price_id: - raise BillingServiceError(f"Stripe price not configured for add-on '{addon_code}'") + raise BillingException(f"Stripe price not configured for add-on '{addon_code}'") from app.modules.tenancy.models import Store store = db.query(Store).filter(Store.id == store_id).first() @@ -522,7 +522,7 @@ class BillingService: Dict with message and addon_code Raises: - BillingServiceError: If addon not found or not owned by store + BillingException: If addon not found or not owned by store """ store_addon = ( db.query(StoreAddOn) @@ -534,7 +534,7 @@ class BillingService: ) if not store_addon: - raise BillingServiceError("Add-on not found") + raise BillingException("Add-on not found") addon_code = store_addon.addon_product.code diff --git a/app/modules/billing/tests/unit/test_billing_service.py b/app/modules/billing/tests/unit/test_billing_service.py index cd9a926a..8aa39ee1 100644 --- a/app/modules/billing/tests/unit/test_billing_service.py +++ b/app/modules/billing/tests/unit/test_billing_service.py @@ -6,6 +6,13 @@ from unittest.mock import MagicMock, patch import pytest +from app.modules.billing.exceptions import ( + NoActiveSubscriptionException, + PaymentSystemNotConfiguredException, + StripePriceNotConfiguredException, + SubscriptionNotCancelledException, + TierNotFoundException, +) from app.modules.billing.models import ( AddOnProduct, BillingHistory, @@ -13,14 +20,7 @@ from app.modules.billing.models import ( SubscriptionStatus, SubscriptionTier, ) -from app.modules.billing.services.billing_service import ( - BillingService, - NoActiveSubscriptionError, - PaymentSystemNotConfiguredError, - StripePriceNotConfiguredError, - SubscriptionNotCancelledError, - TierNotFoundError, -) +from app.modules.billing.services.billing_service import BillingService # ============================================================================ # Tier Lookup @@ -41,17 +41,17 @@ class TestBillingServiceTiers: assert tier.code == "essential" def test_get_tier_by_code_not_found(self, db): - """Nonexistent tier raises TierNotFoundError.""" - with pytest.raises(TierNotFoundError) as exc_info: + """Nonexistent tier raises TierNotFoundException.""" + with pytest.raises(TierNotFoundException) as exc_info: self.service.get_tier_by_code(db, "nonexistent") assert exc_info.value.tier_code == "nonexistent" def test_get_tier_by_code_inactive_not_returned(self, db, bs_tier_essential): - """Inactive tier raises TierNotFoundError (only active tiers returned).""" + """Inactive tier raises TierNotFoundException (only active tiers returned).""" bs_tier_essential.is_active = False db.flush() - with pytest.raises(TierNotFoundError): + with pytest.raises(TierNotFoundException): self.service.get_tier_by_code(db, "essential") @@ -249,8 +249,8 @@ class TestBillingServiceChangeTier: self.service = BillingService() def test_change_tier_no_subscription_raises(self, db, bs_tiers): - """Raises NoActiveSubscriptionError when no subscription exists.""" - with pytest.raises(NoActiveSubscriptionError): + """Raises NoActiveSubscriptionException when no subscription exists.""" + with pytest.raises(NoActiveSubscriptionException): self.service.change_tier(db, 99999, 99999, "professional", False) def test_change_tier_no_stripe_subscription_raises( @@ -258,7 +258,7 @@ class TestBillingServiceChangeTier: ): """Raises when subscription has no stripe_subscription_id.""" # bs_subscription has no Stripe IDs - with pytest.raises(NoActiveSubscriptionError): + with pytest.raises(NoActiveSubscriptionException): self.service.change_tier( db, bs_subscription.merchant_id, @@ -270,8 +270,8 @@ class TestBillingServiceChangeTier: def test_change_tier_nonexistent_tier_raises( self, db, bs_stripe_subscription ): - """Raises TierNotFoundError for nonexistent tier.""" - with pytest.raises(TierNotFoundError): + """Raises TierNotFoundException for nonexistent tier.""" + with pytest.raises(TierNotFoundException): self.service.change_tier( db, bs_stripe_subscription.merchant_id, @@ -283,9 +283,9 @@ class TestBillingServiceChangeTier: def test_change_tier_no_price_id_raises( self, db, bs_stripe_subscription, bs_tiers ): - """Raises StripePriceNotConfiguredError when tier has no Stripe price.""" + """Raises StripePriceNotConfiguredException when tier has no Stripe price.""" # bs_tiers have no stripe_price_* set - with pytest.raises(StripePriceNotConfiguredError): + with pytest.raises(StripePriceNotConfiguredException): self.service.change_tier( db, bs_stripe_subscription.merchant_id, @@ -382,12 +382,12 @@ class TestBillingServiceCancel: def test_cancel_no_subscription_raises(self, db): """Raises when no subscription found.""" - with pytest.raises(NoActiveSubscriptionError): + with pytest.raises(NoActiveSubscriptionException): self.service.cancel_subscription(db, 99999, 99999, None, False) def test_cancel_no_stripe_id_raises(self, db, bs_subscription): """Raises when subscription has no stripe_subscription_id.""" - with pytest.raises(NoActiveSubscriptionError): + with pytest.raises(NoActiveSubscriptionException): self.service.cancel_subscription( db, bs_subscription.merchant_id, @@ -431,12 +431,12 @@ class TestBillingServiceReactivate: def test_reactivate_no_subscription_raises(self, db): """Raises when no subscription found.""" - with pytest.raises(NoActiveSubscriptionError): + with pytest.raises(NoActiveSubscriptionException): self.service.reactivate_subscription(db, 99999, 99999) def test_reactivate_not_cancelled_raises(self, db, bs_stripe_subscription): - """Raises SubscriptionNotCancelledError when not cancelled.""" - with pytest.raises(SubscriptionNotCancelledError): + """Raises SubscriptionNotCancelledException when not cancelled.""" + with pytest.raises(SubscriptionNotCancelledException): self.service.reactivate_subscription( db, bs_stripe_subscription.merchant_id, @@ -480,25 +480,25 @@ class TestBillingServiceCheckout: self.service = BillingService() def test_checkout_stripe_not_configured_raises(self, db, bs_tiers_with_stripe): - """Raises PaymentSystemNotConfiguredError when Stripe is off.""" + """Raises PaymentSystemNotConfiguredException when Stripe is off.""" with patch( "app.modules.billing.services.billing_service.stripe_service" ) as mock_stripe: mock_stripe.is_configured = False - with pytest.raises(PaymentSystemNotConfiguredError): + with pytest.raises(PaymentSystemNotConfiguredException): self.service.create_checkout_session( db, 1, 1, "essential", False, "http://ok", "http://cancel" ) def test_checkout_nonexistent_tier_raises(self, db): - """Raises TierNotFoundError for nonexistent tier.""" + """Raises TierNotFoundException for nonexistent tier.""" with patch( "app.modules.billing.services.billing_service.stripe_service" ) as mock_stripe: mock_stripe.is_configured = True - with pytest.raises(TierNotFoundError): + with pytest.raises(TierNotFoundException): self.service.create_checkout_session( db, 1, 1, "nonexistent", False, "http://ok", "http://cancel" ) @@ -518,23 +518,23 @@ class TestBillingServicePortal: self.service = BillingService() def test_portal_stripe_not_configured_raises(self, db): - """Raises PaymentSystemNotConfiguredError when Stripe is off.""" + """Raises PaymentSystemNotConfiguredException when Stripe is off.""" with patch( "app.modules.billing.services.billing_service.stripe_service" ) as mock_stripe: mock_stripe.is_configured = False - with pytest.raises(PaymentSystemNotConfiguredError): + with pytest.raises(PaymentSystemNotConfiguredException): self.service.create_portal_session(db, 1, 1, "http://return") def test_portal_no_subscription_raises(self, db): - """Raises NoActiveSubscriptionError when no subscription found.""" + """Raises NoActiveSubscriptionException when no subscription found.""" with patch( "app.modules.billing.services.billing_service.stripe_service" ) as mock_stripe: mock_stripe.is_configured = True - with pytest.raises(NoActiveSubscriptionError): + with pytest.raises(NoActiveSubscriptionException): self.service.create_portal_session(db, 99999, 99999, "http://return") def test_portal_no_customer_id_raises(self, db, bs_subscription): @@ -544,7 +544,7 @@ class TestBillingServicePortal: ) as mock_stripe: mock_stripe.is_configured = True - with pytest.raises(NoActiveSubscriptionError): + with pytest.raises(NoActiveSubscriptionException): self.service.create_portal_session( db, bs_subscription.merchant_id, @@ -568,12 +568,12 @@ class TestBillingServiceUpcomingInvoice: def test_upcoming_invoice_no_subscription_raises(self, db): """Raises when no subscription exists.""" - with pytest.raises(NoActiveSubscriptionError): + with pytest.raises(NoActiveSubscriptionException): self.service.get_upcoming_invoice(db, 99999, 99999) def test_upcoming_invoice_no_customer_id_raises(self, db, bs_subscription): """Raises when subscription has no stripe_customer_id.""" - with pytest.raises(NoActiveSubscriptionError): + with pytest.raises(NoActiveSubscriptionException): self.service.get_upcoming_invoice( db, bs_subscription.merchant_id, bs_subscription.platform_id ) diff --git a/app/modules/cms/routes/pages/admin.py b/app/modules/cms/routes/pages/admin.py index 37b239a2..ac0d17d6 100644 --- a/app/modules/cms/routes/pages/admin.py +++ b/app/modules/cms/routes/pages/admin.py @@ -6,7 +6,7 @@ Admin pages for managing platform and store content pages. """ from fastapi import APIRouter, Depends, Path, Request -from fastapi.responses import HTMLResponse, RedirectResponse +from fastapi.responses import HTMLResponse from sqlalchemy.orm import Session from app.api.deps import get_db, require_menu_access @@ -22,22 +22,6 @@ router = APIRouter() # ============================================================================ -@router.get("/platform-homepage", include_in_schema=False) -async def admin_platform_homepage_manager( - request: Request, - current_user: User = Depends(require_menu_access("platforms", FrontendType.ADMIN)), - db: Session = Depends(get_db), -): - """ - Deprecated: Redirects to platforms page. - - Platform homepages are now managed via: - - /admin/platforms → Select platform → Homepage button - - Or directly: /admin/content-pages?platform_code={code}&slug=home - """ - return RedirectResponse(url="/admin/platforms", status_code=302) - - @router.get("/content-pages", response_class=HTMLResponse, include_in_schema=False) async def admin_content_pages_list( request: Request, diff --git a/app/modules/cms/schemas/content_page.py b/app/modules/cms/schemas/content_page.py index a545fc98..6f89fa60 100644 --- a/app/modules/cms/schemas/content_page.py +++ b/app/modules/cms/schemas/content_page.py @@ -90,7 +90,6 @@ class ContentPageResponse(BaseModel): show_in_header: bool show_in_legal: bool is_platform_page: bool = False - is_platform_default: bool = False # Deprecated: use is_platform_page is_store_default: bool = False is_store_override: bool = False page_tier: str | None = None diff --git a/app/modules/cms/schemas/media.py b/app/modules/cms/schemas/media.py index 91dcd2bc..217970e8 100644 --- a/app/modules/cms/schemas/media.py +++ b/app/modules/cms/schemas/media.py @@ -37,7 +37,6 @@ class MediaItemResponse(BaseModel): filename: str original_filename: str | None = None file_url: str - url: str | None = None # Alias for file_url for JS compatibility thumbnail_url: str | None = None media_type: str # image, video, document mime_type: str | None = None @@ -53,11 +52,6 @@ class MediaItemResponse(BaseModel): model_config = {"from_attributes": True} - def model_post_init(self, __context: Any) -> None: - """Set url from file_url if not provided.""" - if self.url is None: - object.__setattr__(self, "url", self.file_url) - class MediaListResponse(BaseModel): """Paginated list of media items.""" @@ -80,13 +74,6 @@ class MediaUploadResponse(BaseModel): success: bool = True message: str | None = None media: MediaItemResponse | None = None - # Legacy fields for backwards compatibility - id: int | None = None - file_url: str | None = None - thumbnail_url: str | None = None - filename: str | None = None - file_size: int | None = None - media_type: str | None = None class UploadedFileInfo(BaseModel): diff --git a/app/modules/cms/services/__init__.py b/app/modules/cms/services/__init__.py index 8acad576..5ccc3f99 100644 --- a/app/modules/cms/services/__init__.py +++ b/app/modules/cms/services/__init__.py @@ -15,7 +15,6 @@ from app.modules.cms.services.media_service import ( ) from app.modules.cms.services.store_email_settings_service import ( StoreEmailSettingsService, - get_store_email_settings_service, # Deprecated: use store_email_settings_service store_email_settings_service, ) from app.modules.cms.services.store_theme_service import ( @@ -32,5 +31,4 @@ __all__ = [ "store_theme_service", "StoreEmailSettingsService", "store_email_settings_service", - "get_store_email_settings_service", # Deprecated ] diff --git a/app/modules/cms/services/store_email_settings_service.py b/app/modules/cms/services/store_email_settings_service.py index e5582800..2db3b303 100644 --- a/app/modules/cms/services/store_email_settings_service.py +++ b/app/modules/cms/services/store_email_settings_service.py @@ -481,14 +481,3 @@ class StoreEmailSettingsService: # Module-level service instance (singleton pattern) store_email_settings_service = StoreEmailSettingsService() - - -# Deprecated: Factory function for backwards compatibility -def get_store_email_settings_service(db: Session) -> StoreEmailSettingsService: - """ - Factory function to get a StoreEmailSettingsService instance. - - Deprecated: Use the singleton `store_email_settings_service` instead and pass - `db` to individual methods. - """ - return store_email_settings_service diff --git a/app/modules/cms/static/shared/js/media-picker.js b/app/modules/cms/static/shared/js/media-picker.js index 35aa26da..73e8283c 100644 --- a/app/modules/cms/static/shared/js/media-picker.js +++ b/app/modules/cms/static/shared/js/media-picker.js @@ -262,9 +262,9 @@ function mediaPickerMixin(storeIdGetter, multiSelect = false) { */ setMainImage(media) { if (this.form) { - this.form.primary_image_url = media.url; + this.form.primary_image_url = media.file_url; } - mediaPickerLog.info('Main image set:', media.url); + mediaPickerLog.info('Main image set:', media.file_url); }, /** @@ -272,13 +272,13 @@ function mediaPickerMixin(storeIdGetter, multiSelect = false) { */ addAdditionalImages(mediaList) { if (this.form && Array.isArray(this.form.additional_images)) { - const newUrls = mediaList.map(m => m.url); + const newUrls = mediaList.map(m => m.file_url); this.form.additional_images = [ ...this.form.additional_images, ...newUrls ]; } - mediaPickerLog.info('Additional images added:', mediaList.map(m => m.url)); + mediaPickerLog.info('Additional images added:', mediaList.map(m => m.file_url)); }, /** diff --git a/app/modules/cms/static/store/js/content-pages.js b/app/modules/cms/static/store/js/content-pages.js index 45d32078..a8d5d717 100644 --- a/app/modules/cms/static/store/js/content-pages.js +++ b/app/modules/cms/static/store/js/content-pages.js @@ -73,7 +73,7 @@ function storeContentPagesManager() { // Platform pages - filter to only show actual platform defaults const allPages = platformResponse.data || platformResponse || []; - this.platformPages = allPages.filter(p => p.is_platform_default); + this.platformPages = allPages.filter(p => p.is_platform_page); // Store's custom pages (includes overrides) this.customPages = storeResponse.data || storeResponse || []; diff --git a/app/modules/messaging/schemas/__init__.py b/app/modules/messaging/schemas/__init__.py index 23f8c8e1..3a50436f 100644 --- a/app/modules/messaging/schemas/__init__.py +++ b/app/modules/messaging/schemas/__init__.py @@ -34,7 +34,6 @@ from app.modules.messaging.schemas.message import ( ConversationCreate, ConversationDetailResponse, ConversationListResponse, - ConversationResponse, ConversationSummary, MarkReadResponse, # Message schemas @@ -90,7 +89,6 @@ __all__ = [ "ConversationSummary", "ConversationDetailResponse", "ConversationListResponse", - "ConversationResponse", # Unread count "UnreadCountResponse", # Notification preferences diff --git a/app/modules/messaging/schemas/message.py b/app/modules/messaging/schemas/message.py index 03e74ba7..22239322 100644 --- a/app/modules/messaging/schemas/message.py +++ b/app/modules/messaging/schemas/message.py @@ -192,10 +192,6 @@ class ConversationListResponse(BaseModel): limit: int -# Backward compatibility alias -ConversationResponse = ConversationDetailResponse - - # ============================================================================ # Unread Count Schemas # ============================================================================ diff --git a/app/modules/orders/schemas/__init__.py b/app/modules/orders/schemas/__init__.py index aa9970b6..8a442d61 100644 --- a/app/modules/orders/schemas/__init__.py +++ b/app/modules/orders/schemas/__init__.py @@ -21,10 +21,6 @@ from app.modules.orders.schemas.invoice import ( InvoiceResponse, # Address schemas InvoiceSellerDetails, - # Backward compatibility - InvoiceSettingsCreate, - InvoiceSettingsResponse, - InvoiceSettingsUpdate, InvoiceStatsResponse, InvoiceStatusUpdate, # Invoice settings schemas @@ -148,8 +144,4 @@ __all__ = [ # PDF "InvoicePDFGeneratedResponse", "InvoiceStatsResponse", - # Backward compatibility - "InvoiceSettingsCreate", - "InvoiceSettingsUpdate", - "InvoiceSettingsResponse", ] diff --git a/app/modules/orders/schemas/invoice.py b/app/modules/orders/schemas/invoice.py index 9b701bc2..b3c4d83c 100644 --- a/app/modules/orders/schemas/invoice.py +++ b/app/modules/orders/schemas/invoice.py @@ -308,9 +308,3 @@ class InvoiceStatsResponse(BaseModel): @property def total_revenue(self) -> float: return self.total_revenue_cents / 100 - - -# Backward compatibility re-exports -InvoiceSettingsCreate = StoreInvoiceSettingsCreate -InvoiceSettingsUpdate = StoreInvoiceSettingsUpdate -InvoiceSettingsResponse = StoreInvoiceSettingsResponse diff --git a/app/modules/routes.py b/app/modules/routes.py index 6c88a5da..bf2934c0 100644 --- a/app/modules/routes.py +++ b/app/modules/routes.py @@ -61,7 +61,7 @@ class RouteInfo: include_in_schema: bool = True module_code: str = "" route_type: str = "" # "api" or "pages" - frontend: str = "" # "admin", "store", "shop" + frontend: str = "" # "admin", "store", "storefront" priority: int = 0 # Higher = registered later (for catch-all routes) custom_prefix: str = "" # Custom prefix from ROUTE_CONFIG @@ -76,7 +76,7 @@ def discover_module_routes() -> list[RouteInfo]: Route discovery looks for: - routes/api/admin.py -> admin API routes - routes/api/store.py -> store API routes - - routes/api/shop.py -> shop API routes + - routes/api/storefront.py -> storefront API routes - routes/pages/admin.py -> admin page routes - routes/pages/store.py -> store page routes diff --git a/app/templates/shared/macros/pagination.html b/app/templates/shared/macros/pagination.html index 43084850..2a44ae4d 100644 --- a/app/templates/shared/macros/pagination.html +++ b/app/templates/shared/macros/pagination.html @@ -93,92 +93,6 @@ {{ pagination_simple() }} #} -{# - Pagination Full Macro (First/Prev/Numbers/Next/Last) - ===================================================== - ⚠️ DEPRECATED: Use the standard 'pagination' macro instead. - - This macro expects flat variables (total, skip, page, limit) but our Alpine.js - components use nested pagination objects (pagination.total, pagination.page, etc.). - - Use: - {% from 'shared/macros/pagination.html' import pagination %} - {{ pagination(show_condition="!loading && pagination.total > 0") }} - - --- - Legacy documentation (for reference only): - - Required Alpine.js data properties: - - page: Current page number - - total: Total number of items - - limit: Items per page - - skip: Current skip value (page - 1) * limit - - totalPages: Computed total pages (Math.ceil(total / limit)) - - Required Alpine.js methods: - - getPageNumbers(): Returns array of page numbers to display - - goToPage(pageNum): Go to specific page - - loadData(): Function to reload data (called internally as loadFn parameter) -#} - -{% macro pagination_full(show_condition="total > limit", load_fn="loadData()", item_label="items") %} -