Example #1
0
    public function printItemBody($id)
    {
        global $dbh;
        $return = '<section>
				<h2>Expenses</h2>
				<div class="sectionData">
					<table class="dataTable stripe row-border"> 
						<thead>
							<tr>
								<th class="dateTimeHeader textLeft">Time</th>
								<th class="textLeft">Order</th>
								<th class="textLeft">Employee</th>
							</tr>
						</thead>
						<tbody>';
        $sth = $dbh->prepare('SELECT expenseID, employeeID, date
								FROM expenses
								WHERE supplierID = :supplierID AND active = 1');
        $sth->execute([':supplierID' => $id]);
        while ($row = $sth->fetch()) {
            $return .= '<tr><td data-sort="' . $row['date'] . '">' . formatDateTime($row['date']) . '</td>';
            $return .= '<td>' . getLinkedName('expense', $row['expenseID']) . '</td>';
            $return .= '<td>' . getLinkedName('employee', $row['employeeID']) . '</td></tr>';
        }
        $return .= '</tbody>
					</table>
				</div>
			</section>';
        return $return;
    }
Example #2
0
    public function printItemBody($id)
    {
        global $dbh;
        global $TYPES;
        $return = '<section>
				<h2>Inventory</h2>
				<div class="sectionData">
					<table class="dataTable stripe row-border"> 
						<thead>
							<tr>
								<th class="textLeft">Location</th>
								<th class="textRight">Quantity</th>
							</tr>
						</thead>
						<tbody>';
        $sth = $dbh->prepare('SELECT *
								FROM locations_products
								WHERE productID = :productID');
        $sth->execute([':productID' => $id]);
        while ($row = $sth->fetch()) {
            $return .= '<tr><td>' . getLinkedName('location', $row['locationID']) . '</td>';
            $return .= '<td class="textRight">' . $row['quantity'] . '</td></tr>';
        }
        $return .= '</tbody>
					</table>
				</div>
			</section>';
        return $return;
    }
Example #3
0
function parseValue($type, $item)
{
    //htmlspecialchars is in the object function
    global $TYPES;
    $factoryItem = Factory::createItem($type);
    $parsed = $factoryItem->parseValue($type, $item);
    foreach ($parsed as $field => $value) {
        if (isset($TYPES[$type]['fields'][$field]) && $TYPES[$type]['fields'][$field]['typeData'][0] == 'id') {
            $parsed[$field] = !is_null($value) ? getLinkedName($TYPES[$type]['fields'][$field]['typeData'][1], $value) : '';
        }
    }
    return $parsed;
}
Example #4
0
    public function printAttachments($type, $id)
    {
        global $dbh;
        global $SETTINGS;
        $return = '';
        $allowsAttachments = in_array($type, ['employee', 'order', 'expense']);
        //anything that has an attachment, show it with a delete option
        $sth = $dbh->prepare('SELECT attachmentID, employeeID, uploadTime, name, extension
				FROM attachments
				WHERE type = :type AND id = :id');
        $sth->execute([':type' => $type, ':id' => $id]);
        $result = $sth->fetchAll();
        $hasAttachments = count($result) > 0 ? true : false;
        //if type currently allows attachments OR already has attachments, build the section
        if ($allowsAttachments || $hasAttachments) {
            $addStr = $allowsAttachments == true ? 'class="controlAdd addEnabled" href="#"' : 'class="controlAdd addDisabled" href="#" title="This item type is not currently configured to allow attachments."';
            $return = '<section>
					<h2>Attachments</h2>
					<div class="sectionData">
						<div class="customAddLink" id="addAttachment"><a ' . $addStr . '>Add Attachment</a></div>
						<table class="attachmentTable" style="width:100%;">
							<thead>
								<tr>
									<th class="textLeft">Attachment</th>
									<th class="textLeft">Added By</th>
									<th class="textLeft">Uploaded</th>
									<th></th>
								</tr>
							</thead>
							<tbody>';
            if ($hasAttachments) {
                foreach ($result as $row) {
                    $return .= '<tr><td><a href="attachment.php?id=' . $row['attachmentID'] . '">' . $row['name'] . '.' . $row['extension'] . '</a></td>';
                    $return .= '<td>' . getLinkedName('employee', $row['employeeID']) . '</td>';
                    $return .= '<td>' . formatDateTime($row['uploadTime']) . '</td>';
                    $return .= '<td class="textCenter"><a class="controlDelete deleteEnabled" href="#" data-id="' . $row['attachmentID'] . '"></a></td></tr>';
                }
            }
            $return .= '</tbody>
						</table>
					</div>
				</section>';
        }
        return $return;
    }
Example #5
0
 public function parseSubTypeValue($subType, $action, $item, $format)
 {
     global $TYPES;
     $dataStr = '';
     $parsed = [];
     foreach ($item as $field => $value) {
         //check to see if it's an id first, then do the switch statement
         if (isset($TYPES['order']['subTypes'][$subType]['fields'][$field]) && $TYPES['order']['subTypes'][$subType]['fields'][$field]['verifyData'][1] == 'id') {
             $parsed[$field] = !is_null($value) ? getLinkedName($TYPES['order']['subTypes'][$subType]['fields'][$field]['verifyData'][2], $value) : '';
         } else {
             if ($subType == 'payment') {
                 switch ($field) {
                     case 'date':
                         $parsed[$field] = formatDate($value);
                         break;
                     case 'paymentType':
                         if ($value == 'CA') {
                             $parsed[$field] = 'Cash';
                         } elseif ($value == 'CH') {
                             $parsed[$field] = 'Check';
                         } elseif ($value == 'CR') {
                             $parsed[$field] = 'Credit Card';
                         }
                         break;
                     case 'paymentAmount':
                         $parsed[$field] = formatCurrency($value);
                         break;
                     default:
                         $parsed[$field] = $value;
                 }
             } elseif ($subType == 'product') {
                 switch ($field) {
                     case 'unitPrice':
                         $parsed[$field] = formatCurrency($value);
                         break;
                     case 'quantity':
                         $parsed[$field] = formatNumber($value);
                         break;
                     case 'recurring':
                         if ($value == 'yes') {
                             $parsed[$field] = 'Yes';
                         } elseif ($value == 'no') {
                             $parsed[$field] = 'No';
                         }
                         break;
                     case 'interval':
                         if ($value == 'monthly') {
                             $parsed[$field] = 'Monthly';
                         }
                         break;
                     case 'startDate':
                     case 'endDate':
                         $parsed[$field] = formatDate($value);
                         break;
                     default:
                         $parsed[$field] = $value;
                 }
             } elseif ($subType == 'service') {
                 switch ($field) {
                     case 'unitPrice':
                         $parsed[$field] = formatCurrency($value);
                         break;
                     case 'quantity':
                         $parsed[$field] = formatNumber($value);
                         break;
                     case 'recurring':
                         if ($value == 'yes') {
                             $parsed[$field] = 'Yes';
                         } elseif ($value == 'no') {
                             $parsed[$field] = 'No';
                         }
                         break;
                     case 'interval':
                         if ($value == 'monthly') {
                             $parsed[$field] = 'Monthly';
                         }
                         break;
                     case 'startDate':
                     case 'endDate':
                         $parsed[$field] = formatDate($value);
                         break;
                     default:
                         $parsed[$field] = $value;
                 }
             } elseif ($subType == 'discountOrder') {
                 switch ($field) {
                     case 'subID':
                         break;
                     default:
                         $parsed[$field] = $value;
                 }
             } else {
                 $parsed[$field] = $value;
             }
             if (isset($parsed[$field]) && $field != 'paymentAmount' && $field != 'unitPrice') {
                 $parsed[$field] = htmlspecialchars($parsed[$field], ENT_QUOTES | ENT_HTML5, 'UTF-8');
             }
         }
     }
     if ($format == 'str') {
         //if we want a string format
         if ($action == 'A') {
             $dataStr = 'Added ';
         } elseif ($action == 'E') {
             $dataStr = 'Edited ';
         } elseif ($action == 'D') {
             $dataStr = 'Deleted ';
         }
         if ($subType == 'payment') {
             $dataStr .= 'payment. ';
         } elseif ($subType == 'product') {
             $dataStr .= 'product. ';
         } elseif ($subType == 'service') {
             $dataStr .= 'service. ';
         } elseif ($subType == 'discountOrder') {
             $dataStr .= $action == 'A' ? 'discount to the order. ' : 'discount from the order. ';
         } elseif ($subType == 'discountProduct' || $subType == 'discountService') {
             $dataStr .= 'discount. ';
         } elseif ($subType == 'attachment') {
             $dataStr .= 'attachment ' . $parsed['name'] . '.' . $parsed['extension'] . '.';
         }
         foreach ($parsed as $key => $value) {
             if ($subType != 'attachment') {
                 $dataStr .= '<b>' . $TYPES['order']['subTypes'][$subType]['fields'][$key]['formalName'] . ':</b> ' . $value . ' ';
             }
         }
         return $dataStr;
     } else {
         //otherwise return the array
         return $parsed;
     }
 }
Example #6
0
if ($_POST['action'] == 'history') {
    $return = ['status' => 'success', 'html' => ''];
    $limit = (int) $_POST['limit'];
    //cast as int because we can't use a placeholder for LIMIT
    $limit = $limit < 0 || $limit > 10000 ? 10000 : $limit;
    $sth = $dbh->prepare('SELECT *
			FROM changes
			WHERE type = :type AND id = :id
			ORDER BY changeTime DESC
			LIMIT ' . $limit);
    $sth->execute([':type' => $_POST['type'], ':id' => $_POST['id']]);
    $result = $sth->fetchAll();
    $parsed = parseHistory($_POST['type'], $result);
    foreach ($parsed as $row) {
        $return['html'] .= '<tr><td data-sort="' . $row['changeTime'] . '">' . formatDateTime($row['changeTime']) . '</td>';
        $return['html'] .= '<td>' . getLinkedName('employee', $row['employeeID']) . '</td>';
        $return['html'] .= '<td>' . $row['data'] . '</td></tr>';
    }
    echo json_encode($return);
}
/*
	Function: addAttachment
	Inputs: 
	Outputs: 
*/
if ($_POST['action'] == 'addAttachment') {
    $return = ['status' => 'fail'];
    if ($_FILES['uploadFile']['error'] == 0) {
        $pos = strrpos($_FILES['uploadFile']['name'], '.');
        $name = $pos !== false ? substr($_FILES['uploadFile']['name'], 0, $pos) : $_FILES['uploadFile']['name'];
        $extension = $pos !== false ? substr($_FILES['uploadFile']['name'], $pos + 1) : '';
Example #7
0
			<thead>
				<tr>
					<th class="textLeft">Expense</th>
					<th class="textLeft">Supplier</th>
					<th class="textRight">Amount</th>
				</tr>
			</thead>
			<tbody>';
    //TODO: sort by expense creation date?
    $sth = $dbh->prepare('SELECT expenseID, supplierID, amountDue
					FROM expenses
					WHERE amountDue > 0 AND expenses.active = 1');
    $sth->execute();
    while ($row = $sth->fetch()) {
        $return['content'] .= '<tr><td><a href="item.php?type=expense&id=' . $row['expenseID'] . '">Expense #' . $row['expenseID'] . '</a></td>';
        $return['content'] .= '<td>' . getLinkedName('supplier', $row['supplierID']) . '</td>';
        $return['content'] .= '<td class="textRight">' . formatCurrency($row['amountDue']) . '</td></tr>';
    }
    $return['content'] .= '</tbody>
		</table>';
    echo json_encode($return);
}
/* Net Income */
if ($_POST['module'] == 'netIncome') {
    $return['title'] = 'Net Income';
    $return['content'] = 'WIP';
    $unix = mktime(0, 0, 0, 1, 1, date('Y'));
    if ($SETTINGS['accounting'] == 'cash') {
        //income
        $sth = $dbh->prepare('SELECT SUM(paymentAmount) AS total
				FROM orderPayments
Example #8
0
?>
				</tr>
			</thead>
			<tbody>
				<?php 
$sth = $dbh->prepare('SELECT *
						FROM ' . $TYPES[$_GET['type']]['pluralName'] . '
						WHERE active = 1');
$sth->execute();
while ($row = $sth->fetch()) {
    $id = $row[$TYPES[$_GET['type']]['idName']];
    $item = parseValue($_GET['type'], $row);
    echo '<tr><td class="selectCol"><input type="checkbox" class="selectCheckbox" id="' . $id . '"></td>';
    foreach ($columns as $column) {
        if ($column == 'name' || $column == 'orderID' || $column == 'expenseID') {
            $temp = getLinkedName($_GET['type'], $id);
        } else {
            $temp = $item[$column];
        }
        echo '<td>' . $temp . '</td>';
    }
    echo '</tr>';
}
?>
			</tbody>
		</table>
		<?php 
require 'footer.php';
?>
	</div>
	<div class="popup" id="defaultPopup">
Example #9
0
    public function customAjax($id, $data)
    {
        global $dbh;
        global $TYPES;
        global $SETTINGS;
        $return = ['status' => 'success'];
        if ($data['subAction'] == 'view') {
            //view subAction
            if ($data['subType'] == 'paystub') {
                $sth = $dbh->prepare('SELECT date, grossPay, firstDate, lastDate, payType, payAmount
						FROM paystubs, timesheets
						WHERE paystubID = :paystubID AND paystubs.timesheetID = timesheets.timesheetID');
                $sth->execute([':paystubID' => $data['subID']]);
                $row = $sth->fetch();
                $return['date'] = formatDate($row['date']);
                $return['grossPay'] = formatCurrency($row['grossPay'], true);
                $return['startDate'] = formatDate($row['firstDate']);
                $return['endDate'] = formatDate($row['lastDate']);
                $parsed = self::parseValue('employee', ['payType' => $row['payType'], 'payAmount' => $row['payAmount']]);
                $return['payType'] = $parsed['payType'];
                $return['payAmount'] = $parsed['payAmount'];
                $sth = $dbh->prepare('SELECT SUM(regularHours) AS regularHours, SUM(overtimeHours) AS overtimeHours, SUM(holidayHours) AS holidayHours, SUM(vacationHours) AS vacationHours
						FROM timesheetHours, paystubs
						WHERE paystubID = :paystubID AND paystubs.timesheetID = timesheetHours.timesheetID');
                $sth->execute([':paystubID' => $data['subID']]);
                $row = $sth->fetch();
                $return['regularHours'] = formatNumber($row['regularHours'] + 0) . ' hours';
                $return['overtimeHours'] = formatNumber($row['overtimeHours'] + 0) . ' hours';
                $return['holidayHours'] = formatNumber($row['holidayHours'] + 0) . ' hours';
                $return['vacationHours'] = formatNumber($row['vacationHours'] + 0) . ' hours';
            } elseif ($data['subType'] == 'timesheet') {
                $sth = $dbh->prepare('SELECT firstDate, lastDate, status
						FROM timesheets
						WHERE timesheetID = :timesheetID');
                $sth->execute([':timesheetID' => $data['subID']]);
                $row = $sth->fetch();
                $firstDate = $row['firstDate'];
                $lastDate = $row['lastDate'];
                $return['timesheetStatus'] = $row['status'];
                //this will fill in days with no hours automatically, no row in timesheetHours is needed
                $sth = $dbh->prepare('SELECT date, regularHours, overtimeHours, holidayHours, vacationHours
						FROM timesheetHours
						WHERE timesheetID = :timesheetID');
                $sth->execute([':timesheetID' => $data['subID']]);
                while ($row = $sth->fetch()) {
                    $hours[$row['date']] = [$row['regularHours'], $row['overtimeHours'], $row['holidayHours'], $row['vacationHours']];
                }
                while ($firstDate <= $lastDate) {
                    $table[$firstDate] = isset($hours[$firstDate]) ? $hours[$firstDate] : [0, 0, 0, 0];
                    $firstDate += 86400;
                }
                $typeArr = ['r', 'o', 'h', 'v'];
                $return['html'] = '';
                foreach ($table as $day => $dayArr) {
                    if ($return['timesheetStatus'] == 'E' || $return['timesheetStatus'] == 'P') {
                        $return['html'] .= '<tr><td>' . formatDate($day) . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . date('l', $day) . '</td>';
                        for ($i = 0; $i < 4; $i++) {
                            $temp = empty($dayArr[$i] + 0) ? '' : formatNumber($dayArr[$i] + 0);
                            $return['html'] .= '<td><input type="text" name="' . $typeArr[$i] . $day . '" autocomplete="off" value="' . $temp . '"></td>';
                        }
                        $return['html'] .= '</tr>';
                    } else {
                        $return['html'] .= '<tr><td>' . formatDate($day) . '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . date('l', $day) . '</td>';
                        for ($i = 0; $i < 4; $i++) {
                            $temp = empty($dayArr[$i] + 0) ? '' : formatNumber($dayArr[$i] + 0);
                            $return['html'] .= '<td>' . $temp . '</td>';
                        }
                        $return['html'] .= '</tr>';
                    }
                }
            }
        } elseif ($data['subAction'] == 'edit') {
            if ($data['subType'] == 'timesheet') {
                $subID = $data['subID'];
                unset($data['subID']);
                unset($data['subAction']);
                unset($data['subType']);
                //make sure this timesheet has the correct status, get dates for later
                $sth = $dbh->prepare('SELECT employeeID, firstDate, lastDate, status
						FROM timesheets
						WHERE timesheetID = :timesheetID');
                $sth->execute([':timesheetID' => $subID]);
                $row = $sth->fetch();
                $employeeID = $row['employeeID'];
                $firstDate = $row['firstDate'];
                $lastDate = $row['lastDate'];
                $timesheetStatus = $row['status'];
                if ($timesheetStatus == 'E' || $timesheetStatus == 'P') {
                    foreach ($data as $key => $value) {
                        $hourType = substr($key, 0, 1);
                        $ts = substr($key, 1);
                        $value = $value == '' ? 0 : $value;
                        //timesheet is not (and as far as I can think, cannot due to the many variable fields) be a subtype, so we need to manually clean and verify the data
                        $value = str_replace($SETTINGS['thousandsSeparator'], '', $value);
                        $value = str_replace($SETTINGS['decimalFormat'], '.', $value);
                        if (filter_var($value, FILTER_VALIDATE_FLOAT) === false) {
                            $return['status'] = 'fail';
                            $return[$key] = 'Must be a decimal';
                        } else {
                            $temp = strrpos($value, '.');
                            if ($temp === false) {
                                $value .= '.';
                                $temp = strrpos($value, '.');
                            }
                            $length = strlen(substr($value, $temp + 1));
                            if ($length < 2) {
                                $value .= str_repeat('0', 2 - $length);
                                $length = strlen(substr($value, $temp + 1));
                            }
                            if ($length > 2) {
                                $return['status'] = 'fail';
                                $return[$key] = 'Must have 2 or fewer digits after the decimal';
                            } else {
                                if ($value < 0 || $value > 24) {
                                    $return['status'] = 'fail';
                                    $return[$key] = 'Must be between 0 and 24';
                                }
                            }
                        }
                        $typeArr = ['r', 'o', 'h', 'v'];
                        $i = array_search($hourType, $typeArr);
                        if ($i === false) {
                            //if we don't get the right hour type, fail with no error because this would never happen normally
                            $return['status'] = 'fail';
                        } else {
                            $newHours[$ts][$i] = $value;
                        }
                    }
                } else {
                    $return['status'] = 'fail';
                }
                //get old hours and then do the update
                if ($return['status'] != 'fail') {
                    $sth = $dbh->prepare('SELECT date, regularHours, overtimeHours, holidayHours, vacationHours
							FROM timesheetHours
							WHERE timesheetID = :timesheetID');
                    $sth->execute([':timesheetID' => $subID]);
                    while ($row = $sth->fetch()) {
                        $oldHours[$row['date']] = [$row['regularHours'], $row['overtimeHours'], $row['holidayHours'], $row['vacationHours']];
                    }
                    while ($firstDate <= $lastDate) {
                        $oldHours[$firstDate] = isset($oldHours[$firstDate]) ? $oldHours[$firstDate] : [0, 0, 0, 0];
                        $firstDate += 86400;
                    }
                    //TODO: check to see if employee has enough vacation time to make the change
                    //need to do it in a foreach loop right here because we need the oldHours to detect the change in vacation time and also do it before we start changing the db
                    //loop through old hours because we know the ts will be right, whereas the ts in new hours could be malicious
                    foreach ($oldHours as $key => $value) {
                        if ($oldHours[$key][0] != $newHours[$key][0] || $oldHours[$key][1] != $newHours[$key][1] || $oldHours[$key][2] != $newHours[$key][2] || $oldHours[$key][3] != $newHours[$key][3]) {
                            if ($oldHours[$key][0] == 0 && $oldHours[$key][1] == 0 && $oldHours[$key][2] == 0 && $oldHours[$key][3] == 0) {
                                //no hours -> some hours
                                $sth = $dbh->prepare('INSERT INTO timesheetHours (timesheetID, date, regularHours, overtimeHours, holidayHours, vacationHours)
										VALUES(:timesheetID, :date, :regular, :overtime, :holiday, :vacation)');
                                $sth->execute([':timesheetID' => $subID, ':date' => $key, ':regular' => $newHours[$key][0], ':overtime' => $newHours[$key][1], ':holiday' => $newHours[$key][2], ':vacation' => $newHours[$key][3]]);
                            } elseif ($newHours[$key][0] == 0 && $newHours[$key][1] == 0 && $newHours[$key][2] == 0 && $newHours[$key][3] == 0) {
                                //some hours -> no hours
                                $sth = $dbh->prepare('DELETE FROM timesheetHours
										WHERE timesheetID = :timesheetID AND date = :date');
                                $sth->execute([':timesheetID' => $subID, ':date' => $key]);
                            } else {
                                //some hours -> different hours
                                $sth = $dbh->prepare('UPDATE timesheetHours
										SET regularHours = :regular, overtimeHours = :overtime, holidayHours = :holiday, vacationHours = :vacation
										WHERE timesheetID = :timesheetID AND date = :date');
                                $sth->execute([':regular' => $newHours[$key][0], ':overtime' => $newHours[$key][1], ':holiday' => $newHours[$key][2], ':vacation' => $newHours[$key][3], ':timesheetID' => $subID, ':date' => $key]);
                            }
                        }
                    }
                }
            }
        } elseif ($data['subAction'] == 'approve') {
            if ($data['subType'] == 'timesheet') {
                $sth = $dbh->prepare('UPDATE timesheets
						SET status = "A"
						WHERE timesheetID = :timesheetID');
                $sth->execute([':timesheetID' => $data['subID']]);
            }
        } elseif ($data['subAction'] == 'changesMadeHistory') {
            $return['html'] = '';
            $limit = (int) $_POST['limit'] == -1 ? 100000 : (int) $_POST['limit'];
            //cast as int because we can't use a placeholder for LIMIT
            $sth = $dbh->prepare('SELECT *
					FROM changes
					WHERE employeeID = :employeeID
					ORDER BY changeTime DESC
					LIMIT ' . $limit);
            $sth->execute([':employeeID' => $id]);
            while ($row = $sth->fetch()) {
                $parsed = parseHistory($row['type'], [$row]);
                $return['html'] .= '<tr><td data-sort="' . $row['changeTime'] . '">' . formatDateTime($row['changeTime']) . '</td>';
                $return['html'] .= '<td>' . getLinkedName($row['type'], $row['id']) . '</td>';
                $return['html'] .= '<td>' . $TYPES[$row['type']]['formalName'] . '</td>';
                $return['html'] .= '<td>' . $parsed[0]['data'] . '</td></tr>';
            }
        }
        return $return;
    }
Example #10
0
    public function customAjax($id, $data)
    {
        global $dbh;
        global $TYPES;
        $return = ['status' => 'success'];
        if ($data['subAction'] == 'add') {
            //add subAction
            //TODO: this could possibly be a subType, right now it looks like the dateTime verify type is stopping it, but I'll wait to see when I do more here
            if ($data['date'] == '') {
                $return['status'] = 'fail';
                $return['date'] = 'Required';
            }
            if ($data['startTime'] == '') {
                $return['status'] = 'fail';
                $return['startTime'] = 'Required';
            }
            if ($data['endTime'] == '') {
                $return['status'] = 'fail';
                $return['endTime'] = 'Required';
            }
            $startDate = DateTime::createFromFormat($SETTINGS['dateTimeFormat'] . '|', $data['date'] . ' ' . $data['startTime']);
            $startTS = $startDate->getTimestamp();
            $endDate = DateTime::createFromFormat($SETTINGS['dateTimeFormat'] . '|', $data['date'] . ' ' . $data['endTime']);
            $endTS = $endDate->getTimestamp();
            if ($return['status'] != 'fail') {
                if ($startDate === false || $endDate === false) {
                    $return['status'] = 'fail';
                    $return['date'] = 'Unrecognized date/time format.';
                } else {
                    if ($endTS < $startTS) {
                        $return['status'] = 'fail';
                        $return['endTime'] = 'End Time must be after Start Time.';
                    }
                    if ($return['status'] != 'fail') {
                        $sth = $dbh->prepare('INSERT INTO vacationRequests (employeeID, submitTime, startTime, endTime, status)
								VALUES(:employeeID, UNIX_TIMESTAMP(), :startTime, :endTime, "P")');
                        $sth->execute([':employeeID' => $id, ':startTime' => $startTS, ':endTime' => $endTS]);
                        //TODO: should a call to addChange be here? or is it already logged enough?
                    }
                }
            }
        } elseif ($data['subAction'] == 'changesMadeHistory') {
            $return['html'] = '';
            $limit = (int) $_POST['limit'] == -1 ? 100000 : (int) $_POST['limit'];
            //cast as int because we can't use a placeholder for LIMIT
            $sth = $dbh->prepare('SELECT *
					FROM changes
					WHERE employeeID = :employeeID
					ORDER BY changeTime DESC
					LIMIT ' . $limit);
            $sth->execute([':employeeID' => $id]);
            while ($row = $sth->fetch()) {
                $parsed = parseHistory($row['type'], [$row]);
                $return['html'] .= '<tr><td data-sort="' . $row['changeTime'] . '">' . formatDateTime($row['changeTime']) . '</td>';
                $return['html'] .= '<td>' . getLinkedName($row['type'], $row['id']) . '</td>';
                $return['html'] .= '<td>' . $TYPES[$row['type']]['formalName'] . '</td>';
                $return['html'] .= '<td>' . $parsed[0]['data'] . '</td></tr>';
            }
        }
        return $return;
    }