123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- import type {Scope} from 'sentry/types/core';
- import type {Organization} from 'sentry/types/organization';
- import {defined} from 'sentry/utils';
- import {
- type BillingMetricHistory,
- BillingType,
- type EventBucket,
- type Subscription,
- } from 'getsentry/types';
- import {isAmPlan, isDeveloperPlan} from 'getsentry/utils/billing';
- import trackGetsentryAnalytics from 'getsentry/utils/trackGetsentryAnalytics';
- import {getBucket} from 'getsentry/views/amCheckout/utils';
- export const hasPermissions = ({access}: Organization, scope: Scope) =>
- access?.includes(scope);
- export const trackSubscriptionView = (
- organization: Organization,
- subscription: Subscription,
- page: string
- ) =>
- trackGetsentryAnalytics('subscription_page.viewed', {
- organization,
- subscription,
- page_tab: page,
- });
- export function calculateCategorySpend(
- subscription: Subscription,
- category: string
- ): {
- onDemandSpent: number;
- onDemandUnitPrice: number;
- prepaidPrice: number;
- prepaidSpent: number;
- unitPrice: number;
- } {
- // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
- const categoryInfo: BillingMetricHistory = subscription.categories[category];
- // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
- const slots: EventBucket[] = subscription.planDetails.planCategories[category];
- if (!defined(categoryInfo?.reserved)) {
- return {
- prepaidSpent: 0,
- onDemandSpent: 0,
- unitPrice: 0,
- onDemandUnitPrice: 0,
- prepaidPrice: 0,
- };
- }
- const priceBucket = getBucket({events: categoryInfo.reserved, buckets: slots});
- const eventsByPrice = getBucket({
- price: priceBucket.price,
- buckets: slots,
- }).events;
- const unitPrice = priceBucket.unitPrice ?? 0;
- // Subtract gifted usage from total usage
- const usage = categoryInfo.usage - categoryInfo.free;
- const reservedUse = Math.min(usage, eventsByPrice);
- const isMonthly = subscription.planDetails.billingInterval === 'monthly';
- // Put prepaid prices into monthly terms
- const prepaidPrice = priceBucket.price / (isMonthly ? 1 : 12);
- const percentPrepaidUsed = Math.min(reservedUse / eventsByPrice, 1);
- const prepaidSpent = percentPrepaidUsed * prepaidPrice;
- const onDemandSpent = categoryInfo.onDemandSpendUsed ?? 0;
- return {
- prepaidSpent,
- prepaidPrice,
- onDemandSpent,
- unitPrice,
- onDemandUnitPrice: priceBucket.onDemandPrice ?? 0,
- };
- }
- export function calculateTotalSpend(subscription: Subscription): {
- onDemandTotalSpent: number;
- prepaidTotalPrice: number;
- prepaidTotalSpent: number;
- } {
- let prepaidTotalSpent = 0;
- let onDemandTotalSpent = 0;
- let prepaidTotalPrice = 0;
- for (const category of subscription.planDetails.categories) {
- const {prepaidSpent, onDemandSpent, prepaidPrice} = calculateCategorySpend(
- subscription,
- category
- );
- prepaidTotalSpent += prepaidSpent;
- onDemandTotalSpent += onDemandSpent;
- prepaidTotalPrice += prepaidPrice;
- }
- return {prepaidTotalSpent, onDemandTotalSpent, prepaidTotalPrice};
- }
- /**
- * Check if the plan is one that we can show spend for
- */
- export function shouldSeeSpendVisibility(subscription: Subscription) {
- return (
- !subscription.isSponsored &&
- !subscription.isTrial &&
- subscription.type !== BillingType.INVOICED &&
- // Exclude mmX as the remaining mmX plans should be managed or partner plans
- isAmPlan(subscription.plan) &&
- // No spend from developer plans
- !isDeveloperPlan(subscription.planDetails)
- );
- }
- export function hasSpendVisibilityNotificationsFeature(organization: Organization) {
- return organization.features.includes('spend-visibility-notifications');
- }
|