2024-03-19 12:46:28 +01:00
#!/usr/bin/env ruby
require 'cmxl' # for parsing the MT940 file
require 'roo' # for parsing the Excel file
require 'humanize' # for converting numbers to words
Humanize . configure do | config |
config . default_locale = :de # [:en, :es, :fr, :tr, :de, :id], default: :en
config . decimals_as = :number # [:digits, :number], default: :digits
end
# usage ./beitragsquittung.rb <MT940 file> <Excel file> <member id>
# the member ID is optional. If not provided, all members are processed
# the time range is used from the MT940 file
def generate_reciept ( mitgliedsnummer , name , vorname , strasse , hausnummer , plz , ort , betrag , betrag_in_worten , start_datum , end_datum , payments )
puts " Generating reciept for #{ mitgliedsnummer } #{ name } #{ vorname } #{ strasse } #{ hausnummer } #{ plz } #{ ort } #{ betrag } #{ betrag_in_worten } #{ start_datum } #{ end_datum } "
# create a csv file with the payments
CSV . open ( " payments.csv " , " wb " ) do | csv |
csv << [ " datum " , " betrag " , " type " , " verzicht " ]
payments . each do | payment |
csv << [ payment [ :date ] , payment [ :amount ] , payment [ :type ] , payment [ :verzicht ] ]
end
end
# call latex to generate the pdf
system ( " pdflatex \" \\ newcommand{ \\ vorname}{ #{ vorname } } \\ newcommand{ \\ nachname}{ #{ name } } \\ newcommand{ \\ strasse}{ #{ strasse + " " + hausnummer . to_s } } \\ newcommand{ \\ plz}{ #{ plz } } \\ newcommand{ \\ ort}{ #{ ort } } \\ newcommand{ \\ betrag}{ #{ betrag } } \\ newcommand{ \\ betraginworten}{ #{ betrag_in_worten } } \\ newcommand{ \\ startdatum}{ #{ start_datum } } \\ newcommand{ \\ findatum}{ #{ end_datum } } \\ newcommand{ \\ mitgliedsnummer}{ #{ mitgliedsnummer } } \\ input{document.tex} \" " )
end
def get_payments_from_member_id ( mitgliedsnummer )
Cmxl . config [ :statement_separator ] = / \ n-. \ n /m
Cmxl . config [ :raise_line_format_errors ] = true
# get the filename from the command line arguments
filename = ARGV [ 0 ]
# check if the filename is valid or not presented
2024-03-19 23:32:05 +01:00
if filename . nil? || ! File . exist? ( filename )
2024-03-19 12:46:28 +01:00
puts " Enter the filename of the MT940 statement to parse "
filename = gets . chomp
end
# parse the MT940 file
statements = Cmxl . parse ( File . read ( filename ) , :encoding = > 'ISO-8859-1' )
# The statement contains transactions called Mitgliedsbeitrag. They are labled like this: "MREF"=>"Mitgliedsbeitrag Nr. 17" where 17 is the mitgliedsnummer.
# for each payment of the mitgliedsbeitrag create an entry with the date and amount in the following datastructure
payments = [ ]
statements . each do | s |
s . transactions . each do | t |
reference = " "
t . sepa . each do | sf |
# add the second entry of the sepa reference (sf) to the reference string if the first entry contans "MREF"
if sf [ 0 ] == " MREF "
reference = sf [ 1 ]
end
end
# if the mitgliedsnummer is a single digit number, the reference is "Mitgliedsbeitrag Nr. 01" and not "Mitgliedsbeitrag Nr. 1"
# so we have to check for both cases
if reference . include? ( " Mitgliedsbeitrag Nr. #{ mitgliedsnummer . to_s . rjust ( 2 , '0' ) } " ) || reference . include? ( " Mitgliedsbeitrag Nr. #{ mitgliedsnummer } " )
# puts t.entry_date
# puts t.amount
payment = {
date : t . entry_date ,
amount : t . amount ,
type : " Mitgliedsbeitrag " ,
verzicht : " nein "
}
payments << payment
# puts
end
end
end
# get the start and end date of the statements
start_datum = statements [ 0 ] . opening_balance . date . strftime ( " %d.%m.%Y " )
end_datum = statements [ statements . length ( ) - 1 ] . closing_balance . date . strftime ( " %d.%m.%Y " )
return payments , start_datum , end_datum
end
def get_member_details_from_mitgliedsnummer ( mitgliedsnummer )
# get the member details from the excel file
# the member details are stored in the first sheet
# the first row contains the headings of the columns
# the second row contains the values for the first member
# the headings are: Nr., Nachname, Vorname, Geburtsdatum, Straße, Hausnummer, Postleitzahl, Ort
# the values are: 1, Mustermann, Max, 01.01.1970, Musterstraße, 1, 12345, Musterstadt
xlsx = Roo :: Spreadsheet . open ( ARGV [ 1 ] )
# create data structure
member = {
name : xlsx . sheet ( 0 ) . row ( mitgliedsnummer + 1 ) [ 1 ] ,
vorname : xlsx . sheet ( 0 ) . row ( mitgliedsnummer + 1 ) [ 2 ] ,
strasse : xlsx . sheet ( 0 ) . row ( mitgliedsnummer + 1 ) [ 4 ] ,
hausnummer : xlsx . sheet ( 0 ) . row ( mitgliedsnummer + 1 ) [ 5 ] ,
plz : xlsx . sheet ( 0 ) . row ( mitgliedsnummer + 1 ) [ 6 ] ,
ort : xlsx . sheet ( 0 ) . row ( mitgliedsnummer + 1 ) [ 7 ]
}
return member
end
mitgliedsnummer = ARGV [ 2 ] . to_i
if mitgliedsnummer . nil?
# get number of members from the excel file it is the first value of the last row
xlsx = Roo :: Spreadsheet . open ( ARGV [ 1 ] )
anzahl_members = xlsx . sheet ( 0 ) . last_row [ 0 ]
puts " Number of members: #{ anzahl_members } "
# loop over all members
for mitgliedsnummer in 1 .. anzahl_members
puts " Processing member #{ mitgliedsnummer } "
get_payments_from_member_id ( mitgliedsnummer )
generate_reciept ( mitgliedsnummer , name , vorname , strasse , plz , ort , betrag , betrag_in_worten , start_datum , end_datum )
end
else
# get the payments from the MT940 file
payments , start_datum , end_datum = get_payments_from_member_id ( mitgliedsnummer )
puts " Payments: "
total_payment = 0
payments . each do | payment |
2024-03-19 13:41:48 +01:00
puts payment [ :date ] . to_s + " " + payment [ :amount ] . to_s
2024-03-19 12:46:28 +01:00
total_payment += payment [ :amount ]
end
# convert the total payment to a string in german
betrag_in_worten = total_payment . humanize
puts " Total payment: " + total_payment . to_s + " in worten: " + betrag_in_worten
puts
member = get_member_details_from_mitgliedsnummer ( mitgliedsnummer )
generate_reciept ( mitgliedsnummer , member [ :name ] , member [ :vorname ] , member [ :strasse ] , member [ :hausnummer ] , member [ :plz ] , member [ :ort ] , total_payment , betrag_in_worten , start_datum , end_datum , payments )
end