lucaterm 0.1.0 → 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: bd6fad88274a18a524e2ce05ca8b6e25fccbe359ba7463c307a6528079861667
4
- data.tar.gz: d045d3218d25d78b6dc69e93140d7eb5ea0b88d72e248028fc6e15fbf9bce077
3
+ metadata.gz: c072f724a2ad3fb09fc648c4912c974d8c874fd82165e525bb6cd3d06073f645
4
+ data.tar.gz: 6d6a198f22a9fab3ea8c6426f98571927577862b61f1b6b4f955e8442375b778
5
5
  SHA512:
6
- metadata.gz: 83ad42fcd1547ad456fb4708818db8b2e5e0dad52ec083a6a575ab83a675c16068a384cd224ba9a36c8d31f33eb81d3bd99f66e10167b8dee4042b219c4a4984
7
- data.tar.gz: 900fcdd82a1afd724e8c7c9a36aa76d767129798719427278dbe5f94c51966d110476cf12828c43038147e7b141fc34267e1558125ad9054c0a6b61e6b6e4d9f
6
+ metadata.gz: 8d6b3ec02aaf1dac932d47dc74fa0c61604915a7ab79a3198c83075f67d94a5cffd5c5a2673cd8c852806aafcc8d875cc3040d5680c248fcfdcc25797c0030c2
7
+ data.tar.gz: 74d40973ea0f60f6e5dc9d40fd52d3548683525ffe018b5c1e054175dc06303eac3da2d4da9a10bff02c4f5e496bef4d495180dac7a5b37352c7d5816a606d11
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
- ## LucaTerm master
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
+
7
+ ## LucaTerm 0.1.2
8
+
9
+ * `luca book`: '<' for prev month, '>' for next month.
10
+ * `luca book`: Refine 'm' command. Year is now optional. Cleanup dialog background.
11
+
12
+ ## LucaTerm 0.1.1
2
13
 
3
14
  * `luca book`: handle cursor on journal detail
4
- * `luca book`: edit journal
15
+ * `luca book`: add / edit journal
5
16
  * `luca book`: move to another 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,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,28 +59,68 @@ 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)
86
+ when 'N'
87
+ newdate = edit_dialog "Enter date of new record: YYYY-m-d", title: 'Create Journal'
88
+ tmpl = {
89
+ date: newdate,
90
+ debit: [
91
+ { code: '10XX', amount: 0 }
92
+ ],
93
+ credit: [
94
+ { code: '50XX', amount: 0 }
95
+ ],
96
+ }
97
+ show_detail(tmpl)
60
98
  when 'q'
61
99
  exit 0
62
100
  end
63
101
  end
64
102
  end
65
103
 
104
+ # render each journal
105
+ #
66
106
  def show_detail(record)
67
107
  @d_v = 0
68
108
  @d_h = 0
69
109
  debit_length = Array(record[:debit]).length
70
110
  credit_length = Array(record[:credit]).length
71
111
  date, txid = LucaSupport::Code.decode_id(record[:id]) if record[:id]
112
+ fileid = record[:id].split('/').last if record[:id]
113
+ date ||= record[:date]
114
+ modeline.setpos(0, 0)
115
+ modeline << "#{date} #{fileid} "
116
+ modeline.clrtoeol
117
+ modeline.refresh
118
+
72
119
  loop do
73
120
  window.setpos(0, 0)
74
- window << "#{date} "
121
+ if record.dig(:headers, 'x-customer')
122
+ window << format(" [%s] ", record.dig(:headers, 'x-customer'))
123
+ end
75
124
  window << record[:note]
76
125
  clrtoeol; window << "\n"
77
126
  [debit_length, credit_length].max.times do |i|
@@ -88,7 +137,7 @@ module LucaTerm
88
137
  clrtoeol
89
138
  window << "\n"
90
139
  end
91
- (window.maxy - window.cury).times { window.deleteln() }
140
+ (window.maxy - window.cury).times { window << "\n" }
92
141
  window.refresh
93
142
 
94
143
  window.keypad(true)
@@ -131,7 +180,7 @@ module LucaTerm
131
180
  @d_h = 0
132
181
  end
133
182
  when 'n'
134
- position = [0,1].include?(@d_h) ? :debit : :credit
183
+ position = [0, 1].include?(@d_h) ? :debit : :credit
135
184
  new_code = select_code
136
185
  next if new_code.nil?
137
186
 
@@ -142,20 +191,25 @@ module LucaTerm
142
191
  debit_length = Array(record[:debit]).length
143
192
  credit_length = Array(record[:credit]).length
144
193
  when KEY_CTRL_J
145
- position = [0,1].include?(@d_h) ? :debit : :credit
194
+ position, counter = [0,1].include?(@d_h) ? [:debit, :credit] : [:credit, :debit]
146
195
  if [0, 2].include? @d_h
147
196
  new_code = select_code
148
197
  next if new_code.nil?
149
198
 
150
199
  record[position][@d_v][:code] = new_code
151
200
  else
152
- 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)
153
203
  next if new_amount.nil?
154
204
 
155
205
  record[position][@d_v][:amount] = new_amount
156
206
  end
157
207
  when 's', KEY_CTRL_S
158
- LucaBook::Journal.save record
208
+ if record[:id]
209
+ LucaBook::Journal.save record
210
+ else
211
+ LucaBook::Journal.create record
212
+ end
159
213
  break
160
214
  when 'q'
161
215
  break
@@ -163,9 +217,12 @@ module LucaTerm
163
217
  end
164
218
  end
165
219
 
166
- 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."
167
224
  begin
168
- scmd = edit_dialog "Current: #{current&.to_s}"
225
+ scmd = edit_dialog "Current: #{current&.to_s}", diff_msg, title: 'Edit Amount'
169
226
  return nil if scmd.length == 0
170
227
  # TODO: guard from not number
171
228
  return scmd.to_i
@@ -174,15 +231,22 @@ module LucaTerm
174
231
  end
175
232
  end
176
233
 
177
- def edit_dialog(message = '')
178
- sub = window.subwin(4, 30, (window.maxy-4)/2, (window.maxx - 30)/2)
179
- sub.box(?|, ?-)
180
- sub.setpos(1, 3)
181
- sub << message
182
- 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
183
241
  sub.setpos(2, 3)
184
- sub << "> "
185
- 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)
186
250
  sub.refresh
187
251
  loop do
188
252
  echo
@@ -193,58 +257,75 @@ module LucaTerm
193
257
  end
194
258
  end
195
259
 
260
+ # returns Account code after selection from list dialog
261
+ #
196
262
  def select_code
197
- list = @dict.map{ |code, entry| { code: code, label: entry[:label] } }
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
198
272
  visible_dup = @visible
199
273
  index_dup = @index
200
274
  active_dup = @active
201
275
  @index = 0
202
276
  @active = 0
203
277
  @visible = nil
204
- @visible = set_visible(list)
278
+ @visible = set_visible(list, sub.maxy - 2)
205
279
  loop do
206
- window.setpos(0,0)
207
280
  @visible.each.with_index(0) do |entry, i|
208
- 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])
209
284
  if i == @active
210
- window.attron(A_REVERSE) { window << line }
211
- elsif @visible[i][:code].length <= 2
212
- window.attron(A_UNDERLINE) { window << line }
285
+ sub.attron(A_REVERSE) { sub << line }
213
286
  else
214
- window << line
287
+ sub << line
215
288
  end
216
- clrtoeol
217
- window << "\n"
289
+ sub.clrtoeol
290
+ #sub << "\n"
218
291
  end
219
- (window.maxy - window.cury).times { window.deleteln() }
220
- 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
221
297
 
222
298
  cmd = window.getch
223
299
  case cmd
224
300
  when KEY_DOWN, 'j', KEY_CTRL_N
225
301
  next if @index >= list.length - 1
226
302
 
227
- cursor_down list
303
+ cursor_down list, sub.maxy - 2
228
304
  when KEY_NPAGE
229
- cursor_pagedown list
305
+ cursor_pagedown list, sub.maxy - 2
230
306
  when KEY_UP, 'k', KEY_CTRL_P
231
307
  next if @index <= 0
232
308
 
233
309
  cursor_up list
234
310
  when KEY_PPAGE
235
- 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
236
316
  when 'G'
237
- cursor_last list
317
+ cursor_last list, sub.maxy - 2
238
318
  when KEY_CTRL_J
239
- code = list[@index][:code]
240
319
  @visible = visible_dup
241
320
  @index = index_dup
242
321
  @active = active_dup
243
- return code
322
+ sub.close
323
+ return list[@index][:code]
244
324
  when 'q'
245
325
  @visible = visible_dup
246
326
  @index = index_dup
247
327
  @active = active_dup
328
+ sub.close
248
329
  return nil
249
330
  end
250
331
  end
@@ -252,17 +333,36 @@ module LucaTerm
252
333
 
253
334
  private
254
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
+
255
348
  def draw_line(dat, cursor = nil, note = false)
256
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, ' ')
257
351
  debit_cd = fmt_code(dat[:debit])
258
352
  debit_amount = fmt_amount(dat[:debit])
259
353
  credit_cd = fmt_code(dat[:credit])
260
354
  credit_amount = fmt_amount(dat[:credit])
261
355
  lines = [Array(dat[:debit]).length, Array(dat[:credit]).length].max
262
- window << sprintf("%s %s |%s| ",
263
- date&.mb_rjust(10, ' ') || '',
264
- txid || '',
265
- 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,
266
366
  )
267
367
  case cursor
268
368
  when 0
@@ -281,8 +381,8 @@ module LucaTerm
281
381
  window.attron(A_REVERSE) { window << credit_amount }
282
382
  else
283
383
  rest = format("%s %s | %s %s", debit_cd, debit_amount, credit_cd, credit_amount)
284
- if note && window.maxx > 80
285
- rest += " | #{dat[:note].mb_truncate(window.maxx - 80)}"
384
+ if note && window.maxx > 70
385
+ rest += " | #{dat[:note]&.mb_truncate(window.maxx - 70)}"
286
386
  end
287
387
  if cursor == :full
288
388
  window.attron(A_REVERSE) { window << rest }
@@ -318,46 +418,62 @@ module LucaTerm
318
418
  @visible = set_visible(data)
319
419
  end
320
420
 
321
- def cursor_pageup(data)
322
- n_idx = @index - window.maxy
421
+ def cursor_pageup(data, maxy = nil)
422
+ maxy ||= window.maxy
423
+ n_idx = @index - maxy
323
424
  return if n_idx <= 0
324
425
 
325
426
  @index = n_idx
326
427
  @active = 0
327
- @visible = set_visible(data)
428
+ @visible = set_visible(data, maxy)
328
429
  end
329
430
 
330
- def cursor_down(data)
431
+ def cursor_down(data, maxy = nil)
432
+ maxy ||= window.maxy
331
433
  @index += 1
332
- @active = @active >= window.maxy - 1 ? window.maxy - 1 : @active + 1
333
- @visible = set_visible(data)
434
+ @active = @active >= maxy - 1 ? @active : @active + 1
435
+ @visible = set_visible(data, maxy)
334
436
  end
335
437
 
336
- def cursor_pagedown(data)
337
- n_idx = @index + window.maxy
438
+ def cursor_pagedown(data, maxy = nil)
439
+ maxy ||= window.maxy
440
+ n_idx = @index + maxy
338
441
  return if n_idx >= data.length - 1
339
442
 
340
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
+
341
455
  @active = 0
342
456
  @visible = set_visible(data)
343
457
  end
344
458
 
345
- def cursor_last(data)
459
+ def cursor_last(data, maxy = nil)
460
+ maxy ||= window.maxy
346
461
  @index = data.length - 1
347
- @active = window.maxy - 1
348
- @visible = set_visible(data)
462
+ @active = maxy - 1
463
+ @visible = set_visible(data, maxy)
349
464
  end
350
465
 
351
- def set_visible(data)
352
- 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
353
469
 
354
470
  if @visible.nil?
355
- data.slice(0, window.maxy)
471
+ data.slice(0, maxy)
356
472
  else
357
- if @active == (window.maxy - 1)
358
- data.slice(@index - window.maxy + 1, window.maxy)
473
+ if @active == (maxy - 1)
474
+ data.slice(@index - maxy + 1, maxy)
359
475
  elsif @active == 0
360
- data.slice(@index, window.maxy)
476
+ data.slice(@index, maxy)
361
477
  else
362
478
  @visible
363
479
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LucaTerm
4
- VERSION = '0.1.0'
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.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: 2021-03-04 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: []