diff --git a/app/modules/analytics/services/__init__.py b/app/modules/analytics/services/__init__.py index 22676b83..1832ff70 100644 --- a/app/modules/analytics/services/__init__.py +++ b/app/modules/analytics/services/__init__.py @@ -9,26 +9,9 @@ from app.modules.analytics.services.stats_service import ( stats_service, StatsService, ) -from app.modules.analytics.services.usage_service import ( - usage_service, - UsageService, - UsageData, - UsageMetricData, - TierInfoData, - UpgradeTierData, - LimitCheckData, -) __all__ = [ # Stats service "stats_service", "StatsService", - # Usage service - "usage_service", - "UsageService", - "UsageData", - "UsageMetricData", - "TierInfoData", - "UpgradeTierData", - "LimitCheckData", ] diff --git a/app/modules/billing/routes/api/store_usage.py b/app/modules/billing/routes/api/store_usage.py index 29782655..f8b47f3a 100644 --- a/app/modules/billing/routes/api/store_usage.py +++ b/app/modules/billing/routes/api/store_usage.py @@ -18,7 +18,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.analytics.services.usage_service import usage_service +from app.modules.billing.services.usage_service import usage_service from app.modules.enums import FrontendType from models.schema.auth import UserContext diff --git a/app/modules/billing/services/__init__.py b/app/modules/billing/services/__init__.py index 9decb01c..b7c7ed3f 100644 --- a/app/modules/billing/services/__init__.py +++ b/app/modules/billing/services/__init__.py @@ -41,6 +41,15 @@ from app.modules.billing.services.platform_pricing_service import ( PlatformPricingService, platform_pricing_service, ) +from app.modules.billing.services.usage_service import ( + UsageService, + usage_service, + UsageData, + UsageMetricData, + TierInfoData, + UpgradeTierData, + LimitCheckData, +) __all__ = [ "SubscriptionService", @@ -63,4 +72,11 @@ __all__ = [ "capacity_forecast_service", "PlatformPricingService", "platform_pricing_service", + "UsageService", + "usage_service", + "UsageData", + "UsageMetricData", + "TierInfoData", + "UpgradeTierData", + "LimitCheckData", ] diff --git a/app/modules/billing/services/capacity_forecast_service.py b/app/modules/billing/services/capacity_forecast_service.py index b5735107..fa4a73bb 100644 --- a/app/modules/billing/services/capacity_forecast_service.py +++ b/app/modules/billing/services/capacity_forecast_service.py @@ -16,13 +16,14 @@ from decimal import Decimal from sqlalchemy import func from sqlalchemy.orm import Session -from app.modules.catalog.models import Product from app.modules.billing.models import ( CapacitySnapshot, MerchantSubscription, SubscriptionStatus, ) -from app.modules.tenancy.models import Store, StoreUser +from app.modules.contracts.metrics import MetricsContext +from app.modules.core.services.stats_aggregator import stats_aggregator +from app.modules.tenancy.models import Platform, Store, StoreUser logger = logging.getLogger(__name__) @@ -87,8 +88,17 @@ class CapacityForecastService: or 0 ) - # Resource metrics - total_products = db.query(func.count(Product.id)).scalar() or 0 + # Resource metrics via provider pattern (avoids direct catalog/orders imports) + start_of_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0) + platform = db.query(Platform).first() + platform_id = platform.id if platform else 1 + + stats = stats_aggregator.get_admin_stats_flat( + db, platform_id, + context=MetricsContext(date_from=start_of_month), + ) + + total_products = stats.get("catalog.total_products", 0) total_team = ( db.query(func.count(StoreUser.id)) .filter(StoreUser.is_active == True) # noqa: E712 @@ -96,15 +106,8 @@ class CapacityForecastService: or 0 ) - # Orders this month - start_of_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0) - from app.modules.orders.models import Order - - total_orders = ( - db.query(func.count(Order.id)) - .filter(Order.created_at >= start_of_month) - .scalar() or 0 - ) + # Orders this month (from stats aggregator) + total_orders = stats.get("orders.in_period", 0) # Storage metrics try: diff --git a/app/modules/analytics/services/usage_service.py b/app/modules/billing/services/usage_service.py similarity index 93% rename from app/modules/analytics/services/usage_service.py rename to app/modules/billing/services/usage_service.py index 8001ac80..9724c9d2 100644 --- a/app/modules/analytics/services/usage_service.py +++ b/app/modules/billing/services/usage_service.py @@ -1,4 +1,4 @@ -# app/modules/analytics/services/usage_service.py +# app/modules/billing/services/usage_service.py """ Usage and limits service. @@ -17,8 +17,8 @@ from dataclasses import dataclass from sqlalchemy import func from sqlalchemy.orm import Session -from app.modules.catalog.models import Product from app.modules.billing.models import MerchantSubscription, SubscriptionTier +from app.modules.billing.services.feature_aggregator import feature_aggregator from app.modules.tenancy.models import StoreUser logger = logging.getLogger(__name__) @@ -213,13 +213,10 @@ class UsageService: # ========================================================================= def _get_product_count(self, db: Session, store_id: int) -> int: - """Get product count for store.""" - return ( - db.query(func.count(Product.id)) - .filter(Product.store_id == store_id) - .scalar() - or 0 - ) + """Get product count for store via feature aggregator.""" + usage = feature_aggregator.get_store_usage(db, store_id) + products = usage.get("products_limit") + return products.current_count if products else 0 def _get_team_member_count(self, db: Session, store_id: int) -> int: """Get active team member count for store.""" @@ -271,23 +268,10 @@ class UsageService: def _get_orders_this_period( self, db: Session, store_id: int, subscription: MerchantSubscription | None ) -> int: - """Get order count for the current billing period.""" - from app.modules.orders.models import Order - - period_start = subscription.period_start if subscription else None - if not period_start: - from datetime import datetime, UTC - period_start = datetime.now(UTC).replace(day=1, hour=0, minute=0, second=0, microsecond=0) - - return ( - db.query(func.count(Order.id)) - .filter( - Order.store_id == store_id, - Order.created_at >= period_start, - ) - .scalar() - or 0 - ) + """Get order count for the current billing period via feature aggregator.""" + usage = feature_aggregator.get_store_usage(db, store_id) + orders = usage.get("orders_per_month") + return orders.current_count if orders else 0 def _get_next_tier( self, db: Session, current_tier: SubscriptionTier | None