lucasalary 0.1.13 → 0.1.18

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: 9ef7b2ded5f89dc7d34c475b5e57aae90efbac496dc02dba4e5f159636b7d9fa
4
- data.tar.gz: 568ffe2f62bf970eb029c4cc890ed459b4bcaa411b92fe29d38229c0df4faa3c
3
+ metadata.gz: 37e8c1758e8ea780f6f124747241c5c43b4047f96e6a72ebe28d3ae546034843
4
+ data.tar.gz: b9d761bbedb5f77d222ac7e2d16cd5258ee60d52d7e2cc75ac941f1faae2b5f2
5
5
  SHA512:
6
- metadata.gz: 168536d2eb3dc3b5b83fd165961b29f36b266e0b314ad3516e213ac7701c849fc8f1357fb13c8cb3ec7aa2f9b16a9999545113aea11db690bc13bf0338ff1231
7
- data.tar.gz: 213d83129344747bf8a3857928f4fbba02495c4c14bebfa24263f77723f12b3b395cd92b4d8505889929833202a72ae9b7850aa77a2b03287c94be0a752d0698
6
+ metadata.gz: de2e967a7585233f87b4f3eeac7729b9d183563fc71610f355ca4329e2ff521d23473514d62e514da8e3735fe7bde4d8bc7f5dd58afd3e3bad2add31bb98630d
7
+ data.tar.gz: b7fa56bbc2a8e15e20b6db16a7e1e4c4f6f555f0f19da175fe41e67785af84a809e7f3ee16ba1c7d5394e86038e18f0ac756f68ea26140169a1b9bb9bb8eb9dc
@@ -0,0 +1,8 @@
1
+ ## LucaSalary 0.1.18
2
+
3
+ * Add summary to payslip. Refactor monthly payment.
4
+
5
+ ## LucaSalary 0.1.17
6
+
7
+ * Breaking change: restructure CLI in sub-sub command format.
8
+ * Add 'x-editor' on export to LucaBook
data/README.md CHANGED
@@ -1,2 +1,5 @@
1
1
  # Luca Salary
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/lucasalary.svg)](https://badge.fury.io/rb/lucasalary)
4
+
2
5
  Salary calculation framework
@@ -5,60 +5,93 @@ require 'optparse'
5
5
  require 'luca_salary'
6
6
  require 'luca_salary/monthly'
7
7
 
8
- def export(args = nil, _params = nil)
9
- if args
10
- args << 28 if args.length == 2 # specify safe last day
11
- LucaSalary::Monthly.new(args.join('-')).export_json
12
- else
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
- def payment(args = nil, _params = nil)
18
- if args
19
- args << 28 if args.length == 2 # specify safe last day
20
- LucaSalary::Base.new(args.join('-')).calc
21
- else
22
- LucaSalary::Base.new.calc
23
- end
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 report(args = nil, params = nil)
27
- if args
28
- args << 28 if args.length == 2 # specify safe last day
29
- LucaSalary::Monthly.new(args.join('-')).report(params.dig('mode'))
30
- else
31
- LucaSalary::Monthly.new.report(params.dig('mode'))
32
- end
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 add_person(args = nil, _params = nil)
36
- LucaSalary::Profile.gen_profile!(args.first)
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 'add'
43
- add_person(ARGV)
44
- when 'export'
45
- export(ARGV)
46
- when 'report'
47
- params = {}
48
- OptionParser.new do |opt|
49
- opt.banner = 'Usage: luca-salary report [--mail] year month [date]'
50
- opt.on('--mail', 'send to managers') { |_v| params['mode'] = 'mail' }
51
- args = opt.parse(ARGV)
52
- report(args, params)
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 'payment'
55
- params = {}
56
- OptionParser.new do |opt|
57
- opt.banner = 'Usage: luca-salary payment [--mail] year month [date]'
58
- opt.on('--mail', 'send to managers') { |_v| params['mode'] = 'mail' }
59
- args = opt.parse(ARGV)
60
- payment(args)
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 'Invalid subcommand'
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
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'luca_record'
3
4
  require 'luca_salary/version'
4
5
 
5
6
  module LucaSalary
@@ -9,39 +9,25 @@ require 'luca_record'
9
9
 
10
10
  module LucaSalary
11
11
  class Base < LucaRecord::Base
12
- attr_reader :driver, :dict, :config, :pjdir
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
17
  @pjdir = Pathname(LucaSupport::Config::Pjdir)
18
- @config = load_config(@pjdir + 'config.yml')
19
- @driver = set_driver
18
+ @config = load_config(@pjdir / 'config.yml')
20
19
  @dict = load_dict
21
20
  end
22
21
 
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
22
  def gen_aggregation!
37
23
  LucaSalary::Profile.all do |profile|
38
24
  id = profile.dig('id')
39
25
  payment = {}
40
26
  targetdir = @date.year.to_s + 'Z'
41
- past_data = LucaRecord::Base.find(id, "payments/#{targetdir}").first
27
+ past_data = LucaRecord::Base.find(id, "payments/#{targetdir}")
42
28
  (1..12).map do |month|
43
29
  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
30
+ origin = LucaRecord::Base.find(id, "payments/#{origin_dir}")
45
31
  # TODO: to be updated null check
46
32
  if origin == {}
47
33
  month
@@ -60,17 +46,31 @@ module LucaSalary
60
46
  dat.filter { |k, _v| /^#{code}[0-9A-Fa-f]{,3}$/.match(k.to_s) }
61
47
  end
62
48
 
49
+ # Subtotal each items.
50
+ # 1::
51
+ # Base salary or wages.
52
+ # 2::
53
+ # Deduction directly related to work payment, including tax, insurance, pension and so on.
54
+ # 3::
55
+ # Deduction for miscellaneous reasons.
56
+ # 4::
57
+ # Addition for miscellaneous reasons.
58
+ # 5::
59
+ # Net payment amount.
60
+ #
63
61
  def amount_by_code(obj)
64
62
  {}.tap do |h|
65
63
  (1..4).each do |n|
66
- h["#{n}00"] = sum_code(obj, n)
64
+ code = n.to_s
65
+ h[code] = sum_code(obj, code)
67
66
  end
67
+ h['5'] = h['1'] - h['2'] - h['3'] + h['4']
68
68
  end
69
69
  end
70
70
 
71
71
  def sum_code(obj, code, exclude = nil)
72
- target = obj.select { |k, v| /^#{code}[0-9A-Fa-f]{,3}$/.match(k) }
73
- target = target.reject { |k, v| exclude.include?(k) } if exclude
72
+ target = obj.select { |k, _v| /^#{code}[0-9A-Fa-f]{,3}$/.match(k) }
73
+ target = target.reject { |k, _v| exclude.include?(k) } if exclude
74
74
  target.values.inject(:+) || 0
75
75
  end
76
76
 
@@ -88,7 +88,7 @@ module LucaSalary
88
88
  code = @config['countryCode']
89
89
  if code
90
90
  require "luca_salary/#{code.downcase}"
91
- Kernel.const_get "LucaSalary#{code.upcase}"
91
+ Kernel.const_get "LucaSalary::#{code.upcase}"
92
92
  else
93
93
  nil
94
94
  end
@@ -9,26 +9,46 @@ require 'luca_salary'
9
9
  require 'luca_record'
10
10
 
11
11
  module LucaSalary
12
- class Monthly < LucaRecord::Base
12
+ class Monthly < LucaSalary::Base
13
+ @dirname = 'payments'
14
+
13
15
  def initialize(date = nil)
14
- @date = parse_date(date)
16
+ @date = date.nil? ? Date.today : Date.parse(date)
15
17
  @pjdir = Pathname(LucaSupport::Config::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(LucaSalary::Payment.new(@date.to_s).payslip)
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(LucaSalary::Payment.new(@date.to_s).payslip)
51
+ puts YAML.dump(LucaSupport::Code.readable(data))
32
52
  end
33
53
  end
34
54
 
@@ -17,34 +17,28 @@ module LucaSalary
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
@@ -61,11 +55,15 @@ module LucaSalary
61
55
  acct_label = @dict[k][:acct_label]
62
56
  h[pos][acct_label] = h[pos].key?(acct_label) ? h[pos][acct_label] + v : v
63
57
  end
64
- res = {}
65
- res['date'] = "#{@date.year}-#{@date.month}-#{@date.day}"
66
- res['debit'] = h[:debit].map { |k, v| { 'label' => k, 'value' => v } }
67
- res['credit'] = h[:credit].map { |k, v| { 'label' => k, 'value' => v } }
68
- puts JSON.dump(res)
58
+ [].tap do |res|
59
+ item = {}
60
+ item['date'] = "#{@date.year}-#{@date.month}-#{@date.day}"
61
+ item['debit'] = h[:debit].map { |k, v| { 'label' => k, 'value' => v } }
62
+ item['credit'] = h[:credit].map { |k, v| { 'label' => k, 'value' => v } }
63
+ item['x-editor'] = 'LucaSalary'
64
+ res << item
65
+ puts JSON.dump(LucaSupport::Code.readable(res))
66
+ end
69
67
  end
70
68
 
71
69
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LucaSalary
4
- VERSION = '0.1.13'
4
+ VERSION = '0.1.18'
5
5
  end
metadata CHANGED
@@ -1,57 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lucasalary
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.13
4
+ version: 0.1.18
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-10-05 00:00:00.000000000 Z
11
+ date: 2020-11-20 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: lucarecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
- - - "~>"
31
+ - - ">="
18
32
  - !ruby/object:Gem::Version
19
33
  version: '1.17'
20
34
  type: :development
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
- - - "~>"
38
+ - - ">="
25
39
  - !ruby/object:Gem::Version
26
40
  version: '1.17'
27
41
  - !ruby/object:Gem::Dependency
28
- name: rake
42
+ name: minitest
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: 12.3.3
47
+ version: '5.0'
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: 12.3.3
54
+ version: '5.0'
41
55
  - !ruby/object:Gem::Dependency
42
- name: minitest
56
+ name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - "~>"
59
+ - - ">="
46
60
  - !ruby/object:Gem::Version
47
- version: '5.0'
61
+ version: 12.3.3
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - "~>"
66
+ - - ">="
53
67
  - !ruby/object:Gem::Version
54
- version: '5.0'
68
+ version: 12.3.3
55
69
  description: 'Salary calculation framework
56
70
 
57
71
  '
@@ -73,7 +87,8 @@ files:
73
87
  - lib/luca_salary/profile.rb
74
88
  - lib/luca_salary/version.rb
75
89
  homepage: https://github.com/chumaltd/luca/tree/master/lucasalary
76
- licenses: []
90
+ licenses:
91
+ - GPL
77
92
  metadata:
78
93
  homepage_uri: https://github.com/chumaltd/luca/tree/master/lucasalary
79
94
  source_code_uri: https://github.com/chumaltd/luca/tree/master/lucasalary
@@ -86,7 +101,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
86
101
  requirements:
87
102
  - - ">="
88
103
  - !ruby/object:Gem::Version
89
- version: '0'
104
+ version: 2.6.0
90
105
  required_rubygems_version: !ruby/object:Gem::Requirement
91
106
  requirements:
92
107
  - - ">="