diff --git a/core-ext/admin/dashboard/index.php b/core-ext/admin/dashboard/index.php
new file mode 100644
index 00000000..53312e4b
--- /dev/null
+++ b/core-ext/admin/dashboard/index.php
@@ -0,0 +1,90 @@
+.
+
+------------------------------------------------------------------
+
+Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
+Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
+ */
+
+$moduleName = 'dashboard';
+define('MODULE_PATH',dirname(__FILE__));
+include APP_BASE_PATH.'header.php';
+include APP_BASE_PATH.'modulejslibs.inc.php';
+
+?>
+
+ getModuleManagers();
+ $dashBoardList = array();
+ foreach($moduleManagers as $moduleManagerObj){
+
+ //Check if this is not an admin module
+ if($moduleManagerObj->getModuleType() != 'admin'){
+ continue;
+ }
+
+ $allowed = BaseService::getInstance()->isModuleAllowedForUser($moduleManagerObj);
+
+ if(!$allowed){
+ continue;
+ }
+
+ $item = $moduleManagerObj->getDashboardItem();
+ if(!empty($item)) {
+ $index = $moduleManagerObj->getDashboardItemIndex();
+ $dashBoardList[$index] = $item;
+ }
+ }
+
+ ksort($dashBoardList);
+
+ foreach($dashBoardList as $k=>$v){
+ echo LanguageManager::translateTnrText($v);
+ }
+ ?>
+
+ licenseExpire) < time()) {
+ ?>
+
+
Your IceHrm Pro License is Expired
+
+ Your IceHrm Pro license is expired. Even though you can continue to use the software you won't receive
+ software upgrades or security patches. Please renew your license.
+
+
+ Renew License
+
+
+
+
+
+
diff --git a/core-ext/app/data/icehrm.log b/core-ext/app/data/icehrm.log
new file mode 100644
index 00000000..0b811205
--- /dev/null
+++ b/core-ext/app/data/icehrm.log
@@ -0,0 +1 @@
+init log
\ No newline at end of file
diff --git a/core-ext/lang/de-ext.po b/core-ext/lang/de-ext.po
new file mode 100644
index 00000000..3cda6b92
--- /dev/null
+++ b/core-ext/lang/de-ext.po
@@ -0,0 +1,10 @@
+msgid ""
+msgstr ""
+"Content-Transfer-Encoding: 8bit\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Language: English\n"
+"Language-Team: IceHrm\n"
+"Last-Translator: Thilina Hasantha\n"
+"MIME-Version: 1.0\n"
+"Project-Id-Version: 18.0\n"
+"Report-Msgid-Bugs-To: https://icehrm.com\n"
\ No newline at end of file
diff --git a/core-ext/lang/en-ext.po b/core-ext/lang/en-ext.po
new file mode 100644
index 00000000..3cda6b92
--- /dev/null
+++ b/core-ext/lang/en-ext.po
@@ -0,0 +1,10 @@
+msgid ""
+msgstr ""
+"Content-Transfer-Encoding: 8bit\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Language: English\n"
+"Language-Team: IceHrm\n"
+"Last-Translator: Thilina Hasantha\n"
+"MIME-Version: 1.0\n"
+"Project-Id-Version: 18.0\n"
+"Report-Msgid-Bugs-To: https://icehrm.com\n"
\ No newline at end of file
diff --git a/core-ext/raml/api.raml b/core-ext/raml/api.raml
new file mode 100644
index 00000000..913f0af7
--- /dev/null
+++ b/core-ext/raml/api.raml
@@ -0,0 +1,61 @@
+#%RAML 0.8
+---
+title: IceHrm
+baseUri: http://icehrm-hosted.gamonoid.com/api
+version: 1
+documentation:
+ - title: Authentication
+ content: !include documentation/authentication.md
+ - title: Responses
+ content: !include documentation/responses.md
+
+/employee:
+ displayName: Employee
+ /list:
+ get:
+ is: [ paginated ]
+ description: Get a list of employees
+ responses:
+ 200:
+ body:
+ application/json:
+ schema: !include schemas/employees.json
+ example: !include examples/employees.json
+ /{id}:
+ get:
+ description: Get an employee by id
+ responses:
+ 200:
+ body:
+ application/json:
+ schema: !include schemas/employee.json
+ example: !include examples/employee.json
+ 404:
+ description: Employee does not exist
+ body:
+ application/json:
+ schema: !include schemas/error.json
+ uriParameters:
+ id:
+ description: Employee id
+ type: integer
+/candidate:
+ displayName: Candidate
+ /{id}:
+ get:
+ description: Get an employee by id
+ responses:
+ 200:
+ body:
+ application/json:
+ schema: !include schemas/candidate.json
+ example: !include examples/candidate.json
+ 404:
+ description: Candidate does not exist
+ body:
+ application/json:
+ schema: !include schemas/error.json
+ uriParameters:
+ id:
+ description: Candidate id
+ type: integer
\ No newline at end of file
diff --git a/core-ext/raml/documentation/authentication.md b/core-ext/raml/documentation/authentication.md
new file mode 100644
index 00000000..b3d823bf
--- /dev/null
+++ b/core-ext/raml/documentation/authentication.md
@@ -0,0 +1 @@
+Admin users of icehrm can find the access token under System->Settings
\ No newline at end of file
diff --git a/core-ext/raml/documentation/responses.md b/core-ext/raml/documentation/responses.md
new file mode 100644
index 00000000..80b901ca
--- /dev/null
+++ b/core-ext/raml/documentation/responses.md
@@ -0,0 +1,9 @@
+| Code | Description |
+|------|--------------------|
+| 200 | Ok |
+| 201 | Created |
+| 400 | Bad request |
+| 401 | Not authorized |
+| 403 | Forbidden |
+| 404 | Resource not found |
+| 500 | Server error |
\ No newline at end of file
diff --git a/core-ext/raml/examples/candidate.json b/core-ext/raml/examples/candidate.json
new file mode 100644
index 00000000..ab52ca46
--- /dev/null
+++ b/core-ext/raml/examples/candidate.json
@@ -0,0 +1,53 @@
+{
+ "id": "1",
+ "first_name": "Jhon",
+ "last_name": "Doe",
+ "nationality": "4",
+ "birthday": null,
+ "gender": "Male",
+ "marital_status": null,
+ "address1": null,
+ "address2": null,
+ "city": "New York",
+ "country": "US",
+ "province": null,
+ "postal_code": null,
+ "email": "icehrm+jhon@web-stalk.com",
+ "home_phone": "+1 455565656",
+ "mobile_phone": null,
+ "cv_title": "Software Engineer",
+ "cv": "cv_rYwHphV7xD5dOe1444302569136",
+ "cvtext": null,
+ "industry": null,
+ "profileImage": null,
+ "head_line": "",
+ "objective": "",
+ "work_history": "",
+ "education": "",
+ "skills": "",
+ "referees": "",
+ "linkedInUrl": null,
+ "linkedInData": null,
+ "totalYearsOfExperience": null,
+ "totalMonthsOfExperience": null,
+ "generatedCVFile": null,
+ "created": "2015-10-08 16:59:20",
+ "updated": "2015-10-08 16:59:20",
+ "expectedSalary": "0",
+ "preferedPositions": null,
+ "preferedJobtype": null,
+ "preferedCountries": null,
+ "tags": null,
+ "notes": null,
+ "calls": null,
+ "age": null,
+ "hash": "663fd20d1859344585f678a0f87b23522b8f9fce8c67c5290a609ce342b81442",
+ "linkedInProfileLink": null,
+ "linkedInProfileId": null,
+ "facebookProfileLink": null,
+ "facebookProfileId": null,
+ "twitterProfileLink": null,
+ "twitterProfileId": null,
+ "googleProfileLink": null,
+ "googleProfileId": null
+}
\ No newline at end of file
diff --git a/core-ext/raml/examples/employee.json b/core-ext/raml/examples/employee.json
new file mode 100644
index 00000000..7d21ad7c
--- /dev/null
+++ b/core-ext/raml/examples/employee.json
@@ -0,0 +1,75 @@
+{
+ "id": "2",
+ "employee_id": "EMP002",
+ "first_name": "Lala",
+ "middle_name": "Nadila ",
+ "last_name": "Lamees",
+ "nationality": "175",
+ "birthday": "1984-03-12",
+ "gender": "Female",
+ "marital_status": "Single",
+ "ssn_num": "",
+ "nic_num": "4594567WE3",
+ "other_id": "4595567WE3",
+ "driving_license": "349-066-YUO",
+ "driving_license_exp_date": "2012-03-01",
+ "employment_status": "1",
+ "job_title": "8",
+ "pay_grade": "2",
+ "work_station_id": "W001",
+ "address1": "Green War Rd, 00123",
+ "address2": "",
+ "city": "Istanbul",
+ "country": "TR",
+ "province": null,
+ "postal_code": "909066",
+ "home_phone": "+960112345",
+ "mobile_phone": "+960112345",
+ "work_phone": "+960112345",
+ "work_email": "icehrm+manager@web-stalk.com",
+ "private_email": "icehrm+manager@web-stalk.com",
+ "joined_date": "2011-03-07",
+ "confirmation_date": "2012-02-14",
+ "supervisor": "1",
+ "indirect_supervisors": "[3,4]",
+ "department": "2",
+ "custom1": null,
+ "custom2": null,
+ "custom3": null,
+ "custom4": null,
+ "custom5": null,
+ "custom6": null,
+ "custom7": null,
+ "custom8": null,
+ "custom9": null,
+ "custom10": null,
+ "termination_date": "0000-00-00",
+ "notes": null,
+ "status": "Active",
+ "ethnicity": null,
+ "immigration_status": null,
+ "approver1": "5",
+ "approver2": "6",
+ "approver3": null,
+ "nationality_Name_id": [],
+ "nationality_Name": "Tajik",
+ "ethnicity_Name_id": [],
+ "ethnicity_Name": null,
+ "immigration_status_Name_id": [],
+ "immigration_status_Name": null,
+ "employment_status_Name_id": [],
+ "employment_status_Name": "Full Time Contract",
+ "job_title_Name_id": [],
+ "job_title_Name": "Pre-Sales Executive",
+ "pay_grade_Name_id": [],
+ "pay_grade_Name": "Executive",
+ "country_Name_id": [],
+ "country_Name": "Turkey",
+ "province_Name_id": [],
+ "province_Name": null,
+ "department_Name_id": [],
+ "department_Name": "Head Office",
+ "supervisor_Name_id": [],
+ "supervisor_Name": "IceHrm Employee",
+ "image": "http:\/\/app.app.dev\/images\/user_female.png"
+}
\ No newline at end of file
diff --git a/core-ext/raml/examples/employees.json b/core-ext/raml/examples/employees.json
new file mode 100644
index 00000000..b0f239ce
--- /dev/null
+++ b/core-ext/raml/examples/employees.json
@@ -0,0 +1,23 @@
+[
+ {
+ "id": "1",
+ "employee_id": "EMP001",
+ "first_name": "IceHrm",
+ "middle_name": "Sample",
+ "last_name": "Employee"
+ },
+ {
+ "id": "2",
+ "employee_id": "EMP002",
+ "first_name": "Lala",
+ "middle_name": "Nadila ",
+ "last_name": "Lamees"
+ },
+ {
+ "id": "3",
+ "employee_id": "EMP003",
+ "first_name": "Sofia",
+ "middle_name": "",
+ "last_name": "O'Sullivan"
+ }
+]
\ No newline at end of file
diff --git a/core-ext/raml/schemas/candidate.json b/core-ext/raml/schemas/candidate.json
new file mode 100644
index 00000000..b09252d1
--- /dev/null
+++ b/core-ext/raml/schemas/candidate.json
@@ -0,0 +1,27 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "email": {
+ "type": "string"
+ },
+ "first_name": {
+ "type": "string"
+ },
+ "last_name": {
+ "type": "string"
+ },
+ "cv_title": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "id",
+ "employee_id",
+ "first_name",
+ "last_name"
+ ]
+}
\ No newline at end of file
diff --git a/core-ext/raml/schemas/employee.json b/core-ext/raml/schemas/employee.json
new file mode 100644
index 00000000..ea76f55b
--- /dev/null
+++ b/core-ext/raml/schemas/employee.json
@@ -0,0 +1,27 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "employee_id": {
+ "type": "string"
+ },
+ "first_name": {
+ "type": "string"
+ },
+ "middle_name": {
+ "type": "string"
+ },
+ "last_name": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "id",
+ "employee_id",
+ "first_name",
+ "last_name"
+ ]
+}
\ No newline at end of file
diff --git a/core-ext/raml/schemas/employees.json b/core-ext/raml/schemas/employees.json
new file mode 100644
index 00000000..77eeaaea
--- /dev/null
+++ b/core-ext/raml/schemas/employees.json
@@ -0,0 +1,30 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "employee_id": {
+ "type": "string"
+ },
+ "first_name": {
+ "type": "string"
+ },
+ "middle_name": {
+ "type": "string"
+ },
+ "last_name": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "id",
+ "employee_id",
+ "first_name",
+ "last_name"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/core-ext/raml/schemas/error.json b/core-ext/raml/schemas/error.json
new file mode 100644
index 00000000..9b3cebed
--- /dev/null
+++ b/core-ext/raml/schemas/error.json
@@ -0,0 +1,39 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "object",
+ "properties": {
+ "error": {
+ "type": "object",
+ "properties": {
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "code",
+ "message"
+ ]
+ }
+ },
+ "description": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "messages",
+ "description"
+ ]
+ }
+ },
+ "required": [
+ "error"
+ ]
+}
\ No newline at end of file
diff --git a/ext/admin/overtime/api/OvertimeActionManager.php b/ext/admin/overtime/api/OvertimeActionManager.php
new file mode 100644
index 00000000..87ce6691
--- /dev/null
+++ b/ext/admin/overtime/api/OvertimeActionManager.php
@@ -0,0 +1,27 @@
+addUserClass("EmployeeOvertime");
+ }
+ }
+
+ public function initializeFieldMappings(){
+
+ }
+
+ public function initializeDatabaseErrorMappings(){
+
+ }
+
+ public function setupModuleClassDefinitions(){
+
+ $this->addModelClass('OvertimeCategory');
+ $this->addModelClass('EmployeeOvertime');
+ $this->addModelClass('EmployeeOvertimeApproval');
+
+ }
+
+ }
+}
+
+
+if (!class_exists('OvertimeCategory')) {
+ class OvertimeCategory extends ICEHRM_Record {
+ var $_table = 'OvertimeCategories';
+
+ 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("get","element");
+ }
+ }
+}
+
+
+
+if (!class_exists('EmployeeOvertime')) {
+ class EmployeeOvertime extends ApproveModel
+ {
+ var $_table = 'EmployeeOvertime';
+
+ var $notificationModuleName = "Overtime Management";
+ var $notificationUnitName = "OvertimeRequest";
+ var $notificationUnitPrefix = "An";
+ var $notificationUnitAdminUrl = "g=modules&n=overtime&m=module_Time_Management#tabSubordinateEmployeeOvertime";
+ var $preApproveSettingName = "Attendance: Pre-Approve Overtime Request";
+
+ public function isMultiLevelApprovalsEnabled(){
+ return (SettingsManager::getInstance()->getSetting('Overtime: Enable Multi Level Approvals') == '1');
+ }
+
+ 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");
+ }
+
+ public function fieldsNeedToBeApproved()
+ {
+ return array(
+ "start_time",
+ "end_time"
+ );
+ }
+
+ public function getType(){
+ return 'EmployeeOvertime';
+ }
+
+ public function allowIndirectMapping(){
+ if(SettingsManager::getInstance()->getSetting('Overtime: Allow Indirect Admins to Approve') == '1'){
+ return true;
+ }
+ return false;
+ }
+
+ }
+}
+
+
+if (!class_exists('EmployeeOvertimeApproval')) {
+
+ class EmployeeOvertimeApproval extends EmployeeOvertime
+ {
+
+ public function Find($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array()){
+ return $this->findApprovals(new EmployeeOvertime(), $whereOrderBy,$bindarr,$pkeysArr,$extra);
+ }
+ }
+}
diff --git a/ext/admin/overtime/index.php b/ext/admin/overtime/index.php
new file mode 100644
index 00000000..24ab532f
--- /dev/null
+++ b/ext/admin/overtime/index.php
@@ -0,0 +1,42 @@
+.
+
+------------------------------------------------------------------
+
+Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
+Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
+ */
+
+$moduleName = 'travel';
+define('MODULE_PATH',dirname(__FILE__));
+include APP_BASE_PATH.'header.php';
+include APP_BASE_PATH.'modulejslibs.inc.php';
+
+$options = array();
+$options['setRemoteTable'] = 'true';
+
+$moduleBuilder = new ModuleBuilder();
+$moduleBuilder->addModuleOrGroup(new ModuleTab('OvertimeCategory','OvertimeCategory','Overtime Categories','OvertimeCategoryAdapter','','',true,$options));
+$moduleBuilder->addModuleOrGroup(new ModuleTab('EmployeeOvertime','EmployeeOvertime','Overtime Requests','EmployeeOvertimeAdminAdapter','','',false,$options));
+echo UIManager::getInstance()->renderModule($moduleBuilder);
+
+
+$itemName = 'OvertimeRequest';
+$moduleName = 'Time Management';
+$itemNameLower = strtolower($itemName);
+
+include APP_BASE_PATH.'footer.php';
\ No newline at end of file
diff --git a/ext/admin/overtime/lib.js b/ext/admin/overtime/lib.js
new file mode 100644
index 00000000..0ac41d45
--- /dev/null
+++ b/ext/admin/overtime/lib.js
@@ -0,0 +1,99 @@
+/**
+ * Author: Thilina Hasantha
+ */
+
+
+/**
+ * OvertimeCategoryAdapter
+ */
+
+function OvertimeCategoryAdapter(endPoint) {
+ this.initAdapter(endPoint);
+}
+
+OvertimeCategoryAdapter.inherits(AdapterBase);
+
+
+
+OvertimeCategoryAdapter.method('getDataMapping', function() {
+ return [
+ "id",
+ "name"
+ ];
+});
+
+OvertimeCategoryAdapter.method('getHeaders', function() {
+ return [
+ { "sTitle": "ID" ,"bVisible":false},
+ { "sTitle": "Name" }
+ ];
+});
+
+OvertimeCategoryAdapter.method('getFormFields', function() {
+ return [
+ [ "id", {"label":"ID","type":"hidden"}],
+ [ "name", {"label":"Name","type":"text","validation":""}]
+ ];
+});
+
+
+
+
+/**
+ * EmployeeOvertimeAdminAdapter
+ */
+
+
+function EmployeeOvertimeAdminAdapter(endPoint,tab,filter,orderBy) {
+ this.initAdapter(endPoint,tab,filter,orderBy);
+ this.itemName = 'OvertimeRequest';
+ this.itemNameLower = 'overtimerequest';
+ this.modulePathName = 'overtime';
+}
+
+EmployeeOvertimeAdminAdapter.inherits(ApproveAdminAdapter);
+
+
+
+EmployeeOvertimeAdminAdapter.method('getDataMapping', function() {
+ return [
+ "id",
+ "employee",
+ "category",
+ "start_time",
+ "end_time",
+ "project",
+ "status"
+ ];
+});
+
+EmployeeOvertimeAdminAdapter.method('getHeaders', function() {
+ return [
+ { "sTitle": "ID" ,"bVisible":false},
+ { "sTitle": "Employee" },
+ { "sTitle": "Category" },
+ { "sTitle": "Start Time" },
+ { "sTitle": "End Time"},
+ { "sTitle": "Project"},
+ { "sTitle": "Status"}
+ ];
+});
+
+EmployeeOvertimeAdminAdapter.method('getFormFields', function() {
+ return [
+ ["id", {"label": "ID", "type": "hidden"}],
+ ["employee", {
+ "label": "Employee",
+ "type": "select2",
+ "sort": "none",
+ "allow-null": false,
+ "remote-source": ["Employee", "id", "first_name+last_name", "getActiveSubordinateEmployees"]
+ }],
+ ["category", {"label": "Category", "type": "select2", "allow-null":false, "remote-source": ["OvertimeCategory", "id", "name"]}],
+ ["start_time", {"label": "Start Time", "type": "datetime", "validation": ""}],
+ ["end_time", {"label": "End Time", "type": "datetime", "validation": ""}],
+ ["project", {"label": "Project", "type": "select2", "allow-null":true,"null=label":"none","remote-source": ["Project", "id", "name"]}],
+ ["notes", {"label": "Notes", "type": "textarea", "validation": "none"}]
+ ];
+});
+
diff --git a/ext/admin/overtime/meta.json b/ext/admin/overtime/meta.json
new file mode 100644
index 00000000..43b5f0bf
--- /dev/null
+++ b/ext/admin/overtime/meta.json
@@ -0,0 +1,12 @@
+{
+ "label":"Overtime Administration",
+ "menu":"Employees",
+ "order":"94",
+ "icon":"fa-align-center",
+ "user_levels":["Admin","Manager"],
+ "dashboardPosition":13,
+
+ "permissions":
+ {
+ }
+}
\ No newline at end of file
diff --git a/ext/admin/reports/reportClasses/OvertimeRequestReport.php b/ext/admin/reports/reportClasses/OvertimeRequestReport.php
new file mode 100644
index 00000000..51485723
--- /dev/null
+++ b/ext/admin/reports/reportClasses/OvertimeRequestReport.php
@@ -0,0 +1,66 @@
+= ? and end_time <= ?";
+ $params = array(
+ $request['date_start']." 00:00:00",
+ $request['date_end']." 23:59:59",
+ );
+ }else{
+ $query = "where start_time >= ? and end_time <= ?";
+ $params = array(
+ $request['date_start']." 00:00:00",
+ $request['date_end']." 23:59:59",
+ );
+ }
+
+ if(!empty($request['category']) && $request['category'] != "NULL"){
+ $query.= " and category = ?";
+ $params[] = $request['category'];
+ }
+
+ if(!empty($request['project']) && $request['project'] != "NULL"){
+ $query.= " and project = ?";
+ $params[] = $request['project'];
+ }
+
+ $query.=" order by start_time desc;";
+
+ LogManager::getInstance()->info("Query:".$query);
+ LogManager::getInstance()->info("Params:".json_encode($params));
+
+ return array($query, $params);
+ }
+}
\ No newline at end of file
diff --git a/ext/modules/overtime/api/OvertimeActionManager.php b/ext/modules/overtime/api/OvertimeActionManager.php
new file mode 100644
index 00000000..8af1fabd
--- /dev/null
+++ b/ext/modules/overtime/api/OvertimeActionManager.php
@@ -0,0 +1,19 @@
+.
+
+------------------------------------------------------------------
+
+Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
+Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
+ */
+
+$moduleName = 'overtime';
+$moduleMainName = "EmployeeOvertime"; // for creating module js lib
+$subModuleMainName = "SubordinateEmployeeOvertime";
+$moduleItemName = "Overtime Request"; // For permissions
+
+$itemName = $moduleItemName; // for status change popup
+$itemNameLower = strtolower($moduleMainName); // for status change popup
+$appModName = $moduleMainName.'Approval';
+
+define('MODULE_PATH',dirname(__FILE__));
+include APP_BASE_PATH.'header.php';
+$additionalJs = array();
+$additionalJs[] = BASE_URL.'admin/overtime/lib.js?v='.$jsVersion;
+include APP_BASE_PATH.'modulejslibs.inc.php';
+?>
+
+
\ No newline at end of file
diff --git a/ext/modules/overtime/lib.js b/ext/modules/overtime/lib.js
new file mode 100644
index 00000000..07af1c42
--- /dev/null
+++ b/ext/modules/overtime/lib.js
@@ -0,0 +1,120 @@
+/*
+This file is part of iCE Hrm.
+
+Original work Copyright (c) 2012 [Gamonoid Media Pvt. Ltd]
+Developer: Thilina Hasantha (thilina.hasantha[at]gmail.com / facebook.com/thilinah)
+ */
+
+
+function EmployeeOvertimeAdapter(endPoint) {
+ this.initAdapter(endPoint);
+ this.itemName = 'Overtime';
+ this.itemNameLower = 'employeeovertime';
+ this.modulePathName = 'overtime';
+}
+
+EmployeeOvertimeAdapter.inherits(ApproveModuleAdapter);
+
+
+
+EmployeeOvertimeAdapter.method('getDataMapping', function() {
+ return [
+ "id",
+ "category",
+ "start_time",
+ "end_time",
+ "project",
+ "status"
+ ];
+});
+
+EmployeeOvertimeAdapter.method('getHeaders', function() {
+ return [
+ { "sTitle": "ID" ,"bVisible":false},
+ { "sTitle": "Category" },
+ { "sTitle": "Start Time" },
+ { "sTitle": "End Time"},
+ { "sTitle": "Project"},
+ { "sTitle": "Status"}
+ ];
+});
+
+EmployeeOvertimeAdapter.method('getFormFields', function() {
+ return [
+ ["id", {"label": "ID", "type": "hidden"}],
+ ["category", {"label": "Category", "type": "select2", "allow-null":false, "remote-source": ["OvertimeCategory", "id", "name"]}],
+ ["start_time", {"label": "Start Time", "type": "datetime", "validation": ""}],
+ ["end_time", {"label": "End Time", "type": "datetime", "validation": ""}],
+ ["project", {"label": "Project", "type": "select2", "allow-null":true,"null=label":"none","remote-source": ["Project", "id", "name"]}],
+ ["notes", {"label": "Notes", "type": "textarea", "validation": "none"}]
+ ];
+});
+
+
+/*
+ EmployeeOvertimeApproverAdapter
+ */
+
+function EmployeeOvertimeApproverAdapter(endPoint) {
+ this.initAdapter(endPoint);
+ this.itemName = 'Overtime';
+ this.itemNameLower = 'employeeovertime';
+ this.modulePathName = 'overtime';
+}
+
+EmployeeOvertimeApproverAdapter.inherits(EmployeeOvertimeAdminAdapter);
+
+EmployeeOvertimeApproverAdapter.method('getActionButtonsHtml', function(id,data) {
+ var statusChangeButton = '
';
+ var viewLogsButton = '
';
+
+ var html = '_status__logs_
';
+
+
+ html = html.replace('_logs_',viewLogsButton);
+
+
+ if(data[this.getStatusFieldPosition()] == 'Processing'){
+ html = html.replace('_status_',statusChangeButton);
+
+ }else{
+ html = html.replace('_status_','');
+ }
+
+ html = html.replace(/_id_/g,id);
+ html = html.replace(/_BASE_/g,this.baseUrl);
+ html = html.replace(/_cstatus_/g,data[this.getStatusFieldPosition()]);
+ return html;
+});
+
+EmployeeOvertimeApproverAdapter.method('getStatusOptionsData', function(currentStatus) {
+ var data = {};
+ if(currentStatus != 'Processing'){
+
+ }else{
+ data["Approved"] = "Approved";
+ data["Rejected"] = "Rejected";
+
+ }
+
+ return data;
+});
+
+EmployeeOvertimeApproverAdapter.method('getStatusOptions', function(currentStatus) {
+ return this.generateOptions(this.getStatusOptionsData(currentStatus));
+});
+
+
+/*
+ EmployeeOvertimeAdapter
+ */
+
+function SubordinateEmployeeOvertimeAdapter(endPoint,tab,filter,orderBy) {
+ this.initAdapter(endPoint,tab,filter,orderBy);
+ this.itemName = 'Overtime';
+ this.itemNameLower = 'employeeovertime';
+ this.modulePathName = 'overtime';
+}
+
+SubordinateEmployeeOvertimeAdapter.inherits(EmployeeOvertimeAdminAdapter);
+
diff --git a/ext/modules/overtime/meta.json b/ext/modules/overtime/meta.json
new file mode 100644
index 00000000..2609d2d9
--- /dev/null
+++ b/ext/modules/overtime/meta.json
@@ -0,0 +1,12 @@
+{
+ "label":"Overtime Requests",
+ "menu":"Time Management",
+ "order":"5",
+ "icon":"fa-align-center",
+ "user_levels":["Admin","Manager","Employee"],
+ "dashboardPosition":106,
+
+ "permissions":
+ {
+ }
+}
\ No newline at end of file
diff --git a/test/classes/ApprovalStatusTest.php b/test/classes/ApprovalStatusTest.php
new file mode 100644
index 00000000..5e6b2318
--- /dev/null
+++ b/test/classes/ApprovalStatusTest.php
@@ -0,0 +1,93 @@
+Load("id = ?",array(1));
+ $emp->supervisor = 2;
+ $emp->indirect_supervisors = json_encode(array(3,4));
+ $emp->approver1 = 5;
+ $emp->approver2 = 6;
+ $emp->approver3 = 7;
+ $emp->Save();
+
+
+
+ $this->travelRec = new EmployeeTravelRecord();
+
+ $this->travelRec->DB()->execute("delete from EmployeeTravelRecords");
+
+ $this->travelRec->employee = 1;
+ $this->travelRec->type = 'International';
+ $this->travelRec->purpose = 'Testing';
+ $this->travelRec->travel_from = 'Colombo';
+ $this->travelRec->travel_to = 'Germany';
+ $this->travelRec->travel_date = date("Y-m-d H:i:s");
+ $this->travelRec->return_date = date("Y-m-d H:i:s");
+ $this->travelRec->status = 'Pending';
+ $this->travelRec->Save();
+ }
+
+ protected function tearDown(){
+ parent::tearDown();
+ }
+
+
+ public function testInitializeApprovalChain(){
+ $id = $this->travelRec->id;
+ $this->initializeObjects();
+ $as = ApprovalStatus::getInstance();
+ $as->initializeApprovalChain('EmployeeTravelRecord',$id);
+ $status = $as->getAllStatuses('EmployeeTravelRecord',$id);
+ $this->assertEquals(3, count($status));
+ }
+
+
+ public function testUpdateApprovalStatus(){
+ $id = $this->travelRec->id;
+ $this->initializeObjects();
+ $as = ApprovalStatus::getInstance();
+
+ $as->initializeApprovalChain('EmployeeTravelRecord',$id);
+ $resp = $as->updateApprovalStatus('EmployeeTravelRecord',$id,2,1);
+
+ $this->assertNull($resp->getObject()[0]);
+ $this->assertEquals(1, $resp->getObject()[1]->active);
+ $this->assertEquals(1, $resp->getObject()[1]->level);
+
+
+ $resp = $as->updateApprovalStatus('EmployeeTravelRecord',$id,3,1);
+ $this->assertEquals(IceResponse::ERROR, $resp->getStatus());
+
+ $resp = $as->updateApprovalStatus('EmployeeTravelRecord',$id,5,1);
+ $this->assertEquals(0, $resp->getObject()[0]->active);
+ $this->assertEquals(1, $resp->getObject()[0]->level);
+ $this->assertEquals(1, $resp->getObject()[1]->active);
+ $this->assertEquals(2, $resp->getObject()[1]->level);
+
+ $resp = $as->updateApprovalStatus('EmployeeTravelRecord',$id,6,1);
+ $this->assertEquals(0, $resp->getObject()[0]->active);
+ $this->assertEquals(2, $resp->getObject()[0]->level);
+ $this->assertEquals(1, $resp->getObject()[1]->active);
+ $this->assertEquals(3, $resp->getObject()[1]->level);
+
+ $resp = $as->updateApprovalStatus('EmployeeTravelRecord',$id,7,1);
+ $this->assertEquals(1, $resp->getObject()[0]->active);
+ $this->assertEquals(3, $resp->getObject()[0]->level);
+ $this->assertNull($resp->getObject()[1]);
+
+
+ fwrite(STDOUT, __METHOD__ . " End\n");
+ }
+
+}
\ No newline at end of file