From 7854f8e66816b7200422156e4e907a7ae313dfc7 Mon Sep 17 00:00:00 2001 From: Peter Turi Date: Tue, 21 Jul 2026 12:35:00 +0200 Subject: [PATCH 1/3] feat: add custom currency support to charges --- api/v3/handlers/customers/charges/convert.go | 106 +-- .../customers/charges/convert_test.go | 4 +- api/v3/handlers/customers/charges/create.go | 27 +- api/v3/handlers/customers/charges/handler.go | 4 +- api/v3/handlers/customers/credits/convert.go | 18 +- .../customers/credits/convert_test.go | 8 +- api/v3/server/server.go | 2 +- app/common/billing.go | 3 + app/common/charges.go | 5 + app/common/openmeter_billingworker.go | 1 + cmd/billing-worker/wire_gen.go | 36 +- cmd/jobs/internal/wire.go | 1 + cmd/jobs/internal/wire_gen.go | 36 +- cmd/server/wire_gen.go | 58 +- .../billing/charges/adapter/search_test.go | 2 +- openmeter/billing/charges/api.go | 91 ++ openmeter/billing/charges/charge.go | 12 +- .../billing/charges/creditpurchase/adapter.go | 23 +- .../charges/creditpurchase/adapter/charge.go | 30 +- .../adapter/funded_credit_activity.go | 30 +- .../adapter/funded_credit_activity_test.go | 2 +- .../charges/creditpurchase/adapter/mapper.go | 32 +- .../billing/charges/creditpurchase/charge.go | 25 +- .../charges/creditpurchase/charge_test.go | 4 + .../charges/creditpurchase/service/create.go | 11 +- .../creditpurchase/service/external_test.go | 43 +- .../charges/creditpurchase/service/get.go | 10 - .../service/promotional_test.go | 22 +- .../charges/creditpurchase/service/void.go | 39 + .../billing/charges/flatfee/adapter/charge.go | 31 +- .../flatfee/adapter/detailedline_test.go | 6 +- .../charges/flatfee/adapter/intentoverride.go | 15 +- .../flatfee/adapter/intentoverride_test.go | 4 +- .../billing/charges/flatfee/adapter/mapper.go | 34 +- .../flatfee/adapter/realizationrun_test.go | 4 +- openmeter/billing/charges/flatfee/charge.go | 27 +- .../billing/charges/flatfee/charge_test.go | 9 +- .../billing/charges/flatfee/service/create.go | 6 +- .../charges/flatfee/service/creditsonly.go | 22 +- .../charges/flatfee/service/lineengine.go | 10 +- .../charges/flatfee/service/manualedit.go | 13 +- .../service/realizations/credittheninvoice.go | 14 +- .../flatfee/service/realizations/preview.go | 8 +- .../billing/charges/lineage/lineage_test.go | 44 + openmeter/billing/charges/lineage/service.go | 14 +- .../charges/lineage/service/service.go | 4 +- openmeter/billing/charges/meta/charge.go | 4 +- openmeter/billing/charges/meta/errors.go | 5 + openmeter/billing/charges/meta/intent.go | 5 +- .../models/chargemeta/{mixin.go => model.go} | 194 ++-- openmeter/billing/charges/service.go | 29 +- openmeter/billing/charges/service/advance.go | 22 +- openmeter/billing/charges/service/api.go | 66 ++ .../billing/charges/service/base_test.go | 3 +- openmeter/billing/charges/service/create.go | 10 +- .../charges/service/creditpurchase_test.go | 5 +- .../charges/service/invoicable_test.go | 3 +- openmeter/billing/charges/service/invoice.go | 8 +- .../billing/charges/service/lineage_test.go | 3 +- .../billing/charges/service/pendinglines.go | 17 +- .../billing/charges/service/recognition.go | 6 +- openmeter/billing/charges/service/service.go | 10 +- .../billing/charges/service/taxcode_test.go | 9 +- .../charges/service/truncation_test.go | 6 +- .../billing/charges/testutils/service.go | 21 + .../charges/usagebased/adapter/charge.go | 18 +- .../usagebased/adapter/detailedline_test.go | 8 +- .../usagebased/adapter/intentoverride.go | 2 +- .../usagebased/adapter/intentoverride_test.go | 4 +- .../charges/usagebased/adapter/mapper.go | 34 +- .../billing/charges/usagebased/charge.go | 6 +- .../charges/usagebased/detailedline.go | 2 +- .../billing/charges/usagebased/rating.go | 4 +- .../charges/usagebased/service/create.go | 6 +- .../service/creditheninvoice_test.go | 24 +- .../usagebased/service/creditsonly_test.go | 17 +- .../charges/usagebased/service/lineengine.go | 10 +- .../charges/usagebased/service/linemapper.go | 10 +- .../usagebased/service/rating/service_test.go | 17 +- .../service/rating/subtract/subtract_test.go | 3 +- .../service/rating/testutils/testutils.go | 4 +- .../usagebased/service/run/payment_test.go | 35 +- .../charges/usagebased/service/triggers.go | 17 +- .../usagebased/service/triggers_test.go | 4 +- .../billing/creditgrant/service/service.go | 24 +- openmeter/billing/rating/line.go | 6 +- .../billing/rating/service/detailedline.go | 6 +- openmeter/billing/stdinvoiceline.go | 11 + .../service/creditsonly_test.go | 4 +- .../service/reconciler/patchcharge_test.go | 11 +- .../service/reconciler/patchchargeflatfee.go | 2 +- .../reconciler/patchchargeusagebased.go | 2 +- .../service/sync_credittheninvoice_test.go | 3 +- .../service/targetstate/targetstate.go | 6 +- .../service/targetstate/targetstateitem.go | 10 +- openmeter/currencies/adapter/currencies.go | 48 +- openmeter/currencies/currency.go | 63 ++ openmeter/currencies/currency_test.go | 43 + .../currencies/testutils/currency/currency.go | 19 + openmeter/ent/db/chargecreditpurchase.go | 54 +- .../chargecreditpurchase.go | 49 +- .../ent/db/chargecreditpurchase/where.go | 197 +++- .../ent/db/chargecreditpurchase_create.go | 83 +- .../ent/db/chargecreditpurchase_query.go | 80 +- .../ent/db/chargecreditpurchase_update.go | 6 + openmeter/ent/db/chargeflatfee.go | 54 +- .../ent/db/chargeflatfee/chargeflatfee.go | 49 +- openmeter/ent/db/chargeflatfee/where.go | 197 +++- openmeter/ent/db/chargeflatfee_create.go | 83 +- openmeter/ent/db/chargeflatfee_query.go | 80 +- openmeter/ent/db/chargeflatfee_update.go | 6 + openmeter/ent/db/chargessearchv1.go | 33 +- .../ent/db/chargessearchv1/chargessearchv1.go | 26 +- openmeter/ent/db/chargessearchv1/where.go | 174 +++- openmeter/ent/db/chargeusagebased.go | 54 +- .../db/chargeusagebased/chargeusagebased.go | 49 +- openmeter/ent/db/chargeusagebased/where.go | 197 +++- openmeter/ent/db/chargeusagebased_create.go | 83 +- openmeter/ent/db/chargeusagebased_query.go | 80 +- openmeter/ent/db/chargeusagebased_update.go | 6 + openmeter/ent/db/client.go | 96 ++ openmeter/ent/db/customcurrency.go | 50 +- .../ent/db/customcurrency/customcurrency.go | 90 ++ openmeter/ent/db/customcurrency/where.go | 69 ++ openmeter/ent/db/customcurrency_create.go | 96 ++ openmeter/ent/db/customcurrency_query.go | 261 ++++- openmeter/ent/db/customcurrency_update.go | 489 ++++++++++ openmeter/ent/db/entmixinaccessor.go | 24 +- openmeter/ent/db/migrate/schema.go | 123 ++- openmeter/ent/db/mutation.go | 891 +++++++++++++++--- openmeter/ent/db/runtime.go | 88 +- openmeter/ent/schema/charges.go | 119 ++- openmeter/ent/schema/chargescreditpurchase.go | 9 +- openmeter/ent/schema/chargesflatfee.go | 9 +- openmeter/ent/schema/chargesusagebased.go | 9 +- openmeter/ent/schema/custom_currencies.go | 6 + .../ledger/chargeadapter/creditpurchase.go | 31 +- .../chargeadapter/creditpurchase_test.go | 10 +- openmeter/ledger/chargeadapter/flatfee.go | 35 +- .../ledger/chargeadapter/flatfee_test.go | 43 +- openmeter/ledger/chargeadapter/usagebased.go | 35 +- .../ledger/chargeadapter/usagebased_test.go | 31 +- .../customerbalance/expired_loader_test.go | 3 +- openmeter/ledger/customerbalance/service.go | 20 +- .../ledger/customerbalance/service_test.go | 5 +- .../ledger/customerbalance/testenv_test.go | 7 +- openmeter/ledger/recognizer/recognize.go | 4 +- openmeter/ledger/recognizer/service.go | 10 +- openmeter/ledger/recognizer/service_test.go | 11 +- openmeter/server/router/router.go | 2 +- openmeter/server/server_test.go | 13 +- pkg/currencyx/code.go | 4 +- pkg/currencyx/code_test.go | 14 +- pkg/framework/entutils/entedge/ornil.go | 14 + test/app/stripe/invoice_credits_test.go | 5 +- test/credits/base.go | 7 +- ...0093016_charges-custom-currencies.down.sql | 6 + ...720093016_charges-custom-currencies.up.sql | 8 + tools/migrate/migrations/atlas.sum | 3 +- tools/migrate/views.sql | 2 +- 160 files changed, 5007 insertions(+), 1244 deletions(-) create mode 100644 openmeter/billing/charges/api.go create mode 100644 openmeter/billing/charges/creditpurchase/service/void.go rename openmeter/billing/charges/models/chargemeta/{mixin.go => model.go} (74%) create mode 100644 openmeter/billing/charges/service/api.go create mode 100644 openmeter/currencies/currency_test.go create mode 100644 openmeter/currencies/testutils/currency/currency.go create mode 100644 pkg/framework/entutils/entedge/ornil.go create mode 100644 tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql create mode 100644 tools/migrate/migrations/20260720093016_charges-custom-currencies.up.sql diff --git a/api/v3/handlers/customers/charges/convert.go b/api/v3/handlers/customers/charges/convert.go index 96d7db2121..085186ce70 100644 --- a/api/v3/handlers/customers/charges/convert.go +++ b/api/v3/handlers/customers/charges/convert.go @@ -59,7 +59,7 @@ func convertFlatFeeChargeToAPI(source flatfee.Charge) (api.BillingChargeFlatFee, AmountAfterProration: ConvertDecimalToCurrencyAmount(source.ChargeBase.State.AmountAfterProration), BillingPeriod: ConvertClosedPeriodToAPI(intent.BillingPeriod), CreatedAt: source.ChargeBase.ManagedResource.ManagedModel.CreatedAt, - Currency: ConvertCurrencyCodeToAPI(source.ChargeBase.Intent.GetCurrency()), + Currency: ConvertCurrencyCodeToAPI(source.ChargeBase.Intent.GetCurrency().GetCode()), Customer: ConvertCustomerIDToReference(source.ChargeBase.Intent.GetCustomerID()), DeletedAt: source.ChargeBase.ManagedResource.ManagedModel.DeletedAt, Description: intent.Description, @@ -109,7 +109,7 @@ func convertUsageBasedChargeToAPI(source usagebased.Charge) (api.BillingChargeUs AdvanceAfter: source.State.AdvanceAfter, BillingPeriod: ConvertClosedPeriodToAPI(intent.BillingPeriod), CreatedAt: source.ChargeBase.ManagedResource.ManagedModel.CreatedAt, - Currency: ConvertCurrencyCodeToAPI(source.ChargeBase.Intent.GetCurrency()), + Currency: ConvertCurrencyCodeToAPI(source.ChargeBase.Intent.GetCurrency().GetCode()), Customer: ConvertCustomerIDToReference(source.ChargeBase.Intent.GetCustomerID()), DeletedAt: source.ChargeBase.ManagedResource.ManagedModel.DeletedAt, Description: intent.Description, @@ -504,8 +504,8 @@ func convertAPIChargeStatus(s string) (meta.ChargeStatus, error) { } } -func convertFlatFeeChargeAPIToIntent(customerID string, flatFee api.CreateChargeFlatFeeRequest) (billingcharges.ChargeIntent, error) { - var zero billingcharges.ChargeIntent +func fromAPICreateChargeFlatFeeRequest(namespace, customerID string, flatFee api.CreateChargeFlatFeeRequest) (billingcharges.CreateCustomerChargeInput, error) { + var zero billingcharges.CreateCustomerChargeInput taxConfig, err := billingprofiles.FromAPIBillingTaxConfig(flatFee.TaxConfig) if err != nil { @@ -549,37 +549,36 @@ func convertFlatFeeChargeAPIToIntent(customerID string, flatFee api.CreateCharge } } - return billingcharges.NewChargeIntent(flatfee.Intent{ - Intent: meta.Intent{ - ManagedBy: billing.ManuallyManagedLine, - CustomerID: customerID, - Currency: currencyx.Code(flatFee.Currency), - TaxConfig: productcatalog.TaxCodeConfigFrom(taxConfig), - UniqueReferenceID: flatFee.UniqueReferenceId, - Subscription: nil, - }, - IntentMutableFields: flatfee.IntentMutableFields{ - IntentMutableFields: meta.IntentMutableFields{ - Name: flatFee.Name, - Description: flatFee.Description, - Metadata: metadata, - ServicePeriod: timeutil.ClosedPeriod(flatFee.ServicePeriod), - FullServicePeriod: timeutil.ClosedPeriod(lo.FromPtrOr(flatFee.FullServicePeriod, flatFee.ServicePeriod)), - BillingPeriod: timeutil.ClosedPeriod(lo.FromPtrOr(flatFee.BillingPeriod, flatFee.ServicePeriod)), + return billingcharges.CreateCustomerChargeInput{ + Namespace: namespace, + CustomerID: customerID, + CurrencyCode: currencyx.Code(flatFee.Currency), + TaxConfig: productcatalog.TaxCodeConfigFrom(taxConfig), + UniqueReferenceID: flatFee.UniqueReferenceId, + FlatFee: &billingcharges.CreateCustomerChargeFlatFeeInput{ + IntentMutableFields: flatfee.IntentMutableFields{ + IntentMutableFields: meta.IntentMutableFields{ + Name: flatFee.Name, + Description: flatFee.Description, + Metadata: metadata, + ServicePeriod: timeutil.ClosedPeriod(flatFee.ServicePeriod), + FullServicePeriod: timeutil.ClosedPeriod(lo.FromPtrOr(flatFee.FullServicePeriod, flatFee.ServicePeriod)), + BillingPeriod: timeutil.ClosedPeriod(lo.FromPtrOr(flatFee.BillingPeriod, flatFee.ServicePeriod)), + }, + InvoiceAt: flatFee.InvoiceAt, + PaymentTerm: productcatalog.PaymentTermType(flatFee.PaymentTerm), + PercentageDiscounts: discount, + ProRating: proRating, + AmountBeforeProration: amountBeforeProration, }, - InvoiceAt: flatFee.InvoiceAt, - PaymentTerm: productcatalog.PaymentTermType(flatFee.PaymentTerm), - PercentageDiscounts: discount, - ProRating: proRating, - AmountBeforeProration: amountBeforeProration, + FeatureKey: flatFee.FeatureKey, + SettlementMode: productcatalog.SettlementMode(flatFee.SettlementMode), }, - FeatureKey: flatFee.FeatureKey, - SettlementMode: productcatalog.SettlementMode(flatFee.SettlementMode), - }), nil + }, nil } -func convertUsageBaseChargeAPIToIntent(customerID string, usageBasedFee api.CreateChargeUsageBasedRequest) (billingcharges.ChargeIntent, error) { - var zero billingcharges.ChargeIntent +func fromAPICreateChargeUsageBasedRequest(namespace, customerID string, usageBasedFee api.CreateChargeUsageBasedRequest) (billingcharges.CreateCustomerChargeInput, error) { + var zero billingcharges.CreateCustomerChargeInput taxConfig, err := billingprofiles.FromAPIBillingTaxConfig(usageBasedFee.TaxConfig) if err != nil { @@ -618,29 +617,28 @@ func convertUsageBaseChargeAPIToIntent(customerID string, usageBasedFee api.Crea if err != nil { return zero, fmt.Errorf("invalid price: %w", err) } - return billingcharges.NewChargeIntent(usagebased.Intent{ - Intent: meta.Intent{ - ManagedBy: billing.ManuallyManagedLine, - CustomerID: customerID, - Currency: currencyx.Code(usageBasedFee.Currency), - TaxConfig: productcatalog.TaxCodeConfigFrom(taxConfig), - UniqueReferenceID: usageBasedFee.UniqueReferenceId, - Subscription: nil, - }, - IntentMutableFields: usagebased.IntentMutableFields{ - IntentMutableFields: meta.IntentMutableFields{ - Name: usageBasedFee.Name, - Description: usageBasedFee.Description, - Metadata: metadata, - ServicePeriod: timeutil.ClosedPeriod(usageBasedFee.ServicePeriod), - FullServicePeriod: timeutil.ClosedPeriod(lo.FromPtrOr(usageBasedFee.FullServicePeriod, usageBasedFee.ServicePeriod)), - BillingPeriod: timeutil.ClosedPeriod(lo.FromPtrOr(usageBasedFee.BillingPeriod, usageBasedFee.ServicePeriod)), + return billingcharges.CreateCustomerChargeInput{ + Namespace: namespace, + CustomerID: customerID, + CurrencyCode: currencyx.Code(usageBasedFee.Currency), + TaxConfig: productcatalog.TaxCodeConfigFrom(taxConfig), + UniqueReferenceID: usageBasedFee.UniqueReferenceId, + UsageBased: &billingcharges.CreateCustomerChargeUsageBasedInput{ + IntentMutableFields: usagebased.IntentMutableFields{ + IntentMutableFields: meta.IntentMutableFields{ + Name: usageBasedFee.Name, + Description: usageBasedFee.Description, + Metadata: metadata, + ServicePeriod: timeutil.ClosedPeriod(usageBasedFee.ServicePeriod), + FullServicePeriod: timeutil.ClosedPeriod(lo.FromPtrOr(usageBasedFee.FullServicePeriod, usageBasedFee.ServicePeriod)), + BillingPeriod: timeutil.ClosedPeriod(lo.FromPtrOr(usageBasedFee.BillingPeriod, usageBasedFee.ServicePeriod)), + }, + InvoiceAt: usageBasedFee.InvoiceAt, + Price: *price, + Discounts: discounts, }, - InvoiceAt: usageBasedFee.InvoiceAt, - Price: *price, - Discounts: discounts, + FeatureKey: usageBasedFee.FeatureKey, + SettlementMode: productcatalog.SettlementMode(usageBasedFee.SettlementMode), }, - FeatureKey: usageBasedFee.FeatureKey, - SettlementMode: productcatalog.SettlementMode(usageBasedFee.SettlementMode), - }), nil + }, nil } diff --git a/api/v3/handlers/customers/charges/convert_test.go b/api/v3/handlers/customers/charges/convert_test.go index 6b507a0359..8cdb93e9b3 100644 --- a/api/v3/handlers/customers/charges/convert_test.go +++ b/api/v3/handlers/customers/charges/convert_test.go @@ -12,8 +12,8 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/productcatalog" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -80,7 +80,7 @@ func TestToAPIBillingChargeUsageBasedSystemIntentReturnsUnsupportedBasePriceErro Intent: meta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: "customer-id", - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), }, IntentMutableFields: usagebased.IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ diff --git a/api/v3/handlers/customers/charges/create.go b/api/v3/handlers/customers/charges/create.go index 08cceba55e..171320162b 100644 --- a/api/v3/handlers/customers/charges/create.go +++ b/api/v3/handlers/customers/charges/create.go @@ -2,7 +2,6 @@ package charges import ( "context" - "errors" "fmt" "net/http" @@ -15,7 +14,7 @@ import ( ) type ( - CreateCustomerChargesRequest = billingcharges.CreateInput + CreateCustomerChargesRequest = billingcharges.CreateCustomerChargeInput CreateCustomerChargesResponse = api.BillingCharge CreateCustomerChargesParams struct { CustomerID api.ULID @@ -31,10 +30,6 @@ func (h *handler) CreateCustomerCharge() CreateCustomerChargesHandler { return CreateCustomerChargesRequest{}, err } - input := billingcharges.CreateInput{ - Namespace: ns, - } - body := api.CreateCustomerChargesJSONRequestBody{} if err := request.ParseBody(r, &body); err != nil { return CreateCustomerChargesRequest{}, err @@ -51,6 +46,7 @@ func (h *handler) CreateCustomerCharge() CreateCustomerChargesHandler { }) } + var input billingcharges.CreateCustomerChargeInput switch discriminator { case string(api.BillingChargeFlatFeeTypeFlatFee): flatFee, err := body.AsCreateChargeFlatFeeRequest() @@ -64,7 +60,7 @@ func (h *handler) CreateCustomerCharge() CreateCustomerChargesHandler { }) } - intent, err := convertFlatFeeChargeAPIToIntent(param.CustomerID, flatFee) + input, err = fromAPICreateChargeFlatFeeRequest(ns, param.CustomerID, flatFee) if err != nil { return CreateCustomerChargesRequest{}, apierrors.NewBadRequestError(ctx, err, apierrors.InvalidParameters{ { @@ -74,8 +70,6 @@ func (h *handler) CreateCustomerCharge() CreateCustomerChargesHandler { }, }) } - - input.Intents = append(input.Intents, intent) case string(api.BillingChargeUsageBasedTypeUsageBased): usageBasedFee, err := body.AsCreateChargeUsageBasedRequest() if err != nil { @@ -88,7 +82,7 @@ func (h *handler) CreateCustomerCharge() CreateCustomerChargesHandler { }) } - intent, err := convertUsageBaseChargeAPIToIntent(param.CustomerID, usageBasedFee) + input, err = fromAPICreateChargeUsageBasedRequest(ns, param.CustomerID, usageBasedFee) if err != nil { return CreateCustomerChargesRequest{}, apierrors.NewBadRequestError(ctx, err, apierrors.InvalidParameters{ { @@ -98,8 +92,6 @@ func (h *handler) CreateCustomerCharge() CreateCustomerChargesHandler { }, }) } - - input.Intents = append(input.Intents, intent) default: err := fmt.Errorf("invalid charge type: %s", discriminator) return CreateCustomerChargesRequest{}, apierrors.NewBadRequestError(ctx, err, apierrors.InvalidParameters{ @@ -124,17 +116,12 @@ func (h *handler) CreateCustomerCharge() CreateCustomerChargesHandler { return input, nil }, func(ctx context.Context, request CreateCustomerChargesRequest) (CreateCustomerChargesResponse, error) { - res, err := h.service.Create(ctx, request) + charge, err := h.service.CreateCustomerCharge(ctx, request) if err != nil { return CreateCustomerChargesResponse{}, err } - if len(res) < 1 { - return CreateCustomerChargesResponse{}, errors.New("no charge created") - } - if len(res) > 1 { - return CreateCustomerChargesResponse{}, errors.New("too many results") - } - return convertChargeToAPI(res[0]) + + return convertChargeToAPI(charge) }, commonhttp.JSONResponseEncoderWithStatus[CreateCustomerChargesResponse](http.StatusCreated), httptransport.AppendOptions( diff --git a/api/v3/handlers/customers/charges/handler.go b/api/v3/handlers/customers/charges/handler.go index 1218059bce..15ea1ef124 100644 --- a/api/v3/handlers/customers/charges/handler.go +++ b/api/v3/handlers/customers/charges/handler.go @@ -14,13 +14,13 @@ type Handler interface { type handler struct { resolveNamespace func(ctx context.Context) (string, error) - service billingcharges.ChargeService + service billingcharges.Service options []httptransport.HandlerOption } func New( resolveNamespace func(ctx context.Context) (string, error), - service billingcharges.ChargeService, + service billingcharges.Service, options ...httptransport.HandlerOption, ) Handler { return &handler{ diff --git a/api/v3/handlers/customers/credits/convert.go b/api/v3/handlers/customers/credits/convert.go index 9cfa1c9bf0..9d2c91f448 100644 --- a/api/v3/handlers/customers/credits/convert.go +++ b/api/v3/handlers/customers/credits/convert.go @@ -29,7 +29,7 @@ func toAPIBillingCreditGrant(charge creditpurchase.Charge) (api.BillingCreditGra Name: charge.Intent.Name, Description: charge.Intent.Description, Amount: charge.Intent.CreditAmount.String(), - Currency: api.BillingCurrencyCode(charge.Intent.Currency), + Currency: api.BillingCurrencyCode(charge.Intent.Currency.GetCode()), EffectiveAt: charge.Intent.EffectiveAt, ExpiresAt: charge.Intent.ExpiresAt, FundingMethod: toAPIBillingCreditFundingMethod(charge.Intent.Settlement), @@ -103,13 +103,7 @@ func toAPICreditGrantPurchase(charge creditpurchase.Charge) (*api.BillingCreditG } costBasis := inv.CostBasis.String() - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(charge.Intent.Currency). - Build() - if err != nil { - return nil, fmt.Errorf("getting currency calculator: %w", err) - } - purchaseAmount := currency.RoundToPrecision(charge.Intent.CreditAmount.Mul(inv.CostBasis)) + purchaseAmount := charge.Intent.Currency.RoundToPrecision(charge.Intent.CreditAmount.Mul(inv.CostBasis)) settlementStatus := api.BillingCreditPurchasePaymentSettlementStatusPending if charge.Realizations.InvoiceSettlement != nil { @@ -130,13 +124,7 @@ func toAPICreditGrantPurchase(charge creditpurchase.Charge) (*api.BillingCreditG } costBasis := ext.CostBasis.String() - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(charge.Intent.Currency). - Build() - if err != nil { - return nil, fmt.Errorf("getting currency calculator: %w", err) - } - purchaseAmount := currency.RoundToPrecision(charge.Intent.CreditAmount.Mul(ext.CostBasis)) + purchaseAmount := charge.Intent.Currency.RoundToPrecision(charge.Intent.CreditAmount.Mul(ext.CostBasis)) availPolicy, err := toAPIBillingCreditAvailabilityPolicy(ext.InitialStatus) if err != nil { return nil, fmt.Errorf("converting availability policy: %w", err) diff --git a/api/v3/handlers/customers/credits/convert_test.go b/api/v3/handlers/customers/credits/convert_test.go index 9902a98b65..d69b44b021 100644 --- a/api/v3/handlers/customers/credits/convert_test.go +++ b/api/v3/handlers/customers/credits/convert_test.go @@ -11,8 +11,8 @@ import ( api "github.com/openmeterio/openmeter/api/v3" "github.com/openmeterio/openmeter/openmeter/billing/charges/creditpurchase" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/pkg/clock" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" ) @@ -34,7 +34,7 @@ func TestToAPIBillingCreditGrantPromotional(t *testing.T) { Intent: creditpurchase.Intent{ Intent: meta.Intent{ CustomerID: "cust-1", - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), }, IntentMutableFields: creditpurchase.IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ @@ -79,7 +79,7 @@ func TestToAPIBillingCreditGrantStatusUsesExpiry(t *testing.T) { Intent: creditpurchase.Intent{ Intent: meta.Intent{ CustomerID: "cust-1", - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), }, IntentMutableFields: creditpurchase.IntentMutableFields{ CreditAmount: alpacadecimal.RequireFromString("25"), @@ -151,7 +151,7 @@ func TestToAPIBillingCreditGrantKey(t *testing.T) { Intent: creditpurchase.Intent{ Intent: meta.Intent{ CustomerID: "cust-1", - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), }, IntentMutableFields: creditpurchase.IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ diff --git a/api/v3/server/server.go b/api/v3/server/server.go index 89da116fc2..30cb79f1f0 100644 --- a/api/v3/server/server.go +++ b/api/v3/server/server.go @@ -108,7 +108,7 @@ type Config struct { SubscriptionWorkflowService subscriptionworkflow.Service TaxCodeService taxcode.Service CurrencyService currencies.Service - ChargeService billingcharges.ChargeService + ChargeService billingcharges.Service CostService cost.Service FeatureConnector feature.FeatureConnector diff --git a/app/common/billing.go b/app/common/billing.go index 27d457a38f..3ac66da9c6 100644 --- a/app/common/billing.go +++ b/app/common/billing.go @@ -30,6 +30,7 @@ import ( subscriptionsyncadapter "github.com/openmeterio/openmeter/openmeter/billing/worker/subscriptionsync/adapter" "github.com/openmeterio/openmeter/openmeter/billing/worker/subscriptionsync/reconciler" subscriptionsyncservice "github.com/openmeterio/openmeter/openmeter/billing/worker/subscriptionsync/service" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/customer" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" "github.com/openmeterio/openmeter/openmeter/ledger" @@ -171,6 +172,7 @@ func NewBillingRegistry( creditsConfig config.CreditsConfiguration, tracer trace.Tracer, taxCodeService taxcode.Service, + currencyResolver currencies.CurrencyResolver, locker *lockr.Locker, ledgerService ledger.Ledger, balanceQuerier ledger.BalanceQuerier, @@ -223,6 +225,7 @@ func NewBillingRegistry( accountService, breakageService, taxCodeService, + currencyResolver, fsConfig.NamespaceLockdown, creditsConfig, featureGate, diff --git a/app/common/charges.go b/app/common/charges.go index 93b2c1912e..05b2bfbdf5 100644 --- a/app/common/charges.go +++ b/app/common/charges.go @@ -27,6 +27,7 @@ import ( usagebasedadapter "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased/adapter" usagebasedservice "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased/service" "github.com/openmeterio/openmeter/openmeter/billing/rating" + "github.com/openmeterio/openmeter/openmeter/currencies" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" enttx "github.com/openmeterio/openmeter/openmeter/ent/tx" "github.com/openmeterio/openmeter/openmeter/ledger" @@ -352,6 +353,7 @@ func NewChargesService( billingService billing.Service, recognizerService recognizer.Service, taxCodeService taxcode.Service, + currencyResolver currencies.CurrencyResolver, fsNamespaceLockdown []string, ) (charges.Service, error) { chargesSvc, err := chargesservice.New(chargesservice.Config{ @@ -365,6 +367,7 @@ func NewChargesService( BillingService: billingService, RecognizerService: recognizerService, TaxCodeService: taxCodeService, + CurrencyResolver: currencyResolver, FSNamespaceLockdown: fsNamespaceLockdown, }) if err != nil { @@ -410,6 +413,7 @@ func newChargesRegistry( accountService ledgeraccount.Service, breakageService ledgerbreakage.Service, taxCodeService taxcode.Service, + currencyResolver currencies.CurrencyResolver, fsNamespaceLockdown []string, creditsConfig config.CreditsConfiguration, featureGate *featuregate.FeatureGateChecker, @@ -557,6 +561,7 @@ func newChargesRegistry( billingService, recognizerService, taxCodeService, + currencyResolver, fsNamespaceLockdown, ) if err != nil { diff --git a/app/common/openmeter_billingworker.go b/app/common/openmeter_billingworker.go index 18c4e71db4..0d759478c7 100644 --- a/app/common/openmeter_billingworker.go +++ b/app/common/openmeter_billingworker.go @@ -31,6 +31,7 @@ var BillingWorker = wire.NewSet( App, Customer, Secret, + Currency, BillingWorkerProvisionTopics, BillingWorkerSubscriber, diff --git a/cmd/billing-worker/wire_gen.go b/cmd/billing-worker/wire_gen.go index 091a7d8752..6da9a87b2c 100644 --- a/cmd/billing-worker/wire_gen.go +++ b/cmd/billing-worker/wire_gen.go @@ -10,6 +10,7 @@ import ( "context" "github.com/openmeterio/openmeter/app/common" "github.com/openmeterio/openmeter/app/config" + "github.com/openmeterio/openmeter/openmeter/currencies/currencyresolver" "github.com/openmeterio/openmeter/openmeter/ledger" "github.com/openmeterio/openmeter/openmeter/meter" "github.com/openmeterio/openmeter/openmeter/namespace" @@ -327,6 +328,39 @@ func initializeApplication(ctx context.Context, conf config.Configuration) (Appl } billingFeatureSwitchesConfiguration := billingConfiguration.FeatureSwitches creditsConfiguration := conf.Credits + currenciesRepository, err := common.NewCurrencyAdapter(client) + if err != nil { + cleanup7() + cleanup6() + cleanup5() + cleanup4() + cleanup3() + cleanup2() + cleanup() + return Application{}, nil, err + } + currenciesService, err := common.NewCurrencyService(currenciesRepository) + if err != nil { + cleanup7() + cleanup6() + cleanup5() + cleanup4() + cleanup3() + cleanup2() + cleanup() + return Application{}, nil, err + } + currencyResolver, err := currencyresolver.New(currenciesService) + if err != nil { + cleanup7() + cleanup6() + cleanup5() + cleanup4() + cleanup3() + cleanup2() + cleanup() + return Application{}, nil, err + } repo := common.NewLedgerHistoricalRepo(client) accountRepo := common.NewLedgerAccountRepo(client) accountService := common.NewLedgerAccountService(creditsConfiguration, accountRepo, locker) @@ -353,7 +387,7 @@ func initializeApplication(ctx context.Context, conf config.Configuration) (Appl gate := featuregate.NewNoop() featureGateConfiguration := conf.FeatureGate featureGateChecker := common.NewFeatureGateChecker(gate, featureGateConfiguration, creditsConfiguration) - billingRegistry, err := common.NewBillingRegistry(logger, service, adapter, ratingService, customerService, featureConnector, meterService, meter, connector, eventbusPublisher, billingConfiguration, subscriptionServiceWithWorkflow, client, billingFeatureSwitchesConfiguration, creditsConfiguration, tracer, taxcodeService, locker, ledger, balanceQuerier, accountResolver, accountService, breakageService, featureGateChecker) + billingRegistry, err := common.NewBillingRegistry(logger, service, adapter, ratingService, customerService, featureConnector, meterService, meter, connector, eventbusPublisher, billingConfiguration, subscriptionServiceWithWorkflow, client, billingFeatureSwitchesConfiguration, creditsConfiguration, tracer, taxcodeService, currencyResolver, locker, ledger, balanceQuerier, accountResolver, accountService, breakageService, featureGateChecker) if err != nil { cleanup7() cleanup6() diff --git a/cmd/jobs/internal/wire.go b/cmd/jobs/internal/wire.go index 0ed8d4699d..097bef600c 100644 --- a/cmd/jobs/internal/wire.go +++ b/cmd/jobs/internal/wire.go @@ -75,6 +75,7 @@ func initializeApplication(ctx context.Context, conf config.Configuration) (Appl common.ClickHouse, common.Config, common.Customer, + common.Currency, common.Subject, common.Database, common.Entitlement, diff --git a/cmd/jobs/internal/wire_gen.go b/cmd/jobs/internal/wire_gen.go index 3f95f5b156..51ab672d2b 100644 --- a/cmd/jobs/internal/wire_gen.go +++ b/cmd/jobs/internal/wire_gen.go @@ -17,6 +17,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/worker/advance" "github.com/openmeterio/openmeter/openmeter/billing/worker/collect" "github.com/openmeterio/openmeter/openmeter/billing/worker/subscriptionsync/reconciler" + "github.com/openmeterio/openmeter/openmeter/currencies/currencyresolver" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/ent/db" "github.com/openmeterio/openmeter/openmeter/llmcost/sync" @@ -336,6 +337,39 @@ func initializeApplication(ctx context.Context, conf config.Configuration) (Appl } billingFeatureSwitchesConfiguration := billingConfiguration.FeatureSwitches creditsConfiguration := conf.Credits + currenciesRepository, err := common.NewCurrencyAdapter(client) + if err != nil { + cleanup7() + cleanup6() + cleanup5() + cleanup4() + cleanup3() + cleanup2() + cleanup() + return Application{}, nil, err + } + currenciesService, err := common.NewCurrencyService(currenciesRepository) + if err != nil { + cleanup7() + cleanup6() + cleanup5() + cleanup4() + cleanup3() + cleanup2() + cleanup() + return Application{}, nil, err + } + currencyResolver, err := currencyresolver.New(currenciesService) + if err != nil { + cleanup7() + cleanup6() + cleanup5() + cleanup4() + cleanup3() + cleanup2() + cleanup() + return Application{}, nil, err + } repo := common.NewLedgerHistoricalRepo(client) accountRepo := common.NewLedgerAccountRepo(client) accountService := common.NewLedgerAccountService(creditsConfiguration, accountRepo, locker) @@ -362,7 +396,7 @@ func initializeApplication(ctx context.Context, conf config.Configuration) (Appl gate := featuregate.NewNoop() featureGateConfiguration := conf.FeatureGate featureGateChecker := common.NewFeatureGateChecker(gate, featureGateConfiguration, creditsConfiguration) - billingRegistry, err := common.NewBillingRegistry(logger, service, adapter, ratingService, customerService, featureConnector, meterService, meter, connector, eventbusPublisher, billingConfiguration, subscriptionServiceWithWorkflow, client, billingFeatureSwitchesConfiguration, creditsConfiguration, tracer, taxcodeService, locker, ledger, balanceQuerier, accountResolver, accountService, breakageService, featureGateChecker) + billingRegistry, err := common.NewBillingRegistry(logger, service, adapter, ratingService, customerService, featureConnector, meterService, meter, connector, eventbusPublisher, billingConfiguration, subscriptionServiceWithWorkflow, client, billingFeatureSwitchesConfiguration, creditsConfiguration, tracer, taxcodeService, currencyResolver, locker, ledger, balanceQuerier, accountResolver, accountService, breakageService, featureGateChecker) if err != nil { cleanup7() cleanup6() diff --git a/cmd/server/wire_gen.go b/cmd/server/wire_gen.go index 443ab8633c..41c08a2671 100644 --- a/cmd/server/wire_gen.go +++ b/cmd/server/wire_gen.go @@ -14,6 +14,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/creditgrant" "github.com/openmeterio/openmeter/openmeter/cost" "github.com/openmeterio/openmeter/openmeter/currencies" + "github.com/openmeterio/openmeter/openmeter/currencies/currencyresolver" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/ent/db" "github.com/openmeterio/openmeter/openmeter/governance" @@ -331,6 +332,39 @@ func initializeApplication(ctx context.Context, conf config.Configuration) (Appl } billingFeatureSwitchesConfiguration := billingConfiguration.FeatureSwitches creditsConfiguration := conf.Credits + currenciesRepository, err := common.NewCurrencyAdapter(client) + if err != nil { + cleanup7() + cleanup6() + cleanup5() + cleanup4() + cleanup3() + cleanup2() + cleanup() + return Application{}, nil, err + } + currenciesService, err := common.NewCurrencyService(currenciesRepository) + if err != nil { + cleanup7() + cleanup6() + cleanup5() + cleanup4() + cleanup3() + cleanup2() + cleanup() + return Application{}, nil, err + } + currencyResolver, err := currencyresolver.New(currenciesService) + if err != nil { + cleanup7() + cleanup6() + cleanup5() + cleanup4() + cleanup3() + cleanup2() + cleanup() + return Application{}, nil, err + } repo := common.NewLedgerHistoricalRepo(client) accountRepo := common.NewLedgerAccountRepo(client) accountService := common.NewLedgerAccountService(creditsConfiguration, accountRepo, locker) @@ -357,7 +391,7 @@ func initializeApplication(ctx context.Context, conf config.Configuration) (Appl gate := featuregate.NewNoop() featureGateConfiguration := conf.FeatureGate featureGateChecker := common.NewFeatureGateChecker(gate, featureGateConfiguration, creditsConfiguration) - billingRegistry, err := common.NewBillingRegistry(logger, appService, billingAdapter, ratingService, customerService, featureConnector, service, meter, connector, eventbusPublisher, billingConfiguration, subscriptionServiceWithWorkflow, client, billingFeatureSwitchesConfiguration, creditsConfiguration, tracer, taxcodeService, locker, ledger, balanceQuerier, accountResolver, accountService, breakageService, featureGateChecker) + billingRegistry, err := common.NewBillingRegistry(logger, appService, billingAdapter, ratingService, customerService, featureConnector, service, meter, connector, eventbusPublisher, billingConfiguration, subscriptionServiceWithWorkflow, client, billingFeatureSwitchesConfiguration, creditsConfiguration, tracer, taxcodeService, currencyResolver, locker, ledger, balanceQuerier, accountResolver, accountService, breakageService, featureGateChecker) if err != nil { cleanup7() cleanup6() @@ -481,28 +515,6 @@ func initializeApplication(ctx context.Context, conf config.Configuration) (Appl cleanup() return Application{}, nil, err } - currenciesRepository, err := common.NewCurrencyAdapter(client) - if err != nil { - cleanup7() - cleanup6() - cleanup5() - cleanup4() - cleanup3() - cleanup2() - cleanup() - return Application{}, nil, err - } - currenciesService, err := common.NewCurrencyService(currenciesRepository) - if err != nil { - cleanup7() - cleanup6() - cleanup5() - cleanup4() - cleanup3() - cleanup2() - cleanup() - return Application{}, nil, err - } llmcostService, err := common.NewLLMCostService(logger, client) if err != nil { cleanup7() diff --git a/openmeter/billing/charges/adapter/search_test.go b/openmeter/billing/charges/adapter/search_test.go index 3251f8fbf7..5baa34902b 100644 --- a/openmeter/billing/charges/adapter/search_test.go +++ b/openmeter/billing/charges/adapter/search_test.go @@ -90,7 +90,7 @@ func (s *ListCustomersToAdvanceSuite) insertFlatFeeCharge(namespace, customerID SetCustomerID(customerID). SetStatus(status). SetStatusDetailed(flatfee.Status(status)). - SetCurrency(currencyx.Code("USD")). + SetFiatCurrencyCode(currencyx.Code("USD")). SetManagedBy(billing.SubscriptionManagedLine). SetName("test-charge"). SetPaymentTerm(productcatalog.InArrearsPaymentTerm). diff --git a/openmeter/billing/charges/api.go b/openmeter/billing/charges/api.go new file mode 100644 index 0000000000..5ff697bb5b --- /dev/null +++ b/openmeter/billing/charges/api.go @@ -0,0 +1,91 @@ +package charges + +import ( + "context" + "errors" + "fmt" + + "github.com/openmeterio/openmeter/openmeter/billing/charges/creditpurchase" + "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" + "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" + "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + "github.com/openmeterio/openmeter/openmeter/productcatalog" + "github.com/openmeterio/openmeter/pkg/currencyx" + "github.com/openmeterio/openmeter/pkg/models" +) + +// CustomerChargeAPIService resolves API-facing references before constructing +// complete charge intents for the core charge creation flow. +type CustomerChargeAPIService interface { + CreateCustomerCharge(ctx context.Context, input CreateCustomerChargeInput) (Charge, error) +} + +type CreditPurchaseFacadeService interface { + HandleCreditPurchaseExternalPaymentStateTransition(ctx context.Context, input HandleCreditPurchaseExternalPaymentStateTransitionInput) (creditpurchase.Charge, error) +} + +type CreateCustomerChargeInput struct { + Namespace string + CustomerID string + CurrencyCode currencyx.Code + TaxConfig productcatalog.TaxCodeConfig + UniqueReferenceID *string + + FlatFee *CreateCustomerChargeFlatFeeInput + UsageBased *CreateCustomerChargeUsageBasedInput +} + +type CreateCustomerChargeFlatFeeInput struct { + IntentMutableFields flatfee.IntentMutableFields + FeatureKey *string + SettlementMode productcatalog.SettlementMode +} + +type CreateCustomerChargeUsageBasedInput struct { + IntentMutableFields usagebased.IntentMutableFields + FeatureKey string + SettlementMode productcatalog.SettlementMode +} + +func (i CreateCustomerChargeInput) Validate() error { + var errs []error + + if i.Namespace == "" { + errs = append(errs, errors.New("namespace is required")) + } + + if i.CustomerID == "" { + errs = append(errs, errors.New("customer ID is required")) + } + + if err := i.CurrencyCode.Validate(); err != nil { + errs = append(errs, fmt.Errorf("currency code: %w", err)) + } + + if (i.FlatFee == nil) == (i.UsageBased == nil) { + errs = append(errs, errors.New("exactly one charge type is required")) + } + + return models.NewNillableGenericValidationError(errors.Join(errs...)) +} + +type HandleCreditPurchaseExternalPaymentStateTransitionInput struct { + ChargeID meta.ChargeID + + TargetPaymentState payment.Status +} + +func (i HandleCreditPurchaseExternalPaymentStateTransitionInput) Validate() error { + var errs []error + + if err := i.ChargeID.Validate(); err != nil { + errs = append(errs, fmt.Errorf("charge ID: %w", err)) + } + + if err := i.TargetPaymentState.Validate(); err != nil { + errs = append(errs, fmt.Errorf("target payment state: %w", err)) + } + + return models.NewNillableGenericValidationError(errors.Join(errs...)) +} diff --git a/openmeter/billing/charges/charge.go b/openmeter/billing/charges/charge.go index 38002aa0a2..c86f785c7b 100644 --- a/openmeter/billing/charges/charge.go +++ b/openmeter/billing/charges/charge.go @@ -10,9 +10,9 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/productcatalog" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/framework/entutils" "github.com/openmeterio/openmeter/pkg/models" ) @@ -187,29 +187,29 @@ func (c Charge) GetCustomerID() (customer.CustomerID, error) { return customer.CustomerID{}, fmt.Errorf("invalid charge type: %s", c.t) } -func (c Charge) GetCurrency() (currencyx.Code, error) { +func (c Charge) GetCurrency() (currencies.Currency, error) { switch c.t { case meta.ChargeTypeFlatFee: if c.flatFee == nil { - return "", fmt.Errorf("flat fee charge is nil") + return currencies.Currency{}, fmt.Errorf("flat fee charge is nil") } return c.flatFee.GetCurrency(), nil case meta.ChargeTypeCreditPurchase: if c.creditPurchase == nil { - return "", fmt.Errorf("credit purchase charge is nil") + return currencies.Currency{}, fmt.Errorf("credit purchase charge is nil") } return c.creditPurchase.GetCurrency(), nil case meta.ChargeTypeUsageBased: if c.usageBased == nil { - return "", fmt.Errorf("usage based charge is nil") + return currencies.Currency{}, fmt.Errorf("usage based charge is nil") } return c.usageBased.GetCurrency(), nil } - return "", fmt.Errorf("invalid charge type: %s", c.t) + return currencies.Currency{}, fmt.Errorf("invalid charge type: %s", c.t) } func (c Charge) SettlementMode() (productcatalog.SettlementMode, error) { diff --git a/openmeter/billing/charges/creditpurchase/adapter.go b/openmeter/billing/charges/creditpurchase/adapter.go index 3927a366af..ce8438fe9c 100644 --- a/openmeter/billing/charges/creditpurchase/adapter.go +++ b/openmeter/billing/charges/creditpurchase/adapter.go @@ -26,9 +26,9 @@ type Adapter interface { } type ChargeAdapter interface { - CreateCharge(ctx context.Context, in CreateChargeInput) (Charge, error) + CreateCharge(ctx context.Context, in CreateInput) (Charge, error) UpdateCharge(ctx context.Context, charge ChargeBase) (ChargeBase, error) - MarkVoided(ctx context.Context, input MarkVoidedInput) (ChargeBase, error) + MarkVoided(ctx context.Context, input MarkVoidedAdapterInput) (ChargeBase, error) GetByIDs(ctx context.Context, ids GetByIDsInput) ([]Charge, error) GetByID(ctx context.Context, id GetByIDInput) (Charge, error) ListCharges(ctx context.Context, input ListChargesInput) (pagination.Result[Charge], error) @@ -105,7 +105,6 @@ func (i MarkVoidedInput) Validate() error { if err := i.ChargeID.Validate(); err != nil { errs = append(errs, fmt.Errorf("charge ID: %w", err)) } - if i.VoidedAt.IsZero() { errs = append(errs, errors.New("voided at is required")) } @@ -113,19 +112,19 @@ func (i MarkVoidedInput) Validate() error { return models.NewNillableGenericValidationError(errors.Join(errs...)) } -type CreateChargeInput struct { - Namespace string - Intent Intent +type MarkVoidedAdapterInput struct { + Charge Charge + VoidedAt time.Time } -func (i CreateChargeInput) Validate() error { +func (i MarkVoidedAdapterInput) Validate() error { var errs []error - if i.Namespace == "" { - errs = append(errs, errors.New("namespace is required")) - } - if err := i.Intent.Validate(); err != nil { - errs = append(errs, fmt.Errorf("intent: %w", err)) + if err := i.Charge.Validate(); err != nil { + errs = append(errs, fmt.Errorf("charge: %w", err)) + } + if i.VoidedAt.IsZero() { + errs = append(errs, errors.New("voided at is required")) } return models.NewNillableGenericValidationError(errors.Join(errs...)) diff --git a/openmeter/billing/charges/creditpurchase/adapter/charge.go b/openmeter/billing/charges/creditpurchase/adapter/charge.go index 8e33e4ba09..8188e7cc7e 100644 --- a/openmeter/billing/charges/creditpurchase/adapter/charge.go +++ b/openmeter/billing/charges/creditpurchase/adapter/charge.go @@ -12,6 +12,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/chargemeta" "github.com/openmeterio/openmeter/openmeter/ent/db" dbchargecreditpurchase "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchase" + dbcustomcurrency "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/pkg/filter" "github.com/openmeterio/openmeter/pkg/framework/entutils" "github.com/openmeterio/openmeter/pkg/pagination" @@ -52,11 +53,11 @@ func (a *adapter) UpdateCharge(ctx context.Context, charge creditpurchase.Charge return creditpurchase.ChargeBase{}, err } - return MapChargeBaseFromDB(dbCreditPurchase), nil + return MapChargeBaseFromDB(dbCreditPurchase, charge.Intent.Currency) }) } -func (a *adapter) CreateCharge(ctx context.Context, in creditpurchase.CreateChargeInput) (creditpurchase.Charge, error) { +func (a *adapter) CreateCharge(ctx context.Context, in creditpurchase.CreateInput) (creditpurchase.Charge, error) { if err := in.Validate(); err != nil { return creditpurchase.Charge{}, err } @@ -109,25 +110,25 @@ func (a *adapter) CreateCharge(ctx context.Context, in creditpurchase.CreateChar return creditpurchase.Charge{}, err } - return MapCreditPurchaseChargeFromDB(dbCreditPurchase, meta.ExpandNone) + return FromDBChargeCreditPurchaseWithCurrency(dbCreditPurchase, in.Intent.Currency, meta.ExpandNone) }) } -func (a *adapter) MarkVoided(ctx context.Context, input creditpurchase.MarkVoidedInput) (creditpurchase.ChargeBase, error) { +func (a *adapter) MarkVoided(ctx context.Context, input creditpurchase.MarkVoidedAdapterInput) (creditpurchase.ChargeBase, error) { if err := input.Validate(); err != nil { return creditpurchase.ChargeBase{}, err } return entutils.TransactingRepo(ctx, a, func(ctx context.Context, tx *adapter) (creditpurchase.ChargeBase, error) { - dbCreditPurchase, err := tx.db.ChargeCreditPurchase.UpdateOneID(input.ChargeID.ID). - Where(dbchargecreditpurchase.NamespaceEQ(input.ChargeID.Namespace)). + dbCreditPurchase, err := tx.db.ChargeCreditPurchase.UpdateOneID(input.Charge.ID). + Where(dbchargecreditpurchase.NamespaceEQ(input.Charge.Namespace)). SetVoidedAt(input.VoidedAt). Save(ctx) if err != nil { - return creditpurchase.ChargeBase{}, fmt.Errorf("marking credit purchase charge voided [id=%s]: %w", input.ChargeID.ID, err) + return creditpurchase.ChargeBase{}, fmt.Errorf("marking credit purchase charge voided [id=%s]: %w", input.Charge.ID, err) } - return MapChargeBaseFromDB(dbCreditPurchase), nil + return MapChargeBaseFromDB(dbCreditPurchase, input.Charge.Intent.Currency) }) } @@ -204,7 +205,16 @@ func (a *adapter) ListCharges(ctx context.Context, input creditpurchase.ListChar } if len(input.Currencies) > 0 { - query = query.Where(dbchargecreditpurchase.CurrencyIn(input.Currencies...)) + query = query.Where( + dbchargecreditpurchase.Or( + dbchargecreditpurchase.FiatCurrencyCodeIn(input.Currencies...), + dbchargecreditpurchase.HasCustomCurrencyWith( + dbcustomcurrency.CodeIn(input.Currencies...), + dbcustomcurrency.Namespace(input.Namespace), + dbcustomcurrency.DeletedAtIsNil(), + ), + ), + ) } if input.Voided != nil { @@ -251,6 +261,8 @@ func (a *adapter) ListCharges(ctx context.Context, input creditpurchase.ListChar } func withExpands(query *db.ChargeCreditPurchaseQuery, expands meta.Expands) *db.ChargeCreditPurchaseQuery { + query = query.WithCustomCurrency() + if expands.Has(meta.ExpandRealizations) { query = query.WithCreditGrant().WithExternalPayment().WithInvoicedPayment() } diff --git a/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go b/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go index 9c281cd6ad..30e7a2c915 100644 --- a/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go +++ b/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go @@ -11,10 +11,13 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/creditpurchase" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + currenciesadapter "github.com/openmeterio/openmeter/openmeter/currencies/adapter" "github.com/openmeterio/openmeter/openmeter/ent/db" dbchargecreditpurchase "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchase" dbchargecreditpurchasecreditgrant "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchasecreditgrant" + dbcustomcurrency "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" + "github.com/openmeterio/openmeter/pkg/framework/entutils/entedge" ) func (a *adapter) ListFundedCreditActivities(ctx context.Context, input creditpurchase.ListFundedCreditActivitiesInput) (creditpurchase.ListFundedCreditActivitiesResult, error) { @@ -41,7 +44,8 @@ func ListFundedCreditActivities(ctx context.Context, dbClient *db.Client, input q.Where( dbchargecreditpurchase.Namespace(input.Customer.Namespace), dbchargecreditpurchase.DeletedAtIsNil(), - ) + ). + WithCustomCurrency() }). Limit(input.Limit + 1) @@ -60,7 +64,14 @@ func ListFundedCreditActivities(ctx context.Context, dbClient *db.Client, input } if input.Currency != nil { - query = query.Where(dbchargecreditpurchasecreditgrant.HasCreditPurchaseWith(dbchargecreditpurchase.CurrencyEQ(*input.Currency))) + query = query.Where(dbchargecreditpurchasecreditgrant.HasCreditPurchaseWith( + dbchargecreditpurchase.HasCustomCurrencyWith( + dbcustomcurrency.CodeEQ(*input.Currency), + dbcustomcurrency.Namespace(input.Customer.Namespace), + dbcustomcurrency.DeletedAtIsNil(), + ), + dbchargecreditpurchase.FiatCurrencyCodeEQ(*input.Currency), + )) } if input.AsOf != nil { @@ -92,6 +103,19 @@ func ListFundedCreditActivities(ctx context.Context, dbClient *db.Client, input return creditpurchase.ListFundedCreditActivitiesResult{}, fmt.Errorf("credit purchase not loaded for grant %s: %w", entity.ID, err) } + dbCustomCurrency, err := entedge.OrNilIfNotFound(creditPurchase.Edges.CustomCurrencyOrErr()) + if err != nil { + return creditpurchase.ListFundedCreditActivitiesResult{}, fmt.Errorf("failed to get custom currency: %w", err) + } + + resolvedCurrency, err := currenciesadapter.MapCustomCurrencyOrFiatCurrencyFromDB(currenciesadapter.CustomCurrencyOrFiatCurrency{ + CustomCurrency: dbCustomCurrency, + FiatCurrency: creditPurchase.FiatCurrencyCode, + }) + if err != nil { + return creditpurchase.ListFundedCreditActivitiesResult{}, fmt.Errorf("failed to resolve currency: %w", err) + } + items = append(items, creditpurchase.FundedCreditActivity{ ChargeID: meta.ChargeID{ Namespace: creditPurchase.Namespace, @@ -100,7 +124,7 @@ func ListFundedCreditActivities(ctx context.Context, dbClient *db.Client, input ChargeCreatedAt: creditPurchase.CreatedAt, FundedAt: entity.GrantedAt, TransactionGroupID: entity.TransactionGroupID, - Currency: creditPurchase.Currency, + Currency: resolvedCurrency.GetCode(), Amount: creditPurchase.CreditAmount, Name: creditPurchase.Name, Description: creditPurchase.Description, diff --git a/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity_test.go b/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity_test.go index e9a4486a1f..b1cd2a0b07 100644 --- a/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity_test.go +++ b/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity_test.go @@ -86,7 +86,7 @@ func (s *ListFundedCreditActivitiesSuite) insertCreditPurchaseWithGrant( SetFullServicePeriodTo(servicePeriodTo). SetStatus(meta.ChargeStatusCreated). SetStatusDetailed(creditpurchase.StatusCreated). - SetCurrency(currency). + SetFiatCurrencyCode(currency). SetManagedBy(billing.SubscriptionManagedLine). SetName(name). SetNillableDescription(description). diff --git a/openmeter/billing/charges/creditpurchase/adapter/mapper.go b/openmeter/billing/charges/creditpurchase/adapter/mapper.go index b728db2890..101c99a57e 100644 --- a/openmeter/billing/charges/creditpurchase/adapter/mapper.go +++ b/openmeter/billing/charges/creditpurchase/adapter/mapper.go @@ -11,13 +11,21 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/chargemeta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" + "github.com/openmeterio/openmeter/openmeter/currencies" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" "github.com/openmeterio/openmeter/pkg/convert" ) -func MapChargeBaseFromDB(dbEntity *entdb.ChargeCreditPurchase) creditpurchase.ChargeBase { - mappedMeta := chargemeta.MapFromDB(dbEntity) +func MapChargeBaseFromDB(dbEntity *entdb.ChargeCreditPurchase, currency currencies.Currency) (creditpurchase.ChargeBase, error) { + mappedMeta, err := chargemeta.FromDBCharge(dbEntity, currency) + if err != nil { + return creditpurchase.ChargeBase{}, fmt.Errorf("failed to map charge base: %w", err) + } + + return mapChargeBaseFromDB(dbEntity, mappedMeta), nil +} +func mapChargeBaseFromDB(dbEntity *entdb.ChargeCreditPurchase, mappedMeta meta.Charge) creditpurchase.ChargeBase { return creditpurchase.ChargeBase{ ManagedResource: mappedMeta.ManagedResource, Status: dbEntity.StatusDetailed, @@ -41,7 +49,25 @@ func MapChargeBaseFromDB(dbEntity *entdb.ChargeCreditPurchase) creditpurchase.Ch } func MapCreditPurchaseChargeFromDB(dbEntity *entdb.ChargeCreditPurchase, expands meta.Expands) (creditpurchase.Charge, error) { - chargeBase := MapChargeBaseFromDB(dbEntity) + mappedMeta, err := chargemeta.FromDBChargeWithCurrencyEdge(dbEntity, dbEntity.Edges) + if err != nil { + return creditpurchase.Charge{}, fmt.Errorf("failed to map charge meta: %w", err) + } + + return mapCreditPurchaseChargeFromDB(dbEntity, mappedMeta, expands) +} + +func FromDBChargeCreditPurchaseWithCurrency(dbEntity *entdb.ChargeCreditPurchase, currency currencies.Currency, expands meta.Expands) (creditpurchase.Charge, error) { + mappedMeta, err := chargemeta.FromDBCharge(dbEntity, currency) + if err != nil { + return creditpurchase.Charge{}, fmt.Errorf("failed to map charge meta: %w", err) + } + + return mapCreditPurchaseChargeFromDB(dbEntity, mappedMeta, expands) +} + +func mapCreditPurchaseChargeFromDB(dbEntity *entdb.ChargeCreditPurchase, mappedMeta meta.Charge, expands meta.Expands) (creditpurchase.Charge, error) { + chargeBase := mapChargeBaseFromDB(dbEntity, mappedMeta) var creditGrantRealization *ledgertransaction.TimedGroupReference var externalPaymentSettlement *payment.External diff --git a/openmeter/billing/charges/creditpurchase/charge.go b/openmeter/billing/charges/creditpurchase/charge.go index 3c47be4581..fc3c810def 100644 --- a/openmeter/billing/charges/creditpurchase/charge.go +++ b/openmeter/billing/charges/creditpurchase/charge.go @@ -11,9 +11,9 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/pkg/clock" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -63,7 +63,7 @@ func (c ChargeBase) GetCustomerID() customer.CustomerID { } } -func (c ChargeBase) GetCurrency() currencyx.Code { +func (c ChargeBase) GetCurrency() currencies.Currency { return c.Intent.Currency } @@ -145,7 +145,7 @@ func (i Intent) Normalized() Intent { return i } -func (f IntentMutableFields) Normalized(currency currencyx.Code) IntentMutableFields { +func (f IntentMutableFields) Normalized(currency currencies.Currency) IntentMutableFields { f.IntentMutableFields = f.IntentMutableFields.Normalized() f.EffectiveAt = meta.NormalizeOptionalTimestamp(f.EffectiveAt) f.ExpiresAt = meta.NormalizeOptionalTimestamp(f.ExpiresAt) @@ -161,12 +161,7 @@ func (f IntentMutableFields) Normalized(currency currencyx.Code) IntentMutableFi f.BillingPeriod = period } - calc, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(currency). - Build() - if err == nil { - f.CreditAmount = calc.RoundToPrecision(f.CreditAmount) - } + f.CreditAmount = currency.RoundToPrecision(f.CreditAmount) return f } @@ -230,16 +225,20 @@ func (i Intent) Validate() error { switch i.Settlement.Type() { case SettlementTypeInvoice: settlement, err := i.Settlement.AsInvoiceSettlement() - if err == nil && settlement.Currency != i.Currency { - errs = append(errs, fmt.Errorf("settlement currency %q must match credit currency %q", settlement.Currency, i.Currency)) + if err == nil && settlement.Currency != i.Currency.GetCode() { + errs = append(errs, fmt.Errorf("settlement currency %q must match credit currency %q", settlement.Currency, i.Currency.GetCode())) } case SettlementTypeExternal: settlement, err := i.Settlement.AsExternalSettlement() - if err == nil && settlement.Currency != i.Currency { - errs = append(errs, fmt.Errorf("settlement currency %q must match credit currency %q", settlement.Currency, i.Currency)) + if err == nil && settlement.Currency != i.Currency.GetCode() { + errs = append(errs, fmt.Errorf("settlement currency %q must match credit currency %q", settlement.Currency, i.Currency.GetCode())) } } + if i.Key != nil && *i.Key == "" { + errs = append(errs, errors.New("key cannot be empty")) + } + return models.NewNillableGenericValidationError(errors.Join(errs...)) } diff --git a/openmeter/billing/charges/creditpurchase/charge_test.go b/openmeter/billing/charges/creditpurchase/charge_test.go index d54c5b906e..e6894f4065 100644 --- a/openmeter/billing/charges/creditpurchase/charge_test.go +++ b/openmeter/billing/charges/creditpurchase/charge_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -18,6 +19,9 @@ func TestIntentNormalizedPinsServicePeriodsToEffectiveAt(t *testing.T) { } intent := Intent{ + Intent: meta.Intent{ + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), + }, IntentMutableFields: IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ ServicePeriod: originalPeriod, diff --git a/openmeter/billing/charges/creditpurchase/service/create.go b/openmeter/billing/charges/creditpurchase/service/create.go index afb3ab9c0e..379a6e5ce9 100644 --- a/openmeter/billing/charges/creditpurchase/service/create.go +++ b/openmeter/billing/charges/creditpurchase/service/create.go @@ -8,6 +8,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/openmeter/billing/charges/creditpurchase" + "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/framework/transaction" @@ -15,15 +16,19 @@ import ( ) func (s *service) Create(ctx context.Context, input creditpurchase.CreateInput) (creditpurchase.ChargeWithGatheringLine, error) { - input.Intent.IntentMutableFields = input.Intent.IntentMutableFields.Normalized(input.Intent.Currency) - if err := input.Validate(); err != nil { return creditpurchase.ChargeWithGatheringLine{}, err } return transaction.Run(ctx, s.adapter, func(ctx context.Context) (creditpurchase.ChargeWithGatheringLine, error) { + if input.Intent.Currency.Type() == currencyx.CurrencyTypeCustom { + return creditpurchase.ChargeWithGatheringLine{}, fmt.Errorf("custom currency %s is not supported for credit purchases: %w", input.Intent.Currency.GetCode(), meta.ErrCustomCurrencyNotSupported) + } + + input.Intent = input.Intent.Normalized() + // Let's create the credit purchase charge - charge, err := s.adapter.CreateCharge(ctx, creditpurchase.CreateChargeInput(input)) + charge, err := s.adapter.CreateCharge(ctx, input) if err != nil { return creditpurchase.ChargeWithGatheringLine{}, err } diff --git a/openmeter/billing/charges/creditpurchase/service/external_test.go b/openmeter/billing/charges/creditpurchase/service/external_test.go index 3e73adb117..89a05989b5 100644 --- a/openmeter/billing/charges/creditpurchase/service/external_test.go +++ b/openmeter/billing/charges/creditpurchase/service/external_test.go @@ -17,6 +17,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" @@ -34,7 +35,7 @@ func TestExternalCreditPurchaseStateMachineAdvancesThroughGrantToPaymentPending( // - the external state machine advances until stable // then: // - it enters the initial credit grant state, grants credits, then persists payment-pending - charge := newExternalStateMachineTestChargeWithInput(externalStateMachineTestChargeInput{ + charge := newExternalStateMachineTestChargeWithInput(t, externalStateMachineTestChargeInput{ status: status, costBasis: alpacadecimal.NewFromFloat(0.5), creditAmount: alpacadecimal.NewFromFloat(100), @@ -58,7 +59,7 @@ func TestExternalCreditPurchaseStateMachineAdvancesThroughGrantToPaymentPending( mock.MatchedBy(func(input lineage.BackfillAdvanceLineageSegmentsInput) bool { return input.Namespace == charge.Namespace && input.CustomerID == charge.Intent.CustomerID && - input.Currency == charge.Intent.Currency && + input.Currency.GetCode() == charge.Intent.Currency.GetCode() && input.Amount.Equal(charge.Intent.CreditAmount) && input.BackingTransactionGroupID == "initiated-ledger-tx" && len(input.FeatureFilters) == 2 && @@ -104,18 +105,14 @@ func TestExternalCreditPurchaseStateMachineUsesRoundedCreditAmount(t *testing.T) // then: // - lineage and payment realization both use the currency-rounded credit amount expectedAmount := alpacadecimal.NewFromFloat(100.12) - charge := newExternalStateMachineTestChargeWithInput(externalStateMachineTestChargeInput{ + charge := newExternalStateMachineTestChargeWithInput(t, externalStateMachineTestChargeInput{ status: creditpurchase.StatusActivePaymentPending, costBasis: alpacadecimal.NewFromFloat(0.5), creditAmount: alpacadecimal.NewFromFloat(100.123), initialStatus: creditpurchase.CreatedInitialPaymentSettlementStatus, }) - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(charge.Intent.Currency). - Build() - require.NoError(t, err) - require.True(t, currency.IsRoundedToPrecision(charge.Intent.CreditAmount)) + require.True(t, charge.Intent.Currency.IsRoundedToPrecision(charge.Intent.CreditAmount)) require.Equal(t, 100.12, charge.Intent.CreditAmount.InexactFloat64()) adapter := &externalStateMachineAdapter{} @@ -227,7 +224,7 @@ func TestExternalCreditPurchaseServiceRoutesInitialStatuses(t *testing.T) { // - the credit-purchase service starts the external lifecycle // then: // - it grants credits first, then routes the initial payment status through the expected transitions - charge := newExternalStateMachineTestChargeWithInput(externalStateMachineTestChargeInput{ + charge := newExternalStateMachineTestChargeWithInput(t, externalStateMachineTestChargeInput{ status: creditpurchase.StatusCreated, costBasis: alpacadecimal.NewFromFloat(0.5), creditAmount: alpacadecimal.NewFromFloat(100), @@ -252,7 +249,7 @@ func TestExternalCreditPurchaseServiceRoutesInitialStatuses(t *testing.T) { mock.MatchedBy(func(input lineage.BackfillAdvanceLineageSegmentsInput) bool { return input.Namespace == charge.Namespace && input.CustomerID == charge.Intent.CustomerID && - input.Currency == charge.Intent.Currency && + input.Currency.GetCode() == charge.Intent.Currency.GetCode() && input.Amount.Equal(charge.Intent.CreditAmount) && input.BackingTransactionGroupID == "initiated-ledger-tx" && len(input.FeatureFilters) == 0 @@ -345,7 +342,7 @@ func TestExternalCreditPurchaseStateMachineGrantCreditsRejectsInvalidExternalSet // - the grant-credit action tries to create the credit grant realization // then: // - it fails with a validation error before creating realizations - charge := newExternalStateMachineTestChargeWithInput(externalStateMachineTestChargeInput{ + charge := newExternalStateMachineTestChargeWithInput(t, externalStateMachineTestChargeInput{ status: creditpurchase.StatusActivePaymentPending, costBasis: tc.costBasis, creditAmount: alpacadecimal.NewFromFloat(100), @@ -384,7 +381,7 @@ func TestExternalCreditPurchaseStateMachineAuthorizesAndSettlesPayment(t *testin // - the payment is authorized and then settled // then: // - payment realization moves to settled and the charge becomes final - charge := newGrantedExternalCreditPurchaseCharge(creditpurchase.StatusActivePaymentPending) + charge := newGrantedExternalCreditPurchaseCharge(t, creditpurchase.StatusActivePaymentPending) handler := &externalStateMachineHandler{} handler.On("OnCreditPurchasePaymentAuthorized", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { @@ -451,7 +448,7 @@ func TestExternalCreditPurchaseStateMachineAuthorizationUsesRealizationDuplicate // - the state machine receives another authorized trigger // then: // - the realization service reports the duplicate payment and the charge status is not persisted - charge := newExternalStateMachineTestCharge(creditpurchase.StatusActivePaymentPending, alpacadecimal.NewFromFloat(0.5)) + charge := newExternalStateMachineTestCharge(t, creditpurchase.StatusActivePaymentPending, alpacadecimal.NewFromFloat(0.5)) charge.Realizations.CreditGrantRealization = &ledgertransaction.TimedGroupReference{ GroupReference: ledgertransaction.GroupReference{TransactionGroupID: "initiated-ledger-tx"}, Time: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), @@ -502,7 +499,7 @@ func TestExternalCreditPurchaseStateMachineAuthorizesAndSettlesInSingleTransitio // - the state machine receives the paid trigger // then: // - it books authorization before settlement and persists the final charge status - charge := newGrantedExternalCreditPurchaseCharge(creditpurchase.StatusActivePaymentPending) + charge := newGrantedExternalCreditPurchaseCharge(t, creditpurchase.StatusActivePaymentPending) handler := &externalStateMachineHandler{} handler.On("OnCreditPurchasePaymentAuthorized", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { @@ -552,8 +549,10 @@ func TestExternalCreditPurchaseStateMachineAuthorizesAndSettlesInSingleTransitio handler.AssertExpectations(t) } -func newGrantedExternalCreditPurchaseCharge(status creditpurchase.Status) creditpurchase.Charge { - charge := newExternalStateMachineTestCharge(status, alpacadecimal.NewFromFloat(0.5)) +func newGrantedExternalCreditPurchaseCharge(t *testing.T, status creditpurchase.Status) creditpurchase.Charge { + t.Helper() + + charge := newExternalStateMachineTestCharge(t, status, alpacadecimal.NewFromFloat(0.5)) charge.Realizations.CreditGrantRealization = &ledgertransaction.TimedGroupReference{ GroupReference: ledgertransaction.GroupReference{TransactionGroupID: "initiated-ledger-tx"}, Time: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), @@ -589,8 +588,10 @@ type externalStateMachineTestChargeInput struct { featureFilters creditpurchase.FeatureFilters } -func newExternalStateMachineTestCharge(status creditpurchase.Status, costBasis alpacadecimal.Decimal) creditpurchase.Charge { - return newExternalStateMachineTestChargeWithInput(externalStateMachineTestChargeInput{ +func newExternalStateMachineTestCharge(t *testing.T, status creditpurchase.Status, costBasis alpacadecimal.Decimal) creditpurchase.Charge { + t.Helper() + + return newExternalStateMachineTestChargeWithInput(t, externalStateMachineTestChargeInput{ status: status, costBasis: costBasis, creditAmount: alpacadecimal.NewFromFloat(100), @@ -598,7 +599,9 @@ func newExternalStateMachineTestCharge(status creditpurchase.Status, costBasis a }) } -func newExternalStateMachineTestChargeWithInput(input externalStateMachineTestChargeInput) creditpurchase.Charge { +func newExternalStateMachineTestChargeWithInput(t *testing.T, input externalStateMachineTestChargeInput) creditpurchase.Charge { + t.Helper() + period := timeutil.ClosedPeriod{ From: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), To: time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC), @@ -607,7 +610,7 @@ func newExternalStateMachineTestChargeWithInput(input externalStateMachineTestCh intent := creditpurchase.Intent{ Intent: meta.Intent{ CustomerID: "customer-1", - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), }, IntentMutableFields: creditpurchase.IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ diff --git a/openmeter/billing/charges/creditpurchase/service/get.go b/openmeter/billing/charges/creditpurchase/service/get.go index 3ba25807ee..b89d603172 100644 --- a/openmeter/billing/charges/creditpurchase/service/get.go +++ b/openmeter/billing/charges/creditpurchase/service/get.go @@ -27,13 +27,3 @@ func (s *service) List(ctx context.Context, input creditpurchase.ListChargesInpu return s.adapter.ListCharges(ctx, input) }) } - -func (s *service) MarkVoided(ctx context.Context, input creditpurchase.MarkVoidedInput) (creditpurchase.ChargeBase, error) { - if err := input.Validate(); err != nil { - return creditpurchase.ChargeBase{}, err - } - - return transaction.Run(ctx, s.adapter, func(ctx context.Context) (creditpurchase.ChargeBase, error) { - return s.adapter.MarkVoided(ctx, input) - }) -} diff --git a/openmeter/billing/charges/creditpurchase/service/promotional_test.go b/openmeter/billing/charges/creditpurchase/service/promotional_test.go index 9bf6b723f9..0dbb545618 100644 --- a/openmeter/billing/charges/creditpurchase/service/promotional_test.go +++ b/openmeter/billing/charges/creditpurchase/service/promotional_test.go @@ -13,7 +13,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/lineage" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction" - "github.com/openmeterio/openmeter/pkg/currencyx" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -74,7 +74,7 @@ func TestPromotionalCreditPurchaseStateMachineRejectsExistingCreditGrant(t *test // - the promotional state machine attempts to grant credits // then: // - it fails before creating another grant - charge := newPromotionalStateMachineTestCharge(creditpurchase.StatusCreated) + charge := newPromotionalStateMachineTestCharge(t, creditpurchase.StatusCreated) charge.Realizations.CreditGrantRealization = &ledgertransaction.TimedGroupReference{ GroupReference: ledgertransaction.GroupReference{ TransactionGroupID: "existing-ledger-tx", @@ -122,7 +122,7 @@ func TestPromotionalCreditPurchaseStateMachineReturnsNilForFinalCharge(t *testin } stateMachine, err := NewPromotionalCreditPurchaseStateMachine(StateMachineConfig{ - Charge: newPromotionalStateMachineTestCharge(creditpurchase.StatusFinal), + Charge: newPromotionalStateMachineTestCharge(t, creditpurchase.StatusFinal), Adapter: adapter, Service: svc, }) @@ -143,7 +143,7 @@ func TestPromotionalCreditPurchaseStateMachineRejectsNonPromotionalCharge(t *tes // - the promotional state machine is constructed // then: // - construction fails before any lifecycle side effect can happen - charge := newPromotionalStateMachineTestCharge(creditpurchase.StatusCreated) + charge := newPromotionalStateMachineTestCharge(t, creditpurchase.StatusCreated) charge.Intent.Settlement = creditpurchase.NewSettlement(creditpurchase.InvoiceSettlement{}) _, err := NewPromotionalCreditPurchaseStateMachine(StateMachineConfig{ @@ -164,7 +164,7 @@ func TestPromotionalCreditPurchaseStateMachineRejectsMissingAdapter(t *testing.T // then: // - construction fails before lifecycle methods can dereference the adapter _, err := NewPromotionalCreditPurchaseStateMachine(StateMachineConfig{ - Charge: newPromotionalStateMachineTestCharge(creditpurchase.StatusCreated), + Charge: newPromotionalStateMachineTestCharge(t, creditpurchase.StatusCreated), Service: &service{}, }) @@ -180,7 +180,7 @@ func TestPromotionalCreditPurchaseStateMachineRejectsMissingService(t *testing.T // then: // - construction fails before final-state entry can dereference the service _, err := NewPromotionalCreditPurchaseStateMachine(StateMachineConfig{ - Charge: newPromotionalStateMachineTestCharge(creditpurchase.StatusCreated), + Charge: newPromotionalStateMachineTestCharge(t, creditpurchase.StatusCreated), Adapter: &promotionalStateMachineAdapter{}, }) @@ -194,7 +194,7 @@ func newPromotionalStateMachineTestMachine( ) (*PromotionalCreditpurchaseStateMachine, creditpurchase.Charge, *promotionalStateMachineAdapter, *promotionalStateMachineLineage) { t.Helper() - charge := newPromotionalStateMachineTestCharge(status) + charge := newPromotionalStateMachineTestCharge(t, status) adapter := &promotionalStateMachineAdapter{} lineageService := &promotionalStateMachineLineage{} handler := &promotionalStateMachineHandler{ @@ -215,7 +215,7 @@ func newPromotionalStateMachineTestMachine( mock.MatchedBy(func(input lineage.BackfillAdvanceLineageSegmentsInput) bool { return input.Namespace == charge.Namespace && input.CustomerID == charge.Intent.CustomerID && - input.Currency == charge.Intent.Currency && + input.Currency.GetCode() == charge.Intent.Currency.GetCode() && input.Amount.Equal(charge.Intent.CreditAmount) && input.BackingTransactionGroupID != "" })). @@ -232,7 +232,9 @@ func newPromotionalStateMachineTestMachine( return stateMachine, charge, adapter, lineageService } -func newPromotionalStateMachineTestCharge(status creditpurchase.Status) creditpurchase.Charge { +func newPromotionalStateMachineTestCharge(t *testing.T, status creditpurchase.Status) creditpurchase.Charge { + t.Helper() + period := timeutil.ClosedPeriod{ From: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), To: time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC), @@ -253,7 +255,7 @@ func newPromotionalStateMachineTestCharge(status creditpurchase.Status) creditpu Intent: creditpurchase.Intent{ Intent: meta.Intent{ CustomerID: "customer-1", - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), }, IntentMutableFields: creditpurchase.IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ diff --git a/openmeter/billing/charges/creditpurchase/service/void.go b/openmeter/billing/charges/creditpurchase/service/void.go new file mode 100644 index 0000000000..8f0490acac --- /dev/null +++ b/openmeter/billing/charges/creditpurchase/service/void.go @@ -0,0 +1,39 @@ +package service + +import ( + "context" + "fmt" + + "github.com/openmeterio/openmeter/openmeter/billing/charges/creditpurchase" + "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + "github.com/openmeterio/openmeter/pkg/framework/transaction" +) + +func (s *service) MarkVoided(ctx context.Context, input creditpurchase.MarkVoidedInput) (creditpurchase.ChargeBase, error) { + if err := input.Validate(); err != nil { + return creditpurchase.ChargeBase{}, err + } + + return transaction.Run(ctx, s.adapter, func(ctx context.Context) (creditpurchase.ChargeBase, error) { + charge, err := s.adapter.GetByID(ctx, creditpurchase.GetByIDInput{ + ChargeID: input.ChargeID, + Expands: meta.ExpandNone, + }) + if err != nil { + return creditpurchase.ChargeBase{}, fmt.Errorf("getting credit purchase charge before voiding [id=%s]: %w", input.ChargeID.ID, err) + } + + if charge.State.VoidedAt != nil { + return charge.ChargeBase, nil + } + + if err := charge.Validate(); err != nil { + return creditpurchase.ChargeBase{}, fmt.Errorf("validating credit purchase charge before voiding [id=%s]: %w", input.ChargeID.ID, err) + } + + return s.adapter.MarkVoided(ctx, creditpurchase.MarkVoidedAdapterInput{ + Charge: charge, + VoidedAt: input.VoidedAt, + }) + }) +} diff --git a/openmeter/billing/charges/flatfee/adapter/charge.go b/openmeter/billing/charges/flatfee/adapter/charge.go index 408ceeff01..830e410762 100644 --- a/openmeter/billing/charges/flatfee/adapter/charge.go +++ b/openmeter/billing/charges/flatfee/adapter/charge.go @@ -87,7 +87,7 @@ func (a *adapter) UpdateCharge(ctx context.Context, charge flatfee.ChargeBase) ( } if overrideLayer := charge.Intent.GetOverrideLayerMutableFields(); overrideLayer != nil { - intentOverride, err := tx.updateIntentOverride(ctx, charge.GetChargeID(), overrideLayer) + intentOverride, err := tx.updateIntentOverride(ctx, charge.GetChargeID(), overrideLayer, intent.Currency) if err != nil { return flatfee.ChargeBase{}, fmt.Errorf("updating flat fee charge override: %w", err) } @@ -95,7 +95,7 @@ func (a *adapter) UpdateCharge(ctx context.Context, charge flatfee.ChargeBase) ( dbUpdatedChargeBase.Edges.IntentOverride = intentOverride } - return MapChargeBaseFromDB(dbUpdatedChargeBase), nil + return MapChargeBaseFromDB(dbUpdatedChargeBase, intent.Currency) }) } @@ -132,7 +132,11 @@ func (a *adapter) UpdateSubscriptionItemID(ctx context.Context, charge flatfee.C } updatedChargeBase.Edges.IntentOverride = override - charge.ChargeBase = MapChargeBaseFromDB(updatedChargeBase) + mappedChargeBase, err := MapChargeBaseFromDB(updatedChargeBase, charge.Intent.GetBaseIntent().Currency) + if err != nil { + return flatfee.Charge{}, err + } + charge.ChargeBase = mappedChargeBase return charge, nil }) @@ -189,7 +193,7 @@ func (a *adapter) DeleteCharge(ctx context.Context, charge flatfee.Charge) error } if overrideLayer := charge.Intent.GetOverrideLayerMutableFields(); overrideLayer != nil { - if _, err := tx.updateIntentOverride(ctx, charge.GetChargeID(), overrideLayer); err != nil { + if _, err := tx.updateIntentOverride(ctx, charge.GetChargeID(), overrideLayer, baseIntent.Currency); err != nil { return fmt.Errorf("updating flat fee intent override: %w", err) } } @@ -231,16 +235,9 @@ func (a *adapter) CreateCharges(ctx context.Context, in flatfee.CreateChargesInp return nil, err } - out := make([]flatfee.Charge, 0, len(entities)) - for _, entity := range entities { - charge, err := MapChargeFlatFeeFromDB(entity, meta.ExpandNone) - if err != nil { - return nil, err - } - out = append(out, charge) - } - - return out, nil + return lo.MapErr(entities, func(entity *db.ChargeFlatFee, idx int) (flatfee.Charge, error) { + return FromDBChargeFlatFeeWithCurrency(entity, in.Intents[idx].Intent.Currency, meta.ExpandNone) + }) }) } @@ -253,7 +250,8 @@ func (a *adapter) GetByIDs(ctx context.Context, input flatfee.GetByIDsInput) ([] query := tx.db.ChargeFlatFee.Query(). Where(dbchargeflatfee.Namespace(input.Namespace)). Where(dbchargeflatfee.IDIn(input.IDs...)). - WithIntentOverride() + WithIntentOverride(). + WithCustomCurrency() if input.Expands.Has(meta.ExpandRealizations) { query = expandRealizations(query) @@ -295,7 +293,8 @@ func (a *adapter) GetByID(ctx context.Context, input flatfee.GetByIDInput) (flat query := tx.db.ChargeFlatFee.Query(). Where(dbchargeflatfee.Namespace(input.ChargeID.Namespace)). Where(dbchargeflatfee.ID(input.ChargeID.ID)). - WithIntentOverride() + WithIntentOverride(). + WithCustomCurrency() if input.Expands.Has(meta.ExpandRealizations) { query = expandRealizations(query) diff --git a/openmeter/billing/charges/flatfee/adapter/detailedline_test.go b/openmeter/billing/charges/flatfee/adapter/detailedline_test.go index e8a75b96e3..a8ffde29e4 100644 --- a/openmeter/billing/charges/flatfee/adapter/detailedline_test.go +++ b/openmeter/billing/charges/flatfee/adapter/detailedline_test.go @@ -16,13 +16,13 @@ import ( metaadapter "github.com/openmeterio/openmeter/openmeter/billing/charges/meta/adapter" "github.com/openmeterio/openmeter/openmeter/billing/models/stddetailedline" "github.com/openmeterio/openmeter/openmeter/billing/models/totals" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" dbchargeflatfee "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfee" dbchargeflatfeerundetailedline "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfeerundetailedline" "github.com/openmeterio/openmeter/openmeter/productcatalog" taxcodetestutils "github.com/openmeterio/openmeter/openmeter/taxcode/testutils" "github.com/openmeterio/openmeter/openmeter/testutils" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -97,7 +97,7 @@ func (s *FlatFeeDetailedLineAdapterSuite) TestUpsertDetailedLinesReplacesAndSoft Intent: chargesmeta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: customerID, - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(s.T(), "USD"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: taxCodeID, }, @@ -271,7 +271,7 @@ func (s *FlatFeeDetailedLineAdapterSuite) newDetailedLine(input newDetailedLineI Description: input.Description, }), ServicePeriod: input.ServicePeriod, - Currency: input.Charge.Intent.GetCurrency(), + Currency: input.Charge.Intent.GetCurrency().GetCode(), ChildUniqueReferenceID: input.ChildUniqueReferenceID, PaymentTerm: baseIntent.PaymentTerm, PerUnitAmount: alpacadecimal.NewFromFloat(0.1), diff --git a/openmeter/billing/charges/flatfee/adapter/intentoverride.go b/openmeter/billing/charges/flatfee/adapter/intentoverride.go index e732d29be2..84dfa5be50 100644 --- a/openmeter/billing/charges/flatfee/adapter/intentoverride.go +++ b/openmeter/billing/charges/flatfee/adapter/intentoverride.go @@ -11,6 +11,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + "github.com/openmeterio/openmeter/openmeter/currencies" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" dbchargeflatfee "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfee" dbchargeflatfeeoverride "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfeeoverride" @@ -65,7 +66,9 @@ func (a *adapter) CreateChargeOverride(ctx context.Context, charge flatfee.Charg } return entutils.TransactingRepo(ctx, a, func(ctx context.Context, tx *adapter) (flatfee.ChargeBase, error) { - dbIntentOverride, err := tx.createIntentOverride(ctx, charge.GetChargeID(), override) + currency := charge.Intent.GetBaseIntent().Currency + + dbIntentOverride, err := tx.createIntentOverride(ctx, charge.GetChargeID(), override, currency) if err != nil { return flatfee.ChargeBase{}, err } @@ -81,7 +84,7 @@ func (a *adapter) CreateChargeOverride(ctx context.Context, charge flatfee.Charg dbCharge.Edges.IntentOverride = dbIntentOverride - return MapChargeBaseFromDB(dbCharge), nil + return MapChargeBaseFromDB(dbCharge, charge.Intent.GetBaseIntent().Currency) }) } @@ -128,12 +131,12 @@ func (a *adapter) DeleteChargeOverride(ctx context.Context, charge flatfee.Charg }) } -func (a *adapter) createIntentOverride(ctx context.Context, chargeID meta.ChargeID, override flatfee.IntentMutableFields) (*entdb.ChargeFlatFeeOverride, error) { +func (a *adapter) createIntentOverride(ctx context.Context, chargeID meta.ChargeID, override flatfee.IntentMutableFields, currency currencies.Currency) (*entdb.ChargeFlatFeeOverride, error) { if err := chargeID.Validate(); err != nil { return nil, fmt.Errorf("charge id: %w", err) } - normalized := override.Normalized("") + normalized := override.Normalized(currency) if err := normalized.Validate(); err != nil { return nil, fmt.Errorf("validating intent override: %w", err) } @@ -165,12 +168,12 @@ func (a *adapter) createIntentOverride(ctx context.Context, chargeID meta.Charge return create.Save(ctx) } -func (a *adapter) updateIntentOverride(ctx context.Context, chargeID meta.ChargeID, override *flatfee.IntentMutableFields) (*entdb.ChargeFlatFeeOverride, error) { +func (a *adapter) updateIntentOverride(ctx context.Context, chargeID meta.ChargeID, override *flatfee.IntentMutableFields, currency currencies.Currency) (*entdb.ChargeFlatFeeOverride, error) { if err := chargeID.Validate(); err != nil { return nil, fmt.Errorf("charge id: %w", err) } - normalized := override.Normalized("") + normalized := override.Normalized(currency) if err := normalized.Validate(); err != nil { return nil, fmt.Errorf("validating intent override: %w", err) } diff --git a/openmeter/billing/charges/flatfee/adapter/intentoverride_test.go b/openmeter/billing/charges/flatfee/adapter/intentoverride_test.go index 4cd94e611c..30399799ad 100644 --- a/openmeter/billing/charges/flatfee/adapter/intentoverride_test.go +++ b/openmeter/billing/charges/flatfee/adapter/intentoverride_test.go @@ -14,12 +14,12 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" chargesmeta "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" metaadapter "github.com/openmeterio/openmeter/openmeter/billing/charges/meta/adapter" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" "github.com/openmeterio/openmeter/openmeter/productcatalog" taxcodetestutils "github.com/openmeterio/openmeter/openmeter/taxcode/testutils" "github.com/openmeterio/openmeter/openmeter/testutils" "github.com/openmeterio/openmeter/pkg/clock" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -302,7 +302,7 @@ func (s *FlatFeeIntentOverrideAdapterSuite) createCharge(namespace string) flatf Intent: chargesmeta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: customerID, - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(s.T(), "USD"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: taxCodeID, }, diff --git a/openmeter/billing/charges/flatfee/adapter/mapper.go b/openmeter/billing/charges/flatfee/adapter/mapper.go index 8fcf8713db..f690c78f78 100644 --- a/openmeter/billing/charges/flatfee/adapter/mapper.go +++ b/openmeter/billing/charges/flatfee/adapter/mapper.go @@ -16,6 +16,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" "github.com/openmeterio/openmeter/openmeter/billing/models/stddetailedline" "github.com/openmeterio/openmeter/openmeter/billing/models/totals" + "github.com/openmeterio/openmeter/openmeter/currencies" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/convert" @@ -25,8 +26,26 @@ import ( // MapFlatFeeChargeFromDB converts a DB Charge entity (with loaded FlatFee edge) to a FlatFeeCharge. func MapChargeFlatFeeFromDB(entity *entdb.ChargeFlatFee, expands meta.Expands) (flatfee.Charge, error) { + mappedMeta, err := chargemeta.FromDBChargeWithCurrencyEdge(entity, entity.Edges) + if err != nil { + return flatfee.Charge{}, fmt.Errorf("mapping flat fee charge meta [id=%s]: %w", entity.ID, err) + } + + return mapChargeFlatFeeFromDB(entity, mappedMeta, expands) +} + +func FromDBChargeFlatFeeWithCurrency(entity *entdb.ChargeFlatFee, currency currencies.Currency, expands meta.Expands) (flatfee.Charge, error) { + mappedMeta, err := chargemeta.FromDBCharge(entity, currency) + if err != nil { + return flatfee.Charge{}, fmt.Errorf("mapping flat fee charge meta [id=%s]: %w", entity.ID, err) + } + + return mapChargeFlatFeeFromDB(entity, mappedMeta, expands) +} + +func mapChargeFlatFeeFromDB(entity *entdb.ChargeFlatFee, mappedMeta meta.Charge, expands meta.Expands) (flatfee.Charge, error) { charge := flatfee.Charge{ - ChargeBase: MapChargeBaseFromDB(entity), + ChargeBase: mapChargeBaseFromDB(entity, mappedMeta), } if expands.Has(meta.ExpandRealizations) { @@ -129,14 +148,21 @@ func sortDetailedLines(lines flatfee.DetailedLines) { slices.SortStableFunc(lines, stddetailedline.Compare[flatfee.DetailedLine]) } -func MapChargeBaseFromDB(entity *entdb.ChargeFlatFee) flatfee.ChargeBase { +func MapChargeBaseFromDB(entity *entdb.ChargeFlatFee, currency currencies.Currency) (flatfee.ChargeBase, error) { + mappedMeta, err := chargemeta.FromDBCharge(entity, currency) + if err != nil { + return flatfee.ChargeBase{}, fmt.Errorf("mapping charge meta: %w", err) + } + + return mapChargeBaseFromDB(entity, mappedMeta), nil +} + +func mapChargeBaseFromDB(entity *entdb.ChargeFlatFee, mappedMeta meta.Charge) flatfee.ChargeBase { var percentageDiscounts *billing.PercentageDiscount if entity.Discounts != nil { percentageDiscounts = entity.Discounts.Percentage } - mappedMeta := chargemeta.MapFromDB(entity) - return flatfee.ChargeBase{ ManagedResource: mappedMeta.ManagedResource, Status: entity.StatusDetailed, diff --git a/openmeter/billing/charges/flatfee/adapter/realizationrun_test.go b/openmeter/billing/charges/flatfee/adapter/realizationrun_test.go index a5dd860305..e220def9e5 100644 --- a/openmeter/billing/charges/flatfee/adapter/realizationrun_test.go +++ b/openmeter/billing/charges/flatfee/adapter/realizationrun_test.go @@ -13,11 +13,11 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" chargesmeta "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" metaadapter "github.com/openmeterio/openmeter/openmeter/billing/charges/meta/adapter" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" "github.com/openmeterio/openmeter/openmeter/productcatalog" taxcodetestutils "github.com/openmeterio/openmeter/openmeter/taxcode/testutils" "github.com/openmeterio/openmeter/openmeter/testutils" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -83,7 +83,7 @@ func (s *FlatFeeRealizationRunAdapterSuite) TestCreateCurrentRunFailsWhenCurrent Intent: chargesmeta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: customerID, - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(s.T(), "USD"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: taxCodeID, }, diff --git a/openmeter/billing/charges/flatfee/charge.go b/openmeter/billing/charges/flatfee/charge.go index 7143f05186..be3785759b 100644 --- a/openmeter/billing/charges/flatfee/charge.go +++ b/openmeter/billing/charges/flatfee/charge.go @@ -11,9 +11,9 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/productcatalog" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -63,7 +63,7 @@ func (c ChargeBase) GetCustomerID() customer.CustomerID { } } -func (c ChargeBase) GetCurrency() currencyx.Code { +func (c ChargeBase) GetCurrency() currencies.Currency { return c.Intent.GetCurrency() } @@ -123,7 +123,7 @@ type Intent struct { } func (i Intent) Normalized() Intent { - i.IntentMutableFields = i.IntentMutableFields.Normalized(i.Currency) + i.IntentMutableFields = i.IntentMutableFields.Normalized(i.Intent.Currency) return i } @@ -180,14 +180,11 @@ func (i Intent) CalculateAmountAfterProration() (alpacadecimal.Decimal, error) { percentage := alpacadecimal.NewFromInt(servicePeriodDuration).Div(alpacadecimal.NewFromInt(fullServicePeriodDuration)) amount := i.AmountBeforeProration.Mul(percentage) - calc, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(i.Currency). - Build() - if err != nil { - return alpacadecimal.Decimal{}, fmt.Errorf("creating currency calculator: %w", err) + if err := i.Currency.Validate(); err != nil { + return alpacadecimal.Decimal{}, fmt.Errorf("currency: %w", err) } - return calc.RoundToPrecision(amount), nil + return i.Intent.Currency.RoundToPrecision(amount), nil } // OverridableIntent stores the immutable intent plus the base and optional @@ -228,7 +225,7 @@ func (i OverridableIntent) GetCustomerID() string { return i.intent.CustomerID } -func (i OverridableIntent) GetCurrency() currencyx.Code { +func (i OverridableIntent) GetCurrency() currencies.Currency { return i.intent.Currency } @@ -492,16 +489,10 @@ type IntentMutableFields struct { AmountBeforeProration alpacadecimal.Decimal `json:"amountBeforeProration"` } -func (f IntentMutableFields) Normalized(currency currencyx.Code) IntentMutableFields { +func (f IntentMutableFields) Normalized(currency currencies.Currency) IntentMutableFields { f.IntentMutableFields = f.IntentMutableFields.Normalized() f.InvoiceAt = meta.NormalizeTimestamp(f.InvoiceAt) - - calc, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(currency). - Build() - if err == nil { - f.AmountBeforeProration = calc.RoundToPrecision(f.AmountBeforeProration) - } + f.AmountBeforeProration = currency.RoundToPrecision(f.AmountBeforeProration) return f } diff --git a/openmeter/billing/charges/flatfee/charge_test.go b/openmeter/billing/charges/flatfee/charge_test.go index 270f2fb99c..e5e289b5ff 100644 --- a/openmeter/billing/charges/flatfee/charge_test.go +++ b/openmeter/billing/charges/flatfee/charge_test.go @@ -9,8 +9,9 @@ import ( "github.com/stretchr/testify/require" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + "github.com/openmeterio/openmeter/openmeter/currencies" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/productcatalog" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/datetime" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -38,7 +39,7 @@ func TestCalculateAmountAfterProration(t *testing.T) { return Intent{ Intent: meta.Intent{ CustomerID: "cust-1", - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), ManagedBy: "system", }, IntentMutableFields: IntentMutableFields{ @@ -135,7 +136,7 @@ func TestCalculateAmountAfterProration(t *testing.T) { t.Run("JPY rounds to zero decimal places", func(t *testing.T) { intent := baseIntent() - intent.Currency = currencyx.Code("JPY") + intent.Currency = currenciestestutils.NewFiatCurrency(t, "JPY") intent.AmountBeforeProration = alpacadecimal.NewFromInt(1000) // 10 days out of 31 = 1000 * 10/31 = 322.580... rounded to 323 for JPY tenDaysEnd := datetime.MustParseTimeInLocation(t, "2026-01-11T00:00:00Z", time.UTC).AsTime() @@ -166,7 +167,7 @@ func TestCalculateAmountAfterProration(t *testing.T) { t.Run("invalid currency returns error", func(t *testing.T) { intent := baseIntent() - intent.Currency = currencyx.Code("INVALID") + intent.Currency = currencies.Currency{} _, err := intent.CalculateAmountAfterProration() require.Error(t, err) diff --git a/openmeter/billing/charges/flatfee/service/create.go b/openmeter/billing/charges/flatfee/service/create.go index 37101e865d..7e595987ce 100644 --- a/openmeter/billing/charges/flatfee/service/create.go +++ b/openmeter/billing/charges/flatfee/service/create.go @@ -29,6 +29,10 @@ func (s *service) Create(ctx context.Context, input flatfee.CreateInput) ([]flat return transaction.Run(ctx, s.adapter, func(ctx context.Context) ([]flatfee.ChargeWithGatheringLine, error) { // Let's create all the flat fee charges in bulk intentsWithStatus, err := slicesx.MapWithErr(input.Intents, func(intent flatfee.Intent) (flatfee.IntentWithInitialStatus, error) { + if intent.Currency.IsCustom() { + return flatfee.IntentWithInitialStatus{}, fmt.Errorf("creating flat fee charge with custom currency %q: %w", intent.Currency.GetCode(), meta.ErrCustomCurrencyNotSupported) + } + chargeIntent := intent.Normalized() amountAfterProration, err := chargeIntent.CalculateAmountAfterProration() @@ -193,7 +197,7 @@ func buildFlatFeeGatheringLine(input buildFlatFeeGatheringLineInput) (billing.Ga ), FeatureKey: lo.FromPtr(lineIntent.FeatureKey), - Currency: lineIntent.Currency, + Currency: lineIntent.Currency.GetCode(), ServicePeriod: lineIntent.ServicePeriod, InvoiceAt: lineIntent.InvoiceAt, diff --git a/openmeter/billing/charges/flatfee/service/creditsonly.go b/openmeter/billing/charges/flatfee/service/creditsonly.go index eaca07df76..3db1f9558b 100644 --- a/openmeter/billing/charges/flatfee/service/creditsonly.go +++ b/openmeter/billing/charges/flatfee/service/creditsonly.go @@ -14,7 +14,6 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/models/totals" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/clock" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/statelessx" ) @@ -91,12 +90,7 @@ func (s *CreditsOnlyStateMachine) AdvanceAfterBookedAt(ctx context.Context) erro } func (s *CreditsOnlyStateMachine) AllocateCredits(ctx context.Context) error { - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(s.Charge.Intent.GetCurrency()). - Build() - if err != nil { - return fmt.Errorf("get currency calculator: %w", err) - } + currency := s.Charge.Intent.GetCurrency() amount := currency.RoundToPrecision(s.Charge.State.AmountAfterProration) @@ -195,12 +189,7 @@ func (s *CreditsOnlyStateMachine) reconcileCurrentRunCredits(ctx context.Context return nil } - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(s.Charge.Intent.GetCurrency()). - Build() - if err != nil { - return fmt.Errorf("get currency calculator: %w", err) - } + currency := s.Charge.Intent.GetCurrency() amount = currency.RoundToPrecision(amount) servicePeriod := s.Charge.Intent.GetEffectiveServicePeriod() @@ -260,12 +249,7 @@ func (s *CreditsOnlyStateMachine) DeleteCharge(ctx context.Context, patch meta.P s.Charge.Status = flatfee.StatusDeleted if patch.GetPolicy().CreditRefundPolicy == meta.CreditRefundPolicyCorrect && s.Charge.Realizations.CurrentRun != nil { - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(s.Charge.Intent.GetCurrency()). - Build() - if err != nil { - return fmt.Errorf("get currency calculator: %w", err) - } + currency := s.Charge.Intent.GetCurrency() if _, err := s.Realizations.CorrectAllCredits(ctx, flatfeerealizations.CorrectAllCreditRealizationsInput{ Charge: s.Charge, diff --git a/openmeter/billing/charges/flatfee/service/lineengine.go b/openmeter/billing/charges/flatfee/service/lineengine.go index a08624868a..d75f8a5460 100644 --- a/openmeter/billing/charges/flatfee/service/lineengine.go +++ b/openmeter/billing/charges/flatfee/service/lineengine.go @@ -14,7 +14,6 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/clock" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/slicesx" ) @@ -692,18 +691,11 @@ func (e *LineEngine) cleanupDeletedStandardLines(ctx context.Context, input bill return fmt.Errorf("flat fee standard line[%s] cannot be deleted because realization run[%s] is still current for charge[%s]", stdLine.ID, run.ID.ID, charge.ID) } - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(charge.Intent.GetCurrency()). - Build() - if err != nil { - return fmt.Errorf("getting currency calculator for charge[%s]: %w", charge.ID, err) - } - if _, err := e.service.realizations.CorrectAllCredits(ctx, flatfeerealizations.CorrectAllCreditRealizationsInput{ Charge: charge, Run: run, AllocateAt: flatfee.UsageBookedAt(charge.Intent.GetEffectivePaymentTerm(), run.ServicePeriod), - CurrencyCalculator: currency, + CurrencyCalculator: charge.Intent.GetCurrency(), }); err != nil { return fmt.Errorf("correcting credits for deleted flat fee standard line[%s] run[%s]: %w", stdLine.ID, run.ID.ID, err) } diff --git a/openmeter/billing/charges/flatfee/service/manualedit.go b/openmeter/billing/charges/flatfee/service/manualedit.go index 91da1e2269..b6523b4d55 100644 --- a/openmeter/billing/charges/flatfee/service/manualedit.go +++ b/openmeter/billing/charges/flatfee/service/manualedit.go @@ -9,6 +9,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/models" ) @@ -34,7 +35,8 @@ func (s *CreditThenInvoiceStateMachine) intentMutableFieldsFromLineManualEdit(li return flatfee.IntentMutableFields{}, fmt.Errorf("getting flat price from line[%s]: %w", line.GetID(), err) } - out := s.Charge.Intent.GetEffectiveIntent().IntentMutableFields + effectiveIntent := s.Charge.Intent.GetEffectiveIntent() + out := effectiveIntent.IntentMutableFields out.Name = line.GetName() out.Description = line.GetDescription() out.Metadata = line.GetMetadata().Clone() @@ -50,7 +52,7 @@ func (s *CreditThenInvoiceStateMachine) intentMutableFieldsFromLineManualEdit(li out.AmountBeforeProration = flatPrice.Amount out.PercentageDiscounts = line.GetRateCardDiscounts().Percentage.CloneOrNil() - out = out.Normalized(s.Charge.Intent.GetCurrency()) + out = out.Normalized(effectiveIntent.Currency) if err := out.Validate(); err != nil { return flatfee.IntentMutableFields{}, err } @@ -76,6 +78,11 @@ func intentFromManualCreatedLine( return flatfee.Intent{}, fmt.Errorf("line id is required") } + currency, err := currencies.NewFiatCurrency(line.GetCurrency()) + if err != nil { + return flatfee.Intent{}, fmt.Errorf("resolving fiat currency %q: %w", line.GetCurrency(), err) + } + if chargeID := line.GetChargeID(); chargeID != nil && *chargeID != "" { return flatfee.Intent{}, fmt.Errorf("line[%s]: charge id must be empty for manual create", line.GetID()) } @@ -122,7 +129,7 @@ func intentFromManualCreatedLine( ManagedBy: billing.ManuallyManagedLine, CustomerID: invoice.GetCustomerID().ID, Annotations: annotations, - Currency: line.GetCurrency(), + Currency: currency, TaxConfig: taxConfig, }, IntentMutableFields: flatfee.IntentMutableFields{ diff --git a/openmeter/billing/charges/flatfee/service/realizations/credittheninvoice.go b/openmeter/billing/charges/flatfee/service/realizations/credittheninvoice.go index 0c98f6069a..39d1ac229d 100644 --- a/openmeter/billing/charges/flatfee/service/realizations/credittheninvoice.go +++ b/openmeter/billing/charges/flatfee/service/realizations/credittheninvoice.go @@ -67,12 +67,7 @@ func (s *Service) StartCreditThenInvoiceRun(ctx context.Context, in StartCreditT } return transaction.Run(ctx, s.adapter, func(ctx context.Context) (StartCreditThenInvoiceRunResult, error) { - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(in.Charge.Intent.GetCurrency()). - Build() - if err != nil { - return StartCreditThenInvoiceRunResult{}, fmt.Errorf("get currency calculator: %w", err) - } + currency := in.Charge.Intent.GetCurrency() amountAfterProration, err := invoiceupdater.GetFlatFeePerUnitAmount(&in.Line) if err != nil { @@ -255,12 +250,7 @@ func (s *Service) ReconcileStandardLineToIntent(ctx context.Context, in Reconcil } return transaction.Run(ctx, s.adapter, func(ctx context.Context) (ReconcileStandardLineToIntentResult, error) { - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(in.Charge.Intent.GetCurrency()). - Build() - if err != nil { - return ReconcileStandardLineToIntentResult{}, fmt.Errorf("get currency calculator: %w", err) - } + currency := in.Charge.Intent.GetCurrency() amountAfterProration, err := invoiceupdater.GetFlatFeePerUnitAmount(&in.Line) if err != nil { diff --git a/openmeter/billing/charges/flatfee/service/realizations/preview.go b/openmeter/billing/charges/flatfee/service/realizations/preview.go index 93a6d5225d..ac8e742a15 100644 --- a/openmeter/billing/charges/flatfee/service/realizations/preview.go +++ b/openmeter/billing/charges/flatfee/service/realizations/preview.go @@ -11,7 +11,6 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" "github.com/openmeterio/openmeter/openmeter/billing/charges/invoiceupdater" "github.com/openmeterio/openmeter/openmeter/productcatalog" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" ) @@ -57,12 +56,7 @@ func (s *Service) BuildCreditThenInvoiceGatheringPreviewRun(in BuildCreditThenIn return BuildCreditThenInvoiceGatheringPreviewRunResult{}, err } - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(in.Charge.Intent.GetCurrency()). - Build() - if err != nil { - return BuildCreditThenInvoiceGatheringPreviewRunResult{}, fmt.Errorf("get currency calculator: %w", err) - } + currency := in.Charge.Intent.GetCurrency() amountAfterProration, err := invoiceupdater.GetFlatFeePerUnitAmount(&in.Line) if err != nil { diff --git a/openmeter/billing/charges/lineage/lineage_test.go b/openmeter/billing/charges/lineage/lineage_test.go index 4e5831e88c..efa723bd67 100644 --- a/openmeter/billing/charges/lineage/lineage_test.go +++ b/openmeter/billing/charges/lineage/lineage_test.go @@ -6,9 +6,53 @@ import ( "github.com/alpacahq/alpacadecimal" "github.com/stretchr/testify/require" + "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/creditrealization" + "github.com/openmeterio/openmeter/openmeter/currencies" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" + "github.com/openmeterio/openmeter/pkg/currencyx" ) +func TestBackfillAdvanceLineageSegmentsInputValidateCurrency(t *testing.T) { + customCurrency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeCustom). + WithCode(currencyx.Code("CREDITS")). + WithName("Credits"). + Build() + require.NoError(t, err) + + baseInput := BackfillAdvanceLineageSegmentsInput{ + Namespace: "test-namespace", + CustomerID: "test-customer", + Amount: alpacadecimal.NewFromInt(10), + BackingTransactionGroupID: "test-transaction-group", + } + + t.Run("fiat currency", func(t *testing.T) { + input := baseInput + input.Currency = currenciestestutils.NewFiatCurrency(t, "USD") + + require.NoError(t, input.Validate()) + }) + + t.Run("custom currency", func(t *testing.T) { + input := baseInput + input.Currency = currencies.Currency{Currency: customCurrency} + + err := input.Validate() + require.Error(t, err) + require.ErrorIs(t, err, meta.ErrCustomCurrencyNotSupported) + require.ErrorContains(t, err, "advance lineage backfill") + }) + + t.Run("missing currency", func(t *testing.T) { + input := baseInput + + err := input.Validate() + require.Error(t, err) + require.ErrorContains(t, err, "currency is required") + }) +} + func TestSegmentValidateRequiresSourceBackingTransactionGroupForAdvanceBackfilledSource(t *testing.T) { sourceState := creditrealization.LineageSegmentStateAdvanceBackfilled backingTransactionGroupID := "recognition-txg" diff --git a/openmeter/billing/charges/lineage/service.go b/openmeter/billing/charges/lineage/service.go index 57c414f9ad..b8f8cc17b5 100644 --- a/openmeter/billing/charges/lineage/service.go +++ b/openmeter/billing/charges/lineage/service.go @@ -8,7 +8,9 @@ import ( "github.com/alpacahq/alpacadecimal" + "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/creditrealization" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/framework/entutils" ) @@ -40,7 +42,7 @@ type CreateInitialLineagesInput struct { Namespace string ChargeID string CustomerID string - Currency currencyx.Code + Currency currencies.Currency Features []string Realizations creditrealization.Realizations } @@ -60,6 +62,11 @@ func (i CreateInitialLineagesInput) Validate() error { if err := i.Currency.Validate(); err != nil { errs = append(errs, fmt.Errorf("currency: %w", err)) } + + if i.Currency.IsCustom() { + errs = append(errs, fmt.Errorf("create initial lineages: custom currency: %w", meta.ErrCustomCurrencyNotSupported)) + } + if err := i.Realizations.Validate(); err != nil { errs = append(errs, fmt.Errorf("realizations: %w", err)) } @@ -95,7 +102,7 @@ func (i PersistCorrectionLineageSegmentsInput) Validate() error { type BackfillAdvanceLineageSegmentsInput struct { Namespace string CustomerID string - Currency currencyx.Code + Currency currencies.Currency Amount alpacadecimal.Decimal BackingTransactionGroupID string FeatureFilters []string @@ -113,6 +120,9 @@ func (i BackfillAdvanceLineageSegmentsInput) Validate() error { if err := i.Currency.Validate(); err != nil { errs = append(errs, fmt.Errorf("currency: %w", err)) } + if i.Currency.IsCustom() { + errs = append(errs, fmt.Errorf("advance lineage backfill: %w", meta.ErrCustomCurrencyNotSupported)) + } if !i.Amount.IsPositive() { errs = append(errs, errors.New("amount must be positive")) } diff --git a/openmeter/billing/charges/lineage/service/service.go b/openmeter/billing/charges/lineage/service/service.go index 17495ad2ee..5f6f287929 100644 --- a/openmeter/billing/charges/lineage/service/service.go +++ b/openmeter/billing/charges/lineage/service/service.go @@ -63,7 +63,7 @@ func (s *service) CreateInitialLineages(ctx context.Context, input lineage.Creat Namespace: input.Namespace, ChargeID: input.ChargeID, CustomerID: input.CustomerID, - Currency: input.Currency, + Currency: input.Currency.GetCode(), Specs: specs, }) }) @@ -181,7 +181,7 @@ func (s *service) BackfillAdvanceLineageSegments(ctx context.Context, input line } return transaction.RunWithNoValue(ctx, s.adapter, func(ctx context.Context) error { - lineages, err := s.adapter.LockAdvanceLineagesForBackfill(ctx, input.Namespace, input.CustomerID, input.Currency) + lineages, err := s.adapter.LockAdvanceLineagesForBackfill(ctx, input.Namespace, input.CustomerID, input.Currency.GetCode()) if err != nil { return fmt.Errorf("lock advance lineages for backfill: %w", err) } diff --git a/openmeter/billing/charges/meta/charge.go b/openmeter/billing/charges/meta/charge.go index 627c8e9e47..3501a69619 100644 --- a/openmeter/billing/charges/meta/charge.go +++ b/openmeter/billing/charges/meta/charge.go @@ -9,8 +9,8 @@ import ( "github.com/samber/lo" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/customer" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/expand" "github.com/openmeterio/openmeter/pkg/models" ) @@ -92,7 +92,7 @@ type Expands = expand.Expand[Expand] type ChargeAccessor interface { GetChargeID() ChargeID GetCustomerID() customer.CustomerID - GetCurrency() currencyx.Code + GetCurrency() currencies.Currency ErrorAttributes() models.Attributes } diff --git a/openmeter/billing/charges/meta/errors.go b/openmeter/billing/charges/meta/errors.go index 551329902d..ccfe4b492a 100644 --- a/openmeter/billing/charges/meta/errors.go +++ b/openmeter/billing/charges/meta/errors.go @@ -1,12 +1,17 @@ package meta import ( + "errors" "net/http" "github.com/openmeterio/openmeter/pkg/framework/commonhttp" "github.com/openmeterio/openmeter/pkg/models" ) +// ErrCustomCurrencyNotSupported is returned when a custom currency is not supported yet, use this as a +// marker to signify where we need to continue adding support for custom currencies. +var ErrCustomCurrencyNotSupported = errors.New("custom currency is not supported") + const ErrCodeUnsupported models.ErrorCode = "unsupported" var ErrUnsupported = models.NewValidationIssue( diff --git a/openmeter/billing/charges/meta/intent.go b/openmeter/billing/charges/meta/intent.go index 1dc16e7e23..943ca0ee74 100644 --- a/openmeter/billing/charges/meta/intent.go +++ b/openmeter/billing/charges/meta/intent.go @@ -9,8 +9,8 @@ import ( "github.com/samber/lo" "github.com/openmeterio/openmeter/openmeter/billing" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/productcatalog" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -21,7 +21,7 @@ type Intent struct { Annotations models.Annotations `json:"annotations"` - Currency currencyx.Code `json:"currency"` + Currency currencies.Currency `json:"-"` TaxConfig productcatalog.TaxCodeConfig `json:"taxConfig"` UniqueReferenceID *string `json:"childUniqueReferenceID"` @@ -34,6 +34,7 @@ func (i Intent) Clone() Intent { // Keep intent cloning infallible for developer ergonomics; annotations are // only shallow-cloned here so GetEffectiveIntent does not need an error return. out.Annotations = maps.Clone(i.Annotations) + out.Currency = i.Currency.Clone() if i.UniqueReferenceID != nil { out.UniqueReferenceID = lo.ToPtr(*i.UniqueReferenceID) diff --git a/openmeter/billing/charges/models/chargemeta/mixin.go b/openmeter/billing/charges/models/chargemeta/model.go similarity index 74% rename from openmeter/billing/charges/models/chargemeta/mixin.go rename to openmeter/billing/charges/models/chargemeta/model.go index 264daa548a..63bfcad4c2 100644 --- a/openmeter/billing/charges/models/chargemeta/mixin.go +++ b/openmeter/billing/charges/models/chargemeta/model.go @@ -1,124 +1,56 @@ package chargemeta import ( + "errors" + "fmt" "time" - "entgo.io/ent" - "entgo.io/ent/dialect" - "entgo.io/ent/dialect/entsql" - "entgo.io/ent/schema/field" - "entgo.io/ent/schema/index" - "entgo.io/ent/schema/mixin" + "github.com/samber/lo" "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + "github.com/openmeterio/openmeter/openmeter/currencies" + currenciesadapter "github.com/openmeterio/openmeter/openmeter/currencies/adapter" + entdb "github.com/openmeterio/openmeter/openmeter/ent/db" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/convert" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/framework/entutils" + "github.com/openmeterio/openmeter/pkg/framework/entutils/entedge" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" ) -type Mixin = entutils.RecursiveMixin[metaMixin] +type CreateInput struct { + Namespace string -type metaMixin struct { - mixin.Schema -} + Intent meta.Intent + IntentMutableFields meta.IntentMutableFields -func (metaMixin) Mixin() []ent.Mixin { - return []ent.Mixin{ - entutils.AnnotationsMixin{}, - entutils.ResourceMixin{}, - } + Status meta.ChargeStatus + AdvanceAfter *time.Time } -func (metaMixin) Fields() []ent.Field { - return []ent.Field{ - field.String("customer_id"). - NotEmpty(). - Immutable(). - SchemaType(map[string]string{ - dialect.Postgres: "char(26)", - }), - - field.Time("service_period_from"), - field.Time("service_period_to"), - field.Time("billing_period_from"), - field.Time("billing_period_to"), - field.Time("full_service_period_from"), - field.Time("full_service_period_to"), - - field.Enum("status"). - GoType(meta.ChargeStatus("")), - - field.String("unique_reference_id"). - Immutable(). - Optional(). - Nillable(), - - field.String("currency"). - GoType(currencyx.Code("")). - NotEmpty(). - Immutable(). - SchemaType(map[string]string{ - dialect.Postgres: "varchar(3)", - }), - - field.Enum("managed_by"). - GoType(billing.InvoiceLineManagedBy("")). - Immutable(), - - // Subscriptions metadata - field.String("subscription_id"). - Optional(). - Nillable(). - Immutable(), - - field.String("subscription_phase_id"). - Optional(). - Nillable(). - Immutable(), - - field.String("subscription_item_id"). - Optional(). - Nillable(), - - field.Time("advance_after"). - Optional(). - Nillable(), - field.String("tax_code_id"). - NotEmpty(). - Immutable(). - SchemaType(map[string]string{ - dialect.Postgres: "char(26)", - }), - field.Enum("tax_behavior"). - GoType(productcatalog.TaxBehavior("")). - Optional(). - Nillable(). - Immutable(), +func (i CreateInput) Validate() error { + var errs []error + + if err := i.Intent.Validate(); err != nil { + errs = append(errs, err) } -} -func (metaMixin) Indexes() []ent.Index { - return []ent.Index{ - index.Fields("namespace", "customer_id", "unique_reference_id"). - Annotations( - entsql.IndexWhere("unique_reference_id IS NOT NULL AND deleted_at IS NULL"), - ). - Unique(), + if err := i.IntentMutableFields.Validate(); err != nil { + errs = append(errs, err) } -} -type CreateInput struct { - Namespace string + if i.Namespace == "" { + errs = append(errs, fmt.Errorf("namespace is required")) + } - Intent meta.Intent - IntentMutableFields meta.IntentMutableFields + if i.Status == "" { + errs = append(errs, fmt.Errorf("status is required")) + } - Status meta.ChargeStatus - AdvanceAfter *time.Time + return models.NewNillableGenericValidationError(errors.Join(errs...)) } type Creator[T any] interface { @@ -127,7 +59,8 @@ type Creator[T any] interface { entutils.TimeMixinCreator[T] SetCustomerID(customerID string) T - SetCurrency(currency currencyx.Code) T + SetNillableFiatCurrencyCode(currency *currencyx.Code) T + SetNillableCustomCurrencyID(customCurrencyID *string) T SetNillableUniqueReferenceID(uniqueReferenceID *string) T SetNillableSubscriptionID(subscriptionID *string) T SetNillableSubscriptionPhaseID(subscriptionPhaseID *string) T @@ -170,14 +103,8 @@ func Create[T Creator[T]](creator Creator[T], in CreateInput) (T, error) { in.IntentMutableFields = in.IntentMutableFields.Normalized() in.AdvanceAfter = meta.NormalizeOptionalTimestamp(in.AdvanceAfter) - if err := in.Intent.Validate(); err != nil { - var empty T - return empty, err - } - - if err := in.IntentMutableFields.Validate(); err != nil { - var empty T - return empty, err + if err := in.Validate(); err != nil { + return lo.Empty[T](), err } var subscriptionID *string @@ -193,6 +120,26 @@ func Create[T Creator[T]](creator Creator[T], in CreateInput) (T, error) { subscriptionItemID = &in.Intent.Subscription.ItemID } + switch in.Intent.Currency.Type() { + case currencyx.CurrencyTypeFiat: + def := in.Intent.Currency.Definition() + + if def == nil { + return lo.Empty[T](), fmt.Errorf("resolved currency definition is required") + } + + creator = creator.SetNillableFiatCurrencyCode(lo.ToPtr(currencyx.Code(def.ISOCode))) + + case currencyx.CurrencyTypeCustom: + if in.Intent.Currency.ID == "" { + return lo.Empty[T](), fmt.Errorf("resolved currency ID is required") + } + + creator = creator.SetNillableCustomCurrencyID(lo.ToPtr(in.Intent.Currency.ID)) + default: + return lo.Empty[T](), fmt.Errorf("unsupported currency type: %s", in.Intent.Currency.Type()) + } + return creator. SetNamespace(in.Namespace). SetName(in.IntentMutableFields.Name). @@ -207,7 +154,6 @@ func Create[T Creator[T]](creator Creator[T], in CreateInput) (T, error) { SetFullServicePeriodFrom(in.IntentMutableFields.FullServicePeriod.From.UTC()). SetFullServicePeriodTo(in.IntentMutableFields.FullServicePeriod.To.UTC()). SetStatus(in.Status). - SetCurrency(in.Intent.Currency). SetManagedBy(in.Intent.ManagedBy). SetNillableUniqueReferenceID(in.Intent.UniqueReferenceID). SetNillableAdvanceAfter(convert.SafeToUTC(in.AdvanceAfter)). @@ -269,7 +215,6 @@ type Getter[T any] interface { GetAnnotations() models.Annotations GetManagedBy() billing.InvoiceLineManagedBy GetCustomerID() string - GetCurrency() currencyx.Code GetServicePeriodFrom() time.Time GetServicePeriodTo() time.Time GetAdvanceAfter() *time.Time @@ -283,9 +228,40 @@ type Getter[T any] interface { GetSubscriptionItemID() *string GetTaxCodeID() string GetTaxBehavior() *productcatalog.TaxBehavior + GetFiatCurrencyCode() *currencyx.Code + GetCustomCurrencyID() *string } -func MapFromDB[T Getter[T]](entity T) meta.Charge { +type EdgeGetter interface { + CustomCurrencyOrErr() (*entdb.CustomCurrency, error) +} + +func FromDBChargeWithCurrencyEdge[T Getter[T]](entity T, edges EdgeGetter) (meta.Charge, error) { + var dbCustomCurrency *entdb.CustomCurrency + if entity.GetCustomCurrencyID() != nil { + var err error + dbCustomCurrency, err = entedge.OrNilIfNotFound(edges.CustomCurrencyOrErr()) + if err != nil { + return meta.Charge{}, fmt.Errorf("failed to get custom currency: %w", err) + } + } + + resolvedCurrency, err := currenciesadapter.MapCustomCurrencyOrFiatCurrencyFromDB(currenciesadapter.CustomCurrencyOrFiatCurrency{ + CustomCurrency: dbCustomCurrency, + FiatCurrency: entity.GetFiatCurrencyCode(), + }) + if err != nil { + return meta.Charge{}, fmt.Errorf("failed to resolve currency: %w", err) + } + + return FromDBCharge(entity, resolvedCurrency) +} + +func FromDBCharge[T Getter[T]](entity T, currency currencies.Currency) (meta.Charge, error) { + if err := currency.Validate(); err != nil { + return meta.Charge{}, fmt.Errorf("currency: %w", err) + } + var subscriptionReference *meta.SubscriptionReference if entity.GetSubscriptionID() != nil && entity.GetSubscriptionPhaseID() != nil && entity.GetSubscriptionItemID() != nil { subscriptionReference = &meta.SubscriptionReference{ @@ -307,7 +283,7 @@ func MapFromDB[T Getter[T]](entity T) meta.Charge { ManagedBy: entity.GetManagedBy(), CustomerID: entity.GetCustomerID(), Annotations: entity.GetAnnotations(), - Currency: entity.GetCurrency(), + Currency: currency, TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: entity.GetTaxCodeID(), Behavior: entity.GetTaxBehavior(), @@ -334,5 +310,5 @@ func MapFromDB[T Getter[T]](entity T) meta.Charge { }, Status: entity.GetStatus(), AdvanceAfter: entity.GetAdvanceAfter(), - } + }, nil } diff --git a/openmeter/billing/charges/service.go b/openmeter/billing/charges/service.go index afb459019d..a49d581db9 100644 --- a/openmeter/billing/charges/service.go +++ b/openmeter/billing/charges/service.go @@ -7,9 +7,7 @@ import ( "time" "github.com/openmeterio/openmeter/openmeter/billing" - "github.com/openmeterio/openmeter/openmeter/billing/charges/creditpurchase" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" - "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/pagination" @@ -19,8 +17,9 @@ import ( type Service interface { ChargeService - // Facade interfaces provide convinience helpers for the API layer. + // Facade interfaces provide convenience helpers for the API layer. CreditPurchaseFacadeService + CustomerChargeAPIService } type ChargeService interface { @@ -39,10 +38,6 @@ type ChargeService interface { ListCharges(ctx context.Context, input ListChargesInput) (pagination.Result[Charge], error) } -type CreditPurchaseFacadeService interface { - HandleCreditPurchaseExternalPaymentStateTransition(ctx context.Context, input HandleCreditPurchaseExternalPaymentStateTransitionInput) (creditpurchase.Charge, error) -} - type CreateInput struct { Namespace string Intents ChargeIntents @@ -112,26 +107,6 @@ func (i GetByIDsInput) Validate() error { return models.NewNillableGenericValidationError(errors.Join(errs...)) } -type HandleCreditPurchaseExternalPaymentStateTransitionInput struct { - ChargeID meta.ChargeID - - TargetPaymentState payment.Status -} - -func (i HandleCreditPurchaseExternalPaymentStateTransitionInput) Validate() error { - var errs []error - - if err := i.ChargeID.Validate(); err != nil { - errs = append(errs, fmt.Errorf("charge ID: %w", err)) - } - - if err := i.TargetPaymentState.Validate(); err != nil { - errs = append(errs, fmt.Errorf("target payment state: %w", err)) - } - - return models.NewNillableGenericValidationError(errors.Join(errs...)) -} - type AdvanceChargesInput struct { Customer customer.CustomerID } diff --git a/openmeter/billing/charges/service/advance.go b/openmeter/billing/charges/service/advance.go index a82200e879..bc4efa6e22 100644 --- a/openmeter/billing/charges/service/advance.go +++ b/openmeter/billing/charges/service/advance.go @@ -11,7 +11,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" - "github.com/openmeterio/openmeter/pkg/currencyx" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/pkg/framework/transaction" ) @@ -114,17 +114,15 @@ func (s *service) AdvanceCharges(ctx context.Context, input charges.AdvanceCharg return advancedCharges, nil } -func collectCurrencies(chargeList charges.Charges) ([]currencyx.Code, error) { - out := make([]currencyx.Code, 0, len(chargeList)) - - for _, c := range chargeList { - currency, err := c.GetCurrency() - if err != nil { - return nil, fmt.Errorf("get charge currency: %w", err) - } - - out = append(out, currency) +func collectCurrencies(chargeList charges.Charges) ([]currencies.Currency, error) { + out, err := lo.MapErr(chargeList, func(c charges.Charge, _ int) (currencies.Currency, error) { + return c.GetCurrency() + }) + if err != nil { + return nil, fmt.Errorf("get currencies: %w", err) } - return lo.Uniq(out), nil + return lo.UniqByErr(out, func(c currencies.Currency) (string, error) { + return c.Identity() + }) } diff --git a/openmeter/billing/charges/service/api.go b/openmeter/billing/charges/service/api.go new file mode 100644 index 0000000000..13d8affae7 --- /dev/null +++ b/openmeter/billing/charges/service/api.go @@ -0,0 +1,66 @@ +package service + +import ( + "context" + "fmt" + + "github.com/openmeterio/openmeter/openmeter/billing" + "github.com/openmeterio/openmeter/openmeter/billing/charges" + "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" + "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + "github.com/openmeterio/openmeter/openmeter/currencies" +) + +func (s *service) CreateCustomerCharge(ctx context.Context, input charges.CreateCustomerChargeInput) (charges.Charge, error) { + if err := input.Validate(); err != nil { + return charges.Charge{}, err + } + + currency, err := s.currencyResolver.ResolveCurrency(ctx, input.Namespace, currencies.CurrencyRef{ + Code: input.CurrencyCode, + }) + if err != nil { + return charges.Charge{}, fmt.Errorf("resolving currency: %w", err) + } + + intent := meta.Intent{ + ManagedBy: billing.ManuallyManagedLine, + CustomerID: input.CustomerID, + Currency: *currency, + TaxConfig: input.TaxConfig, + UniqueReferenceID: input.UniqueReferenceID, + } + + var chargeIntent charges.ChargeIntent + switch { + case input.FlatFee != nil: + chargeIntent = charges.NewChargeIntent(flatfee.Intent{ + Intent: intent, + IntentMutableFields: input.FlatFee.IntentMutableFields, + FeatureKey: input.FlatFee.FeatureKey, + SettlementMode: input.FlatFee.SettlementMode, + }) + case input.UsageBased != nil: + chargeIntent = charges.NewChargeIntent(usagebased.Intent{ + Intent: intent, + IntentMutableFields: input.UsageBased.IntentMutableFields, + FeatureKey: input.UsageBased.FeatureKey, + SettlementMode: input.UsageBased.SettlementMode, + }) + } + + created, err := s.Create(ctx, charges.CreateInput{ + Namespace: input.Namespace, + Intents: charges.ChargeIntents{chargeIntent}, + }) + if err != nil { + return charges.Charge{}, err + } + + if len(created) != 1 { + return charges.Charge{}, fmt.Errorf("expected one created charge, got %d", len(created)) + } + + return created[0], nil +} diff --git a/openmeter/billing/charges/service/base_test.go b/openmeter/billing/charges/service/base_test.go index 0071552082..1653540713 100644 --- a/openmeter/billing/charges/service/base_test.go +++ b/openmeter/billing/charges/service/base_test.go @@ -29,6 +29,7 @@ import ( usagebasedadapter "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased/adapter" usagebasedservice "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased/service" billingratingservice "github.com/openmeterio/openmeter/openmeter/billing/rating/service" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/ledger/recognizer" "github.com/openmeterio/openmeter/openmeter/productcatalog" @@ -262,7 +263,7 @@ func (s *BaseSuite) createMockChargeIntent(input createMockChargeIntentInput) ch ManagedBy: input.managedBy, UniqueReferenceID: lo.EmptyableToPtr(input.uniqueReferenceID), CustomerID: input.customer.ID, - Currency: input.currency, + Currency: currenciestestutils.NewFiatCurrency(s.T(), input.currency), TaxConfig: input.taxConfig, } intentMutableFields := meta.IntentMutableFields{ diff --git a/openmeter/billing/charges/service/create.go b/openmeter/billing/charges/service/create.go index 23c8c7274d..ba2aabd13c 100644 --- a/openmeter/billing/charges/service/create.go +++ b/openmeter/billing/charges/service/create.go @@ -15,6 +15,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/openmeter/taxcode" @@ -388,6 +389,11 @@ func isAdvanceDue(advanceAfter *time.Time) bool { } type currencyAndCustomerID struct { + currency currencies.Currency + customerID customer.CustomerID +} + +type currencyCodeAndCustomerID struct { currency currencyx.Code customerID customer.CustomerID } @@ -439,8 +445,8 @@ func (s *service) createGatheringLines(ctx context.Context, gatheringLinesToCrea return createGatheringLinesResult{}, nil } - gatheringLinesByCurrencyAndCustomer := lo.GroupBy(gatheringLinesToCreate, func(item gatheringLineWithCustomerID) currencyAndCustomerID { - return currencyAndCustomerID{ + gatheringLinesByCurrencyAndCustomer := lo.GroupBy(gatheringLinesToCreate, func(item gatheringLineWithCustomerID) currencyCodeAndCustomerID { + return currencyCodeAndCustomerID{ currency: item.gatheringLine.Currency, customerID: item.customerID, } diff --git a/openmeter/billing/charges/service/creditpurchase_test.go b/openmeter/billing/charges/service/creditpurchase_test.go index 623834ada8..f309a43545 100644 --- a/openmeter/billing/charges/service/creditpurchase_test.go +++ b/openmeter/billing/charges/service/creditpurchase_test.go @@ -20,6 +20,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/creditpurchase" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/pkg/clock" "github.com/openmeterio/openmeter/pkg/currencyx" @@ -223,7 +224,7 @@ func (s *CreditPurchaseTestSuite) TestCreditPurchaseRejectsNonPositiveSettlement Intent: meta.Intent{ ManagedBy: billing.ManuallyManagedLine, CustomerID: cust.ID, - Currency: USD, + Currency: currenciestestutils.NewFiatCurrency(s.T(), USD), }, IntentMutableFields: creditpurchase.IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ @@ -300,7 +301,7 @@ func CreateCreditPurchaseIntent(t *testing.T, input createCreditPurchaseIntentIn Intent: meta.Intent{ ManagedBy: billing.ManuallyManagedLine, CustomerID: input.customer.ID, - Currency: input.currency, + Currency: currenciestestutils.NewFiatCurrency(t, input.currency), }, IntentMutableFields: creditpurchase.IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ diff --git a/openmeter/billing/charges/service/invoicable_test.go b/openmeter/billing/charges/service/invoicable_test.go index e2afc1c77f..9adbd9dcae 100644 --- a/openmeter/billing/charges/service/invoicable_test.go +++ b/openmeter/billing/charges/service/invoicable_test.go @@ -23,6 +23,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" billingtotals "github.com/openmeterio/openmeter/openmeter/billing/models/totals" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/productcatalog" streamingtestutils "github.com/openmeterio/openmeter/openmeter/streaming/testutils" @@ -905,7 +906,7 @@ func (s *InvoicableChargesTestSuite) TestFlatFeeCreditThenInvoiceInvoiceAtBefore ManagedBy: billing.SubscriptionManagedLine, UniqueReferenceID: lo.ToPtr("flat-fee-invoice-at-before-service-period"), CustomerID: cust.ID, - Currency: USD, + Currency: currenciestestutils.NewFiatCurrency(s.T(), USD), }, IntentMutableFields: flatfee.IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ diff --git a/openmeter/billing/charges/service/invoice.go b/openmeter/billing/charges/service/invoice.go index 780f49f695..4250a605ce 100644 --- a/openmeter/billing/charges/service/invoice.go +++ b/openmeter/billing/charges/service/invoice.go @@ -12,6 +12,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/slicesx" @@ -87,7 +88,12 @@ func (s *service) handleStandardInvoiceUpdate(ctx context.Context, invoice billi return err } - return s.recognizeCustomerEarnings(ctx, invoice.CustomerID(), invoice.Currency) + currency, err := currencies.NewFiatCurrency(invoice.Currency) + if err != nil { + return fmt.Errorf("resolving fiat invoice currency %q: %w", invoice.Currency, err) + } + + return s.recognizeCustomerEarnings(ctx, invoice.CustomerID(), currency) } func (s *service) handleChargeEvent(ctx context.Context, invoice billing.StandardInvoice, processorByType processorByType) error { diff --git a/openmeter/billing/charges/service/lineage_test.go b/openmeter/billing/charges/service/lineage_test.go index 2a2c5ae23f..875a840c29 100644 --- a/openmeter/billing/charges/service/lineage_test.go +++ b/openmeter/billing/charges/service/lineage_test.go @@ -24,6 +24,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/creditrealization" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/customer" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" "github.com/openmeterio/openmeter/openmeter/ent/db/creditrealizationlineage" @@ -245,7 +246,7 @@ func (s *CreditRealizationLineageTestSuite) TestBackfillAdvanceLineageSegmentsFi err = service.BackfillAdvanceLineageSegments(ctx, lineage.BackfillAdvanceLineageSegmentsInput{ Namespace: ns, CustomerID: customerID, - Currency: currencyx.Code(currency.USD), + Currency: currenciestestutils.NewFiatCurrency(s.T(), currency.USD), Amount: alpacadecimal.NewFromInt(50), BackingTransactionGroupID: backingTransactionGroupID, FeatureFilters: []string{"api-calls"}, diff --git a/openmeter/billing/charges/service/pendinglines.go b/openmeter/billing/charges/service/pendinglines.go index 958ff7fe9c..a2c1a84e3a 100644 --- a/openmeter/billing/charges/service/pendinglines.go +++ b/openmeter/billing/charges/service/pendinglines.go @@ -13,12 +13,17 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/framework/transaction" "github.com/openmeterio/openmeter/pkg/slicesx" ) +// CreatePendingInvoiceLines creates pending invoice lines for a given input. +// This is the same interface as billing's, but routes the request through the charges service. +// TODO[later]: We need to use the CreateLineRouter instead if possible, so that we don't have this duality (or v3 api will not have +// this method at all) func (s *service) CreatePendingInvoiceLines(ctx context.Context, input charges.CreatePendingInvoiceLinesInput) (*charges.CreatePendingInvoiceLinesResult, error) { for i := range input.Lines { input.Lines[i].Namespace = input.Customer.Namespace @@ -107,10 +112,18 @@ func validateChargePendingInvoiceLinesInput(input charges.CreatePendingInvoiceLi } func mapPendingInvoiceLinesToChargeIntents(input charges.CreatePendingInvoiceLinesInput) (charges.ChargeIntents, error) { + currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). + WithCode(input.Currency). + Build() + if err != nil { + return nil, fmt.Errorf("resolving fiat currency %q: %w", input.Currency, err) + } + resolvedCurrency := currencies.Currency{Currency: currency} + intents := make(charges.ChargeIntents, 0, len(input.Lines)) for idx, line := range input.Lines { - intent, err := mapPendingInvoiceLineToChargeIntent(input.Customer.ID, input.Currency, line) + intent, err := mapPendingInvoiceLineToChargeIntent(input.Customer.ID, resolvedCurrency, line) if err != nil { return nil, fmt.Errorf("line.%d: %w", idx, err) } @@ -121,7 +134,7 @@ func mapPendingInvoiceLinesToChargeIntents(input charges.CreatePendingInvoiceLin return intents, nil } -func mapPendingInvoiceLineToChargeIntent(customerID string, currency currencyx.Code, line billing.GatheringLine) (charges.ChargeIntent, error) { +func mapPendingInvoiceLineToChargeIntent(customerID string, currency currencies.Currency, line billing.GatheringLine) (charges.ChargeIntent, error) { annotations, err := line.Annotations.Clone() if err != nil { return charges.ChargeIntent{}, err diff --git a/openmeter/billing/charges/service/recognition.go b/openmeter/billing/charges/service/recognition.go index 8d07ef740a..c07a7cabd0 100644 --- a/openmeter/billing/charges/service/recognition.go +++ b/openmeter/billing/charges/service/recognition.go @@ -8,20 +8,20 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/ledger/recognizer" "github.com/openmeterio/openmeter/pkg/clock" - "github.com/openmeterio/openmeter/pkg/currencyx" ) -func (s *service) recognizeCustomerEarnings(ctx context.Context, customerID customer.CustomerID, currencies ...currencyx.Code) error { +func (s *service) recognizeCustomerEarnings(ctx context.Context, customerID customer.CustomerID, currencies ...currencies.Currency) error { for _, currency := range lo.Uniq(currencies) { if _, err := s.recognizerService.RecognizeEarnings(ctx, recognizer.RecognizeEarningsInput{ CustomerID: customerID, At: clock.Now(), Currency: currency, }); err != nil { - return fmt.Errorf("recognize earnings for currency %s: %w", currency, err) + return fmt.Errorf("recognize earnings for currency %s: %w", currency.GetCode(), err) } } diff --git a/openmeter/billing/charges/service/service.go b/openmeter/billing/charges/service/service.go index 3af95bb61b..1a9559c4c7 100644 --- a/openmeter/billing/charges/service/service.go +++ b/openmeter/billing/charges/service/service.go @@ -13,6 +13,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/invoiceupdater" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/ledger/recognizer" "github.com/openmeterio/openmeter/openmeter/productcatalog/feature" "github.com/openmeterio/openmeter/openmeter/taxcode" @@ -31,6 +32,7 @@ type service struct { usageBasedService usagebased.Service recognizerService recognizer.Service taxCodeService taxcode.Service + currencyResolver currencies.CurrencyResolver fsNamespaceLockdown []string } @@ -49,7 +51,8 @@ type Config struct { BillingService billing.Service - TaxCodeService taxcode.Service + TaxCodeService taxcode.Service + CurrencyResolver currencies.CurrencyResolver FSNamespaceLockdown []string } @@ -97,6 +100,10 @@ func (c Config) Validate() error { errs = append(errs, errors.New("tax code service cannot be null")) } + if c.CurrencyResolver == nil { + errs = append(errs, errors.New("currency resolver cannot be null")) + } + return errors.Join(errs...) } @@ -124,6 +131,7 @@ func New(config Config) (*service, error) { usageBasedService: config.UsageBasedService, recognizerService: config.RecognizerService, taxCodeService: config.TaxCodeService, + currencyResolver: config.CurrencyResolver, fsNamespaceLockdown: config.FSNamespaceLockdown, } diff --git a/openmeter/billing/charges/service/taxcode_test.go b/openmeter/billing/charges/service/taxcode_test.go index d2e7fc392c..88bec931f6 100644 --- a/openmeter/billing/charges/service/taxcode_test.go +++ b/openmeter/billing/charges/service/taxcode_test.go @@ -20,6 +20,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/creditrealization" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/openmeter/taxcode" "github.com/openmeterio/openmeter/pkg/clock" @@ -265,7 +266,7 @@ func (s *TaxCodePersistenceTestSuite) TestCreditPurchaseChargePersistsTaxConfig( Intent: meta.Intent{ ManagedBy: billing.ManuallyManagedLine, CustomerID: cust.GetID().ID, - Currency: USD, + Currency: currenciestestutils.NewFiatCurrency(s.T(), USD), TaxConfig: productcatalog.TaxCodeConfig{ Behavior: lo.ToPtr(productcatalog.InclusiveTaxBehavior), TaxCodeID: tc.ID, @@ -311,7 +312,7 @@ func (s *TaxCodePersistenceTestSuite) TestCreditPurchaseChargePersistsTaxConfig( Intent: meta.Intent{ ManagedBy: billing.ManuallyManagedLine, CustomerID: cust.GetID().ID, - Currency: USD, + Currency: currenciestestutils.NewFiatCurrency(s.T(), USD), }, IntentMutableFields: creditpurchase.IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ @@ -382,7 +383,7 @@ func (s *TaxCodePersistenceTestSuite) TestCreditPurchaseInvoiceSettlementPropaga Intent: meta.Intent{ ManagedBy: billing.ManuallyManagedLine, CustomerID: cust.GetID().ID, - Currency: USD, + Currency: currenciestestutils.NewFiatCurrency(s.T(), USD), TaxConfig: taxConfig, }, IntentMutableFields: creditpurchase.IntentMutableFields{ @@ -502,7 +503,7 @@ func (s *TaxCodePersistenceTestSuite) TestCreditPurchaseInvoiceSettlementNilTaxC Intent: meta.Intent{ ManagedBy: billing.ManuallyManagedLine, CustomerID: cust.GetID().ID, - Currency: USD, + Currency: currenciestestutils.NewFiatCurrency(s.T(), USD), }, IntentMutableFields: creditpurchase.IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ diff --git a/openmeter/billing/charges/service/truncation_test.go b/openmeter/billing/charges/service/truncation_test.go index 9e50da7f71..696da81b21 100644 --- a/openmeter/billing/charges/service/truncation_test.go +++ b/openmeter/billing/charges/service/truncation_test.go @@ -14,9 +14,9 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/clock" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/datetime" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -54,7 +54,7 @@ func (s *ChargeTimestampTruncationTestSuite) TestCreateTruncatesFlatFeeIntentAnd Intent: meta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: cust.ID, - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(s.T(), "USD"), UniqueReferenceID: lo.ToPtr("flat-fee-truncation"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: defaultTaxCodes.InvoicingTaxCodeID, @@ -127,7 +127,7 @@ func (s *ChargeTimestampTruncationTestSuite) TestUsageBasedAdvanceTruncatesPersi Intent: meta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: cust.ID, - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(s.T(), "USD"), UniqueReferenceID: lo.ToPtr("usage-based-truncation"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: defaultTaxCodes.InvoicingTaxCodeID, diff --git a/openmeter/billing/charges/testutils/service.go b/openmeter/billing/charges/testutils/service.go index 1e9a88e28d..0889bb44d2 100644 --- a/openmeter/billing/charges/testutils/service.go +++ b/openmeter/billing/charges/testutils/service.go @@ -25,6 +25,9 @@ import ( usagebasedadapter "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased/adapter" usagebasedservice "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased/service" billingratingservice "github.com/openmeterio/openmeter/openmeter/billing/rating/service" + currencyadapter "github.com/openmeterio/openmeter/openmeter/currencies/adapter" + "github.com/openmeterio/openmeter/openmeter/currencies/currencyresolver" + currencyservice "github.com/openmeterio/openmeter/openmeter/currencies/service" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" "github.com/openmeterio/openmeter/openmeter/ledger/recognizer" "github.com/openmeterio/openmeter/openmeter/productcatalog/feature" @@ -242,6 +245,23 @@ func NewServices(t testing.TB, config Config) (*Services, error) { return nil, fmt.Errorf("creating charges adapter: %w", err) } + currencyAdapter, err := currencyadapter.New(currencyadapter.Config{ + Client: config.Client, + }) + if err != nil { + return nil, fmt.Errorf("creating currency adapter: %w", err) + } + + currencyService, err := currencyservice.New(currencyAdapter) + if err != nil { + return nil, fmt.Errorf("creating currency service: %w", err) + } + + currencyResolver, err := currencyresolver.New(currencyService) + if err != nil { + return nil, fmt.Errorf("creating currency resolver: %w", err) + } + chargesService, err := chargesservice.New(chargesservice.Config{ Logger: logger, Adapter: rootAdapter, @@ -253,6 +273,7 @@ func NewServices(t testing.TB, config Config) (*Services, error) { RecognizerService: config.RecognizerService, BillingService: config.BillingService, TaxCodeService: config.TaxCodeService, + CurrencyResolver: currencyResolver, }) if err != nil { return nil, fmt.Errorf("creating charges service: %w", err) diff --git a/openmeter/billing/charges/usagebased/adapter/charge.go b/openmeter/billing/charges/usagebased/adapter/charge.go index 4f985afa39..61bbd5ecef 100644 --- a/openmeter/billing/charges/usagebased/adapter/charge.go +++ b/openmeter/billing/charges/usagebased/adapter/charge.go @@ -81,7 +81,7 @@ func (a *adapter) UpdateCharge(ctx context.Context, charge usagebased.ChargeBase dbUpdatedChargeBase.Edges.IntentOverride = intentOverride } - return MapChargeBaseFromDB(dbUpdatedChargeBase), nil + return MapChargeBaseFromDB(dbUpdatedChargeBase, baseIntent.Currency) }) } @@ -110,7 +110,11 @@ func (a *adapter) UpdateSubscriptionItemID(ctx context.Context, charge usagebase } overrideLayer := charge.Intent.GetOverrideLayerMutableFields() - charge.ChargeBase = MapChargeBaseFromDB(updatedChargeBase) + mappedChargeBase, err := MapChargeBaseFromDB(updatedChargeBase, charge.Intent.GetBaseIntent().Currency) + if err != nil { + return usagebased.Charge{}, err + } + charge.ChargeBase = mappedChargeBase charge.Intent = usagebased.NewOverridableIntent(charge.Intent.GetBaseIntent(), overrideLayer) return charge, nil @@ -196,8 +200,8 @@ func (a *adapter) CreateCharges(ctx context.Context, in usagebased.CreateCharges return nil, metaadapter.MapChargeConstraintError(err) } - return slicesx.MapWithErr(entities, func(entity *db.ChargeUsageBased) (usagebased.Charge, error) { - return MapChargeFromDB(entity, meta.ExpandNone) + return lo.MapErr(entities, func(entity *db.ChargeUsageBased, idx int) (usagebased.Charge, error) { + return FromDBChargeUsageBasedWithCurrency(entity, in.Intents[idx].Intent.GetBaseIntent().Currency, meta.ExpandNone) }) }) } @@ -212,7 +216,8 @@ func (a *adapter) GetByIDs(ctx context.Context, input usagebased.GetByIDsInput) // Note: we are skipping the namespace filter here to allow multi-namespace expansions as needed, but InIDOrder filters for namespaces. Where(dbchargeusagebased.Namespace(input.Namespace)). Where(dbchargeusagebased.IDIn(input.IDs...)). - WithIntentOverride() + WithIntentOverride(). + WithCustomCurrency() if input.Expands.Has(meta.ExpandRealizations) { query = expandRealizations(query, input.Expands) @@ -257,7 +262,8 @@ func (a *adapter) GetByID(ctx context.Context, input usagebased.GetByIDInput) (u query := tx.db.ChargeUsageBased.Query(). Where(dbchargeusagebased.Namespace(input.ChargeID.Namespace)). Where(dbchargeusagebased.ID(input.ChargeID.ID)). - WithIntentOverride() + WithIntentOverride(). + WithCustomCurrency() if input.Expands.Has(meta.ExpandRealizations) { query = expandRealizations(query, input.Expands) diff --git a/openmeter/billing/charges/usagebased/adapter/detailedline_test.go b/openmeter/billing/charges/usagebased/adapter/detailedline_test.go index 4e4d335281..e8f57c757e 100644 --- a/openmeter/billing/charges/usagebased/adapter/detailedline_test.go +++ b/openmeter/billing/charges/usagebased/adapter/detailedline_test.go @@ -18,13 +18,13 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" "github.com/openmeterio/openmeter/openmeter/billing/models/stddetailedline" "github.com/openmeterio/openmeter/openmeter/billing/models/totals" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" dbchargeusagebasedrundetailedline "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebasedrundetailedline" dbchargeusagebasedruns "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebasedruns" "github.com/openmeterio/openmeter/openmeter/productcatalog" taxcodetestutils "github.com/openmeterio/openmeter/openmeter/taxcode/testutils" "github.com/openmeterio/openmeter/openmeter/testutils" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -104,7 +104,7 @@ func (s *DetailedLineAdapterSuite) TestUpsertRunDetailedLinesReplacesAndSoftDele ManagedBy: billing.SubscriptionManagedLine, UniqueReferenceID: nil, CustomerID: customerID, - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(s.T(), "USD"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: taxCodeID, }, @@ -465,7 +465,7 @@ func (s *DetailedLineAdapterSuite) createChargeWithRun(namespace string) (usageb ManagedBy: billing.SubscriptionManagedLine, UniqueReferenceID: nil, CustomerID: customerID, - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(s.T(), "USD"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: taxCodeID, }, @@ -548,7 +548,7 @@ func (s *DetailedLineAdapterSuite) newDetailedLine(input newDetailedLineInput) u Description: input.Description, }), ServicePeriod: input.ServicePeriod, - Currency: input.Charge.Intent.GetCurrency(), + Currency: input.Charge.Intent.GetCurrency().GetCode(), ChildUniqueReferenceID: input.ChildUniqueReferenceID, PaymentTerm: productcatalog.InArrearsPaymentTerm, PerUnitAmount: alpacadecimal.NewFromFloat(0.1), diff --git a/openmeter/billing/charges/usagebased/adapter/intentoverride.go b/openmeter/billing/charges/usagebased/adapter/intentoverride.go index f0c5a51a18..3464f28ea3 100644 --- a/openmeter/billing/charges/usagebased/adapter/intentoverride.go +++ b/openmeter/billing/charges/usagebased/adapter/intentoverride.go @@ -74,7 +74,7 @@ func (a *adapter) CreateChargeOverride(ctx context.Context, charge usagebased.Ch dbCharge.Edges.IntentOverride = dbIntentOverride - return MapChargeBaseFromDB(dbCharge), nil + return MapChargeBaseFromDB(dbCharge, charge.Intent.GetBaseIntent().Currency) }) } diff --git a/openmeter/billing/charges/usagebased/adapter/intentoverride_test.go b/openmeter/billing/charges/usagebased/adapter/intentoverride_test.go index 00bfce2be8..21866b0e3b 100644 --- a/openmeter/billing/charges/usagebased/adapter/intentoverride_test.go +++ b/openmeter/billing/charges/usagebased/adapter/intentoverride_test.go @@ -15,12 +15,12 @@ import ( chargesmeta "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" metaadapter "github.com/openmeterio/openmeter/openmeter/billing/charges/meta/adapter" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" "github.com/openmeterio/openmeter/openmeter/productcatalog" taxcodetestutils "github.com/openmeterio/openmeter/openmeter/taxcode/testutils" "github.com/openmeterio/openmeter/openmeter/testutils" "github.com/openmeterio/openmeter/pkg/clock" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -391,7 +391,7 @@ func (s *UsageBasedIntentOverrideAdapterSuite) createCharge(namespace string) us Intent: chargesmeta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: customerID, - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(s.T(), "USD"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: taxCodeID, }, diff --git a/openmeter/billing/charges/usagebased/adapter/mapper.go b/openmeter/billing/charges/usagebased/adapter/mapper.go index 593165de02..feb3a2d444 100644 --- a/openmeter/billing/charges/usagebased/adapter/mapper.go +++ b/openmeter/billing/charges/usagebased/adapter/mapper.go @@ -15,6 +15,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" "github.com/openmeterio/openmeter/openmeter/billing/models/totals" + "github.com/openmeterio/openmeter/openmeter/currencies" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" "github.com/openmeterio/openmeter/pkg/convert" "github.com/openmeterio/openmeter/pkg/framework/entutils" @@ -22,12 +23,27 @@ import ( ) func MapChargeFromDB(entity *entdb.ChargeUsageBased, expands meta.Expands) (usagebased.Charge, error) { - chargeBase := MapChargeBaseFromDB(entity) + chargeMeta, err := chargemeta.FromDBChargeWithCurrencyEdge(entity, entity.Edges) + if err != nil { + return usagebased.Charge{}, fmt.Errorf("mapping usage based charge meta [id=%s]: %w", entity.ID, err) + } + + return mapChargeFromDB(entity, chargeMeta, expands) +} +func FromDBChargeUsageBasedWithCurrency(entity *entdb.ChargeUsageBased, currency currencies.Currency, expands meta.Expands) (usagebased.Charge, error) { + chargeMeta, err := chargemeta.FromDBCharge(entity, currency) + if err != nil { + return usagebased.Charge{}, fmt.Errorf("mapping usage based charge meta [id=%s]: %w", entity.ID, err) + } + + return mapChargeFromDB(entity, chargeMeta, expands) +} + +func mapChargeFromDB(entity *entdb.ChargeUsageBased, chargeMeta meta.Charge, expands meta.Expands) (usagebased.Charge, error) { var realizations usagebased.RealizationRuns if expands.Has(meta.ExpandRealizations) { var err error - realizations, err = MapRealizationRunsFromDB(entity) if err != nil { return usagebased.Charge{}, fmt.Errorf("mapping usage based charge [id=%s]: %w", entity.ID, err) @@ -35,13 +51,21 @@ func MapChargeFromDB(entity *entdb.ChargeUsageBased, expands meta.Expands) (usag } return usagebased.Charge{ - ChargeBase: chargeBase, + ChargeBase: mapChargeBaseFromDB(entity, chargeMeta), Realizations: realizations, }, nil } -func MapChargeBaseFromDB(entity *entdb.ChargeUsageBased) usagebased.ChargeBase { - chargeMeta := chargemeta.MapFromDB(entity) +func MapChargeBaseFromDB(entity *entdb.ChargeUsageBased, currency currencies.Currency) (usagebased.ChargeBase, error) { + chargeMeta, err := chargemeta.FromDBCharge(entity, currency) + if err != nil { + return usagebased.ChargeBase{}, fmt.Errorf("mapping charge meta: %w", err) + } + + return mapChargeBaseFromDB(entity, chargeMeta), nil +} + +func mapChargeBaseFromDB(entity *entdb.ChargeUsageBased, chargeMeta meta.Charge) usagebased.ChargeBase { intent := usagebased.Intent{ Intent: chargeMeta.Intent, FeatureKey: entity.FeatureKey, diff --git a/openmeter/billing/charges/usagebased/charge.go b/openmeter/billing/charges/usagebased/charge.go index d9dfabb886..fdf9b04b01 100644 --- a/openmeter/billing/charges/usagebased/charge.go +++ b/openmeter/billing/charges/usagebased/charge.go @@ -10,10 +10,10 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/models/totals" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/openmeter/productcatalog/feature" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/ref" "github.com/openmeterio/openmeter/pkg/timeutil" @@ -66,7 +66,7 @@ func (c ChargeBase) GetCustomerID() customer.CustomerID { } } -func (c ChargeBase) GetCurrency() currencyx.Code { +func (c ChargeBase) GetCurrency() currencies.Currency { return c.Intent.GetCurrency() } @@ -275,7 +275,7 @@ func (i OverridableIntent) GetCustomerID() string { return i.intent.CustomerID } -func (i OverridableIntent) GetCurrency() currencyx.Code { +func (i OverridableIntent) GetCurrency() currencies.Currency { return i.intent.Currency } diff --git a/openmeter/billing/charges/usagebased/detailedline.go b/openmeter/billing/charges/usagebased/detailedline.go index cb37fa8b32..e68da96f37 100644 --- a/openmeter/billing/charges/usagebased/detailedline.go +++ b/openmeter/billing/charges/usagebased/detailedline.go @@ -77,7 +77,7 @@ func NewDetailedLinesFromBilling( }), ServicePeriod: period, Index: lo.ToPtr(idx), - Currency: intent.Currency, + Currency: intent.Intent.Currency.GetCode(), ChildUniqueReferenceID: line.ChildUniqueReferenceID, PaymentTerm: lo.CoalesceOrEmpty(line.PaymentTerm, productcatalog.InArrearsPaymentTerm), PerUnitAmount: line.PerUnitAmount, diff --git a/openmeter/billing/charges/usagebased/rating.go b/openmeter/billing/charges/usagebased/rating.go index 9b50cbd61a..5ccc119449 100644 --- a/openmeter/billing/charges/usagebased/rating.go +++ b/openmeter/billing/charges/usagebased/rating.go @@ -41,8 +41,8 @@ func (r RateableIntent) GetFeatureKey() string { return r.FeatureKey } -func (r RateableIntent) GetCurrency() currencyx.Code { - return r.Currency +func (r RateableIntent) GetCurrencyCalculator() (currencyx.Currency, error) { + return r.Intent.Intent.Currency, nil } func (r RateableIntent) GetName() string { diff --git a/openmeter/billing/charges/usagebased/service/create.go b/openmeter/billing/charges/usagebased/service/create.go index c317bf0562..4c9d5791eb 100644 --- a/openmeter/billing/charges/usagebased/service/create.go +++ b/openmeter/billing/charges/usagebased/service/create.go @@ -28,6 +28,10 @@ func (s *service) Create(ctx context.Context, input usagebased.CreateInput) ([]u return transaction.Run(ctx, s.adapter, func(ctx context.Context) ([]usagebased.ChargeWithGatheringLine, error) { createIntents, err := slicesx.MapWithErr(input.Intents, func(intent usagebased.Intent) (usagebased.CreateIntent, error) { + if intent.Currency.IsCustom() { + return usagebased.CreateIntent{}, fmt.Errorf("creating usage based charge with custom currency %q: %w", intent.Currency.GetCode(), meta.ErrCustomCurrencyNotSupported) + } + chargeIntent := intent.Normalized() featureMeter, err := input.FeatureMeters.Get(chargeIntent.FeatureKey, false) @@ -124,7 +128,7 @@ func gatheringLineFromUsageBasedChargeForPeriod(charge usagebased.Charge, servic FeatureKey: intent.FeatureKey, UnitConfig: unitConfig, - Currency: intent.Currency, + Currency: intent.Currency.GetCode(), ServicePeriod: servicePeriod, InvoiceAt: invoiceAt, diff --git a/openmeter/billing/charges/usagebased/service/creditheninvoice_test.go b/openmeter/billing/charges/usagebased/service/creditheninvoice_test.go index cc9bc2a67f..3d5259dbf0 100644 --- a/openmeter/billing/charges/usagebased/service/creditheninvoice_test.go +++ b/openmeter/billing/charges/usagebased/service/creditheninvoice_test.go @@ -14,8 +14,8 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/invoicedusage" chargestatemachine "github.com/openmeterio/openmeter/openmeter/billing/charges/statemachine" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/productcatalog" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -152,7 +152,7 @@ func TestShrinkChargeKeepsCurrentRunStateWhenCurrentRunSurvivesShrink(t *testing NamespacedModel: models.NamespacedModel{Namespace: "namespace"}, ID: "charge-id", }, - Intent: newUsageBasedIntentForCreditThenInvoiceTest(servicePeriod), + Intent: newUsageBasedIntentForCreditThenInvoiceTest(t, servicePeriod), Status: usagebased.StatusActiveRealizationProcessing, State: usagebased.State{ CurrentRealizationRunID: ¤tRunID, @@ -197,7 +197,7 @@ func TestExtendChargeDeletesPendingGatheringLineWhenRunsCoverExtendedPeriod(t *t NamespacedModel: models.NamespacedModel{Namespace: "namespace"}, ID: "charge-id", }, - Intent: newUsageBasedIntentForCreditThenInvoiceTest(servicePeriod), + Intent: newUsageBasedIntentForCreditThenInvoiceTest(t, servicePeriod), Status: usagebased.StatusActive, }, Realizations: usagebased.RealizationRuns{ @@ -232,7 +232,7 @@ func TestShrinkChargeMovesToAwaitingPaymentWhenKeptRunCoversNewEnd(t *testing.T) NamespacedModel: models.NamespacedModel{Namespace: "namespace"}, ID: "charge-id", }, - Intent: newUsageBasedIntentForCreditThenInvoiceTest(servicePeriod), + Intent: newUsageBasedIntentForCreditThenInvoiceTest(t, servicePeriod), Status: usagebased.StatusActive, State: usagebased.State{ AdvanceAfter: ¤tAdvanceAfter, @@ -279,7 +279,7 @@ func TestShrinkChargeMovesToFinalWhenKeptRunCoversNewEndAndSettlementIsComplete( NamespacedModel: models.NamespacedModel{Namespace: "namespace"}, ID: "charge-id", }, - Intent: newUsageBasedIntentForCreditThenInvoiceTest(servicePeriod), + Intent: newUsageBasedIntentForCreditThenInvoiceTest(t, servicePeriod), Status: usagebased.StatusActiveAwaitingPaymentSettlement, State: usagebased.State{ AdvanceAfter: ¤tAdvanceAfter, @@ -316,7 +316,7 @@ func TestShrinkToRealizedPeriodFinalizesKeptPartialRunAndPreservesChargeState(t NamespacedModel: models.NamespacedModel{Namespace: "namespace"}, ID: "charge-id", }, - Intent: newUsageBasedIntentForCreditThenInvoiceTest(servicePeriod), + Intent: newUsageBasedIntentForCreditThenInvoiceTest(t, servicePeriod), Status: usagebased.StatusActive, State: usagebased.State{ AdvanceAfter: ¤tAdvanceAfter, @@ -367,7 +367,7 @@ func TestShrinkToRealizedPeriodRejectsPeriodNotCoveredByLatest(t *testing.T) { NamespacedModel: models.NamespacedModel{Namespace: "namespace"}, ID: "charge-id", }, - Intent: newUsageBasedIntentForCreditThenInvoiceTest(servicePeriod), + Intent: newUsageBasedIntentForCreditThenInvoiceTest(t, servicePeriod), Status: usagebased.StatusActive, }, Realizations: usagebased.RealizationRuns{ @@ -396,7 +396,7 @@ func TestShrinkToRealizedPeriodFinalizesCurrentPartialRunAndPreservesChargeState NamespacedModel: models.NamespacedModel{Namespace: "namespace"}, ID: "charge-id", }, - Intent: newUsageBasedIntentForCreditThenInvoiceTest(servicePeriod), + Intent: newUsageBasedIntentForCreditThenInvoiceTest(t, servicePeriod), Status: usagebased.StatusActiveRealizationProcessing, State: usagebased.State{ CurrentRealizationRunID: ¤tRunID, @@ -471,12 +471,14 @@ func newCreditThenInvoiceStateMachineForTest(t *testing.T, status usagebased.Sta return out } -func newUsageBasedIntentForCreditThenInvoiceTest(servicePeriod timeutil.ClosedPeriod) usagebased.OverridableIntent { +func newUsageBasedIntentForCreditThenInvoiceTest(t testing.TB, servicePeriod timeutil.ClosedPeriod) usagebased.OverridableIntent { + t.Helper() + return usagebased.Intent{ Intent: meta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: "customer-id", - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: "tax-code-id", }, @@ -648,7 +650,7 @@ func TestGetInvoiceRealizationRunType(t *testing.T) { charge := usagebased.Charge{ ChargeBase: usagebased.ChargeBase{ - Intent: newUsageBasedIntentForCreditThenInvoiceTest(servicePeriod), + Intent: newUsageBasedIntentForCreditThenInvoiceTest(t, servicePeriod), }, } diff --git a/openmeter/billing/charges/usagebased/service/creditsonly_test.go b/openmeter/billing/charges/usagebased/service/creditsonly_test.go index bc51e7362b..b9fcf7182a 100644 --- a/openmeter/billing/charges/usagebased/service/creditsonly_test.go +++ b/openmeter/billing/charges/usagebased/service/creditsonly_test.go @@ -16,6 +16,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" usagebasedrating "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased/service/rating" usagebasedrun "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased/service/run" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" @@ -87,7 +88,7 @@ func TestCreditsOnlyPeriodPatchWhileCreatedUpdatesIntentAndKeepsCreatedSchedule( machine := newCreditsOnlyStateMachineWithChargeForTest(t, usagebased.Charge{ ChargeBase: usagebased.ChargeBase{ ManagedResource: newUsageBasedChargeTestManagedResource("charge-id"), - Intent: newUsageBasedIntentForCreditOnlyTest(servicePeriod), + Intent: newUsageBasedIntentForCreditOnlyTest(t, servicePeriod), Status: usagebased.StatusCreated, State: usagebased.State{ FeatureID: "feature-id", @@ -132,7 +133,7 @@ func TestCreditsOnlyExtendWhileFinalRealizationInProgressVoidsCurrentRunAndMoves machine := newCreditsOnlyStateMachineWithChargeForTest(t, usagebased.Charge{ ChargeBase: usagebased.ChargeBase{ ManagedResource: newUsageBasedChargeTestManagedResource("charge-id"), - Intent: newUsageBasedIntentForCreditOnlyTest(servicePeriod), + Intent: newUsageBasedIntentForCreditOnlyTest(t, servicePeriod), Status: status, State: usagebased.State{ CurrentRealizationRunID: ¤tRunID, @@ -173,7 +174,7 @@ func TestCreditsOnlyShrinkWhileCompletedVoidsRunBeyondNewEndAndMovesActive(t *te machine := newCreditsOnlyStateMachineWithChargeForTest(t, usagebased.Charge{ ChargeBase: usagebased.ChargeBase{ ManagedResource: newUsageBasedChargeTestManagedResource("charge-id"), - Intent: newUsageBasedIntentForCreditOnlyTest(servicePeriod), + Intent: newUsageBasedIntentForCreditOnlyTest(t, servicePeriod), Status: usagebased.StatusActiveRealizationCompleted, State: usagebased.State{ FeatureID: "feature-id", @@ -211,7 +212,7 @@ func TestCreditsOnlyExtendWhileCompletedVoidsRunAndMovesActive(t *testing.T) { machine := newCreditsOnlyStateMachineWithChargeForTest(t, usagebased.Charge{ ChargeBase: usagebased.ChargeBase{ ManagedResource: newUsageBasedChargeTestManagedResource("charge-id"), - Intent: newUsageBasedIntentForCreditOnlyTest(servicePeriod), + Intent: newUsageBasedIntentForCreditOnlyTest(t, servicePeriod), Status: usagebased.StatusActiveRealizationCompleted, State: usagebased.State{ FeatureID: "feature-id", @@ -239,12 +240,14 @@ func TestCreditsOnlyExtendWhileCompletedVoidsRunAndMovesActive(t *testing.T) { require.NotNil(t, run.DeletedAt) } -func newUsageBasedIntentForCreditOnlyTest(servicePeriod timeutil.ClosedPeriod) usagebased.OverridableIntent { +func newUsageBasedIntentForCreditOnlyTest(t testing.TB, servicePeriod timeutil.ClosedPeriod) usagebased.OverridableIntent { + t.Helper() + return usagebased.Intent{ Intent: meta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: "customer-id", - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: "tax-code-id", }, @@ -277,7 +280,7 @@ func newCreditsOnlyStateMachineForTest(t *testing.T, status usagebased.Status) * return newCreditsOnlyStateMachineWithChargeForTest(t, usagebased.Charge{ ChargeBase: usagebased.ChargeBase{ ManagedResource: newUsageBasedChargeTestManagedResource("charge-id"), - Intent: newUsageBasedIntentForCreditOnlyTest(servicePeriod), + Intent: newUsageBasedIntentForCreditOnlyTest(t, servicePeriod), Status: status, State: usagebased.State{ FeatureID: "feature-id", diff --git a/openmeter/billing/charges/usagebased/service/lineengine.go b/openmeter/billing/charges/usagebased/service/lineengine.go index e688f7e6d3..cef78fc813 100644 --- a/openmeter/billing/charges/usagebased/service/lineengine.go +++ b/openmeter/billing/charges/usagebased/service/lineengine.go @@ -16,7 +16,6 @@ import ( "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/openmeter/streaming" "github.com/openmeterio/openmeter/pkg/clock" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/ref" "github.com/openmeterio/openmeter/pkg/slicesx" @@ -804,19 +803,12 @@ func (e *LineEngine) deleteMutableStandardLineRealization( return usagebased.Charge{}, fmt.Errorf("usage based standard line[%s] cannot be deleted because realization run[%s] has invoice accrued allocation", stdLine.ID, run.ID.ID) } - cur, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(charge.Intent.GetCurrency()). - Build() - if err != nil { - return usagebased.Charge{}, fmt.Errorf("getting currency calculator for charge[%s]: %w", charge.ID, err) - } - now := clock.Now() if _, err := e.service.runs.CorrectAllCredits(ctx, usagebasedrun.CorrectAllCreditRealizationsInput{ Charge: charge, Run: run, AllocateAt: run.ServicePeriodTo, - CurrencyCalculator: cur, + CurrencyCalculator: charge.Intent.GetCurrency(), }); err != nil { return usagebased.Charge{}, fmt.Errorf("correcting credits for deleted usage based standard line[%s] run[%s]: %w", stdLine.ID, run.ID.ID, err) } diff --git a/openmeter/billing/charges/usagebased/service/linemapper.go b/openmeter/billing/charges/usagebased/service/linemapper.go index f8b2d084c5..ef6164f473 100644 --- a/openmeter/billing/charges/usagebased/service/linemapper.go +++ b/openmeter/billing/charges/usagebased/service/linemapper.go @@ -12,6 +12,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" billingrating "github.com/openmeterio/openmeter/openmeter/billing/rating" "github.com/openmeterio/openmeter/openmeter/billing/rating/service/mutator" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/currencyx" ) @@ -34,6 +35,13 @@ func intentFromManualCreatedLine( return usagebased.Intent{}, fmt.Errorf("line id is required") } + currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). + WithCode(line.GetCurrency()). + Build() + if err != nil { + return usagebased.Intent{}, fmt.Errorf("resolving fiat currency %q: %w", line.GetCurrency(), err) + } + if chargeID := line.GetChargeID(); chargeID != nil && *chargeID != "" { return usagebased.Intent{}, fmt.Errorf("line[%s]: charge id must be empty for manual create", line.GetID()) } @@ -73,7 +81,7 @@ func intentFromManualCreatedLine( ManagedBy: billing.ManuallyManagedLine, CustomerID: invoice.GetCustomerID().ID, Annotations: annotations, - Currency: line.GetCurrency(), + Currency: currencies.Currency{Currency: currency}, TaxConfig: taxConfig, }, IntentMutableFields: usagebased.IntentMutableFields{ diff --git a/openmeter/billing/charges/usagebased/service/rating/service_test.go b/openmeter/billing/charges/usagebased/service/rating/service_test.go index 902dbfea8d..6f6d9e35bd 100644 --- a/openmeter/billing/charges/usagebased/service/rating/service_test.go +++ b/openmeter/billing/charges/usagebased/service/rating/service_test.go @@ -19,6 +19,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/models/totals" billingrating "github.com/openmeterio/openmeter/openmeter/billing/rating" billingratingservice "github.com/openmeterio/openmeter/openmeter/billing/rating/service" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/meter" "github.com/openmeterio/openmeter/openmeter/productcatalog" @@ -86,7 +87,7 @@ func TestNewDetailedLinesFromBilling(t *testing.T) { From: time.Date(2025, 1, 15, 0, 0, 0, 0, time.UTC), To: time.Date(2025, 2, 1, 0, 0, 0, 0, time.UTC), } - intent := newDetailedRatingTestCharge(defaultServicePeriod, nil).Intent + intent := newDetailedRatingTestCharge(t, defaultServicePeriod, nil).Intent out := usagebased.NewDetailedLinesFromBilling( intent.GetEffectiveIntent(), @@ -187,7 +188,7 @@ func TestGetDetailedRatingForUsageUsesPeriodPreservingRatingEngine(t *testing.T) }, }) - charge := newDetailedRatingTestCharge(servicePeriod, usagebased.RealizationRuns{priorRun}) + charge := newDetailedRatingTestCharge(t, servicePeriod, usagebased.RealizationRuns{priorRun}) charge.State.RatingEngine = usagebased.RatingEnginePeriodPreserving svc, err := New(Config{ @@ -346,7 +347,7 @@ func TestGetDetailedRatingForUsageFiltersQuantityByServicePeriodToAndStoredAtLT( require.NoError(t, err) out, err := svc.GetDetailedRatingForUsage(t.Context(), GetDetailedRatingForUsageInput{ - Charge: newDetailedRatingTestCharge(servicePeriod, usagebased.RealizationRuns{}), + Charge: newDetailedRatingTestCharge(t, servicePeriod, usagebased.RealizationRuns{}), ServicePeriodTo: servicePeriodTo, StoredAtLT: storedAtLT, Customer: newDetailedRatingTestCustomer(), @@ -396,7 +397,7 @@ func TestGetTotalsForUsageMinimumCommitment(t *testing.T) { }) require.NoError(t, err) - charge := newDetailedRatingTestCharge(servicePeriod, usagebased.RealizationRuns{}) + charge := newDetailedRatingTestCharge(t, servicePeriod, usagebased.RealizationRuns{}) err = charge.Intent.MutateEffective(func(fields *usagebased.IntentMutableFields) error { fields.Price = *productcatalog.NewPriceFrom(productcatalog.UnitPrice{ Amount: alpacadecimal.NewFromInt(3), @@ -446,7 +447,7 @@ func newGetDetailedRatingForUsageFixture(t *testing.T, result billingrating.Gene DetailedLinesFetcher: passthroughDetailedLinesFetcher, }, input: GetDetailedRatingForUsageInput{ - Charge: newDetailedRatingTestCharge(servicePeriod, usagebased.RealizationRuns{}), + Charge: newDetailedRatingTestCharge(t, servicePeriod, usagebased.RealizationRuns{}), ServicePeriodTo: servicePeriod.To, StoredAtLT: currentRun.StoredAtLT, Customer: newDetailedRatingTestCustomer(), @@ -479,7 +480,9 @@ func newDetailedRatingTestRun(id string, servicePeriodTo time.Time, meteredQuant } } -func newDetailedRatingTestCharge(period timeutil.ClosedPeriod, runs usagebased.RealizationRuns) usagebased.Charge { +func newDetailedRatingTestCharge(t testing.TB, period timeutil.ClosedPeriod, runs usagebased.RealizationRuns) usagebased.Charge { + t.Helper() + return usagebased.Charge{ ChargeBase: usagebased.ChargeBase{ ManagedResource: chargesmeta.ManagedResource{ @@ -494,7 +497,7 @@ func newDetailedRatingTestCharge(period timeutil.ClosedPeriod, runs usagebased.R Intent: chargesmeta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: "customer-1", - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: "tax-code-id", }, diff --git a/openmeter/billing/charges/usagebased/service/rating/subtract/subtract_test.go b/openmeter/billing/charges/usagebased/service/rating/subtract/subtract_test.go index 9ad157c9f3..ad11c4c2c7 100644 --- a/openmeter/billing/charges/usagebased/service/rating/subtract/subtract_test.go +++ b/openmeter/billing/charges/usagebased/service/rating/subtract/subtract_test.go @@ -14,6 +14,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/models/totals" billingrating "github.com/openmeterio/openmeter/openmeter/billing/rating" billingratingservice "github.com/openmeterio/openmeter/openmeter/billing/rating/service" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" @@ -1383,7 +1384,7 @@ func rateRunDetailsForTest(t *testing.T, ratingService billingrating.Service, in intent := usagebased.RateableIntent{ Intent: usagebased.Intent{ Intent: meta.Intent{ - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), }, IntentMutableFields: usagebased.IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ diff --git a/openmeter/billing/charges/usagebased/service/rating/testutils/testutils.go b/openmeter/billing/charges/usagebased/service/rating/testutils/testutils.go index e3bda49c70..ad4c7955f6 100644 --- a/openmeter/billing/charges/usagebased/service/rating/testutils/testutils.go +++ b/openmeter/billing/charges/usagebased/service/rating/testutils/testutils.go @@ -14,8 +14,8 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" "github.com/openmeterio/openmeter/openmeter/billing/models/stddetailedline" "github.com/openmeterio/openmeter/openmeter/billing/models/totals" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/productcatalog" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -83,7 +83,7 @@ func NewIntentForTest(t testing.TB, servicePeriod timeutil.ClosedPeriod, price p Intent: chargesmeta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: "customer-1", - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: "tax-code-id", }, diff --git a/openmeter/billing/charges/usagebased/service/run/payment_test.go b/openmeter/billing/charges/usagebased/service/run/payment_test.go index 4b1a4d0221..62b2c344a5 100644 --- a/openmeter/billing/charges/usagebased/service/run/payment_test.go +++ b/openmeter/billing/charges/usagebased/service/run/payment_test.go @@ -15,6 +15,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" "github.com/openmeterio/openmeter/openmeter/billing/models/totals" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" @@ -22,11 +23,11 @@ import ( ) func TestBookInvoicedPaymentAuthorizedInputValidate(t *testing.T) { - valid := newBookPaymentAuthorizedInput() + valid := newBookPaymentAuthorizedInput(t) require.NoError(t, valid.Validate()) t.Run("rejects existing payment", func(t *testing.T) { - in := newBookPaymentAuthorizedInput() + in := newBookPaymentAuthorizedInput(t) in.Run.Payment = &payment.Invoiced{ Payment: payment.Payment{ NamespacedID: models.NamespacedID{Namespace: in.Charge.Namespace, ID: "payment-1"}, @@ -50,7 +51,7 @@ func TestBookInvoicedPaymentAuthorizedInputValidate(t *testing.T) { }) t.Run("rejects mismatched line id", func(t *testing.T) { - in := newBookPaymentAuthorizedInput() + in := newBookPaymentAuthorizedInput(t) other := "other-line" in.Run.LineID = &other require.ErrorContains(t, in.Validate(), "already linked to a different line") @@ -58,30 +59,30 @@ func TestBookInvoicedPaymentAuthorizedInputValidate(t *testing.T) { } func TestSettleInvoicedPaymentInputValidate(t *testing.T) { - valid := newSettlePaymentInput() + valid := newSettlePaymentInput(t) require.NoError(t, valid.Validate()) t.Run("rejects missing payment", func(t *testing.T) { - in := newSettlePaymentInput() + in := newSettlePaymentInput(t) in.Run.Payment = nil require.ErrorContains(t, in.Validate(), "cannot settle an unauthorized payment") }) t.Run("allows missing payment when no fiat transaction is required", func(t *testing.T) { - in := newSettlePaymentInput() + in := newSettlePaymentInput(t) in.Run.Payment = nil in.Run.NoFiatTransactionRequired = true require.NoError(t, in.Validate()) }) t.Run("rejects mismatched payment line id", func(t *testing.T) { - in := newSettlePaymentInput() + in := newSettlePaymentInput(t) in.Run.Payment.LineID = "other-line" require.ErrorContains(t, in.Validate(), "payment line ID does not match") }) t.Run("rejects non authorized payment status", func(t *testing.T) { - in := newSettlePaymentInput() + in := newSettlePaymentInput(t) in.Run.Payment.Status = payment.StatusSettled in.Run.Payment.Settled = &ledgertransaction.TimedGroupReference{ GroupReference: ledgertransaction.GroupReference{ @@ -93,11 +94,13 @@ func TestSettleInvoicedPaymentInputValidate(t *testing.T) { }) } -func newBookPaymentAuthorizedInput() BookInvoicedPaymentAuthorizedInput { +func newBookPaymentAuthorizedInput(t testing.TB) BookInvoicedPaymentAuthorizedInput { + t.Helper() + lineID := "line-1" now := time.Now().UTC() return BookInvoicedPaymentAuthorizedInput{ - Charge: newUsageBasedCharge(), + Charge: newUsageBasedCharge(t), Run: newUsageBasedRun(lineID), Invoice: billing.StandardInvoice{ StandardInvoiceBase: billing.StandardInvoiceBase{ @@ -136,8 +139,10 @@ func newBookPaymentAuthorizedInput() BookInvoicedPaymentAuthorizedInput { } } -func newSettlePaymentInput() SettleInvoicedPaymentInput { - authInput := newBookPaymentAuthorizedInput() +func newSettlePaymentInput(t testing.TB) SettleInvoicedPaymentInput { + t.Helper() + + authInput := newBookPaymentAuthorizedInput(t) authInput.Run.InvoiceUsage = &invoicedusage.AccruedUsage{ ServicePeriod: authInput.Line.Period, Totals: authInput.Line.Totals, @@ -165,7 +170,9 @@ func newSettlePaymentInput() SettleInvoicedPaymentInput { return SettleInvoicedPaymentInput(authInput) } -func newUsageBasedCharge() usagebased.Charge { +func newUsageBasedCharge(t testing.TB) usagebased.Charge { + t.Helper() + now := time.Now().UTC() period := timeutil.ClosedPeriod{From: now.Add(-2 * time.Hour), To: now.Add(-time.Hour)} @@ -180,7 +187,7 @@ func newUsageBasedCharge() usagebased.Charge { Intent: meta.Intent{ ManagedBy: billing.SystemManagedLine, CustomerID: "cust-1", - Currency: currencyx.Code("USD"), + Currency: currenciestestutils.NewFiatCurrency(t, "USD"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: "tax-code-id", }, diff --git a/openmeter/billing/charges/usagebased/service/triggers.go b/openmeter/billing/charges/usagebased/service/triggers.go index 5aec528e4b..651fb99609 100644 --- a/openmeter/billing/charges/usagebased/service/triggers.go +++ b/openmeter/billing/charges/usagebased/service/triggers.go @@ -11,7 +11,6 @@ import ( "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/clock" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/framework/transaction" "github.com/openmeterio/openmeter/pkg/models" ) @@ -27,13 +26,6 @@ func (s *service) AdvanceCharge(ctx context.Context, input usagebased.AdvanceCha return nil, fmt.Errorf("get feature meter: %w", err) } - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(charge.Intent.GetCurrency()). - Build() - if err != nil { - return nil, fmt.Errorf("get currency calculator: %w", err) - } - stateMachine, err := s.newStateMachine(StateMachineConfig{ Charge: charge, Adapter: s.adapter, @@ -41,7 +33,7 @@ func (s *service) AdvanceCharge(ctx context.Context, input usagebased.AdvanceCha Runs: s.runs, CustomerOverride: input.CustomerOverride, FeatureMeter: featureMeter, - CurrencyCalculator: currency, + CurrencyCalculator: charge.Intent.GetCurrency(), }) if err != nil { return nil, fmt.Errorf("new state machine: %w", err) @@ -228,12 +220,7 @@ func (s *service) getStateMachineConfigForCharge(ctx context.Context, charge usa return StateMachineConfig{}, err } - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(charge.Intent.GetCurrency()). - Build() - if err != nil { - return StateMachineConfig{}, fmt.Errorf("get currency calculator: %w", err) - } + currency := charge.Intent.GetCurrency() return StateMachineConfig{ Charge: charge, diff --git a/openmeter/billing/charges/usagebased/service/triggers_test.go b/openmeter/billing/charges/usagebased/service/triggers_test.go index 0b27f7fbde..7fcf73105e 100644 --- a/openmeter/billing/charges/usagebased/service/triggers_test.go +++ b/openmeter/billing/charges/usagebased/service/triggers_test.go @@ -21,7 +21,7 @@ func TestApplyBaseIntentPatchForOverriddenChargeShrinksDeletedEffectiveCharge(t shrunkServicePeriodTo := time.Date(2026, 7, 8, 17, 18, 9, 0, time.UTC) deletedAt := time.Date(2026, 7, 8, 17, 17, 50, 0, time.UTC) - baseIntent := newUsageBasedIntentForCreditThenInvoiceTest(baseServicePeriod) + baseIntent := newUsageBasedIntentForCreditThenInvoiceTest(t, baseServicePeriod) override := baseIntent.GetBaseIntent().IntentMutableFields.Clone() override.IntentDeletedAt = &deletedAt @@ -68,7 +68,7 @@ func TestRejectHiddenIntentTargetRejectsBaseLayerWithOverride(t *testing.T) { From: time.Date(2026, 7, 8, 8, 45, 3, 0, time.UTC), To: time.Date(2026, 8, 8, 8, 45, 3, 0, time.UTC), } - baseIntent := newUsageBasedIntentForCreditThenInvoiceTest(servicePeriod) + baseIntent := newUsageBasedIntentForCreditThenInvoiceTest(t, servicePeriod) override := baseIntent.GetBaseIntent().IntentMutableFields.Clone() machine := newCreditThenInvoiceStateMachineWithChargeForTest(t, usagebased.Charge{ diff --git a/openmeter/billing/creditgrant/service/service.go b/openmeter/billing/creditgrant/service/service.go index 4f66ade878..db40234691 100644 --- a/openmeter/billing/creditgrant/service/service.go +++ b/openmeter/billing/creditgrant/service/service.go @@ -17,6 +17,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/creditgrant" customerbilling "github.com/openmeterio/openmeter/openmeter/billing/validators/customerbilling" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/ledger" "github.com/openmeterio/openmeter/openmeter/ledger/creditvoid" @@ -129,7 +130,10 @@ func (s *service) Create(ctx context.Context, input creditgrant.CreateInput) (cr } // Build the credit purchase intent - intent := toIntent(input) + intent, err := toIntent(input) + if err != nil { + return creditpurchase.Charge{}, fmt.Errorf("build credit grant intent: %w", err) + } result, err := s.chargesService.Create(ctx, charges.CreateInput{ Namespace: input.Namespace, @@ -285,6 +289,10 @@ func (s *service) Void(ctx context.Context, input creditgrant.VoidInput) (credit return creditpurchase.Charge{}, err } + if charge.Intent.Currency.IsCustom() { + return creditpurchase.Charge{}, fmt.Errorf("credit grant with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + if charge.State.VoidedAt != nil { return charge, nil } @@ -300,7 +308,7 @@ func (s *service) Void(ctx context.Context, input creditgrant.VoidInput) (credit ID: input.CustomerID, }, ChargeID: charge.ID, - Currency: charge.Intent.Currency, + Currency: charge.Intent.Currency.GetCode(), Annotations: ledger.ChargeAnnotations(models.NamespacedID{ Namespace: charge.Namespace, ID: charge.ID, @@ -351,14 +359,20 @@ func validateChargeVoidable(charge creditpurchase.Charge) error { return nil } -func toIntent(input creditgrant.CreateInput) creditpurchase.Intent { +func toIntent(input creditgrant.CreateInput) (creditpurchase.Intent, error) { effectiveAt := lo.FromPtrOr(input.EffectiveAt, clock.Now()).UTC() period := timeutil.ClosedPeriod{From: effectiveAt, To: effectiveAt} + currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). + WithCode(input.Currency). + Build() + if err != nil { + return creditpurchase.Intent{}, fmt.Errorf("build fiat currency: %w", err) + } intent := creditpurchase.Intent{ Intent: meta.Intent{ CustomerID: input.CustomerID, - Currency: input.Currency, + Currency: currencies.Currency{Currency: currency}, ManagedBy: billing.ManuallyManagedLine, TaxConfig: productcatalog.TaxCodeConfigFrom(input.TaxConfig), }, @@ -389,7 +403,7 @@ func toIntent(input creditgrant.CreateInput) creditpurchase.Intent { intent.Priority = &p } - return intent + return intent, nil } func calculateExpiresAt(from time.Time, expiresAfter *datetime.ISODuration) *time.Time { diff --git a/openmeter/billing/rating/line.go b/openmeter/billing/rating/line.go index de7caf0973..92696f419f 100644 --- a/openmeter/billing/rating/line.go +++ b/openmeter/billing/rating/line.go @@ -27,8 +27,10 @@ type GatheringLineAccessor interface { type StandardLineAccessor interface { PriceAccessor - // GetCurrency returns the currency of the line - GetCurrency() currencyx.Code + // TODO: Drop the error return once billing has migrated to the new currency engine + // and no longer needs to construct fiat currency calculators from stored codes. + // GetCurrencyCalculator returns the currency calculator of the line. + GetCurrencyCalculator() (currencyx.Currency, error) // GetMeteredUsage returns the metered usage of the line for the current service period GetMeteredQuantity() (*alpacadecimal.Decimal, error) // GetMeteredPreLinePeriodUsage returns the metered usage of the line for the previous service period diff --git a/openmeter/billing/rating/service/detailedline.go b/openmeter/billing/rating/service/detailedline.go index fd08639b9d..b07bbe95bb 100644 --- a/openmeter/billing/rating/service/detailedline.go +++ b/openmeter/billing/rating/service/detailedline.go @@ -43,11 +43,9 @@ func (s *service) GenerateDetailedLines(in rating.StandardLineAccessor, opts ... return rating.GenerateDetailedLinesResult{}, fmt.Errorf("validating billable line: %w", err) } - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(in.GetCurrency()). - Build() + currency, err := in.GetCurrencyCalculator() if err != nil { - return rating.GenerateDetailedLinesResult{}, fmt.Errorf("creating currency calculator: %w", err) + return rating.GenerateDetailedLinesResult{}, fmt.Errorf("getting currency calculator: %w", err) } generateOpts := rating.NewGenerateDetailedLinesOptions(opts...) diff --git a/openmeter/billing/stdinvoiceline.go b/openmeter/billing/stdinvoiceline.go index d6a5422d78..74994ff971 100644 --- a/openmeter/billing/stdinvoiceline.go +++ b/openmeter/billing/stdinvoiceline.go @@ -188,6 +188,17 @@ func (i StandardLineBase) GetCurrency() currencyx.Code { return i.Currency } +func (i StandardLineBase) GetCurrencyCalculator() (currencyx.Currency, error) { + currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). + WithCode(i.Currency). + Build() + if err != nil { + return nil, fmt.Errorf("building fiat currency calculator: %w", err) + } + + return currency, nil +} + func (i StandardLineBase) GetName() string { return i.Name } diff --git a/openmeter/billing/worker/subscriptionsync/service/creditsonly_test.go b/openmeter/billing/worker/subscriptionsync/service/creditsonly_test.go index bd1596ab30..9952472610 100644 --- a/openmeter/billing/worker/subscriptionsync/service/creditsonly_test.go +++ b/openmeter/billing/worker/subscriptionsync/service/creditsonly_test.go @@ -1203,7 +1203,7 @@ func (s *CreditsOnlySubscriptionHandlerTestSuite) assertExpectedFlatFeeCharges(c s.Equalf(expectedCharge.InvoiceAt[periodIdx], charge.Intent.GetBaseIntent().InvoiceAt, "expected[%d] charge[%d] invoice at", expectedIdx, periodIdx) s.Equalf(productcatalog.CreditOnlySettlementMode, charge.Intent.GetSettlementMode(), "expected[%d] charge[%d] settlement mode", expectedIdx, periodIdx) s.Equalf(productcatalog.InAdvancePaymentTerm, charge.Intent.GetBaseIntent().PaymentTerm, "expected[%d] charge[%d] payment term", expectedIdx, periodIdx) - s.Equalf(string(currency.USD), string(charge.Intent.GetCurrency()), "expected[%d] charge[%d] currency", expectedIdx, periodIdx) + s.Equalf(string(currency.USD), charge.Intent.GetCurrency().GetCode().String(), "expected[%d] charge[%d] currency", expectedIdx, periodIdx) s.Equalf(expectedCharge.amountBeforeProration(periodIdx), charge.Intent.GetBaseIntent().AmountBeforeProration, "expected[%d] charge[%d] amount before proration", expectedIdx, periodIdx) s.Equalf(expectedCharge.amountAfterProration(periodIdx), charge.State.AmountAfterProration, "expected[%d] charge[%d] amount after proration", expectedIdx, periodIdx) s.Equalf(subscriptionID, charge.Intent.GetSubscription().SubscriptionID, "expected[%d] charge[%d] subscription id", expectedIdx, periodIdx) @@ -1264,7 +1264,7 @@ func (s *CreditsOnlySubscriptionHandlerTestSuite) assertExpectedUsageBasedCharge s.Equalf(expectedCharge.BillingPeriods[periodIdx], charge.Intent.GetBaseIntent().BillingPeriod, "expected[%d] charge[%d] billing period", expectedIdx, periodIdx) s.Equalf(expectedCharge.InvoiceAt[periodIdx], charge.Intent.GetBaseIntent().InvoiceAt, "expected[%d] charge[%d] invoice at", expectedIdx, periodIdx) s.Equalf(productcatalog.CreditOnlySettlementMode, charge.Intent.GetSettlementMode(), "expected[%d] charge[%d] settlement mode", expectedIdx, periodIdx) - s.Equalf(string(currency.USD), string(charge.Intent.GetCurrency()), "expected[%d] charge[%d] currency", expectedIdx, periodIdx) + s.Equalf(string(currency.USD), charge.Intent.GetCurrency().GetCode().String(), "expected[%d] charge[%d] currency", expectedIdx, periodIdx) s.Equalf(expectedCharge.FeatureKey, charge.Intent.GetBaseIntent().FeatureKey, "expected[%d] charge[%d] feature key", expectedIdx, periodIdx) s.Equalf(expectedCharge.Price, charge.Intent.GetBaseIntent().Price, "expected[%d] charge[%d] price", expectedIdx, periodIdx) s.Equalf(subscriptionID, charge.Intent.GetSubscription().SubscriptionID, "expected[%d] charge[%d] subscription id", expectedIdx, periodIdx) diff --git a/openmeter/billing/worker/subscriptionsync/service/reconciler/patchcharge_test.go b/openmeter/billing/worker/subscriptionsync/service/reconciler/patchcharge_test.go index da92fc0a49..aaafc65abb 100644 --- a/openmeter/billing/worker/subscriptionsync/service/reconciler/patchcharge_test.go +++ b/openmeter/billing/worker/subscriptionsync/service/reconciler/patchcharge_test.go @@ -5,7 +5,6 @@ import ( "time" "github.com/alpacahq/alpacadecimal" - "github.com/invopop/gobl/currency" "github.com/stretchr/testify/require" "github.com/openmeterio/openmeter/openmeter/billing" @@ -14,6 +13,7 @@ import ( chargesusagebased "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" "github.com/openmeterio/openmeter/openmeter/billing/worker/subscriptionsync/service/persistedstate" "github.com/openmeterio/openmeter/openmeter/billing/worker/subscriptionsync/service/targetstate" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/openmeter/subscription" "github.com/openmeterio/openmeter/pkg/currencyx" @@ -296,7 +296,7 @@ func newChargePatchTestExistingIntent(target targetstate.StateItem) chargesusage Intent: chargesmeta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: target.Subscription.CustomerId, - Currency: target.CurrencyCalculator.Details().Code, + Currency: target.Currency, UniqueReferenceID: ptr("existing-charge"), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: "tax-code-id", @@ -324,10 +324,7 @@ func newChargePatchTestExistingIntent(target targetstate.StateItem) chargesusage func newChargePatchTestTarget(t *testing.T, settlementMode productcatalog.SettlementMode, rateCard productcatalog.RateCard) targetstate.StateItem { t.Helper() - cur, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(currencyx.Code(currency.USD)). - Build() - require.NoError(t, err) + cur := currenciestestutils.NewFiatCurrency(t, "USD") servicePeriod := timeutil.ClosedPeriod{ From: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC), @@ -375,7 +372,7 @@ func newChargePatchTestTarget(t *testing.T, settlementMode productcatalog.Settle FullServicePeriod: fullServicePeriod, BillingPeriod: billingPeriod, }, - CurrencyCalculator: cur, + Currency: cur, Subscription: subscription.Subscription{ NamespacedID: models.NamespacedID{ Namespace: "namespace", diff --git a/openmeter/billing/worker/subscriptionsync/service/reconciler/patchchargeflatfee.go b/openmeter/billing/worker/subscriptionsync/service/reconciler/patchchargeflatfee.go index 92150164c9..78cc45fd09 100644 --- a/openmeter/billing/worker/subscriptionsync/service/reconciler/patchchargeflatfee.go +++ b/openmeter/billing/worker/subscriptionsync/service/reconciler/patchchargeflatfee.go @@ -94,7 +94,7 @@ func newFlatFeeChargeIntent(target targetstate.StateItem) (charges.ChargeIntent, ManagedBy: billing.SubscriptionManagedLine, CustomerID: target.Subscription.CustomerId, Annotations: annotations, - Currency: target.CurrencyCalculator.Details().Code, + Currency: target.Currency, UniqueReferenceID: &target.UniqueID, TaxConfig: productcatalog.TaxCodeConfigFrom(rateCardMeta.TaxConfig), Subscription: &chargesmeta.SubscriptionReference{ diff --git a/openmeter/billing/worker/subscriptionsync/service/reconciler/patchchargeusagebased.go b/openmeter/billing/worker/subscriptionsync/service/reconciler/patchchargeusagebased.go index e675bc8e8f..fd2544122b 100644 --- a/openmeter/billing/worker/subscriptionsync/service/reconciler/patchchargeusagebased.go +++ b/openmeter/billing/worker/subscriptionsync/service/reconciler/patchchargeusagebased.go @@ -101,7 +101,7 @@ func newUsageBasedChargeIntent(target targetstate.StateItem) (charges.ChargeInte ManagedBy: billing.SubscriptionManagedLine, CustomerID: target.Subscription.CustomerId, Annotations: annotations, - Currency: target.CurrencyCalculator.Details().Code, + Currency: target.Currency, UniqueReferenceID: &target.UniqueID, TaxConfig: productcatalog.TaxCodeConfigFrom(rateCardMeta.TaxConfig), Subscription: &chargesmeta.SubscriptionReference{ diff --git a/openmeter/billing/worker/subscriptionsync/service/sync_credittheninvoice_test.go b/openmeter/billing/worker/subscriptionsync/service/sync_credittheninvoice_test.go index c8bfa5aa66..ad0d78be3d 100644 --- a/openmeter/billing/worker/subscriptionsync/service/sync_credittheninvoice_test.go +++ b/openmeter/billing/worker/subscriptionsync/service/sync_credittheninvoice_test.go @@ -23,6 +23,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" "github.com/openmeterio/openmeter/openmeter/billing/models/totals" "github.com/openmeterio/openmeter/openmeter/billing/worker/subscriptionsync" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/customer" enttx "github.com/openmeterio/openmeter/openmeter/ent/tx" "github.com/openmeterio/openmeter/openmeter/ledger" @@ -10568,7 +10569,7 @@ func (s *CreditThenInvoiceTestSuite) createPromotionalCreditFunding(ctx context. Intent: chargesmeta.Intent{ ManagedBy: billing.SystemManagedLine, CustomerID: input.Customer.ID, - Currency: input.Currency, + Currency: currenciestestutils.NewFiatCurrency(s.T(), input.Currency), }, IntentMutableFields: creditpurchase.IntentMutableFields{ IntentMutableFields: chargesmeta.IntentMutableFields{ diff --git a/openmeter/billing/worker/subscriptionsync/service/targetstate/targetstate.go b/openmeter/billing/worker/subscriptionsync/service/targetstate/targetstate.go index 63407db206..65f92ed06b 100644 --- a/openmeter/billing/worker/subscriptionsync/service/targetstate/targetstate.go +++ b/openmeter/billing/worker/subscriptionsync/service/targetstate/targetstate.go @@ -14,6 +14,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/openmeter/billing/worker/subscriptionsync/service/persistedstate" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/streaming" "github.com/openmeterio/openmeter/openmeter/subscription" "github.com/openmeterio/openmeter/pkg/currencyx" @@ -122,8 +123,9 @@ func (b Builder) Build(ctx context.Context, input BuildInput) (State, error) { return State{ Items: lo.Map(inScopeLines, func(item SubscriptionItemWithPeriods, _ int) StateItem { return StateItem{ - SubscriptionItemWithPeriods: item, - CurrencyCalculator: currency, + SubscriptionItemWithPeriods: item, + // TODO[later]: Once subscriptions supports custom currencies, we need to use the currency from the subscription item. + Currency: currencies.Currency{Currency: currency}, Subscription: subs.Subscription, SubscriptionEndProrationMode: input.SubscriptionEndProrationMode, } diff --git a/openmeter/billing/worker/subscriptionsync/service/targetstate/targetstateitem.go b/openmeter/billing/worker/subscriptionsync/service/targetstate/targetstateitem.go index a52ebe5b95..522a2b866b 100644 --- a/openmeter/billing/worker/subscriptionsync/service/targetstate/targetstateitem.go +++ b/openmeter/billing/worker/subscriptionsync/service/targetstate/targetstateitem.go @@ -8,9 +8,9 @@ import ( "github.com/samber/lo" "github.com/openmeterio/openmeter/openmeter/billing" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/openmeter/subscription" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -18,7 +18,7 @@ import ( type StateItem struct { SubscriptionItemWithPeriods - CurrencyCalculator currencyx.Currency + Currency currencies.Currency Subscription subscription.Subscription SubscriptionEndProrationMode billing.SubscriptionEndProrationMode } @@ -58,7 +58,7 @@ func (r StateItem) GetExpectedLine() (*billing.GatheringLine, error) { Description: r.Spec.RateCard.AsMeta().Description, }), ManagedBy: billing.SubscriptionManagedLine, - Currency: r.CurrencyCalculator.Details().Code, + Currency: r.Currency.GetCode(), ChildUniqueReferenceID: &r.UniqueID, TaxConfig: r.Spec.RateCard.AsMeta().TaxConfig, ServicePeriod: r.GetServicePeriod(), @@ -94,9 +94,9 @@ func (r StateItem) GetExpectedLine() (*billing.GatheringLine, error) { return nil, fmt.Errorf("converting price to flat: %w", err) } - perUnitAmount := r.CurrencyCalculator.RoundToPrecision(price.Amount) + perUnitAmount := r.Currency.RoundToPrecision(price.Amount) if !r.ServicePeriod.IsEmpty() && r.shouldProrate() { - perUnitAmount = r.CurrencyCalculator.RoundToPrecision(price.Amount.Mul(r.PeriodPercentage())) + perUnitAmount = r.Currency.RoundToPrecision(price.Amount.Mul(r.PeriodPercentage())) } if perUnitAmount.IsZero() { diff --git a/openmeter/currencies/adapter/currencies.go b/openmeter/currencies/adapter/currencies.go index 0ca7851123..ae857c6926 100644 --- a/openmeter/currencies/adapter/currencies.go +++ b/openmeter/currencies/adapter/currencies.go @@ -22,7 +22,45 @@ import ( "github.com/openmeterio/openmeter/pkg/sortx" ) -func mapCurrencyFromDB(c *entdb.CustomCurrency) (currencies.Currency, error) { +type CustomCurrencyOrFiatCurrency struct { + CustomCurrency *entdb.CustomCurrency + FiatCurrency *currencyx.Code +} + +func (c *CustomCurrencyOrFiatCurrency) Validate() error { + if c.CustomCurrency != nil && c.FiatCurrency != nil { + return fmt.Errorf("both custom currency and fiat currency cannot be set") + } + + if c.CustomCurrency == nil && c.FiatCurrency == nil { + return fmt.Errorf("either custom currency or fiat currency must be set") + } + + return nil +} + +func MapCustomCurrencyOrFiatCurrencyFromDB(in CustomCurrencyOrFiatCurrency) (currencies.Currency, error) { + if err := in.Validate(); err != nil { + return currencies.Currency{}, err + } + + if in.CustomCurrency != nil { + return MapCurrencyFromDB(in.CustomCurrency) + } + + fiatCurrency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). + WithCode(*in.FiatCurrency). + Build() + if err != nil { + return currencies.Currency{}, err + } + + return currencies.Currency{ + Currency: fiatCurrency, + }, nil +} + +func MapCurrencyFromDB(c *entdb.CustomCurrency) (currencies.Currency, error) { curr, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeCustom). WithCode(c.Code). WithName(c.Name). @@ -144,7 +182,7 @@ func (a *adapter) ListCustomCurrencies(ctx context.Context, params currencies.Li return pagination.Result[currencies.Currency]{}, fmt.Errorf("failed to list currencies: %w", err) } - return pagination.MapResultErr(paged, mapCurrencyFromDB) + return pagination.MapResultErr(paged, MapCurrencyFromDB) }) } @@ -168,7 +206,7 @@ func (a *adapter) CreateCurrency(ctx context.Context, params currencies.CreateCu return currencies.Currency{}, fmt.Errorf("failed to create currency: %w", err) } - return mapCurrencyFromDB(curr) + return MapCurrencyFromDB(curr) }) } @@ -248,7 +286,7 @@ func (a *adapter) GetCostBasis(ctx context.Context, params currencies.GetCostBas result := mapCostBasisFromDB(costBasis) if params.CustomCurrency { - customCurrency, err := mapCurrencyFromDB(costBasis.Edges.Currency) + customCurrency, err := MapCurrencyFromDB(costBasis.Edges.Currency) if err != nil { return currencies.CostBasis{}, fmt.Errorf("failed to map custom currency: %w", err) } @@ -307,7 +345,7 @@ func (a *adapter) GetCurrency(ctx context.Context, params currencies.GetCurrency return currencies.Currency{}, fmt.Errorf("failed to get currency: %w", err) } - curr, err := mapCurrencyFromDB(c) + curr, err := MapCurrencyFromDB(c) if err != nil { return currencies.Currency{}, fmt.Errorf("failed to map currency from database: %w", err) } diff --git a/openmeter/currencies/currency.go b/openmeter/currencies/currency.go index a8eeecb2bf..3b97811859 100644 --- a/openmeter/currencies/currency.go +++ b/openmeter/currencies/currency.go @@ -1,7 +1,11 @@ package currencies import ( + "errors" "fmt" + "slices" + + "github.com/samber/lo" "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" @@ -39,3 +43,62 @@ type Currency struct { // CostBasis is included only if the Currency is expanded. CostBasis *[]CostBasis `json:"-"` } + +var _ models.Validator = Currency{} + +func NewFiatCurrency(code currencyx.Code) (Currency, error) { + currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). + WithCode(code). + Build() + if err != nil { + return Currency{}, fmt.Errorf("build fiat currency: %w", err) + } + + return Currency{Currency: currency}, nil +} + +func (c Currency) Validate() error { + if c.Currency == nil { + return errors.New("currency is required") + } + + return c.Currency.Validate() +} + +// GetCode returns the resolved currency code, or an empty code when the currency is missing. +func (c Currency) GetCode() currencyx.Code { + if c.Currency == nil { + return "" + } + + return c.Currency.Details().Code +} + +func (c Currency) IsFiat() bool { + return c.Currency != nil && c.Currency.Type() == currencyx.CurrencyTypeFiat +} + +func (c Currency) IsCustom() bool { + return c.Currency != nil && c.Currency.Type() == currencyx.CurrencyTypeCustom +} + +func (c Currency) Clone() Currency { + if c.CostBasis != nil { + c.CostBasis = lo.ToPtr(slices.Clone(*c.CostBasis)) + } + + return c +} + +// Identity uniquely identifies a currency by its fiat code or custom currency ID. +func (c Currency) Identity() (string, error) { + if c.IsFiat() { + return fmt.Sprintf("FIAT:%s", c.GetCode()), nil + } + + if c.IsCustom() { + return fmt.Sprintf("CUSTOM:%s:%s", c.Namespace, c.ID), nil + } + + return "", fmt.Errorf("currency is not fiat or custom") +} diff --git a/openmeter/currencies/currency_test.go b/openmeter/currencies/currency_test.go new file mode 100644 index 0000000000..8cf3c886be --- /dev/null +++ b/openmeter/currencies/currency_test.go @@ -0,0 +1,43 @@ +package currencies_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/openmeterio/openmeter/openmeter/currencies" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" + "github.com/openmeterio/openmeter/pkg/currencyx" +) + +func TestCurrencyValidate(t *testing.T) { + t.Run("missing currency", func(t *testing.T) { + err := (currencies.Currency{}).Validate() + + require.ErrorContains(t, err, "currency is required") + }) + + t.Run("delegates validation", func(t *testing.T) { + err := (currencies.Currency{Currency: ¤cyx.FiatCurrency{}}).Validate() + + require.ErrorContains(t, err, "fiat currency is not initialized") + }) + + t.Run("valid currency", func(t *testing.T) { + currency := currenciestestutils.NewFiatCurrency(t, "USD") + + require.NoError(t, currency.Validate()) + }) +} + +func TestCurrencyGetCode(t *testing.T) { + t.Run("missing currency", func(t *testing.T) { + require.Empty(t, (currencies.Currency{}).GetCode()) + }) + + t.Run("returns currency code", func(t *testing.T) { + currency := currenciestestutils.NewFiatCurrency(t, "USD") + + require.Equal(t, currencyx.Code("USD"), currency.GetCode()) + }) +} diff --git a/openmeter/currencies/testutils/currency/currency.go b/openmeter/currencies/testutils/currency/currency.go new file mode 100644 index 0000000000..7524f20bf3 --- /dev/null +++ b/openmeter/currencies/testutils/currency/currency.go @@ -0,0 +1,19 @@ +package currency + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/openmeterio/openmeter/openmeter/currencies" + "github.com/openmeterio/openmeter/pkg/currencyx" +) + +func NewFiatCurrency[T ~string](t testing.TB, code T) currencies.Currency { + t.Helper() + + currency, err := currencies.NewFiatCurrency(currencyx.Code(code)) + require.NoError(t, err) + + return currency +} diff --git a/openmeter/ent/db/chargecreditpurchase.go b/openmeter/ent/db/chargecreditpurchase.go index 57fcff54e1..65d59964bd 100644 --- a/openmeter/ent/db/chargecreditpurchase.go +++ b/openmeter/ent/db/chargecreditpurchase.go @@ -20,6 +20,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchasecreditgrant" "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchaseexternalpayment" "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchaseinvoicedpayment" + "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/openmeter/ent/db/customer" "github.com/openmeterio/openmeter/openmeter/ent/db/subscription" "github.com/openmeterio/openmeter/openmeter/ent/db/subscriptionitem" @@ -53,8 +54,10 @@ type ChargeCreditPurchase struct { Status meta.ChargeStatus `json:"status,omitempty"` // UniqueReferenceID holds the value of the "unique_reference_id" field. UniqueReferenceID *string `json:"unique_reference_id,omitempty"` - // Currency holds the value of the "currency" field. - Currency currencyx.Code `json:"currency,omitempty"` + // FiatCurrencyCode holds the value of the "fiat_currency_code" field. + FiatCurrencyCode *currencyx.Code `json:"fiat_currency_code,omitempty"` + // CustomCurrencyID holds the value of the "custom_currency_id" field. + CustomCurrencyID *string `json:"custom_currency_id,omitempty"` // ManagedBy holds the value of the "managed_by" field. ManagedBy billing.InvoiceLineManagedBy `json:"managed_by,omitempty"` // SubscriptionID holds the value of the "subscription_id" field. @@ -129,9 +132,11 @@ type ChargeCreditPurchaseEdges struct { Customer *Customer `json:"customer,omitempty"` // TaxCode holds the value of the tax_code edge. TaxCode *TaxCode `json:"tax_code,omitempty"` + // CustomCurrency holds the value of the custom_currency edge. + CustomCurrency *CustomCurrency `json:"custom_currency,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [9]bool + loadedTypes [10]bool } // ExternalPaymentOrErr returns the ExternalPayment value or an error if the edge @@ -233,6 +238,17 @@ func (e ChargeCreditPurchaseEdges) TaxCodeOrErr() (*TaxCode, error) { return nil, &NotLoadedError{edge: "tax_code"} } +// CustomCurrencyOrErr returns the CustomCurrency value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e ChargeCreditPurchaseEdges) CustomCurrencyOrErr() (*CustomCurrency, error) { + if e.CustomCurrency != nil { + return e.CustomCurrency, nil + } else if e.loadedTypes[9] { + return nil, &NotFoundError{label: customcurrency.Label} + } + return nil, &NotLoadedError{edge: "custom_currency"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*ChargeCreditPurchase) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -246,7 +262,7 @@ func (*ChargeCreditPurchase) scanValues(columns []string) ([]any, error) { values[i] = new(pq.StringArray) case chargecreditpurchase.FieldPriority: values[i] = new(sql.NullInt64) - case chargecreditpurchase.FieldID, chargecreditpurchase.FieldCustomerID, chargecreditpurchase.FieldStatus, chargecreditpurchase.FieldUniqueReferenceID, chargecreditpurchase.FieldCurrency, chargecreditpurchase.FieldManagedBy, chargecreditpurchase.FieldSubscriptionID, chargecreditpurchase.FieldSubscriptionPhaseID, chargecreditpurchase.FieldSubscriptionItemID, chargecreditpurchase.FieldTaxCodeID, chargecreditpurchase.FieldTaxBehavior, chargecreditpurchase.FieldNamespace, chargecreditpurchase.FieldName, chargecreditpurchase.FieldDescription, chargecreditpurchase.FieldStatusDetailed, chargecreditpurchase.FieldKey: + case chargecreditpurchase.FieldID, chargecreditpurchase.FieldCustomerID, chargecreditpurchase.FieldStatus, chargecreditpurchase.FieldUniqueReferenceID, chargecreditpurchase.FieldFiatCurrencyCode, chargecreditpurchase.FieldCustomCurrencyID, chargecreditpurchase.FieldManagedBy, chargecreditpurchase.FieldSubscriptionID, chargecreditpurchase.FieldSubscriptionPhaseID, chargecreditpurchase.FieldSubscriptionItemID, chargecreditpurchase.FieldTaxCodeID, chargecreditpurchase.FieldTaxBehavior, chargecreditpurchase.FieldNamespace, chargecreditpurchase.FieldName, chargecreditpurchase.FieldDescription, chargecreditpurchase.FieldStatusDetailed, chargecreditpurchase.FieldKey: values[i] = new(sql.NullString) case chargecreditpurchase.FieldServicePeriodFrom, chargecreditpurchase.FieldServicePeriodTo, chargecreditpurchase.FieldBillingPeriodFrom, chargecreditpurchase.FieldBillingPeriodTo, chargecreditpurchase.FieldFullServicePeriodFrom, chargecreditpurchase.FieldFullServicePeriodTo, chargecreditpurchase.FieldAdvanceAfter, chargecreditpurchase.FieldCreatedAt, chargecreditpurchase.FieldUpdatedAt, chargecreditpurchase.FieldDeletedAt, chargecreditpurchase.FieldEffectiveAt, chargecreditpurchase.FieldExpiresAt, chargecreditpurchase.FieldVoidedAt: values[i] = new(sql.NullTime) @@ -328,11 +344,19 @@ func (_m *ChargeCreditPurchase) assignValues(columns []string, values []any) err _m.UniqueReferenceID = new(string) *_m.UniqueReferenceID = value.String } - case chargecreditpurchase.FieldCurrency: + case chargecreditpurchase.FieldFiatCurrencyCode: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field fiat_currency_code", values[i]) + } else if value.Valid { + _m.FiatCurrencyCode = new(currencyx.Code) + *_m.FiatCurrencyCode = currencyx.Code(value.String) + } + case chargecreditpurchase.FieldCustomCurrencyID: if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field currency", values[i]) + return fmt.Errorf("unexpected type %T for field custom_currency_id", values[i]) } else if value.Valid { - _m.Currency = currencyx.Code(value.String) + _m.CustomCurrencyID = new(string) + *_m.CustomCurrencyID = value.String } case chargecreditpurchase.FieldManagedBy: if value, ok := values[i].(*sql.NullString); !ok { @@ -552,6 +576,11 @@ func (_m *ChargeCreditPurchase) QueryTaxCode() *TaxCodeQuery { return NewChargeCreditPurchaseClient(_m.config).QueryTaxCode(_m) } +// QueryCustomCurrency queries the "custom_currency" edge of the ChargeCreditPurchase entity. +func (_m *ChargeCreditPurchase) QueryCustomCurrency() *CustomCurrencyQuery { + return NewChargeCreditPurchaseClient(_m.config).QueryCustomCurrency(_m) +} + // Update returns a builder for updating this ChargeCreditPurchase. // Note that you need to call ChargeCreditPurchase.Unwrap() before calling this method if this ChargeCreditPurchase // was returned from a transaction, and the transaction was committed or rolled back. @@ -604,8 +633,15 @@ func (_m *ChargeCreditPurchase) String() string { builder.WriteString(*v) } builder.WriteString(", ") - builder.WriteString("currency=") - builder.WriteString(fmt.Sprintf("%v", _m.Currency)) + if v := _m.FiatCurrencyCode; v != nil { + builder.WriteString("fiat_currency_code=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + if v := _m.CustomCurrencyID; v != nil { + builder.WriteString("custom_currency_id=") + builder.WriteString(*v) + } builder.WriteString(", ") builder.WriteString("managed_by=") builder.WriteString(fmt.Sprintf("%v", _m.ManagedBy)) diff --git a/openmeter/ent/db/chargecreditpurchase/chargecreditpurchase.go b/openmeter/ent/db/chargecreditpurchase/chargecreditpurchase.go index d3a93e20a8..3e57eb0230 100644 --- a/openmeter/ent/db/chargecreditpurchase/chargecreditpurchase.go +++ b/openmeter/ent/db/chargecreditpurchase/chargecreditpurchase.go @@ -38,8 +38,10 @@ const ( FieldStatus = "status" // FieldUniqueReferenceID holds the string denoting the unique_reference_id field in the database. FieldUniqueReferenceID = "unique_reference_id" - // FieldCurrency holds the string denoting the currency field in the database. - FieldCurrency = "currency" + // FieldFiatCurrencyCode holds the string denoting the fiat_currency_code field in the database. + FieldFiatCurrencyCode = "currency" + // FieldCustomCurrencyID holds the string denoting the custom_currency_id field in the database. + FieldCustomCurrencyID = "custom_currency_id" // FieldManagedBy holds the string denoting the managed_by field in the database. FieldManagedBy = "managed_by" // FieldSubscriptionID holds the string denoting the subscription_id field in the database. @@ -106,6 +108,8 @@ const ( EdgeCustomer = "customer" // EdgeTaxCode holds the string denoting the tax_code edge name in mutations. EdgeTaxCode = "tax_code" + // EdgeCustomCurrency holds the string denoting the custom_currency edge name in mutations. + EdgeCustomCurrency = "custom_currency" // Table holds the table name of the chargecreditpurchase in the database. Table = "charge_credit_purchases" // ExternalPaymentTable is the table that holds the external_payment relation/edge. @@ -171,6 +175,13 @@ const ( TaxCodeInverseTable = "tax_codes" // TaxCodeColumn is the table column denoting the tax_code relation/edge. TaxCodeColumn = "tax_code_id" + // CustomCurrencyTable is the table that holds the custom_currency relation/edge. + CustomCurrencyTable = "charge_credit_purchases" + // CustomCurrencyInverseTable is the table name for the CustomCurrency entity. + // It exists in this package in order to avoid circular dependency with the "customcurrency" package. + CustomCurrencyInverseTable = "custom_currencies" + // CustomCurrencyColumn is the table column denoting the custom_currency relation/edge. + CustomCurrencyColumn = "custom_currency_id" ) // Columns holds all SQL columns for chargecreditpurchase fields. @@ -185,7 +196,8 @@ var Columns = []string{ FieldFullServicePeriodTo, FieldStatus, FieldUniqueReferenceID, - FieldCurrency, + FieldFiatCurrencyCode, + FieldCustomCurrencyID, FieldManagedBy, FieldSubscriptionID, FieldSubscriptionPhaseID, @@ -225,8 +237,10 @@ func ValidColumn(column string) bool { var ( // CustomerIDValidator is a validator for the "customer_id" field. It is called by the builders before save. CustomerIDValidator func(string) error - // CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. - CurrencyValidator func(string) error + // FiatCurrencyCodeValidator is a validator for the "fiat_currency_code" field. It is called by the builders before save. + FiatCurrencyCodeValidator func(string) error + // CustomCurrencyIDValidator is a validator for the "custom_currency_id" field. It is called by the builders before save. + CustomCurrencyIDValidator func(string) error // TaxCodeIDValidator is a validator for the "tax_code_id" field. It is called by the builders before save. TaxCodeIDValidator func(string) error // NamespaceValidator is a validator for the "namespace" field. It is called by the builders before save. @@ -338,9 +352,14 @@ func ByUniqueReferenceID(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldUniqueReferenceID, opts...).ToFunc() } -// ByCurrency orders the results by the currency field. -func ByCurrency(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldCurrency, opts...).ToFunc() +// ByFiatCurrencyCode orders the results by the fiat_currency_code field. +func ByFiatCurrencyCode(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldFiatCurrencyCode, opts...).ToFunc() +} + +// ByCustomCurrencyID orders the results by the custom_currency_id field. +func ByCustomCurrencyID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCustomCurrencyID, opts...).ToFunc() } // ByManagedBy orders the results by the managed_by field. @@ -515,6 +534,13 @@ func ByTaxCodeField(field string, opts ...sql.OrderTermOption) OrderOption { sqlgraph.OrderByNeighborTerms(s, newTaxCodeStep(), sql.OrderByField(field, opts...)) } } + +// ByCustomCurrencyField orders the results by custom_currency field. +func ByCustomCurrencyField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newCustomCurrencyStep(), sql.OrderByField(field, opts...)) + } +} func newExternalPaymentStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), @@ -578,3 +604,10 @@ func newTaxCodeStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.M2O, true, TaxCodeTable, TaxCodeColumn), ) } +func newCustomCurrencyStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(CustomCurrencyInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, CustomCurrencyTable, CustomCurrencyColumn), + ) +} diff --git a/openmeter/ent/db/chargecreditpurchase/where.go b/openmeter/ent/db/chargecreditpurchase/where.go index 67d39b566f..35e17ef797 100644 --- a/openmeter/ent/db/chargecreditpurchase/where.go +++ b/openmeter/ent/db/chargecreditpurchase/where.go @@ -112,10 +112,15 @@ func UniqueReferenceID(v string) predicate.ChargeCreditPurchase { return predicate.ChargeCreditPurchase(sql.FieldEQ(FieldUniqueReferenceID, v)) } -// Currency applies equality check predicate on the "currency" field. It's identical to CurrencyEQ. -func Currency(v currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCode applies equality check predicate on the "fiat_currency_code" field. It's identical to FiatCurrencyCodeEQ. +func FiatCurrencyCode(v currencyx.Code) predicate.ChargeCreditPurchase { vc := string(v) - return predicate.ChargeCreditPurchase(sql.FieldEQ(FieldCurrency, vc)) + return predicate.ChargeCreditPurchase(sql.FieldEQ(FieldFiatCurrencyCode, vc)) +} + +// CustomCurrencyID applies equality check predicate on the "custom_currency_id" field. It's identical to CustomCurrencyIDEQ. +func CustomCurrencyID(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldEQ(FieldCustomCurrencyID, v)) } // SubscriptionID applies equality check predicate on the "subscription_id" field. It's identical to SubscriptionIDEQ. @@ -618,88 +623,173 @@ func UniqueReferenceIDContainsFold(v string) predicate.ChargeCreditPurchase { return predicate.ChargeCreditPurchase(sql.FieldContainsFold(FieldUniqueReferenceID, v)) } -// CurrencyEQ applies the EQ predicate on the "currency" field. -func CurrencyEQ(v currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCodeEQ applies the EQ predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeEQ(v currencyx.Code) predicate.ChargeCreditPurchase { vc := string(v) - return predicate.ChargeCreditPurchase(sql.FieldEQ(FieldCurrency, vc)) + return predicate.ChargeCreditPurchase(sql.FieldEQ(FieldFiatCurrencyCode, vc)) } -// CurrencyNEQ applies the NEQ predicate on the "currency" field. -func CurrencyNEQ(v currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCodeNEQ applies the NEQ predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeNEQ(v currencyx.Code) predicate.ChargeCreditPurchase { vc := string(v) - return predicate.ChargeCreditPurchase(sql.FieldNEQ(FieldCurrency, vc)) + return predicate.ChargeCreditPurchase(sql.FieldNEQ(FieldFiatCurrencyCode, vc)) } -// CurrencyIn applies the In predicate on the "currency" field. -func CurrencyIn(vs ...currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCodeIn applies the In predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeIn(vs ...currencyx.Code) predicate.ChargeCreditPurchase { v := make([]any, len(vs)) for i := range v { v[i] = string(vs[i]) } - return predicate.ChargeCreditPurchase(sql.FieldIn(FieldCurrency, v...)) + return predicate.ChargeCreditPurchase(sql.FieldIn(FieldFiatCurrencyCode, v...)) } -// CurrencyNotIn applies the NotIn predicate on the "currency" field. -func CurrencyNotIn(vs ...currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCodeNotIn applies the NotIn predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeNotIn(vs ...currencyx.Code) predicate.ChargeCreditPurchase { v := make([]any, len(vs)) for i := range v { v[i] = string(vs[i]) } - return predicate.ChargeCreditPurchase(sql.FieldNotIn(FieldCurrency, v...)) + return predicate.ChargeCreditPurchase(sql.FieldNotIn(FieldFiatCurrencyCode, v...)) } -// CurrencyGT applies the GT predicate on the "currency" field. -func CurrencyGT(v currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCodeGT applies the GT predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeGT(v currencyx.Code) predicate.ChargeCreditPurchase { vc := string(v) - return predicate.ChargeCreditPurchase(sql.FieldGT(FieldCurrency, vc)) + return predicate.ChargeCreditPurchase(sql.FieldGT(FieldFiatCurrencyCode, vc)) } -// CurrencyGTE applies the GTE predicate on the "currency" field. -func CurrencyGTE(v currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCodeGTE applies the GTE predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeGTE(v currencyx.Code) predicate.ChargeCreditPurchase { vc := string(v) - return predicate.ChargeCreditPurchase(sql.FieldGTE(FieldCurrency, vc)) + return predicate.ChargeCreditPurchase(sql.FieldGTE(FieldFiatCurrencyCode, vc)) } -// CurrencyLT applies the LT predicate on the "currency" field. -func CurrencyLT(v currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCodeLT applies the LT predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeLT(v currencyx.Code) predicate.ChargeCreditPurchase { vc := string(v) - return predicate.ChargeCreditPurchase(sql.FieldLT(FieldCurrency, vc)) + return predicate.ChargeCreditPurchase(sql.FieldLT(FieldFiatCurrencyCode, vc)) } -// CurrencyLTE applies the LTE predicate on the "currency" field. -func CurrencyLTE(v currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCodeLTE applies the LTE predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeLTE(v currencyx.Code) predicate.ChargeCreditPurchase { vc := string(v) - return predicate.ChargeCreditPurchase(sql.FieldLTE(FieldCurrency, vc)) + return predicate.ChargeCreditPurchase(sql.FieldLTE(FieldFiatCurrencyCode, vc)) } -// CurrencyContains applies the Contains predicate on the "currency" field. -func CurrencyContains(v currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCodeContains applies the Contains predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeContains(v currencyx.Code) predicate.ChargeCreditPurchase { vc := string(v) - return predicate.ChargeCreditPurchase(sql.FieldContains(FieldCurrency, vc)) + return predicate.ChargeCreditPurchase(sql.FieldContains(FieldFiatCurrencyCode, vc)) } -// CurrencyHasPrefix applies the HasPrefix predicate on the "currency" field. -func CurrencyHasPrefix(v currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCodeHasPrefix applies the HasPrefix predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeHasPrefix(v currencyx.Code) predicate.ChargeCreditPurchase { vc := string(v) - return predicate.ChargeCreditPurchase(sql.FieldHasPrefix(FieldCurrency, vc)) + return predicate.ChargeCreditPurchase(sql.FieldHasPrefix(FieldFiatCurrencyCode, vc)) } -// CurrencyHasSuffix applies the HasSuffix predicate on the "currency" field. -func CurrencyHasSuffix(v currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCodeHasSuffix applies the HasSuffix predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeHasSuffix(v currencyx.Code) predicate.ChargeCreditPurchase { vc := string(v) - return predicate.ChargeCreditPurchase(sql.FieldHasSuffix(FieldCurrency, vc)) + return predicate.ChargeCreditPurchase(sql.FieldHasSuffix(FieldFiatCurrencyCode, vc)) +} + +// FiatCurrencyCodeIsNil applies the IsNil predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeIsNil() predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldIsNull(FieldFiatCurrencyCode)) } -// CurrencyEqualFold applies the EqualFold predicate on the "currency" field. -func CurrencyEqualFold(v currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCodeNotNil applies the NotNil predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeNotNil() predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldNotNull(FieldFiatCurrencyCode)) +} + +// FiatCurrencyCodeEqualFold applies the EqualFold predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeEqualFold(v currencyx.Code) predicate.ChargeCreditPurchase { vc := string(v) - return predicate.ChargeCreditPurchase(sql.FieldEqualFold(FieldCurrency, vc)) + return predicate.ChargeCreditPurchase(sql.FieldEqualFold(FieldFiatCurrencyCode, vc)) } -// CurrencyContainsFold applies the ContainsFold predicate on the "currency" field. -func CurrencyContainsFold(v currencyx.Code) predicate.ChargeCreditPurchase { +// FiatCurrencyCodeContainsFold applies the ContainsFold predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeContainsFold(v currencyx.Code) predicate.ChargeCreditPurchase { vc := string(v) - return predicate.ChargeCreditPurchase(sql.FieldContainsFold(FieldCurrency, vc)) + return predicate.ChargeCreditPurchase(sql.FieldContainsFold(FieldFiatCurrencyCode, vc)) +} + +// CustomCurrencyIDEQ applies the EQ predicate on the "custom_currency_id" field. +func CustomCurrencyIDEQ(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldEQ(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDNEQ applies the NEQ predicate on the "custom_currency_id" field. +func CustomCurrencyIDNEQ(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldNEQ(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDIn applies the In predicate on the "custom_currency_id" field. +func CustomCurrencyIDIn(vs ...string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldIn(FieldCustomCurrencyID, vs...)) +} + +// CustomCurrencyIDNotIn applies the NotIn predicate on the "custom_currency_id" field. +func CustomCurrencyIDNotIn(vs ...string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldNotIn(FieldCustomCurrencyID, vs...)) +} + +// CustomCurrencyIDGT applies the GT predicate on the "custom_currency_id" field. +func CustomCurrencyIDGT(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldGT(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDGTE applies the GTE predicate on the "custom_currency_id" field. +func CustomCurrencyIDGTE(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldGTE(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDLT applies the LT predicate on the "custom_currency_id" field. +func CustomCurrencyIDLT(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldLT(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDLTE applies the LTE predicate on the "custom_currency_id" field. +func CustomCurrencyIDLTE(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldLTE(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDContains applies the Contains predicate on the "custom_currency_id" field. +func CustomCurrencyIDContains(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldContains(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDHasPrefix applies the HasPrefix predicate on the "custom_currency_id" field. +func CustomCurrencyIDHasPrefix(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldHasPrefix(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDHasSuffix applies the HasSuffix predicate on the "custom_currency_id" field. +func CustomCurrencyIDHasSuffix(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldHasSuffix(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDIsNil applies the IsNil predicate on the "custom_currency_id" field. +func CustomCurrencyIDIsNil() predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldIsNull(FieldCustomCurrencyID)) +} + +// CustomCurrencyIDNotNil applies the NotNil predicate on the "custom_currency_id" field. +func CustomCurrencyIDNotNil() predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldNotNull(FieldCustomCurrencyID)) +} + +// CustomCurrencyIDEqualFold applies the EqualFold predicate on the "custom_currency_id" field. +func CustomCurrencyIDEqualFold(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldEqualFold(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDContainsFold applies the ContainsFold predicate on the "custom_currency_id" field. +func CustomCurrencyIDContainsFold(v string) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(sql.FieldContainsFold(FieldCustomCurrencyID, v)) } // ManagedByEQ applies the EQ predicate on the "managed_by" field. @@ -2069,6 +2159,29 @@ func HasTaxCodeWith(preds ...predicate.TaxCode) predicate.ChargeCreditPurchase { }) } +// HasCustomCurrency applies the HasEdge predicate on the "custom_currency" edge. +func HasCustomCurrency() predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, CustomCurrencyTable, CustomCurrencyColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasCustomCurrencyWith applies the HasEdge predicate on the "custom_currency" edge with a given conditions (other predicates). +func HasCustomCurrencyWith(preds ...predicate.CustomCurrency) predicate.ChargeCreditPurchase { + return predicate.ChargeCreditPurchase(func(s *sql.Selector) { + step := newCustomCurrencyStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.ChargeCreditPurchase) predicate.ChargeCreditPurchase { return predicate.ChargeCreditPurchase(sql.AndPredicates(predicates...)) diff --git a/openmeter/ent/db/chargecreditpurchase_create.go b/openmeter/ent/db/chargecreditpurchase_create.go index c09f51c503..e85c2cefdd 100644 --- a/openmeter/ent/db/chargecreditpurchase_create.go +++ b/openmeter/ent/db/chargecreditpurchase_create.go @@ -22,6 +22,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchasecreditgrant" "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchaseexternalpayment" "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchaseinvoicedpayment" + "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/openmeter/ent/db/customer" "github.com/openmeterio/openmeter/openmeter/ent/db/subscription" "github.com/openmeterio/openmeter/openmeter/ent/db/subscriptionitem" @@ -102,9 +103,31 @@ func (_c *ChargeCreditPurchaseCreate) SetNillableUniqueReferenceID(v *string) *C return _c } -// SetCurrency sets the "currency" field. -func (_c *ChargeCreditPurchaseCreate) SetCurrency(v currencyx.Code) *ChargeCreditPurchaseCreate { - _c.mutation.SetCurrency(v) +// SetFiatCurrencyCode sets the "fiat_currency_code" field. +func (_c *ChargeCreditPurchaseCreate) SetFiatCurrencyCode(v currencyx.Code) *ChargeCreditPurchaseCreate { + _c.mutation.SetFiatCurrencyCode(v) + return _c +} + +// SetNillableFiatCurrencyCode sets the "fiat_currency_code" field if the given value is not nil. +func (_c *ChargeCreditPurchaseCreate) SetNillableFiatCurrencyCode(v *currencyx.Code) *ChargeCreditPurchaseCreate { + if v != nil { + _c.SetFiatCurrencyCode(*v) + } + return _c +} + +// SetCustomCurrencyID sets the "custom_currency_id" field. +func (_c *ChargeCreditPurchaseCreate) SetCustomCurrencyID(v string) *ChargeCreditPurchaseCreate { + _c.mutation.SetCustomCurrencyID(v) + return _c +} + +// SetNillableCustomCurrencyID sets the "custom_currency_id" field if the given value is not nil. +func (_c *ChargeCreditPurchaseCreate) SetNillableCustomCurrencyID(v *string) *ChargeCreditPurchaseCreate { + if v != nil { + _c.SetCustomCurrencyID(*v) + } return _c } @@ -479,6 +502,11 @@ func (_c *ChargeCreditPurchaseCreate) SetTaxCode(v *TaxCode) *ChargeCreditPurcha return _c.SetTaxCodeID(v.ID) } +// SetCustomCurrency sets the "custom_currency" edge to the CustomCurrency entity. +func (_c *ChargeCreditPurchaseCreate) SetCustomCurrency(v *CustomCurrency) *ChargeCreditPurchaseCreate { + return _c.SetCustomCurrencyID(v.ID) +} + // Mutation returns the ChargeCreditPurchaseMutation object of the builder. func (_c *ChargeCreditPurchaseCreate) Mutation() *ChargeCreditPurchaseMutation { return _c.mutation @@ -564,12 +592,14 @@ func (_c *ChargeCreditPurchaseCreate) check() error { return &ValidationError{Name: "status", err: fmt.Errorf(`db: validator failed for field "ChargeCreditPurchase.status": %w`, err)} } } - if _, ok := _c.mutation.Currency(); !ok { - return &ValidationError{Name: "currency", err: errors.New(`db: missing required field "ChargeCreditPurchase.currency"`)} + if v, ok := _c.mutation.FiatCurrencyCode(); ok { + if err := chargecreditpurchase.FiatCurrencyCodeValidator(string(v)); err != nil { + return &ValidationError{Name: "fiat_currency_code", err: fmt.Errorf(`db: validator failed for field "ChargeCreditPurchase.fiat_currency_code": %w`, err)} + } } - if v, ok := _c.mutation.Currency(); ok { - if err := chargecreditpurchase.CurrencyValidator(string(v)); err != nil { - return &ValidationError{Name: "currency", err: fmt.Errorf(`db: validator failed for field "ChargeCreditPurchase.currency": %w`, err)} + if v, ok := _c.mutation.CustomCurrencyID(); ok { + if err := chargecreditpurchase.CustomCurrencyIDValidator(v); err != nil { + return &ValidationError{Name: "custom_currency_id", err: fmt.Errorf(`db: validator failed for field "ChargeCreditPurchase.custom_currency_id": %w`, err)} } } if _, ok := _c.mutation.ManagedBy(); !ok { @@ -706,9 +736,9 @@ func (_c *ChargeCreditPurchaseCreate) createSpec() (*ChargeCreditPurchase, *sqlg _spec.SetField(chargecreditpurchase.FieldUniqueReferenceID, field.TypeString, value) _node.UniqueReferenceID = &value } - if value, ok := _c.mutation.Currency(); ok { - _spec.SetField(chargecreditpurchase.FieldCurrency, field.TypeString, value) - _node.Currency = value + if value, ok := _c.mutation.FiatCurrencyCode(); ok { + _spec.SetField(chargecreditpurchase.FieldFiatCurrencyCode, field.TypeString, value) + _node.FiatCurrencyCode = &value } if value, ok := _c.mutation.ManagedBy(); ok { _spec.SetField(chargecreditpurchase.FieldManagedBy, field.TypeEnum, value) @@ -943,6 +973,23 @@ func (_c *ChargeCreditPurchaseCreate) createSpec() (*ChargeCreditPurchase, *sqlg _node.TaxCodeID = nodes[0] _spec.Edges = append(_spec.Edges, edge) } + if nodes := _c.mutation.CustomCurrencyIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: chargecreditpurchase.CustomCurrencyTable, + Columns: []string{chargecreditpurchase.CustomCurrencyColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(customcurrency.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.CustomCurrencyID = &nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec, nil } @@ -1288,8 +1335,11 @@ func (u *ChargeCreditPurchaseUpsertOne) UpdateNewValues() *ChargeCreditPurchaseU if _, exists := u.create.mutation.UniqueReferenceID(); exists { s.SetIgnore(chargecreditpurchase.FieldUniqueReferenceID) } - if _, exists := u.create.mutation.Currency(); exists { - s.SetIgnore(chargecreditpurchase.FieldCurrency) + if _, exists := u.create.mutation.FiatCurrencyCode(); exists { + s.SetIgnore(chargecreditpurchase.FieldFiatCurrencyCode) + } + if _, exists := u.create.mutation.CustomCurrencyID(); exists { + s.SetIgnore(chargecreditpurchase.FieldCustomCurrencyID) } if _, exists := u.create.mutation.ManagedBy(); exists { s.SetIgnore(chargecreditpurchase.FieldManagedBy) @@ -1865,8 +1915,11 @@ func (u *ChargeCreditPurchaseUpsertBulk) UpdateNewValues() *ChargeCreditPurchase if _, exists := b.mutation.UniqueReferenceID(); exists { s.SetIgnore(chargecreditpurchase.FieldUniqueReferenceID) } - if _, exists := b.mutation.Currency(); exists { - s.SetIgnore(chargecreditpurchase.FieldCurrency) + if _, exists := b.mutation.FiatCurrencyCode(); exists { + s.SetIgnore(chargecreditpurchase.FieldFiatCurrencyCode) + } + if _, exists := b.mutation.CustomCurrencyID(); exists { + s.SetIgnore(chargecreditpurchase.FieldCustomCurrencyID) } if _, exists := b.mutation.ManagedBy(); exists { s.SetIgnore(chargecreditpurchase.FieldManagedBy) diff --git a/openmeter/ent/db/chargecreditpurchase_query.go b/openmeter/ent/db/chargecreditpurchase_query.go index 1bdd028a25..d233a66413 100644 --- a/openmeter/ent/db/chargecreditpurchase_query.go +++ b/openmeter/ent/db/chargecreditpurchase_query.go @@ -18,6 +18,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchasecreditgrant" "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchaseexternalpayment" "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchaseinvoicedpayment" + "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/openmeter/ent/db/customer" "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" "github.com/openmeterio/openmeter/openmeter/ent/db/subscription" @@ -42,6 +43,7 @@ type ChargeCreditPurchaseQuery struct { withSubscriptionItem *SubscriptionItemQuery withCustomer *CustomerQuery withTaxCode *TaxCodeQuery + withCustomCurrency *CustomCurrencyQuery modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector @@ -277,6 +279,28 @@ func (_q *ChargeCreditPurchaseQuery) QueryTaxCode() *TaxCodeQuery { return query } +// QueryCustomCurrency chains the current query on the "custom_currency" edge. +func (_q *ChargeCreditPurchaseQuery) QueryCustomCurrency() *CustomCurrencyQuery { + query := (&CustomCurrencyClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(chargecreditpurchase.Table, chargecreditpurchase.FieldID, selector), + sqlgraph.To(customcurrency.Table, customcurrency.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, chargecreditpurchase.CustomCurrencyTable, chargecreditpurchase.CustomCurrencyColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first ChargeCreditPurchase entity from the query. // Returns a *NotFoundError when no ChargeCreditPurchase was found. func (_q *ChargeCreditPurchaseQuery) First(ctx context.Context) (*ChargeCreditPurchase, error) { @@ -478,6 +502,7 @@ func (_q *ChargeCreditPurchaseQuery) Clone() *ChargeCreditPurchaseQuery { withSubscriptionItem: _q.withSubscriptionItem.Clone(), withCustomer: _q.withCustomer.Clone(), withTaxCode: _q.withTaxCode.Clone(), + withCustomCurrency: _q.withCustomCurrency.Clone(), // clone intermediate query. sql: _q.sql.Clone(), path: _q.path, @@ -583,6 +608,17 @@ func (_q *ChargeCreditPurchaseQuery) WithTaxCode(opts ...func(*TaxCodeQuery)) *C return _q } +// WithCustomCurrency tells the query-builder to eager-load the nodes that are connected to +// the "custom_currency" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ChargeCreditPurchaseQuery) WithCustomCurrency(opts ...func(*CustomCurrencyQuery)) *ChargeCreditPurchaseQuery { + query := (&CustomCurrencyClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withCustomCurrency = query + return _q +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -661,7 +697,7 @@ func (_q *ChargeCreditPurchaseQuery) sqlAll(ctx context.Context, hooks ...queryH var ( nodes = []*ChargeCreditPurchase{} _spec = _q.querySpec() - loadedTypes = [9]bool{ + loadedTypes = [10]bool{ _q.withExternalPayment != nil, _q.withInvoicedPayment != nil, _q.withCreditGrant != nil, @@ -671,6 +707,7 @@ func (_q *ChargeCreditPurchaseQuery) sqlAll(ctx context.Context, hooks ...queryH _q.withSubscriptionItem != nil, _q.withCustomer != nil, _q.withTaxCode != nil, + _q.withCustomCurrency != nil, } ) _spec.ScanValues = func(columns []string) ([]any, error) { @@ -748,6 +785,12 @@ func (_q *ChargeCreditPurchaseQuery) sqlAll(ctx context.Context, hooks ...queryH return nil, err } } + if query := _q.withCustomCurrency; query != nil { + if err := _q.loadCustomCurrency(ctx, query, nodes, nil, + func(n *ChargeCreditPurchase, e *CustomCurrency) { n.Edges.CustomCurrency = e }); err != nil { + return nil, err + } + } return nodes, nil } @@ -1016,6 +1059,38 @@ func (_q *ChargeCreditPurchaseQuery) loadTaxCode(ctx context.Context, query *Tax } return nil } +func (_q *ChargeCreditPurchaseQuery) loadCustomCurrency(ctx context.Context, query *CustomCurrencyQuery, nodes []*ChargeCreditPurchase, init func(*ChargeCreditPurchase), assign func(*ChargeCreditPurchase, *CustomCurrency)) error { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*ChargeCreditPurchase) + for i := range nodes { + if nodes[i].CustomCurrencyID == nil { + continue + } + fk := *nodes[i].CustomCurrencyID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(customcurrency.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "custom_currency_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} func (_q *ChargeCreditPurchaseQuery) sqlCount(ctx context.Context) (int, error) { _spec := _q.querySpec() @@ -1060,6 +1135,9 @@ func (_q *ChargeCreditPurchaseQuery) querySpec() *sqlgraph.QuerySpec { if _q.withTaxCode != nil { _spec.Node.AddColumnOnce(chargecreditpurchase.FieldTaxCodeID) } + if _q.withCustomCurrency != nil { + _spec.Node.AddColumnOnce(chargecreditpurchase.FieldCustomCurrencyID) + } } if ps := _q.predicates; len(ps) > 0 { _spec.Predicate = func(selector *sql.Selector) { diff --git a/openmeter/ent/db/chargecreditpurchase_update.go b/openmeter/ent/db/chargecreditpurchase_update.go index 8d75912117..75006d0c98 100644 --- a/openmeter/ent/db/chargecreditpurchase_update.go +++ b/openmeter/ent/db/chargecreditpurchase_update.go @@ -509,6 +509,9 @@ func (_u *ChargeCreditPurchaseUpdate) sqlSave(ctx context.Context) (_node int, e if _u.mutation.UniqueReferenceIDCleared() { _spec.ClearField(chargecreditpurchase.FieldUniqueReferenceID, field.TypeString) } + if _u.mutation.FiatCurrencyCodeCleared() { + _spec.ClearField(chargecreditpurchase.FieldFiatCurrencyCode, field.TypeString) + } if value, ok := _u.mutation.AdvanceAfter(); ok { _spec.SetField(chargecreditpurchase.FieldAdvanceAfter, field.TypeTime, value) } @@ -1221,6 +1224,9 @@ func (_u *ChargeCreditPurchaseUpdateOne) sqlSave(ctx context.Context) (_node *Ch if _u.mutation.UniqueReferenceIDCleared() { _spec.ClearField(chargecreditpurchase.FieldUniqueReferenceID, field.TypeString) } + if _u.mutation.FiatCurrencyCodeCleared() { + _spec.ClearField(chargecreditpurchase.FieldFiatCurrencyCode, field.TypeString) + } if value, ok := _u.mutation.AdvanceAfter(); ok { _spec.SetField(chargecreditpurchase.FieldAdvanceAfter, field.TypeTime, value) } diff --git a/openmeter/ent/db/chargeflatfee.go b/openmeter/ent/db/chargeflatfee.go index 20eac10c91..0ef67d48c7 100644 --- a/openmeter/ent/db/chargeflatfee.go +++ b/openmeter/ent/db/chargeflatfee.go @@ -18,6 +18,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfee" "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfeeoverride" "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfeerun" + "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/openmeter/ent/db/customer" dbfeature "github.com/openmeterio/openmeter/openmeter/ent/db/feature" "github.com/openmeterio/openmeter/openmeter/ent/db/subscription" @@ -52,8 +53,10 @@ type ChargeFlatFee struct { Status meta.ChargeStatus `json:"status,omitempty"` // UniqueReferenceID holds the value of the "unique_reference_id" field. UniqueReferenceID *string `json:"unique_reference_id,omitempty"` - // Currency holds the value of the "currency" field. - Currency currencyx.Code `json:"currency,omitempty"` + // FiatCurrencyCode holds the value of the "fiat_currency_code" field. + FiatCurrencyCode *currencyx.Code `json:"fiat_currency_code,omitempty"` + // CustomCurrencyID holds the value of the "custom_currency_id" field. + CustomCurrencyID *string `json:"custom_currency_id,omitempty"` // ManagedBy holds the value of the "managed_by" field. ManagedBy billing.InvoiceLineManagedBy `json:"managed_by,omitempty"` // SubscriptionID holds the value of the "subscription_id" field. @@ -136,9 +139,11 @@ type ChargeFlatFeeEdges struct { Feature *Feature `json:"feature,omitempty"` // TaxCode holds the value of the tax_code edge. TaxCode *TaxCode `json:"tax_code,omitempty"` + // CustomCurrency holds the value of the custom_currency edge. + CustomCurrency *CustomCurrency `json:"custom_currency,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [10]bool + loadedTypes [11]bool } // RunsOrErr returns the Runs value or an error if the edge @@ -249,6 +254,17 @@ func (e ChargeFlatFeeEdges) TaxCodeOrErr() (*TaxCode, error) { return nil, &NotLoadedError{edge: "tax_code"} } +// CustomCurrencyOrErr returns the CustomCurrency value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e ChargeFlatFeeEdges) CustomCurrencyOrErr() (*CustomCurrency, error) { + if e.CustomCurrency != nil { + return e.CustomCurrency, nil + } else if e.loadedTypes[10] { + return nil, &NotFoundError{label: customcurrency.Label} + } + return nil, &NotLoadedError{edge: "custom_currency"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*ChargeFlatFee) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -258,7 +274,7 @@ func (*ChargeFlatFee) scanValues(columns []string) ([]any, error) { values[i] = new([]byte) case chargeflatfee.FieldAmountBeforeProration, chargeflatfee.FieldAmountAfterProration: values[i] = new(alpacadecimal.Decimal) - case chargeflatfee.FieldID, chargeflatfee.FieldCustomerID, chargeflatfee.FieldStatus, chargeflatfee.FieldUniqueReferenceID, chargeflatfee.FieldCurrency, chargeflatfee.FieldManagedBy, chargeflatfee.FieldSubscriptionID, chargeflatfee.FieldSubscriptionPhaseID, chargeflatfee.FieldSubscriptionItemID, chargeflatfee.FieldTaxCodeID, chargeflatfee.FieldTaxBehavior, chargeflatfee.FieldNamespace, chargeflatfee.FieldName, chargeflatfee.FieldDescription, chargeflatfee.FieldPaymentTerm, chargeflatfee.FieldSettlementMode, chargeflatfee.FieldProRating, chargeflatfee.FieldFeatureKey, chargeflatfee.FieldFeatureID, chargeflatfee.FieldCurrentRealizationRunID, chargeflatfee.FieldStatusDetailed: + case chargeflatfee.FieldID, chargeflatfee.FieldCustomerID, chargeflatfee.FieldStatus, chargeflatfee.FieldUniqueReferenceID, chargeflatfee.FieldFiatCurrencyCode, chargeflatfee.FieldCustomCurrencyID, chargeflatfee.FieldManagedBy, chargeflatfee.FieldSubscriptionID, chargeflatfee.FieldSubscriptionPhaseID, chargeflatfee.FieldSubscriptionItemID, chargeflatfee.FieldTaxCodeID, chargeflatfee.FieldTaxBehavior, chargeflatfee.FieldNamespace, chargeflatfee.FieldName, chargeflatfee.FieldDescription, chargeflatfee.FieldPaymentTerm, chargeflatfee.FieldSettlementMode, chargeflatfee.FieldProRating, chargeflatfee.FieldFeatureKey, chargeflatfee.FieldFeatureID, chargeflatfee.FieldCurrentRealizationRunID, chargeflatfee.FieldStatusDetailed: values[i] = new(sql.NullString) case chargeflatfee.FieldServicePeriodFrom, chargeflatfee.FieldServicePeriodTo, chargeflatfee.FieldBillingPeriodFrom, chargeflatfee.FieldBillingPeriodTo, chargeflatfee.FieldFullServicePeriodFrom, chargeflatfee.FieldFullServicePeriodTo, chargeflatfee.FieldAdvanceAfter, chargeflatfee.FieldCreatedAt, chargeflatfee.FieldUpdatedAt, chargeflatfee.FieldDeletedAt, chargeflatfee.FieldInvoiceAt, chargeflatfee.FieldIntentDeletedAt: values[i] = new(sql.NullTime) @@ -340,11 +356,19 @@ func (_m *ChargeFlatFee) assignValues(columns []string, values []any) error { _m.UniqueReferenceID = new(string) *_m.UniqueReferenceID = value.String } - case chargeflatfee.FieldCurrency: + case chargeflatfee.FieldFiatCurrencyCode: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field fiat_currency_code", values[i]) + } else if value.Valid { + _m.FiatCurrencyCode = new(currencyx.Code) + *_m.FiatCurrencyCode = currencyx.Code(value.String) + } + case chargeflatfee.FieldCustomCurrencyID: if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field currency", values[i]) + return fmt.Errorf("unexpected type %T for field custom_currency_id", values[i]) } else if value.Valid { - _m.Currency = currencyx.Code(value.String) + _m.CustomCurrencyID = new(string) + *_m.CustomCurrencyID = value.String } case chargeflatfee.FieldManagedBy: if value, ok := values[i].(*sql.NullString); !ok { @@ -586,6 +610,11 @@ func (_m *ChargeFlatFee) QueryTaxCode() *TaxCodeQuery { return NewChargeFlatFeeClient(_m.config).QueryTaxCode(_m) } +// QueryCustomCurrency queries the "custom_currency" edge of the ChargeFlatFee entity. +func (_m *ChargeFlatFee) QueryCustomCurrency() *CustomCurrencyQuery { + return NewChargeFlatFeeClient(_m.config).QueryCustomCurrency(_m) +} + // Update returns a builder for updating this ChargeFlatFee. // Note that you need to call ChargeFlatFee.Unwrap() before calling this method if this ChargeFlatFee // was returned from a transaction, and the transaction was committed or rolled back. @@ -638,8 +667,15 @@ func (_m *ChargeFlatFee) String() string { builder.WriteString(*v) } builder.WriteString(", ") - builder.WriteString("currency=") - builder.WriteString(fmt.Sprintf("%v", _m.Currency)) + if v := _m.FiatCurrencyCode; v != nil { + builder.WriteString("fiat_currency_code=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + if v := _m.CustomCurrencyID; v != nil { + builder.WriteString("custom_currency_id=") + builder.WriteString(*v) + } builder.WriteString(", ") builder.WriteString("managed_by=") builder.WriteString(fmt.Sprintf("%v", _m.ManagedBy)) diff --git a/openmeter/ent/db/chargeflatfee/chargeflatfee.go b/openmeter/ent/db/chargeflatfee/chargeflatfee.go index 18a4c66f3f..0674a4f276 100644 --- a/openmeter/ent/db/chargeflatfee/chargeflatfee.go +++ b/openmeter/ent/db/chargeflatfee/chargeflatfee.go @@ -38,8 +38,10 @@ const ( FieldStatus = "status" // FieldUniqueReferenceID holds the string denoting the unique_reference_id field in the database. FieldUniqueReferenceID = "unique_reference_id" - // FieldCurrency holds the string denoting the currency field in the database. - FieldCurrency = "currency" + // FieldFiatCurrencyCode holds the string denoting the fiat_currency_code field in the database. + FieldFiatCurrencyCode = "currency" + // FieldCustomCurrencyID holds the string denoting the custom_currency_id field in the database. + FieldCustomCurrencyID = "custom_currency_id" // FieldManagedBy holds the string denoting the managed_by field in the database. FieldManagedBy = "managed_by" // FieldSubscriptionID holds the string denoting the subscription_id field in the database. @@ -114,6 +116,8 @@ const ( EdgeFeature = "feature" // EdgeTaxCode holds the string denoting the tax_code edge name in mutations. EdgeTaxCode = "tax_code" + // EdgeCustomCurrency holds the string denoting the custom_currency edge name in mutations. + EdgeCustomCurrency = "custom_currency" // Table holds the table name of the chargeflatfee in the database. Table = "charge_flat_fees" // RunsTable is the table that holds the runs relation/edge. @@ -186,6 +190,13 @@ const ( TaxCodeInverseTable = "tax_codes" // TaxCodeColumn is the table column denoting the tax_code relation/edge. TaxCodeColumn = "tax_code_id" + // CustomCurrencyTable is the table that holds the custom_currency relation/edge. + CustomCurrencyTable = "charge_flat_fees" + // CustomCurrencyInverseTable is the table name for the CustomCurrency entity. + // It exists in this package in order to avoid circular dependency with the "customcurrency" package. + CustomCurrencyInverseTable = "custom_currencies" + // CustomCurrencyColumn is the table column denoting the custom_currency relation/edge. + CustomCurrencyColumn = "custom_currency_id" ) // Columns holds all SQL columns for chargeflatfee fields. @@ -200,7 +211,8 @@ var Columns = []string{ FieldFullServicePeriodTo, FieldStatus, FieldUniqueReferenceID, - FieldCurrency, + FieldFiatCurrencyCode, + FieldCustomCurrencyID, FieldManagedBy, FieldSubscriptionID, FieldSubscriptionPhaseID, @@ -243,8 +255,10 @@ func ValidColumn(column string) bool { var ( // CustomerIDValidator is a validator for the "customer_id" field. It is called by the builders before save. CustomerIDValidator func(string) error - // CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. - CurrencyValidator func(string) error + // FiatCurrencyCodeValidator is a validator for the "fiat_currency_code" field. It is called by the builders before save. + FiatCurrencyCodeValidator func(string) error + // CustomCurrencyIDValidator is a validator for the "custom_currency_id" field. It is called by the builders before save. + CustomCurrencyIDValidator func(string) error // TaxCodeIDValidator is a validator for the "tax_code_id" field. It is called by the builders before save. TaxCodeIDValidator func(string) error // NamespaceValidator is a validator for the "namespace" field. It is called by the builders before save. @@ -380,9 +394,14 @@ func ByUniqueReferenceID(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldUniqueReferenceID, opts...).ToFunc() } -// ByCurrency orders the results by the currency field. -func ByCurrency(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldCurrency, opts...).ToFunc() +// ByFiatCurrencyCode orders the results by the fiat_currency_code field. +func ByFiatCurrencyCode(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldFiatCurrencyCode, opts...).ToFunc() +} + +// ByCustomCurrencyID orders the results by the custom_currency_id field. +func ByCustomCurrencyID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCustomCurrencyID, opts...).ToFunc() } // ByManagedBy orders the results by the managed_by field. @@ -586,6 +605,13 @@ func ByTaxCodeField(field string, opts ...sql.OrderTermOption) OrderOption { sqlgraph.OrderByNeighborTerms(s, newTaxCodeStep(), sql.OrderByField(field, opts...)) } } + +// ByCustomCurrencyField orders the results by custom_currency field. +func ByCustomCurrencyField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newCustomCurrencyStep(), sql.OrderByField(field, opts...)) + } +} func newRunsStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), @@ -656,3 +682,10 @@ func newTaxCodeStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.M2O, true, TaxCodeTable, TaxCodeColumn), ) } +func newCustomCurrencyStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(CustomCurrencyInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, CustomCurrencyTable, CustomCurrencyColumn), + ) +} diff --git a/openmeter/ent/db/chargeflatfee/where.go b/openmeter/ent/db/chargeflatfee/where.go index ead19f442e..e1357522e1 100644 --- a/openmeter/ent/db/chargeflatfee/where.go +++ b/openmeter/ent/db/chargeflatfee/where.go @@ -111,10 +111,15 @@ func UniqueReferenceID(v string) predicate.ChargeFlatFee { return predicate.ChargeFlatFee(sql.FieldEQ(FieldUniqueReferenceID, v)) } -// Currency applies equality check predicate on the "currency" field. It's identical to CurrencyEQ. -func Currency(v currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCode applies equality check predicate on the "fiat_currency_code" field. It's identical to FiatCurrencyCodeEQ. +func FiatCurrencyCode(v currencyx.Code) predicate.ChargeFlatFee { vc := string(v) - return predicate.ChargeFlatFee(sql.FieldEQ(FieldCurrency, vc)) + return predicate.ChargeFlatFee(sql.FieldEQ(FieldFiatCurrencyCode, vc)) +} + +// CustomCurrencyID applies equality check predicate on the "custom_currency_id" field. It's identical to CustomCurrencyIDEQ. +func CustomCurrencyID(v string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldEQ(FieldCustomCurrencyID, v)) } // SubscriptionID applies equality check predicate on the "subscription_id" field. It's identical to SubscriptionIDEQ. @@ -623,88 +628,173 @@ func UniqueReferenceIDContainsFold(v string) predicate.ChargeFlatFee { return predicate.ChargeFlatFee(sql.FieldContainsFold(FieldUniqueReferenceID, v)) } -// CurrencyEQ applies the EQ predicate on the "currency" field. -func CurrencyEQ(v currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCodeEQ applies the EQ predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeEQ(v currencyx.Code) predicate.ChargeFlatFee { vc := string(v) - return predicate.ChargeFlatFee(sql.FieldEQ(FieldCurrency, vc)) + return predicate.ChargeFlatFee(sql.FieldEQ(FieldFiatCurrencyCode, vc)) } -// CurrencyNEQ applies the NEQ predicate on the "currency" field. -func CurrencyNEQ(v currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCodeNEQ applies the NEQ predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeNEQ(v currencyx.Code) predicate.ChargeFlatFee { vc := string(v) - return predicate.ChargeFlatFee(sql.FieldNEQ(FieldCurrency, vc)) + return predicate.ChargeFlatFee(sql.FieldNEQ(FieldFiatCurrencyCode, vc)) } -// CurrencyIn applies the In predicate on the "currency" field. -func CurrencyIn(vs ...currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCodeIn applies the In predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeIn(vs ...currencyx.Code) predicate.ChargeFlatFee { v := make([]any, len(vs)) for i := range v { v[i] = string(vs[i]) } - return predicate.ChargeFlatFee(sql.FieldIn(FieldCurrency, v...)) + return predicate.ChargeFlatFee(sql.FieldIn(FieldFiatCurrencyCode, v...)) } -// CurrencyNotIn applies the NotIn predicate on the "currency" field. -func CurrencyNotIn(vs ...currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCodeNotIn applies the NotIn predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeNotIn(vs ...currencyx.Code) predicate.ChargeFlatFee { v := make([]any, len(vs)) for i := range v { v[i] = string(vs[i]) } - return predicate.ChargeFlatFee(sql.FieldNotIn(FieldCurrency, v...)) + return predicate.ChargeFlatFee(sql.FieldNotIn(FieldFiatCurrencyCode, v...)) } -// CurrencyGT applies the GT predicate on the "currency" field. -func CurrencyGT(v currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCodeGT applies the GT predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeGT(v currencyx.Code) predicate.ChargeFlatFee { vc := string(v) - return predicate.ChargeFlatFee(sql.FieldGT(FieldCurrency, vc)) + return predicate.ChargeFlatFee(sql.FieldGT(FieldFiatCurrencyCode, vc)) } -// CurrencyGTE applies the GTE predicate on the "currency" field. -func CurrencyGTE(v currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCodeGTE applies the GTE predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeGTE(v currencyx.Code) predicate.ChargeFlatFee { vc := string(v) - return predicate.ChargeFlatFee(sql.FieldGTE(FieldCurrency, vc)) + return predicate.ChargeFlatFee(sql.FieldGTE(FieldFiatCurrencyCode, vc)) } -// CurrencyLT applies the LT predicate on the "currency" field. -func CurrencyLT(v currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCodeLT applies the LT predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeLT(v currencyx.Code) predicate.ChargeFlatFee { vc := string(v) - return predicate.ChargeFlatFee(sql.FieldLT(FieldCurrency, vc)) + return predicate.ChargeFlatFee(sql.FieldLT(FieldFiatCurrencyCode, vc)) } -// CurrencyLTE applies the LTE predicate on the "currency" field. -func CurrencyLTE(v currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCodeLTE applies the LTE predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeLTE(v currencyx.Code) predicate.ChargeFlatFee { vc := string(v) - return predicate.ChargeFlatFee(sql.FieldLTE(FieldCurrency, vc)) + return predicate.ChargeFlatFee(sql.FieldLTE(FieldFiatCurrencyCode, vc)) } -// CurrencyContains applies the Contains predicate on the "currency" field. -func CurrencyContains(v currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCodeContains applies the Contains predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeContains(v currencyx.Code) predicate.ChargeFlatFee { vc := string(v) - return predicate.ChargeFlatFee(sql.FieldContains(FieldCurrency, vc)) + return predicate.ChargeFlatFee(sql.FieldContains(FieldFiatCurrencyCode, vc)) } -// CurrencyHasPrefix applies the HasPrefix predicate on the "currency" field. -func CurrencyHasPrefix(v currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCodeHasPrefix applies the HasPrefix predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeHasPrefix(v currencyx.Code) predicate.ChargeFlatFee { vc := string(v) - return predicate.ChargeFlatFee(sql.FieldHasPrefix(FieldCurrency, vc)) + return predicate.ChargeFlatFee(sql.FieldHasPrefix(FieldFiatCurrencyCode, vc)) } -// CurrencyHasSuffix applies the HasSuffix predicate on the "currency" field. -func CurrencyHasSuffix(v currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCodeHasSuffix applies the HasSuffix predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeHasSuffix(v currencyx.Code) predicate.ChargeFlatFee { vc := string(v) - return predicate.ChargeFlatFee(sql.FieldHasSuffix(FieldCurrency, vc)) + return predicate.ChargeFlatFee(sql.FieldHasSuffix(FieldFiatCurrencyCode, vc)) +} + +// FiatCurrencyCodeIsNil applies the IsNil predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeIsNil() predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldIsNull(FieldFiatCurrencyCode)) } -// CurrencyEqualFold applies the EqualFold predicate on the "currency" field. -func CurrencyEqualFold(v currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCodeNotNil applies the NotNil predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeNotNil() predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldNotNull(FieldFiatCurrencyCode)) +} + +// FiatCurrencyCodeEqualFold applies the EqualFold predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeEqualFold(v currencyx.Code) predicate.ChargeFlatFee { vc := string(v) - return predicate.ChargeFlatFee(sql.FieldEqualFold(FieldCurrency, vc)) + return predicate.ChargeFlatFee(sql.FieldEqualFold(FieldFiatCurrencyCode, vc)) } -// CurrencyContainsFold applies the ContainsFold predicate on the "currency" field. -func CurrencyContainsFold(v currencyx.Code) predicate.ChargeFlatFee { +// FiatCurrencyCodeContainsFold applies the ContainsFold predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeContainsFold(v currencyx.Code) predicate.ChargeFlatFee { vc := string(v) - return predicate.ChargeFlatFee(sql.FieldContainsFold(FieldCurrency, vc)) + return predicate.ChargeFlatFee(sql.FieldContainsFold(FieldFiatCurrencyCode, vc)) +} + +// CustomCurrencyIDEQ applies the EQ predicate on the "custom_currency_id" field. +func CustomCurrencyIDEQ(v string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldEQ(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDNEQ applies the NEQ predicate on the "custom_currency_id" field. +func CustomCurrencyIDNEQ(v string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldNEQ(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDIn applies the In predicate on the "custom_currency_id" field. +func CustomCurrencyIDIn(vs ...string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldIn(FieldCustomCurrencyID, vs...)) +} + +// CustomCurrencyIDNotIn applies the NotIn predicate on the "custom_currency_id" field. +func CustomCurrencyIDNotIn(vs ...string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldNotIn(FieldCustomCurrencyID, vs...)) +} + +// CustomCurrencyIDGT applies the GT predicate on the "custom_currency_id" field. +func CustomCurrencyIDGT(v string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldGT(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDGTE applies the GTE predicate on the "custom_currency_id" field. +func CustomCurrencyIDGTE(v string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldGTE(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDLT applies the LT predicate on the "custom_currency_id" field. +func CustomCurrencyIDLT(v string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldLT(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDLTE applies the LTE predicate on the "custom_currency_id" field. +func CustomCurrencyIDLTE(v string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldLTE(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDContains applies the Contains predicate on the "custom_currency_id" field. +func CustomCurrencyIDContains(v string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldContains(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDHasPrefix applies the HasPrefix predicate on the "custom_currency_id" field. +func CustomCurrencyIDHasPrefix(v string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldHasPrefix(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDHasSuffix applies the HasSuffix predicate on the "custom_currency_id" field. +func CustomCurrencyIDHasSuffix(v string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldHasSuffix(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDIsNil applies the IsNil predicate on the "custom_currency_id" field. +func CustomCurrencyIDIsNil() predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldIsNull(FieldCustomCurrencyID)) +} + +// CustomCurrencyIDNotNil applies the NotNil predicate on the "custom_currency_id" field. +func CustomCurrencyIDNotNil() predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldNotNull(FieldCustomCurrencyID)) +} + +// CustomCurrencyIDEqualFold applies the EqualFold predicate on the "custom_currency_id" field. +func CustomCurrencyIDEqualFold(v string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldEqualFold(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDContainsFold applies the ContainsFold predicate on the "custom_currency_id" field. +func CustomCurrencyIDContainsFold(v string) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(sql.FieldContainsFold(FieldCustomCurrencyID, v)) } // ManagedByEQ applies the EQ predicate on the "managed_by" field. @@ -2281,6 +2371,29 @@ func HasTaxCodeWith(preds ...predicate.TaxCode) predicate.ChargeFlatFee { }) } +// HasCustomCurrency applies the HasEdge predicate on the "custom_currency" edge. +func HasCustomCurrency() predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, CustomCurrencyTable, CustomCurrencyColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasCustomCurrencyWith applies the HasEdge predicate on the "custom_currency" edge with a given conditions (other predicates). +func HasCustomCurrencyWith(preds ...predicate.CustomCurrency) predicate.ChargeFlatFee { + return predicate.ChargeFlatFee(func(s *sql.Selector) { + step := newCustomCurrencyStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.ChargeFlatFee) predicate.ChargeFlatFee { return predicate.ChargeFlatFee(sql.AndPredicates(predicates...)) diff --git a/openmeter/ent/db/chargeflatfee_create.go b/openmeter/ent/db/chargeflatfee_create.go index 0dc733fc47..5e12522eef 100644 --- a/openmeter/ent/db/chargeflatfee_create.go +++ b/openmeter/ent/db/chargeflatfee_create.go @@ -20,6 +20,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfee" "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfeeoverride" "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfeerun" + "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/openmeter/ent/db/customer" dbfeature "github.com/openmeterio/openmeter/openmeter/ent/db/feature" "github.com/openmeterio/openmeter/openmeter/ent/db/subscription" @@ -101,9 +102,31 @@ func (_c *ChargeFlatFeeCreate) SetNillableUniqueReferenceID(v *string) *ChargeFl return _c } -// SetCurrency sets the "currency" field. -func (_c *ChargeFlatFeeCreate) SetCurrency(v currencyx.Code) *ChargeFlatFeeCreate { - _c.mutation.SetCurrency(v) +// SetFiatCurrencyCode sets the "fiat_currency_code" field. +func (_c *ChargeFlatFeeCreate) SetFiatCurrencyCode(v currencyx.Code) *ChargeFlatFeeCreate { + _c.mutation.SetFiatCurrencyCode(v) + return _c +} + +// SetNillableFiatCurrencyCode sets the "fiat_currency_code" field if the given value is not nil. +func (_c *ChargeFlatFeeCreate) SetNillableFiatCurrencyCode(v *currencyx.Code) *ChargeFlatFeeCreate { + if v != nil { + _c.SetFiatCurrencyCode(*v) + } + return _c +} + +// SetCustomCurrencyID sets the "custom_currency_id" field. +func (_c *ChargeFlatFeeCreate) SetCustomCurrencyID(v string) *ChargeFlatFeeCreate { + _c.mutation.SetCustomCurrencyID(v) + return _c +} + +// SetNillableCustomCurrencyID sets the "custom_currency_id" field if the given value is not nil. +func (_c *ChargeFlatFeeCreate) SetNillableCustomCurrencyID(v *string) *ChargeFlatFeeCreate { + if v != nil { + _c.SetCustomCurrencyID(*v) + } return _c } @@ -489,6 +512,11 @@ func (_c *ChargeFlatFeeCreate) SetTaxCode(v *TaxCode) *ChargeFlatFeeCreate { return _c.SetTaxCodeID(v.ID) } +// SetCustomCurrency sets the "custom_currency" edge to the CustomCurrency entity. +func (_c *ChargeFlatFeeCreate) SetCustomCurrency(v *CustomCurrency) *ChargeFlatFeeCreate { + return _c.SetCustomCurrencyID(v.ID) +} + // Mutation returns the ChargeFlatFeeMutation object of the builder. func (_c *ChargeFlatFeeCreate) Mutation() *ChargeFlatFeeMutation { return _c.mutation @@ -574,12 +602,14 @@ func (_c *ChargeFlatFeeCreate) check() error { return &ValidationError{Name: "status", err: fmt.Errorf(`db: validator failed for field "ChargeFlatFee.status": %w`, err)} } } - if _, ok := _c.mutation.Currency(); !ok { - return &ValidationError{Name: "currency", err: errors.New(`db: missing required field "ChargeFlatFee.currency"`)} + if v, ok := _c.mutation.FiatCurrencyCode(); ok { + if err := chargeflatfee.FiatCurrencyCodeValidator(string(v)); err != nil { + return &ValidationError{Name: "fiat_currency_code", err: fmt.Errorf(`db: validator failed for field "ChargeFlatFee.fiat_currency_code": %w`, err)} + } } - if v, ok := _c.mutation.Currency(); ok { - if err := chargeflatfee.CurrencyValidator(string(v)); err != nil { - return &ValidationError{Name: "currency", err: fmt.Errorf(`db: validator failed for field "ChargeFlatFee.currency": %w`, err)} + if v, ok := _c.mutation.CustomCurrencyID(); ok { + if err := chargeflatfee.CustomCurrencyIDValidator(v); err != nil { + return &ValidationError{Name: "custom_currency_id", err: fmt.Errorf(`db: validator failed for field "ChargeFlatFee.custom_currency_id": %w`, err)} } } if _, ok := _c.mutation.ManagedBy(); !ok { @@ -748,9 +778,9 @@ func (_c *ChargeFlatFeeCreate) createSpec() (*ChargeFlatFee, *sqlgraph.CreateSpe _spec.SetField(chargeflatfee.FieldUniqueReferenceID, field.TypeString, value) _node.UniqueReferenceID = &value } - if value, ok := _c.mutation.Currency(); ok { - _spec.SetField(chargeflatfee.FieldCurrency, field.TypeString, value) - _node.Currency = value + if value, ok := _c.mutation.FiatCurrencyCode(); ok { + _spec.SetField(chargeflatfee.FieldFiatCurrencyCode, field.TypeString, value) + _node.FiatCurrencyCode = &value } if value, ok := _c.mutation.ManagedBy(); ok { _spec.SetField(chargeflatfee.FieldManagedBy, field.TypeEnum, value) @@ -1007,6 +1037,23 @@ func (_c *ChargeFlatFeeCreate) createSpec() (*ChargeFlatFee, *sqlgraph.CreateSpe _node.TaxCodeID = nodes[0] _spec.Edges = append(_spec.Edges, edge) } + if nodes := _c.mutation.CustomCurrencyIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: chargeflatfee.CustomCurrencyTable, + Columns: []string{chargeflatfee.CustomCurrencyColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(customcurrency.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.CustomCurrencyID = &nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec, nil } @@ -1442,8 +1489,11 @@ func (u *ChargeFlatFeeUpsertOne) UpdateNewValues() *ChargeFlatFeeUpsertOne { if _, exists := u.create.mutation.UniqueReferenceID(); exists { s.SetIgnore(chargeflatfee.FieldUniqueReferenceID) } - if _, exists := u.create.mutation.Currency(); exists { - s.SetIgnore(chargeflatfee.FieldCurrency) + if _, exists := u.create.mutation.FiatCurrencyCode(); exists { + s.SetIgnore(chargeflatfee.FieldFiatCurrencyCode) + } + if _, exists := u.create.mutation.CustomCurrencyID(); exists { + s.SetIgnore(chargeflatfee.FieldCustomCurrencyID) } if _, exists := u.create.mutation.ManagedBy(); exists { s.SetIgnore(chargeflatfee.FieldManagedBy) @@ -2115,8 +2165,11 @@ func (u *ChargeFlatFeeUpsertBulk) UpdateNewValues() *ChargeFlatFeeUpsertBulk { if _, exists := b.mutation.UniqueReferenceID(); exists { s.SetIgnore(chargeflatfee.FieldUniqueReferenceID) } - if _, exists := b.mutation.Currency(); exists { - s.SetIgnore(chargeflatfee.FieldCurrency) + if _, exists := b.mutation.FiatCurrencyCode(); exists { + s.SetIgnore(chargeflatfee.FieldFiatCurrencyCode) + } + if _, exists := b.mutation.CustomCurrencyID(); exists { + s.SetIgnore(chargeflatfee.FieldCustomCurrencyID) } if _, exists := b.mutation.ManagedBy(); exists { s.SetIgnore(chargeflatfee.FieldManagedBy) diff --git a/openmeter/ent/db/chargeflatfee_query.go b/openmeter/ent/db/chargeflatfee_query.go index d70b28c904..04c16417c8 100644 --- a/openmeter/ent/db/chargeflatfee_query.go +++ b/openmeter/ent/db/chargeflatfee_query.go @@ -17,6 +17,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfee" "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfeeoverride" "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfeerun" + "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/openmeter/ent/db/customer" dbfeature "github.com/openmeterio/openmeter/openmeter/ent/db/feature" "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" @@ -43,6 +44,7 @@ type ChargeFlatFeeQuery struct { withCustomer *CustomerQuery withFeature *FeatureQuery withTaxCode *TaxCodeQuery + withCustomCurrency *CustomCurrencyQuery modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector @@ -300,6 +302,28 @@ func (_q *ChargeFlatFeeQuery) QueryTaxCode() *TaxCodeQuery { return query } +// QueryCustomCurrency chains the current query on the "custom_currency" edge. +func (_q *ChargeFlatFeeQuery) QueryCustomCurrency() *CustomCurrencyQuery { + query := (&CustomCurrencyClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(chargeflatfee.Table, chargeflatfee.FieldID, selector), + sqlgraph.To(customcurrency.Table, customcurrency.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, chargeflatfee.CustomCurrencyTable, chargeflatfee.CustomCurrencyColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first ChargeFlatFee entity from the query. // Returns a *NotFoundError when no ChargeFlatFee was found. func (_q *ChargeFlatFeeQuery) First(ctx context.Context) (*ChargeFlatFee, error) { @@ -502,6 +526,7 @@ func (_q *ChargeFlatFeeQuery) Clone() *ChargeFlatFeeQuery { withCustomer: _q.withCustomer.Clone(), withFeature: _q.withFeature.Clone(), withTaxCode: _q.withTaxCode.Clone(), + withCustomCurrency: _q.withCustomCurrency.Clone(), // clone intermediate query. sql: _q.sql.Clone(), path: _q.path, @@ -618,6 +643,17 @@ func (_q *ChargeFlatFeeQuery) WithTaxCode(opts ...func(*TaxCodeQuery)) *ChargeFl return _q } +// WithCustomCurrency tells the query-builder to eager-load the nodes that are connected to +// the "custom_currency" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ChargeFlatFeeQuery) WithCustomCurrency(opts ...func(*CustomCurrencyQuery)) *ChargeFlatFeeQuery { + query := (&CustomCurrencyClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withCustomCurrency = query + return _q +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -696,7 +732,7 @@ func (_q *ChargeFlatFeeQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([ var ( nodes = []*ChargeFlatFee{} _spec = _q.querySpec() - loadedTypes = [10]bool{ + loadedTypes = [11]bool{ _q.withRuns != nil, _q.withCurrentRun != nil, _q.withCharge != nil, @@ -707,6 +743,7 @@ func (_q *ChargeFlatFeeQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([ _q.withCustomer != nil, _q.withFeature != nil, _q.withTaxCode != nil, + _q.withCustomCurrency != nil, } ) _spec.ScanValues = func(columns []string) ([]any, error) { @@ -791,6 +828,12 @@ func (_q *ChargeFlatFeeQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([ return nil, err } } + if query := _q.withCustomCurrency; query != nil { + if err := _q.loadCustomCurrency(ctx, query, nodes, nil, + func(n *ChargeFlatFee, e *CustomCurrency) { n.Edges.CustomCurrency = e }); err != nil { + return nil, err + } + } return nodes, nil } @@ -1099,6 +1142,38 @@ func (_q *ChargeFlatFeeQuery) loadTaxCode(ctx context.Context, query *TaxCodeQue } return nil } +func (_q *ChargeFlatFeeQuery) loadCustomCurrency(ctx context.Context, query *CustomCurrencyQuery, nodes []*ChargeFlatFee, init func(*ChargeFlatFee), assign func(*ChargeFlatFee, *CustomCurrency)) error { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*ChargeFlatFee) + for i := range nodes { + if nodes[i].CustomCurrencyID == nil { + continue + } + fk := *nodes[i].CustomCurrencyID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(customcurrency.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "custom_currency_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} func (_q *ChargeFlatFeeQuery) sqlCount(ctx context.Context) (int, error) { _spec := _q.querySpec() @@ -1149,6 +1224,9 @@ func (_q *ChargeFlatFeeQuery) querySpec() *sqlgraph.QuerySpec { if _q.withTaxCode != nil { _spec.Node.AddColumnOnce(chargeflatfee.FieldTaxCodeID) } + if _q.withCustomCurrency != nil { + _spec.Node.AddColumnOnce(chargeflatfee.FieldCustomCurrencyID) + } } if ps := _q.predicates; len(ps) > 0 { _spec.Predicate = func(selector *sql.Selector) { diff --git a/openmeter/ent/db/chargeflatfee_update.go b/openmeter/ent/db/chargeflatfee_update.go index 72ca2ec61d..da9266b6ba 100644 --- a/openmeter/ent/db/chargeflatfee_update.go +++ b/openmeter/ent/db/chargeflatfee_update.go @@ -637,6 +637,9 @@ func (_u *ChargeFlatFeeUpdate) sqlSave(ctx context.Context) (_node int, err erro if _u.mutation.UniqueReferenceIDCleared() { _spec.ClearField(chargeflatfee.FieldUniqueReferenceID, field.TypeString) } + if _u.mutation.FiatCurrencyCodeCleared() { + _spec.ClearField(chargeflatfee.FieldFiatCurrencyCode, field.TypeString) + } if value, ok := _u.mutation.AdvanceAfter(); ok { _spec.SetField(chargeflatfee.FieldAdvanceAfter, field.TypeTime, value) } @@ -1523,6 +1526,9 @@ func (_u *ChargeFlatFeeUpdateOne) sqlSave(ctx context.Context) (_node *ChargeFla if _u.mutation.UniqueReferenceIDCleared() { _spec.ClearField(chargeflatfee.FieldUniqueReferenceID, field.TypeString) } + if _u.mutation.FiatCurrencyCodeCleared() { + _spec.ClearField(chargeflatfee.FieldFiatCurrencyCode, field.TypeString) + } if value, ok := _u.mutation.AdvanceAfter(); ok { _spec.SetField(chargeflatfee.FieldAdvanceAfter, field.TypeTime, value) } diff --git a/openmeter/ent/db/chargessearchv1.go b/openmeter/ent/db/chargessearchv1.go index 5d21cdc0ec..a774014177 100644 --- a/openmeter/ent/db/chargessearchv1.go +++ b/openmeter/ent/db/chargessearchv1.go @@ -43,8 +43,10 @@ type ChargesSearchV1 struct { Status meta.ChargeStatus `json:"status,omitempty"` // UniqueReferenceID holds the value of the "unique_reference_id" field. UniqueReferenceID *string `json:"unique_reference_id,omitempty"` - // Currency holds the value of the "currency" field. - Currency currencyx.Code `json:"currency,omitempty"` + // FiatCurrencyCode holds the value of the "fiat_currency_code" field. + FiatCurrencyCode *currencyx.Code `json:"fiat_currency_code,omitempty"` + // CustomCurrencyID holds the value of the "custom_currency_id" field. + CustomCurrencyID *string `json:"custom_currency_id,omitempty"` // ManagedBy holds the value of the "managed_by" field. ManagedBy billing.InvoiceLineManagedBy `json:"managed_by,omitempty"` // SubscriptionID holds the value of the "subscription_id" field. @@ -87,7 +89,7 @@ func (*ChargesSearchV1) scanValues(columns []string) ([]any, error) { switch columns[i] { case chargessearchv1.FieldAnnotations, chargessearchv1.FieldMetadata: values[i] = new([]byte) - case chargessearchv1.FieldType, chargessearchv1.FieldCustomerID, chargessearchv1.FieldStatus, chargessearchv1.FieldUniqueReferenceID, chargessearchv1.FieldCurrency, chargessearchv1.FieldManagedBy, chargessearchv1.FieldSubscriptionID, chargessearchv1.FieldSubscriptionPhaseID, chargessearchv1.FieldSubscriptionItemID, chargessearchv1.FieldTaxCodeID, chargessearchv1.FieldTaxBehavior, chargessearchv1.FieldID, chargessearchv1.FieldNamespace, chargessearchv1.FieldName, chargessearchv1.FieldDescription: + case chargessearchv1.FieldType, chargessearchv1.FieldCustomerID, chargessearchv1.FieldStatus, chargessearchv1.FieldUniqueReferenceID, chargessearchv1.FieldFiatCurrencyCode, chargessearchv1.FieldCustomCurrencyID, chargessearchv1.FieldManagedBy, chargessearchv1.FieldSubscriptionID, chargessearchv1.FieldSubscriptionPhaseID, chargessearchv1.FieldSubscriptionItemID, chargessearchv1.FieldTaxCodeID, chargessearchv1.FieldTaxBehavior, chargessearchv1.FieldID, chargessearchv1.FieldNamespace, chargessearchv1.FieldName, chargessearchv1.FieldDescription: values[i] = new(sql.NullString) case chargessearchv1.FieldBaseIntentDeletedAt, chargessearchv1.FieldServicePeriodFrom, chargessearchv1.FieldServicePeriodTo, chargessearchv1.FieldBillingPeriodFrom, chargessearchv1.FieldBillingPeriodTo, chargessearchv1.FieldFullServicePeriodFrom, chargessearchv1.FieldFullServicePeriodTo, chargessearchv1.FieldAdvanceAfter, chargessearchv1.FieldCreatedAt, chargessearchv1.FieldUpdatedAt, chargessearchv1.FieldDeletedAt: values[i] = new(sql.NullTime) @@ -174,11 +176,19 @@ func (_m *ChargesSearchV1) assignValues(columns []string, values []any) error { _m.UniqueReferenceID = new(string) *_m.UniqueReferenceID = value.String } - case chargessearchv1.FieldCurrency: + case chargessearchv1.FieldFiatCurrencyCode: if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field currency", values[i]) + return fmt.Errorf("unexpected type %T for field fiat_currency_code", values[i]) } else if value.Valid { - _m.Currency = currencyx.Code(value.String) + _m.FiatCurrencyCode = new(currencyx.Code) + *_m.FiatCurrencyCode = currencyx.Code(value.String) + } + case chargessearchv1.FieldCustomCurrencyID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field custom_currency_id", values[i]) + } else if value.Valid { + _m.CustomCurrencyID = new(string) + *_m.CustomCurrencyID = value.String } case chargessearchv1.FieldManagedBy: if value, ok := values[i].(*sql.NullString); !ok { @@ -352,8 +362,15 @@ func (_m *ChargesSearchV1) String() string { builder.WriteString(*v) } builder.WriteString(", ") - builder.WriteString("currency=") - builder.WriteString(fmt.Sprintf("%v", _m.Currency)) + if v := _m.FiatCurrencyCode; v != nil { + builder.WriteString("fiat_currency_code=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + if v := _m.CustomCurrencyID; v != nil { + builder.WriteString("custom_currency_id=") + builder.WriteString(*v) + } builder.WriteString(", ") builder.WriteString("managed_by=") builder.WriteString(fmt.Sprintf("%v", _m.ManagedBy)) diff --git a/openmeter/ent/db/chargessearchv1/chargessearchv1.go b/openmeter/ent/db/chargessearchv1/chargessearchv1.go index c047e99631..0d6ec6841e 100644 --- a/openmeter/ent/db/chargessearchv1/chargessearchv1.go +++ b/openmeter/ent/db/chargessearchv1/chargessearchv1.go @@ -37,8 +37,10 @@ const ( FieldStatus = "status" // FieldUniqueReferenceID holds the string denoting the unique_reference_id field in the database. FieldUniqueReferenceID = "unique_reference_id" - // FieldCurrency holds the string denoting the currency field in the database. - FieldCurrency = "currency" + // FieldFiatCurrencyCode holds the string denoting the fiat_currency_code field in the database. + FieldFiatCurrencyCode = "currency" + // FieldCustomCurrencyID holds the string denoting the custom_currency_id field in the database. + FieldCustomCurrencyID = "custom_currency_id" // FieldManagedBy holds the string denoting the managed_by field in the database. FieldManagedBy = "managed_by" // FieldSubscriptionID holds the string denoting the subscription_id field in the database. @@ -88,7 +90,8 @@ var Columns = []string{ FieldFullServicePeriodTo, FieldStatus, FieldUniqueReferenceID, - FieldCurrency, + FieldFiatCurrencyCode, + FieldCustomCurrencyID, FieldManagedBy, FieldSubscriptionID, FieldSubscriptionPhaseID, @@ -120,8 +123,10 @@ func ValidColumn(column string) bool { var ( // CustomerIDValidator is a validator for the "customer_id" field. It is called by the builders before save. CustomerIDValidator func(string) error - // CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. - CurrencyValidator func(string) error + // FiatCurrencyCodeValidator is a validator for the "fiat_currency_code" field. It is called by the builders before save. + FiatCurrencyCodeValidator func(string) error + // CustomCurrencyIDValidator is a validator for the "custom_currency_id" field. It is called by the builders before save. + CustomCurrencyIDValidator func(string) error // TaxCodeIDValidator is a validator for the "tax_code_id" field. It is called by the builders before save. TaxCodeIDValidator func(string) error // DefaultID holds the default value on creation for the "id" field. @@ -224,9 +229,14 @@ func ByUniqueReferenceID(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldUniqueReferenceID, opts...).ToFunc() } -// ByCurrency orders the results by the currency field. -func ByCurrency(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldCurrency, opts...).ToFunc() +// ByFiatCurrencyCode orders the results by the fiat_currency_code field. +func ByFiatCurrencyCode(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldFiatCurrencyCode, opts...).ToFunc() +} + +// ByCustomCurrencyID orders the results by the custom_currency_id field. +func ByCustomCurrencyID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCustomCurrencyID, opts...).ToFunc() } // ByManagedBy orders the results by the managed_by field. diff --git a/openmeter/ent/db/chargessearchv1/where.go b/openmeter/ent/db/chargessearchv1/where.go index c7c4edb527..5786e36887 100644 --- a/openmeter/ent/db/chargessearchv1/where.go +++ b/openmeter/ent/db/chargessearchv1/where.go @@ -64,10 +64,15 @@ func UniqueReferenceID(v string) predicate.ChargesSearchV1 { return predicate.ChargesSearchV1(sql.FieldEQ(FieldUniqueReferenceID, v)) } -// Currency applies equality check predicate on the "currency" field. It's identical to CurrencyEQ. -func Currency(v currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCode applies equality check predicate on the "fiat_currency_code" field. It's identical to FiatCurrencyCodeEQ. +func FiatCurrencyCode(v currencyx.Code) predicate.ChargesSearchV1 { vc := string(v) - return predicate.ChargesSearchV1(sql.FieldEQ(FieldCurrency, vc)) + return predicate.ChargesSearchV1(sql.FieldEQ(FieldFiatCurrencyCode, vc)) +} + +// CustomCurrencyID applies equality check predicate on the "custom_currency_id" field. It's identical to CustomCurrencyIDEQ. +func CustomCurrencyID(v string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldEQ(FieldCustomCurrencyID, v)) } // SubscriptionID applies equality check predicate on the "subscription_id" field. It's identical to SubscriptionIDEQ. @@ -674,88 +679,173 @@ func UniqueReferenceIDContainsFold(v string) predicate.ChargesSearchV1 { return predicate.ChargesSearchV1(sql.FieldContainsFold(FieldUniqueReferenceID, v)) } -// CurrencyEQ applies the EQ predicate on the "currency" field. -func CurrencyEQ(v currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCodeEQ applies the EQ predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeEQ(v currencyx.Code) predicate.ChargesSearchV1 { vc := string(v) - return predicate.ChargesSearchV1(sql.FieldEQ(FieldCurrency, vc)) + return predicate.ChargesSearchV1(sql.FieldEQ(FieldFiatCurrencyCode, vc)) } -// CurrencyNEQ applies the NEQ predicate on the "currency" field. -func CurrencyNEQ(v currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCodeNEQ applies the NEQ predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeNEQ(v currencyx.Code) predicate.ChargesSearchV1 { vc := string(v) - return predicate.ChargesSearchV1(sql.FieldNEQ(FieldCurrency, vc)) + return predicate.ChargesSearchV1(sql.FieldNEQ(FieldFiatCurrencyCode, vc)) } -// CurrencyIn applies the In predicate on the "currency" field. -func CurrencyIn(vs ...currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCodeIn applies the In predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeIn(vs ...currencyx.Code) predicate.ChargesSearchV1 { v := make([]any, len(vs)) for i := range v { v[i] = string(vs[i]) } - return predicate.ChargesSearchV1(sql.FieldIn(FieldCurrency, v...)) + return predicate.ChargesSearchV1(sql.FieldIn(FieldFiatCurrencyCode, v...)) } -// CurrencyNotIn applies the NotIn predicate on the "currency" field. -func CurrencyNotIn(vs ...currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCodeNotIn applies the NotIn predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeNotIn(vs ...currencyx.Code) predicate.ChargesSearchV1 { v := make([]any, len(vs)) for i := range v { v[i] = string(vs[i]) } - return predicate.ChargesSearchV1(sql.FieldNotIn(FieldCurrency, v...)) + return predicate.ChargesSearchV1(sql.FieldNotIn(FieldFiatCurrencyCode, v...)) } -// CurrencyGT applies the GT predicate on the "currency" field. -func CurrencyGT(v currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCodeGT applies the GT predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeGT(v currencyx.Code) predicate.ChargesSearchV1 { vc := string(v) - return predicate.ChargesSearchV1(sql.FieldGT(FieldCurrency, vc)) + return predicate.ChargesSearchV1(sql.FieldGT(FieldFiatCurrencyCode, vc)) } -// CurrencyGTE applies the GTE predicate on the "currency" field. -func CurrencyGTE(v currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCodeGTE applies the GTE predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeGTE(v currencyx.Code) predicate.ChargesSearchV1 { vc := string(v) - return predicate.ChargesSearchV1(sql.FieldGTE(FieldCurrency, vc)) + return predicate.ChargesSearchV1(sql.FieldGTE(FieldFiatCurrencyCode, vc)) } -// CurrencyLT applies the LT predicate on the "currency" field. -func CurrencyLT(v currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCodeLT applies the LT predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeLT(v currencyx.Code) predicate.ChargesSearchV1 { vc := string(v) - return predicate.ChargesSearchV1(sql.FieldLT(FieldCurrency, vc)) + return predicate.ChargesSearchV1(sql.FieldLT(FieldFiatCurrencyCode, vc)) } -// CurrencyLTE applies the LTE predicate on the "currency" field. -func CurrencyLTE(v currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCodeLTE applies the LTE predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeLTE(v currencyx.Code) predicate.ChargesSearchV1 { vc := string(v) - return predicate.ChargesSearchV1(sql.FieldLTE(FieldCurrency, vc)) + return predicate.ChargesSearchV1(sql.FieldLTE(FieldFiatCurrencyCode, vc)) } -// CurrencyContains applies the Contains predicate on the "currency" field. -func CurrencyContains(v currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCodeContains applies the Contains predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeContains(v currencyx.Code) predicate.ChargesSearchV1 { vc := string(v) - return predicate.ChargesSearchV1(sql.FieldContains(FieldCurrency, vc)) + return predicate.ChargesSearchV1(sql.FieldContains(FieldFiatCurrencyCode, vc)) } -// CurrencyHasPrefix applies the HasPrefix predicate on the "currency" field. -func CurrencyHasPrefix(v currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCodeHasPrefix applies the HasPrefix predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeHasPrefix(v currencyx.Code) predicate.ChargesSearchV1 { vc := string(v) - return predicate.ChargesSearchV1(sql.FieldHasPrefix(FieldCurrency, vc)) + return predicate.ChargesSearchV1(sql.FieldHasPrefix(FieldFiatCurrencyCode, vc)) } -// CurrencyHasSuffix applies the HasSuffix predicate on the "currency" field. -func CurrencyHasSuffix(v currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCodeHasSuffix applies the HasSuffix predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeHasSuffix(v currencyx.Code) predicate.ChargesSearchV1 { vc := string(v) - return predicate.ChargesSearchV1(sql.FieldHasSuffix(FieldCurrency, vc)) + return predicate.ChargesSearchV1(sql.FieldHasSuffix(FieldFiatCurrencyCode, vc)) } -// CurrencyEqualFold applies the EqualFold predicate on the "currency" field. -func CurrencyEqualFold(v currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCodeIsNil applies the IsNil predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeIsNil() predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldIsNull(FieldFiatCurrencyCode)) +} + +// FiatCurrencyCodeNotNil applies the NotNil predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeNotNil() predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldNotNull(FieldFiatCurrencyCode)) +} + +// FiatCurrencyCodeEqualFold applies the EqualFold predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeEqualFold(v currencyx.Code) predicate.ChargesSearchV1 { vc := string(v) - return predicate.ChargesSearchV1(sql.FieldEqualFold(FieldCurrency, vc)) + return predicate.ChargesSearchV1(sql.FieldEqualFold(FieldFiatCurrencyCode, vc)) } -// CurrencyContainsFold applies the ContainsFold predicate on the "currency" field. -func CurrencyContainsFold(v currencyx.Code) predicate.ChargesSearchV1 { +// FiatCurrencyCodeContainsFold applies the ContainsFold predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeContainsFold(v currencyx.Code) predicate.ChargesSearchV1 { vc := string(v) - return predicate.ChargesSearchV1(sql.FieldContainsFold(FieldCurrency, vc)) + return predicate.ChargesSearchV1(sql.FieldContainsFold(FieldFiatCurrencyCode, vc)) +} + +// CustomCurrencyIDEQ applies the EQ predicate on the "custom_currency_id" field. +func CustomCurrencyIDEQ(v string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldEQ(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDNEQ applies the NEQ predicate on the "custom_currency_id" field. +func CustomCurrencyIDNEQ(v string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldNEQ(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDIn applies the In predicate on the "custom_currency_id" field. +func CustomCurrencyIDIn(vs ...string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldIn(FieldCustomCurrencyID, vs...)) +} + +// CustomCurrencyIDNotIn applies the NotIn predicate on the "custom_currency_id" field. +func CustomCurrencyIDNotIn(vs ...string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldNotIn(FieldCustomCurrencyID, vs...)) +} + +// CustomCurrencyIDGT applies the GT predicate on the "custom_currency_id" field. +func CustomCurrencyIDGT(v string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldGT(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDGTE applies the GTE predicate on the "custom_currency_id" field. +func CustomCurrencyIDGTE(v string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldGTE(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDLT applies the LT predicate on the "custom_currency_id" field. +func CustomCurrencyIDLT(v string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldLT(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDLTE applies the LTE predicate on the "custom_currency_id" field. +func CustomCurrencyIDLTE(v string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldLTE(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDContains applies the Contains predicate on the "custom_currency_id" field. +func CustomCurrencyIDContains(v string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldContains(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDHasPrefix applies the HasPrefix predicate on the "custom_currency_id" field. +func CustomCurrencyIDHasPrefix(v string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldHasPrefix(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDHasSuffix applies the HasSuffix predicate on the "custom_currency_id" field. +func CustomCurrencyIDHasSuffix(v string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldHasSuffix(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDIsNil applies the IsNil predicate on the "custom_currency_id" field. +func CustomCurrencyIDIsNil() predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldIsNull(FieldCustomCurrencyID)) +} + +// CustomCurrencyIDNotNil applies the NotNil predicate on the "custom_currency_id" field. +func CustomCurrencyIDNotNil() predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldNotNull(FieldCustomCurrencyID)) +} + +// CustomCurrencyIDEqualFold applies the EqualFold predicate on the "custom_currency_id" field. +func CustomCurrencyIDEqualFold(v string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldEqualFold(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDContainsFold applies the ContainsFold predicate on the "custom_currency_id" field. +func CustomCurrencyIDContainsFold(v string) predicate.ChargesSearchV1 { + return predicate.ChargesSearchV1(sql.FieldContainsFold(FieldCustomCurrencyID, v)) } // ManagedByEQ applies the EQ predicate on the "managed_by" field. diff --git a/openmeter/ent/db/chargeusagebased.go b/openmeter/ent/db/chargeusagebased.go index ce2c0d8eb3..a1268aa5ac 100644 --- a/openmeter/ent/db/chargeusagebased.go +++ b/openmeter/ent/db/chargeusagebased.go @@ -17,6 +17,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebased" "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebasedoverride" "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebasedruns" + "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/openmeter/ent/db/customer" dbfeature "github.com/openmeterio/openmeter/openmeter/ent/db/feature" "github.com/openmeterio/openmeter/openmeter/ent/db/subscription" @@ -51,8 +52,10 @@ type ChargeUsageBased struct { Status meta.ChargeStatus `json:"status,omitempty"` // UniqueReferenceID holds the value of the "unique_reference_id" field. UniqueReferenceID *string `json:"unique_reference_id,omitempty"` - // Currency holds the value of the "currency" field. - Currency currencyx.Code `json:"currency,omitempty"` + // FiatCurrencyCode holds the value of the "fiat_currency_code" field. + FiatCurrencyCode *currencyx.Code `json:"fiat_currency_code,omitempty"` + // CustomCurrencyID holds the value of the "custom_currency_id" field. + CustomCurrencyID *string `json:"custom_currency_id,omitempty"` // ManagedBy holds the value of the "managed_by" field. ManagedBy billing.InvoiceLineManagedBy `json:"managed_by,omitempty"` // SubscriptionID holds the value of the "subscription_id" field. @@ -135,9 +138,11 @@ type ChargeUsageBasedEdges struct { Feature *Feature `json:"feature,omitempty"` // TaxCode holds the value of the tax_code edge. TaxCode *TaxCode `json:"tax_code,omitempty"` + // CustomCurrency holds the value of the custom_currency edge. + CustomCurrency *CustomCurrency `json:"custom_currency,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [11]bool + loadedTypes [12]bool } // RunsOrErr returns the Runs value or an error if the edge @@ -257,6 +262,17 @@ func (e ChargeUsageBasedEdges) TaxCodeOrErr() (*TaxCode, error) { return nil, &NotLoadedError{edge: "tax_code"} } +// CustomCurrencyOrErr returns the CustomCurrency value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e ChargeUsageBasedEdges) CustomCurrencyOrErr() (*CustomCurrency, error) { + if e.CustomCurrency != nil { + return e.CustomCurrency, nil + } else if e.loadedTypes[11] { + return nil, &NotFoundError{label: customcurrency.Label} + } + return nil, &NotLoadedError{edge: "custom_currency"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*ChargeUsageBased) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -264,7 +280,7 @@ func (*ChargeUsageBased) scanValues(columns []string) ([]any, error) { switch columns[i] { case chargeusagebased.FieldAnnotations, chargeusagebased.FieldMetadata: values[i] = new([]byte) - case chargeusagebased.FieldID, chargeusagebased.FieldCustomerID, chargeusagebased.FieldStatus, chargeusagebased.FieldUniqueReferenceID, chargeusagebased.FieldCurrency, chargeusagebased.FieldManagedBy, chargeusagebased.FieldSubscriptionID, chargeusagebased.FieldSubscriptionPhaseID, chargeusagebased.FieldSubscriptionItemID, chargeusagebased.FieldTaxCodeID, chargeusagebased.FieldTaxBehavior, chargeusagebased.FieldNamespace, chargeusagebased.FieldName, chargeusagebased.FieldDescription, chargeusagebased.FieldSettlementMode, chargeusagebased.FieldFeatureKey, chargeusagebased.FieldFeatureID, chargeusagebased.FieldRatingEngine, chargeusagebased.FieldCurrentRealizationRunID, chargeusagebased.FieldStatusDetailed: + case chargeusagebased.FieldID, chargeusagebased.FieldCustomerID, chargeusagebased.FieldStatus, chargeusagebased.FieldUniqueReferenceID, chargeusagebased.FieldFiatCurrencyCode, chargeusagebased.FieldCustomCurrencyID, chargeusagebased.FieldManagedBy, chargeusagebased.FieldSubscriptionID, chargeusagebased.FieldSubscriptionPhaseID, chargeusagebased.FieldSubscriptionItemID, chargeusagebased.FieldTaxCodeID, chargeusagebased.FieldTaxBehavior, chargeusagebased.FieldNamespace, chargeusagebased.FieldName, chargeusagebased.FieldDescription, chargeusagebased.FieldSettlementMode, chargeusagebased.FieldFeatureKey, chargeusagebased.FieldFeatureID, chargeusagebased.FieldRatingEngine, chargeusagebased.FieldCurrentRealizationRunID, chargeusagebased.FieldStatusDetailed: values[i] = new(sql.NullString) case chargeusagebased.FieldServicePeriodFrom, chargeusagebased.FieldServicePeriodTo, chargeusagebased.FieldBillingPeriodFrom, chargeusagebased.FieldBillingPeriodTo, chargeusagebased.FieldFullServicePeriodFrom, chargeusagebased.FieldFullServicePeriodTo, chargeusagebased.FieldAdvanceAfter, chargeusagebased.FieldCreatedAt, chargeusagebased.FieldUpdatedAt, chargeusagebased.FieldDeletedAt, chargeusagebased.FieldInvoiceAt, chargeusagebased.FieldIntentDeletedAt: values[i] = new(sql.NullTime) @@ -350,11 +366,19 @@ func (_m *ChargeUsageBased) assignValues(columns []string, values []any) error { _m.UniqueReferenceID = new(string) *_m.UniqueReferenceID = value.String } - case chargeusagebased.FieldCurrency: + case chargeusagebased.FieldFiatCurrencyCode: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field fiat_currency_code", values[i]) + } else if value.Valid { + _m.FiatCurrencyCode = new(currencyx.Code) + *_m.FiatCurrencyCode = currencyx.Code(value.String) + } + case chargeusagebased.FieldCustomCurrencyID: if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field currency", values[i]) + return fmt.Errorf("unexpected type %T for field custom_currency_id", values[i]) } else if value.Valid { - _m.Currency = currencyx.Code(value.String) + _m.CustomCurrencyID = new(string) + *_m.CustomCurrencyID = value.String } case chargeusagebased.FieldManagedBy: if value, ok := values[i].(*sql.NullString); !ok { @@ -593,6 +617,11 @@ func (_m *ChargeUsageBased) QueryTaxCode() *TaxCodeQuery { return NewChargeUsageBasedClient(_m.config).QueryTaxCode(_m) } +// QueryCustomCurrency queries the "custom_currency" edge of the ChargeUsageBased entity. +func (_m *ChargeUsageBased) QueryCustomCurrency() *CustomCurrencyQuery { + return NewChargeUsageBasedClient(_m.config).QueryCustomCurrency(_m) +} + // Update returns a builder for updating this ChargeUsageBased. // Note that you need to call ChargeUsageBased.Unwrap() before calling this method if this ChargeUsageBased // was returned from a transaction, and the transaction was committed or rolled back. @@ -645,8 +674,15 @@ func (_m *ChargeUsageBased) String() string { builder.WriteString(*v) } builder.WriteString(", ") - builder.WriteString("currency=") - builder.WriteString(fmt.Sprintf("%v", _m.Currency)) + if v := _m.FiatCurrencyCode; v != nil { + builder.WriteString("fiat_currency_code=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + if v := _m.CustomCurrencyID; v != nil { + builder.WriteString("custom_currency_id=") + builder.WriteString(*v) + } builder.WriteString(", ") builder.WriteString("managed_by=") builder.WriteString(fmt.Sprintf("%v", _m.ManagedBy)) diff --git a/openmeter/ent/db/chargeusagebased/chargeusagebased.go b/openmeter/ent/db/chargeusagebased/chargeusagebased.go index e973f2784e..5b47f2ee5e 100644 --- a/openmeter/ent/db/chargeusagebased/chargeusagebased.go +++ b/openmeter/ent/db/chargeusagebased/chargeusagebased.go @@ -38,8 +38,10 @@ const ( FieldStatus = "status" // FieldUniqueReferenceID holds the string denoting the unique_reference_id field in the database. FieldUniqueReferenceID = "unique_reference_id" - // FieldCurrency holds the string denoting the currency field in the database. - FieldCurrency = "currency" + // FieldFiatCurrencyCode holds the string denoting the fiat_currency_code field in the database. + FieldFiatCurrencyCode = "currency" + // FieldCustomCurrencyID holds the string denoting the custom_currency_id field in the database. + FieldCustomCurrencyID = "custom_currency_id" // FieldManagedBy holds the string denoting the managed_by field in the database. FieldManagedBy = "managed_by" // FieldSubscriptionID holds the string denoting the subscription_id field in the database. @@ -114,6 +116,8 @@ const ( EdgeFeature = "feature" // EdgeTaxCode holds the string denoting the tax_code edge name in mutations. EdgeTaxCode = "tax_code" + // EdgeCustomCurrency holds the string denoting the custom_currency edge name in mutations. + EdgeCustomCurrency = "custom_currency" // Table holds the table name of the chargeusagebased in the database. Table = "charge_usage_based" // RunsTable is the table that holds the runs relation/edge. @@ -193,6 +197,13 @@ const ( TaxCodeInverseTable = "tax_codes" // TaxCodeColumn is the table column denoting the tax_code relation/edge. TaxCodeColumn = "tax_code_id" + // CustomCurrencyTable is the table that holds the custom_currency relation/edge. + CustomCurrencyTable = "charge_usage_based" + // CustomCurrencyInverseTable is the table name for the CustomCurrency entity. + // It exists in this package in order to avoid circular dependency with the "customcurrency" package. + CustomCurrencyInverseTable = "custom_currencies" + // CustomCurrencyColumn is the table column denoting the custom_currency relation/edge. + CustomCurrencyColumn = "custom_currency_id" ) // Columns holds all SQL columns for chargeusagebased fields. @@ -207,7 +218,8 @@ var Columns = []string{ FieldFullServicePeriodTo, FieldStatus, FieldUniqueReferenceID, - FieldCurrency, + FieldFiatCurrencyCode, + FieldCustomCurrencyID, FieldManagedBy, FieldSubscriptionID, FieldSubscriptionPhaseID, @@ -249,8 +261,10 @@ func ValidColumn(column string) bool { var ( // CustomerIDValidator is a validator for the "customer_id" field. It is called by the builders before save. CustomerIDValidator func(string) error - // CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. - CurrencyValidator func(string) error + // FiatCurrencyCodeValidator is a validator for the "fiat_currency_code" field. It is called by the builders before save. + FiatCurrencyCodeValidator func(string) error + // CustomCurrencyIDValidator is a validator for the "custom_currency_id" field. It is called by the builders before save. + CustomCurrencyIDValidator func(string) error // TaxCodeIDValidator is a validator for the "tax_code_id" field. It is called by the builders before save. TaxCodeIDValidator func(string) error // NamespaceValidator is a validator for the "namespace" field. It is called by the builders before save. @@ -388,9 +402,14 @@ func ByUniqueReferenceID(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldUniqueReferenceID, opts...).ToFunc() } -// ByCurrency orders the results by the currency field. -func ByCurrency(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldCurrency, opts...).ToFunc() +// ByFiatCurrencyCode orders the results by the fiat_currency_code field. +func ByFiatCurrencyCode(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldFiatCurrencyCode, opts...).ToFunc() +} + +// ByCustomCurrencyID orders the results by the custom_currency_id field. +func ByCustomCurrencyID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCustomCurrencyID, opts...).ToFunc() } // ByManagedBy orders the results by the managed_by field. @@ -603,6 +622,13 @@ func ByTaxCodeField(field string, opts ...sql.OrderTermOption) OrderOption { sqlgraph.OrderByNeighborTerms(s, newTaxCodeStep(), sql.OrderByField(field, opts...)) } } + +// ByCustomCurrencyField orders the results by custom_currency field. +func ByCustomCurrencyField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newCustomCurrencyStep(), sql.OrderByField(field, opts...)) + } +} func newRunsStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), @@ -680,3 +706,10 @@ func newTaxCodeStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.M2O, true, TaxCodeTable, TaxCodeColumn), ) } +func newCustomCurrencyStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(CustomCurrencyInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, CustomCurrencyTable, CustomCurrencyColumn), + ) +} diff --git a/openmeter/ent/db/chargeusagebased/where.go b/openmeter/ent/db/chargeusagebased/where.go index 906c6ff475..14b42835b9 100644 --- a/openmeter/ent/db/chargeusagebased/where.go +++ b/openmeter/ent/db/chargeusagebased/where.go @@ -110,10 +110,15 @@ func UniqueReferenceID(v string) predicate.ChargeUsageBased { return predicate.ChargeUsageBased(sql.FieldEQ(FieldUniqueReferenceID, v)) } -// Currency applies equality check predicate on the "currency" field. It's identical to CurrencyEQ. -func Currency(v currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCode applies equality check predicate on the "fiat_currency_code" field. It's identical to FiatCurrencyCodeEQ. +func FiatCurrencyCode(v currencyx.Code) predicate.ChargeUsageBased { vc := string(v) - return predicate.ChargeUsageBased(sql.FieldEQ(FieldCurrency, vc)) + return predicate.ChargeUsageBased(sql.FieldEQ(FieldFiatCurrencyCode, vc)) +} + +// CustomCurrencyID applies equality check predicate on the "custom_currency_id" field. It's identical to CustomCurrencyIDEQ. +func CustomCurrencyID(v string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldEQ(FieldCustomCurrencyID, v)) } // SubscriptionID applies equality check predicate on the "subscription_id" field. It's identical to SubscriptionIDEQ. @@ -606,88 +611,173 @@ func UniqueReferenceIDContainsFold(v string) predicate.ChargeUsageBased { return predicate.ChargeUsageBased(sql.FieldContainsFold(FieldUniqueReferenceID, v)) } -// CurrencyEQ applies the EQ predicate on the "currency" field. -func CurrencyEQ(v currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCodeEQ applies the EQ predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeEQ(v currencyx.Code) predicate.ChargeUsageBased { vc := string(v) - return predicate.ChargeUsageBased(sql.FieldEQ(FieldCurrency, vc)) + return predicate.ChargeUsageBased(sql.FieldEQ(FieldFiatCurrencyCode, vc)) } -// CurrencyNEQ applies the NEQ predicate on the "currency" field. -func CurrencyNEQ(v currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCodeNEQ applies the NEQ predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeNEQ(v currencyx.Code) predicate.ChargeUsageBased { vc := string(v) - return predicate.ChargeUsageBased(sql.FieldNEQ(FieldCurrency, vc)) + return predicate.ChargeUsageBased(sql.FieldNEQ(FieldFiatCurrencyCode, vc)) } -// CurrencyIn applies the In predicate on the "currency" field. -func CurrencyIn(vs ...currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCodeIn applies the In predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeIn(vs ...currencyx.Code) predicate.ChargeUsageBased { v := make([]any, len(vs)) for i := range v { v[i] = string(vs[i]) } - return predicate.ChargeUsageBased(sql.FieldIn(FieldCurrency, v...)) + return predicate.ChargeUsageBased(sql.FieldIn(FieldFiatCurrencyCode, v...)) } -// CurrencyNotIn applies the NotIn predicate on the "currency" field. -func CurrencyNotIn(vs ...currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCodeNotIn applies the NotIn predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeNotIn(vs ...currencyx.Code) predicate.ChargeUsageBased { v := make([]any, len(vs)) for i := range v { v[i] = string(vs[i]) } - return predicate.ChargeUsageBased(sql.FieldNotIn(FieldCurrency, v...)) + return predicate.ChargeUsageBased(sql.FieldNotIn(FieldFiatCurrencyCode, v...)) } -// CurrencyGT applies the GT predicate on the "currency" field. -func CurrencyGT(v currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCodeGT applies the GT predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeGT(v currencyx.Code) predicate.ChargeUsageBased { vc := string(v) - return predicate.ChargeUsageBased(sql.FieldGT(FieldCurrency, vc)) + return predicate.ChargeUsageBased(sql.FieldGT(FieldFiatCurrencyCode, vc)) } -// CurrencyGTE applies the GTE predicate on the "currency" field. -func CurrencyGTE(v currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCodeGTE applies the GTE predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeGTE(v currencyx.Code) predicate.ChargeUsageBased { vc := string(v) - return predicate.ChargeUsageBased(sql.FieldGTE(FieldCurrency, vc)) + return predicate.ChargeUsageBased(sql.FieldGTE(FieldFiatCurrencyCode, vc)) } -// CurrencyLT applies the LT predicate on the "currency" field. -func CurrencyLT(v currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCodeLT applies the LT predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeLT(v currencyx.Code) predicate.ChargeUsageBased { vc := string(v) - return predicate.ChargeUsageBased(sql.FieldLT(FieldCurrency, vc)) + return predicate.ChargeUsageBased(sql.FieldLT(FieldFiatCurrencyCode, vc)) } -// CurrencyLTE applies the LTE predicate on the "currency" field. -func CurrencyLTE(v currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCodeLTE applies the LTE predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeLTE(v currencyx.Code) predicate.ChargeUsageBased { vc := string(v) - return predicate.ChargeUsageBased(sql.FieldLTE(FieldCurrency, vc)) + return predicate.ChargeUsageBased(sql.FieldLTE(FieldFiatCurrencyCode, vc)) } -// CurrencyContains applies the Contains predicate on the "currency" field. -func CurrencyContains(v currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCodeContains applies the Contains predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeContains(v currencyx.Code) predicate.ChargeUsageBased { vc := string(v) - return predicate.ChargeUsageBased(sql.FieldContains(FieldCurrency, vc)) + return predicate.ChargeUsageBased(sql.FieldContains(FieldFiatCurrencyCode, vc)) } -// CurrencyHasPrefix applies the HasPrefix predicate on the "currency" field. -func CurrencyHasPrefix(v currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCodeHasPrefix applies the HasPrefix predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeHasPrefix(v currencyx.Code) predicate.ChargeUsageBased { vc := string(v) - return predicate.ChargeUsageBased(sql.FieldHasPrefix(FieldCurrency, vc)) + return predicate.ChargeUsageBased(sql.FieldHasPrefix(FieldFiatCurrencyCode, vc)) } -// CurrencyHasSuffix applies the HasSuffix predicate on the "currency" field. -func CurrencyHasSuffix(v currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCodeHasSuffix applies the HasSuffix predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeHasSuffix(v currencyx.Code) predicate.ChargeUsageBased { vc := string(v) - return predicate.ChargeUsageBased(sql.FieldHasSuffix(FieldCurrency, vc)) + return predicate.ChargeUsageBased(sql.FieldHasSuffix(FieldFiatCurrencyCode, vc)) +} + +// FiatCurrencyCodeIsNil applies the IsNil predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeIsNil() predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldIsNull(FieldFiatCurrencyCode)) } -// CurrencyEqualFold applies the EqualFold predicate on the "currency" field. -func CurrencyEqualFold(v currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCodeNotNil applies the NotNil predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeNotNil() predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldNotNull(FieldFiatCurrencyCode)) +} + +// FiatCurrencyCodeEqualFold applies the EqualFold predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeEqualFold(v currencyx.Code) predicate.ChargeUsageBased { vc := string(v) - return predicate.ChargeUsageBased(sql.FieldEqualFold(FieldCurrency, vc)) + return predicate.ChargeUsageBased(sql.FieldEqualFold(FieldFiatCurrencyCode, vc)) } -// CurrencyContainsFold applies the ContainsFold predicate on the "currency" field. -func CurrencyContainsFold(v currencyx.Code) predicate.ChargeUsageBased { +// FiatCurrencyCodeContainsFold applies the ContainsFold predicate on the "fiat_currency_code" field. +func FiatCurrencyCodeContainsFold(v currencyx.Code) predicate.ChargeUsageBased { vc := string(v) - return predicate.ChargeUsageBased(sql.FieldContainsFold(FieldCurrency, vc)) + return predicate.ChargeUsageBased(sql.FieldContainsFold(FieldFiatCurrencyCode, vc)) +} + +// CustomCurrencyIDEQ applies the EQ predicate on the "custom_currency_id" field. +func CustomCurrencyIDEQ(v string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldEQ(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDNEQ applies the NEQ predicate on the "custom_currency_id" field. +func CustomCurrencyIDNEQ(v string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldNEQ(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDIn applies the In predicate on the "custom_currency_id" field. +func CustomCurrencyIDIn(vs ...string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldIn(FieldCustomCurrencyID, vs...)) +} + +// CustomCurrencyIDNotIn applies the NotIn predicate on the "custom_currency_id" field. +func CustomCurrencyIDNotIn(vs ...string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldNotIn(FieldCustomCurrencyID, vs...)) +} + +// CustomCurrencyIDGT applies the GT predicate on the "custom_currency_id" field. +func CustomCurrencyIDGT(v string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldGT(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDGTE applies the GTE predicate on the "custom_currency_id" field. +func CustomCurrencyIDGTE(v string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldGTE(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDLT applies the LT predicate on the "custom_currency_id" field. +func CustomCurrencyIDLT(v string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldLT(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDLTE applies the LTE predicate on the "custom_currency_id" field. +func CustomCurrencyIDLTE(v string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldLTE(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDContains applies the Contains predicate on the "custom_currency_id" field. +func CustomCurrencyIDContains(v string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldContains(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDHasPrefix applies the HasPrefix predicate on the "custom_currency_id" field. +func CustomCurrencyIDHasPrefix(v string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldHasPrefix(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDHasSuffix applies the HasSuffix predicate on the "custom_currency_id" field. +func CustomCurrencyIDHasSuffix(v string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldHasSuffix(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDIsNil applies the IsNil predicate on the "custom_currency_id" field. +func CustomCurrencyIDIsNil() predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldIsNull(FieldCustomCurrencyID)) +} + +// CustomCurrencyIDNotNil applies the NotNil predicate on the "custom_currency_id" field. +func CustomCurrencyIDNotNil() predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldNotNull(FieldCustomCurrencyID)) +} + +// CustomCurrencyIDEqualFold applies the EqualFold predicate on the "custom_currency_id" field. +func CustomCurrencyIDEqualFold(v string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldEqualFold(FieldCustomCurrencyID, v)) +} + +// CustomCurrencyIDContainsFold applies the ContainsFold predicate on the "custom_currency_id" field. +func CustomCurrencyIDContainsFold(v string) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(sql.FieldContainsFold(FieldCustomCurrencyID, v)) } // ManagedByEQ applies the EQ predicate on the "managed_by" field. @@ -2113,6 +2203,29 @@ func HasTaxCodeWith(preds ...predicate.TaxCode) predicate.ChargeUsageBased { }) } +// HasCustomCurrency applies the HasEdge predicate on the "custom_currency" edge. +func HasCustomCurrency() predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, CustomCurrencyTable, CustomCurrencyColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasCustomCurrencyWith applies the HasEdge predicate on the "custom_currency" edge with a given conditions (other predicates). +func HasCustomCurrencyWith(preds ...predicate.CustomCurrency) predicate.ChargeUsageBased { + return predicate.ChargeUsageBased(func(s *sql.Selector) { + step := newCustomCurrencyStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.ChargeUsageBased) predicate.ChargeUsageBased { return predicate.ChargeUsageBased(sql.AndPredicates(predicates...)) diff --git a/openmeter/ent/db/chargeusagebased_create.go b/openmeter/ent/db/chargeusagebased_create.go index 55801f7797..f9f1c9837f 100644 --- a/openmeter/ent/db/chargeusagebased_create.go +++ b/openmeter/ent/db/chargeusagebased_create.go @@ -20,6 +20,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebasedoverride" "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebasedrundetailedline" "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebasedruns" + "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/openmeter/ent/db/customer" dbfeature "github.com/openmeterio/openmeter/openmeter/ent/db/feature" "github.com/openmeterio/openmeter/openmeter/ent/db/subscription" @@ -101,9 +102,31 @@ func (_c *ChargeUsageBasedCreate) SetNillableUniqueReferenceID(v *string) *Charg return _c } -// SetCurrency sets the "currency" field. -func (_c *ChargeUsageBasedCreate) SetCurrency(v currencyx.Code) *ChargeUsageBasedCreate { - _c.mutation.SetCurrency(v) +// SetFiatCurrencyCode sets the "fiat_currency_code" field. +func (_c *ChargeUsageBasedCreate) SetFiatCurrencyCode(v currencyx.Code) *ChargeUsageBasedCreate { + _c.mutation.SetFiatCurrencyCode(v) + return _c +} + +// SetNillableFiatCurrencyCode sets the "fiat_currency_code" field if the given value is not nil. +func (_c *ChargeUsageBasedCreate) SetNillableFiatCurrencyCode(v *currencyx.Code) *ChargeUsageBasedCreate { + if v != nil { + _c.SetFiatCurrencyCode(*v) + } + return _c +} + +// SetCustomCurrencyID sets the "custom_currency_id" field. +func (_c *ChargeUsageBasedCreate) SetCustomCurrencyID(v string) *ChargeUsageBasedCreate { + _c.mutation.SetCustomCurrencyID(v) + return _c +} + +// SetNillableCustomCurrencyID sets the "custom_currency_id" field if the given value is not nil. +func (_c *ChargeUsageBasedCreate) SetNillableCustomCurrencyID(v *string) *ChargeUsageBasedCreate { + if v != nil { + _c.SetCustomCurrencyID(*v) + } return _c } @@ -482,6 +505,11 @@ func (_c *ChargeUsageBasedCreate) SetTaxCode(v *TaxCode) *ChargeUsageBasedCreate return _c.SetTaxCodeID(v.ID) } +// SetCustomCurrency sets the "custom_currency" edge to the CustomCurrency entity. +func (_c *ChargeUsageBasedCreate) SetCustomCurrency(v *CustomCurrency) *ChargeUsageBasedCreate { + return _c.SetCustomCurrencyID(v.ID) +} + // Mutation returns the ChargeUsageBasedMutation object of the builder. func (_c *ChargeUsageBasedCreate) Mutation() *ChargeUsageBasedMutation { return _c.mutation @@ -567,12 +595,14 @@ func (_c *ChargeUsageBasedCreate) check() error { return &ValidationError{Name: "status", err: fmt.Errorf(`db: validator failed for field "ChargeUsageBased.status": %w`, err)} } } - if _, ok := _c.mutation.Currency(); !ok { - return &ValidationError{Name: "currency", err: errors.New(`db: missing required field "ChargeUsageBased.currency"`)} + if v, ok := _c.mutation.FiatCurrencyCode(); ok { + if err := chargeusagebased.FiatCurrencyCodeValidator(string(v)); err != nil { + return &ValidationError{Name: "fiat_currency_code", err: fmt.Errorf(`db: validator failed for field "ChargeUsageBased.fiat_currency_code": %w`, err)} + } } - if v, ok := _c.mutation.Currency(); ok { - if err := chargeusagebased.CurrencyValidator(string(v)); err != nil { - return &ValidationError{Name: "currency", err: fmt.Errorf(`db: validator failed for field "ChargeUsageBased.currency": %w`, err)} + if v, ok := _c.mutation.CustomCurrencyID(); ok { + if err := chargeusagebased.CustomCurrencyIDValidator(v); err != nil { + return &ValidationError{Name: "custom_currency_id", err: fmt.Errorf(`db: validator failed for field "ChargeUsageBased.custom_currency_id": %w`, err)} } } if _, ok := _c.mutation.ManagedBy(); !ok { @@ -754,9 +784,9 @@ func (_c *ChargeUsageBasedCreate) createSpec() (*ChargeUsageBased, *sqlgraph.Cre _spec.SetField(chargeusagebased.FieldUniqueReferenceID, field.TypeString, value) _node.UniqueReferenceID = &value } - if value, ok := _c.mutation.Currency(); ok { - _spec.SetField(chargeusagebased.FieldCurrency, field.TypeString, value) - _node.Currency = value + if value, ok := _c.mutation.FiatCurrencyCode(); ok { + _spec.SetField(chargeusagebased.FieldFiatCurrencyCode, field.TypeString, value) + _node.FiatCurrencyCode = &value } if value, ok := _c.mutation.ManagedBy(); ok { _spec.SetField(chargeusagebased.FieldManagedBy, field.TypeEnum, value) @@ -1033,6 +1063,23 @@ func (_c *ChargeUsageBasedCreate) createSpec() (*ChargeUsageBased, *sqlgraph.Cre _node.TaxCodeID = nodes[0] _spec.Edges = append(_spec.Edges, edge) } + if nodes := _c.mutation.CustomCurrencyIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: chargeusagebased.CustomCurrencyTable, + Columns: []string{chargeusagebased.CustomCurrencyColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(customcurrency.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.CustomCurrencyID = &nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec, nil } @@ -1456,8 +1503,11 @@ func (u *ChargeUsageBasedUpsertOne) UpdateNewValues() *ChargeUsageBasedUpsertOne if _, exists := u.create.mutation.UniqueReferenceID(); exists { s.SetIgnore(chargeusagebased.FieldUniqueReferenceID) } - if _, exists := u.create.mutation.Currency(); exists { - s.SetIgnore(chargeusagebased.FieldCurrency) + if _, exists := u.create.mutation.FiatCurrencyCode(); exists { + s.SetIgnore(chargeusagebased.FieldFiatCurrencyCode) + } + if _, exists := u.create.mutation.CustomCurrencyID(); exists { + s.SetIgnore(chargeusagebased.FieldCustomCurrencyID) } if _, exists := u.create.mutation.ManagedBy(); exists { s.SetIgnore(chargeusagebased.FieldManagedBy) @@ -2115,8 +2165,11 @@ func (u *ChargeUsageBasedUpsertBulk) UpdateNewValues() *ChargeUsageBasedUpsertBu if _, exists := b.mutation.UniqueReferenceID(); exists { s.SetIgnore(chargeusagebased.FieldUniqueReferenceID) } - if _, exists := b.mutation.Currency(); exists { - s.SetIgnore(chargeusagebased.FieldCurrency) + if _, exists := b.mutation.FiatCurrencyCode(); exists { + s.SetIgnore(chargeusagebased.FieldFiatCurrencyCode) + } + if _, exists := b.mutation.CustomCurrencyID(); exists { + s.SetIgnore(chargeusagebased.FieldCustomCurrencyID) } if _, exists := b.mutation.ManagedBy(); exists { s.SetIgnore(chargeusagebased.FieldManagedBy) diff --git a/openmeter/ent/db/chargeusagebased_query.go b/openmeter/ent/db/chargeusagebased_query.go index 2ab635ab86..b8001f28e3 100644 --- a/openmeter/ent/db/chargeusagebased_query.go +++ b/openmeter/ent/db/chargeusagebased_query.go @@ -18,6 +18,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebasedoverride" "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebasedrundetailedline" "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebasedruns" + "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/openmeter/ent/db/customer" dbfeature "github.com/openmeterio/openmeter/openmeter/ent/db/feature" "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" @@ -45,6 +46,7 @@ type ChargeUsageBasedQuery struct { withCustomer *CustomerQuery withFeature *FeatureQuery withTaxCode *TaxCodeQuery + withCustomCurrency *CustomCurrencyQuery modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector @@ -324,6 +326,28 @@ func (_q *ChargeUsageBasedQuery) QueryTaxCode() *TaxCodeQuery { return query } +// QueryCustomCurrency chains the current query on the "custom_currency" edge. +func (_q *ChargeUsageBasedQuery) QueryCustomCurrency() *CustomCurrencyQuery { + query := (&CustomCurrencyClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(chargeusagebased.Table, chargeusagebased.FieldID, selector), + sqlgraph.To(customcurrency.Table, customcurrency.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, chargeusagebased.CustomCurrencyTable, chargeusagebased.CustomCurrencyColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first ChargeUsageBased entity from the query. // Returns a *NotFoundError when no ChargeUsageBased was found. func (_q *ChargeUsageBasedQuery) First(ctx context.Context) (*ChargeUsageBased, error) { @@ -527,6 +551,7 @@ func (_q *ChargeUsageBasedQuery) Clone() *ChargeUsageBasedQuery { withCustomer: _q.withCustomer.Clone(), withFeature: _q.withFeature.Clone(), withTaxCode: _q.withTaxCode.Clone(), + withCustomCurrency: _q.withCustomCurrency.Clone(), // clone intermediate query. sql: _q.sql.Clone(), path: _q.path, @@ -654,6 +679,17 @@ func (_q *ChargeUsageBasedQuery) WithTaxCode(opts ...func(*TaxCodeQuery)) *Charg return _q } +// WithCustomCurrency tells the query-builder to eager-load the nodes that are connected to +// the "custom_currency" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ChargeUsageBasedQuery) WithCustomCurrency(opts ...func(*CustomCurrencyQuery)) *ChargeUsageBasedQuery { + query := (&CustomCurrencyClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withCustomCurrency = query + return _q +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -732,7 +768,7 @@ func (_q *ChargeUsageBasedQuery) sqlAll(ctx context.Context, hooks ...queryHook) var ( nodes = []*ChargeUsageBased{} _spec = _q.querySpec() - loadedTypes = [11]bool{ + loadedTypes = [12]bool{ _q.withRuns != nil, _q.withDetailedLines != nil, _q.withCurrentRun != nil, @@ -744,6 +780,7 @@ func (_q *ChargeUsageBasedQuery) sqlAll(ctx context.Context, hooks ...queryHook) _q.withCustomer != nil, _q.withFeature != nil, _q.withTaxCode != nil, + _q.withCustomCurrency != nil, } ) _spec.ScanValues = func(columns []string) ([]any, error) { @@ -837,6 +874,12 @@ func (_q *ChargeUsageBasedQuery) sqlAll(ctx context.Context, hooks ...queryHook) return nil, err } } + if query := _q.withCustomCurrency; query != nil { + if err := _q.loadCustomCurrency(ctx, query, nodes, nil, + func(n *ChargeUsageBased, e *CustomCurrency) { n.Edges.CustomCurrency = e }); err != nil { + return nil, err + } + } return nodes, nil } @@ -1172,6 +1215,38 @@ func (_q *ChargeUsageBasedQuery) loadTaxCode(ctx context.Context, query *TaxCode } return nil } +func (_q *ChargeUsageBasedQuery) loadCustomCurrency(ctx context.Context, query *CustomCurrencyQuery, nodes []*ChargeUsageBased, init func(*ChargeUsageBased), assign func(*ChargeUsageBased, *CustomCurrency)) error { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*ChargeUsageBased) + for i := range nodes { + if nodes[i].CustomCurrencyID == nil { + continue + } + fk := *nodes[i].CustomCurrencyID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(customcurrency.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "custom_currency_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} func (_q *ChargeUsageBasedQuery) sqlCount(ctx context.Context) (int, error) { _spec := _q.querySpec() @@ -1222,6 +1297,9 @@ func (_q *ChargeUsageBasedQuery) querySpec() *sqlgraph.QuerySpec { if _q.withTaxCode != nil { _spec.Node.AddColumnOnce(chargeusagebased.FieldTaxCodeID) } + if _q.withCustomCurrency != nil { + _spec.Node.AddColumnOnce(chargeusagebased.FieldCustomCurrencyID) + } } if ps := _q.predicates; len(ps) > 0 { _spec.Predicate = func(selector *sql.Selector) { diff --git a/openmeter/ent/db/chargeusagebased_update.go b/openmeter/ent/db/chargeusagebased_update.go index c08233f21a..40229159bd 100644 --- a/openmeter/ent/db/chargeusagebased_update.go +++ b/openmeter/ent/db/chargeusagebased_update.go @@ -656,6 +656,9 @@ func (_u *ChargeUsageBasedUpdate) sqlSave(ctx context.Context) (_node int, err e if _u.mutation.UniqueReferenceIDCleared() { _spec.ClearField(chargeusagebased.FieldUniqueReferenceID, field.TypeString) } + if _u.mutation.FiatCurrencyCodeCleared() { + _spec.ClearField(chargeusagebased.FieldFiatCurrencyCode, field.TypeString) + } if value, ok := _u.mutation.AdvanceAfter(); ok { _spec.SetField(chargeusagebased.FieldAdvanceAfter, field.TypeTime, value) } @@ -1611,6 +1614,9 @@ func (_u *ChargeUsageBasedUpdateOne) sqlSave(ctx context.Context) (_node *Charge if _u.mutation.UniqueReferenceIDCleared() { _spec.ClearField(chargeusagebased.FieldUniqueReferenceID, field.TypeString) } + if _u.mutation.FiatCurrencyCodeCleared() { + _spec.ClearField(chargeusagebased.FieldFiatCurrencyCode, field.TypeString) + } if value, ok := _u.mutation.AdvanceAfter(); ok { _spec.SetField(chargeusagebased.FieldAdvanceAfter, field.TypeTime, value) } diff --git a/openmeter/ent/db/client.go b/openmeter/ent/db/client.go index c80cd085c1..1de894aa6b 100644 --- a/openmeter/ent/db/client.go +++ b/openmeter/ent/db/client.go @@ -6273,6 +6273,22 @@ func (c *ChargeCreditPurchaseClient) QueryTaxCode(_m *ChargeCreditPurchase) *Tax return query } +// QueryCustomCurrency queries the custom_currency edge of a ChargeCreditPurchase. +func (c *ChargeCreditPurchaseClient) QueryCustomCurrency(_m *ChargeCreditPurchase) *CustomCurrencyQuery { + query := (&CustomCurrencyClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(chargecreditpurchase.Table, chargecreditpurchase.FieldID, id), + sqlgraph.To(customcurrency.Table, customcurrency.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, chargecreditpurchase.CustomCurrencyTable, chargecreditpurchase.CustomCurrencyColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *ChargeCreditPurchaseClient) Hooks() []Hook { return c.hooks.ChargeCreditPurchase @@ -7029,6 +7045,22 @@ func (c *ChargeFlatFeeClient) QueryTaxCode(_m *ChargeFlatFee) *TaxCodeQuery { return query } +// QueryCustomCurrency queries the custom_currency edge of a ChargeFlatFee. +func (c *ChargeFlatFeeClient) QueryCustomCurrency(_m *ChargeFlatFee) *CustomCurrencyQuery { + query := (&CustomCurrencyClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(chargeflatfee.Table, chargeflatfee.FieldID, id), + sqlgraph.To(customcurrency.Table, customcurrency.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, chargeflatfee.CustomCurrencyTable, chargeflatfee.CustomCurrencyColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *ChargeFlatFeeClient) Hooks() []Hook { return c.hooks.ChargeFlatFee @@ -8408,6 +8440,22 @@ func (c *ChargeUsageBasedClient) QueryTaxCode(_m *ChargeUsageBased) *TaxCodeQuer return query } +// QueryCustomCurrency queries the custom_currency edge of a ChargeUsageBased. +func (c *ChargeUsageBasedClient) QueryCustomCurrency(_m *ChargeUsageBased) *CustomCurrencyQuery { + query := (&CustomCurrencyClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(chargeusagebased.Table, chargeusagebased.FieldID, id), + sqlgraph.To(customcurrency.Table, customcurrency.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, chargeusagebased.CustomCurrencyTable, chargeusagebased.CustomCurrencyColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *ChargeUsageBasedClient) Hooks() []Hook { return c.hooks.ChargeUsageBased @@ -10152,6 +10200,54 @@ func (c *CustomCurrencyClient) QueryCostBasisHistory(_m *CustomCurrency) *Curren return query } +// QueryChargesCreditPurchase queries the charges_credit_purchase edge of a CustomCurrency. +func (c *CustomCurrencyClient) QueryChargesCreditPurchase(_m *CustomCurrency) *ChargeCreditPurchaseQuery { + query := (&ChargeCreditPurchaseClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(customcurrency.Table, customcurrency.FieldID, id), + sqlgraph.To(chargecreditpurchase.Table, chargecreditpurchase.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, customcurrency.ChargesCreditPurchaseTable, customcurrency.ChargesCreditPurchaseColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryChargesFlatFee queries the charges_flat_fee edge of a CustomCurrency. +func (c *CustomCurrencyClient) QueryChargesFlatFee(_m *CustomCurrency) *ChargeFlatFeeQuery { + query := (&ChargeFlatFeeClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(customcurrency.Table, customcurrency.FieldID, id), + sqlgraph.To(chargeflatfee.Table, chargeflatfee.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, customcurrency.ChargesFlatFeeTable, customcurrency.ChargesFlatFeeColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryChargesUsageBased queries the charges_usage_based edge of a CustomCurrency. +func (c *CustomCurrencyClient) QueryChargesUsageBased(_m *CustomCurrency) *ChargeUsageBasedQuery { + query := (&ChargeUsageBasedClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(customcurrency.Table, customcurrency.FieldID, id), + sqlgraph.To(chargeusagebased.Table, chargeusagebased.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, customcurrency.ChargesUsageBasedTable, customcurrency.ChargesUsageBasedColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *CustomCurrencyClient) Hooks() []Hook { return c.hooks.CustomCurrency diff --git a/openmeter/ent/db/customcurrency.go b/openmeter/ent/db/customcurrency.go index a4386db7c7..c783be7a33 100644 --- a/openmeter/ent/db/customcurrency.go +++ b/openmeter/ent/db/customcurrency.go @@ -48,9 +48,15 @@ type CustomCurrency struct { type CustomCurrencyEdges struct { // CostBasisHistory holds the value of the cost_basis_history edge. CostBasisHistory []*CurrencyCostBasis `json:"cost_basis_history,omitempty"` + // ChargesCreditPurchase holds the value of the charges_credit_purchase edge. + ChargesCreditPurchase []*ChargeCreditPurchase `json:"charges_credit_purchase,omitempty"` + // ChargesFlatFee holds the value of the charges_flat_fee edge. + ChargesFlatFee []*ChargeFlatFee `json:"charges_flat_fee,omitempty"` + // ChargesUsageBased holds the value of the charges_usage_based edge. + ChargesUsageBased []*ChargeUsageBased `json:"charges_usage_based,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [1]bool + loadedTypes [4]bool } // CostBasisHistoryOrErr returns the CostBasisHistory value or an error if the edge @@ -62,6 +68,33 @@ func (e CustomCurrencyEdges) CostBasisHistoryOrErr() ([]*CurrencyCostBasis, erro return nil, &NotLoadedError{edge: "cost_basis_history"} } +// ChargesCreditPurchaseOrErr returns the ChargesCreditPurchase value or an error if the edge +// was not loaded in eager-loading. +func (e CustomCurrencyEdges) ChargesCreditPurchaseOrErr() ([]*ChargeCreditPurchase, error) { + if e.loadedTypes[1] { + return e.ChargesCreditPurchase, nil + } + return nil, &NotLoadedError{edge: "charges_credit_purchase"} +} + +// ChargesFlatFeeOrErr returns the ChargesFlatFee value or an error if the edge +// was not loaded in eager-loading. +func (e CustomCurrencyEdges) ChargesFlatFeeOrErr() ([]*ChargeFlatFee, error) { + if e.loadedTypes[2] { + return e.ChargesFlatFee, nil + } + return nil, &NotLoadedError{edge: "charges_flat_fee"} +} + +// ChargesUsageBasedOrErr returns the ChargesUsageBased value or an error if the edge +// was not loaded in eager-loading. +func (e CustomCurrencyEdges) ChargesUsageBasedOrErr() ([]*ChargeUsageBased, error) { + if e.loadedTypes[3] { + return e.ChargesUsageBased, nil + } + return nil, &NotLoadedError{edge: "charges_usage_based"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*CustomCurrency) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -173,6 +206,21 @@ func (_m *CustomCurrency) QueryCostBasisHistory() *CurrencyCostBasisQuery { return NewCustomCurrencyClient(_m.config).QueryCostBasisHistory(_m) } +// QueryChargesCreditPurchase queries the "charges_credit_purchase" edge of the CustomCurrency entity. +func (_m *CustomCurrency) QueryChargesCreditPurchase() *ChargeCreditPurchaseQuery { + return NewCustomCurrencyClient(_m.config).QueryChargesCreditPurchase(_m) +} + +// QueryChargesFlatFee queries the "charges_flat_fee" edge of the CustomCurrency entity. +func (_m *CustomCurrency) QueryChargesFlatFee() *ChargeFlatFeeQuery { + return NewCustomCurrencyClient(_m.config).QueryChargesFlatFee(_m) +} + +// QueryChargesUsageBased queries the "charges_usage_based" edge of the CustomCurrency entity. +func (_m *CustomCurrency) QueryChargesUsageBased() *ChargeUsageBasedQuery { + return NewCustomCurrencyClient(_m.config).QueryChargesUsageBased(_m) +} + // Update returns a builder for updating this CustomCurrency. // Note that you need to call CustomCurrency.Unwrap() before calling this method if this CustomCurrency // was returned from a transaction, and the transaction was committed or rolled back. diff --git a/openmeter/ent/db/customcurrency/customcurrency.go b/openmeter/ent/db/customcurrency/customcurrency.go index 57d37b2a9f..bd0c84676d 100644 --- a/openmeter/ent/db/customcurrency/customcurrency.go +++ b/openmeter/ent/db/customcurrency/customcurrency.go @@ -36,6 +36,12 @@ const ( FieldThousandsSeparator = "thousands_separator" // EdgeCostBasisHistory holds the string denoting the cost_basis_history edge name in mutations. EdgeCostBasisHistory = "cost_basis_history" + // EdgeChargesCreditPurchase holds the string denoting the charges_credit_purchase edge name in mutations. + EdgeChargesCreditPurchase = "charges_credit_purchase" + // EdgeChargesFlatFee holds the string denoting the charges_flat_fee edge name in mutations. + EdgeChargesFlatFee = "charges_flat_fee" + // EdgeChargesUsageBased holds the string denoting the charges_usage_based edge name in mutations. + EdgeChargesUsageBased = "charges_usage_based" // Table holds the table name of the customcurrency in the database. Table = "custom_currencies" // CostBasisHistoryTable is the table that holds the cost_basis_history relation/edge. @@ -45,6 +51,27 @@ const ( CostBasisHistoryInverseTable = "currency_cost_bases" // CostBasisHistoryColumn is the table column denoting the cost_basis_history relation/edge. CostBasisHistoryColumn = "currency_id" + // ChargesCreditPurchaseTable is the table that holds the charges_credit_purchase relation/edge. + ChargesCreditPurchaseTable = "charge_credit_purchases" + // ChargesCreditPurchaseInverseTable is the table name for the ChargeCreditPurchase entity. + // It exists in this package in order to avoid circular dependency with the "chargecreditpurchase" package. + ChargesCreditPurchaseInverseTable = "charge_credit_purchases" + // ChargesCreditPurchaseColumn is the table column denoting the charges_credit_purchase relation/edge. + ChargesCreditPurchaseColumn = "custom_currency_id" + // ChargesFlatFeeTable is the table that holds the charges_flat_fee relation/edge. + ChargesFlatFeeTable = "charge_flat_fees" + // ChargesFlatFeeInverseTable is the table name for the ChargeFlatFee entity. + // It exists in this package in order to avoid circular dependency with the "chargeflatfee" package. + ChargesFlatFeeInverseTable = "charge_flat_fees" + // ChargesFlatFeeColumn is the table column denoting the charges_flat_fee relation/edge. + ChargesFlatFeeColumn = "custom_currency_id" + // ChargesUsageBasedTable is the table that holds the charges_usage_based relation/edge. + ChargesUsageBasedTable = "charge_usage_based" + // ChargesUsageBasedInverseTable is the table name for the ChargeUsageBased entity. + // It exists in this package in order to avoid circular dependency with the "chargeusagebased" package. + ChargesUsageBasedInverseTable = "charge_usage_based" + // ChargesUsageBasedColumn is the table column denoting the charges_usage_based relation/edge. + ChargesUsageBasedColumn = "custom_currency_id" ) // Columns holds all SQL columns for customcurrency fields. @@ -170,6 +197,48 @@ func ByCostBasisHistory(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption sqlgraph.OrderByNeighborTerms(s, newCostBasisHistoryStep(), append([]sql.OrderTerm{term}, terms...)...) } } + +// ByChargesCreditPurchaseCount orders the results by charges_credit_purchase count. +func ByChargesCreditPurchaseCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newChargesCreditPurchaseStep(), opts...) + } +} + +// ByChargesCreditPurchase orders the results by charges_credit_purchase terms. +func ByChargesCreditPurchase(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newChargesCreditPurchaseStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByChargesFlatFeeCount orders the results by charges_flat_fee count. +func ByChargesFlatFeeCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newChargesFlatFeeStep(), opts...) + } +} + +// ByChargesFlatFee orders the results by charges_flat_fee terms. +func ByChargesFlatFee(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newChargesFlatFeeStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByChargesUsageBasedCount orders the results by charges_usage_based count. +func ByChargesUsageBasedCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newChargesUsageBasedStep(), opts...) + } +} + +// ByChargesUsageBased orders the results by charges_usage_based terms. +func ByChargesUsageBased(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newChargesUsageBasedStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} func newCostBasisHistoryStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), @@ -177,3 +246,24 @@ func newCostBasisHistoryStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.O2M, false, CostBasisHistoryTable, CostBasisHistoryColumn), ) } +func newChargesCreditPurchaseStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ChargesCreditPurchaseInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ChargesCreditPurchaseTable, ChargesCreditPurchaseColumn), + ) +} +func newChargesFlatFeeStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ChargesFlatFeeInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ChargesFlatFeeTable, ChargesFlatFeeColumn), + ) +} +func newChargesUsageBasedStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ChargesUsageBasedInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ChargesUsageBasedTable, ChargesUsageBasedColumn), + ) +} diff --git a/openmeter/ent/db/customcurrency/where.go b/openmeter/ent/db/customcurrency/where.go index f500336dd7..3af2475d92 100644 --- a/openmeter/ent/db/customcurrency/where.go +++ b/openmeter/ent/db/customcurrency/where.go @@ -729,6 +729,75 @@ func HasCostBasisHistoryWith(preds ...predicate.CurrencyCostBasis) predicate.Cus }) } +// HasChargesCreditPurchase applies the HasEdge predicate on the "charges_credit_purchase" edge. +func HasChargesCreditPurchase() predicate.CustomCurrency { + return predicate.CustomCurrency(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ChargesCreditPurchaseTable, ChargesCreditPurchaseColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasChargesCreditPurchaseWith applies the HasEdge predicate on the "charges_credit_purchase" edge with a given conditions (other predicates). +func HasChargesCreditPurchaseWith(preds ...predicate.ChargeCreditPurchase) predicate.CustomCurrency { + return predicate.CustomCurrency(func(s *sql.Selector) { + step := newChargesCreditPurchaseStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasChargesFlatFee applies the HasEdge predicate on the "charges_flat_fee" edge. +func HasChargesFlatFee() predicate.CustomCurrency { + return predicate.CustomCurrency(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ChargesFlatFeeTable, ChargesFlatFeeColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasChargesFlatFeeWith applies the HasEdge predicate on the "charges_flat_fee" edge with a given conditions (other predicates). +func HasChargesFlatFeeWith(preds ...predicate.ChargeFlatFee) predicate.CustomCurrency { + return predicate.CustomCurrency(func(s *sql.Selector) { + step := newChargesFlatFeeStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasChargesUsageBased applies the HasEdge predicate on the "charges_usage_based" edge. +func HasChargesUsageBased() predicate.CustomCurrency { + return predicate.CustomCurrency(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ChargesUsageBasedTable, ChargesUsageBasedColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasChargesUsageBasedWith applies the HasEdge predicate on the "charges_usage_based" edge with a given conditions (other predicates). +func HasChargesUsageBasedWith(preds ...predicate.ChargeUsageBased) predicate.CustomCurrency { + return predicate.CustomCurrency(func(s *sql.Selector) { + step := newChargesUsageBasedStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.CustomCurrency) predicate.CustomCurrency { return predicate.CustomCurrency(sql.AndPredicates(predicates...)) diff --git a/openmeter/ent/db/customcurrency_create.go b/openmeter/ent/db/customcurrency_create.go index 1a60deb345..d6aec2ce57 100644 --- a/openmeter/ent/db/customcurrency_create.go +++ b/openmeter/ent/db/customcurrency_create.go @@ -12,6 +12,9 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" + "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchase" + "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfee" + "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebased" "github.com/openmeterio/openmeter/openmeter/ent/db/currencycostbasis" "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/pkg/currencyx" @@ -170,6 +173,51 @@ func (_c *CustomCurrencyCreate) AddCostBasisHistory(v ...*CurrencyCostBasis) *Cu return _c.AddCostBasisHistoryIDs(ids...) } +// AddChargesCreditPurchaseIDs adds the "charges_credit_purchase" edge to the ChargeCreditPurchase entity by IDs. +func (_c *CustomCurrencyCreate) AddChargesCreditPurchaseIDs(ids ...string) *CustomCurrencyCreate { + _c.mutation.AddChargesCreditPurchaseIDs(ids...) + return _c +} + +// AddChargesCreditPurchase adds the "charges_credit_purchase" edges to the ChargeCreditPurchase entity. +func (_c *CustomCurrencyCreate) AddChargesCreditPurchase(v ...*ChargeCreditPurchase) *CustomCurrencyCreate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddChargesCreditPurchaseIDs(ids...) +} + +// AddChargesFlatFeeIDs adds the "charges_flat_fee" edge to the ChargeFlatFee entity by IDs. +func (_c *CustomCurrencyCreate) AddChargesFlatFeeIDs(ids ...string) *CustomCurrencyCreate { + _c.mutation.AddChargesFlatFeeIDs(ids...) + return _c +} + +// AddChargesFlatFee adds the "charges_flat_fee" edges to the ChargeFlatFee entity. +func (_c *CustomCurrencyCreate) AddChargesFlatFee(v ...*ChargeFlatFee) *CustomCurrencyCreate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddChargesFlatFeeIDs(ids...) +} + +// AddChargesUsageBasedIDs adds the "charges_usage_based" edge to the ChargeUsageBased entity by IDs. +func (_c *CustomCurrencyCreate) AddChargesUsageBasedIDs(ids ...string) *CustomCurrencyCreate { + _c.mutation.AddChargesUsageBasedIDs(ids...) + return _c +} + +// AddChargesUsageBased adds the "charges_usage_based" edges to the ChargeUsageBased entity. +func (_c *CustomCurrencyCreate) AddChargesUsageBased(v ...*ChargeUsageBased) *CustomCurrencyCreate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddChargesUsageBasedIDs(ids...) +} + // Mutation returns the CustomCurrencyMutation object of the builder. func (_c *CustomCurrencyCreate) Mutation() *CustomCurrencyMutation { return _c.mutation @@ -374,6 +422,54 @@ func (_c *CustomCurrencyCreate) createSpec() (*CustomCurrency, *sqlgraph.CreateS } _spec.Edges = append(_spec.Edges, edge) } + if nodes := _c.mutation.ChargesCreditPurchaseIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesCreditPurchaseTable, + Columns: []string{customcurrency.ChargesCreditPurchaseColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargecreditpurchase.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := _c.mutation.ChargesFlatFeeIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesFlatFeeTable, + Columns: []string{customcurrency.ChargesFlatFeeColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeflatfee.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := _c.mutation.ChargesUsageBasedIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesUsageBasedTable, + Columns: []string{customcurrency.ChargesUsageBasedColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeusagebased.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } diff --git a/openmeter/ent/db/customcurrency_query.go b/openmeter/ent/db/customcurrency_query.go index c60f04cbdc..4fb00abdc7 100644 --- a/openmeter/ent/db/customcurrency_query.go +++ b/openmeter/ent/db/customcurrency_query.go @@ -13,6 +13,9 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" + "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchase" + "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfee" + "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebased" "github.com/openmeterio/openmeter/openmeter/ent/db/currencycostbasis" "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" @@ -21,12 +24,15 @@ import ( // CustomCurrencyQuery is the builder for querying CustomCurrency entities. type CustomCurrencyQuery struct { config - ctx *QueryContext - order []customcurrency.OrderOption - inters []Interceptor - predicates []predicate.CustomCurrency - withCostBasisHistory *CurrencyCostBasisQuery - modifiers []func(*sql.Selector) + ctx *QueryContext + order []customcurrency.OrderOption + inters []Interceptor + predicates []predicate.CustomCurrency + withCostBasisHistory *CurrencyCostBasisQuery + withChargesCreditPurchase *ChargeCreditPurchaseQuery + withChargesFlatFee *ChargeFlatFeeQuery + withChargesUsageBased *ChargeUsageBasedQuery + modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -85,6 +91,72 @@ func (_q *CustomCurrencyQuery) QueryCostBasisHistory() *CurrencyCostBasisQuery { return query } +// QueryChargesCreditPurchase chains the current query on the "charges_credit_purchase" edge. +func (_q *CustomCurrencyQuery) QueryChargesCreditPurchase() *ChargeCreditPurchaseQuery { + query := (&ChargeCreditPurchaseClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(customcurrency.Table, customcurrency.FieldID, selector), + sqlgraph.To(chargecreditpurchase.Table, chargecreditpurchase.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, customcurrency.ChargesCreditPurchaseTable, customcurrency.ChargesCreditPurchaseColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryChargesFlatFee chains the current query on the "charges_flat_fee" edge. +func (_q *CustomCurrencyQuery) QueryChargesFlatFee() *ChargeFlatFeeQuery { + query := (&ChargeFlatFeeClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(customcurrency.Table, customcurrency.FieldID, selector), + sqlgraph.To(chargeflatfee.Table, chargeflatfee.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, customcurrency.ChargesFlatFeeTable, customcurrency.ChargesFlatFeeColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryChargesUsageBased chains the current query on the "charges_usage_based" edge. +func (_q *CustomCurrencyQuery) QueryChargesUsageBased() *ChargeUsageBasedQuery { + query := (&ChargeUsageBasedClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(customcurrency.Table, customcurrency.FieldID, selector), + sqlgraph.To(chargeusagebased.Table, chargeusagebased.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, customcurrency.ChargesUsageBasedTable, customcurrency.ChargesUsageBasedColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first CustomCurrency entity from the query. // Returns a *NotFoundError when no CustomCurrency was found. func (_q *CustomCurrencyQuery) First(ctx context.Context) (*CustomCurrency, error) { @@ -272,12 +344,15 @@ func (_q *CustomCurrencyQuery) Clone() *CustomCurrencyQuery { return nil } return &CustomCurrencyQuery{ - config: _q.config, - ctx: _q.ctx.Clone(), - order: append([]customcurrency.OrderOption{}, _q.order...), - inters: append([]Interceptor{}, _q.inters...), - predicates: append([]predicate.CustomCurrency{}, _q.predicates...), - withCostBasisHistory: _q.withCostBasisHistory.Clone(), + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]customcurrency.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.CustomCurrency{}, _q.predicates...), + withCostBasisHistory: _q.withCostBasisHistory.Clone(), + withChargesCreditPurchase: _q.withChargesCreditPurchase.Clone(), + withChargesFlatFee: _q.withChargesFlatFee.Clone(), + withChargesUsageBased: _q.withChargesUsageBased.Clone(), // clone intermediate query. sql: _q.sql.Clone(), path: _q.path, @@ -295,6 +370,39 @@ func (_q *CustomCurrencyQuery) WithCostBasisHistory(opts ...func(*CurrencyCostBa return _q } +// WithChargesCreditPurchase tells the query-builder to eager-load the nodes that are connected to +// the "charges_credit_purchase" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *CustomCurrencyQuery) WithChargesCreditPurchase(opts ...func(*ChargeCreditPurchaseQuery)) *CustomCurrencyQuery { + query := (&ChargeCreditPurchaseClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withChargesCreditPurchase = query + return _q +} + +// WithChargesFlatFee tells the query-builder to eager-load the nodes that are connected to +// the "charges_flat_fee" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *CustomCurrencyQuery) WithChargesFlatFee(opts ...func(*ChargeFlatFeeQuery)) *CustomCurrencyQuery { + query := (&ChargeFlatFeeClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withChargesFlatFee = query + return _q +} + +// WithChargesUsageBased tells the query-builder to eager-load the nodes that are connected to +// the "charges_usage_based" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *CustomCurrencyQuery) WithChargesUsageBased(opts ...func(*ChargeUsageBasedQuery)) *CustomCurrencyQuery { + query := (&ChargeUsageBasedClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withChargesUsageBased = query + return _q +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -373,8 +481,11 @@ func (_q *CustomCurrencyQuery) sqlAll(ctx context.Context, hooks ...queryHook) ( var ( nodes = []*CustomCurrency{} _spec = _q.querySpec() - loadedTypes = [1]bool{ + loadedTypes = [4]bool{ _q.withCostBasisHistory != nil, + _q.withChargesCreditPurchase != nil, + _q.withChargesFlatFee != nil, + _q.withChargesUsageBased != nil, } ) _spec.ScanValues = func(columns []string) ([]any, error) { @@ -407,6 +518,31 @@ func (_q *CustomCurrencyQuery) sqlAll(ctx context.Context, hooks ...queryHook) ( return nil, err } } + if query := _q.withChargesCreditPurchase; query != nil { + if err := _q.loadChargesCreditPurchase(ctx, query, nodes, + func(n *CustomCurrency) { n.Edges.ChargesCreditPurchase = []*ChargeCreditPurchase{} }, + func(n *CustomCurrency, e *ChargeCreditPurchase) { + n.Edges.ChargesCreditPurchase = append(n.Edges.ChargesCreditPurchase, e) + }); err != nil { + return nil, err + } + } + if query := _q.withChargesFlatFee; query != nil { + if err := _q.loadChargesFlatFee(ctx, query, nodes, + func(n *CustomCurrency) { n.Edges.ChargesFlatFee = []*ChargeFlatFee{} }, + func(n *CustomCurrency, e *ChargeFlatFee) { n.Edges.ChargesFlatFee = append(n.Edges.ChargesFlatFee, e) }); err != nil { + return nil, err + } + } + if query := _q.withChargesUsageBased; query != nil { + if err := _q.loadChargesUsageBased(ctx, query, nodes, + func(n *CustomCurrency) { n.Edges.ChargesUsageBased = []*ChargeUsageBased{} }, + func(n *CustomCurrency, e *ChargeUsageBased) { + n.Edges.ChargesUsageBased = append(n.Edges.ChargesUsageBased, e) + }); err != nil { + return nil, err + } + } return nodes, nil } @@ -440,6 +576,105 @@ func (_q *CustomCurrencyQuery) loadCostBasisHistory(ctx context.Context, query * } return nil } +func (_q *CustomCurrencyQuery) loadChargesCreditPurchase(ctx context.Context, query *ChargeCreditPurchaseQuery, nodes []*CustomCurrency, init func(*CustomCurrency), assign func(*CustomCurrency, *ChargeCreditPurchase)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*CustomCurrency) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(chargecreditpurchase.FieldCustomCurrencyID) + } + query.Where(predicate.ChargeCreditPurchase(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(customcurrency.ChargesCreditPurchaseColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.CustomCurrencyID + if fk == nil { + return fmt.Errorf(`foreign-key "custom_currency_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "custom_currency_id" returned %v for node %v`, *fk, n.ID) + } + assign(node, n) + } + return nil +} +func (_q *CustomCurrencyQuery) loadChargesFlatFee(ctx context.Context, query *ChargeFlatFeeQuery, nodes []*CustomCurrency, init func(*CustomCurrency), assign func(*CustomCurrency, *ChargeFlatFee)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*CustomCurrency) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(chargeflatfee.FieldCustomCurrencyID) + } + query.Where(predicate.ChargeFlatFee(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(customcurrency.ChargesFlatFeeColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.CustomCurrencyID + if fk == nil { + return fmt.Errorf(`foreign-key "custom_currency_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "custom_currency_id" returned %v for node %v`, *fk, n.ID) + } + assign(node, n) + } + return nil +} +func (_q *CustomCurrencyQuery) loadChargesUsageBased(ctx context.Context, query *ChargeUsageBasedQuery, nodes []*CustomCurrency, init func(*CustomCurrency), assign func(*CustomCurrency, *ChargeUsageBased)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*CustomCurrency) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(chargeusagebased.FieldCustomCurrencyID) + } + query.Where(predicate.ChargeUsageBased(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(customcurrency.ChargesUsageBasedColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.CustomCurrencyID + if fk == nil { + return fmt.Errorf(`foreign-key "custom_currency_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "custom_currency_id" returned %v for node %v`, *fk, n.ID) + } + assign(node, n) + } + return nil +} func (_q *CustomCurrencyQuery) sqlCount(ctx context.Context) (int, error) { _spec := _q.querySpec() diff --git a/openmeter/ent/db/customcurrency_update.go b/openmeter/ent/db/customcurrency_update.go index 13ebb7658e..825178a0c3 100644 --- a/openmeter/ent/db/customcurrency_update.go +++ b/openmeter/ent/db/customcurrency_update.go @@ -11,6 +11,9 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" + "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchase" + "github.com/openmeterio/openmeter/openmeter/ent/db/chargeflatfee" + "github.com/openmeterio/openmeter/openmeter/ent/db/chargeusagebased" "github.com/openmeterio/openmeter/openmeter/ent/db/currencycostbasis" "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" @@ -153,6 +156,51 @@ func (_u *CustomCurrencyUpdate) AddCostBasisHistory(v ...*CurrencyCostBasis) *Cu return _u.AddCostBasisHistoryIDs(ids...) } +// AddChargesCreditPurchaseIDs adds the "charges_credit_purchase" edge to the ChargeCreditPurchase entity by IDs. +func (_u *CustomCurrencyUpdate) AddChargesCreditPurchaseIDs(ids ...string) *CustomCurrencyUpdate { + _u.mutation.AddChargesCreditPurchaseIDs(ids...) + return _u +} + +// AddChargesCreditPurchase adds the "charges_credit_purchase" edges to the ChargeCreditPurchase entity. +func (_u *CustomCurrencyUpdate) AddChargesCreditPurchase(v ...*ChargeCreditPurchase) *CustomCurrencyUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddChargesCreditPurchaseIDs(ids...) +} + +// AddChargesFlatFeeIDs adds the "charges_flat_fee" edge to the ChargeFlatFee entity by IDs. +func (_u *CustomCurrencyUpdate) AddChargesFlatFeeIDs(ids ...string) *CustomCurrencyUpdate { + _u.mutation.AddChargesFlatFeeIDs(ids...) + return _u +} + +// AddChargesFlatFee adds the "charges_flat_fee" edges to the ChargeFlatFee entity. +func (_u *CustomCurrencyUpdate) AddChargesFlatFee(v ...*ChargeFlatFee) *CustomCurrencyUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddChargesFlatFeeIDs(ids...) +} + +// AddChargesUsageBasedIDs adds the "charges_usage_based" edge to the ChargeUsageBased entity by IDs. +func (_u *CustomCurrencyUpdate) AddChargesUsageBasedIDs(ids ...string) *CustomCurrencyUpdate { + _u.mutation.AddChargesUsageBasedIDs(ids...) + return _u +} + +// AddChargesUsageBased adds the "charges_usage_based" edges to the ChargeUsageBased entity. +func (_u *CustomCurrencyUpdate) AddChargesUsageBased(v ...*ChargeUsageBased) *CustomCurrencyUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddChargesUsageBasedIDs(ids...) +} + // Mutation returns the CustomCurrencyMutation object of the builder. func (_u *CustomCurrencyUpdate) Mutation() *CustomCurrencyMutation { return _u.mutation @@ -179,6 +227,69 @@ func (_u *CustomCurrencyUpdate) RemoveCostBasisHistory(v ...*CurrencyCostBasis) return _u.RemoveCostBasisHistoryIDs(ids...) } +// ClearChargesCreditPurchase clears all "charges_credit_purchase" edges to the ChargeCreditPurchase entity. +func (_u *CustomCurrencyUpdate) ClearChargesCreditPurchase() *CustomCurrencyUpdate { + _u.mutation.ClearChargesCreditPurchase() + return _u +} + +// RemoveChargesCreditPurchaseIDs removes the "charges_credit_purchase" edge to ChargeCreditPurchase entities by IDs. +func (_u *CustomCurrencyUpdate) RemoveChargesCreditPurchaseIDs(ids ...string) *CustomCurrencyUpdate { + _u.mutation.RemoveChargesCreditPurchaseIDs(ids...) + return _u +} + +// RemoveChargesCreditPurchase removes "charges_credit_purchase" edges to ChargeCreditPurchase entities. +func (_u *CustomCurrencyUpdate) RemoveChargesCreditPurchase(v ...*ChargeCreditPurchase) *CustomCurrencyUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveChargesCreditPurchaseIDs(ids...) +} + +// ClearChargesFlatFee clears all "charges_flat_fee" edges to the ChargeFlatFee entity. +func (_u *CustomCurrencyUpdate) ClearChargesFlatFee() *CustomCurrencyUpdate { + _u.mutation.ClearChargesFlatFee() + return _u +} + +// RemoveChargesFlatFeeIDs removes the "charges_flat_fee" edge to ChargeFlatFee entities by IDs. +func (_u *CustomCurrencyUpdate) RemoveChargesFlatFeeIDs(ids ...string) *CustomCurrencyUpdate { + _u.mutation.RemoveChargesFlatFeeIDs(ids...) + return _u +} + +// RemoveChargesFlatFee removes "charges_flat_fee" edges to ChargeFlatFee entities. +func (_u *CustomCurrencyUpdate) RemoveChargesFlatFee(v ...*ChargeFlatFee) *CustomCurrencyUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveChargesFlatFeeIDs(ids...) +} + +// ClearChargesUsageBased clears all "charges_usage_based" edges to the ChargeUsageBased entity. +func (_u *CustomCurrencyUpdate) ClearChargesUsageBased() *CustomCurrencyUpdate { + _u.mutation.ClearChargesUsageBased() + return _u +} + +// RemoveChargesUsageBasedIDs removes the "charges_usage_based" edge to ChargeUsageBased entities by IDs. +func (_u *CustomCurrencyUpdate) RemoveChargesUsageBasedIDs(ids ...string) *CustomCurrencyUpdate { + _u.mutation.RemoveChargesUsageBasedIDs(ids...) + return _u +} + +// RemoveChargesUsageBased removes "charges_usage_based" edges to ChargeUsageBased entities. +func (_u *CustomCurrencyUpdate) RemoveChargesUsageBased(v ...*ChargeUsageBased) *CustomCurrencyUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveChargesUsageBasedIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (_u *CustomCurrencyUpdate) Save(ctx context.Context) (int, error) { _u.defaults() @@ -322,6 +433,141 @@ func (_u *CustomCurrencyUpdate) sqlSave(ctx context.Context) (_node int, err err } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if _u.mutation.ChargesCreditPurchaseCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesCreditPurchaseTable, + Columns: []string{customcurrency.ChargesCreditPurchaseColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargecreditpurchase.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedChargesCreditPurchaseIDs(); len(nodes) > 0 && !_u.mutation.ChargesCreditPurchaseCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesCreditPurchaseTable, + Columns: []string{customcurrency.ChargesCreditPurchaseColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargecreditpurchase.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.ChargesCreditPurchaseIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesCreditPurchaseTable, + Columns: []string{customcurrency.ChargesCreditPurchaseColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargecreditpurchase.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.ChargesFlatFeeCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesFlatFeeTable, + Columns: []string{customcurrency.ChargesFlatFeeColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeflatfee.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedChargesFlatFeeIDs(); len(nodes) > 0 && !_u.mutation.ChargesFlatFeeCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesFlatFeeTable, + Columns: []string{customcurrency.ChargesFlatFeeColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeflatfee.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.ChargesFlatFeeIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesFlatFeeTable, + Columns: []string{customcurrency.ChargesFlatFeeColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeflatfee.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.ChargesUsageBasedCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesUsageBasedTable, + Columns: []string{customcurrency.ChargesUsageBasedColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeusagebased.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedChargesUsageBasedIDs(); len(nodes) > 0 && !_u.mutation.ChargesUsageBasedCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesUsageBasedTable, + Columns: []string{customcurrency.ChargesUsageBasedColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeusagebased.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.ChargesUsageBasedIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesUsageBasedTable, + Columns: []string{customcurrency.ChargesUsageBasedColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeusagebased.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{customcurrency.Label} @@ -466,6 +712,51 @@ func (_u *CustomCurrencyUpdateOne) AddCostBasisHistory(v ...*CurrencyCostBasis) return _u.AddCostBasisHistoryIDs(ids...) } +// AddChargesCreditPurchaseIDs adds the "charges_credit_purchase" edge to the ChargeCreditPurchase entity by IDs. +func (_u *CustomCurrencyUpdateOne) AddChargesCreditPurchaseIDs(ids ...string) *CustomCurrencyUpdateOne { + _u.mutation.AddChargesCreditPurchaseIDs(ids...) + return _u +} + +// AddChargesCreditPurchase adds the "charges_credit_purchase" edges to the ChargeCreditPurchase entity. +func (_u *CustomCurrencyUpdateOne) AddChargesCreditPurchase(v ...*ChargeCreditPurchase) *CustomCurrencyUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddChargesCreditPurchaseIDs(ids...) +} + +// AddChargesFlatFeeIDs adds the "charges_flat_fee" edge to the ChargeFlatFee entity by IDs. +func (_u *CustomCurrencyUpdateOne) AddChargesFlatFeeIDs(ids ...string) *CustomCurrencyUpdateOne { + _u.mutation.AddChargesFlatFeeIDs(ids...) + return _u +} + +// AddChargesFlatFee adds the "charges_flat_fee" edges to the ChargeFlatFee entity. +func (_u *CustomCurrencyUpdateOne) AddChargesFlatFee(v ...*ChargeFlatFee) *CustomCurrencyUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddChargesFlatFeeIDs(ids...) +} + +// AddChargesUsageBasedIDs adds the "charges_usage_based" edge to the ChargeUsageBased entity by IDs. +func (_u *CustomCurrencyUpdateOne) AddChargesUsageBasedIDs(ids ...string) *CustomCurrencyUpdateOne { + _u.mutation.AddChargesUsageBasedIDs(ids...) + return _u +} + +// AddChargesUsageBased adds the "charges_usage_based" edges to the ChargeUsageBased entity. +func (_u *CustomCurrencyUpdateOne) AddChargesUsageBased(v ...*ChargeUsageBased) *CustomCurrencyUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddChargesUsageBasedIDs(ids...) +} + // Mutation returns the CustomCurrencyMutation object of the builder. func (_u *CustomCurrencyUpdateOne) Mutation() *CustomCurrencyMutation { return _u.mutation @@ -492,6 +783,69 @@ func (_u *CustomCurrencyUpdateOne) RemoveCostBasisHistory(v ...*CurrencyCostBasi return _u.RemoveCostBasisHistoryIDs(ids...) } +// ClearChargesCreditPurchase clears all "charges_credit_purchase" edges to the ChargeCreditPurchase entity. +func (_u *CustomCurrencyUpdateOne) ClearChargesCreditPurchase() *CustomCurrencyUpdateOne { + _u.mutation.ClearChargesCreditPurchase() + return _u +} + +// RemoveChargesCreditPurchaseIDs removes the "charges_credit_purchase" edge to ChargeCreditPurchase entities by IDs. +func (_u *CustomCurrencyUpdateOne) RemoveChargesCreditPurchaseIDs(ids ...string) *CustomCurrencyUpdateOne { + _u.mutation.RemoveChargesCreditPurchaseIDs(ids...) + return _u +} + +// RemoveChargesCreditPurchase removes "charges_credit_purchase" edges to ChargeCreditPurchase entities. +func (_u *CustomCurrencyUpdateOne) RemoveChargesCreditPurchase(v ...*ChargeCreditPurchase) *CustomCurrencyUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveChargesCreditPurchaseIDs(ids...) +} + +// ClearChargesFlatFee clears all "charges_flat_fee" edges to the ChargeFlatFee entity. +func (_u *CustomCurrencyUpdateOne) ClearChargesFlatFee() *CustomCurrencyUpdateOne { + _u.mutation.ClearChargesFlatFee() + return _u +} + +// RemoveChargesFlatFeeIDs removes the "charges_flat_fee" edge to ChargeFlatFee entities by IDs. +func (_u *CustomCurrencyUpdateOne) RemoveChargesFlatFeeIDs(ids ...string) *CustomCurrencyUpdateOne { + _u.mutation.RemoveChargesFlatFeeIDs(ids...) + return _u +} + +// RemoveChargesFlatFee removes "charges_flat_fee" edges to ChargeFlatFee entities. +func (_u *CustomCurrencyUpdateOne) RemoveChargesFlatFee(v ...*ChargeFlatFee) *CustomCurrencyUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveChargesFlatFeeIDs(ids...) +} + +// ClearChargesUsageBased clears all "charges_usage_based" edges to the ChargeUsageBased entity. +func (_u *CustomCurrencyUpdateOne) ClearChargesUsageBased() *CustomCurrencyUpdateOne { + _u.mutation.ClearChargesUsageBased() + return _u +} + +// RemoveChargesUsageBasedIDs removes the "charges_usage_based" edge to ChargeUsageBased entities by IDs. +func (_u *CustomCurrencyUpdateOne) RemoveChargesUsageBasedIDs(ids ...string) *CustomCurrencyUpdateOne { + _u.mutation.RemoveChargesUsageBasedIDs(ids...) + return _u +} + +// RemoveChargesUsageBased removes "charges_usage_based" edges to ChargeUsageBased entities. +func (_u *CustomCurrencyUpdateOne) RemoveChargesUsageBased(v ...*ChargeUsageBased) *CustomCurrencyUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveChargesUsageBasedIDs(ids...) +} + // Where appends a list predicates to the CustomCurrencyUpdate builder. func (_u *CustomCurrencyUpdateOne) Where(ps ...predicate.CustomCurrency) *CustomCurrencyUpdateOne { _u.mutation.Where(ps...) @@ -665,6 +1019,141 @@ func (_u *CustomCurrencyUpdateOne) sqlSave(ctx context.Context) (_node *CustomCu } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if _u.mutation.ChargesCreditPurchaseCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesCreditPurchaseTable, + Columns: []string{customcurrency.ChargesCreditPurchaseColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargecreditpurchase.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedChargesCreditPurchaseIDs(); len(nodes) > 0 && !_u.mutation.ChargesCreditPurchaseCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesCreditPurchaseTable, + Columns: []string{customcurrency.ChargesCreditPurchaseColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargecreditpurchase.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.ChargesCreditPurchaseIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesCreditPurchaseTable, + Columns: []string{customcurrency.ChargesCreditPurchaseColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargecreditpurchase.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.ChargesFlatFeeCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesFlatFeeTable, + Columns: []string{customcurrency.ChargesFlatFeeColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeflatfee.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedChargesFlatFeeIDs(); len(nodes) > 0 && !_u.mutation.ChargesFlatFeeCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesFlatFeeTable, + Columns: []string{customcurrency.ChargesFlatFeeColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeflatfee.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.ChargesFlatFeeIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesFlatFeeTable, + Columns: []string{customcurrency.ChargesFlatFeeColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeflatfee.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.ChargesUsageBasedCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesUsageBasedTable, + Columns: []string{customcurrency.ChargesUsageBasedColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeusagebased.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedChargesUsageBasedIDs(); len(nodes) > 0 && !_u.mutation.ChargesUsageBasedCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesUsageBasedTable, + Columns: []string{customcurrency.ChargesUsageBasedColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeusagebased.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.ChargesUsageBasedIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: customcurrency.ChargesUsageBasedTable, + Columns: []string{customcurrency.ChargesUsageBasedColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(chargeusagebased.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &CustomCurrency{config: _u.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/openmeter/ent/db/entmixinaccessor.go b/openmeter/ent/db/entmixinaccessor.go index 1b5b7f0772..9fcb6ea806 100644 --- a/openmeter/ent/db/entmixinaccessor.go +++ b/openmeter/ent/db/entmixinaccessor.go @@ -1029,8 +1029,12 @@ func (e *ChargeCreditPurchase) GetUniqueReferenceID() *string { return e.UniqueReferenceID } -func (e *ChargeCreditPurchase) GetCurrency() currencyx.Code { - return e.Currency +func (e *ChargeCreditPurchase) GetFiatCurrencyCode() *currencyx.Code { + return e.FiatCurrencyCode +} + +func (e *ChargeCreditPurchase) GetCustomCurrencyID() *string { + return e.CustomCurrencyID } func (e *ChargeCreditPurchase) GetManagedBy() billing.InvoiceLineManagedBy { @@ -1273,8 +1277,12 @@ func (e *ChargeFlatFee) GetUniqueReferenceID() *string { return e.UniqueReferenceID } -func (e *ChargeFlatFee) GetCurrency() currencyx.Code { - return e.Currency +func (e *ChargeFlatFee) GetFiatCurrencyCode() *currencyx.Code { + return e.FiatCurrencyCode +} + +func (e *ChargeFlatFee) GetCustomCurrencyID() *string { + return e.CustomCurrencyID } func (e *ChargeFlatFee) GetManagedBy() billing.InvoiceLineManagedBy { @@ -1737,8 +1745,12 @@ func (e *ChargeUsageBased) GetUniqueReferenceID() *string { return e.UniqueReferenceID } -func (e *ChargeUsageBased) GetCurrency() currencyx.Code { - return e.Currency +func (e *ChargeUsageBased) GetFiatCurrencyCode() *currencyx.Code { + return e.FiatCurrencyCode +} + +func (e *ChargeUsageBased) GetCustomCurrencyID() *string { + return e.CustomCurrencyID } func (e *ChargeUsageBased) GetManagedBy() billing.InvoiceLineManagedBy { diff --git a/openmeter/ent/db/migrate/schema.go b/openmeter/ent/db/migrate/schema.go index 3ff4a8a3ec..ad102a3679 100644 --- a/openmeter/ent/db/migrate/schema.go +++ b/openmeter/ent/db/migrate/schema.go @@ -1775,7 +1775,7 @@ var ( {Name: "full_service_period_to", Type: field.TypeTime}, {Name: "status", Type: field.TypeEnum, Enums: []string{"created", "active", "final", "deleted"}}, {Name: "unique_reference_id", Type: field.TypeString, Nullable: true}, - {Name: "currency", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(3)"}}, + {Name: "currency", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "varchar(3)"}}, {Name: "managed_by", Type: field.TypeEnum, Enums: []string{"subscription", "system", "manual"}}, {Name: "advance_after", Type: field.TypeTime, Nullable: true}, {Name: "tax_behavior", Type: field.TypeEnum, Nullable: true, Enums: []string{"inclusive", "exclusive"}}, @@ -1796,6 +1796,7 @@ var ( {Name: "status_detailed", Type: field.TypeEnum, Enums: []string{"created", "active", "active.initial_credit_grant", "active.payment.pending", "active.payment.authorized", "active.payment.paid_and_authorized", "active.payment.settled", "final", "deleted"}}, {Name: "key", Type: field.TypeString, Nullable: true}, {Name: "voided_at", Type: field.TypeTime, Nullable: true}, + {Name: "custom_currency_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, {Name: "customer_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "char(26)"}}, {Name: "subscription_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, {Name: "subscription_item_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, @@ -1809,32 +1810,38 @@ var ( PrimaryKey: []*schema.Column{ChargeCreditPurchasesColumns[0]}, ForeignKeys: []*schema.ForeignKey{ { - Symbol: "charge_credit_purchases_customers_charges_credit_purchase", + Symbol: "charge_credit_purchases_custom_currencies_charges_credit_purchase", Columns: []*schema.Column{ChargeCreditPurchasesColumns[30]}, + RefColumns: []*schema.Column{CustomCurrenciesColumns[0]}, + OnDelete: schema.Restrict, + }, + { + Symbol: "charge_credit_purchases_customers_charges_credit_purchase", + Columns: []*schema.Column{ChargeCreditPurchasesColumns[31]}, RefColumns: []*schema.Column{CustomersColumns[0]}, OnDelete: schema.NoAction, }, { Symbol: "charge_credit_purchases_subscriptions_charges_credit_purchase", - Columns: []*schema.Column{ChargeCreditPurchasesColumns[31]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[32]}, RefColumns: []*schema.Column{SubscriptionsColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "charge_credit_purchases_subscription_items_charges_credit_purchase", - Columns: []*schema.Column{ChargeCreditPurchasesColumns[32]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[33]}, RefColumns: []*schema.Column{SubscriptionItemsColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "charge_credit_purchases_subscription_phases_charges_credit_purchase", - Columns: []*schema.Column{ChargeCreditPurchasesColumns[33]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[34]}, RefColumns: []*schema.Column{SubscriptionPhasesColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "charge_credit_purchases_tax_codes_charge_credit_purchases", - Columns: []*schema.Column{ChargeCreditPurchasesColumns[34]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[35]}, RefColumns: []*schema.Column{TaxCodesColumns[0]}, OnDelete: schema.NoAction, }, @@ -1843,7 +1850,7 @@ var ( { Name: "chargecreditpurchase_namespace_customer_id_unique_reference_id", Unique: true, - Columns: []*schema.Column{ChargeCreditPurchasesColumns[14], ChargeCreditPurchasesColumns[30], ChargeCreditPurchasesColumns[8]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[14], ChargeCreditPurchasesColumns[31], ChargeCreditPurchasesColumns[8]}, Annotation: &entsql.IndexAnnotation{ Where: "unique_reference_id IS NOT NULL AND deleted_at IS NULL", }, @@ -1876,12 +1883,12 @@ var ( { Name: "chargecreditpurchases_tax_code_id", Unique: false, - Columns: []*schema.Column{ChargeCreditPurchasesColumns[34]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[35]}, }, { Name: "chargecreditpurchase_namespace_customer_id_key", Unique: true, - Columns: []*schema.Column{ChargeCreditPurchasesColumns[14], ChargeCreditPurchasesColumns[30], ChargeCreditPurchasesColumns[28]}, + Columns: []*schema.Column{ChargeCreditPurchasesColumns[14], ChargeCreditPurchasesColumns[31], ChargeCreditPurchasesColumns[28]}, Annotation: &entsql.IndexAnnotation{ Where: "key IS NOT NULL AND deleted_at IS NULL", }, @@ -2062,7 +2069,7 @@ var ( {Name: "full_service_period_to", Type: field.TypeTime}, {Name: "status", Type: field.TypeEnum, Enums: []string{"created", "active", "final", "deleted"}}, {Name: "unique_reference_id", Type: field.TypeString, Nullable: true}, - {Name: "currency", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(3)"}}, + {Name: "currency", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "varchar(3)"}}, {Name: "managed_by", Type: field.TypeEnum, Enums: []string{"subscription", "system", "manual"}}, {Name: "advance_after", Type: field.TypeTime, Nullable: true}, {Name: "tax_behavior", Type: field.TypeEnum, Nullable: true, Enums: []string{"inclusive", "exclusive"}}, @@ -2085,6 +2092,7 @@ var ( {Name: "amount_after_proration", Type: field.TypeOther, SchemaType: map[string]string{"postgres": "numeric"}}, {Name: "status_detailed", Type: field.TypeEnum, Enums: []string{"created", "active", "active.realization.started", "active.realization.waiting_for_collection", "active.realization.processing", "active.realization.issuing", "active.realization.completed", "active.awaiting_payment_settlement", "final", "deleted"}}, {Name: "current_realization_run_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, + {Name: "custom_currency_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, {Name: "customer_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "char(26)"}}, {Name: "feature_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, {Name: "subscription_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, @@ -2105,38 +2113,44 @@ var ( OnDelete: schema.SetNull, }, { - Symbol: "charge_flat_fees_customers_charges_flat_fee", + Symbol: "charge_flat_fees_custom_currencies_charges_flat_fee", Columns: []*schema.Column{ChargeFlatFeesColumns[32]}, + RefColumns: []*schema.Column{CustomCurrenciesColumns[0]}, + OnDelete: schema.Restrict, + }, + { + Symbol: "charge_flat_fees_customers_charges_flat_fee", + Columns: []*schema.Column{ChargeFlatFeesColumns[33]}, RefColumns: []*schema.Column{CustomersColumns[0]}, OnDelete: schema.NoAction, }, { Symbol: "charge_flat_fees_features_flat_fee_charges", - Columns: []*schema.Column{ChargeFlatFeesColumns[33]}, + Columns: []*schema.Column{ChargeFlatFeesColumns[34]}, RefColumns: []*schema.Column{FeaturesColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "charge_flat_fees_subscriptions_charges_flat_fee", - Columns: []*schema.Column{ChargeFlatFeesColumns[34]}, + Columns: []*schema.Column{ChargeFlatFeesColumns[35]}, RefColumns: []*schema.Column{SubscriptionsColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "charge_flat_fees_subscription_items_charges_flat_fee", - Columns: []*schema.Column{ChargeFlatFeesColumns[35]}, + Columns: []*schema.Column{ChargeFlatFeesColumns[36]}, RefColumns: []*schema.Column{SubscriptionItemsColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "charge_flat_fees_subscription_phases_charges_flat_fee", - Columns: []*schema.Column{ChargeFlatFeesColumns[36]}, + Columns: []*schema.Column{ChargeFlatFeesColumns[37]}, RefColumns: []*schema.Column{SubscriptionPhasesColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "charge_flat_fees_tax_codes_charge_flat_fees", - Columns: []*schema.Column{ChargeFlatFeesColumns[37]}, + Columns: []*schema.Column{ChargeFlatFeesColumns[38]}, RefColumns: []*schema.Column{TaxCodesColumns[0]}, OnDelete: schema.NoAction, }, @@ -2145,7 +2159,7 @@ var ( { Name: "chargeflatfee_namespace_customer_id_unique_reference_id", Unique: true, - Columns: []*schema.Column{ChargeFlatFeesColumns[14], ChargeFlatFeesColumns[32], ChargeFlatFeesColumns[8]}, + Columns: []*schema.Column{ChargeFlatFeesColumns[14], ChargeFlatFeesColumns[33], ChargeFlatFeesColumns[8]}, Annotation: &entsql.IndexAnnotation{ Where: "unique_reference_id IS NOT NULL AND deleted_at IS NULL", }, @@ -2178,7 +2192,7 @@ var ( { Name: "chargeflatfees_tax_code_id", Unique: false, - Columns: []*schema.Column{ChargeFlatFeesColumns[37]}, + Columns: []*schema.Column{ChargeFlatFeesColumns[38]}, }, }, } @@ -2606,7 +2620,7 @@ var ( {Name: "full_service_period_to", Type: field.TypeTime}, {Name: "status", Type: field.TypeEnum, Enums: []string{"created", "active", "final", "deleted"}}, {Name: "unique_reference_id", Type: field.TypeString, Nullable: true}, - {Name: "currency", Type: field.TypeString, SchemaType: map[string]string{"postgres": "varchar(3)"}}, + {Name: "currency", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "varchar(3)"}}, {Name: "managed_by", Type: field.TypeEnum, Enums: []string{"subscription", "system", "manual"}}, {Name: "advance_after", Type: field.TypeTime, Nullable: true}, {Name: "tax_behavior", Type: field.TypeEnum, Nullable: true, Enums: []string{"inclusive", "exclusive"}}, @@ -2628,6 +2642,7 @@ var ( {Name: "unit_config", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "jsonb"}}, {Name: "status_detailed", Type: field.TypeEnum, Enums: []string{"created", "active", "active.realization.started", "active.realization.waiting_for_collection", "active.realization.processing", "active.realization.issuing", "active.realization.completed", "active.awaiting_payment_settlement", "final", "deleted"}}, {Name: "current_realization_run_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, + {Name: "custom_currency_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, {Name: "customer_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "char(26)"}}, {Name: "feature_id", Type: field.TypeString, SchemaType: map[string]string{"postgres": "char(26)"}}, {Name: "subscription_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "char(26)"}}, @@ -2648,38 +2663,44 @@ var ( OnDelete: schema.SetNull, }, { - Symbol: "charge_usage_based_customers_charges_usage_based", + Symbol: "charge_usage_based_custom_currencies_charges_usage_based", Columns: []*schema.Column{ChargeUsageBasedColumns[31]}, + RefColumns: []*schema.Column{CustomCurrenciesColumns[0]}, + OnDelete: schema.Restrict, + }, + { + Symbol: "charge_usage_based_customers_charges_usage_based", + Columns: []*schema.Column{ChargeUsageBasedColumns[32]}, RefColumns: []*schema.Column{CustomersColumns[0]}, OnDelete: schema.NoAction, }, { Symbol: "charge_usage_based_features_usage_based_charges", - Columns: []*schema.Column{ChargeUsageBasedColumns[32]}, + Columns: []*schema.Column{ChargeUsageBasedColumns[33]}, RefColumns: []*schema.Column{FeaturesColumns[0]}, OnDelete: schema.NoAction, }, { Symbol: "charge_usage_based_subscriptions_charges_usage_based", - Columns: []*schema.Column{ChargeUsageBasedColumns[33]}, + Columns: []*schema.Column{ChargeUsageBasedColumns[34]}, RefColumns: []*schema.Column{SubscriptionsColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "charge_usage_based_subscription_items_charges_usage_based", - Columns: []*schema.Column{ChargeUsageBasedColumns[34]}, + Columns: []*schema.Column{ChargeUsageBasedColumns[35]}, RefColumns: []*schema.Column{SubscriptionItemsColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "charge_usage_based_subscription_phases_charges_usage_based", - Columns: []*schema.Column{ChargeUsageBasedColumns[35]}, + Columns: []*schema.Column{ChargeUsageBasedColumns[36]}, RefColumns: []*schema.Column{SubscriptionPhasesColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "charge_usage_based_tax_codes_charge_usage_based", - Columns: []*schema.Column{ChargeUsageBasedColumns[36]}, + Columns: []*schema.Column{ChargeUsageBasedColumns[37]}, RefColumns: []*schema.Column{TaxCodesColumns[0]}, OnDelete: schema.NoAction, }, @@ -2688,7 +2709,7 @@ var ( { Name: "chargeusagebased_namespace_customer_id_unique_reference_id", Unique: true, - Columns: []*schema.Column{ChargeUsageBasedColumns[14], ChargeUsageBasedColumns[31], ChargeUsageBasedColumns[8]}, + Columns: []*schema.Column{ChargeUsageBasedColumns[14], ChargeUsageBasedColumns[32], ChargeUsageBasedColumns[8]}, Annotation: &entsql.IndexAnnotation{ Where: "unique_reference_id IS NOT NULL AND deleted_at IS NULL", }, @@ -2721,7 +2742,7 @@ var ( { Name: "chargeusagebased_tax_code_id", Unique: false, - Columns: []*schema.Column{ChargeUsageBasedColumns[36]}, + Columns: []*schema.Column{ChargeUsageBasedColumns[37]}, }, }, } @@ -5730,22 +5751,32 @@ func init() { ChargesTable.ForeignKeys[0].RefTable = ChargeCreditPurchasesTable ChargesTable.ForeignKeys[1].RefTable = ChargeFlatFeesTable ChargesTable.ForeignKeys[2].RefTable = ChargeUsageBasedTable - ChargeCreditPurchasesTable.ForeignKeys[0].RefTable = CustomersTable - ChargeCreditPurchasesTable.ForeignKeys[1].RefTable = SubscriptionsTable - ChargeCreditPurchasesTable.ForeignKeys[2].RefTable = SubscriptionItemsTable - ChargeCreditPurchasesTable.ForeignKeys[3].RefTable = SubscriptionPhasesTable - ChargeCreditPurchasesTable.ForeignKeys[4].RefTable = TaxCodesTable + ChargeCreditPurchasesTable.ForeignKeys[0].RefTable = CustomCurrenciesTable + ChargeCreditPurchasesTable.ForeignKeys[1].RefTable = CustomersTable + ChargeCreditPurchasesTable.ForeignKeys[2].RefTable = SubscriptionsTable + ChargeCreditPurchasesTable.ForeignKeys[3].RefTable = SubscriptionItemsTable + ChargeCreditPurchasesTable.ForeignKeys[4].RefTable = SubscriptionPhasesTable + ChargeCreditPurchasesTable.ForeignKeys[5].RefTable = TaxCodesTable + ChargeCreditPurchasesTable.Annotation = &entsql.Annotation{} + ChargeCreditPurchasesTable.Annotation.Checks = map[string]string{ + "currency_reference": "(currency IS NULL) <> (custom_currency_id IS NULL)", + } ChargeCreditPurchaseCreditGrantsTable.ForeignKeys[0].RefTable = ChargeCreditPurchasesTable ChargeCreditPurchaseExternalPaymentsTable.ForeignKeys[0].RefTable = ChargeCreditPurchasesTable ChargeCreditPurchaseInvoicedPaymentsTable.ForeignKeys[0].RefTable = BillingInvoiceLinesTable ChargeCreditPurchaseInvoicedPaymentsTable.ForeignKeys[1].RefTable = ChargeCreditPurchasesTable ChargeFlatFeesTable.ForeignKeys[0].RefTable = ChargeFlatFeeRunsTable - ChargeFlatFeesTable.ForeignKeys[1].RefTable = CustomersTable - ChargeFlatFeesTable.ForeignKeys[2].RefTable = FeaturesTable - ChargeFlatFeesTable.ForeignKeys[3].RefTable = SubscriptionsTable - ChargeFlatFeesTable.ForeignKeys[4].RefTable = SubscriptionItemsTable - ChargeFlatFeesTable.ForeignKeys[5].RefTable = SubscriptionPhasesTable - ChargeFlatFeesTable.ForeignKeys[6].RefTable = TaxCodesTable + ChargeFlatFeesTable.ForeignKeys[1].RefTable = CustomCurrenciesTable + ChargeFlatFeesTable.ForeignKeys[2].RefTable = CustomersTable + ChargeFlatFeesTable.ForeignKeys[3].RefTable = FeaturesTable + ChargeFlatFeesTable.ForeignKeys[4].RefTable = SubscriptionsTable + ChargeFlatFeesTable.ForeignKeys[5].RefTable = SubscriptionItemsTable + ChargeFlatFeesTable.ForeignKeys[6].RefTable = SubscriptionPhasesTable + ChargeFlatFeesTable.ForeignKeys[7].RefTable = TaxCodesTable + ChargeFlatFeesTable.Annotation = &entsql.Annotation{} + ChargeFlatFeesTable.Annotation.Checks = map[string]string{ + "currency_reference": "(currency IS NULL) <> (custom_currency_id IS NULL)", + } ChargeFlatFeeOverridesTable.ForeignKeys[0].RefTable = ChargeFlatFeesTable ChargeFlatFeeOverridesTable.ForeignKeys[1].RefTable = TaxCodesTable ChargeFlatFeeRunsTable.ForeignKeys[0].RefTable = BillingInvoicesTable @@ -5763,15 +5794,19 @@ func init() { ChargeFlatFeeRunPaymentsTable.ForeignKeys[0].RefTable = BillingInvoiceLinesTable ChargeFlatFeeRunPaymentsTable.ForeignKeys[1].RefTable = ChargeFlatFeeRunsTable ChargeUsageBasedTable.ForeignKeys[0].RefTable = ChargeUsageBasedRunsTable - ChargeUsageBasedTable.ForeignKeys[1].RefTable = CustomersTable - ChargeUsageBasedTable.ForeignKeys[2].RefTable = FeaturesTable - ChargeUsageBasedTable.ForeignKeys[3].RefTable = SubscriptionsTable - ChargeUsageBasedTable.ForeignKeys[4].RefTable = SubscriptionItemsTable - ChargeUsageBasedTable.ForeignKeys[5].RefTable = SubscriptionPhasesTable - ChargeUsageBasedTable.ForeignKeys[6].RefTable = TaxCodesTable + ChargeUsageBasedTable.ForeignKeys[1].RefTable = CustomCurrenciesTable + ChargeUsageBasedTable.ForeignKeys[2].RefTable = CustomersTable + ChargeUsageBasedTable.ForeignKeys[3].RefTable = FeaturesTable + ChargeUsageBasedTable.ForeignKeys[4].RefTable = SubscriptionsTable + ChargeUsageBasedTable.ForeignKeys[5].RefTable = SubscriptionItemsTable + ChargeUsageBasedTable.ForeignKeys[6].RefTable = SubscriptionPhasesTable + ChargeUsageBasedTable.ForeignKeys[7].RefTable = TaxCodesTable ChargeUsageBasedTable.Annotation = &entsql.Annotation{ Table: "charge_usage_based", } + ChargeUsageBasedTable.Annotation.Checks = map[string]string{ + "currency_reference": "(currency IS NULL) <> (custom_currency_id IS NULL)", + } ChargeUsageBasedOverridesTable.ForeignKeys[0].RefTable = ChargeUsageBasedTable ChargeUsageBasedOverridesTable.ForeignKeys[1].RefTable = TaxCodesTable ChargeUsageBasedRunCreditAllocationsTable.ForeignKeys[0].RefTable = ChargeUsageBasedRunCreditAllocationsTable diff --git a/openmeter/ent/db/mutation.go b/openmeter/ent/db/mutation.go index 63e8a5da64..c557248cfe 100644 --- a/openmeter/ent/db/mutation.go +++ b/openmeter/ent/db/mutation.go @@ -39253,7 +39253,7 @@ type ChargeCreditPurchaseMutation struct { full_service_period_to *time.Time status *meta.ChargeStatus unique_reference_id *string - currency *currencyx.Code + fiat_currency_code *currencyx.Code managed_by *billing.InvoiceLineManagedBy advance_after *time.Time tax_behavior *productcatalog.TaxBehavior @@ -39294,6 +39294,8 @@ type ChargeCreditPurchaseMutation struct { clearedcustomer bool tax_code *string clearedtax_code bool + custom_currency *string + clearedcustom_currency bool done bool oldValue func(context.Context) (*ChargeCreditPurchase, error) predicates []predicate.ChargeCreditPurchase @@ -39740,40 +39742,102 @@ func (m *ChargeCreditPurchaseMutation) ResetUniqueReferenceID() { delete(m.clearedFields, chargecreditpurchase.FieldUniqueReferenceID) } -// SetCurrency sets the "currency" field. -func (m *ChargeCreditPurchaseMutation) SetCurrency(c currencyx.Code) { - m.currency = &c +// SetFiatCurrencyCode sets the "fiat_currency_code" field. +func (m *ChargeCreditPurchaseMutation) SetFiatCurrencyCode(c currencyx.Code) { + m.fiat_currency_code = &c } -// Currency returns the value of the "currency" field in the mutation. -func (m *ChargeCreditPurchaseMutation) Currency() (r currencyx.Code, exists bool) { - v := m.currency +// FiatCurrencyCode returns the value of the "fiat_currency_code" field in the mutation. +func (m *ChargeCreditPurchaseMutation) FiatCurrencyCode() (r currencyx.Code, exists bool) { + v := m.fiat_currency_code if v == nil { return } return *v, true } -// OldCurrency returns the old "currency" field's value of the ChargeCreditPurchase entity. +// OldFiatCurrencyCode returns the old "fiat_currency_code" field's value of the ChargeCreditPurchase entity. // If the ChargeCreditPurchase object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ChargeCreditPurchaseMutation) OldCurrency(ctx context.Context) (v currencyx.Code, err error) { +func (m *ChargeCreditPurchaseMutation) OldFiatCurrencyCode(ctx context.Context) (v *currencyx.Code, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCurrency is only allowed on UpdateOne operations") + return v, errors.New("OldFiatCurrencyCode is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCurrency requires an ID field in the mutation") + return v, errors.New("OldFiatCurrencyCode requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldCurrency: %w", err) + return v, fmt.Errorf("querying old value for OldFiatCurrencyCode: %w", err) } - return oldValue.Currency, nil + return oldValue.FiatCurrencyCode, nil } -// ResetCurrency resets all changes to the "currency" field. -func (m *ChargeCreditPurchaseMutation) ResetCurrency() { - m.currency = nil +// ClearFiatCurrencyCode clears the value of the "fiat_currency_code" field. +func (m *ChargeCreditPurchaseMutation) ClearFiatCurrencyCode() { + m.fiat_currency_code = nil + m.clearedFields[chargecreditpurchase.FieldFiatCurrencyCode] = struct{}{} +} + +// FiatCurrencyCodeCleared returns if the "fiat_currency_code" field was cleared in this mutation. +func (m *ChargeCreditPurchaseMutation) FiatCurrencyCodeCleared() bool { + _, ok := m.clearedFields[chargecreditpurchase.FieldFiatCurrencyCode] + return ok +} + +// ResetFiatCurrencyCode resets all changes to the "fiat_currency_code" field. +func (m *ChargeCreditPurchaseMutation) ResetFiatCurrencyCode() { + m.fiat_currency_code = nil + delete(m.clearedFields, chargecreditpurchase.FieldFiatCurrencyCode) +} + +// SetCustomCurrencyID sets the "custom_currency_id" field. +func (m *ChargeCreditPurchaseMutation) SetCustomCurrencyID(s string) { + m.custom_currency = &s +} + +// CustomCurrencyID returns the value of the "custom_currency_id" field in the mutation. +func (m *ChargeCreditPurchaseMutation) CustomCurrencyID() (r string, exists bool) { + v := m.custom_currency + if v == nil { + return + } + return *v, true +} + +// OldCustomCurrencyID returns the old "custom_currency_id" field's value of the ChargeCreditPurchase entity. +// If the ChargeCreditPurchase object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ChargeCreditPurchaseMutation) OldCustomCurrencyID(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCustomCurrencyID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCustomCurrencyID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCustomCurrencyID: %w", err) + } + return oldValue.CustomCurrencyID, nil +} + +// ClearCustomCurrencyID clears the value of the "custom_currency_id" field. +func (m *ChargeCreditPurchaseMutation) ClearCustomCurrencyID() { + m.custom_currency = nil + m.clearedFields[chargecreditpurchase.FieldCustomCurrencyID] = struct{}{} +} + +// CustomCurrencyIDCleared returns if the "custom_currency_id" field was cleared in this mutation. +func (m *ChargeCreditPurchaseMutation) CustomCurrencyIDCleared() bool { + _, ok := m.clearedFields[chargecreditpurchase.FieldCustomCurrencyID] + return ok +} + +// ResetCustomCurrencyID resets all changes to the "custom_currency_id" field. +func (m *ChargeCreditPurchaseMutation) ResetCustomCurrencyID() { + m.custom_currency = nil + delete(m.clearedFields, chargecreditpurchase.FieldCustomCurrencyID) } // SetManagedBy sets the "managed_by" field. @@ -41147,6 +41211,33 @@ func (m *ChargeCreditPurchaseMutation) ResetTaxCode() { m.clearedtax_code = false } +// ClearCustomCurrency clears the "custom_currency" edge to the CustomCurrency entity. +func (m *ChargeCreditPurchaseMutation) ClearCustomCurrency() { + m.clearedcustom_currency = true + m.clearedFields[chargecreditpurchase.FieldCustomCurrencyID] = struct{}{} +} + +// CustomCurrencyCleared reports if the "custom_currency" edge to the CustomCurrency entity was cleared. +func (m *ChargeCreditPurchaseMutation) CustomCurrencyCleared() bool { + return m.CustomCurrencyIDCleared() || m.clearedcustom_currency +} + +// CustomCurrencyIDs returns the "custom_currency" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// CustomCurrencyID instead. It exists only for internal usage by the builders. +func (m *ChargeCreditPurchaseMutation) CustomCurrencyIDs() (ids []string) { + if id := m.custom_currency; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetCustomCurrency resets all changes to the "custom_currency" edge. +func (m *ChargeCreditPurchaseMutation) ResetCustomCurrency() { + m.custom_currency = nil + m.clearedcustom_currency = false +} + // Where appends a list predicates to the ChargeCreditPurchaseMutation builder. func (m *ChargeCreditPurchaseMutation) Where(ps ...predicate.ChargeCreditPurchase) { m.predicates = append(m.predicates, ps...) @@ -41181,7 +41272,7 @@ func (m *ChargeCreditPurchaseMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *ChargeCreditPurchaseMutation) Fields() []string { - fields := make([]string, 0, 34) + fields := make([]string, 0, 35) if m.customer != nil { fields = append(fields, chargecreditpurchase.FieldCustomerID) } @@ -41209,8 +41300,11 @@ func (m *ChargeCreditPurchaseMutation) Fields() []string { if m.unique_reference_id != nil { fields = append(fields, chargecreditpurchase.FieldUniqueReferenceID) } - if m.currency != nil { - fields = append(fields, chargecreditpurchase.FieldCurrency) + if m.fiat_currency_code != nil { + fields = append(fields, chargecreditpurchase.FieldFiatCurrencyCode) + } + if m.custom_currency != nil { + fields = append(fields, chargecreditpurchase.FieldCustomCurrencyID) } if m.managed_by != nil { fields = append(fields, chargecreditpurchase.FieldManagedBy) @@ -41310,8 +41404,10 @@ func (m *ChargeCreditPurchaseMutation) Field(name string) (ent.Value, bool) { return m.Status() case chargecreditpurchase.FieldUniqueReferenceID: return m.UniqueReferenceID() - case chargecreditpurchase.FieldCurrency: - return m.Currency() + case chargecreditpurchase.FieldFiatCurrencyCode: + return m.FiatCurrencyCode() + case chargecreditpurchase.FieldCustomCurrencyID: + return m.CustomCurrencyID() case chargecreditpurchase.FieldManagedBy: return m.ManagedBy() case chargecreditpurchase.FieldSubscriptionID: @@ -41387,8 +41483,10 @@ func (m *ChargeCreditPurchaseMutation) OldField(ctx context.Context, name string return m.OldStatus(ctx) case chargecreditpurchase.FieldUniqueReferenceID: return m.OldUniqueReferenceID(ctx) - case chargecreditpurchase.FieldCurrency: - return m.OldCurrency(ctx) + case chargecreditpurchase.FieldFiatCurrencyCode: + return m.OldFiatCurrencyCode(ctx) + case chargecreditpurchase.FieldCustomCurrencyID: + return m.OldCustomCurrencyID(ctx) case chargecreditpurchase.FieldManagedBy: return m.OldManagedBy(ctx) case chargecreditpurchase.FieldSubscriptionID: @@ -41509,12 +41607,19 @@ func (m *ChargeCreditPurchaseMutation) SetField(name string, value ent.Value) er } m.SetUniqueReferenceID(v) return nil - case chargecreditpurchase.FieldCurrency: + case chargecreditpurchase.FieldFiatCurrencyCode: v, ok := value.(currencyx.Code) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetCurrency(v) + m.SetFiatCurrencyCode(v) + return nil + case chargecreditpurchase.FieldCustomCurrencyID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCustomCurrencyID(v) return nil case chargecreditpurchase.FieldManagedBy: v, ok := value.(billing.InvoiceLineManagedBy) @@ -41732,6 +41837,12 @@ func (m *ChargeCreditPurchaseMutation) ClearedFields() []string { if m.FieldCleared(chargecreditpurchase.FieldUniqueReferenceID) { fields = append(fields, chargecreditpurchase.FieldUniqueReferenceID) } + if m.FieldCleared(chargecreditpurchase.FieldFiatCurrencyCode) { + fields = append(fields, chargecreditpurchase.FieldFiatCurrencyCode) + } + if m.FieldCleared(chargecreditpurchase.FieldCustomCurrencyID) { + fields = append(fields, chargecreditpurchase.FieldCustomCurrencyID) + } if m.FieldCleared(chargecreditpurchase.FieldSubscriptionID) { fields = append(fields, chargecreditpurchase.FieldSubscriptionID) } @@ -41794,6 +41905,12 @@ func (m *ChargeCreditPurchaseMutation) ClearField(name string) error { case chargecreditpurchase.FieldUniqueReferenceID: m.ClearUniqueReferenceID() return nil + case chargecreditpurchase.FieldFiatCurrencyCode: + m.ClearFiatCurrencyCode() + return nil + case chargecreditpurchase.FieldCustomCurrencyID: + m.ClearCustomCurrencyID() + return nil case chargecreditpurchase.FieldSubscriptionID: m.ClearSubscriptionID() return nil @@ -41874,8 +41991,11 @@ func (m *ChargeCreditPurchaseMutation) ResetField(name string) error { case chargecreditpurchase.FieldUniqueReferenceID: m.ResetUniqueReferenceID() return nil - case chargecreditpurchase.FieldCurrency: - m.ResetCurrency() + case chargecreditpurchase.FieldFiatCurrencyCode: + m.ResetFiatCurrencyCode() + return nil + case chargecreditpurchase.FieldCustomCurrencyID: + m.ResetCustomCurrencyID() return nil case chargecreditpurchase.FieldManagedBy: m.ResetManagedBy() @@ -41955,7 +42075,7 @@ func (m *ChargeCreditPurchaseMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *ChargeCreditPurchaseMutation) AddedEdges() []string { - edges := make([]string, 0, 9) + edges := make([]string, 0, 10) if m.external_payment != nil { edges = append(edges, chargecreditpurchase.EdgeExternalPayment) } @@ -41983,6 +42103,9 @@ func (m *ChargeCreditPurchaseMutation) AddedEdges() []string { if m.tax_code != nil { edges = append(edges, chargecreditpurchase.EdgeTaxCode) } + if m.custom_currency != nil { + edges = append(edges, chargecreditpurchase.EdgeCustomCurrency) + } return edges } @@ -42026,13 +42149,17 @@ func (m *ChargeCreditPurchaseMutation) AddedIDs(name string) []ent.Value { if id := m.tax_code; id != nil { return []ent.Value{*id} } + case chargecreditpurchase.EdgeCustomCurrency: + if id := m.custom_currency; id != nil { + return []ent.Value{*id} + } } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *ChargeCreditPurchaseMutation) RemovedEdges() []string { - edges := make([]string, 0, 9) + edges := make([]string, 0, 10) return edges } @@ -42044,7 +42171,7 @@ func (m *ChargeCreditPurchaseMutation) RemovedIDs(name string) []ent.Value { // ClearedEdges returns all edge names that were cleared in this mutation. func (m *ChargeCreditPurchaseMutation) ClearedEdges() []string { - edges := make([]string, 0, 9) + edges := make([]string, 0, 10) if m.clearedexternal_payment { edges = append(edges, chargecreditpurchase.EdgeExternalPayment) } @@ -42072,6 +42199,9 @@ func (m *ChargeCreditPurchaseMutation) ClearedEdges() []string { if m.clearedtax_code { edges = append(edges, chargecreditpurchase.EdgeTaxCode) } + if m.clearedcustom_currency { + edges = append(edges, chargecreditpurchase.EdgeCustomCurrency) + } return edges } @@ -42097,6 +42227,8 @@ func (m *ChargeCreditPurchaseMutation) EdgeCleared(name string) bool { return m.clearedcustomer case chargecreditpurchase.EdgeTaxCode: return m.clearedtax_code + case chargecreditpurchase.EdgeCustomCurrency: + return m.clearedcustom_currency } return false } @@ -42132,6 +42264,9 @@ func (m *ChargeCreditPurchaseMutation) ClearEdge(name string) error { case chargecreditpurchase.EdgeTaxCode: m.ClearTaxCode() return nil + case chargecreditpurchase.EdgeCustomCurrency: + m.ClearCustomCurrency() + return nil } return fmt.Errorf("unknown ChargeCreditPurchase unique edge %s", name) } @@ -42167,6 +42302,9 @@ func (m *ChargeCreditPurchaseMutation) ResetEdge(name string) error { case chargecreditpurchase.EdgeTaxCode: m.ResetTaxCode() return nil + case chargecreditpurchase.EdgeCustomCurrency: + m.ResetCustomCurrency() + return nil } return fmt.Errorf("unknown ChargeCreditPurchase edge %s", name) } @@ -45533,7 +45671,7 @@ type ChargeFlatFeeMutation struct { full_service_period_to *time.Time status *meta.ChargeStatus unique_reference_id *string - currency *currencyx.Code + fiat_currency_code *currencyx.Code managed_by *billing.InvoiceLineManagedBy advance_after *time.Time tax_behavior *productcatalog.TaxBehavior @@ -45577,6 +45715,8 @@ type ChargeFlatFeeMutation struct { clearedfeature bool tax_code *string clearedtax_code bool + custom_currency *string + clearedcustom_currency bool done bool oldValue func(context.Context) (*ChargeFlatFee, error) predicates []predicate.ChargeFlatFee @@ -46023,40 +46163,102 @@ func (m *ChargeFlatFeeMutation) ResetUniqueReferenceID() { delete(m.clearedFields, chargeflatfee.FieldUniqueReferenceID) } -// SetCurrency sets the "currency" field. -func (m *ChargeFlatFeeMutation) SetCurrency(c currencyx.Code) { - m.currency = &c +// SetFiatCurrencyCode sets the "fiat_currency_code" field. +func (m *ChargeFlatFeeMutation) SetFiatCurrencyCode(c currencyx.Code) { + m.fiat_currency_code = &c } -// Currency returns the value of the "currency" field in the mutation. -func (m *ChargeFlatFeeMutation) Currency() (r currencyx.Code, exists bool) { - v := m.currency +// FiatCurrencyCode returns the value of the "fiat_currency_code" field in the mutation. +func (m *ChargeFlatFeeMutation) FiatCurrencyCode() (r currencyx.Code, exists bool) { + v := m.fiat_currency_code if v == nil { return } return *v, true } -// OldCurrency returns the old "currency" field's value of the ChargeFlatFee entity. +// OldFiatCurrencyCode returns the old "fiat_currency_code" field's value of the ChargeFlatFee entity. // If the ChargeFlatFee object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ChargeFlatFeeMutation) OldCurrency(ctx context.Context) (v currencyx.Code, err error) { +func (m *ChargeFlatFeeMutation) OldFiatCurrencyCode(ctx context.Context) (v *currencyx.Code, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCurrency is only allowed on UpdateOne operations") + return v, errors.New("OldFiatCurrencyCode is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCurrency requires an ID field in the mutation") + return v, errors.New("OldFiatCurrencyCode requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldCurrency: %w", err) + return v, fmt.Errorf("querying old value for OldFiatCurrencyCode: %w", err) } - return oldValue.Currency, nil + return oldValue.FiatCurrencyCode, nil } -// ResetCurrency resets all changes to the "currency" field. -func (m *ChargeFlatFeeMutation) ResetCurrency() { - m.currency = nil +// ClearFiatCurrencyCode clears the value of the "fiat_currency_code" field. +func (m *ChargeFlatFeeMutation) ClearFiatCurrencyCode() { + m.fiat_currency_code = nil + m.clearedFields[chargeflatfee.FieldFiatCurrencyCode] = struct{}{} +} + +// FiatCurrencyCodeCleared returns if the "fiat_currency_code" field was cleared in this mutation. +func (m *ChargeFlatFeeMutation) FiatCurrencyCodeCleared() bool { + _, ok := m.clearedFields[chargeflatfee.FieldFiatCurrencyCode] + return ok +} + +// ResetFiatCurrencyCode resets all changes to the "fiat_currency_code" field. +func (m *ChargeFlatFeeMutation) ResetFiatCurrencyCode() { + m.fiat_currency_code = nil + delete(m.clearedFields, chargeflatfee.FieldFiatCurrencyCode) +} + +// SetCustomCurrencyID sets the "custom_currency_id" field. +func (m *ChargeFlatFeeMutation) SetCustomCurrencyID(s string) { + m.custom_currency = &s +} + +// CustomCurrencyID returns the value of the "custom_currency_id" field in the mutation. +func (m *ChargeFlatFeeMutation) CustomCurrencyID() (r string, exists bool) { + v := m.custom_currency + if v == nil { + return + } + return *v, true +} + +// OldCustomCurrencyID returns the old "custom_currency_id" field's value of the ChargeFlatFee entity. +// If the ChargeFlatFee object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ChargeFlatFeeMutation) OldCustomCurrencyID(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCustomCurrencyID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCustomCurrencyID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCustomCurrencyID: %w", err) + } + return oldValue.CustomCurrencyID, nil +} + +// ClearCustomCurrencyID clears the value of the "custom_currency_id" field. +func (m *ChargeFlatFeeMutation) ClearCustomCurrencyID() { + m.custom_currency = nil + m.clearedFields[chargeflatfee.FieldCustomCurrencyID] = struct{}{} +} + +// CustomCurrencyIDCleared returns if the "custom_currency_id" field was cleared in this mutation. +func (m *ChargeFlatFeeMutation) CustomCurrencyIDCleared() bool { + _, ok := m.clearedFields[chargeflatfee.FieldCustomCurrencyID] + return ok +} + +// ResetCustomCurrencyID resets all changes to the "custom_currency_id" field. +func (m *ChargeFlatFeeMutation) ResetCustomCurrencyID() { + m.custom_currency = nil + delete(m.clearedFields, chargeflatfee.FieldCustomCurrencyID) } // SetManagedBy sets the "managed_by" field. @@ -47547,6 +47749,33 @@ func (m *ChargeFlatFeeMutation) ResetTaxCode() { m.clearedtax_code = false } +// ClearCustomCurrency clears the "custom_currency" edge to the CustomCurrency entity. +func (m *ChargeFlatFeeMutation) ClearCustomCurrency() { + m.clearedcustom_currency = true + m.clearedFields[chargeflatfee.FieldCustomCurrencyID] = struct{}{} +} + +// CustomCurrencyCleared reports if the "custom_currency" edge to the CustomCurrency entity was cleared. +func (m *ChargeFlatFeeMutation) CustomCurrencyCleared() bool { + return m.CustomCurrencyIDCleared() || m.clearedcustom_currency +} + +// CustomCurrencyIDs returns the "custom_currency" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// CustomCurrencyID instead. It exists only for internal usage by the builders. +func (m *ChargeFlatFeeMutation) CustomCurrencyIDs() (ids []string) { + if id := m.custom_currency; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetCustomCurrency resets all changes to the "custom_currency" edge. +func (m *ChargeFlatFeeMutation) ResetCustomCurrency() { + m.custom_currency = nil + m.clearedcustom_currency = false +} + // Where appends a list predicates to the ChargeFlatFeeMutation builder. func (m *ChargeFlatFeeMutation) Where(ps ...predicate.ChargeFlatFee) { m.predicates = append(m.predicates, ps...) @@ -47581,7 +47810,7 @@ func (m *ChargeFlatFeeMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *ChargeFlatFeeMutation) Fields() []string { - fields := make([]string, 0, 37) + fields := make([]string, 0, 38) if m.customer != nil { fields = append(fields, chargeflatfee.FieldCustomerID) } @@ -47609,8 +47838,11 @@ func (m *ChargeFlatFeeMutation) Fields() []string { if m.unique_reference_id != nil { fields = append(fields, chargeflatfee.FieldUniqueReferenceID) } - if m.currency != nil { - fields = append(fields, chargeflatfee.FieldCurrency) + if m.fiat_currency_code != nil { + fields = append(fields, chargeflatfee.FieldFiatCurrencyCode) + } + if m.custom_currency != nil { + fields = append(fields, chargeflatfee.FieldCustomCurrencyID) } if m.managed_by != nil { fields = append(fields, chargeflatfee.FieldManagedBy) @@ -47719,8 +47951,10 @@ func (m *ChargeFlatFeeMutation) Field(name string) (ent.Value, bool) { return m.Status() case chargeflatfee.FieldUniqueReferenceID: return m.UniqueReferenceID() - case chargeflatfee.FieldCurrency: - return m.Currency() + case chargeflatfee.FieldFiatCurrencyCode: + return m.FiatCurrencyCode() + case chargeflatfee.FieldCustomCurrencyID: + return m.CustomCurrencyID() case chargeflatfee.FieldManagedBy: return m.ManagedBy() case chargeflatfee.FieldSubscriptionID: @@ -47802,8 +48036,10 @@ func (m *ChargeFlatFeeMutation) OldField(ctx context.Context, name string) (ent. return m.OldStatus(ctx) case chargeflatfee.FieldUniqueReferenceID: return m.OldUniqueReferenceID(ctx) - case chargeflatfee.FieldCurrency: - return m.OldCurrency(ctx) + case chargeflatfee.FieldFiatCurrencyCode: + return m.OldFiatCurrencyCode(ctx) + case chargeflatfee.FieldCustomCurrencyID: + return m.OldCustomCurrencyID(ctx) case chargeflatfee.FieldManagedBy: return m.OldManagedBy(ctx) case chargeflatfee.FieldSubscriptionID: @@ -47930,12 +48166,19 @@ func (m *ChargeFlatFeeMutation) SetField(name string, value ent.Value) error { } m.SetUniqueReferenceID(v) return nil - case chargeflatfee.FieldCurrency: + case chargeflatfee.FieldFiatCurrencyCode: v, ok := value.(currencyx.Code) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetCurrency(v) + m.SetFiatCurrencyCode(v) + return nil + case chargeflatfee.FieldCustomCurrencyID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCustomCurrencyID(v) return nil case chargeflatfee.FieldManagedBy: v, ok := value.(billing.InvoiceLineManagedBy) @@ -48159,6 +48402,12 @@ func (m *ChargeFlatFeeMutation) ClearedFields() []string { if m.FieldCleared(chargeflatfee.FieldUniqueReferenceID) { fields = append(fields, chargeflatfee.FieldUniqueReferenceID) } + if m.FieldCleared(chargeflatfee.FieldFiatCurrencyCode) { + fields = append(fields, chargeflatfee.FieldFiatCurrencyCode) + } + if m.FieldCleared(chargeflatfee.FieldCustomCurrencyID) { + fields = append(fields, chargeflatfee.FieldCustomCurrencyID) + } if m.FieldCleared(chargeflatfee.FieldSubscriptionID) { fields = append(fields, chargeflatfee.FieldSubscriptionID) } @@ -48218,6 +48467,12 @@ func (m *ChargeFlatFeeMutation) ClearField(name string) error { case chargeflatfee.FieldUniqueReferenceID: m.ClearUniqueReferenceID() return nil + case chargeflatfee.FieldFiatCurrencyCode: + m.ClearFiatCurrencyCode() + return nil + case chargeflatfee.FieldCustomCurrencyID: + m.ClearCustomCurrencyID() + return nil case chargeflatfee.FieldSubscriptionID: m.ClearSubscriptionID() return nil @@ -48295,8 +48550,11 @@ func (m *ChargeFlatFeeMutation) ResetField(name string) error { case chargeflatfee.FieldUniqueReferenceID: m.ResetUniqueReferenceID() return nil - case chargeflatfee.FieldCurrency: - m.ResetCurrency() + case chargeflatfee.FieldFiatCurrencyCode: + m.ResetFiatCurrencyCode() + return nil + case chargeflatfee.FieldCustomCurrencyID: + m.ResetCustomCurrencyID() return nil case chargeflatfee.FieldManagedBy: m.ResetManagedBy() @@ -48385,7 +48643,7 @@ func (m *ChargeFlatFeeMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *ChargeFlatFeeMutation) AddedEdges() []string { - edges := make([]string, 0, 10) + edges := make([]string, 0, 11) if m.runs != nil { edges = append(edges, chargeflatfee.EdgeRuns) } @@ -48416,6 +48674,9 @@ func (m *ChargeFlatFeeMutation) AddedEdges() []string { if m.tax_code != nil { edges = append(edges, chargeflatfee.EdgeTaxCode) } + if m.custom_currency != nil { + edges = append(edges, chargeflatfee.EdgeCustomCurrency) + } return edges } @@ -48465,13 +48726,17 @@ func (m *ChargeFlatFeeMutation) AddedIDs(name string) []ent.Value { if id := m.tax_code; id != nil { return []ent.Value{*id} } + case chargeflatfee.EdgeCustomCurrency: + if id := m.custom_currency; id != nil { + return []ent.Value{*id} + } } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *ChargeFlatFeeMutation) RemovedEdges() []string { - edges := make([]string, 0, 10) + edges := make([]string, 0, 11) if m.removedruns != nil { edges = append(edges, chargeflatfee.EdgeRuns) } @@ -48494,7 +48759,7 @@ func (m *ChargeFlatFeeMutation) RemovedIDs(name string) []ent.Value { // ClearedEdges returns all edge names that were cleared in this mutation. func (m *ChargeFlatFeeMutation) ClearedEdges() []string { - edges := make([]string, 0, 10) + edges := make([]string, 0, 11) if m.clearedruns { edges = append(edges, chargeflatfee.EdgeRuns) } @@ -48525,6 +48790,9 @@ func (m *ChargeFlatFeeMutation) ClearedEdges() []string { if m.clearedtax_code { edges = append(edges, chargeflatfee.EdgeTaxCode) } + if m.clearedcustom_currency { + edges = append(edges, chargeflatfee.EdgeCustomCurrency) + } return edges } @@ -48552,6 +48820,8 @@ func (m *ChargeFlatFeeMutation) EdgeCleared(name string) bool { return m.clearedfeature case chargeflatfee.EdgeTaxCode: return m.clearedtax_code + case chargeflatfee.EdgeCustomCurrency: + return m.clearedcustom_currency } return false } @@ -48587,6 +48857,9 @@ func (m *ChargeFlatFeeMutation) ClearEdge(name string) error { case chargeflatfee.EdgeTaxCode: m.ClearTaxCode() return nil + case chargeflatfee.EdgeCustomCurrency: + m.ClearCustomCurrency() + return nil } return fmt.Errorf("unknown ChargeFlatFee unique edge %s", name) } @@ -48625,6 +48898,9 @@ func (m *ChargeFlatFeeMutation) ResetEdge(name string) error { case chargeflatfee.EdgeTaxCode: m.ResetTaxCode() return nil + case chargeflatfee.EdgeCustomCurrency: + m.ResetCustomCurrency() + return nil } return fmt.Errorf("unknown ChargeFlatFee edge %s", name) } @@ -58479,7 +58755,7 @@ type ChargeUsageBasedMutation struct { full_service_period_to *time.Time status *meta.ChargeStatus unique_reference_id *string - currency *currencyx.Code + fiat_currency_code *currencyx.Code managed_by *billing.InvoiceLineManagedBy advance_after *time.Time tax_behavior *productcatalog.TaxBehavior @@ -58525,6 +58801,8 @@ type ChargeUsageBasedMutation struct { clearedfeature bool tax_code *string clearedtax_code bool + custom_currency *string + clearedcustom_currency bool done bool oldValue func(context.Context) (*ChargeUsageBased, error) predicates []predicate.ChargeUsageBased @@ -58971,40 +59249,102 @@ func (m *ChargeUsageBasedMutation) ResetUniqueReferenceID() { delete(m.clearedFields, chargeusagebased.FieldUniqueReferenceID) } -// SetCurrency sets the "currency" field. -func (m *ChargeUsageBasedMutation) SetCurrency(c currencyx.Code) { - m.currency = &c +// SetFiatCurrencyCode sets the "fiat_currency_code" field. +func (m *ChargeUsageBasedMutation) SetFiatCurrencyCode(c currencyx.Code) { + m.fiat_currency_code = &c } -// Currency returns the value of the "currency" field in the mutation. -func (m *ChargeUsageBasedMutation) Currency() (r currencyx.Code, exists bool) { - v := m.currency +// FiatCurrencyCode returns the value of the "fiat_currency_code" field in the mutation. +func (m *ChargeUsageBasedMutation) FiatCurrencyCode() (r currencyx.Code, exists bool) { + v := m.fiat_currency_code if v == nil { return } return *v, true } -// OldCurrency returns the old "currency" field's value of the ChargeUsageBased entity. +// OldFiatCurrencyCode returns the old "fiat_currency_code" field's value of the ChargeUsageBased entity. // If the ChargeUsageBased object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ChargeUsageBasedMutation) OldCurrency(ctx context.Context) (v currencyx.Code, err error) { +func (m *ChargeUsageBasedMutation) OldFiatCurrencyCode(ctx context.Context) (v *currencyx.Code, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCurrency is only allowed on UpdateOne operations") + return v, errors.New("OldFiatCurrencyCode is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCurrency requires an ID field in the mutation") + return v, errors.New("OldFiatCurrencyCode requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldCurrency: %w", err) + return v, fmt.Errorf("querying old value for OldFiatCurrencyCode: %w", err) } - return oldValue.Currency, nil + return oldValue.FiatCurrencyCode, nil } -// ResetCurrency resets all changes to the "currency" field. -func (m *ChargeUsageBasedMutation) ResetCurrency() { - m.currency = nil +// ClearFiatCurrencyCode clears the value of the "fiat_currency_code" field. +func (m *ChargeUsageBasedMutation) ClearFiatCurrencyCode() { + m.fiat_currency_code = nil + m.clearedFields[chargeusagebased.FieldFiatCurrencyCode] = struct{}{} +} + +// FiatCurrencyCodeCleared returns if the "fiat_currency_code" field was cleared in this mutation. +func (m *ChargeUsageBasedMutation) FiatCurrencyCodeCleared() bool { + _, ok := m.clearedFields[chargeusagebased.FieldFiatCurrencyCode] + return ok +} + +// ResetFiatCurrencyCode resets all changes to the "fiat_currency_code" field. +func (m *ChargeUsageBasedMutation) ResetFiatCurrencyCode() { + m.fiat_currency_code = nil + delete(m.clearedFields, chargeusagebased.FieldFiatCurrencyCode) +} + +// SetCustomCurrencyID sets the "custom_currency_id" field. +func (m *ChargeUsageBasedMutation) SetCustomCurrencyID(s string) { + m.custom_currency = &s +} + +// CustomCurrencyID returns the value of the "custom_currency_id" field in the mutation. +func (m *ChargeUsageBasedMutation) CustomCurrencyID() (r string, exists bool) { + v := m.custom_currency + if v == nil { + return + } + return *v, true +} + +// OldCustomCurrencyID returns the old "custom_currency_id" field's value of the ChargeUsageBased entity. +// If the ChargeUsageBased object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ChargeUsageBasedMutation) OldCustomCurrencyID(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCustomCurrencyID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCustomCurrencyID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCustomCurrencyID: %w", err) + } + return oldValue.CustomCurrencyID, nil +} + +// ClearCustomCurrencyID clears the value of the "custom_currency_id" field. +func (m *ChargeUsageBasedMutation) ClearCustomCurrencyID() { + m.custom_currency = nil + m.clearedFields[chargeusagebased.FieldCustomCurrencyID] = struct{}{} +} + +// CustomCurrencyIDCleared returns if the "custom_currency_id" field was cleared in this mutation. +func (m *ChargeUsageBasedMutation) CustomCurrencyIDCleared() bool { + _, ok := m.clearedFields[chargeusagebased.FieldCustomCurrencyID] + return ok +} + +// ResetCustomCurrencyID resets all changes to the "custom_currency_id" field. +func (m *ChargeUsageBasedMutation) ResetCustomCurrencyID() { + m.custom_currency = nil + delete(m.clearedFields, chargeusagebased.FieldCustomCurrencyID) } // SetManagedBy sets the "managed_by" field. @@ -60500,6 +60840,33 @@ func (m *ChargeUsageBasedMutation) ResetTaxCode() { m.clearedtax_code = false } +// ClearCustomCurrency clears the "custom_currency" edge to the CustomCurrency entity. +func (m *ChargeUsageBasedMutation) ClearCustomCurrency() { + m.clearedcustom_currency = true + m.clearedFields[chargeusagebased.FieldCustomCurrencyID] = struct{}{} +} + +// CustomCurrencyCleared reports if the "custom_currency" edge to the CustomCurrency entity was cleared. +func (m *ChargeUsageBasedMutation) CustomCurrencyCleared() bool { + return m.CustomCurrencyIDCleared() || m.clearedcustom_currency +} + +// CustomCurrencyIDs returns the "custom_currency" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// CustomCurrencyID instead. It exists only for internal usage by the builders. +func (m *ChargeUsageBasedMutation) CustomCurrencyIDs() (ids []string) { + if id := m.custom_currency; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetCustomCurrency resets all changes to the "custom_currency" edge. +func (m *ChargeUsageBasedMutation) ResetCustomCurrency() { + m.custom_currency = nil + m.clearedcustom_currency = false +} + // Where appends a list predicates to the ChargeUsageBasedMutation builder. func (m *ChargeUsageBasedMutation) Where(ps ...predicate.ChargeUsageBased) { m.predicates = append(m.predicates, ps...) @@ -60534,7 +60901,7 @@ func (m *ChargeUsageBasedMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *ChargeUsageBasedMutation) Fields() []string { - fields := make([]string, 0, 36) + fields := make([]string, 0, 37) if m.customer != nil { fields = append(fields, chargeusagebased.FieldCustomerID) } @@ -60562,8 +60929,11 @@ func (m *ChargeUsageBasedMutation) Fields() []string { if m.unique_reference_id != nil { fields = append(fields, chargeusagebased.FieldUniqueReferenceID) } - if m.currency != nil { - fields = append(fields, chargeusagebased.FieldCurrency) + if m.fiat_currency_code != nil { + fields = append(fields, chargeusagebased.FieldFiatCurrencyCode) + } + if m.custom_currency != nil { + fields = append(fields, chargeusagebased.FieldCustomCurrencyID) } if m.managed_by != nil { fields = append(fields, chargeusagebased.FieldManagedBy) @@ -60669,8 +61039,10 @@ func (m *ChargeUsageBasedMutation) Field(name string) (ent.Value, bool) { return m.Status() case chargeusagebased.FieldUniqueReferenceID: return m.UniqueReferenceID() - case chargeusagebased.FieldCurrency: - return m.Currency() + case chargeusagebased.FieldFiatCurrencyCode: + return m.FiatCurrencyCode() + case chargeusagebased.FieldCustomCurrencyID: + return m.CustomCurrencyID() case chargeusagebased.FieldManagedBy: return m.ManagedBy() case chargeusagebased.FieldSubscriptionID: @@ -60750,8 +61122,10 @@ func (m *ChargeUsageBasedMutation) OldField(ctx context.Context, name string) (e return m.OldStatus(ctx) case chargeusagebased.FieldUniqueReferenceID: return m.OldUniqueReferenceID(ctx) - case chargeusagebased.FieldCurrency: - return m.OldCurrency(ctx) + case chargeusagebased.FieldFiatCurrencyCode: + return m.OldFiatCurrencyCode(ctx) + case chargeusagebased.FieldCustomCurrencyID: + return m.OldCustomCurrencyID(ctx) case chargeusagebased.FieldManagedBy: return m.OldManagedBy(ctx) case chargeusagebased.FieldSubscriptionID: @@ -60876,12 +61250,19 @@ func (m *ChargeUsageBasedMutation) SetField(name string, value ent.Value) error } m.SetUniqueReferenceID(v) return nil - case chargeusagebased.FieldCurrency: + case chargeusagebased.FieldFiatCurrencyCode: v, ok := value.(currencyx.Code) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetCurrency(v) + m.SetFiatCurrencyCode(v) + return nil + case chargeusagebased.FieldCustomCurrencyID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCustomCurrencyID(v) return nil case chargeusagebased.FieldManagedBy: v, ok := value.(billing.InvoiceLineManagedBy) @@ -61098,6 +61479,12 @@ func (m *ChargeUsageBasedMutation) ClearedFields() []string { if m.FieldCleared(chargeusagebased.FieldUniqueReferenceID) { fields = append(fields, chargeusagebased.FieldUniqueReferenceID) } + if m.FieldCleared(chargeusagebased.FieldFiatCurrencyCode) { + fields = append(fields, chargeusagebased.FieldFiatCurrencyCode) + } + if m.FieldCleared(chargeusagebased.FieldCustomCurrencyID) { + fields = append(fields, chargeusagebased.FieldCustomCurrencyID) + } if m.FieldCleared(chargeusagebased.FieldSubscriptionID) { fields = append(fields, chargeusagebased.FieldSubscriptionID) } @@ -61154,6 +61541,12 @@ func (m *ChargeUsageBasedMutation) ClearField(name string) error { case chargeusagebased.FieldUniqueReferenceID: m.ClearUniqueReferenceID() return nil + case chargeusagebased.FieldFiatCurrencyCode: + m.ClearFiatCurrencyCode() + return nil + case chargeusagebased.FieldCustomCurrencyID: + m.ClearCustomCurrencyID() + return nil case chargeusagebased.FieldSubscriptionID: m.ClearSubscriptionID() return nil @@ -61228,8 +61621,11 @@ func (m *ChargeUsageBasedMutation) ResetField(name string) error { case chargeusagebased.FieldUniqueReferenceID: m.ResetUniqueReferenceID() return nil - case chargeusagebased.FieldCurrency: - m.ResetCurrency() + case chargeusagebased.FieldFiatCurrencyCode: + m.ResetFiatCurrencyCode() + return nil + case chargeusagebased.FieldCustomCurrencyID: + m.ResetCustomCurrencyID() return nil case chargeusagebased.FieldManagedBy: m.ResetManagedBy() @@ -61315,7 +61711,7 @@ func (m *ChargeUsageBasedMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *ChargeUsageBasedMutation) AddedEdges() []string { - edges := make([]string, 0, 11) + edges := make([]string, 0, 12) if m.runs != nil { edges = append(edges, chargeusagebased.EdgeRuns) } @@ -61349,6 +61745,9 @@ func (m *ChargeUsageBasedMutation) AddedEdges() []string { if m.tax_code != nil { edges = append(edges, chargeusagebased.EdgeTaxCode) } + if m.custom_currency != nil { + edges = append(edges, chargeusagebased.EdgeCustomCurrency) + } return edges } @@ -61404,13 +61803,17 @@ func (m *ChargeUsageBasedMutation) AddedIDs(name string) []ent.Value { if id := m.tax_code; id != nil { return []ent.Value{*id} } + case chargeusagebased.EdgeCustomCurrency: + if id := m.custom_currency; id != nil { + return []ent.Value{*id} + } } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *ChargeUsageBasedMutation) RemovedEdges() []string { - edges := make([]string, 0, 11) + edges := make([]string, 0, 12) if m.removedruns != nil { edges = append(edges, chargeusagebased.EdgeRuns) } @@ -61442,7 +61845,7 @@ func (m *ChargeUsageBasedMutation) RemovedIDs(name string) []ent.Value { // ClearedEdges returns all edge names that were cleared in this mutation. func (m *ChargeUsageBasedMutation) ClearedEdges() []string { - edges := make([]string, 0, 11) + edges := make([]string, 0, 12) if m.clearedruns { edges = append(edges, chargeusagebased.EdgeRuns) } @@ -61476,6 +61879,9 @@ func (m *ChargeUsageBasedMutation) ClearedEdges() []string { if m.clearedtax_code { edges = append(edges, chargeusagebased.EdgeTaxCode) } + if m.clearedcustom_currency { + edges = append(edges, chargeusagebased.EdgeCustomCurrency) + } return edges } @@ -61505,6 +61911,8 @@ func (m *ChargeUsageBasedMutation) EdgeCleared(name string) bool { return m.clearedfeature case chargeusagebased.EdgeTaxCode: return m.clearedtax_code + case chargeusagebased.EdgeCustomCurrency: + return m.clearedcustom_currency } return false } @@ -61540,6 +61948,9 @@ func (m *ChargeUsageBasedMutation) ClearEdge(name string) error { case chargeusagebased.EdgeTaxCode: m.ClearTaxCode() return nil + case chargeusagebased.EdgeCustomCurrency: + m.ClearCustomCurrency() + return nil } return fmt.Errorf("unknown ChargeUsageBased unique edge %s", name) } @@ -61581,6 +61992,9 @@ func (m *ChargeUsageBasedMutation) ResetEdge(name string) error { case chargeusagebased.EdgeTaxCode: m.ResetTaxCode() return nil + case chargeusagebased.EdgeCustomCurrency: + m.ResetCustomCurrency() + return nil } return fmt.Errorf("unknown ChargeUsageBased edge %s", name) } @@ -74154,27 +74568,36 @@ func (m *CurrencyCostBasisMutation) ResetEdge(name string) error { // CustomCurrencyMutation represents an operation that mutates the CustomCurrency nodes in the graph. type CustomCurrencyMutation struct { config - op Op - typ string - id *string - namespace *string - created_at *time.Time - updated_at *time.Time - deleted_at *time.Time - code *currencyx.Code - name *string - symbol *string - precision *uint32 - addprecision *int32 - decimal_mark *string - thousands_separator *string - clearedFields map[string]struct{} - cost_basis_history map[string]struct{} - removedcost_basis_history map[string]struct{} - clearedcost_basis_history bool - done bool - oldValue func(context.Context) (*CustomCurrency, error) - predicates []predicate.CustomCurrency + op Op + typ string + id *string + namespace *string + created_at *time.Time + updated_at *time.Time + deleted_at *time.Time + code *currencyx.Code + name *string + symbol *string + precision *uint32 + addprecision *int32 + decimal_mark *string + thousands_separator *string + clearedFields map[string]struct{} + cost_basis_history map[string]struct{} + removedcost_basis_history map[string]struct{} + clearedcost_basis_history bool + charges_credit_purchase map[string]struct{} + removedcharges_credit_purchase map[string]struct{} + clearedcharges_credit_purchase bool + charges_flat_fee map[string]struct{} + removedcharges_flat_fee map[string]struct{} + clearedcharges_flat_fee bool + charges_usage_based map[string]struct{} + removedcharges_usage_based map[string]struct{} + clearedcharges_usage_based bool + done bool + oldValue func(context.Context) (*CustomCurrency, error) + predicates []predicate.CustomCurrency } var _ ent.Mutation = (*CustomCurrencyMutation)(nil) @@ -74741,6 +75164,168 @@ func (m *CustomCurrencyMutation) ResetCostBasisHistory() { m.removedcost_basis_history = nil } +// AddChargesCreditPurchaseIDs adds the "charges_credit_purchase" edge to the ChargeCreditPurchase entity by ids. +func (m *CustomCurrencyMutation) AddChargesCreditPurchaseIDs(ids ...string) { + if m.charges_credit_purchase == nil { + m.charges_credit_purchase = make(map[string]struct{}) + } + for i := range ids { + m.charges_credit_purchase[ids[i]] = struct{}{} + } +} + +// ClearChargesCreditPurchase clears the "charges_credit_purchase" edge to the ChargeCreditPurchase entity. +func (m *CustomCurrencyMutation) ClearChargesCreditPurchase() { + m.clearedcharges_credit_purchase = true +} + +// ChargesCreditPurchaseCleared reports if the "charges_credit_purchase" edge to the ChargeCreditPurchase entity was cleared. +func (m *CustomCurrencyMutation) ChargesCreditPurchaseCleared() bool { + return m.clearedcharges_credit_purchase +} + +// RemoveChargesCreditPurchaseIDs removes the "charges_credit_purchase" edge to the ChargeCreditPurchase entity by IDs. +func (m *CustomCurrencyMutation) RemoveChargesCreditPurchaseIDs(ids ...string) { + if m.removedcharges_credit_purchase == nil { + m.removedcharges_credit_purchase = make(map[string]struct{}) + } + for i := range ids { + delete(m.charges_credit_purchase, ids[i]) + m.removedcharges_credit_purchase[ids[i]] = struct{}{} + } +} + +// RemovedChargesCreditPurchase returns the removed IDs of the "charges_credit_purchase" edge to the ChargeCreditPurchase entity. +func (m *CustomCurrencyMutation) RemovedChargesCreditPurchaseIDs() (ids []string) { + for id := range m.removedcharges_credit_purchase { + ids = append(ids, id) + } + return +} + +// ChargesCreditPurchaseIDs returns the "charges_credit_purchase" edge IDs in the mutation. +func (m *CustomCurrencyMutation) ChargesCreditPurchaseIDs() (ids []string) { + for id := range m.charges_credit_purchase { + ids = append(ids, id) + } + return +} + +// ResetChargesCreditPurchase resets all changes to the "charges_credit_purchase" edge. +func (m *CustomCurrencyMutation) ResetChargesCreditPurchase() { + m.charges_credit_purchase = nil + m.clearedcharges_credit_purchase = false + m.removedcharges_credit_purchase = nil +} + +// AddChargesFlatFeeIDs adds the "charges_flat_fee" edge to the ChargeFlatFee entity by ids. +func (m *CustomCurrencyMutation) AddChargesFlatFeeIDs(ids ...string) { + if m.charges_flat_fee == nil { + m.charges_flat_fee = make(map[string]struct{}) + } + for i := range ids { + m.charges_flat_fee[ids[i]] = struct{}{} + } +} + +// ClearChargesFlatFee clears the "charges_flat_fee" edge to the ChargeFlatFee entity. +func (m *CustomCurrencyMutation) ClearChargesFlatFee() { + m.clearedcharges_flat_fee = true +} + +// ChargesFlatFeeCleared reports if the "charges_flat_fee" edge to the ChargeFlatFee entity was cleared. +func (m *CustomCurrencyMutation) ChargesFlatFeeCleared() bool { + return m.clearedcharges_flat_fee +} + +// RemoveChargesFlatFeeIDs removes the "charges_flat_fee" edge to the ChargeFlatFee entity by IDs. +func (m *CustomCurrencyMutation) RemoveChargesFlatFeeIDs(ids ...string) { + if m.removedcharges_flat_fee == nil { + m.removedcharges_flat_fee = make(map[string]struct{}) + } + for i := range ids { + delete(m.charges_flat_fee, ids[i]) + m.removedcharges_flat_fee[ids[i]] = struct{}{} + } +} + +// RemovedChargesFlatFee returns the removed IDs of the "charges_flat_fee" edge to the ChargeFlatFee entity. +func (m *CustomCurrencyMutation) RemovedChargesFlatFeeIDs() (ids []string) { + for id := range m.removedcharges_flat_fee { + ids = append(ids, id) + } + return +} + +// ChargesFlatFeeIDs returns the "charges_flat_fee" edge IDs in the mutation. +func (m *CustomCurrencyMutation) ChargesFlatFeeIDs() (ids []string) { + for id := range m.charges_flat_fee { + ids = append(ids, id) + } + return +} + +// ResetChargesFlatFee resets all changes to the "charges_flat_fee" edge. +func (m *CustomCurrencyMutation) ResetChargesFlatFee() { + m.charges_flat_fee = nil + m.clearedcharges_flat_fee = false + m.removedcharges_flat_fee = nil +} + +// AddChargesUsageBasedIDs adds the "charges_usage_based" edge to the ChargeUsageBased entity by ids. +func (m *CustomCurrencyMutation) AddChargesUsageBasedIDs(ids ...string) { + if m.charges_usage_based == nil { + m.charges_usage_based = make(map[string]struct{}) + } + for i := range ids { + m.charges_usage_based[ids[i]] = struct{}{} + } +} + +// ClearChargesUsageBased clears the "charges_usage_based" edge to the ChargeUsageBased entity. +func (m *CustomCurrencyMutation) ClearChargesUsageBased() { + m.clearedcharges_usage_based = true +} + +// ChargesUsageBasedCleared reports if the "charges_usage_based" edge to the ChargeUsageBased entity was cleared. +func (m *CustomCurrencyMutation) ChargesUsageBasedCleared() bool { + return m.clearedcharges_usage_based +} + +// RemoveChargesUsageBasedIDs removes the "charges_usage_based" edge to the ChargeUsageBased entity by IDs. +func (m *CustomCurrencyMutation) RemoveChargesUsageBasedIDs(ids ...string) { + if m.removedcharges_usage_based == nil { + m.removedcharges_usage_based = make(map[string]struct{}) + } + for i := range ids { + delete(m.charges_usage_based, ids[i]) + m.removedcharges_usage_based[ids[i]] = struct{}{} + } +} + +// RemovedChargesUsageBased returns the removed IDs of the "charges_usage_based" edge to the ChargeUsageBased entity. +func (m *CustomCurrencyMutation) RemovedChargesUsageBasedIDs() (ids []string) { + for id := range m.removedcharges_usage_based { + ids = append(ids, id) + } + return +} + +// ChargesUsageBasedIDs returns the "charges_usage_based" edge IDs in the mutation. +func (m *CustomCurrencyMutation) ChargesUsageBasedIDs() (ids []string) { + for id := range m.charges_usage_based { + ids = append(ids, id) + } + return +} + +// ResetChargesUsageBased resets all changes to the "charges_usage_based" edge. +func (m *CustomCurrencyMutation) ResetChargesUsageBased() { + m.charges_usage_based = nil + m.clearedcharges_usage_based = false + m.removedcharges_usage_based = nil +} + // Where appends a list predicates to the CustomCurrencyMutation builder. func (m *CustomCurrencyMutation) Where(ps ...predicate.CustomCurrency) { m.predicates = append(m.predicates, ps...) @@ -75057,10 +75642,19 @@ func (m *CustomCurrencyMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *CustomCurrencyMutation) AddedEdges() []string { - edges := make([]string, 0, 1) + edges := make([]string, 0, 4) if m.cost_basis_history != nil { edges = append(edges, customcurrency.EdgeCostBasisHistory) } + if m.charges_credit_purchase != nil { + edges = append(edges, customcurrency.EdgeChargesCreditPurchase) + } + if m.charges_flat_fee != nil { + edges = append(edges, customcurrency.EdgeChargesFlatFee) + } + if m.charges_usage_based != nil { + edges = append(edges, customcurrency.EdgeChargesUsageBased) + } return edges } @@ -75074,16 +75668,43 @@ func (m *CustomCurrencyMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case customcurrency.EdgeChargesCreditPurchase: + ids := make([]ent.Value, 0, len(m.charges_credit_purchase)) + for id := range m.charges_credit_purchase { + ids = append(ids, id) + } + return ids + case customcurrency.EdgeChargesFlatFee: + ids := make([]ent.Value, 0, len(m.charges_flat_fee)) + for id := range m.charges_flat_fee { + ids = append(ids, id) + } + return ids + case customcurrency.EdgeChargesUsageBased: + ids := make([]ent.Value, 0, len(m.charges_usage_based)) + for id := range m.charges_usage_based { + ids = append(ids, id) + } + return ids } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *CustomCurrencyMutation) RemovedEdges() []string { - edges := make([]string, 0, 1) + edges := make([]string, 0, 4) if m.removedcost_basis_history != nil { edges = append(edges, customcurrency.EdgeCostBasisHistory) } + if m.removedcharges_credit_purchase != nil { + edges = append(edges, customcurrency.EdgeChargesCreditPurchase) + } + if m.removedcharges_flat_fee != nil { + edges = append(edges, customcurrency.EdgeChargesFlatFee) + } + if m.removedcharges_usage_based != nil { + edges = append(edges, customcurrency.EdgeChargesUsageBased) + } return edges } @@ -75097,16 +75718,43 @@ func (m *CustomCurrencyMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case customcurrency.EdgeChargesCreditPurchase: + ids := make([]ent.Value, 0, len(m.removedcharges_credit_purchase)) + for id := range m.removedcharges_credit_purchase { + ids = append(ids, id) + } + return ids + case customcurrency.EdgeChargesFlatFee: + ids := make([]ent.Value, 0, len(m.removedcharges_flat_fee)) + for id := range m.removedcharges_flat_fee { + ids = append(ids, id) + } + return ids + case customcurrency.EdgeChargesUsageBased: + ids := make([]ent.Value, 0, len(m.removedcharges_usage_based)) + for id := range m.removedcharges_usage_based { + ids = append(ids, id) + } + return ids } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *CustomCurrencyMutation) ClearedEdges() []string { - edges := make([]string, 0, 1) + edges := make([]string, 0, 4) if m.clearedcost_basis_history { edges = append(edges, customcurrency.EdgeCostBasisHistory) } + if m.clearedcharges_credit_purchase { + edges = append(edges, customcurrency.EdgeChargesCreditPurchase) + } + if m.clearedcharges_flat_fee { + edges = append(edges, customcurrency.EdgeChargesFlatFee) + } + if m.clearedcharges_usage_based { + edges = append(edges, customcurrency.EdgeChargesUsageBased) + } return edges } @@ -75116,6 +75764,12 @@ func (m *CustomCurrencyMutation) EdgeCleared(name string) bool { switch name { case customcurrency.EdgeCostBasisHistory: return m.clearedcost_basis_history + case customcurrency.EdgeChargesCreditPurchase: + return m.clearedcharges_credit_purchase + case customcurrency.EdgeChargesFlatFee: + return m.clearedcharges_flat_fee + case customcurrency.EdgeChargesUsageBased: + return m.clearedcharges_usage_based } return false } @@ -75135,6 +75789,15 @@ func (m *CustomCurrencyMutation) ResetEdge(name string) error { case customcurrency.EdgeCostBasisHistory: m.ResetCostBasisHistory() return nil + case customcurrency.EdgeChargesCreditPurchase: + m.ResetChargesCreditPurchase() + return nil + case customcurrency.EdgeChargesFlatFee: + m.ResetChargesFlatFee() + return nil + case customcurrency.EdgeChargesUsageBased: + m.ResetChargesUsageBased() + return nil } return fmt.Errorf("unknown CustomCurrency edge %s", name) } diff --git a/openmeter/ent/db/runtime.go b/openmeter/ent/db/runtime.go index fc5b2a13f5..c61d64613b 100644 --- a/openmeter/ent/db/runtime.go +++ b/openmeter/ent/db/runtime.go @@ -992,24 +992,28 @@ func init() { chargecreditpurchaseDescCustomerID := chargecreditpurchaseMixinFields0[0].Descriptor() // chargecreditpurchase.CustomerIDValidator is a validator for the "customer_id" field. It is called by the builders before save. chargecreditpurchase.CustomerIDValidator = chargecreditpurchaseDescCustomerID.Validators[0].(func(string) error) - // chargecreditpurchaseDescCurrency is the schema descriptor for currency field. - chargecreditpurchaseDescCurrency := chargecreditpurchaseMixinFields0[9].Descriptor() - // chargecreditpurchase.CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. - chargecreditpurchase.CurrencyValidator = chargecreditpurchaseDescCurrency.Validators[0].(func(string) error) + // chargecreditpurchaseDescFiatCurrencyCode is the schema descriptor for fiat_currency_code field. + chargecreditpurchaseDescFiatCurrencyCode := chargecreditpurchaseMixinFields0[9].Descriptor() + // chargecreditpurchase.FiatCurrencyCodeValidator is a validator for the "fiat_currency_code" field. It is called by the builders before save. + chargecreditpurchase.FiatCurrencyCodeValidator = chargecreditpurchaseDescFiatCurrencyCode.Validators[0].(func(string) error) + // chargecreditpurchaseDescCustomCurrencyID is the schema descriptor for custom_currency_id field. + chargecreditpurchaseDescCustomCurrencyID := chargecreditpurchaseMixinFields0[10].Descriptor() + // chargecreditpurchase.CustomCurrencyIDValidator is a validator for the "custom_currency_id" field. It is called by the builders before save. + chargecreditpurchase.CustomCurrencyIDValidator = chargecreditpurchaseDescCustomCurrencyID.Validators[0].(func(string) error) // chargecreditpurchaseDescTaxCodeID is the schema descriptor for tax_code_id field. - chargecreditpurchaseDescTaxCodeID := chargecreditpurchaseMixinFields0[15].Descriptor() + chargecreditpurchaseDescTaxCodeID := chargecreditpurchaseMixinFields0[16].Descriptor() // chargecreditpurchase.TaxCodeIDValidator is a validator for the "tax_code_id" field. It is called by the builders before save. chargecreditpurchase.TaxCodeIDValidator = chargecreditpurchaseDescTaxCodeID.Validators[0].(func(string) error) // chargecreditpurchaseDescNamespace is the schema descriptor for namespace field. - chargecreditpurchaseDescNamespace := chargecreditpurchaseMixinFields0[19].Descriptor() + chargecreditpurchaseDescNamespace := chargecreditpurchaseMixinFields0[20].Descriptor() // chargecreditpurchase.NamespaceValidator is a validator for the "namespace" field. It is called by the builders before save. chargecreditpurchase.NamespaceValidator = chargecreditpurchaseDescNamespace.Validators[0].(func(string) error) // chargecreditpurchaseDescCreatedAt is the schema descriptor for created_at field. - chargecreditpurchaseDescCreatedAt := chargecreditpurchaseMixinFields0[21].Descriptor() + chargecreditpurchaseDescCreatedAt := chargecreditpurchaseMixinFields0[22].Descriptor() // chargecreditpurchase.DefaultCreatedAt holds the default value on creation for the created_at field. chargecreditpurchase.DefaultCreatedAt = chargecreditpurchaseDescCreatedAt.Default.(func() time.Time) // chargecreditpurchaseDescUpdatedAt is the schema descriptor for updated_at field. - chargecreditpurchaseDescUpdatedAt := chargecreditpurchaseMixinFields0[22].Descriptor() + chargecreditpurchaseDescUpdatedAt := chargecreditpurchaseMixinFields0[23].Descriptor() // chargecreditpurchase.DefaultUpdatedAt holds the default value on creation for the updated_at field. chargecreditpurchase.DefaultUpdatedAt = chargecreditpurchaseDescUpdatedAt.Default.(func() time.Time) // chargecreditpurchase.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. @@ -1018,7 +1022,7 @@ func init() { chargecreditpurchaseDescSettlement := chargecreditpurchaseFields[5].Descriptor() chargecreditpurchase.ValueScanner.Settlement = chargecreditpurchaseDescSettlement.ValueScanner.(field.TypeValueScanner[creditpurchase.Settlement]) // chargecreditpurchaseDescID is the schema descriptor for id field. - chargecreditpurchaseDescID := chargecreditpurchaseMixinFields0[18].Descriptor() + chargecreditpurchaseDescID := chargecreditpurchaseMixinFields0[19].Descriptor() // chargecreditpurchase.DefaultID holds the default value on creation for the id field. chargecreditpurchase.DefaultID = chargecreditpurchaseDescID.Default.(func() string) chargecreditpurchasecreditgrantMixin := schema.ChargeCreditPurchaseCreditGrant{}.Mixin() @@ -1123,24 +1127,28 @@ func init() { chargeflatfeeDescCustomerID := chargeflatfeeMixinFields0[0].Descriptor() // chargeflatfee.CustomerIDValidator is a validator for the "customer_id" field. It is called by the builders before save. chargeflatfee.CustomerIDValidator = chargeflatfeeDescCustomerID.Validators[0].(func(string) error) - // chargeflatfeeDescCurrency is the schema descriptor for currency field. - chargeflatfeeDescCurrency := chargeflatfeeMixinFields0[9].Descriptor() - // chargeflatfee.CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. - chargeflatfee.CurrencyValidator = chargeflatfeeDescCurrency.Validators[0].(func(string) error) + // chargeflatfeeDescFiatCurrencyCode is the schema descriptor for fiat_currency_code field. + chargeflatfeeDescFiatCurrencyCode := chargeflatfeeMixinFields0[9].Descriptor() + // chargeflatfee.FiatCurrencyCodeValidator is a validator for the "fiat_currency_code" field. It is called by the builders before save. + chargeflatfee.FiatCurrencyCodeValidator = chargeflatfeeDescFiatCurrencyCode.Validators[0].(func(string) error) + // chargeflatfeeDescCustomCurrencyID is the schema descriptor for custom_currency_id field. + chargeflatfeeDescCustomCurrencyID := chargeflatfeeMixinFields0[10].Descriptor() + // chargeflatfee.CustomCurrencyIDValidator is a validator for the "custom_currency_id" field. It is called by the builders before save. + chargeflatfee.CustomCurrencyIDValidator = chargeflatfeeDescCustomCurrencyID.Validators[0].(func(string) error) // chargeflatfeeDescTaxCodeID is the schema descriptor for tax_code_id field. - chargeflatfeeDescTaxCodeID := chargeflatfeeMixinFields0[15].Descriptor() + chargeflatfeeDescTaxCodeID := chargeflatfeeMixinFields0[16].Descriptor() // chargeflatfee.TaxCodeIDValidator is a validator for the "tax_code_id" field. It is called by the builders before save. chargeflatfee.TaxCodeIDValidator = chargeflatfeeDescTaxCodeID.Validators[0].(func(string) error) // chargeflatfeeDescNamespace is the schema descriptor for namespace field. - chargeflatfeeDescNamespace := chargeflatfeeMixinFields0[19].Descriptor() + chargeflatfeeDescNamespace := chargeflatfeeMixinFields0[20].Descriptor() // chargeflatfee.NamespaceValidator is a validator for the "namespace" field. It is called by the builders before save. chargeflatfee.NamespaceValidator = chargeflatfeeDescNamespace.Validators[0].(func(string) error) // chargeflatfeeDescCreatedAt is the schema descriptor for created_at field. - chargeflatfeeDescCreatedAt := chargeflatfeeMixinFields0[21].Descriptor() + chargeflatfeeDescCreatedAt := chargeflatfeeMixinFields0[22].Descriptor() // chargeflatfee.DefaultCreatedAt holds the default value on creation for the created_at field. chargeflatfee.DefaultCreatedAt = chargeflatfeeDescCreatedAt.Default.(func() time.Time) // chargeflatfeeDescUpdatedAt is the schema descriptor for updated_at field. - chargeflatfeeDescUpdatedAt := chargeflatfeeMixinFields0[22].Descriptor() + chargeflatfeeDescUpdatedAt := chargeflatfeeMixinFields0[23].Descriptor() // chargeflatfee.DefaultUpdatedAt holds the default value on creation for the updated_at field. chargeflatfee.DefaultUpdatedAt = chargeflatfeeDescUpdatedAt.Default.(func() time.Time) // chargeflatfee.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. @@ -1157,7 +1165,7 @@ func init() { // chargeflatfee.FeatureKeyValidator is a validator for the "feature_key" field. It is called by the builders before save. chargeflatfee.FeatureKeyValidator = chargeflatfeeDescFeatureKey.Validators[0].(func(string) error) // chargeflatfeeDescID is the schema descriptor for id field. - chargeflatfeeDescID := chargeflatfeeMixinFields0[18].Descriptor() + chargeflatfeeDescID := chargeflatfeeMixinFields0[19].Descriptor() // chargeflatfee.DefaultID holds the default value on creation for the id field. chargeflatfee.DefaultID = chargeflatfeeDescID.Default.(func() string) chargeflatfeeoverrideMixin := schema.ChargeFlatFeeOverride{}.Mixin() @@ -1371,24 +1379,28 @@ func init() { chargeusagebasedDescCustomerID := chargeusagebasedMixinFields0[0].Descriptor() // chargeusagebased.CustomerIDValidator is a validator for the "customer_id" field. It is called by the builders before save. chargeusagebased.CustomerIDValidator = chargeusagebasedDescCustomerID.Validators[0].(func(string) error) - // chargeusagebasedDescCurrency is the schema descriptor for currency field. - chargeusagebasedDescCurrency := chargeusagebasedMixinFields0[9].Descriptor() - // chargeusagebased.CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. - chargeusagebased.CurrencyValidator = chargeusagebasedDescCurrency.Validators[0].(func(string) error) + // chargeusagebasedDescFiatCurrencyCode is the schema descriptor for fiat_currency_code field. + chargeusagebasedDescFiatCurrencyCode := chargeusagebasedMixinFields0[9].Descriptor() + // chargeusagebased.FiatCurrencyCodeValidator is a validator for the "fiat_currency_code" field. It is called by the builders before save. + chargeusagebased.FiatCurrencyCodeValidator = chargeusagebasedDescFiatCurrencyCode.Validators[0].(func(string) error) + // chargeusagebasedDescCustomCurrencyID is the schema descriptor for custom_currency_id field. + chargeusagebasedDescCustomCurrencyID := chargeusagebasedMixinFields0[10].Descriptor() + // chargeusagebased.CustomCurrencyIDValidator is a validator for the "custom_currency_id" field. It is called by the builders before save. + chargeusagebased.CustomCurrencyIDValidator = chargeusagebasedDescCustomCurrencyID.Validators[0].(func(string) error) // chargeusagebasedDescTaxCodeID is the schema descriptor for tax_code_id field. - chargeusagebasedDescTaxCodeID := chargeusagebasedMixinFields0[15].Descriptor() + chargeusagebasedDescTaxCodeID := chargeusagebasedMixinFields0[16].Descriptor() // chargeusagebased.TaxCodeIDValidator is a validator for the "tax_code_id" field. It is called by the builders before save. chargeusagebased.TaxCodeIDValidator = chargeusagebasedDescTaxCodeID.Validators[0].(func(string) error) // chargeusagebasedDescNamespace is the schema descriptor for namespace field. - chargeusagebasedDescNamespace := chargeusagebasedMixinFields0[19].Descriptor() + chargeusagebasedDescNamespace := chargeusagebasedMixinFields0[20].Descriptor() // chargeusagebased.NamespaceValidator is a validator for the "namespace" field. It is called by the builders before save. chargeusagebased.NamespaceValidator = chargeusagebasedDescNamespace.Validators[0].(func(string) error) // chargeusagebasedDescCreatedAt is the schema descriptor for created_at field. - chargeusagebasedDescCreatedAt := chargeusagebasedMixinFields0[21].Descriptor() + chargeusagebasedDescCreatedAt := chargeusagebasedMixinFields0[22].Descriptor() // chargeusagebased.DefaultCreatedAt holds the default value on creation for the created_at field. chargeusagebased.DefaultCreatedAt = chargeusagebasedDescCreatedAt.Default.(func() time.Time) // chargeusagebasedDescUpdatedAt is the schema descriptor for updated_at field. - chargeusagebasedDescUpdatedAt := chargeusagebasedMixinFields0[22].Descriptor() + chargeusagebasedDescUpdatedAt := chargeusagebasedMixinFields0[23].Descriptor() // chargeusagebased.DefaultUpdatedAt holds the default value on creation for the updated_at field. chargeusagebased.DefaultUpdatedAt = chargeusagebasedDescUpdatedAt.Default.(func() time.Time) // chargeusagebased.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. @@ -1411,7 +1423,7 @@ func init() { chargeusagebasedDescUnitConfig := chargeusagebasedFields[8].Descriptor() chargeusagebased.ValueScanner.UnitConfig = chargeusagebasedDescUnitConfig.ValueScanner.(field.TypeValueScanner[*productcatalog.UnitConfig]) // chargeusagebasedDescID is the schema descriptor for id field. - chargeusagebasedDescID := chargeusagebasedMixinFields0[18].Descriptor() + chargeusagebasedDescID := chargeusagebasedMixinFields0[19].Descriptor() // chargeusagebased.DefaultID holds the default value on creation for the id field. chargeusagebased.DefaultID = chargeusagebasedDescID.Default.(func() string) chargeusagebasedoverrideMixin := schema.ChargeUsageBasedOverride{}.Mixin() @@ -1626,28 +1638,32 @@ func init() { chargessearchv1DescCustomerID := chargessearchv1Fields[2].Descriptor() // chargessearchv1.CustomerIDValidator is a validator for the "customer_id" field. It is called by the builders before save. chargessearchv1.CustomerIDValidator = chargessearchv1DescCustomerID.Validators[0].(func(string) error) - // chargessearchv1DescCurrency is the schema descriptor for currency field. - chargessearchv1DescCurrency := chargessearchv1Fields[11].Descriptor() - // chargessearchv1.CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. - chargessearchv1.CurrencyValidator = chargessearchv1DescCurrency.Validators[0].(func(string) error) + // chargessearchv1DescFiatCurrencyCode is the schema descriptor for fiat_currency_code field. + chargessearchv1DescFiatCurrencyCode := chargessearchv1Fields[11].Descriptor() + // chargessearchv1.FiatCurrencyCodeValidator is a validator for the "fiat_currency_code" field. It is called by the builders before save. + chargessearchv1.FiatCurrencyCodeValidator = chargessearchv1DescFiatCurrencyCode.Validators[0].(func(string) error) + // chargessearchv1DescCustomCurrencyID is the schema descriptor for custom_currency_id field. + chargessearchv1DescCustomCurrencyID := chargessearchv1Fields[12].Descriptor() + // chargessearchv1.CustomCurrencyIDValidator is a validator for the "custom_currency_id" field. It is called by the builders before save. + chargessearchv1.CustomCurrencyIDValidator = chargessearchv1DescCustomCurrencyID.Validators[0].(func(string) error) // chargessearchv1DescTaxCodeID is the schema descriptor for tax_code_id field. - chargessearchv1DescTaxCodeID := chargessearchv1Fields[17].Descriptor() + chargessearchv1DescTaxCodeID := chargessearchv1Fields[18].Descriptor() // chargessearchv1.TaxCodeIDValidator is a validator for the "tax_code_id" field. It is called by the builders before save. chargessearchv1.TaxCodeIDValidator = chargessearchv1DescTaxCodeID.Validators[0].(func(string) error) // chargessearchv1DescID is the schema descriptor for id field. - chargessearchv1DescID := chargessearchv1Fields[20].Descriptor() + chargessearchv1DescID := chargessearchv1Fields[21].Descriptor() // chargessearchv1.DefaultID holds the default value on creation for the id field. chargessearchv1.DefaultID = chargessearchv1DescID.Default.(func() string) // chargessearchv1DescNamespace is the schema descriptor for namespace field. - chargessearchv1DescNamespace := chargessearchv1Fields[21].Descriptor() + chargessearchv1DescNamespace := chargessearchv1Fields[22].Descriptor() // chargessearchv1.NamespaceValidator is a validator for the "namespace" field. It is called by the builders before save. chargessearchv1.NamespaceValidator = chargessearchv1DescNamespace.Validators[0].(func(string) error) // chargessearchv1DescCreatedAt is the schema descriptor for created_at field. - chargessearchv1DescCreatedAt := chargessearchv1Fields[23].Descriptor() + chargessearchv1DescCreatedAt := chargessearchv1Fields[24].Descriptor() // chargessearchv1.DefaultCreatedAt holds the default value on creation for the created_at field. chargessearchv1.DefaultCreatedAt = chargessearchv1DescCreatedAt.Default.(func() time.Time) // chargessearchv1DescUpdatedAt is the schema descriptor for updated_at field. - chargessearchv1DescUpdatedAt := chargessearchv1Fields[24].Descriptor() + chargessearchv1DescUpdatedAt := chargessearchv1Fields[25].Descriptor() // chargessearchv1.DefaultUpdatedAt holds the default value on creation for the updated_at field. chargessearchv1.DefaultUpdatedAt = chargessearchv1DescUpdatedAt.Default.(func() time.Time) // chargessearchv1.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. diff --git a/openmeter/ent/schema/charges.go b/openmeter/ent/schema/charges.go index 0d5a9d81b3..f2929bd4d9 100644 --- a/openmeter/ent/schema/charges.go +++ b/openmeter/ent/schema/charges.go @@ -9,10 +9,13 @@ import ( "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" "entgo.io/ent/schema/index" + "entgo.io/ent/schema/mixin" + "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" - "github.com/openmeterio/openmeter/openmeter/billing/charges/models/chargemeta" + "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/clock" + "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/framework/entutils" ) @@ -36,6 +39,7 @@ var chargesSearchV1Columns = []string{ "status", "unique_reference_id", "currency", + "custom_currency_id", "managed_by", "subscription_id", "subscription_phase_id", @@ -83,7 +87,7 @@ func (ChargesSearchV1) buildChargesSearchV1TableSelector(s *sql.Selector, table func (ChargesSearchV1) Fields() []ent.Field { mixins := []ent.Mixin{ - chargemeta.Mixin{}, + ChargesMetaMixin{}, } fields := []ent.Field{ @@ -182,3 +186,114 @@ func (Charge) Indexes() []ent.Index { Unique(), } } + +type ChargesMetaMixin = entutils.RecursiveMixin[chargesMetaMixin] + +type chargesMetaMixin struct { + mixin.Schema +} + +func (chargesMetaMixin) Mixin() []ent.Mixin { + return []ent.Mixin{ + entutils.AnnotationsMixin{}, + entutils.ResourceMixin{}, + } +} + +func (chargesMetaMixin) Fields() []ent.Field { + return []ent.Field{ + field.String("customer_id"). + NotEmpty(). + Immutable(). + SchemaType(map[string]string{ + dialect.Postgres: "char(26)", + }), + + field.Time("service_period_from"), + field.Time("service_period_to"), + field.Time("billing_period_from"), + field.Time("billing_period_to"), + field.Time("full_service_period_from"), + field.Time("full_service_period_to"), + + field.Enum("status"). + GoType(meta.ChargeStatus("")), + + field.String("unique_reference_id"). + Immutable(). + Optional(). + Nillable(), + + field.String("fiat_currency_code"). + StorageKey("currency"). + GoType(currencyx.Code("")). + Immutable(). + SchemaType(map[string]string{ + dialect.Postgres: "varchar(3)", + }). + NotEmpty(). + Optional(). + Nillable(), + + field.String("custom_currency_id"). + SchemaType(map[string]string{ + dialect.Postgres: "char(26)", + }). + NotEmpty(). + Optional(). + Nillable(). + Immutable(), + + field.Enum("managed_by"). + GoType(billing.InvoiceLineManagedBy("")). + Immutable(), + + // Subscriptions metadata + field.String("subscription_id"). + Optional(). + Nillable(). + Immutable(), + + field.String("subscription_phase_id"). + Optional(). + Nillable(). + Immutable(), + + field.String("subscription_item_id"). + Optional(). + Nillable(), + + field.Time("advance_after"). + Optional(). + Nillable(), + field.String("tax_code_id"). + NotEmpty(). + Immutable(). + SchemaType(map[string]string{ + dialect.Postgres: "char(26)", + }), + field.Enum("tax_behavior"). + GoType(productcatalog.TaxBehavior("")). + Optional(). + Nillable(). + Immutable(), + } +} + +func (chargesMetaMixin) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("namespace", "customer_id", "unique_reference_id"). + Annotations( + entsql.IndexWhere("unique_reference_id IS NOT NULL AND deleted_at IS NULL"), + ). + Unique(), + } +} + +func (chargesMetaMixin) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Checks(map[string]string{ + "currency_reference": `(currency IS NULL) <> (custom_currency_id IS NULL)`, + }), + } +} diff --git a/openmeter/ent/schema/chargescreditpurchase.go b/openmeter/ent/schema/chargescreditpurchase.go index 9cde6206f3..b790e7fcb5 100644 --- a/openmeter/ent/schema/chargescreditpurchase.go +++ b/openmeter/ent/schema/chargescreditpurchase.go @@ -11,7 +11,6 @@ import ( "github.com/lib/pq" "github.com/openmeterio/openmeter/openmeter/billing/charges/creditpurchase" - "github.com/openmeterio/openmeter/openmeter/billing/charges/models/chargemeta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" "github.com/openmeterio/openmeter/pkg/framework/entutils" ) @@ -24,7 +23,7 @@ type ChargeCreditPurchase struct { func (ChargeCreditPurchase) Mixin() []ent.Mixin { return []ent.Mixin{ - chargemeta.Mixin{}, + ChargesMetaMixin{}, } } @@ -118,6 +117,12 @@ func (ChargeCreditPurchase) Edges() []ent.Edge { Immutable(). // We must not falsify tax code IDs on charges, when deleting a tax code (they have soft delete either ways). Annotations(entsql.OnDelete(entsql.Restrict)), + edge.From("custom_currency", CustomCurrency.Type). + Ref("charges_credit_purchase"). + Field("custom_currency_id"). + Unique(). + Immutable(). + Annotations(entsql.OnDelete(entsql.Restrict)), } } diff --git a/openmeter/ent/schema/chargesflatfee.go b/openmeter/ent/schema/chargesflatfee.go index 0ef30c8c95..92ca961470 100644 --- a/openmeter/ent/schema/chargesflatfee.go +++ b/openmeter/ent/schema/chargesflatfee.go @@ -11,7 +11,6 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" - "github.com/openmeterio/openmeter/openmeter/billing/charges/models/chargemeta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/creditrealization" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/invoicedusage" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" @@ -28,7 +27,7 @@ type ChargeFlatFee struct { func (ChargeFlatFee) Mixin() []ent.Mixin { return []ent.Mixin{ - chargemeta.Mixin{}, + ChargesMetaMixin{}, } } @@ -141,6 +140,12 @@ func (ChargeFlatFee) Edges() []ent.Edge { Immutable(). // We must not falsify tax code IDs on charges, when deleting a tax code (they have soft delete either ways). Annotations(entsql.OnDelete(entsql.Restrict)), + edge.From("custom_currency", CustomCurrency.Type). + Ref("charges_flat_fee"). + Field("custom_currency_id"). + Unique(). + Immutable(). + Annotations(entsql.OnDelete(entsql.Restrict)), } } diff --git a/openmeter/ent/schema/chargesusagebased.go b/openmeter/ent/schema/chargesusagebased.go index 57e3f8969f..3b223cffb0 100644 --- a/openmeter/ent/schema/chargesusagebased.go +++ b/openmeter/ent/schema/chargesusagebased.go @@ -11,7 +11,6 @@ import ( "github.com/alpacahq/alpacadecimal" "github.com/openmeterio/openmeter/openmeter/billing" - "github.com/openmeterio/openmeter/openmeter/billing/charges/models/chargemeta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/creditrealization" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/invoicedusage" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" @@ -29,7 +28,7 @@ type ChargeUsageBased struct { func (ChargeUsageBased) Mixin() []ent.Mixin { return []ent.Mixin{ - chargemeta.Mixin{}, + ChargesMetaMixin{}, } } @@ -148,6 +147,12 @@ func (ChargeUsageBased) Edges() []ent.Edge { Immutable(). // We must not falsify tax code IDs on charges, when deleting a tax code (they have soft delete either ways). Annotations(entsql.OnDelete(entsql.Restrict)), + edge.From("custom_currency", CustomCurrency.Type). + Ref("charges_usage_based"). + Field("custom_currency_id"). + Unique(). + Immutable(). + Annotations(entsql.OnDelete(entsql.Restrict)), } } diff --git a/openmeter/ent/schema/custom_currencies.go b/openmeter/ent/schema/custom_currencies.go index d514ef4c99..b71af38d82 100644 --- a/openmeter/ent/schema/custom_currencies.go +++ b/openmeter/ent/schema/custom_currencies.go @@ -53,6 +53,12 @@ func (CustomCurrency) Edges() []ent.Edge { return []ent.Edge{ edge.To("cost_basis_history", CurrencyCostBasis.Type). Annotations(entsql.OnDelete(entsql.Cascade)), + edge.To("charges_credit_purchase", ChargeCreditPurchase.Type). + Annotations(entsql.OnDelete(entsql.Restrict)), + edge.To("charges_flat_fee", ChargeFlatFee.Type). + Annotations(entsql.OnDelete(entsql.Restrict)), + edge.To("charges_usage_based", ChargeUsageBased.Type). + Annotations(entsql.OnDelete(entsql.Restrict)), } } diff --git a/openmeter/ledger/chargeadapter/creditpurchase.go b/openmeter/ledger/chargeadapter/creditpurchase.go index 0dede419f2..49cd608ee0 100644 --- a/openmeter/ledger/chargeadapter/creditpurchase.go +++ b/openmeter/ledger/chargeadapter/creditpurchase.go @@ -12,6 +12,7 @@ import ( chargecreditpurchase "github.com/openmeterio/openmeter/openmeter/billing/charges/creditpurchase" "github.com/openmeterio/openmeter/openmeter/billing/charges/lineage" + "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/ledger" @@ -75,6 +76,10 @@ func (h *creditPurchaseHandler) OnCreditPurchasePaymentAuthorized(ctx context.Co } charge := input.Charge + if charge.Intent.Currency.IsCustom() { + return ledgertransaction.GroupReference{}, fmt.Errorf("credit purchase with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + costBasis, err := charge.Intent.Settlement.GetCostBasis() if err != nil { return ledgertransaction.GroupReference{}, fmt.Errorf("get cost basis: %w", err) @@ -97,7 +102,7 @@ func (h *creditPurchaseHandler) OnCreditPurchasePaymentAuthorized(ctx context.Co transactions.AuthorizeCustomerReceivablePaymentTemplate{ At: input.EventAt, Amount: charge.Intent.CreditAmount, - Currency: charge.Intent.Currency, + Currency: charge.Intent.Currency.GetCode(), CostBasis: &costBasis, Features: featureFilters, SourceChargeID: &charge.ID, @@ -133,6 +138,10 @@ func (h *creditPurchaseHandler) OnCreditPurchasePaymentSettled(ctx context.Conte } charge := input.Charge + if charge.Intent.Currency.IsCustom() { + return ledgertransaction.GroupReference{}, fmt.Errorf("credit purchase with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + costBasis, err := charge.Intent.Settlement.GetCostBasis() if err != nil { return ledgertransaction.GroupReference{}, fmt.Errorf("get cost basis: %w", err) @@ -155,7 +164,7 @@ func (h *creditPurchaseHandler) OnCreditPurchasePaymentSettled(ctx context.Conte transactions.SettleCustomerReceivableFromPaymentTemplate{ At: input.EventAt, Amount: charge.Intent.CreditAmount, - Currency: charge.Intent.Currency, + Currency: charge.Intent.Currency.GetCode(), CostBasis: &costBasis, Features: featureFilters, SourceChargeID: &charge.ID, @@ -203,6 +212,10 @@ func (h *creditPurchaseHandler) issueCreditPurchaseGroup(ctx context.Context, ch return ledgertransaction.GroupReference{}, nil } + if charge.Intent.Currency.IsCustom() { + return ledgertransaction.GroupReference{}, fmt.Errorf("credit purchase with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + costBasis, err := charge.Intent.Settlement.GetCostBasis() if err != nil { return ledgertransaction.GroupReference{}, fmt.Errorf("get cost basis: %w", err) @@ -216,7 +229,7 @@ func (h *creditPurchaseHandler) issueCreditPurchaseGroup(ctx context.Context, ch featureFilters := charge.Intent.FeatureFilters.Normalize() bookedAt := charge.Intent.ServicePeriod.To - advanceAttributions, err := h.advanceAttributions(ctx, customerID, charge.Intent.Currency, charge.Intent.CreditAmount, featureFilters) + advanceAttributions, err := h.advanceAttributions(ctx, customerID, charge.Intent.Currency.GetCode(), charge.Intent.CreditAmount, featureFilters) if err != nil { return ledgertransaction.GroupReference{}, fmt.Errorf("get advance attributions: %w", err) } @@ -237,7 +250,7 @@ func (h *creditPurchaseHandler) issueCreditPurchaseGroup(ctx context.Context, ch templates = append(templates, transactions.AttributeCustomerAdvanceReceivableCostBasisTemplate{ At: bookedAt, Amount: attribution.advanceAmount, - Currency: charge.Intent.Currency, + Currency: charge.Intent.Currency.GetCode(), CostBasis: &costBasis, AdvanceFeatures: attribution.advanceFeatures, AttributedFeatures: featureFilters, @@ -249,7 +262,7 @@ func (h *creditPurchaseHandler) issueCreditPurchaseGroup(ctx context.Context, ch templates = append(templates, transactions.TranslateCustomerAccruedCostBasisTemplate{ At: bookedAt, Amount: attribution.accruedAmount, - Currency: charge.Intent.Currency, + Currency: charge.Intent.Currency.GetCode(), TaxCode: attribution.taxCode, TaxBehavior: attribution.taxBehavior, FromCostBasis: nil, @@ -264,7 +277,7 @@ func (h *creditPurchaseHandler) issueCreditPurchaseGroup(ctx context.Context, ch templates = append(templates, transactions.IssueCustomerReceivableTemplate{ At: bookedAt, Amount: issuableAmount, - Currency: charge.Intent.Currency, + Currency: charge.Intent.Currency.GetCode(), CostBasis: &costBasis, Features: featureFilters, SourceChargeID: &charge.ID, @@ -280,7 +293,7 @@ func (h *creditPurchaseHandler) issueCreditPurchaseGroup(ctx context.Context, ch transactions.AuthorizeCustomerReceivablePaymentTemplate{ At: bookedAt, Amount: charge.Intent.CreditAmount, - Currency: charge.Intent.Currency, + Currency: charge.Intent.Currency.GetCode(), CostBasis: &costBasis, Features: featureFilters, SourceChargeID: &charge.ID, @@ -288,7 +301,7 @@ func (h *creditPurchaseHandler) issueCreditPurchaseGroup(ctx context.Context, ch transactions.SettleCustomerReceivableFromPaymentTemplate{ At: bookedAt, Amount: charge.Intent.CreditAmount, - Currency: charge.Intent.Currency, + Currency: charge.Intent.Currency.GetCode(), CostBasis: &costBasis, Features: featureFilters, SourceChargeID: &charge.ID, @@ -331,7 +344,7 @@ func (h *creditPurchaseHandler) issueCreditPurchaseGroup(ctx context.Context, ch CustomerID: customerID, Amount: charge.Intent.CreditAmount, ImmediateReleases: immediateReleases, - Currency: charge.Intent.Currency, + Currency: charge.Intent.Currency.GetCode(), CostBasis: &costBasis, CreditPriority: charge.Intent.Priority, Features: featureFilters, diff --git a/openmeter/ledger/chargeadapter/creditpurchase_test.go b/openmeter/ledger/chargeadapter/creditpurchase_test.go index d84e3183ca..3ffbe1fabf 100644 --- a/openmeter/ledger/chargeadapter/creditpurchase_test.go +++ b/openmeter/ledger/chargeadapter/creditpurchase_test.go @@ -10,6 +10,8 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing" chargecreditpurchase "github.com/openmeterio/openmeter/openmeter/billing/charges/creditpurchase" "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + "github.com/openmeterio/openmeter/openmeter/currencies" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" entdb "github.com/openmeterio/openmeter/openmeter/ent/db" ledgerbreakagerecorddb "github.com/openmeterio/openmeter/openmeter/ent/db/ledgerbreakagerecord" ledgerentrydb "github.com/openmeterio/openmeter/openmeter/ent/db/ledgerentry" @@ -492,7 +494,8 @@ func TestOnCreditPurchasePaymentSettled_BacksAdvanceBeforeTopUp(t *testing.T) { type creditPurchaseHandlerTestEnv struct { *ledgertestutils.IntegrationEnv - handler chargecreditpurchase.Handler + handler chargecreditpurchase.Handler + currency currencies.Currency } func newCreditPurchaseHandlerTestEnv(t *testing.T) *creditPurchaseHandlerTestEnv { @@ -525,6 +528,7 @@ func newCreditPurchaseHandlerTestEnv(t *testing.T) *creditPurchaseHandlerTestEnv return &creditPurchaseHandlerTestEnv{ IntegrationEnv: base, handler: handler, + currency: currenciestestutils.NewFiatCurrency(t, "USD"), } } @@ -551,7 +555,7 @@ func (e *creditPurchaseHandlerTestEnv) newPromotionalCharge(amount alpacadecimal Intent: meta.Intent{ ManagedBy: billing.SystemManagedLine, CustomerID: e.CustomerID.ID, - Currency: currencyx.Code("USD"), + Currency: e.currency, TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: "tax-code-id", }, @@ -595,7 +599,7 @@ func (e *creditPurchaseHandlerTestEnv) newExternalCharge(amount, costBasis alpac Intent: meta.Intent{ ManagedBy: billing.SystemManagedLine, CustomerID: e.CustomerID.ID, - Currency: currencyx.Code("USD"), + Currency: e.currency, TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: "tax-code-id", }, diff --git a/openmeter/ledger/chargeadapter/flatfee.go b/openmeter/ledger/chargeadapter/flatfee.go index dae37922f5..2893a11375 100644 --- a/openmeter/ledger/chargeadapter/flatfee.go +++ b/openmeter/ledger/chargeadapter/flatfee.go @@ -8,6 +8,7 @@ import ( "github.com/samber/lo" "github.com/openmeterio/openmeter/openmeter/billing/charges/flatfee" + "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/creditrealization" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction" "github.com/openmeterio/openmeter/openmeter/customer" @@ -15,7 +16,6 @@ import ( "github.com/openmeterio/openmeter/openmeter/ledger/collector" "github.com/openmeterio/openmeter/openmeter/ledger/transactions" "github.com/openmeterio/openmeter/openmeter/productcatalog" - "github.com/openmeterio/openmeter/pkg/currencyx" ) // flatFeeHandler maps charge lifecycle events to ledger transaction templates @@ -53,6 +53,10 @@ func (h *flatFeeHandler) OnAllocateCredits(ctx context.Context, input flatfee.On intent := input.Charge.Intent taxConfig := intent.GetTaxConfig() + if intent.GetCurrency().IsCustom() { + return nil, fmt.Errorf("flat fee charge with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + if err := validateSettlementMode( intent.GetSettlementMode(), productcatalog.CreditThenInvoiceSettlementMode, @@ -68,7 +72,7 @@ func (h *flatFeeHandler) OnAllocateCredits(ctx context.Context, input flatfee.On Annotations: chargeAnnotationsForFlatFeeCharge(input.Charge), BookedAt: input.BookedAt, SourceBalanceAsOf: intent.GetEffectiveInvoiceAt(), - Currency: intent.GetCurrency(), + Currency: intent.GetCurrency().GetCode(), TaxCode: lo.ToPtr(taxConfig.TaxCodeID), TaxBehavior: (*ledger.TaxBehavior)(taxConfig.Behavior), SettlementMode: intent.GetSettlementMode(), @@ -102,6 +106,10 @@ func (h *flatFeeHandler) OnInvoiceUsageAccrued(ctx context.Context, input flatfe intent := input.Charge.Intent taxConfig := intent.GetTaxConfig() + if intent.GetCurrency().IsCustom() { + return ledgertransaction.GroupReference{}, fmt.Errorf("flat fee charge with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + if err := validateSettlementMode( intent.GetSettlementMode(), productcatalog.CreditThenInvoiceSettlementMode, @@ -125,7 +133,7 @@ func (h *flatFeeHandler) OnInvoiceUsageAccrued(ctx context.Context, input flatfe transactions.TransferCustomerReceivableToAccruedTemplate{ At: input.BookedAt, Amount: amount, - Currency: intent.GetCurrency(), + Currency: intent.GetCurrency().GetCode(), TaxCode: lo.ToPtr(taxConfig.TaxCodeID), TaxBehavior: (*ledger.TaxBehavior)(taxConfig.Behavior), CostBasis: invoiceCostBasis, @@ -159,11 +167,10 @@ func (h *flatFeeHandler) OnInvoiceUsageAccrued(ctx context.Context, input flatfe func (h *flatFeeHandler) OnCorrectCreditAllocations(ctx context.Context, input flatfee.CorrectCreditAllocationsInput) (creditrealization.CreateCorrectionInputs, error) { intent := input.Charge.Intent - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(intent.GetCurrency()). - Build() - if err != nil { - return nil, fmt.Errorf("get currency calculator: %w", err) + currency := intent.GetCurrency() + + if currency.IsCustom() { + return nil, fmt.Errorf("flat fee charge with custom currency: %w", meta.ErrCustomCurrencyNotSupported) } if err := input.ValidateWith(currency); err != nil { @@ -194,6 +201,10 @@ func (h *flatFeeHandler) OnPaymentAuthorized(ctx context.Context, input flatfee. intent := input.Charge.Intent + if intent.GetCurrency().IsCustom() { + return ledgertransaction.GroupReference{}, fmt.Errorf("flat fee charge with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + customerID := customer.CustomerID{ Namespace: input.Charge.Namespace, ID: intent.GetCustomerID(), @@ -210,7 +221,7 @@ func (h *flatFeeHandler) OnPaymentAuthorized(ctx context.Context, input flatfee. transactions.AuthorizeCustomerReceivablePaymentTemplate{ At: input.EventAt, Amount: input.Amount, - Currency: intent.GetCurrency(), + Currency: intent.GetCurrency().GetCode(), CostBasis: invoiceCostBasis, SpendChargeID: &input.Charge.ID, }, @@ -250,6 +261,10 @@ func (h *flatFeeHandler) OnPaymentSettled(ctx context.Context, input flatfee.OnP intent := input.Charge.Intent + if intent.GetCurrency().IsCustom() { + return ledgertransaction.GroupReference{}, fmt.Errorf("flat fee charge with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + customerID := customer.CustomerID{ Namespace: input.Charge.Namespace, ID: intent.GetCustomerID(), @@ -266,7 +281,7 @@ func (h *flatFeeHandler) OnPaymentSettled(ctx context.Context, input flatfee.OnP transactions.SettleCustomerReceivableFromPaymentTemplate{ At: input.EventAt, Amount: input.Amount, - Currency: intent.GetCurrency(), + Currency: intent.GetCurrency().GetCode(), CostBasis: invoiceCostBasis, SpendChargeID: &input.Charge.ID, }, diff --git a/openmeter/ledger/chargeadapter/flatfee_test.go b/openmeter/ledger/chargeadapter/flatfee_test.go index a6c99c07b0..01aa02ac9f 100644 --- a/openmeter/ledger/chargeadapter/flatfee_test.go +++ b/openmeter/ledger/chargeadapter/flatfee_test.go @@ -17,6 +17,8 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/creditrealization" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/invoicedusage" "github.com/openmeterio/openmeter/openmeter/billing/models/totals" + "github.com/openmeterio/openmeter/openmeter/currencies" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" ledgertransactiondb "github.com/openmeterio/openmeter/openmeter/ent/db/ledgertransaction" ledgertransactiongroupdb "github.com/openmeterio/openmeter/openmeter/ent/db/ledgertransactiongroup" enttx "github.com/openmeterio/openmeter/openmeter/ent/tx" @@ -28,7 +30,6 @@ import ( "github.com/openmeterio/openmeter/openmeter/ledger/transactions" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/clock" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -220,10 +221,7 @@ func TestOnCorrectCreditAllocations(t *testing.T) { intent.SettlementMode = productcatalog.CreditOnlySettlementMode }) - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(chargeWithRealizations.Intent.GetCurrency()). - Build() - require.NoError(t, err) + currency := chargeWithRealizations.Intent.GetCurrency() correctionsRequest, err := chargeWithRealizations.Realizations.CurrentRun.CreditRealizations.CreateCorrectionRequest(alpacadecimal.NewFromInt(-30), currency) require.NoError(t, err) @@ -258,10 +256,7 @@ func TestOnCorrectCreditAllocations(t *testing.T) { intent.SettlementMode = productcatalog.CreditOnlySettlementMode }) - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(chargeWithRealizations.Intent.GetCurrency()). - Build() - require.NoError(t, err) + currency := chargeWithRealizations.Intent.GetCurrency() correctionsRequest, err := chargeWithRealizations.Realizations.CurrentRun.CreditRealizations.CreateCorrectionRequest(alpacadecimal.NewFromInt(-35), currency) require.NoError(t, err) @@ -294,10 +289,7 @@ func TestOnCorrectCreditAllocations(t *testing.T) { intent.SettlementMode = productcatalog.CreditOnlySettlementMode }) - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(chargeWithRealizations.Intent.GetCurrency()). - Build() - require.NoError(t, err) + currency := chargeWithRealizations.Intent.GetCurrency() correctionsRequest, err := chargeWithRealizations.Realizations.CurrentRun.CreditRealizations.CreateCorrectionRequest(alpacadecimal.NewFromInt(-30), currency) require.NoError(t, err) @@ -327,10 +319,7 @@ func TestOnCorrectCreditAllocations(t *testing.T) { chargeWithRealizations := env.newChargeWithCreditRealizationsAndAccruedUsage(allocations, alpacadecimal.Zero) - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(chargeWithRealizations.Intent.GetCurrency()). - Build() - require.NoError(t, err) + currency := chargeWithRealizations.Intent.GetCurrency() correctionsRequest, err := chargeWithRealizations.Realizations.CurrentRun.CreditRealizations.CreateCorrectionRequest(alpacadecimal.NewFromInt(-30), currency) require.NoError(t, err) @@ -363,10 +352,7 @@ func TestOnCorrectCreditAllocations(t *testing.T) { require.True(t, env.sumBalance(t, env.creditAccruedSubAccount(t)).Equal(alpacadecimal.Zero)) require.True(t, env.sumBalance(t, env.creditEarningsSubAccount(t)).Equal(alpacadecimal.NewFromInt(30))) - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(chargeWithRealizations.Intent.GetCurrency()). - Build() - require.NoError(t, err) + currency := chargeWithRealizations.Intent.GetCurrency() correctionsRequest, err := chargeWithRealizations.Realizations.CurrentRun.CreditRealizations.CreateCorrectionRequest(alpacadecimal.NewFromInt(-30), currency) require.NoError(t, err) @@ -405,10 +391,7 @@ func TestOnCorrectCreditAllocations(t *testing.T) { require.True(t, env.sumBalance(t, env.creditAccruedSubAccount(t)).Equal(alpacadecimal.Zero)) require.True(t, env.sumBalance(t, env.creditEarningsSubAccount(t)).Equal(alpacadecimal.NewFromInt(30))) - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(chargeWithRealizations.Intent.GetCurrency()). - Build() - require.NoError(t, err) + currency := chargeWithRealizations.Intent.GetCurrency() correctionsRequest, err := chargeWithRealizations.Realizations.CurrentRun.CreditRealizations.CreateCorrectionRequest(alpacadecimal.NewFromInt(-30), currency) require.NoError(t, err) @@ -727,6 +710,7 @@ type flatFeeHandlerTestEnv struct { handler chargeflatfee.Handler lineage lineage.Service recognizer recognizer.Service + currency currencies.Currency } func newFlatFeeHandlerTestEnv(t *testing.T) *flatFeeHandlerTestEnv { @@ -770,6 +754,7 @@ func newFlatFeeHandlerTestEnv(t *testing.T) *flatFeeHandlerTestEnv { ), lineage: lineageService, recognizer: recognizerService, + currency: currenciestestutils.NewFiatCurrency(t, "USD"), } } @@ -810,7 +795,7 @@ func (e *flatFeeHandlerTestEnv) newAssignmentInputWithMode(amount alpacadecimal. Intent: meta.Intent{ ManagedBy: billing.SystemManagedLine, CustomerID: e.CustomerID.ID, - Currency: currencyx.Code("USD"), + Currency: e.currency, TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: testChargeTaxCodeID, }, @@ -955,7 +940,7 @@ func (e *flatFeeHandlerTestEnv) newBaseCharge(servicePeriod timeutil.ClosedPerio Intent: meta.Intent{ ManagedBy: billing.SystemManagedLine, CustomerID: e.CustomerID.ID, - Currency: currencyx.Code("USD"), + Currency: e.currency, TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: testChargeTaxCodeID, }, @@ -1144,7 +1129,7 @@ func (e *flatFeeHandlerTestEnv) recognizeCreditAccrued(t *testing.T, amount alpa result, err := e.recognizer.RecognizeEarnings(t.Context(), recognizer.RecognizeEarningsInput{ CustomerID: e.CustomerID, At: e.Now(), - Currency: e.Currency, + Currency: e.currency, }) require.NoError(t, err) require.True(t, result.RecognizedAmount.Equal(amount), "recognized=%s expected=%s", result.RecognizedAmount, amount) @@ -1161,7 +1146,7 @@ func (e *flatFeeHandlerTestEnv) createInitialLineages(t *testing.T, chargeID str Namespace: e.Namespace, ChargeID: chargeID, CustomerID: e.CustomerID.ID, - Currency: e.Currency, + Currency: e.currency, Realizations: realizations, }) require.NoError(t, err) diff --git a/openmeter/ledger/chargeadapter/usagebased.go b/openmeter/ledger/chargeadapter/usagebased.go index c6c7195f13..33c344da1d 100644 --- a/openmeter/ledger/chargeadapter/usagebased.go +++ b/openmeter/ledger/chargeadapter/usagebased.go @@ -7,6 +7,7 @@ import ( "github.com/alpacahq/alpacadecimal" "github.com/samber/lo" + "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/creditrealization" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" @@ -15,7 +16,6 @@ import ( "github.com/openmeterio/openmeter/openmeter/ledger/collector" "github.com/openmeterio/openmeter/openmeter/ledger/transactions" "github.com/openmeterio/openmeter/openmeter/productcatalog" - "github.com/openmeterio/openmeter/pkg/currencyx" ) // usageBasedHandler maps usage-based credit lifecycle events to ledger transaction templates. @@ -44,6 +44,10 @@ func (h *usageBasedHandler) OnInvoiceUsageAccrued(ctx context.Context, input usa return ledgertransaction.GroupReference{}, err } + if input.Charge.Intent.GetCurrency().IsCustom() { + return ledgertransaction.GroupReference{}, fmt.Errorf("usage based charge with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + amount := input.Amount if amount.IsZero() { return ledgertransaction.GroupReference{}, nil @@ -74,7 +78,7 @@ func (h *usageBasedHandler) OnInvoiceUsageAccrued(ctx context.Context, input usa transactions.TransferCustomerReceivableToAccruedTemplate{ At: input.BookedAt, Amount: amount, - Currency: intent.GetCurrency(), + Currency: intent.GetCurrency().GetCode(), TaxCode: lo.ToPtr(taxConfig.TaxCodeID), TaxBehavior: (*ledger.TaxBehavior)(taxConfig.Behavior), CostBasis: invoiceCostBasis, @@ -106,6 +110,10 @@ func (h *usageBasedHandler) OnPaymentAuthorized(ctx context.Context, input usage intent := input.Charge.Intent + if intent.GetCurrency().IsCustom() { + return ledgertransaction.GroupReference{}, fmt.Errorf("usage based charge with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + if err := validateSettlementMode( intent.GetSettlementMode(), productcatalog.CreditThenInvoiceSettlementMode, @@ -138,7 +146,7 @@ func (h *usageBasedHandler) OnPaymentAuthorized(ctx context.Context, input usage transactions.AuthorizeCustomerReceivablePaymentTemplate{ At: input.EventAt, Amount: receivableReplenishment, - Currency: intent.GetCurrency(), + Currency: intent.GetCurrency().GetCode(), CostBasis: invoiceCostBasis, SpendChargeID: &input.Charge.ID, }, @@ -174,6 +182,10 @@ func (h *usageBasedHandler) OnPaymentSettled(ctx context.Context, input usagebas intent := input.Charge.Intent + if input.Charge.Intent.GetCurrency().IsCustom() { + return ledgertransaction.GroupReference{}, fmt.Errorf("usage based charge with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + if err := validateSettlementMode( intent.GetSettlementMode(), productcatalog.CreditThenInvoiceSettlementMode, @@ -201,7 +213,7 @@ func (h *usageBasedHandler) OnPaymentSettled(ctx context.Context, input usagebas transactions.SettleCustomerReceivableFromPaymentTemplate{ At: input.EventAt, Amount: input.Run.InvoiceUsage.Totals.Total, - Currency: intent.GetCurrency(), + Currency: intent.GetCurrency().GetCode(), CostBasis: invoiceCostBasis, SpendChargeID: &input.Charge.ID, }, @@ -242,6 +254,10 @@ func (h *usageBasedHandler) OnCreditsOnlyUsageAccrued(ctx context.Context, input intent := input.Charge.Intent taxConfig := intent.GetTaxConfig() + if intent.GetCurrency().IsCustom() { + return nil, fmt.Errorf("usage based charge with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + if err := validateSettlementMode( intent.GetSettlementMode(), productcatalog.CreditOnlySettlementMode, @@ -257,7 +273,7 @@ func (h *usageBasedHandler) OnCreditsOnlyUsageAccrued(ctx context.Context, input Annotations: chargeAnnotationsForUsageBasedCharge(input.Charge), BookedAt: input.BookedAt, SourceBalanceAsOf: input.BookedAt, - Currency: intent.GetCurrency(), + Currency: intent.GetCurrency().GetCode(), FeatureKey: intent.GetFeatureKey(), TaxCode: lo.ToPtr(taxConfig.TaxCodeID), TaxBehavior: (*ledger.TaxBehavior)(taxConfig.Behavior), @@ -286,13 +302,12 @@ func (h *usageBasedHandler) OnCreditsOnlyUsageAccruedCorrection(ctx context.Cont return nil, fmt.Errorf("credits only usage accrued correction: %w", err) } - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(intent.GetCurrency()). - Build() - if err != nil { - return nil, fmt.Errorf("get currency calculator: %w", err) + if input.Charge.Intent.GetCurrency().IsCustom() { + return nil, fmt.Errorf("usage based charge with custom currency: %w", meta.ErrCustomCurrencyNotSupported) } + currency := intent.GetCurrency() + if err := input.ValidateWith(currency); err != nil { return nil, err } diff --git a/openmeter/ledger/chargeadapter/usagebased_test.go b/openmeter/ledger/chargeadapter/usagebased_test.go index c109ac322c..3f9f743c83 100644 --- a/openmeter/ledger/chargeadapter/usagebased_test.go +++ b/openmeter/ledger/chargeadapter/usagebased_test.go @@ -19,6 +19,8 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" chargeusagebased "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" "github.com/openmeterio/openmeter/openmeter/billing/models/totals" + "github.com/openmeterio/openmeter/openmeter/currencies" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" ledgertransactiondb "github.com/openmeterio/openmeter/openmeter/ent/db/ledgertransaction" enttx "github.com/openmeterio/openmeter/openmeter/ent/tx" "github.com/openmeterio/openmeter/openmeter/ledger" @@ -29,7 +31,6 @@ import ( "github.com/openmeterio/openmeter/openmeter/ledger/transactions" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/clock" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/models" "github.com/openmeterio/openmeter/pkg/timeutil" ) @@ -182,10 +183,7 @@ func TestOnUsageBasedCreditsOnlyUsageAccruedCorrection(t *testing.T) { run.CreditsAllocated = env.realizationsFromAllocations(allocations) - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(env.Currency). - Build() - require.NoError(t, err) + currency := env.currency correctionsRequest, err := run.CreditsAllocated.CreateCorrectionRequest(alpacadecimal.NewFromInt(-30), currency) require.NoError(t, err) @@ -221,10 +219,7 @@ func TestOnUsageBasedCreditsOnlyUsageAccruedCorrection(t *testing.T) { run.CreditsAllocated = env.realizationsFromAllocations(allocations) - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(env.Currency). - Build() - require.NoError(t, err) + currency := env.currency correctionsRequest, err := run.CreditsAllocated.CreateCorrectionRequest(alpacadecimal.NewFromInt(-20), currency) require.NoError(t, err) @@ -266,10 +261,7 @@ func TestOnUsageBasedCreditsOnlyUsageAccruedCorrection(t *testing.T) { require.True(t, env.sumBalance(t, env.creditAccruedSubAccount(t)).Equal(alpacadecimal.Zero)) require.Equal(t, float64(20), env.sumBalance(t, env.creditEarningsSubAccount(t)).InexactFloat64()) - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(env.Currency). - Build() - require.NoError(t, err) + currency := env.currency correctionsRequest, err := run.CreditsAllocated.CreateCorrectionRequest(alpacadecimal.NewFromInt(-20), currency) require.NoError(t, err) @@ -312,10 +304,7 @@ func TestOnUsageBasedCreditsOnlyUsageAccruedCorrection(t *testing.T) { require.True(t, env.sumBalance(t, env.creditAccruedSubAccount(t)).Equal(alpacadecimal.Zero)) require.Equal(t, float64(20), env.sumBalance(t, env.creditEarningsSubAccount(t)).InexactFloat64()) - currency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). - WithCode(env.Currency). - Build() - require.NoError(t, err) + currency := env.currency correctionsRequest, err := run.CreditsAllocated.CreateCorrectionRequest(alpacadecimal.NewFromInt(-20), currency) require.NoError(t, err) @@ -557,6 +546,7 @@ type usageBasedHandlerTestEnv struct { handler chargeusagebased.Handler lineage lineage.Service recognizer recognizer.Service + currency currencies.Currency } func newUsageBasedHandlerTestEnv(t *testing.T) *usageBasedHandlerTestEnv { @@ -604,6 +594,7 @@ func newUsageBasedHandlerTestEnv(t *testing.T) *usageBasedHandlerTestEnv { }, collectorService), lineage: lineageService, recognizer: recognizerService, + currency: currenciestestutils.NewFiatCurrency(t, "USD"), } } @@ -635,7 +626,7 @@ func (e *usageBasedHandlerTestEnv) newCharge(settlementMode productcatalog.Settl Intent: meta.Intent{ ManagedBy: billing.SystemManagedLine, CustomerID: e.CustomerID.ID, - Currency: currencyx.Code("USD"), + Currency: e.currency, TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: testChargeTaxCodeID, }, @@ -921,7 +912,7 @@ func (e *usageBasedHandlerTestEnv) recognizeCreditAccrued(t *testing.T, amount a result, err := e.recognizer.RecognizeEarnings(t.Context(), recognizer.RecognizeEarningsInput{ CustomerID: e.CustomerID, At: e.Now(), - Currency: e.Currency, + Currency: e.currency, }) require.NoError(t, err) require.True(t, result.RecognizedAmount.Equal(amount), "recognized=%s expected=%s", result.RecognizedAmount, amount) @@ -938,7 +929,7 @@ func (e *usageBasedHandlerTestEnv) createInitialLineages(t *testing.T, chargeID Namespace: e.Namespace, ChargeID: chargeID, CustomerID: e.CustomerID.ID, - Currency: e.Currency, + Currency: e.currency, Realizations: realizations, }) require.NoError(t, err) diff --git a/openmeter/ledger/customerbalance/expired_loader_test.go b/openmeter/ledger/customerbalance/expired_loader_test.go index 0252e82883..697c4c3404 100644 --- a/openmeter/ledger/customerbalance/expired_loader_test.go +++ b/openmeter/ledger/customerbalance/expired_loader_test.go @@ -14,6 +14,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing" "github.com/openmeterio/openmeter/openmeter/billing/charges/creditpurchase" chargemeta "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/ledger" ledgerbreakage "github.com/openmeterio/openmeter/openmeter/ledger/breakage" "github.com/openmeterio/openmeter/openmeter/ledger/transactions" @@ -668,7 +669,7 @@ func (e *testEnv) createPromotionalCreditFunding(t *testing.T, fundedAt time.Tim Intent: chargemeta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: e.CustomerID.ID, - Currency: e.Currency, + Currency: currenciestestutils.NewFiatCurrency(t, e.Currency), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: e.taxCodeID, }, diff --git a/openmeter/ledger/customerbalance/service.go b/openmeter/ledger/customerbalance/service.go index 8c85a4a67d..9533285f95 100644 --- a/openmeter/ledger/customerbalance/service.go +++ b/openmeter/ledger/customerbalance/service.go @@ -497,7 +497,11 @@ func (s *service) getPendingGrantCurrencies( continue } - codes = append(codes, creditPurchaseCharge.Intent.Currency) + if creditPurchaseCharge.Intent.Currency.IsCustom() { + return nil, fmt.Errorf("credit purchase charge with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + + codes = append(codes, creditPurchaseCharge.Intent.Currency.GetCode()) } return dedupeCurrencies(codes), nil @@ -522,7 +526,7 @@ func (s *service) getPendingGrantAmount( return alpacadecimal.Zero, fmt.Errorf("map credit purchase charge: %w", err) } - if creditPurchaseCharge.Intent.Currency != currency { + if creditPurchaseCharge.Intent.Currency.GetCode() != currency { continue } @@ -696,10 +700,14 @@ func getFlatFeeChargePendingBalanceImpact(charge charges.Charge, currency curren return nil, fmt.Errorf("map flat fee charge: %w", err) } - if flatFeeCharge.Intent.GetCurrency() != currency { + if flatFeeCharge.Intent.GetCurrency().GetCode() != currency { return nil, nil } + if flatFeeCharge.Intent.GetCurrency().IsCustom() { + return nil, fmt.Errorf("flat fee charge with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + if !featureFilterMatchesChargeFeatureKey(featureFilter, flatFeeCharge.Intent.GetFeatureKey()) { return nil, nil } @@ -713,10 +721,14 @@ func (s *service) getUsageBasedChargePendingBalanceImpact(ctx context.Context, c return nil, fmt.Errorf("map usage based charge: %w", err) } - if usageBasedCharge.Intent.GetCurrency() != currency { + if usageBasedCharge.Intent.GetCurrency().GetCode() != currency { return nil, nil } + if usageBasedCharge.Intent.GetCurrency().IsCustom() { + return nil, fmt.Errorf("usage based charge with custom currency: %w", meta.ErrCustomCurrencyNotSupported) + } + if !featureFilterMatchesChargeFeatureKey(featureFilter, usageBasedCharge.Intent.GetFeatureKey()) { return nil, nil } diff --git a/openmeter/ledger/customerbalance/service_test.go b/openmeter/ledger/customerbalance/service_test.go index 5027e81914..3a001131e8 100644 --- a/openmeter/ledger/customerbalance/service_test.go +++ b/openmeter/ledger/customerbalance/service_test.go @@ -17,6 +17,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/ledger" "github.com/openmeterio/openmeter/openmeter/productcatalog" @@ -343,7 +344,7 @@ func TestGetBalanceForFlatFeeCreditOnlyInvoiceAtBeforeServiceStart(t *testing.T) Intent: chargemeta.Intent{ ManagedBy: billing.SystemManagedLine, CustomerID: env.CustomerID.ID, - Currency: env.Currency, + Currency: currenciestestutils.NewFiatCurrency(t, env.Currency), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: env.taxCodeID, }, @@ -807,7 +808,7 @@ func TestIsPendingCreditGrantAt(t *testing.T) { Intent: creditpurchase.Intent{ Intent: chargemeta.Intent{ CustomerID: "customer-id", - Currency: currency, + Currency: currenciestestutils.NewFiatCurrency(t, currency), }, IntentMutableFields: creditpurchase.IntentMutableFields{ IntentMutableFields: chargemeta.IntentMutableFields{ diff --git a/openmeter/ledger/customerbalance/testenv_test.go b/openmeter/ledger/customerbalance/testenv_test.go index e47f7ff4b4..06c2ec4d83 100644 --- a/openmeter/ledger/customerbalance/testenv_test.go +++ b/openmeter/ledger/customerbalance/testenv_test.go @@ -29,6 +29,7 @@ import ( usagebasedadapter "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased/adapter" usagebasedservice "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased/service" billingratingservice "github.com/openmeterio/openmeter/openmeter/billing/rating/service" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/customer" enttx "github.com/openmeterio/openmeter/openmeter/ent/tx" ledgerbreakage "github.com/openmeterio/openmeter/openmeter/ledger/breakage" @@ -461,7 +462,7 @@ func (e *testEnv) createUsageBasedChargeInCurrency(t *testing.T, unitPrice alpac Intent: chargemeta.Intent{ ManagedBy: billing.SystemManagedLine, CustomerID: e.CustomerID.ID, - Currency: currency, + Currency: currenciestestutils.NewFiatCurrency(t, currency), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: e.taxCodeID, }, @@ -509,7 +510,7 @@ func (e *testEnv) createFlatFeeChargeInCurrency(t *testing.T, amount alpacadecim Intent: chargemeta.Intent{ ManagedBy: billing.SystemManagedLine, CustomerID: e.CustomerID.ID, - Currency: currency, + Currency: currenciestestutils.NewFiatCurrency(t, currency), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: e.taxCodeID, }, @@ -620,7 +621,7 @@ func (e *testEnv) createCreditPurchase( Intent: chargemeta.Intent{ ManagedBy: billing.SubscriptionManagedLine, CustomerID: e.CustomerID.ID, - Currency: currency, + Currency: currenciestestutils.NewFiatCurrency(t, currency), TaxConfig: productcatalog.TaxCodeConfig{ TaxCodeID: e.taxCodeID, }, diff --git a/openmeter/ledger/recognizer/recognize.go b/openmeter/ledger/recognizer/recognize.go index 74f81eef2a..abcf46a026 100644 --- a/openmeter/ledger/recognizer/recognize.go +++ b/openmeter/ledger/recognizer/recognize.go @@ -38,7 +38,7 @@ func (s *service) RecognizeEarnings(ctx context.Context, in RecognizeEarningsInp lineages, err := s.lnge.LoadLineagesByCustomer(ctx, lineage.LoadLineagesByCustomerInput{ Namespace: in.CustomerID.Namespace, CustomerID: in.CustomerID.ID, - Currency: in.Currency, + Currency: in.Currency.GetCode(), }) if err != nil { return RecognizeEarningsResult{}, fmt.Errorf("load lineages: %w", err) @@ -66,7 +66,7 @@ func (s *service) RecognizeEarnings(ctx context.Context, in RecognizeEarningsInp transactions.RecognizeEarningsFromAttributableAccruedTemplate{ At: in.At, Amount: totalEligible, - Currency: in.Currency, + Currency: in.Currency.GetCode(), }, ) if err != nil { diff --git a/openmeter/ledger/recognizer/service.go b/openmeter/ledger/recognizer/service.go index d8266e2abe..b20ec3332a 100644 --- a/openmeter/ledger/recognizer/service.go +++ b/openmeter/ledger/recognizer/service.go @@ -9,10 +9,11 @@ import ( "github.com/alpacahq/alpacadecimal" "github.com/openmeterio/openmeter/openmeter/billing/charges/lineage" + "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" + "github.com/openmeterio/openmeter/openmeter/currencies" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/ledger" "github.com/openmeterio/openmeter/openmeter/ledger/transactions" - "github.com/openmeterio/openmeter/pkg/currencyx" "github.com/openmeterio/openmeter/pkg/framework/transaction" ) @@ -85,7 +86,7 @@ type service struct { type RecognizeEarningsInput struct { CustomerID customer.CustomerID At time.Time - Currency currencyx.Code + Currency currencies.Currency } func (i RecognizeEarningsInput) Validate() error { @@ -97,10 +98,15 @@ func (i RecognizeEarningsInput) Validate() error { if i.At.IsZero() { errs = append(errs, errors.New("at is required")) } + if err := i.Currency.Validate(); err != nil { errs = append(errs, fmt.Errorf("currency: %w", err)) } + if i.Currency.IsCustom() { + errs = append(errs, fmt.Errorf("custom currency: %w", meta.ErrCustomCurrencyNotSupported)) + } + return errors.Join(errs...) } diff --git a/openmeter/ledger/recognizer/service_test.go b/openmeter/ledger/recognizer/service_test.go index 2d244746de..446468c38c 100644 --- a/openmeter/ledger/recognizer/service_test.go +++ b/openmeter/ledger/recognizer/service_test.go @@ -14,6 +14,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/creditrealization" "github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" enttx "github.com/openmeterio/openmeter/openmeter/ent/tx" "github.com/openmeterio/openmeter/openmeter/ledger/recognizer" ledgertestutils "github.com/openmeterio/openmeter/openmeter/ledger/testutils" @@ -123,7 +124,7 @@ func (e *recognizerTestEnv) createLineageForRealization(t *testing.T, chargeID, Namespace: e.Namespace, ChargeID: chargeID, CustomerID: e.CustomerID.ID, - Currency: e.Currency, + Currency: currenciestestutils.NewFiatCurrency(t, e.Currency), Realizations: creditrealization.Realizations{ { CreateInput: creditrealization.CreateInput{ @@ -150,6 +151,7 @@ func (e *recognizerTestEnv) createLineageForRealization(t *testing.T, chargeID, func TestRecognizeEarnings_IdempotencyOnUnchangedState(t *testing.T) { env := newRecognizerTestEnv(t) costBasis := alpacadecimal.NewFromInt(1) + currency := currenciestestutils.NewFiatCurrency(t, env.Currency) chargeID := testID() realID := testID() @@ -164,7 +166,7 @@ func TestRecognizeEarnings_IdempotencyOnUnchangedState(t *testing.T) { result1, err := env.recognizer.RecognizeEarnings(t.Context(), recognizer.RecognizeEarningsInput{ CustomerID: env.CustomerID, At: clock.Now(), - Currency: env.Currency, + Currency: currency, }) require.NoError(t, err) require.True(t, result1.RecognizedAmount.Equal(alpacadecimal.NewFromInt(50))) @@ -174,7 +176,7 @@ func TestRecognizeEarnings_IdempotencyOnUnchangedState(t *testing.T) { result2, err := env.recognizer.RecognizeEarnings(t.Context(), recognizer.RecognizeEarningsInput{ CustomerID: env.CustomerID, At: clock.Now(), - Currency: env.Currency, + Currency: currency, }) require.NoError(t, err) require.True(t, result2.RecognizedAmount.IsZero()) @@ -188,6 +190,7 @@ func TestRecognizeEarnings_IdempotencyOnUnchangedState(t *testing.T) { func TestRecognizeEarnings_DeterministicAllocationAndSegmentTransition(t *testing.T) { env := newRecognizerTestEnv(t) costBasis := alpacadecimal.NewFromInt(1) + currency := currenciestestutils.NewFiatCurrency(t, env.Currency) chargeID := testID() realA := testID() realB := testID() @@ -202,7 +205,7 @@ func TestRecognizeEarnings_DeterministicAllocationAndSegmentTransition(t *testin result, err := env.recognizer.RecognizeEarnings(t.Context(), recognizer.RecognizeEarningsInput{ CustomerID: env.CustomerID, At: clock.Now(), - Currency: env.Currency, + Currency: currency, }) require.NoError(t, err) require.True(t, result.RecognizedAmount.Equal(alpacadecimal.NewFromInt(70))) diff --git a/openmeter/server/router/router.go b/openmeter/server/router/router.go index b20dfc950e..0d7bcd68e7 100644 --- a/openmeter/server/router/router.go +++ b/openmeter/server/router/router.go @@ -106,7 +106,7 @@ type Config struct { AppCustomInvoicing appcustominvoicing.SyncService Billing billing.Service BillingFeatureSwitches config.BillingFeatureSwitchesConfiguration - ChargeService billingcharges.ChargeService + ChargeService billingcharges.Service Credits config.CreditsConfiguration UnitConfig config.UnitConfigConfiguration CurrencyService currencies.Service diff --git a/openmeter/server/server_test.go b/openmeter/server/server_test.go index 5c1e815903..8ac02a1686 100644 --- a/openmeter/server/server_test.go +++ b/openmeter/server/server_test.go @@ -28,6 +28,7 @@ import ( appstripe "github.com/openmeterio/openmeter/openmeter/app/stripe" "github.com/openmeterio/openmeter/openmeter/billing" billingcharges "github.com/openmeterio/openmeter/openmeter/billing/charges" + "github.com/openmeterio/openmeter/openmeter/billing/charges/creditpurchase" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" costpkg "github.com/openmeterio/openmeter/openmeter/cost" "github.com/openmeterio/openmeter/openmeter/credit" @@ -834,7 +835,7 @@ func TestListCustomerChargesRoute(t *testing.T) { }) } -// NoopChargeService is a no-op implementation of billingcharges.ChargeService for testing. +// NoopChargeService is a no-op implementation of billingcharges.Service for testing. type NoopChargeService struct{} func (n NoopChargeService) GetByID(_ context.Context, _ billingcharges.GetByIDInput) (billingcharges.Charge, error) { @@ -877,11 +878,19 @@ func (n NoopChargeService) ListCharges(_ context.Context, input billingcharges.L }, nil } +func (n NoopChargeService) HandleCreditPurchaseExternalPaymentStateTransition(_ context.Context, _ billingcharges.HandleCreditPurchaseExternalPaymentStateTransitionInput) (creditpurchase.Charge, error) { + return creditpurchase.Charge{}, nil +} + +func (n NoopChargeService) CreateCustomerCharge(_ context.Context, _ billingcharges.CreateCustomerChargeInput) (billingcharges.Charge, error) { + return billingcharges.Charge{}, nil +} + func (n NoopChargeService) GetCurrentTotals(_ context.Context, _ usagebased.GetCurrentTotalsInput) (usagebased.GetCurrentTotalsResult, error) { return usagebased.GetCurrentTotalsResult{}, nil } -var _ billingcharges.ChargeService = NoopChargeService{} +var _ billingcharges.Service = NoopChargeService{} // NoopPublisher is a publisher that does nothing (no-operation) // Useful for testing or when publishing needs to be disabled diff --git a/pkg/currencyx/code.go b/pkg/currencyx/code.go index da261a08bb..0425c5ce2c 100644 --- a/pkg/currencyx/code.go +++ b/pkg/currencyx/code.go @@ -34,7 +34,7 @@ func (c Code) Equal(other Code) bool { } func (c Code) Type() CurrencyType { - if len(c) == 3 { + if len(c) <= 3 { return CurrencyTypeFiat } @@ -58,7 +58,7 @@ func (c Code) Validate() error { return models.NewNillableGenericValidationError(errors.Join(errs...)) } - if len(c) == 3 { + if c.IsFiat() { if err := validateFiatCurrencyCode(c); err != nil { errs = append(errs, err) } diff --git a/pkg/currencyx/code_test.go b/pkg/currencyx/code_test.go index a943effc6b..7f1e5a8f0d 100644 --- a/pkg/currencyx/code_test.go +++ b/pkg/currencyx/code_test.go @@ -43,9 +43,9 @@ func TestCodeValidate(t *testing.T) { code: currencyx.Code(strings.Repeat("A", currencyx.CustomCurrencyCodeMaxLength)), }, { - name: "custom code too short", + name: "invalid short fiat code", code: "AB", - expectedError: "between 4 and 24 characters", + expectedError: "invalid fiat currency code", }, { name: "custom code too long", @@ -126,6 +126,16 @@ func TestCodeType(t *testing.T) { code currencyx.Code expected currencyx.CurrencyType }{ + { + name: "one-character fiat classification", + code: "C", + expected: currencyx.CurrencyTypeFiat, + }, + { + name: "two-character fiat classification", + code: "CR", + expected: currencyx.CurrencyTypeFiat, + }, { name: "fiat", code: "USD", diff --git a/pkg/framework/entutils/entedge/ornil.go b/pkg/framework/entutils/entedge/ornil.go new file mode 100644 index 0000000000..bfa14531b5 --- /dev/null +++ b/pkg/framework/entutils/entedge/ornil.go @@ -0,0 +1,14 @@ +package entedge + +import "github.com/openmeterio/openmeter/openmeter/ent/db" + +func OrNilIfNotFound[T any](edgeValue *T, err error) (*T, error) { + if err != nil { + if db.IsNotFound(err) { + return nil, nil + } + return nil, err + } + + return edgeValue, nil +} diff --git a/test/app/stripe/invoice_credits_test.go b/test/app/stripe/invoice_credits_test.go index 82aba776e7..c5c9d1eae0 100644 --- a/test/app/stripe/invoice_credits_test.go +++ b/test/app/stripe/invoice_credits_test.go @@ -21,6 +21,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/payment" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" "github.com/openmeterio/openmeter/openmeter/billing/creditgrant" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/productcatalog" "github.com/openmeterio/openmeter/pkg/clock" @@ -383,7 +384,7 @@ func (s *StripeInvoiceTestSuite) createMockChargeIntent(input createMockChargeIn ManagedBy: input.managedBy, UniqueReferenceID: lo.EmptyableToPtr(input.uniqueReferenceID), CustomerID: input.customer.ID, - Currency: input.currency, + Currency: currenciestestutils.NewFiatCurrency(s.T(), input.currency), }, IntentMutableFields: usagebased.IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ @@ -420,7 +421,7 @@ func (s *StripeInvoiceTestSuite) createCreditPurchaseIntent(input createCreditPu Intent: meta.Intent{ ManagedBy: billing.ManuallyManagedLine, CustomerID: input.customer.ID, - Currency: input.currency, + Currency: currenciestestutils.NewFiatCurrency(s.T(), input.currency), }, IntentMutableFields: creditpurchase.IntentMutableFields{ IntentMutableFields: meta.IntentMutableFields{ diff --git a/test/credits/base.go b/test/credits/base.go index a880c4779f..fcf17ff371 100644 --- a/test/credits/base.go +++ b/test/credits/base.go @@ -21,6 +21,7 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/meta" chargestestutils "github.com/openmeterio/openmeter/openmeter/billing/charges/testutils" "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased" + currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/customer" enttx "github.com/openmeterio/openmeter/openmeter/ent/tx" "github.com/openmeterio/openmeter/openmeter/ledger" @@ -257,7 +258,7 @@ func (s *BaseSuite) CreateMockChargeIntent(input CreateMockChargeIntentInput) ch ManagedBy: input.ManagedBy, UniqueReferenceID: lo.EmptyableToPtr(input.UniqueReferenceID), CustomerID: input.Customer.ID, - Currency: input.Currency, + Currency: currenciestestutils.NewFiatCurrency(s.T(), input.Currency), TaxConfig: input.TaxConfig, } intentMutableFields := meta.IntentMutableFields{ @@ -748,7 +749,7 @@ func (s *BaseSuite) MustRecognizeRevenue(customerID customer.CustomerID, code cu result, err := s.RevenueRecognizer.RecognizeEarnings(s.T().Context(), recognizer.RecognizeEarningsInput{ CustomerID: customerID, At: clock.Now(), - Currency: code, + Currency: currenciestestutils.NewFiatCurrency(s.T(), code), }) s.NoError(err) s.True(result.RecognizedAmount.Equal(amount), "recognized=%s expected=%s", result.RecognizedAmount, amount) @@ -811,7 +812,7 @@ func (s *BaseSuite) CreateCreditPurchaseIntent(input CreateCreditPurchaseIntentI Intent: meta.Intent{ ManagedBy: billing.ManuallyManagedLine, CustomerID: input.Customer.ID, - Currency: input.Currency, + Currency: currenciestestutils.NewFiatCurrency(s.T(), input.Currency), TaxConfig: input.TaxConfig, }, IntentMutableFields: creditpurchase.IntentMutableFields{ diff --git a/tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql b/tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql new file mode 100644 index 0000000000..1888274a9b --- /dev/null +++ b/tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql @@ -0,0 +1,6 @@ +-- reverse: modify "charge_usage_based" table +ALTER TABLE "charge_usage_based" DROP CONSTRAINT "charge_usage_based_custom_currencies_charges_usage_based", DROP COLUMN "custom_currency_id", ALTER COLUMN "currency" SET NOT NULL, DROP CONSTRAINT "currency_reference"; +-- reverse: modify "charge_flat_fees" table +ALTER TABLE "charge_flat_fees" DROP CONSTRAINT "charge_flat_fees_custom_currencies_charges_flat_fee", DROP COLUMN "custom_currency_id", ALTER COLUMN "currency" SET NOT NULL, DROP CONSTRAINT "currency_reference"; +-- reverse: modify "charge_credit_purchases" table +ALTER TABLE "charge_credit_purchases" DROP CONSTRAINT "charge_credit_purchases_custom_currencies_charges_credit_purcha", DROP COLUMN "custom_currency_id", ALTER COLUMN "currency" SET NOT NULL, DROP CONSTRAINT "currency_reference"; diff --git a/tools/migrate/migrations/20260720093016_charges-custom-currencies.up.sql b/tools/migrate/migrations/20260720093016_charges-custom-currencies.up.sql new file mode 100644 index 0000000000..52346eb8f7 --- /dev/null +++ b/tools/migrate/migrations/20260720093016_charges-custom-currencies.up.sql @@ -0,0 +1,8 @@ +-- modify "charge_credit_purchases" table +ALTER TABLE "charge_credit_purchases" ADD CONSTRAINT "currency_reference" CHECK ((currency IS NULL) <> (custom_currency_id IS NULL)), ALTER COLUMN "currency" DROP NOT NULL, ADD COLUMN "custom_currency_id" character(26) NULL, ADD CONSTRAINT "charge_credit_purchases_custom_currencies_charges_credit_purcha" FOREIGN KEY ("custom_currency_id") REFERENCES "custom_currencies" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT; +-- modify "charge_flat_fees" table +ALTER TABLE "charge_flat_fees" ADD CONSTRAINT "currency_reference" CHECK ((currency IS NULL) <> (custom_currency_id IS NULL)), ALTER COLUMN "currency" DROP NOT NULL, ADD COLUMN "custom_currency_id" character(26) NULL, ADD CONSTRAINT "charge_flat_fees_custom_currencies_charges_flat_fee" FOREIGN KEY ("custom_currency_id") REFERENCES "custom_currencies" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT; +-- modify "charge_usage_based" table +ALTER TABLE "charge_usage_based" ADD CONSTRAINT "currency_reference" CHECK ((currency IS NULL) <> (custom_currency_id IS NULL)), ALTER COLUMN "currency" DROP NOT NULL, ADD COLUMN "custom_currency_id" character(26) NULL, ADD CONSTRAINT "charge_usage_based_custom_currencies_charges_usage_based" FOREIGN KEY ("custom_currency_id") REFERENCES "custom_currencies" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT; + +-- TODO: Add empty checks diff --git a/tools/migrate/migrations/atlas.sum b/tools/migrate/migrations/atlas.sum index 28b02922e0..b5804d8b1a 100644 --- a/tools/migrate/migrations/atlas.sum +++ b/tools/migrate/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:HtvV8KiOoPketSBDSgSE93zHFeYgvIdQPpwDHz2OUxs= +h1:GsZiySv+ZGohiTpP5HwaXd4KTBcMZsMOgujJRwI2kho= 20240826120919_init.up.sql h1:tc1V91/smlmaeJGQ8h+MzTEeFjjnrrFDbDAjOYJK91o= 20240903155435_entitlement-expired-index.up.sql h1:Hp8u5uckmLXc1cRvWU0AtVnnK8ShlpzZNp8pbiJLhac= 20240917172257_billing-entities.up.sql h1:Q1dAMo0Vjiit76OybClNfYPGC5nmvov2/M2W1ioi4Kw= @@ -239,3 +239,4 @@ h1:HtvV8KiOoPketSBDSgSE93zHFeYgvIdQPpwDHz2OUxs= 20260716152332_rescope-credit-grant-key-to-customer.up.sql h1:gbeATYlchOi0pwaTUFX+nrbLoS0jZbVDMHn8uMTck0c= 20260717060208_add_bulk_invoice_schema_level_2_migration_function.up.sql h1:kJCvXyNuRC9FMysGg9c6nesUXsfbkNq/RKfTIwnQzEw= 20260717160017_custom_currencies.up.sql h1:HHScXLfjVxumxzNrw/RdxZZZMo3EnCE5Min67fEX9jo= +20260720093016_charges-custom-currencies.up.sql h1:hiascJhglspAGXnm5UY+25dfc0HnDE+GVfMxbvxQ6ok= diff --git a/tools/migrate/views.sql b/tools/migrate/views.sql index 4e017549f4..3e5b63ccb3 100644 --- a/tools/migrate/views.sql +++ b/tools/migrate/views.sql @@ -1,4 +1,4 @@ -- Code generated by viewgen, DO NOT EDIT. -- Regenerate with: make generate-view-sql CREATE VIEW "charges_search_v1s" AS -SELECT "id", "namespace", "metadata", "created_at", "updated_at", "deleted_at", "name", "description", "annotations", "customer_id", "service_period_from", "service_period_to", "billing_period_from", "billing_period_to", "full_service_period_from", "full_service_period_to", "status", "unique_reference_id", "currency", "managed_by", "subscription_id", "subscription_phase_id", "subscription_item_id", "advance_after", "tax_code_id", "tax_behavior", NULL::timestamptz AS "base_intent_deleted_at", 'credit_purchase' AS "type" FROM "charge_credit_purchases" UNION ALL SELECT "id", "namespace", "metadata", "created_at", "updated_at", "deleted_at", "name", "description", "annotations", "customer_id", "service_period_from", "service_period_to", "billing_period_from", "billing_period_to", "full_service_period_from", "full_service_period_to", "status", "unique_reference_id", "currency", "managed_by", "subscription_id", "subscription_phase_id", "subscription_item_id", "advance_after", "tax_code_id", "tax_behavior", "intent_deleted_at" AS "base_intent_deleted_at", 'flat_fee' AS "type" FROM "charge_flat_fees" UNION ALL SELECT "id", "namespace", "metadata", "created_at", "updated_at", "deleted_at", "name", "description", "annotations", "customer_id", "service_period_from", "service_period_to", "billing_period_from", "billing_period_to", "full_service_period_from", "full_service_period_to", "status", "unique_reference_id", "currency", "managed_by", "subscription_id", "subscription_phase_id", "subscription_item_id", "advance_after", "tax_code_id", "tax_behavior", "intent_deleted_at" AS "base_intent_deleted_at", 'usage_based' AS "type" FROM "charge_usage_based"; +SELECT "id", "namespace", "metadata", "created_at", "updated_at", "deleted_at", "name", "description", "annotations", "customer_id", "service_period_from", "service_period_to", "billing_period_from", "billing_period_to", "full_service_period_from", "full_service_period_to", "status", "unique_reference_id", "currency", "custom_currency_id", "managed_by", "subscription_id", "subscription_phase_id", "subscription_item_id", "advance_after", "tax_code_id", "tax_behavior", NULL::timestamptz AS "base_intent_deleted_at", 'credit_purchase' AS "type" FROM "charge_credit_purchases" UNION ALL SELECT "id", "namespace", "metadata", "created_at", "updated_at", "deleted_at", "name", "description", "annotations", "customer_id", "service_period_from", "service_period_to", "billing_period_from", "billing_period_to", "full_service_period_from", "full_service_period_to", "status", "unique_reference_id", "currency", "custom_currency_id", "managed_by", "subscription_id", "subscription_phase_id", "subscription_item_id", "advance_after", "tax_code_id", "tax_behavior", "intent_deleted_at" AS "base_intent_deleted_at", 'flat_fee' AS "type" FROM "charge_flat_fees" UNION ALL SELECT "id", "namespace", "metadata", "created_at", "updated_at", "deleted_at", "name", "description", "annotations", "customer_id", "service_period_from", "service_period_to", "billing_period_from", "billing_period_to", "full_service_period_from", "full_service_period_to", "status", "unique_reference_id", "currency", "custom_currency_id", "managed_by", "subscription_id", "subscription_phase_id", "subscription_item_id", "advance_after", "tax_code_id", "tax_behavior", "intent_deleted_at" AS "base_intent_deleted_at", 'usage_based' AS "type" FROM "charge_usage_based"; From 3e4f5a775d17f70b70b265ef602fa489d24a6ba4 Mon Sep 17 00:00:00 2001 From: Peter Turi Date: Tue, 21 Jul 2026 13:24:23 +0200 Subject: [PATCH 2/3] fix: complete custom currency charge migration --- .../adapter/funded_credit_activity.go | 12 +++++++----- openmeter/billing/charges/service/base_test.go | 17 +++++++++++++++-- .../subscriptionsync/service/base_test.go | 4 ++-- openmeter/ent/db/migrate/schema.go | 3 +++ openmeter/ent/schema/charges.go | 1 + pkg/currencyx/code.go | 4 ++-- pkg/currencyx/code_test.go | 14 ++------------ ...720093016_charges-custom-currencies.down.sql | 10 +++++++--- ...60720093016_charges-custom-currencies.up.sql | 11 +++++++---- tools/migrate/migrations/atlas.sum | 4 ++-- 10 files changed, 48 insertions(+), 32 deletions(-) diff --git a/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go b/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go index 30e7a2c915..040e592f7e 100644 --- a/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go +++ b/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go @@ -65,12 +65,14 @@ func ListFundedCreditActivities(ctx context.Context, dbClient *db.Client, input if input.Currency != nil { query = query.Where(dbchargecreditpurchasecreditgrant.HasCreditPurchaseWith( - dbchargecreditpurchase.HasCustomCurrencyWith( - dbcustomcurrency.CodeEQ(*input.Currency), - dbcustomcurrency.Namespace(input.Customer.Namespace), - dbcustomcurrency.DeletedAtIsNil(), + dbchargecreditpurchase.Or( + dbchargecreditpurchase.HasCustomCurrencyWith( + dbcustomcurrency.CodeEQ(*input.Currency), + dbcustomcurrency.Namespace(input.Customer.Namespace), + dbcustomcurrency.DeletedAtIsNil(), + ), + dbchargecreditpurchase.FiatCurrencyCodeEQ(*input.Currency), ), - dbchargecreditpurchase.FiatCurrencyCodeEQ(*input.Currency), )) } diff --git a/openmeter/billing/charges/service/base_test.go b/openmeter/billing/charges/service/base_test.go index 1653540713..e74e6ca582 100644 --- a/openmeter/billing/charges/service/base_test.go +++ b/openmeter/billing/charges/service/base_test.go @@ -29,6 +29,9 @@ import ( usagebasedadapter "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased/adapter" usagebasedservice "github.com/openmeterio/openmeter/openmeter/billing/charges/usagebased/service" billingratingservice "github.com/openmeterio/openmeter/openmeter/billing/rating/service" + currencyadapter "github.com/openmeterio/openmeter/openmeter/currencies/adapter" + "github.com/openmeterio/openmeter/openmeter/currencies/currencyresolver" + currencyservice "github.com/openmeterio/openmeter/openmeter/currencies/service" currenciestestutils "github.com/openmeterio/openmeter/openmeter/currencies/testutils/currency" "github.com/openmeterio/openmeter/openmeter/customer" "github.com/openmeterio/openmeter/openmeter/ledger/recognizer" @@ -176,6 +179,15 @@ func (s *BaseSuite) SetupSuite() { }) s.NoError(err) + currencyAdapter, err := currencyadapter.New(currencyadapter.Config{ + Client: s.DBClient, + }) + s.NoError(err) + currencyService, err := currencyservice.New(currencyAdapter) + s.NoError(err) + currencyResolver, err := currencyresolver.New(currencyService) + s.NoError(err) + chargesService, err := New(Config{ Logger: slog.Default(), Adapter: chargesAdapter, @@ -187,8 +199,9 @@ func (s *BaseSuite) SetupSuite() { UsageBasedService: usageBasedService, RecognizerService: recognizer.NoopService{}, - BillingService: s.BillingService, - TaxCodeService: s.TaxCodeService, + BillingService: s.BillingService, + TaxCodeService: s.TaxCodeService, + CurrencyResolver: currencyResolver, }) s.NoError(err) s.Charges = chargesService diff --git a/openmeter/billing/worker/subscriptionsync/service/base_test.go b/openmeter/billing/worker/subscriptionsync/service/base_test.go index 9237156d3b..a1f2a79eed 100644 --- a/openmeter/billing/worker/subscriptionsync/service/base_test.go +++ b/openmeter/billing/worker/subscriptionsync/service/base_test.go @@ -501,7 +501,7 @@ func (s *SuiteBase) assertCharge(ctx context.Context, charge charges.Charge, sub s.Equal(expectedCharge.Status, string(usageBasedCharge.Status), "%s: status", childID) s.Equal(subsView.Subscription.SettlementMode, usageBasedCharge.Intent.GetSettlementMode(), "%s: settlement mode", childID) s.Equal(s.Customer.ID, usageBasedCharge.Intent.GetCustomerID(), "%s: customer id", childID) - s.Equal(subsView.Subscription.Currency, usageBasedCharge.Intent.GetCurrency(), "%s: currency", childID) + s.Equal(subsView.Subscription.Currency, usageBasedCharge.Intent.GetCurrency().GetCode(), "%s: currency", childID) s.Equal(expectedCharge.Periods[idx], baseIntent.ServicePeriod, "%s: service period", childID) if len(expectedCharge.FullServicePeriods) > 0 { s.Equal(expectedCharge.FullServicePeriods[idx], baseIntent.FullServicePeriod, "%s: full service period", childID) @@ -535,7 +535,7 @@ func (s *SuiteBase) assertCharge(ctx context.Context, charge charges.Charge, sub s.Equal(expectedCharge.Status, string(flatFeeCharge.Status), "%s: status", childID) s.Equal(subsView.Subscription.SettlementMode, flatFeeCharge.Intent.GetSettlementMode(), "%s: settlement mode", childID) s.Equal(s.Customer.ID, flatFeeCharge.Intent.GetCustomerID(), "%s: customer id", childID) - s.Equal(subsView.Subscription.Currency, flatFeeCharge.Intent.GetCurrency(), "%s: currency", childID) + s.Equal(subsView.Subscription.Currency, flatFeeCharge.Intent.GetCurrency().GetCode(), "%s: currency", childID) s.Equal(expectedCharge.Periods[idx], baseIntent.ServicePeriod, "%s: service period", childID) if len(expectedCharge.FullServicePeriods) > 0 { s.Equal(expectedCharge.FullServicePeriods[idx], baseIntent.FullServicePeriod, "%s: full service period", childID) diff --git a/openmeter/ent/db/migrate/schema.go b/openmeter/ent/db/migrate/schema.go index ad102a3679..09ae834471 100644 --- a/openmeter/ent/db/migrate/schema.go +++ b/openmeter/ent/db/migrate/schema.go @@ -5759,6 +5759,7 @@ func init() { ChargeCreditPurchasesTable.ForeignKeys[5].RefTable = TaxCodesTable ChargeCreditPurchasesTable.Annotation = &entsql.Annotation{} ChargeCreditPurchasesTable.Annotation.Checks = map[string]string{ + "currency_not_empty": "currency IS NULL OR currency <> ''", "currency_reference": "(currency IS NULL) <> (custom_currency_id IS NULL)", } ChargeCreditPurchaseCreditGrantsTable.ForeignKeys[0].RefTable = ChargeCreditPurchasesTable @@ -5775,6 +5776,7 @@ func init() { ChargeFlatFeesTable.ForeignKeys[7].RefTable = TaxCodesTable ChargeFlatFeesTable.Annotation = &entsql.Annotation{} ChargeFlatFeesTable.Annotation.Checks = map[string]string{ + "currency_not_empty": "currency IS NULL OR currency <> ''", "currency_reference": "(currency IS NULL) <> (custom_currency_id IS NULL)", } ChargeFlatFeeOverridesTable.ForeignKeys[0].RefTable = ChargeFlatFeesTable @@ -5805,6 +5807,7 @@ func init() { Table: "charge_usage_based", } ChargeUsageBasedTable.Annotation.Checks = map[string]string{ + "currency_not_empty": "currency IS NULL OR currency <> ''", "currency_reference": "(currency IS NULL) <> (custom_currency_id IS NULL)", } ChargeUsageBasedOverridesTable.ForeignKeys[0].RefTable = ChargeUsageBasedTable diff --git a/openmeter/ent/schema/charges.go b/openmeter/ent/schema/charges.go index f2929bd4d9..e3e3ec9b8c 100644 --- a/openmeter/ent/schema/charges.go +++ b/openmeter/ent/schema/charges.go @@ -294,6 +294,7 @@ func (chargesMetaMixin) Annotations() []schema.Annotation { return []schema.Annotation{ entsql.Checks(map[string]string{ "currency_reference": `(currency IS NULL) <> (custom_currency_id IS NULL)`, + "currency_not_empty": `currency IS NULL OR currency <> ''`, }), } } diff --git a/pkg/currencyx/code.go b/pkg/currencyx/code.go index 0425c5ce2c..da261a08bb 100644 --- a/pkg/currencyx/code.go +++ b/pkg/currencyx/code.go @@ -34,7 +34,7 @@ func (c Code) Equal(other Code) bool { } func (c Code) Type() CurrencyType { - if len(c) <= 3 { + if len(c) == 3 { return CurrencyTypeFiat } @@ -58,7 +58,7 @@ func (c Code) Validate() error { return models.NewNillableGenericValidationError(errors.Join(errs...)) } - if c.IsFiat() { + if len(c) == 3 { if err := validateFiatCurrencyCode(c); err != nil { errs = append(errs, err) } diff --git a/pkg/currencyx/code_test.go b/pkg/currencyx/code_test.go index 7f1e5a8f0d..a943effc6b 100644 --- a/pkg/currencyx/code_test.go +++ b/pkg/currencyx/code_test.go @@ -43,9 +43,9 @@ func TestCodeValidate(t *testing.T) { code: currencyx.Code(strings.Repeat("A", currencyx.CustomCurrencyCodeMaxLength)), }, { - name: "invalid short fiat code", + name: "custom code too short", code: "AB", - expectedError: "invalid fiat currency code", + expectedError: "between 4 and 24 characters", }, { name: "custom code too long", @@ -126,16 +126,6 @@ func TestCodeType(t *testing.T) { code currencyx.Code expected currencyx.CurrencyType }{ - { - name: "one-character fiat classification", - code: "C", - expected: currencyx.CurrencyTypeFiat, - }, - { - name: "two-character fiat classification", - code: "CR", - expected: currencyx.CurrencyTypeFiat, - }, { name: "fiat", code: "USD", diff --git a/tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql b/tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql index 1888274a9b..4485542ea0 100644 --- a/tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql +++ b/tools/migrate/migrations/20260720093016_charges-custom-currencies.down.sql @@ -1,6 +1,10 @@ +-- reverse: recreate charges_search_v1s view without custom_currency_id +DROP VIEW IF EXISTS "charges_search_v1s"; -- reverse: modify "charge_usage_based" table -ALTER TABLE "charge_usage_based" DROP CONSTRAINT "charge_usage_based_custom_currencies_charges_usage_based", DROP COLUMN "custom_currency_id", ALTER COLUMN "currency" SET NOT NULL, DROP CONSTRAINT "currency_reference"; +ALTER TABLE "charge_usage_based" DROP CONSTRAINT "charge_usage_based_custom_currencies_charges_usage_based", DROP CONSTRAINT "currency_reference", DROP CONSTRAINT "currency_not_empty", DROP COLUMN "custom_currency_id", ALTER COLUMN "currency" SET NOT NULL; -- reverse: modify "charge_flat_fees" table -ALTER TABLE "charge_flat_fees" DROP CONSTRAINT "charge_flat_fees_custom_currencies_charges_flat_fee", DROP COLUMN "custom_currency_id", ALTER COLUMN "currency" SET NOT NULL, DROP CONSTRAINT "currency_reference"; +ALTER TABLE "charge_flat_fees" DROP CONSTRAINT "charge_flat_fees_custom_currencies_charges_flat_fee", DROP CONSTRAINT "currency_reference", DROP CONSTRAINT "currency_not_empty", DROP COLUMN "custom_currency_id", ALTER COLUMN "currency" SET NOT NULL; -- reverse: modify "charge_credit_purchases" table -ALTER TABLE "charge_credit_purchases" DROP CONSTRAINT "charge_credit_purchases_custom_currencies_charges_credit_purcha", DROP COLUMN "custom_currency_id", ALTER COLUMN "currency" SET NOT NULL, DROP CONSTRAINT "currency_reference"; +ALTER TABLE "charge_credit_purchases" DROP CONSTRAINT "charge_credit_purchases_custom_currencies_charges_credit_purcha", DROP CONSTRAINT "currency_reference", DROP CONSTRAINT "currency_not_empty", DROP COLUMN "custom_currency_id", ALTER COLUMN "currency" SET NOT NULL; +CREATE VIEW "charges_search_v1s" AS +SELECT "id", "namespace", "metadata", "created_at", "updated_at", "deleted_at", "name", "description", "annotations", "customer_id", "service_period_from", "service_period_to", "billing_period_from", "billing_period_to", "full_service_period_from", "full_service_period_to", "status", "unique_reference_id", "currency", "managed_by", "subscription_id", "subscription_phase_id", "subscription_item_id", "advance_after", "tax_code_id", "tax_behavior", NULL::timestamptz AS "base_intent_deleted_at", 'credit_purchase' AS "type" FROM "charge_credit_purchases" UNION ALL SELECT "id", "namespace", "metadata", "created_at", "updated_at", "deleted_at", "name", "description", "annotations", "customer_id", "service_period_from", "service_period_to", "billing_period_from", "billing_period_to", "full_service_period_from", "full_service_period_to", "status", "unique_reference_id", "currency", "managed_by", "subscription_id", "subscription_phase_id", "subscription_item_id", "advance_after", "tax_code_id", "tax_behavior", "intent_deleted_at" AS "base_intent_deleted_at", 'flat_fee' AS "type" FROM "charge_flat_fees" UNION ALL SELECT "id", "namespace", "metadata", "created_at", "updated_at", "deleted_at", "name", "description", "annotations", "customer_id", "service_period_from", "service_period_to", "billing_period_from", "billing_period_to", "full_service_period_from", "full_service_period_to", "status", "unique_reference_id", "currency", "managed_by", "subscription_id", "subscription_phase_id", "subscription_item_id", "advance_after", "tax_code_id", "tax_behavior", "intent_deleted_at" AS "base_intent_deleted_at", 'usage_based' AS "type" FROM "charge_usage_based"; diff --git a/tools/migrate/migrations/20260720093016_charges-custom-currencies.up.sql b/tools/migrate/migrations/20260720093016_charges-custom-currencies.up.sql index 52346eb8f7..848bc078b8 100644 --- a/tools/migrate/migrations/20260720093016_charges-custom-currencies.up.sql +++ b/tools/migrate/migrations/20260720093016_charges-custom-currencies.up.sql @@ -1,8 +1,11 @@ -- modify "charge_credit_purchases" table -ALTER TABLE "charge_credit_purchases" ADD CONSTRAINT "currency_reference" CHECK ((currency IS NULL) <> (custom_currency_id IS NULL)), ALTER COLUMN "currency" DROP NOT NULL, ADD COLUMN "custom_currency_id" character(26) NULL, ADD CONSTRAINT "charge_credit_purchases_custom_currencies_charges_credit_purcha" FOREIGN KEY ("custom_currency_id") REFERENCES "custom_currencies" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT; +ALTER TABLE "charge_credit_purchases" ADD CONSTRAINT "currency_reference" CHECK ((currency IS NULL) <> (custom_currency_id IS NULL)), ADD CONSTRAINT "currency_not_empty" CHECK (currency IS NULL OR currency <> ''), ALTER COLUMN "currency" DROP NOT NULL, ADD COLUMN "custom_currency_id" character(26) NULL, ADD CONSTRAINT "charge_credit_purchases_custom_currencies_charges_credit_purcha" FOREIGN KEY ("custom_currency_id") REFERENCES "custom_currencies" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT; -- modify "charge_flat_fees" table -ALTER TABLE "charge_flat_fees" ADD CONSTRAINT "currency_reference" CHECK ((currency IS NULL) <> (custom_currency_id IS NULL)), ALTER COLUMN "currency" DROP NOT NULL, ADD COLUMN "custom_currency_id" character(26) NULL, ADD CONSTRAINT "charge_flat_fees_custom_currencies_charges_flat_fee" FOREIGN KEY ("custom_currency_id") REFERENCES "custom_currencies" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT; +ALTER TABLE "charge_flat_fees" ADD CONSTRAINT "currency_reference" CHECK ((currency IS NULL) <> (custom_currency_id IS NULL)), ADD CONSTRAINT "currency_not_empty" CHECK (currency IS NULL OR currency <> ''), ALTER COLUMN "currency" DROP NOT NULL, ADD COLUMN "custom_currency_id" character(26) NULL, ADD CONSTRAINT "charge_flat_fees_custom_currencies_charges_flat_fee" FOREIGN KEY ("custom_currency_id") REFERENCES "custom_currencies" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT; -- modify "charge_usage_based" table -ALTER TABLE "charge_usage_based" ADD CONSTRAINT "currency_reference" CHECK ((currency IS NULL) <> (custom_currency_id IS NULL)), ALTER COLUMN "currency" DROP NOT NULL, ADD COLUMN "custom_currency_id" character(26) NULL, ADD CONSTRAINT "charge_usage_based_custom_currencies_charges_usage_based" FOREIGN KEY ("custom_currency_id") REFERENCES "custom_currencies" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT; +ALTER TABLE "charge_usage_based" ADD CONSTRAINT "currency_reference" CHECK ((currency IS NULL) <> (custom_currency_id IS NULL)), ADD CONSTRAINT "currency_not_empty" CHECK (currency IS NULL OR currency <> ''), ALTER COLUMN "currency" DROP NOT NULL, ADD COLUMN "custom_currency_id" character(26) NULL, ADD CONSTRAINT "charge_usage_based_custom_currencies_charges_usage_based" FOREIGN KEY ("custom_currency_id") REFERENCES "custom_currencies" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT; --- TODO: Add empty checks +-- recreate charges_search_v1s view to include custom_currency_id +DROP VIEW IF EXISTS "charges_search_v1s"; +CREATE VIEW "charges_search_v1s" AS +SELECT "id", "namespace", "metadata", "created_at", "updated_at", "deleted_at", "name", "description", "annotations", "customer_id", "service_period_from", "service_period_to", "billing_period_from", "billing_period_to", "full_service_period_from", "full_service_period_to", "status", "unique_reference_id", "currency", "custom_currency_id", "managed_by", "subscription_id", "subscription_phase_id", "subscription_item_id", "advance_after", "tax_code_id", "tax_behavior", NULL::timestamptz AS "base_intent_deleted_at", 'credit_purchase' AS "type" FROM "charge_credit_purchases" UNION ALL SELECT "id", "namespace", "metadata", "created_at", "updated_at", "deleted_at", "name", "description", "annotations", "customer_id", "service_period_from", "service_period_to", "billing_period_from", "billing_period_to", "full_service_period_from", "full_service_period_to", "status", "unique_reference_id", "currency", "custom_currency_id", "managed_by", "subscription_id", "subscription_phase_id", "subscription_item_id", "advance_after", "tax_code_id", "tax_behavior", "intent_deleted_at" AS "base_intent_deleted_at", 'flat_fee' AS "type" FROM "charge_flat_fees" UNION ALL SELECT "id", "namespace", "metadata", "created_at", "updated_at", "deleted_at", "name", "description", "annotations", "customer_id", "service_period_from", "service_period_to", "billing_period_from", "billing_period_to", "full_service_period_from", "full_service_period_to", "status", "unique_reference_id", "currency", "custom_currency_id", "managed_by", "subscription_id", "subscription_phase_id", "subscription_item_id", "advance_after", "tax_code_id", "tax_behavior", "intent_deleted_at" AS "base_intent_deleted_at", 'usage_based' AS "type" FROM "charge_usage_based"; diff --git a/tools/migrate/migrations/atlas.sum b/tools/migrate/migrations/atlas.sum index b5804d8b1a..982501c0ae 100644 --- a/tools/migrate/migrations/atlas.sum +++ b/tools/migrate/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:GsZiySv+ZGohiTpP5HwaXd4KTBcMZsMOgujJRwI2kho= +h1:f00I9PeE3w9+bBTdFVHzvUpjrFB+Wy4/OTrh7nAtjbk= 20240826120919_init.up.sql h1:tc1V91/smlmaeJGQ8h+MzTEeFjjnrrFDbDAjOYJK91o= 20240903155435_entitlement-expired-index.up.sql h1:Hp8u5uckmLXc1cRvWU0AtVnnK8ShlpzZNp8pbiJLhac= 20240917172257_billing-entities.up.sql h1:Q1dAMo0Vjiit76OybClNfYPGC5nmvov2/M2W1ioi4Kw= @@ -239,4 +239,4 @@ h1:GsZiySv+ZGohiTpP5HwaXd4KTBcMZsMOgujJRwI2kho= 20260716152332_rescope-credit-grant-key-to-customer.up.sql h1:gbeATYlchOi0pwaTUFX+nrbLoS0jZbVDMHn8uMTck0c= 20260717060208_add_bulk_invoice_schema_level_2_migration_function.up.sql h1:kJCvXyNuRC9FMysGg9c6nesUXsfbkNq/RKfTIwnQzEw= 20260717160017_custom_currencies.up.sql h1:HHScXLfjVxumxzNrw/RdxZZZMo3EnCE5Min67fEX9jo= -20260720093016_charges-custom-currencies.up.sql h1:hiascJhglspAGXnm5UY+25dfc0HnDE+GVfMxbvxQ6ok= +20260720093016_charges-custom-currencies.up.sql h1:U9K4UDDHwNCCqcAQ3n9HFTXaibsm66KPebSa/7XB2OE= From 723e57fe753809ced7aae45c14091da4926d64f5 Mon Sep 17 00:00:00 2001 From: Peter Turi Date: Tue, 21 Jul 2026 16:28:43 +0200 Subject: [PATCH 3/3] fix: address custom currency review feedback --- .../charges/creditpurchase/adapter/charge.go | 19 +++----- .../adapter/funded_credit_activity.go | 9 +--- .../charges/creditpurchase/adapter/mapper.go | 24 +++++----- .../creditpurchase/adapter/predicate.go | 16 +++++++ .../billing/charges/flatfee/adapter/charge.go | 10 ++-- .../charges/flatfee/adapter/intentoverride.go | 12 ++--- .../billing/charges/flatfee/adapter/mapper.go | 46 +++++++++---------- .../charges/flatfee/adapter/realizationrun.go | 4 +- openmeter/billing/charges/meta/intent.go | 2 +- .../charges/models/chargemeta/model.go | 8 ++-- .../charges/usagebased/adapter/charge.go | 10 ++-- .../usagebased/adapter/detailedline.go | 4 +- .../usagebased/adapter/intentoverride.go | 12 ++--- .../charges/usagebased/adapter/mapper.go | 39 ++++++++-------- .../usagebased/adapter/realizationrun.go | 4 +- openmeter/currencies/adapter/currencies.go | 23 ++++++---- openmeter/currencies/currency.go | 10 +++- 17 files changed, 133 insertions(+), 119 deletions(-) create mode 100644 openmeter/billing/charges/creditpurchase/adapter/predicate.go diff --git a/openmeter/billing/charges/creditpurchase/adapter/charge.go b/openmeter/billing/charges/creditpurchase/adapter/charge.go index 8188e7cc7e..53e1a7b1c9 100644 --- a/openmeter/billing/charges/creditpurchase/adapter/charge.go +++ b/openmeter/billing/charges/creditpurchase/adapter/charge.go @@ -12,7 +12,6 @@ import ( "github.com/openmeterio/openmeter/openmeter/billing/charges/models/chargemeta" "github.com/openmeterio/openmeter/openmeter/ent/db" dbchargecreditpurchase "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchase" - dbcustomcurrency "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/pkg/filter" "github.com/openmeterio/openmeter/pkg/framework/entutils" "github.com/openmeterio/openmeter/pkg/pagination" @@ -53,7 +52,7 @@ func (a *adapter) UpdateCharge(ctx context.Context, charge creditpurchase.Charge return creditpurchase.ChargeBase{}, err } - return MapChargeBaseFromDB(dbCreditPurchase, charge.Intent.Currency) + return fromDBBaseWithCurrency(dbCreditPurchase, charge.Intent.Currency) }) } @@ -110,7 +109,7 @@ func (a *adapter) CreateCharge(ctx context.Context, in creditpurchase.CreateInpu return creditpurchase.Charge{}, err } - return FromDBChargeCreditPurchaseWithCurrency(dbCreditPurchase, in.Intent.Currency, meta.ExpandNone) + return FromDBWithCurrency(dbCreditPurchase, in.Intent.Currency, meta.ExpandNone) }) } @@ -128,7 +127,7 @@ func (a *adapter) MarkVoided(ctx context.Context, input creditpurchase.MarkVoide return creditpurchase.ChargeBase{}, fmt.Errorf("marking credit purchase charge voided [id=%s]: %w", input.Charge.ID, err) } - return MapChargeBaseFromDB(dbCreditPurchase, input.Charge.Intent.Currency) + return fromDBBaseWithCurrency(dbCreditPurchase, input.Charge.Intent.Currency) }) } @@ -151,7 +150,7 @@ func (a *adapter) GetByID(ctx context.Context, input creditpurchase.GetByIDInput return creditpurchase.Charge{}, fmt.Errorf("getting credit purchase charge [id=%s]: %w", input.ChargeID.ID, err) } - return MapCreditPurchaseChargeFromDB(entity, input.Expands) + return FromDB(entity, input.Expands) }) } @@ -178,7 +177,7 @@ func (a *adapter) GetByIDs(ctx context.Context, input creditpurchase.GetByIDsInp } return slicesx.MapWithErr(entitiesInOrder, func(entity *db.ChargeCreditPurchase) (creditpurchase.Charge, error) { - return MapCreditPurchaseChargeFromDB(entity, input.Expands) + return FromDB(entity, input.Expands) }) }) } @@ -208,11 +207,7 @@ func (a *adapter) ListCharges(ctx context.Context, input creditpurchase.ListChar query = query.Where( dbchargecreditpurchase.Or( dbchargecreditpurchase.FiatCurrencyCodeIn(input.Currencies...), - dbchargecreditpurchase.HasCustomCurrencyWith( - dbcustomcurrency.CodeIn(input.Currencies...), - dbcustomcurrency.Namespace(input.Namespace), - dbcustomcurrency.DeletedAtIsNil(), - ), + hasCustomCurrencyCode(input.Namespace, input.Currencies...), ), ) } @@ -246,7 +241,7 @@ func (a *adapter) ListCharges(ctx context.Context, input creditpurchase.ListChar } charges, err := slicesx.MapWithErr(res.Items, func(entity *db.ChargeCreditPurchase) (creditpurchase.Charge, error) { - return MapCreditPurchaseChargeFromDB(entity, input.Expands) + return FromDB(entity, input.Expands) }) if err != nil { return pagination.Result[creditpurchase.Charge]{}, err diff --git a/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go b/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go index 040e592f7e..516fdbeccc 100644 --- a/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go +++ b/openmeter/billing/charges/creditpurchase/adapter/funded_credit_activity.go @@ -15,7 +15,6 @@ import ( "github.com/openmeterio/openmeter/openmeter/ent/db" dbchargecreditpurchase "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchase" dbchargecreditpurchasecreditgrant "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchasecreditgrant" - dbcustomcurrency "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" "github.com/openmeterio/openmeter/pkg/framework/entutils/entedge" ) @@ -66,11 +65,7 @@ func ListFundedCreditActivities(ctx context.Context, dbClient *db.Client, input if input.Currency != nil { query = query.Where(dbchargecreditpurchasecreditgrant.HasCreditPurchaseWith( dbchargecreditpurchase.Or( - dbchargecreditpurchase.HasCustomCurrencyWith( - dbcustomcurrency.CodeEQ(*input.Currency), - dbcustomcurrency.Namespace(input.Customer.Namespace), - dbcustomcurrency.DeletedAtIsNil(), - ), + hasCustomCurrencyCode(input.Customer.Namespace, *input.Currency), dbchargecreditpurchase.FiatCurrencyCodeEQ(*input.Currency), ), )) @@ -110,7 +105,7 @@ func ListFundedCreditActivities(ctx context.Context, dbClient *db.Client, input return creditpurchase.ListFundedCreditActivitiesResult{}, fmt.Errorf("failed to get custom currency: %w", err) } - resolvedCurrency, err := currenciesadapter.MapCustomCurrencyOrFiatCurrencyFromDB(currenciesadapter.CustomCurrencyOrFiatCurrency{ + resolvedCurrency, err := currenciesadapter.FromDBCustomCurrencyOrFiatCurrency(currenciesadapter.CustomCurrencyOrFiatCurrency{ CustomCurrency: dbCustomCurrency, FiatCurrency: creditPurchase.FiatCurrencyCode, }) diff --git a/openmeter/billing/charges/creditpurchase/adapter/mapper.go b/openmeter/billing/charges/creditpurchase/adapter/mapper.go index 101c99a57e..8d4a86df41 100644 --- a/openmeter/billing/charges/creditpurchase/adapter/mapper.go +++ b/openmeter/billing/charges/creditpurchase/adapter/mapper.go @@ -16,16 +16,16 @@ import ( "github.com/openmeterio/openmeter/pkg/convert" ) -func MapChargeBaseFromDB(dbEntity *entdb.ChargeCreditPurchase, currency currencies.Currency) (creditpurchase.ChargeBase, error) { - mappedMeta, err := chargemeta.FromDBCharge(dbEntity, currency) +func fromDBBaseWithCurrency(dbEntity *entdb.ChargeCreditPurchase, currency currencies.Currency) (creditpurchase.ChargeBase, error) { + mappedMeta, err := chargemeta.FromDBWithCurrency(dbEntity, currency) if err != nil { return creditpurchase.ChargeBase{}, fmt.Errorf("failed to map charge base: %w", err) } - return mapChargeBaseFromDB(dbEntity, mappedMeta), nil + return fromDBBase(dbEntity, mappedMeta), nil } -func mapChargeBaseFromDB(dbEntity *entdb.ChargeCreditPurchase, mappedMeta meta.Charge) creditpurchase.ChargeBase { +func fromDBBase(dbEntity *entdb.ChargeCreditPurchase, mappedMeta meta.Charge) creditpurchase.ChargeBase { return creditpurchase.ChargeBase{ ManagedResource: mappedMeta.ManagedResource, Status: dbEntity.StatusDetailed, @@ -48,26 +48,26 @@ func mapChargeBaseFromDB(dbEntity *entdb.ChargeCreditPurchase, mappedMeta meta.C } } -func MapCreditPurchaseChargeFromDB(dbEntity *entdb.ChargeCreditPurchase, expands meta.Expands) (creditpurchase.Charge, error) { - mappedMeta, err := chargemeta.FromDBChargeWithCurrencyEdge(dbEntity, dbEntity.Edges) +func FromDB(dbEntity *entdb.ChargeCreditPurchase, expands meta.Expands) (creditpurchase.Charge, error) { + mappedMeta, err := chargemeta.FromDB(dbEntity, dbEntity.Edges) if err != nil { return creditpurchase.Charge{}, fmt.Errorf("failed to map charge meta: %w", err) } - return mapCreditPurchaseChargeFromDB(dbEntity, mappedMeta, expands) + return fromDBWithMeta(dbEntity, mappedMeta, expands) } -func FromDBChargeCreditPurchaseWithCurrency(dbEntity *entdb.ChargeCreditPurchase, currency currencies.Currency, expands meta.Expands) (creditpurchase.Charge, error) { - mappedMeta, err := chargemeta.FromDBCharge(dbEntity, currency) +func FromDBWithCurrency(dbEntity *entdb.ChargeCreditPurchase, currency currencies.Currency, expands meta.Expands) (creditpurchase.Charge, error) { + mappedMeta, err := chargemeta.FromDBWithCurrency(dbEntity, currency) if err != nil { return creditpurchase.Charge{}, fmt.Errorf("failed to map charge meta: %w", err) } - return mapCreditPurchaseChargeFromDB(dbEntity, mappedMeta, expands) + return fromDBWithMeta(dbEntity, mappedMeta, expands) } -func mapCreditPurchaseChargeFromDB(dbEntity *entdb.ChargeCreditPurchase, mappedMeta meta.Charge, expands meta.Expands) (creditpurchase.Charge, error) { - chargeBase := mapChargeBaseFromDB(dbEntity, mappedMeta) +func fromDBWithMeta(dbEntity *entdb.ChargeCreditPurchase, mappedMeta meta.Charge, expands meta.Expands) (creditpurchase.Charge, error) { + chargeBase := fromDBBase(dbEntity, mappedMeta) var creditGrantRealization *ledgertransaction.TimedGroupReference var externalPaymentSettlement *payment.External diff --git a/openmeter/billing/charges/creditpurchase/adapter/predicate.go b/openmeter/billing/charges/creditpurchase/adapter/predicate.go new file mode 100644 index 0000000000..09c93b40d2 --- /dev/null +++ b/openmeter/billing/charges/creditpurchase/adapter/predicate.go @@ -0,0 +1,16 @@ +package adapter + +import ( + dbchargecreditpurchase "github.com/openmeterio/openmeter/openmeter/ent/db/chargecreditpurchase" + dbcustomcurrency "github.com/openmeterio/openmeter/openmeter/ent/db/customcurrency" + "github.com/openmeterio/openmeter/openmeter/ent/db/predicate" + "github.com/openmeterio/openmeter/pkg/currencyx" +) + +func hasCustomCurrencyCode(namespace string, codes ...currencyx.Code) predicate.ChargeCreditPurchase { + return dbchargecreditpurchase.HasCustomCurrencyWith( + dbcustomcurrency.CodeIn(codes...), + dbcustomcurrency.Namespace(namespace), + dbcustomcurrency.DeletedAtIsNil(), + ) +} diff --git a/openmeter/billing/charges/flatfee/adapter/charge.go b/openmeter/billing/charges/flatfee/adapter/charge.go index 830e410762..b4a3ee5cfb 100644 --- a/openmeter/billing/charges/flatfee/adapter/charge.go +++ b/openmeter/billing/charges/flatfee/adapter/charge.go @@ -95,7 +95,7 @@ func (a *adapter) UpdateCharge(ctx context.Context, charge flatfee.ChargeBase) ( dbUpdatedChargeBase.Edges.IntentOverride = intentOverride } - return MapChargeBaseFromDB(dbUpdatedChargeBase, intent.Currency) + return fromDBBaseWithCurrency(dbUpdatedChargeBase, intent.Currency) }) } @@ -132,7 +132,7 @@ func (a *adapter) UpdateSubscriptionItemID(ctx context.Context, charge flatfee.C } updatedChargeBase.Edges.IntentOverride = override - mappedChargeBase, err := MapChargeBaseFromDB(updatedChargeBase, charge.Intent.GetBaseIntent().Currency) + mappedChargeBase, err := fromDBBaseWithCurrency(updatedChargeBase, charge.Intent.GetBaseIntent().Currency) if err != nil { return flatfee.Charge{}, err } @@ -236,7 +236,7 @@ func (a *adapter) CreateCharges(ctx context.Context, in flatfee.CreateChargesInp } return lo.MapErr(entities, func(entity *db.ChargeFlatFee, idx int) (flatfee.Charge, error) { - return FromDBChargeFlatFeeWithCurrency(entity, in.Intents[idx].Intent.Currency, meta.ExpandNone) + return FromDBWithCurrency(entity, in.Intents[idx].Intent.Currency, meta.ExpandNone) }) }) } @@ -268,7 +268,7 @@ func (a *adapter) GetByIDs(ctx context.Context, input flatfee.GetByIDsInput) ([] } out, err := slicesx.MapWithErr(entitiesInOrder, func(entity *db.ChargeFlatFee) (flatfee.Charge, error) { - return MapChargeFlatFeeFromDB(entity, input.Expands) + return FromDB(entity, input.Expands) }) if err != nil { return nil, err @@ -309,7 +309,7 @@ func (a *adapter) GetByID(ctx context.Context, input flatfee.GetByIDInput) (flat return flatfee.Charge{}, fmt.Errorf("querying flat fee charge [id=%s]: %w", input.ChargeID, err) } - charge, err := MapChargeFlatFeeFromDB(entity, input.Expands) + charge, err := FromDB(entity, input.Expands) if err != nil { return flatfee.Charge{}, err } diff --git a/openmeter/billing/charges/flatfee/adapter/intentoverride.go b/openmeter/billing/charges/flatfee/adapter/intentoverride.go index 84dfa5be50..239ebb89ee 100644 --- a/openmeter/billing/charges/flatfee/adapter/intentoverride.go +++ b/openmeter/billing/charges/flatfee/adapter/intentoverride.go @@ -20,7 +20,7 @@ import ( "github.com/openmeterio/openmeter/pkg/timeutil" ) -func mapIntentOverrideFromDB(dbOverride *entdb.ChargeFlatFeeOverride) *flatfee.IntentMutableFields { +func fromDBOverride(dbOverride *entdb.ChargeFlatFeeOverride) *flatfee.IntentMutableFields { if dbOverride == nil { return nil } @@ -35,9 +35,9 @@ func mapIntentOverrideFromDB(dbOverride *entdb.ChargeFlatFeeOverride) *flatfee.I Name: dbOverride.Name, Description: dbOverride.Description, Metadata: lo.FromPtr(dbOverride.Metadata), - ServicePeriod: closedPeriodFromDB(dbOverride.ServicePeriodFrom, dbOverride.ServicePeriodTo), - FullServicePeriod: closedPeriodFromDB(dbOverride.FullServicePeriodFrom, dbOverride.FullServicePeriodTo), - BillingPeriod: closedPeriodFromDB(dbOverride.BillingPeriodFrom, dbOverride.BillingPeriodTo), + ServicePeriod: fromDBClosedPeriod(dbOverride.ServicePeriodFrom, dbOverride.ServicePeriodTo), + FullServicePeriod: fromDBClosedPeriod(dbOverride.FullServicePeriodFrom, dbOverride.FullServicePeriodTo), + BillingPeriod: fromDBClosedPeriod(dbOverride.BillingPeriodFrom, dbOverride.BillingPeriodTo), }, IntentDeletedAt: convert.TimePtrIn(dbOverride.IntentDeletedAt, time.UTC), InvoiceAt: dbOverride.InvoiceAt.UTC(), @@ -84,7 +84,7 @@ func (a *adapter) CreateChargeOverride(ctx context.Context, charge flatfee.Charg dbCharge.Edges.IntentOverride = dbIntentOverride - return MapChargeBaseFromDB(dbCharge, charge.Intent.GetBaseIntent().Currency) + return fromDBBaseWithCurrency(dbCharge, charge.Intent.GetBaseIntent().Currency) }) } @@ -225,7 +225,7 @@ func (a *adapter) updateIntentOverride(ctx context.Context, chargeID meta.Charge return dbOverride, nil } -func closedPeriodFromDB(from, to time.Time) timeutil.ClosedPeriod { +func fromDBClosedPeriod(from, to time.Time) timeutil.ClosedPeriod { return timeutil.ClosedPeriod{ From: from.UTC(), To: to.UTC(), diff --git a/openmeter/billing/charges/flatfee/adapter/mapper.go b/openmeter/billing/charges/flatfee/adapter/mapper.go index f690c78f78..2a838f1672 100644 --- a/openmeter/billing/charges/flatfee/adapter/mapper.go +++ b/openmeter/billing/charges/flatfee/adapter/mapper.go @@ -24,32 +24,32 @@ import ( "github.com/openmeterio/openmeter/pkg/timeutil" ) -// MapFlatFeeChargeFromDB converts a DB Charge entity (with loaded FlatFee edge) to a FlatFeeCharge. -func MapChargeFlatFeeFromDB(entity *entdb.ChargeFlatFee, expands meta.Expands) (flatfee.Charge, error) { - mappedMeta, err := chargemeta.FromDBChargeWithCurrencyEdge(entity, entity.Edges) +// FromDB converts a DB ChargeFlatFee entity to a flat fee charge. +func FromDB(entity *entdb.ChargeFlatFee, expands meta.Expands) (flatfee.Charge, error) { + mappedMeta, err := chargemeta.FromDB(entity, entity.Edges) if err != nil { return flatfee.Charge{}, fmt.Errorf("mapping flat fee charge meta [id=%s]: %w", entity.ID, err) } - return mapChargeFlatFeeFromDB(entity, mappedMeta, expands) + return fromDBWithMeta(entity, mappedMeta, expands) } -func FromDBChargeFlatFeeWithCurrency(entity *entdb.ChargeFlatFee, currency currencies.Currency, expands meta.Expands) (flatfee.Charge, error) { - mappedMeta, err := chargemeta.FromDBCharge(entity, currency) +func FromDBWithCurrency(entity *entdb.ChargeFlatFee, currency currencies.Currency, expands meta.Expands) (flatfee.Charge, error) { + mappedMeta, err := chargemeta.FromDBWithCurrency(entity, currency) if err != nil { return flatfee.Charge{}, fmt.Errorf("mapping flat fee charge meta [id=%s]: %w", entity.ID, err) } - return mapChargeFlatFeeFromDB(entity, mappedMeta, expands) + return fromDBWithMeta(entity, mappedMeta, expands) } -func mapChargeFlatFeeFromDB(entity *entdb.ChargeFlatFee, mappedMeta meta.Charge, expands meta.Expands) (flatfee.Charge, error) { +func fromDBWithMeta(entity *entdb.ChargeFlatFee, mappedMeta meta.Charge, expands meta.Expands) (flatfee.Charge, error) { charge := flatfee.Charge{ - ChargeBase: mapChargeBaseFromDB(entity, mappedMeta), + ChargeBase: fromDBBase(entity, mappedMeta), } if expands.Has(meta.ExpandRealizations) { - realizations, err := mapRealizationsFromDB(entity) + realizations, err := fromDBRealizations(entity) if err != nil { return flatfee.Charge{}, fmt.Errorf("mapping flat fee charge [id=%s]: %w", entity.ID, err) } @@ -59,7 +59,7 @@ func mapChargeFlatFeeFromDB(entity *entdb.ChargeFlatFee, mappedMeta meta.Charge, return charge, nil } -func mapRealizationsFromDB(entity *entdb.ChargeFlatFee) (flatfee.Realizations, error) { +func fromDBRealizations(entity *entdb.ChargeFlatFee) (flatfee.Realizations, error) { dbRuns, err := entity.Edges.RunsOrErr() if err != nil { return flatfee.Realizations{}, fmt.Errorf("runs not loaded for flat fee charge [id=%s]: %w", entity.ID, err) @@ -67,7 +67,7 @@ func mapRealizationsFromDB(entity *entdb.ChargeFlatFee) (flatfee.Realizations, e var realizations flatfee.Realizations for _, dbRun := range dbRuns { - run, err := mapRealizationRunFromDB(dbRun) + run, err := fromDBRun(dbRun) if err != nil { return flatfee.Realizations{}, fmt.Errorf("mapping flat fee realization run [id=%s]: %w", dbRun.ID, err) } @@ -87,7 +87,7 @@ func mapRealizationsFromDB(entity *entdb.ChargeFlatFee) (flatfee.Realizations, e return realizations, nil } -func mapRealizationRunBaseFromDB(dbRun *entdb.ChargeFlatFeeRun) flatfee.RealizationRunBase { +func fromDBRunBase(dbRun *entdb.ChargeFlatFeeRun) flatfee.RealizationRunBase { return flatfee.RealizationRunBase{ ID: flatfee.RealizationRunID{ Namespace: dbRun.Namespace, @@ -107,9 +107,9 @@ func mapRealizationRunBaseFromDB(dbRun *entdb.ChargeFlatFeeRun) flatfee.Realizat } } -func mapRealizationRunFromDB(dbRun *entdb.ChargeFlatFeeRun) (flatfee.RealizationRun, error) { +func fromDBRun(dbRun *entdb.ChargeFlatFeeRun) (flatfee.RealizationRun, error) { run := flatfee.RealizationRun{ - RealizationRunBase: mapRealizationRunBaseFromDB(dbRun), + RealizationRunBase: fromDBRunBase(dbRun), } dbCreditsAllocated, err := dbRun.Edges.CreditAllocationsOrErr() @@ -148,16 +148,16 @@ func sortDetailedLines(lines flatfee.DetailedLines) { slices.SortStableFunc(lines, stddetailedline.Compare[flatfee.DetailedLine]) } -func MapChargeBaseFromDB(entity *entdb.ChargeFlatFee, currency currencies.Currency) (flatfee.ChargeBase, error) { - mappedMeta, err := chargemeta.FromDBCharge(entity, currency) +func fromDBBaseWithCurrency(entity *entdb.ChargeFlatFee, currency currencies.Currency) (flatfee.ChargeBase, error) { + mappedMeta, err := chargemeta.FromDBWithCurrency(entity, currency) if err != nil { return flatfee.ChargeBase{}, fmt.Errorf("mapping charge meta: %w", err) } - return mapChargeBaseFromDB(entity, mappedMeta), nil + return fromDBBase(entity, mappedMeta), nil } -func mapChargeBaseFromDB(entity *entdb.ChargeFlatFee, mappedMeta meta.Charge) flatfee.ChargeBase { +func fromDBBase(entity *entdb.ChargeFlatFee, mappedMeta meta.Charge) flatfee.ChargeBase { var percentageDiscounts *billing.PercentageDiscount if entity.Discounts != nil { percentageDiscounts = entity.Discounts.Percentage @@ -181,15 +181,15 @@ func mapChargeBaseFromDB(entity *entdb.ChargeFlatFee, mappedMeta meta.Charge) fl IntentDeletedAt: convert.TimePtrIn(entity.IntentDeletedAt, time.UTC), PaymentTerm: entity.PaymentTerm, PercentageDiscounts: percentageDiscounts, - ProRating: proRatingConfigFromDB(entity.ProRating), + ProRating: fromDBProRatingConfig(entity.ProRating), AmountBeforeProration: entity.AmountBeforeProration, }, - }, mapIntentOverrideFromDB(entity.Edges.IntentOverride)), + }, fromDBOverride(entity.Edges.IntentOverride)), } } -// proRatingConfigFromDB converts a DB ProRatingModeAdapterEnum to a ProRatingConfig. -func proRatingConfigFromDB(pr flatfee.ProRatingModeAdapterEnum) productcatalog.ProRatingConfig { +// fromDBProRatingConfig converts a DB ProRatingModeAdapterEnum to a ProRatingConfig. +func fromDBProRatingConfig(pr flatfee.ProRatingModeAdapterEnum) productcatalog.ProRatingConfig { switch pr { case flatfee.ProratePricesProratingAdapterMode: return productcatalog.ProRatingConfig{ diff --git a/openmeter/billing/charges/flatfee/adapter/realizationrun.go b/openmeter/billing/charges/flatfee/adapter/realizationrun.go index aa27413f98..6260a62264 100644 --- a/openmeter/billing/charges/flatfee/adapter/realizationrun.go +++ b/openmeter/billing/charges/flatfee/adapter/realizationrun.go @@ -62,7 +62,7 @@ func (a *adapter) CreateCurrentRun(ctx context.Context, input flatfee.CreateCurr return flatfee.RealizationRunBase{}, fmt.Errorf("setting flat fee current run [charge_id=%s, run_id=%s]: %w", dbCharge.ID, dbRun.ID, err) } - return mapRealizationRunBaseFromDB(dbRun), nil + return fromDBRunBase(dbRun), nil }) } @@ -121,7 +121,7 @@ func (a *adapter) UpdateRealizationRun(ctx context.Context, input flatfee.Update return flatfee.RealizationRunBase{}, fmt.Errorf("updating flat fee realization run [run_id=%s]: %w", input.ID.ID, err) } - return mapRealizationRunBaseFromDB(dbRun), nil + return fromDBRunBase(dbRun), nil }) } diff --git a/openmeter/billing/charges/meta/intent.go b/openmeter/billing/charges/meta/intent.go index 943ca0ee74..64751f693a 100644 --- a/openmeter/billing/charges/meta/intent.go +++ b/openmeter/billing/charges/meta/intent.go @@ -21,7 +21,7 @@ type Intent struct { Annotations models.Annotations `json:"annotations"` - Currency currencies.Currency `json:"-"` + Currency currencies.Currency `json:"currency"` TaxConfig productcatalog.TaxCodeConfig `json:"taxConfig"` UniqueReferenceID *string `json:"childUniqueReferenceID"` diff --git a/openmeter/billing/charges/models/chargemeta/model.go b/openmeter/billing/charges/models/chargemeta/model.go index 63bfcad4c2..597dd1ade4 100644 --- a/openmeter/billing/charges/models/chargemeta/model.go +++ b/openmeter/billing/charges/models/chargemeta/model.go @@ -236,7 +236,7 @@ type EdgeGetter interface { CustomCurrencyOrErr() (*entdb.CustomCurrency, error) } -func FromDBChargeWithCurrencyEdge[T Getter[T]](entity T, edges EdgeGetter) (meta.Charge, error) { +func FromDB[T Getter[T]](entity T, edges EdgeGetter) (meta.Charge, error) { var dbCustomCurrency *entdb.CustomCurrency if entity.GetCustomCurrencyID() != nil { var err error @@ -246,7 +246,7 @@ func FromDBChargeWithCurrencyEdge[T Getter[T]](entity T, edges EdgeGetter) (meta } } - resolvedCurrency, err := currenciesadapter.MapCustomCurrencyOrFiatCurrencyFromDB(currenciesadapter.CustomCurrencyOrFiatCurrency{ + resolvedCurrency, err := currenciesadapter.FromDBCustomCurrencyOrFiatCurrency(currenciesadapter.CustomCurrencyOrFiatCurrency{ CustomCurrency: dbCustomCurrency, FiatCurrency: entity.GetFiatCurrencyCode(), }) @@ -254,10 +254,10 @@ func FromDBChargeWithCurrencyEdge[T Getter[T]](entity T, edges EdgeGetter) (meta return meta.Charge{}, fmt.Errorf("failed to resolve currency: %w", err) } - return FromDBCharge(entity, resolvedCurrency) + return FromDBWithCurrency(entity, resolvedCurrency) } -func FromDBCharge[T Getter[T]](entity T, currency currencies.Currency) (meta.Charge, error) { +func FromDBWithCurrency[T Getter[T]](entity T, currency currencies.Currency) (meta.Charge, error) { if err := currency.Validate(); err != nil { return meta.Charge{}, fmt.Errorf("currency: %w", err) } diff --git a/openmeter/billing/charges/usagebased/adapter/charge.go b/openmeter/billing/charges/usagebased/adapter/charge.go index 61bbd5ecef..1b2f4621f5 100644 --- a/openmeter/billing/charges/usagebased/adapter/charge.go +++ b/openmeter/billing/charges/usagebased/adapter/charge.go @@ -81,7 +81,7 @@ func (a *adapter) UpdateCharge(ctx context.Context, charge usagebased.ChargeBase dbUpdatedChargeBase.Edges.IntentOverride = intentOverride } - return MapChargeBaseFromDB(dbUpdatedChargeBase, baseIntent.Currency) + return fromDBBaseWithCurrency(dbUpdatedChargeBase, baseIntent.Currency) }) } @@ -110,7 +110,7 @@ func (a *adapter) UpdateSubscriptionItemID(ctx context.Context, charge usagebase } overrideLayer := charge.Intent.GetOverrideLayerMutableFields() - mappedChargeBase, err := MapChargeBaseFromDB(updatedChargeBase, charge.Intent.GetBaseIntent().Currency) + mappedChargeBase, err := fromDBBaseWithCurrency(updatedChargeBase, charge.Intent.GetBaseIntent().Currency) if err != nil { return usagebased.Charge{}, err } @@ -201,7 +201,7 @@ func (a *adapter) CreateCharges(ctx context.Context, in usagebased.CreateCharges } return lo.MapErr(entities, func(entity *db.ChargeUsageBased, idx int) (usagebased.Charge, error) { - return FromDBChargeUsageBasedWithCurrency(entity, in.Intents[idx].Intent.GetBaseIntent().Currency, meta.ExpandNone) + return FromDBWithCurrency(entity, in.Intents[idx].Intent.GetBaseIntent().Currency, meta.ExpandNone) }) }) } @@ -234,7 +234,7 @@ func (a *adapter) GetByIDs(ctx context.Context, input usagebased.GetByIDsInput) } out, err := slicesx.MapWithErr(entitiesInOrder, func(entity *db.ChargeUsageBased) (usagebased.Charge, error) { - return MapChargeFromDB(entity, input.Expands) + return FromDB(entity, input.Expands) }) if err != nil { return nil, err @@ -278,7 +278,7 @@ func (a *adapter) GetByID(ctx context.Context, input usagebased.GetByIDInput) (u return usagebased.Charge{}, fmt.Errorf("querying usage based charge [id=%s]: %w", input.ChargeID, err) } - charge, err := MapChargeFromDB(entity, input.Expands) + charge, err := FromDB(entity, input.Expands) if err != nil { return usagebased.Charge{}, err } diff --git a/openmeter/billing/charges/usagebased/adapter/detailedline.go b/openmeter/billing/charges/usagebased/adapter/detailedline.go index 878899a36b..4d3e6a346a 100644 --- a/openmeter/billing/charges/usagebased/adapter/detailedline.go +++ b/openmeter/billing/charges/usagebased/adapter/detailedline.go @@ -60,7 +60,7 @@ func (a *adapter) FetchDetailedLines(ctx context.Context, charge usagebased.Char linesByRunID := make(map[string]usagebased.DetailedLines, len(charge.Realizations)) for _, dbLine := range dbLines { - line, err := mapDetailedLineFromDB(dbLine) + line, err := fromDBDetailedLine(dbLine) if err != nil { return usagebased.Charge{}, err } @@ -215,7 +215,7 @@ func buildDetailedLineCreate(db *entdb.Client, chargeID chargesmeta.ChargeID, ru return create, nil } -func mapDetailedLineFromDB(dbLine *entdb.ChargeUsageBasedRunDetailedLine) (usagebased.DetailedLine, error) { +func fromDBDetailedLine(dbLine *entdb.ChargeUsageBasedRunDetailedLine) (usagebased.DetailedLine, error) { line := usagebased.DetailedLine{ Base: stddetailedline.FromDB(dbLine), PricerReferenceID: dbLine.PricerReferenceID, diff --git a/openmeter/billing/charges/usagebased/adapter/intentoverride.go b/openmeter/billing/charges/usagebased/adapter/intentoverride.go index 3464f28ea3..d6120d8cff 100644 --- a/openmeter/billing/charges/usagebased/adapter/intentoverride.go +++ b/openmeter/billing/charges/usagebased/adapter/intentoverride.go @@ -18,7 +18,7 @@ import ( "github.com/openmeterio/openmeter/pkg/timeutil" ) -func mapIntentOverrideFromDB(dbOverride *entdb.ChargeUsageBasedOverride) *usagebased.IntentMutableFields { +func fromDBOverride(dbOverride *entdb.ChargeUsageBasedOverride) *usagebased.IntentMutableFields { if dbOverride == nil { return nil } @@ -28,9 +28,9 @@ func mapIntentOverrideFromDB(dbOverride *entdb.ChargeUsageBasedOverride) *usageb Name: dbOverride.Name, Description: dbOverride.Description, Metadata: lo.FromPtr(dbOverride.Metadata), - ServicePeriod: closedPeriodFromDB(dbOverride.ServicePeriodFrom, dbOverride.ServicePeriodTo), - FullServicePeriod: closedPeriodFromDB(dbOverride.FullServicePeriodFrom, dbOverride.FullServicePeriodTo), - BillingPeriod: closedPeriodFromDB(dbOverride.BillingPeriodFrom, dbOverride.BillingPeriodTo), + ServicePeriod: fromDBClosedPeriod(dbOverride.ServicePeriodFrom, dbOverride.ServicePeriodTo), + FullServicePeriod: fromDBClosedPeriod(dbOverride.FullServicePeriodFrom, dbOverride.FullServicePeriodTo), + BillingPeriod: fromDBClosedPeriod(dbOverride.BillingPeriodFrom, dbOverride.BillingPeriodTo), }, IntentDeletedAt: convert.TimePtrIn(dbOverride.IntentDeletedAt, time.UTC), InvoiceAt: dbOverride.InvoiceAt.UTC(), @@ -74,7 +74,7 @@ func (a *adapter) CreateChargeOverride(ctx context.Context, charge usagebased.Ch dbCharge.Edges.IntentOverride = dbIntentOverride - return MapChargeBaseFromDB(dbCharge, charge.Intent.GetBaseIntent().Currency) + return fromDBBaseWithCurrency(dbCharge, charge.Intent.GetBaseIntent().Currency) }) } @@ -213,7 +213,7 @@ func (a *adapter) updateIntentOverride(ctx context.Context, chargeID meta.Charge return dbOverride, nil } -func closedPeriodFromDB(from, to time.Time) timeutil.ClosedPeriod { +func fromDBClosedPeriod(from, to time.Time) timeutil.ClosedPeriod { return timeutil.ClosedPeriod{ From: from.UTC(), To: to.UTC(), diff --git a/openmeter/billing/charges/usagebased/adapter/mapper.go b/openmeter/billing/charges/usagebased/adapter/mapper.go index feb3a2d444..a347d5efe3 100644 --- a/openmeter/billing/charges/usagebased/adapter/mapper.go +++ b/openmeter/billing/charges/usagebased/adapter/mapper.go @@ -22,50 +22,50 @@ import ( "github.com/openmeterio/openmeter/pkg/slicesx" ) -func MapChargeFromDB(entity *entdb.ChargeUsageBased, expands meta.Expands) (usagebased.Charge, error) { - chargeMeta, err := chargemeta.FromDBChargeWithCurrencyEdge(entity, entity.Edges) +func FromDB(entity *entdb.ChargeUsageBased, expands meta.Expands) (usagebased.Charge, error) { + chargeMeta, err := chargemeta.FromDB(entity, entity.Edges) if err != nil { return usagebased.Charge{}, fmt.Errorf("mapping usage based charge meta [id=%s]: %w", entity.ID, err) } - return mapChargeFromDB(entity, chargeMeta, expands) + return fromDBWithMeta(entity, chargeMeta, expands) } -func FromDBChargeUsageBasedWithCurrency(entity *entdb.ChargeUsageBased, currency currencies.Currency, expands meta.Expands) (usagebased.Charge, error) { - chargeMeta, err := chargemeta.FromDBCharge(entity, currency) +func FromDBWithCurrency(entity *entdb.ChargeUsageBased, currency currencies.Currency, expands meta.Expands) (usagebased.Charge, error) { + chargeMeta, err := chargemeta.FromDBWithCurrency(entity, currency) if err != nil { return usagebased.Charge{}, fmt.Errorf("mapping usage based charge meta [id=%s]: %w", entity.ID, err) } - return mapChargeFromDB(entity, chargeMeta, expands) + return fromDBWithMeta(entity, chargeMeta, expands) } -func mapChargeFromDB(entity *entdb.ChargeUsageBased, chargeMeta meta.Charge, expands meta.Expands) (usagebased.Charge, error) { +func fromDBWithMeta(entity *entdb.ChargeUsageBased, chargeMeta meta.Charge, expands meta.Expands) (usagebased.Charge, error) { var realizations usagebased.RealizationRuns if expands.Has(meta.ExpandRealizations) { var err error - realizations, err = MapRealizationRunsFromDB(entity) + realizations, err = fromDBRuns(entity) if err != nil { return usagebased.Charge{}, fmt.Errorf("mapping usage based charge [id=%s]: %w", entity.ID, err) } } return usagebased.Charge{ - ChargeBase: mapChargeBaseFromDB(entity, chargeMeta), + ChargeBase: fromDBBase(entity, chargeMeta), Realizations: realizations, }, nil } -func MapChargeBaseFromDB(entity *entdb.ChargeUsageBased, currency currencies.Currency) (usagebased.ChargeBase, error) { - chargeMeta, err := chargemeta.FromDBCharge(entity, currency) +func fromDBBaseWithCurrency(entity *entdb.ChargeUsageBased, currency currencies.Currency) (usagebased.ChargeBase, error) { + chargeMeta, err := chargemeta.FromDBWithCurrency(entity, currency) if err != nil { return usagebased.ChargeBase{}, fmt.Errorf("mapping charge meta: %w", err) } - return mapChargeBaseFromDB(entity, chargeMeta), nil + return fromDBBase(entity, chargeMeta), nil } -func mapChargeBaseFromDB(entity *entdb.ChargeUsageBased, chargeMeta meta.Charge) usagebased.ChargeBase { +func fromDBBase(entity *entdb.ChargeUsageBased, chargeMeta meta.Charge) usagebased.ChargeBase { intent := usagebased.Intent{ Intent: chargeMeta.Intent, FeatureKey: entity.FeatureKey, @@ -83,7 +83,7 @@ func mapChargeBaseFromDB(entity *entdb.ChargeUsageBased, chargeMeta meta.Charge) return usagebased.ChargeBase{ ManagedResource: chargeMeta.ManagedResource, Status: entity.StatusDetailed, - Intent: usagebased.NewOverridableIntent(intent, mapIntentOverrideFromDB(entity.Edges.IntentOverride)), + Intent: usagebased.NewOverridableIntent(intent, fromDBOverride(entity.Edges.IntentOverride)), State: usagebased.State{ CurrentRealizationRunID: entity.CurrentRealizationRunID, AdvanceAfter: entity.AdvanceAfter, @@ -93,15 +93,14 @@ func mapChargeBaseFromDB(entity *entdb.ChargeUsageBased, chargeMeta meta.Charge) } } -// MapRealizationRunsFromDB converts a DB Charge entity (with loaded UsageBased edge) to a UsageBasedCharge. -func MapRealizationRunsFromDB(entity *entdb.ChargeUsageBased) (usagebased.RealizationRuns, error) { +func fromDBRuns(entity *entdb.ChargeUsageBased) (usagebased.RealizationRuns, error) { dbRuns, err := entity.Edges.RunsOrErr() if err != nil { return nil, fmt.Errorf("mapping usage based charge [id=%s]: %w", entity.ID, err) } runs, err := slicesx.MapWithErr(dbRuns, func(run *entdb.ChargeUsageBasedRuns) (usagebased.RealizationRun, error) { - return MapRealizationRunFromDB(run) + return fromDBRun(run) }) if err != nil { return nil, fmt.Errorf("mapping usage based charge [id=%s]: %w", entity.ID, err) @@ -120,7 +119,7 @@ func MapRealizationRunsFromDB(entity *entdb.ChargeUsageBased) (usagebased.Realiz return runs, nil } -func MapRealizationRunBaseFromDB(dbRun *entdb.ChargeUsageBasedRuns) usagebased.RealizationRunBase { +func fromDBRunBase(dbRun *entdb.ChargeUsageBasedRuns) usagebased.RealizationRunBase { return usagebased.RealizationRunBase{ ID: usagebased.RealizationRunID{ Namespace: dbRun.Namespace, @@ -141,9 +140,9 @@ func MapRealizationRunBaseFromDB(dbRun *entdb.ChargeUsageBasedRuns) usagebased.R } } -func MapRealizationRunFromDB(dbRun *entdb.ChargeUsageBasedRuns) (usagebased.RealizationRun, error) { +func fromDBRun(dbRun *entdb.ChargeUsageBasedRuns) (usagebased.RealizationRun, error) { run := usagebased.RealizationRun{ - RealizationRunBase: MapRealizationRunBaseFromDB(dbRun), + RealizationRunBase: fromDBRunBase(dbRun), } dbCreditsAllocated, err := dbRun.Edges.CreditAllocationsOrErr() diff --git a/openmeter/billing/charges/usagebased/adapter/realizationrun.go b/openmeter/billing/charges/usagebased/adapter/realizationrun.go index c3e3f924e8..04057948e0 100644 --- a/openmeter/billing/charges/usagebased/adapter/realizationrun.go +++ b/openmeter/billing/charges/usagebased/adapter/realizationrun.go @@ -43,7 +43,7 @@ func (a *adapter) CreateRealizationRun(ctx context.Context, chargeID meta.Charge return usagebased.RealizationRunBase{}, err } - return MapRealizationRunBaseFromDB(dbRun), nil + return fromDBRunBase(dbRun), nil }) } @@ -91,6 +91,6 @@ func (a *adapter) UpdateRealizationRun(ctx context.Context, input usagebased.Upd return usagebased.RealizationRunBase{}, err } - return MapRealizationRunBaseFromDB(dbRun), nil + return fromDBRunBase(dbRun), nil }) } diff --git a/openmeter/currencies/adapter/currencies.go b/openmeter/currencies/adapter/currencies.go index ae857c6926..dd210b1ae4 100644 --- a/openmeter/currencies/adapter/currencies.go +++ b/openmeter/currencies/adapter/currencies.go @@ -2,6 +2,7 @@ package adapter import ( "context" + "errors" "fmt" "time" @@ -28,24 +29,26 @@ type CustomCurrencyOrFiatCurrency struct { } func (c *CustomCurrencyOrFiatCurrency) Validate() error { + var errs []error + if c.CustomCurrency != nil && c.FiatCurrency != nil { - return fmt.Errorf("both custom currency and fiat currency cannot be set") + errs = append(errs, errors.New("both custom currency and fiat currency cannot be set")) } if c.CustomCurrency == nil && c.FiatCurrency == nil { - return fmt.Errorf("either custom currency or fiat currency must be set") + errs = append(errs, errors.New("either custom currency or fiat currency must be set")) } - return nil + return models.NewNillableGenericValidationError(errors.Join(errs...)) } -func MapCustomCurrencyOrFiatCurrencyFromDB(in CustomCurrencyOrFiatCurrency) (currencies.Currency, error) { +func FromDBCustomCurrencyOrFiatCurrency(in CustomCurrencyOrFiatCurrency) (currencies.Currency, error) { if err := in.Validate(); err != nil { return currencies.Currency{}, err } if in.CustomCurrency != nil { - return MapCurrencyFromDB(in.CustomCurrency) + return FromDBCustomCurrency(in.CustomCurrency) } fiatCurrency, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeFiat). @@ -60,7 +63,7 @@ func MapCustomCurrencyOrFiatCurrencyFromDB(in CustomCurrencyOrFiatCurrency) (cur }, nil } -func MapCurrencyFromDB(c *entdb.CustomCurrency) (currencies.Currency, error) { +func FromDBCustomCurrency(c *entdb.CustomCurrency) (currencies.Currency, error) { curr, err := currencyx.NewCurrencyBuilder(currencyx.CurrencyTypeCustom). WithCode(c.Code). WithName(c.Name). @@ -182,7 +185,7 @@ func (a *adapter) ListCustomCurrencies(ctx context.Context, params currencies.Li return pagination.Result[currencies.Currency]{}, fmt.Errorf("failed to list currencies: %w", err) } - return pagination.MapResultErr(paged, MapCurrencyFromDB) + return pagination.MapResultErr(paged, FromDBCustomCurrency) }) } @@ -206,7 +209,7 @@ func (a *adapter) CreateCurrency(ctx context.Context, params currencies.CreateCu return currencies.Currency{}, fmt.Errorf("failed to create currency: %w", err) } - return MapCurrencyFromDB(curr) + return FromDBCustomCurrency(curr) }) } @@ -286,7 +289,7 @@ func (a *adapter) GetCostBasis(ctx context.Context, params currencies.GetCostBas result := mapCostBasisFromDB(costBasis) if params.CustomCurrency { - customCurrency, err := MapCurrencyFromDB(costBasis.Edges.Currency) + customCurrency, err := FromDBCustomCurrency(costBasis.Edges.Currency) if err != nil { return currencies.CostBasis{}, fmt.Errorf("failed to map custom currency: %w", err) } @@ -345,7 +348,7 @@ func (a *adapter) GetCurrency(ctx context.Context, params currencies.GetCurrency return currencies.Currency{}, fmt.Errorf("failed to get currency: %w", err) } - curr, err := MapCurrencyFromDB(c) + curr, err := FromDBCustomCurrency(c) if err != nil { return currencies.Currency{}, fmt.Errorf("failed to map currency from database: %w", err) } diff --git a/openmeter/currencies/currency.go b/openmeter/currencies/currency.go index 3b97811859..7c17c0170b 100644 --- a/openmeter/currencies/currency.go +++ b/openmeter/currencies/currency.go @@ -58,11 +58,17 @@ func NewFiatCurrency(code currencyx.Code) (Currency, error) { } func (c Currency) Validate() error { + var errs []error + if c.Currency == nil { - return errors.New("currency is required") + errs = append(errs, errors.New("currency is required")) + } else { + if err := c.Currency.Validate(); err != nil { + errs = append(errs, fmt.Errorf("currency: %w", err)) + } } - return c.Currency.Validate() + return models.NewNillableGenericValidationError(errors.Join(errs...)) } // GetCode returns the resolved currency code, or an empty code when the currency is missing.