-
1
require 'piawe/version'
-
1
require 'piawe/rule_set'
-
1
require 'piawe/file_parser'
-
1
require 'role_playing'
-
1
require 'date'
-
1
require 'bigdecimal'
-
-
# Class to encapsulate PIAWE report generation
-
1
class Piawe
-
-
1
include RolePlaying::Context
-
-
# Create a new Piawe instance to generate reports at varying dates for a particular set of people and rules
-
#
-
# ==== Parameters
-
#
-
# * +people_array+ - An array of people hashes
-
#
-
# * +rules_array+ - An array of rule hashes
-
#
-
# ==== People Hash
-
#
-
# A people hash it a Ruby hash that has has the following format:
-
#
-
# {"people": [
-
# {"name": "Ebony Boycott", "hourlyRate": 75.0030, "overtimeRate": 150.0000, "normalHours": 35.0, "overtimeHours": 7.3, "injuryDate": "2016/05/01" },
-
# {"name": "Geoff Rainford-Brent", "hourlyRate": 30.1234, "overtimeRate": 60.3456, "normalHours": 25.0, "overtimeHours": 10.7, "injuryDate": "2016/08/04" },
-
# {"name": "Meg Gillespie", "hourlyRate": 50.0000, "overtimeRate": 100.0000, "normalHours": 37.5, "overtimeHours": 0.0, "injuryDate": "2015/12/31" },
-
# {"name": "Jason Lanning", "hourlyRate": 40.0055, "overtimeRate": 90.9876, "normalHours": 40.0, "overtimeHours": 12.4, "injuryDate": "2013/01/01" }
-
# ]}
-
#
-
# * name - The person's name.
-
#
-
# * hourlyRate - The person's hourly rate of pay, calculated according to PIAWE rules.
-
#
-
# * overtimeRate - The person's overtime rate of pay, calculated according to PIAWE rules.
-
#
-
# * normalHours - The person's normal weekly hours, calculated according to PIAWE rules.
-
#
-
# * overtimeHours - The person's normal weekly overtime hours, calculated according to PIAWE rules.
-
#
-
# * injuryDate - The date of the injury that caused the person to cease work.
-
#
-
# ==== Rule Hash
-
#
-
# A rule hash it a Ruby hash that has has the following format:
-
#
-
# {"rules":[
-
# {"applicableWeeks": "1-26", "percentagePayable": 90, "overtimeIncluded": true},
-
# {"applicableWeeks": "27-52", "percentagePayable": 80, "overtimeIncluded": true},
-
# {"applicableWeeks": "53-79", "percentagePayable": 70, "overtimeIncluded": true},
-
# {"applicableWeeks": "80-104", "percentagePayable": 60, "overtimeIncluded": false},
-
# {"applicableWeeks": "105+", "percentagePayable": 10, "overtimeIncluded": false}
-
# ]}
-
#
-
# * applicableWeeks - A String that indicates the range of injury weeks during which the rule applies - Week 1 starts at the day of the injury, and Week 2 starts on the 7th day after the injury, and so on. It can have two formats: either a start week and end week joined by a dash, or a start week followed by a plus sign, which indicates the rule should apply to all later weeks as well. The first rule must have a start week of 1, the last rule must use the plus sign syntax, and all intervening rules must have a start week that is one greater than the end week of the preceeding rule.
-
#
-
# * percentagePayable - A Numeric that indicates the percentage of Average Weekly Earnings that are paid when this rule applies.
-
#
-
# * overtimeIncluded - A TrueClass or FalseClass that indicates whether overtime earnings should be considered part of Average Weekly Earnings when this rule applies.
-
1
def initialize( people_array, rules_array )
-
45
@rules = Piawe::RuleSet.new rules_array
-
225
@people = people_array.map { |person_hash| Person.played_by(person_hash) }
-
end # initialize
-
-
-
# Generate a PIAWE report (Ruby Hash format) for the people and rules this Piawe instance encapsulates, as at the specified report date.
-
#
-
# ==== Parameters
-
#
-
# * +report_date+ - The date for which the report should be generated. Defaults to the current date
-
#
-
1
def report( report_date=Date.today )
-
{
-
report_date: report_date.strftime("%Y/%m/%d"),
-
168
report_lines: @people.map { |person| @rules.report_line(person, report_date) }
-
42
}
-
end # method report
-
-
-
# role to be applied to a person hash
-
# This role provides derivided fields that present a hash as a Person object
-
1
role :Person do
-
-
1
def weeks_since_injury(report_date) # :nodoc:
-
686
@weeks_since_injury ||= ( report_date - injury_date) / 7
-
end
-
-
-
1
def injury_date # :nodoc:
-
@injury_date ||= (
-
177
self.has_key?("injuryDate") || (raise ArgumentError, "person_hash does not have a key of injuryDate: #{self.inspect}")
-
176
/^\d{4}\/\d{2}\/\d{2}$/ =~ self["injuryDate"] || (raise ArgumentError, "injury date of #{self["injuryDate"]} is not in yyyy/mm/dd format: #{self.inspect}")
-
175
result = nil
-
175
begin
-
175
result = Date.parse self["injuryDate"]
-
rescue ArgumentError => ex
-
1
raise ArgumentError, "person_hash has an invalidly formatted injuryDate key: #{self.inspect} }"
-
end
-
174
result <= Date.today || (raise ArgumentError, "person_hash has an injuryDate value that is in the future: #{self.inspect}")
-
174
result
-
177
)
-
end
-
-
-
1
def name # :nodoc:
-
176
self.has_key?("name") || (raise ArgumentError, "person_hash does not have a key of name: #{self.inspect}")
-
175
self["name"] || (raise ArgumentError, "person_hash has a nil value for name key: #{self.inspect}")
-
173
self["name"]
-
end
-
-
-
1
def hourly_rate # :nodoc:
-
350
get_decimal "hourlyRate"
-
end
-
-
-
1
def overtime_rate # :nodoc:
-
304
get_decimal "overtimeRate"
-
end
-
-
-
1
def normal_hours # :nodoc:
-
350
get_decimal "normalHours"
-
end
-
-
-
1
def overtime_hours # :nodoc:
-
304
get_decimal "overtimeHours"
-
end
-
-
-
1
def get_decimal(key) # :nodoc:
-
1308
self.has_key?(key) || (raise ArgumentError, "person_hash does not have a key of #{key}: #{self.inspect}")
-
1304
self[key].is_a?(Numeric) || (raise ArgumentError, "person_hash has a non-numeric value for #{key} key: #{self.inspect}")
-
1296
BigDecimal.new self[key], 15
-
end
-
-
-
end
-
-
-
end # class Piawe
-
# Class to read people and rules files and convert them into a PIAWE report
-
1
class Piawe::FileParser
-
-
# Create a new FileParser to generate a PIAWE report from files
-
#
-
#
-
# ==== Parameters
-
#
-
# * +people_file_name+ - Fully qualified path of the people file
-
# * +rules_file_name+ - Fully qualified path of the rules file
-
# * +report_date_string+ - Report date to use, in YYYY/MM/DD format
-
#
-
1
def initialize( people_file_name, rules_file_name, report_date_string )
-
11
@people_file_name = people_file_name
-
11
@rules_file_name = rules_file_name
-
11
@report_date_string = report_date_string
-
end
-
-
# Generate a JSON hash containing the PIAWE report
-
1
def report
-
11
JSON.pretty_generate( { piawe_report: Piawe.new( people_array, rules_array ).report( report_date ) } )
-
end
-
-
1
private
-
-
1
def people_array
-
11
people_hash["people"] || (raise ArgumentError, "people hash did not contain a people key!")
-
end
-
-
-
1
def people_hash
-
11
JSON.parse people_file.read
-
end
-
-
-
1
def people_file
-
11
File.exist?(@people_file_name) ? File.new(@people_file_name) : (raise ArgumentError, "Could not find file #{@people_file_name}")
-
end
-
-
-
1
def rules_array
-
9
rules_hash["rules"] || (raise ArgumentError, "rules hash did not contain a rules key")
-
end
-
-
-
1
def rules_hash
-
9
JSON.parse rules_file.read
-
end
-
-
-
1
def rules_file
-
9
File.exist?(@rules_file_name) ? File.new(@rules_file_name) : (raise ArgumentError, "Could not find file #{@rules_file_name}")
-
end
-
-
-
1
def report_date
-
7
/^(?<yyyy>\d{4})\/(?<mm>\d{2})\/(?<dd>\d{2})$/ =~ @report_date_string || (raise ArgumentError, "report_date_string was not in YYYY/MM/DD format")
-
6
result = nil
-
6
begin
-
6
result = Date.new(yyyy.to_i, mm.to_i, dd.to_i)
-
rescue ArgumentError => ex
-
2
raise ArgumentError, "report_date_string does not represent a valid date: #{@report_date_string}"
-
end
-
4
result
-
end
-
-
end
-
1
require 'bigdecimal'
-
1
require 'role_playing'
-
-
# Class to encapsulate a set of PIAWE payment rules
-
1
class Piawe::RuleSet
-
-
1
include RolePlaying::Context
-
-
-
# Create a new RuleSet to represent the rules contained in a rules array
-
# The primary responsibiltiy of this class is to delegate rendering of a report line
-
# to the appropriate Rule
-
#
-
# ==== Parameters
-
#
-
# * +rules_array+ - An array of rule hashes
-
#
-
# ==== Rule Hash
-
#
-
# A rule hash it a Ruby hash that has has the following format:
-
#
-
# {"rules":[
-
# {"applicableWeeks": "1-26", "percentagePayable": 90, "overtimeIncluded": true},
-
# {"applicableWeeks": "27-52", "percentagePayable": 80, "overtimeIncluded": true},
-
# {"applicableWeeks": "53-79", "percentagePayable": 70, "overtimeIncluded": true},
-
# {"applicableWeeks": "80-104", "percentagePayable": 60, "overtimeIncluded": false},
-
# {"applicableWeeks": "105+", "percentagePayable": 10, "overtimeIncluded": false}
-
# ]}
-
#
-
# * applicableWeeks - A String that indicates the range of injury weeks during which the rule applies - Week 1 starts at the day of the injury, and Week 2 starts on the 7th day after the injury, and so on. It can have two formats: either a start week and end week joined by a dash, or a start week followed by a plus sign, which indicates the rule should apply to all later weeks as well. The first rule must have a start week of 1, the last rule must use the plus sign syntax, and all intervening rules must have a start week that is one greater than the end week of the preceeding rule.
-
#
-
# * percentagePayable - A Numeric that indicates the percentage of Average Weekly Earnings that are paid when this rule applies.
-
#
-
# * overtimeIncluded - A TrueClass or FalseClass that indicates whether overtime earnings should be considered part of Average Weekly Earnings when this rule applies.
-
1
def initialize(rules_array)
-
59
rules_array && rules_array.is_a?(Array) || (raise ArgumentError, "rules array is required - got #{rules_array.inspect}")
-
58
rules_array.size > 0 || (raise ArgumentError, "rules array must contain at least one entry")
-
57
rules_array.each do |rule_hash|
-
273
add(Rule.played_by rule_hash)
-
end
-
53
rules[-1].end_week.nil? || (raise ArgumentError, "last rule must have a terminating + sign")
-
end
-
-
-
# Based on the included Rules, generate a report line for a given person at a given report date
-
# by delegating to matching Rule objects
-
#
-
#
-
# ==== Parameters
-
#
-
# * +person+ - The Piawe::Person for whom the report line should be generated
-
#
-
# * +report_date+ - The Date for which the report line should be generated
-
#
-
1
def report_line(person, report_date)
-
170
rules.each do |rule|
-
513
return rule.report_line( person, report_date ) if rule.matches?( person.weeks_since_injury( report_date ) )
-
end # each rule
-
# this should not be possible - but putting this here defensively...
-
raise "APPLICATION BUG - A RuleSet EXISTS THAT DOES NOT COVER ALL POSSIBLE DATES!! (Date was #{report_date.strftime('%Y/%m/%d')}, person was #{person.inspect})"
-
end # method payment_report
-
-
-
-
-
-
1
private
-
-
-
1
def add(rule)
-
# check consistency of rule dates
-
273
(rule.end_week.nil? || rule.end_week > rule.start_week) || (raise ArgumentError, "rule #{rules.size + 1} has an end week of #{rule.end_week} that is not later than it's start week of #{rule.start_week}")
-
273
if rules[-1] # we have existing rules, check we are consistent
-
216
rules[-1].end_week || (raise ArgumentError, "rule #{rules.size} has a terminating + sign, and should have been the last rule, however there was a subsequent rule: #{rule.inspect}")
-
215
rule.start_week == rules[-1].end_week + 1 || (raise ArgumentError, "rule #{rules.size} ends at week #{rules[-1].end_week} but rule #{rules.size + 1} starts at week #{rule.start_week} - each rule should start one week after the prior rule ends")
-
else # this should be the first rule - check it's start date
-
57
1 == rule.start_week || (raise ArgumentError, "rule 1 should start at week 1, but starts at week #{rule.start_week}")
-
end # if we have existing rules
-
269
rules << rule
-
end # method add
-
-
-
1
def rules
-
1208
@rules ||= []
-
end
-
-
-
-
-
# role to be added to a rule hash
-
# The primary responsibility of this Role is to render a report line for a person and report date
-
1
role :Rule do
-
-
1
def start_week
-
@start_week ||= (
-
280
/(?<starting_week>\d+)[+-]/ =~ applicable_weeks
-
278
starting_week.to_i
-
1020
)
-
end
-
-
-
1
def end_week
-
@end_week ||= if /\+$/ =~ applicable_weeks
-
150
nil
-
else
-
225
( /\d*\-(?<ending_week>\d+)$/ =~ applicable_weeks ) || (raise ArgumentError, "invalid applicableWeeks value: #{applicable_weeks}")
-
225
ending_week.to_i
-
1978
end # if trailing + sign
-
end
-
-
-
1
def applicable_weeks
-
@applicable_weeks ||= (
-
283
self.has_key?("applicableWeeks") || (raise ArgumentError, "rule hash did not have an applicableWeeks key: #{self.inspect}")
-
281
/(^\d+\-\d+$)|(^\d+\+$)/ =~ self["applicableWeeks"] || (raise ArgumentError, "applicableWeeks key is not in valid format")
-
279
self["applicableWeeks"]
-
882
)
-
end
-
-
-
1
def percentage_payable
-
354
self.has_key?("percentagePayable") || (raise ArgumentError, "rule_hash does not have a percentagePayable key: #{self.inspect}")
-
353
self["percentagePayable"].is_a?(Numeric) || (raise ArgumentError, "rule_hash has a non-numeric value for percentagePayable key: #{self['percentagePayable']}")
-
352
BigDecimal.new self["percentagePayable"]
-
end
-
-
-
1
def overtime_included
-
354
self.has_key?("overtimeIncluded") || (raise ArgumentError, "rule_hash does not have an overtimeIncluded key: #{self.inspect}")
-
353
( self["overtimeIncluded"].is_a?(TrueClass) || self["overtimeIncluded"].is_a?(FalseClass) ) || (raise ArgumentError, "overtimeIncluded value was not a boolean true or false - value was #{ self['overtimeIncluded'] }" )
-
352
self["overtimeIncluded"]
-
end
-
-
# The weeks_since_injury value is interpreted as a statement of which injury week we
-
# are currently in - the first week commencing at the date of the injury, the second week
-
# commencing 7 days after that date etc.
-
1
def matches?(weeks_since_injury)
-
517
weeks_since_injury >= (start_week - 1).to_f && # it's after the prior week, i.e. when injured, you are immediately in the first
-
# week of injury
-
(
-
end_week.nil? ||
-
517
weeks_since_injury < end_week.to_f # it's before the end_week number - i.e. exactly 1 week after an injury,
-
# you are now into the second week of injury
-
517
)
-
end
-
-
-
1
def pay_for_this_week( person )
-
(
-
(
-
person.normal_hours * person.hourly_rate + (
-
174
overtime_included ? person.overtime_hours * person.overtime_rate : 0
-
174
)
-
348
) * ( percentage_payable / 100 )
-
174
).round(2)
-
end
-
-
-
1
def report_line(person, report_date)
-
{
-
name: person.name,
-
pay_for_this_week: sprintf( "%.2f", pay_for_this_week( person ) ),
-
weeks_since_injury: sprintf( "%.2f", person.weeks_since_injury( report_date ) ),
-
hourly_rate: sprintf( "%.6f", person.hourly_rate ),
-
overtime_rate: sprintf( "%.6f", person.overtime_rate ),
-
normal_hours: sprintf( "%.2f", person.normal_hours ),
-
overtime_hours: sprintf( "%.2f", person.overtime_hours ),
-
percentage_payable: sprintf( "%.2f", percentage_payable ),
-
overtime_included: overtime_included.to_s
-
172
}
-
end # method report_line
-
-
end # role Rule
-
-
-
-
end # class Piawe::RuleSet
-
-
-
-
1
class Piawe
-
1
VERSION = "0.1.1"
-
end
-
1
require 'spec_helper'
-
-
-
1
describe Piawe::Person do
-
-
1
let (:valid) do
-
7
Piawe::Person.played_by ( {
-
"name" => "Ebony Boycott",
-
"hourlyRate" => 75.0030,
-
"overtimeRate" => 150.0000,
-
"normalHours" => 35.0,
-
"overtimeHours" => 7.3,
-
"injuryDate" => "2016/05/01"
-
} )
-
end
-
-
1
let (:invalid_format) do
-
6
Piawe::Person.played_by ( {
-
"name" => nil,
-
"hourlyRate" => "foo",
-
"overtimeRate" => "bar",
-
"normalHours" => "baz",
-
"overtimeHours" => "twelve",
-
"injuryDate" => "yesterday"
-
} )
-
end
-
-
1
let (:invalid_values) do
-
6
Piawe::Person.played_by ( {
-
"name" => nil,
-
"hourlyRate" => nil,
-
"overtimeRate" => nil,
-
"normalHours" => nil,
-
"overtimeHours" => nil,
-
"injuryDate" => "2016/01/50"
-
} )
-
end
-
-
1
let (:empty) do
-
6
Piawe::Person.played_by ( Hash.new )
-
end
-
-
-
1
context "given a valid person_hash" do
-
2
it "has the correct name" do expect(valid.name ).to eql("Ebony Boycott" ); end
-
2
it "has the correct hourly rate" do expect(valid.hourly_rate ).to eql( 75.003 ); end
-
2
it "has the correct overtime rate" do expect(valid.overtime_rate ).to eql( 150.0 ); end
-
2
it "has the correct normal hours" do expect(valid.normal_hours ).to eql( 35.0 ); end
-
2
it "has the correct overtime hours" do expect(valid.overtime_hours ).to eql( 7.3 ); end
-
2
it "has the correct injury date" do expect(valid.injury_date ).to eql( Date.new(2016, 05, 01) ); end
-
2
it "calculates the correct injury weeks" do expect( valid.weeks_since_injury( Date.new(2016, 05, 29) ).to_f ).to eql(4.0) ; end
-
end # given a valid person_hash
-
-
-
1
context "given a person_hash with invalid format" do
-
3
it "rejects the name value" do expect {invalid_format.name }.to raise_error( ArgumentError, /person_hash has a nil value for name key: #{ invalid_format.inspect}/ ); end
-
3
it "rejects the hourly rate value" do expect {invalid_format.hourly_rate }.to raise_error( ArgumentError, /person_hash has a non-numeric value for hourlyRate key: #{ invalid_format.inspect}/ ); end
-
3
it "rejects the overtime rate value" do expect {invalid_format.overtime_rate }.to raise_error( ArgumentError, /person_hash has a non-numeric value for overtimeRate key: #{ invalid_format.inspect}/ ); end
-
3
it "rejects the normal hours value" do expect {invalid_format.normal_hours }.to raise_error( ArgumentError, /person_hash has a non-numeric value for normalHours key: #{ invalid_format.inspect}/ ); end
-
3
it "rejects the overtime hours value" do expect {invalid_format.overtime_hours }.to raise_error( ArgumentError, /person_hash has a non-numeric value for overtimeHours key: #{ invalid_format.inspect}/ ); end
-
3
it "rejects the injury date value" do expect {invalid_format.injury_date }.to raise_error( ArgumentError, /injury date of yesterday is not in yyyy\/mm\/dd format: #{ invalid_format.inspect}/ ); end
-
end
-
-
-
1
context "given a person_hash with invalid_values" do
-
3
it "rejects the name value" do expect {invalid_values.name }.to raise_error( ArgumentError, /person_hash has a nil value for name key: #{ invalid_values.inspect}/ ); end
-
3
it "rejects the hourly rate value" do expect {invalid_values.hourly_rate }.to raise_error( ArgumentError, /person_hash has a non-numeric value for hourlyRate key: #{ invalid_values.inspect}/ ); end
-
3
it "rejects the overtime rate value" do expect {invalid_values.overtime_rate }.to raise_error( ArgumentError, /person_hash has a non-numeric value for overtimeRate key: #{ invalid_values.inspect}/ ); end
-
3
it "rejects the normal hours value" do expect {invalid_values.normal_hours }.to raise_error( ArgumentError, /person_hash has a non-numeric value for normalHours key: #{ invalid_values.inspect}/ ); end
-
3
it "rejects the overtime hours value" do expect {invalid_values.overtime_hours }.to raise_error( ArgumentError, /person_hash has a non-numeric value for overtimeHours key: #{ invalid_values.inspect}/ ); end
-
3
it "rejects the injury date value" do expect {invalid_values.injury_date }.to raise_error( ArgumentError, /person_hash has an invalidly formatted injuryDate key: #{ invalid_values.inspect}/ ); end
-
end
-
-
-
1
context "given an empty person_hash" do
-
3
it "rejects the name value" do expect {empty.name }.to raise_error( ArgumentError, /person_hash does not have a key of name: #{ empty.inspect}/ ); end
-
3
it "rejects the hourly rate value" do expect {empty.hourly_rate }.to raise_error( ArgumentError, /person_hash does not have a key of hourlyRate: #{ empty.inspect}/ ); end
-
3
it "rejects the overtime rate value" do expect {empty.overtime_rate }.to raise_error( ArgumentError, /person_hash does not have a key of overtimeRate: #{ empty.inspect}/ ); end
-
3
it "rejects the normal hours value" do expect {empty.normal_hours }.to raise_error( ArgumentError, /person_hash does not have a key of normalHours: #{ empty.inspect}/ ); end
-
3
it "rejects the overtime hours value" do expect {empty.overtime_hours }.to raise_error( ArgumentError, /person_hash does not have a key of overtimeHours: #{ empty.inspect}/ ); end
-
3
it "rejects the injury date value" do expect {empty.injury_date }.to raise_error( ArgumentError, /person_hash does not have a key of injuryDate: #{ empty.inspect}/ ); end
-
end
-
-
end # describe Piawe::Person
-
-
-
-
-
1
describe Piawe do
-
-
1
let (:report_date) do
-
38
Date.new(2017, 3, 1)
-
end
-
-
1
let (:people_array) do
-
[
-
38
{"name" => "Ebony Boycott", "hourlyRate" => 75.0030, "overtimeRate" => 150.0000, "normalHours" => 35.0, "overtimeHours" => 7.3, "injuryDate" => "2016/05/01" }, # 43.42857142857143
-
{"name" => "Geoff Rainford-Brent", "hourlyRate" => 30.1234, "overtimeRate" => 60.3456, "normalHours" => 25.0, "overtimeHours" => 10.7, "injuryDate" => "2016/08/04" }, # 29.857142857142858
-
{"name" => "Meg Gillespie", "hourlyRate" => 50.0000, "overtimeRate" => 100.0000, "normalHours" => 37.5, "overtimeHours" => 0.0, "injuryDate" => "2015/12/31" }, # 60.857142857142854
-
{"name" => "Jason Lanning", "hourlyRate" => 40.0055, "overtimeRate" => 90.9876, "normalHours" => 40.0, "overtimeHours" => 12.4, "injuryDate" => "2013/01/01" } # 217.14285714285714
-
]
-
end
-
-
-
1
let (:rules_array) do
-
[
-
38
{"applicableWeeks" => "1-26", "percentagePayable" => 90, "overtimeIncluded" => true},
-
{"applicableWeeks" => "27-52", "percentagePayable" => 80, "overtimeIncluded" => true},
-
{"applicableWeeks" => "53-79", "percentagePayable" => 70, "overtimeIncluded" => true},
-
{"applicableWeeks" => "80-104", "percentagePayable" => 60, "overtimeIncluded" => false},
-
{"applicableWeeks" => "105+", "percentagePayable" => 10, "overtimeIncluded" => false}
-
]
-
end
-
-
-
1
let (:piawe) do
-
38
Piawe.new(people_array, rules_array)
-
end
-
-
-
1
let (:report) do
-
38
piawe.report(report_date)
-
end
-
-
1
it 'has a version number' do
-
1
expect(Piawe::VERSION).not_to be nil
-
end
-
-
-
1
context "given valid arguments, the report" do
-
2
it "should have the correct date" do expect( report[:report_date] ).to eql( report_date.strftime("%Y/%m/%d")); end
-
2
it "should have the correct number of lines" do expect( report[:report_lines].size ).to eql( 4 ); end
-
#
-
2
it "should have the correct name for person 1" do expect( report[:report_lines][0][:name ] ).to eql(people_array[0]["name"]); end
-
2
it "should have the correct pay for person 1" do expect( report[:report_lines][0][:pay_for_this_week ] ).to eql( "2976.08" ); end
-
2
it "should have the correct weeks_since_injury for person 1" do expect( report[:report_lines][0][:weeks_since_injury ] ).to eql( "43.43" ); end
-
2
it "should have the correct hourly rate for person 1" do expect( report[:report_lines][0][:hourly_rate ] ).to eql( "75.003000" ); end
-
2
it "should have the correct overtime rate for person 1" do expect( report[:report_lines][0][:overtime_rate ] ).to eql( "150.000000"); end
-
2
it "should have the correct normal hours for person 1" do expect( report[:report_lines][0][:normal_hours ] ).to eql( "35.00" ); end
-
2
it "should have the correct overtime hours for person 1" do expect( report[:report_lines][0][:overtime_hours ] ).to eql( "7.30" ); end
-
2
it "should have the correct percentage_payable for person 1" do expect( report[:report_lines][0][:percentage_payable ] ).to eql( "80.00" ); end
-
2
it "should have the correct overtime_included for person 1" do expect( report[:report_lines][0][:overtime_included ] ).to eql( "true" ); end
-
#
-
2
it "should have the correct name for person 2" do expect( report[:report_lines][1][:name ] ).to eql(people_array[1]["name"]); end
-
2
it "should have the correct pay for person 2" do expect( report[:report_lines][1][:pay_for_this_week ] ).to eql( "1119.03" ); end
-
2
it "should have the correct weeks_since_injury for person 2" do expect( report[:report_lines][1][:weeks_since_injury ] ).to eql( "29.86" ); end
-
2
it "should have the correct hourly rate for person 2" do expect( report[:report_lines][1][:hourly_rate ] ).to eql( "30.123400" ); end
-
2
it "should have the correct overtime rate for person 2" do expect( report[:report_lines][1][:overtime_rate ] ).to eql( "60.345600" ); end
-
2
it "should have the correct normal hours for person 2" do expect( report[:report_lines][1][:normal_hours ] ).to eql( "25.00" ); end
-
2
it "should have the correct overtime hours for person 2" do expect( report[:report_lines][1][:overtime_hours ] ).to eql( "10.70" ); end
-
2
it "should have the correct percentage_payable for person 2" do expect( report[:report_lines][1][:percentage_payable ] ).to eql( "80.00" ); end
-
2
it "should have the correct overtime_included for person 2" do expect( report[:report_lines][1][:overtime_included ] ).to eql( "true" ); end
-
#
-
2
it "should have the correct name for person 3" do expect( report[:report_lines][2][:name ] ).to eql(people_array[2]["name"]); end
-
2
it "should have the correct pay for person 3" do expect( report[:report_lines][2][:pay_for_this_week ] ).to eql( "1312.50" ); end
-
2
it "should have the correct weeks_since_injury for person 3" do expect( report[:report_lines][2][:weeks_since_injury ] ).to eql( "60.86" ); end
-
2
it "should have the correct hourly rate for person 3" do expect( report[:report_lines][2][:hourly_rate ] ).to eql( "50.000000" ); end
-
2
it "should have the correct overtime rate for person 3" do expect( report[:report_lines][2][:overtime_rate ] ).to eql( "100.000000"); end
-
2
it "should have the correct normal hours for person 3" do expect( report[:report_lines][2][:normal_hours ] ).to eql( "37.50" ); end
-
2
it "should have the correct overtime hours for person 3" do expect( report[:report_lines][2][:overtime_hours ] ).to eql( "0.00" ); end
-
2
it "should have the correct percentage_payable for person 3" do expect( report[:report_lines][2][:percentage_payable ] ).to eql( "70.00" ); end
-
2
it "should have the correct overtime_included for person 3" do expect( report[:report_lines][2][:overtime_included ] ).to eql( "true" ); end
-
#
-
2
it "should have the correct name for person 4" do expect( report[:report_lines][3][:name ] ).to eql(people_array[3]["name"]); end
-
2
it "should have the correct pay for person 4" do expect( report[:report_lines][3][:pay_for_this_week ] ).to eql( "160.02" ); end
-
2
it "should have the correct weeks_since_injury for person 4" do expect( report[:report_lines][3][:weeks_since_injury ] ).to eql( "217.14" ); end
-
2
it "should have the correct hourly rate for person 4" do expect( report[:report_lines][3][:hourly_rate ] ).to eql( "40.005500" ); end
-
2
it "should have the correct overtime rate for person 4" do expect( report[:report_lines][3][:overtime_rate ] ).to eql( "90.987600" ); end
-
2
it "should have the correct normal hours for person 4" do expect( report[:report_lines][3][:normal_hours ] ).to eql( "40.00" ); end
-
2
it "should have the correct overtime hours for person 4" do expect( report[:report_lines][3][:overtime_hours ] ).to eql( "12.40" ); end
-
2
it "should have the correct percentage_payable for person 4" do expect( report[:report_lines][3][:percentage_payable ] ).to eql( "10.00" ); end
-
2
it "should have the correct overtime_included for person 4" do expect( report[:report_lines][3][:overtime_included ] ).to eql( "false" ); end
-
end
-
end # describe Piawe
-
-
-
-
1
require 'spec_helper'
-
-
1
describe Piawe::RuleSet::Rule do
-
-
1
let (:valid) do
-
8
Piawe::RuleSet::Rule.played_by ( {
-
"applicableWeeks" => "1-26",
-
"percentagePayable" => 90,
-
"overtimeIncluded" => true
-
} )
-
end
-
-
1
let (:invalid) do
-
4
Piawe::RuleSet::Rule.played_by ( {
-
"applicableWeeks" => "foo",
-
"percentagePayable" => "bar",
-
"overtimeIncluded" => "baz"
-
} )
-
end
-
-
1
let (:empty) do
-
4
Piawe::RuleSet::Rule.played_by ( Hash.new )
-
end
-
-
1
let (:report_date) do
-
4
Date.new(2017, 3, 1)
-
end
-
-
1
let (:person) do
-
4
Piawe::Person.played_by ( {
-
"name" => "test person",
-
"hourlyRate" => 75.0,
-
"overtimeRate" => 150.0,
-
"normalHours" => 35.0,
-
"overtimeHours" => 5,
-
4
"injuryDate" => (report_date - 10.weeks).strftime("%Y/%m/%d")
-
} )
-
end
-
-
1
let (:overtime_included) do
-
2
Piawe::RuleSet::Rule.played_by ( {
-
"applicableWeeks" => "1-26",
-
"percentagePayable" => 90,
-
"overtimeIncluded" => true
-
} )
-
end
-
-
-
1
let (:overtime_not_included) do
-
2
Piawe::RuleSet::Rule.played_by ( {
-
"applicableWeeks" => "1-26",
-
"percentagePayable" => 60,
-
"overtimeIncluded" => false
-
} )
-
end
-
-
-
1
context "given a valid rule_hash" do
-
2
it "has the correct start week" do expect( valid.start_week ).to eql( 1 ); end
-
2
it "has the correct end week" do expect( valid.end_week ).to eql( 26 ); end
-
2
it "has the correct percentage payable" do expect( valid.percentage_payable ).to eql( 90 ); end
-
2
it "has the correct overtime included" do expect( valid.overtime_included ).to eql( true ); end
-
end
-
-
-
1
context "given an invalid rule_hash" do
-
3
it "rejects the invalid start week value" do expect{ invalid.start_week }.to raise_error( ArgumentError, /applicableWeeks key is not in valid format/ ); end
-
3
it "rejects the invalid end week value" do expect{ invalid.end_week }.to raise_error( ArgumentError, /applicableWeeks key is not in valid format/ ); end
-
3
it "rejects the invalid percentage payable value" do expect{ invalid.percentage_payable }.to raise_error( ArgumentError, /rule_hash has a non-numeric value for percentagePayable key: #{invalid['percentagePayable']}/ ); end
-
3
it "rejects the invalid overtime included value" do expect{ invalid.overtime_included }.to raise_error( ArgumentError, /overtimeIncluded value was not a boolean true or false - value was #{ invalid['overtimeIncluded'] }/); end
-
end
-
-
-
1
context "given an empty rule_hash" do
-
3
it "rejects the missing start week value" do expect{ empty.start_week }.to raise_error( ArgumentError, /rule hash did not have an applicableWeeks key: #{ empty.inspect}/ ); end
-
3
it "rejects the missing end week value" do expect{ empty.end_week }.to raise_error( ArgumentError, /rule hash did not have an applicableWeeks key: #{ empty.inspect}/ ); end
-
3
it "rejects the missing percentage payable value" do expect{ empty.percentage_payable }.to raise_error( ArgumentError, /rule_hash does not have a percentagePayable key: #{empty.inspect}/ ); end
-
3
it "rejects the missing overtime included value" do expect{ empty.overtime_included }.to raise_error( ArgumentError, /rule_hash does not have an overtimeIncluded key: #{empty.inspect}/ ); end
-
end
-
-
1
context "given a matching weeks-since-injury" do
-
2
it "should match" do expect( valid.matches?( 0 ) ).to eql(true); end
-
2
it "should match" do expect( valid.matches?( 10 ) ).to eql(true); end
-
2
it "should match" do expect( valid.matches?( 25.99999999 ) ).to eql(true); end
-
end
-
-
1
context "given a non-matching weeks-since-injury" do
-
2
it "should not match" do expect( valid.matches?( 26 ) ).to eql(false); end
-
end
-
-
-
1
context "given a rule hash with overtime included" do
-
2
it "calculates the correct pay with overtime" do expect( overtime_included.pay_for_this_week( person ) ).to eql( 3037.5 ); end
-
2
it "creates the correct report line" do expect( overtime_included.report_line( person, report_date ).to_s ).to eql( '{:name=>"test person", :pay_for_this_week=>"3037.50", :weeks_since_injury=>"10.00", :hourly_rate=>"75.000000", :overtime_rate=>"150.000000", :normal_hours=>"35.00", :overtime_hours=>"5.00", :percentage_payable=>"90.00", :overtime_included=>"true"}' ); end
-
end
-
-
-
1
context "given a rule hash with overtime not included" do
-
2
it "calculates the correct pay without overtime" do expect( overtime_not_included.pay_for_this_week( person ) ).to eql( 1575.0 ); end
-
2
it "creates the correct report line" do expect( overtime_not_included.report_line(person, report_date ).to_s ).to eql( '{:name=>"test person", :pay_for_this_week=>"1575.00", :weeks_since_injury=>"10.00", :hourly_rate=>"75.000000", :overtime_rate=>"150.000000", :normal_hours=>"35.00", :overtime_hours=>"5.00", :percentage_payable=>"60.00", :overtime_included=>"false"}' ); end
-
end
-
-
-
end # describe Piawe::RuleSet::Rule
-
-
-
-
-
1
describe Piawe::RuleSet, :private do
-
-
1
let (:overlapping_rules_array) do
-
[
-
1
{"applicableWeeks" => "1-26", "percentagePayable" => 90, "overtimeIncluded" => true},
-
{"applicableWeeks" => "26-52", "percentagePayable" => 80, "overtimeIncluded" => true},
-
{"applicableWeeks" => "53-79", "percentagePayable" => 70, "overtimeIncluded" => true},
-
{"applicableWeeks" => "80-104", "percentagePayable" => 60, "overtimeIncluded" => false},
-
{"applicableWeeks" => "104+", "percentagePayable" => 10, "overtimeIncluded" => false}
-
]
-
end
-
-
-
1
let (:gapped_rules_array) do
-
[
-
1
{"applicableWeeks" => "1-26", "percentagePayable" => 90, "overtimeIncluded" => true},
-
{"applicableWeeks" => "28-52", "percentagePayable" => 80, "overtimeIncluded" => true},
-
{"applicableWeeks" => "53-79", "percentagePayable" => 70, "overtimeIncluded" => true},
-
{"applicableWeeks" => "80-104", "percentagePayable" => 60, "overtimeIncluded" => false},
-
{"applicableWeeks" => "105+", "percentagePayable" => 10, "overtimeIncluded" => false}
-
]
-
end
-
-
-
1
let (:early_terminated_rules_array) do
-
[
-
1
{"applicableWeeks" => "1-26", "percentagePayable" => 90, "overtimeIncluded" => true},
-
{"applicableWeeks" => "27-52", "percentagePayable" => 80, "overtimeIncluded" => true},
-
{"applicableWeeks" => "53+", "percentagePayable" => 70, "overtimeIncluded" => true},
-
{"applicableWeeks" => "80-104", "percentagePayable" => 60, "overtimeIncluded" => false},
-
{"applicableWeeks" => "105+", "percentagePayable" => 10, "overtimeIncluded" => false}
-
]
-
end
-
-
-
1
let (:unterminated_rules_array) do
-
[
-
1
{"applicableWeeks" => "1-26", "percentagePayable" => 90, "overtimeIncluded" => true},
-
{"applicableWeeks" => "27-52", "percentagePayable" => 80, "overtimeIncluded" => true},
-
{"applicableWeeks" => "53-79", "percentagePayable" => 70, "overtimeIncluded" => true},
-
{"applicableWeeks" => "80-104", "percentagePayable" => 60, "overtimeIncluded" => false}
-
]
-
end
-
-
-
1
let (:late_starting_rules_array) do
-
[
-
1
{"applicableWeeks" => "10-26", "percentagePayable" => 90, "overtimeIncluded" => true},
-
{"applicableWeeks" => "27-52", "percentagePayable" => 80, "overtimeIncluded" => true},
-
{"applicableWeeks" => "53-79", "percentagePayable" => 70, "overtimeIncluded" => true},
-
{"applicableWeeks" => "80-104", "percentagePayable" => 60, "overtimeIncluded" => false},
-
{"applicableWeeks" => "105+", "percentagePayable" => 10, "overtimeIncluded" => false}
-
]
-
end
-
-
-
1
let (:valid_rules_array) do
-
[
-
7
{"applicableWeeks" => "1-26", "percentagePayable" => 90, "overtimeIncluded" => true},
-
{"applicableWeeks" => "27-52", "percentagePayable" => 80, "overtimeIncluded" => true},
-
{"applicableWeeks" => "53-79", "percentagePayable" => 70, "overtimeIncluded" => true},
-
{"applicableWeeks" => "80-104", "percentagePayable" => 60, "overtimeIncluded" => false},
-
{"applicableWeeks" => "105+", "percentagePayable" => 10, "overtimeIncluded" => false}
-
]
-
end
-
-
-
8
let (:valid) do Piawe::RuleSet.new( valid_rules_array ); end
-
-
1
let (:report_date) do
-
2
Date.new(2017, 3, 1)
-
end
-
-
1
let (:person) do
-
1
Piawe::Person.played_by ( {
-
"name" => "test person",
-
"hourlyRate" => 75.0,
-
"overtimeRate" => 150.0,
-
"normalHours" => 35.0,
-
"overtimeHours" => 5,
-
1
"injuryDate" => (report_date - 100.weeks).strftime("%Y/%m/%d")
-
} )
-
end
-
-
1
let (:person2) do
-
1
Piawe::Person.played_by ( {
-
"name" => "test person 2",
-
"hourlyRate" => 75.0,
-
"overtimeRate" => 150.0,
-
"normalHours" => 35.0,
-
"overtimeHours" => 5,
-
1
"injuryDate" => (report_date - 10000.weeks).strftime("%Y/%m/%d")
-
} )
-
end
-
-
1
context "given an invalid argument" do
-
3
it "should raise an exception" do expect { Piawe::RuleSet.new("foo") }.to raise_error( ArgumentError, /rules array is required - got "foo"/ ); end
-
end
-
-
1
context "given an empty argument" do
-
3
it "should raise an exception" do expect { Piawe::RuleSet.new( Array.new ) }.to raise_error( ArgumentError, /rules array must contain at least one entry/ ); end
-
end
-
-
1
context "given an overlapping rules array" do
-
3
it "should raise an exception" do expect { Piawe::RuleSet.new(overlapping_rules_array) }.to raise_error( ArgumentError, /rule 1 ends at week 26 but rule 2 starts at week 26 - each rule should start one week after the prior rule ends/ ); end
-
end
-
-
1
context "given a gapped rules array" do
-
3
it "should raise an exception" do expect { Piawe::RuleSet.new(gapped_rules_array) }.to raise_error( ArgumentError, /rule 1 ends at week 26 but rule 2 starts at week 28 - each rule should start one week after the prior rule ends/ ); end
-
end
-
-
1
context "given an early-terminating rules array" do
-
3
it "should raise an exception" do expect { Piawe::RuleSet.new(early_terminated_rules_array) }.to raise_error( ArgumentError, /rule 3 has a terminating \+ sign, and should have been the last rule, however there was a subsequent rule: #{early_terminated_rules_array[3].inspect}/ ); end
-
end
-
-
1
context "given an un-terminated rules array" do
-
3
it "should raise an exception" do expect { Piawe::RuleSet.new(unterminated_rules_array) }.to raise_error( ArgumentError, /last rule must have a terminating \+ sign/ ); end
-
end
-
-
1
context "given a late starting rules array" do
-
3
it "should raise an exception" do expect { Piawe::RuleSet.new(late_starting_rules_array) }.to raise_error( ArgumentError, /rule 1 should start at week 1, but starts at week 10/ ); end
-
end
-
-
1
context "given a valid rules array" do
-
2
it "should have the correct size" do expect( valid.rules.size ).to eql(5); end
-
2
it "should have the correct start weeks" do expect( valid.rules.map( &:start_week ) ).to match_array( [ 1, 27, 53, 80, 105 ] ); end
-
2
it "should have the correct end weeks" do expect( valid.rules.map( &:end_week ) ).to match_array( [ 26, 52, 79, 104, nil ] ); end
-
2
it "should have the correct percentage payables" do expect( valid.rules.map( &:percentage_payable ) ).to match_array( [ 90, 80, 70, 60, 10 ] ); end
-
2
it "should have the correct overtime includeds" do expect( valid.rules.map( &:overtime_included ) ).to match_array( [ true, true, true, false, false ] ); end
-
2
it "should return the right early report line" do expect( valid.report_line(person, report_date ).to_s ).to eql( '{:name=>"test person", :pay_for_this_week=>"1575.00", :weeks_since_injury=>"100.00", :hourly_rate=>"75.000000", :overtime_rate=>"150.000000", :normal_hours=>"35.00", :overtime_hours=>"5.00", :percentage_payable=>"60.00", :overtime_included=>"false"}' ); end
-
2
it "should return the right late report line" do expect( valid.report_line(person2, report_date ).to_s ).to eql( '{:name=>"test person 2", :pay_for_this_week=>"262.50", :weeks_since_injury=>"10000.00", :hourly_rate=>"75.000000", :overtime_rate=>"150.000000", :normal_hours=>"35.00", :overtime_hours=>"5.00", :percentage_payable=>"10.00", :overtime_included=>"false"}' ); end
-
end
-
-
-
-
end # describe RuleSet
-
-