fix(billing): resolve 3 IMPORT-001 architecture violations in billing module
Replace direct imports from optional modules (catalog, orders, analytics) with provider pattern calls (stats_aggregator, feature_aggregator) and move usage_service from analytics to billing where it belongs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user