Skip to content

Commit

Permalink
feat: T-940 Create charges allong with plan create/update (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
vincent-pochet authored Mar 25, 2022
1 parent c50af28 commit 15cf660
Show file tree
Hide file tree
Showing 16 changed files with 616 additions and 137 deletions.
3 changes: 2 additions & 1 deletion app/graphql/mutations/plans/create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ class Create < BaseMutation

argument :organization_id, String, required: true
argument :name, String, required: true
argument :billable_metric_ids, [String]
argument :code, String, required: true
argument :frequency, Types::Plans::FrequencyEnum, required: true
argument :billing_period, Types::Plans::BillingPeriodEnum, required: true
Expand All @@ -21,6 +20,8 @@ class Create < BaseMutation
argument :trial_period, Float, required: false
argument :description, String, required: false

argument :charges, [Types::Charges::Input]

type Types::Plans::Object

def resolve(**args)
Expand Down
3 changes: 2 additions & 1 deletion app/graphql/mutations/plans/update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ class Update < BaseMutation

argument :id, String, required: true
argument :name, String, required: true
argument :billable_metric_ids, [String]
argument :code, String, required: true
argument :frequency, Types::Plans::FrequencyEnum, required: true
argument :billing_period, Types::Plans::BillingPeriodEnum, required: true
Expand All @@ -20,6 +19,8 @@ class Update < BaseMutation
argument :trial_period, Float, required: false
argument :description, String, required: false

argument :charges, [Types::Charges::Input]

type Types::Plans::Object

def resolve(**args)
Expand Down
13 changes: 13 additions & 0 deletions app/graphql/types/charges/frequency_enum.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module Types
module Charges
class FrequencyEnum < Types::BaseEnum
graphql_name 'ChargeFrequency'

Charge::FREQUENCIES.each do |type|
value type
end
end
end
end
16 changes: 16 additions & 0 deletions app/graphql/types/charges/input.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

module Types
module Charges
class Input < Types::BaseInputObject
graphql_name 'ChargeInput'

argument :billable_metric_id, ID, required: true
argument :amount_cents, Integer, required: true
argument :amount_currency, Types::CurrencyEnum, required: true
argument :frequency, Types::Charges::FrequencyEnum, required: true
argument :pro_rata, Boolean, required: true
argument :vat_rate, Float, required: false
end
end
end
20 changes: 20 additions & 0 deletions app/graphql/types/charges/object.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module Types
module Charges
class Object < Types::BaseObject
graphql_name 'Charge'

field :id, ID, null: false
field :billable_metric, Types::BillableMetrics::Object, null: false
field :amount_cents, Integer, null: false
field :amount_currency, Types::CurrencyEnum, null: false
field :frequency, Types::Charges::FrequencyEnum, null: false
field :pro_rata, Boolean, null: false
field :vat_rate, Float

field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
end
end
end
2 changes: 2 additions & 0 deletions app/graphql/types/plans/frequency_enum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
module Types
module Plans
class FrequencyEnum < Types::BaseEnum
graphql_name 'PlanFrequency'

Plan::FREQUENCIES.each do |type|
value type
end
Expand Down
2 changes: 1 addition & 1 deletion app/graphql/types/plans/object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Object < Types::BaseObject
field :trial_period, Float
field :description, String

field :billable_metrics, [Types::BillableMetrics::Object]
field :charges, [Types::Charges::Object]

field :created_at, GraphQL::Types::ISO8601DateTime, null: false
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
Expand Down
7 changes: 7 additions & 0 deletions app/models/charge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,11 @@
class Charge < ApplicationRecord
belongs_to :plan
belongs_to :billable_metric

FREQUENCIES = %i[
one_time
recurring
].freeze

enum frequency: FREQUENCIES
end
28 changes: 22 additions & 6 deletions app/services/plans_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ def create(**args)
trial_period: args[:trial_period]
)

# TODO: create charges
# Validates billable metrics
metric_ids = args[:billable_metric_ids]
metric_ids = args[:charges].map { |c| c[:billable_metric_id] }.uniq
if metric_ids.present? && plan.organization.billable_metrics.where(id: metric_ids).count != metric_ids.count
return result.fail!('unprocessable_entity', 'Billable metrics does not exists')
end
Expand All @@ -31,7 +30,8 @@ def create(**args)
# TODO: better handling of validation errors
plan.save!

plan.billable_metric_ids = metric_ids if metric_ids.present?
# TODO: group validation errors
args[:charges].each { |c| create_charge(plan, c) }
end

result.plan = plan
Expand All @@ -54,8 +54,7 @@ def update(**args)
plan.vat_rate = args[:vat_rate]
plan.trial_period = args[:trial_period]

# TODO: create charges
metric_ids = args[:billable_metric_ids]
metric_ids = args[:charges].map { |c| c[:billable_metric_id] }.uniq
if metric_ids.present? && plan.organization.billable_metrics.where(id: metric_ids).count != metric_ids.count
return result.fail!('unprocessable_entity', 'Billable metrics does not exists')
end
Expand All @@ -64,7 +63,11 @@ def update(**args)
# TODO: better handling of validation errors
plan.save!

plan.billable_metric_ids = metric_ids if metric_ids.present?
# TODO: update existing instead of removing all
plan.charges.delete_all

# TODO: group validation errors
args[:charges].each { |c| create_charge(plan, c) }
end

result.plan = plan
Expand All @@ -81,4 +84,17 @@ def destroy(id)
result.plan = plan
result
end

private

def create_charge(plan, args)
plan.charges.create!(
billable_metric_id: args[:billable_metric_id],
amount_cents: args[:amount_cents],
amount_currency: args[:amount_currency],
frequency: args[:frequency].to_sym,
pro_rata: args[:pro_rata],
vat_rate: args[:vat_rate]
)
end
end
13 changes: 13 additions & 0 deletions db/migrate/20220324141856_add_properties_to_charges.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

class AddPropertiesToCharges < ActiveRecord::Migration[7.0]
def change
change_table :charges do |t|
t.integer :amount_cents, null: false
t.string :amount_currency, null: false
t.integer :frequency, null: false
t.boolean :pro_rata, null: false
t.float :vat_rate
end
end
end
7 changes: 6 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 38 additions & 12 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,32 @@ enum BillingPeriodEnum {
subscruption_date
}

type Charge {
amountCents: Int!
amountCurrency: CurrencyEnum!
billableMetric: BillableMetric!
createdAt: ISO8601DateTime!
frequency: ChargeFrequency!
id: ID!
proRata: Boolean!
updatedAt: ISO8601DateTime!
vatRate: Float
}

enum ChargeFrequency {
one_time
recurring
}

input ChargeInput {
amountCents: Int!
amountCurrency: CurrencyEnum!
billableMetricId: ID!
frequency: ChargeFrequency!
proRata: Boolean!
vatRate: Float
}

type CollectionMetadata {
currentPage: Int!
limitValue: Int!
Expand Down Expand Up @@ -56,16 +82,16 @@ Autogenerated input type of CreatePlan
input CreatePlanInput {
amountCents: Int!
amountCurrency: CurrencyEnum!
billableMetricIds: [String!]!
billingPeriod: BillingPeriodEnum!
charges: [ChargeInput!]!

"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
code: String!
description: String
frequency: FrequencyEnum!
frequency: PlanFrequency!
name: String!
organizationId: String!
proRata: Boolean!
Expand Down Expand Up @@ -129,12 +155,6 @@ type DestroyPlanPayload {
id: ID
}

enum FrequencyEnum {
monthly
weekly
yearly
}

"""
An ISO 8601-encoded datetime
"""
Expand Down Expand Up @@ -232,12 +252,12 @@ type Organization {
type Plan {
amountCents: Int!
amountCurrency: CurrencyEnum!
billableMetrics: [BillableMetric!]
billingPeriod: BillingPeriodEnum!
charges: [Charge!]
code: String!
createdAt: ISO8601DateTime!
description: String
frequency: FrequencyEnum!
frequency: PlanFrequency!
id: ID!
name: String!
organization: Organization
Expand All @@ -252,6 +272,12 @@ type PlanCollection {
metadata: CollectionMetadata!
}

enum PlanFrequency {
monthly
weekly
yearly
}

type Query {
"""
Query billable metrics of an organization
Expand Down Expand Up @@ -307,16 +333,16 @@ Autogenerated input type of UpdatePlan
input UpdatePlanInput {
amountCents: Int!
amountCurrency: CurrencyEnum!
billableMetricIds: [String!]!
billingPeriod: BillingPeriodEnum!
charges: [ChargeInput!]!

"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
code: String!
description: String
frequency: FrequencyEnum!
frequency: PlanFrequency!
id: String!
name: String!
proRata: Boolean!
Expand Down
Loading

0 comments on commit 15cf660

Please sign in to comment.