Initial checkin v13.0
This commit is contained in:
105
ext/admin/attendance/api/AttendanceActionManager.php
Normal file
105
ext/admin/attendance/api/AttendanceActionManager.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
/*
|
||||
This file is part of iCE Hrm.
|
||||
|
||||
iCE Hrm is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
iCE Hrm is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with iCE Hrm. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
------------------------------------------------------------------
|
||||
|
||||
Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
|
||||
Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
|
||||
*/
|
||||
|
||||
class AttendanceActionManager extends SubActionManager{
|
||||
|
||||
public function savePunch($req){
|
||||
|
||||
|
||||
$employee = $this->baseService->getElement('Employee',$req->employee,null,true);
|
||||
$inDateTime = $req->in_time;
|
||||
$inDateArr = explode(" ",$inDateTime);
|
||||
$inDate = $inDateArr[0];
|
||||
$outDateTime = $req->out_time;
|
||||
$outDate = "";
|
||||
if(!empty($outDateTime)){
|
||||
$outDateArr = explode(" ",$outDateTime);
|
||||
$outDate = $outDateArr[0];
|
||||
}
|
||||
|
||||
$note = $req->note;
|
||||
|
||||
//check if dates are differnet
|
||||
if(!empty($outDate) && $inDate != $outDate){
|
||||
return new IceResponse(IceResponse::ERROR,"Attendance entry should be within a single day");
|
||||
}
|
||||
|
||||
//compare dates
|
||||
if(!empty($outDateTime) && strtotime($outDateTime) <= strtotime($inDateTime)){
|
||||
return new IceResponse(IceResponse::ERROR,"Punch-in time should be lesser than Punch-out time");
|
||||
}
|
||||
|
||||
|
||||
//Find all punches for the day
|
||||
$attendance = new Attendance();
|
||||
$attendanceList = $attendance->Find("employee = ? and DATE_FORMAT( in_time, '%Y-%m-%d' ) = ?",array($employee->id,$inDate));
|
||||
|
||||
foreach($attendanceList as $attendance){
|
||||
if(!empty($req->id) && $req->id == $attendance->id){
|
||||
continue;
|
||||
}
|
||||
if(empty($attendance->out_time) || $attendance->out_time == "0000-00-00 00:00:00"){
|
||||
return new IceResponse(IceResponse::ERROR,"There is a non closed attendance entry for today. Please mark punch-out time of the open entry before adding a new one");
|
||||
}else if(!empty($outDateTime)){
|
||||
if(strtotime($attendance->out_time) >= strtotime($outDateTime) && strtotime($attendance->in_time) <= strtotime($outDateTime)){
|
||||
//-1---0---1---0 || ---0--1---1---0
|
||||
return new IceResponse(IceResponse::ERROR,"Time entry is overlapping with an existing one");
|
||||
}else if(strtotime($attendance->out_time) >= strtotime($inDateTime) && strtotime($attendance->in_time) <= strtotime($inDateTime)){
|
||||
//---0---1---0---1 || ---0--1---1---0
|
||||
return new IceResponse(IceResponse::ERROR,"Time entry is overlapping with an existing one");
|
||||
}else if(strtotime($attendance->out_time) <= strtotime($outDateTime) && strtotime($attendance->in_time) >= strtotime($inDateTime)){
|
||||
//--1--0---0--1--
|
||||
return new IceResponse(IceResponse::ERROR,"Time entry is overlapping with an existing one");
|
||||
}
|
||||
}else{
|
||||
if(strtotime($attendance->out_time) >= strtotime($inDateTime) && strtotime($attendance->in_time) <= strtotime($inDateTime)){
|
||||
//---0---1---0
|
||||
return new IceResponse(IceResponse::ERROR,"Time entry is overlapping with an existing one");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$attendance = new Attendance();
|
||||
if(!empty($req->id)){
|
||||
$attendance->Load("id = ?",array($req->id));
|
||||
}
|
||||
$attendance->in_time = $inDateTime;
|
||||
if(empty($outDateTime)){
|
||||
$attendance->out_time = "0000-00-00 00:00:00";
|
||||
}else{
|
||||
$attendance->out_time = $outDateTime;
|
||||
}
|
||||
|
||||
$attendance->employee = $req->employee;
|
||||
$attendance->note = $note;
|
||||
$ok = $attendance->Save();
|
||||
if(!$ok){
|
||||
LogManager::getInstance()->info($attendance->ErrorMsg());
|
||||
return new IceResponse(IceResponse::ERROR,"Error occured while saving attendance");
|
||||
}
|
||||
return new IceResponse(IceResponse::SUCCESS,$attendance);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
176
ext/admin/attendance/api/AttendanceAdminManager.php
Normal file
176
ext/admin/attendance/api/AttendanceAdminManager.php
Normal file
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
if (!class_exists('AttendanceAdminManager')) {
|
||||
|
||||
class AttendanceAdminManager extends AbstractModuleManager{
|
||||
|
||||
public function initializeUserClasses(){
|
||||
|
||||
}
|
||||
|
||||
public function initializeFieldMappings(){
|
||||
|
||||
}
|
||||
|
||||
public function initializeDatabaseErrorMappings(){
|
||||
|
||||
}
|
||||
|
||||
public function setupModuleClassDefinitions(){
|
||||
$this->addModelClass('Attendance');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Model Classes
|
||||
|
||||
if (!class_exists('Attendance')) {
|
||||
class Attendance extends ICEHRM_Record {
|
||||
var $_table = 'Attendance';
|
||||
|
||||
public function getAdminAccess(){
|
||||
return array("get","element","save","delete");
|
||||
}
|
||||
|
||||
public function getManagerAccess(){
|
||||
return array("get","element","save","delete");
|
||||
}
|
||||
|
||||
public function getUserAccess(){
|
||||
return array("get");
|
||||
}
|
||||
|
||||
public function getUserOnlyMeAccess(){
|
||||
return array("element","save","delete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!class_exists('AttendanceStatus')) {
|
||||
class AttendanceStatus extends ICEHRM_Record {
|
||||
var $_table = 'Attendance';
|
||||
|
||||
|
||||
public function getRecentAttendanceEntries($limit){
|
||||
$shift = intval(SettingsManager::getInstance()->getSetting("Attendance: Shift (Minutes)"));
|
||||
$attendance = new Attendance();
|
||||
$attendanceToday = $attendance->Find("1 = 1 order by in_time desc limit ".$limit,array());
|
||||
$attendanceData = array();
|
||||
$employees = array();
|
||||
foreach($attendanceToday as $atEntry){
|
||||
$entry = new stdClass();
|
||||
$entry->id = $atEntry->employee;
|
||||
$dayArr = explode(" ",$atEntry->in_time);
|
||||
$day = $dayArr[0];
|
||||
if($atEntry->out_time == "0000-00-00 00:00:00" || empty($atEntry->out_time)){
|
||||
if(strtotime($atEntry->in_time) < (time() + $shift * 60) && $day == date("Y-m-d")){
|
||||
$entry->status = "Clocked In";
|
||||
$entry->statusId = 0;
|
||||
$entry->color = 'green';
|
||||
|
||||
$employee = new Employee();
|
||||
$employee->Load("id = ?",array($entry->id));
|
||||
$entry->employee = $employee->first_name." ".$employee->last_name;
|
||||
$employees[$entry->id] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isset($employees[$entry->id])){
|
||||
$employee = new Employee();
|
||||
$employee->Load("id = ?",array($entry->id));
|
||||
if($day == date("Y-m-d")){
|
||||
$entry->status = "Clocked Out";
|
||||
$entry->statusId = 1;
|
||||
$entry->color = 'yellow';
|
||||
}else{
|
||||
$entry->status = "Not Clocked In";
|
||||
$entry->statusId = 2;
|
||||
$entry->color = 'gray';
|
||||
}
|
||||
$entry->employee = $employee->first_name." ".$employee->last_name;
|
||||
$employees[$entry->id] = $entry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return array_values($employees);
|
||||
}
|
||||
|
||||
public function Find($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array()){
|
||||
$shift = intval(SettingsManager::getInstance()->getSetting("Attendance: Shift (Minutes)"));
|
||||
$employee = new Employee();
|
||||
$data = array();
|
||||
$employees = $employee->Find("1=1");
|
||||
|
||||
$attendance = new Attendance();
|
||||
$attendanceToday = $attendance->Find("date(in_time) = ?",array(date("Y-m-d")));
|
||||
$attendanceData = array();
|
||||
//Group by employee
|
||||
foreach($attendanceToday as $attendance){
|
||||
if(isset($attendanceData[$attendance->employee])){
|
||||
$attendanceData[$attendance->employee][] = $attendance;
|
||||
}else{
|
||||
$attendanceData[$attendance->employee] = array($attendance);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach($employees as $employee){
|
||||
|
||||
$entry = new stdClass();
|
||||
$entry->id = $employee->id;
|
||||
$entry->employee = $employee->id;
|
||||
|
||||
|
||||
|
||||
if(isset($attendanceData[$employee->id])){
|
||||
$attendanceEntries = $attendanceData[$employee->id];
|
||||
foreach($attendanceEntries as $atEntry){
|
||||
if($atEntry->out_time == "0000-00-00 00:00:00" || empty($atEntry->out_time)){
|
||||
if(strtotime($atEntry->in_time) < time() + $shift * 60){
|
||||
$entry->status = "Clocked In";
|
||||
$entry->statusId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($entry->status)){
|
||||
$entry->status = "Clocked Out";
|
||||
$entry->statusId = 1;
|
||||
}
|
||||
}else{
|
||||
$entry->status = "Not Clocked In";
|
||||
$entry->statusId = 2;
|
||||
}
|
||||
|
||||
$data[] = $entry;
|
||||
}
|
||||
|
||||
function cmp($a, $b) {
|
||||
return $a->statusId - $b->statusId;
|
||||
}
|
||||
usort($data, "cmp");
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getAdminAccess(){
|
||||
return array("get","element","save","delete");
|
||||
}
|
||||
|
||||
public function getManagerAccess(){
|
||||
return array("get","element","save","delete");
|
||||
}
|
||||
|
||||
public function getUserAccess(){
|
||||
return array("get");
|
||||
}
|
||||
|
||||
public function getUserOnlyMeAccess(){
|
||||
return array("element","save","delete");
|
||||
}
|
||||
}
|
||||
}
|
||||
82
ext/admin/attendance/index.php
Normal file
82
ext/admin/attendance/index.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/*
|
||||
This file is part of iCE Hrm.
|
||||
|
||||
iCE Hrm is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
iCE Hrm is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with iCE Hrm. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
------------------------------------------------------------------
|
||||
|
||||
Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
|
||||
Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
|
||||
*/
|
||||
|
||||
$moduleName = 'attendance_monitor';
|
||||
define('MODULE_PATH',dirname(__FILE__));
|
||||
include APP_BASE_PATH.'header.php';
|
||||
include APP_BASE_PATH.'modulejslibs.inc.php';
|
||||
?><div class="span9">
|
||||
|
||||
<ul class="nav nav-tabs" id="modTab" style="margin-bottom:0px;margin-left:5px;border-bottom: none;">
|
||||
<li class="active"><a id="tabAttendance" href="#tabPageAttendance">Monitor Attendance</a></li>
|
||||
<li class=""><a id="tabAttendanceStatus" href="#tabPageAttendanceStatus">Current Clocked In Status</a></li>
|
||||
<!--
|
||||
<li class=""><a id="tabAttendanceData" href="#tabPageAttendanceData">Attendance Data Update</a></li>
|
||||
-->
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="tabPageAttendance">
|
||||
<div id="Attendance" class="reviewBlock" data-content="List" style="padding-left:5px;">
|
||||
|
||||
</div>
|
||||
<div id="AttendanceForm" class="reviewBlock" data-content="Form" style="padding-left:5px;display:none;">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="tabPageAttendanceStatus">
|
||||
<div id="AttendanceStatus" class="reviewBlock" data-content="List" style="padding-left:5px;">
|
||||
|
||||
</div>
|
||||
<div id="AttendanceStatusForm" class="reviewBlock" data-content="Form" style="padding-left:5px;display:none;">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
<div class="tab-pane" id="tabPageAttendanceData">
|
||||
<div class="control-group" id="field__id_">
|
||||
<div class="controls">
|
||||
<textarea class="input-xxlarge" placeholder="Insert CSV data to submit" type="textarea" width="96%" rows="100" id="attendanceData" name="attendanceData"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<button onclick="return false;" class="btn">Update Attendance Data</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script>
|
||||
var modJsList = new Array();
|
||||
modJsList['tabAttendance'] = new AttendanceAdapter('Attendance','Attendance','','in_time desc');
|
||||
modJsList['tabAttendance'].setRemoteTable(true);
|
||||
modJsList['tabAttendanceStatus'] = new AttendanceStatusAdapter('AttendanceStatus','AttendanceStatus','','');
|
||||
modJsList['tabAttendanceStatus'].setShowAddNew(false);
|
||||
var modJs = modJsList['tabAttendance'];
|
||||
|
||||
</script>
|
||||
<?php include APP_BASE_PATH.'footer.php';?>
|
||||
218
ext/admin/attendance/lib.js
Normal file
218
ext/admin/attendance/lib.js
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
This file is part of iCE Hrm.
|
||||
|
||||
iCE Hrm is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
iCE Hrm is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with iCE Hrm. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
------------------------------------------------------------------
|
||||
|
||||
Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
|
||||
Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
|
||||
*/
|
||||
|
||||
function AttendanceAdapter(endPoint,tab,filter,orderBy) {
|
||||
this.initAdapter(endPoint,tab,filter,orderBy);
|
||||
}
|
||||
|
||||
AttendanceAdapter.inherits(AdapterBase);
|
||||
|
||||
|
||||
|
||||
AttendanceAdapter.method('getDataMapping', function() {
|
||||
return [
|
||||
"id",
|
||||
"employee",
|
||||
"in_time",
|
||||
"out_time",
|
||||
"note"
|
||||
];
|
||||
});
|
||||
|
||||
AttendanceAdapter.method('getHeaders', function() {
|
||||
return [
|
||||
{ "sTitle": "ID" ,"bVisible":false},
|
||||
{ "sTitle": "Employee" },
|
||||
{ "sTitle": "Time-In" },
|
||||
{ "sTitle": "Time-Out"},
|
||||
{ "sTitle": "Note"}
|
||||
];
|
||||
});
|
||||
|
||||
AttendanceAdapter.method('getFormFields', function() {
|
||||
return [
|
||||
[ "employee", {"label":"Employee","type":"select2","allow-null":false,"remote-source":["Employee","id","first_name+last_name"]}],
|
||||
[ "id", {"label":"ID","type":"hidden"}],
|
||||
[ "in_time", {"label":"Time-In","type":"datetime"}],
|
||||
[ "out_time", {"label":"Time-Out","type":"datetime", "validation":"none"}],
|
||||
[ "note", {"label":"Note","type":"textarea","validation":"none"}]
|
||||
];
|
||||
});
|
||||
|
||||
AttendanceAdapter.method('getFilters', function() {
|
||||
return [
|
||||
[ "employee", {"label":"Employee","type":"select2","allow-null":false,"remote-source":["Employee","id","first_name+last_name"]}]
|
||||
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
AttendanceAdapter.method('getCustomTableParams', function() {
|
||||
var that = this;
|
||||
var dataTableParams = {
|
||||
"aoColumnDefs": [
|
||||
{
|
||||
"fnRender": function(data, cell){
|
||||
return that.preProcessRemoteTableData(data, cell, 2)
|
||||
} ,
|
||||
"aTargets": [2]
|
||||
},
|
||||
{
|
||||
"fnRender": function(data, cell){
|
||||
return that.preProcessRemoteTableData(data, cell, 3)
|
||||
} ,
|
||||
"aTargets": [3]
|
||||
},
|
||||
{
|
||||
"fnRender": function(data, cell){
|
||||
return that.preProcessRemoteTableData(data, cell, 4)
|
||||
} ,
|
||||
"aTargets": [4]
|
||||
},
|
||||
{
|
||||
"fnRender": that.getActionButtons,
|
||||
"aTargets": [that.getDataMapping().length]
|
||||
}
|
||||
]
|
||||
};
|
||||
return dataTableParams;
|
||||
});
|
||||
|
||||
AttendanceAdapter.method('preProcessRemoteTableData', function(data, cell, id) {
|
||||
if(id == 2){
|
||||
if(cell == '0000-00-00 00:00:00' || cell == "" || cell == undefined || cell == null){
|
||||
return "";
|
||||
}
|
||||
return Date.parse(cell).toString('yyyy MMM d <b>HH:mm</b>');
|
||||
}else if(id == 3){
|
||||
if(cell == '0000-00-00 00:00:00' || cell == "" || cell == undefined || cell == null){
|
||||
return "";
|
||||
}
|
||||
return Date.parse(cell).toString('MMM d <b>HH:mm</b>');
|
||||
}else if(id == 4){
|
||||
if(cell != undefined && cell != null){
|
||||
if(cell.length > 10){
|
||||
return cell.substring(0,10)+"..";
|
||||
}
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
AttendanceAdapter.method('save', function() {
|
||||
var validator = new FormValidation(this.getTableName()+"_submit",true,{'ShowPopup':false,"LabelErrorClass":"error"});
|
||||
if(validator.checkValues()){
|
||||
var params = validator.getFormParameters();
|
||||
|
||||
var msg = this.doCustomValidation(params);
|
||||
if(msg == null){
|
||||
var id = $('#'+this.getTableName()+"_submit #id").val();
|
||||
if(id != null && id != undefined && id != ""){
|
||||
$(params).attr('id',id);
|
||||
}
|
||||
|
||||
var reqJson = JSON.stringify(params);
|
||||
var callBackData = [];
|
||||
callBackData['callBackData'] = [];
|
||||
callBackData['callBackSuccess'] = 'saveSuccessCallback';
|
||||
callBackData['callBackFail'] = 'saveFailCallback';
|
||||
|
||||
this.customAction('savePunch','admin=attendance',reqJson,callBackData);
|
||||
}else{
|
||||
$("#"+this.getTableName()+'Form .label').html(msg);
|
||||
$("#"+this.getTableName()+'Form .label').show();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
AttendanceAdapter.method('saveSuccessCallback', function(callBackData) {
|
||||
this.get(callBackData);
|
||||
});
|
||||
|
||||
|
||||
AttendanceAdapter.method('saveFailCallback', function(callBackData) {
|
||||
this.showMessage("Error saving attendance entry", callBackData);
|
||||
});
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Attendance Status
|
||||
*/
|
||||
|
||||
|
||||
function AttendanceStatusAdapter(endPoint,tab,filter,orderBy) {
|
||||
this.initAdapter(endPoint,tab,filter,orderBy);
|
||||
}
|
||||
|
||||
AttendanceStatusAdapter.inherits(AdapterBase);
|
||||
|
||||
|
||||
|
||||
AttendanceStatusAdapter.method('getDataMapping', function() {
|
||||
return [
|
||||
"id",
|
||||
"employee",
|
||||
"status"
|
||||
];
|
||||
});
|
||||
|
||||
AttendanceStatusAdapter.method('getHeaders', function() {
|
||||
return [
|
||||
{ "sTitle": "ID" ,"bVisible":false},
|
||||
{ "sTitle": "Employee" },
|
||||
{ "sTitle": "Clocked In Status" }
|
||||
];
|
||||
});
|
||||
|
||||
AttendanceStatusAdapter.method('getFormFields', function() {
|
||||
return [
|
||||
|
||||
];
|
||||
});
|
||||
|
||||
AttendanceStatusAdapter.method('getFilters', function() {
|
||||
return [
|
||||
[ "employee", {"label":"Employee","type":"select2","allow-null":false,"remote-source":["Employee","id","first_name+last_name"]}]
|
||||
|
||||
];
|
||||
});
|
||||
|
||||
AttendanceStatusAdapter.method('getActionButtonsHtml', function(id,data) {
|
||||
|
||||
|
||||
html = '<div class="online-button-_COLOR_"></div>';
|
||||
html = html.replace(/_BASE_/g,this.baseUrl);
|
||||
if(data[2] == "Not Clocked In"){
|
||||
html = html.replace(/_COLOR_/g,'gray');
|
||||
}else if(data[2] == "Clocked Out"){
|
||||
html = html.replace(/_COLOR_/g,'yellow');
|
||||
}else if(data[2] == "Clocked In"){
|
||||
html = html.replace(/_COLOR_/g,'green');
|
||||
}
|
||||
return html;
|
||||
});
|
||||
10
ext/admin/attendance/meta.json
Normal file
10
ext/admin/attendance/meta.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"label":"Monitor Attendance",
|
||||
"menu":"Employees",
|
||||
"order":"8",
|
||||
"icon":"fa-clock-o",
|
||||
"user_levels":["Admin"],
|
||||
|
||||
"permissions":
|
||||
{}
|
||||
}
|
||||
Reference in New Issue
Block a user