diff --git a/app/controllers/api/v1/billable_metrics_controller.rb b/app/controllers/api/v1/billable_metrics_controller.rb index 804e934cc88a..cdfc4bb342c7 100644 --- a/app/controllers/api/v1/billable_metrics_controller.rb +++ b/app/controllers/api/v1/billable_metrics_controller.rb @@ -103,6 +103,8 @@ def input_params :weighted_interval, :recurring, :field_name, + :rounding_function, + :rounding_precision, filters: [:key, {values: []}] ) end diff --git a/app/graphql/types/billable_metrics/create_input.rb b/app/graphql/types/billable_metrics/create_input.rb index 8ebbb3cc142b..8468ca6ce70e 100644 --- a/app/graphql/types/billable_metrics/create_input.rb +++ b/app/graphql/types/billable_metrics/create_input.rb @@ -11,6 +11,8 @@ class CreateInput < BaseInputObject argument :field_name, String, required: false argument :name, String, required: true argument :recurring, Boolean, required: false + argument :rounding_function, Types::BillableMetrics::RoundingFunctionEnum, required: false + argument :rounding_precision, Integer, required: false argument :weighted_interval, Types::BillableMetrics::WeightedIntervalEnum, required: false argument :filters, [Types::BillableMetricFilters::Input], required: false diff --git a/app/graphql/types/billable_metrics/object.rb b/app/graphql/types/billable_metrics/object.rb index e9dc61f615d7..3be46ca7614c 100644 --- a/app/graphql/types/billable_metrics/object.rb +++ b/app/graphql/types/billable_metrics/object.rb @@ -26,6 +26,9 @@ class Object < Types::BaseObject field :recurring, Boolean, null: false field :subscriptions_count, Integer, null: false + field :rounding_function, Types::BillableMetrics::RoundingFunctionEnum, null: true + field :rounding_precision, Integer, null: true + field :created_at, GraphQL::Types::ISO8601DateTime, null: false field :deleted_at, GraphQL::Types::ISO8601DateTime, null: true field :updated_at, GraphQL::Types::ISO8601DateTime, null: false diff --git a/app/graphql/types/billable_metrics/rounding_function_enum.rb b/app/graphql/types/billable_metrics/rounding_function_enum.rb new file mode 100644 index 000000000000..1ef3dfad46e0 --- /dev/null +++ b/app/graphql/types/billable_metrics/rounding_function_enum.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Types + module BillableMetrics + class RoundingFunctionEnum < Types::BaseEnum + BillableMetric::ROUNDING_FUNCTIONS.values.each do |type| + value type + end + end + end +end diff --git a/app/graphql/types/billable_metrics/update_input.rb b/app/graphql/types/billable_metrics/update_input.rb index 728cb03a92c9..30c07f5c7241 100644 --- a/app/graphql/types/billable_metrics/update_input.rb +++ b/app/graphql/types/billable_metrics/update_input.rb @@ -13,6 +13,8 @@ class UpdateInput < BaseInputObject argument :field_name, String, required: false argument :name, String, required: true argument :recurring, Boolean, required: false + argument :rounding_function, Types::BillableMetrics::RoundingFunctionEnum, required: false + argument :rounding_precision, Integer, required: false argument :weighted_interval, Types::BillableMetrics::WeightedIntervalEnum, required: false argument :filters, [Types::BillableMetricFilters::Input], required: false diff --git a/app/serializers/v1/billable_metric_serializer.rb b/app/serializers/v1/billable_metric_serializer.rb index b058c977f512..9e4044d55401 100644 --- a/app/serializers/v1/billable_metric_serializer.rb +++ b/app/serializers/v1/billable_metric_serializer.rb @@ -11,6 +11,8 @@ def serialize aggregation_type: model.aggregation_type, weighted_interval: model.weighted_interval, recurring: model.recurring, + rounding_function: model.rounding_function, + rounding_precision: model.rounding_precision, created_at: model.created_at.iso8601, field_name: model.field_name, active_subscriptions_count:, diff --git a/app/services/billable_metrics/create_service.rb b/app/services/billable_metrics/create_service.rb index 9127bc327313..4e1d23bf48c1 100644 --- a/app/services/billable_metrics/create_service.rb +++ b/app/services/billable_metrics/create_service.rb @@ -23,7 +23,9 @@ def call recurring: args[:recurring] || false, aggregation_type: args[:aggregation_type]&.to_sym, field_name: args[:field_name], - weighted_interval: args[:weighted_interval]&.to_sym + rounding_function: args[:rounding_function]&.to_sym, + rounding_precision: args[:rounding_precision], + weighted_interval: args[:weighted_interval]&.to_sym, ) if args[:filters].present? diff --git a/app/services/billable_metrics/update_service.rb b/app/services/billable_metrics/update_service.rb index fd5ee645617d..d84cff73634a 100644 --- a/app/services/billable_metrics/update_service.rb +++ b/app/services/billable_metrics/update_service.rb @@ -38,6 +38,8 @@ def call billable_metric.weighted_interval = params[:weighted_interval]&.to_sym if params.key?(:weighted_interval) billable_metric.field_name = params[:field_name] if params.key?(:field_name) billable_metric.recurring = params[:recurring] if params.key?(:recurring) + billable_metric.rounding_function = params[:rounding_function] if params.key?(:rounding_function) + billable_metric.rounding_precision = params[:rounding_precision] if params.key?(:rounding_precision) billable_metric.weighted_interval = params[:weighted_interval]&.to_sym if params.key?(:weighted_interval) end diff --git a/schema.graphql b/schema.graphql index 6390b0c0ab67..23dea5ed9459 100644 --- a/schema.graphql +++ b/schema.graphql @@ -244,6 +244,8 @@ type BillableMetric { organization: Organization plansCount: Int! recurring: Boolean! + roundingFunction: RoundingFunctionEnum + roundingPrecision: Int subscriptionsCount: Int! updatedAt: ISO8601DateTime! weightedInterval: WeightedIntervalEnum @@ -1851,6 +1853,8 @@ input CreateBillableMetricInput { filters: [BillableMetricFiltersInput!] name: String! recurring: Boolean + roundingFunction: RoundingFunctionEnum + roundingPrecision: Int weightedInterval: WeightedIntervalEnum } @@ -6760,6 +6764,12 @@ input RevokeMembershipInput { id: ID! } +enum RoundingFunctionEnum { + ceil + floor + round +} + enum StatusTypeEnum { active canceled @@ -7780,6 +7790,8 @@ input UpdateBillableMetricInput { id: String! name: String! recurring: Boolean + roundingFunction: RoundingFunctionEnum + roundingPrecision: Int weightedInterval: WeightedIntervalEnum } diff --git a/schema.json b/schema.json index c34024dba4d2..7f4994e89d95 100644 --- a/schema.json +++ b/schema.json @@ -2235,6 +2235,34 @@ ] }, + { + "name": "roundingFunction", + "description": null, + "type": { + "kind": "ENUM", + "name": "RoundingFunctionEnum", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, + { + "name": "roundingPrecision", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null, + "args": [ + + ] + }, { "name": "subscriptionsCount", "description": null, @@ -6692,6 +6720,30 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "roundingFunction", + "description": null, + "type": { + "kind": "ENUM", + "name": "RoundingFunctionEnum", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "roundingPrecision", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "weightedInterval", "description": null, @@ -35138,6 +35190,35 @@ ], "enumValues": null }, + { + "kind": "ENUM", + "name": "RoundingFunctionEnum", + "description": null, + "interfaces": null, + "possibleTypes": null, + "fields": null, + "inputFields": null, + "enumValues": [ + { + "name": "round", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "ceil", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "floor", + "description": null, + "isDeprecated": false, + "deprecationReason": null + } + ] + }, { "kind": "ENUM", "name": "StatusTypeEnum", @@ -38037,6 +38118,30 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "roundingFunction", + "description": null, + "type": { + "kind": "ENUM", + "name": "RoundingFunctionEnum", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "roundingPrecision", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "weightedInterval", "description": null, diff --git a/spec/graphql/types/billable_metrics/create_input_spec.rb b/spec/graphql/types/billable_metrics/create_input_spec.rb index 33a108130e57..df44a267a6a3 100644 --- a/spec/graphql/types/billable_metrics/create_input_spec.rb +++ b/spec/graphql/types/billable_metrics/create_input_spec.rb @@ -11,6 +11,8 @@ it { is_expected.to accept_argument(:field_name).of_type('String') } it { is_expected.to accept_argument(:name).of_type('String!') } it { is_expected.to accept_argument(:recurring).of_type('Boolean') } + it { is_expected.to accept_argument(:rounding_function).of_type('RoundingFunctionEnum') } + it { is_expected.to accept_argument(:rounding_precision).of_type('Int') } it { is_expected.to accept_argument(:weighted_interval).of_type('WeightedIntervalEnum') } it { is_expected.to accept_argument(:filters).of_type('[BillableMetricFiltersInput!]') } end diff --git a/spec/graphql/types/billable_metrics/object_spec.rb b/spec/graphql/types/billable_metrics/object_spec.rb index 5e6d4aa4c174..2ee733185bfd 100644 --- a/spec/graphql/types/billable_metrics/object_spec.rb +++ b/spec/graphql/types/billable_metrics/object_spec.rb @@ -23,4 +23,6 @@ it { is_expected.to have_field(:deleted_at).of_type('ISO8601DateTime') } it { is_expected.to have_field(:updated_at).of_type('ISO8601DateTime!') } it { is_expected.to have_field(:integration_mappings).of_type('[Mapping!]') } + it { is_expected.to have_field(:rounding_function).of_type('RoundingFunctionEnum') } + it { is_expected.to have_field(:rounding_precision).of_type('Int') } end diff --git a/spec/graphql/types/billable_metrics/update_input_spec.rb b/spec/graphql/types/billable_metrics/update_input_spec.rb index 6a64dbd81d65..762972980b7f 100644 --- a/spec/graphql/types/billable_metrics/update_input_spec.rb +++ b/spec/graphql/types/billable_metrics/update_input_spec.rb @@ -12,6 +12,8 @@ it { is_expected.to accept_argument(:field_name).of_type('String') } it { is_expected.to accept_argument(:name).of_type('String!') } it { is_expected.to accept_argument(:recurring).of_type('Boolean') } + it { is_expected.to accept_argument(:rounding_function).of_type('RoundingFunctionEnum') } + it { is_expected.to accept_argument(:rounding_precision).of_type('Int') } it { is_expected.to accept_argument(:weighted_interval).of_type('WeightedIntervalEnum') } it { is_expected.to accept_argument(:filters).of_type('[BillableMetricFiltersInput!]') } end diff --git a/spec/requests/api/v1/billable_metrics_controller_spec.rb b/spec/requests/api/v1/billable_metrics_controller_spec.rb index 2fce73e36305..7da0e9bc8e8a 100644 --- a/spec/requests/api/v1/billable_metrics_controller_spec.rb +++ b/spec/requests/api/v1/billable_metrics_controller_spec.rb @@ -13,7 +13,9 @@ description: 'description', aggregation_type: 'sum_agg', field_name: 'amount_sum', - recurring: true + recurring: true, + rounding_function: 'round', + rounding_precision: 2 } end @@ -26,6 +28,8 @@ expect(json[:billable_metric][:name]).to eq(create_params[:name]) expect(json[:billable_metric][:created_at]).to be_present expect(json[:billable_metric][:recurring]).to eq(create_params[:recurring]) + expect(json[:billable_metric][:rounding_function]).to eq(create_params[:rounding_function]) + expect(json[:billable_metric][:rounding_precision]).to eq(create_params[:rounding_precision]) expect(json[:billable_metric][:filters]).to eq([]) end @@ -47,10 +51,7 @@ expect(response).to have_http_status(:success) expect(json[:billable_metric][:lago_id]).to be_present - expect(json[:billable_metric][:recurring]).to eq( - create_params[:recurring - ] - ) + expect(json[:billable_metric][:recurring]).to eq(create_params[:recurring]) expect(json[:billable_metric][:aggregation_type]).to eq('weighted_sum_agg') expect(json[:billable_metric][:weighted_interval]).to eq('seconds') end diff --git a/spec/serializers/v1/billable_metric_serializer_spec.rb b/spec/serializers/v1/billable_metric_serializer_spec.rb index 9f8708a9a286..014be5dbdfba 100644 --- a/spec/serializers/v1/billable_metric_serializer_spec.rb +++ b/spec/serializers/v1/billable_metric_serializer_spec.rb @@ -17,6 +17,8 @@ expect(result['billable_metric']['aggregation_type']).to eq(billable_metric.aggregation_type) expect(result['billable_metric']['field_name']).to eq(billable_metric.field_name) expect(result['billable_metric']['created_at']).to eq(billable_metric.created_at.iso8601) + expect(result['billable_metric']['rounding_function']).to eq(billable_metric.rounding_function) + expect(result['billable_metric']['rounding_precision']).to eq(billable_metric.rounding_precision) expect(result['billable_metric']['weighted_interval']).to eq(billable_metric.weighted_interval) expect(result['billable_metric']['active_subscriptions_count']).to eq(0) expect(result['billable_metric']['draft_invoices_count']).to eq(0) diff --git a/spec/services/billable_metrics/create_service_spec.rb b/spec/services/billable_metrics/create_service_spec.rb index e7253006e87b..d15a11d4f9ee 100644 --- a/spec/services/billable_metrics/create_service_spec.rb +++ b/spec/services/billable_metrics/create_service_spec.rb @@ -20,6 +20,8 @@ description: "New metric description", organization_id: organization.id, aggregation_type: "count_agg", + rounding_function: "ceil", + rounding_precision: 2, recurring: false } end diff --git a/spec/services/billable_metrics/update_service_spec.rb b/spec/services/billable_metrics/update_service_spec.rb index c3ef434a8c40..91f278dce43e 100644 --- a/spec/services/billable_metrics/update_service_spec.rb +++ b/spec/services/billable_metrics/update_service_spec.rb @@ -15,7 +15,9 @@ code: 'new_metric', description: 'New metric description', aggregation_type: 'sum_agg', - field_name: 'field_value' + field_name: 'field_value', + rounding_function: 'ceil', + rounding_precision: 2, }.tap do |p| p[:filters] = filters unless filters.nil? end @@ -23,18 +25,20 @@ let(:filters) { nil } describe '#call' do - it 'updates the billable metric' do + it 'updates the billable metric', aggregate_failures: true do result = update_service.call - aggregate_failures do - expect(result).to be_success - - metric = result.billable_metric - expect(metric.id).to eq(billable_metric.id) - expect(metric.name).to eq('New Metric') - expect(metric.code).to eq('new_metric') - expect(metric.aggregation_type).to eq('sum_agg') - end + expect(result).to be_success + + metric = result.billable_metric + expect(metric).to have_attributes( + id: billable_metric.id, + name: 'New Metric', + code: 'new_metric', + aggregation_type: 'sum_agg', + rounding_function: 'ceil', + rounding_precision: 2 + ) end context 'with filters arguments' do @@ -104,7 +108,7 @@ before { charge } - it 'updates only name and description' do + it 'updates only name and description', aggregate_failures: true do result = update_service.call aggregate_failures do @@ -118,7 +122,9 @@ expect(result.billable_metric).not_to have_attributes( code: 'new_metric', aggregation_type: 'sum_agg', - field_name: 'field_value' + field_name: 'field_value', + rounding_function: 'ceil', + rounding_precision: 2 ) end end