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 +4 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +32 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/lib/king_dta/account.rb +18 -0
- data/lib/king_dta/booking.rb +47 -0
- data/lib/king_dta/dtaus.rb +308 -0
- data/lib/king_dta/helper.rb +33 -0
- data/lib/king_dtaus.rb +5 -0
- data/spec/booking_spec.rb +20 -0
- data/spec/dtaus_spec.rb +106 -0
- data/spec/helper_spec.rb +21 -0
- data/spec/spec_helper.rb +43 -0
- metadata +85 -0
data/.gitignore
ADDED
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,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
|
data/spec/dtaus_spec.rb
ADDED
@@ -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
|
data/spec/helper_spec.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|