inss_calculator 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -1
- data/Gemfile.lock +2 -2
- data/README.md +15 -0
- data/lib/dinheiro.rb +303 -0
- data/lib/inss_calculator/decorator/text.rb +39 -0
- data/lib/inss_calculator/version.rb +1 -1
- data/lib/inss_calculator.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dda408b24c4c0da00dbd74687fb89520ec440d089c169bb16bb9fef83163f287
|
4
|
+
data.tar.gz: d86a04f19fb5fb6b66115f60f0b4026cb4594b8771d50ad422c4f9f9f330adbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 553359d8be075e3c58c83e9512f2f8d5a3247be37fa878bc13baa0c9d76ff6f6530d6ff772ca5925e5c9bce181ddc5212cc73dc5af77293cca500c8dc6dae6ef
|
7
|
+
data.tar.gz: 8b8f0a3a04ea4148c8608b2a76ed8335b8e7a0e6f86f96ac5c6a373c47ccd6fededb58398a1186963d01494ae11a886be90e6b016e9838dea7b3aa709ea8e423
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
inss_calculator (0.
|
4
|
+
inss_calculator (0.4.1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -10,7 +10,7 @@ GEM
|
|
10
10
|
diff-lcs (1.5.1)
|
11
11
|
json (2.7.2)
|
12
12
|
language_server-protocol (3.17.0.3)
|
13
|
-
parallel (1.26.
|
13
|
+
parallel (1.26.3)
|
14
14
|
parser (3.3.4.2)
|
15
15
|
ast (~> 2.4.1)
|
16
16
|
racc
|
data/README.md
CHANGED
@@ -78,6 +78,21 @@ Desta forma, uma requisição que busca somente a primeira faixa salarial seria:
|
|
78
78
|
YourModel.where("salary <= ?", InssCalculator::FIRST_SALARY_LIMIT)
|
79
79
|
```
|
80
80
|
|
81
|
+
## Decorators
|
82
|
+
|
83
|
+
`InssCalculator::Decorator::Text` explica no formato de texto o que o trabalhador precisa saber.
|
84
|
+
Ideal para uso no parágrafo do HTML. Retire da view esta responsabildade e deixe com este decorator.
|
85
|
+
Você ainda tem acesso à classe original com `#calculator`.
|
86
|
+
|
87
|
+
```
|
88
|
+
calculator = InssCalculator::DiscountPrevidenceCalculator.new(3000)
|
89
|
+
text_decorator = InssCalculator::Decorator::Text.new(calculator)
|
90
|
+
text_decorator.present => "Com o salário de R$ 3.000,00, sua contribuição é de R$ 258,81. Seu salário líquido, portanto, é de R$ 2.741,19."
|
91
|
+
|
92
|
+
text_decorator.calculator => InssCalculator::DiscountPrevidenceCalculator.new(3000)
|
93
|
+
|
94
|
+
```
|
95
|
+
|
81
96
|
## Nota sobre trabalhar com números decimais em Ruby
|
82
97
|
|
83
98
|
Após investigar os resultados dos exemplos contábeis, concluiu-se que os números são truncados.
|
data/lib/dinheiro.rb
ADDED
@@ -0,0 +1,303 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Com base em https://github.com/tapajos/brazilian-rails/blob/master/brdinheiro/lib/brdinheiro/dinheiro.rb
|
4
|
+
class Dinheiro
|
5
|
+
include Comparable
|
6
|
+
|
7
|
+
attr_reader :quantia
|
8
|
+
|
9
|
+
FORMATO_VALIDO_BR = /^([R|r]\$\s*)?(([+-]?\d{1,3}(\.?\d{3})*))?(,\d{0,2})?$/
|
10
|
+
FORMATO_VALIDO_EUA = /^([R|r]\$\s*)?(([+-]?\d{1,3}(,?\d{3})*))?(\.\d{0,2})?$/
|
11
|
+
SEPARADOR_MILHAR = '.'
|
12
|
+
SEPARADOR_FRACIONARIO = ','
|
13
|
+
QUANTIDADE_DIGITOS = 3
|
14
|
+
PRECISAO_DECIMAL = 100
|
15
|
+
|
16
|
+
def initialize(quantia)
|
17
|
+
self.quantia = quantia
|
18
|
+
end
|
19
|
+
|
20
|
+
# Retorna o valor em Float quando uma coleção
|
21
|
+
# ou objeto é convertido para JSON
|
22
|
+
#
|
23
|
+
# Exemplo:
|
24
|
+
# produto = Produto.find 1
|
25
|
+
# produto.to_json // {"nome": "MacBook", "valor": 3500.0}
|
26
|
+
def as_json
|
27
|
+
to_f
|
28
|
+
end
|
29
|
+
|
30
|
+
# Retorna o valor armazenado em string.
|
31
|
+
#
|
32
|
+
# Exemplo:
|
33
|
+
# 1000.to_s ==> '1.000,00'
|
34
|
+
def to_s
|
35
|
+
inteiro_com_milhar(parte_inteira) + parte_decimal
|
36
|
+
end
|
37
|
+
|
38
|
+
# Compara com outro dinheiro se eh igual.
|
39
|
+
#
|
40
|
+
# Exemplo:
|
41
|
+
# um_real = Dinheiro.new(1)
|
42
|
+
# um_real == Dinheiro.new(1) ==> true
|
43
|
+
# um_real == Dinheiro.new(2) ==> false
|
44
|
+
def ==(other)
|
45
|
+
begin
|
46
|
+
other = Dinheiro.new(other) unless other.is_a?(Dinheiro)
|
47
|
+
rescue StandardError
|
48
|
+
return false
|
49
|
+
end
|
50
|
+
@quantia == other.quantia
|
51
|
+
end
|
52
|
+
|
53
|
+
# Compara com outro dinheiro se eh maior ou menor.
|
54
|
+
#
|
55
|
+
# Exemplo:
|
56
|
+
# 1.real < 2.reais ==> true
|
57
|
+
# 1.real > 2.reais ==> false
|
58
|
+
# 2.real < 1.reais ==> false
|
59
|
+
# 2.real > 1.reais ==> true
|
60
|
+
def <=>(other)
|
61
|
+
other = Dinheiro.new(other) unless other.is_a?(Dinheiro)
|
62
|
+
@quantia <=> other.quantia
|
63
|
+
end
|
64
|
+
|
65
|
+
# Retorna a adicao entre dinheiros.
|
66
|
+
#
|
67
|
+
# Exemplo:
|
68
|
+
# 1.real + 1.real == 2.reais
|
69
|
+
# 1.real + 1 == 2.reais
|
70
|
+
def +(other)
|
71
|
+
Dinheiro.new(transforma_em_string_que_represente_a_quantia(@quantia + quantia_de(other)))
|
72
|
+
end
|
73
|
+
|
74
|
+
# Retorna a subtracao entre dinheiros.
|
75
|
+
#
|
76
|
+
# Exemplo:
|
77
|
+
# 10.reais - 2.reais == 8.reais
|
78
|
+
# 10.reais - 2 == 8.reais
|
79
|
+
def -(other)
|
80
|
+
Dinheiro.new(transforma_em_string_que_represente_a_quantia(@quantia - quantia_de(other)))
|
81
|
+
end
|
82
|
+
|
83
|
+
# Retorna a multiplicacao entre dinheiros.
|
84
|
+
#
|
85
|
+
# Exemplo:
|
86
|
+
# 5.reais * 2 == 10.reais
|
87
|
+
# 5.reais * 2.reais == 10.reais
|
88
|
+
def *(other)
|
89
|
+
return Dinheiro.new(to_f * other) unless other.is_a? Dinheiro
|
90
|
+
|
91
|
+
other * to_f
|
92
|
+
end
|
93
|
+
|
94
|
+
# Retorna a divisao entre dinheiros.
|
95
|
+
#
|
96
|
+
# Exemplo:
|
97
|
+
# 5.reais / 2 == (2.5).reais
|
98
|
+
# 5.reais / 2.reais == DivisaPorNaoEscalarError
|
99
|
+
# 5.reais / 0 == ZeroDivisionError
|
100
|
+
#
|
101
|
+
# Veja também o método parcelar
|
102
|
+
def /(other)
|
103
|
+
raise DivisaPorNaoEscalarError unless other.is_a?(Numeric)
|
104
|
+
return @quantia / other if other.zero?
|
105
|
+
|
106
|
+
Dinheiro.new(to_f / other)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Retorna um array de dinheiro com as parcelas
|
110
|
+
#
|
111
|
+
# Exemplo:
|
112
|
+
# 6.reais.parcelar(2) == [3.reais, 3.reais]
|
113
|
+
# 6.reais.parcelar(2.reais) == DisivaPorNaoEscalarError
|
114
|
+
# 6.reais.parcelar(0) == ZeroDivisionError
|
115
|
+
def parcelar(numero_de_parcelar)
|
116
|
+
raise DivisaPorNaoEscalarError unless numero_de_parcelar.is_a?(Integer)
|
117
|
+
|
118
|
+
resto = @quantia % numero_de_parcelar
|
119
|
+
valor_menor = Dinheiro.new((@quantia / numero_de_parcelar) / 100.0)
|
120
|
+
valor_maior = Dinheiro.new((@quantia / numero_de_parcelar + 1) / 100.0)
|
121
|
+
[valor_menor] * (numero_de_parcelar - resto) + [valor_maior] * resto
|
122
|
+
end
|
123
|
+
|
124
|
+
# Escreve o valor por extenso.
|
125
|
+
#
|
126
|
+
# Exemplo:
|
127
|
+
# 1.real.to_extenso ==> 'um real'
|
128
|
+
# (100.58).to_extenso ==> 'cem reais e cinquenta e oito centavos'
|
129
|
+
def to_extenso
|
130
|
+
(@quantia / 100.0).por_extenso_em_reais
|
131
|
+
end
|
132
|
+
|
133
|
+
# Alias para o metodo to_extenso.
|
134
|
+
alias por_extenso to_extenso
|
135
|
+
|
136
|
+
# Alias para o metodo to_extenso.
|
137
|
+
alias por_extenso_em_reais to_extenso
|
138
|
+
|
139
|
+
# Verifica se o valor é zero.
|
140
|
+
def zero?
|
141
|
+
to_f.zero?
|
142
|
+
end
|
143
|
+
|
144
|
+
# Retorna um Float.
|
145
|
+
def to_f
|
146
|
+
to_s.gsub('.', '').gsub(',', '.').to_f
|
147
|
+
end
|
148
|
+
|
149
|
+
def coerce(outro) # :nodoc:
|
150
|
+
[Dinheiro.new(outro), self]
|
151
|
+
end
|
152
|
+
|
153
|
+
# Retorna a própria instância/
|
154
|
+
def real
|
155
|
+
self
|
156
|
+
end
|
157
|
+
|
158
|
+
# Alias para real.
|
159
|
+
alias reais real
|
160
|
+
|
161
|
+
# Retorna uma string formatada com sigla em valor monetário.
|
162
|
+
# Exemplo:
|
163
|
+
# Dinheiro.new(1).real_formatado ==> 'R$ 1,00'
|
164
|
+
# Dinheiro.new(-1).real_formatado ==> 'R$ -1,00'
|
165
|
+
def real_formatado
|
166
|
+
"R$ #{self}"
|
167
|
+
end
|
168
|
+
|
169
|
+
# Alias para real_formatado.
|
170
|
+
alias reais_formatado real_formatado
|
171
|
+
|
172
|
+
# Retorna uma string formatada com sigla em valor contábil.
|
173
|
+
#
|
174
|
+
# Exemplo:
|
175
|
+
# Dinheiro.new(1).real_contabil ==> 'R$ 1,00'
|
176
|
+
# Dinheiro.new(-1).real_contabil ==> 'R$ (1,00)'
|
177
|
+
def real_contabil
|
178
|
+
"R$ #{contabil}"
|
179
|
+
end
|
180
|
+
|
181
|
+
# Alias para real_contabil.
|
182
|
+
alias reais_contabeis real_contabil
|
183
|
+
|
184
|
+
# Retorna uma string formatada sem sigla.
|
185
|
+
#
|
186
|
+
# Exemplo:
|
187
|
+
# Dinheiro.new(1).contabil ==> '1,00'
|
188
|
+
# Dinheiro.new(-1).contabil ==> '(1,00)'
|
189
|
+
def contabil
|
190
|
+
if @quantia >= 0
|
191
|
+
to_s
|
192
|
+
else
|
193
|
+
"(#{to_s[1..]})"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# Method missing para retorna um BigDecinal quando chamada .
|
198
|
+
def method_missing(symbol, *args) # :nodoc:
|
199
|
+
# Ex: Chama ao método valor_decimal()
|
200
|
+
if (symbol.to_s =~ /^(.*)_decimal$/) && args.empty?
|
201
|
+
BigDecimal quantia_sem_separacao_milhares.gsub(',', '.')
|
202
|
+
else
|
203
|
+
super.method_missing(symbol, *args)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
def quantia_de(outro)
|
210
|
+
outro = outro.to_f if outro.is_a?(BigDecimal)
|
211
|
+
return outro.quantia if outro.is_a?(Dinheiro)
|
212
|
+
|
213
|
+
(outro * 100).round
|
214
|
+
end
|
215
|
+
|
216
|
+
def transforma_em_string_que_represente_a_quantia(quantia)
|
217
|
+
if /^([+-]?)(\d)$/ =~ quantia.to_s
|
218
|
+
return "#{::Regexp.last_match(1)}0.0#{::Regexp.last_match(2)}"
|
219
|
+
end
|
220
|
+
|
221
|
+
/^([+-]?)(\d*)(\d\d)$/ =~ quantia.to_s
|
222
|
+
"#{::Regexp.last_match(1)}#{::Regexp.last_match(2).to_i}.#{::Regexp.last_match(3)}"
|
223
|
+
end
|
224
|
+
|
225
|
+
def quantia=(quantia)
|
226
|
+
@quantia = (quantia * 100).round if quantia.is_a?(Numeric)
|
227
|
+
@quantia = extrai_quantia_como_inteiro(quantia) if quantia.is_a?(String)
|
228
|
+
end
|
229
|
+
|
230
|
+
def parte_inteira
|
231
|
+
quantia_sem_separacao_milhares[0, quantia_sem_separacao_milhares.length - QUANTIDADE_DIGITOS]
|
232
|
+
end
|
233
|
+
|
234
|
+
def parte_decimal
|
235
|
+
quantia_sem_separacao_milhares[-QUANTIDADE_DIGITOS, QUANTIDADE_DIGITOS]
|
236
|
+
end
|
237
|
+
|
238
|
+
def inteiro_com_milhar(inteiro)
|
239
|
+
return inteiro if quantidade_de_passos(inteiro).zero?
|
240
|
+
|
241
|
+
resultado = ''
|
242
|
+
quantidade_de_passos(inteiro).times do |passo|
|
243
|
+
resultado = ".#{inteiro[-QUANTIDADE_DIGITOS + passo * -QUANTIDADE_DIGITOS, QUANTIDADE_DIGITOS]}#{resultado}"
|
244
|
+
end
|
245
|
+
resultado = inteiro[0, digitos_que_sobraram(inteiro)] + resultado
|
246
|
+
resultado.gsub(/^(-?)\./, '\1')
|
247
|
+
end
|
248
|
+
|
249
|
+
def quantia_sem_separacao_milhares
|
250
|
+
format('%.2f', (@quantia.to_f / PRECISAO_DECIMAL)).gsub(SEPARADOR_MILHAR, SEPARADOR_FRACIONARIO)
|
251
|
+
end
|
252
|
+
|
253
|
+
def quantidade_de_passos(inteiro)
|
254
|
+
resultado = inteiro.length / QUANTIDADE_DIGITOS
|
255
|
+
resultado = (resultado - 1) if (inteiro.length % QUANTIDADE_DIGITOS).zero?
|
256
|
+
resultado
|
257
|
+
end
|
258
|
+
|
259
|
+
def digitos_que_sobraram(inteiro)
|
260
|
+
inteiro.length - (quantidade_de_passos(inteiro) * QUANTIDADE_DIGITOS)
|
261
|
+
end
|
262
|
+
|
263
|
+
def quantia_valida?(quantia)
|
264
|
+
return false if quantia.is_a?(String) && !quantia_respeita_formato?(quantia)
|
265
|
+
|
266
|
+
quantia.is_a?(String) || quantia.is_a?(Numeric)
|
267
|
+
end
|
268
|
+
|
269
|
+
def extrai_quantia_como_inteiro(quantia)
|
270
|
+
return sem_milhar(::Regexp.last_match(2), ::Regexp.last_match(5), '.') if FORMATO_VALIDO_BR =~ quantia
|
271
|
+
return unless FORMATO_VALIDO_EUA =~ quantia
|
272
|
+
|
273
|
+
sem_milhar(::Regexp.last_match(2), ::Regexp.last_match(5), ',')
|
274
|
+
end
|
275
|
+
|
276
|
+
def sem_milhar(parte_inteira, parte_decimal, delimitador_de_milhar)
|
277
|
+
(inteiro(parte_inteira, delimitador_de_milhar) + decimal(parte_decimal)).to_i
|
278
|
+
end
|
279
|
+
|
280
|
+
def inteiro(inteiro_com_separador_milhar, separador)
|
281
|
+
return inteiro_com_separador_milhar.gsub(separador, '') unless inteiro_com_separador_milhar.blank?
|
282
|
+
|
283
|
+
''
|
284
|
+
end
|
285
|
+
|
286
|
+
def decimal(parte_fracionaria)
|
287
|
+
unless parte_fracionaria.blank?
|
288
|
+
return sem_delimitador_decimal(parte_fracionaria) if parte_fracionaria.length == 3
|
289
|
+
return "#{sem_delimitador_decimal(parte_fracionaria)}0" if parte_fracionaria.length == 2
|
290
|
+
end
|
291
|
+
'00'
|
292
|
+
end
|
293
|
+
|
294
|
+
def sem_delimitador_decimal(parte_fracionaria)
|
295
|
+
parte_fracionaria.to_s.gsub(/[.|,]/, '')
|
296
|
+
end
|
297
|
+
|
298
|
+
def quantia_respeita_formato?(quantia)
|
299
|
+
return true if FORMATO_VALIDO_BR.match(quantia) || FORMATO_VALIDO_EUA.match(quantia)
|
300
|
+
|
301
|
+
false
|
302
|
+
end
|
303
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InssCalculator
|
4
|
+
# It extends Calculator contribution to a presentable format.
|
5
|
+
module Decorator
|
6
|
+
require 'forwardable'
|
7
|
+
# InssCalculator::Decorator::Text implements the same Calculator interface
|
8
|
+
# and its #present method presents the contribution result in a text format.
|
9
|
+
class Text
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
def_delegators :@calculator, :salary, :net_salary, :contribution
|
13
|
+
|
14
|
+
attr_reader :calculator
|
15
|
+
|
16
|
+
def initialize(inss_calculator)
|
17
|
+
@calculator = inss_calculator
|
18
|
+
end
|
19
|
+
|
20
|
+
def present
|
21
|
+
"#{salary_text} #{contribution_text} #{net_salary_text}"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def salary_text
|
27
|
+
"Com o salário de #{Dinheiro.new(calculator.salary).real_formatado},"
|
28
|
+
end
|
29
|
+
|
30
|
+
def contribution_text
|
31
|
+
"sua contribuição é de #{Dinheiro.new(calculator.contribution).real_formatado}."
|
32
|
+
end
|
33
|
+
|
34
|
+
def net_salary_text
|
35
|
+
"Seu salário líquido, portanto, é de #{Dinheiro.new(calculator.net_salary).real_formatado}."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/inss_calculator.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'inss_calculator/version'
|
4
|
+
require_relative './dinheiro'
|
4
5
|
require_relative 'inss_calculator/discount_calculator_base'
|
5
6
|
require_relative 'inss_calculator/discount_previdence_calculator'
|
6
7
|
require_relative 'inss_calculator/public_inss_calculator'
|
@@ -12,6 +13,7 @@ require_relative 'inss_calculator/fifth_discount_calculator'
|
|
12
13
|
require_relative 'inss_calculator/sixth_discount_calculator'
|
13
14
|
require_relative 'inss_calculator/seventh_discount_calculator'
|
14
15
|
require_relative 'inss_calculator/eigth_discount_calculator'
|
16
|
+
require_relative 'inss_calculator/decorator/text'
|
15
17
|
|
16
18
|
module InssCalculator
|
17
19
|
class Error < StandardError; end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inss_calculator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paulo Felipe Souza
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-23 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Calcula o valor a descontar de acordo com a faixa salarial. Os novos
|
14
14
|
valores corrente em 2024.
|
@@ -29,7 +29,9 @@ files:
|
|
29
29
|
- README.md
|
30
30
|
- Rakefile
|
31
31
|
- inss_calculator.gemspec
|
32
|
+
- lib/dinheiro.rb
|
32
33
|
- lib/inss_calculator.rb
|
34
|
+
- lib/inss_calculator/decorator/text.rb
|
33
35
|
- lib/inss_calculator/discount_calculator_base.rb
|
34
36
|
- lib/inss_calculator/discount_previdence_calculator.rb
|
35
37
|
- lib/inss_calculator/eigth_discount_calculator.rb
|