class SingaporeCPFCalculator::BaseCalculator

Base class for CPF calculators.

Attributes

additional_wages[R]
estimated_yearly_ow[R]
ordinary_wages[R]
ytd_additional_wages[R]
ytd_ow_subject_to_cpf[R]

Public Class Methods

applies_to?(date, birthdate:, has_70_up:) click to toggle source

@param [Fixnum] age @return [true, false] returns true if the calculator applies to the employee’s age.

# File lib/singapore_cpf_calculator/base_calculator.rb, line 8
def applies_to?(date, birthdate:, has_70_up:)
  AgeGroup.get(date, birthdate: birthdate, has_70_up: has_70_up) == required_age_group
end
calculate(ordinary_wages:, additional_wages:, ytd_additional_wages: 0.0, ytd_ow_subject_to_cpf: 0.0, estimated_yearly_ow: 0.0) click to toggle source

@param [BigDecimal] ordinary_wages:

Ordinary wages are wages due or granted in respect of employment and include allowances (e.g.
food allowance and overtime payments) earned by an employee in the month and payable before
the due date for payment of CPF contributions for that month.

@param [BigDecimal] additional_wages:

Additional wages are wage supplements which are not granted wholly and exclusively for the
month, such as annual bonus and leave pay. These and other incentive payments may be made at
intervals of more than a month.

@param [BigDecimal] ytd_additional_wages The year to date additional wages @param [BigDecimal] ytd_ow_subject_to_cpf The year to date ordinary wages @return [Hash] returns the total, employee, employer amounts for the CPF contribution

# File lib/singapore_cpf_calculator/base_calculator.rb, line 24
def calculate(ordinary_wages:, additional_wages:, ytd_additional_wages: 0.0, ytd_ow_subject_to_cpf: 0.0,
              estimated_yearly_ow: 0.0)
  new(ordinary_wages: ordinary_wages, additional_wages: additional_wages,
      ytd_ow_subject_to_cpf: ytd_ow_subject_to_cpf, ytd_additional_wages: ytd_additional_wages,
      estimated_yearly_ow: estimated_yearly_ow).calculate
end
new(ordinary_wages:, additional_wages:, ytd_additional_wages: 0.0, ytd_ow_subject_to_cpf: 0.0, estimated_yearly_ow: 0.0) click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 40
def initialize(ordinary_wages:, additional_wages:, ytd_additional_wages: 0.0, ytd_ow_subject_to_cpf: 0.0,
               estimated_yearly_ow: 0.0)
  @ordinary_wages = ordinary_wages
  @ytd_ow_subject_to_cpf = ytd_ow_subject_to_cpf
  @ytd_additional_wages = ytd_additional_wages
  @estimated_yearly_ow = estimated_yearly_ow
  @additional_wages = additional_wages
  clip_additional_wages_based_on_ceiling()
end

Private Class Methods

required_age_group() click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 33
def required_age_group
  # :nocov:
  raise NotImplementedError, "sub classes needs to implement .required_age_group"
  # :nocov:
end

Public Instance Methods

calculate() click to toggle source

@return [Hash] returns the total, employee, employer amounts for the CPF contribution

# File lib/singapore_cpf_calculator/base_calculator.rb, line 51
def calculate
  CPFContribution.new total: total_contribution,
                      employee: employee_contribution,
                      aw_subject_to_cpf: additional_wages,
                      ow_subject_to_cpf: capped_ordinary_wages
end

Private Instance Methods

additional_wage_ceiling() click to toggle source

generally applies only to spr3 and citizens

# File lib/singapore_cpf_calculator/base_calculator.rb, line 125
def additional_wage_ceiling
  nil
end
adjustment_rate() click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 148
def adjustment_rate
  # :nocov:
  raise NotImplementedError, "sub classes needs to implement #adjustment_rate"
  # :nocov:
end
calculated_employee_contribution() click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 113
def calculated_employee_contribution
  case
  when total_wages < d("500.0000")
    d("0.0")
  when total_wages < d("750.0000")
    (d(adjustment_rate) * (total_wages - d("500.00")))
  else # >= $750
    (d(ec_rate) * capped_ordinary_wages) + (d(ec_rate) * additional_wages)
  end
end
calculated_remaining_wage_ceiling() click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 90
def calculated_remaining_wage_ceiling
  max_remaining = additional_wage_ceiling - ytd_ow_subject_to_cpf - capped_ordinary_wages - ytd_additional_wages
  [max_remaining, d('0.0')].max
end
calculated_total_contribution() click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 100
def calculated_total_contribution
  case
  when total_wages <= d("50.00")
    d("0.0")
  when total_wages <= d("500.00")
    d(tc_rate_1) * total_wages
  when total_wages < d("750.0000")
    (d(tc_rate_1) * total_wages) + (d(adjustment_rate) * (total_wages - d("500.00")))
  else # >= $750
    (d(tc_rate_2) * capped_ordinary_wages) + (d(tc_rate_2) * additional_wages)
  end
end
capped_ordinary_wages() click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 154
def capped_ordinary_wages
  # :nocov:
  raise NotImplementedError, "sub classes needs to implement #capped_ordinary_wages"
  # :nocov:
end
clip_additional_wages_based_on_ceiling() click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 84
def clip_additional_wages_based_on_ceiling
  unless additional_wage_ceiling.blank?
    @additional_wages = [additional_wages, calculated_remaining_wage_ceiling, estimated_remaining_wage_ceiling].min
  end
end
d(val) click to toggle source

precision helper

# File lib/singapore_cpf_calculator/base_calculator.rb, line 161
def d(val)
  BigDecimal(val)
end
ec_rate() click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 142
def ec_rate
  # :nocov:
  raise NotImplementedError, "sub classes needs to implement #ec_rate"
  # :nocov:
end
employee_contribution() click to toggle source

Steps to compute CPF contribution:

a. Compute the total CPF contribution (rounded to the nearest dollar). An amount of 50 cents
   should be regarded as an additional dollar.
b. Compute the employee’s share of CPF contribution (cents should be dropped)
c. Employer’s share = Total contribution – Employee’s share
# File lib/singapore_cpf_calculator/base_calculator.rb, line 72
def employee_contribution
  @employee_contribution ||= calculated_employee_contribution.round(0, :truncate)
end
employer_contribution() click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 76
def employer_contribution
  @employer_contribution ||= total_contribution - employee_contribution
end
estimated_remaining_wage_ceiling() click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 95
def estimated_remaining_wage_ceiling
  estimated_remaining = additional_wage_ceiling - estimated_yearly_ow - ytd_additional_wages
  [estimated_remaining, d('0.0')].max
end
tc_rate_1() click to toggle source

TC Rate 1 is

# File lib/singapore_cpf_calculator/base_calculator.rb, line 130
def tc_rate_1
  # :nocov:
  raise NotImplementedError, "sub classes needs to implement #tc_rate_1"
  # :nocov:
end
tc_rate_2() click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 136
def tc_rate_2
  # :nocov:
  raise NotImplementedError, "sub classes needs to implement #tc_rate_2"
  # :nocov:
end
total_contribution() click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 63
def total_contribution
  @total_contribution ||= calculated_total_contribution.round(0, :half_up)
end
total_wages() click to toggle source
# File lib/singapore_cpf_calculator/base_calculator.rb, line 80
def total_wages
  ordinary_wages + additional_wages
end