utils.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import type {Scope} from 'sentry/types/core';
  2. import type {Organization} from 'sentry/types/organization';
  3. import {defined} from 'sentry/utils';
  4. import {
  5. type BillingMetricHistory,
  6. BillingType,
  7. type EventBucket,
  8. type Subscription,
  9. } from 'getsentry/types';
  10. import {isAmPlan, isDeveloperPlan} from 'getsentry/utils/billing';
  11. import trackGetsentryAnalytics from 'getsentry/utils/trackGetsentryAnalytics';
  12. import {getBucket} from 'getsentry/views/amCheckout/utils';
  13. export const hasPermissions = ({access}: Organization, scope: Scope) =>
  14. access?.includes(scope);
  15. export const trackSubscriptionView = (
  16. organization: Organization,
  17. subscription: Subscription,
  18. page: string
  19. ) =>
  20. trackGetsentryAnalytics('subscription_page.viewed', {
  21. organization,
  22. subscription,
  23. page_tab: page,
  24. });
  25. export function calculateCategorySpend(
  26. subscription: Subscription,
  27. category: string
  28. ): {
  29. onDemandSpent: number;
  30. onDemandUnitPrice: number;
  31. prepaidPrice: number;
  32. prepaidSpent: number;
  33. unitPrice: number;
  34. } {
  35. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  36. const categoryInfo: BillingMetricHistory = subscription.categories[category];
  37. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  38. const slots: EventBucket[] = subscription.planDetails.planCategories[category];
  39. if (!defined(categoryInfo?.reserved)) {
  40. return {
  41. prepaidSpent: 0,
  42. onDemandSpent: 0,
  43. unitPrice: 0,
  44. onDemandUnitPrice: 0,
  45. prepaidPrice: 0,
  46. };
  47. }
  48. const priceBucket = getBucket({events: categoryInfo.reserved, buckets: slots});
  49. const eventsByPrice = getBucket({
  50. price: priceBucket.price,
  51. buckets: slots,
  52. }).events;
  53. const unitPrice = priceBucket.unitPrice ?? 0;
  54. // Subtract gifted usage from total usage
  55. const usage = categoryInfo.usage - categoryInfo.free;
  56. const reservedUse = Math.min(usage, eventsByPrice);
  57. const isMonthly = subscription.planDetails.billingInterval === 'monthly';
  58. // Put prepaid prices into monthly terms
  59. const prepaidPrice = priceBucket.price / (isMonthly ? 1 : 12);
  60. const percentPrepaidUsed = Math.min(reservedUse / eventsByPrice, 1);
  61. const prepaidSpent = percentPrepaidUsed * prepaidPrice;
  62. const onDemandSpent = categoryInfo.onDemandSpendUsed ?? 0;
  63. return {
  64. prepaidSpent,
  65. prepaidPrice,
  66. onDemandSpent,
  67. unitPrice,
  68. onDemandUnitPrice: priceBucket.onDemandPrice ?? 0,
  69. };
  70. }
  71. export function calculateTotalSpend(subscription: Subscription): {
  72. onDemandTotalSpent: number;
  73. prepaidTotalPrice: number;
  74. prepaidTotalSpent: number;
  75. } {
  76. let prepaidTotalSpent = 0;
  77. let onDemandTotalSpent = 0;
  78. let prepaidTotalPrice = 0;
  79. for (const category of subscription.planDetails.categories) {
  80. const {prepaidSpent, onDemandSpent, prepaidPrice} = calculateCategorySpend(
  81. subscription,
  82. category
  83. );
  84. prepaidTotalSpent += prepaidSpent;
  85. onDemandTotalSpent += onDemandSpent;
  86. prepaidTotalPrice += prepaidPrice;
  87. }
  88. return {prepaidTotalSpent, onDemandTotalSpent, prepaidTotalPrice};
  89. }
  90. /**
  91. * Check if the plan is one that we can show spend for
  92. */
  93. export function shouldSeeSpendVisibility(subscription: Subscription) {
  94. return (
  95. !subscription.isSponsored &&
  96. !subscription.isTrial &&
  97. subscription.type !== BillingType.INVOICED &&
  98. // Exclude mmX as the remaining mmX plans should be managed or partner plans
  99. isAmPlan(subscription.plan) &&
  100. // No spend from developer plans
  101. !isDeveloperPlan(subscription.planDetails)
  102. );
  103. }
  104. export function hasSpendVisibilityNotificationsFeature(organization: Organization) {
  105. return organization.features.includes('spend-visibility-notifications');
  106. }