From fd49363679c37f86cae4945eb8bf702bede84ba6 Mon Sep 17 00:00:00 2001 From: jSasaki Date: Fri, 26 Sep 2025 15:32:37 +0900 Subject: [PATCH 1/4] add-plan-setting-page --- billing_router.py | 133 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) diff --git a/billing_router.py b/billing_router.py index 0adf5a7..0953589 100644 --- a/billing_router.py +++ b/billing_router.py @@ -8,6 +8,7 @@ # SaaS SDK のクライアント from saasus_sdk_python.src.auth import TenantApi +from saasus_sdk_python.src.auth.models.plan_reservation import PlanReservation from saasus_sdk_python.src.pricing import ( PricingPlansApi, MeteringApi, @@ -34,6 +35,11 @@ class UpdateCountBody(BaseModel): method: str = Field(..., pattern="^(add|sub|direct)$") count: int = Field(..., ge=0) +class UpdateTenantPlanRequest(BaseModel): + next_plan_id: str + tax_rate_id: str = None + using_next_plan_from: int = None + # --- 認可ヘルパー --- def has_billing_access(auth_user: Any, tenant_id: str) -> bool: @@ -360,4 +366,129 @@ def update_count_of_now( metering_unit_name=unit, update_metering_unit_timestamp_count_now_param=param, ) - return resp \ No newline at end of file + return resp + +@router.get( + "/pricing_plans", + summary="Get pricing plans list" +) +def get_pricing_plans(auth_user: Any = Depends(fastapi_auth)): + # プラン一覧を取得する + if not auth_user.tenants: + raise HTTPException(status_code=400, detail="No tenants found for the user") + + try: + # 料金プラン一覧を取得 + plans = PricingPlansApi(api_client=pricing_api_client).get_pricing_plans() + return plans.pricing_plans + except Exception as e: + raise HTTPException(status_code=500, detail="Internal server error") + + +@router.get( + "/tax_rates", + summary="Get tax rates list" +) +def get_tax_rates(auth_user: Any = Depends(fastapi_auth)): + # 税率一覧を取得する + if not auth_user.tenants: + raise HTTPException(status_code=400, detail="No tenants found for the user") + + try: + # 税率一覧を取得 + tax_rates = TaxRateApi(api_client=pricing_api_client).get_tax_rates() + return tax_rates.tax_rates + except Exception as e: + raise HTTPException(status_code=500, detail="Internal server error") + + +@router.get( + "/tenants/{tenant_id}/plan", + summary="Get tenant plan information" +) +def get_tenant_plan_info( + tenant_id: str, + auth_user: Any = Depends(fastapi_auth) +): + # テナントプラン情報を取得する + if not tenant_id: + raise HTTPException(status_code=400, detail="tenant_id is required") + + # 管理者権限チェック + if not has_billing_access(auth_user, tenant_id): + raise HTTPException(status_code=403, detail="Insufficient permissions") + + try: + # テナント詳細情報を取得 + tenant = TenantApi(api_client=api_client).get_tenant(tenant_id=tenant_id) + + # 現在のプランの税率情報を取得(プラン履歴の最新エントリから) + current_tax_rate_id = None + if tenant.plan_histories: + latest_plan_history = tenant.plan_histories[-1] + if latest_plan_history.tax_rate_id: + current_tax_rate_id = latest_plan_history.tax_rate_id + + # レスポンスを構築 + response = { + "id": tenant.id, + "name": tenant.name, + "plan_id": tenant.plan_id, + "tax_rate_id": current_tax_rate_id, + "plan_reservation": None, + } + + # 予約情報がある場合は追加(using_next_plan_fromで判定) + if hasattr(tenant, 'using_next_plan_from') and tenant.using_next_plan_from is not None: + plan_reservation = { + "next_plan_id": getattr(tenant, 'next_plan_id', None), + "using_next_plan_from": tenant.using_next_plan_from, + "next_plan_tax_rate_id": getattr(tenant, 'next_plan_tax_rate_id', None), + } + response["plan_reservation"] = plan_reservation + + return response + except Exception as e: + raise HTTPException(status_code=500, detail="Failed to retrieve tenant detail") + + +@router.put( + "/tenants/{tenant_id}/plan", + summary="Update tenant plan" +) +def update_tenant_plan( + tenant_id: str, + request: UpdateTenantPlanRequest, + auth_user: Any = Depends(fastapi_auth) +): + # テナントプランを更新する + if not tenant_id: + raise HTTPException(status_code=400, detail="tenant_id is required") + + # 管理者権限チェック + if not has_billing_access(auth_user, tenant_id): + raise HTTPException(status_code=403, detail="Insufficient permissions") + + try: + # テナントプランを更新 + plan_reservation = PlanReservation( + next_plan_id=request.next_plan_id + ) + + # 税率IDが指定されている場合のみ設定 + if request.tax_rate_id and request.tax_rate_id != "": + plan_reservation.next_plan_tax_rate_id = request.tax_rate_id + + # using_next_plan_fromが指定されている場合のみ設定 + if request.using_next_plan_from and request.using_next_plan_from > 0: + plan_reservation.using_next_plan_from = request.using_next_plan_from + + # テナントプランを更新 + TenantApi(api_client=api_client).update_tenant_plan( + tenant_id=tenant_id, + body=plan_reservation + ) + + return {"message": "Tenant plan updated successfully"} + except Exception as e: + raise HTTPException(status_code=500, detail="Failed to update tenant plan") \ No newline at end of file From 26d04afac13a993da3b76294000d7887d666badf Mon Sep 17 00:00:00 2001 From: jSasaki Date: Thu, 2 Oct 2025 19:25:39 +0900 Subject: [PATCH 2/4] =?UTF-8?q?UpdateTenantPlanRequest=E3=82=92Optional?= =?UTF-8?q?=E3=83=95=E3=82=A3=E3=83=BC=E3=83=AB=E3=83=89=E3=81=AB=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E3=80=81TypeError=E3=82=92=E8=A7=A3=E6=B1=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- billing_router.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/billing_router.py b/billing_router.py index 0953589..bb69f2e 100644 --- a/billing_router.py +++ b/billing_router.py @@ -1,7 +1,7 @@ # billing_router.py from fastapi import APIRouter, Depends, HTTPException, Query -from typing import List, Dict, Any, Tuple +from typing import List, Dict, Any, Tuple, Optional from pydantic import BaseModel, Field from datetime import datetime, timedelta from dateutil.relativedelta import relativedelta @@ -36,9 +36,9 @@ class UpdateCountBody(BaseModel): count: int = Field(..., ge=0) class UpdateTenantPlanRequest(BaseModel): - next_plan_id: str - tax_rate_id: str = None - using_next_plan_from: int = None + next_plan_id: Optional[str] = None + tax_rate_id: Optional[str] = None + using_next_plan_from: Optional[int] = None # --- 認可ヘルパー --- @@ -382,7 +382,7 @@ def get_pricing_plans(auth_user: Any = Depends(fastapi_auth)): plans = PricingPlansApi(api_client=pricing_api_client).get_pricing_plans() return plans.pricing_plans except Exception as e: - raise HTTPException(status_code=500, detail="Internal server error") + raise HTTPException(status_code=500, detail="Failed to retrieve pricing plans") @router.get( @@ -399,7 +399,7 @@ def get_tax_rates(auth_user: Any = Depends(fastapi_auth)): tax_rates = TaxRateApi(api_client=pricing_api_client).get_tax_rates() return tax_rates.tax_rates except Exception as e: - raise HTTPException(status_code=500, detail="Internal server error") + raise HTTPException(status_code=500, detail="Failed to retrieve tax rates") @router.get( @@ -470,17 +470,21 @@ def update_tenant_plan( raise HTTPException(status_code=403, detail="Insufficient permissions") try: - # テナントプランを更新 - plan_reservation = PlanReservation( - next_plan_id=request.next_plan_id - ) + # PlanReservationオブジェクトを作成(指定されたフィールドのみ設定) + plan_reservation_kwargs = {} + + # next_plan_idがNoneでない場合は設定(空文字も含む) + if request.next_plan_id is not None: + plan_reservation_kwargs['next_plan_id'] = request.next_plan_id + + plan_reservation = PlanReservation(**plan_reservation_kwargs) # 税率IDが指定されている場合のみ設定 - if request.tax_rate_id and request.tax_rate_id != "": + if request.tax_rate_id: plan_reservation.next_plan_tax_rate_id = request.tax_rate_id # using_next_plan_fromが指定されている場合のみ設定 - if request.using_next_plan_from and request.using_next_plan_from > 0: + if request.using_next_plan_from is not None and request.using_next_plan_from > 0: plan_reservation.using_next_plan_from = request.using_next_plan_from # テナントプランを更新 From 92040eb7615cba1bdf49fbff8b737aa3ec32fe5e Mon Sep 17 00:00:00 2001 From: jSasaki Date: Thu, 23 Oct 2025 16:07:51 +0900 Subject: [PATCH 3/4] remove unnecessary tenant checks from master data endpoints --- billing_router.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/billing_router.py b/billing_router.py index bb69f2e..120d329 100644 --- a/billing_router.py +++ b/billing_router.py @@ -374,9 +374,6 @@ def update_count_of_now( ) def get_pricing_plans(auth_user: Any = Depends(fastapi_auth)): # プラン一覧を取得する - if not auth_user.tenants: - raise HTTPException(status_code=400, detail="No tenants found for the user") - try: # 料金プラン一覧を取得 plans = PricingPlansApi(api_client=pricing_api_client).get_pricing_plans() @@ -391,14 +388,12 @@ def get_pricing_plans(auth_user: Any = Depends(fastapi_auth)): ) def get_tax_rates(auth_user: Any = Depends(fastapi_auth)): # 税率一覧を取得する - if not auth_user.tenants: - raise HTTPException(status_code=400, detail="No tenants found for the user") - try: # 税率一覧を取得 tax_rates = TaxRateApi(api_client=pricing_api_client).get_tax_rates() return tax_rates.tax_rates except Exception as e: + print(f"TaxRateApi error: {e}") raise HTTPException(status_code=500, detail="Failed to retrieve tax rates") @@ -449,6 +444,7 @@ def get_tenant_plan_info( return response except Exception as e: + print(f"TenantApi error: {e}") raise HTTPException(status_code=500, detail="Failed to retrieve tenant detail") @@ -495,4 +491,5 @@ def update_tenant_plan( return {"message": "Tenant plan updated successfully"} except Exception as e: + print(f"TenantApi error: {e}") raise HTTPException(status_code=500, detail="Failed to update tenant plan") \ No newline at end of file From f2c9715fc877866938b6ab1ee6f2590399c2cd51 Mon Sep 17 00:00:00 2001 From: jSasaki Date: Thu, 23 Oct 2025 16:21:30 +0900 Subject: [PATCH 4/4] remove unnecessary tenant_id checks in path parameters --- billing_router.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/billing_router.py b/billing_router.py index 120d329..585bff9 100644 --- a/billing_router.py +++ b/billing_router.py @@ -406,9 +406,6 @@ def get_tenant_plan_info( auth_user: Any = Depends(fastapi_auth) ): # テナントプラン情報を取得する - if not tenant_id: - raise HTTPException(status_code=400, detail="tenant_id is required") - # 管理者権限チェック if not has_billing_access(auth_user, tenant_id): raise HTTPException(status_code=403, detail="Insufficient permissions") @@ -458,9 +455,6 @@ def update_tenant_plan( auth_user: Any = Depends(fastapi_auth) ): # テナントプランを更新する - if not tenant_id: - raise HTTPException(status_code=400, detail="tenant_id is required") - # 管理者権限チェック if not has_billing_access(auth_user, tenant_id): raise HTTPException(status_code=403, detail="Insufficient permissions")