formulas 0.1.0 → 0.1.1.3

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: 689f6cec1e958e23dba5a41b2eb083aa812961657b4a011911374afe5fef20f6
4
- data.tar.gz: e00de4f4bb6e815516a3cc8ece2c7ba8655921d56143a6a92d4e41b22abcdc90
3
+ metadata.gz: d76de1c75f2a4d040c1ab846d9519939f62aa31b14f211f93acf57c5a145c1dc
4
+ data.tar.gz: 1d888513526b6d285cef7af0fe0a31bc0b75acd97a1a553720667f7770bb4d51
5
5
  SHA512:
6
- metadata.gz: 0f3be3c409bce08fd1606ff9cde1da525353f5d1e25cf87c1083e7a13c592b6dd0c77c507f67bfa444add566f9fe3abab39aa0bcc85b2ffc8a2f2bed0bcb9892
7
- data.tar.gz: 3e3e80e654b6f904342e0e87ba8c92e175c19f5d4e64b1670bba81c2325a8a8d0e8dd7ecf15c1c48234b25e9a8a4f916c3dd46f4f8a0a4cfd07c13534eff75d8
6
+ metadata.gz: 1ef8a70cab87b06bbf82a896a4ba2d7f000490fec2fd123d9eafefc798ad36d1768dc6db29fc69e4c9783e24a015aac66f6a54d3fbed71a41dc0443b51b5033f
7
+ data.tar.gz: 9b0ab2c83dd661d24f0e4d67c3f2e4183c205f253e376964cb56f1af5f8ff631a19a6d665b2950aef149f6f81b6631407f06525872d654dc5f275b81ec356384
data/formulas.gemspec ADDED
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'formulas'
3
+ s.version = '0.1.1.3'
4
+ s.summary = "formulas"
5
+ s.description = "Home loan formulas"
6
+ s.authors = ["formulas"]
7
+ s.email = 'boopage@gmail.com'
8
+ s.platform = Gem::Platform::RUBY
9
+ s.files = Dir['lib/**/*.rb', 'formulas.gemspec']
10
+ s.homepage =
11
+ 'https://rubygems.org/gems/formulas'
12
+ s.license = 'MIT'
13
+ s.require_paths = ["lib"]
14
+
15
+ s.add_development_dependency "minitest", ">= 5.8"
16
+ s.add_development_dependency "minitest-reporters", ">= 1.1"
17
+ end
data/lib/formulas.rb CHANGED
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Formulas
4
- autoload :StudentLoanRepayment, './lib/formulas/student_loan_repayment'
5
- autoload :Salary, './lib/formulas/salary'
6
- autoload :PAYE, './lib/formulas/paye'
7
- autoload :PAYG, './lib/formulas/payg'
8
- autoload :Income, './lib/formulas/income'
9
- autoload :WithholdingTax, './lib/formulas/withholding_tax'
10
- autoload :Superannuation, './lib/formulas/superannuation'
11
- autoload :LoanPrincipalCalculator, './lib/formulas/loan_principal_calculator'
4
+ autoload :StudentLoanRepayment, 'formulas/student_loan_repayment'
5
+ autoload :Salary, 'formulas/salary'
6
+ autoload :PAYE, 'formulas/paye'
7
+ autoload :PAYG, 'formulas/payg'
8
+ autoload :Income, 'formulas/income'
9
+ autoload :WithholdingTax, 'formulas/withholding_tax'
10
+ autoload :Superannuation, 'formulas/superannuation'
11
+ autoload :LoanPrincipalCalculator, 'formulas/loan_principal_calculator'
12
12
 
13
13
  MULTIPLIER_IN_FREQUENCY = {
14
14
  weekly: 52,
@@ -25,6 +25,13 @@ module Formulas
25
25
  FREQUENCIES = [ANNUALLY, WEEKLY, FORTNIGHTLY, MONTHLY]
26
26
 
27
27
  module FrequencyConversions
28
+ def convert_annually_to_annually(number)
29
+ number
30
+ end
31
+ alias :convert_weekly_to_weekly convert_annually_to_annually
32
+ alias :convert_fortnightly_to_fortnightly convert_annually_to_annually
33
+ alias :convert_monthly_to_monthly convert_annually_to_annually
34
+
28
35
  def convert_annually_to_weekly(number)
29
36
  number.to_f / 52
30
37
  end
@@ -60,5 +67,17 @@ module Formulas
60
67
  def convert_monthly_to_fortnightly(number)
61
68
  number.to_f * 12 / 26
62
69
  end
70
+
71
+ def convert_fortnightly_to_annually(number)
72
+ number.to_f * 26
73
+ end
74
+
75
+ def convert_fortnightly_to_weekly(number)
76
+ number.to_f / 2
77
+ end
78
+
79
+ def convert_fortnightly_to_monthly(number)
80
+ (number.to_f * 26) / 12
81
+ end
63
82
  end
64
83
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formulas
4
+ module Expense
5
+ extend self
6
+
7
+ # Expense.monthly_credit_card(10_000, 3)
8
+ # => 300
9
+ def monthly_credit_card(amount, monthly_interest_rate)
10
+ amount * (monthly_interest_rate / 100)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formulas
4
+ # Provide calculation for household expenses based on the number of child
5
+ # and single or pair of adults
6
+ #
7
+ # source = Household::Source.new(independent_expenses: [1262, 1878], dependent_expenses: 347)
8
+ # household = Household.new(independents: integer, dependents: integer, source: source)
9
+ #
10
+ # household.cal(:month)
11
+ #
12
+ class HouseHold
13
+ def initialize(independents:, dependents:, source: Source.new)
14
+ @independents = independents
15
+ @dependents = dependents
16
+ @source = source
17
+ end
18
+
19
+ def cal
20
+ @source.calculate_expense(independents: @independents, dependents: @dependents)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formulas
4
+ module Expense
5
+ module Household
6
+ class Source
7
+ DEFAULT_INDEPENDENT_EXPENSE = [1_262, 1_878]
8
+ DEFAULT_DEPENDENT_EXPENSE = 347
9
+ MAX_DEPENDENT = 4
10
+ MAX_INDEPENDENT = 2
11
+
12
+ def calculate_expense(independents:, dependents:)
13
+ validate(independents, dependents)
14
+
15
+ ind_exp = DEFAULT_DEPENDENT_EXPENSE[0..independents].sum
16
+ de_exp = dependents * DEFAULT_DEPENDENT_EXPENSE
17
+ ind_exp + de_exp
18
+ end
19
+
20
+ private
21
+
22
+ def validate(independents, dependents)
23
+ raise "" if independents < 0
24
+ raise "" if dependents < 0
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formulas
4
+ module Income
5
+ extend self
6
+ DEFAULT_KIWISAVER = 3
7
+ DEFAULT_SUPER = 9.5
8
+
9
+ # Income.monthly_benefit_payment(100, :weekly)
10
+ # => 433
11
+ def monthly_benefit_payment(amount, frequency)
12
+ amount * Formulas::MULTIPLIER_IN_FREQUENCY[frequency] / 12
13
+ end
14
+
15
+ alias monthly_boarder_income monthly_benefit_payment
16
+
17
+ # Income.monthly_rental_income(100, :weekly)
18
+ # => 325
19
+ def monthly_rental_income(amount, frequency, fraction: 0.75)
20
+ (amount * Formulas::MULTIPLIER_IN_FREQUENCY[frequency] / 12) * fraction
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ module Formulas
4
+ class LoanPrincipalCalculator
5
+ attr_accessor :apr, :months, :repayment
6
+
7
+ def divide_by
8
+ interest_with_principal - 1
9
+ end
10
+
11
+ def interest_with_principal
12
+ (1 + (apr / 12)) ** months
13
+ end
14
+
15
+ def principal
16
+ (((repayment * divide_by) / interest_with_principal) * 12 ) / apr
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ # https://www.ato.gov.au/Rates/Individual-income-tax-rates/
3
+ #
4
+ module Formulas
5
+ # Calculate basic salary in frequency
6
+ # paye = Formulas::PAYE.new(gross_pay: 52_000, frequency: Formulas::ANNUAL)
7
+ #
8
+ # paye.tax(:monthly)
9
+ # => 718.33
10
+ #
11
+ # Provide a way to calculate pay as you earn calculator
12
+ class PAYE < WithholdingTax
13
+ # Provide a way to calculate PAYE tax
14
+ TAX_RATES = [
15
+ [[0, 14_000], 10.5],
16
+ [[14_000, 48_000], 17.5],
17
+ [[48_000, 70_000], 30],
18
+ [[70_000, 180_000], 33],
19
+ [[180_000], 39]
20
+ ]
21
+
22
+ MAX_ACC_LEVY_PAYABLE = 130_911
23
+
24
+ def initialize(gross_pay:, frequency: Formulas::MONTHLY)
25
+ super(gross_pay, frequency, TAX_RATES)
26
+ end
27
+
28
+ def tax(request_frequency: Formulas::WEEKLY)
29
+ calculate_tax(request_frequency)
30
+ end
31
+
32
+ def levy
33
+ 0.0139
34
+ end
35
+
36
+ def acc_payable
37
+ annual_gross_pay > MAX_ACC_LEVY_PAYABLE ? MAX_ACC_LEVY_PAYABLE : annual_gross_pay
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ # https://www.ato.gov.au/rates/individual-income-tax-rates/
3
+ #
4
+ module Formulas
5
+ # Calculate basic salary in frequency
6
+ # salary = Income::Salary.new(52_000, Salary::ANNUAL)
7
+ #
8
+ # Taxable income
9
+ #
10
+ # Tax on this income
11
+ #
12
+ # 0 – $18,200
13
+ #
14
+ # Nil
15
+ #
16
+ # $18,201 – $45,000
17
+ #
18
+ # 19 cents for each $1 over $18,200
19
+ #
20
+ # $45,001 – $120,000
21
+ #
22
+ # $5,092 plus 32.5 cents for each $1 over $45,000
23
+ #
24
+ # $120,001 – $180,000
25
+ #
26
+ # $29,467 plus 37 cents for each $1 over $120,000
27
+ #
28
+ # $180,001 and over
29
+ #
30
+ # $51,667 plus 45 cents for each $1 over $180,000
31
+ #
32
+ # Plus 2 percent medicare
33
+ #
34
+ # salary.calculate(:monthly)
35
+ class PAYG < WithholdingTax
36
+ # Provide a way to calculate pay as you earn calculator
37
+ TAX_RATES = [
38
+ [[0, 18_200], 0],
39
+ [[18_201, 45_000], 19],
40
+ [[45_001, 120_000], 32.5],
41
+ [[120_001, 180_000], 37],
42
+ [[180_001], 45]
43
+ ]
44
+
45
+ def initialize(gross_pay:, frequency: Formulas::MONTHLY)
46
+ super(gross_pay, frequency, TAX_RATES)
47
+ end
48
+
49
+ def tax(request_frequency: Formulas::WEEKLY)
50
+ calculate_tax(request_frequency) * 1.02
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formulas
4
+ # Calculate basic salary in frequency
5
+ # salary = Formulas::Salary.new(pay: 52_000, Salary::ANNUAL)
6
+ #
7
+ # salary.pay(:monthly)
8
+ #
9
+ # Provide a way to calculate salary in different frequencies
10
+ # It provide summaries of salary breakdown after deductions
11
+ # such as withholding tax, super, health care and methods to
12
+ # get the take home pay
13
+ #
14
+ class Salary
15
+ include FrequencyConversions
16
+ attr_reader :frequency
17
+
18
+ def initialize(pay:, frequency:)
19
+ @pay = pay
20
+ @frequency = frequency.to_sym
21
+
22
+ invalid_frequency unless FREQUENCIES.include?(@frequency)
23
+ raise ArgumentError, 'Gross pay must be numeric' unless Numeric === pay
24
+ end
25
+
26
+ def pay(request_frequency: @frequency)
27
+ invalid_frequency unless FREQUENCIES.include?(frequency)
28
+ return @pay if @frequency == request_frequency
29
+
30
+ convert_pay_to(request_frequency).round(2)
31
+ end
32
+
33
+ private
34
+
35
+ def invalid_frequency
36
+ raise ArgumentError, "Invalid frequency, frequency are: #{FREQUENCIES.join(', ')}"
37
+ end
38
+
39
+ def convert_pay_to(request_frequency)
40
+ send("convert_#{@frequency}_to_#{request_frequency}", @pay)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require_relative 'student_loan_repayment/fix_repayment_per_period'
4
+
5
+ module Formulas
6
+
7
+ # Provide calculation to work out student loan repayment in frequency
8
+ # loan = StudentLoan.new(salary_amount: 1000, salary_frequency: Income::Salary::WEEKLY)
9
+ #
10
+ # loan.repayment
11
+ # => <#ConstantPerPeriod>
12
+ #
13
+ # loan.repayment
14
+ # => 317
15
+ #
16
+ #
17
+ module StudentLoanRepayment
18
+ autoload :RepaymentBase, 'formulas/student_loan_repayment/repayment_base'
19
+ autoload :FixRepaymentPerPeriod, 'formulas/student_loan_repayment/fix_repayment_per_period'
20
+ autoload :PercentRepaymentPerThreshold, 'formulas/student_loan_repayment/percent_repayment_per_threshold'
21
+ attr_reader :repayment
22
+
23
+ def initialize(income_strategy: , repayment_strategy:, rates:)
24
+ raise 'Missing required salary' unless income_strategy
25
+ raise 'Missing required repayment rates' unless rates
26
+ raise 'Missing required repayment strategy' unless repayment_strategy
27
+
28
+ @repayment = repayment_strategy.new(salary: income_strategy, repayment: rates)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # @salary = Formulas::Salary.new(gross_pay: 100_000, frequency: Formulas::ANNUALLY)
4
+ # @repayment_rates = Formulas::StudentLoanRepayment::FixRepaymentPerPeriod::DEFAULT_THRESHOLD
5
+ #
6
+ # loan = Formulas::StudentLoanRepayment::FixRepaymentPerPeriod.new(salary: @salary, repayment: @repayment_rates)
7
+ # assert_equal loan.repayment, 797.2
8
+ #
9
+ module Formulas
10
+ module StudentLoanRepayment
11
+ class FixRepaymentPerPeriod < RepaymentBase
12
+ FIX_RATE = 0.12
13
+
14
+ DEFAULT_THRESHOLD = {
15
+ weekly: [390, 52],
16
+ fortnightly: [780, 26],
17
+ four_weeks: [1560, 13],
18
+ monthly: [1690, 12],
19
+ annually: [20280, 1],
20
+ }
21
+
22
+ def repayment
23
+ selected_base = @repayment_threshold[@salary.frequency]
24
+ less_base_cost = @salary.pay - selected_base[0]
25
+ ((FIX_RATE * less_base_cost * selected_base[1]) / 12).round(2)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formulas
4
+ module StudentLoanRepayment
5
+ class PercentRepaymentPerThreshold < RepaymentBase
6
+ DEFAULT_THRESHOLD = [
7
+ [[0, 46_620], 0],
8
+ [[46_620, 53_826], 1],
9
+ [[53_826, 57_055], 2],
10
+ [[57_056, 60_479], 2.5],
11
+ [[60_480, 64_108], 3],
12
+ [[64_109, 67_954], 3.5],
13
+ [[67_955, 72_031], 4],
14
+ [[72_032, 76_354], 4.5],
15
+ [[76_355, 80_935], 5],
16
+ [[80_936, 85_792], 5.5],
17
+ [[85_793, 90_939], 6],
18
+ [[90_940, 96_396], 6.5],
19
+ [[96_397, 102_179], 7],
20
+ [[102_180, 108_309], 7.5],
21
+ [[108_310, 114_707], 8],
22
+ [[114_708, 121_698], 8.5],
23
+ [[121_699, 128_999], 9],
24
+ [[129_000, 136_739], 9.5],
25
+ [[136_740], 10],
26
+ ]
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,10 @@
1
+ module Formulas
2
+ module StudentLoanRepayment
3
+ class RepaymentBase
4
+ def initialize(salary:, repayment_threshold:)
5
+ @salary = salary
6
+ @repayment_threshold = repayment_threshold
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formulas
4
+ # Calculate the super using the as per frequency
5
+ #
6
+ # salary = Formulas::Salary.new(gross_pay: 52_000, Salary::ANNUAL)
7
+ # super = Super.new(salary: salary, Super::KIWISAVER)
8
+ #
9
+ # super.calculate
10
+ # => 1,560.00
11
+ #
12
+ # super.calculate(Formulas::WEEKLY)
13
+ # => 30.00
14
+ #
15
+ class Superannuation
16
+ include FrequencyConversions
17
+
18
+ KIWISAVER = 3
19
+ AU_SUPER = 10
20
+
21
+ def initialize(salary:, superannuation:)
22
+ @salary = salary
23
+ @super = superannuation
24
+ end
25
+
26
+ def calculate(request_frequency: Formulas::ANNUALLY)
27
+ result = @salary.pay(request_frequency: request_frequency) * (@super.to_f / 100)
28
+ result.round(2)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formulas
4
+ class WithholdingTax
5
+ include FrequencyConversions
6
+
7
+ attr_reader :origin_frequency
8
+
9
+ def initialize(gross_pay, origin_frequency, tax_rate, options: {})
10
+ @gross_pay = gross_pay
11
+ @origin_frequency = origin_frequency
12
+ @tax_rate = tax_rate
13
+ @options = options
14
+ end
15
+
16
+ def net_pay(request_frequency:)
17
+ annual_pay_after_tax = annual_gross_pay - calculate_tax(Formulas::ANNUALLY)
18
+
19
+ if respond_to?(:levy)
20
+ net_pay = annual_pay_after_tax - (acc_payable * send(:levy))
21
+ end
22
+
23
+ return net_pay if request_frequency.to_s == Formulas::ANNUALLY.to_s
24
+
25
+ send("convert_annually_to_#{request_frequency}", net_pay).round(2)
26
+ end
27
+
28
+ protected
29
+
30
+ def calculate_tax(request_frequency)
31
+ @request_frequency = request_frequency
32
+
33
+ return annual_tax if request_frequency.to_s == Formulas::ANNUALLY.to_s
34
+
35
+ send("convert_annually_to_#{request_frequency}", annual_tax).round(2)
36
+ end
37
+
38
+ private
39
+
40
+ def annual_tax
41
+ annual_result = 0
42
+
43
+ if gross_pay_tax_index > 0
44
+ @tax_rate[0..gross_pay_tax_index].each_with_index do |current, index|
45
+ l = (index == gross_pay_tax_index) ? annual_gross_pay : current[0][1]
46
+ annual_result += ((l - current[0][0]) * (current[1].to_f / 100))
47
+ end
48
+ end
49
+ annual_result
50
+ end
51
+
52
+ def annual_gross_pay
53
+ return @gross_pay if @origin_frequency.to_s == Formulas::ANNUALLY.to_s
54
+ send("convert_#{@origin_frequency}_to_annually", @gross_pay)
55
+ end
56
+
57
+ def gross_pay_tax_index
58
+ @tax_rate.index { |current| current[0][0] <= annual_gross_pay && current[0][1] >= annual_gross_pay }
59
+ end
60
+ end
61
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: formulas
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - formulas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-28 00:00:00.000000000 Z
11
+ date: 2021-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -39,12 +39,27 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.1'
41
41
  description: Home loan formulas
42
- email: ''
42
+ email: boopage@gmail.com
43
43
  executables: []
44
44
  extensions: []
45
45
  extra_rdoc_files: []
46
46
  files:
47
+ - formulas.gemspec
47
48
  - lib/formulas.rb
49
+ - lib/formulas/expense.rb
50
+ - lib/formulas/household.rb
51
+ - lib/formulas/household/source.rb
52
+ - lib/formulas/income.rb
53
+ - lib/formulas/loan_principal_calculator.rb
54
+ - lib/formulas/paye.rb
55
+ - lib/formulas/payg.rb
56
+ - lib/formulas/salary.rb
57
+ - lib/formulas/student_loan_repayment.rb
58
+ - lib/formulas/student_loan_repayment/fix_repayment_per_period.rb
59
+ - lib/formulas/student_loan_repayment/percent_repayment_per_threshold.rb
60
+ - lib/formulas/student_loan_repayment/repayment_base.rb
61
+ - lib/formulas/superannuation.rb
62
+ - lib/formulas/withholding_tax.rb
48
63
  homepage: https://rubygems.org/gems/formulas
49
64
  licenses:
50
65
  - MIT