king_dtaus 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|