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 +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +45 -1
- data/exe/luca-deal +255 -81
- data/lib/luca_deal/contract.rb +11 -0
- data/lib/luca_deal/customer.rb +17 -1
- data/lib/luca_deal/fee.rb +1 -1
- data/lib/luca_deal/invoice.rb +51 -16
- data/lib/luca_deal/product.rb +25 -17
- data/lib/luca_deal/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 36ce9f92d92079f0049ec346da2921116e7cde669314bccca3653c28dca33b51
|
4
|
+
data.tar.gz: b2f591e9bfb9710fa45cdb1d3582f19997d392e84e639531e4b15e77d6b284c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e2ca7f2a7063d44d29013cdd7448f807c58dbabca341cdeb1a8a118e24ca826ca94ac608f1c4a56eb16cb41cb039a0b39d2b07cb011fdd4a57e4b04c24a7d92
|
7
|
+
data.tar.gz: 692b888b1209de7fa6329ffbc7edd1ef7ad84be530b2dad7fcaad64b5c621d0da424e3ac90f21e9db7113057599497c91b61d70f479cf48bdea6bee2c49e1f0e
|
data/CHANGELOG.md
CHANGED
@@ -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
|
-
|
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 |
|
data/exe/luca-deal
CHANGED
@@ -5,57 +5,170 @@ require 'date'
|
|
5
5
|
require 'optparse'
|
6
6
|
require 'luca_deal'
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
params
|
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
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
91
|
-
|
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
|
-
|
94
|
-
|
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 '
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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 '
|
115
|
-
puts
|
116
|
-
puts '
|
117
|
-
puts '
|
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
|
data/lib/luca_deal/contract.rb
CHANGED
@@ -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)
|
data/lib/luca_deal/customer.rb
CHANGED
@@ -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
|
|
data/lib/luca_deal/fee.rb
CHANGED
data/lib/luca_deal/invoice.rb
CHANGED
@@ -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
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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.
|
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['
|
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))
|
232
|
+
amount['tax'] = (amount['items'] * load_tax_rate(rate))
|
198
233
|
end
|
199
234
|
end
|
200
235
|
end
|
data/lib/luca_deal/product.rb
CHANGED
@@ -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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
data/lib/luca_deal/version.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2020-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lucarecord
|