Calculating Timesheets

This script is used in the Approval Manager application that comes with WorkflowFirst to calculate the time booked for a work log entry. It uses configurable rules that can be defined and overridden on multiple levels.

// determine the overtime parameters to use...
var #log = ""
var #otrList = NewList()
var #otr = load(&root/StandingData/TimeComputationRules)
add #otr to #otrList
set #log + "Adding global TCR\r\n"
var #rec = load(&path)
if Exists(#rec/Department) then
if Exists(load(GetPath(#rec/Department)/TCR)) then
set #otr = load(GetPath(#rec/Department)/TCR)
add #otr to #otrList
set #log + "Adding department TCR\r\n"
end if
end if
if Exists(#rec/Customer) then
if Exists(load(GetPath(#rec/Customer)/TCR)) then
set #otr = load(#rec/Customer/TCR)
add #otr to #otrList
set #log + "Adding customer TCR\r\n"
end if
end if
if Exists(#rec/Division) then
if Exists(load(GetPath(#rec/Division)/TCR)) then
set #otr = load(#rec/Division/TCR)
add #otr to #otrList
set #log + "Adding division TCR\r\n"
end if
end if
var #wl = load(Parent(&path))
var #user = load(GetPath(#wl/UserRef))
if NotExists(#user) then
error "The user is not set for this work log"
end if

if Exists(load(GetPath(#user)/TCR)) then
set #otr = load(GetPath(#user)/TCR)
add #otr to #otrList
set #log + "Adding user TCR\r\n"
end if
if NotExists(#otr) then
// there are none, so use standard values...
set #otr = new record for &root/StandingData/TimeComputationRules
set #otr/SetRegularHours = true
set #otr/RegHrsStatic = "Fixed"
set #otr/RegHrsStartTime = "8am"
set #otr/RegHrsEndTime = "5pm"
add #otr to #otrList
set #log + "No TCRs found, setting defaults 8 - 5pm\r\n"
end if

var #input = load(&path)
on error prefix "Problem understanding the start or end time"

var #start = SetTime(Date(Now()), #input/StartTime)
var #end = SetTime(Date(Now()), #input/EndTime)
set #log + "Start: " + #input/StartTime + " end: " + #input/EndTime + "\r\n"

// then look up any exceptions...
var #otr = load(&root/Misc/TCRExceptions[User=GetPath(#user), AddTCRExceptionState="Approve", StartDate>=#rec/Date, EndDate<=#rec/Date] with top 1)
if Exists(#otr) then
add load(GetPath(#otr)/TimeComputationRules with top 1) to #otrList
set #log + "Found TCR exception for " + #rec/Date + "\r\n"
end if
// create out final otr...
var #otr = new record for &root/StandingData/TimeComputationRules
loop through #otrList as #thisOTR
if #thisOTR/SetRegularHours then
set #otr/RegHrsStatic = #thisOTR/RegHrsStatic
set #otr/RegHrsStartTime = #thisOTR/RegHrsStartTime
set #otr/RegHrsEndTime = #thisOTR/RegHrsEndTime
end if
if (#thisOTR/SetOvertime) then
set #otr/OverTimeType = #thisOTR/OverTimeType
set #otr/ReqHoursWorked = #thisOTR/ReqHoursWorked
end if
if #thisOTR/SetEndLatePolicy then
set #otr/OverTimeMin = #thisOTR/OverTimeMin
set #otr/OverTimeGran = #thisOTR/OverTimeGran
end if
if #thisOTR/SetLatePolicy then
set #otr/LateTimeGran = #thisOTR/LateTimeGran
set #otr/LateGracePeriod = #thisOTR/LateGracePeriod
set #otr/LateTimes = #thisOTR/LateTimes
end if
if #thisOTR/SetEarlyPolicy then
set #otr/EarlyTimeGran = #thisOTR/EarlyTimeGran
set #otr/EarlyMaxMins = #thisOTR/EarlyMaxMins
set #otr/EarlyGracePeriod = #thisOTR/EarlyGracePeriod
end if
if #thisOTR/SetBreakPolicy then
set #otr/BreakNormal = #thisOTR/BreakNormal
end if
end loop

var #officialStart = SetTime(Date(Now()), #otr/RegHrsStartTime)
var #officialEnd = SetTime(Date(Now()), #otr/RegHrsEndTime)
set #log + "Official times: " + #otr/RegHrsStartTime + " to " + #otr/RegHrsEndTime + "\r\n"

var #minsEarly = 0
// see if we need to adjust the start or end time...
if #start < #officialStart then
set #log + "Clocked-in early\r\n"
// how many minutes back?
var #minsEarly = SecondsBetween(#start, #officialStart) div 60
set #log + " " + #minsEarly + " minutes early.\r\n"
if #minsEarly < #otr/EarlyGracePeriod then
set #log + " Within grace period, so ignored (" + #otr/EarlyGracePeriod + " mins)\r\n"
set #minsEarly = 0
end if
if #otr/EarlyTimeGran > 0 then
set #log + " Applying minimum block of " + #otr/EarlyTimeGran + "\r\n"
var #minsEarly = Trunc(#minsEarly div (#otr/EarlyTimeGran)) * (#otr/EarlyTimeGran)
set #log + " Which becomes " + #minsEarly + " minutes early.\r\n"
end if
end if

var #minsLate = 0

// were they late?
if #start > #officialStart then
set #log + "Clocked-in late\r\n"
// how many minutes late?
var #minsLate = SecondsBetween(#officialStart, #start) div 60
set #log + " " + #minsLate + " minutes late.\r\n"
if #minsLate < #otr/LateGracePeriod then
set #minsLate = 0
set #log + " Within grace period, so ignored.\r\n"
end if
if #otr/LateTimeGran > 0 then
set #log + " Applying minimum block of " + #otr/LateTimeGran + "\r\n"
var #minsLate = Trunc(#minsLate div (#otr/LateTimeGran)) * (#otr/LateTimeGran)
set #log + " Which becomes " + #minsLate + " minutes late.\r\n"
end if
// flag as being late?
if (#minsLate > 0) then
end if
end if

var #minsBeforeEnd = 0

// did they finish early?
if #end < #officialEnd then
set #log + "Clocked-out early\r\n"
// how many minutes early?
var #minsBeforeEnd = SecondsBetween(#end, #officialEnd) div 60
set #log + " " + #minsBeforeEnd + " minutes early.\r\n"
end if

var #minsOT = 0
// did they finish late
if #end > #officialEnd then
set #log + "Clocked-out late\r\n"
// how many minutes late?
var #minsOT = SecondsBetween(#officialEnd, #end) div 60
set #log + " " + #minsOT + " minutes late.\r\n"
if #minsOT < #otr/OverTimeMin then
set #log + " Not enough to warrant overtime.\r\n"
set #minsOT = 0
end if
if #otr/OverTimeGran > 0 then
set #log + " Applying minimum block of " + #otr/OverTimeGran + " minutes.\r\n"
var #minsOT = Trunc(#minsOT div (#otr/OverTimeGran)) * (#otr/OverTimeGran)
set #log + " Which becomes " + #minsOT + " minutes of overtime.\r\n"
end if
end if
// so now we have #officialStart, #officialEnd, #minsBeforeEnd, #minsOT, #minsLate and #minsEarly
var #reg = ((SecondsBetween(AddMinutes(#officialStart, #minsLate), AddMinutes(#officialEnd, 0 - #minsBeforeEnd)) div 60) div 60)
// and overtime
var #start = SetTime(Date(Now()), #input/BreakStart)
var #end = SetTime(Date(Now()), #input/BreakEnd)
var #break = ((SecondsBetween(#start, #end) div 60) div 60)
set #log + "Break: " + #break + " hour(s) break.\r\n"
set #input/BreakHours = #break
if #break > (#otr/BreakNormal div 60) then
set #log + " Over official break time.\r\n"
set #input/BreakExcess = (#break * 60) - #otr/BreakNormal
end if
if #break < (#otr/BreakNormal div 60) then
set #log + " Under official break time.\r\n"
set #log + "Using official break time of " + (#otr/BreakNormal div 60) + " hour(s).\r\n"
set #break = (#otr/BreakNormal div 60)
end if

var #hoursWorked = #reg - #break
set #log + "Total non-overtime worked: " + #hoursWorked + " hours.\r\n"

var #overtime = 0
if #otr/OverTimeType="ExcessHours" then
set #log + "Overtime based on excess hours.\r\n"
var #actHours = ((SecondsBetween(#start, #end) div 60) div 60)
var #rstart = SetTime(Date(Now()), #otr/RegHrsStartTime)
var #rend = SetTime(Date(Now()), #otr/RegHrsEndTime)
var #standardHours = ((SecondsBetween(#rstart, #rend) div 60) div 60)
var #overtime = (#actHours - #break - #standardHours)
// set granularity
if #otr/OverTimeGran>0 then
set #overtime = Trunc(#overtime div (#otr/OverTimeGran div 60)) * (#otr/OverTimeGran div 60)
end if
end if
if #otr/OverTimeType="OutsideRegHours" then
set #log + "Overtime based on hours outside of official hours.\r\n"
set #overtime = #minsOT + #minsEarly
set #log + "Total Overtime: " + (#overtime div 60) + " hours\r\n"
end if

if #overtime < #otr/OverTimeMin then
set #log + " Under minimum, ignored.\r\n"
set #overtime = 0
end if

if #hoursWorked<0 then
set #hoursWorked = 0
end if
if #overtime<0 then
set #overtime = 0
end if
if #overtime > 0 then
var &newPath = ClearPredicates(&path)
var #totalHoursThisWeek = Sum(&newPath[Date<#rec/Date]/HoursWorked)
var #totalHoursThisWeek = #totalHoursThisWeek + #hoursWorked
set #log + "Total time worked this period: " + #totalHoursThisWeek + " hours.\r\n"
if #totalHoursThisWeek < #otr/ReqHoursWorked then
// if we haven't worked enough hours, we don't get overtime
set #log + " Below minimum requirement for overtime.\r\n"
set #overtime = 0
end if
end if
set #input/HoursWorked = #hoursWorked + (#overtime div 60)
set #input/RegularHours = #hoursWorked
set #input/OvertimeHours = (#overtime div 60)
set #log + "Total time worked today: " + (#hoursWorked + (#overtime div 60)) + " hours."
save #input

// update the totals
var #wl = load(Parent(&path))
set #wl/BreakHours = Sum(ClearPredicates(&path)/BreakHours)
set #wl/OvertimeHours = Sum(ClearPredicates(&path)/OvertimeHours)
set #wl/TotalHours = Sum(ClearPredicates(&path)/HoursWorked)
set #wl/RegularHours = Sum(ClearPredicates(&path)/RegularHours)
set #wl/BreakExcess = Sum(ClearPredicates(&path)/BreakExcess)

// and copy in the latest customer etc.
set #wl/Department = #input/Department
set #wl/Customer = #input/Customer
set #wl/Division = #input/Division
set #wl/ReportingOfficer = #input/ReportingOfficer
set #wl/Location = #input/Location
set #wl/Project = #input/Project

set #wl/WorkingDays = #input/WorkingDays
set #wl/WorkingHours = #input/WorkingHours
set #wl/ShiftPattern = #input/ShiftPattern
set #wl/CostCenter = #input/CostCenter

save #wl

set #input/CalculationLog = #log
save #input



Next Topic:
v4.2.0.956 (beta)
Up Since 2/29/2024 12:02:23 AM