lucadeal 0.2.24 → 0.2.25
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 +9 -0
- data/README.md +2 -0
- data/exe/luca-deal +18 -4
- data/lib/luca_deal/contract.rb +3 -3
- data/lib/luca_deal/fee.rb +74 -8
- data/lib/luca_deal/invoice.rb +40 -9
- data/lib/luca_deal/templates/fee-report.html.erb +5 -1
- 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: 68b497e68738101eafbfe6eab73c3e0cf8d297cf4676ccfb8cd05efdadc273a3
|
4
|
+
data.tar.gz: fb8ed0c2d5eb0f7621c4436ddad91ece16aa723f2861ecd661423802ec01d0c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c8941a80f26d67b4855f2757e00f8241dd0b73f37f0fe2ab8799500388118bec4513fcb398116ba8072889bedf97a083fbb2c4ce3fdd4219cb7a15844785b1c
|
7
|
+
data.tar.gz: 91a84225d3d46201f85e9e38bbedbc102b2a3aaf2ccbf0a812db56bc3b502b78b7f89cb4087ec09642db5ff49b4abe58c15f6632bcbee18d471e95f3c1ff2a20
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## LucaDeal 0.2.25
|
2
|
+
|
3
|
+
* implement deduction rate for fee calculation.
|
4
|
+
* implement `luca-deal fee export`
|
5
|
+
* refine export label for luca-book compatibility
|
6
|
+
* add `luca-deal invoice create --monthly --with-fee` option.
|
7
|
+
* preview_mail can deliver regardless of `mail_delivered` status
|
8
|
+
* `luca-deal fee mail` skip no item record by default.
|
9
|
+
|
1
10
|
## LucaDeal 0.2.24
|
2
11
|
|
3
12
|
* add `luca-deal invoices create --monthly --mail`, send payment list after monthly invoice creation.
|
data/README.md
CHANGED
@@ -155,9 +155,11 @@ Fields for sales fee are as bellows:
|
|
155
155
|
| terms | | | | |
|
156
156
|
| | category | | | If 'sales_fee', contract is treated as selling commission. |
|
157
157
|
| | limit | | | If set, fees are calculated as mas as `limit` months. |
|
158
|
+
| | deduction_label | | | Label for deduction. Used on export |
|
158
159
|
| rate | | optional | | |
|
159
160
|
| | default | | | sales fee rate. |
|
160
161
|
| | initial | | | sales fee rate for items of type=initial. |
|
162
|
+
| | deduction | | | deduction rate(if any) multiplied by fee |
|
161
163
|
|
162
164
|
|
163
165
|
### Invoice
|
data/exe/luca-deal
CHANGED
@@ -124,11 +124,12 @@ class LucaCmd
|
|
124
124
|
|
125
125
|
class Invoice < LucaCmd
|
126
126
|
def self.create(args = nil, params = {})
|
127
|
-
case params[
|
127
|
+
case params[:mode]
|
128
128
|
when 'monthly'
|
129
129
|
date = "#{args[0]}-#{args[1]}-#{args[2] || '1'}" if !args.empty?
|
130
130
|
LucaDeal::Invoice.new(date).monthly_invoice
|
131
131
|
LucaDeal::NoInvoice.new(date).monthly_invoice
|
132
|
+
LucaDeal::Fee.new(date).monthly_fee if params[:fee]
|
132
133
|
if params[:mail]
|
133
134
|
LucaDeal::Invoice.new(date).stats_email
|
134
135
|
end
|
@@ -198,7 +199,7 @@ class LucaCmd
|
|
198
199
|
|
199
200
|
class Fee < LucaCmd
|
200
201
|
def self.create(args = nil, params = {})
|
201
|
-
case params[
|
202
|
+
case params[:mode]
|
202
203
|
when 'monthly'
|
203
204
|
date = "#{args[0]}-#{args[1]}-#{args[2] || '1'}" if !args.empty?
|
204
205
|
LucaDeal::Fee.new(date).monthly_fee
|
@@ -217,6 +218,15 @@ class LucaCmd
|
|
217
218
|
end
|
218
219
|
end
|
219
220
|
|
221
|
+
def self.export(args = nil, _params = nil)
|
222
|
+
if args
|
223
|
+
args << 28 if args.length == 2 # specify safe last day
|
224
|
+
LucaDeal::Fee.new(args.join('-')).export_json
|
225
|
+
else
|
226
|
+
LucaDeal::Fee.new.export_json
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
220
230
|
def self.list(args = nil, params = {})
|
221
231
|
date = "#{args[0]}-#{args[1]}-#{args[2] || '1'}" if !args.empty?
|
222
232
|
if args.empty?
|
@@ -338,8 +348,9 @@ when /invoices?/, 'i'
|
|
338
348
|
when 'create'
|
339
349
|
OptionParser.new do |opt|
|
340
350
|
opt.banner = 'Usage: luca-deal invoices create [options] --monthly|contract_id year month [date]'
|
341
|
-
opt.on('--monthly', 'generate monthly data') { |_v| params['mode'] = 'monthly' }
|
342
351
|
opt.on('--mail', 'send payment list by email. Only works with --monthly') { |_v| params[:mail] = true }
|
352
|
+
opt.on('--monthly', 'generate monthly data') { |_v| params[:mode] = 'monthly' }
|
353
|
+
opt.on('--with-fee', 'generate sales fee data after monthly invoice creation') { |_v| params[:fee] = true }
|
343
354
|
args = opt.parse(ARGV)
|
344
355
|
LucaCmd::Invoice.create(args, params)
|
345
356
|
end
|
@@ -385,12 +396,14 @@ when /fee/
|
|
385
396
|
when 'create'
|
386
397
|
OptionParser.new do |opt|
|
387
398
|
opt.banner = 'Usage: luca-deal fee create [options] year month [date]'
|
388
|
-
opt.on('--monthly', 'generate monthly data') { |_v| params[
|
399
|
+
opt.on('--monthly', 'generate monthly data') { |_v| params[:mode] = 'monthly' }
|
389
400
|
args = opt.parse(ARGV)
|
390
401
|
LucaCmd::Fee.create(args, params)
|
391
402
|
end
|
392
403
|
when 'delete'
|
393
404
|
LucaCmd::Fee.delete(ARGV)
|
405
|
+
when 'export'
|
406
|
+
LucaCmd::Fee.export(ARGV)
|
394
407
|
when 'list'
|
395
408
|
OptionParser.new do |opt|
|
396
409
|
opt.banner = 'Usage: luca-deal fee list [options] year month [date]'
|
@@ -424,6 +437,7 @@ else
|
|
424
437
|
puts ' customers'
|
425
438
|
puts ' contracts'
|
426
439
|
puts ' invoices'
|
440
|
+
puts ' fee'
|
427
441
|
puts ' new: initialize project dir'
|
428
442
|
puts ' export: puts invoice data for LucaBook import'
|
429
443
|
exit 1
|
data/lib/luca_deal/contract.rb
CHANGED
@@ -69,11 +69,11 @@ module LucaDeal
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def active_period?(dat)
|
72
|
-
unless dat
|
73
|
-
defunct = dat
|
72
|
+
unless dat['defunct'].nil?
|
73
|
+
defunct = dat['defunct'].respond_to?(:year) ? dat['defunct'] : Date.parse(dat['defunct'])
|
74
74
|
return false if @date > defunct
|
75
75
|
end
|
76
|
-
effective = dat
|
76
|
+
effective = dat['effective'].respond_to?(:year) ? dat['effective'] : Date.parse(dat['effective'])
|
77
77
|
@date >= effective
|
78
78
|
end
|
79
79
|
|
data/lib/luca_deal/fee.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'
|
@@ -27,7 +28,16 @@ module LucaDeal
|
|
27
28
|
@rate['initial'] = contract.dig('rate', 'initial') ? BigDecimal(contract.dig('rate', 'initial')) : @rate['default']
|
28
29
|
limit = contract.dig('terms', 'limit')
|
29
30
|
|
30
|
-
fee = {
|
31
|
+
fee = {
|
32
|
+
'contract_id' => contract['id'],
|
33
|
+
'items' => [],
|
34
|
+
'sales_fee' => {
|
35
|
+
'fee' => 0,
|
36
|
+
'tax' => 0,
|
37
|
+
'deduction' => 0,
|
38
|
+
'deduction_label' => contract.dig('terms', 'deduction_label')
|
39
|
+
}
|
40
|
+
}
|
31
41
|
fee['customer'] = get_customer(contract['customer_id'])
|
32
42
|
fee['issue_date'] = @date
|
33
43
|
Invoice.asof(@date.year, @date.month) do |invoice|
|
@@ -38,33 +48,36 @@ module LucaDeal
|
|
38
48
|
rate = item['type'] == 'initial' ? @rate['initial'] : @rate['default']
|
39
49
|
fee['items'] << fee_record(invoice, item, rate)
|
40
50
|
end
|
41
|
-
fee['
|
51
|
+
subtotal(fee['items']).each{ |k, v| fee['sales_fee'][k] += v }
|
42
52
|
end
|
43
53
|
NoInvoice.asof(@date.year, @date.month) do |no_invoice|
|
44
54
|
next if no_invoice.dig('sales_fee', 'id') != contract['id']
|
45
|
-
next if exceed_limit?(
|
55
|
+
next if exceed_limit?(no_invoice, limit)
|
46
56
|
|
47
57
|
no_invoice['items'].each do |item|
|
48
58
|
rate = item['type'] == 'initial' ? @rate['initial'] : @rate['default']
|
49
59
|
fee['items'] << fee_record(no_invoice, item, rate)
|
50
60
|
end
|
51
|
-
fee['
|
61
|
+
subtotal(fee['items']).each{ |k, v| fee['sales_fee'][k] += v }
|
52
62
|
end
|
63
|
+
deduction_rate = contract.dig('rate', 'deduction')
|
64
|
+
fee['sales_fee']['deduction'] = -1 * (fee['sales_fee']['fee'] * deduction_rate).floor if deduction_rate
|
53
65
|
self.class.create(fee, date: @date, codes: Array(contract['id']))
|
54
66
|
end
|
55
67
|
end
|
56
68
|
|
57
|
-
def deliver_mail(attachment_type = nil, mode: nil)
|
69
|
+
def deliver_mail(attachment_type = nil, mode: nil, skip_no_item: true)
|
58
70
|
attachment_type = CONFIG.dig('fee', 'attachment') || :html
|
59
71
|
fees = self.class.asof(@date.year, @date.month)
|
60
72
|
raise "No report for #{@date.year}/#{@date.month}" if fees.count.zero?
|
61
73
|
|
62
74
|
fees.each do |dat, path|
|
63
75
|
next if has_status?(dat, 'mail_delivered')
|
76
|
+
next if skip_no_item && dat['items'].empty?
|
64
77
|
|
65
78
|
mail = compose_mail(dat, mode: mode, attachment: attachment_type.to_sym)
|
66
79
|
LucaSupport::Mail.new(mail, PJDIR).deliver
|
67
|
-
self.class.add_status!(path, 'mail_delivered')
|
80
|
+
self.class.add_status!(path, 'mail_delivered') if mode.nil?
|
68
81
|
end
|
69
82
|
end
|
70
83
|
|
@@ -88,7 +101,7 @@ module LucaDeal
|
|
88
101
|
|
89
102
|
mail = Mail.new
|
90
103
|
mail.to = dat.dig('customer', 'to') if mode.nil?
|
91
|
-
mail.subject = CONFIG.dig('
|
104
|
+
mail.subject = CONFIG.dig('fee', 'mail_subject') || 'Your Report is available'
|
92
105
|
if mode == :preview
|
93
106
|
mail.cc = CONFIG.dig('mail', 'preview') || CONFIG.dig('mail', 'from')
|
94
107
|
mail.subject = '[preview] ' + mail.subject
|
@@ -139,6 +152,35 @@ module LucaDeal
|
|
139
152
|
end
|
140
153
|
end
|
141
154
|
|
155
|
+
def export_json
|
156
|
+
labels = export_labels
|
157
|
+
[].tap do |res|
|
158
|
+
self.class.asof(@date.year, @date.month) do |dat|
|
159
|
+
item = {}
|
160
|
+
item['date'] = dat['issue_date']
|
161
|
+
item['debit'] = []
|
162
|
+
item['credit'] = []
|
163
|
+
sub = dat['sales_fee']
|
164
|
+
if readable(sub['fee']) != 0
|
165
|
+
item['debit'] << { 'label' => labels[:debit][:fee], 'amount' => readable(sub['fee']) }
|
166
|
+
item['credit'] << { 'label' => labels[:credit][:fee], 'amount' => readable(sub['fee']) }
|
167
|
+
end
|
168
|
+
if readable(sub['tax']) != 0
|
169
|
+
item['debit'] << { 'label' => labels[:debit][:tax], 'amount' => readable(sub['tax']) }
|
170
|
+
item['credit'] << { 'label' => labels[:credit][:tax], 'amount' => readable(sub['tax']) }
|
171
|
+
end
|
172
|
+
if readable(sub['deduction']) != 0
|
173
|
+
item['debit'] << { 'label' => labels[:debit][:deduction], 'amount' => readable(sub['deduction'] * -1) }
|
174
|
+
item['credit'] << { 'label' => sub['deduction_label'] || labels[:credit][:deduction], 'amount' => readable(sub['deduction'] * -1) }
|
175
|
+
end
|
176
|
+
item['x-customer'] = dat['customer']['name'] if dat.dig('customer', 'name')
|
177
|
+
item['x-editor'] = 'LucaDeal'
|
178
|
+
res << item
|
179
|
+
end
|
180
|
+
puts JSON.dump(res)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
142
184
|
def render_report(file_type = :html)
|
143
185
|
case file_type
|
144
186
|
when :html
|
@@ -173,13 +215,32 @@ module LucaDeal
|
|
173
215
|
@sales_fee = readable(fee_dat['sales_fee'])
|
174
216
|
@issue_date = fee_dat['issue_date']
|
175
217
|
@due_date = fee_dat['due_date']
|
176
|
-
@amount = readable(fee_dat['sales_fee']
|
218
|
+
@amount = readable(fee_dat['sales_fee']
|
219
|
+
.reject{ |k, _v| k == 'deduction_label' }
|
220
|
+
.inject(0) { |sum, (_k, v)| sum + v })
|
177
221
|
end
|
178
222
|
|
179
223
|
def lib_path
|
180
224
|
__dir__
|
181
225
|
end
|
182
226
|
|
227
|
+
# TODO: load labels from CONFIG before country defaults
|
228
|
+
#
|
229
|
+
def export_labels
|
230
|
+
case CONFIG['country']
|
231
|
+
when 'jp'
|
232
|
+
{
|
233
|
+
debit: { fee: '支払手数料', tax: '支払手数料', deduction: '未払費用' },
|
234
|
+
credit: { fee: '未払費用', tax: '未払費用', deduction: '雑収入' }
|
235
|
+
}
|
236
|
+
else
|
237
|
+
{
|
238
|
+
debit: { fee: 'Fees and commisions', tax: 'Fees and commisions', deduction: 'Accounts payable - other' },
|
239
|
+
credit: { fee: 'Accounts payable - other', tax: 'Accounts payable - other', deduction: 'Miscellaneous income' }
|
240
|
+
}
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
183
244
|
# load user company profile from config.
|
184
245
|
#
|
185
246
|
def set_company
|
@@ -201,6 +262,11 @@ module LucaDeal
|
|
201
262
|
end
|
202
263
|
end
|
203
264
|
|
265
|
+
def attachment_name(dat, type)
|
266
|
+
id = %r{/}.match(dat['id']) ? dat['id'].gsub('/', '') : dat['id'][0, 7]
|
267
|
+
"feereport-#{id}.#{type}"
|
268
|
+
end
|
269
|
+
|
204
270
|
def issue_date(date)
|
205
271
|
base = date.nil? ? Date.today : Date.parse(date)
|
206
272
|
Date.new(base.year, base.month, -1)
|
data/lib/luca_deal/invoice.rb
CHANGED
@@ -20,21 +20,23 @@ module LucaDeal
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def deliver_mail(attachment_type = nil, mode: nil)
|
23
|
-
attachment_type = CONFIG.dig('invoice', 'attachment') || :html
|
24
23
|
invoices = self.class.asof(@date.year, @date.month)
|
25
24
|
raise "No invoice for #{@date.year}/#{@date.month}" if invoices.count.zero?
|
26
25
|
|
27
26
|
invoices.each do |dat, path|
|
28
27
|
next if has_status?(dat, 'mail_delivered')
|
29
28
|
|
30
|
-
|
31
|
-
LucaSupport::Mail.new(mail, PJDIR).deliver
|
32
|
-
self.class.add_status!(path, 'mail_delivered')
|
29
|
+
deliver_one(dat, path, mode: mode, attachment_type: attachment_type)
|
33
30
|
end
|
34
31
|
end
|
35
32
|
|
36
33
|
def preview_mail(attachment_type = nil)
|
37
|
-
|
34
|
+
invoices = self.class.asof(@date.year, @date.month)
|
35
|
+
raise "No invoice for #{@date.year}/#{@date.month}" if invoices.count.zero?
|
36
|
+
|
37
|
+
invoices.each do |dat, path|
|
38
|
+
deliver_one(dat, path, mode: :preview, attachment_type: attachment_type)
|
39
|
+
end
|
38
40
|
end
|
39
41
|
|
40
42
|
# Render HTML to console
|
@@ -146,6 +148,7 @@ module LucaDeal
|
|
146
148
|
end
|
147
149
|
|
148
150
|
def export_json
|
151
|
+
labels = export_labels
|
149
152
|
[].tap do |res|
|
150
153
|
self.class.asof(@date.year, @date.month) do |dat|
|
151
154
|
item = {}
|
@@ -153,10 +156,14 @@ module LucaDeal
|
|
153
156
|
item['debit'] = []
|
154
157
|
item['credit'] = []
|
155
158
|
dat['subtotal'].map do |sub|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
159
|
+
if readable(sub['items']) != 0
|
160
|
+
item['debit'] << { 'label' => labels[:debit][:items], 'amount' => readable(sub['items']) }
|
161
|
+
item['credit'] << { 'label' => labels[:credit][:items], 'amount' => readable(sub['items']) }
|
162
|
+
end
|
163
|
+
if readable(sub['tax']) != 0
|
164
|
+
item['debit'] << { 'label' => labels[:debit][:tax], 'amount' => readable(sub['tax']) }
|
165
|
+
item['credit'] << { 'label' => labels[:credit][:tax], 'amount' => readable(sub['tax']) }
|
166
|
+
end
|
160
167
|
end
|
161
168
|
item['x-customer'] = dat['customer']['name'] if dat.dig('customer', 'name')
|
162
169
|
item['x-editor'] = 'LucaDeal'
|
@@ -257,10 +264,34 @@ module LucaDeal
|
|
257
264
|
@amount = readable(invoice_dat['subtotal'].inject(0) { |sum, i| sum + i['items'] + i['tax'] })
|
258
265
|
end
|
259
266
|
|
267
|
+
def deliver_one(invoice, path, mode: nil, attachment_type: nil)
|
268
|
+
attachment_type ||= CONFIG.dig('invoice', 'attachment') || :html
|
269
|
+
mail = compose_mail(invoice, mode: mode, attachment: attachment_type.to_sym)
|
270
|
+
LucaSupport::Mail.new(mail, PJDIR).deliver
|
271
|
+
self.class.add_status!(path, 'mail_delivered') if mode.nil?
|
272
|
+
end
|
273
|
+
|
260
274
|
def lib_path
|
261
275
|
__dir__
|
262
276
|
end
|
263
277
|
|
278
|
+
# TODO: load labels from CONFIG before country defaults
|
279
|
+
#
|
280
|
+
def export_labels
|
281
|
+
case CONFIG['country']
|
282
|
+
when 'jp'
|
283
|
+
{
|
284
|
+
debit: { items: '売掛金', tax: '売掛金' },
|
285
|
+
credit: { items: '売上高', tax: '売上高' }
|
286
|
+
}
|
287
|
+
else
|
288
|
+
{
|
289
|
+
debit: { items: 'Accounts receivable - trade', tax: 'Accounts receivable - trade' },
|
290
|
+
credit: { items: 'Amount of Sales', tax: 'Amount of Sales' }
|
291
|
+
}
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
264
295
|
# load user company profile from config.
|
265
296
|
#
|
266
297
|
def set_company
|
@@ -39,9 +39,13 @@
|
|
39
39
|
<td class="price" colspan="3">Tax</td>
|
40
40
|
<td class="price"><%= delimit_num( @sales_fee['tax'] ) %></td>
|
41
41
|
</tr>
|
42
|
+
<tr>
|
43
|
+
<td class="price" colspan="3">Deduction</td>
|
44
|
+
<td class="price"><%= delimit_num( @sales_fee['deduction'] ) %></td>
|
45
|
+
</tr>
|
42
46
|
<tr>
|
43
47
|
<td class="price" colspan="3">Total</td>
|
44
|
-
<td class="price"><%= delimit_num(
|
48
|
+
<td class="price"><%= delimit_num(@sales_fee['fee'] + @sales_fee['tax'] + @sales_fee['deduction']) %></td>
|
45
49
|
</tr>
|
46
50
|
</table>
|
47
51
|
|
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.25
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chuma Takahiro
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-07-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lucarecord
|