Add subcription feature 6

This commit is contained in:
Urtzi Alfaro
2026-01-16 15:19:34 +01:00
parent 6b43116efd
commit 4bafceed0d
35 changed files with 3826 additions and 1789 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -606,3 +606,75 @@ class TenantRepository(TenantBaseRepository):
customer_id=customer_id,
error=str(e))
raise DatabaseError(f"Failed to get tenant by customer_id: {str(e)}")
async def get_user_primary_tenant(self, user_id: str) -> Optional[Tenant]:
"""
Get the primary tenant for a user (the tenant they own)
Args:
user_id: User ID to find primary tenant for
Returns:
Tenant object if found, None otherwise
"""
try:
logger.debug("Getting primary tenant for user", user_id=user_id)
# Query for tenant where user is the owner
query = select(Tenant).where(Tenant.owner_id == user_id)
result = await self.session.execute(query)
tenant = result.scalar_one_or_none()
if tenant:
logger.debug("Found primary tenant for user",
user_id=user_id,
tenant_id=str(tenant.id))
return tenant
else:
logger.debug("No primary tenant found for user", user_id=user_id)
return None
except Exception as e:
logger.error("Error getting primary tenant for user",
user_id=user_id,
error=str(e))
raise DatabaseError(f"Failed to get primary tenant for user: {str(e)}")
async def get_any_user_tenant(self, user_id: str) -> Optional[Tenant]:
"""
Get any tenant that the user has access to (via tenant_members)
Args:
user_id: User ID to find accessible tenants for
Returns:
Tenant object if found, None otherwise
"""
try:
logger.debug("Getting any accessible tenant for user", user_id=user_id)
# Query for tenant members where user has access
from app.models.tenants import TenantMember
query = select(Tenant).join(
TenantMember, Tenant.id == TenantMember.tenant_id
).where(TenantMember.user_id == user_id)
result = await self.session.execute(query)
tenant = result.scalar_one_or_none()
if tenant:
logger.debug("Found accessible tenant for user",
user_id=user_id,
tenant_id=str(tenant.id))
return tenant
else:
logger.debug("No accessible tenants found for user", user_id=user_id)
return None
except Exception as e:
logger.error("Error getting accessible tenant for user",
user_id=user_id,
error=str(e))
raise DatabaseError(f"Failed to get accessible tenant for user: {str(e)}")

View File

@@ -1545,6 +1545,52 @@ class SubscriptionOrchestrationService:
tenant_id=tenant_id)
return None
async def get_invoices(self, tenant_id: str) -> Dict[str, Any]:
"""
Get invoice history for a tenant's subscription
This is an orchestration method that coordinates between:
1. SubscriptionService (to get subscription data)
2. PaymentService (to get invoices from provider)
Args:
tenant_id: Tenant ID
Returns:
Dictionary with invoices data
"""
try:
# Get subscription from database
subscription = await self.subscription_service.get_subscription_by_tenant_id(tenant_id)
if not subscription:
logger.warning("get_invoices_no_subscription",
tenant_id=tenant_id)
return {"invoices": []}
# Check if subscription has a customer ID
if not subscription.customer_id:
logger.warning("get_invoices_no_customer_id",
tenant_id=tenant_id)
return {"invoices": []}
# Get invoices from payment provider
invoices_result = await self.payment_service.stripe_client.get_invoices(subscription.customer_id)
logger.info("invoices_retrieved",
tenant_id=tenant_id,
customer_id=subscription.customer_id,
invoice_count=len(invoices_result.get("invoices", [])))
return invoices_result
except Exception as e:
logger.error("get_invoices_failed",
error=str(e),
tenant_id=tenant_id,
exc_info=True)
return {"invoices": []}
async def update_payment_method(
self,
tenant_id: str,

View File

@@ -252,7 +252,7 @@ class SubscriptionCreationFlowTester:
async def _verify_subscription_linked_to_tenant(self, subscription_id: str, tenant_id: str):
"""Verify that the subscription is properly linked to the tenant"""
url = f"{self.base_url}/api/v1/subscriptions/{tenant_id}/status"
url = f"{self.base_url}/api/v1/tenants/{tenant_id}/subscription/status"
# Get access token for the user
access_token = await self._get_user_access_token()
@@ -280,7 +280,7 @@ class SubscriptionCreationFlowTester:
async def _verify_tenant_subscription_access(self, tenant_id: str):
"""Verify that the tenant can access its subscription"""
url = f"{self.base_url}/api/v1/subscriptions/{tenant_id}/active"
url = f"{self.base_url}/api/v1/tenants/{tenant_id}/subscription/details"
# Get access token for the user
access_token = await self._get_user_access_token()