diff --git a/bin/drush-install.sh b/bin/drush-install.sh index 5526990dc2a..4a8b57c14f6 100755 --- a/bin/drush-install.sh +++ b/bin/drush-install.sh @@ -20,7 +20,9 @@ org.civicrm.hrmed,\ org.civicrm.hrqual,\ org.civicrm.hrvisa,\ org.civicrm.hremergency,\ -org.civicrm.hrcareer +org.civicrm.hrcareer,\ +uk.co.compucorp.contactaccessrights,\ +uk.co.compucorp.civicrm.tasksassignments ## List of extensions defining applications/UIs on top of the basic entity types APP_EXTS=\ @@ -32,7 +34,11 @@ org.civicrm.hrim,\ org.civicrm.hrprofile,\ org.civicrm.hrcaseutils,\ org.civicrm.hrrecruitment,\ -org.civicrm.reqangular +org.civicrm.reqangular,\ +org.civicrm.contactsummary,\ +org.civicrm.bootstrapcivicrm,\ +org.civicrm.bootstrapcivihr,\ +uk.co.compucorp.civicrm.hrnavigation ################################## ## Main diff --git a/com.civicrm.hrjobroles/CRM/Hrjobroles/BAO/HrJobRoles.php b/com.civicrm.hrjobroles/CRM/Hrjobroles/BAO/HrJobRoles.php index 497eff1e7bc..3e7b9b3e1b2 100644 --- a/com.civicrm.hrjobroles/CRM/Hrjobroles/BAO/HrJobRoles.php +++ b/com.civicrm.hrjobroles/CRM/Hrjobroles/BAO/HrJobRoles.php @@ -47,39 +47,39 @@ public static function getContactRoles($cuid) { } /** - * Get options for a given job roles field along with their database IDs. + * Check Contact if exist . + * + * @param String $searchValue + * @param String $searchField + * @return Integer ( Contact ID or 0 if not exist) + */ + public static function contactExists($searchValue, $searchField) { + $queryParam = array(1 => array($searchValue, 'String')); + $query = "SELECT id from civicrm_contact where ".$searchField." = %1"; + $result = CRM_Core_DAO::executeQuery($query, $queryParam); + return $result->fetch() ? $result->id : 0; + } + + /** + * Get option values for specific option group. * * @param String $fieldName * - * @return Array + * @return array */ public static function buildDbOptions($fieldName) { $queryParam = array(1 => array($fieldName, 'String')); - $query = "SELECT cpv.id, cpv.label from civicrm_option_value cpv + $query = "SELECT cpv.value, cpv.label from civicrm_option_value cpv LEFT JOIN civicrm_option_group cpg on cpv.option_group_id = cpg.id WHERE cpg.name = %1"; - $options = array(); + $options = []; $result = CRM_Core_DAO::executeQuery($query, $queryParam); while ($result->fetch()) { - $options[] = array( 'id'=>$result->id, 'label'=>strtolower($result->label) ); + $options[$result->value] = strtolower($result->label); } return $options; } - /** - * Check Contact if exist . - * - * @param String $searchValue - * @param String $searchField - * @return Integer ( Contact ID or 0 if not exist) - */ - public static function checkContact($searchValue, $searchField) { - $queryParam = array(1 => array($searchValue, 'String')); - $query = "SELECT id from civicrm_contact where ".$searchField." = %1"; - $result = CRM_Core_DAO::executeQuery($query, $queryParam); - return $result->fetch() ? $result->id : 0; - } - public static function importableFields() { $fields = array('' => array('title' => ts('- do not import -'))); return array_merge($fields, static::import()); diff --git a/com.civicrm.hrjobroles/CRM/Hrjobroles/Import/Form/DataSource.php b/com.civicrm.hrjobroles/CRM/Hrjobroles/Import/Form/DataSource.php index db9eba8462b..0ded90a0d76 100644 --- a/com.civicrm.hrjobroles/CRM/Hrjobroles/Import/Form/DataSource.php +++ b/com.civicrm.hrjobroles/CRM/Hrjobroles/Import/Form/DataSource.php @@ -47,8 +47,6 @@ class CRM_Hrjobroles_Import_Form_DataSource extends CRM_Core_Form { public function preProcess() { $session = CRM_Core_Session::singleton(); $session->pushUserContext(CRM_Utils_System::url('civicrm/jobroles/import')); - // check for post max size - CRM_Core_Config_Defaults::formatUnitSize(ini_get('post_max_size'), TRUE); } /** @@ -60,8 +58,7 @@ public function preProcess() { public function buildQuickForm() { //Setting Upload File Size $config = CRM_Core_Config::singleton(); - - $uploadFileSize = CRM_Core_Config_Defaults::formatUnitSize($config->maxFileSize.'m'); + $uploadFileSize = $config->maxImportFileSize; $uploadSize = round(($uploadFileSize / (1024 * 1024)), 2); $this->assign('uploadSize', $uploadSize); diff --git a/com.civicrm.hrjobroles/CRM/Hrjobroles/Import/Parser/HrJobRoles.php b/com.civicrm.hrjobroles/CRM/Hrjobroles/Import/Parser/HrJobRoles.php index dac5d1b547f..8090f594def 100644 --- a/com.civicrm.hrjobroles/CRM/Hrjobroles/Import/Parser/HrJobRoles.php +++ b/com.civicrm.hrjobroles/CRM/Hrjobroles/Import/Parser/HrJobRoles.php @@ -148,7 +148,11 @@ function summary(&$values) { $session = CRM_Core_Session::singleton(); $dateType = $session->get('dateTypes'); - $contractDetails = CRM_Hrjobcontract_BAO_HRJobContract::checkContract($params['job_contract_id']); + $contractDetails = NULL; + if (!empty($params['job_contract_id'])) { + $contractDetails = CRM_Hrjobcontract_BAO_HRJobContract::checkContract($params['job_contract_id']); + } + if (empty($contractDetails)) { CRM_Contact_Import_Parser_Contact::addToErrorMsg('job contract ID is not found', $errorMessage); } @@ -166,7 +170,7 @@ function summary(&$values) { foreach($optionValues as $key) { if (!empty($params[$key])) { $optionID = $this->getOptionKey($key, $params[$key]); - if ($optionID !== 0) { + if ($optionID !== NULL) { $params[$key] = $optionID; } else { CRM_Contact_Import_Parser_Contact::addToErrorMsg($fields[$key]['title'].' is not found', $errorMessage); @@ -177,7 +181,7 @@ function summary(&$values) { $cost_center_error = FALSE; if (!empty($params['hrjc_cost_center'])) { $optionID = $this->getOptionKey('hrjc_cost_center', $params['hrjc_cost_center']); - if ($optionID !== 0) { + if ($optionID !== NULL) { $params['hrjc_cost_center'] = $optionID; if (!empty($params['hrjc_cost_center_val_type'])) { $val = strtolower($params['hrjc_cost_center_val_type']); @@ -205,33 +209,35 @@ function summary(&$values) { $cost_center_error = TRUE; } - if ($params['hrjc_cost_center_val_type'] === 0 && !$cost_center_error) { - if (!empty($params['hrjc_role_amount_pay_cost_center']) && !empty($params['hrjc_role_percent_pay_cost_center'])) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('cost center percent amount should be removed', $errorMessage); - } - elseif (!empty($params['hrjc_role_amount_pay_cost_center'])) { - if ( !filter_var($params['hrjc_role_amount_pay_cost_center'], FILTER_VALIDATE_FLOAT) || $params['hrjc_role_amount_pay_cost_center'] < 0) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Amount of Pay Assigned to cost center should be positive number', $errorMessage); + if (isset($params['hrjc_cost_center_val_type'])) { + if ($params['hrjc_cost_center_val_type'] === 0 && !$cost_center_error) { + if (!empty($params['hrjc_role_amount_pay_cost_center']) && !empty($params['hrjc_role_percent_pay_cost_center'])) { + CRM_Contact_Import_Parser_Contact::addToErrorMsg('cost center percent amount should be removed', $errorMessage); } - } - else { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('cost center absolute amount is not set', $errorMessage); - } - $params['hrjc_role_percent_pay_cost_center'] = 0; - } - elseif ($params['hrjc_cost_center_val_type'] === 1 && !$cost_center_error) { - if (!empty($params['hrjc_role_amount_pay_cost_center']) && !empty($params['hrjc_role_percent_pay_cost_center'])) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('cost center absolute amount should be removed', $errorMessage); - } - elseif (!empty($params['hrjc_role_percent_pay_cost_center'])) { - if ( !filter_var($params['hrjc_role_percent_pay_cost_center'], FILTER_VALIDATE_FLOAT) || $params['hrjc_role_percent_pay_cost_center'] < 0) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Percent of Pay Assigned to cost center should be positive number', $errorMessage); + elseif (!empty($params['hrjc_role_amount_pay_cost_center'])) { + if ( !filter_var($params['hrjc_role_amount_pay_cost_center'], FILTER_VALIDATE_FLOAT) || $params['hrjc_role_amount_pay_cost_center'] < 0) { + CRM_Contact_Import_Parser_Contact::addToErrorMsg('Amount of Pay Assigned to cost center should be positive number', $errorMessage); + } + } + else { + CRM_Contact_Import_Parser_Contact::addToErrorMsg('cost center absolute amount is not set', $errorMessage); } + $params['hrjc_role_percent_pay_cost_center'] = 0; } - else { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('cost center percent amount is not set', $errorMessage); + elseif ($params['hrjc_cost_center_val_type'] === 1 && !$cost_center_error) { + if (!empty($params['hrjc_role_amount_pay_cost_center']) && !empty($params['hrjc_role_percent_pay_cost_center'])) { + CRM_Contact_Import_Parser_Contact::addToErrorMsg('cost center absolute amount should be removed', $errorMessage); + } + elseif (!empty($params['hrjc_role_percent_pay_cost_center'])) { + if ( !filter_var($params['hrjc_role_percent_pay_cost_center'], FILTER_VALIDATE_FLOAT) || $params['hrjc_role_percent_pay_cost_center'] < 0) { + CRM_Contact_Import_Parser_Contact::addToErrorMsg('Percent of Pay Assigned to cost center should be positive number', $errorMessage); + } + } + else { + CRM_Contact_Import_Parser_Contact::addToErrorMsg('cost center percent amount is not set', $errorMessage); + } + $params['hrjc_role_amount_pay_cost_center'] = 0; } - $params['hrjc_role_amount_pay_cost_center'] = 0; } if ($cost_center_error) { @@ -251,7 +257,7 @@ function summary(&$values) { else { $search_field = 'display_name'; } - $result = CRM_Hrjobroles_BAO_HrJobRoles::checkContact($funder_value, $search_field); + $result = CRM_Hrjobroles_BAO_HrJobRoles::contactExists($funder_value, $search_field); if ($result !== 0) { $params['funder'] = $result; if (!empty($params['hrjc_funder_val_type'])) { @@ -280,33 +286,35 @@ function summary(&$values) { $funder_error = TRUE; } - if ($params['hrjc_funder_val_type'] === 0 && !$funder_error) { - if (!empty($params['hrjc_role_amount_pay_funder']) && !empty($params['hrjc_role_percent_pay_funder'])) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('funder percent amount should be removed', $errorMessage); - } - elseif (!empty($params['hrjc_role_amount_pay_funder'])) { - if ( !filter_var($params['hrjc_role_amount_pay_funder'], FILTER_VALIDATE_FLOAT) || $params['hrjc_role_amount_pay_funder'] < 0) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Amount of Pay Assigned to funder should be positive number', $errorMessage); + if (isset($params['hrjc_funder_val_type'])) { + if ($params['hrjc_funder_val_type'] === 0 && !$funder_error) { + if (!empty($params['hrjc_role_amount_pay_funder']) && !empty($params['hrjc_role_percent_pay_funder'])) { + CRM_Contact_Import_Parser_Contact::addToErrorMsg('funder percent amount should be removed', $errorMessage); } - } - else { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('funder absolute amount is not set', $errorMessage); - } - $params['hrjc_role_percent_pay_funder'] = 0; - } - elseif ($params['hrjc_funder_val_type'] === 1 && !$funder_error) { - if (!empty($params['hrjc_role_amount_pay_funder']) && !empty($params['hrjc_role_percent_pay_funder'])) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('funder absolute amount should be removed', $errorMessage); - } - elseif (!empty($params['hrjc_role_percent_pay_funder'])) { - if ( !filter_var($params['hrjc_role_percent_pay_funder'], FILTER_VALIDATE_FLOAT) || $params['hrjc_role_percent_pay_funder'] < 0) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Percent of Pay Assigned to funder should be positive number', $errorMessage); + elseif (!empty($params['hrjc_role_amount_pay_funder'])) { + if ( !filter_var($params['hrjc_role_amount_pay_funder'], FILTER_VALIDATE_FLOAT) || $params['hrjc_role_amount_pay_funder'] < 0) { + CRM_Contact_Import_Parser_Contact::addToErrorMsg('Amount of Pay Assigned to funder should be positive number', $errorMessage); + } + } + else { + CRM_Contact_Import_Parser_Contact::addToErrorMsg('funder absolute amount is not set', $errorMessage); } + $params['hrjc_role_percent_pay_funder'] = 0; } - else { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('funder percent amount is not set', $errorMessage); + elseif ($params['hrjc_funder_val_type'] === 1 && !$funder_error) { + if (!empty($params['hrjc_role_amount_pay_funder']) && !empty($params['hrjc_role_percent_pay_funder'])) { + CRM_Contact_Import_Parser_Contact::addToErrorMsg('funder absolute amount should be removed', $errorMessage); + } + elseif (!empty($params['hrjc_role_percent_pay_funder'])) { + if ( !filter_var($params['hrjc_role_percent_pay_funder'], FILTER_VALIDATE_FLOAT) || $params['hrjc_role_percent_pay_funder'] < 0) { + CRM_Contact_Import_Parser_Contact::addToErrorMsg('Percent of Pay Assigned to funder should be positive number', $errorMessage); + } + } + else { + CRM_Contact_Import_Parser_Contact::addToErrorMsg('funder percent amount is not set', $errorMessage); + } + $params['hrjc_role_amount_pay_funder'] = 0; } - $params['hrjc_role_amount_pay_funder'] = 0; } if ($funder_error) { @@ -418,26 +426,19 @@ function fini() {} /** - * get the option database ID given its label or ID - * @param String|Integer $option - * @param String|Integer $value - * @return Integer + * Get the (Option Value) database value given its + * label or the value itself + * + * @param String| $option + * @param String $value + * @return Integer|String|NULL * @access private */ - private function getOptionKey($option, $value) { - if (is_numeric ($value)) { - $search_field = 'id'; - } - else { - $search_field = 'label'; - } - $index = array_search(strtolower($value), array_column($this->_optionsList[$option], $search_field)); - if ($index !== FALSE) { - return (int) $this->_optionsList[$option][$index]['id']; - } - else { - return 0; - } + private function getOptionKey($option, $value) { + if (CRM_Utils_Array::value($value, $this->_optionsList[$option])){ + return $value; + } + return CRM_Utils_Array::key(strtolower($value), $this->_optionsList[$option]); } } diff --git a/com.civicrm.hrjobroles/js/dist/job-roles.min.js b/com.civicrm.hrjobroles/js/dist/job-roles.min.js index 1eddefbe2e0..a0968f9b1a6 100644 --- a/com.civicrm.hrjobroles/js/dist/job-roles.min.js +++ b/com.civicrm.hrjobroles/js/dist/job-roles.min.js @@ -11,4 +11,5 @@ Build date: 2014-01-10 * @license MIT License, http://www.opensource.org/licenses/MIT */ -angular.module("xeditable",[]).value("editableOptions",{theme:"default",buttons:"right",blurElem:"cancel",blurForm:"ignore",activate:"focus"}),angular.module("xeditable").directive("editableBsdate",["editableDirectiveFactory",function(e){return e({directiveName:"editableBsdate",inputTpl:''})}]),angular.module("xeditable").directive("editableBstime",["editableDirectiveFactory",function(e){return e({directiveName:"editableBstime",inputTpl:"",render:function(){this.parent.render.call(this);var e=angular.element('
');e.attr("ng-model",this.inputEl.attr("ng-model")),this.inputEl.removeAttr("ng-model"),this.attrs.eNgChange&&(e.attr("ng-change",this.inputEl.attr("ng-change")),this.inputEl.removeAttr("ng-change")),this.inputEl.wrap(e)}})}]),angular.module("xeditable").directive("editableCheckbox",["editableDirectiveFactory",function(e){return e({directiveName:"editableCheckbox",inputTpl:'',render:function(){this.parent.render.call(this),this.attrs.eTitle&&(this.inputEl.wrap(""),this.inputEl.after(angular.element("").text(this.attrs.eTitle)))},autosubmit:function(){var e=this;e.inputEl.bind("change",function(){setTimeout(function(){e.scope.$apply(function(){e.scope.$form.$submit()})},500)})}})}]),angular.module("xeditable").directive("editableChecklist",["editableDirectiveFactory","editableNgOptionsParser",function(e,t){return e({directiveName:"editableChecklist",inputTpl:"",useCopy:!0,render:function(){this.parent.render.call(this);var e=t(this.attrs.eNgOptions),n='';this.inputEl.removeAttr("ng-model"),this.inputEl.removeAttr("ng-options"),this.inputEl.html(n)}})}]),function(){var e="text|email|tel|number|url|search|color|date|datetime|time|month|week".split("|");angular.forEach(e,function(e){var t="editable"+e.charAt(0).toUpperCase()+e.slice(1);angular.module("xeditable").directive(t,["editableDirectiveFactory",function(n){return n({directiveName:t,inputTpl:''})}])}),angular.module("xeditable").directive("editableRange",["editableDirectiveFactory",function(e){return e({directiveName:"editableRange",inputTpl:'',render:function(){this.parent.render.call(this),this.inputEl.after("{{$data}}")}})}])}(),angular.module("xeditable").directive("editableRadiolist",["editableDirectiveFactory","editableNgOptionsParser",function(e,t){return e({directiveName:"editableRadiolist",inputTpl:"",render:function(){this.parent.render.call(this);var e=t(this.attrs.eNgOptions),n='';this.inputEl.removeAttr("ng-model"),this.inputEl.removeAttr("ng-options"),this.inputEl.html(n)},autosubmit:function(){var e=this;e.inputEl.bind("change",function(){setTimeout(function(){e.scope.$apply(function(){e.scope.$form.$submit()})},500)})}})}]),angular.module("xeditable").directive("editableSelect",["editableDirectiveFactory",function(e){return e({directiveName:"editableSelect",inputTpl:"",autosubmit:function(){var e=this;e.inputEl.bind("change",function(){e.scope.$apply(function(){e.scope.$form.$submit()})})}})}]),angular.module("xeditable").directive("editableTextarea",["editableDirectiveFactory",function(e){return e({directiveName:"editableTextarea",inputTpl:"",addListeners:function(){var e=this;e.parent.addListeners.call(e),e.single&&"no"!==e.buttons&&e.autosubmit()},autosubmit:function(){var e=this;e.inputEl.bind("keydown",function(t){(t.ctrlKey||t.metaKey)&&13===t.keyCode&&e.scope.$apply(function(){e.scope.$form.$submit()})})}})}]),angular.module("xeditable").factory("editableController",["$q","editableUtils",function(e,t){function n(e,n,r,i,s,o,u,a,f){var l,c,h=this;h.scope=e,h.elem=r,h.attrs=n,h.inputEl=null,h.editorEl=null,h.single=!0,h.error="",h.theme=s[o.theme]||s["default"],h.parent={},h.inputTpl="",h.directiveName="",h.useCopy=!1,h.single=null,h.buttons="right",h.init=function(t){if(h.single=t,h.name=n.eName||n[h.directiveName],!n[h.directiveName])throw"You should provide value for `"+h.directiveName+"` in editable element!";l=i(n[h.directiveName]),h.buttons=h.single?h.attrs.buttons||o.buttons:"no",n.eName&&h.scope.$watch("$data",function(e){h.scope.$form.$data[n.eName]=e}),n.onshow&&(h.onshow=function(){return h.catchError(i(n.onshow)(e))}),n.onhide&&(h.onhide=function(){return i(n.onhide)(e)}),n.oncancel&&(h.oncancel=function(){return i(n.oncancel)(e)}),n.onbeforesave&&(h.onbeforesave=function(){return h.catchError(i(n.onbeforesave)(e))}),n.onaftersave&&(h.onaftersave=function(){return h.catchError(i(n.onaftersave)(e))}),e.$parent.$watch(n[h.directiveName],function(){h.handleEmpty()})},h.render=function(){var e=h.theme;h.inputEl=angular.element(h.inputTpl),h.controlsEl=angular.element(e.controlsTpl),h.controlsEl.append(h.inputEl),"no"!==h.buttons&&(h.buttonsEl=angular.element(e.buttonsTpl),h.submitEl=angular.element(e.submitTpl),h.cancelEl=angular.element(e.cancelTpl),h.buttonsEl.append(h.submitEl).append(h.cancelEl),h.controlsEl.append(h.buttonsEl),h.inputEl.addClass("editable-has-buttons")),h.errorEl=angular.element(e.errorTpl),h.controlsEl.append(h.errorEl),h.editorEl=angular.element(h.single?e.formTpl:e.noformTpl),h.editorEl.append(h.controlsEl);for(var r in n.$attr)if(!(r.length<=1)){var i=!1,s=r.substring(1,2);if("e"===r.substring(0,1)&&s===s.toUpperCase()&&(i=r.substring(1),"Form"!==i&&"NgSubmit"!==i)){i=i.substring(0,1).toLowerCase()+t.camelToDash(i.substring(1));var u=""===n[r]?i:n[r];h.inputEl.attr(i,u)}}h.inputEl.addClass("editable-input"),h.inputEl.attr("ng-model","$data"),h.editorEl.addClass(t.camelToDash(h.directiveName)),h.single&&(h.editorEl.attr("editable-form","$form"),h.editorEl.attr("blur",h.attrs.blur||("no"===h.buttons?"cancel":o.blurElem))),angular.isFunction(e.postrender)&&e.postrender.call(h)},h.setLocalValue=function(){h.scope.$data=h.useCopy?angular.copy(l(e.$parent)):l(e.$parent)},h.show=function(){return h.setLocalValue(),h.render(),r.after(h.editorEl),a(h.editorEl)(e),h.addListeners(),r.addClass("editable-hide"),h.onshow()},h.hide=function(){return h.editorEl.remove(),r.removeClass("editable-hide"),h.onhide()},h.cancel=function(){h.oncancel()},h.addListeners=function(){h.inputEl.bind("keyup",function(e){if(h.single)switch(e.keyCode){case 27:h.scope.$apply(function(){h.scope.$form.$cancel()})}}),h.single&&"no"===h.buttons&&h.autosubmit(),h.editorEl.bind("click",function(e){1===e.which&&h.scope.$form.$visible&&(h.scope.$form._clicked=!0)})},h.setWaiting=function(e){e?(c=!h.inputEl.attr("disabled")&&!h.inputEl.attr("ng-disabled")&&!h.inputEl.attr("ng-enabled"),c&&(h.inputEl.attr("disabled","disabled"),h.buttonsEl&&h.buttonsEl.find("button").attr("disabled","disabled"))):c&&(h.inputEl.removeAttr("disabled"),h.buttonsEl&&h.buttonsEl.find("button").removeAttr("disabled"))},h.activate=function(){setTimeout(function(){var e=h.inputEl[0];"focus"===o.activate&&e.focus&&e.focus(),"select"===o.activate&&e.select&&e.select()},0)},h.setError=function(t){angular.isObject(t)||(e.$error=t,h.error=t)},h.catchError=function(e,t){return angular.isObject(e)&&t!==!0?f.when(e).then(angular.bind(this,function(e){this.catchError(e,!0)}),angular.bind(this,function(e){this.catchError(e,!0)})):t&&angular.isObject(e)&&e.status&&200!==e.status&&e.data&&angular.isString(e.data)?(this.setError(e.data),e=e.data):angular.isString(e)&&this.setError(e),e},h.save=function(){l.assign(e.$parent,angular.copy(h.scope.$data))},h.handleEmpty=function(){var t=l(e.$parent),n=null===t||void 0===t||""===t||angular.isArray(t)&&0===t.length;r.toggleClass("editable-empty",n)},h.autosubmit=angular.noop,h.onshow=angular.noop,h.onhide=angular.noop,h.oncancel=angular.noop,h.onbeforesave=angular.noop,h.onaftersave=angular.noop}return n.$inject=["$scope","$attrs","$element","$parse","editableThemes","editableOptions","$rootScope","$compile","$q"],n}]),angular.module("xeditable").factory("editableDirectiveFactory",["$parse","$compile","editableThemes","$rootScope","$document","editableController","editableFormController",function(e,t,n,r,i,s,o){return function(t){return{restrict:"A",scope:!0,require:[t.directiveName,"?^form"],controller:s,link:function(n,s,u,f){var l,c=f[0],h=!1;if(f[1])l=f[1],h=!0;else if(u.eForm){var p=e(u.eForm)(n);if(p)l=p,h=!0;else for(var v=0;v=0&&e.splice(n,1),t},camelToDash:function(e){var t=/[A-Z]/g;return e.replace(t,function(e,t){return(t?"-":"")+e.toLowerCase()})},dashToCamel:function(e){var t=/([\:\-\_]+(.))/g,n=/^moz([A-Z])/;return e.replace(t,function(e,t,n,r){return r?n.toUpperCase():n}).replace(n,"Moz$1")}}}]),angular.module("xeditable").factory("editableNgOptionsParser",[function(){function e(e){var n;if(!(n=e.match(t)))throw"ng-options parse error";var r,i=n[2]||n[1],s=n[4]||n[6],o=n[5],u=(n[3]||"",n[2]?n[1]:s),a=n[7],f=n[8],l=f?n[8]:null;return void 0===o?(r=s+" in "+a,void 0!==f&&(r+=" track by "+l)):r="("+o+", "+s+") in "+a,{ngRepeat:r,locals:{valueName:s,keyName:o,valueFn:u,displayFn:i}}}var t=/^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/;return e}]),angular.module("xeditable").factory("editableThemes",function(){var e={"default":{formTpl:'
',noformTpl:'',controlsTpl:'',inputTpl:"",errorTpl:'
',buttonsTpl:'',submitTpl:'',cancelTpl:''},bs2:{formTpl:'
',noformTpl:'',controlsTpl:'
',inputTpl:"",errorTpl:'
',buttonsTpl:'',submitTpl:'',cancelTpl:''},bs3:{formTpl:'
',noformTpl:'',controlsTpl:'
',inputTpl:"",errorTpl:'
',buttonsTpl:'',submitTpl:'',cancelTpl:'',buttonsClass:"",inputClass:"",postrender:function(){switch(this.directiveName){case"editableText":case"editableSelect":case"editableTextarea":case"editableEmail":case"editableTel":case"editableNumber":case"editableUrl":case"editableSearch":case"editableDate":case"editableDatetime":case"editableTime":case"editableMonth":case"editableWeek":if(this.inputEl.addClass("form-control"),this.theme.inputClass){if(!(!this.inputEl.attr("multiple")||"input-sm"!==this.theme.inputClass&&"input-lg"!==this.theme.inputClass))break;this.inputEl.addClass(this.theme.inputClass)}}this.buttonsEl&&this.theme.buttonsClass&&this.buttonsEl.find("button").addClass(this.theme.buttonsClass)}}};return e}),!function(e,t,n){"use strict";function r(e){return A(e)?e:Object.keys(e).map(function(t){return e[t]})}function i(e){return null===e}function s(e,t){var r=Object.keys(e);return-1==r.map(function(r){return t[r]!==n&&t[r]==e[r]}).indexOf(!1)}function o(e,t){if(""===t)return e;var n=e.indexOf(t.charAt(0));return-1===n?!1:o(e.substr(n+1),t.substr(1))}function u(e,t,n){var r=0;return e.filter(function(e){var i=x(n)?t>r&&n(e):t>r;return r=i?r+1:r,i})}function a(e,t,n){return n.round(e*n.pow(10,t))/n.pow(10,t)}function f(e,t,n){t=t||[];var r=Object.keys(e);return r.forEach(function(r){if(L(e[r])&&!A(e[r])){var i=n?n+"."+r:n;f(e[r],t,i||r)}else{var s=n?n+"."+r:r;t.push(s)}}),t}function l(e){return e&&e.$evalAsync&&e.$watch}function c(){return function(e,t){return e>t}}function h(){return function(e,t){return e>=t}}function p(){return function(e,t){return t>e}}function d(){return function(e,t){return t>=e}}function v(){return function(e,t){return e==t}}function m(){return function(e,t){return e!=t}}function g(){return function(e,t){return e===t}}function y(){return function(e,t){return e!==t}}function b(e){return function(t,n){return t=L(t)?r(t):t,!A(t)||T(n)?!1:t.some(function(t){return L(t)||N(n)?e(n)(t):t===n})}}function w(e,t){return t=t||0,t>=e.length?e:A(e[t])?w(e.slice(0,t).concat(e[t],e.slice(t+1)),t):w(e,t+1)}function E(e){return function(t,n){function i(e,t){return T(t)?!1:e.some(function(e){return D(e,t)})}if(t=L(t)?r(t):t,!A(t))return t;var s=[],o=e(n);return t.filter(T(n)?function(e,t,n){return n.indexOf(e)===t}:function(e){var t=o(e);return i(s,t)?!1:(s.push(t),!0)})}}function S(e,t,n){return t?e+n+S(e,--t,n):e}var x=t.isDefined,T=t.isUndefined,N=t.isFunction,C=t.isString,k=t.isNumber,L=t.isObject,A=t.isArray,O=t.forEach,M=t.extend,_=t.copy,D=t.equals;String.prototype.contains||(String.prototype.contains=function(){return-1!==String.prototype.indexOf.apply(this,arguments)}),t.module("a8m.angular",[]).filter("isUndefined",function(){return function(e){return t.isUndefined(e)}}).filter("isDefined",function(){return function(e){return t.isDefined(e)}}).filter("isFunction",function(){return function(e){return t.isFunction(e)}}).filter("isString",function(){return function(e){return t.isString(e)}}).filter("isNumber",function(){return function(e){return t.isNumber(e)}}).filter("isArray",function(){return function(e){return t.isArray(e)}}).filter("isObject",function(){return function(e){return t.isObject(e)}}).filter("isEqual",function(){return function(e,n){return t.equals(e,n)}}),t.module("a8m.conditions",[]).filter({isGreaterThan:c,">":c,isGreaterThanOrEqualTo:h,">=":h,isLessThan:p,"<":p,isLessThanOrEqualTo:d,"<=":d,isEqualTo:v,"==":v,isNotEqualTo:m,"!=":m,isIdenticalTo:g,"===":g,isNotIdenticalTo:y,"!==":y}),t.module("a8m.is-null",[]).filter("isNull",function(){return function(e){return i(e)}}),t.module("a8m.after-where",[]).filter("afterWhere",function(){return function(e,t){if(e=L(e)?r(e):e,!A(e)||T(t))return e;var n=e.map(function(e){return s(t,e)}).indexOf(!0);return e.slice(-1===n?0:n)}}),t.module("a8m.after",[]).filter("after",function(){return function(e,t){return e=L(e)?r(e):e,A(e)?e.slice(t):e}}),t.module("a8m.before-where",[]).filter("beforeWhere",function(){return function(e,t){if(e=L(e)?r(e):e,!A(e)||T(t))return e;var n=e.map(function(e){return s(t,e)}).indexOf(!0);return e.slice(0,-1===n?e.length:++n)}}),t.module("a8m.before",[]).filter("before",function(){return function(e,t){return e=L(e)?r(e):e,A(e)?e.slice(0,t?--t:t):e}}),t.module("a8m.concat",[]).filter("concat",[function(){return function(e,t){if(T(t))return e;if(A(e))return e.concat(L(t)?r(t):t);if(L(e)){var n=r(e);return n.concat(L(t)?r(t):t)}return e}}]),t.module("a8m.contains",[]).filter({contains:["$parse",b],some:["$parse",b]}),t.module("a8m.count-by",[]).filter("countBy",["$parse",function(e){return function(t,n){var i,s={},o=e(n);return t=L(t)?r(t):t,!A(t)||T(n)?t:(t.forEach(function(e){i=o(e),s[i]||(s[i]=0),s[i]++}),s)}}]),t.module("a8m.defaults",[]).filter("defaults",["$parse",function(e){return function(t,n){if(t=L(t)?r(t):t,!A(t)||!L(n))return t;var i=f(n);return t.forEach(function(t){i.forEach(function(r){var i=e(r),s=i.assign;T(i(t))&&s(t,i(n))})}),t}}]),t.module("a8m.every",[]).filter("every",["$parse",function(e){return function(t,n){return t=L(t)?r(t):t,!A(t)||T(n)?!0:t.every(function(t){return L(t)||N(n)?e(n)(t):t===n})}}]),t.module("a8m.filter-by",[]).filter("filterBy",["$parse",function(e){return function(t,i,s){var o;return s=C(s)||k(s)?String(s).toLowerCase():n,t=L(t)?r(t):t,!A(t)||T(s)?t:t.filter(function(t){return i.some(function(n){if(~n.indexOf("+")){var r=n.replace(new RegExp("\\s","g"),"").split("+");o=r.reduce(function(n,r,i){return 1===i?e(n)(t)+" "+e(r)(t):n+" "+e(r)(t)})}else o=e(n)(t);return C(o)||k(o)?String(o).toLowerCase().contains(s):!1})})}}]),t.module("a8m.first",[]).filter("first",["$parse",function(e){return function(t){var i,s,o;return t=L(t)?r(t):t,A(t)?(o=Array.prototype.slice.call(arguments,1),i=k(o[0])?o[0]:1,s=k(o[0])?k(o[1])?n:o[1]:o[0],o.length?u(t,i,s?e(s):s):t[0]):t}}]),t.module("a8m.flatten",[]).filter("flatten",function(){return function(e,t){return t=t||!1,e=L(e)?r(e):e,A(e)?t?[].concat.apply([],e):w(e,0):e}}),t.module("a8m.fuzzy-by",[]).filter("fuzzyBy",["$parse",function(e){return function(t,n,i,s){var u,a,f=s||!1;return t=L(t)?r(t):t,!A(t)||T(n)||T(i)?t:(a=e(n),t.filter(function(e){return u=a(e),C(u)?(u=f?u:u.toLowerCase(),i=f?i:i.toLowerCase(),o(u,i)!==!1):!1}))}}]),t.module("a8m.fuzzy",[]).filter("fuzzy",function(){return function(e,t,n){function i(e,t){var n,r,i=Object.keys(e);return 0=0&&k(t)&&isFinite(t)?1024>t?a(t,n,e)+" B":1048576>t?a(t/1024,n,e)+" KB":1073741824>t?a(t/1048576,n,e)+" MB":a(t/1073741824,n,e)+" GB":"NaN"}}]),t.module("a8m.math.degrees",["a8m.math"]).filter("degrees",["$math",function(e){return function(t,n){if(k(n)&&isFinite(n)&&n%1===0&&n>=0&&k(t)&&isFinite(t)){var r=180*t/e.PI;return e.round(r*e.pow(10,n))/e.pow(10,n)}return"NaN"}}]),t.module("a8m.math.kbFmt",["a8m.math"]).filter("kbFmt",["$math",function(e){return function(t,n){return k(n)&&isFinite(n)&&n%1===0&&n>=0&&k(t)&&isFinite(t)?1024>t?a(t,n,e)+" KB":1048576>t?a(t/1024,n,e)+" MB":a(t/1048576,n,e)+" GB":"NaN"}}]),t.module("a8m.math",[]).factory("$math",["$window",function(e){return e.Math}]),t.module("a8m.math.max",["a8m.math"]).filter("max",["$math","$parse",function(e,t){function n(n,r){var i=n.map(function(e){return t(r)(e)});return i.indexOf(e.max.apply(e,i))}return function(t,r){return A(t)?T(r)?e.max.apply(e,t):t[n(t,r)]:t}}]),t.module("a8m.math.min",["a8m.math"]).filter("min",["$math","$parse",function(e,t){function n(n,r){var i=n.map(function(e){return t(r)(e)});return i.indexOf(e.min.apply(e,i))}return function(t,r){return A(t)?T(r)?e.min.apply(e,t):t[n(t,r)]:t}}]),t.module("a8m.math.percent",["a8m.math"]).filter("percent",["$math","$window",function(e,t){return function(n,r,i){var s=C(n)?t.Number(n):n;return r=r||100,i=i||!1,!k(s)||t.isNaN(s)?n:i?e.round(s/r*100):s/r*100}}]),t.module("a8m.math.radians",["a8m.math"]).filter("radians",["$math",function(e){return function(t,n){if(k(n)&&isFinite(n)&&n%1===0&&n>=0&&k(t)&&isFinite(t)){var r=3.14159265359*t/180;return e.round(r*e.pow(10,n))/e.pow(10,n)}return"NaN"}}]),t.module("a8m.math.radix",[]).filter("radix",function(){return function(e,t){var n=/^[2-9]$|^[1-2]\d$|^3[0-6]$/;return k(e)&&n.test(t)?e.toString(t).toUpperCase():e}}),t.module("a8m.math.shortFmt",["a8m.math"]).filter("shortFmt",["$math",function(e){return function(t,n){return k(n)&&isFinite(n)&&n%1===0&&n>=0&&k(t)&&isFinite(t)?1e3>t?t:1e6>t?a(t/1e3,n,e)+" K":1e9>t?a(t/1e6,n,e)+" M":a(t/1e9,n,e)+" B":"NaN"}}]),t.module("a8m.math.sum",[]).filter("sum",function(){return function(e,t){return A(e)?e.reduce(function(e,t){return e+t},t||0):e}}),t.module("a8m.ends-with",[]).filter("endsWith",function(){return function(e,t,n){var r,i=n||!1;return!C(e)||T(t)?e:(e=i?e:e.toLowerCase(),r=e.length-t.length,-1!==e.indexOf(i?t:t.toLowerCase(),r))}}),t.module("a8m.latinize",[]).filter("latinize",[function(){function e(e){return e.replace(/[^\u0000-\u007E]/g,function(e){return n[e]||e})}for(var t=[{base:"A",letters:"AⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ"},{base:"AA",letters:"Ꜳ"},{base:"AE",letters:"ÆǼǢ"},{base:"AO",letters:"Ꜵ"},{base:"AU",letters:"Ꜷ"},{base:"AV",letters:"ꜸꜺ"},{base:"AY",letters:"Ꜽ"},{base:"B",letters:"BⒷBḂḄḆɃƂƁ"},{base:"C",letters:"CⒸCĆĈĊČÇḈƇȻꜾ"},{base:"D",letters:"DⒹDḊĎḌḐḒḎĐƋƊƉꝹ"},{base:"DZ",letters:"DZDŽ"},{base:"Dz",letters:"DzDž"},{base:"E",letters:"EⒺEÈÉÊỀẾỄỂẼĒḔḖĔĖËẺĚȄȆẸỆȨḜĘḘḚƐƎ"},{base:"F",letters:"FⒻFḞƑꝻ"},{base:"G",letters:"GⒼGǴĜḠĞĠǦĢǤƓꞠꝽꝾ"},{base:"H",letters:"HⒽHĤḢḦȞḤḨḪĦⱧⱵꞍ"},{base:"I",letters:"IⒾIÌÍÎĨĪĬİÏḮỈǏȈȊỊĮḬƗ"},{base:"J",letters:"JⒿJĴɈ"},{base:"K",letters:"KⓀKḰǨḲĶḴƘⱩꝀꝂꝄꞢ"},{base:"L",letters:"LⓁLĿĹĽḶḸĻḼḺŁȽⱢⱠꝈꝆꞀ"},{base:"LJ",letters:"LJ"},{base:"Lj",letters:"Lj"},{base:"M",letters:"MⓂMḾṀṂⱮƜ"},{base:"N",letters:"NⓃNǸŃÑṄŇṆŅṊṈȠƝꞐꞤ"},{base:"NJ",letters:"NJ"},{base:"Nj",letters:"Nj"},{base:"O",letters:"OⓄOÒÓÔỒỐỖỔÕṌȬṎŌṐṒŎȮȰÖȪỎŐǑȌȎƠỜỚỠỞỢỌỘǪǬØǾƆƟꝊꝌ"},{base:"OI",letters:"Ƣ"},{base:"OO",letters:"Ꝏ"},{base:"OU",letters:"Ȣ"},{base:"OE",letters:"ŒŒ"},{base:"oe",letters:"œœ"},{base:"P",letters:"PⓅPṔṖƤⱣꝐꝒꝔ"},{base:"Q",letters:"QⓆQꝖꝘɊ"},{base:"R",letters:"RⓇRŔṘŘȐȒṚṜŖṞɌⱤꝚꞦꞂ"},{base:"S",letters:"SⓈSẞŚṤŜṠŠṦṢṨȘŞⱾꞨꞄ"},{base:"T",letters:"TⓉTṪŤṬȚŢṰṮŦƬƮȾꞆ"},{base:"TZ",letters:"Ꜩ"},{base:"U",letters:"UⓊUÙÚÛŨṸŪṺŬÜǛǗǕǙỦŮŰǓȔȖƯỪỨỮỬỰỤṲŲṶṴɄ"},{base:"V",letters:"VⓋVṼṾƲꝞɅ"},{base:"VY",letters:"Ꝡ"},{base:"W",letters:"WⓌWẀẂŴẆẄẈⱲ"},{base:"X",letters:"XⓍXẊẌ"},{base:"Y",letters:"YⓎYỲÝŶỸȲẎŸỶỴƳɎỾ"},{base:"Z",letters:"ZⓏZŹẐŻŽẒẔƵȤⱿⱫꝢ"},{base:"a",letters:"aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐ"},{base:"aa",letters:"ꜳ"},{base:"ae",letters:"æǽǣ"},{base:"ao",letters:"ꜵ"},{base:"au",letters:"ꜷ"},{base:"av",letters:"ꜹꜻ"},{base:"ay",letters:"ꜽ"},{base:"b",letters:"bⓑbḃḅḇƀƃɓ"},{base:"c",letters:"cⓒcćĉċčçḉƈȼꜿↄ"},{base:"d",letters:"dⓓdḋďḍḑḓḏđƌɖɗꝺ"},{base:"dz",letters:"dzdž"},{base:"e",letters:"eⓔeèéêềếễểẽēḕḗĕėëẻěȅȇẹệȩḝęḙḛɇɛǝ"},{base:"f",letters:"fⓕfḟƒꝼ"},{base:"g",letters:"gⓖgǵĝḡğġǧģǥɠꞡᵹꝿ"},{base:"h",letters:"hⓗhĥḣḧȟḥḩḫẖħⱨⱶɥ"},{base:"hv",letters:"ƕ"},{base:"i",letters:"iⓘiìíîĩīĭïḯỉǐȉȋịįḭɨı"},{base:"j",letters:"jⓙjĵǰɉ"},{base:"k",letters:"kⓚkḱǩḳķḵƙⱪꝁꝃꝅꞣ"},{base:"l",letters:"lⓛlŀĺľḷḹļḽḻſłƚɫⱡꝉꞁꝇ"},{base:"lj",letters:"lj"},{base:"m",letters:"mⓜmḿṁṃɱɯ"},{base:"n",letters:"nⓝnǹńñṅňṇņṋṉƞɲʼnꞑꞥ"},{base:"nj",letters:"nj"},{base:"o",letters:"oⓞoòóôồốỗổõṍȭṏōṑṓŏȯȱöȫỏőǒȍȏơờớỡởợọộǫǭøǿɔꝋꝍɵ"},{base:"oi",letters:"ƣ"},{base:"ou",letters:"ȣ"},{base:"oo",letters:"ꝏ"},{base:"p",letters:"pⓟpṕṗƥᵽꝑꝓꝕ"},{base:"q",letters:"qⓠqɋꝗꝙ"},{base:"r",letters:"rⓡrŕṙřȑȓṛṝŗṟɍɽꝛꞧꞃ"},{base:"s",letters:"sⓢsßśṥŝṡšṧṣṩșşȿꞩꞅẛ"},{base:"t",letters:"tⓣtṫẗťṭțţṱṯŧƭʈⱦꞇ"},{base:"tz",letters:"ꜩ"},{base:"u",letters:"uⓤuùúûũṹūṻŭüǜǘǖǚủůűǔȕȗưừứữửựụṳųṷṵʉ"},{base:"v",letters:"vⓥvṽṿʋꝟʌ"},{base:"vy",letters:"ꝡ"},{base:"w",letters:"wⓦwẁẃŵẇẅẘẉⱳ"},{base:"x",letters:"xⓧxẋẍ"},{base:"y",letters:"yⓨyỳýŷỹȳẏÿỷẙỵƴɏỿ"},{base:"z",letters:"zⓩzźẑżžẓẕƶȥɀⱬꝣ"}],n={},r=0;r<]*>/g,""):e}}),t.module("a8m.test",[]).filter("test",function(){return function(e,t,n){var r=new RegExp(t,n);return C(e)?r.test(e):e}}),t.module("a8m.trim",[]).filter("trim",function(){return function(e,t){var n=t||"\\s";return C(e)?e.replace(new RegExp("^"+n+"+|"+n+"+$","g"),""):e}}),t.module("a8m.truncate",[]).filter("truncate",function(){return function(e,t,n,r){return t=T(t)?e.length:t,r=r||!1,n=n||"",!C(e)||e.length<=t?e:e.substring(0,r?-1===e.indexOf(" ",t)?e.length:e.indexOf(" ",t):t)+n}}),t.module("a8m.ucfirst",[]).filter("ucfirst",[function(){return function(e){return C(e)?e.split(" ").map(function(e){return e.charAt(0).toUpperCase()+e.substring(1)}).join(" "):e}}]),t.module("a8m.uri-component-encode",[]).filter("uriComponentEncode",["$window",function(e){return function(t){return C(t)?e.encodeURIComponent(t):t}}]),t.module("a8m.uri-encode",[]).filter("uriEncode",["$window",function(e){return function(t){return C(t)?e.encodeURI(t):t}}]),t.module("a8m.wrap",[]).filter("wrap",function(){return function(e,t,n){return C(e)&&x(t)?[t,e,n||t].join(""):e}}),t.module("a8m.filter-watcher",[]).provider("filterWatcher",function(){this.$get=["$window","$rootScope",function(e,n){function r(e,n){return[e,t.toJson(n)].join("#").replace(/"/g,"")}function i(e){var t=e.targetScope.$id;O(c[t],function(e){delete f[e]}),delete c[t]}function s(){h(function(){n.$$phase||(f={})})}function o(e,t){var n=e.$id;return T(c[n])&&(e.$on("$destroy",i),c[n]=[]),c[n].push(t)}function u(e,t){var n=r(e,t);return f[n]}function a(e,t,n,i){var u=r(e,t);return f[u]=i,l(n)?o(n,u):s(),i}var f={},c={},h=e.setTimeout;return{isMemoized:u,memoize:a}}]}),t.module("angular.filter",["a8m.ucfirst","a8m.uri-encode","a8m.uri-component-encode","a8m.slugify","a8m.latinize","a8m.strip-tags","a8m.stringular","a8m.truncate","a8m.starts-with","a8m.ends-with","a8m.wrap","a8m.trim","a8m.ltrim","a8m.rtrim","a8m.repeat","a8m.test","a8m.match","a8m.to-array","a8m.concat","a8m.contains","a8m.unique","a8m.is-empty","a8m.after","a8m.after-where","a8m.before","a8m.before-where","a8m.defaults","a8m.where","a8m.reverse","a8m.remove","a8m.remove-with","a8m.group-by","a8m.count-by","a8m.search-field","a8m.fuzzy-by","a8m.fuzzy","a8m.omit","a8m.pick","a8m.every","a8m.filter-by","a8m.xor","a8m.map","a8m.first","a8m.last","a8m.flatten","a8m.join","a8m.math","a8m.math.max","a8m.math.min","a8m.math.percent","a8m.math.radix","a8m.math.sum","a8m.math.degrees","a8m.math.radians","a8m.math.byteFmt","a8m.math.kbFmt","a8m.math.shortFmt","a8m.angular","a8m.conditions","a8m.is-null","a8m.filter-watcher"])}(window,window.angular),define("job-roles/controllers/controllers",["common/angular"],function(e){"use strict";return e.module("hrjobroles.controllers",[])}),define("job-roles/directives/directives",["common/angular"],function(e){"use strict";return e.module("hrjobroles.directives",[])}),define("job-roles/filters/filters",["common/angular"],function(e){"use strict";return e.module("hrjobroles.filters",[])}),define("job-roles/services/services",["common/angular"],function(e){"use strict";return e.module("hrjobroles.services",[])}),define("job-roles/controllers/hr-job-roles-controller",["common/angular","job-roles/controllers/controllers","common/moment","common/filters/angular-date/format-date"],function(e,t,n){"use strict";t.controller("HRJobRolesController",["$scope","$log","$routeParams","$route","$timeout","$filter","$q","HR_settings","HRJobRolesService","DateValidation","HRJobRolesServiceFilters",function(t,r,i,s,o,u,a,f,l,c,h){function g(e,t,n){var n=n?n:{start:"start_date",end:"end_date"};e[n.start]=t.start?d(t.start,Date):null,e[n.end]=t.end?d(t.end,Date):null}function y(e){var n=t.edit_data[e].job_contract_id;if(t.checkIfDatesAreCustom(t.edit_data[e].start_date,t.edit_data[e].end_date)){var r=O(n),i=r.revisions.some(function(n){return n.period_start_date===d(t.edit_data[e].start_date)&&n.period_end_date===d(t.edit_data[e].end_date)});i&&(g(t.edit_data[e],{start:r.start_date,end:r.end_date}),t.updateRole(e))}else g(t.edit_data[e],{start:t.edit_data[e].start_date,end:t.edit_data[e].end_date})}function b(e,n){t.edit_data.hasOwnProperty(e)&&(n==="funders"&&(t.edit_data[e][n]=h.issetFunder(t.edit_data[e][n])),n==="cost_centers"&&(t.edit_data[e][n]=h.issetCostCentre(t.edit_data[e][n])))}function E(){l.getContactList().then(function(e){if(e.is_error===1)w.message_type="alert-danger",w.message="Cannot get contact lit!";else{var t=[],n={};for(var r=0;r-1&&t.start.push(e),r.indexOf("end_date")>-1&&t.end.push(e)}),c.validate(e.start,e.end,e.contractStart,e.contractEnd),n===0}function O(e){return p.contractsData[e]}function M(e){var t=d(e,"YYYY-MM-DD");return t!=="Unspecified"?t:null}r.debug("Controller: HRJobRolesController"),t.format=f.DATE_FORMAT;var p=this,d=u("formatDate"),v=["funders","cost_centers"];t.present_job_roles=[],t.past_job_roles=[],t.dpOpen=function(e){e.preventDefault(),e.stopPropagation(),t.picker.opened=!0};var m=function(e){return e==="0000-00-00 00:00:00"};t.onContractSelected=function(){var e=O(t.edit_data.new_role_id.job_contract_id),n=t.checkIfDatesAreCustom(t.edit_data.new_role_id.newStartDate,t.edit_data.new_role_id.newEndDate);g(t.edit_data.new_role_id,{start:n?t.edit_data.new_role_id.newStartDate:e.start_date,end:n?t.edit_data.new_role_id.newEndDate:e.end_date},{start:"newStartDate",end:"newEndDate"})},t.checkIfDatesAreCustom=function(t,n){m(t)&&(t=null),m(n)&&(n=null);var r=!0;return t?(e.forEach(p.contractsData,function(e){d(t)===d(e.start_date)&&d(n)===d(e.end_date)&&(r=!1)}),r):!1},t.onContractEdited=function(e,n){var r=e||t.edit_data[n].job_contract_id,i=O(r),s=t.checkIfDatesAreCustom(t.edit_data[n].start_date,t.edit_data[n].end_date);s?g(t.edit_data[n],{start:t.edit_data[n].start_date,end:t.edit_data[n].end_date}):g(t.edit_data[n],{start:i.start_date,end:i.end_date})},t.validateTitle=function(e){if(e==="title"||e===" ")return"Title cannot be title!"},t.validateRole=function(e){e.start_date.$error.custom=[],e.end_date.$error.custom=[];var t=O(e.contract.$viewValue),n=A({start:e.start_date.$viewValue,end:e.end_date.$viewValue,contractStart:t.start_date,contractEnd:t.end_date},{start:e.start_date.$error.custom,end:e.end_date.$error.custom});return n?!0:"Error"},t.today=function(){t.CalendarShow.newStartDate=!1,t.CalendarShow.newEndDate=!1,t.CalendarShow.start_date=!1,t.CalendarShow.end_date=!1},t.isOpen=function(e){return!!t.CalendarShow[e]},t.CalendarShow=[],t.today(),t.open=function(e){t.CalendarShow[e]=!0},t.select=function(e){t.CalendarShow[e]=!1},t.collapsedRows=[],t.view_tab=[],t.edit_data={},t.add_new_role_url=t.$parent.pathBaseUrl+t.$parent.pathIncludeTpl+"add_new_role.html",t.job_role_panel_url=t.$parent.pathBaseUrl+t.$parent.pathIncludeTpl+"job_role_panel.html",t.contractsData=[],t.LevelsData={},t.LocationsData={},t.RegionsData={},t.DepartmentsData={},t.contactList=[],t.contactListObject={},t.changeTab=function(e,n){t.view_tab[e]=n},t.isTab=function(e,n){return t.view_tab[e]==n},t.isRowCollapsed=function(e){return!!t.collapsedRows[e]},t.collapseRow=function(e){t.collapsedRows[e]=!t.collapsedRows[e]},t.initData=function(e,r,i){typeof t.edit_data[e]=="undefined"&&(t.edit_data[e]={});if(r==="funders"){t.edit_data[e].funders=[];var s=h.isNotUndefined(i.funder.split("|")),o=i.funder_val_type.split("|"),u=i.percent_pay_funder.split("|"),a=i.amount_pay_funder.split("|");for(var f=0;f0||e.type==="0"&&parseInt(e.amount)>0)}):e}catch(t){return e}},issetFunder:function(e){try{return e.constructor===Array?e.filter(function(e){return e.funder_id!==""&&(e.type==="1"&&parseInt(e.percentage)>0||e.type==="0"&&parseInt(e.amount)>0)}):e}catch(t){return e}}}})}),define("job-roles/directives/example",["job-roles/directives/directives"],function(e){"use strict";e.directive("example",["$rootScope","$log",function(e,t){return t.debug("Directive: example"),{link:function(e,t,n){}}}])}),define("job-roles/app",["job-roles/vendor/angular-editable","job-roles/vendor/angular-filter","job-roles/controllers/controllers","job-roles/directives/directives","job-roles/filters/filters","job-roles/services/services","job-roles/controllers/hr-job-roles-controller","job-roles/services/hr-job-roles-service","job-roles/services/date-validation","job-roles/services/hr-job-roles-service-filter","job-roles/directives/example","common/services/angular-date/date-format","common/modules/routers/compu-ng-route","common/directives/angular-date/date-input"],function(){"use strict";angular.module("hrjobroles",["ngAnimate","common.angularDate","compuNgRoute","xeditable","angular.filter","ngResource","ui.bootstrap","hrjobroles.controllers","hrjobroles.directives","hrjobroles.filters","hrjobroles.services"]).constant("settings",{classNamePrefix:"hrjobroles-",contactId:decodeURIComponent(((new RegExp("[?|&]cid=([^&;]+?)(&|#|;|$)")).exec(location.search)||[,""])[1].replace(/\+/g,"%20"))||null,debug:!0,pathApp:"",pathRest:CRM.url("civicrm/ajax/rest"),pathBaseUrl:CRM.vars.hrjobroles.baseURL+"/",pathTpl:"views/",pathIncludeTpl:"views/include/"}).config(["settings","$routeProvider","$resourceProvider","$httpProvider","$logProvider",function(e,t,n,r,i){i.debugEnabled(e.debug),t.resolveForAll({format:["DateFormat",function(e){return e.getDateFormat()}]}).when("/",{templateUrl:e.pathBaseUrl+e.pathTpl+"mainTemplate.html?v=1",resolve:{},controller:"HRJobRolesController",controllerAs:"jobroles"}).otherwise({redirectTo:"/"}),n.defaults.stripTrailingSlashes=!1,r.defaults.headers.common["X-Requested-With"]="XMLHttpRequest"}]).run(["settings","$rootScope","$q","$log","editableOptions",function(e,t,n,r,i){r.debug("app.run"),i.theme="bs3",t.contactId=e.contactId,t.pathBaseUrl=e.pathBaseUrl,t.pathTpl=e.pathTpl,t.pathIncludeTpl=e.pathIncludeTpl,t.prefix=e.classNamePrefix}])}),function(){var e=CRM.vars.hrjobroles.baseURL+"/js/src/job-roles";require.config({urlArgs:"bust="+(new Date).getTime(),paths:{"job-roles":e,"job-roles/vendor/angular-editable":e+"/vendor/angular/xeditable.min","job-roles/vendor/angular-filter":e+"/vendor/angular/angular-filter.min"}}),require(["job-roles/app"],function(e){"use strict";document.addEventListener("hrjobrolesLoad",function(){angular.bootstrap(document.getElementById("hrjobroles"),["hrjobroles"])})})}(require); \ No newline at end of file +angular.module("xeditable",[]).value("editableOptions",{theme:"default",buttons:"right",blurElem:"cancel",blurForm:"ignore",activate:"focus"}),angular.module("xeditable").directive("editableBsdate",["editableDirectiveFactory",function(e){return e({directiveName:"editableBsdate",inputTpl:''})}]),angular.module("xeditable").directive("editableBstime",["editableDirectiveFactory",function(e){return e({directiveName:"editableBstime",inputTpl:"",render:function(){this.parent.render.call(this);var e=angular.element('
');e.attr("ng-model",this.inputEl.attr("ng-model")),this.inputEl.removeAttr("ng-model"),this.attrs.eNgChange&&(e.attr("ng-change",this.inputEl.attr("ng-change")),this.inputEl.removeAttr("ng-change")),this.inputEl.wrap(e)}})}]),angular.module("xeditable").directive("editableCheckbox",["editableDirectiveFactory",function(e){return e({directiveName:"editableCheckbox",inputTpl:'',render:function(){this.parent.render.call(this),this.attrs.eTitle&&(this.inputEl.wrap(""),this.inputEl.after(angular.element("").text(this.attrs.eTitle)))},autosubmit:function(){var e=this;e.inputEl.bind("change",function(){setTimeout(function(){e.scope.$apply(function(){e.scope.$form.$submit()})},500)})}})}]),angular.module("xeditable").directive("editableChecklist",["editableDirectiveFactory","editableNgOptionsParser",function(e,t){return e({directiveName:"editableChecklist",inputTpl:"",useCopy:!0,render:function(){this.parent.render.call(this);var e=t(this.attrs.eNgOptions),n='';this.inputEl.removeAttr("ng-model"),this.inputEl.removeAttr("ng-options"),this.inputEl.html(n)}})}]),function(){var e="text|email|tel|number|url|search|color|date|datetime|time|month|week".split("|");angular.forEach(e,function(e){var t="editable"+e.charAt(0).toUpperCase()+e.slice(1);angular.module("xeditable").directive(t,["editableDirectiveFactory",function(n){return n({directiveName:t,inputTpl:''})}])}),angular.module("xeditable").directive("editableRange",["editableDirectiveFactory",function(e){return e({directiveName:"editableRange",inputTpl:'',render:function(){this.parent.render.call(this),this.inputEl.after("{{$data}}")}})}])}(),angular.module("xeditable").directive("editableRadiolist",["editableDirectiveFactory","editableNgOptionsParser",function(e,t){return e({directiveName:"editableRadiolist",inputTpl:"",render:function(){this.parent.render.call(this);var e=t(this.attrs.eNgOptions),n='';this.inputEl.removeAttr("ng-model"),this.inputEl.removeAttr("ng-options"),this.inputEl.html(n)},autosubmit:function(){var e=this;e.inputEl.bind("change",function(){setTimeout(function(){e.scope.$apply(function(){e.scope.$form.$submit()})},500)})}})}]),angular.module("xeditable").directive("editableSelect",["editableDirectiveFactory",function(e){return e({directiveName:"editableSelect",inputTpl:"",autosubmit:function(){var e=this;e.inputEl.bind("change",function(){e.scope.$apply(function(){e.scope.$form.$submit()})})}})}]),angular.module("xeditable").directive("editableTextarea",["editableDirectiveFactory",function(e){return e({directiveName:"editableTextarea",inputTpl:"",addListeners:function(){var e=this;e.parent.addListeners.call(e),e.single&&"no"!==e.buttons&&e.autosubmit()},autosubmit:function(){var e=this;e.inputEl.bind("keydown",function(t){(t.ctrlKey||t.metaKey)&&13===t.keyCode&&e.scope.$apply(function(){e.scope.$form.$submit()})})}})}]),angular.module("xeditable").factory("editableController",["$q","editableUtils",function(e,t){function n(e,n,r,a,i,o,s,l,u){var c,d,f=this;f.scope=e,f.elem=r,f.attrs=n,f.inputEl=null,f.editorEl=null,f.single=!0,f.error="",f.theme=i[o.theme]||i["default"],f.parent={},f.inputTpl="",f.directiveName="",f.useCopy=!1,f.single=null,f.buttons="right",f.init=function(t){if(f.single=t,f.name=n.eName||n[f.directiveName],!n[f.directiveName])throw"You should provide value for `"+f.directiveName+"` in editable element!";c=a(n[f.directiveName]),f.buttons=f.single?f.attrs.buttons||o.buttons:"no",n.eName&&f.scope.$watch("$data",function(e){f.scope.$form.$data[n.eName]=e}),n.onshow&&(f.onshow=function(){return f.catchError(a(n.onshow)(e))}),n.onhide&&(f.onhide=function(){return a(n.onhide)(e)}),n.oncancel&&(f.oncancel=function(){return a(n.oncancel)(e)}),n.onbeforesave&&(f.onbeforesave=function(){return f.catchError(a(n.onbeforesave)(e))}),n.onaftersave&&(f.onaftersave=function(){return f.catchError(a(n.onaftersave)(e))}),e.$parent.$watch(n[f.directiveName],function(){f.handleEmpty()})},f.render=function(){var e=f.theme;f.inputEl=angular.element(f.inputTpl),f.controlsEl=angular.element(e.controlsTpl),f.controlsEl.append(f.inputEl),"no"!==f.buttons&&(f.buttonsEl=angular.element(e.buttonsTpl),f.submitEl=angular.element(e.submitTpl),f.cancelEl=angular.element(e.cancelTpl),f.buttonsEl.append(f.submitEl).append(f.cancelEl),f.controlsEl.append(f.buttonsEl),f.inputEl.addClass("editable-has-buttons")),f.errorEl=angular.element(e.errorTpl),f.controlsEl.append(f.errorEl),f.editorEl=angular.element(f.single?e.formTpl:e.noformTpl),f.editorEl.append(f.controlsEl);for(var r in n.$attr)if(!(r.length<=1)){var a=!1,i=r.substring(1,2);if("e"===r.substring(0,1)&&i===i.toUpperCase()&&(a=r.substring(1),"Form"!==a&&"NgSubmit"!==a)){a=a.substring(0,1).toLowerCase()+t.camelToDash(a.substring(1));var s=""===n[r]?a:n[r];f.inputEl.attr(a,s)}}f.inputEl.addClass("editable-input"),f.inputEl.attr("ng-model","$data"),f.editorEl.addClass(t.camelToDash(f.directiveName)),f.single&&(f.editorEl.attr("editable-form","$form"),f.editorEl.attr("blur",f.attrs.blur||("no"===f.buttons?"cancel":o.blurElem))),angular.isFunction(e.postrender)&&e.postrender.call(f)},f.setLocalValue=function(){f.scope.$data=f.useCopy?angular.copy(c(e.$parent)):c(e.$parent)},f.show=function(){return f.setLocalValue(),f.render(),r.after(f.editorEl),l(f.editorEl)(e),f.addListeners(),r.addClass("editable-hide"),f.onshow()},f.hide=function(){return f.editorEl.remove(),r.removeClass("editable-hide"),f.onhide()},f.cancel=function(){f.oncancel()},f.addListeners=function(){f.inputEl.bind("keyup",function(e){if(f.single)switch(e.keyCode){case 27:f.scope.$apply(function(){f.scope.$form.$cancel()})}}),f.single&&"no"===f.buttons&&f.autosubmit(),f.editorEl.bind("click",function(e){1===e.which&&f.scope.$form.$visible&&(f.scope.$form._clicked=!0)})},f.setWaiting=function(e){e?(d=!f.inputEl.attr("disabled")&&!f.inputEl.attr("ng-disabled")&&!f.inputEl.attr("ng-enabled"),d&&(f.inputEl.attr("disabled","disabled"),f.buttonsEl&&f.buttonsEl.find("button").attr("disabled","disabled"))):d&&(f.inputEl.removeAttr("disabled"),f.buttonsEl&&f.buttonsEl.find("button").removeAttr("disabled"))},f.activate=function(){setTimeout(function(){var e=f.inputEl[0];"focus"===o.activate&&e.focus&&e.focus(),"select"===o.activate&&e.select&&e.select()},0)},f.setError=function(t){angular.isObject(t)||(e.$error=t,f.error=t)},f.catchError=function(e,t){return angular.isObject(e)&&t!==!0?u.when(e).then(angular.bind(this,function(e){this.catchError(e,!0)}),angular.bind(this,function(e){this.catchError(e,!0)})):t&&angular.isObject(e)&&e.status&&200!==e.status&&e.data&&angular.isString(e.data)?(this.setError(e.data),e=e.data):angular.isString(e)&&this.setError(e),e},f.save=function(){c.assign(e.$parent,angular.copy(f.scope.$data))},f.handleEmpty=function(){var t=c(e.$parent),n=null===t||void 0===t||""===t||angular.isArray(t)&&0===t.length;r.toggleClass("editable-empty",n)},f.autosubmit=angular.noop,f.onshow=angular.noop,f.onhide=angular.noop,f.oncancel=angular.noop,f.onbeforesave=angular.noop,f.onaftersave=angular.noop}return n.$inject=["$scope","$attrs","$element","$parse","editableThemes","editableOptions","$rootScope","$compile","$q"],n}]),angular.module("xeditable").factory("editableDirectiveFactory",["$parse","$compile","editableThemes","$rootScope","$document","editableController","editableFormController",function(e,t,n,r,a,i,o){return function(t){return{restrict:"A",scope:!0,require:[t.directiveName,"?^form"],controller:i,link:function(n,i,s,l){var u,c=l[0],d=!1;if(l[1])u=l[1],d=!0;else if(s.eForm){var f=e(s.eForm)(n);if(f)u=f,d=!0;else for(var p=0;p=0&&e.splice(n,1),t},camelToDash:function(e){var t=/[A-Z]/g;return e.replace(t,function(e,t){return(t?"-":"")+e.toLowerCase()})},dashToCamel:function(e){var t=/([\:\-\_]+(.))/g,n=/^moz([A-Z])/;return e.replace(t,function(e,t,n,r){return r?n.toUpperCase():n}).replace(n,"Moz$1")}}}]),angular.module("xeditable").factory("editableNgOptionsParser",[function(){function e(e){var n;if(!(n=e.match(t)))throw"ng-options parse error";var r,a=n[2]||n[1],i=n[4]||n[6],o=n[5],s=(n[3]||"",n[2]?n[1]:i),l=n[7],u=n[8],c=u?n[8]:null;return void 0===o?(r=i+" in "+l,void 0!==u&&(r+=" track by "+c)):r="("+o+", "+i+") in "+l,{ngRepeat:r,locals:{valueName:i,keyName:o,valueFn:s,displayFn:a}}}var t=/^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/;return e}]),angular.module("xeditable").factory("editableThemes",function(){var e={"default":{formTpl:'
',noformTpl:'',controlsTpl:'',inputTpl:"",errorTpl:'
',buttonsTpl:'',submitTpl:'',cancelTpl:''},bs2:{formTpl:'
',noformTpl:'',controlsTpl:'
',inputTpl:"",errorTpl:'
',buttonsTpl:'',submitTpl:'',cancelTpl:''},bs3:{formTpl:'
',noformTpl:'',controlsTpl:'
',inputTpl:"",errorTpl:'
',buttonsTpl:'',submitTpl:'',cancelTpl:'',buttonsClass:"",inputClass:"",postrender:function(){switch(this.directiveName){case"editableText":case"editableSelect":case"editableTextarea":case"editableEmail":case"editableTel":case"editableNumber":case"editableUrl":case"editableSearch":case"editableDate":case"editableDatetime":case"editableTime":case"editableMonth":case"editableWeek":if(this.inputEl.addClass("form-control"),this.theme.inputClass){if(this.inputEl.attr("multiple")&&("input-sm"===this.theme.inputClass||"input-lg"===this.theme.inputClass))break;this.inputEl.addClass(this.theme.inputClass)}}this.buttonsEl&&this.theme.buttonsClass&&this.buttonsEl.find("button").addClass(this.theme.buttonsClass)}}};return e}),!function(e,t,n){"use strict";function r(e){return F(e)?e:Object.keys(e).map(function(t){return e[t]})}function a(e){return null===e}function i(e,t){var r=Object.keys(e);return-1==r.map(function(r){return t[r]!==n&&t[r]==e[r]}).indexOf(!1)}function o(e,t){if(""===t)return e;var n=e.indexOf(t.charAt(0));return-1===n?!1:o(e.substr(n+1),t.substr(1))}function s(e,t,n){var r=0;return e.filter(function(e){var a=E(n)?t>r&&n(e):t>r;return r=a?r+1:r,a})}function l(e,t,n){return n.round(e*n.pow(10,t))/n.pow(10,t)}function u(e,t,n){t=t||[];var r=Object.keys(e);return r.forEach(function(r){if(D(e[r])&&!F(e[r])){var a=n?n+"."+r:n;u(e[r],t,a||r)}else{var i=n?n+"."+r:r;t.push(i)}}),t}function c(e){return e&&e.$evalAsync&&e.$watch}function d(){return function(e,t){return e>t}}function f(){return function(e,t){return e>=t}}function p(){return function(e,t){return t>e}}function m(){return function(e,t){return t>=e}}function b(){return function(e,t){return e==t}}function h(){return function(e,t){return e!=t}}function _(){return function(e,t){return e===t}}function v(){return function(e,t){return e!==t}}function g(e){return function(t,n){return t=D(t)?r(t):t,!F(t)||j(n)?!1:t.some(function(t){return D(t)||C(n)?e(n)(t):t===n})}}function $(e,t){return t=t||0,t>=e.length?e:F(e[t])?$(e.slice(0,t).concat(e[t],e.slice(t+1)),t):$(e,t+1)}function y(e){return function(t,n){function a(e,t){return j(t)?!1:e.some(function(e){return S(e,t)})}if(t=D(t)?r(t):t,!F(t))return t;var i=[],o=e(n);return t.filter(j(n)?function(e,t,n){return n.indexOf(e)===t}:function(e){var t=o(e);return a(i,t)?!1:(i.push(t),!0)})}}function w(e,t,n){return t?e+n+w(e,--t,n):e}var E=t.isDefined,j=t.isUndefined,C=t.isFunction,R=t.isString,T=t.isNumber,D=t.isObject,F=t.isArray,x=t.forEach,N=t.extend,O=t.copy,S=t.equals;String.prototype.contains||(String.prototype.contains=function(){return-1!==String.prototype.indexOf.apply(this,arguments)}),t.module("a8m.angular",[]).filter("isUndefined",function(){return function(e){return t.isUndefined(e)}}).filter("isDefined",function(){return function(e){return t.isDefined(e)}}).filter("isFunction",function(){return function(e){return t.isFunction(e)}}).filter("isString",function(){return function(e){return t.isString(e)}}).filter("isNumber",function(){return function(e){return t.isNumber(e)}}).filter("isArray",function(){return function(e){return t.isArray(e)}}).filter("isObject",function(){return function(e){return t.isObject(e)}}).filter("isEqual",function(){return function(e,n){return t.equals(e,n)}}),t.module("a8m.conditions",[]).filter({isGreaterThan:d,">":d,isGreaterThanOrEqualTo:f,">=":f,isLessThan:p,"<":p,isLessThanOrEqualTo:m,"<=":m,isEqualTo:b,"==":b,isNotEqualTo:h,"!=":h,isIdenticalTo:_,"===":_,isNotIdenticalTo:v,"!==":v}),t.module("a8m.is-null",[]).filter("isNull",function(){return function(e){return a(e)}}),t.module("a8m.after-where",[]).filter("afterWhere",function(){return function(e,t){if(e=D(e)?r(e):e,!F(e)||j(t))return e;var n=e.map(function(e){return i(t,e)}).indexOf(!0);return e.slice(-1===n?0:n)}}),t.module("a8m.after",[]).filter("after",function(){return function(e,t){return e=D(e)?r(e):e,F(e)?e.slice(t):e}}),t.module("a8m.before-where",[]).filter("beforeWhere",function(){return function(e,t){if(e=D(e)?r(e):e,!F(e)||j(t))return e;var n=e.map(function(e){return i(t,e)}).indexOf(!0);return e.slice(0,-1===n?e.length:++n)}}),t.module("a8m.before",[]).filter("before",function(){return function(e,t){return e=D(e)?r(e):e,F(e)?e.slice(0,t?--t:t):e}}),t.module("a8m.concat",[]).filter("concat",[function(){return function(e,t){if(j(t))return e;if(F(e))return e.concat(D(t)?r(t):t);if(D(e)){var n=r(e);return n.concat(D(t)?r(t):t)}return e}}]),t.module("a8m.contains",[]).filter({contains:["$parse",g],some:["$parse",g]}),t.module("a8m.count-by",[]).filter("countBy",["$parse",function(e){return function(t,n){var a,i={},o=e(n);return t=D(t)?r(t):t,!F(t)||j(n)?t:(t.forEach(function(e){a=o(e),i[a]||(i[a]=0),i[a]++}),i)}}]),t.module("a8m.defaults",[]).filter("defaults",["$parse",function(e){return function(t,n){if(t=D(t)?r(t):t,!F(t)||!D(n))return t;var a=u(n);return t.forEach(function(t){a.forEach(function(r){var a=e(r),i=a.assign;j(a(t))&&i(t,a(n))})}),t}}]),t.module("a8m.every",[]).filter("every",["$parse",function(e){return function(t,n){return t=D(t)?r(t):t,!F(t)||j(n)?!0:t.every(function(t){return D(t)||C(n)?e(n)(t):t===n})}}]),t.module("a8m.filter-by",[]).filter("filterBy",["$parse",function(e){return function(t,a,i){var o;return i=R(i)||T(i)?String(i).toLowerCase():n,t=D(t)?r(t):t,!F(t)||j(i)?t:t.filter(function(t){return a.some(function(n){if(~n.indexOf("+")){var r=n.replace(new RegExp("\\s","g"),"").split("+");o=r.reduce(function(n,r,a){return 1===a?e(n)(t)+" "+e(r)(t):n+" "+e(r)(t)})}else o=e(n)(t);return R(o)||T(o)?String(o).toLowerCase().contains(i):!1})})}}]),t.module("a8m.first",[]).filter("first",["$parse",function(e){return function(t){var a,i,o;return t=D(t)?r(t):t,F(t)?(o=Array.prototype.slice.call(arguments,1),a=T(o[0])?o[0]:1,i=T(o[0])?T(o[1])?n:o[1]:o[0],o.length?s(t,a,i?e(i):i):t[0]):t}}]),t.module("a8m.flatten",[]).filter("flatten",function(){return function(e,t){return t=t||!1,e=D(e)?r(e):e,F(e)?t?[].concat.apply([],e):$(e,0):e}}),t.module("a8m.fuzzy-by",[]).filter("fuzzyBy",["$parse",function(e){return function(t,n,a,i){var s,l,u=i||!1;return t=D(t)?r(t):t,!F(t)||j(n)||j(a)?t:(l=e(n),t.filter(function(e){return s=l(e),R(s)?(s=u?s:s.toLowerCase(),a=u?a:a.toLowerCase(),o(s,a)!==!1):!1}))}}]),t.module("a8m.fuzzy",[]).filter("fuzzy",function(){return function(e,t,n){function a(e,t){var n,r,a=Object.keys(e);return 0=0&&T(t)&&isFinite(t)?1024>t?l(t,n,e)+" B":1048576>t?l(t/1024,n,e)+" KB":1073741824>t?l(t/1048576,n,e)+" MB":l(t/1073741824,n,e)+" GB":"NaN"}}]),t.module("a8m.math.degrees",["a8m.math"]).filter("degrees",["$math",function(e){return function(t,n){if(T(n)&&isFinite(n)&&n%1===0&&n>=0&&T(t)&&isFinite(t)){var r=180*t/e.PI;return e.round(r*e.pow(10,n))/e.pow(10,n)}return"NaN"}}]),t.module("a8m.math.kbFmt",["a8m.math"]).filter("kbFmt",["$math",function(e){return function(t,n){return T(n)&&isFinite(n)&&n%1===0&&n>=0&&T(t)&&isFinite(t)?1024>t?l(t,n,e)+" KB":1048576>t?l(t/1024,n,e)+" MB":l(t/1048576,n,e)+" GB":"NaN"}}]),t.module("a8m.math",[]).factory("$math",["$window",function(e){return e.Math}]),t.module("a8m.math.max",["a8m.math"]).filter("max",["$math","$parse",function(e,t){function n(n,r){var a=n.map(function(e){return t(r)(e)});return a.indexOf(e.max.apply(e,a))}return function(t,r){return F(t)?j(r)?e.max.apply(e,t):t[n(t,r)]:t}}]),t.module("a8m.math.min",["a8m.math"]).filter("min",["$math","$parse",function(e,t){function n(n,r){var a=n.map(function(e){return t(r)(e)});return a.indexOf(e.min.apply(e,a))}return function(t,r){return F(t)?j(r)?e.min.apply(e,t):t[n(t,r)]:t}}]),t.module("a8m.math.percent",["a8m.math"]).filter("percent",["$math","$window",function(e,t){return function(n,r,a){var i=R(n)?t.Number(n):n;return r=r||100,a=a||!1,!T(i)||t.isNaN(i)?n:a?e.round(i/r*100):i/r*100}}]),t.module("a8m.math.radians",["a8m.math"]).filter("radians",["$math",function(e){return function(t,n){if(T(n)&&isFinite(n)&&n%1===0&&n>=0&&T(t)&&isFinite(t)){var r=3.14159265359*t/180;return e.round(r*e.pow(10,n))/e.pow(10,n)}return"NaN"}}]),t.module("a8m.math.radix",[]).filter("radix",function(){return function(e,t){var n=/^[2-9]$|^[1-2]\d$|^3[0-6]$/;return T(e)&&n.test(t)?e.toString(t).toUpperCase():e}}),t.module("a8m.math.shortFmt",["a8m.math"]).filter("shortFmt",["$math",function(e){return function(t,n){return T(n)&&isFinite(n)&&n%1===0&&n>=0&&T(t)&&isFinite(t)?1e3>t?t:1e6>t?l(t/1e3,n,e)+" K":1e9>t?l(t/1e6,n,e)+" M":l(t/1e9,n,e)+" B":"NaN"}}]),t.module("a8m.math.sum",[]).filter("sum",function(){return function(e,t){return F(e)?e.reduce(function(e,t){return e+t},t||0):e}}),t.module("a8m.ends-with",[]).filter("endsWith",function(){return function(e,t,n){var r,a=n||!1;return!R(e)||j(t)?e:(e=a?e:e.toLowerCase(),r=e.length-t.length,-1!==e.indexOf(a?t:t.toLowerCase(),r))}}),t.module("a8m.latinize",[]).filter("latinize",[function(){function e(e){return e.replace(/[^\u0000-\u007E]/g,function(e){return n[e]||e})}for(var t=[{base:"A",letters:"AⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ"},{base:"AA",letters:"Ꜳ"},{base:"AE",letters:"ÆǼǢ"},{base:"AO",letters:"Ꜵ"},{base:"AU",letters:"Ꜷ"},{base:"AV",letters:"ꜸꜺ"},{base:"AY",letters:"Ꜽ"},{base:"B",letters:"BⒷBḂḄḆɃƂƁ"},{base:"C",letters:"CⒸCĆĈĊČÇḈƇȻꜾ"},{base:"D",letters:"DⒹDḊĎḌḐḒḎĐƋƊƉꝹ"},{base:"DZ",letters:"DZDŽ"},{base:"Dz",letters:"DzDž"},{base:"E",letters:"EⒺEÈÉÊỀẾỄỂẼĒḔḖĔĖËẺĚȄȆẸỆȨḜĘḘḚƐƎ"},{base:"F",letters:"FⒻFḞƑꝻ"},{base:"G",letters:"GⒼGǴĜḠĞĠǦĢǤƓꞠꝽꝾ"},{base:"H",letters:"HⒽHĤḢḦȞḤḨḪĦⱧⱵꞍ"},{base:"I",letters:"IⒾIÌÍÎĨĪĬİÏḮỈǏȈȊỊĮḬƗ"},{base:"J",letters:"JⒿJĴɈ"},{base:"K",letters:"KⓀKḰǨḲĶḴƘⱩꝀꝂꝄꞢ"},{base:"L",letters:"LⓁLĿĹĽḶḸĻḼḺŁȽⱢⱠꝈꝆꞀ"},{base:"LJ",letters:"LJ"},{base:"Lj",letters:"Lj"},{base:"M",letters:"MⓂMḾṀṂⱮƜ"},{base:"N",letters:"NⓃNǸŃÑṄŇṆŅṊṈȠƝꞐꞤ"},{base:"NJ",letters:"NJ"},{base:"Nj",letters:"Nj"},{base:"O",letters:"OⓄOÒÓÔỒỐỖỔÕṌȬṎŌṐṒŎȮȰÖȪỎŐǑȌȎƠỜỚỠỞỢỌỘǪǬØǾƆƟꝊꝌ"},{base:"OI",letters:"Ƣ"},{base:"OO",letters:"Ꝏ"},{base:"OU",letters:"Ȣ"},{base:"OE",letters:"ŒŒ"},{base:"oe",letters:"œœ"},{base:"P",letters:"PⓅPṔṖƤⱣꝐꝒꝔ"},{base:"Q",letters:"QⓆQꝖꝘɊ"},{base:"R",letters:"RⓇRŔṘŘȐȒṚṜŖṞɌⱤꝚꞦꞂ"},{base:"S",letters:"SⓈSẞŚṤŜṠŠṦṢṨȘŞⱾꞨꞄ"},{base:"T",letters:"TⓉTṪŤṬȚŢṰṮŦƬƮȾꞆ"},{base:"TZ",letters:"Ꜩ"},{base:"U",letters:"UⓊUÙÚÛŨṸŪṺŬÜǛǗǕǙỦŮŰǓȔȖƯỪỨỮỬỰỤṲŲṶṴɄ"},{base:"V",letters:"VⓋVṼṾƲꝞɅ"},{base:"VY",letters:"Ꝡ"},{base:"W",letters:"WⓌWẀẂŴẆẄẈⱲ"},{base:"X",letters:"XⓍXẊẌ"},{base:"Y",letters:"YⓎYỲÝŶỸȲẎŸỶỴƳɎỾ"},{base:"Z",letters:"ZⓏZŹẐŻŽẒẔƵȤⱿⱫꝢ"},{base:"a",letters:"aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐ"},{base:"aa",letters:"ꜳ"},{base:"ae",letters:"æǽǣ"},{base:"ao",letters:"ꜵ"},{base:"au",letters:"ꜷ"},{base:"av",letters:"ꜹꜻ"},{base:"ay",letters:"ꜽ"},{base:"b",letters:"bⓑbḃḅḇƀƃɓ"},{base:"c",letters:"cⓒcćĉċčçḉƈȼꜿↄ"},{base:"d",letters:"dⓓdḋďḍḑḓḏđƌɖɗꝺ"},{base:"dz",letters:"dzdž"},{base:"e",letters:"eⓔeèéêềếễểẽēḕḗĕėëẻěȅȇẹệȩḝęḙḛɇɛǝ"},{base:"f",letters:"fⓕfḟƒꝼ"},{base:"g",letters:"gⓖgǵĝḡğġǧģǥɠꞡᵹꝿ"},{base:"h",letters:"hⓗhĥḣḧȟḥḩḫẖħⱨⱶɥ"},{base:"hv",letters:"ƕ"},{base:"i",letters:"iⓘiìíîĩīĭïḯỉǐȉȋịįḭɨı"},{base:"j",letters:"jⓙjĵǰɉ"},{base:"k",letters:"kⓚkḱǩḳķḵƙⱪꝁꝃꝅꞣ"},{base:"l",letters:"lⓛlŀĺľḷḹļḽḻſłƚɫⱡꝉꞁꝇ"},{base:"lj",letters:"lj"},{base:"m",letters:"mⓜmḿṁṃɱɯ"},{base:"n",letters:"nⓝnǹńñṅňṇņṋṉƞɲʼnꞑꞥ"},{base:"nj",letters:"nj"},{base:"o",letters:"oⓞoòóôồốỗổõṍȭṏōṑṓŏȯȱöȫỏőǒȍȏơờớỡởợọộǫǭøǿɔꝋꝍɵ"},{base:"oi",letters:"ƣ"},{base:"ou",letters:"ȣ"},{base:"oo",letters:"ꝏ"},{base:"p",letters:"pⓟpṕṗƥᵽꝑꝓꝕ"},{base:"q",letters:"qⓠqɋꝗꝙ"},{base:"r",letters:"rⓡrŕṙřȑȓṛṝŗṟɍɽꝛꞧꞃ"},{base:"s",letters:"sⓢsßśṥŝṡšṧṣṩșşȿꞩꞅẛ"},{base:"t",letters:"tⓣtṫẗťṭțţṱṯŧƭʈⱦꞇ" +},{base:"tz",letters:"ꜩ"},{base:"u",letters:"uⓤuùúûũṹūṻŭüǜǘǖǚủůűǔȕȗưừứữửựụṳųṷṵʉ"},{base:"v",letters:"vⓥvṽṿʋꝟʌ"},{base:"vy",letters:"ꝡ"},{base:"w",letters:"wⓦwẁẃŵẇẅẘẉⱳ"},{base:"x",letters:"xⓧxẋẍ"},{base:"y",letters:"yⓨyỳýŷỹȳẏÿỷẙỵƴɏỿ"},{base:"z",letters:"zⓩzźẑżžẓẕƶȥɀⱬꝣ"}],n={},r=0;r<]*>/g,""):e}}),t.module("a8m.test",[]).filter("test",function(){return function(e,t,n){var r=new RegExp(t,n);return R(e)?r.test(e):e}}),t.module("a8m.trim",[]).filter("trim",function(){return function(e,t){var n=t||"\\s";return R(e)?e.replace(new RegExp("^"+n+"+|"+n+"+$","g"),""):e}}),t.module("a8m.truncate",[]).filter("truncate",function(){return function(e,t,n,r){return t=j(t)?e.length:t,r=r||!1,n=n||"",!R(e)||e.length<=t?e:e.substring(0,r?-1===e.indexOf(" ",t)?e.length:e.indexOf(" ",t):t)+n}}),t.module("a8m.ucfirst",[]).filter("ucfirst",[function(){return function(e){return R(e)?e.split(" ").map(function(e){return e.charAt(0).toUpperCase()+e.substring(1)}).join(" "):e}}]),t.module("a8m.uri-component-encode",[]).filter("uriComponentEncode",["$window",function(e){return function(t){return R(t)?e.encodeURIComponent(t):t}}]),t.module("a8m.uri-encode",[]).filter("uriEncode",["$window",function(e){return function(t){return R(t)?e.encodeURI(t):t}}]),t.module("a8m.wrap",[]).filter("wrap",function(){return function(e,t,n){return R(e)&&E(t)?[t,e,n||t].join(""):e}}),t.module("a8m.filter-watcher",[]).provider("filterWatcher",function(){this.$get=["$window","$rootScope",function(e,n){function r(e,n){return[e,t.toJson(n)].join("#").replace(/"/g,"")}function a(e){var t=e.targetScope.$id;x(d[t],function(e){delete u[e]}),delete d[t]}function i(){f(function(){n.$$phase||(u={})})}function o(e,t){var n=e.$id;return j(d[n])&&(e.$on("$destroy",a),d[n]=[]),d[n].push(t)}function s(e,t){var n=r(e,t);return u[n]}function l(e,t,n,a){var s=r(e,t);return u[s]=a,c(n)?o(n,s):i(),a}var u={},d={},f=e.setTimeout;return{isMemoized:s,memoize:l}}]}),t.module("angular.filter",["a8m.ucfirst","a8m.uri-encode","a8m.uri-component-encode","a8m.slugify","a8m.latinize","a8m.strip-tags","a8m.stringular","a8m.truncate","a8m.starts-with","a8m.ends-with","a8m.wrap","a8m.trim","a8m.ltrim","a8m.rtrim","a8m.repeat","a8m.test","a8m.match","a8m.to-array","a8m.concat","a8m.contains","a8m.unique","a8m.is-empty","a8m.after","a8m.after-where","a8m.before","a8m.before-where","a8m.defaults","a8m.where","a8m.reverse","a8m.remove","a8m.remove-with","a8m.group-by","a8m.count-by","a8m.search-field","a8m.fuzzy-by","a8m.fuzzy","a8m.omit","a8m.pick","a8m.every","a8m.filter-by","a8m.xor","a8m.map","a8m.first","a8m.last","a8m.flatten","a8m.join","a8m.math","a8m.math.max","a8m.math.min","a8m.math.percent","a8m.math.radix","a8m.math.sum","a8m.math.degrees","a8m.math.radians","a8m.math.byteFmt","a8m.math.kbFmt","a8m.math.shortFmt","a8m.angular","a8m.conditions","a8m.is-null","a8m.filter-watcher"])}(window,window.angular),define("job-roles/controllers/controllers",["common/angular"],function(e){"use strict";return e.module("hrjobroles.controllers",[])}),define("job-roles/directives/directives",["common/angular"],function(e){"use strict";return e.module("hrjobroles.directives",[])}),define("job-roles/filters/filters",["common/angular"],function(e){"use strict";return e.module("hrjobroles.filters",[])}),define("job-roles/services/services",["common/angular"],function(e){"use strict";return e.module("hrjobroles.services",[])}),define("job-roles/controllers/hr-job-roles-controller",["common/angular","job-roles/controllers/controllers","common/moment","common/filters/angular-date/format-date"],function(e,t,n){"use strict";t.controller("HRJobRolesController",["$scope","$log","$routeParams","$route","$timeout","$filter","$q","HR_settings","HRJobRolesService","DateValidation","HRJobRolesServiceFilters",function(t,r,a,i,o,s,l,u,c,d,f){function p(e,t,n){var n=n?n:{start:"start_date",end:"end_date"};e[n.start]=t.start?D(t.start,Date):null,e[n.end]=t.end?D(t.end,Date):null}function m(e){var n=t.edit_data[e].job_contract_id;if(t.checkIfDatesAreCustom(t.edit_data[e].start_date,t.edit_data[e].end_date)){var r=C(n),a=r.revisions.some(function(n){return n.period_start_date===D(t.edit_data[e].start_date)&&n.period_end_date===D(t.edit_data[e].end_date)});a&&(p(t.edit_data[e],{start:r.start_date,end:r.end_date}),t.updateRole(e))}else p(t.edit_data[e],{start:t.edit_data[e].start_date,end:t.edit_data[e].end_date})}function b(e,n){t.edit_data.hasOwnProperty(e)&&("funders"===n&&(t.edit_data[e][n]=f.issetFunder(t.edit_data[e][n])),"cost_centers"===n&&(t.edit_data[e][n]=f.issetCostCentre(t.edit_data[e][n])))}function h(){c.getContactList().then(function(e){if(1===e.is_error)N.message_type="alert-danger",N.message="Cannot get contact lit!";else{for(var t=[],n={},r=0;r-1&&t.start.push(e),r.indexOf("end_date")>-1&&t.end.push(e)}),d.validate(e.start,e.end,e.contractStart,e.contractEnd),0===n}function C(e){return T.contractsData[e]}function R(e){var t=D(e,"YYYY-MM-DD");return"Unspecified"!==t?t:null}r.debug("Controller: HRJobRolesController"),t.format=u.DATE_FORMAT;var T=this,D=s("formatDate"),F=["funders","cost_centers"];t.present_job_roles=[],t.past_job_roles=[],t.dpOpen=function(e){e.preventDefault(),e.stopPropagation(),t.picker.opened=!0};var x=function(e){return"0000-00-00 00:00:00"===e};t.onContractSelected=function(){var e=C(t.edit_data.new_role_id.job_contract_id),n=t.checkIfDatesAreCustom(t.edit_data.new_role_id.newStartDate,t.edit_data.new_role_id.newEndDate);p(t.edit_data.new_role_id,{start:n?t.edit_data.new_role_id.newStartDate:e.start_date,end:n?t.edit_data.new_role_id.newEndDate:e.end_date},{start:"newStartDate",end:"newEndDate"})},t.checkIfDatesAreCustom=function(t,n){x(t)&&(t=null),x(n)&&(n=null);var r=!0;return t?(e.forEach(T.contractsData,function(e){D(t)===D(e.start_date)&&D(n)===D(e.end_date)&&(r=!1)}),r):!1},t.onContractEdited=function(e,n){var r=e||t.edit_data[n].job_contract_id,a=C(r),i=t.checkIfDatesAreCustom(t.edit_data[n].start_date,t.edit_data[n].end_date);i?p(t.edit_data[n],{start:t.edit_data[n].start_date,end:t.edit_data[n].end_date}):p(t.edit_data[n],{start:a.start_date,end:a.end_date})},t.validateTitle=function(e){return"title"===e||" "===e?"Title cannot be title!":void 0},t.validateRole=function(e){e.start_date.$error.custom=[],e.end_date.$error.custom=[];var t=C(e.contract.$viewValue),n=j({start:e.start_date.$viewValue,end:e.end_date.$viewValue,contractStart:t.start_date,contractEnd:t.end_date},{start:e.start_date.$error.custom,end:e.end_date.$error.custom});return n?!0:"Error"},t.today=function(){t.CalendarShow.newStartDate=!1,t.CalendarShow.newEndDate=!1,t.CalendarShow.start_date=!1,t.CalendarShow.end_date=!1},t.isOpen=function(e){return!!t.CalendarShow[e]},t.CalendarShow=[],t.today(),t.open=function(e){t.CalendarShow[e]=!0},t.select=function(e){t.CalendarShow[e]=!1},t.collapsedRows=[],t.view_tab=[],t.edit_data={},t.add_new_role_url=t.$parent.pathBaseUrl+t.$parent.pathIncludeTpl+"add_new_role.html",t.job_role_panel_url=t.$parent.pathBaseUrl+t.$parent.pathIncludeTpl+"job_role_panel.html",t.contractsData=[],t.LevelsData={},t.LocationsData={},t.RegionsData={},t.DepartmentsData={},t.contactList=[],t.contactListObject={},t.changeTab=function(e,n){t.view_tab[e]=n},t.isTab=function(e,n){return t.view_tab[e]==n},t.isRowCollapsed=function(e){return!!t.collapsedRows[e]},t.collapseRow=function(e){t.collapsedRows[e]=!t.collapsedRows[e]},t.initData=function(e,r,a){if("undefined"==typeof t.edit_data[e]&&(t.edit_data[e]={}),"funders"===r){t.edit_data[e].funders=[];for(var i=f.isNotUndefined(a.funder.split("|")),o=a.funder_val_type.split("|"),s=a.percent_pay_funder.split("|"),l=a.amount_pay_funder.split("|"),u=0;uc;c++)e.funders[c]&&(n+=e.funders[c].funder_id.id+"|",r+=e.funders[c].type+"|",a+=e.funders[c].percentage+"|",i+=e.funders[c].amount+"|");if("undefined"!=typeof e.cost_centers)for(c=0,d=e.cost_centers.length;d>c;c++)e.cost_centers[c]&&(o+=e.cost_centers[c].cost_centre_id+"|",s+=e.cost_centers[c].type+"|",l+=e.cost_centers[c].percentage+"|",u+=e.cost_centers[c].amount+"|");var f=t.defer();return this.getNewJobRole(e.job_contract_id).then(function(t){return CRM.api3("HrJobRoles","update",{id:t.id,sequential:1,job_contract_id:e.job_contract_id,title:e.title,description:e.description,funder:n,funder_val_type:r,percent_pay_funder:a,amount_pay_funder:i,cost_center:o,cost_center_val_type:s,percent_pay_cost_center:l,amount_pay_cost_center:u,level_type:e.level,location:e.location,region:e.region,department:e.department,start_date:e.newStartDate,end_date:e.newEndDate||null})}).then(function(e){f.resolve(e)},function(e){f.reject("An error occured while adding items")}),f.promise},updateJobRole:function(e,n){var r="|",a="|",i="|",o="|",s="|",l="|",u="|",c="|";if("undefined"!=typeof n.funders)for(var d=0,f=n.funders.length;f>d;d++)n.funders[d]&&(r+=n.funders[d].funder_id.id+"|",a+=n.funders[d].type+"|",i+=n.funders[d].percentage+"|",o+=n.funders[d].amount+"|");if("undefined"!=typeof n.cost_centers)for(var d=0,f=n.cost_centers.length;f>d;d++)n.cost_centers[d]&&(s+=n.cost_centers[d].cost_centre_id+"|",l+=n.cost_centers[d].type+"|",u+=n.cost_centers[d].percentage+"|",c+=n.cost_centers[d].amount+"|");var p=t.defer();return CRM.api3("HrJobRoles","create",{sequential:1,id:e,job_contract_id:n.job_contract_id,title:n.title,description:n.description,status:n.status,funder:r,funder_val_type:a,percent_pay_funder:i,amount_pay_funder:o,cost_center:s,cost_center_val_type:l,percent_pay_cost_center:u,amount_pay_cost_center:c,level_type:n.level,location:n.location,region:n.region,start_date:n.start_date,end_date:n.end_date||0,department:n.department}).done(function(e){p.resolve(e)}).error(function(e){p.reject("An error occured while updating items")}),p.promise},getContactList:function(e){var n=t.defer();return CRM.api3("Contact","get",{sequential:1,"return":"id,sort_name"}).done(function(e){n.resolve(e)}).error(function(e){n.reject("An error occured while fetching items")}),n.promise},getOptionValues:function(e){var n=t.defer(),r={};return CRM.api3("OptionGroup","get",{sequential:1,name:{IN:e},options:{limit:1e3}}).done(function(e){if(1!==e.is_error){var t=[];angular.forEach(e.values,function(e,n){r[e.name]=e.id,t.push(e.id)}),CRM.api3("OptionValue","get",{sequential:1,option_group_id:{IN:t},options:{limit:1e3}}).done(function(e){e.optionGroupData=r,n.resolve(e)}).error(function(e){n.reject("An error occured while fetching items")})}}),n.promise},getNewJobRole:function(e){return CRM.api3("HrJobRoles","create",{sequential:1,job_contract_id:e,title:""})}}}])}),define("job-roles/services/date-validation",["job-roles/services/services","common/moment"],function(e,t){e.factory("DateValidation",["HR_settings",function(e){function n(e,t){e.isValid()||l("Date is not valid!!",t)}function r(e,t){e.isSameOrAfter(t)&&l("Start Date cannot be the same as or after the End Date.",["start_date","end_date"])}function a(e,t){e.isBefore(t)&&l("Start Date cannot be lower than Contract Start Date.",["start_date"])}function i(e,t){e.isAfter(t)&&l("Start Date cannot be higher than Contract End Date.",["start_date"])}function o(e,t){e.isAfter(t)&&l("End Date cannot be higher than Contract End Date.",["end_date"])}function s(e,n){return e instanceof Date&&(e=t(e).valueOf()),t(e,n,!0).startOf("day")}var l=function(e,t){throw new Error(e,t[0])},u={dateFormats:["x","YYYY-MM-DD"],setErrorCallback:function(e){if("function"!=typeof e)throw new TypeError("Error callback must be a function.");l=e},validate:function(e,t,l,u){e=s(e,this.dateFormats),l=s(l,this.dateFormats),u=s(u,this.dateFormats),n(e,["start_date"]),i(e,u),a(e,l),(0===t||t)&&(t=s(t,this.dateFormats),n(t,["end_date"]),o(t,u),r(e,t))}};return e.DATE_FORMAT&&u.dateFormats.push(e.DATE_FORMAT.toUpperCase()),u}])}),define("job-roles/services/hr-job-roles-service-filter",["job-roles/services/services"],function(e){"use strict";e.factory("HRJobRolesServiceFilters",function(){return{isNotUndefined:function(e){try{return e.constructor===Array?e.filter(function(e){return"undefined"!==e&&void 0!==e}):e}catch(t){return e}},issetCostCentre:function(e){try{return e.constructor===Array?e.filter(function(e){return""!==e.cost_centre_id&&("1"===e.type&&parseInt(e.percentage)>0||"0"===e.type&&parseInt(e.amount)>0)}):e}catch(t){return e}},issetFunder:function(e){try{return e.constructor===Array?e.filter(function(e){return""!==e.funder_id&&("1"===e.type&&parseInt(e.percentage)>0||"0"===e.type&&parseInt(e.amount)>0)}):e}catch(t){return e}}}})}),define("job-roles/directives/example",["job-roles/directives/directives"],function(e){"use strict";e.directive("example",["$rootScope","$log",function(e,t){return t.debug("Directive: example"),{link:function(e,t,n){}}}])}),define("job-roles/app",["job-roles/vendor/angular-editable","job-roles/vendor/angular-filter","job-roles/controllers/controllers","job-roles/directives/directives","job-roles/filters/filters","job-roles/services/services","job-roles/controllers/hr-job-roles-controller","job-roles/services/hr-job-roles-service","job-roles/services/date-validation","job-roles/services/hr-job-roles-service-filter","job-roles/directives/example","common/services/angular-date/date-format","common/modules/routers/compu-ng-route","common/directives/angular-date/date-input"],function(){"use strict";angular.module("hrjobroles",["ngAnimate","common.angularDate","compuNgRoute","xeditable","angular.filter","ngResource","ui.bootstrap","hrjobroles.controllers","hrjobroles.directives","hrjobroles.filters","hrjobroles.services"]).constant("settings",{classNamePrefix:"hrjobroles-",contactId:decodeURIComponent((new RegExp("[?|&]cid=([^&;]+?)(&|#|;|$)").exec(location.search)||[,""])[1].replace(/\+/g,"%20"))||null,debug:!0,pathApp:"",pathRest:CRM.url("civicrm/ajax/rest"),pathBaseUrl:CRM.vars.hrjobroles.baseURL+"/",pathTpl:"views/",pathIncludeTpl:"views/include/"}).config(["settings","$routeProvider","$resourceProvider","$httpProvider","$logProvider",function(e,t,n,r,a){a.debugEnabled(e.debug),t.resolveForAll({format:["DateFormat",function(e){return e.getDateFormat()}]}).when("/",{templateUrl:e.pathBaseUrl+e.pathTpl+"mainTemplate.html?v=1",resolve:{},controller:"HRJobRolesController",controllerAs:"jobroles"}).otherwise({redirectTo:"/"}),n.defaults.stripTrailingSlashes=!1,r.defaults.headers.common["X-Requested-With"]="XMLHttpRequest"}]).run(["settings","$rootScope","$q","$log","editableOptions",function(e,t,n,r,a){r.debug("app.run"),a.theme="bs3",t.contactId=e.contactId,t.pathBaseUrl=e.pathBaseUrl,t.pathTpl=e.pathTpl,t.pathIncludeTpl=e.pathIncludeTpl,t.prefix=e.classNamePrefix}])}),function(){var e=CRM.vars.hrjobroles.baseURL+"/js/src/job-roles";require.config({urlArgs:"bust="+(new Date).getTime(),paths:{"job-roles":e,"job-roles/vendor/angular-editable":e+"/vendor/angular/xeditable.min","job-roles/vendor/angular-filter":e+"/vendor/angular/angular-filter.min"}}),require(["job-roles/app"],function(e){"use strict";document.addEventListener("hrjobrolesLoad",function(){angular.bootstrap(document.getElementById("hrjobroles"),["hrjobroles"])})})}(require); \ No newline at end of file diff --git a/com.civicrm.hrjobroles/js/src/job-roles/controllers/hr-job-roles-controller.js b/com.civicrm.hrjobroles/js/src/job-roles/controllers/hr-job-roles-controller.js index 2d121341ee1..8762ba43f59 100755 --- a/com.civicrm.hrjobroles/js/src/job-roles/controllers/hr-job-roles-controller.js +++ b/com.civicrm.hrjobroles/js/src/job-roles/controllers/hr-job-roles-controller.js @@ -1,1067 +1,1069 @@ define([ - 'common/angular', - 'job-roles/controllers/controllers', - 'common/moment', - 'common/filters/angular-date/format-date' + 'common/angular', + 'job-roles/controllers/controllers', + 'common/moment', + 'common/filters/angular-date/format-date' ], function (angular, controllers, moment) { - 'use strict'; - - controllers.controller('HRJobRolesController', ['$scope', '$log', '$routeParams', '$route', '$timeout', '$filter', '$q', 'HR_settings', 'HRJobRolesService', 'DateValidation', 'HRJobRolesServiceFilters', - function ($scope, $log, $routeParams, $route, $timeout, $filter, $q, HR_settings, HRJobRolesService, DateValidation, HRJobRolesServiceFilters) { - $log.debug('Controller: HRJobRolesController'); - - $scope.format = HR_settings.DATE_FORMAT; - - var me = this; - var formatDate = $filter('formatDate'); - var roles_type = ['funders', 'cost_centers']; - - $scope.present_job_roles = []; - $scope.past_job_roles = []; - - $scope.dpOpen = function ($event) { - $event.preventDefault(); - $event.stopPropagation(); - - $scope.picker.opened = true; - }; - - /** - * Checks if date should be considered empty. - * Empty date is saved to database as 0000-00-00 00:00:00 - * @param {String} date - * @returns {boolean} - */ - var isDateEmpty = function(date){ - return date === '0000-00-00 00:00:00'; - }; - - /** - * Method responsible for updating new JobRole with dates from Contract - */ - $scope.onContractSelected = function () { - var contract = getContractData($scope.edit_data.new_role_id.job_contract_id); - var areDatesCustom = $scope.checkIfDatesAreCustom($scope.edit_data.new_role_id.newStartDate, $scope.edit_data.new_role_id.newEndDate); - - formatRoleDates($scope.edit_data.new_role_id, { - start: areDatesCustom ? $scope.edit_data.new_role_id.newStartDate : contract.start_date, - end: areDatesCustom ? $scope.edit_data.new_role_id.newEndDate : contract.end_date - }, - { - start: 'newStartDate', - end: 'newEndDate' - }); - }; - - /** - * Checks if dates don't exist in any of contracts - * @param start - * @param end - * @returns {boolean} - */ - $scope.checkIfDatesAreCustom = function (start, end) { - if (isDateEmpty(start)) start = null; - if (isDateEmpty(end)) end = null; - - var custom = true; - - if (!start) return false; - - - angular.forEach(me.contractsData, function (value) { - if (formatDate(start) === formatDate(value.start_date) - && formatDate(end) === formatDate(value.end_date)) - custom = false; - }); - - return custom; - }; - - /** - * Method responsible for updating existing JobRole with dates from Contract - * @param jobContractId - * @param role_id - */ - $scope.onContractEdited = function (jobContractId, role_id) { - var id = jobContractId || $scope.edit_data[role_id]['job_contract_id']; - var contract = getContractData(id); - var areDatesCustom = $scope.checkIfDatesAreCustom($scope.edit_data[role_id]['start_date'], $scope.edit_data[role_id]['end_date']); - - if (!areDatesCustom) { - formatRoleDates($scope.edit_data[role_id], { - start: contract.start_date, - end: contract.end_date - }); - } else { - formatRoleDates($scope.edit_data[role_id], { - start: $scope.edit_data[role_id].start_date, - end: $scope.edit_data[role_id].end_date - }); - } - }; - - /** - * - * @param title - * @returns {string|undefined} - */ - $scope.validateTitle = function (title) { - if (title === 'title' || title === ' ') { - return "Title cannot be title!"; - } - }; - - /** - * Validation method for JobRole data. - * If string is returned form is not submitted. - * @param data - * @returns {boolean|string} - */ - $scope.validateRole = function (data) { - // Reset Error Messages - data.start_date.$error.custom = []; - data.end_date.$error.custom = []; - - var contract = getContractData(data.contract.$viewValue); - - var validateResponse = validateDates({ - 'start': data.start_date.$viewValue, - 'end': data.end_date.$viewValue, - 'contractStart': contract.start_date, - 'contractEnd': contract.end_date, - }, - { - 'start': data.start_date.$error.custom, - 'end': data.end_date.$error.custom - }); - - return (validateResponse ? true : 'Error'); - }; - - $scope.today = function () { - $scope.CalendarShow['newStartDate'] = false; - $scope.CalendarShow['newEndDate'] = false; - $scope.CalendarShow['start_date'] = false; - $scope.CalendarShow['end_date'] = false; - }; - - $scope.isOpen = function (name) { - return !!($scope.CalendarShow[name]); - }; - - // As default hide the datepickers - $scope.CalendarShow = []; - - // Init values - $scope.today(); - - $scope.open = function (event) { - $scope.CalendarShow[event] = true; - }; - - $scope.select = function (event) { - $scope.CalendarShow[event] = false; - }; - - // Tracks collapsed / expanded rows - $scope.collapsedRows = []; - - // Tracks clicked tabs per each row - $scope.view_tab = []; - - // Tracks edit data changes on the forms - $scope.edit_data = {}; - - // Define the add new role URL - $scope.add_new_role_url = $scope.$parent.pathBaseUrl + $scope.$parent.pathIncludeTpl + 'add_new_role.html'; - $scope.job_role_panel_url = $scope.$parent.pathBaseUrl + $scope.$parent.pathIncludeTpl + 'job_role_panel.html'; - - // Store the contractsData - $scope.contractsData = []; - - // Store the level types - $scope.LevelsData = {}; - - // Store the location types - $scope.LocationsData = {}; - - // Store the region types - $scope.RegionsData = {}; - - // Store the department types - $scope.DepartmentsData = {}; - - // Contact List IDs array to use for the select lists - $scope.contactList = []; - - // Contact List object stores more details about the contact - $scope.contactListObject = {}; - - // Implement angular tabs - $scope.changeTab = function (row_id, tab_id) { - $scope.view_tab[row_id] = tab_id; - }; + 'use strict'; + + controllers.controller('HRJobRolesController', ['$scope', '$log', '$routeParams', '$route', '$timeout', '$filter', '$q', 'HR_settings', 'HRJobRolesService', 'DateValidation', 'HRJobRolesServiceFilters', + function ($scope, $log, $routeParams, $route, $timeout, $filter, $q, HR_settings, HRJobRolesService, DateValidation, HRJobRolesServiceFilters) { + $log.debug('Controller: HRJobRolesController'); + + $scope.format = HR_settings.DATE_FORMAT; + + var me = this; + var formatDate = $filter('formatDate'); + var roles_type = ['funders', 'cost_centers']; + + $scope.present_job_roles = []; + $scope.past_job_roles = []; + + $scope.dpOpen = function ($event) { + $event.preventDefault(); + $event.stopPropagation(); + + $scope.picker.opened = true; + }; + + /** + * Checks if date should be considered empty. + * Empty date is saved to database as 0000-00-00 00:00:00 + * @param {String} date + * @returns {boolean} + */ + var isDateEmpty = function(date){ + return date === '0000-00-00 00:00:00'; + }; + + /** + * Method responsible for updating new JobRole with dates from Contract + */ + $scope.onContractSelected = function () { + var contract = getContractData($scope.edit_data.new_role_id.job_contract_id); + var areDatesCustom = $scope.checkIfDatesAreCustom($scope.edit_data.new_role_id.newStartDate, $scope.edit_data.new_role_id.newEndDate); + + formatRoleDates($scope.edit_data.new_role_id, { + start: areDatesCustom ? $scope.edit_data.new_role_id.newStartDate : contract.start_date, + end: areDatesCustom ? $scope.edit_data.new_role_id.newEndDate : contract.end_date + }, + { + start: 'newStartDate', + end: 'newEndDate' + }); + }; + + /** + * Checks if dates don't exist in any of contracts + * @param start + * @param end + * @returns {boolean} + */ + $scope.checkIfDatesAreCustom = function (start, end) { + if (isDateEmpty(start)) start = null; + if (isDateEmpty(end)) end = null; + + var custom = true; + + if (!start) return false; + + + angular.forEach(me.contractsData, function (value) { + if (formatDate(start) === formatDate(value.start_date) + && formatDate(end) === formatDate(value.end_date)) + custom = false; + }); + + return custom; + }; + + /** + * Method responsible for updating existing JobRole with dates from Contract + * @param jobContractId + * @param role_id + */ + $scope.onContractEdited = function (jobContractId, role_id) { + var id = jobContractId || $scope.edit_data[role_id]['job_contract_id']; + var contract = getContractData(id); + var areDatesCustom = $scope.checkIfDatesAreCustom($scope.edit_data[role_id]['start_date'], $scope.edit_data[role_id]['end_date']); + + if (!areDatesCustom) { + formatRoleDates($scope.edit_data[role_id], { + start: contract.start_date, + end: contract.end_date + }); + } else { + formatRoleDates($scope.edit_data[role_id], { + start: $scope.edit_data[role_id].start_date, + end: $scope.edit_data[role_id].end_date + }); + } + }; + + /** + * + * @param title + * @returns {string|undefined} + */ + $scope.validateTitle = function (title) { + if (title === 'title' || title === ' ') { + return "Title cannot be title!"; + } + }; + + /** + * Validation method for JobRole data. + * If string is returned form is not submitted. + * @param data + * @returns {boolean|string} + */ + $scope.validateRole = function (data) { + // Reset Error Messages + data.start_date.$error.custom = []; + data.end_date.$error.custom = []; + + var contract = getContractData(data.contract.$viewValue); + + var validateResponse = validateDates({ + 'start': data.start_date.$viewValue, + 'end': data.end_date.$viewValue, + 'contractStart': contract.start_date, + 'contractEnd': contract.end_date, + }, + { + 'start': data.start_date.$error.custom, + 'end': data.end_date.$error.custom + }); + + return (validateResponse ? true : 'Error'); + }; + + $scope.today = function () { + $scope.CalendarShow['newStartDate'] = false; + $scope.CalendarShow['newEndDate'] = false; + $scope.CalendarShow['start_date'] = false; + $scope.CalendarShow['end_date'] = false; + }; + + $scope.isOpen = function (name) { + return !!($scope.CalendarShow[name]); + }; + + // As default hide the datepickers + $scope.CalendarShow = []; + + // Init values + $scope.today(); + + $scope.open = function (event) { + $scope.CalendarShow[event] = true; + }; + + $scope.select = function (event) { + $scope.CalendarShow[event] = false; + }; + + // Tracks collapsed / expanded rows + $scope.collapsedRows = []; + + // Tracks clicked tabs per each row + $scope.view_tab = []; + + // Tracks edit data changes on the forms + $scope.edit_data = {}; + + // Define the add new role URL + $scope.add_new_role_url = $scope.$parent.pathBaseUrl + $scope.$parent.pathIncludeTpl + 'add_new_role.html'; + $scope.job_role_panel_url = $scope.$parent.pathBaseUrl + $scope.$parent.pathIncludeTpl + 'job_role_panel.html'; + + // Store the contractsData + $scope.contractsData = []; + + // Store the level types + $scope.LevelsData = {}; + + // Store the location types + $scope.LocationsData = {}; + + // Store the region types + $scope.RegionsData = {}; + + // Store the department types + $scope.DepartmentsData = {}; + + // Contact List IDs array to use for the select lists + $scope.contactList = []; + + // Contact List object stores more details about the contact + $scope.contactListObject = {}; + + // Implement angular tabs + $scope.changeTab = function (row_id, tab_id) { + $scope.view_tab[row_id] = tab_id; + }; + + // Check if current tab + $scope.isTab = function (row_id, tab_id) { + return ($scope.view_tab[row_id] == tab_id); + }; + + // Check for collapsed rows + $scope.isRowCollapsed = function (row_id) { + return !!($scope.collapsedRows[row_id]); + }; + + // Collapse the row or Expand when clicked + $scope.collapseRow = function (row_id) { + $scope.collapsedRows[row_id] = !$scope.collapsedRows[row_id]; + }; - // Check if current tab - $scope.isTab = function (row_id, tab_id) { - return ($scope.view_tab[row_id] == tab_id); - }; + // Set the data from the webservice call + $scope.initData = function (role_id, form_id, data) { - // Check for collapsed rows - $scope.isRowCollapsed = function (row_id) { - return !!($scope.collapsedRows[row_id]); - }; + // Check if we have the array already + if (typeof $scope.edit_data[role_id] === "undefined") { + $scope.edit_data[role_id] = {}; + } + // If we have funders or cost centers, we have a special way to init our data + if (form_id === 'funders') { - // Collapse the row or Expand when clicked - $scope.collapseRow = function (row_id) { - $scope.collapsedRows[row_id] = !$scope.collapsedRows[row_id]; - }; + // Init empty array for funder default values + $scope.edit_data[role_id]['funders'] = []; - // Set the data from the webservice call - $scope.initData = function (role_id, form_id, data) { + // Split data from the stored funder contact IDs + var funder_contact_ids = HRJobRolesServiceFilters.isNotUndefined(data['funder'].split("|")); - // Check if we have the array already - if (typeof $scope.edit_data[role_id] === "undefined") { - $scope.edit_data[role_id] = {}; - } - // If we have funders or cost centers, we have a special way to init our data - if (form_id === 'funders') { - - // Init empty array for funder default values - $scope.edit_data[role_id]['funders'] = []; - - // Split data from the stored funder contact IDs - var funder_contact_ids = HRJobRolesServiceFilters.isNotUndefined(data['funder'].split("|")); - - // Split the funder types - var funder_types = data['funder_val_type'].split("|"); - - // Split the percent value for the funder - var percent_funders = data['percent_pay_funder'].split("|"); - - // Split the amount value for the funder - var amount_funders = data['amount_pay_funder'].split("|"); - - // Loop data and crete the required array of values - for (var i = 0; i < funder_contact_ids.length; i++) { - if (funder_contact_ids[i] != "") { - // Set default funder rows funder rows - $scope.edit_data[role_id]['funders'].push({ - id: $scope.edit_data[role_id]['funders'].length + 1, - funder_id: { - id: funder_contact_ids[i], - sort_name: job_roles.contactListObject[funder_contact_ids[i]]['sort_name'] - }, - type: funder_types[i], - percentage: percent_funders[i], - amount: amount_funders[i] - }); - } - } - } - // If we have funders or cost centers, we have a special way to init our data - else if (form_id === 'cost_centers') { - // Init empty array for funder default values - $scope.edit_data[role_id]['cost_centers'] = []; + // Split the funder types + var funder_types = data['funder_val_type'].split("|"); - // Split data from the stored funder contact IDs - var cost_center_contact_ids = HRJobRolesServiceFilters.isNotUndefined(data['cost_center'].split("|")); + // Split the percent value for the funder + var percent_funders = data['percent_pay_funder'].split("|"); - // Split the cost_centers types - var cost_center_types = data['cost_center_val_type'].split("|"); + // Split the amount value for the funder + var amount_funders = data['amount_pay_funder'].split("|"); - // Split the percent value for the cost_center - var percent_cost_centers = data['percent_pay_cost_center'].split("|"); + // Loop data and crete the required array of values + for (var i = 0; i < funder_contact_ids.length; i++) { + if (funder_contact_ids[i] != "") { + // Set default funder rows funder rows + $scope.edit_data[role_id]['funders'].push({ + id: $scope.edit_data[role_id]['funders'].length + 1, + funder_id: { + id: funder_contact_ids[i], + sort_name: job_roles.contactListObject[funder_contact_ids[i]]['sort_name'] + }, + type: funder_types[i], + percentage: percent_funders[i], + amount: amount_funders[i] + }); + } + } + } + // If we have funders or cost centers, we have a special way to init our data + else if (form_id === 'cost_centers') { + // Init empty array for funder default values + $scope.edit_data[role_id]['cost_centers'] = []; - // Split the amount value for the cost_center - var amount_cost_centers = data['amount_pay_cost_center'].split("|"); + // Split data from the stored funder contact IDs + var cost_center_contact_ids = HRJobRolesServiceFilters.isNotUndefined(data['cost_center'].split("|")); - // Loop data and crete the required array of values - for (var i = 0; i < cost_center_contact_ids.length; i++) { + // Split the cost_centers types + var cost_center_types = data['cost_center_val_type'].split("|"); - if (cost_center_contact_ids[i] != "") { + // Split the percent value for the cost_center + var percent_cost_centers = data['percent_pay_cost_center'].split("|"); - // Set default funder rows funder rows - $scope.edit_data[role_id]['cost_centers'].push({ - id: $scope.edit_data[role_id]['cost_centers'].length + 1, - cost_centre_id: cost_center_contact_ids[i], - type: cost_center_types[i], - percentage: percent_cost_centers[i], - amount: amount_cost_centers[i] - }); + // Split the amount value for the cost_center + var amount_cost_centers = data['amount_pay_cost_center'].split("|"); - } + // Loop data and crete the required array of values + for (var i = 0; i < cost_center_contact_ids.length; i++) { - } + if (cost_center_contact_ids[i] != "") { - } else { + // Set default funder rows funder rows + $scope.edit_data[role_id]['cost_centers'].push({ + id: $scope.edit_data[role_id]['cost_centers'].length + 1, + cost_centre_id: cost_center_contact_ids[i], + type: cost_center_types[i], + percentage: percent_cost_centers[i], + amount: amount_cost_centers[i] + }); - var bothJustSet = (typeof $scope.edit_data[role_id].start_date === 'undefined' - || typeof $scope.edit_data[role_id].job_contract_id === 'undefined'); + } - // Default data init - $scope.edit_data[role_id][form_id] = data; + } - if (!!$scope.edit_data[role_id].start_date) { - var date = moment($scope.edit_data[role_id].start_date); - /* If dates are not set, we programatically set them here. */ - var invalidDate = (isNaN(date) && typeof $scope.edit_data[role_id].start_date != 'undefined'); + } else { - var presentJobContract = !(typeof $scope.edit_data[role_id].job_contract_id === 'undefined'); + var bothJustSet = (typeof $scope.edit_data[role_id].start_date === 'undefined' + || typeof $scope.edit_data[role_id].job_contract_id === 'undefined'); - if (invalidDate && presentJobContract && bothJustSet) { - $scope.onContractEdited(null, role_id).then(function () { - $scope.$apply(); - return $scope.updateRole(role_id); - }); - } else { - formatRoleDates($scope.edit_data[role_id], { - start: $scope.edit_data[role_id].start_date, - end: $scope.edit_data[role_id].end_date - }); - } - } - } + // Default data init + $scope.edit_data[role_id][form_id] = data; - if (form_id === 'end_date' && !$scope.edit_data[role_id].end_date) { - $scope.edit_data[role_id].end_date = null; - } + if (!!$scope.edit_data[role_id].start_date) { + var date = moment($scope.edit_data[role_id].start_date); + /* If dates are not set, we programatically set them here. */ + var invalidDate = (isNaN(date) && typeof $scope.edit_data[role_id].start_date != 'undefined'); - if ($scope.edit_data[role_id].job_contract_id - && $scope.edit_data[role_id].start_date - && typeof $scope.edit_data[role_id].end_date != 'undefined' - && (form_id === 'start_date' || form_id === 'job_contract_id' || form_id === 'end_date')) { + var presentJobContract = !(typeof $scope.edit_data[role_id].job_contract_id === 'undefined'); - updateRolesWithContractData(role_id); - } - }; - - /** - * Sets the values of the given role's start and end date properties - * to the Date objects representing the given start and end dates - * - * @param {Object} role - The job role - * @param {Object} dates - An object with `start` and `end ` dates - * @param {Object} keys - Custom names of the role's start and end date properties - */ - function formatRoleDates(role, dates, keys) { - var keys = keys ? keys : { start: 'start_date', end: 'end_date' }; - - role[keys.start] = !!dates.start ? formatDate(dates.start, Date) : null; - role[keys.end] = !!dates.end ? formatDate(dates.end, Date) : null; + if (invalidDate && presentJobContract && bothJustSet) { + $scope.onContractEdited(null, role_id).then(function () { + $scope.$apply(); + return $scope.updateRole(role_id); + }); + } else { + formatRoleDates($scope.edit_data[role_id], { + start: $scope.edit_data[role_id].start_date, + end: $scope.edit_data[role_id].end_date + }); } + } + } - /** - * Checks if JobRole dates are actual, if not checks if they exist in any of contract's revisions. - * @param role_id - */ - function updateRolesWithContractData(role_id) { - var contract_id = $scope.edit_data[role_id].job_contract_id; - - if ($scope.checkIfDatesAreCustom($scope.edit_data[role_id]['start_date'], $scope.edit_data[role_id]['end_date'])) { - var contract = getContractData(contract_id); - - // search for revision containing these dates - var revision = contract.revisions.some(function (rev) { - return rev.period_start_date === formatDate($scope.edit_data[role_id]['start_date']) - && rev.period_end_date === formatDate($scope.edit_data[role_id]['end_date']); - }); - - // check if dates match with revision - if (revision) { - formatRoleDates($scope.edit_data[role_id], { - start: contract.start_date, - end: contract.end_date - }); + if (form_id === 'end_date' && !$scope.edit_data[role_id].end_date) { + $scope.edit_data[role_id].end_date = null; + } - $scope.updateRole(role_id); - } - } else { - formatRoleDates($scope.edit_data[role_id], { - start: $scope.edit_data[role_id].start_date, - end: $scope.edit_data[role_id].end_date - }); - } - } + if ($scope.edit_data[role_id].job_contract_id + && $scope.edit_data[role_id].start_date + && typeof $scope.edit_data[role_id].end_date != 'undefined' + && (form_id === 'start_date' || form_id === 'job_contract_id' || form_id === 'end_date')) { - /** - * Check if the data are changed in the form (based on job role ID) - * @param row_id - * @returns {boolean} - */ - $scope.isChanged = function (row_id) { - // If there are data it means we edited the form - return !!($scope.edit_data[row_id]['is_edit']); - }; - - /** - * Set the is_edit value - * @param row_id - */ - $scope.showSave = function (row_id) { - $scope.edit_data[row_id]['is_edit'] = true; - }; - - /** - * Check if we allow to submit the form - * Rule -> Allow only if the minimum required data are filled - * @returns {boolean} - */ - $scope.checkNewRole = function checkNewRole() { - - return (typeof $scope.edit_data['new_role_id'] === 'undefined' - || typeof $scope.edit_data['new_role_id']['title'] === 'undefined' - || $scope.edit_data['new_role_id']['title'] === '' - || typeof $scope.edit_data['new_role_id']['job_contract_id'] === 'undefined' - || $scope.edit_data['new_role_id']['job_contract_id'] === ''); - }; - - /** - * Validates Dates and saves the new Job Role - */ - $scope.saveNewRole = function saveNewRole() { - var newRole; - - $log.debug('Add New Role'); - - $scope.errors = {}; - $scope.errors.newStartDate = []; - $scope.errors.newEndDate = []; - - var contract = getContractData($scope.edit_data.new_role_id.job_contract_id); - var validateResponse = validateDates({ - 'start': $scope.edit_data.new_role_id.newStartDate, - 'end': $scope.edit_data.new_role_id.newEndDate, - 'contractStart': contract.start_date, - 'contractEnd': contract.end_date, - }, - { - 'start': $scope.errors.newStartDate, - 'end': $scope.errors.newEndDate - }); + updateRolesWithContractData(role_id); + } + }; + + /** + * Sets the values of the given role's start and end date properties + * to the Date objects representing the given start and end dates + * + * @param {Object} role - The job role + * @param {Object} dates - An object with `start` and `end ` dates + * @param {Object} keys - Custom names of the role's start and end date properties + */ + function formatRoleDates(role, dates, keys) { + var keys = keys ? keys : { start: 'start_date', end: 'end_date' }; + + role[keys.start] = !!dates.start ? formatDate(dates.start, Date) : null; + role[keys.end] = !!dates.end ? formatDate(dates.end, Date) : null; + } + + /** + * Checks if JobRole dates are actual, if not checks if they exist in any of contract's revisions. + * @param role_id + */ + function updateRolesWithContractData(role_id) { + var contract_id = $scope.edit_data[role_id].job_contract_id; + + if ($scope.checkIfDatesAreCustom($scope.edit_data[role_id]['start_date'], $scope.edit_data[role_id]['end_date'])) { + var contract = getContractData(contract_id); + + // search for revision containing these dates + var revision = contract.revisions.some(function (rev) { + return rev.period_start_date === formatDate($scope.edit_data[role_id]['start_date']) + && rev.period_end_date === formatDate($scope.edit_data[role_id]['end_date']); + }); + + // check if dates match with revision + if (revision) { + formatRoleDates($scope.edit_data[role_id], { + start: contract.start_date, + end: contract.end_date + }); + + $scope.updateRole(role_id); + } + } else { + formatRoleDates($scope.edit_data[role_id], { + start: $scope.edit_data[role_id].start_date, + end: $scope.edit_data[role_id].end_date + }); + } + } + + /** + * Check if the data are changed in the form (based on job role ID) + * @param row_id + * @returns {boolean} + */ + $scope.isChanged = function (row_id) { + // If there are data it means we edited the form + return !!($scope.edit_data[row_id]['is_edit']); + }; + + /** + * Set the is_edit value + * @param row_id + */ + $scope.showSave = function (row_id) { + $scope.edit_data[row_id]['is_edit'] = true; + }; + + /** + * Check if we allow to submit the form + * Rule -> Allow only if the minimum required data are filled + * @returns {boolean} + */ + $scope.checkNewRole = function checkNewRole() { + + return (typeof $scope.edit_data['new_role_id'] === 'undefined' + || typeof $scope.edit_data['new_role_id']['title'] === 'undefined' + || $scope.edit_data['new_role_id']['title'] === '' + || typeof $scope.edit_data['new_role_id']['job_contract_id'] === 'undefined' + || $scope.edit_data['new_role_id']['job_contract_id'] === ''); + }; + + /** + * Validates Dates and saves the new Job Role + */ + $scope.saveNewRole = function saveNewRole() { + var newRole; + + $log.debug('Add New Role'); + + $scope.errors = {}; + $scope.errors.newStartDate = []; + $scope.errors.newEndDate = []; + + var contract = getContractData($scope.edit_data.new_role_id.job_contract_id); + var validateResponse = validateDates({ + 'start': $scope.edit_data.new_role_id.newStartDate, + 'end': $scope.edit_data.new_role_id.newEndDate, + 'contractStart': contract.start_date, + 'contractEnd': contract.end_date, + }, + { + 'start': $scope.errors.newStartDate, + 'end': $scope.errors.newEndDate + }); + + if (validateResponse) { + newRole = angular.copy($scope.edit_data.new_role_id); + + newRole.newStartDate = convertDateToServerFormat(newRole.newStartDate); + + if (newRole.newEndDate) { + newRole.newEndDate = convertDateToServerFormat(newRole.newEndDate); + } else { + delete newRole.newEndDate; + } + + // Create the new job role + createJobRole(newRole).then(function () { + // Hide the add new form + $scope.add_new = false; + + // Remove if any data are added / Reset form + delete $scope.edit_data['new_role_id']; + + // Hide the empty message if visible + $scope.empty = false; + + return getJobRolesList($scope.$parent.contactId); + }); + } + }; + + /** + * Sets the add new job role form visibility + */ + $scope.add_new_role = function () { + $scope.add_new = true; + }; + + /** + * Hides the add new job role form and removes any data added. + */ + $scope.cancelNewRole = function () { + $scope.add_new = false; + delete $scope.edit_data['new_role_id']; + }; + + /** + * Removes the Role based on Role ID + * @param row_id + */ + $scope.removeRole = function (row_id) { + $log.debug('Remove Role'); + + // Delete job role + deleteJobRole(row_id).then(function () { + return getJobRolesList($scope.$parent.contactId); + }); + }; + + /** + * Prepares data and updates existing role + * @param role_id + */ + $scope.updateRole = function (role_id, role_type) { + var updatedRole; + + $log.debug('Update Role'); + + if (typeof role_type === 'string') { + filterEmptyData(role_id, role_type); + } - if (validateResponse) { - newRole = angular.copy($scope.edit_data.new_role_id); + updatedRole = angular.copy($scope.edit_data[role_id]); + updatedRole.start_date = convertDateToServerFormat(updatedRole.start_date); - newRole.newStartDate = convertDateToServerFormat(newRole.newStartDate); + if (updatedRole.end_date) { + updatedRole.end_date = convertDateToServerFormat(updatedRole.end_date); + } else { + delete updatedRole.end_date; + } - if (newRole.newEndDate) { - newRole.newEndDate = convertDateToServerFormat(newRole.newEndDate); - } else { - delete newRole.newEndDate; - } + // Update the job role + updateJobRole(role_id, updatedRole).then(function () { + return getJobRolesList($scope.$parent.contactId); + }); + }; - // Create the new job role - createJobRole(newRole).then(function () { - // Hide the add new form - $scope.add_new = false; + // Select list for Row Types (used for Funders and Cost Centers) + $scope.rowTypes = {}; + $scope.rowTypes[0] = { id: 0, name: 'Fixed' }; + $scope.rowTypes[1] = { id: 1, name: '%' }; - // Remove if any data are added / Reset form - delete $scope.edit_data['new_role_id']; + //$scope.rowTypes = [ {id: 0, name: 'Fixed'}, {id: 1, name: '%'}]; - // Hide the empty message if visible - $scope.empty = false; + /** + * Show Row Type default value + * @param object + * @returns {string} + */ + $scope.showRowType = function (object) { + var selected = ''; - return getJobRolesList($scope.$parent.contactId); - }); - } - }; - - /** - * Sets the add new job role form visibility - */ - $scope.add_new_role = function () { - $scope.add_new = true; - }; - - /** - * Hides the add new job role form and removes any data added. - */ - $scope.cancelNewRole = function () { - $scope.add_new = false; - delete $scope.edit_data['new_role_id']; - }; - - /** - * Removes the Role based on Role ID - * @param row_id - */ - $scope.removeRole = function (row_id) { - $log.debug('Remove Role'); - - // Delete job role - deleteJobRole(row_id).then(function () { - return getJobRolesList($scope.$parent.contactId); - }); - }; - - /** - * Prepares data and updates existing role - * @param role_id - */ - $scope.updateRole = function (role_id, role_type) { - var updatedRole; - - $log.debug('Update Role'); - - if (typeof role_type === 'string') { - filterEmptyData(role_id, role_type); - } + if (typeof object.type !== "undefined") { - updatedRole = angular.copy($scope.edit_data[role_id]); - updatedRole.start_date = convertDateToServerFormat(updatedRole.start_date); + // Get the human readable Type Value + selected = $scope.rowTypes[object.type]; - if (updatedRole.end_date) { - updatedRole.end_date = convertDateToServerFormat(updatedRole.end_date); - } else { - delete updatedRole.end_date; - } + return selected.name; + } + return 'Not set'; + }; + + $scope.getCostLabel = function (id) { + var label = ''; + angular.forEach($scope.CostCentreList, function (v, k) { + if (v.id == id) { + label = v.title; + } + }); + + return label; + }; + + // Update funder type scope on request + $scope.updateAdditionalRowType = function (role_id, row_type, key, data) { + if (row_type === 'cost_centre') { + // Update cost centers row + $scope.edit_data[role_id]['cost_centers'][key]['type'] = data; + } else { + // Update funder Type scope as default + $scope.edit_data[role_id]['funders'][key]['type'] = data; + } + }; - // Update the job role - updateJobRole(role_id, updatedRole).then(function () { - return getJobRolesList($scope.$parent.contactId); - }); - }; + // Add additional rows (funder or cost centres) + $scope.addAdditionalRow = function (role_id, row_type) { - // Select list for Row Types (used for Funders and Cost Centers) - $scope.rowTypes = {}; - $scope.rowTypes[0] = { id: 0, name: 'Fixed' }; - $scope.rowTypes[1] = { id: 1, name: '%' }; + // Check if we have the array already + if (typeof $scope.edit_data[role_id] === "undefined") { + $scope.edit_data[role_id] = {}; + } - //$scope.rowTypes = [ {id: 0, name: 'Fixed'}, {id: 1, name: '%'}]; + if (row_type === 'cost_centre') { + + // Add cost centres + // Check if we have the array already + if (typeof $scope.edit_data[role_id]['cost_centers'] === "undefined" || !($scope.edit_data[role_id]['cost_centers'] instanceof Array)) { + $scope.edit_data[role_id]['cost_centers'] = []; + } + + $scope.edit_data[role_id]['cost_centers'].push({ + id: $scope.edit_data[role_id]['cost_centers'].length + 1, + cost_centre_id: '', + type: "1", + percentage: "0", + amount: "0" + }); + + } else { + + // As default add funder rows + // Check if we have the array already + if (typeof $scope.edit_data[role_id]['funders'] === "undefined" || !($scope.edit_data[role_id]['funders'] instanceof Array)) { + $scope.edit_data[role_id]['funders'] = []; + } + + $scope.edit_data[role_id]['funders'].push({ + id: $scope.edit_data[role_id]['funders'].length + 1, + funder_id: '', + type: "1", + percentage: "0", + amount: "0" + }); + } + }; + + // Delete Additional rows (funder or cost centres) + $scope.deleteAdditionalRow = function (role_id, row_type, row_id) { + if (row_type === 'cost_centre') { + // Remove the cost centre row + $scope.edit_data[role_id]['cost_centers'].splice(row_id, 1); + } else { + // Remove the funder row as default + $scope.edit_data[role_id]['funders'].splice(row_id, 1); + } + }; + + /** + * Called on angular-xeditable's onaftersave callback. + * It'll filter the rows which are without data. + * + * @param {string|int} role_id + * @param {string} role_type + */ + $scope.onAfterSave = function (role_id, role_type) { + filterEmptyData(role_id, role_type); + } + + /** + * Called on angular-xeditable's cancel callback. + * It'll filter the rows which are without data. + * + * @param {string|int} role_id + * @param {string} role_type + */ + $scope.onCancel = function (role_id, role_type) { + if (role_type === 'both') { + roles_type.map(function (type) { + filterEmptyData(role_id, type); + }); + } else { + filterEmptyData(role_id, role_type); + } + } + + /** + * Filter the edit_data property to remove + * the funders/cost_centers entries which are empty + * + * @param {string|int} role_id + * @param {string} role_type + */ + function filterEmptyData(role_id, role_type) { + if ($scope.edit_data.hasOwnProperty(role_id)) { + if (role_type === 'funders') { + $scope.edit_data[role_id][role_type] = HRJobRolesServiceFilters.issetFunder($scope.edit_data[role_id][role_type]); + } + + if (role_type === 'cost_centers') { + $scope.edit_data[role_id][role_type] = HRJobRolesServiceFilters.issetCostCentre($scope.edit_data[role_id][role_type]); + } + } + } - /** - * Show Row Type default value - * @param object - * @returns {string} - */ - $scope.showRowType = function (object) { - var selected = ''; + // Variable to check if we adding new job role + var job_roles = this; - if (typeof object.type !== "undefined") { + // Get the option groups and option values + getOptionValues(); - // Get the human readable Type Value - selected = $scope.rowTypes[object.type]; + // Get job roles based on the passed Contact ID + getJobRolesList($scope.$parent.contactId); - return selected.name; - } - return 'Not set'; - }; - - $scope.getCostLabel = function (id) { - var label = ''; - angular.forEach($scope.CostCentreList, function (v, k) { - if (v.id == id) { - label = v.title; - } - }); - - return label; - }; - - // Update funder type scope on request - $scope.updateAdditionalRowType = function (role_id, row_type, key, data) { - if (row_type === 'cost_centre') { - // Update cost centers row - $scope.edit_data[role_id]['cost_centers'][key]['type'] = data; - } else { - // Update funder Type scope as default - $scope.edit_data[role_id]['funders'][key]['type'] = data; - } - }; + // Get the contact list and store the data + getContactList(); - // Add additional rows (funder or cost centres) - $scope.addAdditionalRow = function (role_id, row_type) { + function getContactList() { - // Check if we have the array already - if (typeof $scope.edit_data[role_id] === "undefined") { - $scope.edit_data[role_id] = {}; - } + HRJobRolesService.getContactList().then(function (data) { - if (row_type === 'cost_centre') { - - // Add cost centres - // Check if we have the array already - if (typeof $scope.edit_data[role_id]['cost_centers'] === "undefined" || !($scope.edit_data[role_id]['cost_centers'] instanceof Array)) { - $scope.edit_data[role_id]['cost_centers'] = []; - } - - $scope.edit_data[role_id]['cost_centers'].push({ - id: $scope.edit_data[role_id]['cost_centers'].length + 1, - cost_centre_id: '', - type: "1", - percentage: "0", - amount: "0" - }); - - } else { - - // As default add funder rows - // Check if we have the array already - if (typeof $scope.edit_data[role_id]['funders'] === "undefined" || !($scope.edit_data[role_id]['funders'] instanceof Array)) { - $scope.edit_data[role_id]['funders'] = []; - } - - $scope.edit_data[role_id]['funders'].push({ - id: $scope.edit_data[role_id]['funders'].length + 1, - funder_id: '', - type: "1", - percentage: "0", - amount: "0" - }); - } - }; - - // Delete Additional rows (funder or cost centres) - $scope.deleteAdditionalRow = function (role_id, row_type, row_id) { - if (row_type === 'cost_centre') { - // Remove the cost centre row - $scope.edit_data[role_id]['cost_centers'].splice(row_id, 1); - } else { - // Remove the funder row as default - $scope.edit_data[role_id]['funders'].splice(row_id, 1); - } - }; - - /** - * Called on angular-xeditable's onaftersave callback. - * It'll filter the rows which are without data. - * - * @param {string|int} role_id - * @param {string} role_type - */ - $scope.onAfterSave = function (role_id, role_type) { - filterEmptyData(role_id, role_type); + if (data.is_error === 1) { + job_roles.message_type = 'alert-danger'; + job_roles.message = 'Cannot get contact lit!'; } + else { - /** - * Called on angular-xeditable's cancel callback. - * It'll filter the rows which are without data. - * - * @param {string|int} role_id - * @param {string} role_type - */ - $scope.onCancel = function (role_id, role_type) { - if (role_type === 'both') { - roles_type.map(function (type) { - filterEmptyData(role_id, type); - }); - } else { - filterEmptyData(role_id, role_type); - } - } + // Pass the contact list to the scope + var contactList = []; + var contactListObject = {}; - /** - * Filter the edit_data property to remove - * the funders/cost_centers entries which are empty - * - * @param {string|int} role_id - * @param {string} role_type - */ - function filterEmptyData(role_id, role_type) { - if ($scope.edit_data.hasOwnProperty(role_id)) { - if (role_type === 'funders') { - $scope.edit_data[role_id][role_type] = HRJobRolesServiceFilters.issetFunder($scope.edit_data[role_id][role_type]); - } + for (var i = 0; i < data.count; i++) { - if (role_type === 'cost_centers') { - $scope.edit_data[role_id][role_type] = HRJobRolesServiceFilters.issetCostCentre($scope.edit_data[role_id][role_type]); - } + // Build the contact list + contactList.push({ id: data.values[i]['id'], sort_name: data.values[i]['sort_name'] }); + contactListObject[data.values[i]['id']] = { + id: data.values[i]['id'], + sort_name: data.values[i]['sort_name'] + }; } - } - - // Variable to check if we adding new job role - var job_roles = this; - - // Get the option groups and option values - getOptionValues(); - - // Get job roles based on the passed Contact ID - getJobRolesList($scope.$parent.contactId); - - // Get the contact list and store the data - getContactList(); - - function getContactList() { - - HRJobRolesService.getContactList().then(function (data) { - - if (data.is_error === 1) { - job_roles.message_type = 'alert-danger'; - job_roles.message = 'Cannot get contact lit!'; - } - else { - - // Pass the contact list to the scope - var contactList = []; - var contactListObject = {}; - for (var i = 0; i < data.count; i++) { + // Store the ContactList as Array as typeahead needs array what we can reuse later + job_roles.contactList = contactList; - // Build the contact list - contactList.push({ id: data.values[i]['id'], sort_name: data.values[i]['sort_name'] }); - contactListObject[data.values[i]['id']] = { - id: data.values[i]['id'], - sort_name: data.values[i]['sort_name'] - }; - } + // Store the object too, so we can point to right values by Contact ID + job_roles.contactListObject = contactListObject; - // Store the ContactList as Array as typeahead needs array what we can reuse later - job_roles.contactList = contactList; - - // Store the object too, so we can point to right values by Contact ID - job_roles.contactListObject = contactListObject; - - job_roles.message_type = 'alert-success'; - job_roles.message = 'Contact list OK!'; - } - - // Hide the message after some seconds - $timeout(function () { - job_roles.message = null; - }, 3000); - }, - function (errorMessage) { - $scope.error = errorMessage; - }); + job_roles.message_type = 'alert-success'; + job_roles.message = 'Contact list OK!'; } - function getOptionValues() { - - // Set the option groups for which we want to get the values - var option_groups = ['hrjc_department', 'hrjc_region', 'hrjc_location', 'hrjc_level_type', 'cost_centres']; - - HRJobRolesService.getOptionValues(option_groups).then(function (data) { - - if (data.is_error === 1) { - job_roles.message_type = 'alert-danger'; - job_roles.message = 'Cannot get option values!'; - } - else { - - // Pass the department option group list to the scope - var DepartmentList = {}; - - // Pass the region option group list to the scope - var RegionList = {}; - - // Pass the location option group list to the scope - var LocationList = {}; - - // Pass the level option group list to the scope - var LevelList = {}; - - // Pass the Cost Centers option group list to the scope - var CostCentreList = []; - - angular.forEach(data['optionGroupData'], function (option_group_id, option_group_name) { - - for (var i = 0; i < data.count; i++) { - - switch (option_group_name) { - case 'hrjc_department': - - if (option_group_id === data.values[i]['option_group_id']) { - // Build the department list - DepartmentList[data.values[i]['id']] = { - id: data.values[i]['id'], - title: data.values[i]['label'] - }; - } + // Hide the message after some seconds + $timeout(function () { + job_roles.message = null; + }, 3000); + }, + function (errorMessage) { + $scope.error = errorMessage; + }); + } - break; - case 'hrjc_region': + function getOptionValues() { - if (option_group_id === data.values[i]['option_group_id']) { - // Build the region list - RegionList[data.values[i]['id']] = { - id: data.values[i]['id'], - title: data.values[i]['label'] - }; - } + // Set the option groups for which we want to get the values + var option_groups = ['hrjc_department', 'hrjc_region', 'hrjc_location', 'hrjc_level_type', 'cost_centres']; - break; - case 'hrjc_location': + HRJobRolesService.getOptionValues(option_groups).then(function (data) { - if (option_group_id === data.values[i]['option_group_id']) { - // Build the contact list - LocationList[data.values[i]['id']] = { - id: data.values[i]['id'], - title: data.values[i]['label'] - }; - } - - break; - case 'hrjc_level_type': - - if (option_group_id === data.values[i]['option_group_id']) { - // Build the contact list - LevelList[data.values[i]['id']] = { - id: data.values[i]['id'], - title: data.values[i]['label'] - }; - - } - - break; - case 'cost_centres': - if (option_group_id === data.values[i]['option_group_id']) { - // Build the contact list - CostCentreList.push({ - id: data.values[i]['id'], - title: data.values[i]['label'], - weight: data.values[i]['weight'] - }); - } - - break; - } - - - } - - }); - - // Store the Department types what we can reuse later - job_roles.DepartmentsData = DepartmentList; - - // Store the Region types what we can reuse later - job_roles.RegionsData = RegionList; - - // Store the Location types what we can reuse later - job_roles.LocationsData = LocationList; - - // Store the Level types what we can reuse later - job_roles.LevelsData = LevelList; - - // Store the Level types what we can reuse later - $scope.CostCentreList = CostCentreList; - $log.info($scope.CostCentreList); - - job_roles.message_type = 'alert-success'; - job_roles.message = 'Option values list OK!'; - } - - // Hide the message after some seconds - $timeout(function () { - job_roles.message = null; - }, 3000); - }, - function (errorMessage) { - $scope.error = errorMessage; - }); + if (data.is_error === 1) { + job_roles.message_type = 'alert-danger'; + job_roles.message = 'Cannot get option values!'; } + else { + + // Pass the department option group list to the scope + var DepartmentList = {}; + + // Pass the region option group list to the scope + var RegionList = {}; + + // Pass the location option group list to the scope + var LocationList = {}; + + // Pass the level option group list to the scope + var LevelList = {}; + + // Pass the Cost Centers option group list to the scope + var CostCentreList = []; + + angular.forEach(data['optionGroupData'], function (option_group_id, option_group_name) { + + for (var i = 0; i < data.count; i++) { + + switch (option_group_name) { + case 'hrjc_department': + + if (option_group_id === data.values[i]['option_group_id']) { + // Build the department list + DepartmentList[data.values[i]['value']] = { + id: data.values[i]['value'], + title: data.values[i]['label'] + }; + } + + break; + case 'hrjc_region': + + if (option_group_id === data.values[i]['option_group_id']) { + // Build the region list + RegionList[data.values[i]['value']] = { + id: data.values[i]['value'], + title: data.values[i]['label'] + }; + } + + break; + case 'hrjc_location': + + if (option_group_id === data.values[i]['option_group_id']) { + // Build the contact list + LocationList[data.values[i]['value']] = { + id: data.values[i]['value'], + title: data.values[i]['label'] + }; + } + + break; + case 'hrjc_level_type': + + if (option_group_id === data.values[i]['option_group_id']) { + // Build the contact list + LevelList[data.values[i]['value']] = { + id: data.values[i]['value'], + title: data.values[i]['label'] + }; + + } + + break; + case 'cost_centres': + if (option_group_id === data.values[i]['option_group_id']) { + // Build the contact list + CostCentreList.push({ + id: data.values[i]['value'], + title: data.values[i]['label'], + weight: data.values[i]['weight'] + }); + } + break; + } - /** - * Fetches the contract ids of the given contact - * - * @param {int} contactId - * @return {Promise} resolves with an array of contract ids - */ - function contractIdsFromContact(contactId) { - return HRJobRolesService.getContracts(contactId).then(function (data) { - var jobContractIds = []; - var contractsData = {}; - - // If we have job contracts, try to get the job roles for the contract - if (data.count !== 0) { - for (var i = 0; i < data.count; i++) { - // Job contract IDs which will be passed to the "getAllJobRoles" service - jobContractIds.push(data.values[i]['id']); - - var contract = { - id: data.values[i]['id'], - title: data.values[i]['title'], - start_date: data.values[i]['period_start_date'], - end_date: data.values[i]['period_end_date'], - status: status, - revisions: data.values[i]['revisions'] - }; - - var optionalEndDate = formatDate(contract.end_date) || 'Unspecified'; - contract.label = contract.title + ' (' + formatDate(contract.start_date) + ' - ' + optionalEndDate + ')'; - - contractsData[data.values[i]['id']] = contract; - } - - // Store the ContractsData what we can reuse later - job_roles.contractsData = contractsData; - } else { - job_roles.empty = 'No Job Contracts found for this Contact!'; - } - - job_roles.job_contract_ids = jobContractIds; - - return jobContractIds; - }, function (errorMessage) { - $scope.error = errorMessage; - }); - } - - /** - * Fetches the job roles of the contracts with the given ids - * - * @param {Array} contractIds - * @return {Promise} - */ - function jobRolesFromContracts(contractIds) { - return HRJobRolesService.getAllJobRoles(contractIds).then(function (data) { - // Assign data - job_roles.present_job_roles = []; - job_roles.past_job_roles = []; - - data.values.forEach(function (object_data) { - var end_date = isDateEmpty(object_data.end_date) ? null : object_data.end_date; - - if (!end_date || moment(end_date).isAfter(moment())) { - job_roles.present_job_roles.push(object_data); - } else { - job_roles.past_job_roles.push(object_data); - } - }); - - if (data.is_error === 1) { - job_roles.error = 'Data load failure'; - } else if (data.count === 0) { - job_roles.empty = 'No Job Roles found!'; - } else { - job_roles.empty = null; - } - - job_roles.status = 'Data load OK'; - }, function (errorMessage) { - $scope.error = errorMessage; - }); - } - /** - * Get job roles based on the passed Contact ID (refresh part of the page) - * @param contactId - * @returns {promise} - */ - function getJobRolesList(contactId) { - var contractsPromise; - - if (!job_roles.job_contract_ids) { - contractsPromise = contractIdsFromContact(contactId); - } else { - contractsPromise = $q.when(job_roles.job_contract_ids); } - contractsPromise.then(function (contractIds) { - !!contractIds.length && jobRolesFromContracts(contractIds); - }); - } + }); - // Implements the "deleteJobRole" service - function deleteJobRole(job_role_id) { - - return HRJobRolesService.deleteJobRole(job_role_id).then(function (data) { - if (data.is_error === 1) { - job_roles.message_type = 'alert-danger'; - job_roles.message = 'Role delete failure!'; - } else { - job_roles.message_type = 'alert-success'; - job_roles.message = 'Role deleted successfully!'; - } - - // Hide the message after some seconds - $timeout(function () { - job_roles.message = null; - }, 3000); - }, - function (errorMessage) { - $scope.error = errorMessage; - }); + // Store the Department types what we can reuse later + job_roles.DepartmentsData = DepartmentList; - } + // Store the Region types what we can reuse later + job_roles.RegionsData = RegionList; - // Implements the "createJobRole" service - function createJobRole(job_roles_data) { - return HRJobRolesService.createJobRole(job_roles_data).then(function (data) { - if (data.is_error === 1) { - job_roles.message_type = 'alert-danger'; - job_roles.message = 'Role creation failed!'; - } else { - job_roles.message_type = 'alert-success'; - job_roles.message = 'Role added successfully!'; - } - - // Hide the message after some seconds - $timeout(function () { - job_roles.message = null; - }, 3000); - }, function (errorMessage) { - $scope.error = errorMessage; - }); - } + // Store the Location types what we can reuse later + job_roles.LocationsData = LocationList; - // Implements the "updateJobRole" service - function updateJobRole(role_id, job_roles_data) { - return HRJobRolesService.updateJobRole(role_id, job_roles_data).then(function (data) { - - if (data.is_error === 1) { - job_roles.message_type = 'alert-danger'; - job_roles.message = 'Role update failed!'; - } - else { - job_roles.message_type = 'alert-success'; - job_roles.message = 'Role updated successfully!'; - } - - // Hide the message after some seconds - $timeout(function () { - job_roles.message = null; - }, 3000); - }, function (errorMessage) { - $scope.error = errorMessage; - }); - } + // Store the Level types what we can reuse later + job_roles.LevelsData = LevelList; - /** - * Trigger validation on JobRole Dates + attach error callback - * @param {object} data - The dates to validate - * @param {object} errors - The error recipients - * @returns {boolean} - */ - function validateDates(data, errors) { - var errorsCount = 0; - - DateValidation.setErrorCallback(function (error, field) { - errorsCount++; - if (field.indexOf('start_date') > -1) { - errors.start.push(error); - } - if (field.indexOf('end_date') > -1) { - errors.end.push(error); - } - }); - DateValidation.validate(data.start, data.end, data.contractStart, data.contractEnd); + // Store the Level types what we can reuse later + $scope.CostCentreList = CostCentreList; + $log.info($scope.CostCentreList); - return (errorsCount === 0); + job_roles.message_type = 'alert-success'; + job_roles.message = 'Option values list OK!'; } - /** - * Get a contract with the given contractId - * @param {int} contractId - * @returns {object} - */ - function getContractData(contractId) { - return me.contractsData[contractId]; + // Hide the message after some seconds + $timeout(function () { + job_roles.message = null; + }, 3000); + }, + function (errorMessage) { + $scope.error = errorMessage; + }); + } + + + /** + * Fetches the contract ids of the given contact + * + * @param {int} contactId + * @return {Promise} resolves with an array of contract ids + */ + function contractIdsFromContact(contactId) { + return HRJobRolesService.getContracts(contactId).then(function (data) { + var jobContractIds = []; + var contractsData = {}; + + // If we have job contracts, try to get the job roles for the contract + if (data.count !== 0) { + for (var i = 0; i < data.count; i++) { + // Job contract IDs which will be passed to the "getAllJobRoles" service + jobContractIds.push(data.values[i]['id']); + + var contract = { + id: data.values[i]['id'], + title: data.values[i]['title'], + start_date: data.values[i]['period_start_date'], + end_date: data.values[i]['period_end_date'], + status: status, + revisions: data.values[i]['revisions'] + }; + + var optionalEndDate = formatDate(contract.end_date) || 'Unspecified'; + contract.label = contract.title + ' (' + formatDate(contract.start_date) + ' - ' + optionalEndDate + ')'; + + contractsData[data.values[i]['id']] = contract; } - /** - * Parse dates so they can be correctly read by server. - * - * @param {string|Date} date - * @returns {string|null} - */ - function convertDateToServerFormat(date) { - var dateString = formatDate(date, 'YYYY-MM-DD'); - - return dateString !== 'Unspecified' ? dateString : null; + // Store the ContractsData what we can reuse later + job_roles.contractsData = contractsData; + } else { + job_roles.empty = 'No Job Contracts found for this Contact!'; + } + + job_roles.job_contract_ids = jobContractIds; + + return jobContractIds; + }, function (errorMessage) { + $scope.error = errorMessage; + }); + } + + /** + * Fetches the job roles of the contracts with the given ids + * + * @param {Array} contractIds + * @return {Promise} + */ + function jobRolesFromContracts(contractIds) { + return HRJobRolesService.getAllJobRoles(contractIds).then(function (data) { + // Assign data + job_roles.present_job_roles = []; + job_roles.past_job_roles = []; + + data.values.forEach(function (object_data) { + var end_date = isDateEmpty(object_data.end_date) ? null : object_data.end_date; + var todaysDate = moment().startOf('day'); + end_date = moment(end_date).startOf('day'); + + if (!end_date || moment(end_date).isSameOrAfter(todaysDate)) { + job_roles.present_job_roles.push(object_data); + } else { + job_roles.past_job_roles.push(object_data); } + }); + + if (data.is_error === 1) { + job_roles.error = 'Data load failure'; + } else if (data.count === 0) { + job_roles.empty = 'No Job Roles found!'; + } else { + job_roles.empty = null; + } + + job_roles.status = 'Data load OK'; + }, function (errorMessage) { + $scope.error = errorMessage; + }); + } + + /** + * Get job roles based on the passed Contact ID (refresh part of the page) + * @param contactId + * @returns {promise} + */ + function getJobRolesList(contactId) { + var contractsPromise; + + if (!job_roles.job_contract_ids) { + contractsPromise = contractIdsFromContact(contactId); + } else { + contractsPromise = $q.when(job_roles.job_contract_ids); } - ]); + + contractsPromise.then(function (contractIds) { + !!contractIds.length && jobRolesFromContracts(contractIds); + }); + } + + // Implements the "deleteJobRole" service + function deleteJobRole(job_role_id) { + + return HRJobRolesService.deleteJobRole(job_role_id).then(function (data) { + if (data.is_error === 1) { + job_roles.message_type = 'alert-danger'; + job_roles.message = 'Role delete failure!'; + } else { + job_roles.message_type = 'alert-success'; + job_roles.message = 'Role deleted successfully!'; + } + + // Hide the message after some seconds + $timeout(function () { + job_roles.message = null; + }, 3000); + }, + function (errorMessage) { + $scope.error = errorMessage; + }); + + } + + // Implements the "createJobRole" service + function createJobRole(job_roles_data) { + return HRJobRolesService.createJobRole(job_roles_data).then(function (data) { + if (data.is_error === 1) { + job_roles.message_type = 'alert-danger'; + job_roles.message = 'Role creation failed!'; + } else { + job_roles.message_type = 'alert-success'; + job_roles.message = 'Role added successfully!'; + } + + // Hide the message after some seconds + $timeout(function () { + job_roles.message = null; + }, 3000); + }, function (errorMessage) { + $scope.error = errorMessage; + }); + } + + // Implements the "updateJobRole" service + function updateJobRole(role_id, job_roles_data) { + return HRJobRolesService.updateJobRole(role_id, job_roles_data).then(function (data) { + + if (data.is_error === 1) { + job_roles.message_type = 'alert-danger'; + job_roles.message = 'Role update failed!'; + } + else { + job_roles.message_type = 'alert-success'; + job_roles.message = 'Role updated successfully!'; + } + + // Hide the message after some seconds + $timeout(function () { + job_roles.message = null; + }, 3000); + }, function (errorMessage) { + $scope.error = errorMessage; + }); + } + + /** + * Trigger validation on JobRole Dates + attach error callback + * @param {object} data - The dates to validate + * @param {object} errors - The error recipients + * @returns {boolean} + */ + function validateDates(data, errors) { + var errorsCount = 0; + + DateValidation.setErrorCallback(function (error, field) { + errorsCount++; + if (field.indexOf('start_date') > -1) { + errors.start.push(error); + } + if (field.indexOf('end_date') > -1) { + errors.end.push(error); + } + }); + DateValidation.validate(data.start, data.end, data.contractStart, data.contractEnd); + + return (errorsCount === 0); + } + + /** + * Get a contract with the given contractId + * @param {int} contractId + * @returns {object} + */ + function getContractData(contractId) { + return me.contractsData[contractId]; + } + + /** + * Parse dates so they can be correctly read by server. + * + * @param {string|Date} date + * @returns {string|null} + */ + function convertDateToServerFormat(date) { + var dateString = formatDate(date, 'YYYY-MM-DD'); + + return dateString !== 'Unspecified' ? dateString : null; + } + } + ]); }); diff --git a/com.civicrm.hrjobroles/js/test/controllers/hr-job-roles-controller/hr-job-roles-controller_test.js b/com.civicrm.hrjobroles/js/test/controllers/hr-job-roles-controller/hr-job-roles-controller_test.js index 76736329c0b..a981a4abdf7 100644 --- a/com.civicrm.hrjobroles/js/test/controllers/hr-job-roles-controller/hr-job-roles-controller_test.js +++ b/com.civicrm.hrjobroles/js/test/controllers/hr-job-roles-controller/hr-job-roles-controller_test.js @@ -1,10 +1,11 @@ define([ 'common/angular', 'common/lodash', + 'common/moment', 'mocks/job-roles', 'common/angularMocks', 'job-roles/app' -], function (angular, _, Mock) { +], function (angular, _, moment, Mock) { 'use strict'; describe('HRJobRolesController', function () { @@ -354,6 +355,8 @@ define([ expect(scope.edit_data[2].start_date).toEqual(convertToDateObject(Mock.contracts_data[0].start_date)); expect(scope.edit_data[2].end_date).toEqual(convertToDateObject(Mock.contracts_data[0].end_date)); }); + + }); describe('When call onAfterSave', function() { @@ -486,6 +489,45 @@ define([ } }); + describe('When call updateRole passing a job contract with end date equals todays date', function() { + beforeEach(function () { + var todaysDate = moment().format('YYYY-MM-DD'); + + spyOn(HRJobRolesService, 'getAllJobRoles').and.returnValue($q.resolve({ + values: [{ + title:"Test", + id:"19", + job_contract_id:"22", + end_date:todaysDate, + start_date:"2016-04-01 00:00:00", + }], + })); + + spyOn(HRJobRolesService, 'getContracts').and.returnValue($q.resolve({ + count:1, + values: [{ + contact_id:"158", + deleted:"0", + id:"22", + is_current:"1", + period_end_date:"2016-08-31", + period_start_date:"2916-04-01", + revisions:[], + title:"Test", + }], + })); + + initController(); + scope.edit_data = angular.copy(Mock.roles_data); + $rootScope.$digest(); + scope.updateRole(1); + }); + + it('the present_job_roles.length should be 1', function() { + expect(ctrl.present_job_roles.length).toBe(1); + }); + }); + /** * * diff --git a/com.civicrm.hrjobroles/tests/phpunit/CRM/Hrjobroles/BAO/HrJobRolesTest.php b/com.civicrm.hrjobroles/tests/phpunit/CRM/Hrjobroles/BAO/HrJobRolesTest.php new file mode 100644 index 00000000000..eed517619a8 --- /dev/null +++ b/com.civicrm.hrjobroles/tests/phpunit/CRM/Hrjobroles/BAO/HrJobRolesTest.php @@ -0,0 +1,102 @@ +'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // create role + $roleParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test role' + ]; + $jobRole = $this->createJobRole($roleParams); + + $roleEntity = $this->findRole(['id' => $jobRole->id]); + $this->assertEquals($roleParams['title'], $roleEntity->title); + } + + /** + * @expectedException PEAR_Exception + * @expectedExceptionMessage DB Error: unknown error + */ + function testCreateJobRoleWithoutContractID() { + $roleParams = [ + 'title' => 'test role' + ]; + $this->createJobRole($roleParams); + } + + function testCreateJobRoleWithOptionValueFields() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // create option group and values + $this->createSampleOptionGroupsAndValues(); + + // create role + $roleParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test role', + 'location' => "amman", + 'region' => "south amman", + 'department' => "amman devs", + 'level_type' => "guru" + ]; + $jobRole = $this->createJobRole($roleParams); + + $roleEntity = $this->findRole(['id' => $jobRole->id]); + $this->assertEquals($roleParams['title'], $roleEntity->title); + } + + function testGetContactRoles() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contracts + $contract1 = $this->createJobContract($contactID, date('Y-m-d', strtotime('-3 years')), date('Y-m-d', strtotime('-1 years'))); + $contract2 = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // create roles + $roleParams1 = [ + 'job_contract_id' => $contract1->id, + 'title' => 'test role 1' + ]; + $roleParams2 = [ + 'job_contract_id' => $contract1->id, + 'title' => 'test role 2' + ]; + $roleParams3 = [ + 'job_contract_id' => $contract2->id, + 'title' => 'test role 3' + ]; + + $this->createJobRole($roleParams1); + $this->createJobRole($roleParams2); + $this->createJobRole($roleParams3); + + $this->assertCount(3, CRM_Hrjobroles_BAO_HrJobRoles::getContactRoles($contactID)); + } + +} diff --git a/com.civicrm.hrjobroles/tests/phpunit/CRM/Hrjobroles/Import/Parser/HrJobRolesTest.php b/com.civicrm.hrjobroles/tests/phpunit/CRM/Hrjobroles/Import/Parser/HrJobRolesTest.php new file mode 100644 index 00000000000..7b601c9eb1a --- /dev/null +++ b/com.civicrm.hrjobroles/tests/phpunit/CRM/Hrjobroles/Import/Parser/HrJobRolesTest.php @@ -0,0 +1,370 @@ +set('dateTypes', 1); + $this->createSampleOptionGroupsAndValues(); + } + + function tearDown() { + parent::tearDown(); + } + + function testBasicImport() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // run importer + $importParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test import role' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::VALID, $importResponse); + + $roleEntity = $this->findRole(['title' => $importParams['title']]); + $this->assertEquals($importParams['title'], $roleEntity->title); + } + + function testImportWithoutMandatoryFields() { + // run importer + $importParams = [ + 'title' => 'test import role' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::ERROR, $importResponse); + } + + function testImportWithValidOptionValues() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // run importer + $importParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test import role', + 'location' => 'amman', + 'hrjc_region' => 'south amman', + 'hrjc_role_department' => 'amman devs', + 'hrjc_level_type' => 'guru' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::VALID, $importResponse); + + $roleEntity = $this->findRole(['title' => $importParams['title']]); + $this->assertEquals($importParams['title'], $roleEntity->title); + $this->assertEquals($importParams['location'], $roleEntity->location); + $this->assertEquals($importParams['hrjc_region'], $roleEntity->region); + $this->assertEquals($importParams['hrjc_role_department'], $roleEntity->department); + $this->assertEquals($importParams['hrjc_level_type'], $roleEntity->level_type); + } + + function testImportWithInvalidOptionValues() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // run importer + $importParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test import role2', + 'location' => 'amman', + 'hrjc_region' => 'southhggh ammandshhghg', + 'hrjc_role_department' => 'amman devs', + 'hrjc_level_type' => 'guru' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::ERROR, $importResponse);; + } + + function testImportWithEmptyOptionValues() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // run importer + $importParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test import role3', + 'location' => '', + 'hrjc_region' => '', + 'hrjc_role_department' => '', + 'hrjc_level_type' => '' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::VALID, $importResponse); + + $roleEntity = $this->findRole(['title' => $importParams['title']]); + $this->assertEquals($importParams['title'], $roleEntity->title); + } + + function testImportFunderByIDAndPercent() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // run importer + $importParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test import role', + 'location' => 'amman', + 'hrjc_region' => 'south amman', + 'hrjc_role_department' => 'amman devs', + 'hrjc_level_type' => 'guru', + 'funder' => $contactID, + 'hrjc_funder_val_type' => '%', + 'hrjc_role_percent_pay_funder' => '30' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::VALID, $importResponse); + + $roleEntity = $this->findRole(['title' => $importParams['title']]); + $this->assertEquals($importParams['title'], $roleEntity->title); + $this->assertEquals($importParams['funder'], $roleEntity->funder); + $this->assertEquals(1, $roleEntity->funder_val_type); + $this->assertEquals($importParams['hrjc_role_percent_pay_funder'], $roleEntity->percent_pay_funder); + } + + function testImportFunderByIDAndAmount() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // run importer + $importParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test import role', + 'location' => 'amman', + 'hrjc_region' => 'south amman', + 'hrjc_role_department' => 'amman devs', + 'hrjc_level_type' => 'guru', + 'funder' => $contactID, + 'hrjc_funder_val_type' => 'fixed', + 'hrjc_role_amount_pay_funder' => '30' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::VALID, $importResponse); + + $roleEntity = $this->findRole(['title' => $importParams['title']]); + $this->assertEquals($importParams['title'], $roleEntity->title); + $this->assertEquals($importParams['funder'], $roleEntity->funder); + $this->assertEquals(0, $roleEntity->funder_val_type); + $this->assertEquals($importParams['hrjc_role_amount_pay_funder'], $roleEntity->amount_pay_funder); + } + + function testImportFunderByDisplayNameAndAmount() { + // create contact + $contactParams = [ + 'first_name'=>'walter', + 'last_name'=>'white', + 'display_name' => 'walter white', + 'prefix_id' => '', + 'suffix_id' => '' + ]; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // run importer + $importParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test import role', + 'location' => 'amman', + 'hrjc_region' => 'south amman', + 'hrjc_role_department' => 'amman devs', + 'hrjc_level_type' => 'guru', + 'funder' => $contactParams['display_name'], + 'hrjc_funder_val_type' => 'fixed', + 'hrjc_role_amount_pay_funder' => '30' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::VALID, $importResponse); + + $roleEntity = $this->findRole(['title' => $importParams['title']]); + $this->assertEquals($importParams['title'], $roleEntity->title); + $this->assertEquals($contactID, $roleEntity->funder); + $this->assertEquals(0, $roleEntity->funder_val_type); + $this->assertEquals($importParams['hrjc_role_amount_pay_funder'], $roleEntity->amount_pay_funder); + } + + function testImportFunderWithInvalidID() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // run importer + $importParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test import role', + 'location' => 'amman', + 'hrjc_region' => 'south amman', + 'hrjc_role_department' => 'amman devs', + 'hrjc_level_type' => 'guru', + 'funder' => 100000, + 'hrjc_funder_val_type' => 'fixed', + 'hrjc_role_amount_pay_funder' => '30' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::ERROR, $importResponse); + } + + // I commented out these tests because I didn't had the chance to run them before the release + // and I run and uncomment them if the passed in other PR later + /*function testImportFunderWithInvalidDisplayName() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // run importer + $importParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test import role', + 'location' => 'amman', + 'hrjc_region' => 'south amman', + 'hrjc_role_department' => 'amman devs', + 'hrjc_level_type' => 'guru', + 'funder' => 'wrong name', + 'hrjc_funder_val_type' => 'fixed', + 'hrjc_role_amount_pay_funder' => '30' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::ERROR, $importResponse); + } + + function testImportFunderWithInvalidValueType() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // run importer + $importParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test import role', + 'location' => 'amman', + 'hrjc_region' => 'south amman', + 'hrjc_role_department' => 'amman devs', + 'hrjc_level_type' => 'guru', + 'funder' => $contactID, + 'hrjc_funder_val_type' => 'wrong_type', + 'hrjc_role_amount_pay_funder' => '30' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::ERROR, $importResponse); + } + + function testImportFunderWithInvalidPercentPay() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // run importer + $importParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test import role', + 'location' => 'amman', + 'hrjc_region' => 'south amman', + 'hrjc_role_department' => 'amman devs', + 'hrjc_level_type' => 'guru', + 'funder' => $contactID, + 'hrjc_funder_val_type' => '%', + 'hrjc_role_percent_pay_funder' => 'should_be_number' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::ERROR, $importResponse); + } + + function testImportFunderWithInvalidAmountPay() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // run importer + $importParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test import role', + 'location' => 'amman', + 'hrjc_region' => 'south amman', + 'hrjc_role_department' => 'amman devs', + 'hrjc_level_type' => 'guru', + 'funder' => $contactID, + 'hrjc_funder_val_type' => 'fixed', + 'hrjc_role_percent_pay_funder' => 'should_be_number' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::ERROR, $importResponse); + } + + function testImportFunderWithoutValueType() { + // create contact + $contactParams = ['first_name'=>'walter', 'last_name'=>'white']; + $contactID = $this->individualCreate($contactParams); + + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // run importer + $importParams = [ + 'job_contract_id' => $contract->id, + 'title' => 'test import role', + 'location' => 'amman', + 'hrjc_region' => 'south amman', + 'hrjc_role_department' => 'amman devs', + 'hrjc_level_type' => 'guru', + 'funder' => $contactID, + 'hrjc_role_percent_pay_funder' => '30' + ]; + $importResponse = $this->runImport($importParams); + $this->assertEquals(CRM_Import_Parser::ERROR, $importResponse); + }*/ + + private function runImport($params) { + $fields = array_keys($params); + $values = array_values($params); + $importObject = new CRM_Hrjobroles_Import_Parser_HrJobRoles($fields); + $importObject->init(); + return $importObject->import(NULL, $values); + } + +} diff --git a/com.civicrm.hrjobroles/tests/phpunit/HrJobRolesTestBase.php b/com.civicrm.hrjobroles/tests/phpunit/HrJobRolesTestBase.php new file mode 100644 index 00000000000..e3d5f170142 --- /dev/null +++ b/com.civicrm.hrjobroles/tests/phpunit/HrJobRolesTestBase.php @@ -0,0 +1,130 @@ +cleanDB(); + parent::setUp(); + $jobContractUpgrader = CRM_Hrjobcontract_Upgrader::instance(); + $jobContractUpgrader->install(); + } + + function tearDown() { + parent::tearDown(); + } + + /** + * Creates a new Job Contract for the given contact + * + * If a startDate is given, it will also create a JobDetails instance to save + * the contract's start date and end date(if given) + * + * @param $contactID + * @param null $startDate + * @param null $endDate + * @param array $extraParams + * + * @return \CRM_HRJob_DAO_HRJobContract|NULL + */ + public function createJobContract($contactID, $startDate = null, $endDate = null, $extraParams = array()) { + $contract = CRM_Hrjobcontract_BAO_HRJobContract::create(['contact_id' => $contactID]); + + if($startDate) { + $params = [ + 'jobcontract_id' => $contract->id, + 'period_start_date' => CRM_Utils_Date::processDate($startDate), + 'period_end_date' => null, + ]; + if($endDate) { + $params['period_end_date'] = CRM_Utils_Date::processDate($endDate); + } + $params = array_merge($params, $extraParams); + + CRM_Hrjobcontract_BAO_HRJobDetails::create($params); + } + + return $contract; + } + + /** + * Creates a new Job Role with specified parameters + * + * @param $params + * + * @return \CRM_Hrjobroles_BAO_HrJobRoles|NULL + */ + public function createJobRole($params) { + return CRM_Hrjobroles_BAO_HrJobRoles::create($params); + } + + /** + * Creates sample option group and values to be used in tests + * + */ + public function createSampleOptionGroupsAndValues() { + + // Create required Option Groups + $optionGroupsValuesList = [ + 'hrjc_location' => 'amman', + 'hrjc_region' => 'south amman', + 'hrjc_department' => 'amman devs', + 'hrjc_level_type' => 'guru', + 'cost_centres' => 'abdali' + ]; + + $optionGroupsList = array_keys($optionGroupsValuesList); + $INList = implode("','", $optionGroupsList ); + + CRM_Core_DAO::executeQuery( + "UPDATE civicrm_option_group SET is_active = 1 + WHERE name IN ('$INList')" + ); + + $query = "SELECT id,name FROM civicrm_option_group WHERE + name IN ('$INList')"; + $optionGroups = CRM_Core_DAO::executeQuery($query); + + $existingGroups = []; + while($optionGroups->fetch()) { + $existingGroups[$optionGroups->id] = $optionGroups->name; + } + + $newGroups = []; + foreach($optionGroupsList as $neededGroup) { + if(array_search($neededGroup, $existingGroups) === FALSE) { + $params = ['name' => $neededGroup, 'is_active' => 1]; + $newGroup = CRM_Core_BAO_OptionGroup::add($params); + $newGroups[$newGroup->id] = $newGroup->name; + } + } + + $finalGroupList = $existingGroups + $newGroups; + // Create sample option values + foreach ($optionGroupsValuesList as $group => $value) { + $groupID = array_search($group, $finalGroupList); + $params = ['option_group_id' => $groupID, 'name' => $value, 'value' => $value ]; + CRM_Core_BAO_OptionValue::create($params); + } + + } + + /** + * Find and retrieve job role by any of its properties + * + * @param array $params + * + * @return \CRM_Hrjobroles_BAO_HrJobRoles|NULL + */ + public function findRole($params) { + $default = NUll; + return CRM_Hrjobroles_BAO_HrJobRoles::commonRetrieve( + 'CRM_Hrjobroles_BAO_HrJobRoles', + $params, + $default + ); + } + +} diff --git a/com.civicrm.hrjobroles/tests/phpunit/api/v3/HrJobRolesTest.php b/com.civicrm.hrjobroles/tests/phpunit/api/v3/HrJobRolesTest.php new file mode 100644 index 00000000000..f4516aa72d9 --- /dev/null +++ b/com.civicrm.hrjobroles/tests/phpunit/api/v3/HrJobRolesTest.php @@ -0,0 +1,134 @@ +callAPIFailure('HrJobRoles', 'create', array()); + } + + /** + * check if required fields are not passed + */ + function testJobRoleCreateWithoutJobContract() { + $params = array( + 'title' => 'test role', + ); + $this->callAPIFailure('HrJobRoles', 'create', $params); + } + + /** + * Test creating new role with valid parameters + */ + function testJobRoleCreate() { + // create contact + $contactParams = array('first_name'=>'walter', 'last_name'=>'white'); + $contactID = $this->individualCreate($contactParams); + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + $params = ['job_contract_id' => $contract->id, 'title' => 'test role', 'sequential' => 1]; + $result = $this->callAPISuccess('HrJobRoles', 'create', $params); + $this->assertEquals($result['values'][0]['job_contract_id'], $contract->id); + $this->assertEquals($result['values'][0]['title'], 'test role'); + } + + /** + * Test creating new role with valid + * (location, region, department, level, cost center ) fields + * + */ + function testJobRoleCreateWithValidOptionValues() { + // create contact + $contactParams = array('first_name'=>'walter', 'last_name'=>'white'); + $contactID = $this->individualCreate($contactParams); + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // create option group and values + $this->createSampleOptionGroupsAndValues(); + + $params = [ + 'job_contract_id' => $contract->id, + 'title' => 'test role', + 'location' => 'amman', + 'region' => 'south amman', + 'department' => 'amman devs', + 'level_type' => 'guru', + 'cost_center' => 'abdali', + 'sequential' => 1 + ]; + $result = $this->callAPISuccess('HrJobRoles', 'create', $params); + $this->assertEquals($result['values'][0]['location'], 'amman'); + $this->assertEquals($result['values'][0]['region'], 'south amman'); + $this->assertEquals($result['values'][0]['department'], 'amman devs'); + $this->assertEquals($result['values'][0]['level_type'], 'guru'); + $this->assertEquals($result['values'][0]['cost_center'], 'abdali'); + } + + /** + * Test creating new role with Invalid + * (location, region, department, level, cost center ) fields + * + */ + function testJobRoleCreateWithInvalidOptionValues() { + // create contact + $contactParams = array('first_name'=>'walter', 'last_name'=>'white'); + $contactID = $this->individualCreate($contactParams); + // create contract + $contract = $this->createJobContract($contactID, date('Y-m-d', strtotime('-14 days'))); + + // create option group and values + $this->createSampleOptionGroupsAndValues(); + + // test invalid location + $params = [ + 'job_contract_id' => $contract->id, + 'title' => 'test role1', + 'location' => 'ammandadas', + ]; + $this->callAPIFailure('HrJobRoles', 'create', $params); + // test invalid region + $params = [ + 'job_contract_id' => $contract->id, + 'title' => 'test role2', + 'region' => 'soutsh ammansasdad', + ]; + $this->callAPIFailure('HrJobRoles', 'create', $params); + // test invalid department + $params = [ + 'job_contract_id' => $contract->id, + 'title' => 'test role3', + 'department' => 'ammadan devsdadada', + ]; + $this->callAPIFailure('HrJobRoles', 'create', $params); + // test invalid level type + $params = [ + 'job_contract_id' => $contract->id, + 'title' => 'test role4', + 'level_type' => 'gurfdsfsdfu', + ]; + $this->callAPIFailure('HrJobRoles', 'create', $params); + // test cost center + $params = [ + 'job_contract_id' => $contract->id, + 'title' => 'test role5', + 'cost_center' => 'abdfdsfsdali', + ]; + $this->callAPIFailure('HrJobRoles', 'create', $params); + } + +} diff --git a/com.civicrm.hrjobroles/xml/schema/CRM/Hrjobroles/HrJobRoles.xml b/com.civicrm.hrjobroles/xml/schema/CRM/Hrjobroles/HrJobRoles.xml index 48622b67d0e..b0122804c6b 100644 --- a/com.civicrm.hrjobroles/xml/schema/CRM/Hrjobroles/HrJobRoles.xml +++ b/com.civicrm.hrjobroles/xml/schema/CRM/Hrjobroles/HrJobRoles.xml @@ -96,6 +96,9 @@ true Role region value. hrjc_region + + hrjc_region + index_region @@ -111,6 +114,9 @@ true Role department. hrjc_role_department + + hrjc_department + index_department @@ -126,6 +132,9 @@ true true hrjc_level_type + + hrjc_level_type + index_level_type @@ -280,6 +289,9 @@ Main work location true true + + hrjc_location + index_location @@ -308,4 +320,4 @@ hrjc_role_end_date - \ No newline at end of file + diff --git a/contactsummary/CRM/Contactsummary/Utils/Absences.php b/contactsummary/CRM/Contactsummary/Utils/Absences.php index e3742a8adb2..7ef74e048fc 100644 --- a/contactsummary/CRM/Contactsummary/Utils/Absences.php +++ b/contactsummary/CRM/Contactsummary/Utils/Absences.php @@ -1,69 +1,66 @@ = NOW() OR jcd.period_end_date IS NULL)) - - INNER JOIN civicrm_activity a1 - ON (a.id = a1.source_record_id AND a1.activity_type_id = %1 AND a1.status_id = %2) + LEFT JOIN civicrm_activity a2 ON a.source_record_id = a2.id + LEFT JOIN civicrm_activity_contact ac ON a2.id = ac.activity_id + LEFT JOIN civicrm_contact c ON ac.contact_id = c.id + LEFT JOIN civicrm_hrjobcontract hrjc ON (c.id = hrjc.contact_id) + LEFT JOIN civicrm_hrjobcontract_revision hrjr ON hrjr.jobcontract_id = hrjc.id + LEFT JOIN civicrm_hrjobcontract_details hrjd ON hrjr.details_revision_id = hrjd.jobcontract_revision_id WHERE - a.id IN (" . implode(',', $absenceTypeIds) . ") - AND a.status_id = %2 + a.source_record_id + IN ( + SELECT t2.id + FROM civicrm_activity_contact t1 + LEFT JOIN civicrm_activity t2 ON t1.activity_id = t2.id + WHERE t2.activity_type_id = %1 + AND t1.record_type_id = {$RECORD_TYPE} + ) + + AND a.status_id = %2 + AND (a.activity_date_time >= %3 AND a.activity_date_time < %4) + AND a.is_deleted = 0 + + AND ac.record_type_id ={$RECORD_TYPE} + + AND c.contact_type = 'Individual' + + AND hrjc.deleted = 0 + AND hrjr.deleted = 0 + AND hrjd.period_start_date <= '{$currentDate}' + AND ( hrjd.period_end_date >= '{$currentDate}' OR hrjd.period_end_date IS NULL) + group by a.id + ) AS total_minutes "; + $activityStatuses = CRM_HRAbsence_BAO_HRAbsenceType::getActivityStatus('name'); + $periodDetails = CRM_HRAbsence_BAO_HRAbsencePeriod::getDefaultValues($periodId); + $params = array( - 1 => array(static::getAbsenceActivityTypeId(), 'Integer'), - 2 => array(CRM_Utils_Array::key('Completed', $activityStatuses), 'Integer'), + 1 => array(static::getAbsenceTypeId($absenceTypeName), 'Integer'), + 2 => array(CRM_Utils_Array::key('Completed', $activityStatuses), 'Integer'), + 3 => array($periodDetails['start_date'], 'String'), + 4 => array($periodDetails['end_date'], 'String'), ); $duration = 0; @@ -77,64 +74,31 @@ private static function getAbsenceDuration($absenceTypeNames = array(), $periodI } /** - * Get an array of activity IDs for absences, corresponding to a given array of absence type names. + * Get the ID for the requested absence type using its name * - * @param array $absenceTypeNames - * @param $periodId + * @param string $absenceTypeName * - * @return array + * @return int|NULL */ - private static function getActivityIdsForAbsenceTypeNames($absenceTypeNames = array(), $periodId) { - $ids = array(); - - $absenceTypeIds = array(); - foreach (static::getAbsenceTypes() as $type) { - if (in_array(strtolower($type['name']), $absenceTypeNames) || !$absenceTypeNames) { - $absenceTypeIds[] = $type['debit_activity_type_id']; - } - } + private static function getAbsenceTypeId($absenceTypeName) { + $sql = " + SELECT cov.value from civicrm_option_group cog + LEFT JOIN civicrm_option_value cov ON cov.option_group_id = cog.id + WHERE cog.name = 'activity_type' + AND cov.name = %1 + AND cov.is_active = 1 + "; - $absences = static::getAbsences($periodId); + $params = array( + 1 => array($absenceTypeName, 'String'), + ); - foreach ($absences as $id => $absence) { - if (in_array($absence['activity_type_id'], $absenceTypeIds)) { - $ids[] = $id; - } + $dao = CRM_Core_DAO::executeQuery($sql, $params); + if ($dao->fetch()) { + return $dao->value; } - return array_filter($ids); - } - - /** - * Get a list of all absence types. - * - * @return mixed - * @throws \CiviCRM_API3_Exception - */ - private static function getAbsenceTypes() { - $result = civicrm_api3('HRAbsenceType', 'get'); - - return $result['values']; + return null; } - /** - * Get a list of all absences. - * - * @param $periodId - * - * @return - * @throws \CiviCRM_API3_Exception - */ - private static function getAbsences($periodId) { - $result = civicrm_api3('Activity', 'getabsences', array('period_id' => $periodId)); - - return $result['values']; - } - - /** - * Get the activity type ID for absences. - */ - private static function getAbsenceActivityTypeId() { - return array_search('Absence', CRM_Core_PseudoConstant::activityType()); - } -} \ No newline at end of file +} diff --git a/contactsummary/CRM/Contactsummary/Utils/Staff.php b/contactsummary/CRM/Contactsummary/Utils/Staff.php deleted file mode 100644 index f80586ca67d..00000000000 --- a/contactsummary/CRM/Contactsummary/Utils/Staff.php +++ /dev/null @@ -1,34 +0,0 @@ -= NOW() OR jcd.period_end_date IS NULL)) - - WHERE c.contact_type = 'Individual'"; - - $total = 0; - - $dao = CRM_Core_DAO::executeQuery($query); - if ($dao->fetch()) { - $total = $dao->count; - } - - return $total; - } -} \ No newline at end of file diff --git a/contactsummary/api/v3/ContactSummary/Getabsenceaggregate.php b/contactsummary/api/v3/ContactSummary/Getabsenceaggregate.php index 0dd881463d7..fc6e2b24fe3 100644 --- a/contactsummary/api/v3/ContactSummary/Getabsenceaggregate.php +++ b/contactsummary/api/v3/ContactSummary/Getabsenceaggregate.php @@ -29,10 +29,10 @@ function civicrm_api3_contact_summary_GetAbsenceAggregate($params) { $aggregateType = !empty($params['aggregate_type']) ? $params['aggregate_type'] : CRM_Contactsummary_Utils_Aggregate::TYPE_AVERAGE; - $absenceTypes = !empty($params['absence_types']) ? (array) $params['absence_types'] : array(); + $absenceTypes = !empty($params['absence_types']) ? $params['absence_types'] : null; $periodId = !empty($params['period_id']) ? $params['period_id'] : 0; - $numStaff = CRM_Contactsummary_Utils_Staff::getStaffNum(); + $numStaff = CRM_Hrjobcontract_BAO_HRJobContract::getStaffCount(); $totalAbsences = CRM_Contactsummary_Utils_Absences::getTotalAbsences($absenceTypes, $periodId); $values = array('staff' => $numStaff, 'absences' => $totalAbsences); diff --git a/contactsummary/tests/phpunit/CRM/Contactsummary/Utils/AbsencesTest.php b/contactsummary/tests/phpunit/CRM/Contactsummary/Utils/AbsencesTest.php new file mode 100644 index 00000000000..36637133570 --- /dev/null +++ b/contactsummary/tests/phpunit/CRM/Contactsummary/Utils/AbsencesTest.php @@ -0,0 +1,117 @@ +cleanDB(); + parent::setUp(); + $jobContractUpgrader = CRM_Hrjobcontract_Upgrader::instance(); + $jobContractUpgrader->install(); + $hrAbsenceUpgrader = CRM_HRAbsence_Upgrader::instance(); + $hrAbsenceUpgrader->installAbsenceTypes(); + $params = array( + 'weight' => 100, + 'label' => 'Absence', + 'filter' => 1, + 'is_active' => 1, + 'is_optgroup' => 0, + 'is_default' => 0, + 'grouping' => 'Timesheet', + ); + civicrm_api3('activity_type', 'create', $params); + } + + function tearDown() { + parent::tearDown(); + } + + function testGetTotalAbsencesForSickLeaveType() { + + // create absence periods + $StartDate1 = date('YmdHis', strtotime('-1 year')); + $EndDate1 = date('YmdHis', strtotime('+1 year')); + $StartDate2 = date('YmdHis', strtotime('-4 years')); + $EndDate2 = date('YmdHis', strtotime('-3 years')); + $StartDate3 = date('YmdHis', strtotime('+3 years')); + $EndDate3 = date('YmdHis', strtotime('+4 years')); + + $currentPeriodID = $this->createAbsencePeriod($StartDate1, $EndDate1); + $this->createAbsencePeriod($StartDate2, $EndDate2); + $this->createAbsencePeriod($StartDate3, $EndDate3); + + // create contacts + $contact1 = array('first_name'=>'walter', 'last_name'=>'white'); + $contactID1 = $this->individualCreate($contact1); + $contact2 = array('first_name'=>'walter1', 'last_name'=>'white1'); + $contactID2 = $this->individualCreate($contact2); + $contact3 = array('first_name'=>'walter2', 'last_name'=>'white2'); + $contactID3 = $this->individualCreate($contact3); + $contact4 = array('first_name'=>'walter3', 'last_name'=>'white3'); + $contactID4 = $this->individualCreate($contact4); + + + // create contracts + $this->createJobContract($contactID1, date('Y-m-d', strtotime('-14 days'))); + $this->createJobContract($contactID2, date('Y-m-d', strtotime('-5 days'))); + $this->createJobContract($contactID3, date('Y-m-d', strtotime('-10 days'))); + + // get sick leave type ID + $sickType = civicrm_api3('HRAbsenceType', 'getsingle', array( + 'sequential' => 1, + 'name' => "sick", + )); + $sickTypeID = $sickType['id']; + + // create absence entitlements + $params1 = array( + 'contact_id' => $contactID1, + 'type_id' => $sickTypeID, + 'amount' => 20, + 'period_id' => $currentPeriodID + ); + $this->createAbsenceEntitlement($params1); + + $params2 = array( + 'contact_id' => $contactID2, + 'type_id' => $sickTypeID, + 'amount' => 20, + 'period_id' => $currentPeriodID + ); + $this->createAbsenceEntitlement($params2); + + $params3 = array( + 'contact_id' => $contactID3, + 'type_id' => $sickTypeID, + 'amount' => 20, + 'period_id' => $currentPeriodID + ); + $this->createAbsenceEntitlement($params3); + + // request sick leaves + + $this->requestLeave( + 'sick', + $contactID1, + date('Y-m-d', strtotime('+5 days')), + date('Y-m-d', strtotime('+8 days')), + 'full_day' + ); + $this->requestLeave( + 'sick', + $contactID2, + date('Y-m-d', strtotime('+5 days')), + date('Y-m-d', strtotime('+12 days')), + 'full_day' + ); + + $leaveDays = CRM_Contactsummary_Utils_Absences::getTotalAbsences('sick', $currentPeriodID); + $this->assertEquals(5760, round($leaveDays, 2)); + + } + +} diff --git a/contactsummary/tests/phpunit/CRM/Contactsummary/Utils/ContractSummaryTestTrait.php b/contactsummary/tests/phpunit/CRM/Contactsummary/Utils/ContractSummaryTestTrait.php new file mode 100644 index 00000000000..2b209ad068e --- /dev/null +++ b/contactsummary/tests/phpunit/CRM/Contactsummary/Utils/ContractSummaryTestTrait.php @@ -0,0 +1,146 @@ + $startDate, 'start_date' => $startDate, 'end_date' => $endDate); + $period = CRM_HRAbsence_BAO_HRAbsencePeriod::create($params); + return $period->id; + } + + /** + * Creates an absence entitlement + * + * @param $params + * + * @return int entitlement ID + */ + protected function createAbsenceEntitlement($params) { + $entitlement = CRM_HRAbsence_BAO_HRAbsenceEntitlement::create($params); + return $entitlement->id; + } + + /** + * Creates a new Job Contract for the given contact + * + * If a startDate is given, it will also create a JobDetails instance to save + * the contract's start date and end date(if given) + * + * @param $contactID + * @param null $startDate + * @param null $endDate + * @param array $extraParams + * + * @return \CRM_HRJob_DAO_HRJobContract|NULL + */ + protected function createJobContract($contactID, $startDate = null, $endDate = null, $extraParams = array()) { + $contract = CRM_Hrjobcontract_BAO_HRJobContract::create(['contact_id' => $contactID]); + if($startDate) { + $params = [ + 'jobcontract_id' => $contract->id, + 'period_start_date' => CRM_Utils_Date::processDate($startDate), + 'period_end_date' => null, + ]; + if($endDate) { + $params['period_end_date'] = CRM_Utils_Date::processDate($endDate); + } + $params = array_merge($params, $extraParams); + CRM_Hrjobcontract_BAO_HRJobDetails::create($params); + } + return $contract; + } + + /** + * Request Leave for a specific contact + * + * @param string $absenceType + * @param int $contactID + * @param string $from leave request start date in Y-m-d format + * @param string $to leave request end date in Y-m-d format + * @param string $absence full_day or half_day + * + * @return int + */ + protected function requestLeave($absenceType, $contactID, $from, $to, $absence) { + $begin = new DateTime($from); + $end = new DateTime($to); + $end = $end->modify('+1 day'); + + $interval = DateInterval::createFromDateString('1 day'); + $period = new DatePeriod($begin, $interval, $end); + + $activityStatus = CRM_HRAbsence_BAO_HRAbsenceType::getActivityStatus('name'); + $statusID = CRM_Utils_Array::key('Completed', $activityStatus); + + $absenceTypeID = $this->getAbsenceTypeId('absence'); + $sickTypeID = $this->getAbsenceTypeId($absenceType); + + $activityParam = array( + 'activity_type_id' => $sickTypeID, + 'activity_date_time' => date('Y-m-d H:i:s'), + 'status_id' => $statusID, + 'source_contact_id' => 1, // 1 is the (default organization) contract ID + ); + $mainActivity = civicrm_api3('Activity', 'create', $activityParam); + $sourceActivityID = $mainActivity['id']; + + civicrm_api3('ActivityContact', 'create', array( + 'activity_id' => $sourceActivityID, + 'contact_id' => $contactID, + 'record_type_id' => 3, + )); + + foreach ( $period as $dt ) { + $leaveDate = $dt->format('Y-m-d'); + $activityParam = array( + 'source_record_id' => $sourceActivityID, + 'activity_type_id' => $absenceTypeID, + 'activity_date_time' => $leaveDate, + 'duration' => ($absence == 'full_day') ? 480 : 240, + 'status_id' => $statusID, + 'source_contact_id' => 1, // 1 is the (default organization) contract ID + ); + civicrm_api3('Activity', 'create', $activityParam); + } + } + + /** + * Get the ID for the requested absence type using its name + * + * @param $absenceTypeName + * + * @return int|NULL + */ + protected function getAbsenceTypeId($absenceTypeName) { + $sql = " + SELECT cov.value from civicrm_option_group cog + LEFT JOIN civicrm_option_value cov ON cov.option_group_id = cog.id + WHERE cog.name = 'activity_type' + AND cov.name = %1 + AND cov.is_active = 1 + "; + + $params = array( + 1 => array($absenceTypeName, 'String'), + ); + + $dao = CRM_Core_DAO::executeQuery($sql, $params); + if ($dao->fetch()) { + return $dao->value; + } + + return null; + } + +} diff --git a/hrabsence/CRM/HRAbsence/DAO/HRAbsencePeriod.php b/hrabsence/CRM/HRAbsence/DAO/HRAbsencePeriod.php index 1b7f1f667bd..1bed3c08e2a 100644 --- a/hrabsence/CRM/HRAbsence/DAO/HRAbsencePeriod.php +++ b/hrabsence/CRM/HRAbsence/DAO/HRAbsencePeriod.php @@ -109,13 +109,13 @@ class CRM_HRAbsence_DAO_HRAbsencePeriod extends CRM_Core_DAO /** * Absence Period Start Date * - * @var timestamp + * @var datetime */ public $start_date; /** * Absence Period End Date * - * @var timestamp + * @var datetime */ public $end_date; /** @@ -169,12 +169,12 @@ static function &fields() ) , 'start_date' => array( 'name' => 'start_date', - 'type' => CRM_Utils_Type::T_TIMESTAMP, + 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, 'title' => ts('Start Date') , ) , 'end_date' => array( 'name' => 'end_date', - 'type' => CRM_Utils_Type::T_TIMESTAMP, + 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME, 'title' => ts('End Date') , ) , ); diff --git a/hrabsence/CRM/HRAbsence/Upgrader.php b/hrabsence/CRM/HRAbsence/Upgrader.php index 59958666307..2895dcbdf64 100644 --- a/hrabsence/CRM/HRAbsence/Upgrader.php +++ b/hrabsence/CRM/HRAbsence/Upgrader.php @@ -58,8 +58,8 @@ public function addDefaultPeriod() { $params = array( 'name' => $currentYear, 'title' => $currentYear.' (Jan 1 to Dec 31)', - 'start_date' => $currentYear.'-01-01 00:00:00', - 'end_date' => $currentYear.'-12-31 23:59:59', + 'start_date' => $currentYear.'0101000000', + 'end_date' => $currentYear.'1231235959', ); CRM_HRAbsence_BAO_HRAbsencePeriod::create($params); } @@ -130,7 +130,7 @@ public function installAbsenceTypes() { ), 'is_active' => 1, ); - + $customGroup = civicrm_api3('CustomGroup', 'get', array( 'sequential' => 1, 'title' => $paramsCGroup['title'], @@ -163,7 +163,7 @@ public function installAbsenceTypes() { ), 'is_active' => 1, ); - + $sickGroup = civicrm_api3('CustomGroup', 'get', array( 'sequential' => 1, 'title' => $paramsSGroup['title'], diff --git a/hrabsence/xml/schema/CRM/HRAbsence/HRAbsencePeriod.xml b/hrabsence/xml/schema/CRM/HRAbsence/HRAbsencePeriod.xml index 47e9f1dbd34..4adca08c04e 100644 --- a/hrabsence/xml/schema/CRM/HRAbsence/HRAbsencePeriod.xml +++ b/hrabsence/xml/schema/CRM/HRAbsence/HRAbsencePeriod.xml @@ -50,14 +50,14 @@ start_date Absence Period Start Date - timestamp + datetime Absence Period Start Date - + end_date Absence Period End Date - timestamp + datetime Absence Period End Date diff --git a/hrjobcontract/CRM/Hrjobcontract/BAO/HRJobContract.php b/hrjobcontract/CRM/Hrjobcontract/BAO/HRJobContract.php index c819539f9b1..afa0df1abb5 100644 --- a/hrjobcontract/CRM/Hrjobcontract/BAO/HRJobContract.php +++ b/hrjobcontract/CRM/Hrjobcontract/BAO/HRJobContract.php @@ -1,7 +1,7 @@ copyValues($params); $instance->save(); CRM_Utils_Hook::post($hook, $entityName, $instance->id, $instance); - + if ((is_numeric(CRM_Utils_Array::value('is_primary', $params)) || $hook === 'create') && empty($params['import'])) { CRM_Hrjobcontract_DAO_HRJobContract::handlePrimary($instance, $params); } - + $deleted = isset($params['deleted']) ? $params['deleted'] : 0; if ($deleted) { CRM_Hrjobcontract_JobContractDates::removeDates($instance->id); } - + if (function_exists('module_exists') && module_exists('rules')) { rules_invoke_event('hrjobcontract_after_create', $instance); } return $instance; } - + /** * Delete current HRJobContract based on array-data * @@ -53,7 +53,7 @@ public function delete($useWhere = false) { rules_invoke_event('hrjobcontract_after_delete', $id); } } - + /** * Get a count of records with the given property * @@ -112,11 +112,11 @@ public static function findById($id) { /** * Return 'length_of_service' in days for given Contact ID, and optionally * Date and Break (allowed number of days between Contracts). - * + * * @param int $contactId CiviCRM Contact ID * @param string $date Y-m-d format of a date for which we calculate the result * @param int $break Allowed number of days between Contracts - * + * * @throws Exception * @return int */ @@ -177,9 +177,9 @@ public static function getContactContracts($cuid) { /** * Return an assotiative array with Contracts dates. - * + * * @param array $contracts - * + * * @return array */ protected static function getContractDates($contracts) { @@ -219,10 +219,10 @@ protected static function getContractDates($contracts) { /** * Return an array with calculated Service Start Date and Service End Date. - * + * * @param array $dates * @param int $break Number of Break days - * + * * @return array */ protected static function getServiceDates($dates, $break) { @@ -255,11 +255,11 @@ protected static function getServiceDates($dates, $break) { /** * Return a difference of Service dates in days (including break days). - * + * * @param array $serviceDates Array containing 'startDate' and 'endDate' keys * @param string $date Date in Y-m-d format for which we calculate the result * @param int $break Allowed number of days between Contracts - * + * * @return int */ protected static function calculateLength($serviceDates, $date, $break) { @@ -282,10 +282,10 @@ protected static function calculateLength($serviceDates, $date, $break) { /** * Calculate a new Date which is sum of given Date and number of Break days. * Returns null if given date is null. - * + * * @param string $date Date in Y-m-d format * @param int $break Number of Break days - * + * * @return string|null */ protected static function sumDateAndBreak($date, $break) { @@ -296,10 +296,10 @@ protected static function sumDateAndBreak($date, $break) { $newDate->add(new DateInterval('P' . $break . 'D')); return $newDate->format('Y-m-d'); } - + /** * Update Length of Service for specific Contact. - * + * * @return bool */ public static function updateLengthOfService($contactId) { @@ -326,7 +326,7 @@ public static function updateLengthOfService($contactId) { /** * Update Length of Service for all Individual Contacts. - * + * * @return bool */ public static function updateLengthOfServiceAllContacts() { @@ -362,6 +362,39 @@ public static function checkContract($contractID) { return $result->fetch() ? $result : 0; } + /** + * Get the total number of staff (Individual contacts with active contracts). + * + * @return int + */ + public static function getStaffCount() { + + $currentDate = date('Y-m-d'); + + $query = " + SELECT COUNT(DISTINCT c.id) count + FROM civicrm_contact c + LEFT JOIN civicrm_hrjobcontract hrjc ON (c.id = hrjc.contact_id) + LEFT JOIN civicrm_hrjobcontract_revision hrjr ON hrjr.jobcontract_id = hrjc.id + LEFT JOIN civicrm_hrjobcontract_details hrjd + ON hrjr.details_revision_id = hrjd.jobcontract_revision_id + WHERE c.contact_type = 'Individual' + AND hrjd.period_start_date <= '{$currentDate}' + AND ( hrjd.period_end_date >= '{$currentDate}' OR hrjd.period_end_date IS NULL) + AND c.is_deleted = 0 + AND hrjc.deleted = 0 + AND hrjr.deleted = 0"; + + $total = 0; + + $dao = CRM_Core_DAO::executeQuery($query); + if ($dao->fetch()) { + $total = $dao->count; + } + + return $total; + } + /** * combine all the importable fields from the lower levels object * @@ -385,14 +418,14 @@ static function &importableFields($contactType = 'Individual', $checkPermission = TRUE, $withMultiCustomFields = FALSE ) { - + $contactType = 'Individual'; - + $fields = CRM_Hrjobcontract_DAO_HRJobContract::import(); - + $tmpContactField = $contactFields = array(); $contactFields = array( ); - + $contactFields = CRM_Contact_BAO_Contact::importableFields($contactType, NULL); // Using new Dedupe rule. @@ -420,7 +453,7 @@ static function &importableFields($contactType = 'Individual', $tmpContactField[trim($value)]['title'] = $title; } } - + $extIdentifier = CRM_Utils_Array::value('external_identifier', $contactFields); if ($extIdentifier) { $tmpContactField['external_identifier'] = $extIdentifier; diff --git a/hrjobcontract/CRM/Hrjobcontract/ExportImportValuesConverter.php b/hrjobcontract/CRM/Hrjobcontract/ExportImportValuesConverter.php index fc03742b769..7541876944a 100644 --- a/hrjobcontract/CRM/Hrjobcontract/ExportImportValuesConverter.php +++ b/hrjobcontract/CRM/Hrjobcontract/ExportImportValuesConverter.php @@ -3,7 +3,7 @@ class CRM_Hrjobcontract_ExportImportValuesConverter { static private $_singleton = NULL; - + protected $_annualOptions = array(); protected $_annualOptionsFlipped = array(); protected $_contractTypeOptions = array(); @@ -11,6 +11,8 @@ class CRM_Hrjobcontract_ExportImportValuesConverter protected $_hoursLocationOptions = array(); protected $_hoursTypeOptions = array(); protected $_hoursTypeOptionsFlipped = array(); + protected $_endReasonOptions = array(); + protected $_endReasonOptionsFlipped = array(); protected $_leaveTypes = array(); protected $_leaveTypesFlipped = array(); protected $_locationOptions = array(); @@ -20,12 +22,12 @@ class CRM_Hrjobcontract_ExportImportValuesConverter protected $_payScaleOptions = array(); protected $_pensionTypeOptions = array(); protected $_pensionTypeOptionsFlipped = array(); - + private function __construct() { $this->_initialize(); } - + /** * singleton function used to manage this object * @@ -40,7 +42,7 @@ static function &singleton() } return self::$_singleton; } - + protected function _initialize() { // annual benefits options: @@ -54,7 +56,7 @@ protected function _initialize() { $this->_annualOptionsFlipped['benefit']['type'][$value] = $key; } - + // annual deductions options: CRM_Core_OptionGroup::getAssoc('hrjc_deduction_name', $this->_annualOptions['deduction']['name']); CRM_Core_OptionGroup::getAssoc('hrjc_deduction_type', $this->_annualOptions['deduction']['type']); @@ -66,7 +68,7 @@ protected function _initialize() { $this->_annualOptionsFlipped['deduction']['type'][$value] = $key; } - + // contract type options: $contractTypeOptions = array(); CRM_Core_OptionGroup::getAssoc('hrjc_contract_type', $contractTypeOptions, true); @@ -74,14 +76,21 @@ protected function _initialize() $this->_contractTypeOptions[$contractType['value']] = $contractType; $this->_contractTypeOptionsFlipped[$contractType['label']] = $contractType['value']; } - + + // end reason type options: + CRM_Core_OptionGroup::getAssoc('hrjc_contract_end_reason', $endReasons, true); + foreach ($endReasons as $endReason) { + $this->_endReasonOptions[$endReason['value']] = $endReason; + $this->_endReasonOptionsFlipped[$endReason['label']] = $endReason['value']; + } + // hours location options: $hoursLocation = new CRM_Hrjobcontract_BAO_HoursLocation(); $hoursLocation->find(); while ($hoursLocation->fetch()) { $this->_hoursLocationOptions[$hoursLocation->id] = (array)$hoursLocation; } - + // hours type options: $hoursTypeOptions = array(); CRM_Core_OptionGroup::getAssoc('hrjc_hours_type', $hoursType, true); @@ -89,7 +98,7 @@ protected function _initialize() $this->_hoursTypeOptions[$hourType['value']] = $hourType; $this->_hoursTypeOptionsFlipped[$hourType['label']] = $hourType['value']; } - + // leave types options: $absenceType = new CRM_HRAbsence_BAO_HRAbsenceType(); $absenceType->find(); @@ -98,7 +107,7 @@ protected function _initialize() $this->_leaveTypes[$absenceType->id] = $absenceTypeArray; $this->_leaveTypesFlipped[$absenceTypeArray['title']] = $absenceType->id; } - + // location options: $locationOptions = array(); CRM_Core_OptionGroup::getAssoc('hrjc_location', $locationOptions, true); @@ -106,7 +115,7 @@ protected function _initialize() $this->_locationOptions[$location['value']] = $location; $this->_locationOptionsFlipped[$location['label']] = $location['value']; } - + // pay cycle options: $payCycleOptions = array(); CRM_Core_OptionGroup::getAssoc('hrjc_pay_cycle', $payCycleOptions, true); @@ -114,14 +123,14 @@ protected function _initialize() $this->_payCycleOptions[$payCycle['value']] = $payCycle; $this->_payCycleOptionsFlipped[$payCycle['label']] = $payCycle['value']; } - + // pay scale options: $payScale = new CRM_Hrjobcontract_BAO_PayScale(); $payScale->find(); while ($payScale->fetch()) { $this->_payScaleOptions[$payScale->id] = (array)$payScale; } - + // pension type options: $pensionTypeOptions = array(); CRM_Core_OptionGroup::getAssoc('hrjc_pension_type', $pensionTypeOptions, true); @@ -130,7 +139,7 @@ protected function _initialize() $this->_pensionTypeOptionsFlipped[$pensionType['label']] = $pensionType['value']; } } - + public function export($entityName, $fieldName, $value) { $functionName = $entityName . '_' . $fieldName . '_export'; @@ -140,7 +149,7 @@ public function export($entityName, $fieldName, $value) } return $value; } - + public function import($entityName, $fieldName, $value) { $functionName = $entityName . '_' . $fieldName . '_import'; @@ -150,7 +159,7 @@ public function import($entityName, $fieldName, $value) } return $value; } - + public function contract_is_primary_export($value) { return (int)$value ? 'Yes' : 'No'; @@ -159,7 +168,7 @@ public function contract_is_primary_import($value) { return strtolower($value) === 'yes' ? 1 : 0; } - + public function details_contract_type_export($value) { return isset($this->_contractTypeOptions[$value]['label']) ? $this->_contractTypeOptions[$value]['label'] : $value; @@ -168,7 +177,7 @@ public function details_contract_type_import($value) { return $this->_contractTypeOptionsFlipped[$value]; } - + public function details_location_export($value) { return $this->_locationOptions[$value]['label']; @@ -181,7 +190,16 @@ public function details_location_import($value) return $this->_locationOptionsFlipped[$value]; } - + + public function details_end_reason_export($value) + { + return isset($value) ? $this->_endReasonOptions[$value]['label'] : null; + } + public function details_end_reason_import($value) + { + return !empty($value) ? $this->_endReasonOptionsFlipped[$value] : null; + } + public function hour_hours_type_export($value) { return isset($value) ? $this->_hoursTypeOptions[$value]['label'] : null; @@ -190,7 +208,7 @@ public function hour_hours_type_import($value) { return !empty($value) ? $this->_hoursTypeOptionsFlipped[$value] : null; } - + public function hour_location_standard_hours_export($value) { return isset($value) ? $this->_hoursLocationOptions[$value]['location'] . ' - ' . @@ -215,7 +233,7 @@ public function hour_location_standard_hours_import($value) } return $value; } - + public function leave_leave_amount_export($value) { $leaves = explode(',', $value); @@ -233,7 +251,7 @@ public function leave_leave_amount_import($value) { $value = $value[0]['leave_amount']; } - + $leaves = explode(',', $value); $output = array(); foreach ($leaves as $leave) @@ -241,10 +259,10 @@ public function leave_leave_amount_import($value) list($typeTitle, $leaveAmount) = explode(':', $leave); $output[$this->_leaveTypesFlipped[trim($typeTitle)]] = trim($leaveAmount); } - + return $output; } - + public function leave_leave_type_export($value) { $typeIds = explode(',', $value); @@ -260,7 +278,7 @@ public function leave_leave_type_import($value) //return isset($value) ? $this->_leaveTypesFlipped[$value] : null; return null; } - + public function pay_annual_benefits_export($value) { return isset($value) ? $this->_getAnnualReadableValues('benefit', $value) : null; @@ -269,7 +287,7 @@ public function pay_annual_benefits_import($value) { return !empty($value) ? $this->_getAnnualValues('benefit', $value) : null; } - + public function pay_annual_deductions_export($value) { return isset($value) ? $this->_getAnnualReadableValues('deduction', $value) : null; @@ -278,7 +296,7 @@ public function pay_annual_deductions_import($value) { return !empty($value) ? $this->_getAnnualValues('deduction', $value) : null; } - + public function pay_pay_cycle_export($value) { return isset($value) ? $this->_payCycleOptions[$value]['label'] : null; @@ -287,7 +305,7 @@ public function pay_pay_cycle_import($value) { return !empty($value) ? $this->_payCycleOptionsFlipped[$value] : null; } - + public function pay_pay_is_auto_est_export($value) { return (int)$value ? 'Yes' : 'No'; @@ -296,7 +314,7 @@ public function pay_pay_is_auto_est_import($value) { return strtolower($value) === 'yes' ? 1 : 0; } - + public function pay_is_paid_export($value) { return (int)$value ? 'Yes' : 'No'; @@ -305,7 +323,7 @@ public function pay_is_paid_import($value) { return strtolower($value) === 'yes' ? 1 : 0; } - + public function pay_pay_scale_export($value) { $result = ''; @@ -334,7 +352,7 @@ public function pay_pay_scale_import($value) { $parts = explode(' ', $keys[2]); } - + if (count($keys) === 4 && count($parts) === 2) { $result = civicrm_api3('HRPayScale', 'get', array( @@ -393,10 +411,10 @@ public function pay_pay_scale_import($value) return $result['id']; } } - + return null; } - + public function pension_is_enrolled_export($value) { $result = ''; @@ -431,7 +449,7 @@ public function pension_is_enrolled_import($value) } return $result; } - + public function pension_pension_type_export($value) { return $this->_pensionTypeOptions[$value]['label']; @@ -440,7 +458,7 @@ public function pension_pension_type_import($value) { return $this->_pensionTypeOptionsFlipped[$value]; } - + public function role_location_export($value) { return $this->details_location_export($value); @@ -449,7 +467,7 @@ public function role_location_import($value) { return $this->details_location_import($value); } - + protected function _getAnnualReadableValues($field, $json) { $list = json_decode($json, true); @@ -468,10 +486,10 @@ protected function _getAnnualReadableValues($field, $json) $output .= 'amount abs: ' . $row['amount_abs'] . '; '; } } - + return $output; } - + protected function _getAnnualValues($field, $value) { if (empty($value)) @@ -513,14 +531,14 @@ protected function _getAnnualValues($field, $value) } $outputArray[] = $outputRow; } - + return $outputArray; } - + public function getContactByLookup($data) { $contactId = null; - + // external_identifier: if (!empty($data['external_identifier']) && !$contactId) { $checkCid = new CRM_Contact_DAO_Contact(); @@ -530,7 +548,7 @@ public function getContactByLookup($data) $contactId = $checkCid->id; } } - + // email: if (!empty($data['email']) && !$contactId) { @@ -542,7 +560,7 @@ public function getContactByLookup($data) $contactId = $checkEmail->contact_id; } } - + // id: if (!empty($data['id']) && !$contactId) { $checkId = new CRM_Contact_DAO_Contact(); @@ -552,7 +570,7 @@ public function getContactByLookup($data) $contactId = $checkId->id; } } - + return $contactId; } } diff --git a/hrjobcontract/hrjobcontract.php b/hrjobcontract/hrjobcontract.php index 8df124038bf..a5dd98bdcd6 100644 --- a/hrjobcontract/hrjobcontract.php +++ b/hrjobcontract/hrjobcontract.php @@ -588,9 +588,10 @@ function hrjobcontract_civicrm_searchColumns( $objectName, &$headers, &$rows, &$ // replace location options values with label foreach($rows as &$row){ - $location = $row['hrjobcontract_details_location']; - if(!empty($location)){ - $row['hrjobcontract_details_location'] = $options[$location]; + if(!empty($row['hrjobcontract_details_location']) && isset($options[$row['hrjobcontract_details_location']])){ + $row['hrjobcontract_details_location'] = $options[$row['hrjobcontract_details_location']]; + } else { + $row['hrjobcontract_details_location'] = ''; } } } diff --git a/hrjobcontract/tests/phpunit/CRM/Hrjobcontract/BAO/HRJobContractTest.php b/hrjobcontract/tests/phpunit/CRM/Hrjobcontract/BAO/HRJobContractTest.php new file mode 100644 index 00000000000..605460b1655 --- /dev/null +++ b/hrjobcontract/tests/phpunit/CRM/Hrjobcontract/BAO/HRJobContractTest.php @@ -0,0 +1,67 @@ +cleanDB(); + parent::setUp(); + $jobContractUpgrader = CRM_Hrjobcontract_Upgrader::instance(); + $jobContractUpgrader->install(); + } + + function tearDown() { + parent::tearDown(); + } + + function testGetStaffCount() { + // create contacts + $contact1 = array('first_name'=>'walter', 'last_name'=>'white'); + $contactID1 = $this->individualCreate($contact1); + $contact2 = array('first_name'=>'walter1', 'last_name'=>'white1'); + $contactID2 = $this->individualCreate($contact2); + $contact3 = array('first_name'=>'walter2', 'last_name'=>'white2'); + $contactID3 = $this->individualCreate($contact3); + $contact4 = array('first_name'=>'walter3', 'last_name'=>'white3'); + $contactID4 = $this->individualCreate($contact4); + + // create contracts + $this->createJobContract($contactID1, date('Y-m-d', strtotime('-14 days'))); + $this->createJobContract($contactID2, date('Y-m-d', strtotime('-5 days'))); + $this->createJobContract($contactID3, date('Y-m-d', strtotime('+3 years'))); + + $this->assertEquals(2, CRM_Hrjobcontract_BAO_HRJobContract::getStaffCount()); + } + + /** + * Creates a new Job Contract for the given contact + * + * If a startDate is given, it will also create a JobDetails instance to save + * the contract's start date and end date(if given) + * + * @param $contactID + * @param null $startDate + * @param null $endDate + * @param array $extraParams + * + * @return \CRM_HRJob_DAO_HRJobContract|NULL + */ + private function createJobContract($contactID, $startDate = null, $endDate = null, $extraParams = array()) { + $contract = CRM_Hrjobcontract_BAO_HRJobContract::create(['contact_id' => $contactID]); + if($startDate) { + $params = [ + 'jobcontract_id' => $contract->id, + 'period_start_date' => CRM_Utils_Date::processDate($startDate), + 'period_end_date' => null, + ]; + if($endDate) { + $params['period_end_date'] = CRM_Utils_Date::processDate($endDate); + } + $params = array_merge($params, $extraParams); + CRM_Hrjobcontract_BAO_HRJobDetails::create($params); + } + return $contract; + } +} diff --git a/org.civicrm.reqangular/reqangular.php b/org.civicrm.reqangular/reqangular.php index 9820f37640b..6220927b742 100755 --- a/org.civicrm.reqangular/reqangular.php +++ b/org.civicrm.reqangular/reqangular.php @@ -111,19 +111,38 @@ function reqangular_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) { * Implementation of hook_civicrm_pageRun */ function reqangular_civicrm_pageRun($page) { + /** + * Avoids injecting the common dependencies (which include angular) in core pages + * that are already using angular themselves. A quick patch while we + * figure out a better solution to avoid having our own copy of angular in CiviHR + */ + if (!_reqangular_isAngularCorePage($page)) { $url = CRM_Extension_System::singleton()->getMapper()->keyToUrl('org.civicrm.reqangular'); CRM_Core_Resources::singleton()->addVars('reqAngular', array( - 'baseUrl' => $url, - 'angular' => "$url/src/common/vendor/angular/angular.min", - 'angularAnimate' => "$url/src/common/vendor/angular/angular-animate.min", - 'angularBootstrap' => "$url/src/common/vendor/angular/ui-bootstrap", - 'angularFileUpload' => "$url/src/common/vendor/angular/angular-file-upload", - 'angularResource' => "$url/src/common/vendor/angular/angular-resource.min", - 'angularRoute' => "$url/src/common/vendor/angular/angular-route.min", - 'requireLib' => "$url/src/common/vendor/require.min", - 'reqangular' => "$url/dist/reqangular.min", + 'baseUrl' => $url, + 'angular' => "$url/src/common/vendor/angular/angular.min", + 'angularAnimate' => "$url/src/common/vendor/angular/angular-animate.min", + 'angularBootstrap' => "$url/src/common/vendor/angular/ui-bootstrap", + 'angularFileUpload' => "$url/src/common/vendor/angular/angular-file-upload", + 'angularResource' => "$url/src/common/vendor/angular/angular-resource.min", + 'angularRoute' => "$url/src/common/vendor/angular/angular-route.min", + 'requireLib' => "$url/src/common/vendor/require.min", + 'reqangular' => "$url/dist/reqangular.min", )); CRM_Core_Resources::singleton()->addScriptFile('org.civicrm.reqangular', 'dist/reqangular.min.js', 1000); + } +} + +/** + * Checks if the given page is an angular-based page from core + * + * Note: CRM_Core_Page_Angular is CiviCrm v1.5, Civi\Angular\Page\Main is CiviCrm v4.7 + * + * @param [object] $page + * @return boolean + */ +function _reqangular_isAngularCorePage($page) { + return ($page instanceof CRM_Core_Page_Angular) || ($page instanceof Civi\Angular\Page\Main); }