king_dtaus 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ nbproject/*
2
+ coverage/*
3
+ rdoc/*
4
+ pkg/*
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 [Georg Leciejewski SalesKing GmbH]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,32 @@
1
+ = Dtaus
2
+
3
+ DTAUS is a format for german bank transfers and is short for
4
+ "Datenträgeraustausch". The format itself totally sucks because it was
5
+ established in the last century, to be used on floppy disks. Still almost
6
+ all german banks use it(they only seem innovative at robbing), and it is
7
+ therefore supported in common banking programs too.
8
+
9
+ This gem saves you all the trouble when generating dtaus-text.
10
+
11
+ == Install
12
+
13
+ gem install king_dtaus
14
+
15
+ == Features
16
+
17
+
18
+
19
+ == Example
20
+
21
+ TODO - read the specs
22
+
23
+
24
+ == Credits
25
+
26
+ This gem used https://rubygems.org/gems/DTAUS as a starting point.
27
+ It was disected, turned into a real class structure, bugs where fixed and
28
+ of course a full test suite ensures its functionality.
29
+
30
+ Copyright (c) 2009-2010 Georg Leciejewski,
31
+ Bugfixes and enhancements by Georg Ledermann
32
+ released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+ require 'spec/rake/spectask'
4
+
5
+ desc 'Default: run specs.'
6
+ task :default => :spec
7
+
8
+ spec_files = Rake::FileList["spec/**/*_spec.rb"]
9
+
10
+ desc "Run specs"
11
+ Spec::Rake::SpecTask.new do |t|
12
+ t.spec_files = spec_files
13
+ t.spec_opts = ["-c"]
14
+ end
15
+
16
+ desc "Generate code coverage"
17
+ Spec::Rake::SpecTask.new(:coverage) do |t|
18
+ t.spec_files = spec_files
19
+ t.rcov = true
20
+ t.rcov_opts = ['--exclude', 'spec,/var/lib/gems']
21
+ end
22
+
23
+ desc 'Generate documentation'
24
+ Rake::RDocTask.new(:rdoc) do |rdoc|
25
+ rdoc.rdoc_dir = 'rdoc'
26
+ rdoc.title = 'dtaus'
27
+ rdoc.options << '--line-numbers' << '--inline-source'
28
+ rdoc.rdoc_files.include('README')
29
+ rdoc.rdoc_files.include('lib/**/*.rb')
30
+ end
31
+
32
+ begin
33
+ require 'jeweler'
34
+ Jeweler::Tasks.new do |gem|
35
+ gem.name = "king_dtaus"
36
+ gem.summary = %Q{Generate DTAUS strings and files}
37
+ gem.description = %Q{DTAUS is a text-based format, to create bank transfers for german banks. This gem helps with the creation of those transfer files.}
38
+ gem.email = "gl@salesking.eu"
39
+ gem.homepage = "http://github.com/salesking/king_dtaus"
40
+ gem.authors = ["Georg Leciejewski", "Georg Ledermann"]
41
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
42
+ end
43
+ Jeweler::GemcutterTasks.new
44
+ rescue LoadError
45
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
46
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require "#{File.dirname(__FILE__)}/lib/king_dtaus"
@@ -0,0 +1,18 @@
1
+ module KingDta
2
+ #Kontodaten verwalten mit Name des Inhabers und Bank, Bankleitzahl und Kontonummer.
3
+ class Account
4
+ include KingDta::Helper
5
+ # dta~ jeweilige Feld in DTAUS-Norm
6
+ attr_reader :bank_account_number, :bank_number, :owner, :kunnr, :dtakunnr
7
+ def initialize( ban, bank_number, owner, kunnr="" )
8
+ @bank_account_number = ban.kind_of?( Integer ) ? ban : ban.gsub(/\s/, '').to_i
9
+ @bank_number = bank_number.kind_of?( Integer ) ? bank_number : bank_number.gsub(/\s/, '').to_i
10
+ @owner= convert_text( owner )
11
+ @kunnr = kunnr.gsub(/\s/, '').to_i
12
+ raise "Invalid bank account number #{ban}" if @bank_account_number == 0
13
+ raise "BLZnummer #{bank_number} ungültig" if @bank_number == 0
14
+ raise "Invalid account owner #{owner}" unless @owner.kind_of?(String)
15
+ # @dtakunnr = convert_text( @kunnr )
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,47 @@
1
+ module KingDta
2
+ class Booking
3
+ include KingDta::Helper
4
+
5
+ # Die am häufigsten benötigten Textschlüssel
6
+ LASTSCHRIFT_ABBUCHUNG = '04000'
7
+ LASTSCHRIFT_EINZUGSERMAECHTIGUNG = '05000'
8
+ UEBERWEISUNG_GUTSCHRIFT = '51000'
9
+
10
+ attr_reader :value, :account, :text, :schluessel
11
+ #Eine Buchung ist definiert durch:
12
+ #- Konto (siehe Klasse Konto
13
+ #- Betrag
14
+ # Der Betrag kann , oder . als Dezimaltrenner enthalten.
15
+ #- optional Buchungstext
16
+ def initialize( account, value, text=nil, schluessel=nil )
17
+ raise "Hey, a booking should have an Account" unless account.kind_of?( Account )
18
+ @account = account
19
+ @text = text ? convert_text( text ) : text
20
+ @schluessel = schluessel
21
+ if value.is_a?(String)
22
+ value = BigDecimal.new value.sub(',', '.')
23
+ elsif value.is_a?(Numeric)
24
+ value = BigDecimal.new value.to_s
25
+ else
26
+ raise "Übergabefehler: Betrag ist kein String/Numeric"
27
+ end
28
+ value = ( value * 100 ).to_i #€-Cent
29
+ if value == 0
30
+ raise "Booking Value is 0"
31
+ elsif value > 0
32
+ @value = value
33
+ @pos = true
34
+ else
35
+ @value = - value
36
+ @pos = false
37
+ end
38
+ end
39
+
40
+ def text=(text)
41
+ @text = convert_text( text )
42
+ end
43
+
44
+ def pos?; @pos end
45
+
46
+ end #class Buchung
47
+ end #module dtaus
@@ -0,0 +1,308 @@
1
+ #Erstelle eine DTAUS-Datei.
2
+ #Typ ist 'LK' (Lastschrift Kunde) oder 'GK' (Gutschrift Kunde)
3
+ #
4
+ #Infos zu DTAUS: http://www.infodrom.org/projects/dtaus/dtaus.php3
5
+ #
6
+ #Die Methoden müssen in der Reihenfolge:
7
+ #- account (definieren des eigenen Kontos)
8
+ #- bookings
9
+ #- dtaDatei/begleitblatt
10
+ #abgearbeitet werden.
11
+ #
12
+ #Kontodaten verwalten mit Name des Inhabers und Bank, Bankleitzahl und Kontonummer.
13
+ module KingDta
14
+ class Dtaus
15
+ include KingDta::Helper
16
+ attr_reader :sum_bank_account_numbers, :sum_bank_numbers, :sumBetrag, :default_text
17
+
18
+ #Zieldatei und Datum der Datei werden definiert
19
+ # ==== Parameter
20
+ def initialize( typ, datum=Date.today )
21
+ raise "Wrong date format. Make it a Time or Date object with yyyy-mm-dd" unless datum.respond_to?(:strftime)
22
+ raise "Unknown order type: #{typ}. Allowed Values are LK, GK" unless ['LK','GK'].include?(typ)
23
+ @datum = datum
24
+ @typ = typ
25
+ @value_pos = true #alle Beträge sind positiv. Variable wird mit erstem Eintrag geändert
26
+ @closed = false
27
+ @default_text = '' # default verwendungzweck
28
+ end
29
+
30
+ # Übergabe der eigenen Kontodaten als Objekt der Klasse Konto
31
+ def account=( account )
32
+ raise "Come on i need an Account object" unless account.kind_of?( Account )
33
+ @account = account
34
+ end
35
+
36
+ # The dtaus format as string. all data is appended to it during creation
37
+ def dta_string
38
+ @dta_string ||= ''
39
+ end
40
+ def bookings
41
+ @bookings ||= []
42
+ end
43
+ # default text used on all bookings with emtpy text
44
+ def default_text=(text='')
45
+ @default_text = convert_text( text )
46
+ end
47
+
48
+ #Eine Buchung hinzufügen. Es wird geprüft, ob das Vorzeichen identisch mit den bisherigen Vorzeichen ist.
49
+ def add ( booking )
50
+ raise "The file has alreay been closed, cannot add new booking" if @closed
51
+ #Die erste Buchung bestimmt, ob alle Beträge positiv oder negativ sind.
52
+ #alle Beträge sind positiv. Variable wird mit erstem Eintrag geändert
53
+ @value_pos = booking.pos? if bookings.empty? == []
54
+ raise "The prefix within bookings changed from #{@value_pos} to #{booking.pos?}" if @value_pos != booking.pos?
55
+ bookings << booking
56
+ end
57
+
58
+ # Creates the whole dta string(in the right order) and returns it
59
+ def create
60
+ raise "Cannot create DTAUS without bookings" if bookings.empty?
61
+ @closed = true
62
+ # cleanup before we start
63
+ @dta_string = dta_string.empty? ? dta_string : ''
64
+ set_checksums
65
+ add_a
66
+ bookings.each{ |b| add_c( b) }
67
+ add_e
68
+ dta_string
69
+ end
70
+
71
+ def set_checksums
72
+ @sum_bank_account_numbers, @sum_bank_numbers, @sumBetrag = 0,0,0
73
+ bookings.each do |b|
74
+ @sum_bank_account_numbers += b.account.bank_account_number
75
+ @sum_bank_numbers += b.account.bank_number
76
+ @sumBetrag+= b.value
77
+ end
78
+ end
79
+
80
+ #Create a DTA-File
81
+ def create_file(filename ='DTAUS0.TXT')
82
+ file = open( filename, 'w')
83
+ file << create
84
+ file.close()
85
+ print "#{filename} erstellt , #{@bookings.size} Einträge\n"
86
+ end
87
+
88
+
89
+ #Erstellen A-Segment der DTAUS-Datei
90
+ #Aufbau des Segments:
91
+ # Nr. Start Länge Beschreibung
92
+ # 1 0 4 Zeichen Länge des Datensatzes, immer 128 Bytes, also immer "0128"
93
+ # 2 4 1 Zeichen Datensatz-Typ, immer 'A'
94
+ # 3 5 2 Zeichen Art der Transaktionen
95
+ # "LB" für Lastschriften Bankseitig
96
+ # "LK" für Lastschriften Kundenseitig
97
+ # "GB" für Gutschriften Bankseitig
98
+ # "GK" für Gutschriften Kundenseitig
99
+ # 4 7 8 Zeichen Bankleitzahl des Auftraggebers
100
+ # 5 15 8 Zeichen CST, "00000000", nur belegt, wenn Diskettenabsender Kreditinstitut
101
+ # 6 23 27 Zeichen Name des Auftraggebers
102
+ # 7 50 6 Zeichen aktuelles Datum im Format DDMMJJ
103
+ # 8 56 4 Zeichen CST, " " (Blanks)
104
+ # 9 60 10 Zeichen Kontonummer des Auftraggebers
105
+ # 10 70 10 Zeichen Optionale Referenznummer
106
+ # 11a 80 15 Zeichen Reserviert, 15 Blanks
107
+ # 11b 95 8 Zeichen Ausführungsdatum im Format DDMMJJJJ. Nicht jünger als Erstellungsdatum (A7), jedoch höchstens 15 Kalendertage später. Sonst Blanks.
108
+ # 11c 103 24 Zeichen Reserviert, 24 Blanks
109
+ # 12 127 1 Zeichen Währungskennzeichen
110
+ # " " = DM
111
+ # "1" = Euro
112
+ # Insgesamt 128 Zeichen
113
+ def add_a
114
+ data = '0128'
115
+ data += 'A' #Segment
116
+ data += @typ #Lastschriften Kunde
117
+ data += '%8i' % @account.bank_number #.rjust(8) #bank_number
118
+ data += '%08i' % 0 #belegt, wenn Bank
119
+ data += '%-27.27s' % @account.owner
120
+ data += @datum.strftime("%d%m%y") #aktuelles Datum im Format DDMMJJ
121
+ data += ' ' * 4 #bankinternes Feld
122
+ data += '%010i' % @account.bank_account_number
123
+ data += '%010i' % 0 #Referenznummer
124
+ data += ' ' * 15 #Reserve
125
+ data += '%8s' % @datum.strftime("%d%m%Y") #Ausführungsdatum (ja hier 8 Stellen, Erzeugungsdat. hat 6 Stellen)
126
+ data += ' ' * 24 #Reserve
127
+ data += '1' #Kennzeichen Euro
128
+ raise "DTAUS: Längenfehler A (#{data.size} <> 128)\n" if data.size != 128
129
+ dta_string << data
130
+ end #dataA
131
+
132
+ #Erstellen C-Segmente (Buchungen mit Texten) der DTAUS-Datei
133
+ #Aufbau:
134
+ # Nr. St Länge Beschreibung
135
+ # 1 0 4 Zeichen Länge des Datensatzes, 187 + x * 29 (x..Anzahl Erweiterungsteile)
136
+ # 2 4 1 Zeichen Datensatz-Typ, immer 'C'
137
+ # 3 5 8 Zeichen Bankleitzahl des Auftraggebers (optional)
138
+ # 4 13 8 Zeichen Bankleitzahl des Kunden
139
+ # 5 21 10 Zeichen Kontonummer des Kunden
140
+ # 6 31 13 Zeichen Verschiedenes
141
+ # 1. Zeichen: "0"
142
+ # 2. - 12. Zeichen: interne Kundennummer oder Nullen
143
+ # 13. Zeichen: "0"
144
+ # Die interne Nummer wird vom erstbeauftragten Institut zum endbegünstigten Institut weitergeleitet. Die Weitergabe der internenen Nummer an den Überweisungsempfänger ist der Zahlstelle freigestellt.
145
+ # 7 44 5 Zeichen Art der Transaktion (7a: 2 Zeichen, 7b: 3 Zeichen)
146
+ # "04000" Lastschrift des Abbuchungsauftragsverfahren
147
+ # "05000" Lastschrift des Einzugsermächtigungsverfahren
148
+ # "05005" Lastschrift aus Verfügung im elektronischen Cash-System
149
+ # "05006" Wie 05005 mit ausländischen Karten
150
+ # "51000" Überweisungs-Gutschrift
151
+ # "53000" Überweisung Lohn/Gehalt/Rente
152
+ # "5400J" Vermögenswirksame Leistung (VL) ohne Sparzulage
153
+ # "5400J" Vermögenswirksame Leistung (VL) mit Sparzulage
154
+ # "56000" Überweisung öffentlicher Kassen
155
+ # Die im Textschlüssel mit J bezeichnete Stelle, wird bei Übernahme in eine Zahlung automatisch mit der jeweils aktuellen Jahresendziffer (7, wenn 97) ersetzt.
156
+ # 8 49 1 Zeichen Reserviert, " " (Blank)
157
+ # 9 50 11 Zeichen Betrag in DM
158
+ # 10 61 8 Zeichen Bankleitzahl des Auftraggebers
159
+ # 11 69 10 Zeichen Kontonummer des Auftraggebers
160
+ # 12 79 11 Zeichen Betrag in Euro einschließlich Nachkommastellen, nur belegt, wenn Euro als Währung angegeben wurde (A12, C17a), sonst Nullen
161
+ # 13 90 3 Zeichen Reserviert, 3 Blanks
162
+ # 14a 93 27 Zeichen Name des Kunden
163
+ # 14b 120 8 Zeichen Reserviert, 8 Blanks
164
+ # Insgesamt 128 Zeichen
165
+ #
166
+ # 15 128 27 Zeichen Name des Auftraggebers
167
+ # 16 155 27 Zeichen Verwendungszweck
168
+ # 17a 182 1 Zeichen Währungskennzeichen
169
+ # " " = DM
170
+ # "1" = Euro
171
+ # 17b 183 2 Zeichen Reserviert, 2 Blanks
172
+ # 18 185 2 Zeichen Anzahl der Erweiterungsdatensätze, "00" bis "15"
173
+ # 19 187 2 Zeichen Typ (1. Erweiterungsdatensatz)
174
+ # "01" Name des Kunden
175
+ # "02" Verwendungszweck
176
+ # "03" Name des Auftraggebers
177
+ # 20 189 27 Zeichen Beschreibung gemäß Typ
178
+ # 21 216 2 Zeichen wie C19, oder Blanks (2. Erweiterungsdatensatz)
179
+ # 22 218 27 Zeichen wie C20, oder Blanks
180
+ # 23 245 11 Zeichen 11 Blanks
181
+ # Insgesamt 256 Zeichen, kann wiederholt werden (max 3 mal)
182
+ # ==== Parameter
183
+ # booking<Object>::Booking object to be written to c-sektion
184
+ # ==== Returns
185
+ # <String>:: The current dta_string
186
+ def add_c( booking)
187
+ zahlungsart = if @typ == 'LK'
188
+ booking.schluessel || Booking::LASTSCHRIFT_EINZUGSERMAECHTIGUNG
189
+ elsif @typ == 'GK'
190
+ booking.schluessel || Booking::UEBERWEISUNG_GUTSCHRIFT
191
+ else
192
+ raise 'Wrong booking type'
193
+ end
194
+ #Extended segments Long name & booking texts
195
+ exts = [] #('xx', 'inhalt') xx: 01=Name 02=Verwendung 03=Name
196
+ # 1. Satzabschnitt
197
+ #data1 = '%4i' % ?? #Satzlänge kommt später
198
+ data1 = 'C'
199
+ data1 += '%08i' % 0 #freigestellt
200
+ data1 += '%08i' % booking.account.bank_number
201
+ data1 += '%010i' % booking.account.bank_account_number
202
+ data1 += '0%011i0' % booking.account.kunnr #interne Kundennummer
203
+ data1 += zahlungsart
204
+ data1 += ' ' #bankintern
205
+ data1 += '0' * 11 #Reserve
206
+ data1 += '%08i' % @account.bank_number
207
+ data1 += '%010i' % @account.bank_account_number
208
+ data1 += '%011i' % booking.value #Betrag in Euro einschl. Nachkomma
209
+ data1 += ' ' * 3
210
+ data1 += '%-27.27s' % booking.account.owner #Name Begünstigter/Zahlungspflichtiger
211
+ exts << ['01', booking.account.owner[27..999] ] if booking.account.owner.size > 27
212
+ data1 += ' ' * 8
213
+ #Einfügen erst möglich, wenn Satzlänge bekannt
214
+
215
+ # 2. Satzabschnitt
216
+ data2 = "%27.27s" % @account.owner
217
+ zweck = booking.text || default_text
218
+ #Erste 27 Zeichen
219
+ #Wenn text < 26 Zeichen, dann mit spaces auffüllen.
220
+ data2 += zweck[0..26].ljust(27)
221
+ zweck = zweck[27..999]
222
+ # cur text into 27 long pieces
223
+ while zweck && zweck.size > 0 && exts.size < 13
224
+ exts << ['02', zweck.ljust(27) ]
225
+ zweck = zweck[27..999]
226
+ end
227
+ exts << ['03', @account.owner[27..999] ] if @account.owner.size > 27
228
+
229
+ data2 += '1' #Währungskennzeichen
230
+ data2 += ' ' * 2
231
+ # Gesamte Satzlänge ermitteln ( data1(+4) + data2 + Erweiterungen )
232
+ data1 = "%04i#{data1}" % ( data1.size + 4 + data2.size+ 2 + exts.size * 29 )
233
+ raise "DTAUS: Längenfehler C/1 #{data1.size}, #{booking.account.name}" unless data1.size == 128
234
+ dta_string << data1
235
+ #Anzahl Erweiterungen anfügen
236
+ data2 += '%02i' % exts.size #Anzahl Erweiterungsteile
237
+ #Die ersten zwei Erweiterungen gehen in data2,
238
+ #Satz 3/4/5 à 4 Erweiterungen -> max. 14 Erweiterungen (ich ignoriere möglichen Satz 6)
239
+ exts += [ ['00', "" ] ] * (14 - exts.size)
240
+ #Die ersten zwei Erweiterungen gehen in data2, wenn leer nur mit blanks als füllung
241
+ exts[0..1].each{|e| data2 += "%2.2s%-27.27s" % format_ext(e[0], e[1]) }
242
+ data2 += ' ' * 11
243
+ # add the final piece of the second C section
244
+ raise "DTAUS: Längenfehler C/2 #{data2.size}, #{booking.account.name}" if data2.size != 128
245
+ dta_string << data2
246
+ #Erstellen der Texterweiterungen à vier Stück
247
+ add_ext( exts[2..5] )
248
+ add_ext( exts[6..9] )
249
+ add_ext( exts[10..13] )
250
+ dta_string
251
+ end #dataC
252
+
253
+ # Format an extension so it can be used in string substitution
254
+ # ==== Returns
255
+ # Array[String, String]::[Extesnsion type(01 02 03), 'text content ..']
256
+ def format_ext(type, content)
257
+ ext = ( type == '00' ) ? [' ', ' '] : [ type, convert_text(content) ]
258
+ ext
259
+ end
260
+
261
+ # Add a section-C extension, always containing the section type followed by
262
+ # 4 segments with 27 chars and an ending seperator of 12 blanks
263
+ # Only adds the segement if something is in there
264
+ # ==== Parameter
265
+ # <Array[Array[String,String]]>:: two dim. ary containing: [extension type(01 02 03),string content]
266
+ def add_ext( ext)
267
+ raise "Nur #{ext.size} Erweiterungstexte, 4 benötigt" if ext.size != 4
268
+ str = "%2.2s%-27.27s" % format_ext(ext[0][0], ext[0][1] )
269
+ str += "%2.2s%-27.27s" % format_ext( ext[1][0], ext[1][1] )
270
+ str += "%2.2s%-27.27s" % format_ext( ext[2][0], ext[2][1] )
271
+ str += "%2.2s%-27.27s" % format_ext( ext[3][0], ext[3][1] )
272
+ # Trennzeichen 12 blanks
273
+ str += ' ' * 12
274
+ unless str !~ /\S/ # only add if something is in there .. only whitespace => same as str.blank?
275
+ raise "DTAUS: Längenfehler C/3 #{str.size} " if str.size != 128
276
+ dta_string << str
277
+ end
278
+ end #dataC
279
+
280
+ #Erstellen E-Segment (Prüfsummen) der DTAUS-Datei
281
+ #Aufbau:
282
+ # Nr. Start Länge Beschreibung
283
+ # 1 0 4 Zeichen Länge des Datensatzes, immer 128 Bytes, also immer "0128"
284
+ # 2 4 1 Zeichen Datensatz-Typ, immer 'E'
285
+ # 3 5 5 Zeichen " " (Blanks)
286
+ # 4 10 7 Zeichen Anzahl der Datensätze vom Typ C
287
+ # 5 17 13 Zeichen Kontrollsumme Beträge
288
+ # 6 30 17 Zeichen Kontrollsumme Kontonummern
289
+ # 7 47 17 Zeichen Kontrollsumme Bankleitzahlen
290
+ # 8 64 13 Zeichen Kontrollsumme Euro, nur belegt, wenn Euro als Währung angegeben wurde (A12, C17a)
291
+ # 9 77 51 Zeichen 51 Blanks
292
+ # Insgesamt 128 Zeichen
293
+ def add_e
294
+ str = '0128'
295
+ str += 'E'
296
+ str += ' ' * 5
297
+ str += '%07i' % @bookings.size
298
+ str += '0' * 13 #Reserve
299
+ str += '%017i' % @sum_bank_account_numbers
300
+ str += '%017i' % @sum_bank_numbers
301
+ str += '%013i' % @sumBetrag
302
+ str += ' ' * 51 #Abgrenzung Datensatz
303
+ raise "DTAUS: Längenfehler E #{str.size} <> 128" if str.size != 128
304
+ dta_string << str
305
+ end
306
+ # private :dataA, :dataC, :dataE
307
+ end #class dtaus
308
+ end
@@ -0,0 +1,33 @@
1
+ module KingDta
2
+ module Helper
3
+ #Zeichen umsetzen gemäss DTA-Norm
4
+ def convert_text( text)
5
+ text = text.to_s
6
+ return text if text.empty?
7
+ raise "Text kein String >#{text}< (#{text.class})" unless text.kind_of?( String )
8
+ text.gsub!('Ä', 'AE')
9
+ text.gsub!('Ü', 'UE')
10
+ text.gsub!('Ö', 'OE')
11
+ text.gsub!('ä', 'AE')
12
+ text.gsub!('ü', 'UE')
13
+ text.gsub!('ö', 'OE')
14
+ text.gsub!('ß', 'SS')
15
+
16
+ text.gsub! /[^a-zA-Z0-9\ \.\,\&\-\/\+\*\$\%]/, '' # Remove all invalid chars
17
+
18
+ text = text.upcase.strip
19
+ text
20
+ # text = text.to_s()
21
+ # puts "Text kein String >#{text}< (#{text.class})" if ! text.kind_of?( String )
22
+ # text = text.upcase()
23
+ # text = text.gsub('Ä', 'AE')
24
+ # text = text.gsub('Ü', 'UE')
25
+ # text = text.gsub('Ö', 'OE')
26
+ # text = text.gsub('ä', 'AE')
27
+ # text = text.gsub('ü', 'UE')
28
+ # text = text.gsub('ö', 'OE')
29
+ # text = text.gsub('ß', 'SS')
30
+ # return text = text.strip
31
+ end
32
+ end
33
+ end
data/lib/king_dtaus.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "bigdecimal"
2
+ require "#{File.dirname(__FILE__)}/king_dta/helper"
3
+ require "#{File.dirname(__FILE__)}/king_dta/account"
4
+ require "#{File.dirname(__FILE__)}/king_dta/booking"
5
+ require "#{File.dirname(__FILE__)}/king_dta/dtaus"
@@ -0,0 +1,20 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe KingDta::Booking do
4
+
5
+ before :each do
6
+ kto1 = test_kto1
7
+ kto2 = test_kto2
8
+ @account = KingDta::Account.new( kto2.nr, kto2.blz, kto2.name, kto2.bank )
9
+ end
10
+
11
+ it "should have no rounding error for string" do
12
+ booking = KingDta::Booking.new(@account, "159.73")
13
+ booking.value.should == 15973
14
+ end
15
+
16
+ it "should have no rounding error for float" do
17
+ booking = KingDta::Booking.new(@account, 159.73)
18
+ booking.value.should == 15973
19
+ end
20
+ end
@@ -0,0 +1,106 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ # All Test DTA output strings are validated with sFirm => lokal Sparkassen Software
4
+ describe KingDta::Dtaus do
5
+
6
+ before :each do
7
+ @dtaus = KingDta::Dtaus.new('LK', Date.today)
8
+ @kto1 = test_kto1
9
+ @kto2 = test_kto2
10
+ @dtaus.account = KingDta::Account.new( @kto1.nr, @kto1.blz, @kto1.name, @kto1.bank )
11
+ @booking = KingDta::Booking.new(
12
+ KingDta::Account.new( @kto2.nr, @kto2.blz, @kto2.name, @kto2.bank ),
13
+ 220.25 )
14
+ end
15
+
16
+ it "should not init without values" do
17
+ lambda{ KingDta::Dtaus.new }.should raise_error(ArgumentError)
18
+ end
19
+ it "should init with valid values" do
20
+ lambda{ KingDta::Dtaus.new('LK', Date.today) }.should_not raise_error(ArgumentError)
21
+ end
22
+
23
+ it "should create header" do
24
+ str = @dtaus.add_a
25
+ str.length.should == 128
26
+ out = "0128ALK3704004400000000GIMME YOUR MONEY AG #{Date.today.strftime("%d%m%y")} 00028800370000000000 #{Date.today.strftime("%d%m%Y")} 1"
27
+ str.should == out
28
+ #60-70 kontonummer mit nullen aufgefüllt
29
+ str[60...70].should == "000#{test_kto1.nr}"
30
+ str.should include(test_kto1.blz)
31
+ end
32
+
33
+ it "should create checksums" do
34
+ @dtaus.add(@booking)
35
+ @dtaus.set_checksums
36
+ @dtaus.sum_bank_account_numbers.should == 2787777
37
+ @dtaus.sum_bank_numbers.should == 37040044
38
+ @dtaus.sumBetrag.should == 22025
39
+ end
40
+
41
+ it "should create c-sektion with booking text at 19" do
42
+ @dtaus.add(@booking)
43
+ @dtaus.bookings.first.text = 'SalesKing Monatsbeitrag 08/10 Freelancer Version'
44
+ @dtaus.add_c(@booking)
45
+ str = @dtaus.dta_string
46
+ str.length.should == 256
47
+ str.should include(@kto2.name.upcase)
48
+ out = "0216C00000000370400440002787777000000000000005000 0000000000037040044000288003700000022025 PETER & MAY GMBH GIMME YOUR MONEY AGSALESKING MONATSBEITRAG 08/1 010210 FREELANCER VERSION "
49
+ str.should == out
50
+ end
51
+
52
+ it "should create c-sektion with default booking text" do
53
+ @dtaus.default_text = 'Default verwendungszweck'
54
+ @dtaus.add_c(@booking)
55
+ str = @dtaus.dta_string
56
+ str.length.should == 256
57
+ str.should include(@kto2.name.upcase)
58
+ out = "0187C00000000370400440002787777000000000000005000 0000000000037040044000288003700000022025 PETER & MAY GMBH GIMME YOUR MONEY AGDEFAULT VERWENDUNGSZWECK 1 00 "
59
+ str.should == out
60
+ end
61
+
62
+ it "should create the whole dta string with a single booking" do
63
+ @dtaus.default_text = 'Default verwendungszweck'
64
+ @dtaus.add(@booking)
65
+ str = @dtaus.create
66
+ str.length.should == 512
67
+ str.should include(@kto1.name.upcase)
68
+ str.should include(@kto2.name.upcase)
69
+ str.should include(@dtaus.default_text.upcase)
70
+ out = "0128ALK3704004400000000GIMME YOUR MONEY AG #{Date.today.strftime("%d%m%y")} 00028800370000000000 #{Date.today.strftime("%d%m%Y")} 1"+
71
+ "0187C00000000370400440002787777000000000000005000 0000000000037040044000288003700000022025 PETER & MAY GMBH GIMME YOUR MONEY AGDEFAULT VERWENDUNGSZWECK 1 00 "+
72
+ "0128E 0000001000000000000000000000002787777000000000370400440000000022025 "
73
+ str.should == out
74
+ end
75
+
76
+ it "should create whole dta string with long booking text in extension" do
77
+ @dtaus.add(@booking)
78
+ @dtaus.bookings.first.text = 'Rgn R-3456-0102220 Monatsbeitrag 08/10 Freelancer Version Vielen Dank Ihre SalesKing GmbH'
79
+ str = @dtaus.create
80
+ str.length.should == 640
81
+ str.should include(@kto2.name.upcase)
82
+ out = "0128ALK3704004400000000GIMME YOUR MONEY AG #{Date.today.strftime("%d%m%y")} 00028800370000000000 #{Date.today.strftime("%d%m%Y")} 1"+
83
+ "0274C00000000370400440002787777000000000000005000 0000000000037040044000288003700000022025 PETER & MAY GMBH GIMME YOUR MONEY AGRGN R-3456-0102220 MONATSBE1 0302ITRAG 08/10 FREELANCER VERS02ION VIELEN DANK IHRE SALESK 02ING GMBH "+
84
+ "0128E 0000001000000000000000000000002787777000000000370400440000000022025 "
85
+ str.should == out
86
+ end
87
+
88
+ it "should create the whole dta string with a lot of bookings" do
89
+ @dtaus.default_text = 'Default Verwendungszweck'
90
+ 6.times { @dtaus.add(@booking) }
91
+ str = @dtaus.create
92
+ str.length.should == 1792
93
+ str.should include(@kto1.name.upcase)
94
+ str.should include(@kto2.name.upcase)
95
+ str.should include(@dtaus.default_text.upcase)
96
+ out = "0128ALK3704004400000000GIMME YOUR MONEY AG #{Date.today.strftime("%d%m%y")} 00028800370000000000 #{Date.today.strftime("%d%m%Y")} 1"+
97
+ "0187C00000000370400440002787777000000000000005000 0000000000037040044000288003700000022025 PETER & MAY GMBH GIMME YOUR MONEY AGDEFAULT VERWENDUNGSZWECK 1 00 "+
98
+ "0187C00000000370400440002787777000000000000005000 0000000000037040044000288003700000022025 PETER & MAY GMBH GIMME YOUR MONEY AGDEFAULT VERWENDUNGSZWECK 1 00 "+
99
+ "0187C00000000370400440002787777000000000000005000 0000000000037040044000288003700000022025 PETER & MAY GMBH GIMME YOUR MONEY AGDEFAULT VERWENDUNGSZWECK 1 00 "+
100
+ "0187C00000000370400440002787777000000000000005000 0000000000037040044000288003700000022025 PETER & MAY GMBH GIMME YOUR MONEY AGDEFAULT VERWENDUNGSZWECK 1 00 "+
101
+ "0187C00000000370400440002787777000000000000005000 0000000000037040044000288003700000022025 PETER & MAY GMBH GIMME YOUR MONEY AGDEFAULT VERWENDUNGSZWECK 1 00 "+
102
+ "0187C00000000370400440002787777000000000000005000 0000000000037040044000288003700000022025 PETER & MAY GMBH GIMME YOUR MONEY AGDEFAULT VERWENDUNGSZWECK 1 00 "+
103
+ "0128E 0000006000000000000000000000016726662000000002222402640000000132150 "
104
+ str.should == out
105
+ end
106
+ end
@@ -0,0 +1,21 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe KingDta::Helper do
4
+ include KingDta::Helper
5
+
6
+ it "should convert to upper case" do
7
+ convert_text('2&2 GmbH & Co. KG').should == '2&2 GMBH & CO. KG'
8
+ end
9
+
10
+ it "should remove invalid chars" do
11
+ convert_text('@()"=<>!§').should == ''
12
+ end
13
+
14
+ it "should leave valid chars" do
15
+ convert_text('abc-ABC-0123- .,&/+*$%').should == 'ABC-ABC-0123- .,&/+*$%'
16
+ end
17
+
18
+ it "should convert umlaute" do
19
+ convert_text('üöäÜÖÄß').should == 'UEOEAEUEOEAESS'
20
+ end
21
+ end
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'ostruct'
4
+ require 'date'
5
+ require "#{File.dirname(__FILE__)}/../lib/king_dtaus"
6
+
7
+ #Filename der eigenen Kontodaten
8
+ # Beispiel:
9
+ # typ:LK
10
+ # blz:99988811
11
+ # konto:123456
12
+ # bank:Nord-Ostschwaebische Sandbank
13
+ #
14
+ # name:Jodelverein Holladrioe 1863 e.V.
15
+ # zweck:Mitgliedsbeitrag 2003
16
+ # Der Typ ist LK oder GK. Siehe Option -t
17
+ # zweck ist ein optionaler Default-Text, der verwendet wird,
18
+ # falls eine Buchung keinen Text hat.
19
+ # Die Adressdaten der Bank sind optional und werdezum erzeugen
20
+ # des Begleitblatts verwendet
21
+ # bankstrasse:Kieselweg 3
22
+ # bankplz:0815
23
+ # bankort:Felsblock
24
+ def test_kto1
25
+ opts = { :nr => '2880037', :blz => '37040044', :name =>'Gimme your Money AG',
26
+ :bank => 'Commerzbank Köln', :zweck => 'Monatsbeitrag' }
27
+ TestKonto.new(opts)
28
+ end
29
+
30
+ def test_kto2
31
+ opts = { :nr => '2787777', :blz => '37040044', :name =>'Peter & May GmbH',
32
+ :bank => 'Commerzbank Köln', :zweck => 'Monatsbeitrag' }
33
+ TestKonto.new(opts)
34
+ end
35
+
36
+ def test_kto3
37
+ opts = { :nr => '2787777', :blz => '37040044', :name =>'Andrew Müller',
38
+ :bank => 'Commerzbank Köln', :zweck => 'Monatsbeitrag' }
39
+ TestKonto.new(opts)
40
+ end
41
+
42
+ # the test account responds to everything
43
+ class TestKonto < OpenStruct; end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: king_dtaus
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Georg Leciejewski
14
+ - Georg Ledermann
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-09-14 00:00:00 +02:00
20
+ default_executable:
21
+ dependencies: []
22
+
23
+ description: DTAUS is a text-based format, to create bank transfers for german banks. This gem helps with the creation of those transfer files.
24
+ email: gl@salesking.eu
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files:
30
+ - README.rdoc
31
+ files:
32
+ - .gitignore
33
+ - MIT-LICENSE
34
+ - README.rdoc
35
+ - Rakefile
36
+ - VERSION
37
+ - init.rb
38
+ - lib/king_dta/account.rb
39
+ - lib/king_dta/booking.rb
40
+ - lib/king_dta/dtaus.rb
41
+ - lib/king_dta/helper.rb
42
+ - lib/king_dtaus.rb
43
+ - spec/booking_spec.rb
44
+ - spec/dtaus_spec.rb
45
+ - spec/helper_spec.rb
46
+ - spec/spec_helper.rb
47
+ has_rdoc: true
48
+ homepage: http://github.com/salesking/king_dtaus
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options:
53
+ - --charset=UTF-8
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.3.7
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Generate DTAUS strings and files
81
+ test_files:
82
+ - spec/spec_helper.rb
83
+ - spec/booking_spec.rb
84
+ - spec/helper_spec.rb
85
+ - spec/dtaus_spec.rb