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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5c025e9af43938b46faab013fb8ad4bde69e7467c022deb4d08497aaa7b32eb
4
- data.tar.gz: 2cf8e35f2adfbe9ab7957aa45e94c8af5354b97a19c410dee0a996c0e144884a
3
+ metadata.gz: c072f724a2ad3fb09fc648c4912c974d8c874fd82165e525bb6cd3d06073f645
4
+ data.tar.gz: 6d6a198f22a9fab3ea8c6426f98571927577862b61f1b6b4f955e8442375b778
5
5
  SHA512:
6
- metadata.gz: ab2d8588d7e8f0903a74ed9005aee9637ac711c2653303606b7ed93b2c867da47eb6b6ec47836f6c2819534ba7099b94903124dec3a1fcde1b245b5814204425
7
- data.tar.gz: ee9956b01ffdd4022052f8a6d573005c3546106f33b5d14a013f29a30c19779fcc2adab42bfb655340a3cc327e041577ca5d8041d11dc990a3b44691cb8cef14
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, 0, 0)
18
- LucaTerm::Book.journals(window, *ARGV)
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
@@ -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.deleteln() }
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('Change month: [yyyy] m')&.split(/[\/\s]/)
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
- window << "#{date} "
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.deleteln() }
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
- new_amount = edit_amount(record[position][@d_v][:amount])
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
- def edit_amount(current = nil)
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(4, 30, (window.maxy-4)/2, (window.maxx - 30)/2)
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
- padding = [0, 30 - message.length - 4].max
217
- sub << " #{message + ' ' * padding}"
218
- clrtoeol
237
+ sub << " #{message}"
238
+ sub.clrtoeol
219
239
  sub.setpos(2, 1)
220
- sub << " > #{' ' * (30 - 6)}"
221
- sub.setpos(2, 6)
222
- clrtoeol
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
- list = @dict.map{ |code, entry| { code: code, label: entry[:label] } }
235
- .select{ |d| d[:code].length >= 3 }
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
- line = format("%s %s", entry[:code], entry[:label])
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
- window.attron(A_REVERSE) { window << line }
285
+ sub.attron(A_REVERSE) { sub << line }
249
286
  else
250
- window << line
287
+ sub << line
251
288
  end
252
- clrtoeol
253
- window << "\n"
289
+ sub.clrtoeol
290
+ #sub << "\n"
254
291
  end
255
- (window.maxy - window.cury).times { window.deleteln() }
256
- window.refresh
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
- return code
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
- window << sprintf("%s %s |%s| ",
299
- date&.mb_rjust(10, ' ') || '',
300
- txid || '',
301
- lines > 1 ? lines.to_s : ' ',
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 > 80
321
- rest += " | #{dat[:note].mb_truncate(window.maxx - 80)}"
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
- n_idx = @index - window.maxy
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 >= window.maxy - 1 ? window.maxy - 1 : @active + 1
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
- n_idx = @index + window.maxy
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 = window.maxy - 1
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
- return data if data.nil? || data.length <= window.maxy
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, window.maxy)
471
+ data.slice(0, maxy)
392
472
  else
393
- if @active == (window.maxy - 1)
394
- data.slice(@index - window.maxy + 1, window.maxy)
473
+ if @active == (maxy - 1)
474
+ data.slice(@index - maxy + 1, maxy)
395
475
  elsif @active == 0
396
- data.slice(@index, window.maxy)
476
+ data.slice(@index, maxy)
397
477
  else
398
478
  @visible
399
479
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LucaTerm
4
- VERSION = '0.1.2'
4
+ VERSION = '0.2.0'
5
5
  end
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.1.2
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: 2021-07-30 00:00:00.000000000 Z
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.3
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: []