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("")}})}])}(),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("")}})}])}(),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);
}