app/workers/billing_worker.rb
# frozen_string_literal: true
class BillingWorker
include Sidekiq::Job
sidekiq_options queue: :billing, retry: 3
sidekiq_retry_in do |_count|
# after lock has been released
1.hours + 10
end
class Callback
delegate :logger, to: 'Rails'
def on_complete(status, options)
batch_id = options['batch_id']
account_id = options['account_id']
billing_date = options['billing_date']
logger.info("Billing batch complete: #{batch_id} (account_id: #{account_id}, billing_date: #{billing_date})")
billing_summary = BillingSummary.new(batch_id)
billing_service = Finance::BillingService.new(account_id, now: billing_date)
billing_service.notify_billing_finished(status, billing_summary.build_result(account_id, billing_date))
billing_summary.unstore
batch_status = Sidekiq::Batch::Status.new(batch_id)
batch_status.try(:delete)
end
end
# @param [Account] provider
# @param [Time] billing_date
# @param [ActiveRecord::Relation] buyers_scope
def self.enqueue(provider, billing_date, buyers_scope = nil)
with_billing_batch(provider.id, billing_date) do
scope = provider.buyer_accounts.select(:id, :provider_account_id)
scope = scope.merge(buyers_scope) if buyers_scope
scope.find_in_batches do |group|
group.each { |buyer| enqueue_for_buyer(buyer, billing_date) }
end
end
end
def self.enqueue_for_buyer(buyer, billing_date)
if buyer.id == buyer.provider_account_id
System::ErrorReporting.report_error(ArgumentError.new("invalid buyer #{buyer.id}, has self as provider"),
buyer_id: buyer.id,
billing_date: billing_date)
return
end
time = billing_date.to_s(:iso8601)
perform_async(buyer.id, buyer.provider_account_id, time)
end
def self.with_billing_batch(provider_id, billing_date, &block)
batch = Sidekiq::Batch.new
batch.description = "Billing (id: #{provider_id})"
batch.on(:complete, BillingWorker::Callback, batch_id: batch.bid, account_id: provider_id, billing_date: billing_date)
batch.jobs(&block)
end
# @param [Integer] buyer_id
# @param [Integer] provider_id
# @param [String] time
def perform(buyer_id, provider_id, time)
Rails.logger.info("[billing] provider #{provider_id} buyer #{buyer_id}: BillingWorker#perform invoked at #{time}")
billing_results = Finance::BillingService.call!(buyer_id, { provider_account_id: provider_id, now: time, skip_notifications: true })
store_summary(buyer_id, billing_results[provider_id]) if billing_results
end
private
def store_summary(buyer_id, billing_result)
summary = BillingSummary.new(batch && bid)
summary.store(buyer_id, billing_result)
end
delegate :logger, to: :Rails
end