lucasalary 0.1.13 → 0.1.18

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 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
  - - ">="