lucaterm 0.1.1 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb1f374a09f0de19e379ea58a0d99d4dc7a0e5eb417bb98121c45a9b60b16043
4
- data.tar.gz: 7fe2cf713fafcc830e1e43132385988073e9ea60b856916f19980f8e9bc8e7d1
3
+ metadata.gz: 2b89d7a16d5e1b11ec8bc4caccc299fff6dcaa7edceda8d3ae3e0ad8d24686bb
4
+ data.tar.gz: 448a0ed6d343ffd78a09f8f83e80cf729699f49db1f0c3cced73d7cb116cc595
5
5
  SHA512:
6
- metadata.gz: db9a08c3a2014a9c35e51ec242ea1b1d1ab74fb211dddf6cf2baedc6a6ef7d1a615db33dc12ae6d1c06f1b275049e3da7f767c34e5f13aa89f2c770e9d76309a
7
- data.tar.gz: 24da76ce1f12b9a55d974375274ce7c8a406502bd1e083760b1e22a565eb78bb6ccf0a9559105d84aaae0703a0d6cfc3a228f6bf732f221a16fe55ac9280e56d
6
+ metadata.gz: 9e50a20eb61aa70ac29caefedbb0316aaa578179eed5d5c5df340161e821ab5b02b65657a4b39e473bba547e356537153e405a0c508ecf48ec3b036238dffbce
7
+ data.tar.gz: 53fb5a7d452aaaf0a528e2785601a06d36d16f8790fa48981ab21d43d4f08dbdd43925db67a522f6911be7ff348cd911baa1a49447b199f8271a621f6b23cf53
data/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## LucaTerm 0.2.1
2
+
3
+ * `luca book` Fix: proper account code selection
4
+
5
+ ## LucaTerm 0.2.0
6
+
7
+ * `luca book`: Works without args. Show journals of the latest month.
8
+ * `luca book`: Show categories on Account code selection
9
+ * `luca book`: Show x-customer header on detail screen
10
+
11
+ ## LucaTerm 0.1.2
12
+
13
+ * `luca book`: '<' for prev month, '>' for next month.
14
+ * `luca book`: Refine 'm' command. Year is now optional. Cleanup dialog background.
15
+
1
16
  ## LucaTerm 0.1.1
2
17
 
3
18
  * `luca book`: handle cursor on journal detail
data/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # LucaTerm
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/lucaterm.svg)](https://badge.fury.io/rb/lucaterm)
4
+ [![doc](https://img.shields.io/badge/doc-rubydoc-green.svg)](https://www.rubydoc.info/gems/lucaterm/index)
5
+ ![license](https://img.shields.io/github/license/chumaltd/luca)
6
+
7
+ Terminal frontend app for LucaSuite.
8
+
9
+ ## Usage
10
+
11
+ ```bash
12
+ $ luca book [yyyy m]
13
+ ```
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,14 +4,17 @@ 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
- def initialize(window, data=nil)
13
+ def initialize(window, year, month, data=nil)
14
+ @modeline = Window.new(1, 0, 0, 0)
14
15
  @window = window
16
+ @year = year
17
+ @month = month
15
18
  @data = data
16
19
  @index = 0
17
20
  @active = 0 # active line in window
@@ -21,19 +24,25 @@ module LucaTerm
21
24
  end
22
25
 
23
26
  def self.journals(window, *args)
24
- new(window, LucaSupport::Code.readable(LucaBook::List.term(*args).data))
27
+ new(window, args[0], args[1], LucaSupport::Code.readable(LucaBook::List.term(*args).data))
25
28
  end
26
29
 
30
+ # render monthly journal list
31
+ #
27
32
  def main_loop
28
33
  loop do
34
+ modeline.setpos(0, 0)
35
+ modeline << "#{Date::ABBR_MONTHNAMES[@month.to_i]} #{@year}"
36
+ modeline.clrtoeol
37
+ modeline.refresh
38
+
29
39
  window.setpos(0,0)
30
40
  @visible.each.with_index(0) do |dat, i|
31
41
  cursor = i == @active ? :full : nil
32
42
  draw_line(dat, cursor, true)
33
- clrtoeol
34
43
  window << "\n"
35
44
  end
36
- (window.maxy - window.cury).times { window.deleteln() }
45
+ (window.maxy - window.cury).times { window << "\n" }
37
46
  window.refresh
38
47
 
39
48
  window.keypad(true)
@@ -50,15 +59,32 @@ module LucaTerm
50
59
  when 'G'
51
60
  cursor_last @data
52
61
  when 'm'
53
- ym = edit_dialog('Change month: yyyy m')&.split(/[\/\s]/)
62
+ ym = edit_dialog('Enter: [yyyy] m', title: 'Change Month')&.split(/[\/\s]/)
63
+ ym = [@year, ym[0]] if ym.length == 1
54
64
  @data = LucaSupport::Code.readable(LucaBook::List.term(*ym).data)
65
+ @year, @month = ym
66
+ @index = 0
67
+ @active = 0
68
+ @visible = set_visible(@data)
69
+ when '<'
70
+ target = Date.parse("#{@year}-#{@month}-1").prev_month
71
+ @data = LucaSupport::Code.readable(LucaBook::List.term(target.year, target.month).data)
72
+ @year, @month = target.year, target.month
73
+ @index = 0
74
+ @active = 0
75
+ @visible = set_visible(@data)
76
+ when '>'
77
+ target = Date.parse("#{@year}-#{@month}-1").next_month
78
+ @data = LucaSupport::Code.readable(LucaBook::List.term(target.year, target.month).data)
79
+ @year, @month = target.year, target.month
55
80
  @index = 0
56
81
  @active = 0
57
82
  @visible = set_visible(@data)
58
83
  when KEY_ENTER, KEY_CTRL_J
59
84
  show_detail(@data[@index])
85
+ @visible = set_visible(@data)
60
86
  when 'N'
61
- 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'
62
88
  tmpl = {
63
89
  date: newdate,
64
90
  debit: [
@@ -75,16 +101,26 @@ module LucaTerm
75
101
  end
76
102
  end
77
103
 
104
+ # render each journal
105
+ #
78
106
  def show_detail(record)
79
107
  @d_v = 0
80
108
  @d_h = 0
81
109
  debit_length = Array(record[:debit]).length
82
110
  credit_length = Array(record[:credit]).length
83
111
  date, txid = LucaSupport::Code.decode_id(record[:id]) if record[:id]
112
+ fileid = record[:id].split('/').last if record[:id]
84
113
  date ||= record[:date]
114
+ modeline.setpos(0, 0)
115
+ modeline << "#{date} #{fileid} "
116
+ modeline.clrtoeol
117
+ modeline.refresh
118
+
85
119
  loop do
86
120
  window.setpos(0, 0)
87
- window << "#{date} "
121
+ if record.dig(:headers, 'x-customer')
122
+ window << format(" [%s] ", record.dig(:headers, 'x-customer'))
123
+ end
88
124
  window << record[:note]
89
125
  clrtoeol; window << "\n"
90
126
  [debit_length, credit_length].max.times do |i|
@@ -101,7 +137,7 @@ module LucaTerm
101
137
  clrtoeol
102
138
  window << "\n"
103
139
  end
104
- (window.maxy - window.cury).times { window.deleteln() }
140
+ (window.maxy - window.cury).times { window << "\n" }
105
141
  window.refresh
106
142
 
107
143
  window.keypad(true)
@@ -155,14 +191,15 @@ module LucaTerm
155
191
  debit_length = Array(record[:debit]).length
156
192
  credit_length = Array(record[:credit]).length
157
193
  when KEY_CTRL_J
158
- position = [0,1].include?(@d_h) ? :debit : :credit
194
+ position, counter = [0,1].include?(@d_h) ? [:debit, :credit] : [:credit, :debit]
159
195
  if [0, 2].include? @d_h
160
196
  new_code = select_code
161
197
  next if new_code.nil?
162
198
 
163
199
  record[position][@d_v][:code] = new_code
164
200
  else
165
- 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)
166
203
  next if new_amount.nil?
167
204
 
168
205
  record[position][@d_v][:amount] = new_amount
@@ -180,9 +217,12 @@ module LucaTerm
180
217
  end
181
218
  end
182
219
 
183
- 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."
184
224
  begin
185
- scmd = edit_dialog "Current: #{current&.to_s}"
225
+ scmd = edit_dialog "Current: #{current&.to_s}", diff_msg, title: 'Edit Amount'
186
226
  return nil if scmd.length == 0
187
227
  # TODO: guard from not number
188
228
  return scmd.to_i
@@ -191,15 +231,22 @@ module LucaTerm
191
231
  end
192
232
  end
193
233
 
194
- def edit_dialog(message = '')
195
- sub = window.subwin(4, 30, (window.maxy-4)/2, (window.maxx - 30)/2)
196
- sub.box(?|, ?-)
197
- sub.setpos(1, 3)
198
- sub << message
199
- clrtoeol
234
+ def edit_dialog(message = '', submessage = '', title: '')
235
+ sub = window.subwin(5, 30, (window.maxy-5)/2, (window.maxx - 30)/2)
236
+ sub.setpos(1, 1)
237
+ sub << " #{message}"
238
+ sub.clrtoeol
239
+ sub.setpos(2, 1)
240
+ sub.clrtoeol
200
241
  sub.setpos(2, 3)
201
- sub << "> "
202
- clrtoeol
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)
203
250
  sub.refresh
204
251
  loop do
205
252
  echo
@@ -210,57 +257,76 @@ module LucaTerm
210
257
  end
211
258
  end
212
259
 
260
+ # returns Account code after selection from list dialog
261
+ #
213
262
  def select_code
214
- list = @dict.map{ |code, entry| { code: code, label: entry[:label] } }
215
- .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
216
272
  visible_dup = @visible
217
273
  index_dup = @index
218
274
  active_dup = @active
219
275
  @index = 0
220
276
  @active = 0
221
277
  @visible = nil
222
- @visible = set_visible(list)
278
+ @visible = set_visible(list, sub.maxy - 2)
223
279
  loop do
224
- window.setpos(0,0)
225
280
  @visible.each.with_index(0) do |entry, i|
226
- 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])
227
284
  if i == @active
228
- window.attron(A_REVERSE) { window << line }
285
+ sub.attron(A_REVERSE) { sub << line }
229
286
  else
230
- window << line
287
+ sub << line
231
288
  end
232
- clrtoeol
233
- window << "\n"
289
+ sub.clrtoeol
290
+ #sub << "\n"
234
291
  end
235
- (window.maxy - window.cury).times { window.deleteln() }
236
- 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
237
297
 
238
298
  cmd = window.getch
239
299
  case cmd
240
300
  when KEY_DOWN, 'j', KEY_CTRL_N
241
301
  next if @index >= list.length - 1
242
302
 
243
- cursor_down list
303
+ cursor_down list, sub.maxy - 2
244
304
  when KEY_NPAGE
245
- cursor_pagedown list
305
+ cursor_pagedown list, sub.maxy - 2
246
306
  when KEY_UP, 'k', KEY_CTRL_P
247
307
  next if @index <= 0
248
308
 
249
309
  cursor_up list
250
310
  when KEY_PPAGE
251
- 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
252
316
  when 'G'
253
- cursor_last list
317
+ cursor_last list, sub.maxy - 2
254
318
  when KEY_CTRL_J
255
- code = list[@index][:code]
319
+ selected = list[@index][:code]
256
320
  @visible = visible_dup
257
321
  @index = index_dup
258
322
  @active = active_dup
259
- return code
323
+ sub.close
324
+ return selected
260
325
  when 'q'
261
326
  @visible = visible_dup
262
327
  @index = index_dup
263
328
  @active = active_dup
329
+ sub.close
264
330
  return nil
265
331
  end
266
332
  end
@@ -268,17 +334,36 @@ module LucaTerm
268
334
 
269
335
  private
270
336
 
337
+ def account_index(maxx = 50)
338
+ term = if maxx >= 45
339
+ ['Assets', 'Liabilities', 'Net Assets', 'Sales', 'Expenses']
340
+ elsif maxx >= 40
341
+ ['Assets', 'LIAB', 'NetAsset', 'Sales', 'EXP']
342
+ else
343
+ ['', '', '', '', '']
344
+ end
345
+ len = term.map { |str| str.length }.max
346
+ term.map { |str| str += ' ' * (len - str.length) }
347
+ end
348
+
271
349
  def draw_line(dat, cursor = nil, note = false)
272
350
  date, txid = LucaSupport::Code.decode_id(dat[:id]) if dat[:id]
351
+ date_str = date.nil? ? '' : date.split('-')[1, 2].join('/')&.mb_rjust(5, ' ')
273
352
  debit_cd = fmt_code(dat[:debit])
274
353
  debit_amount = fmt_amount(dat[:debit])
275
354
  credit_cd = fmt_code(dat[:credit])
276
355
  credit_amount = fmt_amount(dat[:credit])
277
356
  lines = [Array(dat[:debit]).length, Array(dat[:credit]).length].max
278
- window << sprintf("%s %s |%s| ",
279
- date&.mb_rjust(10, ' ') || '',
280
- txid || '',
281
- lines > 1 ? lines.to_s : ' ',
357
+ lines = if lines == 1
358
+ ' '
359
+ elsif lines > 9
360
+ '+'
361
+ else
362
+ lines
363
+ end
364
+ window << sprintf("%s |%s| ",
365
+ date_str,
366
+ lines,
282
367
  )
283
368
  case cursor
284
369
  when 0
@@ -297,8 +382,8 @@ module LucaTerm
297
382
  window.attron(A_REVERSE) { window << credit_amount }
298
383
  else
299
384
  rest = format("%s %s | %s %s", debit_cd, debit_amount, credit_cd, credit_amount)
300
- if note && window.maxx > 80
301
- rest += " | #{dat[:note].mb_truncate(window.maxx - 80)}"
385
+ if note && window.maxx > 70
386
+ rest += " | #{dat[:note]&.mb_truncate(window.maxx - 70)}"
302
387
  end
303
388
  if cursor == :full
304
389
  window.attron(A_REVERSE) { window << rest }
@@ -334,46 +419,62 @@ module LucaTerm
334
419
  @visible = set_visible(data)
335
420
  end
336
421
 
337
- def cursor_pageup(data)
338
- n_idx = @index - window.maxy
422
+ def cursor_pageup(data, maxy = nil)
423
+ maxy ||= window.maxy
424
+ n_idx = @index - maxy
339
425
  return if n_idx <= 0
340
426
 
341
427
  @index = n_idx
342
428
  @active = 0
343
- @visible = set_visible(data)
429
+ @visible = set_visible(data, maxy)
344
430
  end
345
431
 
346
- def cursor_down(data)
432
+ def cursor_down(data, maxy = nil)
433
+ maxy ||= window.maxy
347
434
  @index += 1
348
- @active = @active >= window.maxy - 1 ? window.maxy - 1 : @active + 1
349
- @visible = set_visible(data)
435
+ @active = @active >= maxy - 1 ? @active : @active + 1
436
+ @visible = set_visible(data, maxy)
350
437
  end
351
438
 
352
- def cursor_pagedown(data)
353
- n_idx = @index + window.maxy
439
+ def cursor_pagedown(data, maxy = nil)
440
+ maxy ||= window.maxy
441
+ n_idx = @index + maxy
354
442
  return if n_idx >= data.length - 1
355
443
 
356
444
  @index = n_idx
445
+ @active = 0
446
+ @visible = set_visible(data, maxy)
447
+ end
448
+
449
+ def cursor_jump(tabstop, data, rev: false)
450
+ @index = if rev
451
+ tabstop.filter{ |t| t < @index ? t : nil }.max || @index
452
+ else
453
+ tabstop.filter{ |t| t > @index ? t : nil }.min || @index
454
+ end
455
+
357
456
  @active = 0
358
457
  @visible = set_visible(data)
359
458
  end
360
459
 
361
- def cursor_last(data)
460
+ def cursor_last(data, maxy = nil)
461
+ maxy ||= window.maxy
362
462
  @index = data.length - 1
363
- @active = window.maxy - 1
364
- @visible = set_visible(data)
463
+ @active = maxy - 1
464
+ @visible = set_visible(data, maxy)
365
465
  end
366
466
 
367
- def set_visible(data)
368
- return data if data.nil? || data.length <= window.maxy
467
+ def set_visible(data, maxy = nil)
468
+ maxy ||= window.maxy
469
+ return data if data.nil? || data.length <= maxy
369
470
 
370
471
  if @visible.nil?
371
- data.slice(0, window.maxy)
472
+ data.slice(0, maxy)
372
473
  else
373
- if @active == (window.maxy - 1)
374
- data.slice(@index - window.maxy + 1, window.maxy)
474
+ if @active == (maxy - 1)
475
+ data.slice(@index - maxy + 1, maxy)
375
476
  elsif @active == 0
376
- data.slice(@index, window.maxy)
477
+ data.slice(@index, maxy)
377
478
  else
378
479
  @visible
379
480
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LucaTerm
4
- VERSION = '0.1.1'
4
+ VERSION = '0.2.1'
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.1
4
+ version: 0.2.1
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-03-13 00:00:00.000000000 Z
11
+ date: 2022-03-27 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:
@@ -105,6 +119,7 @@ extensions: []
105
119
  extra_rdoc_files: []
106
120
  files:
107
121
  - CHANGELOG.md
122
+ - README.md
108
123
  - exe/luca
109
124
  - lib/luca_term.rb
110
125
  - lib/luca_term/book.rb
@@ -116,7 +131,7 @@ metadata:
116
131
  homepage_uri: https://github.com/chumaltd/luca/tree/master/lucaterm
117
132
  source_code_uri: https://github.com/chumaltd/luca/tree/master/lucaterm
118
133
  changelog_uri: https://github.com/chumaltd/luca/tree/master/lucaterm/CHANGELOG.md
119
- post_install_message:
134
+ post_install_message:
120
135
  rdoc_options: []
121
136
  require_paths:
122
137
  - lib
@@ -131,8 +146,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
146
  - !ruby/object:Gem::Version
132
147
  version: '0'
133
148
  requirements: []
134
- rubygems_version: 3.2.3
135
- signing_key:
149
+ rubygems_version: 3.2.5
150
+ signing_key:
136
151
  specification_version: 4
137
152
  summary: Terminal frontend for Luca Suite
138
153
  test_files: []