Discussion on how to implement “Sliding time edit windows” to keep people from editing their time past the current payroll period.
The way I've accomplished this is to calculate an “earliest editable date”. And then if the date being displayed is earlier than that, the field is a grey, non-editable field. So, in this case, we don't care if the user wants to put in time ahead of the current period, we're only checking against things in the past.
We exclusively use the simple.php module, so that's what I'm working with below.
Calculate the “earliest editable date”: Note: This code is taken straight from our version, and hasn't been refactored to work in the NG version
$realTodayYear = date("Y", $today); $realTodayMonth = date("n", $today); $realTodayDay = date("j", $today); // Need real last month and real last month's year $real_last_month = $realTodayMonth - 1; $real_last_month_year = $realTodayYear; if (!checkdate($real_last_month, 1, $real_last_month_year)) { $real_last_month += 12; $real_last_month_year --; } // Calculate earliest editable date: // Allow 4 day window past 1st and 15th to allow edit of previous pay period // If less than 5th of month, allow edit back to 16th of previous month // else if less than 20th allow edit back to 1st of current month // else allow edit back to 16th of current month if ($realTodayDay < 4) { $earliest_editable_date = mktime(0,0,0, $real_last_month, 16, $real_last_month_year); } elseif ($realTodayDay < 19) { $earliest_editable_date = mktime(0,0,0, $realTodayMonth, 1, $realTodayYear); } else { $earliest_editable_date = mktime(0,0,0, $realTodayMonth, 16, $realTodayYear); }
Then, we use that when displaying the time entry rows:
// We're within the loop that display's the time cells... if ($listOfDays[$currentDay - 1] < $earliest_editable_date) { $isDisabled = "readonly"; } else { $isDisabled = ""; } // debug // $lodx=$listOfDays[$currentDay-1]; // print "lod= $lodx "; //make sure the cell has at least a space in it so that its rendered by the browser if ($emptyCell) { print "<span nowrap><input type=\"text\" id=\"hours" . $rowCol . "\" name=\"hours" . $rowCol . "\" size=\"1\" onChange=\"recalculateRowCol(this.id)\" onKeyDown=\"setDirty()\" " . $isDisabled . " />h </span>"; print "<span nowrap><input type=\"text\" id=\"mins" . $rowCol . "\" name=\"mins" . $rowCol . "\" size=\"1\" onChange=\"recalculateRowCol(this.id)\" onKeyDown=\"setDirty()\" " . $isDisabled . " />m </span>"; } else { //split todays total into hours and minutes $todaysHours = floor($todaysTotal / 60 / 60); $todaysMinutes = ($todaysTotal - ($todaysHours * 60 * 60)) / 60; print "<span nowrap><input type=\"text\" id=\"hours" . $rowCol . "\" name=\"hours" . $rowCol . "\" size=\"1\" value=\"" . $todaysHours . "\" onChange=\"recalculateRowCol(this.id)\" onKeyDown=\"setDirty()\" " . $isDisabled . " />h </span>"; print "<span nowrap><input type=\"text\" id=\"mins" . $rowCol . "\" name=\"mins" . $rowCol . "\" size=\"1\" value=\"" . $todaysMinutes . "\" onChange=\"recalculateRowCol(this.id)\" onKeyDown=\"setDirty()\" " .$isDisabled . " />m</span>"; }
So, if we stick with only needing the “earliest_editable_date”, it shouldn't be terribly difficult to add in logic to handle other payroll period types, and thus we should be able to generalize things to allow for configurable options.
Some pseudo-code examples/ideas for configuration stuff
//payrollType = (monthly | biweekly | weekly) biweekly is every two weeks, BTW payrollType = monthly; //payrollMonthlySOPP = array of month dates for the Start Of Payroll Periods payrollMonthlySOPP[0]=1; payrollMonthlySOPP[1]=16; // day of week weekly payroll period starts payrollWeeklyDOW = (sunday - saturday) default to be monday // biweekly - we need to know when the "corporate year" starts... default to be January 1 payrollWeeklySOY = July 15th // how long, in days, after a payroll period ends, the user is able to edit the previous payroll period. payrollGracePeriod = 4;
This is fairly clear, and simple way to protect entries, but there will be times when an admin or manager (or the secretary) has to correct entries due to mistakes. This needs to be cleanly handled.
An alternative way, which is probably more conventional, is to create a status field for entries. Entries marked as “billed” for example could be set to be read-only. You could combine this with “approved”, “archived” and so on in a workflow style.
I agree that we'll need to create something that allows some group of people access to be able to 'correct' time entries. I'm not very familiar with the rights management, so I'd need some hints on how to handle that.
I know we'll need some mechanism that allows that 'group' to select the user on which they need to view/edit the timesheet entries, and then the code which disables the editing of the cells will also need to check for editing rights. This shouldn't be very difficult to accomplish.
Creating a status field and workflow style for timesheets is way outside the scope of what I'm currently interested in writing.