diff --git a/api/v3/handlers/subscriptions/convert_test.go b/api/v3/handlers/subscriptions/convert_test.go index bffd0c8284..f1d2c45cf4 100644 --- a/api/v3/handlers/subscriptions/convert_test.go +++ b/api/v3/handlers/subscriptions/convert_test.go @@ -137,7 +137,9 @@ func TestToAPIBillingSubscriptionViewRoundtrip(t *testing.T) { // given: a subscription created from the example plan, which has phases and rate cards cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) spec, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ CustomerId: cust.ID, diff --git a/api/v3/handlers/subscriptions/migrate_test.go b/api/v3/handlers/subscriptions/migrate_test.go index 2f7cadf531..6a3fda388a 100644 --- a/api/v3/handlers/subscriptions/migrate_test.go +++ b/api/v3/handlers/subscriptions/migrate_test.go @@ -68,13 +68,15 @@ func TestMigrateSubscriptionHandler(t *testing.T) { defer db.Cleanup(t) deps := subscriptiontestutils.NewService(t, db) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone()).Build()) + AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig)).Build()) before := subscriptiontestutils.CreateSubscriptionFromPlan(t, &deps, plan, start) clock.FreezeTime(start.Add(24 * time.Hour)) deps.PlanHelper.CreatePlan(t, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone(), subscriptiontestutils.ExampleRateCard2.Clone()).Build()) + AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig), subscriptiontestutils.ExampleRateCard2.Clone()).Build()) clock.FreezeTime(start.Add(10 * 24 * time.Hour)) resolver, err := featureresolver.New(deps.FeatureConnector) diff --git a/openmeter/productcatalog/plan/adapter/adapter_test.go b/openmeter/productcatalog/plan/adapter/adapter_test.go index 0c372cb6d9..417bbdd047 100644 --- a/openmeter/productcatalog/plan/adapter/adapter_test.go +++ b/openmeter/productcatalog/plan/adapter/adapter_test.go @@ -305,7 +305,6 @@ func TestPostgresAdapter(t *testing.T) { t, namespace, pctestutils.WithPlanKey("custom-plan-currency"), - withoutTaxConfig, func(t *testing.T, p *productcatalog.Plan) { t.Helper() p.Currency = custom.Reference() @@ -686,11 +685,11 @@ func TestListPlansExcludeUnrepresentableCurrencies(t *testing.T) { namespace := pctestutils.NewTestNamespace(t) - plain := pctestutils.NewTestPlan(t, namespace, pctestutils.WithPlanKey("plain"), withoutTaxConfig) + plain := pctestutils.NewTestPlan(t, namespace, pctestutils.WithPlanKey("plain")) _, err := env.PlanRepository.CreatePlan(t.Context(), plain) require.NoError(t, err, "creating plain plan must not fail") - withOverride := pctestutils.NewTestPlan(t, namespace, pctestutils.WithPlanKey("with-override"), withoutTaxConfig) + withOverride := pctestutils.NewTestPlan(t, namespace, pctestutils.WithPlanKey("with-override")) overriddenRateCard, ok := withOverride.Phases[0].RateCards[0].(*productcatalog.FlatFeeRateCard) require.True(t, ok, "default test plan rate card must be flat fee") custom, err := env.Currency.CreateCurrency(t.Context(), currencytestutils.NewCreateCurrencyInput(namespace, "TOKEN", "Tokens", "tok")) @@ -699,7 +698,7 @@ func TestListPlansExcludeUnrepresentableCurrencies(t *testing.T) { _, err = env.PlanRepository.CreatePlan(t.Context(), withOverride) require.NoError(t, err, "creating plan with rate-card currency override must not fail") - withCustomDefault := pctestutils.NewTestPlan(t, namespace, pctestutils.WithPlanKey("with-custom-default"), withoutTaxConfig) + withCustomDefault := pctestutils.NewTestPlan(t, namespace, pctestutils.WithPlanKey("with-custom-default")) withCustomDefault.Currency = custom.Reference() _, err = env.PlanRepository.CreatePlan(t.Context(), withCustomDefault) require.NoError(t, err, "creating plan with custom default currency must not fail") @@ -728,21 +727,6 @@ func TestListPlansExcludeUnrepresentableCurrencies(t *testing.T) { }) } -func withoutTaxConfig(t *testing.T, p *productcatalog.Plan) { - t.Helper() - - for _, phase := range p.Phases { - for _, rateCard := range phase.RateCards { - err := rateCard.ChangeMeta(func(meta productcatalog.RateCardMeta) (productcatalog.RateCardMeta, error) { - meta.TaxConfig = nil - - return meta, nil - }) - require.NoError(t, err) - } - } -} - type createPlanVersionInput struct { Namespace string Version int @@ -776,7 +760,7 @@ func testListPlanStatusFilter(ctx context.Context, t *testing.T, repo plan.Repos ns := "list-plan-status-filter" - planV1Input := pctestutils.NewTestPlan(t, ns, withoutTaxConfig) + planV1Input := pctestutils.NewTestPlan(t, ns) err := createPlanVersion(ctx, repo, createPlanVersionInput{ Namespace: ns, diff --git a/openmeter/productcatalog/planaddon/adapter/adapter_test.go b/openmeter/productcatalog/planaddon/adapter/adapter_test.go index 90a13f677e..7034086dc2 100644 --- a/openmeter/productcatalog/planaddon/adapter/adapter_test.go +++ b/openmeter/productcatalog/planaddon/adapter/adapter_test.go @@ -42,15 +42,7 @@ func TestPlanAddonAdapterBackfillsTaxConfigFromTaxCode(t *testing.T) { }, }) - planInput := pctestutils.NewTestPlan(t, namespace) - err := planInput.Phases[0].RateCards[0].ChangeMeta(func(meta productcatalog.RateCardMeta) (productcatalog.RateCardMeta, error) { - meta.TaxConfig = &productcatalog.TaxConfig{ - TaxCodeID: lo.ToPtr(taxCode.ID), - } - - return meta, nil - }) - require.NoError(t, err) + planInput := pctestutils.NewTestPlan(t, namespace, pctestutils.WithRateCardTaxCode(taxCode)) createdPlan, err := env.PlanRepository.CreatePlan(ctx, planInput) require.NoError(t, err) diff --git a/openmeter/productcatalog/subscription/service/change_test.go b/openmeter/productcatalog/subscription/service/change_test.go index e4a7cf5aa6..1e431c7222 100644 --- a/openmeter/productcatalog/subscription/service/change_test.go +++ b/openmeter/productcatalog/subscription/service/change_test.go @@ -46,7 +46,9 @@ func TestChange(t *testing.T) { t.Run("Should change to different plan", func(t *testing.T) { withDeps(t, func(t *testing.T, deps tDeps) { - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.subDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) now := testutils.GetRFC3339Time(t, "2021-01-01T00:01:10Z") clock.SetTime(now) @@ -98,7 +100,7 @@ func TestChange(t *testing.T) { Description: lo.ToPtr("Test Phase 4 Description"), }, RateCards: productcatalog.RateCards{ - subscriptiontestutils.ExampleRateCard1.Clone(), + subscriptiontestutils.ExampleRateCard1(taxConfig), }, }) @@ -146,7 +148,9 @@ func TestChange(t *testing.T) { t.Run("Should change to different plan starting from specific phase", func(t *testing.T) { withDeps(t, func(t *testing.T, deps tDeps) { - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.subDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) now := testutils.GetRFC3339Time(t, "2021-01-01T00:01:10Z") clock.SetTime(now) @@ -198,7 +202,7 @@ func TestChange(t *testing.T) { Description: lo.ToPtr("Test Phase 4 Description"), }, RateCards: productcatalog.RateCards{ - subscriptiontestutils.ExampleRateCard1.Clone(), + subscriptiontestutils.ExampleRateCard1(taxConfig), }, }) @@ -268,7 +272,9 @@ func TestChange(t *testing.T) { t.Run("Should not allow changing to inactive plan", func(t *testing.T) { withDeps(t, func(t *testing.T, deps tDeps) { - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.subDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) now := testutils.GetRFC3339Time(t, "2021-01-01T00:01:10Z") clock.SetTime(now) @@ -320,7 +326,7 @@ func TestChange(t *testing.T) { Description: lo.ToPtr("Test Phase 4 Description"), }, RateCards: productcatalog.RateCards{ - subscriptiontestutils.ExampleRateCard1.Clone(), + subscriptiontestutils.ExampleRateCard1(taxConfig), }, }) @@ -386,7 +392,9 @@ func TestChange(t *testing.T) { t.Run("Should apply the settlement mode override when creating from a plan reference", func(t *testing.T) { withDeps(t, func(t *testing.T, deps tDeps) { - examplePlanInput := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.subDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) require.Equal(t, productcatalog.CreditThenInvoiceSettlementMode, examplePlanInput.Plan.SettlementMode) now := testutils.GetRFC3339Time(t, "2021-01-01T00:01:10Z") @@ -430,7 +438,9 @@ func TestChange(t *testing.T) { t.Run("Should apply the settlement mode override when changing to a plan reference", func(t *testing.T) { withDeps(t, func(t *testing.T, deps tDeps) { - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.subDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) now := testutils.GetRFC3339Time(t, "2021-01-01T00:01:10Z") clock.SetTime(now) @@ -532,7 +542,9 @@ func TestChangeInlineCustomPlanRejectsMissingFeature(t *testing.T) { customer := deps.CustomerAdapter.CreateExampleCustomer(t) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - currentPlanInput := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + currentPlanInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) currentPlanInput.Plan.Key = "" currentPlanInput.Plan.Version = 0 currentPlan := plansubscription.PlanInput{} @@ -552,7 +564,7 @@ func TestChangeInlineCustomPlanRejectsMissingFeature(t *testing.T) { require.NoError(t, err) const missingFeatureKey = "missing-change-feature" - replacementPlanInput := subscriptiontestutils.GetExamplePlanInput(t) + replacementPlanInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) replacementPlanInput.Plan.Key = "" replacementPlanInput.Plan.Version = 0 require.NoError(t, replacementPlanInput.Plan.Phases[0].RateCards[0].ChangeMeta(func(meta productcatalog.RateCardMeta) (productcatalog.RateCardMeta, error) { diff --git a/openmeter/productcatalog/subscription/service/create_test.go b/openmeter/productcatalog/subscription/service/create_test.go index c3aafd210b..3d07261057 100644 --- a/openmeter/productcatalog/subscription/service/create_test.go +++ b/openmeter/productcatalog/subscription/service/create_test.go @@ -79,7 +79,9 @@ func TestCreateInlineCustomPlanRejectsMissingFeature(t *testing.T) { deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) const missingFeatureKey = "missing-feature" - planInput := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + planInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) planInput.Plan.Key = "" planInput.Plan.Version = 0 require.NoError(t, planInput.Plan.Phases[0].RateCards[0].ChangeMeta(func(meta productcatalog.RateCardMeta) (productcatalog.RateCardMeta, error) { @@ -151,8 +153,10 @@ func TestCreateSettlementModeOverride(t *testing.T) { cust := deps.subDeps.CustomerAdapter.CreateExampleCustomer(t) deps.subDeps.FeatureConnector.CreateExampleFeatures(t, deps.subDeps.ExampleMeterID) + taxCode := deps.subDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) // Build a custom plan input; clear Key/Version so PlanFromPlanInput accepts it. - planInput := subscriptiontestutils.GetExamplePlanInput(t) + planInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) require.Equal(t, productcatalog.CreditThenInvoiceSettlementMode, planInput.Plan.SettlementMode, "precondition: plan default must be CreditThenInvoice") planInput.Plan.Key = "" planInput.Plan.Version = 0 @@ -198,7 +202,9 @@ func TestCreateSettlementModeOverride(t *testing.T) { cust := deps.subDeps.CustomerAdapter.CreateExampleCustomer(t) deps.subDeps.FeatureConnector.CreateExampleFeatures(t, deps.subDeps.ExampleMeterID) - examplePlanInput := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.subDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) require.Equal(t, productcatalog.CreditThenInvoiceSettlementMode, examplePlanInput.Plan.SettlementMode, "precondition: plan default must be CreditThenInvoice") plan1 := deps.subDeps.PlanHelper.CreatePlan(t, examplePlanInput) @@ -255,7 +261,9 @@ func TestCreateInlineCustomCurrencyMaterializesManagedIdentity(t *testing.T) { )) require.NoError(t, err) - planInput := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + planInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) planInput.Plan.Key = "" planInput.Plan.Version = 0 planInput.Plan.Currency = managedCurrency.Reference() @@ -322,7 +330,9 @@ func TestCreateInlineCustomCurrencyGatedByCredits(t *testing.T) { customer := deps.CustomerAdapter.CreateExampleCustomer(t) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - planInput := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + planInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) planInput.Plan.Key = "" planInput.Plan.Version = 0 planInput.Plan.Currency = currencies.NewCurrencyReference("CREDITS") @@ -377,7 +387,9 @@ func TestCreateInlineEntitlementRateCardResolvesFeatureByID(t *testing.T) { featureIDByKey[f.Key] = f.ID } - planInput := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + planInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) planInput.Plan.Key = "" planInput.Plan.Version = 0 @@ -430,7 +442,9 @@ func TestCreateInlineCreditOnlyPlanSkipsCurrencyCostBasis(t *testing.T) { )) require.NoError(t, err) - planInput := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + planInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) planInput.Plan.Key = "" planInput.Plan.Version = 0 planInput.Plan.SettlementMode = productcatalog.CreditOnlySettlementMode diff --git a/openmeter/productcatalog/subscription/service/migrate_test.go b/openmeter/productcatalog/subscription/service/migrate_test.go index 4871aaf070..252e0927cb 100644 --- a/openmeter/productcatalog/subscription/service/migrate_test.go +++ b/openmeter/productcatalog/subscription/service/migrate_test.go @@ -44,7 +44,9 @@ func TestMigrate(t *testing.T) { t.Run("Should migrate to latest version of plan when none is specified", func(t *testing.T) { withDeps(t, func(t *testing.T, deps tDeps) { - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.subDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) now := testutils.GetRFC3339Time(t, "2021-01-01T00:01:10Z") clock.SetTime(now) @@ -120,7 +122,9 @@ func TestMigrate(t *testing.T) { t.Run("Should migrate to specific version of plan when provided", func(t *testing.T) { withDeps(t, func(t *testing.T, deps tDeps) { - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.subDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) now := testutils.GetRFC3339Time(t, "2021-01-01T00:01:10Z") clock.SetTime(now) @@ -194,7 +198,9 @@ func TestMigrate(t *testing.T) { t.Run("Should not allow migrating to same or smaller version", func(t *testing.T) { withDeps(t, func(t *testing.T, deps tDeps) { - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.subDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) now := testutils.GetRFC3339Time(t, "2021-01-01T00:01:10Z") clock.SetTime(now) @@ -273,7 +279,9 @@ func TestMigrate(t *testing.T) { t.Run("Should replace the subscription when a starting phase is supplied", func(t *testing.T) { withDeps(t, func(t *testing.T, deps tDeps) { - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.subDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) now := testutils.GetRFC3339Time(t, "2021-01-01T00:01:10Z") clock.SetTime(now) @@ -396,8 +404,10 @@ func TestMigrateReturnsCurrentSnapshotFromUnderLock(t *testing.T) { deps := subscriptiontestutils.NewService(t, db) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) planInput := subscriptiontestutils.BuildTestPlanInput(t).AddPhase(nil, - subscriptiontestutils.ExampleRateCard1.Clone(), + subscriptiontestutils.ExampleRateCard1(taxConfig), ).Build() p1 := deps.PlanHelper.CreatePlan(t, planInput) before := subscriptiontestutils.CreateSubscriptionFromPlan(t, &deps, p1, start) diff --git a/openmeter/productcatalog/subscription/service/plan_test.go b/openmeter/productcatalog/subscription/service/plan_test.go index bfb224f979..ddc54c74b1 100644 --- a/openmeter/productcatalog/subscription/service/plan_test.go +++ b/openmeter/productcatalog/subscription/service/plan_test.go @@ -39,8 +39,10 @@ func TestDiscountPersisting(t *testing.T) { t.Run("Should persist discounts", func(t *testing.T) { withDeps(t, func(t *testing.T, deps tDeps) { - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) - examplePlanInput1.Phases[0].RateCards[0] = subscriptiontestutils.ExampleRateCardWithDiscounts.Clone() + taxCode := deps.subDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) + examplePlanInput1.Phases[0].RateCards[0] = subscriptiontestutils.ExampleRateCardWithDiscounts(taxConfig) ctx := context.Background() diff --git a/openmeter/productcatalog/testutils/plan.go b/openmeter/productcatalog/testutils/plan.go index 47b9036fb2..578efb069a 100644 --- a/openmeter/productcatalog/testutils/plan.go +++ b/openmeter/productcatalog/testutils/plan.go @@ -6,10 +6,12 @@ import ( decimal "github.com/alpacahq/alpacadecimal" "github.com/invopop/gobl/currency" "github.com/samber/lo" + "github.com/stretchr/testify/require" "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/openmeter/productcatalog/plan" + "github.com/openmeterio/openmeter/openmeter/taxcode" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/datetime" "github.com/openmeterio/openmeter/pkg/models" @@ -35,6 +37,31 @@ func WithPlanKey(key string) TransformerFunc[productcatalog.Plan] { } } +// WithRateCardTaxCode points every rate card in the plan at tc by ID, keeping any tax behavior. +// Apply it after WithPlanPhases to cover the replacement phases. +func WithRateCardTaxCode(tc taxcode.TaxCode) TransformerFunc[productcatalog.Plan] { + return func(t *testing.T, plan *productcatalog.Plan) { + t.Helper() + + for _, phase := range plan.Phases { + for _, rc := range phase.RateCards { + require.NoError(t, rc.ChangeMeta(func(m productcatalog.RateCardMeta) (productcatalog.RateCardMeta, error) { + var behavior *productcatalog.TaxBehavior + if m.TaxConfig != nil { + behavior = m.TaxConfig.Behavior + } + + m.TaxConfig = &productcatalog.TaxConfig{TaxCodeID: &tc.ID, Behavior: behavior} + + return m, nil + })) + } + } + } +} + +// NewTestPlan returns a plan input with one untaxed flat-fee rate card; use WithRateCardTaxCode to +// reference a tax code. func NewTestPlan(t *testing.T, namespace string, transformers ...TransformerFunc[productcatalog.Plan]) plan.CreatePlanInput { t.Helper() @@ -73,11 +100,6 @@ func NewTestPlan(t *testing.T, namespace string, transformers ...TransformerFunc Description: lo.ToPtr("API Requests"), Metadata: models.Metadata{"name": "api_requests"}, EntitlementTemplate: nil, - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000000", - }, - }, Price: productcatalog.NewPriceFrom(productcatalog.FlatPrice{ Amount: decimal.NewFromInt(0), PaymentTerm: productcatalog.InArrearsPaymentTerm, diff --git a/openmeter/subscription/addon/diff/apply_test.go b/openmeter/subscription/addon/diff/apply_test.go index 5e8d312a62..096a96a2c1 100644 --- a/openmeter/subscription/addon/diff/apply_test.go +++ b/openmeter/subscription/addon/diff/apply_test.go @@ -12,7 +12,6 @@ import ( "github.com/samber/lo" "github.com/stretchr/testify/require" - "github.com/openmeterio/openmeter/openmeter/app" "github.com/openmeterio/openmeter/openmeter/currencies" currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils" "github.com/openmeterio/openmeter/openmeter/productcatalog" @@ -21,7 +20,6 @@ import ( subscriptionaddon "github.com/openmeterio/openmeter/openmeter/subscription/addon" addondiff "github.com/openmeterio/openmeter/openmeter/subscription/addon/diff" subscriptiontestutils "github.com/openmeterio/openmeter/openmeter/subscription/testutils" - "github.com/openmeterio/openmeter/openmeter/taxcode" "github.com/openmeterio/openmeter/pkg/clock" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/datetime" @@ -56,10 +54,13 @@ func TestApply(t *testing.T) { t.Run("Should return nil when there's no quantities listed", func(t *testing.T) { runWithDeps(t, func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + p, a := subscriptiontestutils.CreatePlanWithAddon( t, deps.deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.GetExampleAddonInput(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, EffectiveTo: nil, @@ -85,10 +86,13 @@ func TestApply(t *testing.T) { t.Run("Should add the new item to the subscription", func(t *testing.T) { runWithDeps(t, func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + p, a := subscriptiontestutils.CreatePlanWithAddon( t, deps.deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -145,10 +149,13 @@ func TestApply(t *testing.T) { t.Run("Should materialize the addon currency on new priced items", func(t *testing.T) { runWithDeps(t, func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + p, a := subscriptiontestutils.CreatePlanWithAddon( t, deps.deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -299,13 +306,16 @@ func TestApply(t *testing.T) { t.Run("Should add multiple instances of new item to the subscription", func(t *testing.T) { runWithDeps(t, func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + exampleAddonRateCard3, ok := subscriptiontestutils.ExampleAddonRateCard3.Clone().(*productcatalog.FlatFeeRateCard) require.True(t, ok, "expected FlatFeeRateCard, got %T", exampleAddonRateCard3) p, a := subscriptiontestutils.CreatePlanWithAddon( t, deps.deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -367,13 +377,16 @@ func TestApply(t *testing.T) { require.Equal(t, int64(300), pr.Amount.IntPart()) // It should have the proper RateCard info, which is price * quantity + bool access - compareRateCardsWithAmountChange(t, withTaxCodeIDAndCurrency(exampleAddonRateCard3, nil, currency.USD), 100*3, item.RateCard, "phase %s", p.PhaseKey) + compareRateCardsWithAmountChange(t, withCurrency(exampleAddonRateCard3, currency.USD), 100*3, item.RateCard, "phase %s", p.PhaseKey) } }) }) t.Run("Should add item for defined cadence of the addon", func(t *testing.T) { runWithDeps(t, func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + effPer := productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), } @@ -381,7 +394,7 @@ func TestApply(t *testing.T) { p, a := subscriptiontestutils.CreatePlanWithAddon( t, deps.deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.BuildAddonForTesting(t, effPer, productcatalog.AddonInstanceTypeSingle, @@ -548,6 +561,9 @@ func TestApply(t *testing.T) { t.Run("Should update an existing Item that fills its entire phase", func(t *testing.T) { runWithDeps(t, func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + effPer := productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now.AddDate(0, 0, 0)), EffectiveTo: nil, @@ -557,8 +573,8 @@ func TestApply(t *testing.T) { t, deps.deps, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(lo.ToPtr(datetime.MustParseDuration(t, "P1M")), subscriptiontestutils.ExampleRateCard3ForAddons.Clone()). - AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons.Clone()). + AddPhase(lo.ToPtr(datetime.MustParseDuration(t, "P1M")), subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig)). + AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, effPer, @@ -569,8 +585,7 @@ func TestApply(t *testing.T) { subView := subscriptiontestutils.CreateSubscriptionFromPlan(t, &deps.deps, p, now) - taxCodeID := lookupTaxCodeID(t, deps.deps, "txcd_10000000") - rc3 := withTaxCodeIDAndCurrency(&subscriptiontestutils.ExampleRateCard3ForAddons, taxCodeID, currency.USD) + rc3 := withCurrency(subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig), currency.USD) subsAdd := subscriptiontestutils.CreateAddonForSubscription(t, &deps.deps, subView.Subscription.NamespacedID, a.NamespacedID, models.CadencedModel{ ActiveFrom: *effPer.EffectiveFrom, @@ -607,6 +622,9 @@ func TestApply(t *testing.T) { t.Run("Should partially update an existing item in accordance with the cadence", func(t *testing.T) { runWithDeps(t, func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + effPer := productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), EffectiveTo: nil, @@ -616,8 +634,8 @@ func TestApply(t *testing.T) { t, deps.deps, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(lo.ToPtr(datetime.MustParseDuration(t, "P1M")), subscriptiontestutils.ExampleRateCard3ForAddons.Clone()). - AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons.Clone()). + AddPhase(lo.ToPtr(datetime.MustParseDuration(t, "P1M")), subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig)). + AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, effPer, @@ -628,8 +646,7 @@ func TestApply(t *testing.T) { subView := subscriptiontestutils.CreateSubscriptionFromPlan(t, &deps.deps, p, now) - taxCodeID := lookupTaxCodeID(t, deps.deps, "txcd_10000000") - rc3 := withTaxCodeIDAndCurrency(&subscriptiontestutils.ExampleRateCard3ForAddons, taxCodeID, currency.USD) + rc3 := withCurrency(subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig), currency.USD) subsAdd := subscriptiontestutils.CreateAddonForSubscription(t, &deps.deps, subView.Subscription.NamespacedID, a.NamespacedID, models.CadencedModel{ ActiveFrom: now.AddDate(0, 0, 5), @@ -676,6 +693,9 @@ func TestApply(t *testing.T) { t.Run("Should partially update an existing item in accordance with the cadence - but for multi instance", func(t *testing.T) { runWithDeps(t, func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + effPer := productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), EffectiveTo: nil, @@ -685,8 +705,8 @@ func TestApply(t *testing.T) { t, deps.deps, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(lo.ToPtr(datetime.MustParseDuration(t, "P1M")), subscriptiontestutils.ExampleRateCard3ForAddons.Clone()). - AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons.Clone()). + AddPhase(lo.ToPtr(datetime.MustParseDuration(t, "P1M")), subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig)). + AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, effPer, @@ -697,8 +717,7 @@ func TestApply(t *testing.T) { subView := subscriptiontestutils.CreateSubscriptionFromPlan(t, &deps.deps, p, now) - taxCodeID := lookupTaxCodeID(t, deps.deps, "txcd_10000000") - rc3 := withTaxCodeIDAndCurrency(&subscriptiontestutils.ExampleRateCard3ForAddons, taxCodeID, currency.USD) + rc3 := withCurrency(subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig), currency.USD) subsAdd := subscriptiontestutils.CreateAddonForSubscription(t, &deps.deps, subView.Subscription.NamespacedID, a.NamespacedID, models.CadencedModel{ ActiveFrom: now.AddDate(0, 0, 5), @@ -773,6 +792,9 @@ func TestApply(t *testing.T) { t.Run("Should guarantee access is continuous across changing items", func(t *testing.T) { runWithDeps(t, func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + t0 := now clock.FreezeTime(t0.Add(time.Millisecond)) // Addons are written so that effective period is exclusive on both ends :) @@ -799,7 +821,7 @@ func TestApply(t *testing.T) { t, deps.deps, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, effPer, @@ -821,16 +843,16 @@ func TestApply(t *testing.T) { spec := subView.Spec // Let's just manipulate the spec object directly - ogItem := spec.Phases["test_phase_1"].ItemsByKey[subscriptiontestutils.ExampleRateCard3ForAddons.Key()][0] + ogItem := spec.Phases["test_phase_1"].ItemsByKey[subscriptiontestutils.ExampleFeatureKey2][0] ogRateCard, ok := ogItem.RateCard.(*productcatalog.FlatFeeRateCard) require.True(t, ok) - spec.Phases["test_phase_1"].ItemsByKey[subscriptiontestutils.ExampleRateCard3ForAddons.Key()] = []*subscription.SubscriptionItemSpec{ + spec.Phases["test_phase_1"].ItemsByKey[subscriptiontestutils.ExampleFeatureKey2] = []*subscription.SubscriptionItemSpec{ // First, before gap { CreateSubscriptionItemInput: subscription.CreateSubscriptionItemInput{ CreateSubscriptionItemPlanInput: subscription.CreateSubscriptionItemPlanInput{ PhaseKey: spec.Phases["test_phase_1"].PhaseKey, - ItemKey: subscriptiontestutils.ExampleRateCard3ForAddons.Key(), + ItemKey: subscriptiontestutils.ExampleFeatureKey2, RateCard: ogItem.RateCard.Clone(), }, CreateSubscriptionItemCustomerInput: subscription.CreateSubscriptionItemCustomerInput{ @@ -844,7 +866,7 @@ func TestApply(t *testing.T) { CreateSubscriptionItemInput: subscription.CreateSubscriptionItemInput{ CreateSubscriptionItemPlanInput: subscription.CreateSubscriptionItemPlanInput{ PhaseKey: spec.Phases["test_phase_1"].PhaseKey, - ItemKey: subscriptiontestutils.ExampleRateCard3ForAddons.Key(), + ItemKey: subscriptiontestutils.ExampleFeatureKey2, RateCard: ogItem.RateCard.Clone(), }, CreateSubscriptionItemCustomerInput: subscription.CreateSubscriptionItemCustomerInput{ @@ -859,7 +881,7 @@ func TestApply(t *testing.T) { require.NoError(t, err) // Now let's make sure that we have 5 items: - key := subscriptiontestutils.ExampleRateCard3ForAddons.Key() + key := subscriptiontestutils.ExampleFeatureKey2 phase := spec.GetSortedPhases()[0] pCad, err := spec.GetPhaseCadence(phase.PhaseKey) @@ -882,7 +904,7 @@ func TestApply(t *testing.T) { // [t2-t3]: ExampleAddonRateCard4 require.True(t, items[2].GetCadence(pCad).ActiveFrom.Equal(t2)) require.True(t, items[2].GetCadence(pCad).ActiveTo.Equal(t3)) - compareRateCardsWithAmountChange(t, withTaxCodeIDAndCurrency(subsAdd.RateCards[0].AddonRateCard.RateCard.(*productcatalog.FlatFeeRateCard), nil, currency.USD), 100, items[2].RateCard, "phase %s", phase.PhaseKey) + compareRateCardsWithAmountChange(t, withCurrency(subsAdd.RateCards[0].AddonRateCard.RateCard.(*productcatalog.FlatFeeRateCard), currency.USD), 100, items[2].RateCard, "phase %s", phase.PhaseKey) // [t3-t4]: ExampleRateCard3ForAddons + ExampleAddonRateCard4 require.True(t, items[3].GetCadence(pCad).ActiveFrom.Equal(t3)) @@ -899,6 +921,9 @@ func TestApply(t *testing.T) { t.Run("Should create multiple rate cards in the addon", func(t *testing.T) { runWithDeps(t, func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + // we'll reuse this for subsadd cadence too effPer := productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), @@ -909,7 +934,7 @@ func TestApply(t *testing.T) { t, deps.deps, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, effPer, @@ -921,8 +946,7 @@ func TestApply(t *testing.T) { subView := subscriptiontestutils.CreateSubscriptionFromPlan(t, &deps.deps, pl, now) - taxCodeID := lookupTaxCodeID(t, deps.deps, "txcd_10000000") - rc3 := withTaxCodeIDAndCurrency(&subscriptiontestutils.ExampleRateCard3ForAddons, taxCodeID, currency.USD) + rc3 := withCurrency(subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig), currency.USD) subAdd := subscriptiontestutils.CreateAddonForSubscription(t, &deps.deps, subView.Subscription.NamespacedID, a.NamespacedID, models.CadencedModel{ ActiveFrom: *effPer.EffectiveFrom, @@ -944,9 +968,9 @@ func TestApply(t *testing.T) { require.NoError(t, err) // Should update ExampleRateCard3ForAddons with ExampleAddonRateCard4 - require.Contains(t, p.ItemsByKey, subscriptiontestutils.ExampleRateCard3ForAddons.Key()) + require.Contains(t, p.ItemsByKey, subscriptiontestutils.ExampleFeatureKey2) - items := p.ItemsByKey[subscriptiontestutils.ExampleRateCard3ForAddons.Key()] + items := p.ItemsByKey[subscriptiontestutils.ExampleFeatureKey2] require.Len(t, items, 1) item := items[0] @@ -967,7 +991,7 @@ func TestApply(t *testing.T) { require.True(t, item.GetCadence(pCad).ActiveFrom.Equal(now)) require.Nil(t, item.GetCadence(pCad).ActiveTo) - compareRateCardsWithAmountChange(t, withTaxCodeIDAndCurrency(&subscriptiontestutils.ExampleAddonRateCard3, nil, currency.USD), 100, item.RateCard, "phase %s", p.PhaseKey) + compareRateCardsWithAmountChange(t, withCurrency(&subscriptiontestutils.ExampleAddonRateCard3, currency.USD), 100, item.RateCard, "phase %s", p.PhaseKey) }) }) } @@ -996,6 +1020,9 @@ func TestApplyWithMultiInstance(t *testing.T) { t.Run("Should create items in phase according to quantities", func(t *testing.T) { runWithDeps(t, func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + t0 := now t1 := t0.AddDate(0, 0, 1) @@ -1011,7 +1038,7 @@ func TestApplyWithMultiInstance(t *testing.T) { t, deps.deps, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, effPer, @@ -1022,8 +1049,7 @@ func TestApplyWithMultiInstance(t *testing.T) { subView := subscriptiontestutils.CreateSubscriptionFromPlan(t, &deps.deps, pl, now) - taxCodeID := lookupTaxCodeID(t, deps.deps, "txcd_10000000") - rc3 := withTaxCodeIDAndCurrency(&subscriptiontestutils.ExampleRateCard3ForAddons, taxCodeID, currency.USD) + rc3 := withCurrency(subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig), currency.USD) subAdd := subscriptiontestutils.CreateMultiInstanceAddonForSubscription(t, &deps.deps, subView.Subscription.NamespacedID, a.NamespacedID, []subscriptionaddon.CreateSubscriptionAddonQuantityInput{ { @@ -1075,28 +1101,10 @@ func TestApplyWithMultiInstance(t *testing.T) { }) } -// lookupTaxCodeID resolves the TaxCode entity ID for a given Stripe tax code from the database. -// This is used to set the expected TaxCodeID on test rate cards after plan creation resolves them. -func lookupTaxCodeID(t *testing.T, deps subscriptiontestutils.SubscriptionDependencies, stripeCode string) *string { - t.Helper() - - tc, err := deps.TaxCodeEnv.Service.GetTaxCodeByAppMapping(context.Background(), taxcode.GetTaxCodeByAppMappingInput{ - Namespace: subscriptiontestutils.ExampleNamespace, - AppType: app.AppTypeStripe, - TaxCode: stripeCode, - }) - require.NoError(t, err) - - return &tc.ID -} - -// withTaxCodeIDAndCurrency clones a FlatFeeRateCard and materializes the fields -// resolved when a catalog rate card becomes a subscription item. -func withTaxCodeIDAndCurrency(rc *productcatalog.FlatFeeRateCard, taxCodeID *string, currencyCode currency.Code) *productcatalog.FlatFeeRateCard { +// withCurrency clones a FlatFeeRateCard and materializes the currency resolved when a catalog +// rate card becomes a subscription item. +func withCurrency(rc *productcatalog.FlatFeeRateCard, currencyCode currency.Code) *productcatalog.FlatFeeRateCard { clone := rc.Clone().(*productcatalog.FlatFeeRateCard) - if clone.TaxConfig != nil { - clone.TaxConfig.TaxCodeID = taxCodeID - } currencyReference := currencies.NewCurrencyReference(currencyx.Code(currencyCode)) clone.Currency = ¤cyReference @@ -1144,8 +1152,8 @@ func compareRateCardsWithAmountChange(t *testing.T, baseTarget *productcatalog.F if len(msgAndArgs) > 0 { customMsg := fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) - require.True(t, left.Equal(right), "%s: %s", customMsg, msgX) + require.True(t, subscriptiontestutils.RateCardsEqual(left, right), "%s: %s", customMsg, msgX) } else { - require.True(t, left.Equal(right), msgX) + require.True(t, subscriptiontestutils.RateCardsEqual(left, right), msgX) } } diff --git a/openmeter/subscription/addon/diff/restore_test.go b/openmeter/subscription/addon/diff/restore_test.go index 77580241aa..526102fcad 100644 --- a/openmeter/subscription/addon/diff/restore_test.go +++ b/openmeter/subscription/addon/diff/restore_test.go @@ -107,6 +107,9 @@ func TestRestore(t *testing.T) { })) t.Run("Should delete a single RateCard that was created by the addon and leave in place the RateCard with the same key as that was part of the subscription", withDeps(func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + oneMonth := datetime.MustParseDuration(t, "P1M") oneMonthLater, _ := oneMonth.AddTo(now) @@ -114,7 +117,7 @@ func TestRestore(t *testing.T) { env := buildSubAndAddon( t, &deps.deps, - subscriptiontestutils.BuildTestPlanInput(t).AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone(), subscriptiontestutils.ExampleRateCard2.Clone()).Build(), + subscriptiontestutils.BuildTestPlanInput(t).AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig), subscriptiontestutils.ExampleRateCard2.Clone()).Build(), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, }, productcatalog.AddonInstanceTypeSingle, subscriptiontestutils.ExampleAddonRateCard1.Clone()), @@ -143,6 +146,9 @@ func TestRestore(t *testing.T) { })) t.Run("Should delete a single RateCard from a phase where it was created by the addon but leave RateCard with same key in another phase where it was already present", withDeps(func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + oneMonth := datetime.MustParseDuration(t, "P1M") oneMonthLater, _ := oneMonth.AddTo(now) @@ -151,7 +157,7 @@ func TestRestore(t *testing.T) { t, &deps.deps, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(&oneMonth, subscriptiontestutils.ExampleRateCard1.Clone(), subscriptiontestutils.ExampleRateCard2.Clone()). + AddPhase(&oneMonth, subscriptiontestutils.ExampleRateCard1(taxConfig), subscriptiontestutils.ExampleRateCard2.Clone()). AddPhase(nil, subscriptiontestutils.ExampleRateCard2.Clone()). Build(), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ @@ -178,11 +184,14 @@ func TestRestore(t *testing.T) { })) t.Run("Should restore a single RateCard by undoing the effects of the addon RateCard restoring original entitlement template", withDeps(func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + env := buildSubAndAddon( t, &deps.deps, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -208,13 +217,16 @@ func TestRestore(t *testing.T) { })) t.Run("Should restore a single RateCard and delete subsequent created RateCards", withDeps(func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + oneMonth := datetime.MustParseDuration(t, "P1M") env := buildSubAndAddon( t, &deps.deps, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -244,6 +256,9 @@ func TestRestore(t *testing.T) { })) t.Run("Should restore a single RateCard and then delete it in the later step", withDeps(func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + // First we add apply an AddonRateCard once, // then twice, // then remove it once, (which should leave the first addition in place) @@ -254,7 +269,7 @@ func TestRestore(t *testing.T) { t, &deps.deps, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -303,6 +318,9 @@ func TestRestore(t *testing.T) { })) t.Run("Should re-combine single split item when restoring", withDeps(func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + oneMonth := datetime.MustParseDuration(t, "P1M") oneMonthLater, _ := oneMonth.AddTo(now) @@ -311,7 +329,7 @@ func TestRestore(t *testing.T) { t, &deps.deps, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -338,6 +356,9 @@ func TestRestore(t *testing.T) { })) t.Run("Should not recombine different original items into one", withDeps(func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + twoMonths := datetime.MustParseDuration(t, "P2M") twoMonthsLater, _ := twoMonths.AddTo(now) @@ -359,7 +380,7 @@ func TestRestore(t *testing.T) { }, SettlementMode: productcatalog.CreditThenInvoiceSettlementMode, }). - AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -368,7 +389,7 @@ func TestRestore(t *testing.T) { sView := subscriptiontestutils.CreateSubscriptionFromPlan(t, &deps.deps, p, now) - ogItem := sView.Spec.Phases["test_phase_1"].ItemsByKey[subscriptiontestutils.ExampleRateCard1.Key()][0] + ogItem := sView.Spec.Phases["test_phase_1"].ItemsByKey[subscriptiontestutils.ExampleFeatureKey][0] require.NoError(t, ogItem.RateCard.ChangeMeta(func(m productcatalog.RateCardMeta) (productcatalog.RateCardMeta, error) { m.Name = "Different Name for This one" @@ -385,17 +406,17 @@ func TestRestore(t *testing.T) { []subscription.Patch{ patch.PatchRemoveItem{ PhaseKey: "test_phase_1", - ItemKey: subscriptiontestutils.ExampleRateCard1.Key(), + ItemKey: subscriptiontestutils.ExampleFeatureKey, }, patch.PatchAddItem{ PhaseKey: "test_phase_1", - ItemKey: subscriptiontestutils.ExampleRateCard1.Key(), + ItemKey: subscriptiontestutils.ExampleFeatureKey, CreateInput: subscription.SubscriptionItemSpec{ CreateSubscriptionItemInput: subscription.CreateSubscriptionItemInput{ Annotations: ogItem.Annotations, CreateSubscriptionItemPlanInput: subscription.CreateSubscriptionItemPlanInput{ PhaseKey: "test_phase_1", - ItemKey: subscriptiontestutils.ExampleRateCard1.Key(), + ItemKey: subscriptiontestutils.ExampleFeatureKey, RateCard: ogItem.RateCard, }, }, @@ -508,11 +529,14 @@ func TestRestore(t *testing.T) { }) t.Run("Should respect addon quantity when restoring", withDeps(func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + env := buildSubAndMultiInstanceAddon( t, &deps.deps, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -554,6 +578,9 @@ func TestRestore(t *testing.T) { })) t.Run("Should apply and remove same single instance addon 4 times in a row with slightly progressive times", withDeps(func(t *testing.T, deps *tcDeps) { + taxCode := deps.deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + // We will set up a subscriptionAddon which has a series of quantities set: // 1st, single quantity with 1 at t1 // 2nd, two quantities, second with 0 at t2 @@ -572,7 +599,7 @@ func TestRestore(t *testing.T) { deps.deps, subscriptiontestutils.BuildTestPlanInput(t). AddPhase(nil, - subscriptiontestutils.ExampleRateCard1.Clone(), // Flat price + subscriptiontestutils.ExampleRateCard1(taxConfig), // Flat price &productcatalog.UsageBasedRateCard{ // Dynamic price RateCardMeta: productcatalog.RateCardMeta{ Key: subscriptiontestutils.ExampleFeatureKey2, diff --git a/openmeter/subscription/addon/service/change_test.go b/openmeter/subscription/addon/service/change_test.go index 1dbc25524e..06e7efd2aa 100644 --- a/openmeter/subscription/addon/service/change_test.go +++ b/openmeter/subscription/addon/service/change_test.go @@ -75,7 +75,9 @@ func TestAddonServiceChangeQuantity(t *testing.T) { clock.SetTime(now) defer clock.ResetTime() - planInp := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + planInp := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) addonInp := subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), @@ -187,10 +189,13 @@ func createExampleSubscriptionAddon(t *testing.T, deps subscriptiontestutils.Sub clock.SetTime(now) defer clock.ResetTime() - p, add := createPlanWithAddon( + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + + p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.GetExampleAddonInput(t, productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), }), diff --git a/openmeter/subscription/addon/service/create_test.go b/openmeter/subscription/addon/service/create_test.go index 17136ce9f2..5386304561 100644 --- a/openmeter/subscription/addon/service/create_test.go +++ b/openmeter/subscription/addon/service/create_test.go @@ -31,10 +31,13 @@ func TestAddonServiceCreate(t *testing.T) { clock.SetTime(now) defer clock.ResetTime() - p, add := createPlanWithAddon( + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + + p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.GetExampleAddonInput(t, productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), }), @@ -77,10 +80,13 @@ func TestAddonServiceCreate(t *testing.T) { clock.SetTime(now) defer clock.ResetTime() - p, _ := createPlanWithAddon( + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + + p, _ := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.GetExampleAddonInput(t, productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), }), @@ -150,10 +156,13 @@ func TestAddonServiceCreate(t *testing.T) { clock.SetTime(now) defer clock.ResetTime() - p, add := createPlanWithAddon( + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + + p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.GetExampleAddonInput(t, productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), }), @@ -196,10 +205,13 @@ func TestAddonServiceCreate(t *testing.T) { clock.SetTime(now) defer clock.ResetTime() - p, _ := createPlanWithAddon( + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + + p, _ := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.GetExampleAddonInput(t, productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), }), @@ -249,7 +261,9 @@ func TestAddonServiceCreate(t *testing.T) { defer clock.ResetTime() // Let's create a plan with addon thats only available in a later phase - planInp := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + planInp := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) addonInp := subscriptiontestutils.GetExampleAddonInput(t, productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), }) @@ -325,7 +339,9 @@ func TestAddonServiceCreate(t *testing.T) { defer clock.ResetTime() // Let's create a plan with addon thats only available in a later phase - planInp := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + planInp := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) addonInp := subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), }, productcatalog.AddonInstanceTypeMultiple, subscriptiontestutils.ExampleAddonRateCard1.Clone()) @@ -404,7 +420,9 @@ func TestAddonServiceCreate(t *testing.T) { // Let's create a subscription without a plan reference cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) spec1, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ CustomerId: cust.ID, @@ -452,10 +470,13 @@ func TestAddonServiceCreate(t *testing.T) { clock.SetTime(now) defer clock.ResetTime() - p, add := createPlanWithAddon( + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + + p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.GetExampleAddonInput(t, productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), }), @@ -512,51 +533,12 @@ func TestAddonServiceCreate(t *testing.T) { }) } -func createPlanWithAddon( - t *testing.T, - deps subscriptiontestutils.SubscriptionDependencies, - planInp plan.CreatePlanInput, - addonInp addon.CreateAddonInput, -) (subscription.Plan, addon.Addon) { - t.Helper() - - _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - - p, err := deps.PlanService.CreatePlan(context.Background(), planInp) - require.Nil(t, err) - require.NotNil(t, p) - - add := deps.AddonService.CreateTestAddon(t, addonInp) - - _, err = deps.PlanAddonService.CreatePlanAddon(context.Background(), planaddon.CreatePlanAddonInput{ - NamespacedModel: models.NamespacedModel{ - Namespace: subscriptiontestutils.ExampleNamespace, - }, - PlanID: p.ID, - AddonID: add.ID, - FromPlanPhase: p.Phases[0].Key, - }) - require.Nil(t, err, "received error: %s", err) - - p, err = deps.PlanService.PublishPlan(context.Background(), plan.PublishPlanInput{ - NamespacedID: p.NamespacedID, - EffectivePeriod: productcatalog.EffectivePeriod{ - EffectiveFrom: lo.ToPtr(clock.Now()), - EffectiveTo: lo.ToPtr(testutils.GetRFC3339Time(t, "2099-01-01T00:00:00Z")), - }, - }) - require.Nil(t, err, "received error: %s", err) - - return &plansubscription.Plan{ - Plan: p.AsProductCatalogPlan(), - Ref: &p.NamespacedID, - }, add -} - func createExampleSubscription(t *testing.T, deps subscriptiontestutils.SubscriptionDependencies, currentTime time.Time) subscription.SubscriptionView { cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) spec1, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ CustomerId: cust.ID, diff --git a/openmeter/subscription/addon/service/currency_test.go b/openmeter/subscription/addon/service/currency_test.go index 45f462fba3..38086c6231 100644 --- a/openmeter/subscription/addon/service/currency_test.go +++ b/openmeter/subscription/addon/service/currency_test.go @@ -216,10 +216,13 @@ func createSubscriptionForPlanWithAddon( ) (models.NamespacedID, addon.Addon) { t.Helper() - p, add := createPlanWithAddon( + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + + p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), addonInput, ) diff --git a/openmeter/subscription/addon/service/list_test.go b/openmeter/subscription/addon/service/list_test.go index 08663bea53..56b8cd91f4 100644 --- a/openmeter/subscription/addon/service/list_test.go +++ b/openmeter/subscription/addon/service/list_test.go @@ -28,10 +28,13 @@ func TestAddonServiceGet(t *testing.T) { clock.SetTime(now) defer clock.ResetTime() - p, add := createPlanWithAddon( + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + + p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.GetExampleAddonInput(t, productcatalog.EffectivePeriod{ EffectiveFrom: lo.ToPtr(now), }), @@ -101,7 +104,9 @@ func TestAddonServiceList(t *testing.T) { // Let's create a plan - p, err := deps.PlanService.CreatePlan(context.Background(), subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + p, err := deps.PlanService.CreatePlan(context.Background(), subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) require.Nil(t, err) require.NotNil(t, p) diff --git a/openmeter/subscription/entitlement/adapter_test.go b/openmeter/subscription/entitlement/adapter_test.go index 2acc4ed129..3eb9b489e2 100644 --- a/openmeter/subscription/entitlement/adapter_test.go +++ b/openmeter/subscription/entitlement/adapter_test.go @@ -27,7 +27,8 @@ func TestGetForItemsAt(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, subscriptiontestutils.ExampleTaxConfig(taxCode))) cust := deps.CustomerAdapter.CreateExampleCustomer(t) spec, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ diff --git a/openmeter/subscription/patch/patch_test.go b/openmeter/subscription/patch/patch_test.go index 73a997df88..8bf553216e 100644 --- a/openmeter/subscription/patch/patch_test.go +++ b/openmeter/subscription/patch/patch_test.go @@ -197,7 +197,7 @@ func (ts *testsuite[T]) Run(t *testing.T) { } func getDefaultSpec(t *testing.T, activeFrom time.Time) (*subscription.SubscriptionSpec, *psubs.Plan) { - pInp := subscriptiontestutils.GetExamplePlanInput(t) + pInp := subscriptiontestutils.GetExamplePlanInput(t, productcatalog.TaxConfig{TaxCodeID: lo.ToPtr("example-tax-code")}) p := &psubs.Plan{ Plan: pInp.Plan, Ref: &models.NamespacedID{Namespace: pInp.Namespace, ID: pInp.Key}, diff --git a/openmeter/subscription/service/listviews_test.go b/openmeter/subscription/service/listviews_test.go index 6551c0b2ed..f8d59ac843 100644 --- a/openmeter/subscription/service/listviews_test.go +++ b/openmeter/subscription/service/listviews_test.go @@ -34,7 +34,9 @@ func TestListViewsMultiCustomer(t *testing.T) { service := deps.SubscriptionService _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) // given: two distinct customers, each with a subscription from the same plan cust1 := deps.CustomerAdapter.CreateExampleCustomer(t) @@ -87,7 +89,9 @@ func TestGetViewResolvesSoftDeletedCustomer(t *testing.T) { service := deps.SubscriptionService _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) // given: a subscription whose customer is subsequently soft-deleted cust := deps.CustomerAdapter.CreateExampleCustomer(t) diff --git a/openmeter/subscription/service/migration_test.go b/openmeter/subscription/service/migration_test.go index 1fdd17328e..573ae6ce79 100644 --- a/openmeter/subscription/service/migration_test.go +++ b/openmeter/subscription/service/migration_test.go @@ -58,11 +58,13 @@ func TestUpdateRejectsMigrationBetweenReadAndLock(t *testing.T) { defer db.Cleanup(t) deps := subscriptiontestutils.NewService(t, db) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - p1 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.BuildTestPlanInput(t).AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone()).Build()) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + p1 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.BuildTestPlanInput(t).AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig)).Build()) before := subscriptiontestutils.CreateSubscriptionFromPlan(t, &deps, p1, start) clock.FreezeTime(start.Add(10 * 24 * time.Hour)) p2 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.BuildTestPlanInput(t).AddPhase(nil, - subscriptiontestutils.ExampleRateCard1.Clone(), subscriptiontestutils.ExampleRateCard2.Clone()).Build()) + subscriptiontestutils.ExampleRateCard1(taxConfig), subscriptiontestutils.ExampleRateCard2.Clone()).Build()) var migrated subscription.SubscriptionView customerService := &migrateDuringUpdateValidation{Service: deps.CustomerService, migrate: func(ctx context.Context) error { var err error @@ -142,15 +144,17 @@ func TestCancelIncludesMigrationCommittedBeforeLock(t *testing.T) { deps := subscriptiontestutils.NewService(t, db) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) p1 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.BuildTestPlanInput(t).AddPhase(nil, - subscriptiontestutils.ExampleRateCard1.Clone()).Build()) + subscriptiontestutils.ExampleRateCard1(taxConfig)).Build()) before := subscriptiontestutils.CreateSubscriptionFromPlan(t, &deps, p1, start) clock.FreezeTime(start.Add(10 * 24 * time.Hour)) defer clock.UnFreeze() p2 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.BuildTestPlanInput(t).AddPhase(nil, - subscriptiontestutils.ExampleRateCard1.Clone(), subscriptiontestutils.ExampleRateCard3ForAddons.Clone()).Build()) + subscriptiontestutils.ExampleRateCard1(taxConfig), subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig)).Build()) var migrated subscription.SubscriptionView features := &migrateBeforeCancelLock{ FeatureConnector: deps.FeatureConnector, diff --git a/openmeter/subscription/service/service_test.go b/openmeter/subscription/service/service_test.go index 4469f5820a..6c74691998 100644 --- a/openmeter/subscription/service/service_test.go +++ b/openmeter/subscription/service/service_test.go @@ -41,7 +41,9 @@ func TestCreation(t *testing.T) { cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) defaultSpecFromPlan, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ CustomerId: cust.ID, @@ -112,7 +114,9 @@ func TestCreation(t *testing.T) { cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) specWithAnnotations, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ CustomerId: cust.ID, @@ -170,7 +174,9 @@ func TestCreation(t *testing.T) { require.Nil(t, err) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) defaultSpecFromPlan, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ CustomerId: cust.ID, @@ -215,7 +221,9 @@ func TestCreation(t *testing.T) { require.Nil(t, err) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) defaultSpecFromPlan, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ CustomerId: cust.ID, @@ -318,7 +326,9 @@ func TestCancellation(t *testing.T) { cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) // First, let's create a subscription defaultSpecFromPlan, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ @@ -436,7 +446,9 @@ func TestContinuing(t *testing.T) { cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) // First, let's create a subscription defaultSpecFromPlan, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ @@ -523,7 +535,9 @@ func TestContinuing(t *testing.T) { cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) // First, let's create a subscription spec1, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ @@ -631,7 +645,9 @@ func TestList(t *testing.T) { require.Nil(t, err) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) // Let's create some subscriptions: // - One active @@ -815,7 +831,9 @@ func TestList(t *testing.T) { require.Nil(t, err) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) spec1, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ CustomerId: cust1.ID, @@ -911,7 +929,9 @@ func TestSubscriptionChangeTrackingAnnotations(t *testing.T) { cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan1 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan1 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) // Create first subscription sub1, err := workflowService.CreateFromPlan(ctx, subscriptionworkflow.CreateSubscriptionWorkflowInput{ @@ -927,7 +947,7 @@ func TestSubscriptionChangeTrackingAnnotations(t *testing.T) { require.Nil(t, err) // Create second plan - examplePlanInput2 := subscriptiontestutils.GetExamplePlanInput(t) + examplePlanInput2 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) examplePlanInput2.Key = "example-plan-2" examplePlanInput2.Name = "Example Plan 2" plan2 := deps.PlanHelper.CreatePlan(t, examplePlanInput2) @@ -974,7 +994,9 @@ func TestSubscriptionChangeTrackingAnnotations(t *testing.T) { cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan1 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan1 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) // Create first subscription sub1, err := workflowService.CreateFromPlan(ctx, subscriptionworkflow.CreateSubscriptionWorkflowInput{ @@ -990,7 +1012,7 @@ func TestSubscriptionChangeTrackingAnnotations(t *testing.T) { require.Nil(t, err) // Create second plan - examplePlanInput2 := subscriptiontestutils.GetExamplePlanInput(t) + examplePlanInput2 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) examplePlanInput2.Key = "example-plan-2" examplePlanInput2.Name = "Example Plan 2" plan2 := deps.PlanHelper.CreatePlan(t, examplePlanInput2) @@ -1038,7 +1060,9 @@ func TestSubscriptionChangeTrackingAnnotations(t *testing.T) { cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan1 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan1 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) // Create first subscription sub1, err := workflowService.CreateFromPlan(ctx, subscriptionworkflow.CreateSubscriptionWorkflowInput{ @@ -1054,7 +1078,7 @@ func TestSubscriptionChangeTrackingAnnotations(t *testing.T) { require.Nil(t, err) // Create second plan - examplePlanInput2 := subscriptiontestutils.GetExamplePlanInput(t) + examplePlanInput2 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) examplePlanInput2.Key = "example-plan-2" examplePlanInput2.Name = "Example Plan 2" plan2 := deps.PlanHelper.CreatePlan(t, examplePlanInput2) @@ -1102,7 +1126,9 @@ func TestSubscriptionChangeTrackingAnnotations(t *testing.T) { cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan1 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan1 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) // Create standalone scheduled subscription (can be deleted) futureTime := currentTime.AddDate(0, 1, 0) @@ -1144,7 +1170,9 @@ func TestSubscriptionChangeTrackingAnnotations(t *testing.T) { cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan1 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan1 := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) // Create scheduled subscription with nil annotations (can be deleted) futureTime := currentTime.AddDate(0, 1, 0) @@ -1173,6 +1201,11 @@ func TestSubscriptionChangeTrackingAnnotations(t *testing.T) { } func TestTaxCodeResolution(t *testing.T) { + // The Stripe-code cases exercise the legacy input: the example plan with its taxed rate cards + // carrying only a Stripe code, which the services resolve to a tax code. + const stripeCode = "txcd_10000000" + stripeTaxConfig := productcatalog.TaxConfig{Stripe: &productcatalog.StripeTaxConfig{Code: stripeCode}} + t.Run("Should resolve and persist TaxCodeID for subscription items with a Stripe tax code", func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -1188,7 +1221,7 @@ func TestTaxCodeResolution(t *testing.T) { cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, stripeTaxConfig)) spec, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ CustomerId: cust.ID, @@ -1206,9 +1239,7 @@ func TestTaxCodeResolution(t *testing.T) { view, err := service.GetView(ctx, models.NamespacedID{ID: sub.ID, Namespace: sub.Namespace}) require.NoError(t, err) - // Look up the TaxCode entity that should have been created during subscription creation. - // ExampleRateCard1 and ExampleRateCard3ForAddons both use Stripe code "txcd_10000000". - const stripeCode = "txcd_10000000" + // Look up the TaxCode entity that should have been created for the Stripe code. tc, err := deps.TaxCodeEnv.Service.GetTaxCodeByAppMapping(ctx, taxcode.GetTaxCodeByAppMappingInput{ Namespace: subscriptiontestutils.ExampleNamespace, AppType: app.AppTypeStripe, @@ -1256,7 +1287,7 @@ func TestTaxCodeResolution(t *testing.T) { cust := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, stripeTaxConfig)) spec, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ CustomerId: cust.ID, @@ -1275,7 +1306,6 @@ func TestTaxCodeResolution(t *testing.T) { view, err := service.GetView(ctx, models.NamespacedID{ID: sub.ID, Namespace: sub.Namespace}) require.NoError(t, err) - const stripeCode = "txcd_10000000" tc, err := deps.TaxCodeEnv.Service.GetTaxCodeByAppMapping(ctx, taxcode.GetTaxCodeByAppMappingInput{ Namespace: subscriptiontestutils.ExampleNamespace, AppType: app.AppTypeStripe, @@ -1509,10 +1539,14 @@ func TestBillingCadenceLegacyCancellation(t *testing.T) { func TestBillingCadenceLegacyFeatureResolution(t *testing.T) { for _, tc := range []struct { name string - rateCard productcatalog.RateCard + rateCard func(productcatalog.TaxConfig) productcatalog.RateCard }{ - {name: "flat fee with item feature", rateCard: subscriptiontestutils.ExampleAddonRateCard4.Clone()}, - {name: "usage based with entitlement feature", rateCard: subscriptiontestutils.ExampleRateCard1.Clone()}, + {name: "flat fee with item feature", rateCard: func(productcatalog.TaxConfig) productcatalog.RateCard { + return subscriptiontestutils.ExampleAddonRateCard4.Clone() + }}, + {name: "usage based with entitlement feature", rateCard: func(taxConfig productcatalog.TaxConfig) productcatalog.RateCard { + return subscriptiontestutils.ExampleRateCard1(taxConfig) + }}, } { t.Run(tc.name, func(t *testing.T) { // given: a persisted weekly subscription whose feature-linked item predates the cadence minimum @@ -1524,7 +1558,10 @@ func TestBillingCadenceLegacyFeatureResolution(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) customer := deps.CustomerAdapter.CreateExampleCustomer(t) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - spec, err := subscriptiontestutils.BuildTestSubscriptionSpec(t).AddPhase(nil, tc.rateCard).Build() + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + rateCard := tc.rateCard(taxConfig) + spec, err := subscriptiontestutils.BuildTestSubscriptionSpec(t).AddPhase(nil, rateCard).Build() require.NoError(t, err) spec.Plan = nil spec.CustomerId = customer.ID @@ -1535,7 +1572,7 @@ func TestBillingCadenceLegacyFeatureResolution(t *testing.T) { require.NoError(t, err) view, err := deps.SubscriptionService.GetView(t.Context(), sub.NamespacedID) require.NoError(t, err) - itemID := view.Phases[0].ItemsByKey[tc.rateCard.Key()][0].SubscriptionItem.ID + itemID := view.Phases[0].ItemsByKey[rateCard.Key()][0].SubscriptionItem.ID _, err = dbDeps.DBClient.SubscriptionItem.UpdateOneID(itemID).SetBillingCadence(datetime.ISODurationString("PT1H")).Save(t.Context()) require.NoError(t, err) @@ -1544,7 +1581,7 @@ func TestBillingCadenceLegacyFeatureResolution(t *testing.T) { // then: reads and cancellation work, but unrelated updates still reject the short cadence require.NoError(t, err) - item := view.Phases[0].ItemsByKey[tc.rateCard.Key()][0] + item := view.Phases[0].ItemsByKey[rateCard.Key()][0] require.NotNil(t, item.Feature) require.True(t, item.Spec.RateCard.AsMeta().Feature.IsResolved()) require.Equal(t, "PT1H", item.Spec.RateCard.GetBillingCadence().String()) diff --git a/openmeter/subscription/service/sync_test.go b/openmeter/subscription/service/sync_test.go index 8304b9b74f..46692ceeea 100644 --- a/openmeter/subscription/service/sync_test.go +++ b/openmeter/subscription/service/sync_test.go @@ -373,7 +373,7 @@ func TestEdit(t *testing.T) { require.True(t, ok) // Let's make sure it has the item we want to change - iKey := subscriptiontestutils.ExampleRateCard1.Key() + iKey := subscriptiontestutils.ExampleFeatureKey v, ok := spec.Phases[pKey].ItemsByKey[iKey] require.True(t, ok) @@ -420,7 +420,9 @@ func TestEdit(t *testing.T) { require.NotNil(t, cust) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) tc.Handler(t, TDeps{ CurrentTime: currentTime, @@ -670,7 +672,9 @@ func TestSyncReconciliation(t *testing.T) { cust := deps.CustomerAdapter.CreateExampleCustomer(t) require.NotNil(t, cust) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) spec, err := subscription.NewSpecFromPlan(plan, subscription.CreateSubscriptionCustomerInput{ Name: "Test", @@ -737,14 +741,16 @@ func TestDeleteScheduledDowngradeCanExpandDeletedSubscriptionForSync(t *testing. deps := subscriptiontestutils.NewService(t, dbDeps) customerEntity := deps.CustomerAdapter.CreateExampleCustomer(t) _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) billingAnchor := time.Date(2026, 5, 1, 0, 0, 0, 0, time.UTC) - premiumPlanInput := subscriptiontestutils.GetExamplePlanInput(t) + premiumPlanInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) premiumPlanInput.Key = "premium-plan" premiumPlanInput.Name = "Premium" premiumPlan := deps.PlanHelper.CreatePlan(t, premiumPlanInput) - basicPlanInput := subscriptiontestutils.GetExamplePlanInput(t) + basicPlanInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) basicPlanInput.Key = "basic-plan" basicPlanInput.Name = "Basic" basicPlan := deps.PlanHelper.CreatePlan(t, basicPlanInput) diff --git a/openmeter/subscription/testutils/compare.go b/openmeter/subscription/testutils/compare.go index a960682351..c41fb9c86e 100644 --- a/openmeter/subscription/testutils/compare.go +++ b/openmeter/subscription/testutils/compare.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/openmeterio/openmeter/openmeter/entitlement" + "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/openmeter/subscription" subscriptionaddon "github.com/openmeterio/openmeter/openmeter/subscription/addon" "github.com/openmeterio/openmeter/pkg/datetime" @@ -80,21 +81,7 @@ func ValidateSpecAndView(t *testing.T, expected subscription.SubscriptionSpec, f rc1JSON, _ := json.Marshal(specItem.RateCard) rc2JSON, _ := json.Marshal(foundItem.SubscriptionItem.RateCard) - assert.True(t, specItem.RateCard.Equal(foundItem.SubscriptionItem.RateCard), "rate card mismatch for item %s in phase %s: \nspec: %s \n\nview: %s", specItem.ItemKey, specPhase.PhaseKey, rc1JSON, rc2JSON) - - // Let's validate the TaxCodeID - specMeta := specItem.RateCard.AsMeta() - foundMeta := foundItem.SubscriptionItem.RateCard.AsMeta() - if specMeta.TaxConfig != nil && specMeta.TaxConfig.TaxCodeID != nil && *specMeta.TaxConfig.TaxCodeID != "" { - require.NotNil(t, foundMeta.TaxConfig, "TaxConfig must be set for item %s (phase %s) with TaxCodeID %s", specItem.ItemKey, specPhase.PhaseKey, *specMeta.TaxConfig.TaxCodeID) - assert.Equal(t, specMeta.TaxConfig.TaxCodeID, foundMeta.TaxConfig.TaxCodeID, "TaxCodeID mismatch for item %s in phase %s", specItem.ItemKey, specPhase.PhaseKey) - } else if specMeta.TaxConfig != nil && specMeta.TaxConfig.Stripe != nil && specMeta.TaxConfig.Stripe.Code != "" { - require.NotNil(t, foundMeta.TaxConfig, "TaxConfig must be set for item %s (phase %s) with Stripe tax code %s", specItem.ItemKey, specPhase.PhaseKey, specMeta.TaxConfig.Stripe.Code) - require.NotNil(t, foundMeta.TaxConfig.TaxCodeID, "TaxCodeID must be backfilled for item %s (phase %s) with Stripe tax code %s", specItem.ItemKey, specPhase.PhaseKey, specMeta.TaxConfig.Stripe.Code) - assert.Equal(t, specMeta.TaxConfig.TaxCodeID, foundMeta.TaxConfig.TaxCodeID, "TaxCodeID mismatch for item %s in phase %s", specItem.ItemKey, specPhase.PhaseKey) - } else if foundMeta.TaxConfig != nil { - assert.Nil(t, foundMeta.TaxConfig.TaxCodeID, "TaxCodeID must be nil for item %s (phase %s) without a tax code", specItem.ItemKey, specPhase.PhaseKey) - } + assert.True(t, RateCardsEqual(specItem.RateCard, foundItem.SubscriptionItem.RateCard), "rate card mismatch for item %s in phase %s: \nspec: %s \n\nview: %s", specItem.ItemKey, specPhase.PhaseKey, rc1JSON, rc2JSON) // Let's validate the Feature linking var expectedFeatureKey, actualFeatureKey *string @@ -170,6 +157,7 @@ func ValidateSpecAndView(t *testing.T, expected subscription.SubscriptionSpec, f } } +// SpecsEqual treats s1 as the expected spec; see RateCardsEqual. func SpecsEqual(t *testing.T, s1, s2 subscription.SubscriptionSpec) { t.Helper() @@ -233,7 +221,7 @@ func SpecsEqual(t *testing.T, s1, s2 subscription.SubscriptionSpec) { // Let's validate the item properties assert.Equal(t, i1.ItemKey, i2.ItemKey) - assert.True(t, i1.RateCard.Equal(i2.RateCard), "rate card mismatch for item %s in phase %s: \nspec: %+v\n\nview: %+v", itemKey, key, i1.RateCard, i2.RateCard) + assert.True(t, RateCardsEqual(i1.RateCard, i2.RateCard), "rate card mismatch for item %s in phase %s: \nspec: %+v\n\nview: %+v", itemKey, key, i1.RateCard, i2.RateCard) assert.Equal(t, i1.PhaseKey, i2.PhaseKey, "phase key mismatch for item %s in phase %s", itemKey, key) // We'll compare the time offsets separately @@ -276,6 +264,47 @@ func SpecsEqual(t *testing.T, s1, s2 subscription.SubscriptionSpec) { } } +// RateCardsEqual is RateCard.Equal, except that found's Stripe tax code is ignored when expected +// identifies the tax code by TaxCodeID alone. The services fill in the Stripe code from the tax +// code's mapping, so a spec built from a TaxCodeID-only config can't know it. Otherwise the Stripe +// code is compared, so a wrong or stale code still fails, and an expectation without a tax config +// doesn't match a taxed card. +func RateCardsEqual(expected, found productcatalog.RateCard) bool { + if expected.Type() != found.Type() { + return false + } + + if lo.FromPtr(expected.GetBillingCadence()).ISOString() != lo.FromPtr(found.GetBillingCadence()).ISOString() { + return false + } + + // AsMeta returns copies, so adjusting TaxConfig here doesn't touch the rate cards. + expectedMeta := expected.AsMeta() + foundMeta := found.AsMeta() + + if c := expectedMeta.TaxConfig; c != nil && c.TaxCodeID != nil && c.Stripe == nil { + foundMeta.TaxConfig = withoutStripeTaxCode(foundMeta.TaxConfig) + } + + return expectedMeta.Equal(foundMeta) +} + +// withoutStripeTaxCode returns a copy of c without the Stripe code, or nil if nothing else is set. +func withoutStripeTaxCode(c *productcatalog.TaxConfig) *productcatalog.TaxConfig { + if c == nil { + return nil + } + + out := c.Clone() + out.Stripe = nil + + if out.Behavior == nil && out.TaxCodeID == nil { + return nil + } + + return &out +} + func tsPlusNillableISO(ts time.Time, iso *datetime.ISODuration) *time.Time { if iso == nil { return nil diff --git a/openmeter/subscription/testutils/plan.go b/openmeter/subscription/testutils/plan.go index 11a68ea5cd..18dd8991ca 100644 --- a/openmeter/subscription/testutils/plan.go +++ b/openmeter/subscription/testutils/plan.go @@ -16,12 +16,13 @@ import ( "github.com/openmeterio/openmeter/pkg/datetime" ) -func GetExamplePlanInput(t *testing.T) plan.CreatePlanInput { +// GetExamplePlanInput builds the example plan in ExampleNamespace; its taxed rate cards carry taxConfig. +func GetExamplePlanInput(t *testing.T, taxConfig productcatalog.TaxConfig) plan.CreatePlanInput { b := BuildTestPlanInput(t) - b.AddPhase(lo.ToPtr(datetime.MustParseDuration(t, "P1M")), ExampleRateCard1.Clone()) - b.AddPhase(lo.ToPtr(datetime.MustParseDuration(t, "P2M")), ExampleRateCard1.Clone(), ExampleRateCard2.Clone(), ExampleRateCard3ForAddons.Clone()) - b.AddPhase(nil, ExampleRateCard1.Clone(), ExampleRateCard3ForAddons.Clone()) + b.AddPhase(lo.ToPtr(datetime.MustParseDuration(t, "P1M")), ExampleRateCard1(taxConfig)) + b.AddPhase(lo.ToPtr(datetime.MustParseDuration(t, "P2M")), ExampleRateCard1(taxConfig), ExampleRateCard2.Clone(), ExampleRateCard3ForAddons(taxConfig)) + b.AddPhase(nil, ExampleRateCard1(taxConfig), ExampleRateCard3ForAddons(taxConfig)) return b.Build() } diff --git a/openmeter/subscription/testutils/ratecard.go b/openmeter/subscription/testutils/ratecard.go index 0f808db3af..c6ef153983 100644 --- a/openmeter/subscription/testutils/ratecard.go +++ b/openmeter/subscription/testutils/ratecard.go @@ -13,29 +13,8 @@ import ( ) var ( - ExamplePriceAmount int64 = 100 - ExampleRateCard1 productcatalog.UsageBasedRateCard = productcatalog.UsageBasedRateCard{ - RateCardMeta: productcatalog.RateCardMeta{ - Key: ExampleFeatureKey, - Name: "Rate Card 1", - Description: lo.ToPtr("Rate Card 1 Description"), - Feature: productcatalog.NewFeatureReference(nil, lo.ToPtr(ExampleFeatureKey)), - EntitlementTemplate: productcatalog.NewEntitlementTemplateFrom(productcatalog.MeteredEntitlementTemplate{ - IssueAfterReset: lo.ToPtr(100.0), - UsagePeriod: ISOMonth, - }), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000000", - }, - }, - Price: productcatalog.NewPriceFrom(productcatalog.UnitPrice{ - Amount: alpacadecimal.NewFromInt(ExamplePriceAmount), - }), - }, - BillingCadence: ISOMonth, - } - ExampleRateCard2 productcatalog.FlatFeeRateCard = productcatalog.FlatFeeRateCard{ + ExamplePriceAmount int64 = 100 + ExampleRateCard2 productcatalog.FlatFeeRateCard = productcatalog.FlatFeeRateCard{ RateCardMeta: productcatalog.RateCardMeta{ Key: "rate-card-2", Name: "Rate Card 2", @@ -47,28 +26,6 @@ var ( }, BillingCadence: &ISOMonth, } - ExampleRateCard3ForAddons productcatalog.FlatFeeRateCard = productcatalog.FlatFeeRateCard{ - RateCardMeta: productcatalog.RateCardMeta{ - Key: ExampleFeatureKey2, - Name: "Rate Card 3 for Addons", - Description: lo.ToPtr("Rate Card 3 Description"), - Feature: productcatalog.NewFeatureReference(nil, lo.ToPtr(ExampleFeatureKey2)), - EntitlementTemplate: productcatalog.NewEntitlementTemplateFrom(productcatalog.MeteredEntitlementTemplate{ - IssueAfterReset: lo.ToPtr(100.0), - UsagePeriod: ISOMonth, - }), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000000", - }, - }, - Price: productcatalog.NewPriceFrom(productcatalog.FlatPrice{ - Amount: alpacadecimal.NewFromInt(ExamplePriceAmount), - PaymentTerm: productcatalog.InAdvancePaymentTerm, - }), - }, - BillingCadence: &ISOMonth, - } // Has a boolean entitlement ExampleRateCard4ForAddons productcatalog.FlatFeeRateCard = productcatalog.FlatFeeRateCard{ RateCardMeta: productcatalog.RateCardMeta{ @@ -91,7 +48,16 @@ var ( }, BillingCadence: &ISOMonth, } - ExampleRateCardWithDiscounts productcatalog.FlatFeeRateCard = productcatalog.FlatFeeRateCard{ +) + +// The taxed example rate cards (ExampleRateCard1, ExampleRateCard3ForAddons, +// ExampleRateCardWithDiscounts) carry their own copy of taxConfig, so the plan service resolving +// one rate card's config can't leak into another. Reference the tax code by TaxCodeID; it must +// belong to the namespace the rate card is created in. + +// ExampleRateCard1 returns a new taxed usage-based rate card for the example feature. +func ExampleRateCard1(taxConfig productcatalog.TaxConfig) *productcatalog.UsageBasedRateCard { + return &productcatalog.UsageBasedRateCard{ RateCardMeta: productcatalog.RateCardMeta{ Key: ExampleFeatureKey, Name: "Rate Card 1", @@ -101,11 +67,50 @@ var ( IssueAfterReset: lo.ToPtr(100.0), UsagePeriod: ISOMonth, }), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000000", - }, - }, + TaxConfig: lo.ToPtr(taxConfig.Clone()), + Price: productcatalog.NewPriceFrom(productcatalog.UnitPrice{ + Amount: alpacadecimal.NewFromInt(ExamplePriceAmount), + }), + }, + BillingCadence: ISOMonth, + } +} + +// ExampleRateCard3ForAddons returns a new taxed flat-fee rate card for addon tests. +func ExampleRateCard3ForAddons(taxConfig productcatalog.TaxConfig) *productcatalog.FlatFeeRateCard { + return &productcatalog.FlatFeeRateCard{ + RateCardMeta: productcatalog.RateCardMeta{ + Key: ExampleFeatureKey2, + Name: "Rate Card 3 for Addons", + Description: lo.ToPtr("Rate Card 3 Description"), + Feature: productcatalog.NewFeatureReference(nil, lo.ToPtr(ExampleFeatureKey2)), + EntitlementTemplate: productcatalog.NewEntitlementTemplateFrom(productcatalog.MeteredEntitlementTemplate{ + IssueAfterReset: lo.ToPtr(100.0), + UsagePeriod: ISOMonth, + }), + TaxConfig: lo.ToPtr(taxConfig.Clone()), + Price: productcatalog.NewPriceFrom(productcatalog.FlatPrice{ + Amount: alpacadecimal.NewFromInt(ExamplePriceAmount), + PaymentTerm: productcatalog.InAdvancePaymentTerm, + }), + }, + BillingCadence: lo.ToPtr(ISOMonth), + } +} + +// ExampleRateCardWithDiscounts returns a new taxed flat-fee rate card with discounts. +func ExampleRateCardWithDiscounts(taxConfig productcatalog.TaxConfig) *productcatalog.FlatFeeRateCard { + return &productcatalog.FlatFeeRateCard{ + RateCardMeta: productcatalog.RateCardMeta{ + Key: ExampleFeatureKey, + Name: "Rate Card 1", + Description: lo.ToPtr("Rate Card 1 Description"), + Feature: productcatalog.NewFeatureReference(nil, lo.ToPtr(ExampleFeatureKey)), + EntitlementTemplate: productcatalog.NewEntitlementTemplateFrom(productcatalog.MeteredEntitlementTemplate{ + IssueAfterReset: lo.ToPtr(100.0), + UsagePeriod: ISOMonth, + }), + TaxConfig: lo.ToPtr(taxConfig.Clone()), Price: productcatalog.NewPriceFrom(productcatalog.UnitPrice{ Amount: alpacadecimal.NewFromInt(ExamplePriceAmount), }), @@ -115,9 +120,9 @@ var ( }, }, }, - BillingCadence: &ISOMonth, + BillingCadence: lo.ToPtr(ISOMonth), } -) +} func GetEntitlementTemplateUsagePeriod(t *testing.T, et productcatalog.EntitlementTemplate) *datetime.ISODuration { t.Helper() diff --git a/openmeter/subscription/testutils/taxcode.go b/openmeter/subscription/testutils/taxcode.go new file mode 100644 index 0000000000..d8a26bbf92 --- /dev/null +++ b/openmeter/subscription/testutils/taxcode.go @@ -0,0 +1,27 @@ +package subscriptiontestutils + +import ( + "github.com/samber/lo" + + "github.com/openmeterio/openmeter/openmeter/app" + "github.com/openmeterio/openmeter/openmeter/productcatalog" + "github.com/openmeterio/openmeter/openmeter/taxcode" +) + +// GetExampleTaxCodeInput is passed to TaxCodeEnv.CreateTaxCode for the example rate cards' tax code. +// The Stripe mapping is the code those rate cards carried before they referenced tax codes by ID. +func GetExampleTaxCodeInput() taxcode.CreateTaxCodeInput { + return taxcode.CreateTaxCodeInput{ + AppMappings: taxcode.TaxCodeAppMappings{ + {AppType: app.AppTypeStripe, TaxCode: "txcd_10000000"}, + }, + } +} + +// ExampleTaxConfig points rate cards at tc. Behavior is left nil, so it comes from the billing +// profile; set it on the result if a test needs one. +func ExampleTaxConfig(tc taxcode.TaxCode) productcatalog.TaxConfig { + return productcatalog.TaxConfig{ + TaxCodeID: lo.ToPtr(tc.ID), + } +} diff --git a/openmeter/subscription/workflow/service/addon_test.go b/openmeter/subscription/workflow/service/addon_test.go index d58e3ef470..047f02c0b8 100644 --- a/openmeter/subscription/workflow/service/addon_test.go +++ b/openmeter/subscription/workflow/service/addon_test.go @@ -52,10 +52,12 @@ func TestAddAddon(t *testing.T) { } t.Run("Should error on invalid input", runWithDeps(func(t *testing.T, deps subscriptiontestutils.SubscriptionDependencies) { + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -88,10 +90,12 @@ func TestAddAddon(t *testing.T) { })) t.Run("Should error if the subscription is inactive or in wrong state", runWithDeps(func(t *testing.T, deps subscriptiontestutils.SubscriptionDependencies) { + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -131,9 +135,12 @@ func TestAddAddon(t *testing.T) { t.Run("Should add a new addon to a subscription that already has a different addon", runWithDeps(func(t *testing.T, deps subscriptiontestutils.SubscriptionDependencies) { _ = deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + // Let's create a plan p, err := deps.PlanService.CreatePlan(context.Background(), subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig)). Build()) require.Nil(t, err) require.NotNil(t, p) @@ -214,15 +221,17 @@ func TestAddAddon(t *testing.T) { require.NoError(t, err) // And lets validate that both of them updated the entitlement limit - item := subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleRateCard1.Key()][0] + item := subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleFeatureKey][0] require.Equal(t, 200.0, *item.Entitlement.Entitlement.IssueAfterReset) })) t.Run("Should sync subscription with new addons contents", runWithDeps(func(t *testing.T, deps subscriptiontestutils.SubscriptionDependencies) { + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -276,10 +285,12 @@ func TestAddAddon(t *testing.T) { })) t.Run("Should return conflict error if subscription already has that addon purchased", runWithDeps(func(t *testing.T, deps subscriptiontestutils.SubscriptionDependencies) { + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -334,10 +345,12 @@ func TestChangeAddonQuantity(t *testing.T) { } t.Run("Should error if SubscriptionAddon and Subscription are in different namespaces", runWithDeps(func(t *testing.T, deps subscriptiontestutils.SubscriptionDependencies) { + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -382,10 +395,12 @@ func TestChangeAddonQuantity(t *testing.T) { })) t.Run("Should error if SubscriptionAddon doesn't belong to Subscription", runWithDeps(func(t *testing.T, deps subscriptiontestutils.SubscriptionDependencies) { + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -456,10 +471,12 @@ func TestChangeAddonQuantity(t *testing.T) { })) t.Run("Should error if the subscription is inactive or in wrong state", runWithDeps(func(t *testing.T, deps subscriptiontestutils.SubscriptionDependencies) { + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ EffectiveFrom: &now, @@ -509,11 +526,13 @@ func TestChangeAddonQuantity(t *testing.T) { })) t.Run("Should update the quantity of the addon", runWithDeps(func(t *testing.T, deps subscriptiontestutils.SubscriptionDependencies) { + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ @@ -555,7 +574,7 @@ func TestChangeAddonQuantity(t *testing.T) { require.Equal(t, subAdd.Quantities.GetAt(0).GetValue().Quantity, 1) require.Equal(t, subAdd.Quantities.GetAt(1).GetValue().Quantity, 0) - require.Len(t, subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleRateCard1.Key()], 2) + require.Len(t, subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleFeatureKey], 2) })) t.Run("Should not combine two subsequent identical items (present due to an edit) when changing the quantity after an addon is already purchased", runWithDeps(func(t *testing.T, deps subscriptiontestutils.SubscriptionDependencies) { @@ -563,6 +582,8 @@ func TestChangeAddonQuantity(t *testing.T) { twoMonthsFromNow, _ := twoMonths.AddTo(now) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, @@ -579,7 +600,7 @@ func TestChangeAddonQuantity(t *testing.T) { }, SettlementMode: productcatalog.CreditThenInvoiceSettlementMode, }). - AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig)). Build(), subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{ @@ -594,7 +615,7 @@ func TestChangeAddonQuantity(t *testing.T) { subView := subscriptiontestutils.CreateSubscriptionFromPlan(t, &deps, p, now) // Now let's edit the sub - ogItem := subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleRateCard1.Key()][0].Spec + ogItem := subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleFeatureKey][0].Spec timing := subscription.Timing{ Enum: lo.ToPtr(subscription.TimingNextBillingCycle), @@ -613,7 +634,7 @@ func TestChangeAddonQuantity(t *testing.T) { }, timing) require.NoError(t, err) - require.Len(t, subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleRateCard1.Key()], 2) + require.Len(t, subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleFeatureKey], 2) // Now let's add the addon @@ -629,7 +650,7 @@ func TestChangeAddonQuantity(t *testing.T) { require.NoError(t, err) // assert that its still two items - require.Len(t, subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleRateCard1.Key()], 2) + require.Len(t, subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleFeatureKey], 2) // Now let's change the quantity of the addon changeInp := subscriptionworkflow.ChangeAddonQuantityWorkflowInput{ @@ -644,7 +665,7 @@ func TestChangeAddonQuantity(t *testing.T) { require.NoError(t, err) // Now it should be three items: original two with addon included + 3rd without the addon after 3 months - require.Len(t, subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleRateCard1.Key()], 3) + require.Len(t, subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleFeatureKey], 3) })) } @@ -694,10 +715,13 @@ func TestAddonCombinations(t *testing.T) { } t.Run("Should handle repeated add/remove of single instance addon with dynamic price", runWithDeps(func(t *testing.T, deps subscriptiontestutils.SubscriptionDependencies) { + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + // Create a plan with mixed rate cards planInput := subscriptiontestutils.BuildTestPlanInput(t). AddPhase(nil, - subscriptiontestutils.ExampleRateCard1.Clone(), // Flat price + subscriptiontestutils.ExampleRateCard1(taxConfig), // Flat price &productcatalog.UsageBasedRateCard{ // Dynamic price RateCardMeta: productcatalog.RateCardMeta{ Key: subscriptiontestutils.ExampleFeatureKey2, diff --git a/openmeter/subscription/workflow/service/migrate_test.go b/openmeter/subscription/workflow/service/migrate_test.go index 0a47d61aa4..80fa214bf5 100644 --- a/openmeter/subscription/workflow/service/migrate_test.go +++ b/openmeter/subscription/workflow/service/migrate_test.go @@ -31,8 +31,10 @@ func TestMigrateToPlanAddons(t *testing.T) { db := subscriptiontestutils.SetupDBDeps(t) defer db.Cleanup(t) deps := subscriptiontestutils.NewService(t, db) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) planInput := subscriptiontestutils.BuildTestPlanInput(t).AddPhase(nil, - subscriptiontestutils.ExampleRateCard3ForAddons.Clone(), subscriptiontestutils.ExampleRateCard2.Clone()).Build() + subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig), subscriptiontestutils.ExampleRateCard2.Clone()).Build() p1, addon := subscriptiontestutils.CreatePlanWithAddon(t, deps, planInput, subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{EffectiveFrom: &start}, productcatalog.AddonInstanceTypeMultiple, subscriptiontestutils.ExampleAddonRateCard4.Clone())) @@ -51,7 +53,7 @@ func TestMigrateToPlanAddons(t *testing.T) { at := start.Add(9 * 24 * time.Hour) clock.FreezeTime(at) nextInput := subscriptiontestutils.BuildTestPlanInput(t).AddPhase(nil, - subscriptiontestutils.ExampleRateCard3ForAddons.Clone(), subscriptiontestutils.ExampleRateCard2.Clone(), subscriptiontestutils.ExampleRateCard1.Clone()).Build() + subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig), subscriptiontestutils.ExampleRateCard2.Clone(), subscriptiontestutils.ExampleRateCard1(taxConfig)).Build() if scenario == "changed base" { require.NoError(t, nextInput.Phases[0].RateCards[0].ChangeMeta(func(meta productcatalog.RateCardMeta) (productcatalog.RateCardMeta, error) { meta.Price = productcatalog.NewPriceFrom(productcatalog.FlatPrice{Amount: alpacadecimal.NewFromInt(200), PaymentTerm: productcatalog.InAdvancePaymentTerm}) diff --git a/openmeter/subscription/workflow/service/subscription_test.go b/openmeter/subscription/workflow/service/subscription_test.go index 73519ccef2..3ffd808a5c 100644 --- a/openmeter/subscription/workflow/service/subscription_test.go +++ b/openmeter/subscription/workflow/service/subscription_test.go @@ -111,7 +111,9 @@ func TestCreateFromPlan(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) cust := deps.CustomerAdapter.CreateExampleCustomer(t) require.NotNil(t, cust) @@ -137,8 +139,10 @@ func TestCreateFromPlan(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) p := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons.Clone(), subscriptiontestutils.ExampleRateCard4ForAddons.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig), subscriptiontestutils.ExampleRateCard4ForAddons.Clone()). Build()) cust := deps.CustomerAdapter.CreateExampleCustomer(t) @@ -154,7 +158,7 @@ func TestCreateFromPlan(t *testing.T) { }, p) require.Nil(t, err) - nonBoolItem := subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleRateCard3ForAddons.Key()][0] + nonBoolItem := subView.Phases[0].ItemsByKey[subscriptiontestutils.ExampleFeatureKey2][0] require.NotNil(t, nonBoolItem) require.Equal(t, 0, subscription.AnnotationParser.GetBooleanEntitlementCount(nonBoolItem.SubscriptionItem.Annotations)) @@ -640,7 +644,9 @@ func TestEditRunning(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) features := deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) cust := deps.CustomerAdapter.CreateExampleCustomer(t) require.NotNil(t, cust) @@ -839,7 +845,9 @@ func TestEditingCurrentPhase(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + plan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) cust := deps.CustomerAdapter.CreateExampleCustomer(t) require.NotNil(t, cust) @@ -1079,7 +1087,9 @@ func TestEditingWithTiming(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - planInput := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + planInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) plan := deps.PlanHelper.CreatePlan(t, planInput) cust := deps.CustomerAdapter.CreateExampleCustomer(t) @@ -1126,9 +1136,29 @@ func TestChangeToPlan(t *testing.T) { withDeps := func(t *testing.T) func(fn func(t *testing.T, deps testCaseDeps)) { return func(fn func(t *testing.T, deps testCaseDeps)) { + tcDeps := testCaseDeps{ + CurrentTime: testutils.GetRFC3339Time(t, "2021-01-01T00:00:00Z"), + } + + clock.SetTime(tcDeps.CurrentTime) + + // Let's build the dependencies + dbDeps := subscriptiontestutils.SetupDBDeps(t) + require.NotNil(t, dbDeps) + defer dbDeps.Cleanup(t) + + deps := subscriptiontestutils.NewService(t, dbDeps) + deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) + + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + // Let's define two plans. One is the example plan, and the second is a slightly modified version of that. // Build them per invocation so plan service mutations (e.g. resolving TaxCodeIDs) don't leak across subtests. - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) + + // Plan 2's new rate cards use a different tax code than the example plan. + plan2TaxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace) rc1 := productcatalog.UsageBasedRateCard{ RateCardMeta: productcatalog.RateCardMeta{ @@ -1140,11 +1170,7 @@ func TestChangeToPlan(t *testing.T) { IssueAfterReset: lo.ToPtr(111.0), UsagePeriod: subscriptiontestutils.ISOMonth, }), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000001", - }, - }, + TaxConfig: &productcatalog.TaxConfig{TaxCodeID: &plan2TaxCode.ID}, Price: productcatalog.NewPriceFrom(productcatalog.UnitPrice{ Amount: alpacadecimal.NewFromInt(int64(1)), }), @@ -1157,12 +1183,8 @@ func TestChangeToPlan(t *testing.T) { Key: "new-rc-2", Name: "Rate Card 2", Description: lo.ToPtr("Rate Card 2 Description"), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000001", - }, - }, - Price: nil, + TaxConfig: &productcatalog.TaxConfig{TaxCodeID: &plan2TaxCode.ID}, + Price: nil, }, BillingCadence: nil, } @@ -1204,7 +1226,7 @@ func TestChangeToPlan(t *testing.T) { Duration: lo.ToPtr(datetime.MustParseDuration(t, "P1M")), }, RateCards: productcatalog.RateCards{ - subscriptiontestutils.ExampleRateCard1.Clone(), + subscriptiontestutils.ExampleRateCard1(taxConfig), &rc2, }, }, @@ -1223,20 +1245,6 @@ func TestChangeToPlan(t *testing.T) { }, } - tcDeps := testCaseDeps{ - CurrentTime: testutils.GetRFC3339Time(t, "2021-01-01T00:00:00Z"), - } - - clock.SetTime(tcDeps.CurrentTime) - - // Let's build the dependencies - dbDeps := subscriptiontestutils.SetupDBDeps(t) - require.NotNil(t, dbDeps) - defer dbDeps.Cleanup(t) - - deps := subscriptiontestutils.NewService(t, dbDeps) - deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - // Let's create the two plans plan1 := deps.PlanHelper.CreatePlan(t, examplePlanInput1) plan2 := deps.PlanHelper.CreatePlan(t, examplePlanInput2) @@ -1319,12 +1327,11 @@ func TestEditCombinations(t *testing.T) { DBDeps *subscriptiontestutils.DBDeps Plan1 subscription.Plan SubsDeps *subscriptiontestutils.SubscriptionDependencies + TaxConfig productcatalog.TaxConfig } withDeps := func(t *testing.T) func(fn func(t *testing.T, deps testCaseDeps)) { return func(fn func(t *testing.T, deps testCaseDeps)) { - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) - tcDeps := testCaseDeps{ CurrentTime: testutils.GetRFC3339Time(t, "2021-01-01T00:00:00Z"), } @@ -1339,6 +1346,10 @@ func TestEditCombinations(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) + // Let's create the plan plan1 := deps.PlanHelper.CreatePlan(t, examplePlanInput1) @@ -1351,6 +1362,7 @@ func TestEditCombinations(t *testing.T) { tcDeps.WorkflowService = deps.WorkflowService tcDeps.Plan1 = plan1 tcDeps.SubsDeps = &deps + tcDeps.TaxConfig = taxConfig fn(t, tcDeps) } @@ -1367,11 +1379,13 @@ func TestEditCombinations(t *testing.T) { defer dbDeps.Cleanup(t) deps := subscriptiontestutils.NewService(t, dbDeps) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) p, add := subscriptiontestutils.CreatePlanWithAddon( t, deps, - subscriptiontestutils.GetExamplePlanInput(t), + subscriptiontestutils.GetExamplePlanInput(t, taxConfig), subscriptiontestutils.BuildAddonForTesting( t, productcatalog.EffectivePeriod{ @@ -1417,7 +1431,7 @@ func TestEditCombinations(t *testing.T) { t.Run("Should add boolean entitlement count annotations", func(t *testing.T) { withDeps(t)(func(t *testing.T, deps testCaseDeps) { plan := deps.SubsDeps.PlanHelper.CreatePlan(t, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard3ForAddons(deps.TaxConfig)). Build()) timingNow := subscription.Timing{ @@ -1694,8 +1708,6 @@ func TestRestore(t *testing.T) { withDeps := func(t *testing.T) func(fn func(t *testing.T, deps testCaseDeps)) { return func(fn func(t *testing.T, deps testCaseDeps)) { - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) - tcDeps := testCaseDeps{ CurrentTime: testutils.GetRFC3339Time(t, "2021-01-01T00:00:00Z"), } @@ -1710,6 +1722,10 @@ func TestRestore(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) + // Let's create the plan plan1 := deps.PlanHelper.CreatePlan(t, examplePlanInput1) @@ -1883,8 +1899,6 @@ func TestMultiSubscription(t *testing.T) { withDeps := func(t *testing.T) func(fn func(t *testing.T, deps testCaseDeps)) { return func(fn func(t *testing.T, deps testCaseDeps)) { - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) - tcDeps := testCaseDeps{ CurrentTime: testutils.GetRFC3339Time(t, "2021-01-01T00:00:00Z"), } @@ -1899,6 +1913,10 @@ func TestMultiSubscription(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) + // Let's create the plan plan1 := deps.PlanHelper.CreatePlan(t, examplePlanInput1) @@ -2078,11 +2096,6 @@ func TestSubscriptionChangeTrackingAnnotations(t *testing.T) { withDeps := func(t *testing.T) func(fn func(t *testing.T, deps testCaseDeps)) { return func(fn func(t *testing.T, deps testCaseDeps)) { - examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t) - examplePlanInput2 := subscriptiontestutils.GetExamplePlanInput(t) - examplePlanInput2.Key = "example-plan-2" - examplePlanInput2.Name = "Example Plan 2" - tcDeps := testCaseDeps{ CurrentTime: testutils.GetRFC3339Time(t, "2021-01-01T00:00:00Z"), } @@ -2097,6 +2110,13 @@ func TestSubscriptionChangeTrackingAnnotations(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + examplePlanInput1 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) + examplePlanInput2 := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) + examplePlanInput2.Key = "example-plan-2" + examplePlanInput2.Name = "Example Plan 2" + // Let's create the two plans plan1 := deps.PlanHelper.CreatePlan(t, examplePlanInput1) plan2 := deps.PlanHelper.CreatePlan(t, examplePlanInput2) @@ -2280,7 +2300,9 @@ func TestSettlementMode(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - p := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + p := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) cust := deps.CustomerAdapter.CreateExampleCustomer(t) subView, err := deps.WorkflowService.CreateFromPlan(context.Background(), subscriptionworkflow.CreateSubscriptionWorkflowInput{ @@ -2311,7 +2333,9 @@ func TestSettlementMode(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - planInput := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + planInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) planInput.SettlementMode = productcatalog.CreditOnlySettlementMode p := deps.PlanHelper.CreatePlan(t, planInput) cust := deps.CustomerAdapter.CreateExampleCustomer(t) @@ -2345,8 +2369,10 @@ func TestSettlementMode(t *testing.T) { deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) cust := deps.CustomerAdapter.CreateExampleCustomer(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) spec, err := subscriptiontestutils.BuildTestSubscriptionSpec(t). - AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone()). + AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig)). Build() require.NoError(t, err) @@ -2383,7 +2409,9 @@ func TestSettlementMode(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - p := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + p := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) cust := deps.CustomerAdapter.CreateExampleCustomer(t) subView, err := deps.WorkflowService.CreateFromPlan(context.Background(), subscriptionworkflow.CreateSubscriptionWorkflowInput{ @@ -2436,7 +2464,9 @@ func TestSettlementMode(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - planInput := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + planInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) planInput.SettlementMode = productcatalog.CreditOnlySettlementMode p := deps.PlanHelper.CreatePlan(t, planInput) cust := deps.CustomerAdapter.CreateExampleCustomer(t) @@ -2467,7 +2497,9 @@ func TestSettlementMode(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - planInput := subscriptiontestutils.GetExamplePlanInput(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + planInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) planInput.SettlementMode = productcatalog.CreditOnlySettlementMode p := deps.PlanHelper.CreatePlan(t, planInput) cust := deps.CustomerAdapter.CreateExampleCustomer(t) @@ -2498,9 +2530,11 @@ func TestSettlementMode(t *testing.T) { deps := subscriptiontestutils.NewService(t, dbDeps) deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) - basePlan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t)) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + basePlan := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.GetExamplePlanInput(t, taxConfig)) - creditOnlyPlanInput := subscriptiontestutils.GetExamplePlanInput(t) + creditOnlyPlanInput := subscriptiontestutils.GetExamplePlanInput(t, taxConfig) creditOnlyPlanInput.Key = "credit_only_plan" creditOnlyPlanInput.SettlementMode = productcatalog.CreditOnlySettlementMode creditOnlyPlan := deps.PlanHelper.CreatePlan(t, creditOnlyPlanInput) @@ -2557,12 +2591,15 @@ func TestCreateFromPlanSnapshotsUnitConfig(t *testing.T) { Rounding: productcatalog.UnitConfigRoundingModeCeiling, } + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + // ExampleRateCard1 is usage-based with a UnitPrice, which supports unit_config. - rc := subscriptiontestutils.ExampleRateCard1 + rc := subscriptiontestutils.ExampleRateCard1(taxConfig) rc.UnitConfig = unitConfig p := deps.PlanHelper.CreatePlan(t, subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(nil, &rc). + AddPhase(nil, rc). Build()) cust := deps.CustomerAdapter.CreateExampleCustomer(t) @@ -2581,7 +2618,7 @@ func TestCreateFromPlanSnapshotsUnitConfig(t *testing.T) { reloaded, err := deps.SubscriptionService.GetView(context.Background(), subView.Subscription.NamespacedID) require.NoError(t, err) - items := reloaded.Phases[0].ItemsByKey[subscriptiontestutils.ExampleRateCard1.Key()] + items := reloaded.Phases[0].ItemsByKey[subscriptiontestutils.ExampleFeatureKey] require.Len(t, items, 1) got := items[0].SubscriptionItem.RateCard.AsMeta().UnitConfig diff --git a/test/subscription/migrate_phases_test.go b/test/subscription/migrate_phases_test.go index 08396b29dc..03710db570 100644 --- a/test/subscription/migrate_phases_test.go +++ b/test/subscription/migrate_phases_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/openmeterio/openmeter/openmeter/productcatalog" + "github.com/openmeterio/openmeter/openmeter/productcatalog/plan" plansubscription "github.com/openmeterio/openmeter/openmeter/productcatalog/subscription" "github.com/openmeterio/openmeter/openmeter/subscription" subscriptionaddon "github.com/openmeterio/openmeter/openmeter/subscription/addon" @@ -48,7 +49,7 @@ func TestMigrationStartingPhaseReplacesIncompatibleTimeline(t *testing.T) { // given a single-phase subscription and a target with two phases fixture := newMigrationFixture(t) clock.FreezeTime(start.Add(10 * 24 * time.Hour)) - target := fixture.deps.PlanHelper.CreatePlan(t, migrationPlanWithTwoPhases(t)) + target := fixture.deps.PlanHelper.CreatePlan(t, migrationPlanWithTwoPhases(t, fixture.taxConfig)) clock.FreezeTime(clock.Now().Add(time.Second)) // when the caller selects the target's second phase @@ -70,7 +71,7 @@ func TestMigrationStartingPhaseAtNextCycle(t *testing.T) { // given an incompatible target timeline fixture := newMigrationFixture(t) clock.FreezeTime(start.Add(10 * 24 * time.Hour)) - target := fixture.deps.PlanHelper.CreatePlan(t, migrationPlanWithTwoPhases(t)) + target := fixture.deps.PlanHelper.CreatePlan(t, migrationPlanWithTwoPhases(t, fixture.taxConfig)) clock.FreezeTime(clock.Now().Add(time.Second)) // when replacement is explicitly scheduled for the next cycle @@ -93,7 +94,7 @@ func TestMigrationStartingPhaseWithAnchorAndCustomTime(t *testing.T) { // given an incompatible target timeline and an explicit new anchor fixture := newMigrationFixture(t) clock.FreezeTime(start.Add(10 * 24 * time.Hour)) - target := fixture.deps.PlanHelper.CreatePlan(t, migrationPlanWithTwoPhases(t)) + target := fixture.deps.PlanHelper.CreatePlan(t, migrationPlanWithTwoPhases(t, fixture.taxConfig)) clock.FreezeTime(clock.Now().Add(time.Second)) anchor := start.Add(3 * 24 * time.Hour) @@ -119,7 +120,7 @@ func TestMigrationRejectsChangedPhaseCountWithoutReset(t *testing.T) { // given a target with an additional phase fixture := newMigrationFixture(t) clock.FreezeTime(start.Add(10 * 24 * time.Hour)) - fixture.deps.PlanHelper.CreatePlan(t, migrationPlanWithTwoPhases(t)) + fixture.deps.PlanHelper.CreatePlan(t, migrationPlanWithTwoPhases(t, fixture.taxConfig)) clock.FreezeTime(clock.Now().Add(time.Second)) // when no replacement is requested @@ -149,8 +150,10 @@ func TestMigrationRejectsChangedPhaseStartWithoutReset(t *testing.T) { defer clock.UnFreeze() // given a target whose second phase starts a month later - fixture := newMigrationFixtureWithPlan(t, migrationPlanWithTwoPhases(t)) - next := migrationPlanWithTwoPhases(t) + fixture := newMigrationFixtureWithPlan(t, func(taxConfig productcatalog.TaxConfig) plan.CreatePlanInput { + return migrationPlanWithTwoPhases(t, taxConfig) + }) + next := migrationPlanWithTwoPhases(t, fixture.taxConfig) next.Phases[0].Duration = lo.ToPtr(datetime.MustParseDuration(t, "P2M")) clock.FreezeTime(start.Add(10 * 24 * time.Hour)) fixture.deps.PlanHelper.CreatePlan(t, next) @@ -218,13 +221,15 @@ func TestMigrationStartingPhaseDoesNotTransferAddons(t *testing.T) { deps := setup(t, setupConfig{}) defer deps.cleanup(t) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) input := subscriptiontestutils.BuildTestPlanInput(t).AddPhase(nil, - subscriptiontestutils.ExampleRateCard1.Clone(), subscriptiontestutils.ExampleRateCard2.Clone()).Build() + subscriptiontestutils.ExampleRateCard1(taxConfig), subscriptiontestutils.ExampleRateCard2.Clone()).Build() original, addon := subscriptiontestutils.CreatePlanWithAddon(t, deps.SubscriptionDependencies, input, subscriptiontestutils.BuildAddonForTesting(t, productcatalog.EffectivePeriod{EffectiveFrom: &start}, productcatalog.AddonInstanceTypeSingle, subscriptiontestutils.ExampleAddonRateCard1.Clone())) before := subscriptiontestutils.CreateSubscriptionFromPlan(t, &deps.SubscriptionDependencies, original, start) - fixture := migrationFixture{t: t, deps: deps, before: before} + fixture := migrationFixture{t: t, deps: deps, taxConfig: taxConfig, before: before} var err error diff --git a/test/subscription/migrate_scheduling_test.go b/test/subscription/migrate_scheduling_test.go index 5e3e3142f1..7541b98900 100644 --- a/test/subscription/migrate_scheduling_test.go +++ b/test/subscription/migrate_scheduling_test.go @@ -24,9 +24,11 @@ func TestMigrationNextCycleAcrossPhaseBoundary(t *testing.T) { defer clock.UnFreeze() // given matching timelines with the next billing cycle in the second phase - fixture := newMigrationFixtureWithPlan(t, migrationPlanWithTwoPhases(t)) + fixture := newMigrationFixtureWithPlan(t, func(taxConfig productcatalog.TaxConfig) plan.CreatePlanInput { + return migrationPlanWithTwoPhases(t, taxConfig) + }) clock.FreezeTime(start.Add(10 * 24 * time.Hour)) - target := fixture.deps.PlanHelper.CreatePlan(t, migrationPlanAddingItemToBothPhases(t)) + target := fixture.deps.PlanHelper.CreatePlan(t, migrationPlanAddingItemToBothPhases(t, fixture.taxConfig)) clock.FreezeTime(clock.Now().Add(time.Second)) // when migration is scheduled for that boundary @@ -46,9 +48,11 @@ func TestMigrationCustomTimeInLaterPhase(t *testing.T) { defer clock.UnFreeze() // given matching timelines and a target that adds an item - fixture := newMigrationFixtureWithPlan(t, migrationPlanWithTwoPhases(t)) + fixture := newMigrationFixtureWithPlan(t, func(taxConfig productcatalog.TaxConfig) plan.CreatePlanInput { + return migrationPlanWithTwoPhases(t, taxConfig) + }) clock.FreezeTime(start.Add(10 * 24 * time.Hour)) - target := fixture.deps.PlanHelper.CreatePlan(t, migrationPlanAddingItemToBothPhases(t)) + target := fixture.deps.PlanHelper.CreatePlan(t, migrationPlanAddingItemToBothPhases(t, fixture.taxConfig)) clock.FreezeTime(clock.Now().Add(time.Second)) // when migration is scheduled beyond the next cycle, inside the second phase @@ -107,20 +111,20 @@ func TestMigrationRejectsPastCustomTime(t *testing.T) { assertMigrationViewUnchanged(t, fixture) } -func migrationPlanWithTwoPhases(t *testing.T) plan.CreatePlanInput { +func migrationPlanWithTwoPhases(t *testing.T, taxConfig productcatalog.TaxConfig) plan.CreatePlanInput { t.Helper() return subscriptiontestutils.BuildTestPlanInput(t). - AddPhase(lo.ToPtr(datetime.MustParseDuration(t, "P1M")), subscriptiontestutils.ExampleRateCard1.Clone()). - AddPhase(nil, subscriptiontestutils.ExampleRateCard1.Clone()).Build() + AddPhase(lo.ToPtr(datetime.MustParseDuration(t, "P1M")), subscriptiontestutils.ExampleRateCard1(taxConfig)). + AddPhase(nil, subscriptiontestutils.ExampleRateCard1(taxConfig)).Build() } -func migrationPlanAddingItemToBothPhases(t *testing.T) plan.CreatePlanInput { +func migrationPlanAddingItemToBothPhases(t *testing.T, taxConfig productcatalog.TaxConfig) plan.CreatePlanInput { t.Helper() - input := migrationPlanWithTwoPhases(t) + input := migrationPlanWithTwoPhases(t, taxConfig) for idx := range input.Phases { - input.Phases[idx].RateCards = append(input.Phases[idx].RateCards, subscriptiontestutils.ExampleRateCard3ForAddons.Clone()) + input.Phases[idx].RateCards = append(input.Phases[idx].RateCards, subscriptiontestutils.ExampleRateCard3ForAddons(taxConfig)) } return input @@ -190,8 +194,10 @@ func TestMigrationScheduledPriceChangeInLaterPhase(t *testing.T) { defer clock.UnFreeze() // given a target that reprices the second phase - fixture := newMigrationFixtureWithPlan(t, migrationPlanWithTwoPhases(t)) - next := migrationPlanWithTwoPhases(t) + fixture := newMigrationFixtureWithPlan(t, func(taxConfig productcatalog.TaxConfig) plan.CreatePlanInput { + return migrationPlanWithTwoPhases(t, taxConfig) + }) + next := migrationPlanWithTwoPhases(t, fixture.taxConfig) require.NoError(t, next.Phases[1].RateCards[0].ChangeMeta(func(meta productcatalog.RateCardMeta) (productcatalog.RateCardMeta, error) { meta.Price = productcatalog.NewPriceFrom(productcatalog.UnitPrice{Amount: alpacadecimal.NewFromInt(50)}) return meta, nil diff --git a/test/subscription/migrate_test.go b/test/subscription/migrate_test.go index eeb44835ea..7d0a8224be 100644 --- a/test/subscription/migrate_test.go +++ b/test/subscription/migrate_test.go @@ -30,7 +30,7 @@ func TestMigrationAdditionPreservesExistingItems(t *testing.T) { fixture := newMigrationFixture(t) nextPlan := fixture.newPlanVersion() - nextPlan.Phases[0].RateCards = append(nextPlan.Phases[0].RateCards, subscriptiontestutils.ExampleRateCard3ForAddons.Clone()) + nextPlan.Phases[0].RateCards = append(nextPlan.Phases[0].RateCards, subscriptiontestutils.ExampleRateCard3ForAddons(fixture.taxConfig)) clock.FreezeTime(start.Add(24 * time.Hour)) target := fixture.deps.PlanHelper.CreatePlan(t, nextPlan) @@ -158,7 +158,7 @@ func TestMigrationNextCycleAdditionPreservesExistingItems(t *testing.T) { fixture := newMigrationFixture(t) nextPlan := fixture.newPlanVersion() - nextPlan.Phases[0].RateCards = append(nextPlan.Phases[0].RateCards, subscriptiontestutils.ExampleRateCard3ForAddons.Clone()) + nextPlan.Phases[0].RateCards = append(nextPlan.Phases[0].RateCards, subscriptiontestutils.ExampleRateCard3ForAddons(fixture.taxConfig)) clock.FreezeTime(start.Add(24 * time.Hour)) target := fixture.deps.PlanHelper.CreatePlan(t, nextPlan) @@ -307,23 +307,25 @@ func TestMigrationInvalidAnchorLeavesSubscriptionIntact(t *testing.T) { } type migrationFixture struct { - t *testing.T - deps testDeps - before subscription.SubscriptionView + t *testing.T + deps testDeps + taxConfig productcatalog.TaxConfig + before subscription.SubscriptionView } func newMigrationFixture(t *testing.T) migrationFixture { t.Helper() - input := subscriptiontestutils.BuildTestPlanInput(t).AddPhase(nil, - subscriptiontestutils.ExampleRateCard1.Clone(), - subscriptiontestutils.ExampleRateCard2.Clone(), - ).Build() - - return newMigrationFixtureWithPlan(t, input) + return newMigrationFixtureWithPlan(t, func(taxConfig productcatalog.TaxConfig) plan.CreatePlanInput { + return subscriptiontestutils.BuildTestPlanInput(t).AddPhase(nil, + subscriptiontestutils.ExampleRateCard1(taxConfig), + subscriptiontestutils.ExampleRateCard2.Clone(), + ).Build() + }) } -func newMigrationFixtureWithPlan(t *testing.T, input plan.CreatePlanInput) migrationFixture { +// newMigrationFixtureWithPlan builds the plan only after setup, so it can reference the tax code created there. +func newMigrationFixtureWithPlan(t *testing.T, buildPlan func(taxConfig productcatalog.TaxConfig) plan.CreatePlanInput) migrationFixture { t.Helper() deps := setup(t, setupConfig{}) @@ -332,7 +334,9 @@ func newMigrationFixtureWithPlan(t *testing.T, input plan.CreatePlanInput) migra deps.FeatureConnector.CreateExampleFeatures(t, deps.ExampleMeterID) customer := deps.CustomerAdapter.CreateExampleCustomer(t) - original := deps.PlanHelper.CreatePlan(t, input) + taxCode := deps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + original := deps.PlanHelper.CreatePlan(t, buildPlan(taxConfig)) before, err := deps.WorkflowService.CreateFromPlan(t.Context(), subscriptionworkflow.CreateSubscriptionWorkflowInput{ Namespace: customer.Namespace, @@ -343,14 +347,14 @@ func newMigrationFixtureWithPlan(t *testing.T, input plan.CreatePlanInput) migra }, original) require.NoError(t, err) - return migrationFixture{t: t, deps: deps, before: before} + return migrationFixture{t: t, deps: deps, taxConfig: taxConfig, before: before} } func (f migrationFixture) newPlanVersion() plan.CreatePlanInput { f.t.Helper() return subscriptiontestutils.BuildTestPlanInput(f.t).AddPhase(nil, - subscriptiontestutils.ExampleRateCard1.Clone(), + subscriptiontestutils.ExampleRateCard1(f.taxConfig), subscriptiontestutils.ExampleRateCard2.Clone(), ).Build() } diff --git a/test/subscription/scenario_editaligned_test.go b/test/subscription/scenario_editaligned_test.go index 74b77fc485..2da2f421e1 100644 --- a/test/subscription/scenario_editaligned_test.go +++ b/test/subscription/scenario_editaligned_test.go @@ -17,6 +17,7 @@ import ( pcsubscription "github.com/openmeterio/openmeter/openmeter/productcatalog/subscription" "github.com/openmeterio/openmeter/openmeter/subscription" "github.com/openmeterio/openmeter/openmeter/subscription/patch" + subscriptiontestutils "github.com/openmeterio/openmeter/openmeter/subscription/testutils" subscriptionworkflow "github.com/openmeterio/openmeter/openmeter/subscription/workflow" "github.com/openmeterio/openmeter/openmeter/testutils" "github.com/openmeterio/openmeter/pkg/clock" @@ -48,6 +49,9 @@ func TestEditingEntitlementOfAlignedSub(t *testing.T) { }) require.NoError(t, err) + taxCode := tDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + // 2nd, let's create the plan p, err := tDeps.PlanService.CreatePlan(ctx, plan.CreatePlanInput{ NamespacedModel: models.NamespacedModel{ @@ -80,11 +84,7 @@ func TestEditingEntitlementOfAlignedSub(t *testing.T) { Price: productcatalog.NewPriceFrom(productcatalog.UnitPrice{ Amount: alpacadecimal.NewFromInt(100), }), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000000", - }, - }, + TaxConfig: lo.ToPtr(taxConfig.Clone()), EntitlementTemplate: productcatalog.NewEntitlementTemplateFrom(productcatalog.MeteredEntitlementTemplate{ UsagePeriod: datetime.MustParseDuration(t, "P1M"), IssueAfterReset: lo.ToPtr(0.0), // We will change this in the update @@ -177,11 +177,7 @@ func TestEditingEntitlementOfAlignedSub(t *testing.T) { Price: productcatalog.NewPriceFrom(productcatalog.UnitPrice{ Amount: alpacadecimal.NewFromInt(101), }), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000000", - }, - }, + TaxConfig: lo.ToPtr(taxConfig.Clone()), }, BillingCadence: datetime.MustParseDuration(t, "P1M"), }, diff --git a/test/subscription/scenario_editcancel_test.go b/test/subscription/scenario_editcancel_test.go index ddc9ad0bb5..c6c15870e6 100644 --- a/test/subscription/scenario_editcancel_test.go +++ b/test/subscription/scenario_editcancel_test.go @@ -18,6 +18,7 @@ import ( pcsubscription "github.com/openmeterio/openmeter/openmeter/productcatalog/subscription" "github.com/openmeterio/openmeter/openmeter/subscription" "github.com/openmeterio/openmeter/openmeter/subscription/patch" + subscriptiontestutils "github.com/openmeterio/openmeter/openmeter/subscription/testutils" subscriptionworkflow "github.com/openmeterio/openmeter/openmeter/subscription/workflow" "github.com/openmeterio/openmeter/openmeter/testutils" "github.com/openmeterio/openmeter/pkg/clock" @@ -49,6 +50,9 @@ func TestEditingAndCanceling(t *testing.T) { }) require.NoError(t, err) + taxCode := tDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + // Second, let's create the plan p, err := tDeps.PlanService.CreatePlan(ctx, plan.CreatePlanInput{ NamespacedModel: models.NamespacedModel{ @@ -81,11 +85,7 @@ func TestEditingAndCanceling(t *testing.T) { Price: productcatalog.NewPriceFrom(productcatalog.UnitPrice{ Amount: alpacadecimal.NewFromInt(100), }), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000000", - }, - }, + TaxConfig: lo.ToPtr(taxConfig.Clone()), EntitlementTemplate: productcatalog.NewEntitlementTemplateFrom(productcatalog.BooleanEntitlementTemplate{}), }, BillingCadence: datetime.MustParseDuration(t, "P1M"), @@ -202,11 +202,7 @@ func TestEditingAndCanceling(t *testing.T) { Price: productcatalog.NewPriceFrom(productcatalog.UnitPrice{ Amount: alpacadecimal.NewFromInt(101), }), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000000", - }, - }, + TaxConfig: lo.ToPtr(taxConfig.Clone()), }, BillingCadence: datetime.MustParseDuration(t, "P1M"), }, diff --git a/test/subscription/scenario_entinnextphase_test.go b/test/subscription/scenario_entinnextphase_test.go index 42e576300e..17578b329d 100644 --- a/test/subscription/scenario_entinnextphase_test.go +++ b/test/subscription/scenario_entinnextphase_test.go @@ -14,6 +14,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/productcatalog/plan" pcsubscription "github.com/openmeterio/openmeter/openmeter/productcatalog/subscription" subscription "github.com/openmeterio/openmeter/openmeter/subscription" + subscriptiontestutils "github.com/openmeterio/openmeter/openmeter/subscription/testutils" subscriptionworkflow "github.com/openmeterio/openmeter/openmeter/subscription/workflow" "github.com/openmeterio/openmeter/openmeter/testutils" "github.com/openmeterio/openmeter/pkg/clock" @@ -41,6 +42,9 @@ func TestSubWithMeteredEntitlement(t *testing.T) { feats := tDeps.FeatureConnector.CreateExampleFeatures(t, tDeps.ExampleMeterID) require.Len(t, feats, 3) + taxCode := tDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + // 2nd, let's create the plan p, err := tDeps.PlanService.CreatePlan(ctx, plan.CreatePlanInput{ NamespacedModel: models.NamespacedModel{ @@ -68,14 +72,10 @@ func TestSubWithMeteredEntitlement(t *testing.T) { // Let's have an in-arrears monthly entitlement ratecard &productcatalog.UsageBasedRateCard{ RateCardMeta: productcatalog.RateCardMeta{ - Key: feats[0].Key, - Name: "Test Rate Card", - Feature: productcatalog.NewFeatureReference(lo.ToPtr(feats[0].ID), lo.ToPtr(feats[0].Key)), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000000", - }, - }, + Key: feats[0].Key, + Name: "Test Rate Card", + Feature: productcatalog.NewFeatureReference(lo.ToPtr(feats[0].ID), lo.ToPtr(feats[0].Key)), + TaxConfig: lo.ToPtr(taxConfig.Clone()), EntitlementTemplate: productcatalog.NewEntitlementTemplateFrom(productcatalog.MeteredEntitlementTemplate{ UsagePeriod: datetime.MustParseDuration(t, "P1M"), // compatible with the billing cadence IssueAfterReset: lo.ToPtr(10.0), @@ -100,11 +100,7 @@ func TestSubWithMeteredEntitlement(t *testing.T) { Price: productcatalog.NewPriceFrom(productcatalog.UnitPrice{ Amount: alpacadecimal.NewFromInt(100), }), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000000", - }, - }, + TaxConfig: lo.ToPtr(taxConfig.Clone()), EntitlementTemplate: productcatalog.NewEntitlementTemplateFrom(productcatalog.MeteredEntitlementTemplate{ UsagePeriod: datetime.MustParseDuration(t, "P1M"), // compatible with the billing cadence IssueAfterReset: lo.ToPtr(100.0), diff --git a/test/subscription/scenario_firstofmonth_test.go b/test/subscription/scenario_firstofmonth_test.go index af6adbef17..f488c1dd5f 100644 --- a/test/subscription/scenario_firstofmonth_test.go +++ b/test/subscription/scenario_firstofmonth_test.go @@ -18,6 +18,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/productcatalog/plan" pcsubscription "github.com/openmeterio/openmeter/openmeter/productcatalog/subscription" subscription "github.com/openmeterio/openmeter/openmeter/subscription" + subscriptiontestutils "github.com/openmeterio/openmeter/openmeter/subscription/testutils" subscriptionworkflow "github.com/openmeterio/openmeter/openmeter/subscription/workflow" "github.com/openmeterio/openmeter/openmeter/testutils" "github.com/openmeterio/openmeter/pkg/clock" @@ -46,6 +47,9 @@ func TestBillingOnFirstOfMonth(t *testing.T) { feats := tDeps.FeatureConnector.CreateExampleFeatures(t, tDeps.ExampleMeterID) require.Len(t, feats, 3) + taxCode := tDeps.TaxCodeEnv.CreateTaxCode(t, subscriptiontestutils.ExampleNamespace, subscriptiontestutils.GetExampleTaxCodeInput()) + taxConfig := subscriptiontestutils.ExampleTaxConfig(taxCode) + // 2nd, let's create the plan p, err := tDeps.PlanService.CreatePlan(ctx, plan.CreatePlanInput{ NamespacedModel: models.NamespacedModel{ @@ -79,11 +83,7 @@ func TestBillingOnFirstOfMonth(t *testing.T) { Price: productcatalog.NewPriceFrom(productcatalog.UnitPrice{ Amount: alpacadecimal.NewFromInt(100), }), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000000", - }, - }, + TaxConfig: lo.ToPtr(taxConfig.Clone()), EntitlementTemplate: productcatalog.NewEntitlementTemplateFrom(productcatalog.MeteredEntitlementTemplate{ UsagePeriod: datetime.MustParseDuration(t, "P1M"), // compatible with the billing cadence IssueAfterReset: lo.ToPtr(10.0), @@ -101,11 +101,7 @@ func TestBillingOnFirstOfMonth(t *testing.T) { Amount: alpacadecimal.NewFromInt(10), PaymentTerm: productcatalog.InAdvancePaymentTerm, }), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000000", - }, - }, + TaxConfig: lo.ToPtr(taxConfig.Clone()), }, BillingCadence: lo.ToPtr(datetime.MustParseDuration(t, "P1M")), }, @@ -118,11 +114,7 @@ func TestBillingOnFirstOfMonth(t *testing.T) { Price: productcatalog.NewPriceFrom(productcatalog.UnitPrice{ Amount: alpacadecimal.NewFromInt(1), }), - TaxConfig: &productcatalog.TaxConfig{ - Stripe: &productcatalog.StripeTaxConfig{ - Code: "txcd_10000000", - }, - }, + TaxConfig: lo.ToPtr(taxConfig.Clone()), EntitlementTemplate: productcatalog.NewEntitlementTemplateFrom(productcatalog.MeteredEntitlementTemplate{ UsagePeriod: datetime.MustParseDuration(t, "P1D"), // compatible with the billing cadence IssueAfterReset: lo.ToPtr(10.0),