Skip to content

Commit

Permalink
feat(wallet): Improve wallet's ongoing balance refresh
Browse files Browse the repository at this point in the history
  • Loading branch information
rsempe committed Oct 14, 2024
1 parent a3bbd3c commit 7174978
Show file tree
Hide file tree
Showing 11 changed files with 55 additions and 11 deletions.
2 changes: 1 addition & 1 deletion app/jobs/clock/refresh_wallets_ongoing_balance_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class RefreshWalletsOngoingBalanceJob < ApplicationJob
def perform
return unless License.premium?

Wallet.active.find_each do |wallet|
Wallet.active.ready_to_be_refreshed.find_each do |wallet|
Wallets::RefreshOngoingBalanceJob.perform_later(wallet)
end
end
Expand Down
2 changes: 2 additions & 0 deletions app/jobs/wallets/refresh_ongoing_balance_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ class RefreshOngoingBalanceJob < ApplicationJob
unique :until_executed, on_conflict: :log

def perform(wallet)
return unless wallet.ready_to_be_refreshed?

Wallets::Balance::RefreshOngoingService.call(wallet:)
end
end
Expand Down
5 changes: 4 additions & 1 deletion app/models/wallet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def mark_as_terminated!(timestamp = Time.zone.now)
end

scope :expired, -> { where('wallets.expiration_at::timestamp(0) <= ?', Time.current) }
scope :ready_to_be_refreshed, -> { where(ready_to_be_refreshed: true) }

def currency=(currency)
self.balance_currency = currency
Expand Down Expand Up @@ -62,6 +63,7 @@ def currency
# ongoing_balance_cents :bigint default(0), not null
# ongoing_usage_balance_cents :bigint default(0), not null
# rate_amount :decimal(30, 5) default(0.0), not null
# ready_to_be_refreshed :boolean default(FALSE), not null
# status :integer not null
# terminated_at :datetime
# created_at :datetime not null
Expand All @@ -70,7 +72,8 @@ def currency
#
# Indexes
#
# index_wallets_on_customer_id (customer_id)
# index_wallets_on_customer_id (customer_id)
# index_wallets_on_ready_to_be_refreshed (ready_to_be_refreshed) WHERE ready_to_be_refreshed
#
# Foreign Keys
#
Expand Down
8 changes: 8 additions & 0 deletions app/services/events/post_process_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def call

expire_cached_charges(subscriptions)
flag_refresh_from_subscription
flag_wallets_for_refresh

handle_pay_in_advance

Expand Down Expand Up @@ -95,6 +96,13 @@ def flag_refresh_from_subscription
subscriptions.select(&:active?).each { |s| LifetimeUsages::FlagRefreshFromSubscriptionService.new(subscription: s).call }
end

def flag_wallets_for_refresh
return unless customer
return unless customer.wallets.active.any?

customer.wallets.active.update_all(ready_to_be_refreshed: true) # rubocop:disable Rails/SkipsModelValidations
end

def handle_pay_in_advance
return unless billable_metric
return unless charges.any?
Expand Down
3 changes: 2 additions & 1 deletion app/services/wallets/balance/update_ongoing_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ def compute_update_params
ongoing_usage_balance_cents: total_usage_amount_cents,
credits_ongoing_usage_balance:,
ongoing_balance_cents:,
credits_ongoing_balance:
credits_ongoing_balance:,
ready_to_be_refreshed: false
}

if !wallet.depleted_ongoing_balance? && ongoing_balance_cents <= 0
Expand Down
10 changes: 10 additions & 0 deletions db/migrate/20241014093451_add_ready_to_be_refreshed_to_wallets.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

class AddReadyToBeRefreshedToWallets < ActiveRecord::Migration[7.1]
disable_ddl_transaction!

def change
add_column :wallets, :ready_to_be_refreshed, :boolean, default: false, null: false
add_index :wallets, :ready_to_be_refreshed, where: "ready_to_be_refreshed", algorithm: :concurrently
end
end
4 changes: 3 additions & 1 deletion db/schema.rb

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

6 changes: 3 additions & 3 deletions spec/jobs/clock/refresh_wallets_ongoing_balance_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
describe '.perform' do
let(:organization) { create(:organization) }
let(:customer) { create(:customer, organization:) }
let(:wallet) { create(:wallet, customer:) }
let(:wallet) { create(:wallet, customer:, ready_to_be_refreshed: true) }

before do
wallet
Expand All @@ -30,8 +30,8 @@
expect(Wallets::RefreshOngoingBalanceJob).to have_been_enqueued.with(wallet)
end

context 'when not active' do
let(:wallet) { create(:wallet, :terminated) }
context 'when not ready to be refreshed' do
let(:wallet) { create(:wallet, customer:, ready_to_be_refreshed: false) }

it 'does not call the refresh service' do
described_class.perform_now
Expand Down
16 changes: 13 additions & 3 deletions spec/jobs/wallets/refresh_ongoing_balance_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,29 @@
require 'rails_helper'

RSpec.describe Wallets::RefreshOngoingBalanceJob, type: :job do
let(:wallet) { create(:wallet) }
let(:wallet) { create(:wallet, ready_to_be_refreshed: true) }
let(:result) { BaseService::Result.new }

let(:refresh_service) do
instance_double(Wallets::Balance::RefreshOngoingService)
end

it 'delegates to the RefreshOngoingBalance service' do
before do
allow(Wallets::Balance::RefreshOngoingService).to receive(:new).with(wallet:).and_return(refresh_service)
allow(refresh_service).to receive(:call).and_return(result)
end

described_class.perform_now(wallet)
context 'when wallet is ready to be refreshed' do
let(:wallet) { create(:wallet, ready_to_be_refreshed: false) }

it 'does not call the RefreshOngoingBalance service' do
described_class.perform_now(wallet)
expect(Wallets::Balance::RefreshOngoingService).not_to have_received(:new)
end
end

it 'delegates to the RefreshOngoingBalance service' do
described_class.perform_now(wallet)
expect(Wallets::Balance::RefreshOngoingService).to have_received(:new)
expect(refresh_service).to have_received(:call)
end
Expand Down
6 changes: 6 additions & 0 deletions spec/services/events/post_process_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@
expect(subscription.lifetime_usage.recalculate_current_usage).to be(true)
end

it 'flags wallets for refresh' do
wallet = create(:wallet, customer:)

expect { process_service.call }.to change { wallet.reload.ready_to_be_refreshed }.from(false).to(true)
end

context 'when event matches an pay_in_advance charge' do
let(:charge) { create(:standard_charge, :pay_in_advance, plan:, billable_metric:, invoiceable: false) }
let(:billable_metric) { create(:billable_metric, organization:, aggregation_type: 'sum_agg', field_name: 'item_id') }
Expand Down
4 changes: 3 additions & 1 deletion spec/services/wallets/balance/update_ongoing_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
ongoing_usage_balance_cents: 200,
credits_balance: 10.0,
credits_ongoing_balance: 8.0,
credits_ongoing_usage_balance: 2.0
credits_ongoing_usage_balance: 2.0,
ready_to_be_refreshed: true
)
end
let(:total_usage_amount_cents) { 450 }
Expand All @@ -28,6 +29,7 @@
.and change(wallet, :credits_ongoing_usage_balance).from(2.0).to(4.5)
.and change(wallet, :ongoing_balance_cents).from(800).to(550)
.and change(wallet, :credits_ongoing_balance).from(8.0).to(5.5)
.and change(wallet, :ready_to_be_refreshed).from(true).to(false)

expect(wallet).not_to be_depleted_ongoing_balance
end
Expand Down

0 comments on commit 7174978

Please sign in to comment.