lucadeal 0.2.13 → 0.2.19
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 +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
|
+
[](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
|