lucadeal 0.2.13 → 0.2.19

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: 9a2f79f88f7a7047f07f4c2137888f43e29cec70129e0b8d79544d4c8137e2f4
4
- data.tar.gz: 8d3d2dbf4908f7e5851f64003eaf39fd455f34c1e8f51c07dff7685fa913e036
3
+ metadata.gz: 36ce9f92d92079f0049ec346da2921116e7cde669314bccca3653c28dca33b51
4
+ data.tar.gz: b2f591e9bfb9710fa45cdb1d3582f19997d392e84e639531e4b15e77d6b284c7
5
5
  SHA512:
6
- metadata.gz: feb4db73cf4f5d59a1caafb5e18c7e00460e9903244ba9a43ac1781b8a2ffd7b6465d95a309211e98e487b5b77daa5e1463c4647313f001b80f779c2e23354ad
7
- data.tar.gz: 6204831c6bb54534a3527c7280a5c761bfb422a52831d1dd4d26b484fc221ddc78a979d5b66e42263c1a6d76ebeb140fefcb54c63a203d8b292e6ce40e5e2e05
6
+ metadata.gz: 8e2ca7f2a7063d44d29013cdd7448f807c58dbabca341cdeb1a8a118e24ca826ca94ac608f1c4a56eb16cb41cb039a0b39d2b07cb011fdd4a57e4b04c24a7d92
7
+ data.tar.gz: 692b888b1209de7fa6329ffbc7edd1ef7ad84be530b2dad7fcaad64b5c621d0da424e3ac90f21e9db7113057599497c91b61d70f479cf48bdea6bee2c49e1f0e
@@ -0,0 +1,25 @@
1
+ ## LucaDeal 0.2.19
2
+
3
+ * CLI id completion on Customer delete, Contract create/delete
4
+ * add `describe` to Customer / Contract
5
+
6
+ ## LucaDeal 0.2.18
7
+
8
+ * Breaking change: restructure CLI in sub-sub command format.
9
+ * Add 'x-customer', 'x-editor' on export to LucaBook
10
+
11
+ ## LucaDeal 0.2.17
12
+
13
+ * `luca-deal export` export JSON for LucaBook
14
+
15
+ ## LucaDeal 0.2.14
16
+
17
+ * Introduce Product for selling items template.
18
+
19
+ ## LucaDeal 0.2.12
20
+
21
+ * Introduce Sales fee calculation.
22
+
23
+ ## LucaDeal 0.2.10
24
+
25
+ * items can have one time cost at initial month of contract.
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # LucaDeal
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/lucadeal.svg)](https://badge.fury.io/rb/lucadeal)
4
+
3
5
  LucaDeal is Sales contract management application.
4
6
 
5
7
  ## Installation
@@ -20,7 +22,46 @@ Or install it yourself as:
20
22
 
21
23
  ## Usage
22
24
 
23
- TODO: Write usage instructions here
25
+ You can create project skelton with `new` sub command.
26
+
27
+ ```
28
+ $ luca-deal new Dir
29
+ ```
30
+
31
+ Example assumes setup with bundler shim. `bundle exec` prefix may be needed in most cases.
32
+
33
+
34
+ ### Manage Contract
35
+
36
+ Customer object can be created by `customer create` subcommand with name.
37
+
38
+ ```
39
+ $ luca-deal customer create CustomerName
40
+ Successfully generated Customer 5976652cc2d9c0ebf4a8646f7a28aa8d6bd2d606
41
+ Edit customer detail.
42
+ ```
43
+
44
+ Customer is filed under `data/customers` as YAML. Detail need to be editted.
45
+ Then, Contract object is created by `contract create` sub command with customer id.
46
+
47
+ ```
48
+ $ luca-deal contract create 5976652cc2d9c0ebf4a8646f7a28aa8d6bd2d606
49
+ uccessfully generated Contract 814c6fc9fffe5566fe8e7ef683b439b355d612dc
50
+ Conditions are tentative. Edit contract detail.
51
+ ```
52
+
53
+ Contract is filed under `data/contracts` as YAML. Detail need to be editted.
54
+
55
+
56
+ ### Issue invoice
57
+
58
+ Monthly invoices are generated with `invoice create --monthly` sub command. Target month is optional. Without month, this month including today is the target.
59
+
60
+ ```
61
+ $ luca-deal invoice create --monthly [yyyy m]
62
+ ```
63
+
64
+ Invoice conditions are defined by contracts.
24
65
 
25
66
 
26
67
  ## Data Structure
@@ -75,6 +116,8 @@ Fields for subscription customers are as bellows:
75
116
  | terms | | | | |
76
117
  | | billing_cycle | optional | | If 'monthly', invoices are generated on each month. |
77
118
  | | category | optional | | Default: 'subscription' |
119
+ | products | | | | Array of products. |
120
+ | | id | | | reference for Product |
78
121
  | items | | | | Array of items. |
79
122
  | | name | | x | Item name. |
80
123
  | | price | | x | Item price. |
@@ -115,6 +158,7 @@ Invoice is basically auto generated from Customer and Contract objects.
115
158
  | | price | Item price. |
116
159
  | | qty | quantity. Default: 1. |
117
160
  | | type | |
161
+ | | product_id | refrence for Product |
118
162
  | subtotal | | Array of subtotal by tax category. |
119
163
  | | items | amount of items |
120
164
  | | tax | amount of tax |
@@ -5,57 +5,170 @@ require 'date'
5
5
  require 'optparse'
6
6
  require 'luca_deal'
7
7
 
8
- def customer(args = nil, params = {})
9
- case params['mode']
10
- when 'list'
11
- LucaDeal::Customer.new.list_name
12
- when 'create'
13
- if params['name']
14
- id = LucaDeal::Customer.create(name: params['name'])
15
- puts "Successfully generated Customer #{id}" if id
16
- puts 'Edit customer detail.' if id
17
- else
18
- puts 'requires customer\'s name. exit'
19
- exit 1
8
+ module LucaCmd
9
+ class Customer
10
+ def self.create(args = nil, params = {})
11
+ if args
12
+ id = LucaDeal::Customer.create(name: args[0])
13
+ puts "Successfully generated Customer #{id}" if id
14
+ puts 'Edit customer detail.' if id
15
+ else
16
+ puts 'requires customer\'s name. exit'
17
+ exit 1
18
+ end
19
+ end
20
+
21
+ def self.describe(args = nil, params = {})
22
+ if args.empty?
23
+ list = LucaDeal::Customer.id_completion('')
24
+ puts 'requires customer\'s id. exit'
25
+ list.each { |item| puts " #{item[:id]} #{item[:label]}" }
26
+ exit 1
27
+ else
28
+ list = LucaDeal::Customer.id_completion(args[0])
29
+ case list.length
30
+ when 1
31
+ id = LucaDeal::Customer.new.describe(list.first)
32
+ else
33
+ puts 'found multiple contract id. exit'
34
+ list.each { |item| puts " #{item[:id]} #{item[:label]}" }
35
+ exit 1
36
+ end
37
+ end
38
+ end
39
+
40
+ def self.delete(args = nil, params = {})
41
+ if args.empty?
42
+ list = LucaDeal::Customer.id_completion('')
43
+ puts 'requires customer\'s id. exit'
44
+ list.each { |item| puts " #{item[:id]} #{item[:label]}" }
45
+ exit 1
46
+ else
47
+ list = LucaDeal::Customer.id_completion(args[0])
48
+ case list.length
49
+ when 1
50
+ id = LucaDeal::Customer.delete(list.first)
51
+ else
52
+ puts 'found multiple customer\'s id. exit'
53
+ list.each { |item| puts " #{item[:id]} #{item[:label]}" }
54
+ exit 1
55
+ end
56
+ end
57
+ end
58
+
59
+ def self.list(args = nil, params = {})
60
+ LucaDeal::Customer.new.list_name
20
61
  end
21
- else
22
- puts 'invalid option. --help for usage'
23
- exit 1
24
62
  end
25
- end
26
63
 
27
- def contract(args = nil, params = {})
28
- case params['mode']
29
- when 'create'
30
- if params['customer_id']
31
- id = LucaDeal::Contract.new.generate!(params['customer_id'], params['category'])
32
- puts "Successfully generated Contract #{id}" if id
33
- puts 'Conditions are tentative. Edit contract detail.' if id
34
- else
35
- puts 'requires customer\'s id. exit'
36
- exit 1
64
+ class Contract
65
+ def self.create(args = nil, params = {})
66
+ if args.empty?
67
+ list = LucaDeal::Customer.id_completion('')
68
+ puts 'requires customer\'s id. exit'
69
+ list.each { |item| puts " #{item[:id]} #{item[:label]}" }
70
+ exit 1
71
+ else
72
+ list = LucaDeal::Customer.id_completion(args[0])
73
+ case list.length
74
+ when 1
75
+ id = LucaDeal::Contract.new.generate!(list.first, params['category'])
76
+ puts "Successfully generated Contract #{id}" if id
77
+ puts 'Conditions are tentative. Edit contract detail.' if id
78
+ else
79
+ puts 'found multiple customer\'s id. exit'
80
+ list.each { |item| puts " #{item[:id]} #{item[:label]}" }
81
+ exit 1
82
+ end
83
+ end
84
+ end
85
+
86
+ def self.describe(args = nil, params = {})
87
+ if args.empty?
88
+ list = LucaDeal::Contract.id_completion('', label: 'customer_name')
89
+ puts 'requires contract id. exit'
90
+ list.each { |item| puts " #{item[:id]} #{item[:label]}" }
91
+ exit 1
92
+ else
93
+ list = LucaDeal::Contract.id_completion(args[0])
94
+ case list.length
95
+ when 1
96
+ id = LucaDeal::Contract.new.describe(list.first)
97
+ else
98
+ puts 'found multiple contract id. exit'
99
+ list.each { |item| puts " #{item[:id]} #{item[:label]}" }
100
+ exit 1
101
+ end
102
+ end
103
+ end
104
+
105
+ def self.delete(args = nil, params = {})
106
+ if args.empty?
107
+ list = LucaDeal::Contract.id_completion('', label: 'customer_name')
108
+ puts 'requires contract id. exit'
109
+ list.each { |item| puts " #{item[:id]} #{item[:label]}" }
110
+ exit 1
111
+ else
112
+ list = LucaDeal::Contract.id_completion(args[0])
113
+ case list.length
114
+ when 1
115
+ id = LucaDeal::Contract.delete(list.first)
116
+ else
117
+ puts 'found multiple contract id. exit'
118
+ list.each { |item| puts " #{item[:id]} #{item[:label]}" }
119
+ exit 1
120
+ end
121
+ end
37
122
  end
38
- else
39
123
  end
40
- end
41
124
 
42
- def invoice(args = nil, params = {})
43
- date = "#{args[0]}-#{args[1]}-#{args[2] || '1'}" if !args.empty?
44
- case params['mode']
45
- when 'monthly'
46
- LucaDeal::Invoice.new(date).monthly_invoice
47
- when 'mail'
48
- LucaDeal::Invoice.new(date).deliver_mail
49
- when 'preview'
50
- LucaDeal::Invoice.new(date).preview_mail
51
- when 'stats'
52
- if args.empty?
53
- date = "#{Date.today.year}-#{Date.today.month}-1"
54
- count = 3
55
- end
56
- LucaDeal::Invoice.new(date).stats(count || 1)
57
- else
58
- puts 'not implemented mode'
125
+ class Invoice
126
+ def self.create(args = nil, params = {})
127
+ date = "#{args[0]}-#{args[1]}-#{args[2] || '1'}" if !args.empty?
128
+ case params['mode']
129
+ when 'monthly'
130
+ LucaDeal::Invoice.new(date).monthly_invoice
131
+ else
132
+ puts 'not implemented mode'
133
+ end
134
+ end
135
+
136
+ def self.delete(args = nil, params = {})
137
+ if args
138
+ id = LucaDeal::Invoice.delete(args[0])
139
+ else
140
+ puts 'requires contract id. exit'
141
+ exit 1
142
+ end
143
+ end
144
+
145
+ def self.export(args = nil, _params = nil)
146
+ if args
147
+ args << 28 if args.length == 2 # specify safe last day
148
+ LucaDeal::Invoice.new(args.join('-')).export_json
149
+ else
150
+ LucaDeal::Invoice.new.export_json
151
+ end
152
+ end
153
+
154
+ def self.list(args = nil, params = {})
155
+ date = "#{args[0]}-#{args[1]}-#{args[2] || '1'}" if !args.empty?
156
+ if args.empty?
157
+ date = "#{Date.today.year}-#{Date.today.month}-1"
158
+ count = 3
159
+ end
160
+ LucaDeal::Invoice.new(date).stats(count || 1)
161
+ end
162
+
163
+ def self.mail(args = nil, params = {})
164
+ date = "#{args[0]}-#{args[1]}-#{args[2] || '1'}" if !args.empty?
165
+ case params['mode']
166
+ when 'preview'
167
+ LucaDeal::Invoice.new(date).preview_mail
168
+ else
169
+ LucaDeal::Invoice.new(date).deliver_mail
170
+ end
171
+ end
59
172
  end
60
173
  end
61
174
 
@@ -65,54 +178,115 @@ end
65
178
 
66
179
  LucaRecord::Base.valid_project?
67
180
  cmd = ARGV.shift
181
+ params = {}
68
182
 
69
183
  case cmd
70
- when 'customer'
71
- params = {}
72
- OptionParser.new do |opt|
73
- opt.banner = 'Usage: luca-deal customer [options]'
74
- opt.on('--list', 'list all customers') { |v| params['mode'] = 'list' }
75
- opt.on('--create CustomerName', 'register new customer') do |v|
76
- params['mode'] = 'create'
77
- params['name'] = v
184
+ when /customers?/
185
+ subcmd = ARGV.shift
186
+ case subcmd
187
+ when 'list'
188
+ OptionParser.new do |opt|
189
+ opt.banner = 'Usage: luca-deal customers list [options]'
190
+ args = opt.parse(ARGV)
191
+ LucaCmd::Customer.list(args, params)
78
192
  end
79
- args = opt.parse(ARGV)
80
- customer(args, params)
81
- end
82
- when 'contract'
83
- params = {}
84
- OptionParser.new do |opt|
85
- opt.banner = 'Usage: luca-deal contract [options]'
86
- opt.on('--create CustomerId', 'register new contract') do |v|
87
- params['mode'] = 'create'
88
- params['customer_id'] = v
193
+ when 'create'
194
+ OptionParser.new do |opt|
195
+ opt.banner = 'Usage: luca-deal customers create CustomerName'
196
+ args = opt.parse(ARGV)
197
+ LucaCmd::Customer.create(args, params)
89
198
  end
90
- opt.on('--salesfee', 'create contract as sales fee definition') do |_v|
91
- params['category'] = 'sales_fee'
199
+ when 'describe'
200
+ LucaCmd::Customer.describe(ARGV)
201
+ when 'delete'
202
+ LucaCmd::Customer.delete(ARGV)
203
+ else
204
+ puts 'Proper subcommand needed.'
205
+ puts
206
+ puts 'Usage: luca-deal customer[s] subcommands [--help|options]'
207
+ puts ' create'
208
+ puts ' list'
209
+ puts ' describe: show customer with contracts info'
210
+ puts ' delete'
211
+ exit 1
212
+ end
213
+ when /contracts?/
214
+ subcmd = ARGV.shift
215
+ case subcmd
216
+ when 'create'
217
+ OptionParser.new do |opt|
218
+ opt.banner = 'Usage: luca-deal contracts create [options] CustomerId'
219
+ opt.on('--salesfee', 'create contract as sales fee definition') do |_v|
220
+ params['category'] = 'sales_fee'
221
+ end
222
+ args = opt.parse(ARGV)
223
+ LucaCmd::Contract.create(args, params)
92
224
  end
93
- args = opt.parse(ARGV)
94
- contract(args, params)
225
+ when 'describe'
226
+ LucaCmd::Contract.describe(ARGV)
227
+ when 'delete'
228
+ LucaCmd::Contract.delete(ARGV)
229
+ else
230
+ puts 'Proper subcommand needed.'
231
+ puts
232
+ puts 'Usage: luca-deal contract[s] subcommand [--help|options]'
233
+ puts ' create'
234
+ puts ' describe: show contract with puroducts or items info'
235
+ puts ' delete'
236
+ exit 1
95
237
  end
96
- when 'invoice'
97
- params = {}
98
- OptionParser.new do |opt|
99
- opt.banner = 'Usage: luca-deal invoice [options] year month [date]'
100
- opt.on('--monthly', 'generate monthly data') { |v| params['mode'] = 'monthly' }
101
- opt.on('--mail', 'send to customers') { |v| params['mode'] = 'mail' }
102
- opt.on('--preview', 'send to preview user') { |v| params['mode'] = 'preview' }
103
- opt.on('--stats', 'list invoices') { |v| params['mode'] = 'stats' }
104
- args = opt.parse(ARGV)
105
- invoice(args, params)
238
+ when 'export'
239
+ LucaCmd::Invoice.export(ARGV)
240
+ when /invoices?/, 'i'
241
+ subcmd = ARGV.shift
242
+ case subcmd
243
+ when 'create'
244
+ OptionParser.new do |opt|
245
+ opt.banner = 'Usage: luca-deal invoices create [options] year month [date]'
246
+ opt.on('--monthly', 'generate monthly data') { |_v| params['mode'] = 'monthly' }
247
+ args = opt.parse(ARGV)
248
+ LucaCmd::Invoice.create(args, params)
249
+ end
250
+ when 'delete'
251
+ LucaCmd::Invoice.delete(ARGV)
252
+ when 'list'
253
+ OptionParser.new do |opt|
254
+ opt.banner = 'Usage: luca-deal invoices list [options] year month [date]'
255
+ args = opt.parse(ARGV)
256
+ LucaCmd::Invoice.list(args, params)
257
+ end
258
+ when 'mail'
259
+ OptionParser.new do |opt|
260
+ opt.banner = 'Usage: luca-deal invoices mail [options] year month [date]'
261
+ opt.on('--preview', 'send to preview user') { |_v| params['mode'] = 'preview' }
262
+ args = opt.parse(ARGV)
263
+ LucaCmd::Invoice.mail(args, params)
264
+ end
265
+ else
266
+ puts 'Proper subcommand needed.'
267
+ puts
268
+ puts 'Usage: luca-deal invoices subcommand [--help|options]'
269
+ puts ' create'
270
+ puts ' delete'
271
+ puts ' list'
272
+ puts ' mail: send mail with invoice'
273
+ exit 1
106
274
  end
107
275
  when 'new'
108
276
  params = {}
109
277
  OptionParser.new do |opt|
278
+ opt.banner = 'Usage: luca-deal new DIR'
110
279
  args = opt.parse(ARGV)
111
280
  new_pj(args, params)
112
281
  end
113
282
  else
114
- puts 'Usage: luca-deal sub-command [--help|options]'
115
- puts ' customer'
116
- puts ' contract'
117
- puts ' invoice'
283
+ puts 'Proper subcommand needed.'
284
+ puts
285
+ puts 'Usage: luca-deal subcommand [options]'
286
+ puts ' customers'
287
+ puts ' contracts'
288
+ puts ' invoices'
289
+ puts ' new: initialize project dir'
290
+ puts ' export: puts invoice data for LucaBook import'
291
+ exit 1
118
292
  end
@@ -9,6 +9,7 @@ require 'luca_record'
9
9
  module LucaDeal
10
10
  class Contract < LucaRecord::Base
11
11
  @dirname = 'contracts'
12
+ @required = ['customer_id', 'terms']
12
13
 
13
14
  def initialize(date = nil)
14
15
  @date = date ? Date.parse(date) : Date.today
@@ -42,6 +43,16 @@ module LucaDeal
42
43
  end
43
44
  end
44
45
 
46
+ def describe(id)
47
+ contract = parse_current(self.class.find(id))
48
+ if contract['products']
49
+ contract['products'] = contract['products'].map do |product|
50
+ LucaDeal::Product.find(product['id'])
51
+ end
52
+ end
53
+ YAML.dump(readable(contract)).tap{ |d| puts d }
54
+ end
55
+
45
56
  def generate!(customer_id, mode = 'subscription')
46
57
  LucaDeal::Customer.find(customer_id) do |customer|
47
58
  current_customer = parse_current(customer)
@@ -10,6 +10,7 @@ require 'luca_record'
10
10
  module LucaDeal
11
11
  class Customer < LucaRecord::Base
12
12
  @dirname = 'customers'
13
+ @required = ['name']
13
14
 
14
15
  def initialize(pjdir = nil)
15
16
  @date = Date.today
@@ -17,10 +18,25 @@ module LucaDeal
17
18
  end
18
19
 
19
20
  def list_name
20
- list = self.class.all.map { |dat| parse_current(dat) }
21
+ list = self.class.all.map { |dat| parse_current(dat).sort.to_h }
21
22
  YAML.dump(list).tap { |l| puts l }
22
23
  end
23
24
 
25
+ def describe(id)
26
+ customer = parse_current(self.class.find(id))
27
+ contracts = LucaDeal::Contract.all.select { |contract| contract['customer_id'] == customer['id'] }
28
+ if !contracts.empty?
29
+ customer['contracts'] = contracts.map do |c|
30
+ {
31
+ 'id' => c['id'],
32
+ 'effective' => c['terms']['effective'],
33
+ 'defunct' => c['terms']['defunct']
34
+ }
35
+ end
36
+ end
37
+ YAML.dump(readable(customer)).tap{ |d| puts d }
38
+ end
39
+
24
40
  def self.create(obj)
25
41
  raise ':name is required' if obj[:name].nil?
26
42
 
@@ -60,7 +60,7 @@ module LucaDeal
60
60
 
61
61
  def gen_fee!(fee)
62
62
  id = fee.dig('invoice', 'contract_id')
63
- self.class.create_record!(fee, @date, Array(id))
63
+ self.class.create(fee, date: @date, codes: Array(id))
64
64
  end
65
65
 
66
66
  private
@@ -1,6 +1,7 @@
1
1
  require 'luca_deal/version'
2
2
 
3
3
  require 'mail'
4
+ require 'json'
4
5
  require 'yaml'
5
6
  require 'pathname'
6
7
  require 'bigdecimal'
@@ -12,6 +13,7 @@ require 'luca_record'
12
13
  module LucaDeal
13
14
  class Invoice < LucaRecord::Base
14
15
  @dirname = 'invoices'
16
+ @required = ['issue_date', 'customer', 'items', 'subtotal']
15
17
 
16
18
  def initialize(date = nil)
17
19
  @date = issue_date(date)
@@ -86,7 +88,8 @@ module LucaDeal
86
88
  'customer' => invoice.dig('customer', 'name'),
87
89
  'subtotal' => amount,
88
90
  'tax' => tax,
89
- 'due' => invoice.dig('due_date')
91
+ 'due' => invoice.dig('due_date'),
92
+ 'mail' => invoice.dig('status')&.select { |a| a.keys.include?('mail_delivered') }&.first
90
93
  }
91
94
  end
92
95
  stat['issue_date'] = scan_date.to_s
@@ -96,7 +99,28 @@ module LucaDeal
96
99
  collection << stat
97
100
  end
98
101
  end
99
- puts YAML.dump(collection)
102
+ puts YAML.dump(LucaSupport::Code.readable(collection))
103
+ end
104
+ end
105
+
106
+ def export_json
107
+ [].tap do |res|
108
+ self.class.asof(@date.year, @date.month) do |dat|
109
+ item = {}
110
+ item['date'] = dat['issue_date']
111
+ item['debit'] = []
112
+ item['credit'] = []
113
+ dat['subtotal'].map do |sub|
114
+ item['debit'] << { 'label' => '売掛金', 'value' => LucaSupport::Code.readable(sub['items']) }
115
+ item['debit'] << { 'label' => '売掛金', 'value' => LucaSupport::Code.readable(sub['tax']) }
116
+ item['credit'] << { 'label' => '売上高', 'value' => LucaSupport::Code.readable(sub['items']) }
117
+ item['credit'] << { 'label' => '売上高', 'value' => LucaSupport::Code.readable(sub['tax']) }
118
+ end
119
+ item['x-customer'] = dat['customer']['name'] if dat.dig('customer', 'name')
120
+ item['x-editor'] = 'LucaDeal'
121
+ res << item
122
+ end
123
+ puts JSON.dump(res)
100
124
  end
101
125
  end
102
126
 
@@ -112,17 +136,13 @@ module LucaDeal
112
136
  invoice['customer'] = get_customer(contract.dig('customer_id'))
113
137
  invoice['due_date'] = due_date(@date)
114
138
  invoice['issue_date'] = @date
115
- invoice['sales_fee'] = contract.dig('sales_fee')
116
- invoice['items'] = contract.dig('items').map do |item|
117
- next if item.dig('type') == 'initial' && subsequent_month?(contract.dig('terms', 'effective'))
118
-
119
- {}.tap do |h|
120
- h['name'] = item.dig('name')
121
- h['price'] = item.dig('price')
122
- h['qty'] = item.dig('qty') || 1
123
- h['type'] = item.dig('type') if item.dig('type')
124
- end
125
- end.compact
139
+ invoice['sales_fee'] = contract['sales_fee'] if contract.dig('sales_fee')
140
+ invoice['items'] = get_products(contract['products'])
141
+ .concat(contract['items']&.map { |i| i['qty'] ||= 1; i } || [])
142
+ .compact
143
+ invoice['items'].reject! do |item|
144
+ item.dig('type') == 'initial' && subsequent_month?(contract.dig('terms', 'effective'))
145
+ end
126
146
  invoice['subtotal'] = subtotal(invoice['items'])
127
147
  .map { |k, v| v.tap { |dat| dat['rate'] = k } }
128
148
  gen_invoice!(invoice)
@@ -153,9 +173,23 @@ module LucaDeal
153
173
  end
154
174
  end
155
175
 
176
+ def get_products(products)
177
+ return [] if products.nil?
178
+
179
+ [].tap do |res|
180
+ products.each do |product|
181
+ LucaDeal::Product.find(product['id'])['items'].each do |item|
182
+ item['product_id'] = product['id']
183
+ item['qty'] ||= 1
184
+ res << item
185
+ end
186
+ end
187
+ end
188
+ end
189
+
156
190
  def gen_invoice!(invoice)
157
191
  id = invoice.dig('contract_id')
158
- self.class.create_record!(invoice, @date, Array(id))
192
+ self.class.create(invoice, date: @date, codes: Array(id))
159
193
  end
160
194
 
161
195
  def issue_date(date)
@@ -190,11 +224,12 @@ module LucaDeal
190
224
  {}.tap do |subtotal|
191
225
  items.each do |i|
192
226
  rate = i.dig('tax') || 'default'
227
+ qty = i['qty'] || BigDecimal('1')
193
228
  subtotal[rate] = { 'items' => 0, 'tax' => 0 } if subtotal.dig(rate).nil?
194
- subtotal[rate]['items'] += i['qty'] * i['price']
229
+ subtotal[rate]['items'] += i['price'] * qty
195
230
  end
196
231
  subtotal.each do |rate, amount|
197
- amount['tax'] = (amount['items'] * load_tax_rate(rate)).to_i
232
+ amount['tax'] = (amount['items'] * load_tax_rate(rate))
198
233
  end
199
234
  end
200
235
  end
@@ -10,32 +10,40 @@ require 'luca_record'
10
10
  module LucaDeal
11
11
  class Product < LucaRecord::Base
12
12
  @dirname = 'products'
13
+ @required = ['items']
13
14
 
14
15
  def list_name
15
16
  list = self.class.all.map { |dat| parse_current(dat) }
16
17
  YAML.dump(list).tap { |l| puts l }
17
18
  end
18
19
 
20
+ # Save data with hash in Product format. Simple format is also available as bellows:
21
+ # {
22
+ # name: 'item_name(required)', price: 'item_price', qty: 'item_qty',
23
+ # initial: { name: 'item_name', price: 'item_price', qty: 'item_qty' }
24
+ # }
19
25
  def self.create(obj)
20
- raise ':name is required' if obj[:name].nil?
21
-
22
- items = [{
23
- 'name' => obj[:name],
24
- 'price' => obj[:price] || 0,
25
- 'qty' => obj[:qty] || 1
26
- }]
27
- if obj[:initial]
28
- items << {
29
- 'name' => obj.dig(:initial, :name),
30
- 'price' => obj.dig(:initial, :price) || 0,
31
- 'qty' => obj.dig(:initial, :qty) || 1,
32
- 'type' => 'initial'
26
+ if obj[:name].nil?
27
+ h = obj
28
+ else
29
+ items = [{
30
+ 'name' => obj[:name],
31
+ 'price' => obj[:price] || 0,
32
+ 'qty' => obj[:qty] || 1
33
+ }]
34
+ if obj[:initial]
35
+ items << {
36
+ 'name' => obj.dig(:initial, :name),
37
+ 'price' => obj.dig(:initial, :price) || 0,
38
+ 'qty' => obj.dig(:initial, :qty) || 1,
39
+ 'type' => 'initial'
40
+ }
41
+ end
42
+ h = {
43
+ 'name' => obj[:name],
44
+ 'items' => items
33
45
  }
34
46
  end
35
- h = {
36
- 'name' => obj[:name],
37
- 'items' => items
38
- }
39
47
  super(h)
40
48
  end
41
49
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LucaDeal
4
- VERSION = '0.2.13'
4
+ VERSION = '0.2.19'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lucadeal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.13
4
+ version: 0.2.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuma Takahiro
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-05 00:00:00.000000000 Z
11
+ date: 2020-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lucarecord