diff --git a/components/gitpod-db/go/cost_center.go b/components/gitpod-db/go/cost_center.go index 8ba758e3c0a829..cfb50de79e7c0a 100644 --- a/components/gitpod-db/go/cost_center.go +++ b/components/gitpod-db/go/cost_center.go @@ -22,8 +22,9 @@ var CostCenterNotFound = errors.New("CostCenter not found") type BillingStrategy string const ( - CostCenter_Stripe BillingStrategy = "stripe" - CostCenter_Other BillingStrategy = "other" + CostCenter_Stripe BillingStrategy = "stripe" + CostCenter_Other BillingStrategy = "other" + CostCenter_ChargebeeCancelled BillingStrategy = "chargebee-cancelled" ) type CostCenter struct { @@ -103,7 +104,7 @@ func (c *CostCenterManager) GetOrCreateCostCenter(ctx context.Context, attributi // we want to reset it immediately. // This can happen in the following scenario: // * User accesses gitpod just after their CostCenter expired, but just before our periodic CostCenter reset kicks in. - if result.BillingStrategy == CostCenter_Other && result.IsExpired() { + if result.BillingStrategy != CostCenter_Stripe && result.IsExpired() { cc, err := c.ResetUsage(ctx, result.ID) if err != nil { logger.WithError(err).Error("Failed to reset expired usage.") @@ -237,7 +238,7 @@ func (c *CostCenterManager) newInvoiceUsageRecord(ctx context.Context, attributi }, nil } -func (c *CostCenterManager) ListLatestCostCentersWithBillingTimeBefore(ctx context.Context, strategy BillingStrategy, billingTimeBefore time.Time) ([]CostCenter, error) { +func (c *CostCenterManager) ListManagedCostCentersWithBillingTimeBefore(ctx context.Context, billingTimeBefore time.Time) ([]CostCenter, error) { db := c.conn.WithContext(ctx) var results []CostCenter @@ -250,7 +251,7 @@ func (c *CostCenterManager) ListLatestCostCentersWithBillingTimeBefore(ctx conte tx := db.Table(fmt.Sprintf("%s as cc", (&CostCenter{}).TableName())). // Join on our set of latest CostCenter records Joins("INNER JOIN (?) AS expiredCC on cc.id = expiredCC.id AND cc.creationTime = expiredCC.creationTime", subquery). - Where("cc.billingStrategy = ?", strategy). + Where("cc.billingStrategy != ?", CostCenter_Stripe). // Stripe is managed externally Where("nextBillingTime != ?", ""). Where("nextBillingTime < ?", TimeToISO8601(billingTimeBefore)). FindInBatches(&batch, 1000, func(tx *gorm.DB, iteration int) error { @@ -273,7 +274,7 @@ func (c *CostCenterManager) ResetUsage(ctx context.Context, id AttributionID) (C return cc, err } logger = logger.WithField("cost_center", cc) - if cc.BillingStrategy != CostCenter_Other { + if cc.BillingStrategy == CostCenter_Stripe { return CostCenter{}, fmt.Errorf("cannot reset usage for Billing Strategy %s for Cost Center ID: %s", cc.BillingStrategy, cc.ID) } if !cc.IsExpired() { @@ -290,11 +291,19 @@ func (c *CostCenterManager) ResetUsage(ctx context.Context, id AttributionID) (C nextBillingTime = cc.NextBillingTime.Time().AddDate(0, 1, 0) } + futureSpendingLimit := cc.SpendingLimit + futurebillingStrategy := cc.BillingStrategy + // chargebee cancellations will be switched to free plan (strategy: other) + if cc.BillingStrategy == CostCenter_ChargebeeCancelled { + futureSpendingLimit = c.cfg.ForTeams + futurebillingStrategy = CostCenter_Other + } + // All fields on the new cost center remain the same, except for BillingCycleStart, NextBillingTime, and CreationTime newCostCenter := CostCenter{ ID: cc.ID, - SpendingLimit: cc.SpendingLimit, - BillingStrategy: cc.BillingStrategy, + SpendingLimit: futureSpendingLimit, + BillingStrategy: futurebillingStrategy, BillingCycleStart: NewVarCharTime(billingCycleStart), NextBillingTime: NewVarCharTime(nextBillingTime), CreationTime: NewVarCharTime(now), diff --git a/components/gitpod-db/go/cost_center_test.go b/components/gitpod-db/go/cost_center_test.go index a874baef32c516..d26a2b7da858c4 100644 --- a/components/gitpod-db/go/cost_center_test.go +++ b/components/gitpod-db/go/cost_center_test.go @@ -321,7 +321,7 @@ func TestCostCenter_ListLatestCostCentersWithBillingTimeBefore(t *testing.T) { ts := time.Date(2022, 10, 10, 10, 10, 10, 10, time.UTC) - retrieved, err := mnr.ListLatestCostCentersWithBillingTimeBefore(context.Background(), db.CostCenter_Other, ts.Add(7*24*time.Hour)) + retrieved, err := mnr.ListManagedCostCentersWithBillingTimeBefore(context.Background(), ts.Add(7*24*time.Hour)) require.NoError(t, err) require.Len(t, retrieved, 0) }) @@ -356,7 +356,7 @@ func TestCostCenter_ListLatestCostCentersWithBillingTimeBefore(t *testing.T) { dbtest.CreateCostCenters(t, conn, costCenters...) - retrieved, err := mnr.ListLatestCostCentersWithBillingTimeBefore(context.Background(), db.CostCenter_Other, secondCreation.Add(7*24*time.Hour)) + retrieved, err := mnr.ListManagedCostCentersWithBillingTimeBefore(context.Background(), secondCreation.Add(7*24*time.Hour)) require.NoError(t, err) require.Len(t, retrieved, 1) @@ -392,7 +392,7 @@ func TestCostCenter_ListLatestCostCentersWithBillingTimeBefore(t *testing.T) { dbtest.CreateCostCenters(t, conn, costCenters...) - retrieved, err := mnr.ListLatestCostCentersWithBillingTimeBefore(context.Background(), db.CostCenter_Other, secondCreation.Add(7*24*time.Hour)) + retrieved, err := mnr.ListManagedCostCentersWithBillingTimeBefore(context.Background(), secondCreation.Add(7*24*time.Hour)) require.NoError(t, err) require.Len(t, retrieved, 0) }) diff --git a/components/gitpod-protocol/src/usage.ts b/components/gitpod-protocol/src/usage.ts index a2b3c2edee1b03..79050559922e5d 100644 --- a/components/gitpod-protocol/src/usage.ts +++ b/components/gitpod-protocol/src/usage.ts @@ -81,5 +81,6 @@ export interface CostCenterJSON { export enum CostCenter_BillingStrategy { BILLING_STRATEGY_STRIPE = "BILLING_STRATEGY_STRIPE", BILLING_STRATEGY_OTHER = "BILLING_STRATEGY_OTHER", + BILLING_STRATEGY_CHARGEBEE_CANCELLATION = "BILLING_STRATEGY_CHARGEBEE_CANCELLATION", UNRECOGNIZED = "UNRECOGNIZED", } diff --git a/components/usage-api/go/v1/usage.pb.go b/components/usage-api/go/v1/usage.pb.go index abbd83686b28f1..215d2a122259cc 100644 --- a/components/usage-api/go/v1/usage.pb.go +++ b/components/usage-api/go/v1/usage.pb.go @@ -120,8 +120,9 @@ func (Usage_Kind) EnumDescriptor() ([]byte, []int) { type CostCenter_BillingStrategy int32 const ( - CostCenter_BILLING_STRATEGY_STRIPE CostCenter_BillingStrategy = 0 - CostCenter_BILLING_STRATEGY_OTHER CostCenter_BillingStrategy = 1 + CostCenter_BILLING_STRATEGY_STRIPE CostCenter_BillingStrategy = 0 + CostCenter_BILLING_STRATEGY_OTHER CostCenter_BillingStrategy = 1 + CostCenter_BILLING_STRATEGY_CHARGEBEE_CANCELLATION CostCenter_BillingStrategy = 2 ) // Enum value maps for CostCenter_BillingStrategy. @@ -129,10 +130,12 @@ var ( CostCenter_BillingStrategy_name = map[int32]string{ 0: "BILLING_STRATEGY_STRIPE", 1: "BILLING_STRATEGY_OTHER", + 2: "BILLING_STRATEGY_CHARGEBEE_CANCELLATION", } CostCenter_BillingStrategy_value = map[string]int32{ - "BILLING_STRATEGY_STRIPE": 0, - "BILLING_STRATEGY_OTHER": 1, + "BILLING_STRATEGY_STRIPE": 0, + "BILLING_STRATEGY_OTHER": 1, + "BILLING_STRATEGY_CHARGEBEE_CANCELLATION": 2, } ) @@ -1301,7 +1304,7 @@ var file_usage_v1_usage_proto_rawDesc = []byte{ 0x12, 0x35, 0x0a, 0x0b, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x63, 0x6f, 0x73, - 0x74, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x8b, 0x03, 0x0a, 0x0a, 0x43, 0x6f, 0x73, 0x74, + 0x74, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x22, 0xb8, 0x03, 0x0a, 0x0a, 0x43, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x25, 0x0a, @@ -1321,66 +1324,69 @@ var file_usage_v1_usage_proto_rawDesc = []byte{ 0x74, 0x61, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x11, 0x62, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x43, - 0x79, 0x63, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0x4a, 0x0a, 0x0f, 0x42, 0x69, 0x6c, + 0x79, 0x63, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0x77, 0x0a, 0x0f, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1b, 0x0a, 0x17, 0x42, 0x49, 0x4c, 0x4c, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x50, 0x45, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x42, 0x49, 0x4c, 0x4c, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x4f, 0x54, - 0x48, 0x45, 0x52, 0x10, 0x01, 0x22, 0x13, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x65, 0x74, 0x55, 0x73, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x14, 0x0a, 0x12, 0x52, 0x65, - 0x73, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x97, 0x01, 0x0a, 0x19, 0x41, 0x64, 0x64, 0x55, 0x73, 0x61, 0x67, 0x65, 0x43, 0x72, 0x65, - 0x64, 0x69, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, - 0x0a, 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x12, - 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x1c, 0x0a, 0x1a, 0x41, 0x64, - 0x64, 0x55, 0x73, 0x61, 0x67, 0x65, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x4e, 0x6f, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xce, 0x04, 0x0a, 0x0c, 0x55, 0x73, 0x61, - 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x75, 0x73, 0x61, - 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x6e, - 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x75, 0x73, 0x61, - 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x6e, - 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, - 0x0d, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1e, - 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x73, - 0x74, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, - 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x73, - 0x74, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x55, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x55, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x1f, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x0a, 0x52, 0x65, 0x73, 0x65, - 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x73, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x1a, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x75, - 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x61, 0x67, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x0a, 0x47, - 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1b, 0x2e, 0x75, 0x73, 0x61, 0x67, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x55, 0x73, 0x61, - 0x67, 0x65, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x75, - 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x55, 0x73, 0x61, 0x67, 0x65, - 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x24, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, - 0x55, 0x73, 0x61, 0x67, 0x65, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2d, 0x69, - 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2d, 0x61, - 0x70, 0x69, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x48, 0x45, 0x52, 0x10, 0x01, 0x12, 0x2b, 0x0a, 0x27, 0x42, 0x49, 0x4c, 0x4c, 0x49, 0x4e, 0x47, + 0x5f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x43, 0x48, 0x41, 0x52, 0x47, 0x45, + 0x42, 0x45, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, + 0x10, 0x02, 0x22, 0x13, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x14, 0x0a, 0x12, 0x52, 0x65, 0x73, 0x65, 0x74, + 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x97, 0x01, + 0x0a, 0x19, 0x41, 0x64, 0x64, 0x55, 0x73, 0x61, 0x67, 0x65, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, + 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x07, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, + 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x1c, 0x0a, 0x1a, 0x41, 0x64, 0x64, 0x55, 0x73, + 0x61, 0x67, 0x65, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xce, 0x04, 0x0a, 0x0c, 0x55, 0x73, 0x61, 0x67, 0x65, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x73, + 0x74, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x0d, 0x53, 0x65, + 0x74, 0x43, 0x6f, 0x73, 0x74, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x75, 0x73, + 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x73, 0x74, 0x43, 0x65, + 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x75, 0x73, + 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x73, 0x74, 0x43, 0x65, + 0x6e, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x55, + 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x55, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x1f, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x6f, + 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, + 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x0a, 0x52, 0x65, 0x73, 0x65, 0x74, 0x55, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x1b, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x73, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1c, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x65, + 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x46, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x2e, + 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x75, 0x73, 0x61, 0x67, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, + 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1b, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x61, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x55, 0x73, 0x61, 0x67, 0x65, 0x43, + 0x72, 0x65, 0x64, 0x69, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x75, 0x73, 0x61, 0x67, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x55, 0x73, 0x61, 0x67, 0x65, 0x43, 0x72, 0x65, + 0x64, 0x69, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x64, 0x55, 0x73, 0x61, + 0x67, 0x65, 0x43, 0x72, 0x65, 0x64, 0x69, 0x74, 0x4e, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2d, 0x69, 0x6f, 0x2f, 0x67, + 0x69, 0x74, 0x70, 0x6f, 0x64, 0x2f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x2d, 0x61, 0x70, 0x69, 0x2f, + 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/components/usage-api/typescript/src/usage/v1/usage.pb.ts b/components/usage-api/typescript/src/usage/v1/usage.pb.ts index 62a1f5d494d52b..b523021558fc3d 100644 --- a/components/usage-api/typescript/src/usage/v1/usage.pb.ts +++ b/components/usage-api/typescript/src/usage/v1/usage.pb.ts @@ -201,6 +201,7 @@ export interface CostCenter { export enum CostCenter_BillingStrategy { BILLING_STRATEGY_STRIPE = "BILLING_STRATEGY_STRIPE", BILLING_STRATEGY_OTHER = "BILLING_STRATEGY_OTHER", + BILLING_STRATEGY_CHARGEBEE_CANCELLATION = "BILLING_STRATEGY_CHARGEBEE_CANCELLATION", UNRECOGNIZED = "UNRECOGNIZED", } @@ -212,6 +213,9 @@ export function costCenter_BillingStrategyFromJSON(object: any): CostCenter_Bill case 1: case "BILLING_STRATEGY_OTHER": return CostCenter_BillingStrategy.BILLING_STRATEGY_OTHER; + case 2: + case "BILLING_STRATEGY_CHARGEBEE_CANCELLATION": + return CostCenter_BillingStrategy.BILLING_STRATEGY_CHARGEBEE_CANCELLATION; case -1: case "UNRECOGNIZED": default: @@ -225,6 +229,8 @@ export function costCenter_BillingStrategyToJSON(object: CostCenter_BillingStrat return "BILLING_STRATEGY_STRIPE"; case CostCenter_BillingStrategy.BILLING_STRATEGY_OTHER: return "BILLING_STRATEGY_OTHER"; + case CostCenter_BillingStrategy.BILLING_STRATEGY_CHARGEBEE_CANCELLATION: + return "BILLING_STRATEGY_CHARGEBEE_CANCELLATION"; case CostCenter_BillingStrategy.UNRECOGNIZED: default: return "UNRECOGNIZED"; @@ -237,6 +243,8 @@ export function costCenter_BillingStrategyToNumber(object: CostCenter_BillingStr return 0; case CostCenter_BillingStrategy.BILLING_STRATEGY_OTHER: return 1; + case CostCenter_BillingStrategy.BILLING_STRATEGY_CHARGEBEE_CANCELLATION: + return 2; case CostCenter_BillingStrategy.UNRECOGNIZED: default: return -1; diff --git a/components/usage-api/usage/v1/usage.proto b/components/usage-api/usage/v1/usage.proto index e82b9f366e1727..bfb869df3c8f8c 100644 --- a/components/usage-api/usage/v1/usage.proto +++ b/components/usage-api/usage/v1/usage.proto @@ -127,6 +127,7 @@ message CostCenter { enum BillingStrategy { BILLING_STRATEGY_STRIPE = 0; BILLING_STRATEGY_OTHER = 1; + BILLING_STRATEGY_CHARGEBEE_CANCELLATION = 2; } BillingStrategy billing_strategy = 3; diff --git a/components/usage/pkg/apiv1/usage.go b/components/usage/pkg/apiv1/usage.go index 57fff29793c348..a05c5da693650a 100644 --- a/components/usage/pkg/apiv1/usage.go +++ b/components/usage/pkg/apiv1/usage.go @@ -204,6 +204,9 @@ func convertBillingStrategyToDB(in v1.CostCenter_BillingStrategy) db.BillingStra if in == v1.CostCenter_BILLING_STRATEGY_STRIPE { return db.CostCenter_Stripe } + if in == v1.CostCenter_BILLING_STRATEGY_CHARGEBEE_CANCELLATION { + return db.CostCenter_ChargebeeCancelled + } return db.CostCenter_Other } @@ -211,6 +214,9 @@ func convertBillingStrategyToAPI(in db.BillingStrategy) v1.CostCenter_BillingStr if in == db.CostCenter_Stripe { return v1.CostCenter_BILLING_STRATEGY_STRIPE } + if in == db.CostCenter_ChargebeeCancelled { + return v1.CostCenter_BILLING_STRATEGY_CHARGEBEE_CANCELLATION + } return v1.CostCenter_BILLING_STRATEGY_OTHER } @@ -240,7 +246,7 @@ func (s *UsageService) SetCostCenter(ctx context.Context, in *v1.SetCostCenterRe func (s *UsageService) ResetUsage(ctx context.Context, req *v1.ResetUsageRequest) (*v1.ResetUsageResponse, error) { now := time.Now() - costCentersToUpdate, err := s.costCenterManager.ListLatestCostCentersWithBillingTimeBefore(ctx, db.CostCenter_Other, now) + costCentersToUpdate, err := s.costCenterManager.ListManagedCostCentersWithBillingTimeBefore(ctx, now) if err != nil { log.WithError(err).Error("Failed to list cost centers to update.") return nil, status.Errorf(codes.Internal, "Failed to identify expired cost centers for Other billing strategy") diff --git a/components/usage/pkg/apiv1/usage_test.go b/components/usage/pkg/apiv1/usage_test.go index 4b8b1f0c3a088b..8eedf0aa3a9ad6 100644 --- a/components/usage/pkg/apiv1/usage_test.go +++ b/components/usage/pkg/apiv1/usage_test.go @@ -341,7 +341,7 @@ func TestListUsage(t *testing.T) { } -func TestAddUSageCreditNote(t *testing.T) { +func TestAddUsageCreditNote(t *testing.T) { conn := dbtest.ConnectForTests(t) attributionID := db.NewTeamAttributionID(uuid.New().String()) @@ -382,3 +382,54 @@ func TestAddUSageCreditNote(t *testing.T) { } } + +func TestHandleChargebeeCancellations(t *testing.T) { + conn := dbtest.ConnectForTests(t) + + cancellationDate := time.Date(2022, 8, 1, 0, 0, 0, 0, time.UTC) + + attributionID := db.NewTeamAttributionID(uuid.New().String()) + + dbtest.CreateUsageRecords(t, conn, dbtest.NewUsage(t, db.Usage{ + AttributionID: attributionID, + EffectiveTime: db.NewVarCharTime(cancellationDate.Add(-6 * time.Hour)), + CreditCents: 100, + Draft: false, + }), dbtest.NewUsage(t, db.Usage{ + AttributionID: attributionID, + EffectiveTime: db.NewVarCharTime(cancellationDate.Add(-4 * time.Hour)), + CreditCents: 200, + Draft: false, + }), dbtest.NewUsage(t, db.Usage{ + AttributionID: attributionID, + EffectiveTime: db.NewVarCharTime(cancellationDate.Add(-2 * time.Hour)), + CreditCents: 300, + Draft: true, + })) + + cc := dbtest.CreateCostCenters(t, conn, db.CostCenter{ + ID: attributionID, + BillingStrategy: db.CostCenter_ChargebeeCancelled, + SpendingLimit: 1000, + BillingCycleStart: db.NewVarCharTime(cancellationDate.Add(-2 * time.Hour)), + NextBillingTime: db.NewVarCharTime(cancellationDate), + CreationTime: db.NewVarCharTime(cancellationDate.Add(-2 * time.Hour)), + })[0] + + usageService := newUsageService(t, conn) + + balanceBefore, err := usageService.GetBalance(context.Background(), &v1.GetBalanceRequest{AttributionId: string(cc.ID)}) + require.NoError(t, err) + require.Equal(t, float64(6), balanceBefore.Credits) + + _, err = usageService.ResetUsage(context.Background(), &v1.ResetUsageRequest{}) + require.NoError(t, err) + + costCenterAfter, err := usageService.GetCostCenter(context.Background(), &v1.GetCostCenterRequest{AttributionId: string(cc.ID)}) + require.NoError(t, err) + require.Equal(t, v1.CostCenter_BILLING_STRATEGY_OTHER, costCenterAfter.CostCenter.BillingStrategy) + + balanceAfter, err := usageService.GetBalance(context.Background(), &v1.GetBalanceRequest{AttributionId: string(cc.ID)}) + require.NoError(t, err) + require.Equal(t, float64(0), balanceAfter.Credits) +}