konto_check_ruby 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/README.md ADDED
@@ -0,0 +1,295 @@
1
+ # konto_check_ruby
2
+
3
+ Copyright (C) 2026 tickettoaster GmbH. LGPL 2.1 or later.
4
+ Source: <https://github.com/provideal/konto_check_ruby>
5
+
6
+ A pure Ruby port of [konto_check](https://sourceforge.net/projects/kontocheck/),
7
+ Michael Plugge's C library for validating German bank accounts.
8
+ No C extension, no native dependencies.
9
+
10
+ * all check digit methods of the Deutsche Bundesbank (00 to E4, including
11
+ all sub-methods) for German account numbers,
12
+ * IBAN generation and validation for German accounts including the
13
+ Bundesbank IBAN rules (rules 0000 to 0057 as of konto_check 6.15),
14
+ IBAN checksum validation for all countries,
15
+ * BIC, creditor identifier (Gläubiger-ID) and structured remittance (IPI)
16
+ checks,
17
+ * bank directory lookups (name, BIC, place, postal code, successor BLZ, ...)
18
+ and searches (by name, place, BIC, BLZ/PLZ ranges, full text),
19
+ * reads the **LUT files** of the original library (`blz.lut2f`, LUT format 2.x)
20
+ and writes them (including the second data set and the index blocks),
21
+ * reads the **bank code files of the Deutsche Bundesbank** directly, in all
22
+ three published formats (fixed width TXT, CSV, XML),
23
+ * drop-in compatible `KontoCheck` and `KontoCheckRaw` modules for users of
24
+ the original [konto_check gem](https://rubygems.org/gems/konto_check).
25
+
26
+ The port is verified bit for bit against the C library: differential tests
27
+ compare millions of randomly generated account numbers for every check
28
+ method and sub-method, all IBAN rules and all bank lookups with the results
29
+ of the original code (see `tools/`).
30
+
31
+ ## Installation
32
+
33
+ ```ruby
34
+ gem "konto_check_ruby"
35
+ ```
36
+
37
+ Requires Ruby >= 3.0, no other dependencies.
38
+
39
+ The version of the ported C library is available as
40
+ `KontoCheckRuby::C_LIBRARY_VERSION` (currently 6.15.0).
41
+
42
+ ## Bank data
43
+
44
+ The library needs the bank directory of the Deutsche Bundesbank. Two sources
45
+ are supported:
46
+
47
+ 1. **Bundesbank bank code files** ("Bankleitzahlendatei"), downloadable from
48
+ [bundesbank.de](https://www.bundesbank.de/de/aufgaben/unbarer-zahlungsverkehr/serviceangebot/bankleitzahlen/download-bankleitzahlen-602592)
49
+ as TXT (fixed width), CSV or XML. They are published quarterly and can be
50
+ loaded directly:
51
+
52
+ ```ruby
53
+ engine = KontoCheckRuby::Engine.new
54
+ engine.load_blz_file("blz-aktuell-txt-data.txt") # or .csv / .xml
55
+ ```
56
+
57
+ Note: the current Bundesbank files no longer contain the IBAN rule
58
+ column. konto_check_ruby therefore applies a built-in table of IBAN rules
59
+ per BLZ (`data/iban_regeln.txt`, extracted from the LUT file of
60
+ konto_check 6.15 with bank data of December 2025) to such files. Pass `iban_rules: false` to disable that
61
+ or `iban_rules: "my_rules.txt"` / a Hash `{blz => rule * 100 + version}`
62
+ to supply your own table.
63
+
64
+ 2. **LUT files** of the original library (`blz.lut2f`). A LUT file is a
65
+ compressed block format; it may contain two data sets with different
66
+ validity periods, and the currently valid one is chosen automatically.
67
+ The gem ships a LUT file generated from the current Bundesbank data
68
+ (`data/blz.lut2f`, valid from 2026-09-07 to 2026-12-06, IBAN rules from
69
+ the built-in table, see `data/README.md`). **It is the default:** `init`
70
+ without a file name (or `KontoCheck.init` without arguments) loads it.
71
+ You can also use the files distributed with konto_check or generate them
72
+ yourself from a Bundesbank file:
73
+
74
+ ```ruby
75
+ KontoCheckRuby::BlzFile.generate_lut("blz-aktuell-txt-data.txt", "blz.lut2f",
76
+ gueltigkeit: "20260907-20261206")
77
+ engine.init("blz.lut2f") # level 5 by default, 0..9 = amount of data loaded
78
+ engine.init # the bundled data/blz.lut2f
79
+ ```
80
+
81
+ Files written by konto_check_ruby are readable by the C library and vice
82
+ versa. XML files carry their validity period, for TXT/CSV files pass it
83
+ with `gueltigkeit:` (format `JJJJMMTT-JJJJMMTT`).
84
+
85
+ Data older than its validity period still works; `lut_valid` (or
86
+ `KontoCheck.lut_info`) tells whether the loaded data set is current. The
87
+ test fixtures contain a LUT file from 2023 and a Bundesbank TXT file.
88
+
89
+ ### Keeping the data current (self update)
90
+
91
+ The Bundesbank publishes a new bank code file every quarter (validity starts
92
+ in March, June, September and December). `KontoCheckRuby::Update` fetches the
93
+ current file from the
94
+ [download page](https://www.bundesbank.de/de/aufgaben/unbarer-zahlungsverkehr/serviceangebot/bankleitzahlen/download-bankleitzahlen-602592)
95
+ into a local cache directory (`$KONTO_CHECK_RUBY_CACHE`, else
96
+ `~/.cache/konto_check_ruby`) and the engine loads it directly:
97
+
98
+ ```ruby
99
+ engine = KontoCheckRuby::Engine.new
100
+ engine.load_current # cached file valid today, downloaded if there is none
101
+ engine.lut_valid # => 4 (LUT2_VALID)
102
+
103
+ result = KontoCheckRuby::Update.update # download only if the file changed
104
+ result.status # => :downloaded or :current
105
+ result.path # => ".../blz-20260907-20261206.xml"
106
+ KontoCheckRuby::Update.update(lut: "blz.lut2f") # additionally write a LUT file
107
+ ```
108
+
109
+ `load_current(refresh: :auto)` (default) contacts the server only when no
110
+ cached file is valid for today; `refresh: :always` checks the download page
111
+ on every call, `refresh: :never` works offline. If a download fails, the
112
+ newest cached file is used, and without any cached file the bundled LUT
113
+ file; `engine.last_update_error` holds the reason. Network access uses only
114
+ the standard library (`net/http`); the download is about 5 MB for the XML
115
+ file (`format: :txt` or `:csv` are smaller but need the validity from the
116
+ download page).
117
+
118
+ On the command line: `konto_check_ruby update [LUTFILE]` downloads the
119
+ current file (and optionally writes a LUT file), `konto_check_ruby -d current
120
+ ...` uses the cached current file for any command.
121
+
122
+ **LUT file or Bundesbank file?** For a pure Ruby application the LUT format
123
+ brings no advantage any more: loading the Bundesbank XML directly takes about
124
+ as long as loading a level 9 LUT file (70 ms), and all functions work the
125
+ same. The LUT format is still useful to exchange data with the C library or
126
+ its other ports, to bundle the data compactly (1 MB instead of 5 MB) and to
127
+ keep two data sets with different validity periods in one file, which is why
128
+ reading and writing it remains supported. The only thing the Bundesbank files
129
+ lack is the IBAN rule column, which the built-in rule table compensates.
130
+
131
+ ## Usage
132
+
133
+ ### Ruby API (`KontoCheckRuby::Engine`)
134
+
135
+ ```ruby
136
+ require "konto_check_ruby"
137
+
138
+ engine = KontoCheckRuby::Engine.new
139
+ engine.init # bundled data; or init("blz.lut2f", 9), load_blz_file("blz.txt")
140
+
141
+ engine.kto_check_blz("37040044", "532013000") # => 1 (OK)
142
+ engine.kto_check_pz("13", "532013000") # check with an explicit method
143
+ engine.kto_check_regel("10050000", "1111") # with IBAN rules => 18 (OK_KTO_REPLACED)
144
+
145
+ engine.iban_check("DE89 3704 0044 0532 0130 00") # => [1, 1] (IBAN status, account status)
146
+ engine.iban_gen("37040044", "532013000") # => ["DE89370400440532013000", 1]
147
+ engine.iban_bic_gen("37040044", "532013000") # => [1, "DE89 3704 0044 0532 0130 00", "COBADEFFXXX", "37040044", "0532013000"]
148
+ engine.iban2bic("DE89370400440532013000") # => ["COBADEFFXXX", 1, "37040044", "0532013000"]
149
+ engine.bic_check("COBADEFFXXX") # => [1, count]
150
+ engine.ci_check("DE98ZZZ09999999999") # creditor identifier => 1
151
+
152
+ engine.lut_name("37040044") # => ["Commerzbank", 1]
153
+ engine.lut_ort("37040044", 2) # branch 2 => ["Dormagen", 1]
154
+ engine.lut_bic("37040044") # => ["COBADEFFXXX", 1]
155
+ engine.lut_multiple("37040044") # Hash with all fields of all branches
156
+ engine.lut_suche_namen("Sparkasse") # => [1, [flat indexes...]]
157
+ engine.lut_suche_multiple("Sparkasse Köln", true) # => [1, [[37050198, 0]]]
158
+
159
+ engine.retval2txt(-4) # => "die Bankleitzahl ist ungültig"
160
+ engine.retval2txt_short(-4) # => "INVALID_BLZ"
161
+ ```
162
+
163
+ All return codes are integer constants in `KontoCheckRuby` (`OK = 1`,
164
+ `FALSE = 0`, `OK_NO_CHK = 2`, `INVALID_BLZ = -4`, ...), identical to the C
165
+ library. Values greater than 0 mean "ok" (possibly with a remark), 0 means
166
+ "wrong", negative values are errors. Functions that return a value and a
167
+ status return `[value, status]`.
168
+
169
+ Several engines with different data sets can be used side by side.
170
+
171
+ ### Compatible API (`KontoCheck` / `KontoCheckRaw`)
172
+
173
+ The modules `KontoCheck` (Ruby-like, mostly plain values) and
174
+ `KontoCheckRaw` (arrays of `[value, status]`, like the C extension) have the
175
+ same functions, parameters and return values as the original gem, so
176
+ existing code keeps working:
177
+
178
+ ```ruby
179
+ require "konto_check_ruby"
180
+
181
+ KontoCheck.init # bundled data, or KontoCheck.init("blz.lut2f", 9)
182
+ KontoCheck.konto_check?("37040044", "532013000") # => true
183
+ KontoCheck.konto_check("37040044", 532013000) # => 1
184
+ KontoCheck.bank_name("37040044") # => "Commerzbank"
185
+ KontoCheck.bank_alles("37040044") # => [1, 17, "Commerzbank", "Commerzbank Köln", 50447, "Köln", ...]
186
+ KontoCheck.iban_gen(37040044, 532013000) # => "DE89370400440532013000"
187
+ KontoCheck.iban_check("DE89370400440532013000") # => 1
188
+ KontoCheck.suche(ort: "Köln", uniq: 1) # => [37040044, ...]
189
+ KontoCheckRaw.bank_suche_namen("Postbank") # => [names, blzs, branches, status, count]
190
+ KontoCheckRaw.iban_gen("37040044", "532013000") # => [iban, iban_papier, status, bic, blz2, kto2, regel]
191
+ ```
192
+
193
+ Both modules use a shared default engine (`KontoCheckRuby.engine`).
194
+
195
+ ### Command line
196
+
197
+ ```
198
+ konto_check_ruby check 37040044 532013000 # uses the bundled data, -d FILE for another
199
+ konto_check_ruby -d blz-aktuell-txt-data.txt iban DE89370400440532013000
200
+ konto_check_ruby iban-gen 10050000 1111
201
+ konto_check_ruby bank 37040044
202
+ konto_check_ruby search "Sparkasse Köln"
203
+ konto_check_ruby -g 20260907-20261206 generate-lut blz-aktuell-txt-data.txt blz.lut2f
204
+ konto_check_ruby dump blz.lut2f
205
+ ```
206
+
207
+ ## Performance
208
+
209
+ Measured in-process on the same 2023 LUT file (level 9), C library compiled
210
+ with `-O2`, Ruby 3.3 with and without YJIT, Apple Silicon.
211
+
212
+ One-time costs:
213
+
214
+ | | C library | Ruby port |
215
+ |---|---|---|
216
+ | `require "konto_check_ruby"` | – | 24 ms |
217
+ | init level 9 (all blocks) | 6 ms | 67 ms |
218
+ | init level 0 (BLZ and check methods only) | 0.5 ms | 5 ms |
219
+ | load a Bundesbank TXT file directly | – | 72 ms |
220
+ | first name search (builds the sort index once) | – | 35 ms |
221
+
222
+ Per call, microseconds (200,000 calls each):
223
+
224
+ | Function | C | Ruby | Ruby + YJIT |
225
+ |---|---|---|---|
226
+ | `kto_check_blz` (account check) | 0.03 | 2.8 | 1.5 |
227
+ | `kto_check_pz` | 0.02 | 2.8 | 1.5 |
228
+ | `iban_check` (German IBAN incl. rules) | 0.33 | 22 | 18 |
229
+ | `iban_bic_gen` | 0.14 | 12 | 8 |
230
+ | `lut_name` | 0.004 | 0.6 | 0.4 |
231
+ | `lut_bic` (with rule check) | 0.04 | 3.2 | 2.1 |
232
+ | `lut_suche_namen("Sparkasse")` | 55 | 126 | 48 |
233
+ | `KontoCheck.konto_check?` (compatibility layer) | – | 3.2 | 1.7 |
234
+
235
+ In relative terms the Ruby port is 50 to 100 times slower per call, in
236
+ absolute terms a single operation costs a few microseconds: an account
237
+ check takes about 3 µs, a complete IBAN check about 20 µs, which is not
238
+ noticeable next to a request or a database query. 1,000 account checks take
239
+ about 3 ms (C: 0.03 ms), 1,000 IBAN checks about 22 ms (C: 0.3 ms). The only
240
+ cost you can feel is initialisation (67 ms versus 6 ms), so load the data
241
+ once per process, as the default engine does, not per request. The C
242
+ figures are pure library calls; the C extension of the original gem adds
243
+ Ruby-to-C argument conversion on top, so the gap inside a Ruby application
244
+ is somewhat smaller.
245
+
246
+ ## Differences to the C library / original gem
247
+
248
+ * Default output encoding is UTF-8 (the C library defaults to ISO-8859-1).
249
+ `encoding("i")` etc. still switches the encoding of texts and names.
250
+ * Bank names, places etc. are returned as proper UTF-8 Ruby strings.
251
+ * The SCL directory functions (`scl_*`) are not implemented; they return
252
+ `NO_SCL_BLOCKS_LOADED`.
253
+ * A missing IBAN rule block in a LUT file does not disable the IBAN functions
254
+ (the C library refuses them); the standard rule is used instead.
255
+ * LUT files of the old format 1.x are not supported (`LUT1_FILE_USED`).
256
+ * Only zlib compressed (and uncompressed) LUT files are supported; bzip2,
257
+ lzo and lzma compressed files return `KTO_CHECK_UNSUPPORTED_COMPRESSION`.
258
+ * Account numbers containing non-digit characters return `INVALID_KTO`
259
+ (the C library computes with undefined values there).
260
+ * `lut_bic` with a branch index beyond the number of branches always returns
261
+ `LUT2_INDEX_OUT_OF_RANGE` (the C library may return `OK_INVALID_FOR_IBAN`
262
+ for banks with IBAN rules 31..35 due to an unterminated buffer).
263
+ * A failed `init` keeps the previously loaded data (the C library drops it).
264
+ * `init` without a file name loads the bundled LUT file (the C library
265
+ searches `./blz.lut2f`, `/etc/blz.lut2f`, ... instead).
266
+ * If neither data set of a LUT file is currently valid, the younger one is
267
+ loaded (the C library always falls back to the first set).
268
+
269
+ ## Development
270
+
271
+ ```
272
+ bundle install
273
+ rake test
274
+ ```
275
+
276
+ The differential tests in `tools/` need the C library compiled as a small
277
+ test driver; see `tools/README.md`. `tools/PORTING_SPEC.md` and
278
+ `tools/IBAN_PORTING_SPEC.md` describe the porting rules that were followed.
279
+
280
+ ## Copyright and license
281
+
282
+ Copyright (C) 2026 tickettoaster GmbH, <https://tickettoaster.de>.
283
+
284
+ konto_check_ruby is a derivative work of konto_check, Copyright (C)
285
+ 2002-2023 Michael Plugge, and is licensed like the original under the GNU
286
+ Lesser General Public License, version 2.1 or (at your option) any later
287
+ version (see LICENSE and COPYRIGHT). The `KontoCheck` module is taken from the original
288
+ konto_check gem. The bank data in `data/` comes from the Deutsche Bundesbank
289
+ (see `data/README.md`).
290
+
291
+ Using the gem in your own application, also a closed source one, is
292
+ permitted by the LGPL as long as konto_check_ruby stays a separate library
293
+ (a gem dependency) whose source and license are available to the recipients
294
+ of your application; changes to konto_check_ruby itself must be published
295
+ under the LGPL. This is not legal advice.
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright (C) 2026 tickettoaster GmbH <https://tickettoaster.de>.
5
+ # Part of konto_check_ruby, a Ruby port of konto_check (Copyright (C)
6
+ # 2002-2023 Michael Plugge); licensed under the GNU Lesser General Public
7
+ # License, version 2.1 or later, see the file LICENSE.
8
+
9
+ # Command line interface of konto_check_ruby.
10
+ #
11
+ # konto_check_ruby [-d DATAFILE] COMMAND ARGS...
12
+ #
13
+ # DATAFILE is a LUT file (blz.lut2f) or a Bundesbank bank code file (TXT,
14
+ # CSV or XML); "current" uses the current Bundesbank file from the local
15
+ # cache (downloaded on demand, see the update command). Without -d the LUT
16
+ # file bundled with the gem is used.
17
+
18
+ require "optparse"
19
+ require_relative "../lib/konto_check_ruby"
20
+
21
+ options = { data: nil, level: 9 }
22
+ parser = OptionParser.new do |o|
23
+ o.banner = <<~BANNER
24
+ Usage: konto_check_ruby [options] COMMAND [ARGS]
25
+
26
+ Commands:
27
+ check BLZ KTO check an account number (with IBAN rules: check-regel)
28
+ check-pz METHODE KTO check an account number with a given check method (e.g. 00, 13a)
29
+ iban IBAN validate an IBAN
30
+ iban-gen BLZ KTO generate the IBAN and BIC for an account
31
+ bank BLZ [FILIALE] show the bank data of a BLZ
32
+ search TEXT search banks (e.g. "Sparkasse Köln", "50667@plz", "COBADEFF@bic")
33
+ generate-lut IN OUT generate a LUT file from a Bundesbank file
34
+ dump LUT show the directory of a LUT file
35
+ info [LUT] show the info block(s) of a LUT file / the loaded data
36
+ update [LUTFILE] download the current Bundesbank file into the cache
37
+ (and optionally write a LUT file from it)
38
+ version
39
+
40
+ Options:
41
+ BANNER
42
+ o.on("-d", "--data FILE", "LUT file, Bundesbank file (TXT/CSV/XML) or 'current'; default: the bundled blz.lut2f") { |v| options[:data] = v }
43
+ o.on("--cache DIR", "cache directory for 'current'/update (default #{KontoCheckRuby::Update.cache_dir})") { |v| options[:cache] = v }
44
+ o.on("--force", "update: download even if the file is cached") { options[:force] = true }
45
+ o.on("-l", "--level N", Integer, "init level for LUT files (0..9, default 9)") { |v| options[:level] = v }
46
+ o.on("-g", "--gueltigkeit RANGE", "validity JJJJMMTT-JJJJMMTT (generate-lut, Bundesbank files)") { |v| options[:gueltigkeit] = v }
47
+ o.on("-h", "--help") do
48
+ puts o
49
+ exit
50
+ end
51
+ end
52
+ parser.parse!
53
+
54
+ command = ARGV.shift
55
+ if command.nil?
56
+ puts parser
57
+ exit 1
58
+ end
59
+
60
+ engine = KontoCheckRuby::Engine.new
61
+
62
+ def load_data(engine, options)
63
+ file = options[:data]
64
+ code = if file == "current"
65
+ c = engine.load_current(dir: options[:cache] || KontoCheckRuby::Update.cache_dir)
66
+ warn "download failed (#{engine.last_update_error}), using #{engine.current_lutfile_name[0]}" if engine.last_update_error
67
+ c
68
+ elsif file && file =~ /\.(txt|csv|xml)\z/i
69
+ engine.load_blz_file(file, gueltigkeit: options[:gueltigkeit])
70
+ else
71
+ engine.init(file, options[:level])
72
+ end
73
+ return if code > 0 || code == KontoCheckRuby::LUT2_PARTIAL_OK
74
+ warn "could not load bank data#{file ? " from #{file}" : ''}: #{engine.retval2txt_short(code)} (#{engine.retval2txt(code)})"
75
+ exit 2
76
+ end
77
+
78
+ def status(engine, code)
79
+ "#{code} #{engine.retval2txt_short(code)}: #{engine.retval2txt(code)}"
80
+ end
81
+
82
+ case command
83
+ when "check", "check-regel"
84
+ load_data(engine, options)
85
+ blz, kto = ARGV
86
+ abort "usage: check BLZ KTO" unless blz && kto
87
+ code = command == "check" ? engine.kto_check_blz(blz, kto) : engine.kto_check_regel(blz, kto)
88
+ puts status(engine, code)
89
+ exit(code > 0 ? 0 : 1)
90
+ when "check-pz"
91
+ pz, kto, blz = ARGV
92
+ abort "usage: check-pz METHODE KTO [BLZ]" unless pz && kto
93
+ code = engine.kto_check_pz(pz, kto, blz)
94
+ puts status(engine, code)
95
+ exit(code > 0 ? 0 : 1)
96
+ when "iban"
97
+ load_data(engine, options)
98
+ iban = ARGV[0] or abort "usage: iban IBAN"
99
+ code, kc = engine.iban_check(iban)
100
+ puts "IBAN: #{status(engine, code)}"
101
+ puts "Konto: #{status(engine, kc)}"
102
+ if code > 0 && iban.gsub(/\s/, "")[0, 2].casecmp("DE").zero? && engine.initialized?
103
+ bic, ret, blz, kto = engine.iban2bic(iban)
104
+ puts "BLZ #{blz}, Konto #{kto}, BIC #{bic} (#{engine.retval2txt_short(ret)})"
105
+ name, r = engine.lut_name(blz)
106
+ puts "Bank: #{name}, #{engine.lut_ort(blz)[0]}" if r == KontoCheckRuby::OK
107
+ end
108
+ exit(code > 0 ? 0 : 1)
109
+ when "iban-gen"
110
+ load_data(engine, options)
111
+ blz, kto = ARGV
112
+ abort "usage: iban-gen BLZ KTO" unless blz && kto
113
+ ret, iban, bic, blz2, kto2 = engine.iban_bic_gen(blz, kto)
114
+ puts status(engine, ret)
115
+ if iban
116
+ puts "IBAN: #{iban}"
117
+ puts "BIC: #{bic}"
118
+ puts "verwendete BLZ/Konto: #{blz2} / #{kto2}" if blz2 != blz || kto2 != kto.rjust(10, "0")
119
+ end
120
+ exit(ret > 0 ? 0 : 1)
121
+ when "bank"
122
+ load_data(engine, options)
123
+ blz = ARGV[0] or abort "usage: bank BLZ [FILIALE]"
124
+ fil = (ARGV[1] || 0).to_i
125
+ m = engine.lut_multiple(blz)
126
+ if m[:retval] <= 0 && m[:retval] != KontoCheckRuby::LUT2_PARTIAL_OK
127
+ puts status(engine, m[:retval])
128
+ exit 1
129
+ end
130
+ if fil >= m[:cnt]
131
+ puts status(engine, KontoCheckRuby::LUT2_INDEX_OUT_OF_RANGE)
132
+ exit 1
133
+ end
134
+ f = ->(k) { m[k] ? m[k][fil] : "-" }
135
+ bic, bret = engine.lut_bic(blz, fil)
136
+ puts "BLZ: #{m[:blz]} (#{m[:cnt]} Stelle#{m[:cnt] == 1 ? '' : 'n'}, Filiale #{fil})"
137
+ puts "Name: #{f.call(:name)}"
138
+ puts "Kurzname: #{f.call(:name_kurz)}"
139
+ puts "PLZ / Ort: #{f.call(:plz)} #{f.call(:ort)}"
140
+ puts "BIC: #{bic} (#{engine.retval2txt_short(bret)})"
141
+ puts "Prüfziffer: #{m[:pz] ? KontoCheckRuby::BlzFile.pz_to_string(m[:pz]) : '-'}"
142
+ puts "PAN: #{f.call(:pan)}"
143
+ puts "Datensatz-Nr.: #{f.call(:nr)}"
144
+ puts "Änderung: #{f.call(:aenderung)}"
145
+ puts "Löschung: #{f.call(:loeschung)}"
146
+ puts "Nachfolge-BLZ: #{f.call(:nachfolge_blz)}"
147
+ r = f.call(:iban_regel)
148
+ puts "IBAN-Regel: #{r.is_a?(Integer) ? format('%04d.%02d', r / 100, r % 100) : r}"
149
+ when "search"
150
+ load_data(engine, options)
151
+ text = ARGV.join(" ")
152
+ abort "usage: search TEXT" if text.empty?
153
+ code, pairs = engine.lut_suche_multiple(text, true)
154
+ if code < 0 || pairs.empty?
155
+ puts status(engine, code == KontoCheckRuby::OK ? KontoCheckRuby::KEY_NOT_FOUND : code)
156
+ exit 1
157
+ end
158
+ pairs.each do |blz, zw|
159
+ name, = engine.lut_name(blz.to_s, zw)
160
+ ort, = engine.lut_ort(blz.to_s, zw)
161
+ bic, = engine.lut_bic_int(blz.to_s, zw)
162
+ puts format("%08d %-11s %-40s %s", blz, bic.to_s.strip, name.to_s[0, 40], ort)
163
+ end
164
+ puts "#{pairs.size} Treffer#{code == KontoCheckRuby::SOME_KEYS_NOT_FOUND ? ' (nicht alle Suchbegriffe gefunden)' : ''}"
165
+ when "generate-lut"
166
+ input, output = ARGV
167
+ abort "usage: generate-lut INPUT OUTPUT" unless input && output
168
+ code = KontoCheckRuby::BlzFile.generate_lut(input, output, gueltigkeit: options[:gueltigkeit], felder: 9, filialen: true,
169
+ user_info: "generiert von konto_check_ruby #{KontoCheckRuby::VERSION}")
170
+ puts status(engine, code)
171
+ exit(code > 0 ? 0 : 1)
172
+ when "dump"
173
+ lut = ARGV[0] or abort "usage: dump LUT"
174
+ code, dump = KontoCheckRuby::LutFile.dump(lut)
175
+ puts(code == KontoCheckRuby::OK ? dump : status(engine, code))
176
+ when "info"
177
+ if ARGV[0]
178
+ code, i1, i2, v1, v2 = engine.lut_info(ARGV[0])
179
+ puts status(engine, code)
180
+ puts "\n1. Datensatz (#{engine.retval2txt_short(v1)}):\n#{i1}" if i1
181
+ puts "\n2. Datensatz (#{engine.retval2txt_short(v2)}):\n#{i2}" if i2
182
+ else
183
+ load_data(engine, options)
184
+ puts engine.lut_info[1]
185
+ puts "Gültigkeit: #{engine.retval2txt_short(engine.lut_valid)}"
186
+ end
187
+ when "update"
188
+ begin
189
+ r = KontoCheckRuby::Update.update(dir: options[:cache] || KontoCheckRuby::Update.cache_dir, lut: ARGV[0], force: options[:force])
190
+ puts "#{r.status == :downloaded ? 'downloaded' : 'already cached'}: #{r.path} (gültig #{r.valid_from}-#{r.valid_to})"
191
+ puts "LUT file written: #{r.lut}" if r.lut
192
+ rescue KontoCheckRuby::Update::Error => e
193
+ warn e.message
194
+ exit 2
195
+ end
196
+ when "version"
197
+ puts engine.version
198
+ else
199
+ warn "unknown command #{command}"
200
+ puts parser
201
+ exit 1
202
+ end
data/data/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # Bundled bank data
2
+
3
+ Copyright (C) 2026 tickettoaster GmbH (compilation); the bank data itself is
4
+ published by the Deutsche Bundesbank.
5
+
6
+ `blz.lut2f` is a LUT file generated by konto_check_ruby
7
+ (`KontoCheckRuby::Update.update(lut: ...)`) from the current
8
+ "Bankleitzahlendatei" of the Deutsche Bundesbank
9
+ (https://www.bundesbank.de/de/aufgaben/unbarer-zahlungsverkehr/serviceangebot/bankleitzahlen/download-bankleitzahlen-602592,
10
+ XML variant, validity 2026-09-07 to 2026-12-06). The IBAN rules were added
11
+ from `iban_regeln.txt`, since the Bundesbank files no longer contain them.
12
+
13
+ `iban_regeln.txt` maps BLZ to IBAN rule (rule * 100 + version); it was
14
+ extracted from the LUT file `blz.lut2f` of konto_check 6.15 (bank data of
15
+ December 2025, https://sourceforge.net/projects/kontocheck/).
16
+
17
+ To refresh the data yourself: `konto_check_ruby update path/to/blz.lut2f`.
data/data/blz.lut2f ADDED
Binary file