lucasalary-jp 0.1.2 → 0.1.4

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: 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