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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a05163c92393153a574007ef8c0f932f0f203d94563ba4757c94b8250ae019b5
4
- data.tar.gz: cc17641c475339eb14d34b8a66d6f691d5a16afc00773c2da021b742249a5d1c
3
+ metadata.gz: dda408b24c4c0da00dbd74687fb89520ec440d089c169bb16bb9fef83163f287
4
+ data.tar.gz: d86a04f19fb5fb6b66115f60f0b4026cb4594b8771d50ad422c4f9f9f330adbb
5
5
  SHA512:
6
- metadata.gz: 1dbe2a8d65e5c6c6bc96fc31a5bdafa456b5cb216bb8758b0076ba8651e5b44e73de7355a9bfd84b582d285c3be1b345aab4255838942a1739fb5045ad5c4da1
7
- data.tar.gz: 42a4e83b651b23f20108dd414a93ac2ec308a8e03e686e13ab1c61f5b24c8e534d734ce5b55658a82c0e33a3634e428d63c57670091d0db3a87e893bed7c5f32
6
+ metadata.gz: 553359d8be075e3c58c83e9512f2f8d5a3247be37fa878bc13baa0c9d76ff6f6530d6ff772ca5925e5c9bce181ddc5212cc73dc5af77293cca500c8dc6dae6ef
7
+ data.tar.gz: 8b8f0a3a04ea4148c8608b2a76ed8335b8e7a0e6f86f96ac5c6a373c47ccd6fededb58398a1186963d01494ae11a886be90e6b016e9838dea7b3aa709ea8e423
data/.rubocop.yml CHANGED
@@ -1,7 +1,8 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 3.2.2
3
3
  Include:
4
- - 'lib/**/*.rb'
4
+ - 'lib/inss_calculator/*.rb'
5
+ - 'inss_calculator.rb'
5
6
 
6
7
  Style/StringLiterals:
7
8
  Enabled: true
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- inss_calculator (0.3.2)
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.0)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module InssCalculator
4
- VERSION = '0.4.0'
4
+ VERSION = '0.4.1'
5
5
  end
@@ -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.0
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-08-08 00:00:00.000000000 Z
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