lucasalary 0.1.14 → 0.1.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 +16 -0
- data/README.md +3 -0
- data/exe/luca-salary +77 -44
- data/lib/luca_salary.rb +1 -0
- data/lib/luca_salary/base.rb +4 -48
- data/lib/luca_salary/monthly.rb +25 -5
- data/lib/luca_salary/payment.rb +64 -24
- data/lib/luca_salary/version.rb +1 -1
- metadata +12 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dacc04e5464dfd8f7d38db60b2053af1fe609bc4d71d65bdd7fb83c5d460618a
|
4
|
+
data.tar.gz: 800e13698b5e92482085abd8b0928e9aed42fa23c24f5a046b2e06b4cea15889
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99bf549286ee85d693afcbb0fd9774fc833316fb34c6ebb14edcbd31264948c0393d9561ff262a345dedad75a9e3b6ae93cf325730b5106e89c9a02e201ffc97
|
7
|
+
data.tar.gz: dbac5444029228f95b84cf173431f65f657f49a5cfbb7d17b161765d09528a8933680a5d74fe490c11c788f68264abb51ab717c1c13d894db58554c780b0c448
|
data/CHANGELOG.md
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
## LucaSalary 0.1.20
|
2
|
+
|
3
|
+
* Implement `year_total`
|
4
|
+
|
5
|
+
## LucaSalary 0.1.19
|
6
|
+
|
7
|
+
* Support `payment_term` on config.yml for accounting export.
|
8
|
+
|
9
|
+
## LucaSalary 0.1.18
|
10
|
+
|
11
|
+
* Add summary to payslip. Refactor monthly payment.
|
12
|
+
|
13
|
+
## LucaSalary 0.1.17
|
14
|
+
|
15
|
+
* Breaking change: restructure CLI in sub-sub command format.
|
16
|
+
* Add 'x-editor' on export to LucaBook
|
data/README.md
CHANGED
data/exe/luca-salary
CHANGED
@@ -5,60 +5,93 @@ require 'optparse'
|
|
5
5
|
require 'luca_salary'
|
6
6
|
require 'luca_salary/monthly'
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
LucaSalary::Monthly.new.export_json
|
8
|
+
module LucaCmd
|
9
|
+
class Profile
|
10
|
+
def self.create(args = nil, _params = nil)
|
11
|
+
LucaSalary::Profile.gen_profile!(args.first)
|
12
|
+
end
|
14
13
|
end
|
15
|
-
end
|
16
14
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
15
|
+
class Payment
|
16
|
+
def self.create(args = nil, _params = nil)
|
17
|
+
if args
|
18
|
+
args << 28 if args.length == 2 # specify safe last day
|
19
|
+
LucaSalary::Monthly.new(args.join('-')).calc
|
20
|
+
else
|
21
|
+
LucaSalary::Monthly.new.calc
|
22
|
+
end
|
23
|
+
end
|
25
24
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
25
|
+
def self.export(args = nil, _params = nil)
|
26
|
+
if args
|
27
|
+
args << 28 if args.length == 2 # specify safe last day
|
28
|
+
LucaSalary::Payment.new(args.join('-')).export_json
|
29
|
+
else
|
30
|
+
LucaSalary::Payment.new.export_json
|
31
|
+
end
|
32
|
+
end
|
34
33
|
|
35
|
-
def
|
36
|
-
|
34
|
+
def self.list(args = nil, params = nil)
|
35
|
+
if args
|
36
|
+
args << 28 if args.length == 2 # specify safe last day
|
37
|
+
LucaSalary::Monthly.new(args.join('-')).report(params.dig('mode'))
|
38
|
+
else
|
39
|
+
LucaSalary::Monthly.new.report(params.dig('mode'))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
37
43
|
end
|
38
44
|
|
45
|
+
LucaRecord::Base.valid_project?
|
39
46
|
cmd = ARGV.shift
|
47
|
+
params = {}
|
40
48
|
|
41
49
|
case cmd
|
42
|
-
when
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
50
|
+
when /profiles?/
|
51
|
+
subcmd = ARGV.shift
|
52
|
+
case subcmd
|
53
|
+
when 'create'
|
54
|
+
OptionParser.new do |opt|
|
55
|
+
opt.banner = 'Usage: luca-salary profiles create Name'
|
56
|
+
args = opt.parse(ARGV)
|
57
|
+
LucaCmd::Profile.create(args)
|
58
|
+
end
|
59
|
+
else
|
60
|
+
puts 'Proper subcommand needed.'
|
61
|
+
puts
|
62
|
+
puts 'Usage: luca-salary profile[s] create Name'
|
63
|
+
exit 1
|
53
64
|
end
|
54
|
-
when '
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
65
|
+
when 'export'
|
66
|
+
LucaCmd::Payment.export(ARGV)
|
67
|
+
when /payments?/
|
68
|
+
subcmd = ARGV.shift
|
69
|
+
case subcmd
|
70
|
+
when 'create'
|
71
|
+
OptionParser.new do |opt|
|
72
|
+
opt.banner = 'Usage: luca-salary payments create year month [date]'
|
73
|
+
args = opt.parse(ARGV)
|
74
|
+
LucaCmd::Payment.create(args)
|
75
|
+
end
|
76
|
+
when 'list'
|
77
|
+
OptionParser.new do |opt|
|
78
|
+
opt.banner = 'Usage: luca-salary payments list [--mail] year month [date]'
|
79
|
+
opt.on('--mail', 'send to managers') { |_v| params['mode'] = 'mail' }
|
80
|
+
args = opt.parse(ARGV)
|
81
|
+
LucaCmd::Payment.list(args, params)
|
82
|
+
end
|
83
|
+
else
|
84
|
+
puts 'Proper subcommand needed.'
|
85
|
+
puts
|
86
|
+
puts 'Usage: luca-salary payment[s] (create|list) [--help|options]'
|
87
|
+
exit 1
|
61
88
|
end
|
62
89
|
else
|
63
|
-
puts '
|
90
|
+
puts 'Proper subcommand needed.'
|
91
|
+
puts
|
92
|
+
puts 'Usage: luca-salary subcommand [options]'
|
93
|
+
puts ' profiles'
|
94
|
+
puts ' payments'
|
95
|
+
puts ' export: puts payment data for LucaBook import'
|
96
|
+
exit 1
|
64
97
|
end
|
data/lib/luca_salary.rb
CHANGED
data/lib/luca_salary/base.rb
CHANGED
@@ -9,58 +9,18 @@ require 'luca_record'
|
|
9
9
|
|
10
10
|
module LucaSalary
|
11
11
|
class Base < LucaRecord::Base
|
12
|
-
attr_reader :
|
12
|
+
attr_reader :dict, :config, :pjdir
|
13
13
|
@dirname = 'payments'
|
14
14
|
|
15
15
|
def initialize(date = nil)
|
16
16
|
@date = date.nil? ? Date.today : Date.parse(date)
|
17
|
-
@pjdir = Pathname(LucaSupport::Config::Pjdir)
|
18
|
-
@config = load_config(@pjdir / 'config.yml')
|
19
|
-
@driver = set_driver
|
20
17
|
@dict = load_dict
|
21
18
|
end
|
22
19
|
|
23
|
-
#
|
24
|
-
# call country specific calculation
|
25
|
-
#
|
26
|
-
def calc
|
27
|
-
self.class.prepare_dir!(datadir / 'payments', @date)
|
28
|
-
country = @driver.new(@pjdir, @config, @date)
|
29
|
-
LucaSalary::Profile.all do |profile|
|
30
|
-
current_profile = parse_current(profile)
|
31
|
-
h = country.calc_payment(current_profile)
|
32
|
-
LucaSalary::Payment.new(@date.to_s).create(current_profile, h)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def gen_aggregation!
|
37
|
-
LucaSalary::Profile.all do |profile|
|
38
|
-
id = profile.dig('id')
|
39
|
-
payment = {}
|
40
|
-
targetdir = @date.year.to_s + 'Z'
|
41
|
-
past_data = LucaRecord::Base.find(id, "payments/#{targetdir}").first
|
42
|
-
(1..12).map do |month|
|
43
|
-
origin_dir = @date.year.to_s + [nil, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'][month]
|
44
|
-
origin = LucaRecord::Base.find(id, "payments/#{origin_dir}").first
|
45
|
-
# TODO: to be updated null check
|
46
|
-
if origin == {}
|
47
|
-
month
|
48
|
-
else
|
49
|
-
origin.select { |k, _v| /^[1-4][0-9A-Fa-f]{,3}$/.match(k) }.each do |k, v|
|
50
|
-
payment[k] = payment[k] ? payment[k] + v : v
|
51
|
-
end
|
52
|
-
nil
|
53
|
-
end
|
54
|
-
end
|
55
|
-
self.class.create(past_data.merge!(payment), "payments/#{targetdir}")
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
20
|
def select_code(dat, code)
|
60
21
|
dat.filter { |k, _v| /^#{code}[0-9A-Fa-f]{,3}$/.match(k.to_s) }
|
61
22
|
end
|
62
23
|
|
63
|
-
#
|
64
24
|
# Subtotal each items.
|
65
25
|
# 1::
|
66
26
|
# Base salary or wages.
|
@@ -91,19 +51,15 @@ module LucaSalary
|
|
91
51
|
|
92
52
|
private
|
93
53
|
|
94
|
-
def datadir
|
95
|
-
@pjdir / 'data'
|
96
|
-
end
|
97
|
-
|
98
54
|
def load_dict
|
99
|
-
LucaRecord::Dict.load_tsv_dict(
|
55
|
+
LucaRecord::Dict.load_tsv_dict(Pathname(PJDIR) / 'dict' / 'code.tsv')
|
100
56
|
end
|
101
57
|
|
102
58
|
def set_driver
|
103
|
-
code =
|
59
|
+
code = CONFIG['country']
|
104
60
|
if code
|
105
61
|
require "luca_salary/#{code.downcase}"
|
106
|
-
Kernel.const_get "LucaSalary::#{code.
|
62
|
+
Kernel.const_get "LucaSalary::#{code.capitalise}"
|
107
63
|
else
|
108
64
|
nil
|
109
65
|
end
|
data/lib/luca_salary/monthly.rb
CHANGED
@@ -9,26 +9,46 @@ require 'luca_salary'
|
|
9
9
|
require 'luca_record'
|
10
10
|
|
11
11
|
module LucaSalary
|
12
|
-
class Monthly <
|
12
|
+
class Monthly < LucaSalary::Base
|
13
|
+
@dirname = 'payments'
|
14
|
+
|
13
15
|
def initialize(date = nil)
|
14
|
-
@date =
|
15
|
-
@pjdir = Pathname(LucaSupport::
|
16
|
+
@date = date.nil? ? Date.today : Date.parse(date)
|
17
|
+
@pjdir = Pathname(LucaSupport::PJDIR)
|
16
18
|
@config = load_config(@pjdir + 'config.yml')
|
19
|
+
@driver = set_driver
|
17
20
|
end
|
18
21
|
|
22
|
+
# call country specific calculation
|
19
23
|
#
|
24
|
+
def calc
|
25
|
+
country = @driver.new(@pjdir, @config, @date)
|
26
|
+
# TODO: handle retirement
|
27
|
+
LucaSalary::Profile.all do |profile|
|
28
|
+
current_profile = parse_current(profile)
|
29
|
+
if self.class.search(@date.year, @date.month, @date.day, current_profile['id']).count > 0
|
30
|
+
puts "payment record already exists: #{current_profile['id']}"
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
h = country.calc_payment(current_profile)
|
34
|
+
h['profile_id'] = current_profile['id']
|
35
|
+
self.class.create(h, date: @date, codes: Array(current_profile['id']))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
20
39
|
# output payslips via mail or console
|
21
40
|
#
|
22
41
|
def report(mode = nil)
|
42
|
+
data = LucaSalary::Payment.new(@date.to_s).payslip
|
23
43
|
if mode == 'mail'
|
24
44
|
mail = Mail.new do
|
25
45
|
subject '[luca salary] Monthly Payment'
|
26
46
|
end
|
27
47
|
mail.to = @config.dig('mail', 'report_mail')
|
28
|
-
mail.text_part = YAML.dump(
|
48
|
+
mail.text_part = YAML.dump(LucaSupport::Code.readable(data))
|
29
49
|
LucaSupport::Mail.new(mail, @pjdir).deliver
|
30
50
|
else
|
31
|
-
puts YAML.dump(
|
51
|
+
puts YAML.dump(LucaSupport::Code.readable(data))
|
32
52
|
end
|
33
53
|
end
|
34
54
|
|
data/lib/luca_salary/payment.rb
CHANGED
@@ -13,47 +13,82 @@ module LucaSalary
|
|
13
13
|
|
14
14
|
def initialize(date = nil)
|
15
15
|
@date = Date.parse(date)
|
16
|
-
@pjdir = Pathname(LucaSupport::
|
16
|
+
@pjdir = Pathname(LucaSupport::PJDIR)
|
17
17
|
@dict = LucaRecord::Dict.load_tsv_dict(@pjdir / 'dict' / 'code.tsv')
|
18
18
|
end
|
19
19
|
|
20
|
-
#
|
21
|
-
# create record with LucaSalary::Profile instance and apyment data
|
22
|
-
#
|
23
|
-
def create(profile, payment)
|
24
|
-
id = profile.dig('id')
|
25
|
-
if self.class.search(@date.year, @date.month, @date.day, id).first
|
26
|
-
puts "payment record already exists: #{id}"
|
27
|
-
return nil
|
28
|
-
end
|
29
|
-
|
30
|
-
self.class.gen_record_file!('payments', @date, Array(id)) do |f|
|
31
|
-
f.write(YAML.dump(payment.sort.to_h))
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
20
|
def payslip
|
36
21
|
{}.tap do |report|
|
37
22
|
report['asof'] = "#{@date.year}/#{@date.month}"
|
23
|
+
report['payments'] = []
|
38
24
|
report['records'] = []
|
39
25
|
|
40
26
|
self.class.asof(@date.year, @date.month) do |payment|
|
27
|
+
profile = LucaSalary::Profile.find(payment['profile_id'])
|
28
|
+
summary = {
|
29
|
+
'name' => profile['name'],
|
30
|
+
"#{@dict.dig('5', :label) || '5'}" => payment['5']
|
31
|
+
}
|
32
|
+
|
41
33
|
slip = {}.tap do |line|
|
34
|
+
line['name'] = profile['name']
|
42
35
|
payment.each do |k, v|
|
43
36
|
next if k == 'id'
|
44
37
|
|
45
38
|
line["#{@dict.dig(k, :label) || k}"] = v
|
46
39
|
end
|
47
40
|
end
|
41
|
+
report['payments'] << summary
|
48
42
|
report['records'] << slip
|
49
43
|
end
|
50
44
|
end
|
51
45
|
end
|
52
46
|
|
47
|
+
def self.year_total(year)
|
48
|
+
Profile.all do |profile|
|
49
|
+
id = profile.dig('id')
|
50
|
+
payment = { 'profile_id' => id }
|
51
|
+
# payment = {}
|
52
|
+
(1..12).each do |month|
|
53
|
+
search(year, month, nil, id).each do |origin|
|
54
|
+
# TODO: to be updated null check
|
55
|
+
if origin == {}
|
56
|
+
month
|
57
|
+
else
|
58
|
+
origin.select { |k, _v| /^[1-4][0-9A-Fa-f]{,3}$/.match(k) }.each do |k, v|
|
59
|
+
payment[k] = payment[k] ? payment[k] + v : v
|
60
|
+
end
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
payment = local_convert(payment)
|
66
|
+
create(payment, date: Date.new(year, 12, 31), codes: [id], basedir: 'payments/total')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.local_convert(payment)
|
71
|
+
return payment if CONFIG['country'].nil?
|
72
|
+
|
73
|
+
require "luca_salary/#{CONFIG['country'].downcase}"
|
74
|
+
klass = Kernel.const_get("LucaSalary::#{CONFIG['country'].capitalize}")
|
75
|
+
klass.year_total(payment)
|
76
|
+
rescue
|
77
|
+
return payment
|
78
|
+
end
|
79
|
+
|
80
|
+
# Export json for LucaBook.
|
81
|
+
# Accrual_date can be change with `payment_term` on config.yml. Default value is 0.
|
82
|
+
# `payment_term: 1` means payment on the next month of calculation target.
|
83
|
+
#
|
53
84
|
def export_json
|
54
|
-
|
55
|
-
|
56
|
-
|
85
|
+
accrual_date = if LucaSupport::CONFIG['payment_term']
|
86
|
+
pt = LucaSupport::CONFIG['payment_term']
|
87
|
+
Date.new(@date.prev_month(pt).year, @date.prev_month(pt).month, -1)
|
88
|
+
else
|
89
|
+
Date.new(@date.year, @date.month, -1)
|
90
|
+
end
|
91
|
+
h = { debit: {}, credit: {} }
|
57
92
|
accumulate.each do |k, v|
|
58
93
|
next if @dict.dig(k, :acct_label).nil?
|
59
94
|
|
@@ -61,11 +96,16 @@ module LucaSalary
|
|
61
96
|
acct_label = @dict[k][:acct_label]
|
62
97
|
h[pos][acct_label] = h[pos].key?(acct_label) ? h[pos][acct_label] + v : v
|
63
98
|
end
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
99
|
+
[].tap do |res|
|
100
|
+
{}.tap do |item|
|
101
|
+
item['date'] = "#{accrual_date.year}-#{accrual_date.month}-#{accrual_date.day}"
|
102
|
+
item['debit'] = h[:debit].map { |k, v| { 'label' => k, 'value' => v } }
|
103
|
+
item['credit'] = h[:credit].map { |k, v| { 'label' => k, 'value' => v } }
|
104
|
+
item['x-editor'] = 'LucaSalary'
|
105
|
+
res << item
|
106
|
+
end
|
107
|
+
puts JSON.dump(readable(res))
|
108
|
+
end
|
69
109
|
end
|
70
110
|
|
71
111
|
private
|
data/lib/luca_salary/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lucasalary
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.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
|
+
date: 2020-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lucarecord
|
@@ -28,44 +28,44 @@ dependencies:
|
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '1.17'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.17'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: minitest
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: '5.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: '5.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 12.3.3
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 12.3.3
|
69
69
|
description: 'Salary calculation framework
|
70
70
|
|
71
71
|
'
|