houdini/lib/audit.rb
Bradley M. Kuhn 6772312ea7 Relicense all .rb files under new project license.
The primary license of the project is changing to:
  AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later

with some specific files to be licensed under the one of two licenses:
   CC0-1.0
   LGPL-3.0-or-later

This commit is one of the many steps to relicense the entire codebase.

Documentation granting permission for this relicensing (from all past
contributors who hold copyrights) is on file with Software Freedom
Conservancy, Inc.
2018-03-25 15:10:40 -04:00

109 lines
4.3 KiB
Ruby

# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
module Audit
# Given a list of pairs of nonprofit ids and stripe_account_ids (eg [[4341, 'acct_arst'], [3624, 'acct_arst']])
# Find all their available balances on both stripe and CC
# Give all the ones that dont match up with the difference
# Negative difference = more balance on CC
# Positive difference = more balance on Stripe
# Returns an array of pairs of ids with balance difference (eg [[4341, -10], [3624, 100]])
def self.match_available_balances(nps, date)
date ||= Time.current
nps.map do |id, stripe_account_id|
cc_bal = QueryPayments.get_payout_totals(QueryPayments.ids_for_payout(id, date: date))['net_amount']
stripe_bal = Stripe::Balance.retrieve(stripe_account: stripe_account_id).available.first.amount
[id, stripe_bal - cc_bal]
end
end
# Print a report of whether saved payout balances match up to the sum of the payment records
def self.payout_check(id)
p = Payout.find(id)
gross = p.payments.sum(:gross_amount)
fees = p.payments.sum(:fee_total)
net = p.payments.sum(:net_amount)
puts [
[p.gross_amount, p.fee_total, p.net_amount].join(", ") + " -- payout columns",
[gross, fees, net].join(", ") + " -- summed from payments",
[p.gross_amount - gross, p.fee_total - fees, p.net_amount - net].join(", ") + " -- differences"
].join("\n")
end
# Return a list of all Stripe transaction objects for an account
def self.find_all_transactions(np_id)
logger = ActiveRecord::Base.logger; ActiveRecord::Base.logger = nil
acct = Nonprofit.find(np_id).stripe_account_id
starting_after = nil
transfers = []
loop do
new_transfers = Stripe::Transfer.all({limit: 100, starting_after: starting_after, destination: acct}).data
break if new_transfers.empty?
transfers += new_transfers
starting_after = new_transfers.last.id
end
ActiveRecord::Base.logger = logger
return transfers
end
# Given a list of Stripe transaction objects, see if any are missing on CommitChange
def self.find_missing_charges(transfers)
transfers
.map{|t| [t.source_transaction, t.amount]}
.select{|id, amount| Charge.where(stripe_charge_id: id, amount: amount).empty?}
end
# Audit some basic balances for a nonprofit with those on Stripe
def self.audit_balances(id)
np = Nonprofit.find(id)
puts "Stripe Dashboard: https://dashboard.stripe.com/#{np.stripe_account_id}"
puts "CC Payments: https://commitchange.com/nonprofits/#{id}/payments"
puts "CC Payouts: https://commitchange.com/nonprofits/#{id}/payouts"
begin
stripe_balances = Stripe::Balance.retrieve(stripe_account: np.stripe_account_id)
available = stripe_balances['available'].first['amount']
pending = stripe_balances['pending'].first['amount']
rescue Exception => e
available = 0
pending = 0
puts "UNRECOGNIZED STRIPE ACCOUNT ID: #{np.stripe_account_id}"
end
bal = np_balances(id)
data = {
stripe_net: available + pending,
cc_net: bal['net_amount'],
diff: bal['net_amount'] - (available + pending)
}
return data
end
# Get the total gross, net
# Pretty much duped from QueryPayments
def self.np_balances(np_id)
payment_ids_expr = Qx.select('DISTINCT payments.id')
.from(:payments)
.left_join(
[:charges, 'charges.payment_id=payments.id'],
[:refunds, 'refunds.payment_id=payments.id'],
[:disputes, 'disputes.payment_id=payments.id']
)
.where('payments.nonprofit_id=$id', id: np_id)
.and_where("refunds.payment_id IS NOT NULL OR charges.payment_id IS NOT NULL OR disputes.payment_id IS NOT NULL")
.and_where(%Q(
(refunds.payment_id IS NOT NULL AND (refunds.disbursed IS NULL OR refunds.disbursed='f'))
OR (charges.status='available' OR charges.status='pending')
OR (disputes.status='lost')
))
return Qx.select(
'coalesce(SUM(payments.gross_amount), 0) AS gross_amount',
'coalesce(SUM(payments.fee_total), 0) AS fee_total',
'coalesce(SUM(payments.net_amount), 0) AS net_amount',
'COUNT(payments.*) AS count'
)
.from(:payments)
.where("payments.id IN ($ids)", ids: payment_ids_expr)
.execute
.first
end
end