# frozen_string_literal: true require "csv" require "date" module Reconciliation class GocardlessPayoutsParser Payout = Struct.new( :id, :amount_cents, # Net amount transferred to bank (after all fees) :total_payment_cents, # Gross sum of underlying payments (before fees) :fee_cents, # Total fee deducted (total_payment - amount) :transaction_fee_cents, # Per-transaction fee component :surcharge_fee_cents, # Surcharge fee component :tax_cents, # Tax on fees (informational, already in total fee) :reference, # e.g. "CYANET-DKV4KN8FTM2" — appears in Shine Libellé :status, :arrival_date, keyword_init: true ) def self.parse(csv_path) rows = CSV.read(csv_path, headers: true, encoding: "UTF-8") payouts = rows.filter_map do |row| next if row["status"] != "paid" arrival_date = safe_parse_date(row["arrival_date"]) next unless arrival_date amount_cents = to_cents(row["amount"]) total_payment_cents = to_cents(row["total_payment_amount"]) Payout.new( id: row["id"].to_s.strip, amount_cents: amount_cents, total_payment_cents: total_payment_cents, fee_cents: total_payment_cents - amount_cents, transaction_fee_cents: to_cents(row["transaction_fee_debit"]), surcharge_fee_cents: to_cents(row["surcharge_fee_debit"]), tax_cents: to_cents(row["tax_debit"]), reference: row["reference"].to_s.strip, status: row["status"].to_s.strip, arrival_date: arrival_date ) end $stderr.puts "[GocardlessPayoutsParser] Loaded #{payouts.size} paid payouts from #{csv_path}" payouts end private_class_method def self.to_cents(str) return 0 if str.nil? || str.strip.empty? (str.strip.to_f * 100).round end private_class_method def self.safe_parse_date(str) return nil if str.nil? || str.strip.empty? Date.parse(str.strip) rescue Date::Error, ArgumentError nil end end end