lucaterm 0.1.2 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/exe/luca +8 -5
- data/lib/luca_term/book.rb +141 -61
- data/lib/luca_term/version.rb +1 -1
- metadata +21 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c072f724a2ad3fb09fc648c4912c974d8c874fd82165e525bb6cd3d06073f645
|
4
|
+
data.tar.gz: 6d6a198f22a9fab3ea8c6426f98571927577862b61f1b6b4f955e8442375b778
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d6b3ec02aaf1dac932d47dc74fa0c61604915a7ab79a3198c83075f67d94a5cffd5c5a2673cd8c852806aafcc8d875cc3040d5680c248fcfdcc25797c0030c2
|
7
|
+
data.tar.gz: 74d40973ea0f60f6e5dc9d40fd52d3548683525ffe018b5c1e054175dc06303eac3da2d4da9a10bff02c4f5e496bef4d495180dac7a5b37352c7d5816a606d11
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## LucaTerm 0.2.0
|
2
|
+
|
3
|
+
* `luca book`: Works without args. Show journals of the latest month.
|
4
|
+
* `luca book`: Show categories on Account code selection
|
5
|
+
* `luca book`: Show x-customer header on detail screen
|
6
|
+
|
1
7
|
## LucaTerm 0.1.2
|
2
8
|
|
3
9
|
* `luca book`: '<' for prev month, '>' for next month.
|
data/exe/luca
CHANGED
@@ -3,19 +3,22 @@
|
|
3
3
|
|
4
4
|
require 'optparse'
|
5
5
|
require 'curses'
|
6
|
+
require 'luca_book/journal'
|
6
7
|
require 'luca_term'
|
7
8
|
|
8
9
|
include Curses
|
9
10
|
|
10
|
-
init_screen
|
11
|
-
curs_set(0)
|
12
|
-
noecho
|
13
11
|
|
14
12
|
class LucaCmd
|
15
13
|
def self.book(args, params = {})
|
14
|
+
init_screen
|
15
|
+
curs_set(0)
|
16
|
+
noecho
|
17
|
+
|
16
18
|
begin
|
17
|
-
window = Curses::Window.new(0, 0,
|
18
|
-
|
19
|
+
window = Curses::Window.new(0, 0, 1, 0)
|
20
|
+
args = ARGV.empty? ? LucaBook::Journal.latest_month : ARGV
|
21
|
+
LucaTerm::Book.journals(window, *args)
|
19
22
|
ensure
|
20
23
|
close_screen
|
21
24
|
end
|
data/lib/luca_term/book.rb
CHANGED
@@ -4,13 +4,14 @@ require 'curses'
|
|
4
4
|
require "unicode/display_width/string_ext"
|
5
5
|
require 'mb_string'
|
6
6
|
require 'luca_book'
|
7
|
-
|
7
|
+
require 'json'
|
8
8
|
module LucaTerm
|
9
9
|
class Book
|
10
10
|
include Curses
|
11
|
-
attr_accessor :window
|
11
|
+
attr_accessor :window, :modeline
|
12
12
|
|
13
13
|
def initialize(window, year, month, data=nil)
|
14
|
+
@modeline = Window.new(1, 0, 0, 0)
|
14
15
|
@window = window
|
15
16
|
@year = year
|
16
17
|
@month = month
|
@@ -26,16 +27,22 @@ module LucaTerm
|
|
26
27
|
new(window, args[0], args[1], LucaSupport::Code.readable(LucaBook::List.term(*args).data))
|
27
28
|
end
|
28
29
|
|
30
|
+
# render monthly journal list
|
31
|
+
#
|
29
32
|
def main_loop
|
30
33
|
loop do
|
34
|
+
modeline.setpos(0, 0)
|
35
|
+
modeline << "#{Date::ABBR_MONTHNAMES[@month.to_i]} #{@year}"
|
36
|
+
modeline.clrtoeol
|
37
|
+
modeline.refresh
|
38
|
+
|
31
39
|
window.setpos(0,0)
|
32
40
|
@visible.each.with_index(0) do |dat, i|
|
33
41
|
cursor = i == @active ? :full : nil
|
34
42
|
draw_line(dat, cursor, true)
|
35
|
-
clrtoeol
|
36
43
|
window << "\n"
|
37
44
|
end
|
38
|
-
(window.maxy - window.cury).times { window
|
45
|
+
(window.maxy - window.cury).times { window << "\n" }
|
39
46
|
window.refresh
|
40
47
|
|
41
48
|
window.keypad(true)
|
@@ -52,7 +59,7 @@ module LucaTerm
|
|
52
59
|
when 'G'
|
53
60
|
cursor_last @data
|
54
61
|
when 'm'
|
55
|
-
ym = edit_dialog('
|
62
|
+
ym = edit_dialog('Enter: [yyyy] m', title: 'Change Month')&.split(/[\/\s]/)
|
56
63
|
ym = [@year, ym[0]] if ym.length == 1
|
57
64
|
@data = LucaSupport::Code.readable(LucaBook::List.term(*ym).data)
|
58
65
|
@year, @month = ym
|
@@ -75,8 +82,9 @@ module LucaTerm
|
|
75
82
|
@visible = set_visible(@data)
|
76
83
|
when KEY_ENTER, KEY_CTRL_J
|
77
84
|
show_detail(@data[@index])
|
85
|
+
@visible = set_visible(@data)
|
78
86
|
when 'N'
|
79
|
-
newdate = edit_dialog "Enter date of new record: YYYY-m-d"
|
87
|
+
newdate = edit_dialog "Enter date of new record: YYYY-m-d", title: 'Create Journal'
|
80
88
|
tmpl = {
|
81
89
|
date: newdate,
|
82
90
|
debit: [
|
@@ -93,16 +101,26 @@ module LucaTerm
|
|
93
101
|
end
|
94
102
|
end
|
95
103
|
|
104
|
+
# render each journal
|
105
|
+
#
|
96
106
|
def show_detail(record)
|
97
107
|
@d_v = 0
|
98
108
|
@d_h = 0
|
99
109
|
debit_length = Array(record[:debit]).length
|
100
110
|
credit_length = Array(record[:credit]).length
|
101
111
|
date, txid = LucaSupport::Code.decode_id(record[:id]) if record[:id]
|
112
|
+
fileid = record[:id].split('/').last if record[:id]
|
102
113
|
date ||= record[:date]
|
114
|
+
modeline.setpos(0, 0)
|
115
|
+
modeline << "#{date} #{fileid} "
|
116
|
+
modeline.clrtoeol
|
117
|
+
modeline.refresh
|
118
|
+
|
103
119
|
loop do
|
104
120
|
window.setpos(0, 0)
|
105
|
-
|
121
|
+
if record.dig(:headers, 'x-customer')
|
122
|
+
window << format(" [%s] ", record.dig(:headers, 'x-customer'))
|
123
|
+
end
|
106
124
|
window << record[:note]
|
107
125
|
clrtoeol; window << "\n"
|
108
126
|
[debit_length, credit_length].max.times do |i|
|
@@ -119,7 +137,7 @@ module LucaTerm
|
|
119
137
|
clrtoeol
|
120
138
|
window << "\n"
|
121
139
|
end
|
122
|
-
(window.maxy - window.cury).times { window
|
140
|
+
(window.maxy - window.cury).times { window << "\n" }
|
123
141
|
window.refresh
|
124
142
|
|
125
143
|
window.keypad(true)
|
@@ -173,14 +191,15 @@ module LucaTerm
|
|
173
191
|
debit_length = Array(record[:debit]).length
|
174
192
|
credit_length = Array(record[:credit]).length
|
175
193
|
when KEY_CTRL_J
|
176
|
-
position = [0,1].include?(@d_h) ? :debit : :credit
|
194
|
+
position, counter = [0,1].include?(@d_h) ? [:debit, :credit] : [:credit, :debit]
|
177
195
|
if [0, 2].include? @d_h
|
178
196
|
new_code = select_code
|
179
197
|
next if new_code.nil?
|
180
198
|
|
181
199
|
record[position][@d_v][:code] = new_code
|
182
200
|
else
|
183
|
-
|
201
|
+
diff = record[counter].map { |c| c[:amount] }.sum - record[position].map { |p| p[:amount] }.sum + record[position][@d_v][:amount]
|
202
|
+
new_amount = edit_amount(record[position][@d_v][:amount], diff)
|
184
203
|
next if new_amount.nil?
|
185
204
|
|
186
205
|
record[position][@d_v][:amount] = new_amount
|
@@ -198,9 +217,12 @@ module LucaTerm
|
|
198
217
|
end
|
199
218
|
end
|
200
219
|
|
201
|
-
|
220
|
+
# returns amount after edit
|
221
|
+
#
|
222
|
+
def edit_amount(current = nil, diff = nil)
|
223
|
+
diff_msg = diff.nil? ? '' : "#{diff} meets balance."
|
202
224
|
begin
|
203
|
-
scmd = edit_dialog "Current: #{current&.to_s}"
|
225
|
+
scmd = edit_dialog "Current: #{current&.to_s}", diff_msg, title: 'Edit Amount'
|
204
226
|
return nil if scmd.length == 0
|
205
227
|
# TODO: guard from not number
|
206
228
|
return scmd.to_i
|
@@ -209,17 +231,22 @@ module LucaTerm
|
|
209
231
|
end
|
210
232
|
end
|
211
233
|
|
212
|
-
def edit_dialog(message = '')
|
213
|
-
sub = window.subwin(
|
214
|
-
sub.box(?|, ?-)
|
234
|
+
def edit_dialog(message = '', submessage = '', title: '')
|
235
|
+
sub = window.subwin(5, 30, (window.maxy-5)/2, (window.maxx - 30)/2)
|
215
236
|
sub.setpos(1, 1)
|
216
|
-
|
217
|
-
sub
|
218
|
-
clrtoeol
|
237
|
+
sub << " #{message}"
|
238
|
+
sub.clrtoeol
|
219
239
|
sub.setpos(2, 1)
|
220
|
-
sub
|
221
|
-
sub.setpos(2,
|
222
|
-
|
240
|
+
sub.clrtoeol
|
241
|
+
sub.setpos(2, 3)
|
242
|
+
sub.attron(A_REVERSE) { sub << " > #{' ' * (30 - 9)}" }
|
243
|
+
sub.setpos(3, 1)
|
244
|
+
sub << " #{submessage}"
|
245
|
+
sub.clrtoeol
|
246
|
+
sub.box(?|, ?-)
|
247
|
+
sub.setpos(0, 2)
|
248
|
+
sub << "[ #{title} ]"
|
249
|
+
sub.setpos(2, 7)
|
223
250
|
sub.refresh
|
224
251
|
loop do
|
225
252
|
echo
|
@@ -230,57 +257,75 @@ module LucaTerm
|
|
230
257
|
end
|
231
258
|
end
|
232
259
|
|
260
|
+
# returns Account code after selection from list dialog
|
261
|
+
#
|
233
262
|
def select_code
|
234
|
-
|
235
|
-
|
263
|
+
top = window.maxy >= 25 ? 5 : 2
|
264
|
+
sub = window.subwin(window.maxy - top, window.maxx - 4, top, 2)
|
265
|
+
padding = ' ' * account_index(sub.maxx)[0].length
|
266
|
+
list = @dict.reject{ |code, _e| code.length < 3 || /^[15]0XX/.match(code) || /^[89]ZZ/.match(code) }
|
267
|
+
.map{ |code, entry| { code: code, label: entry[:label], category: padding } }
|
268
|
+
tabstop = ['1', '5', '9', 'A', 'C'].map { |cap| list.index { |ac| /^#{cap}/.match(ac[:code]) } }.compact
|
269
|
+
account_index(sub.maxx).each.with_index do |cat, i|
|
270
|
+
list[tabstop[i]][:category] = cat
|
271
|
+
end
|
236
272
|
visible_dup = @visible
|
237
273
|
index_dup = @index
|
238
274
|
active_dup = @active
|
239
275
|
@index = 0
|
240
276
|
@active = 0
|
241
277
|
@visible = nil
|
242
|
-
@visible = set_visible(list)
|
278
|
+
@visible = set_visible(list, sub.maxy - 2)
|
243
279
|
loop do
|
244
|
-
window.setpos(0,0)
|
245
280
|
@visible.each.with_index(0) do |entry, i|
|
246
|
-
|
281
|
+
sub.setpos(i+1, 1)
|
282
|
+
head = entry[:code].length == 3 ? '' : ' '
|
283
|
+
line = format("%s %s %s %s", head, entry[:category], entry[:code], entry[:label])
|
247
284
|
if i == @active
|
248
|
-
|
285
|
+
sub.attron(A_REVERSE) { sub << line }
|
249
286
|
else
|
250
|
-
|
287
|
+
sub << line
|
251
288
|
end
|
252
|
-
clrtoeol
|
253
|
-
|
289
|
+
sub.clrtoeol
|
290
|
+
#sub << "\n"
|
254
291
|
end
|
255
|
-
(window.maxy - window.cury).times { window
|
256
|
-
|
292
|
+
(window.maxy - window.cury).times { window << "\n" }
|
293
|
+
sub.box(?|, ?-)
|
294
|
+
sub.setpos(0, 2)
|
295
|
+
sub << "[ Select Account ]"
|
296
|
+
sub.refresh
|
257
297
|
|
258
298
|
cmd = window.getch
|
259
299
|
case cmd
|
260
300
|
when KEY_DOWN, 'j', KEY_CTRL_N
|
261
301
|
next if @index >= list.length - 1
|
262
302
|
|
263
|
-
cursor_down list
|
303
|
+
cursor_down list, sub.maxy - 2
|
264
304
|
when KEY_NPAGE
|
265
|
-
cursor_pagedown list
|
305
|
+
cursor_pagedown list, sub.maxy - 2
|
266
306
|
when KEY_UP, 'k', KEY_CTRL_P
|
267
307
|
next if @index <= 0
|
268
308
|
|
269
309
|
cursor_up list
|
270
310
|
when KEY_PPAGE
|
271
|
-
cursor_pageup list
|
311
|
+
cursor_pageup list, sub.maxy - 2
|
312
|
+
when KEY_LEFT
|
313
|
+
cursor_jump tabstop, list, rev: true
|
314
|
+
when KEY_RIGHT
|
315
|
+
cursor_jump tabstop, list
|
272
316
|
when 'G'
|
273
|
-
cursor_last list
|
317
|
+
cursor_last list, sub.maxy - 2
|
274
318
|
when KEY_CTRL_J
|
275
|
-
code = list[@index][:code]
|
276
319
|
@visible = visible_dup
|
277
320
|
@index = index_dup
|
278
321
|
@active = active_dup
|
279
|
-
|
322
|
+
sub.close
|
323
|
+
return list[@index][:code]
|
280
324
|
when 'q'
|
281
325
|
@visible = visible_dup
|
282
326
|
@index = index_dup
|
283
327
|
@active = active_dup
|
328
|
+
sub.close
|
284
329
|
return nil
|
285
330
|
end
|
286
331
|
end
|
@@ -288,17 +333,36 @@ module LucaTerm
|
|
288
333
|
|
289
334
|
private
|
290
335
|
|
336
|
+
def account_index(maxx = 50)
|
337
|
+
term = if maxx >= 45
|
338
|
+
['Assets', 'Liabilities', 'Net Assets', 'Sales', 'Expenses']
|
339
|
+
elsif maxx >= 40
|
340
|
+
['Assets', 'LIAB', 'NetAsset', 'Sales', 'EXP']
|
341
|
+
else
|
342
|
+
['', '', '', '', '']
|
343
|
+
end
|
344
|
+
len = term.map { |str| str.length }.max
|
345
|
+
term.map { |str| str += ' ' * (len - str.length) }
|
346
|
+
end
|
347
|
+
|
291
348
|
def draw_line(dat, cursor = nil, note = false)
|
292
349
|
date, txid = LucaSupport::Code.decode_id(dat[:id]) if dat[:id]
|
350
|
+
date_str = date.nil? ? '' : date.split('-')[1, 2].join('/')&.mb_rjust(5, ' ')
|
293
351
|
debit_cd = fmt_code(dat[:debit])
|
294
352
|
debit_amount = fmt_amount(dat[:debit])
|
295
353
|
credit_cd = fmt_code(dat[:credit])
|
296
354
|
credit_amount = fmt_amount(dat[:credit])
|
297
355
|
lines = [Array(dat[:debit]).length, Array(dat[:credit]).length].max
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
356
|
+
lines = if lines == 1
|
357
|
+
' '
|
358
|
+
elsif lines > 9
|
359
|
+
'+'
|
360
|
+
else
|
361
|
+
lines
|
362
|
+
end
|
363
|
+
window << sprintf("%s |%s| ",
|
364
|
+
date_str,
|
365
|
+
lines,
|
302
366
|
)
|
303
367
|
case cursor
|
304
368
|
when 0
|
@@ -317,8 +381,8 @@ module LucaTerm
|
|
317
381
|
window.attron(A_REVERSE) { window << credit_amount }
|
318
382
|
else
|
319
383
|
rest = format("%s %s | %s %s", debit_cd, debit_amount, credit_cd, credit_amount)
|
320
|
-
if note && window.maxx >
|
321
|
-
rest += " | #{dat[:note]
|
384
|
+
if note && window.maxx > 70
|
385
|
+
rest += " | #{dat[:note]&.mb_truncate(window.maxx - 70)}"
|
322
386
|
end
|
323
387
|
if cursor == :full
|
324
388
|
window.attron(A_REVERSE) { window << rest }
|
@@ -354,46 +418,62 @@ module LucaTerm
|
|
354
418
|
@visible = set_visible(data)
|
355
419
|
end
|
356
420
|
|
357
|
-
def cursor_pageup(data)
|
358
|
-
|
421
|
+
def cursor_pageup(data, maxy = nil)
|
422
|
+
maxy ||= window.maxy
|
423
|
+
n_idx = @index - maxy
|
359
424
|
return if n_idx <= 0
|
360
425
|
|
361
426
|
@index = n_idx
|
362
427
|
@active = 0
|
363
|
-
@visible = set_visible(data)
|
428
|
+
@visible = set_visible(data, maxy)
|
364
429
|
end
|
365
430
|
|
366
|
-
def cursor_down(data)
|
431
|
+
def cursor_down(data, maxy = nil)
|
432
|
+
maxy ||= window.maxy
|
367
433
|
@index += 1
|
368
|
-
@active = @active >=
|
369
|
-
@visible = set_visible(data)
|
434
|
+
@active = @active >= maxy - 1 ? @active : @active + 1
|
435
|
+
@visible = set_visible(data, maxy)
|
370
436
|
end
|
371
437
|
|
372
|
-
def cursor_pagedown(data)
|
373
|
-
|
438
|
+
def cursor_pagedown(data, maxy = nil)
|
439
|
+
maxy ||= window.maxy
|
440
|
+
n_idx = @index + maxy
|
374
441
|
return if n_idx >= data.length - 1
|
375
442
|
|
376
443
|
@index = n_idx
|
444
|
+
@active = 0
|
445
|
+
@visible = set_visible(data, maxy)
|
446
|
+
end
|
447
|
+
|
448
|
+
def cursor_jump(tabstop, data, rev: false)
|
449
|
+
@index = if rev
|
450
|
+
tabstop.filter{ |t| t < @index ? t : nil }.max || @index
|
451
|
+
else
|
452
|
+
tabstop.filter{ |t| t > @index ? t : nil }.min || @index
|
453
|
+
end
|
454
|
+
|
377
455
|
@active = 0
|
378
456
|
@visible = set_visible(data)
|
379
457
|
end
|
380
458
|
|
381
|
-
def cursor_last(data)
|
459
|
+
def cursor_last(data, maxy = nil)
|
460
|
+
maxy ||= window.maxy
|
382
461
|
@index = data.length - 1
|
383
|
-
@active =
|
384
|
-
@visible = set_visible(data)
|
462
|
+
@active = maxy - 1
|
463
|
+
@visible = set_visible(data, maxy)
|
385
464
|
end
|
386
465
|
|
387
|
-
def set_visible(data)
|
388
|
-
|
466
|
+
def set_visible(data, maxy = nil)
|
467
|
+
maxy ||= window.maxy
|
468
|
+
return data if data.nil? || data.length <= maxy
|
389
469
|
|
390
470
|
if @visible.nil?
|
391
|
-
data.slice(0,
|
471
|
+
data.slice(0, maxy)
|
392
472
|
else
|
393
|
-
if @active == (
|
394
|
-
data.slice(@index -
|
473
|
+
if @active == (maxy - 1)
|
474
|
+
data.slice(@index - maxy + 1, maxy)
|
395
475
|
elsif @active == 0
|
396
|
-
data.slice(@index,
|
476
|
+
data.slice(@index, maxy)
|
397
477
|
else
|
398
478
|
@visible
|
399
479
|
end
|
data/lib/luca_term/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lucaterm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chuma Takahiro
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: curses
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: lucarecord
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.3'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.3'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: bundler
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -96,7 +110,7 @@ dependencies:
|
|
96
110
|
version: 12.3.3
|
97
111
|
description: 'Terminal frontend for Luca Suite
|
98
112
|
|
99
|
-
'
|
113
|
+
'
|
100
114
|
email:
|
101
115
|
- co.chuma@gmail.com
|
102
116
|
executables:
|
@@ -116,7 +130,7 @@ metadata:
|
|
116
130
|
homepage_uri: https://github.com/chumaltd/luca/tree/master/lucaterm
|
117
131
|
source_code_uri: https://github.com/chumaltd/luca/tree/master/lucaterm
|
118
132
|
changelog_uri: https://github.com/chumaltd/luca/tree/master/lucaterm/CHANGELOG.md
|
119
|
-
post_install_message:
|
133
|
+
post_install_message:
|
120
134
|
rdoc_options: []
|
121
135
|
require_paths:
|
122
136
|
- lib
|
@@ -131,8 +145,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
145
|
- !ruby/object:Gem::Version
|
132
146
|
version: '0'
|
133
147
|
requirements: []
|
134
|
-
rubygems_version: 3.2.
|
135
|
-
signing_key:
|
148
|
+
rubygems_version: 3.2.5
|
149
|
+
signing_key:
|
136
150
|
specification_version: 4
|
137
151
|
summary: Terminal frontend for Luca Suite
|
138
152
|
test_files: []
|