Last active
March 11, 2018 19:44
-
-
Save markburns/d4a0368e6400bdc852d0 to your computer and use it in GitHub Desktop.
MetroBank UK to FreeAgent CSV format converter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "csv" | |
class MetroBankCsvConverter | |
def initialize(lines) | |
lines =lines.split("\n") | |
lines.shift(3) | |
lines = lines.join("\n") | |
@raw_csv = CSV.parse(lines, headers: headers) | |
end | |
def headers | |
["date","type","reference","currency","money_out","money_in","balance"] | |
end | |
def to_freeagent_csv(start_date=nil, end_date=nil) | |
CSV.generate do |csv| | |
transactions.each do |t| | |
if t.within(start_date, end_date) | |
csv << [t.date_string, t.amount, t.long_description] | |
end | |
end | |
end | |
end | |
def columns | |
@raw_csv.headers | |
end | |
def transaction_count | |
transactions.count | |
end | |
def transactions | |
transactions = [] | |
current_transaction = nil | |
@raw_csv.each do |row| | |
if row["date"].length > 0 | |
current_transaction = Transaction.new(row) | |
transactions << current_transaction | |
else | |
current_transaction << Detail.new(row) | |
end | |
end | |
transactions | |
end | |
module CsvRowWrapper | |
def initialize(row) | |
@row = row | |
end | |
def method_missing(meth, *args, &block) | |
if @row.headers.include?(meth.to_s) | |
@row[meth.to_s] | |
else | |
super | |
end | |
end | |
end | |
class Transaction | |
include CsvRowWrapper | |
extend Forwardable | |
def_delegator :@details, :<< | |
def initialize(row) | |
super row | |
@details = Details.new | |
end | |
def within(start_date, end_date) | |
return true if start_date.nil? && end_date.nil? | |
start_date, end_date = Date.parse(start_date), Date.parse(end_date) | |
outside_range = false | |
if start_date && start_date > date | |
outside_range = true | |
end | |
if end_date && end_date < date | |
outside_range = true | |
end | |
!outside_range | |
end | |
def long_description | |
[type, reference, details].join(" | ") | |
end | |
def details | |
@details.to_s | |
end | |
def date | |
Date.parse(@row["date"]) | |
end | |
def date_string | |
date.strftime("%d/%m/%Y") | |
end | |
def amount | |
money_in - money_out | |
end | |
def money_in | |
to_money("money_in") | |
end | |
def money_out | |
to_money("money_out") | |
end | |
def balance | |
to_money("balance") | |
end | |
private | |
def to_money(column) | |
@row[column].gsub(/\,/,"").to_f | |
end | |
end | |
class Details < Array | |
def to_s | |
map(&:to_s).join(" | ") | |
end | |
end | |
class Detail | |
include CsvRowWrapper | |
def to_s | |
reference | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "byebug" | |
require "./converter" | |
describe MetroBankCsvConverter do | |
let(:converter) { MetroBankCsvConverter.new lines } | |
let(:lines) { <<-EOF.gsub(/^\s+/, "") | |
"Transaction Search Results" | |
"","","","","","" | |
"Date","Transaction Type","Reference","Currency","Money Out","Money In","Balance" | |
"03 DEC 2014","Outward Faster Payment","1234567","GBP","1,000,000.00","0.00","121,472,236.98" | |
"","","Something something","","","","" | |
"","","More silliness","","","","" | |
"","","Hopefully we handle a comma, even when it's inside a string","","","","" | |
"01 DEC 2014","Card Purchase","ABCDEFG","GBP","134.34","0.00","122,472,236.98" | |
"","","01/12/14 01:17:02","","","","" | |
"","","LINODE.COM","","","","" | |
"","","855-4546633","","","","" | |
"","","USD 86.84@1.54690142","","","","" | |
EOF | |
} | |
it do | |
expect(converter.columns).to eq ["date","type","reference","currency","money_out","money_in","balance"] | |
expect(converter.transaction_count).to eq 2 | |
expect(converter.transactions[0].date_string).to eq "03/12/2014" | |
end | |
it do | |
transaction = converter.transactions[1] | |
expect(transaction.date).to eq Date.parse("2014/12/01") | |
expect(transaction.date_string).to eq "01/12/2014" | |
expect(transaction.type).to eq "Card Purchase" | |
expect(transaction.reference).to eq "ABCDEFG" | |
expect(transaction.currency).to eq "GBP" | |
expect(transaction.amount).to eq -134.34 | |
expect(transaction.money_out).to eq 134.34 | |
expect(transaction.money_in).to eq 0.00 | |
expect(transaction.balance).to eq 122_472_236.98 | |
expect(transaction.details).to eq [ | |
"01/12/14 01:17:02", | |
"LINODE.COM", | |
"855-4546633", | |
"USD 86.84@1.54690142" | |
].join(" | ") | |
expect(converter.transactions[0].details).to eq "Something something | More silliness | Hopefully we handle a comma, even when it's inside a string" | |
end | |
it "Transaction#long_description" do | |
long_description = converter.transactions[0].long_description | |
expect(long_description).to eq "Outward Faster Payment | 1234567 | Something something | More silliness | Hopefully we handle a comma, even when it's inside a string" | |
end | |
describe "#to_freeagent_csv" do | |
it "returns all transactions with no args" do | |
csv = converter.to_freeagent_csv | |
expect(csv).to eq <<-CSV.gsub(/^\s+/,"") | |
03/12/2014,-1000000.0,"Outward Faster Payment | 1234567 | Something something | More silliness | Hopefully we handle a comma, even when it's inside a string" | |
01/12/2014,-134.34,Card Purchase | ABCDEFG | 01/12/14 01:17:02 | LINODE.COM | 855-4546633 | USD 86.84@1.54690142 | |
CSV | |
end | |
it "accepts an inclusive date range" do | |
csv = converter.to_freeagent_csv("2014/12/01", "2014/12/01") | |
expect(csv).to eq <<-CSV.gsub(/^\s+/,"") | |
01/12/2014,-134.34,Card Purchase | ABCDEFG | 01/12/14 01:17:02 | LINODE.COM | 855-4546633 | USD 86.84@1.54690142 | |
CSV | |
csv = converter.to_freeagent_csv("2014/12/03", "2014/12/03") | |
expect(csv).to eq <<-CSV.gsub(/^\s+/,"") | |
03/12/2014,-1000000.0,"Outward Faster Payment | 1234567 | Something something | More silliness | Hopefully we handle a comma, even when it's inside a string" | |
CSV | |
csv = converter.to_freeagent_csv("2014/12/01", "2014/12/03") | |
expect(csv).to eq <<-CSV.gsub(/^\s+/,"") | |
03/12/2014,-1000000.0,"Outward Faster Payment | 1234567 | Something something | More silliness | Hopefully we handle a comma, even when it's inside a string" | |
01/12/2014,-134.34,Card Purchase | ABCDEFG | 01/12/14 01:17:02 | LINODE.COM | 855-4546633 | USD 86.84@1.54690142 | |
CSV | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment