lucasalary-jp 0.1.2 → 0.1.4

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: 5c7fff7c7b40c95899b960a3c507508cfd073514123ca0e6c0ab6cf2a6a47c7c
4
- data.tar.gz: c2e35dcf8879de84427e7a26e46e5c67f6b8281cb4ac121adeebd536887f16dc
3
+ metadata.gz: b0ef779615419be07dda49d1da0ede0d8ba65f4495362d98986db2418aac566e
4
+ data.tar.gz: 19b8490e555293b74f1d8a22304a12880c79ee6f238e74e2816d87fb814075c1
5
5
  SHA512:
6
- metadata.gz: 259a1429a5bf2e542213aaf6f9f83ac9628b77d7fc7b51537c52017b25cb43258eee3131b5e6e5e9f15a407f3ba10b046cee64313a1a660848ea812e013017a6
7
- data.tar.gz: 5225c85430a43e60516f40b5be8582448b501df056351e7b303809d1fa90b0d1d5a0d295af8cfa8846ffdc2822282d36c3f50770640cf2ae474dac8422fa5dd9
6
+ metadata.gz: ad7f44549ec79cb06612f6b764990711dff913ef2f122a08cc73dbff47d7b8b67973dd0dd38997b6953ba3d2ac59edd56c3cc9e69de7123ff251877b0d79bdf2
7
+ data.tar.gz: ea2e34d29189ebca7b84d301a97d3d85cb2522ca9c7704f98e7124436034cf51fc95d07ef773dbdbd45d5dd5b942cb5b140c8fbfeb0ada5c258f2b38f7e4ae56
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'optparse'
5
+ require 'luca_salary/jp_payreport'
6
+
7
+ module LucaSalaryJpCmd
8
+ class Export
9
+ def self.payreport(args = nil, _params = nil)
10
+ puts LucaSalary::JpPayreport.export(args.first)
11
+ end
12
+ end
13
+ end
14
+
15
+ LucaRecord::Base.valid_project?
16
+ cmd = ARGV.shift
17
+ params = {}
18
+
19
+ case cmd
20
+ when /export/
21
+ OptionParser.new do |opt|
22
+ opt.banner = 'Usage: luca-salary-jp export year'
23
+ args = opt.parse(ARGV)
24
+ LucaSalaryJpCmd::Export.payreport(args)
25
+ end
26
+ else
27
+ puts 'Proper subcommand needed.'
28
+ puts
29
+ puts 'Usage: luca-salary-jp subcommand [options]'
30
+ puts ' export: puts CSV payment report for eLTAX'
31
+ exit 1
32
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LucaSalaryJp
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.4'
5
5
  end
@@ -21,7 +21,7 @@ class LucaSalary::Jp < LucaSalary::Base
21
21
  select_code(profile, '1').each { |k, v| h[k] = v }
22
22
  h['201'] = @insurance.health_insurance_salary(insurance_rank(profile))
23
23
  h['202'] = @insurance.pension_salary(pension_rank(profile))
24
- tax_base = sum_code(h, '1', income_tax_exception) - h['201'] - h['202']
24
+ tax_base = self.class.sum_code(h, '1', income_tax_exception) - h['201'] - h['202']
25
25
  h['203'] = JpNationalTax::IncomeTax.calc_kouran(tax_base, Date.today, true)
26
26
  h['211'] = resident_tax(profile)
27
27
  select_code(profile, '3').each { |k, v| h[k] = v }
@@ -31,88 +31,26 @@ class LucaSalary::Jp < LucaSalary::Base
31
31
  end
32
32
  end
33
33
 
34
- def self.year_total(payment)
34
+ def self.year_total(payment, date)
35
35
  payment.tap do |p|
36
- p['911'] = basic_deduction(p['1'])
36
+ p['911'] = JpNationalTax::IncomeTax.basic_deduction(p['1'], date)
37
37
  p['916'] = partner_deduction(p['1'])
38
- p['912'] = (p['201'] || 0) + (p['202'] || 0)
39
- p['901'] = year_salary(p['1'])
38
+ p['912'] = ['201', '202', '204', '205'].map{ |cd| p[cd] }.compact.sum
39
+ p['901'] = JpNationalTax::IncomeTax.year_salary_taxable(p['1'], date)
40
40
  p['941'] = p['901'] - p['911'] - p['912'] - p['916']
41
- p['961'] = year_tax(p['941'])
41
+ p['961'] = JpNationalTax::IncomeTax.year_tax(p['941'], date)
42
42
  diff = p['961'] - p['203']
43
43
  if diff.positive?
44
44
  p['3A1'] = diff
45
+ p['4A1'] = BigDecimal('0')
45
46
  else
46
47
  p['4A1'] = diff * -1
48
+ p['3A1'] = BigDecimal('0')
47
49
  end
48
- end
49
- end
50
-
51
- def self.year_salary(income)
52
- rounded = if income < 1_619_000
53
- income
54
- elsif income < 1_620_000
55
- income - ((income - 1_619_000) % 1_000)
56
- elsif income < 1_624_000
57
- income - ((income - 1_620_000) % 2_000)
58
- elsif income < 1_624_000
59
- income - ((income - 1_624_000) % 4_000)
60
- else
61
- income
62
- end
63
- if rounded < 551_000
64
- 0
65
- elsif rounded < 1_619_000
66
- rounded - 550_000
67
- elsif rounded < 1_620_000
68
- rounded * 0.6 + 97_600
69
- elsif rounded < 1_622_000
70
- rounded * 0.6 + 98_000
71
- elsif rounded < 1_624_000
72
- rounded * 0.6 + 98_800
73
- elsif rounded < 1_628_000
74
- rounded * 0.6 + 99_600
75
- elsif rounded < 1_800_000
76
- rounded * 0.6 + 100_000
77
- elsif rounded < 3_600_000
78
- rounded * 0.7 - 80_000
79
- elsif rounded < 6_600_000
80
- rounded * 0.8 - 440_000
81
- elsif rounded < 8_500_000
82
- rounded * 0.9 - 1_100_000
83
- else
84
- rounded - 1_950_000
85
- end
86
- end
87
-
88
- def self.year_tax(income)
89
- tax = if income < 1_950_000
90
- income * 0.05
91
- elsif income <= 3_300_000
92
- income * 0.1 - 97_500
93
- elsif income <= 6_950_000
94
- income * 0.2 - 427_500
95
- elsif income <= 9_000_000
96
- income * 0.23 - 636_000
97
- elsif income <= 18_000_000
98
- income * 0.33 - 1_536_000
99
- elsif income <= 18_050_000
100
- income * 0.4 - 2_796_000
101
- else
102
- raise "no target"
103
- end
104
- (tax / 1000).floor * 1000
105
- end
106
-
107
- def self.basic_deduction(income)
108
- if income <= 24_000_000
109
- 480_000
110
- elsif income <= 24_500_000
111
- 320_000
112
- elsif income <= 25_000_000
113
- 160_000
114
- else
115
- 0
50
+ p.delete '3'
51
+ p.delete '4'
52
+ p['3'] = sum_code(p, '3')
53
+ p['4'] = sum_code(p, '4')
116
54
  end
117
55
  end
118
56
 
@@ -0,0 +1,247 @@
1
+ require 'luca_salary'
2
+ require 'luca_support/config'
3
+ require 'csv'
4
+ require 'open3'
5
+
6
+ module LucaSalary
7
+ class JpPayreport < LucaSalary::Base
8
+
9
+ def self.export(year)
10
+ if !system("uconv --version > /dev/null")
11
+ exit 1 # 半角カナ必須
12
+ end
13
+
14
+ slips = LucaSalary::Total.new(year).slips
15
+ company = set_company(year.to_i)
16
+ CSV.generate do |csv|
17
+ slips.each do |s|
18
+ csv << 給与支払報告明細行(LucaSupport::Code.readable(s), company, year.to_i)
19
+ end
20
+ end
21
+ end
22
+
23
+ # TODO: extract effective field
24
+ def self.set_company(year)
25
+ {}.tap do |h|
26
+ h['name'] = CONFIG.dig('company', 'name')
27
+ h['address'] = CONFIG.dig('company', 'address')
28
+ h['address2'] = CONFIG.dig('company', 'address2')
29
+ h['tel'] = CONFIG.dig('company', 'tel')
30
+ h['tax_id'] = CONFIG.dig('company', 'tax_id')
31
+
32
+ raise "会社名、住所の設定は必須" if h['name'].nil? or h['address'].nil?
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def 給与支払報告明細行(slip, company, year)
41
+ [
42
+ 315, # 法定資料の種類
43
+ 提出義務者(company),
44
+ 0, # 提出区分(新規0, 追加1, 訂正2, 取消3)
45
+ 和暦(Date.new(year, 12, -1))[1], # 年分
46
+ 支払を受ける者(slip['profile']),
47
+ 支払(slip, year),
48
+ 支払を受ける者の詳細(slip['profile'], year),
49
+ company['tax_id'], # 法人番号
50
+ 支払を受ける者の扶養情報(slip['profile'], year),
51
+ slip['911'], # 基礎控除の額
52
+ nil, # 所得金額調整控除額 TODO: 未実装 措法41の3の3
53
+ nil, # ひとり親
54
+ 提出先判定(slip), # 必須:作成区分(国税のみ0, 地方のみ1, 両方2)
55
+ ].flatten
56
+ end
57
+
58
+ def 提出義務者(company)
59
+ [
60
+ nil, # 整理番号1
61
+ nil, # 本支店等区分番号
62
+ ['address', 'address2'].map { |attr| company[attr] }
63
+ .compact.join(' '), # 必須:住所又は所在地
64
+ company['name'], # 必須:氏名又は名称
65
+ company['tel'], # 電話番号
66
+ nil, # 整理番号2
67
+ nil, # 提出者の住所(省略する)
68
+ nil, # 提出者の氏名(省略する)
69
+ ]
70
+ end
71
+
72
+ def 支払を受ける者(profile)
73
+ [
74
+ ['address', 'address2'].map { |attr| profile[attr] }
75
+ .compact.join(' '), # 必須:住所又は居所
76
+ nil, # 国外住所表示(国内は"0"、国外は"1")
77
+ profile['name'], # 必須:氏名
78
+ nil, # 役職名
79
+ ]
80
+ end
81
+
82
+ def 支払(slip, year)
83
+ 配偶者控除等 = [slip['916'], slip['917']].compact.sum
84
+ [
85
+ '給料', # 種別
86
+ slip['1'], # 支払金額
87
+ nil, # 未払金額
88
+ slip['901'], # 給与所得控除後の給与等の金額
89
+ slip['901'] - slip['941'], # 所得控除の額の金額
90
+ slip['961'], # 源泉徴収税額
91
+ nil, # 未徴収税額
92
+ 配偶者控除等 > 0 ? 1 : 2, # 控除対象配偶者の有無 TODO: 従たる給与
93
+ 老人控除対象配偶者(slip.dig('profile', 'spouse'), year),
94
+ 配偶者控除等, # 配偶者控除の額
95
+ nil, # 控除対象扶養親族の数 特定 主
96
+ nil, # 控除対象扶養親族の数 特定 従
97
+ nil, # 控除対象扶養親族の数 老人 主
98
+ nil, # 控除対象扶養親族の数 老人 上の内訳
99
+ nil, # 控除対象扶養親族の数 老人 従
100
+ nil, # 控除対象扶養親族の数 その他 主
101
+ nil, # 控除対象扶養親族の数 その他 従
102
+ nil, # 障害者の数 特別障害者
103
+ nil, # 障害者の数 上の内訳 NOTE: 同居・同一生計
104
+ nil, # 障害者の数 その他
105
+ slip['912'], # 社会保険料等の額
106
+ nil, # 上の内訳 NOTE: 小規模企業共済等掛金
107
+ ]
108
+ end
109
+
110
+ def 支払を受ける者の詳細(profile, year)
111
+ birth = if profile['birth_date'].is_a?(String)
112
+ Date.parse(profile['birth_date'])
113
+ else
114
+ profile['birth_date']
115
+ end
116
+ 生年月日 = 和暦(birth)
117
+ 扶養対象 = 扶養親族分類(profile['family'], year)
118
+
119
+ [
120
+ nil, # 生命保険料の控除額
121
+ nil, # 地震保険料の控除額
122
+ nil, # 住宅借入金等特別控除等の額
123
+ nil, # 旧個人年金保険料の額
124
+ nil, # 配偶者の合計所得
125
+ nil, # 旧長期損害保険料の額
126
+ 生年月日[0], # 必須:受給者の生年月日 元号
127
+ 生年月日[1], # 必須:受給者の生年月日 年
128
+ 生年月日[2], # 必須:受給者の生年月日 月
129
+ 生年月日[3], # 必須:受給者の生年月日 日
130
+ nil, # 夫あり(省略する)
131
+ birth.next_year(18) < Date.new(year, 12, -1) ? 0 : 1, # 未成年者
132
+ nil, # 乙欄適用
133
+ nil, # 本人が特別障害者
134
+ nil, # 本人がその他障害者
135
+ nil, # 老年者(省略する)
136
+ nil, # 寡婦
137
+ nil, # 寡夫(省略する)
138
+ Array.new(35), # col90まで未実装
139
+ 扶養対象['16才未満'].length,
140
+ nil, # 国民年金保険料等の額
141
+ nil, # 非居住者である親族の数
142
+ ]
143
+ end
144
+
145
+ def 支払を受ける者の扶養情報(profile, year)
146
+ 扶養対象 = 扶養親族分類(profile['family'], year)
147
+
148
+ [
149
+ profile['tax_id'], # 個人番号
150
+ profile.dig('spouse', 'katakana'), # 控除対象配偶者カナ
151
+ profile.dig('spouse', 'name'), # 控除対象配偶者 氏名
152
+ profile.dig('spouse') ? 00 : nil, # 控除対象配偶者 区分 TODO: 非居住者(01)の設定
153
+ profile.dig('spouse', 'tax_id'), # 控除対象配偶者 個人番号
154
+ [0, 1, 2, 3].map { |i|
155
+ [
156
+ 扶養対象.dig('16才以上', i, 'katakana'), # カナ
157
+ 扶養対象.dig('16才以上', i, 'name'), # 氏名
158
+ 扶養対象.dig('16才以上', i) ? '00' : nil, # 区分 TODO: 非居住者(01)の設定
159
+ 扶養対象.dig('16才以上', i, 'tax_id'), # 個人番号
160
+ ]
161
+ },
162
+ [0, 1, 2, 3].map { |i|
163
+ [
164
+ 扶養対象.dig('16才未満', i, 'katakana'), # カナ
165
+ 扶養対象.dig('16才未満', i, 'name'), # 氏名
166
+ 扶養対象.dig('16才未満', i) ? '00' : nil, # 区分 TODO: 非居住者(01)の設定
167
+ 扶養対象.dig('16才未満', i, 'tax_id'), # 個人番号
168
+ ]
169
+ },
170
+ Array(扶養対象.dig('16才以上'))[4..]&.map{ |p| "(#{p['tax_id']})#{p['name']}" }&.join(' '), # 5人目以降
171
+ Array(扶養対象.dig('16才未満'))[4..]&.map{ |p| "(#{p['tax_id']})#{p['name']}" }&.join(' '), # 5人目以降
172
+ nil, # 普通徴収
173
+ nil, # 青色専従者
174
+ nil, # 条約免除
175
+ 半角変換(profile['katakana']), # 必須:支払を受ける者のフリガナ(半角)
176
+ nil, # 需給者番号
177
+ profile.dig('resident', 'area_code'), # 必須:提出先市町村コード
178
+ profile.dig('resident', 'tax_id'), # 指定番号
179
+ ]
180
+ end
181
+
182
+ # https://www.nta.go.jp/taxes/shiraberu/taxanswer/hotei/7411.htm
183
+ # TODO: 役員・弁護士の考慮
184
+ def 提出先判定(slip)
185
+ case slip['1']
186
+ when 0 .. 5_000_000
187
+ 1
188
+ else
189
+ 2
190
+ end
191
+ end
192
+
193
+ def 老人控除対象配偶者(spouse, year)
194
+ return nil if spouse.nil?
195
+ date = spouse['birth_date']
196
+ return 0 if date.nil?
197
+
198
+ date = Date.parse(date) if date.is_a?(String)
199
+ date.next_year(70) < Date.new(year, 12, -1) ? 1 : 0
200
+ end
201
+
202
+ def 扶養親族分類(family, year)
203
+ return {} if family.nil?
204
+
205
+ { '16才以上' => [], '16才未満' => [] }.tap do |result|
206
+ family.each do |p|
207
+ if !p['birth_date']
208
+ result['16才以上'] << p
209
+ else
210
+ date = p['birth_date']
211
+ date = Date.parse(date) if date.is_a?(String)
212
+ if date.next_year(16) < Date.new(year, 12, -1)
213
+ result['16才以上'] << p
214
+ else
215
+ result['16才未満'] << p
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+
222
+ def 和暦(date)
223
+ return [nil, nil, nil, nil] if date.nil?
224
+
225
+ date = Date.parse(date) if date.is_a?(String)
226
+ case date
227
+ when Date.new(0, 1, 1) .. Date.new(1868, 10, 22)
228
+ raise "Not supported before 明治"
229
+ when Date.new(1868, 10, 23) .. Date.new(1912, 7, 29)
230
+ [3, format("%02d", date.year - 1867), format("%02d", date.month), format("%02d", date.day)]
231
+ when Date.new(1912, 7, 30) .. Date.new(1926, 12, 24)
232
+ [2, format("%02d", date.year - 1911), format("%02d", date.month), format("%02d", date.day)]
233
+ when Date.new(1926, 12, 25) .. Date.new(1989, 1, 7)
234
+ [1, format("%02d", date.year - 1925), format("%02d", date.month), format("%02d", date.day)]
235
+ when Date.new(1989, 1, 8) .. Date.new(2019, 4, 30)
236
+ [4, format("%02d", date.year - 1988), format("%02d", date.month), format("%02d", date.day)]
237
+ else
238
+ [5, format("%02d", date.year - 2018), format("%02d", date.month), format("%02d", date.day)]
239
+ end
240
+ end
241
+
242
+ def 半角変換(str)
243
+ return nil if str.nil? or str.empty?
244
+
245
+ result, _e, _s = Open3.capture3("uconv -x 'Fullwidth-Halfwidth'", stdin_data: str)
246
+ result
247
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lucasalary-jp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuma Takahiro
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-30 00:00:00.000000000 Z
11
+ date: 2023-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lucasalary
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.1.14
19
+ version: 0.1.26
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.1.14
26
+ version: 0.1.26
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.17'
33
+ version: '2.3'
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
- version: '1.17'
40
+ version: '2.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -68,14 +68,16 @@ dependencies:
68
68
  version: '5.0'
69
69
  description: 'LucaSalary calculation module for Japan
70
70
 
71
- '
71
+ '
72
72
  email:
73
73
  - co.chuma@gmail.com
74
- executables: []
74
+ executables:
75
+ - luca-salary-jp
75
76
  extensions: []
76
77
  extra_rdoc_files: []
77
78
  files:
78
79
  - LICENSE
80
+ - exe/luca-salary-jp
79
81
  - lib/luca_salary/dict/code.tsv
80
82
  - lib/luca_salary/jp-national-tax/.git
81
83
  - lib/luca_salary/jp-national-tax/.gitignore
@@ -85,6 +87,7 @@ files:
85
87
  - lib/luca_salary/jp.rb
86
88
  - lib/luca_salary/jp/insurance.rb
87
89
  - lib/luca_salary/jp/version.rb
90
+ - lib/luca_salary/jp_payreport.rb
88
91
  homepage: https://github.com/chumaltd/luca-salary-jp
89
92
  licenses:
90
93
  - GPL
@@ -100,14 +103,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
100
103
  requirements:
101
104
  - - ">="
102
105
  - !ruby/object:Gem::Version
103
- version: 2.6.0
106
+ version: 3.0.0
104
107
  required_rubygems_version: !ruby/object:Gem::Requirement
105
108
  requirements:
106
109
  - - ">="
107
110
  - !ruby/object:Gem::Version
108
111
  version: '0'
109
112
  requirements: []
110
- rubygems_version: 3.1.2
113
+ rubygems_version: 3.4.1
111
114
  signing_key:
112
115
  specification_version: 4
113
116
  summary: LucaSalary calculation molule for Japan