diff --git a/apiserver/plane/payment/utils/workspace_license_request.py b/apiserver/plane/payment/utils/workspace_license_request.py index 046d3db39f..d7c90d096c 100644 --- a/apiserver/plane/payment/utils/workspace_license_request.py +++ b/apiserver/plane/payment/utils/workspace_license_request.py @@ -13,7 +13,6 @@ from plane.ee.models import WorkspaceLicense def fetch_workspace_license(workspace_id, workspace_slug, free_seats=12): - # If the number of free seats is less than 12, set it to 12 workspace_free_seats = 12 if free_seats <= 12 else free_seats owner_email = Workspace.objects.get(slug=workspace_slug).owner.email @@ -78,6 +77,7 @@ def is_on_trial(workspace_license): "Check if the workspace is on a trial" if ( workspace_license.subscription + and not has_upgraded(workspace_license) and workspace_license.trial_end_date and workspace_license.trial_end_date >= timezone.now() ): @@ -89,6 +89,7 @@ def trial_remaining_days(workspace_license): """Calculate the remaining days of the trial""" if ( workspace_license.subscription + and not has_upgraded(workspace_license) and workspace_license.trial_end_date and workspace_license.trial_end_date >= timezone.now() ): @@ -96,6 +97,21 @@ def trial_remaining_days(workspace_license): return None +def is_billing_active(workspace_license): + """Check if the billing is active""" + + if workspace_license.plan == WorkspaceLicense.PlanChoice.FREE: + return False + + if ( + workspace_license.plan == WorkspaceLicense.PlanChoice.PRO + and is_on_trial(workspace_license) + ): + return False + + return True + + def show_payment_button(workspace_license): """Check if the workspace is allowed to show the payment button""" @@ -135,7 +151,6 @@ def resync_workspace_license(workspace_slug, force=False): if ( workspace_license.last_synced_at - timezone.now() ).total_seconds() > 3600 or force: - # Fetch the workspace license response = fetch_workspace_license( workspace_id=str(workspace.id), diff --git a/apiserver/plane/payment/views/product.py b/apiserver/plane/payment/views/product.py index 0a095d8790..776923cd58 100644 --- a/apiserver/plane/payment/views/product.py +++ b/apiserver/plane/payment/views/product.py @@ -21,6 +21,8 @@ from plane.ee.models import WorkspaceLicense from plane.utils.exception_logger import log_exception from plane.payment.utils.workspace_license_request import ( resync_workspace_license, + is_billing_active, + is_on_trial, ) @@ -93,36 +95,55 @@ class WebsiteUserWorkspaceEndpoint(BaseAPIView): def get(self, request): try: # Get all the workspaces where the user is admin - workspace_ids = WorkspaceMember.objects.filter( - member=request.user, - is_active=True, - role=20, - ).values_list("workspace_id", flat=True) - - # Fetch the workspaces from the workspace license - workspace_licenses = ( - WorkspaceLicense.objects.filter(workspace_id__in=workspace_ids) - .annotate(slug=F("workspace__slug")) - .annotate(name=F("workspace__name")) - .annotate(logo=F("workspace__logo")) - .annotate(product=F("plan")) + workspaces = ( + WorkspaceMember.objects.filter( + member=request.user, + is_active=True, + role=20, + ) + .annotate( + slug=F("workspace__slug"), + logo=F("workspace__logo"), + name=F("workspace__name"), + ) .values( "workspace_id", "slug", "name", "logo", - "product", - "trial_end_date", - "has_activated_free_trial", - "has_added_payment_method", - "current_period_end_date", - "is_offline_payment", - "subscription", ) ) + workspace_ids = [workspace["workspace_id"] for workspace in workspaces] + + + # Fetch the workspaces from the workspace license + workspace_licenses = WorkspaceLicense.objects.filter( + workspace_id__in=workspace_ids + ) + + for workspace in workspaces: + licenses = [ + license + for license in workspace_licenses + if str(license.workspace_id) == str(workspace["workspace_id"]) + ] + + if licenses: + workspace["product"] = licenses[0].plan + workspace["is_on_trial"] = is_on_trial( + licenses[0] + ) + workspace["is_billing_active"] = is_billing_active( + licenses[0] + ) + else: + workspace["product"] = "FREE" + workspace["is_on_trial"] = False + workspace["is_billing_active"] = False + # Get the workspace details - return Response(workspace_licenses, status=status.HTTP_200_OK) + return Response(workspaces, status=status.HTTP_200_OK) except requests.exceptions.RequestException as e: if e.response.status_code == 400: @@ -165,7 +186,6 @@ class WorkspaceProductEndpoint(BaseAPIView): class WorkspaceLicenseRefreshEndpoint(BaseAPIView): - def post(self, request, slug): # Resync the workspace license _ = resync_workspace_license(workspace_slug=slug, force=True) diff --git a/web/app/upgrade/pro/cloud/[selectedWorkspace]/page.tsx b/web/app/upgrade/pro/cloud/[selectedWorkspace]/page.tsx index 2e93767461..b1ba5e2716 100644 --- a/web/app/upgrade/pro/cloud/[selectedWorkspace]/page.tsx +++ b/web/app/upgrade/pro/cloud/[selectedWorkspace]/page.tsx @@ -57,13 +57,6 @@ const CloudUpgradePlanPage = observer(() => { } ); - // fetch workspace current plan - const { data: workspaceSubscription, isLoading: workspaceSubscriptionLoading } = useSWR( - workspaceSlug ? `CLOUD_PRO_WORKSPACE_SUBSCRIPTION_PLAN_${workspaceSlug}` : null, - workspaceSlug ? () => paymentService.getWorkspaceCurrentPlan(workspaceSlug.toString()) : null, - { revalidateIfStale: false, revalidateOnFocus: false } - ); - // derived values const totalWorkspaceMembers = (workspaceMembers || [])?.filter((member) => member.role >= 15)?.length; const proProduct = (products || [])?.find((product: IPaymentProduct) => product?.type === "PRO"); @@ -75,9 +68,6 @@ const CloudUpgradePlanPage = observer(() => { const yearlyPlanUnitPrice = (yearlyPlan?.unit_amount || 0) / 1200; const yearlyDiscountedPrice = calculateYearlyDiscount(monthlyPlanUnitPrice, yearlyPlanUnitPrice); - const workspaceOnTrial = - workspaceSubscription?.has_activated_free_trial && workspaceSubscription?.trial_end_date ? true : false; - const handleStripeCheckout = (priceId: string) => { if (!workspaceSlug) { setToast({ @@ -93,38 +83,8 @@ const CloudUpgradePlanPage = observer(() => { product_id: proProduct?.id, }) .then((response) => { - if (response.payment_link) { - window.open(response.payment_link, "_self"); - } - }) - .catch((error) => { - setToast({ - type: TOAST_TYPE.ERROR, - title: "Error!", - message: error?.detail ?? "Failed to generate payment link. Please try again.", - }); - }) - .finally(() => { - setLoading(false); - }); - }; - - const handleTrialStripeCheckout = (priceId: string) => { - if (!workspaceSlug) { - setToast({ - type: TOAST_TYPE.INFO, - title: "Please select a workspace to continue", - }); - return; - } - setLoading(true); - paymentService - .modifyTrailSubscription(workspaceSlug.toString(), { - price_id: priceId, - }) - .then((response) => { - if (response.session_url) { - window.open(response.session_url, "_self"); + if (response.url) { + window.open(response.url, "_self"); } }) .catch((error) => { @@ -143,12 +103,12 @@ const CloudUpgradePlanPage = observer(() => {
Choose your billing frequency
- {yearlyDiscountedPrice && ( + {!!yearlyDiscountedPrice && (
{`Upgrade to our yearly plan and get ${Math.round(yearlyDiscountedPrice)}% off.`}
)}
- {isFetching || isLoadingProduct || workspaceSubscriptionLoading ? ( + {isFetching || isLoadingProduct ? (
@@ -209,12 +169,7 @@ const CloudUpgradePlanPage = observer(() => { {selectedPlan?.recurring && selectedPlan.unit_amount ? ( <> -