lucabook 0.2.12 → 0.2.20

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: b58568499ebfa245c9f86863537eb2a9b26e563ac20a09583a3b12e29bc713a2
4
- data.tar.gz: 77a382f306e0c269182caca7d48bb6fd8d4f6513664d8cef36a28356cacd8998
3
+ metadata.gz: 82bb098ac3acfbb37c600908b75174c34a665f4d7c9003d9956738783bb9a617
4
+ data.tar.gz: b963a24873e226c898c7b14dba4cf21edaacf4cfdc9f7e6eeab0d04959fa788b
5
5
  SHA512:
6
- metadata.gz: 1526e2b525c357dc06f87253095a8e8434bacc03994432f90dec6571f4bde9fe24b9d1a81ab3b9747a3d45be767022bb9bf64f1a0b88b298e6997bbb95ca4fd2
7
- data.tar.gz: 111d7eff70797d30bc2df769cbd3d1c8c8b83d92ef5bba8ffa74b60bc06fa9f0754fd54d507f556f7c9831c87a418113ecc797755436f84926e8df88cf117622
6
+ metadata.gz: b56ef4dd42ee782bf488cd50e6992415d878f388663ed0fb4f983f7353310ea53a2a0a4e1d94be5f06b51102eb73f7e9d0a7404a05cdccf6487248a1041196d3
7
+ data.tar.gz: 12cfe5f1194d34236ebe69541a9d4b0adf3333a39c1e73024ce0d3ad43639e4147ab48c19c5fc52e08bcbe6473821c850189c0b81e252101456ce70eea80a3c4
@@ -2,71 +2,144 @@
2
2
 
3
3
  require 'optparse'
4
4
  require 'luca_book'
5
- require 'luca_book/console'
6
5
 
7
- def import(args, params)
8
- if params['config']
9
- LucaBook::Import.new(args[0], params['config']).import_csv
10
- else
11
- puts 'Usage: luca-book import -c import_config'
6
+ module LucaCmd
7
+ class Journal
8
+ def self.import(args, params)
9
+ if params['config']
10
+ LucaBook::Import.new(args[0], params['config']).import_csv
11
+ elsif params['json']
12
+ LucaBook::Import.import_json(STDIN.read)
13
+ else
14
+ puts 'Usage: luca-book import -c import_config'
15
+ exit 1
16
+ end
17
+ end
18
+
19
+ def self.list(args, params)
20
+ args = LucaCmd.gen_range(params[:n] || 1) if args.empty?
21
+ if params['code']
22
+ LucaBook::List.term(*args, code: params['code']).list_on_code.to_yaml
23
+ else
24
+ LucaBook::List.term(*args).list_journals.to_yaml
25
+ end
26
+ end
27
+
28
+ def self.stats(args, params)
29
+ args = LucaCmd.gen_range(params[:n]) if args.empty?
30
+ LucaBook::State.term(*args).stats(params[:level])
31
+ end
12
32
  end
13
33
 
14
- def list(args, params)
15
- if params['c'] or params['code']
16
- code = params['c'] || params['code']
17
- LucaBookConsole.new.by_code(code, args.dig(0), args.dig(1))
18
- elsif args.length > 0
19
- LucaBookConsole.new.by_month(args[0], args.dig(1))
20
- else
21
- LucaBookConsole.new.all
34
+ class Report
35
+ def self.balancesheet(args, params)
36
+ level = params[:level] || 3
37
+ legal = params[:legal] || false
38
+ args = LucaCmd.gen_range(params[:n] || 1) if args.empty?
39
+ LucaBook::State.term(*args).bs(level, legal: legal)
40
+ end
41
+
42
+ def self.profitloss(args, params)
43
+ level = params[:level] || 2
44
+ args = LucaCmd.gen_range(params[:n]) if args.empty?
45
+ LucaBook::State.term(*args).pl(level).to_yaml
46
+ end
22
47
  end
23
- end
24
48
 
25
- def report(args, params)
26
- if params['bs']
27
- LucaBook::State.term(*args).bs.to_yaml
28
- elsif params['pl']
29
- LucaBook::State.term(*args).pl.to_yaml
30
- else
31
- LucaBook::State.term(*args).to_yaml
49
+ module_function
50
+
51
+ def gen_range(count)
52
+ count ||= 3
53
+ today = Date.today
54
+ start = today.prev_month(count - 1)
55
+ [start.year, start.month, today.year, today.month]
32
56
  end
33
57
  end
34
58
 
59
+ def new_pj(args = nil, params = {})
60
+ LucaBook::Setup.create_project params['country'], args[0]
61
+ end
62
+
35
63
  LucaRecord::Base.valid_project?
36
64
  cmd = ARGV.shift
65
+ params = {}
37
66
 
38
67
  case cmd
39
- when 'import'
40
- params = {}
41
- OptionParser.new do |opt|
42
- opt.banner = 'Usage: luca import filepath'
43
- opt.on('-c', '--config VAL', 'import definition'){|v| params["config"] = v }
44
- args = opt.parse!(ARGV)
45
- import(args, params)
68
+ when /journals?/, 'j'
69
+ subcmd = ARGV.shift
70
+ case subcmd
71
+ when 'import'
72
+ OptionParser.new do |opt|
73
+ opt.banner = 'Usage: luca-book journals import [options] filepath'
74
+ opt.on('-c', '--config VAL', 'import definition'){ |v| params['config'] = v }
75
+ opt.on('-j', '--json', 'import via json format'){ |_v| params['json'] = true }
76
+ args = opt.parse!(ARGV)
77
+ LucaCmd::Journal.import(args, params)
78
+ end
79
+ when 'list'
80
+ OptionParser.new do |opt|
81
+ opt.banner = 'Usage: luca-book journals list [options] [YYYY M]'
82
+ opt.on('-c', '--code VAL', 'search with code') { |v| params['code'] = v }
83
+ opt.on('-n VAL', 'report count') { |v| params[:n] = v.to_i }
84
+ opt.on_tail('List records. If you specify code and/or month, search on each criteria.')
85
+ args = opt.parse!(ARGV)
86
+ LucaCmd::Journal.list(args, params)
87
+ end
88
+ when 'stats'
89
+ OptionParser.new do |opt|
90
+ opt.banner = 'Usage: luca-book journals stats [options] [YYYY M]'
91
+ opt.on('-n VAL', 'report count') { |v| params[:n] = v.to_i }
92
+ args = opt.parse!(ARGV)
93
+ LucaCmd::Journal.stats(args, params)
94
+ end
95
+ else
96
+ puts 'Proper subcommand needed.'
97
+ puts
98
+ puts 'Usage: luca-book (j|journal[s]) subcommand [options] [YYYY M YYYY M]'
99
+ puts ' import: import journals from JSON/TSV'
100
+ puts ' list: list journals'
101
+ puts ' stats: list account statistics'
102
+ exit 1
46
103
  end
47
- when 'list'
48
- params = {}
104
+ when 'new'
49
105
  OptionParser.new do |opt|
50
- opt.banner = 'Usage: luca list [year month]'
51
- opt.on('-c', '--code VAL', 'search with code'){|v| params["code"] = v }
52
- opt.on_tail('List records. If you specify code and/or month, search on each criteria.')
53
- args = opt.parse!(ARGV)
54
- list(args, params)
106
+ opt.banner = 'Usage: luca-book new [options] Dir'
107
+ opt.on('-c', '--country VAL', 'specify country code') { |v| params['coountry'] = v }
108
+ args = opt.parse(ARGV)
109
+ new_pj(args, params)
55
110
  end
56
- when 'report'
57
- params = {}
58
- OptionParser.new do |opt|
59
- opt.banner = 'Usage: luca-book report'
60
- opt.on('--bs', 'show Balance sheet'){|v| params["bs"] = v }
61
- opt.on('--pl', 'show Income statement'){|v| params["pl"] = v }
62
- args = opt.parse!(ARGV)
63
- report(args, params)
111
+ when /reports?/, 'r'
112
+ subcmd = ARGV.shift
113
+ case subcmd
114
+ when 'bs'
115
+ OptionParser.new do |opt|
116
+ opt.banner = 'Usage: luca-book reports bs [options] [YYYY M]'
117
+ opt.on('-l', '--level VAL', 'account level') { |v| params[:level] = v.to_i }
118
+ opt.on('--legal', 'show legal mandatory account') { |_v| params[:legal] = true }
119
+ args = opt.parse!(ARGV)
120
+ LucaCmd::Report.balancesheet(args, params)
121
+ end
122
+ when 'pl'
123
+ OptionParser.new do |opt|
124
+ opt.banner = 'Usage: luca-book reports pl [options] [YYYY M YYYY M]'
125
+ opt.on('-l', '--level VAL', 'account level') { |v| params[:level] = v.to_i }
126
+ opt.on('-n VAL', 'report count') { |v| params[:n] = v.to_i }
127
+ args = opt.parse!(ARGV)
128
+ LucaCmd::Report.profitloss(args, params)
129
+ end
130
+ else
131
+ puts 'Proper subcommand needed.'
132
+ puts
133
+ puts 'Usage: luca-book (r|report[s]) (bs|pl) [options] YYYY M'
134
+ puts ' bs: show balance sheet'
135
+ puts ' pl: show statement of income'
136
+ exit 1
64
137
  end
65
- when '--help'
66
- puts 'Usage: luca-book subcommand'
67
- puts ' import: import records'
68
- puts ' list: list records'
69
- puts ' report: show reports'
70
138
  else
71
- puts 'Invalid subcommand'
139
+ puts 'Proper subcommand needed.'
140
+ puts
141
+ puts 'Usage: luca-book (j[ournals]|r[eports]) subcommand'
142
+ puts ' journals: operate journal records'
143
+ puts ' reports: show reports'
144
+ exit 1
72
145
  end
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'csv'
3
4
  require 'luca_record'
4
5
  require 'luca_book/version'
5
6
 
6
7
  module LucaBook
8
+ autoload :Dict, 'luca_book/dict'
7
9
  autoload :Import, 'luca_book/import'
8
10
  autoload :Journal, 'luca_book/journal'
11
+ autoload :List, 'luca_book/list'
9
12
  autoload :Setup, 'luca_book/setup'
10
13
  autoload :State, 'luca_book/state'
14
+ autoload :Util, 'luca_book/util'
11
15
  end
@@ -1,25 +1,15 @@
1
1
  require 'luca_book'
2
2
 
3
+ # This class will be deleted
4
+ #
3
5
  class LucaBookConsole
4
6
 
5
7
  def initialize(dir_path=nil)
6
- @report = LucaBookReport.new(dir_path)
8
+ @report = LucaBook::State.new(dir_path)
7
9
  end
8
10
 
9
- def all
10
- array = @report.scan_terms(@report.book.pjdir).map{|y,m| y}.uniq.map{|year|
11
- @report.book.search(year)
12
- }.flatten
13
- show_records(array)
14
- end
15
-
16
- def by_code(code, year=nil, month=nil)
17
- array = @report.by_code(code, year, month)
18
- show_records(array)
19
- end
20
-
21
- def by_month(year, month)
22
- array = @report.book.search(year, month)
11
+ def by_term(year, month, end_year = year, end_month = month)
12
+ array = @report.book.class.term(year, month, end_year, end_month)
23
13
  show_records(array)
24
14
  end
25
15
 
@@ -39,6 +29,7 @@ class LucaBookConsole
39
29
  end
40
30
  end
41
31
 
32
+ # TODO: deprecated. accumulate_all() already removed.
42
33
  def bs
43
34
  target = []
44
35
  report = []
@@ -66,6 +57,7 @@ class LucaBookConsole
66
57
  puts "---- ----"
67
58
  end
68
59
 
60
+ # TODO: deprecated. accumulate_all() already removed.
69
61
  def pl
70
62
  target = []
71
63
  report = []
@@ -2,12 +2,19 @@
2
2
 
3
3
  require 'luca_support/config'
4
4
  require 'luca_record/dict'
5
+ require 'date'
6
+ require 'pathname'
5
7
 
6
8
  module LucaBook
7
9
  class Dict < LucaRecord::Dict
10
+ def self.latest_balance
11
+ dict_dir = Pathname(LucaSupport::Config::Pjdir) / 'data' / 'balance'
12
+ # TODO: search latest balance dictionary
13
+ load_tsv_dict(dict_dir / 'start.tsv')
14
+ end
8
15
 
9
- @filename = 'dict.tsv'
10
-
11
- Data = load
16
+ def self.issue_date(obj)
17
+ Date.parse(obj.dig('_date', :label))
18
+ end
12
19
  end
13
20
  end
@@ -9,46 +9,50 @@ require 'luca_record'
9
9
 
10
10
  module LucaBook
11
11
  class Import
12
- DEBIT_DEFAULT = '仮払金'
13
- CREDIT_DEFAULT = '仮受金'
12
+ DEBIT_DEFAULT = '10XX'
13
+ CREDIT_DEFAULT = '50XX'
14
14
 
15
15
  def initialize(path, dict)
16
16
  raise 'no such file' unless FileTest.file?(path)
17
17
 
18
18
  @target_file = path
19
19
  # TODO: yaml need to be configurable
20
+ @dict_name = dict
20
21
  @dict = LucaRecord::Dict.new("import-#{dict}.yaml")
21
22
  @code_map = LucaRecord::Dict.reverse(LucaRecord::Dict.load('base.tsv'))
22
- @config = @dict.csv_config
23
+ @config = @dict.csv_config if dict
23
24
  end
24
25
 
25
26
  # === JSON Format:
26
- # {
27
- # "date": "2020-05-04",
28
- # "debit" : [
29
- # {
30
- # "label": "savings accounts",
31
- # "value": 20000
32
- # }
33
- # ],
34
- # "credit" : [
35
- # {
36
- # "label": "trade notes receivable",
37
- # "value": 20000
38
- # }
39
- # ],
40
- # "note": "settlement for the last month trade"
41
- # }
27
+ # [
28
+ # {
29
+ # "date": "2020-05-04",
30
+ # "debit" : [
31
+ # {
32
+ # "label": "savings accounts",
33
+ # "value": 20000
34
+ # }
35
+ # ],
36
+ # "credit" : [
37
+ # {
38
+ # "label": "trade notes receivable",
39
+ # "value": 20000
40
+ # }
41
+ # ],
42
+ # "note": "settlement for the last month trade"
43
+ # }
44
+ # ]
42
45
  #
43
- def import_json(io)
44
- d = JSON.parse(io)
45
- validate(d)
46
+ def self.import_json(io)
47
+ JSON.parse(io).each do |d|
48
+ validate(d)
46
49
 
47
- # dict = LucaBook::Dict.reverse_dict(LucaBook::Dict::Data)
48
- d['debit'].each { |h| h['code'] = @dict.search(h['label'], DEBIT_DEFAULT) }
49
- d['credit'].each { |h| h['code'] = @dict.search(h['label'], CREDIT_DEFAULT) }
50
+ code_map = LucaRecord::Dict.reverse(LucaRecord::Dict.load('base.tsv'))
51
+ d['debit'].each { |h| h['code'] = code_map.dig(h['label']) || DEBIT_DEFAULT }
52
+ d['credit'].each { |h| h['code'] = code_map.dig(h['label']) || CREDIT_DEFAULT }
50
53
 
51
- LucaBook.new.create(d)
54
+ LucaBook::Journal.create(d)
55
+ end
52
56
  end
53
57
 
54
58
  def import_csv
@@ -56,13 +60,23 @@ module LucaBook
56
60
  if @config[:type] == 'single'
57
61
  LucaBook::Journal.create(parse_single(row))
58
62
  elsif @config[:type] == 'double'
59
- p parse_double(row)
63
+ p parse_double(row) # TODO: Not implemented yet
60
64
  else
61
65
  p row
62
66
  end
63
67
  end
64
68
  end
65
69
 
70
+ def self.validate(obj)
71
+ raise 'NoDateKey' unless obj.key?('date')
72
+ raise 'NoDebitKey' unless obj.key?('debit')
73
+ raise 'NoDebitValue' if obj['debit'].empty?
74
+ raise 'NoCreditKey' unless obj.key?('credit')
75
+ raise 'NoCreditValue' if obj['credit'].empty?
76
+ end
77
+
78
+ private
79
+
66
80
  #
67
81
  # convert single entry data
68
82
  #
@@ -72,7 +86,7 @@ module LucaBook
72
86
  d['date'] = parse_date(row)
73
87
  if row.dig(@config[:credit_value])&.empty?
74
88
  d['debit'] = [
75
- { 'code' => search_code(row[@config[:label]], DEBIT_DEFAULT) }
89
+ { 'code' => search_code(row[@config[:label]], @config.dig(:default_debit)) || DEBIT_DEFAULT }
76
90
  ]
77
91
  d['credit'] = [
78
92
  { 'code' => @code_map.dig(@config[:counter_label]) }
@@ -82,12 +96,13 @@ module LucaBook
82
96
  { 'code' => @code_map.dig(@config[:counter_label]) }
83
97
  ]
84
98
  d['credit'] = [
85
- { 'code' => search_code(row[@config[:label]], CREDIT_DEFAULT) }
99
+ { 'code' => search_code(row[@config[:label]], @config.dig(:default_credit)) || CREDIT_DEFAULT }
86
100
  ]
87
101
  end
88
102
  d['debit'][0]['value'] = value
89
103
  d['credit'][0]['value'] = value
90
- d['note'] = row[@config[:note]]
104
+ d['note'] = Array(@config[:note]).map{ |col| row[col] }.join(' ')
105
+ d['x-editor'] = "LucaBook::Import/#{@dict_name}"
91
106
  end
92
107
  end
93
108
 
@@ -98,14 +113,15 @@ module LucaBook
98
113
  {}.tap do |d|
99
114
  d['date'] = parse_date(row)
100
115
  d['debit'] = {
101
- 'code' => search_code(row[@config[:debit_label]], DEBIT_DEFAULT),
116
+ 'code' => search_code(row[@config[:label]], @config.dig(:default_debit)) || DEBIT_DEFAULT,
102
117
  'value' => row.dig(@config[:debit_value])
103
118
  }
104
119
  d['credit'] = {
105
- 'code' => search_code(row[@config[:credit_label]], CREDIT_DEFAULT),
120
+ 'code' => search_code(row[@config[:label]], @config.dig(:default_credit)) || CREDIT_DEFAULT,
106
121
  'value' => row.dig(@config[:credit_value])
107
122
  }
108
- d['note'] = row[@config[:note]]
123
+ d['note'] = Array(@config[:note]).map{ |col| row[col] }.join(' ')
124
+ d['x-editor'] = "LucaBook::Import/#{@dict_name}"
109
125
  end
110
126
  end
111
127
 
@@ -118,13 +134,5 @@ module LucaBook
118
134
 
119
135
  "#{row.dig(@config[:year])}-#{row.dig(@config[:month])}-#{row.dig(@config[:day])}"
120
136
  end
121
-
122
- def validate(obj)
123
- raise 'NoDateKey' if ! obj.has_key?('date')
124
- raise 'NoDebitKey' if ! obj.has_key?('debit')
125
- raise 'NoDebitValue' if obj['debit'].length < 1
126
- raise 'NoCreditKey' if ! obj.has_key?('credit')
127
- raise 'NoCreditValue' if obj['credit'].length < 1
128
- end
129
137
  end
130
138
  end