Change Contract Terms: When an employeees job or role changes, i.e. promotion, secondment or move,you can use this wizard to update the details of the contract and record a newrevision of the contract. A contract history is kept so you can always see theprevious version of the contract.
Correct an error on the contract record: If you notice an issue or error with the job terms you can correct these withoutcreating a new job history record. These changes are not stored as a new revisionof the contract.
')},c.utils={contractListLen:u.length},c.delete=j,c.modalContract=C,c.toggleIsPrimary=$,function(){for(w in R)S[w]=R[w].getFields();o.all(S).then(function(e){c.fields=e,n.debug("FIELDS:"),n.debug(e);for(w in R)D[w]=R[w].model(e[w]);return o.all(D)}).then(function(o){c.model=o,n.debug("MODEL:"),n.debug(o),u=t("orderBy")(u,"-is_primary"),e.forEach(u,function(e){+e.is_current?c.contractCurrent.push(e):c.contractPast.push(e)}),c.$watchCollection("contractCurrent",function(){c.utils.contractListLen=c.contractCurrent.length+c.contractPast.length}),c.$watchCollection("contractPast",function(){c.utils.contractListLen=c.contractCurrent.length+c.contractPast.length}),i.$broadcast("hrjc-loader-hide"),c.contractListLoaded=!0}),o.all(P).then(function(t){e.extend(c.utils,t)})}()}return n.__name="ContractListController",n.$inject=["$filter","$log","$q","$rootElement","$rootScope","$sce","$scope","$window","$uibModal","contractList","contractService","contractDetailsService","contractHourService","contractPayService","contractLeaveService","contractHealthService","contractPensionService","utilsService","settings","pubSub"],n}),define("job-contract/controllers/revision-list.controller",["common/angular","common/lodash"],function(e,t){"use strict";function n(t,n,o,r,i,a,c,s,l,u,d,f,v,m,p){function _(){var e=(a.currentPage-1)*a.itemsPerPage,t=e+a.itemsPerPage;a.revisionDataListPage=$.slice(e,t)}function h(e,t){if(1===a.revisionList.length)return void t.stopPropagation();if(e&&"number"==typeof+e){c.open({appendTo:r.find("div").eq(0),templateUrl:s.pathApp+"views/modalDialog.html",size:"sm",controller:"ModalDialogController",resolve:{content:function(){return{msg:"Are you sure you want to delete this job contract revision?"}}}}).result.then(function(t){t&&(a.$broadcast("hrjc-loader-show"),l.deleteRevision(e).then(function(t){var n=0,o=a.revisionList.length;if(!t.is_error){for(n;nt&&g(),a.sortBy(),a.createPage()})}function y(e){var t=e.effective_date,n=e.change_reason;c.open({appendTo:r.find("div").eq(0),templateUrl:s.pathApp+"views/modalChangeReason.html?v="+(new Date).getTime(),controller:"ModalChangeReasonController",resolve:{content:function(){return{copy:{title:"Edit revision data"}}},date:function(){return t},reasonId:function(){return n}}}).result.then(function(o){o.date===t&&o.reasonId===n||l.saveRevision({id:e.id,change_reason:o.reasonId,effective_date:o.date}).then(function(){e.effective_date=o.date,e.change_reason=o.reasonId,a.sortBy(),a.createPage(),a.revisionCurrent.id!==g()&&a.$emit("updateContractView")})})}function g(){var n,o=0;if(a.revisionList.length){var r=t("orderBy")(a.revisionList,["effective_date","id"]);if(e.forEach(r,function(e){new Date(e.effective_date).setHours(0,0,0,0)<=(new Date).setHours(0,0,0,0)&&(n=e)}),!n)do{n=r[o],o++}while(r[o]&&r[o-1].effective_date===r[o].effective_date);return e.extend(a.revisionCurrent,n),n.id}return null}function j(e,n){void 0!==e&&(a.sortCol===e?a.sortReverse=!a.sortReverse:a.sortCol=e),void 0!==n&&(a.sortReverse=n),$=t("orderBy")(a.revisionDataList,a.sortCol,a.sortReverse)}n.debug("Controller: RevisionListController");var C=a.contract.id,$=a.revisionDataList;a.changeReasons=i.options.contract.change_reason,a.currentPage=1,a.itemsPerPage=5,a.maxSize=5,a.sortCol="revisionEntityIdObj.effective_date",a.sortReverse=!0,a.urlCSV=function(){var t=s.pathReport+(s.pathReport.indexOf("?")>-1?"&":"?"),n=a.fields;return e.forEach(n,function(n,o){t+="fields["+o+"_revision_id]=1&",e.forEach(n,function(e){t+="fields["+o+"_"+e.name+"]=1&"})}),t+="fields[sort_name]=1&fields[first_name]=1&fields[last_name]=1&fields[external_identifier]=1&fields[email]=1&fields[street_address]=1&fields[city]=1&fields[name]=1&fields[contract_contact_id]=1&fields[contract_contract_id]=1&fields[jobcontract_revision_id]=1&fields[change_reason]=1&fields[created_date]=1&fields[effective_date]=1&fields[modified_date]=1&order_bys[1][column]=id&order_bys[1][order]=ASC&order_bys[2][column]=civicrm_hrjobcontract_revision_revision_id&order_bys[2][order]=ASC&order_bys[3][column]=-&order_bys[3][order]=ASC&order_bys[4][column]=-&order_bys[4][order]=ASC&order_bys[5][column]=-&order_bys[5][order]=ASC&contract_id_op=eq&permission=access+CiviReport&row_count=&_qf_Summary_submit_csv=Preview+CSV&groups=&contract_id_value="+C+"&group_bys[civicrm_hrjobcontract_revision_revision_id]=1"}(),a.display={effectiveDate:!0,position:!0,payScale:!0,totalSalary:!0,hours:!0,placeOfWork:!0,recordedBy:!0,changeReason:!0},a.createPage=_,a.deleteRevision=h,a.modalRevisionEdit=y,a.sortBy=j,function(){b(),a.revisionDataList||(a.$broadcast("hrjc-loader-show"),p.fetchRevisions(C).then(function(e){a.revisionList=e.revisionList,a.revisionDataList=e.revisionDataList,a.$broadcast("hrjc-loader-hide")}))}()}return n.__name="RevisionListController",n.$inject=["$filter","$log","$q","$rootElement","$rootScope","$scope","$uibModal","settings","contractService","contractDetailsService","contractHourService","contractPayService","contractFilesService","contractRevisionService","contractRevisionListService"],n}),define("job-contract/controllers/form/form-general.controller",["common/moment"],function(e){"use strict";function t(t,n,o){function r(e,t){e.preventDefault(),e.stopPropagation(),n[t]=!0}function i(t,n){if(!t||!n)return null;var o,r,i,a;return i=e(n),i.add(1,"days"),a=i.diff(t,"years"),i.add(-a,"years"),r=i.diff(t,"months"),i.add(-r,"months"),o=i.diff(t,"days"),a=a>0?a>1?a+" years ":a+" year ":"",r=r>0?r>1?r+" months ":r+" month ":"",o=o>0?o>1?o+" days":o+" day":"",a+r+o||"0 days"}function a(t,n){return n=n||"min",e(t)["max"===n?"subtract":"add"](1,"day").toDate()}function c(){n.$watch("entity.details.period_start_date",function(){n.datepickerOptions.end.minDate=a(s.period_start_date,"min"),n.duration=i(s.period_start_date,s.period_end_date)}),n.$watch("entity.details.period_end_date",function(){s.period_end_date?n.datepickerOptions.start.maxDate=a(s.period_end_date,"max"):(n.datepickerOptions.start.maxDate=null,s.end_reason=null),n.duration=i(s.period_start_date,s.period_end_date)}),n.$watch("entity.details.position",function(e,t){e!==t&&s.title===t&&(n.contractForm.detailsTitle.$setViewValue(e),n.contractForm.detailsTitle.$render())}),n.$watch("entity.details.notice_amount",function(e,t){+e&&!s.notice_unit&&(n.contractForm.detailsNoticeUnit.$setValidity("required",!1),n.contractForm.detailsNoticeUnit.$dirty=!0),e!==t&&s.notice_amount_employee===t&&(s.notice_amount_employee=e)}),n.$watch("entity.details.notice_amount_employee",function(e){+e&&!s.notice_unit_employee&&(n.contractForm.detailsNoticeUnitEmployee.$setValidity("required",!1),n.contractForm.detailsNoticeUnitEmployee.$dirty=!0)}),n.$watch("entity.details.notice_unit",function(e,t){e!==t&&s.notice_unit_employee===t&&(s.notice_unit_employee=e)})}t.debug("Controller: FormGeneralController");var s=n.entity.details;n.format=o.DATE_FORMAT,n.datepickerOptions=function(){return{start:{maxDate:s.period_end_date?a(s.period_end_date,"max"):null},end:{minDate:s.period_start_date?a(s.period_start_date,"min"):null}}}(),n.dpOpen=r,function(){c()}()}return t.__name="FormGeneralController",t.$inject=["$log","$scope","HR_settings"],t}),define("job-contract/controllers/form/form-health.controller",[],function(){"use strict";function e(e,t,n){function o(e,o){e&&n.search(e,{contact_type:"Organization",contact_sub_type:o}).then(function(e){t.contacts[o]=e})}e.debug("Controller: FormHealthController"),t.contacts={Health_Insurance_Provider:[],Life_Insurance_Provider:[]},t.refreshContacts=o,function(){t.entity.health.provider&&n.getOne(t.entity.health.provider).then(function(e){t.contacts.Health_Insurance_Provider.push(e)}),t.entity.health.provider_life_insurance&&n.getOne(t.entity.health.provider_life_insurance).then(function(e){t.contacts.Life_Insurance_Provider.push(e)})}()}return e.__name="FormHealthController",e.$inject=["$log","$scope","contactService"],e}),define("job-contract/vendor/fraction",[],function(){var e=function(t,n){if(void 0!==t&&n)"number"==typeof t&&"number"==typeof n?(this.numerator=t,this.denominator=n):"string"==typeof t&&"string"==typeof n&&(this.numerator=parseInt(t),this.denominator=parseInt(n));else if(void 0===n)if(num=t,"number"==typeof num)this.numerator=num,this.denominator=1;else if("string"==typeof num){var o,r,i=num.split(" ");if(i[0]&&(o=i[0]),i[1]&&(r=i[1]),o%1==0&&r&&r.match("/"))return new e(o).add(new e(r));if(!o||r)return;if("string"==typeof o&&o.match("/")){var a=o.split("/");this.numerator=a[0],this.denominator=a[1]}else{if("string"==typeof o&&o.match("."))return new e(parseFloat(o));this.numerator=parseInt(o),this.denominator=1}}this.normalize()};return e.prototype.clone=function(){return new e(this.numerator,this.denominator)},e.prototype.toString=function(){if("NaN"===this.denominator)return"NaN";var e=this.numerator/this.denominator>0?Math.floor(this.numerator/this.denominator):Math.ceil(this.numerator/this.denominator),t=this.numerator%this.denominator,n=this.denominator,o=[];return 0!=e&&o.push(e),0!=t&&o.push((0===e?t:Math.abs(t))+"/"+n),o.length>0?o.join(" "):0},e.prototype.rescale=function(e){return this.numerator*=e,this.denominator*=e,this},e.prototype.add=function(t){var n=this.clone();return t=t instanceof e?t.clone():new e(t),td=n.denominator,n.rescale(t.denominator),t.rescale(td),n.numerator+=t.numerator,n.normalize()},e.prototype.subtract=function(t){var n=this.clone();return t=t instanceof e?t.clone():new e(t),td=n.denominator,n.rescale(t.denominator),t.rescale(td),n.numerator-=t.numerator,n.normalize()},e.prototype.multiply=function(t){var n=this.clone();if(t instanceof e)n.numerator*=t.numerator,n.denominator*=t.denominator;else{if("number"!=typeof t)return n.multiply(new e(t));n.numerator*=t}return n.normalize()},e.prototype.divide=function(t){var n=this.clone();if(t instanceof e)n.numerator*=t.denominator,n.denominator*=t.numerator;else{if("number"!=typeof t)return n.divide(new e(t));n.denominator*=t}return n.normalize()},e.prototype.equals=function(t){t instanceof e||(t=new e(t));var n=this.clone().normalize(),t=t.clone().normalize();return n.numerator===t.numerator&&n.denominator===t.denominator},e.prototype.normalize=function(){var t=function(e){return"number"==typeof e&&(e>0&&e%1>0&&e%1<1||e<0&&e%-1<0&&e%-1>-1)},n=function(e,t){if(t){var n=Math.pow(10,t);return Math.round(e*n)/n}return Math.round(e)};return function(){if(t(this.denominator)){var o=n(this.denominator,9),r=Math.pow(10,o.toString().split(".")[1].length);this.denominator=Math.round(this.denominator*r),this.numerator*=r}if(t(this.numerator)){var o=n(this.numerator,9),r=Math.pow(10,o.toString().split(".")[1].length);this.numerator=Math.round(this.numerator*r),this.denominator*=r}var i=e.gcf(this.numerator,this.denominator);return this.numerator/=i,this.denominator/=i,(this.numerator<0&&this.denominator<0||this.numerator>0&&this.denominator<0)&&(this.numerator*=-1,this.denominator*=-1),this}}(),e.gcf=function(t,n){var o=[],r=e.primeFactors(t),i=e.primeFactors(n);return r.forEach(function(e){var t=i.indexOf(e);t>=0&&(o.push(e),i.splice(t,1))}),0===o.length?1:function(){var e,t=o[0];for(e=1;eTHE FILE IS TOO LARGE AND CANNOT BE UPLOADED. PLEASE REDUCE THE SIZE OF THE FILE AND TRY AGAIN."),fte:c.trustAsHtml("
FTE stands forFull Time Equivalent. This is a useful measure foran organisation that has peopleworking part-time.For a full-time person, FTE is always equal to1.0, whereas for a part-time person, the FTE will representthe fraction of standard hours that the person works on aregular basis. E.g. if the standard working day at an organisationcomprises of 8 hours, then a person who regularly works for8 hours each day would be considered to be full- time andwould have an FTE value of 1.0. A person who regularly worksfor only 4 hours each day would be considered to be apart-time person and would have an FTE value of 0.5. If theorganisation had 10 people, each with an FTE of 1.0 theactual headcount of full-time people would be 10 and theFTE headcount (equal to actual headcount multiplied by theFTE value) would also be 10. However, if the organisationhad another 10 people who each worked part-time with an FTEvalue of 0.5 the actual headcount of part-time people wouldbe 10 while the FTE headcount would only be 5. Thus for anorganisation that had a total of 10 full-time people, and 10part-time people (each with an FTE of 0.5) the actualheadcount for the organisation would be 20 while the FTEheadcount would be 15.
")},s.uploader={details:{contract_file:y.uploader("civicrm_hrjobcontract_details")},pension:{evidence_file:y.uploader("civicrm_hrjobcontract_pension",1)}},s.cancel=S,s.filesValidate=D,s.save=M,function(){e.copy(g,s.entity),s.entity.contract={is_primary:0},e.forEach(s.uploader,function(t){e.forEach(t,function(e){e.onAfterAddingAll=function(){s.filesValidate()}})}),A(),a.$broadcast("hrjc-loader-show"),P().then(function(){a.$broadcast("hrjc-loader-hide")})}()}return o.__name="ModalContractNewController",o.$inject=["$log","$q","$rootElement","$rootScope","$sce","$scope","$uibModalInstance","$uibModal","Contract","contractService","contractDetailsService","contractHourService","contractPayService","contractLeaveService","contractHealthService","contractPensionService","contractFilesService","model","utilsService","utils","settings","pubSub"],o}),define("job-contract/controllers/modal/modal-contract.controller",["common/angular","common/lodash","common/moment"],function(e,t,n){"use strict";function o(o,r,i,a,c,s,l,u,d,f,v,m,p,_,h,b,y,g,j,C,$,w,S,D,R){function P(){if("view"===y||e.equals(g,o.entity)&&e.equals(C,o.files)&&!o.uploader.details.contract_file.queue.length&&!o.uploader.pension.evidence_file.queue.length)return o.$broadcast("hrjc-loader-hide"),void i.dismiss("cancel");S.debug&&e.forEach(g,function(t,n){e.equals(t,o.entity[n])||(D.debug("======================"),D.debug("Changed entity: "+n),D.debug("Before:"),D.debug(t),D.debug("After:"),D.debug(o.entity[n]))}),r.open({appendTo:c.find("div").eq(0),templateUrl:S.pathApp+"views/modalDialog.html?v="+(new Date).getTime(),size:"sm",controller:"ModalDialogController",resolve:{content:function(){return{copyCancel:"No",title:"Alert",msg:"Are you sure you want to cancel? Changes will be lost!"}}}}).result.then(function(e){e&&(o.$broadcast("hrjc-loader-hide"),i.dismiss("cancel"))})}function M(){return r.open({appendTo:c.find("div").eq(0),templateUrl:S.pathApp+"views/modalChangeReason.html?v="+(new Date).getTime(),controller:"ModalChangeReasonController",resolve:{content:function(){return{copy:{title:Y.title}}},date:null,reasonId:null}}).result}function A(){var t=x("period_end_date"),n=x("period_start_date"),r=!e.equals(o.entity.leave,g.leave);return n||t||r}function L(){return r.open({appendTo:c.find("div").eq(0),templateUrl:S.pathApp+"views/modalConfirmEdit.html?v="+(new Date).getTime(),controller:"ModalDialogController",resolve:{content:function(){return{msg:"Save without making a new revision?"}}}}).result}function E(e,t){return o.$broadcast("hrjc-loader-show"),d.validateEffectiveDate({contact_id:S.contactId,effective_date:t}).then(function(n){if(n.success)return N(e,t);CRM.alert(n.message,"Error","error"),o.$broadcast("hrjc-loader-hide")},function(e){})}function T(){o.$broadcast("hrjc-loader-show"),o.entity.details.period_end_date=o.entity.details.period_end_date||"";var t,n,s,l,d,y=e.copy(o.entity),g=o.filesTrash,j=o.uploader,C={contract:u.save(y.contract),details:f.save(y.details),hour:v.save(y.hour),pay:m.save(y.pay),leave:p.save(y.leave),health:_.save(y.health),pension:h.save(y.pension)},$=[],w=[];for(t in g)for(s=0,l=g[t].length,s;so.fileMaxSize&&e.remove()})})}),j.details.contract_file.queue.length&&$.push(b.upload(j.details.contract_file,y.details.jobcontract_revision_id)),j.pension.evidence_file.queue.length&&$.push(b.upload(j.pension.evidence_file,y.pension.jobcontract_revision_id)),t.details.period_start_date=y.details.period_start_date,t.details.period_end_date=y.details.period_end_date,t.pay.annual_benefits=y.pay.annual_benefits,t.pay.annual_deductions=y.pay.annual_deductions,$.length?(d=r.open({appendTo:c.find("div").eq(0),templateUrl:S.pathApp+"views/modalProgress.html?v="+(new Date).getTime(),size:"sm",controller:"ModalProgressController",resolve:{uploader:function(){return j},promiseFilesUpload:function(){return $}}}),t.files=d.result,a.all(t)):(t.haveEntitlementFieldsChanged=A(),t)}).then(function(e){o.$broadcast("hrjc-loader-hide"),i.close(e),R.publish("Contract::updated")},function(e){o.$broadcast("hrjc-loader-hide"),CRM.alert(e,"Error","error")})}function F(e){var t=l("formatDate")(e,Date);return"Unspecified"!==t?t:e}function I(){return a.all([{name:"hrjobcontract_health_health_plan_type",key:"plan_type"},{name:"hrjobcontract_health_life_insurance_plan_type",key:"plan_type_life_insurance"}].map(function(e){_.getOptions(e.name,!0).then(function(n){s.options.health[e.key]=t.transform(n,function(e,t){e[t.key]=t.value},{})})}))}function U(e,t){var n=o.files[t];o.filesTrash[t].push(n[e]),n.splice(e,1)}function q(){var e=t.every(o.uploader,function(e){return t.every(e,function(e){return!e.queue||t.every(e.queue,function(e){return e.file.sizeo.fileMaxSize&&e.remove()}),F.queue.length&&P.push(b.upload(F,l))}),P.length?(s=r.open({appendTo:c.find("div").eq(0),templateUrl:S.pathApp+"views/modalProgress.html",size:"sm",controller:"ModalProgressController",resolve:{uploader:function(){return C},promiseFilesUpload:function(){return P}}}),e.files=s.result,a.all(e)):(e.haveEntitlementFieldsChanged=A(),e)}).then(function(e){o.$broadcast("hrjc-loader-hide"),i.close(e),R.publish("Contract::updated")})):(o.$broadcast("hrjc-loader-hide"),i.close())}D.debug("Controller: ModalContractController");var Y=j.copy||{};Y.close=Y.close||"Close",Y.save=Y.save||"Save changes",Y.title=Y.title||"Contract",o.action=y||"view",o.allowSave=void 0!==j.allowSave&&j.allowSave,o.copy=Y,o.entity={},o.fileMaxSize=S.CRM.maxFileSize||0,o.files={},o.filesTrash={},o.isDisabled=void 0===j.isDisabled||j.isDisabled,o.isPrimaryDisabled=+g.contract.is_primary,o.showIsPrimary=w.contractListLen>1&&"change"!==y,o.uploader={details:{contract_file:b.uploader("civicrm_hrjobcontract_details")},pension:{evidence_file:b.uploader("civicrm_hrjobcontract_pension",1)}},o.utils=w,o.cancel=P,o.fileMoveToTrash=U,o.filesValidate=q,o.save=z,function(){O(),H(),i.opened.then(function(){s.$broadcast("hrjc-loader-hide")}),s.$broadcast("hrjc-loader-show"),I().then(function(){s.$broadcast("hrjc-loader-hide")})}()}return o.__name="ModalContractController",o.$inject=["$scope","$uibModal","$uibModalInstance","$q","$rootElement","$rootScope","$filter","contractService","contractRevisionService","contractDetailsService","contractHourService","contractPayService","contractLeaveService","contractHealthService","contractPensionService","contractFilesService","action","entity","content","files","utilsService","utils","settings","$log","pubSub"],o}),define("job-contract/controllers/modal/modal-dialog.controller",[],function(){"use strict";function e(e,t,n,o,r){function i(){o.dismiss("Cancel")}function a(e){o.close(e||!0)}e.debug("Controller: ModalDialogController"),t.copyCancel=r.copyCancel||"Cancel",t.copyConfirm=r.copyConfirm||"Yes",t.msg=r.msg||"",t.title=r.title||"CiviHR Job Contract",t.cancel=i,t.confirm=a}return e.__name="ModalDialogController",e.$inject=["$log","$scope","$timeout","$uibModalInstance","content"],e}),define("job-contract/controllers/modal/modal-progress.controller",[],function(){"use strict";function e(e,t,n,o,r,i,a){function c(){r.dismiss("File upload canceled")}e.debug("Controller: ModalProgressController");var s,l;n.uploader=i,n.cancel=c,function(){for(s in i)for(l in i[s])i[s][l].queue.length&&(i[s][l].item=i[s][l].queue[0].file.name),i[s][l].onProgressItem=function(e){this.item=e.file.name};t.all(a).then(function(e){o(function(){r.close(e)},500)})}()}return e.__name="ModalProgressController",e.$inject=["$log","$q","$scope","$timeout","$uibModalInstance","uploader","promiseFilesUpload"],e}),define("job-contract/controllers/modal/modal-revision.controller",["common/angular"],function(e){"use strict";function t(t,n,o,r,i,a,c,s,l,u,d,f,v,m,p){function _(){a.dismiss("cancel")}function h(){var e=(i.currentPage-1)*i.itemsPerPage,t=e+i.itemsPerPage;i.revisionDataListPage=i.revisionDataList.slice(e,t)}function b(){var e,t=0,n=i.fields.length;for(t;t-1?"&":"?"),r=i.entity;return e.forEach(i.fields,function(e){t="editor_name"!==e.name?e.name:"editor_uid",n=e.extends?"":r+"_",e.selected&&(o+="fields["+n+t+"]=1&")}),o+="fields[sort_name]=1&fields[first_name]=1&fields[last_name]=1&fields[external_identifier]=1&fields[email]=1&fields[street_address]=1&fields[city]=1&fields[name]=1&fields[contract_contact_id]=1&fields[contract_contract_id]=1&fields[jobcontract_revision_id]=1&fields[change_reason]=1&fields[created_date]=1&fields[effective_date]=1&fields[modified_date]=1&order_bys[1][column]=id&order_bys[1][order]=ASC&order_bys[2][column]=civicrm_hrjobcontract_revision_revision_id&order_bys[2][order]=ASC&order_bys[3][column]=-&order_bys[3][order]=ASC&order_bys[4][column]=-&order_bys[4][order]=ASC&order_bys[5][column]=-&order_bys[5][order]=ASC&contract_id_op=eq&permission=access+CiviReport&row_count=&_qf_Summary_submit_csv=Preview+CSV&groups=&contract_id_value="+l[0].jobcontract_id+"&group_bys[civicrm_hrjobcontract_revision_revision_id]=1"}n.debug("Controller: ModalRevisionController"),i.$broadcast("hrjc-loader-show"),i.currentPage=1,i.entity=u,i.fields=e.copy(d),i.itemsPerPage=5,i.revisionDataList=[],i.revisionList=[],i.sortCol="effective_date",i.subFields={},i.maxSize=5,i.modalContract=v,i.sortReverse=!0,i.urlCSV=w(),i.cancel=_,i.createPage=h,i.sortBy=C,i.toggleFieldsSelected=$,function(){b(),y(),g(),j(),i.sortBy(),a.opened.then(function(){r.$broadcast("hrjc-loader-hide")})}()}return t.__name="ModalRevisionController",t.$inject=["$filter","$log","$q","$rootScope","$scope","$uibModalInstance","settings","revisionDataList","revisionList","entity","fields","model","modalContract","utils","contactService"],t}),define("job-contract/modules/job-contract.controllers",["common/angular","job-contract/controllers/contract.controller","job-contract/controllers/contract-list.controller","job-contract/controllers/revision-list.controller","job-contract/controllers/form/form-general.controller","job-contract/controllers/form/form-health.controller","job-contract/controllers/form/form-hour.controller","job-contract/controllers/form/form-leave.controller","job-contract/controllers/form/form-pay.controller","job-contract/controllers/form/form-pension.controller","job-contract/controllers/modal/modal-change-reason.controller","job-contract/controllers/modal/modal-contract-new.controller","job-contract/controllers/modal/modal-contract.controller","job-contract/controllers/modal/modal-dialog.controller","job-contract/controllers/modal/modal-progress.controller","job-contract/controllers/modal/modal-revision.controller"],function(e,t,n,o,r,i,a,c,s,l,u,d,f,v,m,p){"use strict";return e.module("job-contract.controllers",[]).controller(t.__name,t).controller(n.__name,n).controller(o.__name,o).controller(r.__name,r).controller(i.__name,i).controller(a.__name,a).controller(c.__name,c).controller(s.__name,s).controller(l.__name,l).controller(u.__name,u).controller(d.__name,d).controller(f.__name,f).controller(v.__name,v).controller(m.__name,m).controller(p.__name,p)}),function(e){define("leave-absences/shared/modules/shared-settings",["common/angular"],function(t){return t.module("leave-absences.settings",[]).constant("shared-settings",{attachmentToken:e.vars.leaveAndAbsences.attachmentToken,debug:e.debug,managerPathTpl:e.vars.leaveAndAbsences.baseURL+"/views/manager-leave/",sharedPathTpl:e.vars.leaveAndAbsences.baseURL+"/views/shared/",serverDateFormat:"YYYY-MM-DD",serverDateTimeFormat:"YYYY-MM-DD HH:mm:ss",permissions:{admin:{access:"access leave and absences",administer:"administer leave and absences"},ssp:{access:"access leave and absences in ssp",manage:"manage leave and absences in ssp"}},fileUploader:{queueLimit:10},statusNames:{approved:"approved",adminApproved:"admin_approved",awaitingApproval:"awaiting_approval",moreInformationRequired:"more_information_required",rejected:"rejected",cancelled:"cancelled"}})})}(CRM),define("leave-absences/shared/modules/apis",["common/angular","common/modules/apis","leave-absences/shared/modules/shared-settings"],function(e){"use strict";return e.module("leave-absences.apis",["common.apis","leave-absences.settings"])}),define("leave-absences/shared/modules/models-instances",["common/angular","common/models/instances/instance","common/modules/services","common/modules/models","common/services/check-permissions","leave-absences/shared/modules/shared-settings"],function(e){"use strict";return e.module("leave-absences.models.instances",["common.models","common.models.instances","common.services","leave-absences.settings"])}),define("leave-absences/shared/modules/models",["common/angular","common/modules/models","common/modules/services","leave-absences/shared/modules/apis","leave-absences/shared/modules/models-instances","leave-absences/shared/modules/shared-settings"],function(e){"use strict";return e.module("leave-absences.models",["common.models","common.services","leave-absences.apis","leave-absences.models.instances","leave-absences.settings"])}),define("leave-absences/shared/apis/absence-type.api",["common/lodash","common/moment","leave-absences/shared/modules/apis","common/services/api"],function(e,t,n){"use strict";n.factory("AbsenceTypeAPI",["$log","api","shared-settings",function(n,o,r){return n.debug("AbsenceTypeAPI"),o.extend({all:function(t){return n.debug("AbsenceTypeAPI.all"),this.sendGET("AbsenceType","get",e.defaultsDeep(t||{},{is_active:!0,options:{sort:"weight ASC"}})).then(function(e){return e.values})},calculateToilExpiryDate:function(o,i,a){return n.debug("AbsenceTypeAPI.calculateToilExpiryDate"),a=e.assign({},a,{absence_type_id:o,date:t(i).format(r.serverDateFormat)}),this.sendPOST("AbsenceType","calculateToilExpiryDate",a).then(function(e){return e.values.expiry_date})}})}])}),define("leave-absences/shared/instances/absence-type.instance",["leave-absences/shared/modules/models-instances","common/models/instances/instance"],function(e){"use strict";e.factory("AbsenceTypeInstance",["$log","ModelInstance",function(e,t){return e.debug("AbsenceTypeInstance"),t.extend({})}])}),define("leave-absences/shared/models/absence-type.model",["common/lodash","leave-absences/shared/modules/models","common/models/model","common/models/option-group","leave-absences/shared/apis/absence-type.api","leave-absences/shared/instances/absence-type.instance"],function(e,t){"use strict";t.factory("AbsenceType",["$log","$q","Model","OptionGroup","AbsenceTypeAPI","AbsenceTypeInstance",function(t,n,o,r,i,a){return t.debug("AbsenceType"),o.extend({all:function(e){return i.all(e).then(function(e){return e.map(function(e){return a.init(e,!0)})})},calculateToilExpiryDate:function(e,t,n){return i.calculateToilExpiryDate(e,t,n)},canExpire:function(e){return i.all({accrual_expiration_unit:{"IS NOT NULL":1},accrual_expiration_duration:{"IS NOT NULL":1},allow_accruals_request:1,id:e,options:{limit:1},return:["id"]}).then(function(e){return e.length>0})},loadCalculationUnits:function(t){return r.valuesOf("hrleaveandabsences_absence_type_calculation_unit").then(function(n){return n=e.indexBy(n,"value"),e.map(t,function(t){return e.assign(t,{calculation_unit_label:n[t.calculation_unit].label,calculation_unit_name:n[t.calculation_unit].name})})})}})}])}),define("leave-absences/shared/instances/absence-period.instance",["leave-absences/shared/modules/models-instances","common/moment","common/models/instances/instance","common/services/hr-settings"],function(e,t){"use strict";e.factory("AbsencePeriodInstance",["$log","ModelInstance","HR_settings",function(e,n,o){return e.debug("AbsencePeriodInstance"),n.extend({defaultCustomData:function(){return{current:!1}},transformAttributes:function(e){var n=t();return e.current=!1,t(e.start_date).isSameOrBefore(n,"day")&&t(e.end_date).isSameOrAfter(n,"day")&&(e.current=!0),e},isInPeriod:function(e){var n=o.DATE_FORMAT.toUpperCase(),r=t(e,n);return t(this.start_date).isSameOrBefore(r)&&t(this.end_date).isSameOrAfter(r)}})}])}),define("leave-absences/shared/apis/absence-period.api",["leave-absences/shared/modules/apis","common/services/api"],function(e){"use strict";e.factory("AbsencePeriodAPI",["$log","api",function(e,t){return e.debug("AbsencePeriodAPI"),t.extend({all:function(t){return e.debug("AbsencePeriodAPI"),this.sendGET("AbsencePeriod","get",t).then(function(e){return e.values})}})}])}),define("leave-absences/shared/models/absence-period.model",["leave-absences/shared/modules/models","common/moment","leave-absences/shared/modules/shared-settings","leave-absences/shared/instances/absence-period.instance","leave-absences/shared/apis/absence-period.api","common/models/model","common/services/hr-settings"],function(e,t){"use strict";e.factory("AbsencePeriod",["$log","Model","AbsencePeriodAPI","AbsencePeriodInstance","shared-settings",function(e,n,o,r,i){return e.debug("AbsencePeriod"),n.extend({all:function(e){return o.all(e).then(function(e){return e.map(function(e){return r.init(e,!0)})})},current:function(){var e=t().format(i.serverDateFormat),n={start_date:{"<=":e},end_date:{">=":e}};return o.all(n).then(function(e){return e&&e.length?r.init(e[0],!0):null})}})}])}),function(e,t){function n(){var e={nodiff:"",year:"year",years:"years",month:"month",months:"months",day:"day",days:"days",hour:"hour",hours:"hours",minute:"minute",minutes:"minutes",second:"second",seconds:"seconds",delimiter:" "};moment.fn.preciseDiff=function(e){return moment.preciseDiff(this,e)},moment.preciseDiff=function(t,n){function o(t,n){return t+" "+e[n+(1===t?"":"s")]}var r=moment(t),i=moment(n);if(r.isSame(i))return e.nodiff;if(r.isAfter(i)){var a=r;r=i,i=a}var c=i.year()-r.year(),s=i.month()-r.month(),l=i.date()-r.date();if(l<0){var u=moment(i.year()+"-"+(i.month()+1),"YYYY-MM").subtract("months",1).daysInMonth();l=u
Change Contract Terms: When an employeees job or role changes, i.e. promotion, secondment or move,you can use this wizard to update the details of the contract and record a newrevision of the contract. A contract history is kept so you can always see theprevious version of the contract.
Correct an error on the contract record: If you notice an issue or error with the job terms you can correct these withoutcreating a new job history record. These changes are not stored as a new revisionof the contract.
')},c.utils={contractListLen:u.length},c.delete=j,c.modalContract=C,c.toggleIsPrimary=$,function(){for(S in R)w[S]=R[S].getFields();o.all(w).then(function(e){c.fields=e,n.debug("FIELDS:"),n.debug(e);for(S in R)D[S]=R[S].model(e[S]);return o.all(D)}).then(function(o){c.model=o,n.debug("MODEL:"),n.debug(o),u=t("orderBy")(u,"-is_primary"),e.forEach(u,function(e){+e.is_current?c.contractCurrent.push(e):c.contractPast.push(e)}),c.$watchCollection("contractCurrent",function(){c.utils.contractListLen=c.contractCurrent.length+c.contractPast.length}),c.$watchCollection("contractPast",function(){c.utils.contractListLen=c.contractCurrent.length+c.contractPast.length}),i.$broadcast("hrjc-loader-hide"),c.contractListLoaded=!0}),o.all(P).then(function(t){e.extend(c.utils,t)})}()}return n.__name="ContractListController",n.$inject=["$filter","$log","$q","$rootElement","$rootScope","$sce","$scope","$window","$uibModal","contractList","contractService","contractDetailsService","contractHourService","contractPayService","contractLeaveService","contractHealthService","contractPensionService","utilsService","settings","pubSub"],n}),define("job-contract/controllers/revision-list.controller",["common/angular","common/lodash"],function(e,t){"use strict";function n(t,n,o,r,i,a,c,s,l,u,d,f,p,m,v){function _(){var e=(a.currentPage-1)*a.itemsPerPage,t=e+a.itemsPerPage;a.revisionDataListPage=$.slice(e,t)}function h(e,t){if(1===a.revisionList.length)return void t.stopPropagation();if(e&&"number"==typeof+e){c.open({appendTo:r.find("div").eq(0),templateUrl:s.pathApp+"views/modalDialog.html",size:"sm",controller:"ModalDialogController",resolve:{content:function(){return{msg:"Are you sure you want to delete this job contract revision?"}}}}).result.then(function(t){t&&(a.$broadcast("hrjc-loader-show"),l.deleteRevision(e).then(function(t){var n=0,o=a.revisionList.length;if(!t.is_error){for(n;nt&&g(),a.sortBy(),a.createPage()})}function y(e){var t=e.effective_date,n=e.change_reason;c.open({appendTo:r.find("div").eq(0),templateUrl:s.pathApp+"views/modalChangeReason.html?v="+(new Date).getTime(),controller:"ModalChangeReasonController",resolve:{content:function(){return{copy:{title:"Edit revision data"}}},date:function(){return t},reasonId:function(){return n}}}).result.then(function(o){o.date===t&&o.reasonId===n||l.saveRevision({id:e.id,change_reason:o.reasonId,effective_date:o.date}).then(function(){e.effective_date=o.date,e.change_reason=o.reasonId,a.sortBy(),a.createPage(),a.revisionCurrent.id!==g()&&a.$emit("updateContractView")})})}function g(){var n,o=0;if(a.revisionList.length){var r=t("orderBy")(a.revisionList,["effective_date","id"]);if(e.forEach(r,function(e){new Date(e.effective_date).setHours(0,0,0,0)<=(new Date).setHours(0,0,0,0)&&(n=e)}),!n)do{n=r[o],o++}while(r[o]&&r[o-1].effective_date===r[o].effective_date);return e.extend(a.revisionCurrent,n),n.id}return null}function j(e,n){void 0!==e&&(a.sortCol===e?a.sortReverse=!a.sortReverse:a.sortCol=e),void 0!==n&&(a.sortReverse=n),$=t("orderBy")(a.revisionDataList,a.sortCol,a.sortReverse)}n.debug("Controller: RevisionListController");var C=a.contract.id,$=a.revisionDataList;a.changeReasons=i.options.contract.change_reason,a.currentPage=1,a.itemsPerPage=5,a.maxSize=5,a.sortCol="revisionEntityIdObj.effective_date",a.sortReverse=!0,a.urlCSV=function(){var t=s.pathReport+(s.pathReport.indexOf("?")>-1?"&":"?"),n=a.fields;return e.forEach(n,function(n,o){t+="fields["+o+"_revision_id]=1&",e.forEach(n,function(e){t+="fields["+o+"_"+e.name+"]=1&"})}),t+="fields[sort_name]=1&fields[first_name]=1&fields[last_name]=1&fields[external_identifier]=1&fields[email]=1&fields[street_address]=1&fields[city]=1&fields[name]=1&fields[contract_contact_id]=1&fields[contract_contract_id]=1&fields[jobcontract_revision_id]=1&fields[change_reason]=1&fields[created_date]=1&fields[effective_date]=1&fields[modified_date]=1&order_bys[1][column]=id&order_bys[1][order]=ASC&order_bys[2][column]=civicrm_hrjobcontract_revision_revision_id&order_bys[2][order]=ASC&order_bys[3][column]=-&order_bys[3][order]=ASC&order_bys[4][column]=-&order_bys[4][order]=ASC&order_bys[5][column]=-&order_bys[5][order]=ASC&contract_id_op=eq&permission=access+CiviReport&row_count=&_qf_Summary_submit_csv=Preview+CSV&groups=&contract_id_value="+C+"&group_bys[civicrm_hrjobcontract_revision_revision_id]=1"}(),a.display={effectiveDate:!0,position:!0,payScale:!0,totalSalary:!0,hours:!0,placeOfWork:!0,recordedBy:!0,changeReason:!0},a.createPage=_,a.deleteRevision=h,a.modalRevisionEdit=y,a.sortBy=j,function(){b(),a.revisionDataList||(a.$broadcast("hrjc-loader-show"),v.fetchRevisions(C).then(function(e){a.revisionList=e.revisionList,a.revisionDataList=e.revisionDataList,a.$broadcast("hrjc-loader-hide")}))}()}return n.__name="RevisionListController",n.$inject=["$filter","$log","$q","$rootElement","$rootScope","$scope","$uibModal","settings","contractService","contractDetailsService","contractHourService","contractPayService","contractFilesService","contractRevisionService","contractRevisionListService"],n}),define("job-contract/controllers/form/form-general.controller",["common/moment"],function(e){"use strict";function t(t,n,o){function r(e,t){e.preventDefault(),e.stopPropagation(),n[t]=!0}function i(t,n){if(!t||!n)return null;var o,r,i,a;return i=e(n),i.add(1,"days"),a=i.diff(t,"years"),i.add(-a,"years"),r=i.diff(t,"months"),i.add(-r,"months"),o=i.diff(t,"days"),a=a>0?a>1?a+" years ":a+" year ":"",r=r>0?r>1?r+" months ":r+" month ":"",o=o>0?o>1?o+" days":o+" day":"",a+r+o||"0 days"}function a(t,n){return n=n||"min",e(t)["max"===n?"subtract":"add"](1,"day").toDate()}function c(){n.$watch("entity.details.period_start_date",function(){n.datepickerOptions.end.minDate=a(s.period_start_date,"min"),n.duration=i(s.period_start_date,s.period_end_date)}),n.$watch("entity.details.period_end_date",function(){s.period_end_date?n.datepickerOptions.start.maxDate=a(s.period_end_date,"max"):(n.datepickerOptions.start.maxDate=null,s.end_reason=null),n.duration=i(s.period_start_date,s.period_end_date)}),n.$watch("entity.details.position",function(e,t){e!==t&&s.title===t&&(n.contractForm.detailsTitle.$setViewValue(e),n.contractForm.detailsTitle.$render())}),n.$watch("entity.details.notice_amount",function(e,t){+e&&!s.notice_unit&&(n.contractForm.detailsNoticeUnit.$setValidity("required",!1),n.contractForm.detailsNoticeUnit.$dirty=!0),e!==t&&s.notice_amount_employee===t&&(s.notice_amount_employee=e)}),n.$watch("entity.details.notice_amount_employee",function(e){+e&&!s.notice_unit_employee&&(n.contractForm.detailsNoticeUnitEmployee.$setValidity("required",!1),n.contractForm.detailsNoticeUnitEmployee.$dirty=!0)}),n.$watch("entity.details.notice_unit",function(e,t){e!==t&&s.notice_unit_employee===t&&(s.notice_unit_employee=e)})}t.debug("Controller: FormGeneralController");var s=n.entity.details;n.format=o.DATE_FORMAT,n.datepickerOptions=function(){return{start:{maxDate:s.period_end_date?a(s.period_end_date,"max"):null},end:{minDate:s.period_start_date?a(s.period_start_date,"min"):null}}}(),n.dpOpen=r,function(){c()}()}return t.__name="FormGeneralController",t.$inject=["$log","$scope","HR_settings"],t}),define("job-contract/controllers/form/form-health.controller",[],function(){"use strict";function e(e,t,n){function o(e,o){e&&n.search(e,{contact_type:"Organization",contact_sub_type:o}).then(function(e){t.contacts[o]=e})}e.debug("Controller: FormHealthController"),t.contacts={Health_Insurance_Provider:[],Life_Insurance_Provider:[]},t.refreshContacts=o,function(){t.entity.health.provider&&n.getOne(t.entity.health.provider).then(function(e){t.contacts.Health_Insurance_Provider.push(e)}),t.entity.health.provider_life_insurance&&n.getOne(t.entity.health.provider_life_insurance).then(function(e){t.contacts.Life_Insurance_Provider.push(e)})}()}return e.__name="FormHealthController",e.$inject=["$log","$scope","contactService"],e}),define("job-contract/vendor/fraction",[],function(){var e=function(t,n){if(void 0!==t&&n)"number"==typeof t&&"number"==typeof n?(this.numerator=t,this.denominator=n):"string"==typeof t&&"string"==typeof n&&(this.numerator=parseInt(t),this.denominator=parseInt(n));else if(void 0===n)if(num=t,"number"==typeof num)this.numerator=num,this.denominator=1;else if("string"==typeof num){var o,r,i=num.split(" ");if(i[0]&&(o=i[0]),i[1]&&(r=i[1]),o%1==0&&r&&r.match("/"))return new e(o).add(new e(r));if(!o||r)return;if("string"==typeof o&&o.match("/")){var a=o.split("/");this.numerator=a[0],this.denominator=a[1]}else{if("string"==typeof o&&o.match("."))return new e(parseFloat(o));this.numerator=parseInt(o),this.denominator=1}}this.normalize()};return e.prototype.clone=function(){return new e(this.numerator,this.denominator)},e.prototype.toString=function(){if("NaN"===this.denominator)return"NaN";var e=this.numerator/this.denominator>0?Math.floor(this.numerator/this.denominator):Math.ceil(this.numerator/this.denominator),t=this.numerator%this.denominator,n=this.denominator,o=[];return 0!=e&&o.push(e),0!=t&&o.push((0===e?t:Math.abs(t))+"/"+n),o.length>0?o.join(" "):0},e.prototype.rescale=function(e){return this.numerator*=e,this.denominator*=e,this},e.prototype.add=function(t){var n=this.clone();return t=t instanceof e?t.clone():new e(t),td=n.denominator,n.rescale(t.denominator),t.rescale(td),n.numerator+=t.numerator,n.normalize()},e.prototype.subtract=function(t){var n=this.clone();return t=t instanceof e?t.clone():new e(t),td=n.denominator,n.rescale(t.denominator),t.rescale(td),n.numerator-=t.numerator,n.normalize()},e.prototype.multiply=function(t){var n=this.clone();if(t instanceof e)n.numerator*=t.numerator,n.denominator*=t.denominator;else{if("number"!=typeof t)return n.multiply(new e(t));n.numerator*=t}return n.normalize()},e.prototype.divide=function(t){var n=this.clone();if(t instanceof e)n.numerator*=t.denominator,n.denominator*=t.numerator;else{if("number"!=typeof t)return n.divide(new e(t));n.denominator*=t}return n.normalize()},e.prototype.equals=function(t){t instanceof e||(t=new e(t));var n=this.clone().normalize(),t=t.clone().normalize();return n.numerator===t.numerator&&n.denominator===t.denominator},e.prototype.normalize=function(){var t=function(e){return"number"==typeof e&&(e>0&&e%1>0&&e%1<1||e<0&&e%-1<0&&e%-1>-1)},n=function(e,t){if(t){var n=Math.pow(10,t);return Math.round(e*n)/n}return Math.round(e)};return function(){if(t(this.denominator)){var o=n(this.denominator,9),r=Math.pow(10,o.toString().split(".")[1].length);this.denominator=Math.round(this.denominator*r),this.numerator*=r}if(t(this.numerator)){var o=n(this.numerator,9),r=Math.pow(10,o.toString().split(".")[1].length);this.numerator=Math.round(this.numerator*r),this.denominator*=r}var i=e.gcf(this.numerator,this.denominator);return this.numerator/=i,this.denominator/=i,(this.numerator<0&&this.denominator<0||this.numerator>0&&this.denominator<0)&&(this.numerator*=-1,this.denominator*=-1),this}}(),e.gcf=function(t,n){var o=[],r=e.primeFactors(t),i=e.primeFactors(n);return r.forEach(function(e){var t=i.indexOf(e);t>=0&&(o.push(e),i.splice(t,1))}),0===o.length?1:function(){var e,t=o[0];for(e=1;eTHE FILE IS TOO LARGE AND CANNOT BE UPLOADED. PLEASE REDUCE THE SIZE OF THE FILE AND TRY AGAIN."),fte:c.trustAsHtml("
FTE stands forFull Time Equivalent. This is a useful measure foran organisation that has peopleworking part-time.For a full-time person, FTE is always equal to1.0, whereas for a part-time person, the FTE will representthe fraction of standard hours that the person works on aregular basis. E.g. if the standard working day at an organisationcomprises of 8 hours, then a person who regularly works for8 hours each day would be considered to be full- time andwould have an FTE value of 1.0. A person who regularly worksfor only 4 hours each day would be considered to be apart-time person and would have an FTE value of 0.5. If theorganisation had 10 people, each with an FTE of 1.0 theactual headcount of full-time people would be 10 and theFTE headcount (equal to actual headcount multiplied by theFTE value) would also be 10. However, if the organisationhad another 10 people who each worked part-time with an FTEvalue of 0.5 the actual headcount of part-time people wouldbe 10 while the FTE headcount would only be 5. Thus for anorganisation that had a total of 10 full-time people, and 10part-time people (each with an FTE of 0.5) the actualheadcount for the organisation would be 20 while the FTEheadcount would be 15.
")},s.uploader={details:{contract_file:g.uploader("civicrm_hrjobcontract_details")},pension:{evidence_file:g.uploader("civicrm_hrjobcontract_pension",1)}},s.cancel=R,s.filesValidate=P,s.openOptionsEditor=F,s.openHoursLocationOptionsEditor=T,s.openPayScaleGradeOptionsEditor=U,s.openAnnualBenefitOptionsEditor=I,s.openAnnualDeductionOptionsEditor=q,s.save=E,function(){e.copy(j,s.entity),s.entity.contract={is_primary:0},e.forEach(s.uploader,function(t){e.forEach(t,function(e){e.onAfterAddingAll=function(){s.filesValidate()}})}),L(),a.$broadcast("hrjc-loader-show"),M().then(function(){a.$broadcast("hrjc-loader-hide")})}()}return o.__name="ModalContractNewController",o.$inject=["$log","$q","$rootElement","$rootScope","$sce","$scope","$uibModalInstance","$uibModal","crmAngService","Contract","contractService","contractDetailsService","contractHourService","contractPayService","contractLeaveService","contractHealthService","contractPensionService","contractFilesService","model","OptionGroup","utilsService","utils","settings","pubSub"],o}),define("job-contract/controllers/modal/modal-contract.controller",["common/angular","common/lodash","common/moment"],function(e,t,n){"use strict";function o(o,r,i,a,c,s,l,u,d,f,p,m,v,_,h,b,y,g,j,C,$,S,w,D,R,P,A){function M(){if("view"===g||e.equals(j,o.entity)&&e.equals($,o.files)&&!o.uploader.details.contract_file.queue.length&&!o.uploader.pension.evidence_file.queue.length)return o.$broadcast("hrjc-loader-hide"),void i.dismiss("cancel");R.debug&&e.forEach(j,function(t,n){e.equals(t,o.entity[n])||(P.debug("======================"),P.debug("Changed entity: "+n),P.debug("Before:"),P.debug(t),P.debug("After:"),P.debug(o.entity[n]))}),r.open({appendTo:c.find("div").eq(0),templateUrl:R.pathApp+"views/modalDialog.html?v="+(new Date).getTime(),size:"sm",controller:"ModalDialogController",resolve:{content:function(){return{copyCancel:"No",title:"Alert",msg:"Are you sure you want to cancel? Changes will be lost!"}}}}).result.then(function(e){e&&(o.$broadcast("hrjc-loader-hide"),i.dismiss("cancel"))})}function E(){return r.open({appendTo:c.find("div").eq(0),templateUrl:R.pathApp+"views/modalChangeReason.html?v="+(new Date).getTime(),controller:"ModalChangeReasonController",resolve:{content:function(){return{copy:{title:K.title}}},date:null,reasonId:null}}).result}function L(){var t=k("period_end_date"),n=k("period_start_date"),r=!e.equals(o.entity.leave,j.leave);return n||t||r}function F(){return r.open({appendTo:c.find("div").eq(0),templateUrl:R.pathApp+"views/modalConfirmEdit.html?v="+(new Date).getTime(),controller:"ModalDialogController",resolve:{content:function(){return{msg:"Save without making a new revision?"}}}}).result}function T(e,t){return o.$broadcast("hrjc-loader-show"),f.validateEffectiveDate({contact_id:R.contactId,effective_date:t}).then(function(n){if(n.success)return Y(e,t);CRM.alert(n.message,"Error","error"),o.$broadcast("hrjc-loader-hide")},function(e){})}function U(){o.$broadcast("hrjc-loader-show"),o.entity.details.period_end_date=o.entity.details.period_end_date||"";var t,n,s,l,u,f=e.copy(o.entity),g=o.filesTrash,j=o.uploader,C={contract:d.save(f.contract),details:p.save(f.details),hour:m.save(f.hour),pay:v.save(f.pay),leave:_.save(f.leave),health:h.save(f.health),pension:b.save(f.pension)},$=[],S=[];for(t in g)for(s=0,l=g[t].length,s;so.fileMaxSize&&e.remove()})})}),j.details.contract_file.queue.length&&$.push(y.upload(j.details.contract_file,f.details.jobcontract_revision_id)),j.pension.evidence_file.queue.length&&$.push(y.upload(j.pension.evidence_file,f.pension.jobcontract_revision_id)),t.details.period_start_date=f.details.period_start_date,t.details.period_end_date=f.details.period_end_date,t.pay.annual_benefits=f.pay.annual_benefits,t.pay.annual_deductions=f.pay.annual_deductions,$.length?(u=r.open({appendTo:c.find("div").eq(0),templateUrl:R.pathApp+"views/modalProgress.html?v="+(new Date).getTime(),size:"sm",controller:"ModalProgressController",resolve:{uploader:function(){return j},promiseFilesUpload:function(){return $}}}),t.files=u.result,a.all(t)):(t.haveEntitlementFieldsChanged=L(),t)}).then(function(e){o.$broadcast("hrjc-loader-hide"),i.close(e),A.publish("Contract::updated")},function(e){o.$broadcast("hrjc-loader-hide"),CRM.alert(e,"Error","error")})}function I(e){var t=l("formatDate")(e,Date);return"Unspecified"!==t?t:e}function q(){return a.all([{name:"hrjobcontract_health_health_plan_type",key:"plan_type"},{name:"hrjobcontract_health_life_insurance_plan_type",key:"plan_type_life_insurance"}].map(function(e){h.getOptions(e.name,!0).then(function(n){s.options.health[e.key]=t.transform(n,function(e,t){e[t.key]=t.value},{})})}))}function H(e,t){var n=o.files[t];o.filesTrash[t].push(n[e]),n.splice(e,1)}function x(){var e=t.every(o.uploader,function(e){return t.every(e,function(e){return!e.queue||t.every(e.queue,function(e){return e.file.sizeo.fileMaxSize&&e.remove()}),T.queue.length&&D.push(y.upload(T,l))}),D.length?(s=r.open({appendTo:c.find("div").eq(0),templateUrl:R.pathApp+"views/modalProgress.html",size:"sm",controller:"ModalProgressController",resolve:{uploader:function(){return C},promiseFilesUpload:function(){return D}}}),e.files=s.result,a.all(e)):(e.haveEntitlementFieldsChanged=L(),e)}).then(function(e){o.$broadcast("hrjc-loader-hide"),i.close(e),A.publish("Contract::updated")})):(o.$broadcast("hrjc-loader-hide"),i.close())}function J(e,n){var o={hrjobcontract_details_contract_type:"contract_type",hrjobcontract_details_location:"location",hrjobcontract_details_end_reason:"end_reason",hrjobcontract_health_health_plan_type:"provider_life_insurance"};u.loadForm(e).on("crmUnload",function(){"hrjobcontract_health_health_plan_type"===n?h.getOptions(n,!0).then(function(e){var n={};t.each(e,function(e){n[e.key]=e.value}),s.options.health.plan_type=n,s.options.health.plan_type_life_insurance=n}):p.getOptions(n,!0).then(function(e){s.options.details[o[n]]=e.obj})})}function V(){u.loadForm("/civicrm/hours_location?reset=1").on("crmUnload",function(){w.getHoursLocation().then(function(e){o.utils.hoursLocation=e})})}function G(){u.loadForm("/civicrm/pay_scale?reset=1").on("crmUnload",function(){w.getPayScaleGrade().then(function(e){o.utils.payScaleGrade=e})})}function W(){u.loadForm("/civicrm/admin/options/hrjc_benefit_name?reset=1").on("crmUnload",function(){Z("hrjc_benefit_name","benefit_name")})}function X(){u.loadForm("/civicrm/admin/options/hrjc_deduction_name?reset=1").on("crmUnload",function(){Z("hrjc_deduction_name","deduction_name")})}function Z(e,n){return S.valuesOf(e,!1).then(function(e){s.options.pay[n]=t.mapValues(t.indexBy(e,"value"),"label")})}P.debug("Controller: ModalContractController");var K=C.copy||{};K.close=K.close||"Close",K.save=K.save||"Save changes",K.title=K.title||"Contract",o.action=g||"view",o.allowSave=void 0!==C.allowSave&&C.allowSave,o.copy=K,o.entity={},o.fileMaxSize=R.CRM.maxFileSize||0,o.files={},o.filesTrash={},o.isDisabled=void 0===C.isDisabled||C.isDisabled,o.isPrimaryDisabled=+j.contract.is_primary,o.showIsPrimary=D.contractListLen>1&&"change"!==g,o.uploader={details:{contract_file:y.uploader("civicrm_hrjobcontract_details")},pension:{evidence_file:y.uploader("civicrm_hrjobcontract_pension",1)}},o.utils=D,o.cancel=M,o.fileMoveToTrash=H,o.filesValidate=x,o.save=N,o.openOptionsEditor=J,o.openHoursLocationOptionsEditor=V,o.openPayScaleGradeOptionsEditor=G,o.openAnnualBenefitOptionsEditor=W,o.openAnnualDeductionOptionsEditor=X,function(){z(),O(),i.opened.then(function(){s.$broadcast("hrjc-loader-hide")}),s.$broadcast("hrjc-loader-show"),q().then(function(){s.$broadcast("hrjc-loader-hide")})}()}return o.__name="ModalContractController",o.$inject=["$scope","$uibModal","$uibModalInstance","$q","$rootElement","$rootScope","$filter","crmAngService","contractService","contractRevisionService","contractDetailsService","contractHourService","contractPayService","contractLeaveService","contractHealthService","contractPensionService","contractFilesService","action","entity","content","files","OptionGroup","utilsService","utils","settings","$log","pubSub"],o}),define("job-contract/controllers/modal/modal-dialog.controller",[],function(){"use strict";function e(e,t,n,o,r){function i(){o.dismiss("Cancel")}function a(e){o.close(e||!0)}e.debug("Controller: ModalDialogController"),t.copyCancel=r.copyCancel||"Cancel",t.copyConfirm=r.copyConfirm||"Yes",t.msg=r.msg||"",t.title=r.title||"CiviHR Job Contract",t.cancel=i,t.confirm=a}return e.__name="ModalDialogController",e.$inject=["$log","$scope","$timeout","$uibModalInstance","content"],e}),define("job-contract/controllers/modal/modal-progress.controller",[],function(){"use strict";function e(e,t,n,o,r,i,a){function c(){r.dismiss("File upload canceled")}e.debug("Controller: ModalProgressController");var s,l;n.uploader=i,n.cancel=c,function(){for(s in i)for(l in i[s])i[s][l].queue.length&&(i[s][l].item=i[s][l].queue[0].file.name),i[s][l].onProgressItem=function(e){this.item=e.file.name};t.all(a).then(function(e){o(function(){r.close(e)},500)})}()}return e.__name="ModalProgressController",e.$inject=["$log","$q","$scope","$timeout","$uibModalInstance","uploader","promiseFilesUpload"],e}),define("job-contract/controllers/modal/modal-revision.controller",["common/angular"],function(e){"use strict";function t(t,n,o,r,i,a,c,s,l,u,d,f,p,m,v){function _(){a.dismiss("cancel")}function h(){var e=(i.currentPage-1)*i.itemsPerPage,t=e+i.itemsPerPage;i.revisionDataListPage=i.revisionDataList.slice(e,t)}function b(){var e,t=0,n=i.fields.length;for(t;t-1?"&":"?"),r=i.entity;return e.forEach(i.fields,function(e){t="editor_name"!==e.name?e.name:"editor_uid",n=e.extends?"":r+"_",e.selected&&(o+="fields["+n+t+"]=1&")}),o+="fields[sort_name]=1&fields[first_name]=1&fields[last_name]=1&fields[external_identifier]=1&fields[email]=1&fields[street_address]=1&fields[city]=1&fields[name]=1&fields[contract_contact_id]=1&fields[contract_contract_id]=1&fields[jobcontract_revision_id]=1&fields[change_reason]=1&fields[created_date]=1&fields[effective_date]=1&fields[modified_date]=1&order_bys[1][column]=id&order_bys[1][order]=ASC&order_bys[2][column]=civicrm_hrjobcontract_revision_revision_id&order_bys[2][order]=ASC&order_bys[3][column]=-&order_bys[3][order]=ASC&order_bys[4][column]=-&order_bys[4][order]=ASC&order_bys[5][column]=-&order_bys[5][order]=ASC&contract_id_op=eq&permission=access+CiviReport&row_count=&_qf_Summary_submit_csv=Preview+CSV&groups=&contract_id_value="+l[0].jobcontract_id+"&group_bys[civicrm_hrjobcontract_revision_revision_id]=1"}n.debug("Controller: ModalRevisionController"),i.$broadcast("hrjc-loader-show"),i.currentPage=1,i.entity=u,i.fields=e.copy(d),i.itemsPerPage=5,i.revisionDataList=[],i.revisionList=[],i.sortCol="effective_date",i.subFields={},i.maxSize=5,i.modalContract=p,i.sortReverse=!0,i.urlCSV=S(),i.cancel=_,i.createPage=h,i.sortBy=C,i.toggleFieldsSelected=$,function(){b(),y(),g(),j(),i.sortBy(),a.opened.then(function(){r.$broadcast("hrjc-loader-hide")})}()}return t.__name="ModalRevisionController",t.$inject=["$filter","$log","$q","$rootScope","$scope","$uibModalInstance","settings","revisionDataList","revisionList","entity","fields","model","modalContract","utils","contactService"],t}),define("job-contract/modules/job-contract.controllers",["common/angular","job-contract/controllers/contract.controller","job-contract/controllers/contract-list.controller","job-contract/controllers/revision-list.controller","job-contract/controllers/form/form-general.controller","job-contract/controllers/form/form-health.controller","job-contract/controllers/form/form-hour.controller","job-contract/controllers/form/form-leave.controller","job-contract/controllers/form/form-pay.controller","job-contract/controllers/form/form-pension.controller","job-contract/controllers/modal/modal-change-reason.controller","job-contract/controllers/modal/modal-contract-new.controller","job-contract/controllers/modal/modal-contract.controller","job-contract/controllers/modal/modal-dialog.controller","job-contract/controllers/modal/modal-progress.controller","job-contract/controllers/modal/modal-revision.controller"],function(e,t,n,o,r,i,a,c,s,l,u,d,f,p,m,v){"use strict";return e.module("job-contract.controllers",[]).controller(t.__name,t).controller(n.__name,n).controller(o.__name,o).controller(r.__name,r).controller(i.__name,i).controller(a.__name,a).controller(c.__name,c).controller(s.__name,s).controller(l.__name,l).controller(u.__name,u).controller(d.__name,d).controller(f.__name,f).controller(p.__name,p).controller(m.__name,m).controller(v.__name,v)}),function(e){define("leave-absences/shared/modules/shared-settings",["common/angular"],function(t){return t.module("leave-absences.settings",[]).constant("shared-settings",{attachmentToken:e.vars.leaveAndAbsences.attachmentToken,debug:e.debug,managerPathTpl:e.vars.leaveAndAbsences.baseURL+"/views/manager-leave/",sharedPathTpl:e.vars.leaveAndAbsences.baseURL+"/views/shared/",serverDateFormat:"YYYY-MM-DD",serverDateTimeFormat:"YYYY-MM-DD HH:mm:ss",permissions:{admin:{access:"access leave and absences",administer:"administer leave and absences"},ssp:{access:"access leave and absences in ssp",manage:"manage leave and absences in ssp"}},fileUploader:{queueLimit:10},statusNames:{approved:"approved",adminApproved:"admin_approved",awaitingApproval:"awaiting_approval",moreInformationRequired:"more_information_required",rejected:"rejected",cancelled:"cancelled"}})})}(CRM),define("leave-absences/shared/modules/apis",["common/angular","common/modules/apis","leave-absences/shared/modules/shared-settings"],function(e){"use strict";return e.module("leave-absences.apis",["common.apis","leave-absences.settings"])}),define("leave-absences/shared/modules/models-instances",["common/angular","common/models/instances/instance","common/modules/services","common/modules/models","common/services/check-permissions","leave-absences/shared/modules/shared-settings"],function(e){"use strict";return e.module("leave-absences.models.instances",["common.models","common.models.instances","common.services","leave-absences.settings"])}),define("leave-absences/shared/modules/models",["common/angular","common/modules/models","common/modules/services","leave-absences/shared/modules/apis","leave-absences/shared/modules/models-instances","leave-absences/shared/modules/shared-settings"],function(e){"use strict";return e.module("leave-absences.models",["common.models","common.services","leave-absences.apis","leave-absences.models.instances","leave-absences.settings"])}),define("leave-absences/shared/apis/absence-type.api",["common/lodash","common/moment","leave-absences/shared/modules/apis","common/services/api"],function(e,t,n){"use strict";n.factory("AbsenceTypeAPI",["$log","api","shared-settings",function(n,o,r){return n.debug("AbsenceTypeAPI"),o.extend({all:function(t){return n.debug("AbsenceTypeAPI.all"),this.sendGET("AbsenceType","get",e.defaultsDeep(t||{},{is_active:!0,options:{sort:"weight ASC"}})).then(function(e){return e.values})},calculateToilExpiryDate:function(o,i,a){return n.debug("AbsenceTypeAPI.calculateToilExpiryDate"),a=e.assign({},a,{absence_type_id:o,date:t(i).format(r.serverDateFormat)}),this.sendPOST("AbsenceType","calculateToilExpiryDate",a).then(function(e){return e.values.expiry_date})}})}])}),define("leave-absences/shared/instances/absence-type.instance",["leave-absences/shared/modules/models-instances","common/models/instances/instance"],function(e){"use strict";e.factory("AbsenceTypeInstance",["$log","ModelInstance",function(e,t){return e.debug("AbsenceTypeInstance"),t.extend({})}])}),define("leave-absences/shared/models/absence-type.model",["common/lodash","leave-absences/shared/modules/models","common/models/model","common/models/option-group","leave-absences/shared/apis/absence-type.api","leave-absences/shared/instances/absence-type.instance"],function(e,t){"use strict";t.factory("AbsenceType",["$log","$q","Model","OptionGroup","AbsenceTypeAPI","AbsenceTypeInstance",function(t,n,o,r,i,a){return t.debug("AbsenceType"),o.extend({all:function(e){return i.all(e).then(function(e){return e.map(function(e){return a.init(e,!0)})})},calculateToilExpiryDate:function(e,t,n){return i.calculateToilExpiryDate(e,t,n)},canExpire:function(e){return i.all({accrual_expiration_unit:{"IS NOT NULL":1},accrual_expiration_duration:{"IS NOT NULL":1},allow_accruals_request:1,id:e,options:{limit:1},return:["id"]}).then(function(e){return e.length>0})},loadCalculationUnits:function(t){return r.valuesOf("hrleaveandabsences_absence_type_calculation_unit").then(function(n){return n=e.indexBy(n,"value"),e.map(t,function(t){return e.assign(t,{calculation_unit_label:n[t.calculation_unit].label,calculation_unit_name:n[t.calculation_unit].name})})})}})}])}),define("leave-absences/shared/instances/absence-period.instance",["leave-absences/shared/modules/models-instances","common/moment","common/models/instances/instance","common/services/hr-settings"],function(e,t){"use strict";e.factory("AbsencePeriodInstance",["$log","ModelInstance","HR_settings",function(e,n,o){return e.debug("AbsencePeriodInstance"),n.extend({defaultCustomData:function(){return{current:!1}},transformAttributes:function(e){var n=t();return e.current=!1,t(e.start_date).isSameOrBefore(n,"day")&&t(e.end_date).isSameOrAfter(n,"day")&&(e.current=!0),e},isInPeriod:function(e){var n=o.DATE_FORMAT.toUpperCase(),r=t(e,n);return t(this.start_date).isSameOrBefore(r)&&t(this.end_date).isSameOrAfter(r)}})}])}),define("leave-absences/shared/apis/absence-period.api",["leave-absences/shared/modules/apis","common/services/api"],function(e){"use strict";e.factory("AbsencePeriodAPI",["$log","api",function(e,t){return e.debug("AbsencePeriodAPI"),t.extend({all:function(t){return e.debug("AbsencePeriodAPI"),this.sendGET("AbsencePeriod","get",t).then(function(e){return e.values})}})}])}),define("leave-absences/shared/models/absence-period.model",["leave-absences/shared/modules/models","common/moment","leave-absences/shared/modules/shared-settings","leave-absences/shared/instances/absence-period.instance","leave-absences/shared/apis/absence-period.api","common/models/model","common/services/hr-settings"],function(e,t){"use strict";e.factory("AbsencePeriod",["$log","Model","AbsencePeriodAPI","AbsencePeriodInstance","shared-settings",function(e,n,o,r,i){return e.debug("AbsencePeriod"),n.extend({all:function(e){return o.all(e).then(function(e){return e.map(function(e){return r.init(e,!0)})})},current:function(){var e=t().format(i.serverDateFormat),n={start_date:{"<=":e},end_date:{">=":e}};return o.all(n).then(function(e){return e&&e.length?r.init(e[0],!0):null})}})}])}),function(e,t){function n(){var e={nodiff:"",year:"year",years:"years",month:"month",months:"months",day:"day",days:"days",hour:"hour",hours:"hours",minute:"minute",minutes:"minutes",second:"second",seconds:"seconds",delimiter:" "};moment.fn.preciseDiff=function(e){return moment.preciseDiff(this,e)},moment.preciseDiff=function(t,n){function o(t,n){return t+" "+e[n+(1===t?"":"s")]}var r=moment(t),i=moment(n);if(r.isSame(i))return e.nodiff;if(r.isAfter(i)){var a=r;r=i,i=a}var c=i.year()-r.year(),s=i.month()-r.month(),l=i.date()-r.date();if(l<0){var u=moment(i.year()+"-"+(i.month()+1),"YYYY-MM").subtract("months",1).daysInMonth();l=u
When All filter is selected, all staff members with contracts which are active in the selected absence period are displayed.
","
People I approve filter displays only staff members who you approve leave for.
","
People without approver filter displays all staff members with contracts which are active in the selected absence period and who do not have any leave approver assigned.
When All filter is selected, all staff members with contracts which are active in the selected absence period are displayed.
","
People I approve filter displays only staff members who you approve leave for.
","
People without approver filter displays all staff members with contracts which are active in the selected absence period and who do not have any leave approver assigned.
Last updated: By: "+e.author_name+" Date: "+t.utc(e.date).local().format("DD/M/YYYY HH:mm")+"";d.info("Calculation comment:",n)}a.debug("Component: annual-entitlements");var q=[],C=[],T=this;T.absencePeriods=[],T.loading={absencePeriods:!0},T.getEditEntitlementsPageURL=p,T.openAnnualEntitlementChangeLog=g,T.showComment=y,function(){b().then(v).then(h).then(m).then(f).then(_).finally(function(){T.loading.absencePeriods=!1})}()}a.component("annualEntitlements",{bindings:{absenceTypes:"<",contactId:"<"},templateUrl:["settings",function(e){return e.pathTpl+"components/annual-entitlements.html"}],controllerAs:"entitlements",controller:o}),o.$inject=["$log","$q","$rootElement","$uibModal","AbsenceType","AbsencePeriod","Entitlement","Contact","notificationService"]})}(CRM),define("leave-absences/absence-tab/components/contract-entitlements.component",["common/lodash","common/moment","leave-absences/absence-tab/modules/components","common/models/contract"],function(e,n,t){function a(t,a,o,s,i,r){function c(){m.absenceTypes=e.filter(m.absenceTypes,function(n){return e.find(m.contracts,function(t){return e.find(t.info.leave,function(e){return e.leave_type===n.id})})})}function u(e){var t=o.DATE_FORMAT.toUpperCase();return e?n(e).format(t):""}function l(){return i.all({contact_id:m.contactId}).then(function(e){m.contracts=e})}function d(){m.contracts=e.sortBy(m.contracts,function(e){return n(e.info.details.period_start_date)}).map(function(n){var t=n.info,a=t.details,o=e.map(m.absenceTypes,function(n){var a=e.filter(t.leave,function(e){return e.leave_type===n.id})[0];return{amount:a?a.leave_amount:"",calculation_unit:n.calculation_unit_name}});return{position:a.position,start_date:u(a.period_start_date),end_date:u(a.period_end_date),absences:o}})}t.debug("Component: contract-entitlements");var m=this;m.contracts=[],m.loading={contracts:!0},function(){r.getDateFormat().then(l).then(c).then(d).finally(function(){m.loading.contracts=!1})}()}t.component("contractEntitlements",{bindings:{absenceTypes:"<",contactId:"<"},templateUrl:["settings",function(e){return e.pathTpl+"components/contract-entitlements.html"}],controllerAs:"entitlements",controller:a}),a.$inject=["$log","$q","HR_settings","AbsenceType","Contract","DateFormat"]}),function(e){define("leave-absences/absence-tab/modules/settings",["common/angular"],function(n){return n.module("absence-tab.settings",[]).constant("settings",{contactId:e.vars.leaveAndAbsences.contactId,debug:e.debug,pathTpl:e.vars.leaveAndAbsences.baseURL+"/views/absence-tab/"})})}(CRM),define("leave-absences/absence-tab/modules/config",["common/angular","leave-absences/absence-tab/modules/settings"],function(e){return e.module("absence-tab.config",["absence-tab.settings"]).config(["$resourceProvider","$httpProvider","$logProvider","settings",function(e,n,t,a){t.debugEnabled(a.debug),e.defaults.stripTrailingSlashes=!1,n.defaults.headers.common["X-Requested-With"]="XMLHttpRequest"}])}),define("leave-absences/absence-tab/app",["common/angular","common/angularBootstrap","common/text-angular","common/directives/scroll-shadows.directive","common/directives/time-amount-picker.directive","common/directives/timepicker-select.directive","common/filters/angular-date/format-date","common/filters/time-unit-applier.filter","common/modules/dialog","common/modules/directives","common/services/check-permissions","common/services/crm-ang.service","common/services/angular-date/date-format","common/services/notification.service","leave-absences/shared/modules/shared-settings","leave-absences/shared/models/absence-type.model","leave-absences/shared/models/calendar.model","leave-absences/shared/models/entitlement.model","leave-absences/shared/models/leave-request.model","leave-absences/shared/models/work-pattern.model","leave-absences/shared/components/leave-calendar.component","leave-absences/shared/components/leave-calendar-day.component","leave-absences/shared/components/leave-calendar-legend.component","leave-absences/shared/components/leave-calendar-month.component","leave-absences/shared/components/leave-request-actions.component","leave-absences/shared/components/leave-request-popup-comments-tab.component","leave-absences/shared/components/leave-request-popup-details-tab.component","leave-absences/shared/components/leave-request-popup-files-tab","leave-absences/shared/components/leave-request-record-actions.component","leave-absences/shared/components/staff-leave-report.component","leave-absences/shared/controllers/sub-controllers/request-modal-details-leave.controller","leave-absences/shared/controllers/sub-controllers/request-modal-details-sickness.controller","leave-absences/shared/controllers/sub-controllers/request-modal-details-toil.controller","leave-absences/shared/models/absence-type.model","leave-absences/shared/models/calendar.model","leave-absences/shared/models/leave-request.model","leave-absences/shared/models/work-pattern.model","leave-absences/shared/models/absence-type.model","leave-absences/shared/models/entitlement.model","leave-absences/shared/modules/shared-settings","leave-absences/shared/services/leave-calendar.service","leave-absences/shared/services/leave-popup.service","leave-absences/absence-tab/components/absence-tab-container.component","leave-absences/absence-tab/components/absence-tab-entitlements.component","leave-absences/absence-tab/components/absence-tab-work-patterns.component","leave-absences/absence-tab/components/annual-entitlement-change-log.component","leave-absences/absence-tab/components/annual-entitlements.component","leave-absences/absence-tab/components/contract-entitlements.component","leave-absences/absence-tab/modules/config"],function(e){return e.module("absence-tab",["ngResource","ui.bootstrap","textAngular","common.angularDate","common.dialog","common.directives","common.filters","common.services","common.mocks","leave-absences.settings","leave-absences.models","leave-absences.components","leave-absences.controllers","leave-absences.models","leave-absences.services","leave-absences.settings","absence-tab.config","absence-tab.components"]).run(["$log","$rootScope","shared-settings","settings",function(e,n,t,a){e.debug("app.run"),n.sharedPathTpl=t.sharedPathTpl,n.settings=a}]),e}),function(e,n){var t=e.vars.leaveAndAbsences.baseURL+"/js/angular/src/leave-absences";n.config({urlArgs:"bust="+(new Date).getTime(),paths:{"leave-absences/shared":t+"/shared","leave-absences/absence-tab":t+"/absence-tab"}}),n(["leave-absences/shared/config"],function(){n(["leave-absences/absence-tab/app"],function(){document.dispatchEvent("function"==typeof window.CustomEvent?new CustomEvent("absenceTabReady"):function(){var e=document.createEvent("Event");return e.initEvent("absenceTabReady",!0,!0),e}())})})}(CRM,require),define("absence-tab",function(){});
//# sourceMappingURL=/sites/all/modules/civicrm/tools/extensions/civihr/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/dist/absence-tab.js.map
\ No newline at end of file
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/dist/admin-dashboard.min.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/dist/admin-dashboard.min.js
index 989685bc2ee..2d51b384a89 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/dist/admin-dashboard.min.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/dist/admin-dashboard.min.js
@@ -6,9 +6,9 @@
*/
-!function(e){define("leave-absences/shared/config",[],function(){var t=CRM.vars.leaveAndAbsences.baseURL+"/js/angular/src/leave-absences/shared";e.config({paths:{"leave-absences/shared/ui-router":"leave-absences/shared/vendor/angular-ui-router.min",mocks:"../test/mocks"},shim:{"leave-absences/shared/ui-router":{}}}),e.config({paths:{"leave-absences/shared/ui-router":t+"/vendor/angular-ui-router.min",mocks:CRM.vars.leaveAndAbsences.baseURL+"/js/angular/test/mocks"}})})}(require),function(e){define("leave-absences/shared/ui-router",[],function(){return function(){"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="ui.router"),function(e,t,n){"use strict";function a(e,t){return W(new(W(function(){},{prototype:e})),t)}function r(e){return H(arguments,function(t){t!==e&&H(t,function(t,n){e.hasOwnProperty(n)||(e[n]=t)})}),e}function o(e,t){var n=[];for(var a in e.path){if(e.path[a]!==t.path[a])break;n.push(e.path[a])}return n}function s(e){if(Object.keys)return Object.keys(e);var t=[];return H(e,function(e,n){t.push(n)}),t}function i(e,t){if(Array.prototype.indexOf)return e.indexOf(t,Number(arguments[2])||0);var n=e.length>>>0,a=Number(arguments[2])||0;for((a=a<0?Math.ceil(a):Math.floor(a))<0&&(a+=n);a=0||(l.push(r[f]),c[r[f]]=e[r[f]]);return W({},c,t)}function c(e,t,n){if(!n){n=[];for(var a in e)n.push(a)}for(var r=0;r "));if(b[n]=a,N(e))v.push(n,[function(){return t.get(e)}],c);else{var r=t.annotate(e);H(r,function(e){e!==n&&u.hasOwnProperty(e)&&m(u[e],e)}),v.push(n,e,r)}g.pop(),b[n]=o}}function p(e){return F(e)&&e.then&&e.$$promises}if(!F(u))throw new Error("'invocables' must be an object");var h=s(u||{}),v=[],g=[],b={};return H(u,m),u=g=b=null,function(a,o,s){function i(){--y||(_||r(b,o.$$values),m.$$values=b,m.$$promises=m.$$promises||!0,delete m.$$inheritedValues,c.resolve(b))}function u(e){m.$$failure=e,c.reject(e)}if(p(a)&&s===n&&(s=o,o=a,a=null),a){if(!F(a))throw new Error("'locals' must be an object")}else a=l;if(o){if(!p(o))throw new Error("'parent' must be a promise returned by $resolve.resolve()")}else o=d;var c=e.defer(),m=c.promise,g=m.$$promises={},b=W({},a),y=1+v.length/3,_=!1;if(j(o.$$failure))return u(o.$$failure),m;o.$$inheritedValues&&r(b,f(o.$$inheritedValues,h)),W(g,o.$$promises),o.$$values?(_=r(b,f(o.$$values,h)),m.$$inheritedValues=f(o.$$values,h),i()):(o.$$inheritedValues&&(m.$$inheritedValues=f(o.$$inheritedValues,h)),o.then(i,u));for(var q=0,$=v.length;q<$;q+=3)a.hasOwnProperty(v[q])?i():function(n,r,o){function c(e){d.reject(e),u(e)}function l(){if(!j(m.$$failure))try{d.resolve(t.invoke(r,s,b)),d.promise.then(function(e){b[n]=e,i()},c)}catch(e){c(e)}}var d=e.defer(),f=0;H(o,function(e){g.hasOwnProperty(e)&&!a.hasOwnProperty(e)&&(f++,g[e].then(function(t){b[e]=t,--f||l()},c))}),f||l(),g[n]=d.promise}(v[q],v[q+1],v[q+2]);return m}},this.resolve=function(e,t,n,a){return this.study(e)(t,n,a)}}function g(e,t,n){this.fromConfig=function(e,t,n){return j(e.template)?this.fromString(e.template,t):j(e.templateUrl)?this.fromUrl(e.templateUrl,t):j(e.templateProvider)?this.fromProvider(e.templateProvider,t,n):null},this.fromString=function(e,t){return B(e)?e(t):e},this.fromUrl=function(n,a){return B(n)&&(n=n(a)),null==n?null:e.get(n,{cache:t,headers:{Accept:"text/html"}}).then(function(e){return e.data})},this.fromProvider=function(e,t,a){return n.invoke(e,null,a||{params:t})}}function b(e,t,r){function o(t,n,a,r){if(v.push(t),p[t])return p[t];if(!/^\w+([-.]+\w+)*(?:\[\])?$/.test(t))throw new Error("Invalid parameter name '"+t+"' in pattern '"+e+"'");if(h[t])throw new Error("Duplicate parameter name '"+t+"' in pattern '"+e+"'");return h[t]=new Y.Param(t,n,a,r),h[t]}function s(e,t,n,a){var r=["",""],o=e.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&");if(!t)return o;switch(n){case!1:r=["(",")"+(a?"?":"")];break;case!0:o=o.replace(/\/$/,""),r=["(?:/(",")|/)?"];break;default:r=["("+n+"|",")?"]}return o+r[0]+t+r[1]}function i(r,o){var s,i,u,c,l;return s=r[2]||r[3],l=t.params[s],u=e.substring(f,r.index),i=o?r[4]:r[4]||("*"==r[1]?".*":null),i&&(c=Y.type(i)||a(Y.type("string"),{pattern:new RegExp(i,t.caseInsensitive?"i":n)})),{id:s,regexp:i,segment:u,type:c,cfg:l}}t=W({params:{}},F(t)?t:{});var u,c=/([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,l=/([:]?)([\w\[\].-]+)|\{([\w\[\].-]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,d="^",f=0,m=this.segments=[],p=r?r.params:{},h=this.params=r?r.params.$$new():new Y.ParamSet,v=[];this.source=e;for(var g,b,y;(u=c.exec(e))&&(g=i(u,!1),!(g.segment.indexOf("?")>=0));)b=o(g.id,g.type,g.cfg,"path"),d+=s(g.segment,b.type.pattern.source,b.squash,b.isOptional),m.push(g.segment),f=c.lastIndex;y=e.substring(f);var _=y.indexOf("?");if(_>=0){var q=this.sourceSearch=y.substring(_);if(y=y.substring(0,_),this.sourcePath=e.substring(0,f+_),q.length>0)for(f=0;u=l.exec(q);)g=i(u,!0),b=o(g.id,g.type,g.cfg,"search"),f=c.lastIndex}else this.sourcePath=e,this.sourceSearch="";d+=s(y)+(!1===t.strict?"/?":"")+"$",m.push(y),this.regexp=new RegExp(d,t.caseInsensitive?"i":n),this.prefix=m[0],this.$$paramNames=v}function y(e){W(this,e)}function _(){function e(e){return null!=e?e.toString().replace(/(~|\/)/g,function(e){return{"~":"~~","/":"~2F"}[e]}):e}function r(e){return null!=e?e.toString().replace(/(~~|~2F)/g,function(e){return{"~~":"~","~2F":"/"}[e]}):e}function o(){return{strict:h,caseInsensitive:f}}function u(e){return B(e)||U(e)&&B(e[e.length-1])}function c(){for(;$.length;){var e=$.shift();if(e.pattern)throw new Error("You cannot override a type's .pattern at runtime.");t.extend(g[e.name],d.invoke(e.def))}}function l(e){W(this,e||{})}Y=this;var d,f=!1,h=!0,v=!1,g={},q=!0,$=[],C={string:{encode:e,decode:r,is:function(e){return null==e||!j(e)||"string"==typeof e},pattern:/[^\/]*/},int:{encode:e,decode:function(e){return parseInt(e,10)},is:function(e){return j(e)&&this.decode(e.toString())===e},pattern:/\d+/},bool:{encode:function(e){return e?1:0},decode:function(e){return 0!==parseInt(e,10)},is:function(e){return!0===e||!1===e},pattern:/0|1/},date:{encode:function(e){return this.is(e)?[e.getFullYear(),("0"+(e.getMonth()+1)).slice(-2),("0"+e.getDate()).slice(-2)].join("-"):n},decode:function(e){if(this.is(e))return e;var t=this.capture.exec(e);return t?new Date(t[1],t[2]-1,t[3]):n},is:function(e){return e instanceof Date&&!isNaN(e.valueOf())},equals:function(e,t){return this.is(e)&&this.is(t)&&e.toISOString()===t.toISOString()},pattern:/[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,capture:/([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/},json:{encode:t.toJson,decode:t.fromJson,is:t.isObject,equals:t.equals,pattern:/[^\/]*/},any:{encode:t.identity,decode:t.identity,equals:t.equals,pattern:/.*/}};_.$$getDefaultValue=function(e){if(!u(e.value))return e.value;if(!d)throw new Error("Injectable functions cannot be called at configuration time");return d.invoke(e.value)},this.caseInsensitive=function(e){return j(e)&&(f=e),f},this.strictMode=function(e){return j(e)&&(h=e),h},this.defaultSquashPolicy=function(e){if(!j(e))return v;if(!0!==e&&!1!==e&&!N(e))throw new Error("Invalid squash policy: "+e+". Valid policies: false, true, arbitrary-string");return v=e,e},this.compile=function(e,t){return new b(e,W(o(),t))},this.isMatcher=function(e){if(!F(e))return!1;var t=!0;return H(b.prototype,function(n,a){B(n)&&(t=t&&j(e[a])&&B(e[a]))}),t},this.type=function(e,t,n){if(!j(t))return g[e];if(g.hasOwnProperty(e))throw new Error("A type named '"+e+"' has already been defined.");return g[e]=new y(W({name:e},t)),n&&($.push({name:e,def:n}),q||c()),this},H(C,function(e,t){g[t]=new y(W({name:t},e))}),g=a(g,{}),this.$get=["$injector",function(e){return d=e,q=!1,c(),H(C,function(e,t){g[t]||(g[t]=new y(e))}),this}],this.Param=function(e,a,r,o){function c(){if(!d)throw new Error("Injectable functions cannot be called at configuration time");var e=d.invoke(r.$$fn);if(null!==e&&e!==n&&!h.type.is(e))throw new Error("Default value ("+e+") for parameter '"+h.id+"' is not an instance of Type ("+h.type.name+")");return e}function l(e){function t(e){return function(t){return t.from===e}}return e=function(e){var n=p(m(h.replace,t(e)),function(e){return e.to});return n.length?n[0]:e}(e),j(e)?h.type.$normalize(e):c()}function f(){return"{Param:"+e+" "+a+" squash: '"+q+"' optional: "+_+"}"}var h=this;r=function(e){var t=F(e)?s(e):[];return-1===i(t,"value")&&-1===i(t,"type")&&-1===i(t,"squash")&&-1===i(t,"array")&&(e={value:e}),e.$$fn=u(e.value)?e.value:function(){return e.value},e}(r),a=function(n,a,r){if(n.type&&a)throw new Error("Param '"+e+"' has two type configurations.");return a||(n.type?t.isString(n.type)?g[n.type]:n.type instanceof y?n.type:new y(n.type):"config"===r?g.any:g.string)}(r,a,o);var b=function(){var t={array:"search"===o&&"auto"},n=e.match(/\[\]$/)?{array:!0}:{};return W(t,n,r).array}();a=b?a.$asArray(b,"search"===o):a,"string"!==a.name||b||"path"!==o||r.value!==n||(r.value="");var _=r.value!==n,q=function(e,t){var n=e.squash;if(!t||!1===n)return!1;if(!j(n)||null==n)return v;if(!0===n||N(n))return n;throw new Error("Invalid squash policy: '"+n+"'. Valid policies: false, true, or arbitrary string")}(r,_),$=function(e,t,a,r){var o,s,u=[{from:"",to:a||t?n:""},{from:null,to:a||t?n:""}];return o=U(e.replace)?e.replace:[],N(r)&&o.push({from:r,to:n}),s=p(o,function(e){return e.from}),m(u,function(e){return-1===i(s,e.from)}).concat(o)}(r,b,_,q);W(this,{id:e,type:a,location:o,array:b,squash:q,replace:$,isOptional:_,value:l,dynamic:n,config:r,toString:f})},l.prototype={$$new:function(){return a(this,W(new l,{$$parent:this}))},$$keys:function(){for(var e=[],t=[],n=this,a=s(l.prototype);n;)t.push(n),n=n.$$parent;return t.reverse(),H(t,function(t){H(s(t),function(t){-1===i(e,t)&&-1===i(a,t)&&e.push(t)})}),e},$$values:function(e){var t={},n=this;return H(n.$$keys(),function(a){t[a]=n[a].value(e&&e[a])}),t},$$equals:function(e,t){var n=!0,a=this;return H(a.$$keys(),function(r){var o=e&&e[r],s=t&&t[r];a[r].type.equals(o,s)||(n=!1)}),n},$$validates:function(e){var a,r,o,s,i,u=this.$$keys();for(a=0;a=0)throw new Error("State must have a valid name");if(P.hasOwnProperty(n))throw new Error("State '"+n+"' is already defined");var r=-1!==n.indexOf(".")?n.substring(0,n.lastIndexOf(".")):N(t.parent)?t.parent:F(t.parent)&&N(t.parent.name)?t.parent.name:"";if(r&&!P[r])return m(r,t.self);for(var o in S)B(S[o])&&(t[o]=S[o](t,S.$delegates[o]));return P[n]=t,!t[R]&&t.url&&e.when(t.url,["$match","$stateParams",function(e,n){w.$current.navigable==t&&c(e,n)||w.transitionTo(t,e,{inherit:!0,location:!1})}]),v(n),t}function b(e){return e.indexOf("*")>-1}function y(e){for(var t=e.split("."),n=w.$current.name.split("."),a=0,r=t.length;a=P;a--)s=p[a],s.self.onExit&&i.invoke(s.self.onExit,s.self,s.locals.globals),s.locals=null;for(a=P;a<_.length;a++)r=_[a],r.locals=x[a],r.self.onEnter&&i.invoke(r.self.onEnter,r.self,r.locals.globals);return w.transition!==B?(e.$broadcast("$stateChangeCancel",t.self,n,c.self,d),S):(w.$current=t,w.current=t.self,w.params=n,V(w.params,m),w.transition=null,o.location&&t.navigable&&v.push(t.navigable.url,t.navigable.locals.globals.$stateParams,{$$avoidResync:!0,replace:"replace"===o.location}),o.notify&&e.$broadcast("$stateChangeSuccess",t.self,n,c.self,d),v.update(!0),w.current)}).then(null,function(a){return a===D?S:w.transition!==B?(e.$broadcast("$stateChangeCancel",t.self,n,c.self,d),S):(w.transition=null,s=e.$broadcast("$stateChangeError",t.self,n,c.self,d,a),s.defaultPrevented||v.update(),r.reject(a))});return B},w.is=function(e,t,a){a=W({relative:w.$current},a||{});var r=f(e,a.relative);return j(r)?w.$current===r&&(!t||c(r.params.$$values(t),m)):n},w.includes=function(e,t,a){if(a=W({relative:w.$current},a||{}),N(e)&&b(e)){if(!y(e))return!1;e=w.$current.name}var r=f(e,a.relative);if(!j(r))return n;if(!j(w.$current.includes[r.name]))return!1;if(!t)return!0;for(var o=s(t),i=0;i2?c.enter(e,null,n).then(a):c.enter(e,null,n,a)},leave:function(e,n){t.version.minor>2?c.leave(e).then(n):c.leave(e,n)}};if(u){var a=u&&u(n,e);return{enter:function(e,t,n){a.enter(e,null,t),n()},leave:function(e,t){a.leave(e),t()}}}return function(){return{enter:function(e,t,n){t.after(e),n()},leave:function(e,t){e.remove(),t()}}}()}var i=function(){return n.has?function(e){return n.has(e)?n.get(e):null}:function(e){try{return n.get(e)}catch(e){return null}}}(),u=i("$animator"),c=i("$animate");return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",compile:function(n,i,u){return function(n,i,c){function l(){if(f&&(f.remove(),f=null),p&&(p.$destroy(),p=null),m){var e=m.data("$uiViewAnim");b.leave(m,function(){e.$$animLeave.resolve(),f=null}),f=m,m=null}}function d(s){var d,f=D(n,c,i,r),y=f&&e.$current&&e.$current.locals[f];if(s||y!==h){d=n.$new(),h=e.$current.locals[f],d.$emit("$viewContentLoading",f);var _=u(d,function(e){var r=o.defer(),s=o.defer(),u={$animEnter:r.promise,$animLeave:s.promise,$$animLeave:s};e.data("$uiViewAnim",u),b.enter(e,i,function(){r.resolve(),p&&p.$emit("$viewContentAnimationEnded"),(t.isDefined(g)&&!g||n.$eval(g))&&a(e)}),l()});m=_,p=d,p.$emit("$viewContentLoaded",f),p.$eval(v)}}var f,m,p,h,v=c.onload||"",g=c.autoscroll,b=s(c,n);i.inheritedData("$uiView"),n.$on("$stateChangeSuccess",function(){d(!1)}),d(!0)}}}}function P(e,n,a,r){return{restrict:"ECA",priority:-400,compile:function(o){var s=o.html();return function(o,i,u){var c=a.$current,l=D(o,u,i,r),d=c&&c.locals[l];if(d){i.data("$uiView",{name:l,state:d.$$state}),i.html(d.$template?d.$template:s);var f=t.extend({},d);o[d.$$resolveAs]=f;var m=e(i.contents());if(d.$$controller){d.$scope=o,d.$element=i;var p=n(d.$$controller,d);d.$$controllerAs&&(o[d.$$controllerAs]=p,o[d.$$controllerAs][d.$$resolveAs]=f),B(p.$onInit)&&p.$onInit(),i.data("$ngControllerController",p),i.children().data("$ngControllerController",p)}m(o)}}}}}function D(e,t,n,a){var r=a(t.uiView||t.name||"")(e),o=n.inheritedData("$uiView");return r.indexOf("@")>=0?r:r+"@"+(o?o.state.name:"")}function R(e,t){var n,a=e.match(/^\s*({[^}]*})\s*$/);if(a&&(e=t+"("+a[1]+")"),!(n=e.replace(/\n/g," ").match(/^([^(]+?)\s*(\((.*)\))?$/))||4!==n.length)throw new Error("Invalid state ref '"+e+"'");return{state:n[1],paramExpr:n[3]||null}}function S(e){var t=e.parent().inheritedData("$uiView");if(t&&t.state&&t.state.name)return t.state}function T(e){var t="[object SVGAnimatedString]"===Object.prototype.toString.call(e.prop("href")),n="FORM"===e[0].nodeName;return{attr:n?"action":t?"xlink:href":"href",isAnchor:"A"===e.prop("tagName").toUpperCase(),clickable:!n}}function O(e,t,n,a,r){return function(o){var s=o.which||o.button,i=r();if(!(s>1||o.ctrlKey||o.metaKey||o.shiftKey||e.attr("target"))){var u=n(function(){t.go(i.state,i.params,i.options)});o.preventDefault();var c=a.isAnchor&&!i.href?1:0;o.preventDefault=function(){c--<=0&&n.cancel(u)}}}}function I(e,t){return{relative:S(e)||t.$current,inherit:!0}}function E(e,n){return{restrict:"A",require:["?^uiSrefActive","?^uiSrefActiveEq"],link:function(a,r,o,s){var i,u=R(o.uiSref,e.current.name),c={state:u.state,href:null,params:null},l=T(r),d=s[1]||s[0],f=null;c.options=W(I(r,e),o.uiSrefOpts?a.$eval(o.uiSrefOpts):{});var m=function(n){n&&(c.params=t.copy(n)),c.href=e.href(u.state,c.params,c.options),f&&f(),d&&(f=d.$$addStateInfo(u.state,c.params)),null!==c.href&&o.$set(l.attr,c.href)};u.paramExpr&&(a.$watch(u.paramExpr,function(e){e!==c.params&&m(e)},!0),c.params=t.copy(a.$eval(u.paramExpr))),m(),l.clickable&&(i=O(r,e,n,l,function(){return c}),r[r.on?"on":"bind"]("click",i),a.$on("$destroy",function(){r[r.off?"off":"unbind"]("click",i)}))}}}function x(e,t){return{restrict:"A",require:["?^uiSrefActive","?^uiSrefActiveEq"],link:function(n,a,r,o){function s(t){f.state=t[0],f.params=t[1],f.options=t[2],f.href=e.href(f.state,f.params,f.options),m&&m(),c&&(m=c.$$addStateInfo(f.state,f.params)),f.href&&r.$set(u.attr,f.href)}var i,u=T(a),c=o[1]||o[0],l=[r.uiState,r.uiStateParams||null,r.uiStateOpts||null],d="["+l.map(function(e){return e||"null"}).join(", ")+"]",f={state:null,params:null,options:null,href:null},m=null;n.$watch(d,s,!0),s(n.$eval(d)),u.clickable&&(i=O(a,e,t,u,function(){return f}),a[a.on?"on":"bind"]("click",i),n.$on("$destroy",function(){a[a.off?"off":"unbind"]("click",i)}))}}}function M(e,t,n){return{restrict:"A",controller:["$scope","$element","$attrs","$timeout",function(t,a,r,o){function s(t,n,r){var o=e.get(t,S(a)),s=i(t,n),u={state:o||{name:t},params:n,hash:s};return h.push(u),v[s]=r,function(){var e=h.indexOf(u);-1!==e&&h.splice(e,1)}}function i(e,n){if(!N(e))throw new Error("state should be a string");return F(n)?e+G(n):(n=t.$eval(n),F(n)?e+G(n):e)}function u(){for(var e=0;e0)){var n=s(e,t,p);return u(),n}},t.$on("$stateChangeSuccess",u),u()}]}}function L(e){var t=function(t,n){return e.is(t,n)};return t.$stateful=!0,t}function k(e){var t=function(t,n,a){return e.includes(t,n,a)};return t.$stateful=!0,t}var j=t.isDefined,B=t.isFunction,N=t.isString,F=t.isObject,U=t.isArray,H=t.forEach,W=t.extend,V=t.copy,G=t.toJson;t.module("ui.router.util",["ng"]),t.module("ui.router.router",["ui.router.util"]),t.module("ui.router.state",["ui.router.router","ui.router.util"]),t.module("ui.router",["ui.router.state"]),t.module("ui.router.compat",["ui.router"]),v.$inject=["$q","$injector"],t.module("ui.router.util").service("$resolve",v),g.$inject=["$http","$templateCache","$injector"],t.module("ui.router.util").service("$templateFactory",g);var Y;b.prototype.concat=function(e,t){var n={caseInsensitive:Y.caseInsensitive(),strict:Y.strictMode(),squash:Y.defaultSquashPolicy()};return new b(this.sourcePath+e+this.sourceSearch,W(n,t),this)},b.prototype.toString=function(){return this.source},b.prototype.exec=function(e,t){var n=this.regexp.exec(e);if(!n)return null;t=t||{};var a,r,o,s=this.parameters(),i=s.length,u=this.segments.length-1,c={};if(u!==n.length-1)throw new Error("Unbalanced capture group in route '"+this.source+"'");var l,d;for(a=0;aWhen All filter is selected, all staff members with contracts which are active in the selected absence period are displayed.","
People I approve filter displays only staff members who you approve leave for.
","
People without approver filter displays all staff members with contracts which are active in the selected absence period and who do not have any leave approver assigned.
People I approve filter displays only staff members who you approve leave for.
","
People without approver filter displays all staff members with contracts which are active in the selected absence period and who do not have any leave approver assigned.
When All filter is selected, all staff members with contracts which are active in the selected absence period are displayed.
","
People I approve filter displays only staff members who you approve leave for.
","
People without approver filter displays all staff members with contracts which are active in the selected absence period and who do not have any leave approver assigned.
When All filter is selected, all staff members with contracts which are active in the selected absence period are displayed.
","
People I approve filter displays only staff members who you approve leave for.
","
People without approver filter displays all staff members with contracts which are active in the selected absence period and who do not have any leave approver assigned.
When All filter is selected, all staff members with contracts which are active in the selected absence period are displayed.
","
People I approve filter displays only staff members who you approve leave for.
","
People without approver filter displays all staff members with contracts which are active in the selected absence period and who do not have any leave approver assigned.
When All filter is selected, all staff members with contracts which are active in the selected absence period are displayed.
","
People I approve filter displays only staff members who you approve leave for.
","
People without approver filter displays all staff members with contracts which are active in the selected absence period and who do not have any leave approver assigned.
"].join(""))}t.debug("LeaveCalendarAdminController");var l,d;return{init:function(t){return d=t,l=i.init(d),d.filters.userSettings.assignedTo=e.find(d.filtersByAssignee,{type:"me"}),d.showContactDetailsLink=!0,d.showContactName=!0,d.showFilters=!0,d.showAdminFilteringHint=c,u()}}}n.controller("LeaveCalendarAdminController",["$log","$q","Contact","ContactInstance","Contract","notificationService","LeaveCalendarService",a])}),define("leave-absences/shared/controllers/sub-controllers/leave-calendar-manager.controller",["common/lodash","common/moment","leave-absences/shared/modules/controllers"],function(e,t,n){function a(t,n,a,r){function o(){return{loadContacts:function(){return s.loadLookUpAndFilteredContacts()}}}t.debug("LeaveCalendarManagerController");var s,i;return{init:function(t){return i=t,s=r.init(i),i.filters.userSettings.assignedTo=e.find(i.filtersByAssignee,{type:"me"}),i.showContactName=!0,i.showFilters=!0,o()}}}n.controller("LeaveCalendarManagerController",["$log","Contact","ContactInstance","LeaveCalendarService",a])}),define("leave-absences/shared/controllers/sub-controllers/leave-calendar-staff.controller",["common/lodash","common/moment","leave-absences/shared/modules/controllers"],function(e,t,n){function a(e,t,n,a){function r(){return{loadContacts:function(){return s.displaySingleContact?o.loadFilteredContacts():"admin"===s.userPermissionRole?o.loadContactsForAdmin():o.loadLookUpAndFilteredContacts()}}}e.debug("LeaveCalendarStaffController");var o,s;return{init:function(e){return s=e,o=a.init(s),s.filters.userSettings.contacts_with_leaves=!0,s.showTheseContacts=[s.contactId],s.showContactName=!0,s.showFilters=!0,s.displaySingleContact&&(s.showFilters=!1,s.lookupContacts=[{id:s.contactId}]),r()}}}n.controller("LeaveCalendarStaffController",["$log","$q","Contact","LeaveCalendarService",a])}),define("leave-absences/shared/components/leave-calendar.component",["common/lodash","common/moment","leave-absences/shared/modules/components","leave-absences/shared/controllers/sub-controllers/leave-calendar-admin.controller","leave-absences/shared/controllers/sub-controllers/leave-calendar-manager.controller","leave-absences/shared/controllers/sub-controllers/leave-calendar-staff.controller"],function(e,t,n){function a(n,a,r,o,s,i,u,c,l,d){function m(){H.supportData.absenceTypes.push({id:"",title:"Leave",color:"#4D4D68",calculation_unit:e.chain(H.supportData.calculationUnits).find({name:"days"}).get("value").value()})}function f(){for(var e=[],n=t(H.selectedPeriod.start_date).clone().startOf("month"),a=t(H.selectedPeriod.end_date).clone().endOf("month");n.isBefore(a);)e.push(S(n)),n.add(1,"month");H.months=e}function p(){return e.includes(["admin","manager"],H.userPermissionRole)}function h(e){return e.format("YYYY-MM")}function v(){o.$on("LeaveCalendar::updateFiltersByAbsenceType",function(e,t){H.supportData.absenceTypesToFilterBy=t,O(!0)})}function g(){o.$new().$watch(function(){return H.selectedMonthIndex},function(e,t){null!==t&&e!==t&&(B(),k(),O())})}function b(e){H.injectMonth=!0,F("injected").then(function(){O(e)}).then(function(){H.loading.calendar=!1})}function y(){N=n("LeaveCalendar"+e.capitalize(U)+"Controller").init(H)}function _(e){return e.current?"Current Period ("+e.title+")":e.title}function q(){return i.all().then(function(t){H.absencePeriods=e.sortBy(t,"start_date"),H.selectedPeriod=e.find(H.absencePeriods,function(e){return!!e.current})}).then(f).then(L)}function $(){return u.all().then(u.loadCalculationUnits)}function C(){return l.valuesOf(["hrleaveandabsences_absence_type_calculation_unit","hrleaveandabsences_leave_request_day_type","hrleaveandabsences_leave_request_status","hrleaveandabsences_toil_amounts"])}function A(){return N.loadContacts().then(function(e){H.contacts=e})}function w(){return l.valuesOf(["hrjc_region","hrjc_location","hrjc_level_type","hrjc_department"]).then(function(e){H.filters.optionValues.regions=e.hrjc_region,H.filters.optionValues.locations=e.hrjc_location,H.filters.optionValues.levelTypes=e.hrjc_level_type,H.filters.optionValues.departments=e.hrjc_department})}function T(){return c.all()}function P(){return a.all([$(),T(),C()]).then(function(t){H.supportData.absenceTypes=t[0],H.supportData.publicHolidays=t[1],H.supportData.calculationUnits=t[2].hrleaveandabsences_absence_type_calculation_unit,H.supportData.dayTypes=t[2].hrleaveandabsences_leave_request_day_type,H.supportData.leaveRequestStatuses=t[2].hrleaveandabsences_leave_request_status,H.supportData.toilAmounts=e.indexBy(t[2].hrleaveandabsences_toil_amounts,"value")})}function D(){var e=a.resolve();return H.injectMonth&&(e=F("destroyed"),H.injectMonth=!1),e}function S(e){return{index:h(e),month:e.month(),year:e.year(),name:e.format("MMMM"),moment:t().year(e.year()).month(e.month())}}function R(){var t=H.selectedPeriod.id;H.selectedPeriod=e.find(H.absencePeriods,function(e){return!!e.current}),t!==H.selectedPeriod.id&&f(),L(),k(),I("month")}function x(e){var n="previous"===e?"subtract":"add";B(t(H.selectedMonth.moment)[n](1,"month")),k(),I("month")}function I(t){t=e.includes(["contacts","period","month"],t)?t:"period",a.resolve().then(D).then("period"===t&&f).then("period"===t&&E).then("contacts"===t&&A).then("month"===t&&k).then(function(){b("contacts"===t)})}function O(e){o.$emit("LeaveCalendar::showMonth",!!e)}function L(){B(t())}function E(){B(H.months[0].moment)}function M(n){var a="previous"===n?"first":"last",r=e[a](H.months),o=t().year(r.year).month(r.month);H.monthPaginatorsAvailability[n]=!H.selectedMonth.moment.isSame(o,"month")}function k(){M("previous"),M("next")}function B(t){t&&(H.selectedMonthIndex=h(t)),H.selectedMonth=e.find(H.months,{index:H.selectedMonthIndex})}function j(){return a.all([d(s.permissions.admin.administer),d(s.permissions.ssp.manage)]).then(function(e){H.userPermissionRole=e[0]?"admin":e[1]?"manager":"staff",U=H.roleOverride?H.roleOverride:H.userPermissionRole})}function F(t){return a(function(n){
+var a=o.$on("LeaveCalendar::month"+e.capitalize(t),function(){a(),n()})})}r.debug("Component: leave-calendar");var N,U,H=this;H.absencePeriods=[],H.contacts=[],H.contactIdsToReduceTo=null,H.injectMonth=!1,H.months=[],H.selectedMonth={},H.selectedMonthIndex="",H.selectedPeriod=null,H.showContactName=!1,H.showFilters=!1,H.userPermissionRole="staff",H.loading={calendar:!0,page:!0},H.filters={hideOnMobile:!0,optionValues:{},userSettings:{contact:null,contacts_with_leaves:!0,department:null,level_type:null,location:null,region:null},absenceTypes:{}},H.filtersByAssignee=[{type:"me",label:"People I approve"},{type:"unassigned",label:"People without approver"},{type:"all",label:"All"}],H.filters.userSettings.assignedTo=H.filtersByAssignee[2],H.monthPaginatorsAvailability={previous:!0,next:!0},H.supportData={absenceTypesToFilterBy:[]},H.canManageRequests=p,H.labelPeriod=_,H.navigateToCurrentMonth=R,H.paginateMonth=x,H.refresh=I,function(){j().then(g).then(v).then(y).then(D).then(function(){return a.all([q(),A(),P(),H.showFilters?w():e.noop])}).then(function(){m(),b(),k()}).then(function(){H.loading.page=!1})}()}n.component("leaveCalendar",{bindings:{contactId:"<",displaySingleContact:"",roleOverride:"@?"},templateUrl:["shared-settings",function(e){return e.sharedPathTpl+"components/leave-calendar.html"}],controllerAs:"calendar",controller:["$controller","$q","$log","$rootScope","shared-settings","AbsencePeriod","AbsenceType","PublicHoliday","OptionGroup","checkPermissions",a]})}),define("leave-absences/shared/components/leave-calendar-day.component",["common/lodash","common/moment","leave-absences/shared/modules/components"],function(e,t,n){function a(n,a,r){"use strict";function o(e,t){r.openModalByID(t.id)}function s(e){return e.isAM?"AM":e.isPM?"PM":""}function i(e){var n=t(e.from_date).isSame(h.date,"day"),a=t(e.to_date).isSame(h.date,"day");return n?t(e.from_date).format("HH:mm"):a?t(e.to_date).format("HH:mm"):""}function u(t,n){var a=e.find(h.supportData.absenceTypes,{id:t.type_id});h.contactData.leaveRequestsAttributes[t.id].absenceTypeTitle=a.title}function c(t,n){var a,r;if(!t.type_id)return void(n.unit=t.from_date_type?"days":"hours");a=e.find(h.supportData.absenceTypes,{id:t.type_id}),r=e.find(h.supportData.calculationUnits,{value:a.calculation_unit}),n.unit=r.name}function l(e,n){n.from_date=t(e.from_date).toDate(),n.to_date=t(e.to_date).toDate()}function d(t,n){"days"===n.unit&&(n.from_date_type=e.find(h.supportData.dayTypes,{value:t.from_date_type}).label,n.to_date_type=e.find(h.supportData.dayTypes,{value:t.to_date_type}).label)}function m(e,t){var n="";n=t.isAccruedTOIL?"AT":"days"===t.unit?s(t):i(e),t.label=n}function f(e){var t=h.contactData.leaveRequestsAttributes[e.id];[c,d,m,l,u].forEach(function(n){n.call(this,e,t)})}function p(){a.$watch("day.contactData.leaveRequests",function(){h.contactData&&h.contactData.leaveRequests&&h.contactData.leaveRequests.forEach(f)},!0)}n.debug("Component: leave-calendar-day");var h=this;h.openLeavePopup=o,function(){p()}()}n.component("leaveCalendarDay",{bindings:{contactData:"<",date:"<",supportData:"<"},templateUrl:["shared-settings",function(e){return e.sharedPathTpl+"components/leave-calendar-day.html"}],controllerAs:"day",controller:a}),a.$inject=["$log","$scope","LeavePopup"]}),define("leave-absences/shared/components/leave-calendar-legend.component",["common/lodash","leave-absences/shared/modules/components"],function(e,t){function n(t,n){function a(e){return!!e.id}function r(t){var n=e.includes(c.absenceTypesToFilterBy,t);return!c.absenceTypesToFilterBy.length||n}function o(e){return{backgroundColor:e.color}}function s(){n.$new().$watch(function(){return c.absenceTypesToFilterBy},function(e,t){e!==t&&n.$emit("LeaveCalendar::updateFiltersByAbsenceType",c.absenceTypesToFilterBy)},!0)}function i(){c.absenceTypesToFilterBy=[]}function u(t){e.includes(c.absenceTypesToFilterBy,t)?e.remove(c.absenceTypesToFilterBy,function(e){return t===e}):c.absenceTypesToFilterBy.push(t)}t.debug("Component: leave-calendar-legend");var c=this;c.absenceTypesToFilterBy=[],c.legendCollapsed=!1,c.nonWorkingDayTypes=[{label:"Weekend",cssClassSuffix:"weekend"},{label:"Public Holiday",cssClassSuffix:"public-holiday"},{label:"Non Working Day",cssClassSuffix:"non-working-day"}],c.otherBadges=[{label:"AM",description:"AM Only"},{label:"PM",description:"PM Only"},{label:"HH:MM",description:"Time",cssClassSuffix:"hours"},{label:"",description:"Requested",cssClassSuffix:"requested"},{label:"AT",description:"Accrued TOIL"}],c.checkIfAbsenceTypeIdIsDefined=a,c.checkIfAbsenceTypeIsSelectedForFiltering=r,c.getAbsenceTypeStyle=o,c.resetFilteringByAbsenceTypes=i,c.toggleFilteringByAbsenceType=u,function(){s()}()}t.component("leaveCalendarLegend",{bindings:{absenceTypes:"<"},templateUrl:["shared-settings",function(e){return e.sharedPathTpl+"components/leave-calendar-legend.html"}],controllerAs:"legend",controller:n}),n.$inject=["$log","$rootScope"]}),define("leave-absences/shared/components/leave-calendar-month.component",["common/lodash","common/moment","leave-absences/shared/modules/components","common/services/pub-sub"],function(e,t,n){function a(n,a,r,o,s,i,u){function c(e){C([e]),W(e)}function l(e){return{index:e.format("YYYY-MM"),month:e.month(),year:e.year(),name:e.format("MMMM"),loading:!0,days:d(e)}}function d(n){var a=t(),r=n.clone().startOf("month");return e.map(e.times(n.daysInMonth()),function(){var e={date:r.format("YYYY-MM-DD"),name:r.format("ddd"),index:r.format("D"),current:a.isSame(r,"day"),enabled:r.isSameOrAfter(X.period.start_date)&&r.isSameOrBefore(X.period.end_date),contactsData:{}};return r.add(1,"day"),e})}function m(e){return G[e]}function f(){return X.showOnlyWithLeaveRequests?X.contacts.filter(function(t){var n=Object.keys(z[t.id]||{}).length,a=e.includes(X.showTheseContacts,t.id);return n||a}):X.contacts}function p(e){return t(e,u.serverDateFormat)}function h(e){var t=g(e);t&&(M(t),W(t))}function v(t){var n={request_type:"toil"};return e.some(t,n)&&!e.every(t,n)?e.filter(t,n):t}function g(t){var n;return e.find(z[t.contact_id],function(a){return n=e.find(a,function(e){return+e.id==+t.id})}),n}function b(){X.month.days.forEach(function(e){e.contactsData={}})}function y(e){return CRM.url("civicrm/contact/view",{cid:e})}function _(t){return e.filter(t,function(e){return!!e.type_id})}function q(e,n){var a={};return n.forEach(function(n){a[n.id]={styles:U(n),isAccruedTOIL:T(n,"toil"),isRequested:D(n),isAM:w("half_day_am",n,e.date),isPM:w("half_day_pm",n,e.date),isSingleDay:t(n.from_date).isSame(n.to_date,"day")}}),a}function $(){X.supportData.dayTypes=e.indexBy(X.supportData.dayTypes,"name"),X.supportData.leaveRequestStatuses=e.indexBy(X.supportData.leaveRequestStatuses,"value"),X.supportData.publicHolidays=e.transform(X.supportData.publicHolidays,function(e,t){e[p(t.date).valueOf()]=t},{})}function C(e){return e.forEach(function(e){var t=S(e);z[e.contact_id]=z[e.contact_id]||{},t.forEach(function(t){z[e.contact_id][t.date]||(z[e.contact_id][t.date]=[]),z[e.contact_id][t.date].push(e)})}),a.resolve()}function A(){Y.push(r.$on("LeaveCalendar::showMonth",F)),Y.push(i.subscribe("LeaveRequest::new",c)),Y.push(i.subscribe("LeaveRequest::edit",H)),Y.push(i.subscribe("LeaveRequest::updatedByManager",H)),Y.push(i.subscribe("LeaveRequest::delete",h)),Y.push(i.subscribe("LeaveRequest::statusUpdate",function(e){"delete"===e.status?h(e.leaveRequest):H(e.leaveRequest)}))}function w(e,n,a){var r=X.supportData.dayTypes[e];return t(a).isSame(n.from_date,"day")?r.value===n.from_date_type:t(a).isSame(n.to_date,"day")?r.value===n.to_date_type:void 0}function T(e,t){return e.request_type===t}function P(e){return!!X.supportData.publicHolidays[p(e).valueOf()]}function D(t){var n=X.supportData.leaveRequestStatuses[t.status_id].name;return e.contains([u.statusNames.awaitingApproval,u.statusNames.moreInformationRequired],n)}function S(n){for(var a=[],r=t(n.from_date).clone(),o=t(n.to_date);r.isSameOrBefore(o);)r.month()===X.month.month&&r.year()===X.month.year&&a.push(e.find(X.month.days,function(e){return e.date===r.format("YYYY-MM-DD")})),r.add(1,"day");return a}function R(t){var n=e.find(X.supportData.leaveRequestStatuses,function(e){return e.name===t});return n?n.value:null}function x(){return[R(u.statusNames.approved),R(u.statusNames.adminApproved),R(u.statusNames.awaitingApproval),R(u.statusNames.moreInformationRequired)]}function I(){return X.month.loading=!0,a.all([L(),O()]).then(E).then(j).then(function(){V=!0,X.month.loading=!1})}function O(){var t=X.supportData.absenceTypesToFilterBy.length>0,n={from_date:{to:X.month.days[X.month.days.length-1].date+" 23:59:59"},to_date:{from:X.month.days[0].date+" 00:00:00"},status_id:{IN:x()},contact_id:{IN:X.contacts.map(function(e){return e.id})},type_id:{IN:t?X.supportData.absenceTypesToFilterBy:e.pluck(X.supportData.absenceTypes,"id")}};return b(),s.all(n,null,null,null,!1).then(function(e){return z={},t&&(e.list=_(e.list)),C(e.list)})}function L(){var t=X.month.days[0].date,n=X.month.days[X.month.days.length-1].date;return o.get(X.contacts.map(function(e){return e.id}),t,n).then(function(t){G=e.indexBy(t,"contact_id")})}function E(){return X.contactIdsToReduceTo&&(X.contacts=X.contacts.filter(function(t){return e.includes(X.contactIdsToReduceTo,t.contact_id)||e.find(z,function(e){return e.contact_id===t.contact_id})})),a.resolve()}function M(t){var n=S(t);z[t.contact_id]=z[t.contact_id]||{},n.forEach(function(n){e.remove(z[t.contact_id][n.date],function(e){return e.id===t.id})})}function k(){r.$emit("LeaveCalendar::monthDestroyed"),Y.map(function(e){e.remove?e.remove():e()})}function B(t,n,r){var o,s;return t.contactsData[n]=t.contactsData[n]||{},s=m(n),o=!0===r?a.resolve():a.all([s.isWeekend(p(t.date)),s.isNonWorkingDay(p(t.date))]).then(function(a){e.assign(t.contactsData[n],{isWeekend:a[0],isNonWorkingDay:a[1],isPublicHoliday:P(t.date)})}),o.then(function(){return z[n]&&z[n][t.date]?z[n][t.date]:[]}).then(function(a){a=N(a),e.assign(t.contactsData[n],{leaveRequests:a,leaveRequestsToShowInCell:v(a),leaveRequestsAttributes:q(t,a)})})}function j(){return a.all(X.month.days.map(function(e){return a.all(X.contacts.map(function(t){return B(e,t.id)}))}))}function F(e,t){X.currentPage=0,X.visible=!0,(t||!V)&&I()}function N(n){return e.sortBy(n,function(e){return+t(e.from_date).format("X")+(w("half_day_pm",e,e.from_date)?1:0)})}function U(t){var n=e.find(X.supportData.absenceTypes,function(e){return e.id===t.type_id});return t.balance_change>0?{borderColor:n.color}:{borderColor:n.color,backgroundColor:n.color}}function H(e){h(e),-1!==x().indexOf(e.status_id)&&c(e)}function W(e){return a.all(S(e).map(function(t){return B(t,e.contact_id,!0)}))}n.debug("Component: leave-calendar-month");var V=!1,Y=[],G={},z={},X=this;X.currentPage=0,X.pageSize=20,X.visible=!1,X.showContactName=!!X.showContactName,X.showOnlyWithLeaveRequests=!!X.showOnlyWithLeaveRequests,X.$onDestroy=k,X.contactsList=f,X.getContactUrl=y,function(){var e=t().month(X.month.month).year(X.month.year);$(),A(),X.month=l(e),r.$emit("LeaveCalendar::monthInjected")}()}n.component("leaveCalendarMonth",{bindings:{showTheseContacts:"<",contacts:"<",contactIdsToReduceTo:"<",month:"<",period:"<",showContactName:"<",showContactDetailsLink:"<",showOnlyWithLeaveRequests:"<",supportData:"<"},templateUrl:["shared-settings",function(e){return e.sharedPathTpl+"components/leave-calendar-month.html"}],controllerAs:"month",controller:["$log","$q","$rootScope","Calendar","LeaveRequest","pubSub","shared-settings",a]})}),define("leave-absences/shared/modules/services",["common/angular","common/modules/services"],function(e){"use strict";return e.module("leave-absences.services",["common.services"])}),define("leave-absences/shared/services/leave-request.service",["common/lodash","leave-absences/shared/modules/services"],function(e,t){"use strict";function n(t,n,a){function r(){var t=n.defer();return a.open(e.defaults(o(),{onConfirm:function(){t.resolve(!0)}})),t.promise}function o(){return{title:"Recalculate Balance Change?",copyCancel:"Cancel",copyConfirm:"Yes",classConfirm:"btn-warning",msg:"The leave balance change has updated since this leave request was created. Do you want to recalculate the balance change?"}}return t.debug("LeaveRequest"),{getBalanceChangeRecalculationPromptOptions:o,promptBalanceChangeRecalculation:r}}t.factory("LeaveRequestService",n),n.$inject=["$log","$q","dialog"]}),define("leave-absences/shared/components/leave-request-actions.component",["common/lodash","common/moment","leave-absences/shared/modules/components","common/services/hr-settings","common/services/notification.service","common/services/pub-sub","leave-absences/shared/services/leave-request.service"],function(e,t,n){function a(n,a,r,o,s,i,u,c){function l(t){_=q.leaveRequest.status_id,e.includes(["cancel","reject","delete"],t)||"toil"===q.leaveRequest.request_type?r.open(h(t)):f(t)}function d(){return"admin"===q.role&&"public_holiday"===q.leaveRequest.request_type}function m(n){var a=q.absenceTypes[q.leaveRequest.type_id].allow_request_cancelation;return"admin"===q.role||("manager"===q.role?e.includes([$.awaitingApproval,$.moreInformationRequired],n):"3"===a?t().isBefore(q.leaveRequest.from_date):"2"===a)}function f(t){r.open({title:"Verifying balance...",loading:!0,optionsPromise:function(){return q.leaveRequest.checkIfBalanceChangeNeedsRecalculation().then(function(n){return n?e.assign(s.getBalanceChangeRecalculationPromptOptions(),{onCloseAfterConfirm:function(){o.openModal(q.leaveRequest,q.leaveRequest.request_type,q.leaveRequest.contact_id,"my-leave"===a.section,!0)}}):h(t)})}})}function p(){e.isArray(q.leaveRequestStatuses)&&(q.leaveRequestStatuses=e.indexBy(q.leaveRequestStatuses,"value")),e.isArray(q.absenceTypes)&&(q.absenceTypes=e.indexBy(q.absenceTypes,"id"))}function h(e){var t=C[e].dialog;return{title:"Confirm "+t.title+"?",copyCancel:"Cancel",copyConfirm:t.btnLabel,classConfirm:"btn-"+t.btnClass,msg:t.msg,onConfirm:function(){return q.leaveRequest[e]().then(function(){g(e)}).catch(function(e){c.error("Error:",e)})}}}function v(e,t,n,a){e.stopPropagation(),o.openModal(t,n,a)}function g(t){var n=e.find(q.leaveRequestStatuses,function(e){return e.name===u.statusNames.awaitingApproval}).value;_===n&&i.publish("ManagerBadge:: Update Count"),i.publish("LeaveRequest::statusUpdate",{status:t,leaveRequest:q.leaveRequest})}function b(){var t=q.leaveRequestStatuses[q.leaveRequest.status_id].name,n=e.compact(e.map(C,function(n,a){return e.includes(n.allowedStatuses,t)?a:null}));!m(t)&&e.pull(n,"cancel"),"admin"!==q.role&&e.pull(n,"delete"),"staff"===q.role&&e.pull(n,"approve","reject"),"staff"!==q.role&&y(n),d()&&n.push("delete"),q.allowedActions=e.map(n,function(e){return{key:e,label:C[e].label,isDirectAction:C[e].isDirectAction}})}function y(t){e.each(t,function(n,a){"edit"===n&&(t[a]="respond"),e.includes(["respond","view"],n)&&(t[a]="edit")})}n.debug("Component: leave-request-action-dropdown");var _,q=this,$=u.statusNames,C={edit:{label:"Edit",allowedStatuses:[$.awaitingApproval]},respond:{label:"Respond",allowedStatuses:[$.moreInformationRequired]},view:{label:"View",allowedStatuses:[$.approved,$.rejected,$.cancelled]},approve:{label:"Approve",isDirectAction:!0,allowedStatuses:[$.awaitingApproval],dialog:{title:"Approval",btnClass:"success",btnLabel:"Approve",msg:"Please confirm approval"}},reject:{label:"Reject",isDirectAction:!0,allowedStatuses:[$.awaitingApproval],dialog:{title:"Rejection",btnClass:"warning",btnLabel:"Reject",msg:"Please confirm rejection"}},cancel:{label:"Cancel",isDirectAction:!0,allowedStatuses:[$.awaitingApproval,$.approved,$.rejected,$.moreInformationRequired],dialog:{title:"Cancellation",btnClass:"danger",btnLabel:"Confirm",msg:"Please confirm cancellation"}},delete:{label:"Delete",isDirectAction:!0,allowedStatuses:[$.awaitingApproval,$.moreInformationRequired,$.approved,$.rejected,$.cancelled],dialog:{title:"Deletion",btnClass:"danger",btnLabel:"Confirm",msg:"This cannot be undone"}}};q.allowedActions=[],q.action=l,q.openLeavePopup=v,function(){p(),b()}()}n.component("leaveRequestActions",{bindings:{leaveRequest:"<",leaveRequestStatuses:"<",absenceTypes:"<",role:"<"},templateUrl:["shared-settings",function(e){return e.sharedPathTpl+"components/leave-request-actions.html"}],controllerAs:"actions",controller:a}),a.$inject=["$log","$rootScope","dialog","LeavePopup","LeaveRequestService","pubSub","shared-settings","notificationService"]}),define("leave-absences/shared/components/leave-request-popup-comments-tab.component",["common/lodash","common/moment","leave-absences/shared/modules/components","common/services/hr-settings","common/models/session.model"],function(e,t,n){function a(n,a,r,o,s,i,u){function c(){q.request.comments.push({contact_id:_,leave_request_id:q.request.id,text:q.comment.text}),q.comment.text=""}function l(){return q.comment.text.length>0}function d(e){return!e.comment_id||q.canManage}function m(e){return t.utc(e,s.serverDateTimeFormat).local().format(o.DATE_FORMAT.toUpperCase()+" HH:mm")}function f(){return q.request.comments.filter(function(e){return!e.toBeDeleted})}function p(e){return e===_?"Me":q.comment.contacts[e]?q.comment.contacts[e].display_name:void 0}function h(e){return q.mode===e}function v(){q.comment.text.length&&q.addComment()}function g(){var t=e.indexBy(q.request.comments,"contact_id"),n=Object.keys(t);return i.all({id:{IN:n}},{page:1,size:0}).then(function(t){q.comment.contacts=e.indexBy(t.list,"contact_id")})}function b(){return q.request.loadComments().then(function(){a.$broadcast("LeaveRequestPopup::requestObjectUpdated"),q.request.comments.length&&g()})}function y(){return q.loading.component=!0,u.get().then(function(e){_=e.contactId}).then(function(){q.loading.component=!1})}n.debug("Component: leave-request-popup-comments-tab");var _=null,q=this;q.loading={component:!0},q.comment={text:"",contacts:{}},q.addComment=c,q.canRemoveComment=d,q.canSubmit=l,q.formatDateTime=m,q.getActiveComments=f,q.getCommentorName=p,q.isMode=h,q.onBeforeSubmit=v,function(){r.$emit("LeaveRequestPopup::addTab",q),b(),y()}()}n.component("leaveRequestPopupCommentsTab",{bindings:{canManage:"<",mode:"<",request:"<"},templateUrl:["shared-settings",function(e){return e.sharedPathTpl+"components/leave-request-popup/leave-request-popup-comments-tab.html"}],controllerAs:"commentsCtrl",controller:["$log","$rootScope","$scope","HR_settings","shared-settings","Contact","Session",a]})}),define("leave-absences/shared/components/leave-request-popup-details-tab.component",["common/lodash","common/moment","leave-absences/shared/modules/components","common/models/session.model","leave-absences/shared/models/calendar.model"],function(e,t,n){function a(n,a,r,o,s,i,u,c,l,d,m){function f(){te.balance.closing=te.balance.opening+te.balance.change.amount}function p(e){return t(e,u.serverDateFormat).toDate()}function h(e){return t(e).format(u.serverDateFormat)}function v(e){var t;return s.resolve().then(function(){N(e),"from"===e&&F(),x("hours")&&(y(e),"from"!==e||te.uiOptions.multipleDays||y("to")),te.loading[e+"DayTypes"]=!0}).then(function(){return q(e)}).then(function(e){if(t=e.id!==te.period.id,t&&(te.period=e),!te.period.id)return s.reject("Please change date as it is not in any absence period")}).then(H).then(function(){if(t)return E()}).then(function(){return te.onDateChangeExtended&&te.onDateChangeExtended(e)}).then(function(){if(G(),!t)return k();r.$broadcast("LeaveRequestPopup::absencePeriodChanged")}).catch(w).finally(_)}function g(){return G(),k()}function b(){return te.uiOptions.toDate=null,N("to"),s.resolve().then(G).then(te.setDaysSelectionModeExtended).then(_).then(!te.uiOptions.multipleDays&&k)}function y(e){te.uiOptions.times[e].loading=!0}function _(){["from","to"].forEach(function(e){te.loading[e+"DayTypes"]=!1,x("hours")&&(te.uiOptions.times[e].loading=!1)})}function q(n){var a=t(te.uiOptions[n+"Date"]).format(te.uiOptions.userDateFormat.toUpperCase());return e.find(te.absencePeriods,function(e){return e.isInPeriod(a)})||{}}function $(){return te.request?te.request.request_type:te.leaveType||null}function C(e){return t().set({hours:e.split(":")[0],minutes:e.split(":")[1]})}function A(){return z(),te.loading.balanceChange=!0,te.request.getBalanceChangeBreakdown().then(U).catch(w)}function w(t){r.$broadcast("LeaveRequestPopup::handleError",e.isArray(t)?t:[t]),te.loading.fromDayTypes=!1,te.loading.balanceChange=!1,te.loading.tab=!1,te.loading.toDayTypes=!1}function T(){return te.isMode("create")||te.forceRecalculateBalanceChange?k():A()}function P(){var e=te.request.attributes();return te.uiOptions.fromDate=p(te.request.from_date),te.request.to_date=e.to_date,te.request.to_date_type=e.to_date_type,te.uiOptions.toDate=p(te.request.to_date),te.initDayTypesExtended?te.initDayTypesExtended():s.resolve()}function D(){ee.push(r.$on("LeaveRequestPopup::absenceTypeChanged",function(){J()}),r.$on("LeaveRequestPopup::absencePeriodBalancesUpdated",function(e,t){K(t)}),r.$on("LeaveRequestPopup::recalculateBalanceChange",k))}function S(){te.isMode("edit")&&(te.isLeaveStatus(u.statusNames.approved)||te.isLeaveStatus(u.statusNames.adminApproved))&&(Z={absenceTypeId:te.request.type_id,value:te.selectedAbsenceType.remainder-te.request.balance_change})}function R(){r.$watch(function(){return te.uiOptions.times.from.time},function(e,t){e!==t&&(te.uiOptions.multipleDays||(Q(e),G()))})}function x(e){return te.selectedAbsenceType.calculation_unit_name===e}function I(e){return te.request.request_type===e}function O(t){return e.includes(["weekend","non_working_day","public_holiday"],t)}function L(){return l.valuesOf("hrleaveandabsences_leave_request_day_type").then(function(e){te.requestDayTypes=e})}function E(){return c.get(te.request.contact_id,te.period.start_date,te.period.end_date).then(function(e){te.calendar=e})}function M(){var e=(te.pagination.currentPage-1)*te.pagination.numPerPage,t=e+te.pagination.numPerPage;te.pagination.filteredbreakdown=te.balance.change.breakdown.slice(e,t)}function k(){return z(),te.canCalculateChange()?(te.loading.balanceChange=!0,te.request.change_balance=!0,te.calculateBalanceChange().then(U).catch(w).finally(function(){te.loading.balanceChange=!1})):s.resolve()}function B(){te.pagination.totalItems=te.balance.change.breakdown.length,te.pagination.filteredbreakdown=te.balance.change.breakdown,te.pagination.pageChanged()}function j(){return h(te.request.from_date)===h(te.request.to_date)}function F(){te.uiOptions.toDate&&te.uiOptions.fromDate&&te.uiOptions.multipleDays&&t(te.uiOptions.toDate).isSameOrBefore(te.uiOptions.fromDate)&&(te.uiOptions.toDate=null,N("to"))}function N(e){te.resetUIInputsExtended&&te.resetUIInputsExtended(e),G(),z()}function U(e){te.balance.change=e,te.request.balance_change=e.amount,f(),B(),te.loading.balanceChange=!1}function H(){var e,n,a;te.uiOptions.fromDate?(e=t(te.uiOptions.fromDate).add(1,"day").toDate(),a=e,n=e):(a=p(te.period.start_date),n=te.uiOptions.date.to.options.minDate),te.uiOptions.date.to.options.initDate=n,te.uiOptions.date.to.options.minDate=a,te.uiOptions.date.to.options.maxDate=p(te.period.end_date)}function W(){return!te.isMode("create")&&j()||te.isMode("create")&&(I("sickness")||x("hours"))?te.uiOptions.multipleDays=!1:te.uiOptions.multipleDays=!0,s.resolve().then(te.setDaysSelectionModeExtended)}function V(){Z&&Z.absenceTypeId===te.selectedAbsenceType.id?te.balance.opening=Z.value||0:te.balance.opening=te.selectedAbsenceType.remainder}function Y(){te.selectedAbsenceType=e.find(te.absenceTypes,function(e){return e.id===te.request.type_id})}function G(){var e=te.uiOptions,t=e.times;te.request.from_date=e.fromDate?h(e.fromDate):null,te.request.to_date=e.toDate?h(e.toDate):null,!e.multipleDays&&e.fromDate&&(te.request.to_date=te.request.from_date,te.request.to_date_type=te.request.from_date_type),(x("hours")||I("toil"))&&(te.request.from_date=te.request.from_date&&t.from.time?te.request.from_date+" "+t.from.time:null,te.request.to_date=te.request.to_date&&t.to.time?te.request.to_date+" "+t.to.time:null)}function z(){te.uiOptions.showBalance=te.canCalculateChange()}function X(){e.forEach(ee,function(e){e()})}function J(){var e,t=te.selectedAbsenceType.calculation_unit_name;return Y(),e=t!==te.selectedAbsenceType.calculation_unit_name,V(),s.resolve().then(e&&W).then(e&&te.onAbsenceTypeUpdateExtended).then(e&&G).then(_).then(k)}function K(e){return te.absenceTypes=e,Y(),t(te.uiOptions.toDate).isAfter(te.period.end_date)&&(te.uiOptions.toDate=void 0,N("to")),V(),k()}function Q(e){var t=C(e).add(te.uiOptions.time_interval,"minutes");t.isAfter(C(te.uiOptions.times.to.max))||(te.uiOptions.times.to.min=t.format("HH:mm"),t.isAfter(C(te.uiOptions.times.to.time))&&(te.uiOptions.times.to.time=""))}a.debug("Component: leave-request-popup-details-tab");var Z=null,ee=[],te=this;te.canManage=!1,te.calendar={},te.errors=[],te.isRequired=!0,te.requestDayTypes=[],te.statusNames=u.statusNames,te.loading={tab:!1,balanceChange:!1,fromDayTypes:!1,toDayTypes:!1},te.pagination={currentPage:1,filteredbreakdown:te.balance.change.breakdown,numPerPage:7,totalItems:te.balance.change.breakdown.length,pageChanged:M},te.uiOptions={isChangeExpanded:!1,multipleDays:!0,userDateFormat:i.DATE_FORMAT,showBalance:!1,date:{from:{show:!1,options:{startingDay:1,showWeeks:!1}},to:{show:!1,options:{minDate:null,maxDate:null,startingDay:1,showWeeks:!1}},expiry:{show:!1,options:{minDate:null,maxDate:null,startingDay:1,showWeeks:!1}}},times:{from:{amount:0,amountExpanded:!1,loading:!1,max:"00:00",maxAmount:0,min:"00:00",time:""},to:{amount:0,amountExpanded:!1,loading:!1,max:"00:00",maxAmount:0,min:"00:00",time:""}},time_interval:15},te.convertDateFormatFromServer=p,te.convertDateToServerFormat=h,te.dateChangeHandler=v,te.dateTypeChangeHandler=g,te.daysSelectionModeChangeHandler=b,te.disableAndShowLoadingTimeInput=y,te.getMomentDateWithGivenTime=C,te.handleError=w,te.isCalculationUnit=x,te.isLeaveType=I,te.isNotWorkingDay=O,te.performBalanceChangeCalculation=k,te.setRequestDateTimesAndDateTypes=G,te.updateEndTimeInputMinTime=Q,te.$onDestroy=X,function(){n("RequestModalDetails"+e.capitalize($(te.leaveType,te.request))+"Controller",{detailsController:te}),te.canManage=te.isRole("manager")||te.isRole("admin"),te.loading.tab=!0,o.$emit("LeaveRequestPopup::addTab",te),D(),te.initChildController().then(function(){return s.all([E(),L()])}).then(!te.isMode("create")&&P).then(W).then(function(){if(!te.isMode("create"))return s.resolve().then(te.initTimesExtended).then(G)}).then(!te.isMode("create")&&H).then(S).then(V).then(T).then(R).then(!te.isMode("view")&&te.initWatchersExtended).catch(w).finally(function(){te.loading.tab=!1})}()}n.component("leaveRequestPopupDetailsTab",{bindings:{absencePeriods:"<",absenceTypes:"<",balance:"=",request:"<",isLeaveStatus:"<",leaveType:"<",isMode:"<",isSelfRecord:"<",period:"=",isRole:"<",selectedAbsenceType:"=",forceRecalculateBalanceChange:"<"},templateUrl:["shared-settings",function(e){return e.sharedPathTpl+"components/leave-request-popup/leave-request-popup-details-tab.html"}],controllerAs:"detailsTab",controller:a}),a.$inject=["$controller","$log","$rootScope","$scope","$q","HR_settings","shared-settings","Calendar","OptionGroup","LeaveRequest","$timeout"]}),define("leave-absences/shared/components/leave-request-popup-files-tab",["common/lodash","common/moment","leave-absences/shared/modules/components","common/services/file-upload","common/services/file-mime-types","common/services/hr-settings"],function(e,t,n){function a(n,a,r,o,s,i,u,c,l){function d(){return R.fileUploader&&R.fileUploader.queue.length>0}function m(){return R.getFilesAmount()0?P.map(function(e){return e.label}).join(", "):""}function q(){return R.request.loadAttachments().then(function(){r.$broadcast("LeaveRequestPopup::requestObjectUpdated")})}function $(){return a.all(P.map(function(e){return l.getMimeTypeFor(e.label).then(function(t){S[e.label]=t})})).catch(function(){S=null})}function C(){return u.valuesOf("safe_file_extension").then(function(e){P=e})}function A(e){return!e.attachment_id||R.canManage}function w(){e.forEach(D,function(e){e()})}function T(e,t){R.fileUploader.queue&&R.fileUploader.queue.length>0?R.fileUploader.uploadAll({entityID:R.request.id}).then(function(){t()}).catch(t):t()}n.debug("Component: leave-request-popup-files-tab");var P=[],D=[],S={},R=Object.create(this);return R.filesLoaded=!1,R.fileUploader=null,R.today=Date.now(),R.userDateFormatWithTime=s.DATE_FORMAT+" HH:mm",R.userDateFormat=s.DATE_FORMAT,R.$onDestroy=w,R.canRemoveAttachment=A,R.canSubmit=d,R.canUploadMore=m,R.formatDateTime=f,R.getAuthorName=p,R.getFilesAmount=h,R.listFileTypes=_,function(){r.$broadcast("LeaveRequestPopup::childComponent::register"),o.$emit("LeaveRequestPopup::addTab",R),b(),a.all([C(),q()]).then(y).finally(function(){R.filesLoaded=!0})}(),R}n.component("leaveRequestPopupFilesTab",{bindings:{canManage:"<",mode:"<",request:"<"},templateUrl:["shared-settings",function(e){return e.sharedPathTpl+"components/leave-request-popup/leave-request-popup-files-tab.html"}],controllerAs:"filesTab",controller:["$log","$q","$rootScope","$scope","HR_settings","shared-settings","OptionGroup","FileUpload","fileMimeTypes",a]})}),define("leave-absences/shared/components/leave-request-record-actions.component",["leave-absences/shared/modules/components","common/services/hr-settings","common/services/before-hash-query-params.service"],function(e){function t(e,t,n){function a(e,n,a){t.openModal.apply(t,arguments)}e.debug("Component: leave-request-record-actions");var r,o=this;o.leaveRequestOptions=[{type:"leave",icon:"briefcase",label:"Leave"},{type:"sickness",icon:"stethoscope",label:"Sickness"}],o.openLeavePopup=a,function(){r=n.parse(),r.openModal&&a(null,r.openModal,o.selectedContactId)}()}e.component("leaveRequestRecordActions",{bindings:{contactId:"<",selectedContactId:"<"},templateUrl:["shared-settings",function(e){return e.sharedPathTpl+"components/leave-request-record-actions.html"}],controllerAs:"vm",controller:["$log","LeavePopup","beforeHashQueryParams",t]})}),define("leave-absences/shared/components/staff-leave-report.component",["common/lodash","common/moment","leave-absences/shared/modules/components","common/services/pub-sub"],function(e,t,n){function a(t,n,a,r,o,s,i,u,c,l,d,m){function f(e,t){t.data.push(e),t.dataIndex[e.id]=e}function p(){Y.absenceTypes=Y.absenceTypes.map(function(t){var n=e.find(Y.entitlements,function(e){return e.type_id===t.id});return t.entitlement=n?n.value:0,t.remainder=n?n.remainder:{current:0,future:0},t})}function h(e){F(e),Y.sections.other.open&&f(e,Y.sections.other)}function v(){Object.values(Y.sections).forEach(function(e){e.data=[]})}function g(){Y.absenceTypesFiltered=Y.absenceTypes.filter(function(e){return!(0===e.entitlement&&"1"!==e.allow_overuse&&"1"!==e.allow_accruals_request)})}function b(e){var t=V[e.status];t?t(e.leaveRequest):Y.refresh()}function y(t){t.dataIndex=e.indexBy(t.data,"id")}function _(e){return e.current?"Current Period ("+e.title+")":e.title}function q(){return o.all().then(function(t){Y.absencePeriods=e.sortBy(t,"start_date"),Y.selectedPeriod=e.find(Y.absencePeriods,function(e){return!0===e.current})})}function $(){return s.all().then(s.loadCalculationUnits).then(function(t){
+Y.absenceTypes=t,Y.absenceTypesIndexed=e.indexBy(t,"id")})}function C(){return S("approved",{status_id:W(m.statusNames.approved)})}function A(){var t={contact_id:Y.contactId,period_id:Y.selectedPeriod.id};return n.all([u.balanceChangeByAbsenceType(e.assign({},t,{public_holiday:!0})),u.balanceChangeByAbsenceType(e.assign({},t,{expired:!0})),u.balanceChangeByAbsenceType(e.assign({},t,{statuses:{in:[W(m.statusNames.approved)]}})),u.balanceChangeByAbsenceType(e.assign({},t,{statuses:{in:[W(m.statusNames.awaitingApproval),W(m.statusNames.moreInformationRequired)]}}))]).then(function(e){Y.absenceTypes.forEach(function(t){t.balanceChanges={holidays:e[0][t.id],expired:e[1][t.id],approved:e[2][t.id],pending:e[3][t.id]}})})}function w(){return i.all({contact_id:Y.contactId,period_id:Y.selectedPeriod.id},!0).then(function(e){Y.entitlements=e})}function T(){return i.breakdown({contact_id:Y.contactId,period_id:Y.selectedPeriod.id},Y.entitlements).then(function(){return k(Y.entitlements)}).then(function(e){Y.sections.entitlements.data=e})}function P(){return n.all([i.breakdown({contact_id:Y.contactId,period_id:Y.selectedPeriod.id,expired:!0}),D({request_type:"toil",expired:!0})]).then(function(e){return n.all({expiredBalanceChangesFlatten:k(e[0]),expiredTOILS:B(e[1].list)})}).then(function(t){Y.sections.expired.data=t.expiredBalanceChangesFlatten.concat(t.expiredTOILS),Y.sections.expired.data=e.sortBy(Y.sections.expired.data,"expiry_date")})}function D(t){return u.all(e.assign({contact_id:Y.contactId,from_date:{from:Y.selectedPeriod.start_date},to_date:{to:Y.selectedPeriod.end_date},type_id:{IN:Y.absenceTypes.map(function(e){return e.id})}},t),null,"from_date ASC",null,!1)}function S(e,t){return D(t).then(function(t){Y.sections[e].data=t.list})}function R(){return n.all(Object.values(Y.sections).filter(function(e){return e.open}).map(L))}function x(){return S("other",{status_id:{in:[W(m.statusNames.rejected),W(m.statusNames.cancelled)]}})}function I(){return S("pending",{status_id:{in:[W(m.statusNames.awaitingApproval),W(m.statusNames.moreInformationRequired)]}})}function O(){return S("holidays",{public_holiday:!0})}function L(e){return e.loading=!0,e.loadLeaveRequests().then(y.bind(this,e)).then(function(){e.loading=!1})}function E(){return c.valuesOf("hrleaveandabsences_leave_request_status").then(function(t){Y.leaveRequestStatuses=e.indexBy(t,"value")})}function M(){p(),g()}function k(t){return n.resolve().then(function(){return t.map(function(t){var n=e.find(Y.entitlements,function(e){return e.id===t.id});return t.breakdown.map(function(t){return e.assign(e.clone(t),{type_id:n.type_id})})})}).then(function(e){return Array.prototype.concat.apply([],e)})}function B(t){return n.resolve().then(function(){return t.map(function(t){return e.assign({},t,{expiry_date:t.toil_expiry_date,amount:t.toil_to_accrue,type:{label:"Accrued TOIL"}})})})}function j(){l.subscribe("LeaveRequest::new",function(){Y.refresh()}),l.subscribe("LeaveRequest::edit",function(){Y.refresh()}),l.subscribe("LeaveRequest::statusUpdate",b),l.subscribe("LeaveRequest::delete",function(e){F(e)})}function F(t){e.forEach(Y.sections,function(n,a){n.dataIndex[t.id]&&(e.remove(n.data,function(e){return e.id===t.id}),delete n.dataIndex[t.id],"other"!==a&&N(t,a))})}function N(t,n){var a=["future","current"],r=Y.absenceTypesIndexed[t.type_id];"pending"===n&&e.pull(a,"current"),r.balanceChanges[n]-=t.balance_change,a.forEach(function(e){r.remainder[e]-=t.balance_change})}function U(){Y.loading.content=!0,n.all([w(),A()]).then(M).then(function(){Y.loading.content=!1}).then(function(){return n.all([R(),v()])})}function H(e){var t=Y.sections[e];t.open=!t.open,t.open&&!t.data.length&&L(t)}function W(t){return e.find(Y.leaveRequestStatuses,function(e){return e.name===t}).value}t.debug("Component: staff-leave-report");var V={delete:F,cancel:h},Y=this;Y.absencePeriods=[],Y.absenceTypes=[],Y.absenceTypesFiltered=[],Y.absenceTypesIndexed={},Y.dateFormat=d.DATE_FORMAT,Y.leaveRequestStatuses={},Y.selectedPeriod=null,Y.role="absence-tab"===a.section?"admin":"staff",Y.loading={content:!0,page:!0},Y.sections={approved:{open:!1,data:[],dataIndex:{},loading:!1,loadLeaveRequests:C},entitlements:{open:!1,data:[],dataIndex:{},loading:!1,loadLeaveRequests:T},expired:{open:!1,data:[],dataIndex:{},loading:!1,loadLeaveRequests:P},holidays:{open:!1,data:[],dataIndex:{},loading:!1,loadLeaveRequests:O},pending:{open:!1,data:[],dataIndex:{},loading:!1,loadLeaveRequests:I},other:{open:!1,data:[],dataIndex:{},loading:!1,loadLeaveRequests:x}},Y.labelPeriod=_,Y.refresh=U,Y.toggleSection=H,function(){n.all([E(),$(),q()]).then(function(){Y.loading.page=!1}).then(function(){return n.all([w(),A()])}).then(M).then(function(){Y.loading.content=!1}),j()}()}n.component("staffLeaveReport",{bindings:{contactId:"<"},templateUrl:["shared-settings",function(e){return e.sharedPathTpl+"components/staff-leave-report.html"}],controllerAs:"report",controller:["$log","$q","$rootScope","checkPermissions","AbsencePeriod","AbsenceType","Entitlement","LeaveRequest","OptionGroup","pubSub","HR_settings","shared-settings",a]})}),define("leave-absences/shared/controllers/sub-controllers/request-modal-details-leave.controller",["common/lodash","common/moment","leave-absences/shared/modules/controllers"],function(e,t,n){function a(n,a,r,o,s,i,u){function c(){return s.request.calculateBalanceChange(s.selectedAbsenceType.calculation_unit_name)}function l(){var e=s.request,t=!!e.from_date&&!!e.to_date,n=s.selectedAbsenceType.calculation_unit_name;return"days"===n?t=t&&!!e.from_date_type&&!!e.to_date_type:"hours"===n&&(t=t&&!isNaN(+e.from_date_amount)&&!isNaN(+e.to_date_amount)),t}function d(){return s.canCalculateChange()}function m(t,n){var a=s.uiOptions.times[t],r=e.clone(n.time_from),o=e.clone(n.time_to);s.uiOptions.multipleDays||("from"===t&&o&&r&&(o=s.getMomentDateWithGivenTime(o).subtract(s.uiOptions.time_interval,"minutes").format("HH:mm")),"to"===t&&r&&o&&(r=s.getMomentDateWithGivenTime(r).add(s.uiOptions.time_interval,"minutes").format("HH:mm"))),a.min=r||"00:00",a.max=o||"00:00",a.time="to"===t?a.max:a.min}function f(t,n){return t?(t=s.convertDateToServerFormat(t),v(t).then(function(n){return n?s.requestDayTypes.filter(function(e){return"public_holiday"===e.name}):p(t,s.requestDayTypes).then(function(t){return t.length?t:s.requestDayTypes.filter(function(t){return e.includes(["all_day","half_day_am","half_day_pm"],t.name)})})}).then(function(e){return D(n,e),e})):r.reject([])}function p(e,n){return e=t(e),r.all([s.calendar.isNonWorkingDay(e),s.calendar.isWeekend(e)]).then(function(e){return e[0]?"non_working_day":e[1]?"weekend":null}).then(function(e){return e?n.filter(function(t){return t.name===e}):[]})}function h(e,n){return t.duration(n).subtract(t.duration(e)).asHours()}function v(e){return u.all({public_holiday:1,contact_id:s.request.contact_id,from_date:{from:e},to_date:{to:e}}).then(function(e){return!!e.list.length})}function g(){return r.resolve()}function b(){return A(s.uiOptions.fromDate,"from").then(function(){return A(s.uiOptions.toDate,"to")})}function y(e){o.$watch(function(){return s.uiOptions.times[e].amount},function(e,t){s.isCalculationUnit("days")||+e==+t||(s.isRole("staff")&&(s.request.change_balance=!0),x(),s.performBalanceChangeCalculation())})}function _(e){var n=t(s.request[e+"_date"]).format("HH:mm"),a=s.uiOptions.times[e];(h(a.min,n)<=0||h(a.max,n)>=0)&&(n="from"===e?a.min:a.max),s.uiOptions.times[e].time=n}function q(){var e=s.uiOptions.multipleDays?["from","to"]:["from"],t=s.uiOptions.times;if(s.isCalculationUnit("hours"))return r.all(e.map(T)).then(function(){["from","to"].forEach(function(e){_(e),R(e),t[e].amount=Math.min(s.request[e+"_date_amount"],t[e].maxAmount).toString()}),s.uiOptions.multipleDays||s.updateEndTimeInputMinTime(s.uiOptions.times.from.time)}).then(x)}function $(){["from","to"].forEach(function(e){y(e),C(e)})}function C(e){o.$watch(function(){return s.uiOptions.times[e].time},function(t,n){s.isCalculationUnit("days")||t===n||(s.setRequestDateTimesAndDateTypes(),t&&R(e,!0))})}function A(e,t){return f(e,t).then(function(){s.loading[t+"DayTypes"]=!1})}function w(e){return A(s.uiOptions[e+"Date"],e).then(function(){if(s.isCalculationUnit("hours"))return T(e).then(function(){R(e,!0)})})}function T(e){var t=s.uiOptions[e+"Date"],n=!s.uiOptions.multipleDays;return t?s.request.getWorkDayForDate(s.convertDateToServerFormat(t)).then(function(t){O[e]=t,m(e,t),n&&"from"===e&&m("to",t)}).catch(function(t){return O[e]={},s.handleError(t)}).finally(function(){s.uiOptions.times[e].loading=!1,n&&(s.uiOptions.times.to.loading=!1)}):r.resolve()}function P(t){var n=s.uiOptions.times[t];s["request"+e.startCase(t)+"DayTypes"]=[],n.time="",n.min="00:00",n.max="00:00",n.amount="0",n.maxAmount="0"}function D(t,n){var a="request"+e.startCase(t)+"DayTypes";s[a]=n,s.isMode("create")&&(s.request[t+"_date_type"]=s[a][0].value)}function S(){return s.isCalculationUnit("hours")&&s.uiOptions.fromDate?(s.disableAndShowLoadingTimeInput("from"),!s.uiOptions.multipleDays&&s.disableAndShowLoadingTimeInput("to"),T("from").then(function(){R("from",!0)})):r.resolve()}function R(e,t){var n=s.uiOptions,a=n.multipleDays?e:"from",r=n.times[a],o=n.multipleDays&&"to"===a?r.min:n.times.from.time,i=n.multipleDays&&"from"===a?r.max:n.times.to.time,u=O[a].number_of_hours?h(o,i).toString():"0";r.maxAmount=u,t&&(r.amount=r.maxAmount)}function x(){var e=s.uiOptions.times;s.request.from_date_amount=isNaN(+e.from.amount)?null:e.from.amount,s.request.to_date_amount=isNaN(+e.to.amount)?null:e.to.amount}function I(){return s.isCalculationUnit("hours")&&s.uiOptions.fromDate?T("from").then(function(){R("from",!0)}):r.resolve()}var O={};a.debug("RequestModalDetailsLeaveController"),s.calculateBalanceChange=c,s.canCalculateChange=l,s.canSubmit=d,s.initChildController=g,s.initDayTypesExtended=b,s.initTimesExtended=q,s.initWatchersExtended=$,s.onAbsenceTypeUpdateExtended=I,s.onDateChangeExtended=w,s.resetUIInputsExtended=P,s.setDaysSelectionModeExtended=S}n.controller("RequestModalDetailsLeaveController",a),a.$inject=["$controller","$log","$q","$rootScope","detailsController","PublicHoliday","LeaveRequest"]}),define("leave-absences/shared/controllers/sub-controllers/request-modal-details-sickness.controller",["common/lodash","leave-absences/shared/modules/controllers"],function(e,t){function n(t,n,a,r,o,s,i){function u(){return!(!i.canCalculateChange()||!i.request.sickness_reason)}function c(){return a.all([m(),h(),f(!0)])}function l(t){var n=i.request.getDocumentArray();return!!e.find(n,function(e){return e===t})}function d(t){return!!e.find(i.sicknessDocumentTypes,function(e){return e.value===t})}function m(){return s.valuesOf("hrleaveandabsences_leave_request_required_document").then(function(e){i.sicknessDocumentTypes=e})}function f(t){return s.valuesOf("hrleaveandabsences_sickness_reason",t).then(function(t){i.sicknessReasons=e.indexBy(t,"name")})}function p(){o.loadForm("/civicrm/admin/options/hrleaveandabsences_sickness_reason?reset=1").on("crmUnload",function(){f(!1)})}function h(){i.showSicknessOptionsEditorIcon=e.includes(["admin-dashboard","absence-tab"],r.section)}n.debug("RequestModalDetailsSicknessController"),t("RequestModalDetailsLeaveController",{detailsController:i}),i.canSubmit=u,i.initChildController=c,i.isChecked=l,i.isDocumentInRequest=d,i.openSicknessReasonOptionsEditor=p}t.controller("RequestModalDetailsSicknessController",n),n.$inject=["$controller","$log","$q","$rootScope","crmAngService","api.optionGroup","detailsController"]}),define("leave-absences/shared/controllers/sub-controllers/request-modal-details-toil.controller",["common/lodash","common/moment","leave-absences/shared/modules/controllers"],function(e,t,n){function a(n,a,r,o,s,i,u){function c(){return u.balance.change.amount=+u.request.toil_to_accrue,a.resolve(u.balance.change)}function l(){if(!u.request.from_date||!u.request.to_date)return u.uiOptions.max_toil_duration_and_accrual=null,void(u.uiOptions.toil_duration_in_hours=null);u.uiOptions.max_toil_duration_and_accrual=t.duration(t(u.request.to_date).diff(u.request.from_date)).asHours()}function d(){return v().catch(function(e){return e.length&&(u.errors=e),a.reject(e)}).then(function(e){return i.calculateToilExpiryDate(u.request.type_id,e)}).then(function(e){return u.request.toil_expiry_date=e,u.uiOptions.expiryDate=new Date(e),e})}function m(){return!!u.request.toil_to_accrue}function f(){var e=u.uiOptions.multipleDays&&!!u.request.to_date,t=!u.uiOptions.multipleDays&&!!u.request.from_date,n=t||e,a=N.from_date!==u.request.from_date||N.to_date!==u.request.to_date;return u.canDisplayToilExpirationField&&n&&H.hasExpirationFromAdminSettings&&a}function p(){return!!(u.request.from_date&&u.request.to_date&&u.request.toil_duration&&u.request.toil_to_accrue)}function h(){u.request.toil_expiry_date=!1,u.uiOptions.expiryDate=null}function v(){var e=u.uiOptions.multipleDays,t=u.request;return g({hasErrors:e?!t.to_date&&!t.from_date:!t.from_date,label:e?"To Date":"From Date",value:t.to_date})}function g(e){if(e.hasErrors){var n="Please select "+e.label+" to find expiry date";return a.reject([n])}return e.value?a.resolve(t(e.value).format("YYYY-MM-DD")):a.reject([])}function b(){var e=u.isMode("create")&&H.hasExpirationFromAdminSettings,t=H.hasPreviousExpirationDate,n=u.isLeaveType("toil"),a=u.canManage;u.canDisplayToilExpirationField=n&&(a||e||t)}function y(){u.uiOptions.toil_duration_in_hours=u.request.toil_duration/60}function _(){return H.hasPreviousExpirationDate=u.isMode("edit")&&!!u.request.toil_expiry_date,i.canExpire(u.request.type_id).then(function(e){H.hasExpirationFromAdminSettings=e})}function q(){return u.request.to_date_type=u.request.from_date_type="1",_().then(b).then($).then(D)}function $(){u.canManage&&(u.uiOptions.expiryDate=u.convertDateFormatFromServer(u.request.toil_expiry_date))}function C(){r.$watch(function(){return u.request.toil_to_accrue},function(e,t){+e!=+t&&u.performBalanceChangeCalculation()})}function A(){r.$watch(function(){return u.uiOptions.toil_duration_in_hours},function(e,t){e!==t&&u.isCalculationUnit("hours")&&I(),u.request.toil_duration=u.uiOptions.toil_duration_in_hours?60*u.uiOptions.toil_duration_in_hours:null})}function w(){var e=u.uiOptions.times;e.from.time=t(u.request.from_date).format("HH:mm"),e.to.time=t(u.request.to_date).format("HH:mm"),u.uiOptions.multipleDays||u.updateEndTimeInputMinTime(u.uiOptions.times.from.time)}function T(){["from","to"].forEach(function(e){r.$watch(function(){return u.uiOptions.times[e].time},function(e,t){e!==t&&(u.setRequestDateTimesAndDateTypes(),j(),l(),O())})})}function P(){u.isMode("view")||(C(),A(),T())}function D(t){return s.valuesOf("hrleaveandabsences_toil_amounts",t).then(function(t){u.toilAmounts=e.sortBy(t,function(e){return+e.weight})})}function S(){return l(),O(),j()}function R(){return E(),u.uiOptions.multipleDays||u.updateEndTimeInputMinTime(u.uiOptions.times.from.time),l(),U?U=!1:O(),j()}function x(e){u.uiOptions.times[e].time=""}function I(){u.request.toil_to_accrue=u.uiOptions.toil_duration_in_hours}function O(){u.uiOptions.toil_duration_in_hours=u.uiOptions.max_toil_duration_and_accrual}function L(){N=e.cloneDeep(u.request.attributes())}function E(){u.uiOptions.multipleDays?["from","to"].forEach(function(e){u.uiOptions.times[e].min="00:00",u.uiOptions.times[e].max="23:45"}):(u.uiOptions.times.from.min="00:00",u.uiOptions.times.from.max="23:30",u.uiOptions.times.to.min="00:15",u.uiOptions.times.to.max="23:45")}function M(){u.setRequestDateTimesAndDateTypes(),l(),O(),u.isCalculationUnit("hours")?I():u.request.toil_to_accrue=null}function k(){o.loadForm("/civicrm/admin/options/hrleaveandabsences_toil_amounts?reset=1").on("crmUnload",function(){D(!1)})}function B(){u.showTOILAccrualsOptionEditorIcon=e.includes(["admin-dashboard","absence-tab"],r.section)}function j(){return f()?d().catch(a.resolve):a.resolve()}function F(){u.uiOptions.expiryDate&&(u.request.toil_expiry_date=u.convertDateToServerFormat(u.uiOptions.expiryDate))}n.debug("RequestModalDetailsToilController");var N,U=!u.isMode("create"),H={hasPreviousExpirationDate:null,hasExpirationFromAdminSettings:null};u.canDisplayToilExpirationField=!1,u.calculateBalanceChange=c,u.canCalculateChange=m,u.canSubmit=p,u.clearExpiryDate=h,u.initChildController=q,u.initTimesExtended=w,u.initWatchersExtended=P,u.onAbsenceTypeUpdateExtended=M,u.onDateChangeExtended=S,u.openToilInDaysAccrualOptionsEditor=k,u.resetUIInputsExtended=x,u.setDaysSelectionModeExtended=R,u.updateExpiryDate=F,function(){L(),E(),B(),!u.isMode("create")&&y()}()}n.controller("RequestModalDetailsToilController",a),a.$inject=["$log","$q","$rootScope","crmAngService","OptionGroup","AbsenceType","detailsController"]}),define("leave-absences/shared/services/leave-calendar.service",["common/lodash","common/moment","leave-absences/shared/modules/services"],function(e,t,n){"use strict";function a(n,a,r,o){function s(n){function s(){return r.all().then(function(e){return e.list})}function i(){return r.leaveManagees(void 0,{unassigned:!0})}function u(){var t=e.get(n,"filters.userSettings.assignedTo.type","all");return m().then(function(e){return n.lookupContacts=e,a.all([d(),"me"!==t?c():a.resolve(null)])}).then(function(e){var t=e[0];return n.contactIdsToReduceTo=e[1],t})}function c(){return l().then(function(a){var r=a.filter(function(e){var a=e.info.details;return t(a.period_start_date).isSameOrBefore(n.selectedPeriod.end_date)&&(t(a.period_end_date).isSameOrAfter(n.selectedPeriod.start_date)||!a.period_end_date)});return e.uniq(r.map(function(e){return e.contact_id}))})}function l(){return v?a.resolve(v):o.all()}function d(){return r.all(h(),null,"display_name").then(function(e){return e.list})}function m(){var t=e.get(n,"filters.userSettings.assignedTo.type","all");return(0,g[t])()}function f(){return m().then(function(e){n.lookupContacts=e}).then(d)}function p(){return r.leaveManagees(n.contactId)}function h(){var t={department:e.get(n,"filters.userSettings.department.value",null),level_type:e.get(n,"filters.userSettings.level_type.value",null),location:e.get(n,"filters.userSettings.location.value",null),region:e.get(n,"filters.userSettings.region.value",null)},a=!!n.filters.userSettings.contact,r=e.isArray(n.lookupContacts)&&n.lookupContacts.length,o="all"!==e.get(n,"filters.userSettings.assignedTo.type","all");return a?t.id={IN:[n.filters.userSettings.contact.id]}:(o||r)&&(t.id={IN:e.pluck(n.lookupContacts,"id")}),t}var v,g={all:s,me:p,unassigned:i};return{loadContactsForAdmin:u,loadFilteredContacts:d,loadLookUpContacts:m,loadLookUpAndFilteredContacts:f}}return n.debug("LeaveCalendarService"),{init:s}}n.factory("LeaveCalendarService",a),a.$inject=["$log","$q","Contact","Contract"]}),define("leave-absences/shared/instances/sickness-request.instance",["common/lodash","leave-absences/shared/modules/models-instances","leave-absences/shared/instances/leave-request.instance"],function(e,t){"use strict";t.factory("SicknessRequestInstance",["LeaveRequestAPI","LeaveRequestInstance",function(t,n){return n.extend({defaultCustomData:function(){var t={sickness_reason:null,sickness_required_documents:"",request_type:"sickness"};return e.assign({},n.defaultCustomData(),t)},getDocumentArray:function(){return this.sickness_required_documents?this.sickness_required_documents.split(","):[]},toggleDocument:function(t){var n=this.getDocumentArray(),a=n.indexOf(t);e.contains(n,t)?n.splice(a,1):n.push(t),this.sickness_required_documents=n.join(",")},toAPIFilter:function(t,n,a){e.includes(["balance_change","dates","comments","files"],a)||(t[a]=this[a])}})}])}),define("leave-absences/shared/instances/toil-request.instance",["common/lodash","leave-absences/shared/modules/models-instances","leave-absences/shared/instances/leave-request.instance"],function(e,t){"use strict";t.factory("TOILRequestInstance",["LeaveRequestAPI","LeaveRequestInstance",function(t,n){return n.extend({defaultCustomData:function(){var t={from_date_amount:0,to_date_amount:0,request_type:"toil"};return e.assign({},n.defaultCustomData(),t)},toAPIFilter:function(t,n,a){e.includes(["balance_change","dates","comments","files"],a)||(t[a]=this[a])}})}])}),define("leave-absences/shared/controllers/request.controller",["common/angular","leave-absences/shared/modules/controllers","common/lodash","common/moment","common/models/contact","common/models/session.model","common/services/api/option-group","common/services/hr-settings","common/services/pub-sub","leave-absences/shared/models/absence-period.model","leave-absences/shared/models/absence-type.model","leave-absences/shared/models/entitlement.model","leave-absences/shared/models/leave-request.model","leave-absences/shared/models/public-holiday.model","leave-absences/shared/instances/leave-request.instance","leave-absences/shared/instances/sickness-request.instance","leave-absences/shared/instances/toil-request.instance","leave-absences/shared/services/leave-request.service"],function(e,t,n,a){"use strict";function r(t,r,o,s,i,u,c,l,d,m,f,p,h,v,g,b,y,_,q,$,C){function A(e){"days"===ke.selectedAbsenceType.calculation_unit_name?delete ke.request[e+"_date_amount"]:delete ke.request[e+"_date_type"]}function w(){["from","to"].forEach(A)}function T(){var e=ke.requestStatuses[_.statusNames.awaitingApproval].value;xe.status_id===e&&e!==ke.request.status_id&&d.publish("ManagerBadge:: Update Count")}function P(){return Me.filter(function(e){return e.isRequired}).every(function(e){return e.canSubmit&&e.canSubmit()})}function D(){return Me.filter(function(e){return!e.isRequired}).some(function(e){return e.canSubmit&&e.canSubmit()})}function S(){return!ke.loading.entitlements&&(!!ie("admin")||!oe("view")&&!(ie("manager")&&!oe("create")))}function R(){var e=P();return ke.isMode("edit")&&(e=e&&(Y()||D())),ke.canManage&&ke.requestStatuses&&(e=e&&!!ke.getStatusFromValue(ke.newStatusOnSave)),(e=e&&!!ke.period.id)&&!ke.isMode("view")}function x(){ke.isSelfRecord?ke.request.status_id=ke.requestStatuses[_.statusNames.awaitingApproval].value:ke.canManage&&(ke.request.status_id=ke.newStatusOnSave||ke.request.status_id)}function I(){if(ke.isMode("edit")&&!ke.isRole("staff")&&"toil"!==U())return ke.request.calculateBalanceChange(ke.selectedAbsenceType.calculation_unit_name).then(function(e){if(+ke.balance.change.amount!=+e.amount)return C.promptBalanceChangeRecalculation().then(function(){o.$emit("LeaveRequestPopup::recalculateBalanceChange")}),r.reject()})}function O(){return"toil"!==U()&&!ke.request.change_balance}function L(){ke.errors=[]}function E(){return ke.request.create().then(Ae).then(function(){he("LeaveRequest::new")})}function M(){ne()&&!ke.isRole("staff")&&(ke.request.change_balance=!0)}function k(){l.open({title:"Confirm Deletion?",copyCancel:"Cancel",copyConfirm:"Confirm",classConfirm:"btn-danger",msg:"This cannot be undone",onConfirm:function(){return ke.request.delete().then(function(){ke.dismissModal(),d.publish("LeaveRequest::delete",ke.request)})}})}function B(){i.dismiss({$value:"cancel"})}function j(){var e=U();return"leave"===e?{is_sick:!1}:"sickness"===e?{is_sick:!0}:"toil"===e?{allow_accruals_request:!0}:void 0}function F(){return N(ke.getStatusFromValue(ke.request.status_id).name)}function N(e){return n.map(Se[e],function(e){return ke.requestStatuses[e]})}function U(){return ke.request?ke.request.request_type:ke.leaveType||null}function H(){return!ke.request||e.equals({},ke.requestStatuses)?[]:ke.request.status_id?F():N("none")}function W(e){return n.find(ke.requestStatuses,function(t){return t.value===e})}function V(e){ke.errors=n.isArray(e)?e:[e],ke.loading.absenceTypes=!1,ke.submitting=!1}function Y(){return!e.equals(xe,ke.request.attributes())||ke.canManage&&ke.newStatusOnSave}function G(){return ke.postContactSelection=!0,ke.staffMemberSelectionComplete=!1,ke.request.contact_id?r.resolve().then(ce).then(le).then(ge).then(ye).then(te).then(X).then(ke.isMode("edit")?_e:n.noop).then(function(){ke.postContactSelection=!1,ke.staffMemberSelectionComplete=!0}).catch(function(e){if(e!==Le)return r.reject(e)}):r.reject("The contact id was not set")}function z(){var e=[_.statusNames.moreInformationRequired,_.statusNames.approved,_.statusNames.rejected,_.statusNames.cancelled];Se.none=[_.statusNames.moreInformationRequired,_.statusNames.approved],Se.awaiting_approval=e,Se.more_information_required=e,Se.rejected=e,Se.approved=e,Se.cancelled=[_.statusNames.awaitingApproval].concat(e)}function X(){return ke.canManage?f.find(ke.request.contact_id).then(function(e){ke.contactName=e.display_name}):r.resolve()}function J(){var e="my-leave"===o.section,t=+Oe==+n.get(ke,"leaveRequest.contact_id"),a=!n.get(ke,"leaveRequest.id");ke.isSelfRecord=e&&(t||a)}function K(){Ie.push(o.$on("LeaveRequestPopup::requestObjectUpdated",_e),o.$on("LeaveRequestPopup::absencePeriodChanged",function(){le().then(ge).then(function(){o.$emit("LeaveRequestPopup::absencePeriodBalancesUpdated",ke.absenceTypes)})}),o.$on("LeaveRequestPopup::handleError",function(e,t){V(t)}),o.$on("LeaveRequestPopup::childComponent::register",function(){Re++})),s.$on("$destroy",we),s.$on("LeaveRequestPopup::addTab",function(e,t){Me.push(t)})}function Q(){var e,t;ke.request=ke.leaveRequest||null,e=U(),t=ke.initRequestAttributes(),"leave"===e?ke.request=y.init(t):"sickness"===e?ke.request=q.init(t):"toil"===e&&(ke.request=$.init(t))}function Z(){var e={};return ke.request?e=ke.request.attributes():ke.canManage||(e={contact_id:Oe}),e}function ee(){if(Ee="staff",!ke.isSelfRecord)return u(_.permissions.admin.administer).then(function(e){e&&(Ee="admin")}).then(function(){return"staff"===Ee&&u(_.permissions.ssp.manage).then(function(e){e&&(Ee="manager")})}).finally(function(){ke.canManage=ke.isRole("manager")||ke.isRole("admin")})}function te(){(ke.isRole("admin")||ke.isMode("create")&&ke.isRole("manager"))&&(ke.newStatusOnSave=ke.requestStatuses[_.statusNames.approved].value)}function ne(){return!ke.request.status_id||!n.includes(["cancelled","rejected"],W(ke.request.status_id).name)}function ae(e){var t=ke.getStatusFromValue(ke.request.status_id);return!!t&&t.name===e}function re(e){return ke.request&&ke.request.request_type===e}function oe(e){return ke.mode===e}function se(e,t){var n=a(e.from_date),r=a(e.to_date);return n.isSameOrAfter(t.start_date,"day")&&r.isSameOrBefore(t.end_date,"day")}function ie(e){return Ee===e}function ue(){return h.all().then(function(e){ke.absencePeriods=e})}function ce(){return v.all(j()).then(v.loadCalculationUnits).then(function(e){De={types:e,ids:e.map(function(e){return e.id})}})}function le(){return ke.loading.entitlements=!0,g.all({contact_id:ke.request.contact_id,period_id:ke.period.id,type_id:{IN:De.ids}},!0).finally(function(){ke.loading.entitlements=!1})}function de(){return p.get().then(function(e){Oe=e.contactId})}function me(){return ke.selectedContactId?f.find(ke.selectedContactId).then(function(e){ke.managedContacts=[e]}):ke.isRole("admin")?f.all().then(function(e){ke.managedContacts=n.remove(e.list,function(e){return e.id!==Oe})}):f.find(Oe).then(function(e){return e.leaveManagees()}).then(function(e){ke.managedContacts=e})}function fe(){return c.valuesOf("hrleaveandabsences_leave_request_status").then(function(e){ke.requestStatuses=n.indexBy(e,"name")})}function pe(e,t){var a;return n.compact(e.map(function(e){if(a=n.find(t,{type_id:e.id}))return{id:a.type_id,title:e.title+" ( "+a.remainder.current+" ) ",remainder:a.remainder.current,allow_overuse:e.allow_overuse,calculation_unit_name:e.calculation_unit_name}}))}function he(e){T(),d.publish(e,ke.request),ke.errors=[],ke.dismissModal()}function ve(){["from","to"].forEach(function(e){ke.request[e+"_date"]=xe[e+"_date"]})}function ge(e){if(ke.absenceTypes=pe(De.types,e),!ke.absenceTypes.length)return r.reject(Le)}function be(){ke.period=n.find(ke.absencePeriods,function(e){return ke.isMode("create")?e.current:se(ke.request,e)})}function ye(){ke.isMode("create")?(ke.selectedAbsenceType=ke.absenceTypes[0],ke.request.type_id=ke.selectedAbsenceType.id):ke.selectedAbsenceType=n.find(ke.absenceTypes,function(e){return e.id===ke.request.type_id})}function _e(){xe=e.copy(ke.request.attributes())}function qe(){var e;ke.request.id?(e=[ke.requestStatuses[_.statusNames.approved].value,ke.requestStatuses[_.statusNames.adminApproved].value,ke.requestStatuses[_.statusNames.rejected].value,ke.requestStatuses[_.statusNames.cancelled].value],ke.mode="edit",ke.isRole("staff")&&e.indexOf(ke.request.status_id)>-1&&(ke.mode="view")):ke.mode="create"}function $e(){var e=ke.request.status_id;if(!ke.isMode("view")&&!ke.submitting)return ke.submitting=!0,x(),w(),ke.request.isValid().then(ne()&&I).then(M).then(O()&&ve).then(Ce).then(function(){return ke.isMode("edit")?Pe():E()}).catch(function(t){ke.request.status_id=e,t&&V(t)}).finally(function(){ke.submitting=!1})}function Ce(){return r.all(Me.map(function(e){return e.onBeforeSubmit&&e.onBeforeSubmit()}))}function Ae(){function e(e){e&&n.push(e),++a===Re&&(n.length>0?t.reject(n):t.resolve())}var t=r.defer(),n=[],a=0;return Re>0?o.$broadcast("LeaveRequestPopup::submit",e):t.resolve(),t.promise}function we(){Ie.forEach(function(e){e()})}function Te(){o.$broadcast("LeaveRequestPopup::absenceTypeChanged")}function Pe(){return ke.request.update().then(Ae).then(function(){ke.isRole("manager")?he("LeaveRequest::updatedByManager"):(ke.isRole("staff")||ke.isRole("admin"))&&he("LeaveRequest::edit")})}t.debug("RequestCtrl");var De,Se={},Re=0,xe={},Ie=[],Oe="",Le="No entitlement",Ee="",Me=[],ke=n.assign(this,m);ke.absencePeriods=[],ke.absenceTypes=[],ke.canManage=!1,ke.contactName=null,ke.errors=[],ke.loading={absenceTypes:!0,entitlements:!0},ke.managedContacts=[],ke.mode="",ke.newStatusOnSave=null,ke.period={},ke.postContactSelection=!1,ke.requestStatuses={},ke.selectedAbsenceType={},ke.staffMemberSelectionComplete=!1,ke.submitting=!1,ke.balance={closing:0,opening:0,change:{amount:0,breakdown:[]}},ke.canChangeAbsenceType=S,ke.canSubmit=R,ke.closeAlert=L,ke.deleteLeaveRequest=k,ke.dismissModal=B,ke.getStatuses=H,ke.getStatusFromValue=W,ke.initAfterContactSelection=G,ke.initRequestAttributes=Z,ke.isLeaveStatus=ae,ke.isLeaveType=re,ke.isMode=oe,ke.isRole=ie,ke.submit=$e,ke.updateAbsenceType=Te,function(){ke.loading.absenceTypes=!0,z(),K(),de().then(J).then(function(){return r.all([ee(),ue(),fe()])}).then(Q).then(qe).then(be).then(function(){return ke.canManage&&!ke.isMode("edit")&&me()}).then(function(){if(ke.selectedContactId&&(ke.request.contact_id=ke.selectedContactId),ke.request.contact_id)return ke.initAfterContactSelection()}).catch(V).finally(function(){ke.loading.absenceTypes=!1})}()}t.controller("RequestCtrl",r),r.$inject=["$log","$q","$rootScope","$scope","$uibModalInstance","checkPermissions","api.optionGroup","dialog","pubSub","directiveOptions","Contact","Session","AbsencePeriod","AbsenceType","Entitlement","LeaveRequest","LeaveRequestInstance","shared-settings","SicknessRequestInstance","TOILRequestInstance","LeaveRequestService"]}),define("leave-absences/shared/services/leave-popup.service",["common/lodash","common/modules/angular-date","leave-absences/shared/modules/services","common/services/angular-date/date-format","common/services/notification.service","leave-absences/shared/controllers/request.controller"],function(e,t){"use strict";function n(e,t,n,a,r,o,s,i,u){function c(e){return i.get().then(function(t){return e.roleOf(t.contactId)}).then(function(e){return"none"!==e})}function l(e,n,r,s){a.open({appendTo:t.children().eq(0),templateUrl:o.sharedPathTpl+"components/leave-request-popup/leave-request-popup.html",controller:"RequestCtrl",controllerAs:"$ctrl",windowClass:"chr_leave-request-modal",resolve:{directiveOptions:function(){return{leaveType:n,leaveRequest:e,selectedContactId:r,forceRecalculateBalanceChange:s}},format:["DateFormat",function(e){return e.getDateFormat()}]}})}function d(e){return u.find(e).then(function(e){return c(e).then(function(t){
+t?l(e,e.request_type,e.contact_id):r.error("Error","You dont have permission to see this leave request")})}).catch(function(e){r.error("Error",e)})}return e.debug("LeavePopup"),{openModal:l,openModalByID:d}}t.factory("LeavePopup",n),n.$inject=["$log","$rootElement","$rootScope","$uibModal","notificationService","shared-settings","DateFormat","Session","LeaveRequest"]}),define("leave-absences/my-leave/modules/components",["common/angular"],function(e){return e.module("my-leave.components",[])}),define("leave-absences/my-leave/components/my-leave-container.component",["leave-absences/my-leave/modules/components"],function(e){e.component("myLeaveContainer",{bindings:{contactId:"<"},templateUrl:["settings",function(e){return e.pathTpl+"components/my-leave-container.html"}],controllerAs:"myleave",controller:["$log","$rootScope","$state",function(e,t,n){function a(){n.go(r.tabName)}e.debug("Component: my-leave-container"),t.section="my-leave";var r=this;r.tabName=n.current.name,r.changeTab=a}]})}),function(e){define("leave-absences/my-leave/modules/settings",["common/angular"],function(t){return t.module("my-leave.settings",[]).constant("settings",{debug:e.debug,pathTpl:e.vars.leaveAndAbsences.baseURL+"/views/my-leave/"})})}(CRM),function(e){define("leave-absences/my-leave/modules/config",["common/angular","leave-absences/my-leave/modules/settings"],function(t){return t.module("my-leave.config",["my-leave.settings"]).config(["$stateProvider","$resourceProvider","$urlRouterProvider","$httpProvider","$logProvider","settings",function(t,n,a,r,o,s){o.debugEnabled(s.debug),n.defaults.stripTrailingSlashes=!1,r.defaults.headers.common["X-Requested-With"]="XMLHttpRequest",a.otherwise("/my-leave/report"),t.state("my-leave",{abstract:!0,url:"/my-leave",template:'',resolve:{contactId:function(){return e.vars.leaveAndAbsences.contactId},format:["DateFormat",function(e){return e.getDateFormat()}]}}).state("my-leave.report",{url:"/report?leave-request-id",template:'',onEnter:["$stateParams","LeavePopup",function(e,t){e["leave-request-id"]&&t.openModalByID(e["leave-request-id"])}]}).state("my-leave.calendar",{url:"/calendar",template:''})}])})}(CRM),define("leave-absences/my-leave/app",["common/angular","common/angularBootstrap","common/text-angular","common/directives/loading","common/directives/scroll-shadows.directive","common/directives/time-amount-picker.directive","common/directives/timepicker-select.directive","common/filters/angular-date/format-date","common/filters/time-unit-applier.filter","common/models/option-group","common/modules/dialog","common/services/check-permissions","common/services/crm-ang.service","common/services/angular-date/date-format","leave-absences/shared/ui-router","leave-absences/shared/modules/shared-settings","leave-absences/shared/models/absence-period.model","leave-absences/shared/models/absence-type.model","leave-absences/shared/models/calendar.model","leave-absences/shared/models/entitlement.model","leave-absences/shared/models/entitlement.model","leave-absences/shared/models/leave-request.model","leave-absences/shared/models/public-holiday.model","leave-absences/shared/components/leave-calendar.component","leave-absences/shared/components/leave-calendar-day.component","leave-absences/shared/components/leave-calendar-legend.component","leave-absences/shared/components/leave-calendar-month.component","leave-absences/shared/components/leave-request-actions.component","leave-absences/shared/components/leave-request-popup-comments-tab.component","leave-absences/shared/components/leave-request-popup-details-tab.component","leave-absences/shared/components/leave-request-popup-files-tab","leave-absences/shared/components/leave-request-record-actions.component","leave-absences/shared/components/staff-leave-report.component","leave-absences/shared/controllers/sub-controllers/request-modal-details-leave.controller","leave-absences/shared/controllers/sub-controllers/request-modal-details-sickness.controller","leave-absences/shared/controllers/sub-controllers/request-modal-details-toil.controller","leave-absences/shared/models/absence-period.model","leave-absences/shared/models/absence-type.model","leave-absences/shared/models/calendar.model","leave-absences/shared/models/entitlement.model","leave-absences/shared/models/leave-request.model","leave-absences/shared/models/public-holiday.model","leave-absences/shared/modules/shared-settings","leave-absences/shared/modules/shared-settings","leave-absences/shared/services/leave-calendar.service","leave-absences/shared/services/leave-popup.service","leave-absences/my-leave/components/my-leave-container.component","leave-absences/my-leave/modules/config"],function(e){return e.module("my-leave",["ngResource","ngAnimate","ui.bootstrap","ui.router","textAngular","common.angularDate","common.dialog","common.directives","common.filters","common.mocks","common.models","common.services","leave-absences.components","leave-absences.controllers","leave-absences.models","leave-absences.services","leave-absences.settings","my-leave.components","my-leave.config"]).run(["$log","$rootScope","shared-settings","settings",function(e,t,n,a){e.debug("app.run"),t.sharedPathTpl=n.sharedPathTpl,t.settings=a}]),e}),function(e,t){var n=e.vars.leaveAndAbsences.baseURL+"/js/angular/src/leave-absences";t.config({urlArgs:"bust="+(new Date).getTime(),paths:{"leave-absences/shared":n+"/shared","leave-absences/my-leave":n+"/my-leave"}}),t(["leave-absences/shared/config"],function(){t(["leave-absences/my-leave/app"],function(e){e.bootstrap(document.querySelector("[data-leave-absences-my-leave]"),["my-leave"])})})}(CRM,require),define("my-leave",function(){});
//# sourceMappingURL=/sites/all/modules/civicrm/tools/extensions/civihr/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/dist/my-leave.js.map
\ No newline at end of file
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/absence-tab/app.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/absence-tab/app.js
index 40ec97e1787..d1e659d8942 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/absence-tab/app.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/absence-tab/app.js
@@ -41,6 +41,7 @@ define([
'leave-absences/shared/models/absence-type.model',
'leave-absences/shared/models/entitlement.model',
'leave-absences/shared/modules/shared-settings',
+ 'leave-absences/shared/services/leave-calendar.service',
'leave-absences/shared/services/leave-popup.service',
'leave-absences/absence-tab/components/absence-tab-container.component',
'leave-absences/absence-tab/components/absence-tab-entitlements.component',
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/admin-dashboard/app.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/admin-dashboard/app.js
index e496863348b..9a12c3eeb69 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/admin-dashboard/app.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/admin-dashboard/app.js
@@ -28,6 +28,7 @@ define([
'leave-absences/shared/controllers/sub-controllers/request-modal-details-sickness.controller',
'leave-absences/shared/controllers/sub-controllers/request-modal-details-toil.controller',
'leave-absences/shared/modules/shared-settings',
+ 'leave-absences/shared/services/leave-calendar.service',
'leave-absences/shared/services/leave-popup.service',
'leave-absences/admin-dashboard/modules/config',
'leave-absences/admin-dashboard/components/admin-dashboard-container'
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/manager-leave/app.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/manager-leave/app.js
index ac140d1f4cb..10842627286 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/manager-leave/app.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/manager-leave/app.js
@@ -33,6 +33,7 @@ define([
'leave-absences/shared/controllers/sub-controllers/request-modal-details-toil.controller',
'leave-absences/shared/models/absence-period.model',
'leave-absences/shared/models/absence-type.model',
+ 'leave-absences/shared/services/leave-calendar.service',
'leave-absences/shared/services/leave-popup.service',
'leave-absences/manager-leave/components/manager-leave-container',
'leave-absences/manager-leave/modules/config'
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/manager-notification-badge/app.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/manager-notification-badge/app.js
index 53b711886eb..65ffa041952 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/manager-notification-badge/app.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/manager-notification-badge/app.js
@@ -3,24 +3,26 @@
define([
'common/angular',
'common/models/session.model',
+ 'common/modules/templates',
'common/services/pub-sub',
+ 'common/components/notification-badge.component',
'leave-absences/shared/modules/shared-settings',
'leave-absences/shared/models/leave-request.model',
- 'leave-absences/shared/components/leave-notification-badge.component',
'leave-absences/manager-notification-badge/modules/config',
'leave-absences/manager-notification-badge/components/manager-notification-badge.component'
], function (angular) {
angular.module('manager-notification-badge', [
'ngResource',
+ 'common.components',
+ 'common.templates',
'leave-absences.settings',
'leave-absences.models',
- 'leave-absences.components',
'manager-notification-badge.components',
'manager-notification-badge.config'
])
- .run(['$log', function ($log) {
- $log.debug('app.run');
- }]);
+ .run(['$log', function ($log) {
+ $log.debug('app.run');
+ }]);
return angular;
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/manager-notification-badge/components/manager-notification-badge.component.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/manager-notification-badge/components/manager-notification-badge.component.js
index 28245767952..f6611775af5 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/manager-notification-badge/components/manager-notification-badge.component.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/manager-notification-badge/components/manager-notification-badge.component.js
@@ -17,8 +17,12 @@ define([
function ManagerNotificationBadgeController ($log, $q, Session, OptionGroup, sharedSettings) {
$log.debug('Component: manager-notification-badge');
- var filters = {};
var vm = this;
+ var leaveRequestFilters = {
+ apiName: 'LeaveRequest',
+ params: {}
+ };
+
vm.refreshCountEventName = 'ManagerBadge:: Update Count';
(function init () {
@@ -26,7 +30,7 @@ define([
getManagerId(),
getStatusId()
]).then(function () {
- vm.filters = filters;
+ vm.filters = [leaveRequestFilters];
});
})();
@@ -38,7 +42,7 @@ define([
function getManagerId () {
return Session.get()
.then(function (session) {
- filters.managed_by = session.contactId;
+ leaveRequestFilters.params.managed_by = session.contactId;
});
}
@@ -50,7 +54,7 @@ define([
function getStatusId () {
return loadStatuses()
.then(function (leaveRequestStatuses) {
- filters.status_id = _.find(leaveRequestStatuses, function (status) {
+ leaveRequestFilters.params.status_id = _.find(leaveRequestStatuses, function (status) {
return status.name === sharedSettings.statusNames.awaitingApproval;
}).value;
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/my-leave/app.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/my-leave/app.js
index a5adeb64718..dedd083c9d7 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/my-leave/app.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/my-leave/app.js
@@ -45,6 +45,7 @@ define([
'leave-absences/shared/models/public-holiday.model',
'leave-absences/shared/modules/shared-settings',
'leave-absences/shared/modules/shared-settings',
+ 'leave-absences/shared/services/leave-calendar.service',
'leave-absences/shared/services/leave-popup.service',
'leave-absences/my-leave/components/my-leave-container.component',
'leave-absences/my-leave/modules/config'
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/apis/leave-request.api.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/apis/leave-request.api.js
index 0da8fe32c30..423784caf24 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/apis/leave-request.api.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/apis/leave-request.api.js
@@ -17,13 +17,13 @@ define([
* This method returns all the Leave Requests.
* It supports filters, pagination, sort and extra params
*
- * @param {object} filters - Values the full list should be filtered by
- * @param {object} pagination
+ * @param {Object} filters Values the full list should be filtered by
+ * @param {Object} pagination
* `page` for the current page, `size` for number of items per page
- * @param {string} sort - The field and direction to order by
+ * @param {String} sort The field and direction to order by
* @param {Object} params
* @param {Boolean} cache
- * @return {Promise} Resolved with {Object} All leave requests
+ * @return {Promise} Resolves with {Object} All leave requests
*/
all: function (filters, pagination, sort, params, cache) {
$log.debug('LeaveRequestAPI.all');
@@ -45,32 +45,23 @@ define([
* This method returns all the total change in balance that is caused by the
* leave requests of a given absence type, or of all the absence types of a given contact and period.
*
- * @param {string} contactId The ID of the Contact to get the balance change for
- * @param {string} periodId The ID of the Absence Period to get the balance change for
- * @param {array} [statuses = null] An array of OptionValue values which the list will be filtered by
- * @param {boolean} [isPublicHoliday=false] Based on the value of this param,
- * the calculation will include only the leave requests that aren't/are public holidays
- * @return {Promise} Resolved with {Object} Balance Change data or Error data
+ * @param {Object} params
+ * @return {Promise} Resolves with {Object} Balance Change data or Error data
*/
- balanceChangeByAbsenceType: function (contactId, periodId, statuses, isPublicHoliday) {
+ balanceChangeByAbsenceType: function (params) {
$log.debug('LeaveRequestAPI.balanceChangeByAbsenceType');
var deferred = $q.defer();
- if (!contactId || !periodId) {
+ if (!params.contact_id || !params.period_id) {
deferred.reject('contact_id and period_id are mandatory');
}
- var params = {
- contact_id: contactId,
- period_id: periodId,
- statuses: statuses ? {'IN': statuses} : null,
- public_holiday: isPublicHoliday || false
- };
+ params = _.defaults(params, { statuses: null, public_holiday: false });
this.sendGET('LeaveRequest', 'getbalancechangebyabsencetype', params, false)
- .then(function (data) {
- deferred.resolve(data.values);
- });
+ .then(function (data) {
+ deferred.resolve(data.values);
+ });
return deferred.promise;
},
@@ -79,10 +70,9 @@ define([
* Gets the overall balance change after a leave request is created. The
* API will create and return the detailed breakdown of it in days.
*
- * @param {Object} params matched the API end point params like
+ * @param {Object} params matched the API end point params like
* mandatory values for contact_id, from_date, from_date_type and optional values for
* to_date and to_date_type.
- *
* @return {Promise} containing the detailed breakdown of balance leaves
*/
calculateBalanceChange: function (params) {
@@ -103,8 +93,8 @@ define([
* Gets the balance change breakdown
* @NOTE: This breakdown is not affected by a work pattern change
*
- * @param {Integer} leaveRequestId Leave Request ID
- * @return {Promise} resolves with the detailed balance breakdown
+ * @param {Number} leaveRequestId Leave Request ID
+ * @return {Promise} Resolves with the detailed balance breakdown
*/
getBalanceChangeBreakdown: function (leaveRequestId) {
return this.sendGET('LeaveRequest', 'getBreakdown',
@@ -113,11 +103,11 @@ define([
/**
* Get the "from" and "to" times and number of hours
- * for a given date according to the current work pattern
+ * for a given date according to the current work pattern
*
* @param {String} leaveDate in the "YYYY-MM-DD" format
* @param {String|Number} contactId
- * @return {Promise} resolved with the response
+ * @return {Promise} Resolves with the response
* as per LeaveRequest.getWorkDayForDate API
*/
getWorkDayForDate: function (date, contactId) {
@@ -128,11 +118,10 @@ define([
/**
* Create a new leave request with given params.
*
- * @param {Object} params matched the API end point params with
+ * @param {Object} params matched the API end point params with
* mandatory values for contact_id, status_id, from_date, from_date_type
* and optional values for to_date and to_date_type.
* If to_date is given then to_date_type is also mandotory.
- *
* @return {Promise} containing the leave request object additionally with id key set
* else rejects the promise with error data
*/
@@ -148,7 +137,7 @@ define([
/**
* Calls the `delete` endpoint with the given leave request id
*
- * @param {int/string} id
+ * @param {Number/String} id
* @return {Promise}
*/
delete: function (id) {
@@ -158,10 +147,9 @@ define([
/**
* Calls the deletecomment backend API.
*
- * @param {String} leaveRequestID - leave request ID
- * @param {String} attachmentID - attachment ID
- * @param {Object} params
- *
+ * @param {String} leaveRequestID
+ * @param {String} attachmentID
+ * @param {Object} params
* @return {Promise}
*/
deleteAttachment: function (leaveRequestID, attachmentID, params) {
@@ -171,17 +159,16 @@ define([
});
return this.sendPOST('LeaveRequest', 'deleteattachment', params)
- .then(function (result) {
- return result.values;
- });
+ .then(function (result) {
+ return result.values;
+ });
},
/**
* Calls the deletecomment backend API.
*
- * @param {String} commentID - comment ID
- * @param {Object} params
- *
+ * @param {String} commentID
+ * @param {Object} params
* @return {Promise}
*/
deleteComment: function (commentID, params) {
@@ -190,37 +177,35 @@ define([
});
return this.sendPOST('LeaveRequest', 'deletecomment', params)
- .then(function (commentsData) {
- return commentsData.values;
- });
+ .then(function (commentsData) {
+ return commentsData.values;
+ });
},
/**
* Get leave request for the given id
*
- * @param {object} id - leave request id
- *
- * @return {Promise} resolves with {Object}
+ * @param {Object} id leave request id
+ * @return {Promise} Resolves with {Object}
*/
find: function (id) {
$log.debug('LeaveRequestAPI.find');
- return this.sendGET('LeaveRequest', 'getFull', { id: id })
- .then(function (response) {
- if (response.values.length === 0) {
- return $q.reject('LeaveRequest not found with this ID');
- }
+ return this.sendGET('LeaveRequest', 'getFull', { id: id }, false)
+ .then(function (response) {
+ if (response.values.length === 0) {
+ return $q.reject('LeaveRequest not found with this ID');
+ }
- return response.values[0];
- });
+ return response.values[0];
+ });
},
/**
* Calls the getattachments backend API.
*
- * @param {String} leaveRequestID - ID of leave request
- * @param {Object} params
- *
+ * @param {String} leaveRequestID ID of leave request
+ * @param {Object} params
* @return {Promise}
*/
getAttachments: function (leaveRequestID, params) {
@@ -229,17 +214,16 @@ define([
});
return this.sendGET('LeaveRequest', 'getattachments', params, false)
- .then(function (attachments) {
- return attachments.values;
- });
+ .then(function (attachments) {
+ return attachments.values;
+ });
},
/**
* Calls the getcomment backend API.
*
- * @param {String} leaveRequestID - ID of leave request
- * @param {Object} params
- *
+ * @param {String} leaveRequestID
+ * @param {Object} params
* @return {Promise}
*/
getComments: function (leaveRequestID, params) {
@@ -248,17 +232,17 @@ define([
});
return this.sendGET('LeaveRequest', 'getcomment', params, false)
- .then(function (commentsData) {
- return commentsData.values;
- });
+ .then(function (commentsData) {
+ return commentsData.values;
+ });
},
/**
* Calls the isManagedBy backend API.
*
- * @param {String} leaveRequestID - ID of leave request
- * @param {String} contactID - ID of contact
- * @return {Promise} resolves with an {Boolean}
+ * @param {String} leaveRequestID
+ * @param {String} contactID
+ * @return {Promise} Resolves with a {Boolean}
*/
isManagedBy: function (leaveRequestID, contactID) {
$log.debug('LeaveRequestAPI.isManagedBy');
@@ -267,31 +251,31 @@ define([
leave_request_id: leaveRequestID,
contact_id: contactID
})
- .then(function (response) {
- return response.values;
- });
+ .then(function (response) {
+ return response.values;
+ });
},
/**
* Validate params for a new new leave request. It can be used before
* creating a leave request to validate data.
*
- * @param {Object} params matched the API end point params with
+ * @param {Object} params matched the API end point params with
* values like contact_id, status_id, from_date, from_date_type etc.,
- * @return {Promise} returns an array of errors for invalid data else empty array
+ * @return {Promise} Returns an array of errors for invalid data else empty array
*/
isValid: function (params) {
$log.debug('LeaveRequestAPI.isValid', params);
var deferred = $q.defer();
this.sendPOST('LeaveRequest', 'isValid', params)
- .then(function (data) {
- if (data.count > 0) {
- deferred.reject(_(data.values).map().flatten().value());
- } else {
- deferred.resolve(data.values);
- }
- });
+ .then(function (data) {
+ if (data.count > 0) {
+ deferred.reject(_(data.values).map().flatten().value());
+ } else {
+ deferred.resolve(data.values);
+ }
+ });
return deferred.promise;
},
@@ -299,10 +283,9 @@ define([
/**
* Calls the addcomment backend API.
*
- * @param {string} leaveRequestID - ID of Leave Request
- * @param {Object} comment - Comment object
- * @param {Object} params
- *
+ * @param {String} leaveRequestID
+ * @param {Object} comment
+ * @param {Object} params
* @return {Promise}
*/
saveComment: function (leaveRequestID, comment, params) {
@@ -313,16 +296,16 @@ define([
});
return this.sendPOST('LeaveRequest', 'addcomment', params)
- .then(function (commentsData) {
- return commentsData.values;
- });
+ .then(function (commentsData) {
+ return commentsData.values;
+ });
},
/**
* This method is used to update a leave request
*
- * @param {object} params - Updated values of leave request
- * @return {Promise} Resolved with {Object} Updated Leave request
+ * @param {Object} params Updated values of leave request
+ * @return {Promise} Resolves with {Object} Updated Leave request
*/
update: function (params) {
$log.debug('LeaveRequestAPI.update', params);
@@ -333,9 +316,9 @@ define([
}
return this.sendPOST('LeaveRequest', 'create', params)
- .then(function (data) {
- return data.values[0];
- });
+ .then(function (data) {
+ return data.values[0];
+ });
}
});
}]);
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar-day.component.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar-day.component.js
index 837fe05646a..620556f5878 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar-day.component.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar-day.component.js
@@ -18,22 +18,15 @@ define([
controller: LeaveCalendarDayController
});
- LeaveCalendarDayController.$inject = ['$log', '$scope', '$timeout', 'LeavePopup'];
+ LeaveCalendarDayController.$inject = ['$log', '$scope', 'LeavePopup'];
- function LeaveCalendarDayController ($log, $scope, $timeout, LeavePopup) {
+ function LeaveCalendarDayController ($log, $scope, LeavePopup) {
'use strict';
$log.debug('Component: leave-calendar-day');
var vm = this;
- vm.tooltip = {
- show: false,
- day_cell_hovered: false,
- tooltip_hovered: false
- };
-
vm.openLeavePopup = openLeavePopup;
- vm.toggleTooltip = toggleTooltip;
(function init () {
watchLeaveRequests();
@@ -42,19 +35,11 @@ define([
/**
* Opens the leave request popup
*
- * When leave-request-actions.component sits inside manage-request component's table rows,
- * and the table row has a click event to open leave request, so event.stopPropagation()
- * is necessary to prevent the parents click event from being called
- *
* @param {Object} event
* @param {Object} leaveRequest
- * @param {String} leaveType
- * @param {String} selectedContactId
- * @param {Boolean} isSelfRecord
*/
- function openLeavePopup (event, leaveRequest, leaveType, selectedContactId, isSelfRecord) {
- event.stopPropagation();
- LeavePopup.openModal(leaveRequest, leaveType, selectedContactId, isSelfRecord);
+ function openLeavePopup (event, leaveRequest) {
+ LeavePopup.openModalByID(leaveRequest.id);
}
/**
@@ -107,19 +92,36 @@ define([
* @param {Object} leaveRequestAttributes
*/
function resolveLeaveRequestAbsenceTypeTitle (leaveRequest, leaveRequestAttributes) {
+ var absenceType = _.find(vm.supportData.absenceTypes, { id: leaveRequest.type_id });
+
vm.contactData.leaveRequestsAttributes[leaveRequest.id].absenceTypeTitle =
- _.find(vm.supportData.absenceTypes, { id: leaveRequest.type_id }).title;
+ absenceType.title;
}
/**
* Sets a unit name to the leave requests attributes
+ * @NOTE this function contains an adhoc solution
+ * and should be refactored as soon as possible.
+ * We do not know what calculation to use for generic leave types
+ * so we rely on the "from_date_type" field to identify it.
+ * @see PCHR-3774
*
* @param {LeaveRequestInstance} leaveRequest
* @param {Object} leaveRequestAttributes
*/
function resolveLeaveRequestCalculationUnit (leaveRequest, leaveRequestAttributes) {
- var absenceType = _.find(vm.supportData.absenceTypes, { id: leaveRequest.type_id });
- var calculationUnit = _.find(vm.supportData.calculationUnits, { 'value': absenceType.calculation_unit });
+ var absenceType, calculationUnit;
+
+ // @NOTE This block is an adhoc mentioned in the function description
+ if (!leaveRequest.type_id) {
+ leaveRequestAttributes.unit = leaveRequest.from_date_type
+ ? 'days' : 'hours';
+
+ return;
+ }
+
+ absenceType = _.find(vm.supportData.absenceTypes, { id: leaveRequest.type_id });
+ calculationUnit = _.find(vm.supportData.calculationUnits, { 'value': absenceType.calculation_unit });
leaveRequestAttributes.unit = calculationUnit.name;
}
@@ -132,8 +134,8 @@ define([
* @param {Object} leaveRequestAttributes
*/
function resolveLeaveRequestDates (leaveRequest, leaveRequestAttributes) {
- leaveRequestAttributes.from_date = new Date(leaveRequest.from_date);
- leaveRequestAttributes.to_date = new Date(leaveRequest.to_date);
+ leaveRequestAttributes.from_date = moment(leaveRequest.from_date).toDate();
+ leaveRequestAttributes.to_date = moment(leaveRequest.to_date).toDate();
}
/**
@@ -195,26 +197,6 @@ define([
});
}
- /**
- * Toggles tooltip for the day.
- * It reacts to entering/leaving either day cell or the tooltip itself,
- * if either of the elements are hovered, it remains the tooltip open.
- * It instantly shows a tooltip, but has a 100ms timeout to hide it once unhovered.
- *
- * @TODO this should be moved to a decorator to uib-tooltip
- *
- * @param {String} sourceElement day_cell|tooltip
- * @param {Boolean} isHovered
- */
- function toggleTooltip (sourceElement, isHovered) {
- $timeout(function () {
- vm.tooltip[sourceElement + '_hovered'] = isHovered;
-
- vm.tooltip.show =
- vm.tooltip.day_cell_hovered || vm.tooltip.tooltip_hovered;
- }, isHovered ? 0 : 100);
- }
-
/**
* Waits for the leave request to be accesible before mapping the necessary
* leave request fields to it.
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar-legend.component.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar-legend.component.js
index 4d40d39661b..98285e7e5ad 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar-legend.component.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar-legend.component.js
@@ -1,8 +1,9 @@
/* eslint-env amd */
define([
+ 'common/lodash',
'leave-absences/shared/modules/components'
-], function (components) {
+], function (_, components) {
components.component('leaveCalendarLegend', {
bindings: {
absenceTypes: '<'
@@ -11,28 +12,112 @@ define([
return sharedSettings.sharedPathTpl + 'components/leave-calendar-legend.html';
}],
controllerAs: 'legend',
- controller: ['$log', controller]
+ controller: leaveCalendarLegendController
});
- function controller ($log) {
+ leaveCalendarLegendController.$inject = ['$log', '$rootScope'];
+
+ function leaveCalendarLegendController ($log, $rootScope) {
$log.debug('Component: leave-calendar-legend');
var vm = this;
- vm.legendCollapsed = true;
+ vm.absenceTypesToFilterBy = [];
+ vm.legendCollapsed = false;
+ vm.nonWorkingDayTypes = [
+ { label: 'Weekend', cssClassSuffix: 'weekend' },
+ { label: 'Public Holiday', cssClassSuffix: 'public-holiday' },
+ { label: 'Non Working Day', cssClassSuffix: 'non-working-day' }
+ ];
+ vm.otherBadges = [
+ { label: 'AM', description: 'AM Only' },
+ { label: 'PM', description: 'PM Only' },
+ { label: 'HH:MM', description: 'Time', cssClassSuffix: 'hours' },
+ { label: '', description: 'Requested', cssClassSuffix: 'requested' },
+ { label: 'AT', description: 'Accrued TOIL' }
+ ];
+
+ vm.checkIfAbsenceTypeIdIsDefined = checkIfAbsenceTypeIdIsDefined;
+ vm.checkIfAbsenceTypeIsSelectedForFiltering = checkIfAbsenceTypeIsSelectedForFiltering;
vm.getAbsenceTypeStyle = getAbsenceTypeStyle;
+ vm.resetFilteringByAbsenceTypes = resetFilteringByAbsenceTypes;
+ vm.toggleFilteringByAbsenceType = toggleFilteringByAbsenceType;
+
+ (function init () {
+ initWatchers();
+ }());
+
+ /**
+ * Checks if the given absence type has a defined id or not.
+ *
+ * @param {Object} absenceType
+ * @return {Boolean}
+ */
+ function checkIfAbsenceTypeIdIsDefined (absenceType) {
+ return !!absenceType.id;
+ }
+
+ /**
+ * Checks if absence type is selected for filtering
+ *
+ * @param {String} absenceTypeId
+ * @return {Boolean}
+ */
+ function checkIfAbsenceTypeIsSelectedForFiltering (absenceTypeId) {
+ var isIncludedInTheAbsenceTypeFilters = _.includes(vm.absenceTypesToFilterBy, absenceTypeId);
+ var noAbsenceTypesFiltersHaveBeenSelected = !vm.absenceTypesToFilterBy.length;
+
+ return noAbsenceTypesFiltersHaveBeenSelected || isIncludedInTheAbsenceTypeFilters;
+ }
/**
- * Uses the absence type color to return border and background color styles
+ * Uses the absence type color to return background color style
*
* @param {AbsenceTypeInstance} absenceType
* @return {Object}
*/
function getAbsenceTypeStyle (absenceType) {
return {
- backgroundColor: absenceType.color,
- borderColor: absenceType.color
+ backgroundColor: absenceType.color
};
}
+
+ /**
+ * Watches the state of the absence types filter
+ */
+ function initWatchers () {
+ $rootScope.$new().$watch(function () {
+ return vm.absenceTypesToFilterBy;
+ }, function (newValue, oldValue) {
+ if (newValue !== oldValue) {
+ $rootScope.$emit('LeaveCalendar::updateFiltersByAbsenceType',
+ vm.absenceTypesToFilterBy);
+ }
+ }, true);
+ }
+
+ /**
+ * Resets filtering by absence types
+ */
+ function resetFilteringByAbsenceTypes () {
+ vm.absenceTypesToFilterBy = [];
+ }
+
+ /**
+ * Toggles filtering by a given absence type
+ *
+ * @param {String} absenceTypeId
+ */
+ function toggleFilteringByAbsenceType (absenceTypeId) {
+ var hasAlreadyBeenSelected = _.includes(vm.absenceTypesToFilterBy, absenceTypeId);
+
+ if (hasAlreadyBeenSelected) {
+ _.remove(vm.absenceTypesToFilterBy, function (_absenceTypeId_) {
+ return absenceTypeId === _absenceTypeId_;
+ });
+ } else {
+ vm.absenceTypesToFilterBy.push(absenceTypeId);
+ }
+ }
}
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar-month.component.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar-month.component.js
index b3d0068c8e7..eb8db37aa64 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar-month.component.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar-month.component.js
@@ -8,6 +8,7 @@ define([
], function (_, moment, components) {
components.component('leaveCalendarMonth', {
bindings: {
+ showTheseContacts: '<',
contacts: '<',
contactIdsToReduceTo: '<',
month: '<',
@@ -46,7 +47,7 @@ define([
vm.getContactUrl = getContactUrl;
(function init () {
- var dateFromMonth = moment().month(vm.month.index).year(vm.month.year);
+ var dateFromMonth = moment().month(vm.month.month).year(vm.month.year);
indexData();
initListeners();
@@ -74,7 +75,8 @@ define([
*/
function buildMonthStructure (dateMoment) {
return {
- index: dateMoment.month(),
+ index: dateMoment.format('YYYY-MM'),
+ month: dateMoment.month(),
year: dateMoment.year(),
name: dateMoment.format('MMMM'),
loading: true,
@@ -126,7 +128,10 @@ define([
*/
function contactsList () {
return !vm.showOnlyWithLeaveRequests ? vm.contacts : vm.contacts.filter(function (contact) {
- return Object.keys(leaveRequests[contact.id] || {}).length;
+ var hasLeaveRequests = Object.keys(leaveRequests[contact.id] || {}).length;
+ var isAlwaysShown = _.includes(vm.showTheseContacts, contact.id);
+
+ return hasLeaveRequests || isAlwaysShown;
});
}
@@ -195,6 +200,15 @@ define([
return indexedLeaveRequest;
}
+ /**
+ * Flushes days data
+ */
+ function flushDays () {
+ vm.month.days.forEach(function (day) {
+ day.contactsData = {};
+ });
+ }
+
/**
* Get profile URL for the given contact id
*
@@ -204,6 +218,20 @@ define([
return CRM.url('civicrm/contact/view', { cid: contactId });
}
+ /**
+ * Returns a filtered list of leave requests with defined absence types.
+ * This is useful to only get leave requests the contact has access to.
+ *
+ * @param {Array} leaveRequests a list of leave requests.
+ * @return {Array} a filtered list of leave requests.
+ * @TODO this check should be performed on the back-end.
+ */
+ function getLeaveRequestsWithDefinedAbsenceTypes (leaveRequests) {
+ return _.filter(leaveRequests, function (leaveRequest) {
+ return !!leaveRequest.type_id;
+ });
+ }
+
/**
* Returns leave requests additional attributes for UI
*
@@ -269,7 +297,7 @@ define([
* Initializes the event listeners
*/
function initListeners () {
- eventListeners.push($rootScope.$on('LeaveCalendar::showMonths', showMonthIfInList));
+ eventListeners.push($rootScope.$on('LeaveCalendar::showMonth', showMonth));
eventListeners.push(pubSub.subscribe('LeaveRequest::new', addLeaveRequest));
eventListeners.push(pubSub.subscribe('LeaveRequest::edit', updateLeaveRequest));
eventListeners.push(pubSub.subscribe('LeaveRequest::updatedByManager', updateLeaveRequest));
@@ -355,7 +383,7 @@ define([
while (pointerDate.isSameOrBefore(toDate)) {
// Ensure that pointerDate is in same month/year that component represents
- if (pointerDate.month() === vm.month.index && pointerDate.year() === vm.month.year) {
+ if (pointerDate.month() === vm.month.month && pointerDate.year() === vm.month.year) {
days.push(_.find(vm.month.days, function (day) {
return day.date === pointerDate.format('YYYY-MM-DD');
}));
@@ -424,16 +452,30 @@ define([
* @return {Promise}
*/
function loadMonthLeaveRequests () {
- return LeaveRequest.all({
- from_date: { to: vm.month.days[vm.month.days.length - 1].date },
- to_date: { from: vm.month.days[0].date },
+ var isRequestFilteredByAbsenceType = vm.supportData.absenceTypesToFilterBy.length > 0;
+ var params = {
+ from_date: { to: vm.month.days[vm.month.days.length - 1].date + ' 23:59:59' },
+ to_date: { from: vm.month.days[0].date + ' 00:00:00' },
status_id: { 'IN': leaveStatusesToBeDisplayed() },
contact_id: { 'IN': vm.contacts.map(function (contact) {
return contact.id;
})},
- type_id: { IN: _.pluck(vm.supportData.absenceTypes, 'id') }
- }, null, null, null, false)
+ type_id: { 'IN': isRequestFilteredByAbsenceType
+ ? vm.supportData.absenceTypesToFilterBy
+ : _.pluck(vm.supportData.absenceTypes, 'id') }
+ };
+
+ flushDays();
+
+ return LeaveRequest.all(params, null, null, null, false)
.then(function (leaveRequestsData) {
+ leaveRequests = {};
+
+ if (isRequestFilteredByAbsenceType) {
+ leaveRequestsData.list = getLeaveRequestsWithDefinedAbsenceTypes(
+ leaveRequestsData.list);
+ }
+
return indexLeaveRequests(leaveRequestsData.list);
});
}
@@ -561,24 +603,20 @@ define([
/**
* Show the month and its data if it's included in the given list
*
- * @param {Array} monthsToShow
- * @param {Boolean} forceReload If true it forces the reload of the data
+ * @param {Boolean} forceReload If true it forces the reload of the data
*/
- function showMonthIfInList (__, monthsToShow, forceReload) {
- var isIncluded = !!_.find(monthsToShow, function (month) {
- return month.index === vm.month.index;
- });
-
- if (isIncluded) {
- vm.currentPage = 0;
- vm.visible = true;
+ function showMonth (__, forceReload) {
+ vm.currentPage = 0;
+ vm.visible = true;
- (forceReload || !dataLoaded) && loadMonthData();
- } else {
- vm.visible = false;
- }
+ (forceReload || !dataLoaded) && loadMonthData();
}
+ /**
+ * Sorts leave requests by either date or AM/PM
+ *
+ * @param {Array} leaveRequests array of leave requests instances
+ */
function sortLeaveRequests (leaveRequests) {
return _.sortBy(leaveRequests, function (leaveRequest) {
return +moment(leaveRequest.from_date).format('X') +
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar.component.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar.component.js
index af8e8ffab11..73dc4393e8c 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar.component.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-calendar.component.js
@@ -1,17 +1,17 @@
/* eslint-env amd */
define([
- 'common/angular',
'common/lodash',
'common/moment',
'leave-absences/shared/modules/components',
'leave-absences/shared/controllers/sub-controllers/leave-calendar-admin.controller',
'leave-absences/shared/controllers/sub-controllers/leave-calendar-manager.controller',
'leave-absences/shared/controllers/sub-controllers/leave-calendar-staff.controller'
-], function (angular, _, moment, components) {
+], function (_, moment, components) {
components.component('leaveCalendar', {
bindings: {
contactId: '<',
+ displaySingleContact: '',
roleOverride: '@?'
},
templateUrl: ['shared-settings', function (sharedSettings) {
@@ -33,15 +33,17 @@ define([
vm.absencePeriods = [];
vm.contacts = [];
vm.contactIdsToReduceTo = null;
- vm.injectMonths = false;
+ vm.injectMonth = false;
vm.months = [];
- vm.selectedMonths = null;
+ vm.selectedMonth = {};
+ vm.selectedMonthIndex = '';
vm.selectedPeriod = null;
vm.showContactName = false;
vm.showFilters = false;
- vm.supportData = {};
+ vm.userPermissionRole = 'staff';
vm.loading = { calendar: true, page: true };
vm.filters = {
+ hideOnMobile: true,
optionValues: {},
userSettings: {
contact: null,
@@ -50,35 +52,67 @@ define([
level_type: null,
location: null,
region: null
- }
+ },
+ absenceTypes: {}
+ };
+ vm.filtersByAssignee = [
+ { type: 'me', label: 'People I approve' },
+ { type: 'unassigned', label: 'People without approver' },
+ { type: 'all', label: 'All' }
+ ];
+ vm.filters.userSettings.assignedTo = vm.filtersByAssignee[2];
+ vm.monthPaginatorsAvailability = {
+ previous: true,
+ next: true
+ };
+ vm.supportData = {
+ absenceTypesToFilterBy: []
};
+ vm.canManageRequests = canManageRequests;
vm.labelPeriod = labelPeriod;
+ vm.navigateToCurrentMonth = navigateToCurrentMonth;
+ vm.paginateMonth = paginateMonth;
vm.refresh = refresh;
(function init () {
setUserRole()
.then(initWatchers)
+ .then(initListeners)
.then(injectSubController)
- .then(makeSureMonthsAreNotInjected)
- .then(loadAbsencePeriods)
+ .then(makeSureMonthIsNotInjected)
.then(function () {
return $q.all([
+ loadAbsencePeriods(),
loadContacts(),
- loadSupportData()
+ loadSupportData(),
+ vm.showFilters ? loadFiltersOptionValues() : _.noop
]);
})
.then(function () {
- return vm.showFilters ? loadFiltersOptionValues() : _.noop;
- })
- .then(function () {
- injectAndShowMonths();
+ appendGenericAbsenceType();
+ injectAndShowMonth();
+ setMonthPaginatorsAvailability();
})
.then(function () {
vm.loading.page = false;
});
}());
+ /**
+ * Appends a generic absence type that can be used for private
+ * leave requests.
+ */
+ function appendGenericAbsenceType () {
+ vm.supportData.absenceTypes.push({
+ id: '',
+ title: 'Leave',
+ color: '#4D4D68',
+ calculation_unit: _.chain(vm.supportData.calculationUnits)
+ .find({ name: 'days' }).get('value').value()
+ });
+ }
+
/**
* Creates a list of all the months in the currently selected period
*/
@@ -95,30 +129,61 @@ define([
vm.months = months;
}
+ /**
+ * Returns true if the user is an admin or manager.
+ *
+ * @return {Boolean}
+ */
+ function canManageRequests () {
+ return _.includes(['admin', 'manager'], vm.userPermissionRole);
+ }
+
+ /**
+ * Returns a month index in the format "YYYY-MM"
+ *
+ * @param {Moment} dateMoment
+ * @return {String}
+ */
+ function getMonthIndex (dateMoment) {
+ return dateMoment.format('YYYY-MM');
+ }
+
+ /**
+ * Initializes the event listeners
+ */
+ function initListeners () {
+ $rootScope.$on('LeaveCalendar::updateFiltersByAbsenceType', function (event, absenceTypesToFilterBy) {
+ vm.supportData.absenceTypesToFilterBy = absenceTypesToFilterBy;
+
+ sendShowMonthSignal(true);
+ });
+ }
+
/**
* Initializes the scope properties' watchers
*/
function initWatchers () {
$rootScope.$new().$watch(function () {
- return vm.selectedMonths;
+ return vm.selectedMonthIndex;
}, function (newValue, oldValue) {
- if (oldValue !== null && !angular.equals(newValue, oldValue)) {
- sendShowMonthsSignal();
+ if (oldValue !== null && newValue !== oldValue) {
+ setSelectedMonth();
+ setMonthPaginatorsAvailability();
+ sendShowMonthSignal();
}
});
}
/**
- * Injects the leave-calendar-month components
- * and sends the "show months" signal
+ * Injects the leave-calendar-month component and sends the "show month" signal
*
- * @param {Boolean} forceDataReload whether the months need to force data reload
+ * @param {Boolean} forceDataReload whether the month needs a force data reload
*/
- function injectAndShowMonths (forceDataReload) {
- vm.injectMonths = true;
+ function injectAndShowMonth (forceDataReload) {
+ vm.injectMonth = true;
- waitUntilMonthsAre('injected').then(function () {
- sendShowMonthsSignal(forceDataReload);
+ waitUntilMonthIs('injected').then(function () {
+ sendShowMonthSignal(forceDataReload);
}).then(function () {
vm.loading.calendar = false;
});
@@ -155,7 +220,7 @@ define([
});
})
.then(buildPeriodMonthsList)
- .then(setDefaultMonths);
+ .then(setCurrentMonth);
}
/**
@@ -242,17 +307,17 @@ define([
}
/**
- * If the months are already injected, it removes then and then wait
- * for their components to confirme that they are destroyed
+ * If a month is already injected, it removes it and then waits
+ * for its component to confirm that it is destroyed
*
* @return {Promise}
*/
- function makeSureMonthsAreNotInjected () {
+ function makeSureMonthIsNotInjected () {
var promise = $q.resolve();
- if (vm.injectMonths) {
- promise = waitUntilMonthsAre('destroyed');
- vm.injectMonths = false;
+ if (vm.injectMonth) {
+ promise = waitUntilMonthIs('destroyed');
+ vm.injectMonth = false;
}
return promise;
@@ -266,35 +331,68 @@ define([
*/
function monthStructure (dateMoment) {
return {
- index: dateMoment.month(),
+ index: getMonthIndex(dateMoment),
+ month: dateMoment.month(),
year: dateMoment.year(),
name: dateMoment.format('MMMM'),
- shortName: dateMoment.format('MMM')
+ moment: moment().year(dateMoment.year()).month(dateMoment.month())
};
}
/**
- * Reloads the selected months data
+ * Navigates to the current month by setting the current month,
+ * absence period, building months list, updating months paginators
+ * availability and finally refreshing the month component
+ */
+ function navigateToCurrentMonth () {
+ var previousSelectedPeriodId = vm.selectedPeriod.id;
+
+ vm.selectedPeriod = _.find(vm.absencePeriods, function (period) {
+ return !!period.current;
+ });
+
+ (previousSelectedPeriodId !== vm.selectedPeriod.id) && buildPeriodMonthsList();
+ setCurrentMonth();
+ setMonthPaginatorsAvailability();
+ refresh('month');
+ }
+
+ /**
+ * Paginates the currently selected month in a specified direction
+ *
+ * @param {String} direction previous|next
+ */
+ function paginateMonth (direction) {
+ var monthAction = direction === 'previous' ? 'subtract' : 'add';
+ // moment() is used again to ensure we do not mutate the object
+ var dateFromMonth = moment(vm.selectedMonth.moment)[monthAction](1, 'month');
+
+ setSelectedMonth(dateFromMonth);
+ setMonthPaginatorsAvailability();
+ refresh('month');
+ }
+
+ /**
+ * Reloads the selected month's data
*
* If the source of the refresh is a period change, then
* it rebuilds the months list as well
* If the source of the refresh is a change in contacts filters, then
* it reloads the contacts as well
*
- * @param {string} source The source of the refresh (period or contacts change)
+ * @param {String} source The source of the refresh (period or contacts change)
*/
function refresh (source) {
- source = _.includes(['contacts', 'period'], source) ? source : 'period';
+ source = _.includes(['contacts', 'period', 'month'], source) ? source : 'period';
$q.resolve()
+ .then(makeSureMonthIsNotInjected)
+ .then(source === 'period' && buildPeriodMonthsList)
+ .then(source === 'period' && setFirstPeriodMonth)
+ .then(source === 'contacts' && loadContacts)
+ .then(source === 'month' && setMonthPaginatorsAvailability)
.then(function () {
- vm.loading.calendar = true;
- })
- .then(makeSureMonthsAreNotInjected)
- .then(source === 'period' ? buildPeriodMonthsList : _.noop)
- .then(source === 'contacts' ? loadContacts : _.noop)
- .then(function () {
- injectAndShowMonths((source === 'contacts'));
+ injectAndShowMonth((source === 'contacts'));
});
}
@@ -304,21 +402,60 @@ define([
* @param {Boolean} forceDataReload if true, then a month will load its data
* regardless if it had already loaded it
*/
- function sendShowMonthsSignal (forceDataReload) {
- var monthsToShow = !vm.selectedMonths.length
- ? vm.months
- : vm.months.filter(function (month) {
- return _.includes(vm.selectedMonths, month.index);
- });
+ function sendShowMonthSignal (forceDataReload) {
+ $rootScope.$emit('LeaveCalendar::showMonth', !!forceDataReload);
+ }
+
+ /**
+ * Sets the month that is to be selected by default
+ */
+ function setCurrentMonth () {
+ setSelectedMonth(moment());
+ }
+
+ /**
+ * Sets the first month from the currently selected period as the selected month
+ */
+ function setFirstPeriodMonth () {
+ setSelectedMonth(vm.months[0].moment);
+ }
- $rootScope.$emit('LeaveCalendar::showMonths', monthsToShow, !!forceDataReload);
+ /**
+ * Enables or disables the month paginator of a specified direction.
+ * It disables the paginator if there are no months to paginate to.
+ *
+ * @param {String} direction previous|next
+ */
+ function setMonthPaginatorAvailability (direction) {
+ var edgeMonthSelector = direction === 'previous' ? 'first' : 'last';
+ var edgeMonth = _[edgeMonthSelector](vm.months);
+ var edgeMonthMoment = moment().year(edgeMonth.year).month(edgeMonth.month);
+
+ vm.monthPaginatorsAvailability[direction] =
+ !vm.selectedMonth.moment.isSame(edgeMonthMoment, 'month');
}
/**
- * Sets the months that are to be selected by default
+ * Enables or disables the month paginators
*/
- function setDefaultMonths () {
- vm.selectedMonths = [moment().month()];
+ function setMonthPaginatorsAvailability () {
+ setMonthPaginatorAvailability('previous');
+ setMonthPaginatorAvailability('next');
+ }
+
+ /**
+ * Sets the selected month
+ *
+ * @param {Moment} [momentMonth]
+ * If momentMonth parameter is ommited, the month index will not be set
+ * and the selected month will be set from the current set month index value
+ */
+ function setSelectedMonth (momentMonth) {
+ if (momentMonth) {
+ vm.selectedMonthIndex = getMonthIndex(momentMonth);
+ }
+
+ vm.selectedMonth = _.find(vm.months, { index: vm.selectedMonthIndex });
}
/**
@@ -327,33 +464,26 @@ define([
* @return {Promise}
*/
function setUserRole () {
- if (vm.roleOverride) {
- return $q.resolve().then(function () {
- userRole = vm.roleOverride;
- });
- } else {
- return $q.all([
- checkPermissions(sharedSettings.permissions.admin.administer),
- checkPermissions(sharedSettings.permissions.ssp.manage)
- ]).then(function (results) {
- userRole = results[0] ? 'admin' : (results[1] ? 'manager' : 'staff');
- });
- }
+ return $q.all([
+ checkPermissions(sharedSettings.permissions.admin.administer),
+ checkPermissions(sharedSettings.permissions.ssp.manage)
+ ]).then(function (results) {
+ vm.userPermissionRole = results[0] ? 'admin' : (results[1] ? 'manager' : 'staff');
+ userRole = vm.roleOverride ? vm.roleOverride : vm.userPermissionRole;
+ });
}
/**
* Waits until all leave-calendar-month components are
*
+ * @param {String} status
* @return {Promise}
*/
- function waitUntilMonthsAre (status) {
+ function waitUntilMonthIs (status) {
return $q(function (resolve) {
- var monthLoadedCounter = 0;
var removeListener = $rootScope.$on('LeaveCalendar::month' + _.capitalize(status), function () {
- if (++monthLoadedCounter === vm.months.length) {
- removeListener();
- resolve();
- }
+ removeListener();
+ resolve();
});
});
}
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-notification-badge.component.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-notification-badge.component.js
deleted file mode 100644
index d08c8908f84..00000000000
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-notification-badge.component.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/* eslint-env amd */
-
-define([
- 'leave-absences/shared/modules/components'
-], function (components) {
- components.component('leaveNotificationBadge', {
- bindings: {
- filters: '<',
- refreshCountEventName: '<'
- },
- templateUrl: ['shared-settings', function (sharedSettings) {
- return sharedSettings.sharedPathTpl + 'components/leave-notification-badge.html';
- }],
- controllerAs: 'badge',
- controller: LeaveNotificationBadgeController
- });
-
- LeaveNotificationBadgeController.$inject = ['$log', 'pubSub', 'LeaveRequest'];
-
- function LeaveNotificationBadgeController ($log, pubSub, LeaveRequest) {
- $log.debug('Component: leave-notification-badge');
-
- var vm = this;
- vm.count = 0;
-
- (function init () {
- initListeners();
- fetchCount();
- })();
-
- /**
- * Fetch count of leave requests which matches the filter
- *
- * @return {Promise}
- */
- function fetchCount () {
- return LeaveRequest.all(vm.filters, null, null, null, false)
- .then(function (leaveRequests) {
- vm.count = leaveRequests.list.length;
- });
- }
-
- /**
- * Initializes the event listeners
- */
- function initListeners () {
- pubSub.subscribe(vm.refreshCountEventName, fetchCount);
- }
- }
-});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-request-actions.component.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-request-actions.component.js
index 609dec4a0ff..42eada5272f 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-request-actions.component.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-request-actions.component.js
@@ -268,11 +268,10 @@ define([
* @param {Object} leaveRequest
* @param {String} leaveType
* @param {String} selectedContactId
- * @param {Boolean} isSelfRecord
*/
- function openLeavePopup (event, leaveRequest, leaveType, selectedContactId, isSelfRecord) {
+ function openLeavePopup (event, leaveRequest, leaveType, selectedContactId) {
event.stopPropagation();
- LeavePopup.openModal(leaveRequest, leaveType, selectedContactId, isSelfRecord);
+ LeavePopup.openModal(leaveRequest, leaveType, selectedContactId);
}
/**
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-request-record-actions.component.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-request-record-actions.component.js
index 8f7cf0ec81e..cb7fd715143 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-request-record-actions.component.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/leave-request-record-actions.component.js
@@ -8,7 +8,6 @@ define([
components.component('leaveRequestRecordActions', {
bindings: {
contactId: '<',
- isSelfRecord: '<',
selectedContactId: '<'
},
templateUrl: ['shared-settings', function (sharedSettings) {
@@ -39,7 +38,7 @@ define([
queryParams = beforeHashQueryParams.parse();
if (queryParams.openModal) {
- openLeavePopup(null, queryParams.openModal, vm.selectedContactId, vm.isSelfRecord);
+ openLeavePopup(null, queryParams.openModal, vm.selectedContactId);
}
}());
@@ -49,9 +48,8 @@ define([
* @param {Object} leaveRequest
* @param {String} leaveType
* @param {String} selectedContactId
- * @param {Boolean} isSelfRecord
*/
- function openLeavePopup (leaveRequest, leaveType, selectedContactId, isSelfRecord) {
+ function openLeavePopup (leaveRequest, leaveType, selectedContactId) {
LeavePopup.openModal.apply(LeavePopup, arguments);
}
}
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/manage-leave-requests.component.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/manage-leave-requests.component.js
index ad438c9f138..d57c23464d7 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/manage-leave-requests.component.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/manage-leave-requests.component.js
@@ -22,8 +22,8 @@ define([
];
function ManageLeaveRequestsController ($log, $q, $rootScope, Contact,
- checkPermissions, OptionGroup, sharedSettings, AbsencePeriod, AbsenceType,
- LeaveRequest, LeavePopup, pubSub) {
+ checkPermissions, OptionGroup, sharedSettings, AbsencePeriod, AbsenceType,
+ LeaveRequest, LeavePopup, pubSub) {
'use strict';
$log.debug('Component: manage-leave-requests');
@@ -403,9 +403,8 @@ define([
* @param {Object} leaveRequest
* @param {String} leaveType
* @param {String} selectedContactId
- * @param {Boolean} isSelfRecord
*/
- function openLeavePopup (leaveRequest, leaveType, selectedContactId, isSelfRecord) {
+ function openLeavePopup (leaveRequest, leaveType, selectedContactId) {
LeavePopup.openModal.apply(LeavePopup, arguments);
}
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/staff-leave-report.component.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/staff-leave-report.component.js
index f48b81b8c2f..22833255a0f 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/staff-leave-report.component.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/components/staff-leave-report.component.js
@@ -72,6 +72,7 @@ define([
loadBalanceChanges()
]);
})
+ .then(processAbsenceTypes)
.then(function () {
vm.loading.content = false;
});
@@ -92,6 +93,25 @@ define([
section.dataIndex[leaveRequest.id] = leaveRequest;
}
+ /**
+ * Attaches the entitlement information to the absence type it belongs to
+ * If there is no entitlement for a given absence type, a default
+ * entitlement object is assigned instead
+ */
+ function attachEntitlementsToAbsenceTypes () {
+ vm.absenceTypes = vm.absenceTypes.map(function (absenceType) {
+ var entitlement = _.find(vm.entitlements, function (entitlement) {
+ return entitlement.type_id === absenceType.id;
+ });
+
+ // set entitlement to 0 if no entitlement is present
+ absenceType.entitlement = entitlement ? entitlement.value : 0;
+ absenceType.remainder = entitlement ? entitlement.remainder : { current: 0, future: 0 };
+
+ return absenceType;
+ });
+ }
+
/**
* Handles the cancel status update of leave request by removing them from
* their current section and adding them to the "Cancelled and Other" section
@@ -116,6 +136,18 @@ define([
});
}
+ /**
+ * Filters the absence types, keeping only the ones that either have
+ * an entitlement greater than 0 or that allow overuse or accrual requests
+ */
+ function filterAbsenceTypes () {
+ vm.absenceTypesFiltered = vm.absenceTypes.filter(function (absenceType) {
+ return !((absenceType.entitlement === 0) &&
+ (absenceType.allow_overuse !== '1') &&
+ (absenceType.allow_accruals_request !== '1'));
+ });
+ }
+
/**
* Forwards the status update event to a specific status handler. If none
* exists for the given status, a refresh is triggered.
@@ -200,32 +232,43 @@ define([
* @return {Promise}
*/
function loadBalanceChanges () {
+ var basicParams = { contact_id: vm.contactId, period_id: vm.selectedPeriod.id };
+
return $q.all([
- LeaveRequest.balanceChangeByAbsenceType(vm.contactId, vm.selectedPeriod.id, null, true),
- LeaveRequest.balanceChangeByAbsenceType(vm.contactId, vm.selectedPeriod.id, [
- valueOfRequestStatus(sharedSettings.statusNames.approved)
- ]),
- LeaveRequest.balanceChangeByAbsenceType(vm.contactId, vm.selectedPeriod.id, [
- valueOfRequestStatus(sharedSettings.statusNames.awaitingApproval),
- valueOfRequestStatus(sharedSettings.statusNames.moreInformationRequired)
- ])
+ LeaveRequest.balanceChangeByAbsenceType(_.assign({}, basicParams, {
+ public_holiday: true
+ })),
+ LeaveRequest.balanceChangeByAbsenceType(_.assign({}, basicParams, {
+ expired: true
+ })),
+ LeaveRequest.balanceChangeByAbsenceType(_.assign({}, basicParams, {
+ statuses: {
+ in: [ valueOfRequestStatus(sharedSettings.statusNames.approved) ]
+ }
+ })),
+ LeaveRequest.balanceChangeByAbsenceType(_.assign({}, basicParams, {
+ statuses: {
+ in: [
+ valueOfRequestStatus(sharedSettings.statusNames.awaitingApproval),
+ valueOfRequestStatus(sharedSettings.statusNames.moreInformationRequired)
+ ]
+ }
+ }))
])
.then(function (results) {
vm.absenceTypes.forEach(function (absenceType) {
absenceType.balanceChanges = {
holidays: results[0][absenceType.id],
- approved: results[1][absenceType.id],
- pending: results[2][absenceType.id]
+ expired: results[1][absenceType.id],
+ approved: results[2][absenceType.id],
+ pending: results[3][absenceType.id]
};
});
});
}
/**
- * Loads the entitlements, including current and future balance,
- * and groups the entitlements value and remainder by absence type
- * Also Filters the absence types which allows overuse or allows
- * accrual request or has entitlement more than 0
+ * Loads the entitlements, including current and future balance
*
* @return {Promise}
*/
@@ -236,21 +279,6 @@ define([
}, true)
.then(function (entitlements) {
vm.entitlements = entitlements;
- })
- .then(function () {
- vm.absenceTypesFiltered = vm.absenceTypes.filter(function (absenceType) {
- var entitlement = _.find(vm.entitlements, function (entitlement) {
- return entitlement.type_id === absenceType.id;
- });
-
- // set entitlement to 0 if no entitlement is present
- absenceType.entitlement = entitlement ? entitlement.value : 0;
- absenceType.remainder = entitlement ? entitlement.remainder : { current: 0, future: 0 };
-
- return !((absenceType.entitlement === 0) &&
- (absenceType.allow_overuse !== '1') &&
- (absenceType.allow_accruals_request !== '1'));
- });
});
}
@@ -417,6 +445,14 @@ define([
});
}
+ /**
+ * Process the list of absence types objects by augmenting and filter them
+ */
+ function processAbsenceTypes () {
+ attachEntitlementsToAbsenceTypes();
+ filterAbsenceTypes();
+ }
+
/**
* For each breakdowns, it sets the absence type id to
* each list entry (based on the entitlement they belong to)
@@ -447,21 +483,24 @@ define([
}
/**
- * Process each expired TOIL requests
+ * Process each expired TOIL request, so that they have the same
+ * key properties that an entitlement breakdown object has, given that
+ * they need to be listed in the same section
*
- * @param {Array} list of expired TOIL request
- * @return {Promise} resolves to the flatten list
+ * @param {Array} toils
+ * @return {Promise} resolves to {Array}
*/
- function processExpiredTOILS (list) {
+ function processExpiredTOILS (toils) {
return $q.resolve()
.then(function () {
- return list.map(function (listEntry) {
- return {
- 'expiry_date': listEntry.toil_expiry_date,
- 'type': {
+ return toils.map(function (toil) {
+ return _.assign({}, toil, {
+ expiry_date: toil.toil_expiry_date,
+ amount: toil.toil_to_accrue,
+ type: {
'label': 'Accrued TOIL'
}
- };
+ });
});
});
}
@@ -534,6 +573,7 @@ define([
loadEntitlements(),
loadBalanceChanges()
])
+ .then(processAbsenceTypes)
.then(function () {
vm.loading.content = false;
})
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/request.controller.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/request.controller.js
index 852a7e022ad..f62756b8d26 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/request.controller.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/request.controller.js
@@ -95,12 +95,15 @@ define([
initAvailableStatusesMatrix();
initListeners();
- return $q.all([
- loadLoggedInContactId(),
- initRoles(),
- loadAbsencePeriods(),
- loadStatuses()
- ])
+ return loadLoggedInContactId()
+ .then(initIsSelfRecord)
+ .then(function () {
+ return $q.all([
+ initRoles(),
+ loadAbsencePeriods(),
+ loadStatuses()
+ ]);
+ })
.then(initRequest)
.then(setModalMode)
.then(setInitialAbsencePeriod)
@@ -273,6 +276,17 @@ define([
});
}
+ /**
+ * Checks if request dates and times need to be reverted to the original state.
+ * They need to be reverted if the balance has not been changed for all requests
+ * except TOIL because its balance is independent from the dates and times.
+ *
+ * @return {Boolean}
+ */
+ function checkIfRequestDatesAndTimesNeedToBeReverted () {
+ return getLeaveType() !== 'toil' && !vm.request.change_balance;
+ }
+
/**
* Closes the error alerts if any
*/
@@ -509,6 +523,19 @@ define([
return $q.resolve();
}
+ /**
+ * Initializes the is self record property and sets it to true when
+ * on My Leave section and the user is editing their own request or creating
+ * a new one for themselves.
+ */
+ function initIsSelfRecord () {
+ var isSectionMyLeave = $rootScope.section === 'my-leave';
+ var isMyOwnRequest = +loggedInContactId === +_.get(vm, 'leaveRequest.contact_id');
+ var isNewRequest = !_.get(vm, 'leaveRequest.id');
+
+ vm.isSelfRecord = isSectionMyLeave && (isMyOwnRequest || isNewRequest);
+ }
+
/**
* Initialises listeners
*/
@@ -585,7 +612,7 @@ define([
// If the user is creating or editing their own leave, they will be
// treated as a staff regardless of their actual role.
- if ($rootScope.section === 'my-leave') {
+ if (vm.isSelfRecord) {
return;
}
@@ -931,7 +958,7 @@ define([
return vm.request.isValid()
.then(isBalanceChangeRecalculationNeeded() && checkIfBalanceChangeHasChanged)
.then(decideIfBalanceChangeNeedsAForceRecalculation)
- .then(!vm.request.change_balance && revertRequestOriginalDatesAndTimes)
+ .then(checkIfRequestDatesAndTimesNeedToBeReverted() && revertRequestOriginalDatesAndTimes)
.then(submitAllTabs)
.then(function () {
return vm.isMode('edit') ? updateRequest() : createRequest();
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/sub-controllers/leave-calendar-admin.controller.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/sub-controllers/leave-calendar-admin.controller.js
index 88a456d8e1f..cd9be49e531 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/sub-controllers/leave-calendar-admin.controller.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/sub-controllers/leave-calendar-admin.controller.js
@@ -8,12 +8,14 @@ define([
'common/services/notification.service'
], function (_, moment, controllers) {
controllers.controller('LeaveCalendarAdminController', ['$log', '$q',
- 'Contact', 'ContactInstance', 'Contract', 'notificationService', controller]);
+ 'Contact', 'ContactInstance', 'Contract', 'notificationService',
+ 'LeaveCalendarService', controller]);
- function controller ($log, $q, Contact, ContactInstance, Contract, notification) {
+ function controller ($log, $q, Contact, ContactInstance, Contract, notification,
+ LeaveCalendarService) {
$log.debug('LeaveCalendarAdminController');
- var contracts, vm;
+ var leaveCalendar, vm;
return {
/**
@@ -22,15 +24,11 @@ define([
*/
init: function (_vm_) {
vm = _vm_;
+ leaveCalendar = LeaveCalendarService.init(vm);
+ vm.filters.userSettings.assignedTo = _.find(vm.filtersByAssignee, { type: 'me' });
vm.showContactDetailsLink = true;
vm.showContactName = true;
vm.showFilters = true;
- vm.filtersByAssignee = [
- { type: 'me', label: 'People I approve' },
- { type: 'unassigned', label: 'People without approver' },
- { type: 'all', label: 'All' }
- ];
- vm.filters.userSettings.assignedTo = vm.filtersByAssignee[0];
vm.showAdminFilteringHint = showAdminFilteringHint;
@@ -38,31 +36,6 @@ define([
}
};
- /**
- * Get contact IDs filtered according to contracts that belong
- * to the currently selected absence period
- *
- * @return {Promise} resolved to contacts list
- */
- function loadContactIdsToReduceTo () {
- return loadContracts()
- .then(function (contracts) {
- var contractsInAbsencePeriod = contracts.filter(function (contract) {
- var details = contract.info.details;
-
- return (
- moment(details.period_start_date).isSameOrBefore(vm.selectedPeriod.end_date) &&
- (moment(details.period_end_date).isSameOrAfter(vm.selectedPeriod.start_date) ||
- !details.period_end_date)
- );
- });
-
- return _.uniq(contractsInAbsencePeriod.map(function (contract) {
- return contract.contact_id;
- }));
- });
- }
-
/**
* Returns the api of the sub-controller
*
@@ -76,89 +49,7 @@ define([
* @return {Promise} resolves as an {Array}
*/
loadContacts: function () {
- var filterByAssignee = vm.filters.userSettings.assignedTo.type;
-
- return lookupContacts(filterByAssignee)
- .then(function (contacts) {
- vm.lookupContacts = contacts;
- })
- .then(function () {
- return (filterByAssignee !== 'me'
- ? loadContactIdsToReduceTo() : $q.resolve(null));
- })
- .then(function (contactIdsToReduceTo) {
- vm.contactIdsToReduceTo = contactIdsToReduceTo;
-
- return loadContacts();
- })
- .then(function (contacts) {
- return contacts;
- });
- }
- };
- }
-
- /**
- * Load all contacts with respect to filters
- *
- * @return {Promise}
- */
- function loadContacts () {
- return Contact.all(prepareContactFilters(), null, 'display_name')
- .then(function (contacts) {
- return contacts.list;
- });
- }
-
- /**
- * Load all contracts or retrieve them from cache
- *
- * @return {Promise}
- */
- function loadContracts () {
- return contracts ? $q.resolve(contracts) : Contract.all();
- }
-
- /**
- * Returns the loading contacts promise depending on the
- * filter by assignee chosen
- *
- * @param {String} filterByAssignee (me|unassigned|all)
- * @return {Promise} resolved to a list of loaded contacts
- */
- function lookupContacts (filterByAssignee) {
- if (filterByAssignee === 'me') {
- return Contact.leaveManagees(vm.contactId);
- } else if (filterByAssignee === 'unassigned') {
- return Contact.leaveManagees(undefined, {
- unassigned: true
- });
- } else {
- return Contact.all().then(function (contacts) {
- return contacts.list;
- });
- }
- }
-
- /**
- * Returns the filter object for contacts api
- *
- * @TODO This function should be a part of a Filter component, which is planned for future
- *
- * @return {Object}
- */
- function prepareContactFilters () {
- return {
- department: vm.filters.userSettings.department ? vm.filters.userSettings.department.value : null,
- level_type: vm.filters.userSettings.level_type ? vm.filters.userSettings.level_type.value : null,
- location: vm.filters.userSettings.location ? vm.filters.userSettings.location.value : null,
- region: vm.filters.userSettings.region ? vm.filters.userSettings.region.value : null,
- id: {
- 'IN': vm.filters.userSettings.contact
- ? [vm.filters.userSettings.contact.id]
- : vm.lookupContacts.map(function (contact) {
- return contact.id;
- })
+ return leaveCalendar.loadContactsForAdmin();
}
};
}
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/sub-controllers/leave-calendar-manager.controller.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/sub-controllers/leave-calendar-manager.controller.js
index 279c60aef90..226646d3179 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/sub-controllers/leave-calendar-manager.controller.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/sub-controllers/leave-calendar-manager.controller.js
@@ -6,12 +6,12 @@ define([
'leave-absences/shared/modules/controllers'
], function (_, moment, controllers) {
controllers.controller('LeaveCalendarManagerController', ['$log', 'Contact',
- 'ContactInstance', controller]);
+ 'ContactInstance', 'LeaveCalendarService', controller]);
- function controller ($log, Contact, ContactInstance) {
+ function controller ($log, Contact, ContactInstance, LeaveCalendarService) {
$log.debug('LeaveCalendarManagerController');
- var vm;
+ var leaveCalendar, vm;
return {
/**
@@ -20,6 +20,8 @@ define([
*/
init: function (_vm_) {
vm = _vm_;
+ leaveCalendar = LeaveCalendarService.init(vm);
+ vm.filters.userSettings.assignedTo = _.find(vm.filtersByAssignee, { type: 'me' });
vm.showContactName = true;
vm.showFilters = true;
@@ -40,47 +42,7 @@ define([
* @return {Promise} resolves as an {Array}
*/
loadContacts: function () {
- return ContactInstance.init({ id: vm.contactId })
- .leaveManagees()
- .then(function (contacts) {
- vm.lookupContacts = contacts;
- })
- .then(loadContacts);
- }
- };
- }
-
- /**
- * Load all contacts with respect to filters
- *
- * @return {Promise}
- */
- function loadContacts () {
- return Contact.all(prepareContactFilters(), null, 'display_name')
- .then(function (contacts) {
- return contacts.list;
- });
- }
-
- /**
- * Returns the filter object for contacts api
- *
- * @TODO This function should be a part of a Filter component, which is planned for future
- *
- * @return {Object}
- */
- function prepareContactFilters () {
- return {
- department: vm.filters.userSettings.department ? vm.filters.userSettings.department.value : null,
- level_type: vm.filters.userSettings.level_type ? vm.filters.userSettings.level_type.value : null,
- location: vm.filters.userSettings.location ? vm.filters.userSettings.location.value : null,
- region: vm.filters.userSettings.region ? vm.filters.userSettings.region.value : null,
- id: {
- 'IN': vm.filters.userSettings.contact
- ? [vm.filters.userSettings.contact.id]
- : vm.lookupContacts.map(function (contact) {
- return contact.id;
- })
+ return leaveCalendar.loadLookUpAndFilteredContacts();
}
};
}
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/sub-controllers/leave-calendar-staff.controller.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/sub-controllers/leave-calendar-staff.controller.js
index 4e364514ae5..f2ec7dd1a50 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/sub-controllers/leave-calendar-staff.controller.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/controllers/sub-controllers/leave-calendar-staff.controller.js
@@ -5,12 +5,13 @@ define([
'common/moment',
'leave-absences/shared/modules/controllers'
], function (_, moment, controllers) {
- controllers.controller('LeaveCalendarStaffController', ['$log', 'Contact', controller]);
+ controllers.controller('LeaveCalendarStaffController', ['$log', '$q', 'Contact',
+ 'LeaveCalendarService', controller]);
- function controller ($log, Contact) {
+ function controller ($log, $q, Contact, LeaveCalendarService) {
$log.debug('LeaveCalendarStaffController');
- var vm;
+ var leaveCalendar, vm;
return {
/**
@@ -19,7 +20,16 @@ define([
*/
init: function (_vm_) {
vm = _vm_;
- vm.filters.userSettings.contacts_with_leaves = false;
+ leaveCalendar = LeaveCalendarService.init(vm);
+ vm.filters.userSettings.contacts_with_leaves = true;
+ vm.showTheseContacts = [vm.contactId];
+ vm.showContactName = true;
+ vm.showFilters = true;
+
+ if (vm.displaySingleContact) {
+ vm.showFilters = false;
+ vm.lookupContacts = [{ id: vm.contactId }];
+ }
return api();
}
@@ -33,20 +43,22 @@ define([
function api () {
return {
/**
- * Returns the data of the current contact
+ * Returns the data of the current contact.
*
- * It returns it as a single-item array to comply with the standard
- * structure leave-calendar expect to receive the contacts as
+ * It displays a list of contacts taking leave for the current selected
+ * period. If the display single contact property is set, it will only
+ * fetch the information for the contact provided.
*
* @return {Promise} resolves as an {Array}
*/
loadContacts: function () {
- return Contact.all({
- id: { in: [vm.contactId] }
- })
- .then(function (contacts) {
- return contacts.list;
- });
+ if (vm.displaySingleContact) {
+ return leaveCalendar.loadFilteredContacts();
+ } else if (vm.userPermissionRole === 'admin') {
+ return leaveCalendar.loadContactsForAdmin();
+ } else {
+ return leaveCalendar.loadLookUpAndFilteredContacts();
+ }
}
};
}
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/models/leave-request.model.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/models/leave-request.model.js
index 3b4389db882..bf34204f17a 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/models/leave-request.model.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/models/leave-request.model.js
@@ -22,10 +22,10 @@ define([
* Get all the Leave Requests.
* It supports filters, pagination, sort and extra params
*
- * @param {object} filters - Values the full list should be filtered by
- * @param {object} pagination
+ * @param {Object} filters Values the full list should be filtered by
+ * @param {Object} pagination
* `page` for the current page, `size` for number of items per page
- * @param {string} sort - The field and direction to order by
+ * @param {String} sort The field and direction to order by
* @param {Object} params
* @param {Boolean} cache
* @return {Promise} resolves with {Object}
@@ -45,23 +45,18 @@ define([
* Get all the total change in balance that is caused by the
* leave requests of a given absence type, or of all the absence types of a given contact and period.
*
- * @param {string} contactId The ID of the Contact to get the balance change for
- * @param {string} periodId The ID of the Absence Period to get the balance change for
- * @param statuses {array} An array of OptionValue values which the list will be filtered by
- * @param isPublicHoliday {boolean} Based on the value of this param,
- * the calculation will include only the leave requests that aren't/are public holidays
- * @return {Promise} Resolved with {Object} Balance Change data
+ * @param {Object} filters
+ * @return {Promise} Resolves with {Object} Balance Change data
*/
- balanceChangeByAbsenceType: function (contactId, periodId, statuses, isPublicHoliday) {
- return leaveRequestAPI.balanceChangeByAbsenceType(contactId, periodId, statuses, isPublicHoliday);
+ balanceChangeByAbsenceType: function (filters) {
+ return leaveRequestAPI.balanceChangeByAbsenceType(this.processFilters(filters));
},
/**
* Get leave request for the given id
*
- * @param {object} id - leave request id
- *
- * @return {Promise} resolves with {Object}
+ * @param {Object} id leave request id
+ * @return {Promise} Resolves with {Object}
*/
find: function (id) {
return leaveRequestAPI.find(id)
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/services/leave-calendar.service.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/services/leave-calendar.service.js
new file mode 100644
index 00000000000..77e191dca04
--- /dev/null
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/services/leave-calendar.service.js
@@ -0,0 +1,209 @@
+/* eslint-env amd */
+
+define([
+ 'common/lodash',
+ 'common/moment',
+ 'leave-absences/shared/modules/services'
+], function (_, moment, services) {
+ 'use strict';
+
+ services.factory('LeaveCalendarService', LeaveCalendarService);
+
+ LeaveCalendarService.$inject = [
+ '$log', '$q', 'Contact', 'Contract'
+ ];
+
+ function LeaveCalendarService ($log, $q, Contact, Contract) {
+ $log.debug('LeaveCalendarService');
+
+ /**
+ * Returns a list of common functions that are sahred between leave calendar
+ * sub controller.
+ *
+ * @param {Object} vm - the sub controller's view model.
+ * @return {Object} a collection of functions.
+ */
+ function init (vm) {
+ var contracts;
+ var contactsLookUpStrategies = {
+ all: loadAllContacts,
+ me: loadMyManagees,
+ unassigned: loadAllUnassignedContacts
+ };
+
+ return {
+ loadContactsForAdmin: loadContactsForAdmin,
+ loadFilteredContacts: loadFilteredContacts,
+ loadLookUpContacts: loadLookUpContacts,
+ loadLookUpAndFilteredContacts: loadLookUpAndFilteredContacts
+ };
+
+ /**
+ * Returns a promise of all the contacts that can be used for look up
+ * against the ids to reduce by.
+ *
+ * @return {Promise} resolves to an array of contacts.
+ */
+ function loadAllContacts () {
+ return Contact.all().then(function (contacts) {
+ return contacts.list;
+ });
+ }
+
+ /**
+ * Returns a promise of all the contacts that are not assigned to another
+ * contact.
+ *
+ * @return {Promise} resolves to an array of contacts.
+ */
+ function loadAllUnassignedContacts () {
+ return Contact.leaveManagees(undefined, {
+ unassigned: true
+ });
+ }
+
+ /**
+ * Returns contacts depending on the selected assignation type (my assignees,
+ * unnassigned contacts, or all contacts) and stores a list of lookup contact
+ * ids to based on contacts with active contracts.
+ *
+ * @return {Promise} resolves to an array of contacts.
+ */
+ function loadContactsForAdmin () {
+ var filterByAssignee = _.get(vm, 'filters.userSettings.assignedTo.type', 'all');
+
+ return loadLookUpContacts()
+ .then(function (contacts) {
+ vm.lookupContacts = contacts;
+
+ return $q.all([
+ loadFilteredContacts(),
+ filterByAssignee !== 'me'
+ ? loadContactIdsToReduceTo()
+ : $q.resolve(null)
+ ]);
+ })
+ .then(function (results) {
+ var contacts = results[0];
+
+ vm.contactIdsToReduceTo = results[1];
+
+ return contacts;
+ });
+ }
+
+ /**
+ * Returns a promise of a list of contact ids for contacts with contracts
+ * that are valid for the selected period's start and end dates.
+ *
+ * @return {Promise} resolves to an array of contact ids.
+ */
+ function loadContactIdsToReduceTo () {
+ return loadContracts()
+ .then(function (contracts) {
+ var contractsInAbsencePeriod = contracts.filter(function (contract) {
+ var details = contract.info.details;
+
+ return (
+ moment(details.period_start_date).isSameOrBefore(vm.selectedPeriod.end_date) &&
+ (moment(details.period_end_date).isSameOrAfter(vm.selectedPeriod.start_date) ||
+ !details.period_end_date)
+ );
+ });
+
+ return _.uniq(contractsInAbsencePeriod.map(function (contract) {
+ return contract.contact_id;
+ }));
+ });
+ }
+
+ /**
+ * Returns a list of all contracts. The result is cached locally.
+ *
+ * @return {Promise} resolves to an array of contracts.
+ */
+ function loadContracts () {
+ return contracts ? $q.resolve(contracts) : Contract.all();
+ }
+
+ /**
+ * Returns a list of contacts reduced by the leave calendar filters and
+ * sorts them by the contact's display name.
+ *
+ * @return {Promise} resolves to an array of contacts.
+ */
+ function loadFilteredContacts () {
+ return Contact.all(prepareContactFilters(), null, 'display_name')
+ .then(function (contacts) {
+ return contacts.list;
+ });
+ }
+
+ /**
+ * Loads a list of contacts that can be used for look up. The list is based
+ * on the assignees type filter.
+ *
+ * @return {Promise} resolves to an array of contacts.
+ */
+ function loadLookUpContacts () {
+ var filterByAssignee = _.get(vm, 'filters.userSettings.assignedTo.type', 'all');
+ var loadLookUpContactsMethod = contactsLookUpStrategies[filterByAssignee];
+
+ return loadLookUpContactsMethod();
+ }
+
+ /**
+ * Requests a list of look up contacts, stores them, and then returns
+ * a list of filtered contacts based on the previously stored look ups.
+ *
+ * @return {Promise} resolve to an array of contacts.
+ */
+ function loadLookUpAndFilteredContacts () {
+ return loadLookUpContacts()
+ .then(function (lookupContacts) {
+ vm.lookupContacts = lookupContacts;
+ })
+ .then(loadFilteredContacts);
+ }
+
+ /**
+ * Returns a promise of all the contacts that are managed by the logged in
+ * user.
+ *
+ * @return {Promise} resolves to an array of contacts.
+ */
+ function loadMyManagees () {
+ return Contact.leaveManagees(vm.contactId);
+ }
+
+ /**
+ * Returns a map of filters to pass to the Contact API.
+ *
+ * @return {Object}
+ */
+ function prepareContactFilters () {
+ var filters = {
+ department: _.get(vm, 'filters.userSettings.department.value', null),
+ level_type: _.get(vm, 'filters.userSettings.level_type.value', null),
+ location: _.get(vm, 'filters.userSettings.location.value', null),
+ region: _.get(vm, 'filters.userSettings.region.value', null)
+ };
+ var hasContactFilter = !!vm.filters.userSettings.contact;
+ var hasLookUpContactsFilter = _.isArray(vm.lookupContacts) && vm.lookupContacts.length;
+ var notRequestingAllContacts = _.get(vm, 'filters.userSettings.assignedTo.type', 'all') !== 'all';
+
+ if (hasContactFilter) {
+ filters.id = { 'IN': [vm.filters.userSettings.contact.id] };
+ } else if (notRequestingAllContacts || hasLookUpContactsFilter) {
+ filters.id = { 'IN': _.pluck(vm.lookupContacts, 'id') };
+ }
+
+ return filters;
+ }
+ }
+
+ return {
+ init: init
+ };
+ }
+});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/services/leave-popup.service.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/services/leave-popup.service.js
index 02088efcfb3..9bf45fe7648 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/services/leave-popup.service.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/src/leave-absences/shared/services/leave-popup.service.js
@@ -47,10 +47,9 @@ define([
* @param {String} leaveType
* @param {String} selectedContactId - Contact ID for the contact dropdown
* when the manager/admin is opening the request
- * @param {Boolean} isSelfRecord - True If the owner is opening the leave request
* @param {Boolean} forceRecalculateBalanceChange optional
*/
- function openModal (leaveRequest, leaveType, selectedContactId, isSelfRecord, forceRecalculateBalanceChange) {
+ function openModal (leaveRequest, leaveType, selectedContactId, forceRecalculateBalanceChange) {
$modal.open({
appendTo: $rootElement.children().eq(0),
templateUrl: sharedSettings.sharedPathTpl + 'components/leave-request-popup/leave-request-popup.html',
@@ -63,7 +62,6 @@ define([
leaveType: leaveType,
leaveRequest: leaveRequest,
selectedContactId: selectedContactId,
- isSelfRecord: isSelfRecord,
forceRecalculateBalanceChange: forceRecalculateBalanceChange
};
},
@@ -88,7 +86,7 @@ define([
return checkPermissionBeforeOpeningPopup(leaveRequest)
.then(function (hasPermission) {
if (hasPermission) {
- openModal(leaveRequest, leaveRequest.request_type, leaveRequest.contact_id, $rootScope.section === 'my-leave');
+ openModal(leaveRequest, leaveRequest.request_type, leaveRequest.contact_id);
} else {
notification.error('Error', 'You dont have permission to see this leave request');
}
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/manager-notification-badge/component/manager-notification-badge.component.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/manager-notification-badge/component/manager-notification-badge.component.spec.js
index 9d3320f66d0..efffec59320 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/manager-notification-badge/component/manager-notification-badge.component.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/manager-notification-badge/component/manager-notification-badge.component.spec.js
@@ -30,11 +30,14 @@ define([
expect($log.debug).toHaveBeenCalled();
});
- it('sets the filter with manager id and status id', function () {
- expect(controller.filters).toEqual({
- managed_by: window.Drupal.settings.currentCiviCRMUserId,
- status_id: '3'
- });
+ it('sets the filter with manager id and status id for leave request', function () {
+ expect(controller.filters).toEqual([{
+ apiName: 'LeaveRequest',
+ params: {
+ managed_by: window.Drupal.settings.currentCiviCRMUserId,
+ status_id: '3'
+ }
+ }]);
});
function compileComponent () {
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/mocks/services/leave-calendar.service.mock.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/mocks/services/leave-calendar.service.mock.js
new file mode 100644
index 00000000000..ae7f76f2d56
--- /dev/null
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/mocks/services/leave-calendar.service.mock.js
@@ -0,0 +1,79 @@
+/* eslint-env amd, jasmine */
+
+define([
+ 'common/lodash',
+ 'common/mocks/data/contact.data'
+], function (_, contactsMockData) {
+ var $q, vm;
+ var LeaveCalendarService = jasmine.createSpyObj('LeaveCalendarService', ['init']);
+ var leaveCalendarInstance = jasmine.createSpyObj('leaveCalendarInstance', [
+ 'loadContactsForAdmin', 'loadFilteredContacts', 'loadLookUpContacts',
+ 'loadLookUpAndFilteredContacts']);
+ var data = {
+ contactIdsToReduceTo: [_.uniqueId(), _.uniqueId(), _.uniqueId()],
+ filteredContacts: _.clone(contactsMockData.all.values.slice(0, 2)),
+ lookedUpContacts: _.clone(contactsMockData.all.values)
+ };
+
+ /**
+ * Simulates the init function of the leave calendar service. It stores the view
+ * model reference and returns a leave calendar instance.
+ *
+ * @param {Object} vm the view model containing the filters information, selected period, etc.
+ * @return {Object} a mock leave calendar instance.
+ */
+ function LeaveCalendarServiceInit (_vm_) {
+ vm = _vm_;
+
+ return leaveCalendarInstance;
+ }
+
+ /**
+ * Simulates a call to the leave calendar instance's loadContactsForAdmin method.
+ * It assigns the contact ids to reduce to and the look up contacts to the previously provided
+ * view model. Finally, it returns a list of filtered contacts.
+ *
+ * @return {Promise} resolves to a list of contacts.
+ */
+ function loadContactsForAdmin () {
+ vm.contactIdsToReduceTo = data.contactIdsToReduceTo;
+
+ return loadLookUpAndFilteredContacts();
+ }
+
+ /**
+ * Simulates a call to the leave calendar instance's loadLookUpAndFilteredContacts
+ * method. It stores a list of look up contacts and returns a list contacts.
+ *
+ * @return {Promise} resolves to a list of contacts.
+ */
+ function loadLookUpAndFilteredContacts () {
+ vm.lookupContacts = data.lookedUpContacts;
+
+ return $q.resolve(data.filteredContacts);
+ }
+
+ /**
+ * Initializes the leave calendar service mock for testing purposes.
+ *
+ * @param $q the angular $q service.
+ */
+ function setup (_$q_) {
+ $q = _$q_;
+ LeaveCalendarService.init.and.callFake(LeaveCalendarServiceInit);
+ leaveCalendarInstance.loadContactsForAdmin.and.callFake(loadContactsForAdmin);
+ leaveCalendarInstance.loadFilteredContacts.and.returnValue($q.resolve(
+ data.filteredContacts));
+ leaveCalendarInstance.loadLookUpContacts.and.returnValue($q.resolve(
+ data.lookedUpContacts));
+ leaveCalendarInstance.loadLookUpAndFilteredContacts.and
+ .callFake(loadLookUpAndFilteredContacts);
+ }
+
+ return {
+ data: data,
+ instance: leaveCalendarInstance,
+ service: LeaveCalendarService,
+ setup: setup
+ };
+});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/apis/leave-request.api.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/apis/leave-request.api.spec.js
index bce43676dae..a9b70933bd3 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/apis/leave-request.api.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/apis/leave-request.api.spec.js
@@ -87,7 +87,12 @@ define([
var statuses = [jasmine.any(String), jasmine.any(String), jasmine.any(String)];
beforeEach(function () {
- promise = LeaveRequestAPI.balanceChangeByAbsenceType(jasmine.any(String), jasmine.any(String), statuses, true);
+ promise = LeaveRequestAPI.balanceChangeByAbsenceType({
+ contact_id: jasmine.any(String),
+ period_id: jasmine.any(String),
+ statuses: { IN: statuses },
+ public_holiday: true
+ });
});
afterEach(function () {
@@ -101,12 +106,6 @@ define([
});
});
- it('sends as `statuses` an "IN" parameter', function () {
- expect(LeaveRequestAPI.sendGET.calls.mostRecent().args[2]).toEqual(jasmine.objectContaining({
- statuses: { 'IN': statuses }
- }));
- });
-
it('returns the api data as is', function () {
promise.then(function (response) {
expect(response).toEqual(mockData.balanceChangeByAbsenceType().values);
@@ -121,7 +120,10 @@ define([
describe('when passing falsy values for status and publicHolidays', function () {
beforeEach(function () {
- LeaveRequestAPI.balanceChangeByAbsenceType(jasmine.any(String), jasmine.any(String));
+ LeaveRequestAPI.balanceChangeByAbsenceType({
+ contact_id: jasmine.any(String),
+ period_id: jasmine.any(String)
+ });
});
it('assigns default values to them', function () {
@@ -135,12 +137,12 @@ define([
describe('error handling', function () {
it('throws error if contact_id is blank', function () {
- LeaveRequestAPI.balanceChangeByAbsenceType(null, jasmine.any(String))
+ LeaveRequestAPI.balanceChangeByAbsenceType({ period_id: jasmine.any(String) })
.catch(commonExpect);
});
it('throws error if periodId is blank', function () {
- LeaveRequestAPI.balanceChangeByAbsenceType(jasmine.any(String), null)
+ LeaveRequestAPI.balanceChangeByAbsenceType({ contact_id: jasmine.any(String) })
.catch(commonExpect);
});
@@ -580,9 +582,9 @@ define([
$httpBackend.flush();
});
- it('calls the LeaveRequest.get endpoint', function () {
+ it('calls the LeaveRequest.get endpoint and does not cache results', function () {
promise.then(function () {
- expect(LeaveRequestAPI.sendGET).toHaveBeenCalledWith('LeaveRequest', 'getFull', { id: id });
+ expect(LeaveRequestAPI.sendGET).toHaveBeenCalledWith('LeaveRequest', 'getFull', { id: id }, false);
});
});
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar-day.component.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar-day.component.spec.js
index 34e66b77bb4..287cdecddc7 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar-day.component.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar-day.component.spec.js
@@ -12,16 +12,15 @@ define([
'use strict';
describe('leaveCalendarDay', function () {
- var $componentController, $log, $rootScope, $timeout, absenceType, absenceTypes,
+ var $componentController, $log, $rootScope, absenceType, absenceTypes,
calculationUnits, calculationUnitInDays, calculationUnitInHours,
contactData, controller, dayTypes, LeavePopup, leaveRequest, leaveRequestAttributes;
beforeEach(module('manager-leave'));
- beforeEach(inject(function (_$componentController_, _$log_, _$rootScope_, _$timeout_, _LeavePopup_) {
+ beforeEach(inject(function (_$componentController_, _$log_, _$rootScope_, _LeavePopup_) {
$componentController = _$componentController_;
$log = _$log_;
$rootScope = _$rootScope_;
- $timeout = _$timeout_;
absenceTypes = _.cloneDeep(absenceTypeData.all().values);
contactData = {};
leaveRequest = _.cloneDeep(leaveRequestData.all().values[0]);
@@ -46,6 +45,7 @@ define([
'hrleaveandabsences_absence_type_calculation_unit', 'name',
'hours').value;
+ absenceTypes.push({ id: '', title: 'Leave' });
spyOn($log, 'debug');
compileComponent();
}));
@@ -128,6 +128,46 @@ define([
});
});
+ /**
+ * @NOTE this block tests an adhoc solution.
+ * @see /shared/components/leave-calendar-day.component.js
+ * resolveLeaveRequestCalculationUnit()
+ * @see PCHR-3774
+ */
+ describe('when absence type is a generic leave type', function () {
+ beforeEach(function () {
+ leaveRequest.type_id = '';
+ });
+
+ describe('when leave request "from_date_type" is *not* empty', function () {
+ beforeEach(function () {
+ leaveRequest.from_date_type = '1';
+ contactData.leaveRequests = [leaveRequest];
+
+ compileComponent();
+ $rootScope.$digest();
+ });
+
+ it('sets the "days" calculation unit', function () {
+ expect(leaveRequestAttributes.unit).toEqual('days');
+ });
+ });
+
+ describe('when leave request "from_date_type" is empty', function () {
+ beforeEach(function () {
+ delete leaveRequest.from_date_type;
+ contactData.leaveRequests = [leaveRequest];
+
+ compileComponent();
+ $rootScope.$digest();
+ });
+
+ it('sets the "hours" calculation unit', function () {
+ expect(leaveRequestAttributes.unit).toEqual('hours');
+ });
+ });
+ });
+
describe('Resolving the day\'s label', function () {
var absenceType;
@@ -239,90 +279,19 @@ define([
describe('openLeavePopup()', function () {
var event;
- var leaveRequest = { key: 'value' };
- var leaveType = 'some_leave_type';
- var selectedContactId = '101';
- var isSelfRecord = true;
+ var leaveRequest = { id: _.uniqueId() };
beforeEach(function () {
event = jasmine.createSpyObj('event', ['stopPropagation']);
- spyOn(LeavePopup, 'openModal');
- controller.openLeavePopup(event, leaveRequest, leaveType, selectedContactId, isSelfRecord);
+ spyOn(LeavePopup, 'openModalByID');
+ controller.openLeavePopup(event, leaveRequest);
});
it('opens the leave request popup', function () {
- expect(LeavePopup.openModal).toHaveBeenCalledWith(leaveRequest, leaveType, selectedContactId, isSelfRecord);
- });
-
- it('stops the event from propagating', function () {
- expect(event.stopPropagation).toHaveBeenCalled();
+ expect(LeavePopup.openModalByID).toHaveBeenCalledWith(leaveRequest.id);
});
});
- // @TODO this should be moved to a decorator to uib-tooltip
- describe('toggleTooltip()', function () {
- it('has the tooltip hidden by default', function () {
- expect(controller.tooltip.show).toBe(false);
- });
-
- describe('when user hovers the day cell', function () {
- beforeEach(function () {
- toggleTooltip('day_cell', true);
- });
-
- it('shows tooltip', function () {
- expect(controller.tooltip.show).toBe(true);
- });
-
- describe('and then user unhovers the day cell', function () {
- beforeEach(function () {
- toggleTooltip('day_cell', false);
- });
-
- it('hides the tooltip', function () {
- expect(controller.tooltip.show).toBe(false);
- });
- });
-
- describe('and then user unhovers the day cell but hovers the tooltip', function () {
- beforeEach(function () {
- toggleTooltip('day_cell', false);
- toggleTooltip('tooltip', true);
- });
-
- it('leaves the tooltip shown', function () {
- expect(controller.tooltip.show).toBe(true);
- });
-
- describe('and then user unhovers the tooltip', function () {
- beforeEach(function () {
- toggleTooltip('tooltip', false);
- });
-
- it('hides the tooltip', function () {
- expect(controller.tooltip.show).toBe(false);
- });
- });
-
- describe('and then user unhovers the tooltip but hovers the day cell back', function () {
- beforeEach(function () {
- toggleTooltip('tooltip', false);
- toggleTooltip('tooltip', true);
- });
-
- it('leaves the tooltip shown', function () {
- expect(controller.tooltip.show).toBe(true);
- });
- });
- });
- });
-
- function toggleTooltip (sourceElement, isHovered) {
- controller.toggleTooltip(sourceElement, isHovered);
- $timeout.flush();
- }
- });
-
/**
* Compiles and stores the component instance. Passes the contact and
* support data to the controller.
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar-legend.component.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar-legend.component.spec.js
index c5ddb67b8bb..5d3db391fda 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar-legend.component.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar-legend.component.spec.js
@@ -8,12 +8,13 @@ define([
'use strict';
describe('leaveCalendarLegend', function () {
- var $componentController, $log, controller, mockedAbsenceTypes;
+ var $componentController, $log, $rootScope, controller, mockedAbsenceTypes;
beforeEach(module('leave-absences.templates', 'leave-absences.components'));
beforeEach(inject(function (_$componentController_, _$log_, _$rootScope_) {
$componentController = _$componentController_;
$log = _$log_;
+ $rootScope = _$rootScope_;
mockedAbsenceTypes = AbsenceTypeData.all().values;
@@ -25,8 +26,46 @@ define([
expect($log.debug).toHaveBeenCalled();
});
- it('is collapsed', function () {
- expect(controller.legendCollapsed).toBe(true);
+ it('is expanded', function () {
+ expect(controller.legendCollapsed).toBe(false);
+ });
+
+ it('has a list of "non working" day types badges', function () {
+ expect(controller.nonWorkingDayTypes).toEqual(jasmine.objectContaining([{
+ label: jasmine.any(String),
+ cssClassSuffix: jasmine.any(String)
+ }]));
+ });
+
+ it('has a total of 3 "non working" day types badges', function () {
+ expect(controller.nonWorkingDayTypes.length).toBe(3);
+ });
+
+ it('has a list of "other" badges', function () {
+ expect(controller.otherBadges).toEqual(jasmine.objectContaining([{
+ label: jasmine.any(String),
+ description: jasmine.any(String)
+ }]));
+ });
+
+ it('has a total of 5 "other" badges', function () {
+ expect(controller.otherBadges.length).toBe(5);
+ });
+
+ describe('checkIfAbsenceTypeIdIsDefined()', function () {
+ describe('when the absence type has an id defined', function () {
+ it('returns true', function () {
+ expect(controller.checkIfAbsenceTypeIdIsDefined({ id: _.uniqueId() }))
+ .toBe(true);
+ });
+ });
+
+ describe('when the absence type has an empty id', function () {
+ it('returns false', function () {
+ expect(controller.checkIfAbsenceTypeIdIsDefined({ id: '' }))
+ .toBe(false);
+ });
+ });
});
describe('getAbsenceTypeStyle()', function () {
@@ -38,9 +77,78 @@ define([
});
it('uses the color of the given absence type to define border and background colors', function () {
- expect(style).toEqual({
- backgroundColor: absenceType.color,
- borderColor: absenceType.color
+ expect(style).toEqual({ backgroundColor: absenceType.color });
+ });
+ });
+
+ describe('absence types filter selection storage', function () {
+ beforeEach(function () {
+ spyOn($rootScope, '$emit');
+ $rootScope.$digest();
+ });
+
+ it('has all absence types selelected', function () {
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('1')).toEqual(true);
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('2')).toEqual(true);
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('3')).toEqual(true);
+ });
+
+ describe('when an absence type is selected', function () {
+ beforeEach(function () {
+ controller.toggleFilteringByAbsenceType('1');
+ $rootScope.$digest();
+ });
+
+ it('has the absence type selected', function () {
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('1')).toEqual(true);
+ });
+
+ it('has other absence type not selected', function () {
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('2')).toEqual(false);
+ });
+
+ it('notifies parent controller', function () {
+ expect($rootScope.$emit).toHaveBeenCalledWith(
+ 'LeaveCalendar::updateFiltersByAbsenceType', ['1']);
+ });
+
+ describe('when the same absence type is deselected', function () {
+ beforeEach(function () {
+ controller.toggleFilteringByAbsenceType('1');
+ });
+
+ it('selects all absence types back', function () {
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('1')).toEqual(true);
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('2')).toEqual(true);
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('3')).toEqual(true);
+ });
+ });
+
+ describe('when another absence type is selected', function () {
+ beforeEach(function () {
+ controller.toggleFilteringByAbsenceType('2');
+ });
+
+ it('contains multiple absence types', function () {
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('1')).toEqual(true);
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('2')).toEqual(true);
+ });
+
+ it('leave other absence types not selected', function () {
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('3')).toEqual(false);
+ });
+
+ describe('when absence types filter is reset', function () {
+ beforeEach(function () {
+ controller.resetFilteringByAbsenceTypes();
+ });
+
+ it('selects all absence types back', function () {
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('1')).toEqual(true);
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('2')).toEqual(true);
+ expect(controller.checkIfAbsenceTypeIsSelectedForFiltering('3')).toEqual(true);
+ });
+ });
});
});
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar-month.component.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar-month.component.spec.js
index 68cfd525b48..b1bdf017d82 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar-month.component.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar-month.component.spec.js
@@ -47,8 +47,8 @@
LeaveRequest = _LeaveRequest_;
OptionGroup = _OptionGroup_;
- february = { index: 1, year: 2016 };
- daysInFebruary = moment().month(february.index).year(february.year).daysInMonth();
+ february = { index: '2016-02', month: 1, year: 2016 };
+ daysInFebruary = moment().month(february.month).year(february.year).daysInMonth();
period2016 = _.clone(AbsencePeriodData.all().values[0]);
publicHolidays = PublicHolidayData.all().values;
leaveRequestInFebruary = LeaveRequestData.all().values[0];
@@ -104,7 +104,7 @@
describe('on "show months" event', function () {
describe('when it is included in the list of months to show', function () {
beforeEach(function () {
- sendShowMonthsSignal();
+ sendShowMonthSignal();
});
it('shows itself', function () {
@@ -173,11 +173,23 @@
expect(requestRecentCallFirstArg).toEqual(
jasmine.objectContaining({
- from_date: { to: month.days[month.days.length - 1].date },
- to_date: { from: month.days[0].date }
+ from_date: { to: month.days[month.days.length - 1].date + ' 23:59:59' },
+ to_date: { from: month.days[0].date + ' 00:00:00' }
})
);
});
+
+ describe('when leave requests are reloaded', function () {
+ beforeEach(function () {
+ $rootScope.$emit('LeaveCalendar::showMonth', true);
+ });
+
+ it('flushes days data before populating it', function () {
+ expect(_.every(controller.month.days, function (day) {
+ return !Object.keys(day.contactsData).length;
+ })).toBe(true);
+ });
+ });
});
describe('contacts', function () {
@@ -188,7 +200,7 @@
contactIdsToReduceTo = randomContactIds;
compileComponent();
- sendShowMonthsSignal();
+ sendShowMonthSignal();
$rootScope.$digest();
});
@@ -203,7 +215,7 @@
contactIdsToReduceTo = null;
compileComponent();
- sendShowMonthsSignal();
+ sendShowMonthSignal();
$rootScope.$digest();
});
@@ -217,7 +229,7 @@
beforeEach(function () {
controller.currentPage = 5;
- sendShowMonthsSignal();
+ sendShowMonthSignal();
});
it('resets it to 0', function () {
@@ -230,7 +242,7 @@
Calendar.get.calls.reset();
LeaveRequest.all.calls.reset();
- sendShowMonthsSignal();
+ sendShowMonthSignal();
});
it('does not fetch the data again', function () {
@@ -244,7 +256,7 @@
Calendar.get.calls.reset();
LeaveRequest.all.calls.reset();
- sendShowMonthsSignal(true, true);
+ sendShowMonthSignal(true, true);
});
it('fetches the data again', function () {
@@ -252,23 +264,58 @@
expect(LeaveRequest.all).toHaveBeenCalled();
});
});
- });
- describe('when it is not included in the list of months to show', function () {
- beforeEach(function () {
- sendShowMonthsSignal(false);
- });
+ describe('filter by absence types', function () {
+ var filterValue = ['777', '888'];
- it('hides itself', function () {
- expect(controller.visible).toBe(false);
- });
+ beforeEach(function () {
+ controller.supportData.absenceTypesToFilterBy = filterValue;
- it("does not load the contacts' work pattern calendars", function () {
- expect(Calendar.get).not.toHaveBeenCalled();
- });
+ $rootScope.$emit('LeaveCalendar::showMonth', true);
+ $rootScope.$digest();
+ });
- it("does not load the contacts' leave requests", function () {
- expect(LeaveRequest.all).not.toHaveBeenCalled();
+ it('loads leave requests for only selected absence types', function () {
+ expect(LeaveRequest.all).toHaveBeenCalledWith(jasmine.objectContaining({
+ type_id: { 'IN': filterValue }
+ }), null, null, null, false);
+ });
+
+ describe('displaying only public leave requests', function () {
+ var privateLeaveRequests;
+ var contactId = _.uniqueId();
+
+ beforeEach(function () {
+ var leaveRequests = _.cloneDeep(LeaveRequestData.all().values);
+
+ leaveRequests.slice(0, 3).forEach(function (leaveRequest) {
+ leaveRequest.contact_id = contactId;
+ leaveRequest.type_id = '';
+
+ return leaveRequest;
+ });
+
+ LeaveRequest.all.and.returnValue($q.resolve({
+ count: leaveRequests.length,
+ list: leaveRequests
+ }));
+
+ $rootScope.$emit('LeaveCalendar::updateFiltersByAbsenceType', filterValue);
+ $rootScope.$digest();
+
+ // Gets private requests assigned to the contact and stored in
+ // the calendar month controller:
+ privateLeaveRequests = _.chain(controller.month.days).pluck('contactsData')
+ .pluck(contactId).pluck('leaveRequests').flatten()
+ .filter(function (leaveRequest) {
+ return leaveRequest.type_id === '';
+ }).value();
+ });
+
+ it('is does not store information about private requests', function () {
+ expect(privateLeaveRequests.length).toBe(0);
+ });
+ });
});
});
});
@@ -402,7 +449,7 @@
describe("day's data specific for each contact", function () {
beforeEach(function () {
- sendShowMonthsSignal();
+ sendShowMonthSignal();
});
it('is indexed by contact id', function () {
@@ -652,7 +699,7 @@
describe('event listeners', function () {
beforeEach(function () {
- sendShowMonthsSignal();
+ sendShowMonthSignal();
});
describe('when a leave request is deleted', function () {
@@ -838,7 +885,7 @@
describe('contactsList()', function () {
beforeEach(function () {
- sendShowMonthsSignal();
+ sendShowMonthSignal();
});
describe('when show-only-with-leave-requests is set to false', function () {
@@ -863,6 +910,22 @@
});
});
+ describe('when show-only-with-leave-requests is set to true, but specific contacts must be shown even if they have no leave requests', function () {
+ var expectedContact;
+
+ beforeEach(function () {
+ expectedContact = { id: _.uniqueId() };
+ controller.showOnlyWithLeaveRequests = true;
+ controller.showTheseContacts = [expectedContact.id];
+
+ controller.contacts.push(expectedContact);
+ });
+
+ it('returns a list including the contacts that have no leave requests', function () {
+ expect(controller.contactsList()).toContain(expectedContact);
+ });
+ });
+
describe('when show-only-with-leave-requests is set to true and there is no leave request for contacts', function () {
beforeEach(function () {
LeaveRequest.all.and.callFake(function () {
@@ -941,12 +1004,21 @@
* @param {Boolean} sendSignal - if to send a month signal or not
*/
function compileComponent (sendSignal) {
+ var absenceTypes = _.clone(AbsenceTypeData.all().values);
+
+ // append generic absence type:
+ absenceTypes.push({
+ id: '',
+ label: 'Leave'
+ });
+
controller = $componentController('leaveCalendarMonth', null, {
- contacts: ContactData.all.values,
+ contacts: _.clone(ContactData.all.values),
month: february,
period: period2016,
supportData: {
- absenceTypes: AbsenceTypeData.all().values,
+ absenceTypes: absenceTypes,
+ absenceTypesToFilterBy: [],
dayTypes: OptionGroupData.getCollection('hrleaveandabsences_leave_request_day_type'),
leaveRequestStatuses: OptionGroupData.getCollection('hrleaveandabsences_leave_request_status'),
publicHolidays: publicHolidays
@@ -954,7 +1026,7 @@
contactIdsToReduceTo: contactIdsToReduceTo
});
- !!sendSignal && sendShowMonthsSignal();
+ !!sendSignal && sendShowMonthSignal();
}
/**
@@ -973,18 +1045,12 @@
/**
* Sends the "show months" signal to the component
*
- * @param {Boolean} includeFebruary Whether to include Feb (the current month)
- * @param {Boolean} forceReload Whether to force reloading the month's data
+ * @param {Boolean} forceReload Whether to force reloading the month's data
*/
- function sendShowMonthsSignal (includeFebruary, forceReload) {
- var selectedMonths = [{ index: 11, year: 2016 }];
-
- includeFebruary = typeof includeFebruary === 'undefined' ? true : !!includeFebruary;
+ function sendShowMonthSignal (forceReload) {
forceReload = typeof forceReload === 'undefined' ? false : !!forceReload;
- includeFebruary && selectedMonths.push(february);
-
- $rootScope.$emit('LeaveCalendar::showMonths', selectedMonths, forceReload);
+ $rootScope.$emit('LeaveCalendar::showMonth', forceReload);
$rootScope.$digest();
}
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar.component.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar.component.spec.js
index 013cf4c6b0e..4ebac68ce7c 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar.component.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-calendar.component.spec.js
@@ -11,6 +11,7 @@
'leave-absences/mocks/data/option-group.data',
'leave-absences/mocks/data/public-holiday.data',
'common/mocks/services/api/contact-mock',
+ 'common/mocks/services/api/contract-mock',
'leave-absences/mocks/apis/absence-period-api-mock',
'leave-absences/mocks/apis/absence-type-api-mock',
'leave-absences/mocks/apis/public-holiday-api-mock',
@@ -29,6 +30,8 @@
id: CRM.vars.leaveAndAbsences.contactId,
role: 'staff'
};
+ var currentYear = 2016;
+ var currentMonth = 1;
beforeEach(module('common.mocks', 'leave-absences.templates', 'leave-absences.mocks', 'my-leave', function (_$provide_, _$controllerProvider_) {
$provide = _$provide_;
@@ -69,6 +72,7 @@
sharedSettings = _sharedSettings_;
notification = _notificationService_;
+ spyOn(window, 'alert');
spyOn($log, 'debug');
spyOn($rootScope, '$emit').and.callThrough();
spyOn(AbsencePeriod, 'all');
@@ -93,7 +97,7 @@
// The mocked "work pattern calendar" and "leave request" data is made for
// the month of February, so we pretend we are in February
beforeAll(function () {
- jasmine.clock().mockDate(new Date(2016, 1, 1));
+ jasmine.clock().mockDate(new Date(currentYear, currentMonth, 1));
});
afterAll(function () {
@@ -104,6 +108,22 @@
expect($log.debug).toHaveBeenCalled();
});
+ it('has an array of filters by assignee', function () {
+ expect(controller.filtersByAssignee).toEqual([
+ { type: 'me', label: 'People I approve' },
+ { type: 'unassigned', label: 'People without approver' },
+ { type: 'all', label: 'All' }
+ ]);
+ });
+
+ it('selects the assignee filter "All" by default', function () {
+ expect(controller.filters.userSettings.assignedTo.type).toBe('all');
+ });
+
+ it('sets the user permission role to staff by default', function () {
+ expect(controller.userPermissionRole).toBe('staff');
+ });
+
describe('on init', function () {
it('hides the loader for the whole page', function () {
expect(controller.loading.page).toBe(false);
@@ -122,6 +142,10 @@
]);
});
+ it('hides filters on mobile viewport by default', function () {
+ expect(controller.filters.hideOnMobile).toBe(true);
+ });
+
describe('after loading support data', function () {
it('stores absence types', function () {
expect(controller.supportData.absenceTypes.length).not.toBe(0);
@@ -143,9 +167,63 @@
expect(controller.supportData.leaveRequestStatuses.length).not.toBe(0);
});
+ it('has default filter by absence type', function () {
+ expect(controller.supportData.absenceTypesToFilterBy).toEqual([]);
+ });
+
it('stores toil amounts', function () {
expect(controller.supportData.toilAmounts).toBeDefined();
});
+
+ it('appends a generic absence type that can be used for private leave requests', function () {
+ expect(controller.supportData.absenceTypes).toContain({
+ id: '',
+ title: 'Leave',
+ color: '#4D4D68',
+ calculation_unit: _.chain(controller.supportData.calculationUnits)
+ .find({ name: 'days' }).get('value').value()
+ });
+ });
+
+ describe('setting the user permission role', function () {
+ var scenarios = [
+ { role: 'admin' },
+ { role: 'manager' },
+ { role: 'staff' }
+ ];
+
+ scenarios.forEach(function (scenario) {
+ describe('when the ' + scenario.role + ' opens the calendar', function () {
+ beforeEach(function () {
+ currentContact.role = scenario.role;
+ compileComponent();
+ });
+
+ it('sets the user permission role equal to ' + scenario.role, function () {
+ expect(controller.userPermissionRole).toBe(scenario.role);
+ });
+ });
+ });
+ });
+ });
+
+ describe('filter by absence types', function () {
+ var filterValue = ['777', '888'];
+
+ beforeEach(function () {
+ $rootScope.$emit.calls.reset();
+ $rootScope.$emit('LeaveCalendar::updateFiltersByAbsenceType', filterValue);
+ $rootScope.$digest();
+ });
+
+ it('stores the filter value in the support data', function () {
+ expect(controller.supportData.absenceTypesToFilterBy)
+ .toEqual(filterValue);
+ });
+
+ it('refreshes the month data', function () {
+ expect($rootScope.$emit.calls.mostRecent().args[0]).toBe('LeaveCalendar::showMonth');
+ });
});
describe('taking leave filter', function () {
@@ -164,8 +242,8 @@
compileComponent();
});
- it('sets the filter to *off* by default', function () {
- expect(controller.filters.userSettings.contacts_with_leaves).toBe(false);
+ it('sets the filter to *on* by default', function () {
+ expect(controller.filters.userSettings.contacts_with_leaves).toBe(true);
});
});
});
@@ -243,111 +321,83 @@
});
});
- describe('additional contacts filter', function () {
- describe('when the user is an admin', function () {
+ describe('month paginators', function () {
+ describe('when current month is the first month of the current absence period', function () {
beforeEach(function () {
- currentContact.role = 'admin';
+ controller.selectedMonthIndex = _.first(controller.months).index;
- compileComponent();
+ $rootScope.$digest();
});
- describe('when filter by assignee is set to "Me"', function () {
- beforeEach(function () {
- selectFilterByAssignee('me');
- });
-
- it('does *not* load additional contacts IDs to filter', function () {
- expect(controller.contactIdsToReduceTo).toEqual(null);
- });
+ it('does not allow to paginate to the previous month', function () {
+ expect(controller.monthPaginatorsAvailability.previous).toBe(false);
});
+ });
- describe('when filter by assignee is *not* set to "Me"', function () {
- beforeEach(function () {
- selectFilterByAssignee('all');
- });
+ describe('when current month is the last month of the current absence period', function () {
+ beforeEach(function () {
+ controller.selectedMonthIndex = _.last(controller.months).index;
- it('loads additional contacts IDs to filter', function () {
- expect(controller.contactIdsToReduceTo).toEqual(jasmine.any(Array));
- });
+ $rootScope.$digest();
+ });
+
+ it('does not allow to paginate to the previous month', function () {
+ expect(controller.monthPaginatorsAvailability.next).toBe(false);
});
});
- describe('when the user is a manager', function () {
+ describe('when current month is neither the first nor the last month of the current absence period', function () {
beforeEach(function () {
- currentContact.role = 'manager';
+ controller.selectedMonthIndex = controller.months[1].index;
- compileComponent();
+ $rootScope.$digest();
});
- it('does not load additional contacts IDs to filter', function () {
- expect(controller.contactIdsToReduceTo).toBe(null);
+ it('allows to paginate the month in both directions', function () {
+ expect(controller.monthPaginatorsAvailability.previous).toBe(true);
+ expect(controller.monthPaginatorsAvailability.next).toBe(true);
});
});
-
- afterEach(function () {
- currentContact.role = 'staff';
- });
});
- describe('filter by assignee', function () {
- it('does *not* have such a filter for staff', function () {
- expect(controller.filtersByAssignee).not.toBeDefined();
- });
-
- describe('when user is Admin', function () {
+ describe('additional contacts filter', function () {
+ describe('when the user is an admin', function () {
beforeEach(function () {
currentContact.role = 'admin';
compileComponent();
});
- it('has the filter available', function () {
- expect(controller.filtersByAssignee).toBeDefined();
- });
-
describe('when filter by assignee is set to "Me"', function () {
beforeEach(function () {
selectFilterByAssignee('me');
});
- it('does *not* load contracts', function () {
- expect(Contract.all).not.toHaveBeenCalledWith();
- });
-
- it('loads only managees', function () {
- expect(Contact.all).not.toHaveBeenCalledWith();
- expect(Contact.leaveManagees).toHaveBeenCalledWith(currentContact.id);
+ it('does *not* load additional contacts IDs to filter', function () {
+ expect(controller.contactIdsToReduceTo).toEqual(null);
});
});
- describe('when filter by assignee is set to "Unassigned"', function () {
+ describe('when filter by assignee is *not* set to "Me"', function () {
beforeEach(function () {
- selectFilterByAssignee('unassigned');
- });
-
- it('loads all contracts', function () {
- expect(Contract.all).toHaveBeenCalledWith();
+ selectFilterByAssignee('all');
});
- it('loads all contacts', function () {
- expect(Contact.leaveManagees).toHaveBeenCalledWith(undefined, {
- unassigned: true
- });
+ it('loads additional contacts IDs to filter', function () {
+ expect(controller.contactIdsToReduceTo).toEqual(jasmine.any(Array));
});
});
+ });
- describe('when filter by assignee is set to "All"', function () {
- beforeEach(function () {
- selectFilterByAssignee('all');
- });
+ describe('when the user is a manager', function () {
+ beforeEach(function () {
+ currentContact.role = 'manager';
- it('loads all contracts', function () {
- expect(Contract.all).toHaveBeenCalledWith();
- });
+ compileComponent();
+ });
- it('loads all contacts', function () {
- expect(Contact.all).toHaveBeenCalledWith();
- });
+ it('does not load additional contacts IDs to filter', function () {
+ expect(controller.contactIdsToReduceTo).toBe(null);
});
});
@@ -401,6 +451,20 @@
it('selects the current period', function () {
expect(controller.selectedPeriod.current).toBe(true);
});
+
+ describe('when absence period has been changed', function () {
+ beforeEach(function () {
+ controller.injectMonth = false;
+ controller.selectedPeriod = controller.absencePeriods[1];
+
+ controller.refresh('period');
+ $rootScope.$digest();
+ });
+
+ it('sets the first month from the period as the selected month', function () {
+ expect(controller.selectedMonth).toEqual(controller.months[0]);
+ });
+ });
});
describe('months', function () {
@@ -409,14 +473,24 @@
var periodStartDate = moment(controller.selectedPeriod.start_date);
var periodEndDate = moment(controller.selectedPeriod.end_date);
- expect(months[0].index).toEqual(periodStartDate.month());
+ expect(months[0].month).toEqual(periodStartDate.month());
expect(months[0].year).toEqual(periodStartDate.year());
- expect(months[months.length - 1].index).toEqual(periodEndDate.month());
+ expect(months[months.length - 1].month).toEqual(periodEndDate.month());
expect(months[months.length - 1].year).toEqual(periodEndDate.year());
});
+ it('sorts the list of the months', function () {
+ var months = controller.months;
+ var monthsSorted = _.sortBy(months, function (month) {
+ return new Date(month.moment);
+ });
+
+ expect(months).toEqual(monthsSorted);
+ });
+
it('selects the current month', function () {
- expect(controller.selectedMonths).toEqual([moment().month()]);
+ expect(controller.selectedMonth).toEqual(_.find(controller.months,
+ { index: moment().format('YYYY-MM') }));
});
});
@@ -445,7 +519,25 @@
describe('filter option values', function () {
var optionGroups = ['hrjc_region', 'hrjc_location', 'hrjc_level_type', 'hrjc_department'];
- describe('when the filters should not be shown', function () {
+ describe('when the subcontroller does not show filters', function () {
+ beforeEach(function () {
+ var staffController = $controller('LeaveCalendarStaffController').init(controller);
+
+ // mocks the staff sub controller to hide the filters:
+ $controllerProvider.register('LeaveCalendarStaffController', function () {
+ return {
+ init: function (vm) {
+ vm.showFilters = false;
+
+ return staffController;
+ }
+ };
+ });
+
+ OptionGroup.valuesOf.calls.reset();
+ compileComponent();
+ });
+
it('does not fetch the filters option values', function () {
expect(OptionGroup.valuesOf).not.toHaveBeenCalledWith(optionGroups);
});
@@ -470,30 +562,13 @@
controller.injectMonths = true;
});
- describe('when it has not yet received the "month injected" event from all the months', function () {
- beforeEach(function () {
- simulateMonthsWithSignal('injected', 2);
- });
-
- it('does not send the event', function () {
- expect($rootScope.$emit).not.toHaveBeenCalled();
- });
- });
-
- describe('when it has received the "month injected" event from all the months', function () {
+ describe('when it has received the "month injected" event from the month', function () {
beforeEach(function () {
- simulateMonthsWithSignal('injected', controller.months.length);
+ simulateMonthWithSignal('injected');
});
it('sends the event', function () {
- expect($rootScope.$emit).toHaveBeenCalled();
- expect($rootScope.$emit.calls.mostRecent().args[0]).toBe('LeaveCalendar::showMonths');
- });
-
- it('attaches to the event only the currently selected months', function () {
- expect($rootScope.$emit.calls.mostRecent().args[1]).toEqual(controller.months.filter(function (month) {
- return _.includes(controller.selectedMonths, month.index);
- }));
+ expect($rootScope.$emit.calls.mostRecent().args[0]).toBe('LeaveCalendar::showMonth');
});
});
});
@@ -510,39 +585,99 @@
controller.refresh('contacts');
$rootScope.$digest();
- simulateMonthsWithSignal('destroyed', controller.months.length);
+ simulateMonthWithSignal('destroyed', controller.months.length);
}
});
- describe('selected months watcher', function () {
- describe('when some other months are selected', function () {
- beforeEach(function () {
- controller.selectedMonths = [1, 2, 3];
- $rootScope.$digest();
- });
+ describe('canManageRequests()', function () {
+ var scenarios = [
+ { role: 'admin', expectedResult: true },
+ { role: 'manager', expectedResult: true },
+ { role: 'staff', expectedResult: false }
+ ];
+
+ scenarios.forEach(function (scenario) {
+ describe('when the ' + scenario.role + ' checks if they can manage requests', function () {
+ beforeEach(function () {
+ currentContact.role = scenario.role;
+ compileComponent();
+ });
- it('sends the "show months" event with the newly selected months', function () {
- expect($rootScope.$emit).toHaveBeenCalledWith(
- 'LeaveCalendar::showMonths',
- controller.months.filter(function (month) {
- return _.includes([1, 2, 3], month.index);
- }),
- jasmine.any(Boolean)
- );
+ it('returns ' + scenario.expectedResult, function () {
+ expect(controller.canManageRequests()).toBe(scenario.expectedResult);
+ });
});
});
+ });
- describe('when none of the months are selected', function () {
- beforeEach(function () {
- controller.selectedMonths = [];
- $rootScope.$digest();
- });
+ describe('navigateToCurrentMonth()', function () {
+ var currentAbsencePeriod;
+
+ beforeEach(function () {
+ currentAbsencePeriod = _.find(controller.absencePeriods,
+ { current: true });
+ controller.injectMonth = false;
+ controller.selectedPeriod = controller.absencePeriods[1];
+ $rootScope.$digest();
+ controller.selectedMonthIndex =
+ moment().year(currentYear).month(currentMonth).format('YYYY-MM');
+ $rootScope.$digest();
+ controller.navigateToCurrentMonth();
+ $rootScope.$digest();
+ });
- it('sends the "show months" event with the all the months', function () {
- expect($rootScope.$emit).toHaveBeenCalledWith(
- 'LeaveCalendar::showMonths',
- controller.months,
- jasmine.any(Boolean));
+ it('sets the selected month as the current month', function () {
+ expect(controller.selectedMonth.year).toBe(currentYear);
+ expect(controller.selectedMonth.month).toBe(currentMonth);
+ });
+
+ it('sets the current absence period', function () {
+ expect(controller.selectedPeriod).toEqual(currentAbsencePeriod);
+ });
+ });
+
+ describe('paginateMonth()', function () {
+ var currentlySelectedMonth;
+ var tests = [
+ { directions: 'previous', monthDifference: -1 },
+ { directions: 'next', monthDifference: 1 },
+ { directions: 'previous next previous next next', monthDifference: 1 },
+ { directions: 'next previous next previous previous', monthDifference: -1 }
+ ];
+
+ beforeEach(function () {
+ currentlySelectedMonth = controller.selectedMonth.month;
+ // This is needed to test the paginators availability
+ controller.months = _.slice(controller.months, 0, 3);
+ });
+
+ tests.forEach(function (test) {
+ describe('when user paginates to the ' + test.directions + ' month', function () {
+ var directions;
+
+ beforeEach(function () {
+ directions = test.directions.split(' ');
+ controller.injectMonth = false;
+ directions.forEach(function (direction) {
+ controller.paginateMonth(direction);
+ });
+ $rootScope.$digest();
+ simulateMonthWithSignal('injected', controller.months.length);
+ $rootScope.$digest();
+ });
+
+ it('sets the selected month according to ' + test.directions + ' directions', function () {
+ expect(controller.selectedMonth.moment
+ .diff(currentlySelectedMonth.moment, 'month')).toBe(test.monthDifference);
+ });
+
+ it('does not allow to paginate further because the are no more months in the last mentioned direction', function () {
+ expect(controller.monthPaginatorsAvailability[_.last(directions)]).toBe(false);
+ });
+
+ it('refreshes the month component without force data reload', function () {
+ expect($rootScope.$emit).toHaveBeenCalledWith('LeaveCalendar::showMonth', false);
+ });
});
});
});
@@ -610,8 +745,8 @@
controller.refresh('period');
$rootScope.$digest();
- simulateMonthsWithSignal('destroyed', controller.months.length);
- simulateMonthsWithSignal('injected', controller.months.length);
+ simulateMonthWithSignal('destroyed', controller.months.length);
+ simulateMonthWithSignal('injected', controller.months.length);
});
it('rebuilds the months structure', function () {
@@ -624,10 +759,7 @@
it('sends the "show months" signal without forcing data reload', function () {
expect($rootScope.$emit).toHaveBeenCalledWith(
- 'LeaveCalendar::showMonths',
- jasmine.any(Array),
- false
- );
+ 'LeaveCalendar::showMonth', false);
});
});
@@ -636,8 +768,8 @@
controller.refresh('contacts');
$rootScope.$digest();
- simulateMonthsWithSignal('destroyed', controller.months.length);
- simulateMonthsWithSignal('injected', controller.months.length);
+ simulateMonthWithSignal('destroyed', controller.months.length);
+ simulateMonthWithSignal('injected', controller.months.length);
});
it('does not rebuild the months structure', function () {
@@ -650,10 +782,7 @@
it('sends the "show months" signal with forcing data reload', function () {
expect($rootScope.$emit).toHaveBeenCalledWith(
- 'LeaveCalendar::showMonths',
- jasmine.any(Array),
- true
- );
+ 'LeaveCalendar::showMonth', true);
});
});
});
@@ -706,13 +835,10 @@
* Simulates that the given number of months sends the given
* signal to the component
*
- * @param {string} signal
- * @param {int} numberOfMonths
+ * @param {String} signal
*/
- function simulateMonthsWithSignal (signal, numberOfMonths) {
- _.times(numberOfMonths, function () {
- $rootScope.$emit('LeaveCalendar::month' + _.capitalize(signal));
- });
+ function simulateMonthWithSignal (signal) {
+ $rootScope.$emit('LeaveCalendar::month' + _.capitalize(signal));
$rootScope.$emit.calls.reset();
$rootScope.$digest();
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-notification-badge.component.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-notification-badge.component.spec.js
deleted file mode 100644
index 6ca694b9ba5..00000000000
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-notification-badge.component.spec.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/* eslint-env amd, jasmine */
-
-define([
- 'leave-absences/shared/components/leave-notification-badge.component'
-], function () {
- 'use strict';
-
- describe('leaveNotificationBadge', function () {
- var $componentController, $log, $rootScope, $q, controller, LeaveRequest, pubSub;
- var apiReturnValue = { list: [1, 2, 3] };
- var eventName = 'some-event';
- var filters = { list: 'somevalue' };
-
- beforeEach(module('leave-absences.templates', 'leave-absences.mocks', 'manager-leave'));
-
- beforeEach(inject(function (_$componentController_, _$log_, _$rootScope_, _$q_, _pubSub_, _LeaveRequest_) {
- $componentController = _$componentController_;
- $log = _$log_;
- $q = _$q_;
- $rootScope = _$rootScope_;
- pubSub = _pubSub_;
- LeaveRequest = _LeaveRequest_;
-
- spyOn($log, 'debug');
- spyOn(LeaveRequest, 'all').and.returnValue($q.resolve(apiReturnValue));
-
- compileComponent();
- }));
-
- it('is initialized', function () {
- expect($log.debug).toHaveBeenCalled();
- });
-
- describe('on init', function () {
- it('sets the event name same as the passed attribute', function () {
- expect(controller.refreshCountEventName).toBe(eventName);
- });
-
- it('calls Leave Request API to get the count', function () {
- expect(LeaveRequest.all).toHaveBeenCalledWith(filters, null, null, null, false);
- });
-
- describe('after api returns with value', function () {
- it('sets count to number of records returned', function () {
- expect(controller.count).toBe(apiReturnValue.list.length);
- });
- });
- });
-
- describe('when event is fired', function () {
- beforeEach(function () {
- apiReturnValue = { list: [1, 2, 3, 4] };
-
- pubSub.publish(eventName);
- LeaveRequest.all.and.returnValue($q.resolve(apiReturnValue));
- });
-
- it('calls Leave Request API to get the count', function () {
- expect(LeaveRequest.all).toHaveBeenCalledWith(filters, null, null, null, false);
- });
-
- describe('after api returns with value', function () {
- it('sets count to number of records returned', function () {
- expect(controller.count).toBe(apiReturnValue.list.length);
- });
- });
- });
-
- function compileComponent () {
- controller = $componentController('leaveNotificationBadge', null, {
- refreshCountEventName: eventName,
- filters: filters
- });
- $rootScope.$digest();
- }
- });
-});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-request-actions.component.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-request-actions.component.spec.js
index 248c2a17f1c..845809c3d74 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-request-actions.component.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-request-actions.component.spec.js
@@ -668,16 +668,15 @@ define([
var leaveRequest = { key: 'value' };
var leaveType = 'some_leave_type';
var selectedContactId = '101';
- var isSelfRecord = true;
beforeEach(function () {
event = jasmine.createSpyObj('event', ['stopPropagation']);
spyOn(LeavePopup, 'openModal');
- controller.openLeavePopup(event, leaveRequest, leaveType, selectedContactId, isSelfRecord);
+ controller.openLeavePopup(event, leaveRequest, leaveType, selectedContactId);
});
it('opens the leave request popup', function () {
- expect(LeavePopup.openModal).toHaveBeenCalledWith(leaveRequest, leaveType, selectedContactId, isSelfRecord);
+ expect(LeavePopup.openModal).toHaveBeenCalledWith(leaveRequest, leaveType, selectedContactId);
});
it('stops the event from propagating', function () {
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-request-record-actions.component.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-request-record-actions.component.spec.js
index 7888174f7bb..5b152b07bfd 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-request-record-actions.component.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/leave-request-record-actions.component.spec.js
@@ -117,15 +117,14 @@ define([
var leaveRequest = { key: 'value' };
var leaveType = 'some_leave_type';
var selectedContactId = '101';
- var isSelfRecord = true;
beforeEach(function () {
spyOn(LeavePopup, 'openModal');
- controller.openLeavePopup(leaveRequest, leaveType, selectedContactId, isSelfRecord);
+ controller.openLeavePopup(leaveRequest, leaveType, selectedContactId);
});
it('opens the leave request popup', function () {
- expect(LeavePopup.openModal).toHaveBeenCalledWith(leaveRequest, leaveType, selectedContactId, isSelfRecord);
+ expect(LeavePopup.openModal).toHaveBeenCalledWith(leaveRequest, leaveType, selectedContactId);
});
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/manage-leave-requests.component.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/manage-leave-requests.component.spec.js
index 81d474b6505..acfb6854101 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/manage-leave-requests.component.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/manage-leave-requests.component.spec.js
@@ -870,15 +870,14 @@ define([
var leaveRequest = { key: 'value' };
var leaveType = 'some_leave_type';
var selectedContactId = '101';
- var isSelfRecord = true;
beforeEach(function () {
spyOn(LeavePopup, 'openModal');
- controller.openLeavePopup(leaveRequest, leaveType, selectedContactId, isSelfRecord);
+ controller.openLeavePopup(leaveRequest, leaveType, selectedContactId);
});
it('opens the leave request popup', function () {
- expect(LeavePopup.openModal).toHaveBeenCalledWith(leaveRequest, leaveType, selectedContactId, isSelfRecord);
+ expect(LeavePopup.openModal).toHaveBeenCalledWith(leaveRequest, leaveType, selectedContactId);
});
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/staff-leave-report.component.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/staff-leave-report.component.spec.js
index 50189afe2e0..d8f18a7e322 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/staff-leave-report.component.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/components/staff-leave-report.component.spec.js
@@ -242,16 +242,17 @@
});
it('has fetched the balance changes for the current contact and selected period', function () {
- var args = LeaveRequest.balanceChangeByAbsenceType.calls.argsFor(0);
+ var args = LeaveRequest.balanceChangeByAbsenceType.calls.argsFor(0)[0];
- expect(args[0]).toEqual(contactId);
- expect(args[1]).toEqual(controller.selectedPeriod.id);
+ expect(args.contact_id).toEqual(contactId);
+ expect(args.period_id).toEqual(controller.selectedPeriod.id);
});
describe('public holidays', function () {
it('has fetched the balance changes for the public holidays', function () {
- var args = LeaveRequest.balanceChangeByAbsenceType.calls.argsFor(0);
- expect(args[3]).toEqual(true);
+ var args = LeaveRequest.balanceChangeByAbsenceType.calls.argsFor(0)[0];
+
+ expect(args.public_holiday).toEqual(true);
});
it('has stored them in each absence type', function () {
@@ -264,10 +265,30 @@
});
});
+ describe('expired requests', function () {
+ it('has fetched the balance changes for the expired requests', function () {
+ var args = LeaveRequest.balanceChangeByAbsenceType.calls.argsFor(1)[0];
+
+ expect(args.expired).toEqual(true);
+ });
+
+ it('has stored them in each absence type', function () {
+ _.forEach(controller.absenceTypes, function (absenceType) {
+ var balanceChanges = absenceType.balanceChanges.expired;
+
+ expect(balanceChanges).toBeDefined();
+ expect(balanceChanges).toBe(mockData[absenceType.id]);
+ });
+ });
+ });
+
describe('approved requests', function () {
it('has fetched the balance changes for the approved requests', function () {
- var args = LeaveRequest.balanceChangeByAbsenceType.calls.argsFor(1);
- expect(args[2]).toEqual([ valueOfRequestStatus(sharedSettings.statusNames.approved) ]);
+ var args = LeaveRequest.balanceChangeByAbsenceType.calls.argsFor(2)[0];
+
+ expect(args.statuses.in).toEqual([
+ valueOfRequestStatus(sharedSettings.statusNames.approved)
+ ]);
});
it('has stored them in each absence type', function () {
@@ -282,9 +303,9 @@
describe('open requests', function () {
it('has fetched the balance changes for the open requests', function () {
- var args = LeaveRequest.balanceChangeByAbsenceType.calls.argsFor(2);
+ var args = LeaveRequest.balanceChangeByAbsenceType.calls.argsFor(3)[0];
- expect(args[2]).toEqual([
+ expect(args.statuses.in).toEqual([
valueOfRequestStatus(sharedSettings.statusNames.awaitingApproval),
valueOfRequestStatus(sharedSettings.statusNames.moreInformationRequired)
]);
@@ -364,10 +385,10 @@
});
it('reloads all the balance changes', function () {
- var args = LeaveRequest.balanceChangeByAbsenceType.calls.argsFor(_.random(0, 2));
+ var args = LeaveRequest.balanceChangeByAbsenceType.calls.argsFor(_.random(0, 2))[0];
- expect(LeaveRequest.balanceChangeByAbsenceType).toHaveBeenCalledTimes(3);
- expect(args[1]).toEqual(newPeriod.id);
+ expect(LeaveRequest.balanceChangeByAbsenceType).toHaveBeenCalledTimes(4);
+ expect(args.period_id).toEqual(newPeriod.id);
});
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/request.controller.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/request.controller.spec.js
index 671dff7d386..84bbced6b25 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/request.controller.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/request.controller.spec.js
@@ -575,8 +575,8 @@
var leaveRequest = LeaveRequestInstance.init(mockData.findBy('status_id', status));
leaveRequest.contact_id = CRM.vars.leaveAndAbsences.contactId.toString();
-
- initTestController({ leaveRequest: leaveRequest, isSelfRecord: true });
+ $rootScope.section = 'my-leave';
+ initTestController({ leaveRequest: leaveRequest });
expectedStatusValue = optionGroupMock.specificValue('hrleaveandabsences_leave_request_status', 'value', '3');
controller.balance.closing = 5;
@@ -778,29 +778,56 @@
requestOriginalDates.to = controller.request.to_date;
controller.newStatusOnSave = optionGroupMock.specificObject(
'hrleaveandabsences_leave_request_status', 'name', 'cancelled').value;
-
- controller.submit();
+ controller.request.from_date =
+ moment(controller.request.from_date).add(15, 'minutes')
+ .format('YYYY-MM-DD HH:mm');
+ controller.request.to_date =
+ moment(controller.request.to_date).add(15, 'minutes')
+ .format('YYYY-MM-DD HH:mm');
$rootScope.$digest();
});
- it('does not check the balance change', function () {
- expect(LeaveRequestService.promptBalanceChangeRecalculation)
- .not.toHaveBeenCalled();
- });
+ describe('when request is not TOIL', function () {
+ beforeEach(function () {
+ controller.submit();
+ $rootScope.$digest();
+ });
- it('updates request', function () {
- expect(controller.request.update).toHaveBeenCalled();
- });
+ it('does not check the balance change', function () {
+ expect(LeaveRequestService.promptBalanceChangeRecalculation)
+ .not.toHaveBeenCalled();
+ });
- it('tells the backend to not recalculate balance change', function () {
- expect(controller.request.change_balance).toBeUndefined();
+ it('updates request', function () {
+ expect(controller.request.update).toHaveBeenCalled();
+ });
+
+ it('tells the backend to not recalculate balance change', function () {
+ expect(controller.request.change_balance).toBeUndefined();
+ });
+
+ it('reverts to original request times', function () {
+ expect(moment(controller.request.from_date).format('HH:mm')).toEqual(
+ moment(requestOriginalDates.from).format('HH:mm'));
+ expect(moment(controller.request.to_date).format('HH:mm')).toEqual(
+ moment(requestOriginalDates.to).format('HH:mm'));
+ });
});
- it('reverts original request times', function () {
- expect(moment(controller.request.from_date).format('HH:mm')).toEqual(
- moment(requestOriginalDates.from).format('HH:mm'));
- expect(moment(controller.request.to_date).format('HH:mm')).toEqual(
- moment(requestOriginalDates.to).format('HH:mm'));
+ describe('when request is TOIL', function () {
+ beforeEach(function () {
+ controller.request.request_type = 'toil';
+
+ controller.submit();
+ $rootScope.$digest();
+ });
+
+ it('does not revert to original request times', function () {
+ expect(moment(controller.request.from_date).format('HH:mm')).not.toEqual(
+ moment(requestOriginalDates.from).format('HH:mm'));
+ expect(moment(controller.request.to_date).format('HH:mm')).not.toEqual(
+ moment(requestOriginalDates.to).format('HH:mm'));
+ });
});
});
@@ -1323,6 +1350,101 @@
}
});
+ describe('checking if it is a self record', function () {
+ var leaveRequest;
+ var loggedInContactId = CRM.vars.leaveAndAbsences.contactId.toString();
+ var anotherContactId = _.uniqueId();
+
+ beforeEach(function () {
+ role = 'admin';
+ leaveRequest = LeaveRequestInstance.init({});
+ });
+
+ describe('when the section is My Leave', function () {
+ beforeEach(function () {
+ $rootScope.section = 'my-leave';
+ });
+
+ describe('and the user is checking someone else\'s request', function () {
+ beforeEach(function () {
+ leaveRequest.id = _.uniqueId();
+ leaveRequest.contact_id = anotherContactId;
+
+ initTestController({ leaveRequest: leaveRequest });
+ });
+
+ it('sets is self record as false', function () {
+ expect(controller.isSelfRecord).toBe(false);
+ });
+ });
+
+ describe('and the user is checking my own request', function () {
+ beforeEach(function () {
+ leaveRequest.id = _.uniqueId();
+ leaveRequest.contact_id = loggedInContactId;
+
+ initTestController({ leaveRequest: leaveRequest });
+ });
+
+ it('sets is self record as true', function () {
+ expect(controller.isSelfRecord).toBe(true);
+ });
+ });
+
+ describe('and the user creates a new request for themselves', function () {
+ beforeEach(function () {
+ initTestController({ mode: 'create', leaveRequest: leaveRequest });
+ });
+
+ it('sets is self record as true', function () {
+ expect(controller.isSelfRecord).toBe(true);
+ });
+ });
+ });
+
+ describe('when the section is Manager Leave', function () {
+ beforeEach(function () {
+ $rootScope.section = 'manager-leave';
+ });
+
+ describe('and the user is checking someone else\'s request', function () {
+ beforeEach(function () {
+ leaveRequest.id = _.uniqueId();
+ leaveRequest.contact_id = anotherContactId;
+
+ initTestController({ leaveRequest: leaveRequest });
+ });
+
+ it('sets is self record as false', function () {
+ expect(controller.isSelfRecord).toBe(false);
+ });
+ });
+
+ describe('and the user is checking my own request', function () {
+ beforeEach(function () {
+ leaveRequest.id = _.uniqueId();
+ leaveRequest.contact_id = loggedInContactId;
+
+ initTestController({ leaveRequest: leaveRequest });
+ });
+
+ it('sets is self record as false', function () {
+ expect(controller.isSelfRecord).toBe(false);
+ });
+ });
+
+ describe('and the user creates a new request for themselves', function () {
+ beforeEach(function () {
+ initTestController({ mode: 'create', leaveRequest: leaveRequest });
+ });
+
+ it('sets is self record as false', function () {
+ expect(controller.isSelfRecord).toBe(false);
+ });
+ });
+ });
+ });
+
/**
* Initialize the controller
*
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/leave-calendar-admin.controller.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/leave-calendar-admin.controller.spec.js
index 5488d3ca8f3..d019f3510cd 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/leave-calendar-admin.controller.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/leave-calendar-admin.controller.spec.js
@@ -2,12 +2,13 @@
(function (CRM) {
define([
+ 'leave-absences/mocks/services/leave-calendar.service.mock',
'leave-absences/my-leave/app'
- ], function () {
+ ], function (leaveCalendarServiceMock) {
'use strict';
describe('LeaveCalendarAdminController', function () {
- var $controller, $log, $provide, $rootScope, Contact, controller;
+ var $controller, $log, $provide, $rootScope, Contact, controller, vm;
var contactId = CRM.vars.leaveAndAbsences.contactId;
beforeEach(module('my-leave', function (_$provide_) {
@@ -16,9 +17,10 @@
beforeEach(inject(['api.contact.mock', function (ContactAPIMock) {
$provide.value('api.contact', ContactAPIMock);
+ $provide.value('LeaveCalendarService', leaveCalendarServiceMock.service);
}]));
- beforeEach(inject(function (_$controller_, _$log_, _$rootScope_, _Contact_) {
+ beforeEach(inject(function (_$controller_, _$log_, $q, _$rootScope_, _Contact_) {
$controller = _$controller_;
$log = _$log_;
$rootScope = _$rootScope_;
@@ -26,6 +28,7 @@
spyOn($log, 'debug');
spyOn(Contact, 'all').and.callThrough();
+ leaveCalendarServiceMock.setup($q);
initController();
}));
@@ -34,27 +37,49 @@
expect($log.debug).toHaveBeenCalled();
});
+ it('initializes the leave calendar service', function () {
+ expect(leaveCalendarServiceMock.service.init).toHaveBeenCalledWith(vm);
+ });
+
+ it('selects the assignee filter "People I approve" by default', function () {
+ expect(vm.filters.userSettings.assignedTo.type).toBe('me');
+ });
+
describe('loadContacts()', function () {
- beforeEach(function () {
- controller.loadContacts();
- $rootScope.$digest();
- });
+ var contacts;
+
+ describe('when loading all of the contacts', function () {
+ beforeEach(function (done) {
+ controller.loadContacts()
+ .then(function (_contacts_) {
+ contacts = _contacts_;
+ })
+ .finally(done);
+ $rootScope.$digest();
+ });
- it('filters the contact using the filters selected by the user', function () {
- expect(Object.keys(Contact.all.calls.mostRecent().args[0])).toContain('department');
- expect(Object.keys(Contact.all.calls.mostRecent().args[0])).toContain('level_type');
- expect(Object.keys(Contact.all.calls.mostRecent().args[0])).toContain('location');
- expect(Object.keys(Contact.all.calls.mostRecent().args[0])).toContain('region');
- expect(Object.keys(Contact.all.calls.mostRecent().args[0])).toContain('id');
+ it('requests contacts by assignation type', function () {
+ expect(leaveCalendarServiceMock.instance.loadContactsForAdmin).toHaveBeenCalledWith();
+ });
+
+ it('returns the list of filtered contacts', function () {
+ expect(contacts).toEqual(leaveCalendarServiceMock.data.filteredContacts);
+ });
});
});
function initController () {
- controller = $controller('LeaveCalendarAdminController').init({
+ vm = {
contactId: contactId,
filters: { userSettings: {} },
- selectedPeriod: { start_date: '2016-01-01', end_date: '2016-12-31' }
- });
+ selectedPeriod: { start_date: '2016-01-01', end_date: '2016-12-31' },
+ filtersByAssignee: [
+ { type: 'me', label: 'People I approve' },
+ { type: 'unassigned', label: 'People without approver' },
+ { type: 'all', label: 'All' }
+ ]
+ };
+ controller = $controller('LeaveCalendarAdminController').init(vm);
}
});
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/leave-calendar-manager.controller.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/leave-calendar-manager.controller.spec.js
index 1a1ac13d1b2..1f01a6af8c7 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/leave-calendar-manager.controller.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/leave-calendar-manager.controller.spec.js
@@ -2,12 +2,14 @@
(function (CRM) {
define([
+ 'leave-absences/mocks/services/leave-calendar.service.mock',
'leave-absences/my-leave/app'
- ], function () {
+ ], function (leaveCalendarServiceMock) {
'use strict';
describe('LeaveCalendarManagerController', function () {
- var $controller, $log, $provide, $rootScope, Contact, ContactInstance, controller, realContactInstance;
+ var $controller, $log, $provide, $rootScope, Contact, ContactInstance, controller,
+ realContactInstance, vm;
var contactId = CRM.vars.leaveAndAbsences.contactId;
beforeEach(module('my-leave', function (_$provide_) {
@@ -16,15 +18,17 @@
beforeEach(inject(['api.contact.mock', function (ContactAPIMock) {
$provide.value('api.contact', ContactAPIMock);
+ $provide.value('LeaveCalendarService', leaveCalendarServiceMock.service);
}]));
- beforeEach(inject(function (_$controller_, _$log_, _$rootScope_, _Contact_, _ContactInstance_) {
+ beforeEach(inject(function (_$controller_, _$log_, $q, _$rootScope_, _Contact_, _ContactInstance_) {
$controller = _$controller_;
$log = _$log_;
$rootScope = _$rootScope_;
Contact = _Contact_;
ContactInstance = _ContactInstance_;
+ leaveCalendarServiceMock.setup($q);
spyOn($log, 'debug');
spyOn(Contact, 'all').and.callThrough();
spyOnContactInstance();
@@ -36,22 +40,32 @@
expect($log.debug).toHaveBeenCalled();
});
+ it('initializes the leave calendar service', function () {
+ expect(leaveCalendarServiceMock.service.init).toHaveBeenCalledWith(vm);
+ });
+
+ it('selects the assignee filter "People I approve" by default', function () {
+ expect(vm.filters.userSettings.assignedTo.type).toBe('me');
+ });
+
describe('loadContacts()', function () {
- beforeEach(function () {
- controller.loadContacts();
+ var contacts;
+
+ beforeEach(function (done) {
+ controller.loadContacts()
+ .then(function (_contacts_) {
+ contacts = _contacts_;
+ })
+ .finally(done);
$rootScope.$digest();
});
- it('gets the leave managees of the current contact', function () {
- expect(realContactInstance.leaveManagees).toHaveBeenCalled();
+ it('requests the look up and filtered contacts', function () {
+ expect(leaveCalendarServiceMock.instance.loadLookUpAndFilteredContacts).toHaveBeenCalledWith();
});
- it('filters the contact using the filters selected by the user', function () {
- expect(Object.keys(Contact.all.calls.mostRecent().args[0])).toContain('department');
- expect(Object.keys(Contact.all.calls.mostRecent().args[0])).toContain('level_type');
- expect(Object.keys(Contact.all.calls.mostRecent().args[0])).toContain('location');
- expect(Object.keys(Contact.all.calls.mostRecent().args[0])).toContain('region');
- expect(Object.keys(Contact.all.calls.mostRecent().args[0])).toContain('id');
+ it('returns the filtered contacts', function () {
+ expect(contacts).toEqual(leaveCalendarServiceMock.data.filteredContacts);
});
});
@@ -72,10 +86,16 @@
}
function initController () {
- controller = $controller('LeaveCalendarManagerController').init({
+ vm = {
contactId: contactId,
- filters: { userSettings: {} }
- });
+ filters: { userSettings: {} },
+ filtersByAssignee: [
+ { type: 'me', label: 'People I approve' },
+ { type: 'unassigned', label: 'People without approver' },
+ { type: 'all', label: 'All' }
+ ]
+ };
+ controller = $controller('LeaveCalendarManagerController').init(vm);
}
});
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/leave-calendar-staff.controller.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/leave-calendar-staff.controller.spec.js
index ab62da48daf..529f6edc164 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/leave-calendar-staff.controller.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/leave-calendar-staff.controller.spec.js
@@ -2,23 +2,40 @@
(function (CRM) {
define([
+ 'common/lodash',
+ 'leave-absences/mocks/services/leave-calendar.service.mock',
'leave-absences/my-leave/app'
- ], function () {
+ ], function (_, leaveCalendarServiceMock) {
'use strict';
describe('LeaveCalendarStaffController', function () {
- var $controller, $log, Contact, controller;
+ var $controller, $log, $provide, $rootScope, canManageRequests, controller,
+ vm;
var contactId = CRM.vars.leaveAndAbsences.contactId;
- beforeEach(module('my-leave'));
- beforeEach(inject(function (_$controller_, _$log_, _Contact_) {
+ beforeEach(module('my-leave', function (_$provide_) {
+ $provide = _$provide_;
+ }));
+
+ beforeEach(inject(['api.contact.mock', function (ContactAPIMock) {
+ $provide.value('api.contact', ContactAPIMock);
+ $provide.value('LeaveCalendarService', leaveCalendarServiceMock.service);
+ }]));
+
+ beforeEach(inject(function (_$controller_, _$log_, $q, _$rootScope_) {
$controller = _$controller_;
$log = _$log_;
- Contact = _Contact_;
+ $rootScope = _$rootScope_;
+ canManageRequests = jasmine.createSpy('canManageRequests');
+ vm = {
+ displaySingleContact: false,
+ contactId: contactId,
+ filters: { userSettings: {} },
+ canManageRequests: canManageRequests
+ };
spyOn($log, 'debug');
- spyOn(Contact, 'all').and.callThrough();
-
+ leaveCalendarServiceMock.setup($q);
initController();
}));
@@ -26,23 +43,105 @@
expect($log.debug).toHaveBeenCalled();
});
+ it('initializes the leave calendar service', function () {
+ expect(leaveCalendarServiceMock.service.init).toHaveBeenCalledWith(vm);
+ });
+
+ it('displays the contact names', function () {
+ expect(vm.showContactName).toBe(true);
+ });
+
+ it('displays the contact filters', function () {
+ expect(vm.showFilters).toBe(true);
+ });
+
+ it('always displays the logged in contact even if they do not have requests for the selected period', function () {
+ expect(vm.showTheseContacts).toEqual([vm.contactId]);
+ });
+
describe('loadContacts()', function () {
- beforeEach(function () {
- controller.loadContacts();
+ var loadedContacts;
+ var scenarios = [
+ { role: 'staff' },
+ { role: 'manager' }
+ ];
+
+ scenarios.forEach(function (scenario) {
+ describe('as a ' + scenario.role, function () {
+ beforeEach(function (done) {
+ vm.userPermissionRole = scenario.role;
+
+ controller.loadContacts()
+ .then(function (_loadedContacts_) {
+ loadedContacts = _loadedContacts_;
+ })
+ .finally(done);
+ $rootScope.$digest();
+ });
+
+ it('loads the look up and filtered contacts', function () {
+ expect(leaveCalendarServiceMock.instance.loadLookUpAndFilteredContacts).toHaveBeenCalledWith();
+ });
+
+ it('returns the filtered contacts', function () {
+ expect(loadedContacts).toEqual(leaveCalendarServiceMock.data.filteredContacts);
+ });
+ });
});
- it('simply fetches the data of the current contact', function () {
- expect(Contact.all).toHaveBeenCalledWith({
- id: { in: [contactId] }
+ describe('as an admin', function () {
+ beforeEach(function (done) {
+ vm.userPermissionRole = 'admin';
+ controller.loadContacts()
+ .then(function (_loadedContacts_) {
+ loadedContacts = _loadedContacts_;
+ })
+ .finally(done);
+ $rootScope.$digest();
+ });
+
+ it('loads the filtered contacts', function () {
+ expect(leaveCalendarServiceMock.instance.loadContactsForAdmin).toHaveBeenCalledWith();
+ });
+
+ it('returns the filtered contacts', function () {
+ expect(loadedContacts).toEqual(leaveCalendarServiceMock.data.filteredContacts);
+ });
+ });
+
+ describe('when the component should only display a single contact', function () {
+ beforeEach(function () {
+ vm.displaySingleContact = true;
+ vm.contactId = _.uniqueId();
+ vm.lookupContacts = [];
+ initController();
+
+ controller.loadContacts();
+ });
+
+ it('only loads the information for the given contact', function () {
+ expect(vm.lookupContacts).toEqual([{ id: vm.contactId }]);
+ expect(leaveCalendarServiceMock.instance.loadFilteredContacts).toHaveBeenCalledWith();
});
});
});
- function initController () {
- controller = $controller('LeaveCalendarStaffController').init({
- contactId: contactId,
- filters: { userSettings: {} }
+ describe('when the component only displays a single contact', function () {
+ beforeEach(function () {
+ vm.displaySingleContact = true;
+ initController();
});
+
+ it('does not show the filters', function () {
+ expect(vm.showFilters).toEqual(false);
+ });
+ });
+
+ /**
+ * Initializes the leave calendar staff sub controller.
+ */
+ function initController () {
+ controller = $controller('LeaveCalendarStaffController').init(vm);
}
});
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/request-modal-details-toil.controller.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/request-modal-details-toil.controller.spec.js
index 85b5be7bdb3..8750d3b2664 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/request-modal-details-toil.controller.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/controllers/sub-controllers/request-modal-details-toil.controller.spec.js
@@ -476,7 +476,7 @@ define([
requestModalHelper.setTestDates(controller, date2016, date2016To);
$rootScope.$digest();
- expiryDate = new Date(controller.request.toil_expiry_date);
+ expiryDate = controller.convertDateFormatFromServer(controller.request.toil_expiry_date);
originalToilToAccrue = optionGroupMock.specificObject('hrleaveandabsences_toil_amounts', 'name', 'quarter_day');
controller.request.toil_to_accrue = originalToilToAccrue.value;
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/models/leave-request.model.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/models/leave-request.model.spec.js
index 6444385fc57..b5b35f85a90 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/models/leave-request.model.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/models/leave-request.model.spec.js
@@ -2,7 +2,8 @@
define([
'leave-absences/shared/models/leave-request.model',
- 'leave-absences/mocks/apis/leave-request-api-mock'
+ 'leave-absences/mocks/apis/leave-request-api-mock',
+ 'leave-absences/mocks/apis/option-group-api-mock'
], function () {
'use strict';
@@ -85,7 +86,7 @@ define([
var leaveRequestPromise;
beforeEach(function () {
- leaveRequestPromise = LeaveRequest.balanceChangeByAbsenceType(jasmine.any(String), jasmine.any(String));
+ leaveRequestPromise = LeaveRequest.balanceChangeByAbsenceType(jasmine.any(Object));
});
afterEach(function () {
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/services/leave-calendar.service.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/services/leave-calendar.service.spec.js
new file mode 100644
index 00000000000..fb958ab0e61
--- /dev/null
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/services/leave-calendar.service.spec.js
@@ -0,0 +1,385 @@
+/* eslint-env amd, jasmine */
+
+define([
+ 'common/lodash',
+ 'common/mocks/data/contact.data',
+ 'common/mocks/data/contract.data',
+ 'common/models/contact',
+ 'common/models/contract',
+ 'common/services/hr-settings',
+ 'leave-absences/shared/services/leave-calendar.service'
+], function (_, contactsMockData, contractsMockData) {
+ 'use strict';
+
+ describe('LeaveCalendarService', function () {
+ var $rootScope, Contact, contractApi, customContractValues, leaveCalendar,
+ loadedContacts, mockedContracts, vm;
+ var mockData = {};
+
+ beforeEach(module('common.mocks', 'common.models', 'common.services',
+ 'leave-absences.services', function ($provide) {
+ contractApi = {
+ all: jasmine.createSpy('all')
+ };
+ customContractValues = [
+ { contact_id: _.uniqueId(), period_start_date: '2000-01-01', period_end_date: '2000-01-31' },
+ { contact_id: _.uniqueId(), period_start_date: '2000-02-01', period_end_date: '2000-02-29' },
+ { contact_id: _.uniqueId(), period_start_date: '2000-03-01', period_end_date: '2000-03-31' }
+ ];
+ mockedContracts = getMockedContracts(customContractValues);
+
+ $provide.value('api.contract', contractApi);
+ })
+ );
+
+ beforeEach(inject(function ($q, _$rootScope_, _Contact_, LeaveCalendarService) {
+ var contacts = contactsMockData.all.values;
+ $rootScope = _$rootScope_;
+ Contact = _Contact_;
+ mockData = {
+ lookupContacts: contacts,
+ contacts: contacts.slice(0, 2)
+ };
+ vm = {
+ contactId: _.uniqueId(),
+ lookupContacts: [],
+ selectedPeriod: { start_date: '2000-01-01', end_date: '2000-03-31' },
+ filters: {
+ userSettings: {
+ assignedTo: { type: 'all' },
+ department: { value: _.uniqueId() },
+ level_type: { value: _.uniqueId() },
+ location: { value: _.uniqueId() },
+ region: { value: _.uniqueId() }
+ }
+ }
+ };
+
+ spyOn(Contact, 'all').and.returnValue($q.resolve({ list: mockData.contacts }));
+ spyOn(Contact, 'leaveManagees').and.returnValue($q.resolve(mockData.lookupContacts));
+ contractApi.all.and.returnValue($q.resolve(mockedContracts));
+
+ leaveCalendar = LeaveCalendarService.init(vm);
+ }));
+
+ it('returns a leave calendar service instance', function () {
+ expect(leaveCalendar).toBeDefined();
+ });
+
+ describe('loadContactsForAdmin()', function () {
+ describe('when loading all contacts', function () {
+ beforeEach(function (done) {
+ vm.filters.userSettings.assignedTo.type = 'all';
+ loadContactsForAdmin(done);
+ });
+
+ it('stores all look up contact', function () {
+ expect(vm.lookupContacts).toEqual(mockData.contacts);
+ });
+ });
+
+ describe('when loading my assigned contacts', function () {
+ beforeEach(function (done) {
+ vm.filters.userSettings.assignedTo.type = 'me';
+ loadContactsForAdmin(done);
+ });
+
+ it('requests my assigned contacts', function () {
+ expect(Contact.leaveManagees).toHaveBeenCalledWith(vm.contactId);
+ });
+
+ it('returns a list of contacts', function () {
+ expect(loadedContacts).toEqual(mockData.contacts);
+ });
+
+ it('stores my assignees as look up contacts', function () {
+ expect(vm.lookupContacts).toEqual(mockData.lookupContacts);
+ });
+ });
+
+ describe('when loading unassigned contacts', function () {
+ beforeEach(function (done) {
+ vm.filters.userSettings.assignedTo.type = 'unassigned';
+
+ loadContactsForAdmin(done);
+ });
+
+ it('requests unassigned contacts', function () {
+ expect(Contact.leaveManagees)
+ .toHaveBeenCalledWith(undefined, { unassigned: true });
+ });
+
+ it('returns a list of contacts', function () {
+ expect(loadedContacts).toEqual(mockData.contacts);
+ });
+
+ it('stores the unassigned contacts as look up contacts', function () {
+ expect(vm.lookupContacts).toEqual(mockData.lookupContacts);
+ });
+ });
+
+ describe('populating the contact ids to reduce to', function () {
+ var expectedContactIdsToReduceTo;
+
+ describe('when all the contracts are covered by the period filters', function () {
+ beforeEach(function (done) {
+ expectedContactIdsToReduceTo = _.pluck(customContractValues, 'contact_id');
+ vm.selectedPeriod = {
+ start_date: customContractValues[0].period_start_date,
+ end_date: customContractValues[2].period_end_date
+ };
+
+ loadContactsForAdmin(done);
+ });
+
+ it('returns all the contact ids with contracts within the selected period', function () {
+ expect(vm.contactIdsToReduceTo).toEqual(expectedContactIdsToReduceTo);
+ });
+ });
+
+ describe('when only some of the contracts are covered by the period filters', function () {
+ beforeEach(function (done) {
+ expectedContactIdsToReduceTo = _.pluck(customContractValues, 'contact_id').slice(1, 3);
+ vm.selectedPeriod = {
+ start_date: customContractValues[1].period_start_date,
+ end_date: customContractValues[2].period_end_date
+ };
+
+ loadContactsForAdmin(done);
+ });
+
+ it('returns all the contact ids with contracts within the selected period', function () {
+ expect(vm.contactIdsToReduceTo).toEqual(expectedContactIdsToReduceTo);
+ });
+ });
+ });
+ });
+
+ describe('loadFilteredContacts()', function () {
+ var expectedFilters;
+
+ beforeEach(function () {
+ expectedFilters = {
+ department: vm.filters.userSettings.department.value,
+ level_type: vm.filters.userSettings.level_type.value,
+ location: vm.filters.userSettings.location.value,
+ region: vm.filters.userSettings.region.value
+ };
+ });
+
+ describe('general case', function () {
+ var filteredContacts;
+
+ beforeEach(function (done) {
+ leaveCalendar.loadFilteredContacts()
+ .then(function (_filteredContacts_) {
+ filteredContacts = _filteredContacts_;
+ })
+ .finally(done);
+ $rootScope.$digest();
+ });
+
+ it('requests a list of contacts using the selected filters and sorted by display name', function () {
+ expect(Contact.all).toHaveBeenCalledWith(
+ jasmine.objectContaining(expectedFilters), null, 'display_name');
+ });
+
+ it('returns a list of filtered contacts', function () {
+ expect(filteredContacts).toEqual(mockData.contacts);
+ });
+ });
+
+ describe('when the contact filter is selected', function () {
+ beforeEach(function () {
+ vm.filters.userSettings.contact = { id: _.uniqueId() };
+ expectedFilters.id = { IN: [vm.filters.userSettings.contact.id] };
+
+ leaveCalendar.loadFilteredContacts();
+ });
+
+ it('only returns the selected contact from the filter', function () {
+ expect(Contact.all).toHaveBeenCalledWith(expectedFilters, null, 'display_name');
+ });
+ });
+
+ describe('when there is a list of look up contacts', function () {
+ var expectedContactIds;
+
+ beforeEach(function () {
+ vm.lookupContacts = _.shuffle(mockData.contacts).slice(0, 2);
+ expectedContactIds = _.pluck(vm.lookupContacts, 'id');
+ expectedFilters.id = { IN: expectedContactIds };
+
+ leaveCalendar.loadFilteredContacts();
+ });
+
+ it('only returns the contacts from the look up list', function () {
+ expect(Contact.all).toHaveBeenCalledWith(expectedFilters, null, 'display_name');
+ });
+ });
+
+ describe('when no contact or look up list are selected as filters', function () {
+ beforeEach(function () {
+ delete vm.lookupContacts;
+ delete vm.filters.userSettings.contact;
+
+ leaveCalendar.loadFilteredContacts();
+ });
+
+ it('does not filter the request by contact id', function () {
+ expect(Contact.all).not.toHaveBeenCalledWith(jasmine.objectContaining({
+ id: { 'IN': jasmine.any(Array) }
+ }), null, 'display_name');
+ });
+ });
+
+ describe('when the asegnee filter is not "all" and the look up list is empty', function () {
+ beforeEach(function () {
+ vm.lookupContacts = [];
+ vm.filters.userSettings.assignedTo.type = 'me';
+
+ leaveCalendar.loadFilteredContacts();
+ });
+
+ it('filters the request by look up contact ids', function () {
+ expect(Contact.all).toHaveBeenCalledWith(jasmine.objectContaining({
+ id: { 'IN': vm.lookupContacts }
+ }), null, 'display_name');
+ });
+ });
+ });
+
+ describe('loadLookUpAndFilteredContacts()', function () {
+ beforeEach(function (done) {
+ vm.filters.userSettings.assignedTo.type = 'me';
+
+ leaveCalendar.loadLookUpAndFilteredContacts()
+ .then(function (contacts) {
+ loadedContacts = contacts;
+ })
+ .finally(done);
+ $rootScope.$digest();
+ });
+
+ it('requests a list of look up contacts', function () {
+ expect(Contact.leaveManagees).toHaveBeenCalled();
+ });
+
+ it('stores a list of look up contacts', function () {
+ expect(vm.lookupContacts).toEqual(mockData.lookupContacts);
+ });
+
+ it('requests a list of filtered contacts using the look ups', function () {
+ expect(Contact.all).toHaveBeenCalledWith(jasmine.objectContaining({
+ id: { 'IN': _.pluck(vm.lookupContacts, 'id') }
+ }), null, 'display_name');
+ });
+
+ it('returns a list of contacts', function () {
+ expect(loadedContacts).toEqual(mockData.contacts);
+ });
+ });
+
+ describe('loadLookUpContacts()', function () {
+ describe('when the assignees filter value is "all"', function () {
+ beforeEach(function (done) {
+ vm.filters.userSettings.assignedTo.type = 'all';
+
+ loadLookUpContacts(done);
+ });
+
+ it('requests all contacts', function () {
+ expect(Contact.all).toHaveBeenCalledWith();
+ });
+
+ it('returns a list of contacts', function () {
+ expect(loadedContacts).toEqual(mockData.contacts);
+ });
+ });
+
+ describe('when the assignees filter value is "me"', function () {
+ beforeEach(function (done) {
+ vm.filters.userSettings.assignedTo.type = 'me';
+
+ loadLookUpContacts(done);
+ });
+
+ it('requests the logged in user\'s assigned contacts', function () {
+ expect(Contact.leaveManagees).toHaveBeenCalledWith(vm.contactId);
+ });
+
+ it('returns a list of the logged in user\'s assigned contacts', function () {
+ expect(loadedContacts).toEqual(mockData.lookupContacts);
+ });
+ });
+
+ describe('when the assignees filter value is "unassigned"', function () {
+ beforeEach(function (done) {
+ vm.filters.userSettings.assignedTo.type = 'unassigned';
+
+ loadLookUpContacts(done);
+ });
+
+ it('requests unassigned contacts', function () {
+ expect(Contact.leaveManagees)
+ .toHaveBeenCalledWith(undefined, { unassigned: true });
+ });
+
+ it('returns a list of unassigned contacts', function () {
+ expect(loadedContacts).toEqual(mockData.lookupContacts);
+ });
+ });
+ });
+
+ /**
+ * Returns a list of mocked contracts with overriden values as provided by
+ * the custom contract values parameter.
+ *
+ * @param {Array} customContractValues a list of values to override for each contract.
+ * @return {Array} the list of overriden contracts.
+ */
+ function getMockedContracts (customContractValues) {
+ var mockedContracts = _.cloneDeep(contractsMockData.all);
+
+ mockedContracts.values = customContractValues.map(function (customContractValue, i) {
+ var contractData = mockedContracts.values[i] || {};
+
+ return _.extend(contractData, {
+ contact_id: customContractValue.contact_id,
+ 'info': {
+ details: {
+ period_start_date: customContractValue.period_start_date,
+ period_end_date: customContractValue.period_end_date
+ }
+ }
+ });
+ });
+
+ return mockedContracts.values;
+ }
+
+ /**
+ * Executes the load contacts for admin function and stores the result.
+ *
+ * @param {Function} done the jasmine done function to execute once the contact
+ * ids have been loaded.
+ */
+ function loadContactsForAdmin (done) {
+ leaveCalendar.loadContactsForAdmin()
+ .then(function (_loadedContacts_) {
+ loadedContacts = _loadedContacts_;
+ })
+ .finally(done);
+ $rootScope.$digest();
+ }
+
+ function loadLookUpContacts (done) {
+ leaveCalendar.loadLookUpContacts()
+ .then(function (_loadedContacts_) {
+ loadedContacts = _loadedContacts_;
+ })
+ .finally(done);
+ $rootScope.$digest();
+ }
+ });
+});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/services/leave-popup.service.spec.js b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/services/leave-popup.service.spec.js
index 68aea8ac628..8ab88dd2f8b 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/services/leave-popup.service.spec.js
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/js/angular/test/shared/services/leave-popup.service.spec.js
@@ -44,7 +44,7 @@ define([
var forceRecalculateBalanceChange = true;
beforeEach(function () {
- LeavePopup.openModal(jasmine.any(String), jasmine.any(String), jasmine.any(String), jasmine.any(String),
+ LeavePopup.openModal(jasmine.any(String), jasmine.any(String), jasmine.any(String),
forceRecalculateBalanceChange);
$rootScope.$digest();
});
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/scss/components/_leave-calendar.scss b/uk.co.compucorp.civicrm.hrleaveandabsences/scss/components/_leave-calendar.scss
index e4766859fd4..712707b8b64 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/scss/components/_leave-calendar.scss
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/scss/components/_leave-calendar.scss
@@ -1,9 +1,30 @@
-$chr-leave-calendar-cell-height: 35px;
-$chr-leave-calendar-cell-font-size: 10px;
-$chr-leave-calendar-item-requested-border-color: #c7cbd0;
-$chr-leave-calendar-item-requested-border-height: 0.5em;
-$chr-leave-calendar-legend-size: 20px;
+$chr-leave-calendar-cell-height: 35px;
+$chr-leave-calendar-cell-font-size: 10px;
+$chr-leave-calendar-item-generic-leave-background-color: #4d4d68;
+$chr-leave-calendar-item-weekend-background-color: #c7cbd0;
+$chr-leave-calendar-item-non-working-day-background-color: #41afcb;
+$chr-leave-calendar-item-public-holiday-background-color: #8ec68a;
+$chr-leave-calendar-item-requested-border-height: 0.5em;
+$chr-leave-calendar-legend-header-top-margin: 20px;
+$chr-leave-calendar-legend-badge-border-radius: 2px;
+$chr-leave-calendar-legend-badge-padding: 8px;
+$chr-leave-calendar-legend-badge-margin: 10px;
+$chr-leave-calendar-legend-badge-size: 24px;
+$chr-leave-calendar-legend-badge-square-right-margin: 20px;
+$chr-leave-calendar-legend-badge-square-font-size: 12px;
+$chr-leave-calendar-legend-badge-square-hours-font-size: 6px;
+.chr_leave-calendar__admin-filters-hint {
+ display: inline-block;
+ font-size: 20px;
+ vertical-align: middle;
+}
+
+.chr_leave-calendar__admin-filters-select {
+ display: inline-block;
+ margin-right: 20px;
+ width: calc(100% - 50px);
+}
.chr_leave-calendar__dates-container {
overflow-x: auto;
@@ -15,12 +36,6 @@ $chr-leave-calendar-legend-size: 20px;
}
}
-.chr_leave-calendar__names-container {
- .chr_leave-calendar {
- table-layout: fixed;
- }
-}
-
.chr_leave-calendar__day {
font-size: $chr-leave-calendar-cell-font-size !important;
line-height: 1.4em !important;
@@ -40,6 +55,21 @@ $chr-leave-calendar-legend-size: 20px;
color: $brand-primary !important;
}
+.chr_leave-calendar__day-container--type--non-working {
+ background-color: $chr-leave-calendar-item-non-working-day-background-color;
+}
+
+.chr_leave-calendar__day-container--type--public-holiday {
+ background-color: $chr-leave-calendar-item-public-holiday-background-color;
+}
+
+.chr_leave-calendar__day-container--type--weekend {
+ background-color: $chr-leave-calendar-item-weekend-background-color;
+}
+
+.chr_leave-calendar__day-container {
+ height: 100%;
+}
.chr_leave-calendar__item {
color: #fff;
@@ -97,21 +127,9 @@ $chr-leave-calendar-legend-size: 20px;
border-color: #eca67e;
}
-.chr_leave-calendar__day-container--type--non-working {
- background-color: #41afcb;
-}
-
-.chr_leave-calendar__day-container--type--public-holiday {
- background-color: #8ec68a;
-}
-
-.chr_leave-calendar__day-container--type--weekend {
- background-color: #c7cbd0;
-}
-
.chr_leave-calendar__item--alt {
background-color: #fff !important;
- border: 1px solid transparent;
+ border: 2px solid transparent;
color: $gray-darker;
}
@@ -119,7 +137,8 @@ $chr-leave-calendar-legend-size: 20px;
@include striped-background(rgba(255, 255, 255, 0.3));
&.chr_leave-calendar__item--alt {
- @include striped-background(rgba(0, 0, 0, 0.1));
+ @include striped-background($crm-white);
+ background-color: $crm-grayblue-dark !important;
}
}
@@ -139,50 +158,106 @@ $chr-leave-calendar-legend-size: 20px;
}
}
-.chr_leave-calendar__day-container {
- height: 100%;
-}
-
.chr_leave-calendar__legend__title {
font-size: $font-size-small;
}
-.chr_leave-calendar__legend {
- > .row:not(:first-child) {
- margin-top: 20px;
+.chr_leave-calendar__legend__header:not(:first-child) {
+ margin-top: $chr-leave-calendar-legend-header-top-margin;
+}
+
+.chr_leave-calendar__legend__type-badge,
+.chr_leave-calendar__legend__other-badge-wrapper {
+ float: left;
+ line-height: $chr-leave-calendar-legend-badge-size;
+ margin-top: $chr-leave-calendar-legend-badge-margin;
+ white-space: nowrap;
+}
+
+.chr_leave-calendar__legend__other-badge-wrapper {
+ margin-right: $chr-leave-calendar-legend-badge-square-right-margin;
+}
+
+.chr_leave-calendar__legend__type-badge,
+.chr_leave-calendar__legend__other-badge {
+ color: $gray-dark;
+ height: $chr-leave-calendar-legend-badge-size;
+}
+
+.chr_leave-calendar__legend__type-badge {
+ border-radius: $chr-leave-calendar-legend-badge-border-radius;
+ color: $crm-white;
+ margin-right: $chr-leave-calendar-legend-badge-margin;
+ margin-top: 10px;
+ padding: 0 $chr-leave-calendar-legend-badge-padding;
+
+ &.chr_leave-calendar__legend__type-badge-weekend {
+ background-color: $chr-leave-calendar-item-weekend-background-color;
}
- .chr_leave-calendar__legend__entry {
- margin-top: 10px;
+ &.chr_leave-calendar__legend__type-badge-non-working-day {
+ background-color: $chr-leave-calendar-item-non-working-day-background-color;
+ }
- @media (min-width: $screen-sm-min) {
- margin-top: 0;
- }
+ &.chr_leave-calendar__legend__type-badge-public-holiday {
+ background-color: $chr-leave-calendar-item-public-holiday-background-color;
}
-}
-.chr_leave-calendar__legend__entry {
- overflow: auto;
+ &.chr_leave-calendar__legend__type-badge-generic-leave {
+ background-color: $chr-leave-calendar-item-generic-leave-background-color;
+ }
- & + & {
- margin-top: 10px;
+ &.chr_leave-calendar__legend__type-badge-selected,
+ &.chr_leave-calendar__legend__type-badge-clickable:hover {
+ border: solid 1px $crm-white;
+ border-radius: #{$chr-leave-calendar-legend-badge-border-radius + 2px};
+ box-shadow: 0 0 1px 0 $chr-leave-calendar-item-weekend-background-color;
+ height: #{$chr-leave-calendar-legend-badge-size + 2px};
+ margin-bottom: -1px;
+ margin-left: -2px;
+ margin-right: #{$chr-leave-calendar-legend-badge-margin - 1px};
+ margin-top: #{$chr-leave-calendar-legend-badge-margin - 1px};
+ padding-left: #{$chr-leave-calendar-legend-badge-padding + 1px};
}
- > .chr_leave-calendar__item {
- font-size: 8px;
- height: $chr-leave-calendar-legend-size;
- margin-right: 10px;
- position: absolute;
- width: $chr-leave-calendar-legend-size;
+ &.chr_leave-calendar__legend__type-badge-not-selected {
+ opacity: 0.4;
}
}
-.chr_leave-calendar__legend__label {
- display: inline-block;
- font-size: $font-size-small;
- margin-left: 26px;
+.chr_leave-calendar__legend__reset-absence-types-filter {
+ color: $gray-darker;
+}
+
+.chr_leave-calendar__legend__other-badge {
+ border: solid 1px $crm-grayblue-dark;
+ border-radius: $chr-leave-calendar-legend-badge-border-radius;
+ float: left;
+ font-size: $chr-leave-calendar-legend-badge-square-font-size;
+ font-weight: 600;
+ margin-right: $chr-leave-calendar-legend-badge-margin;
+ text-align: center;
+ width: $chr-leave-calendar-legend-badge-size;
+
+ &.chr_leave-calendar__legend__other-badge-hours {
+ font-size: $chr-leave-calendar-legend-badge-square-hours-font-size;
+ font-weight: $badge-font-weight;
+ }
+
+ &.chr_leave-calendar__legend__other-badge-requested {
+ @include striped-background(rgba(255, 255, 255, 0.6));
+ background-color: $crm-grayblue-dark !important;
+ border: 0;
+ }
}
+.chr_leave-calendar__month-container {
+ margin: 0;
+
+ > div {
+ padding: 0;
+ }
+}
.chr_leave-calendar__month-header {
background: $panel-default-heading-bg;
@@ -192,6 +267,21 @@ $chr-leave-calendar-legend-size: 20px;
}
}
+.chr_leave-calendar__month-header__go-to-current-month {
+ text-transform: uppercase;
+}
+
+.chr_leave-calendar__month-header__go-to-current-month,
+.chr_leave-calendar__month-header__month-paginator {
+ margin-right: 10px;
+}
+
+.chr_leave-calendar__month-header__month-paginator {
+ color: $gray-darker;
+ padding-left: 0;
+ padding-right: 0;
+}
+
.chr_leave-calendar__name {
font-size: $font-size-small !important;
line-height: 100%;
@@ -214,6 +304,12 @@ $chr-leave-calendar-legend-size: 20px;
vertical-align: middle !important;
}
+.chr_leave-calendar__names-container {
+ .chr_leave-calendar {
+ table-layout: fixed;
+ }
+}
+
.chr_leave-calendar__pagination {
font-size: $chr-leave-calendar-cell-font-size;
text-align: center;
@@ -253,23 +349,3 @@ $chr-leave-calendar-legend-size: 20px;
padding-right: 3px !important;
}
}
-
-.chr_leave-calendar__month-container {
- margin: 0;
-
- > div {
- padding: 0;
- }
-}
-
-.chr_leave-calendar__admin-filters-hint {
- display: inline-block;
- font-size: 20px;
- vertical-align: middle;
-}
-
-.chr_leave-calendar__admin-filters-select {
- display: inline-block;
- margin-right: 20px;
- width: calc(100% - 50px);
-}
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/scss/components/outside-namespace/_leave-calendar.scss b/uk.co.compucorp.civicrm.hrleaveandabsences/scss/components/outside-namespace/_leave-calendar.scss
index 01dcf9d5315..3b09ee00812 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/scss/components/outside-namespace/_leave-calendar.scss
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/scss/components/outside-namespace/_leave-calendar.scss
@@ -1,5 +1,6 @@
$chr-leave-calendar-tooltip-shadow: 0 3px 8px 0 rgba(49, 40, 40, 0.25);
$chr-leave-calendar-tooltip-item-min-width: 154px;
+$chr-leave-calendar-tooltip-font-size: 11px;
// @TODO the tooltip style should be moved to the global styling
.chr_leave-calendar__day-tooltip {
@@ -20,8 +21,9 @@ $chr-leave-calendar-tooltip-item-min-width: 154px;
}
}
-.chr_leave-calendar__day-tooltip_item {
+.chr_leave-calendar__day-tooltip__item {
cursor: pointer;
+ font-size: $chr-leave-calendar-tooltip-font-size;
min-width: $chr-leave-calendar-tooltip-item-min-width;
padding: $crm-gap-medium;
@@ -29,3 +31,12 @@ $chr-leave-calendar-tooltip-item-min-width: 154px;
background-color: $gray-light;
}
}
+
+.chr_leave-calendar__day-tooltip__item_absence-type {
+ font-size: $font-size-base;
+}
+
+.chr_leave-calendar__day-tooltip__item_request-status {
+ color: $crm-gray-matte;
+ margin-bottom: #{$font-size-base - $chr-leave-calendar-tooltip-font-size};
+}
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/scss/mixins/_gradients.scss b/uk.co.compucorp.civicrm.hrleaveandabsences/scss/mixins/_gradients.scss
index 23e156f3f70..74a24ddf024 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/scss/mixins/_gradients.scss
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/scss/mixins/_gradients.scss
@@ -1,3 +1,3 @@
@mixin striped-background($stripe-color) {
- background: repeating-linear-gradient(45deg, $stripe-color, $stripe-color 5px, transparent 5px, transparent 8px);
+ background: repeating-linear-gradient(45deg, $stripe-color, $stripe-color 5px, transparent 5px, transparent 7px);
}
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/sql/auto_install.sql b/uk.co.compucorp.civicrm.hrleaveandabsences/sql/auto_install.sql
index 22fc0da4816..3356c8d3f13 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/sql/auto_install.sql
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/sql/auto_install.sql
@@ -30,6 +30,7 @@ CREATE TABLE `civicrm_hrleaveandabsences_absence_type` (
`carry_forward_expiration_unit` int unsigned COMMENT 'The unit (months or days) of carry_forward_expiration_duration of this type default expiry',
`is_sick` tinyint DEFAULT 0 COMMENT 'A flag which is used to determine if this Absence Type can be used for a Sickness Request',
`calculation_unit` varchar(512) NOT NULL COMMENT 'One of the values of the Absence type calculation units option group',
+ `hide_label` tinyint DEFAULT 0 COMMENT 'This controls the visibility of the Leave Type label in the calendar and feeds.',
PRIMARY KEY ( `id` ),
UNIQUE INDEX `hrleaveandabsences_absence_type_title`(title)
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/templates/CRM/HRLeaveAndAbsences/Form/AbsenceType.tpl b/uk.co.compucorp.civicrm.hrleaveandabsences/templates/CRM/HRLeaveAndAbsences/Form/AbsenceType.tpl
index d54e4e10f18..426bdb5e1bb 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/templates/CRM/HRLeaveAndAbsences/Form/AbsenceType.tpl
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/templates/CRM/HRLeaveAndAbsences/Form/AbsenceType.tpl
@@ -49,6 +49,10 @@
{$form.is_active.label}
{$form.is_active.html}
+
+
{$form.hide_label.label}
+
{$form.hide_label.html}
+
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/API/Handler/GenericLeaveFieldPermissionsTest.php b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/API/Handler/GenericLeaveFieldPermissionsTest.php
new file mode 100644
index 00000000000..f488acec0e3
--- /dev/null
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/API/Handler/GenericLeaveFieldPermissionsTest.php
@@ -0,0 +1,322 @@
+ 0,
+ 'version' => 3,
+ 'count' => 6,
+ 'values' =>
+ [
+ 0 =>
+ [
+ 'id' => '17',
+ 'type_id' => '1',
+ 'contact_id' => '209',
+ 'status_id' => '6',
+ 'from_date' => '2016-01-30 00:00:00',
+ 'from_date_type' => '1',
+ 'to_date' => '2016-02-01 00:00:00',
+ 'to_date_type' => '1',
+ 'request_type' => 'leave',
+ 'is_deleted' => '0',
+ 'from_date_amount' => '0.00',
+ 'to_date_amount' => '0.00',
+ 'balance_change' => -1,
+ 'dates' =>
+ [
+ 0 =>
+ [
+ 'id' => '17',
+ 'date' => '2016-01-30',
+ ],
+ 1 =>
+ [
+ 'id' => '18',
+ 'date' => '2016-01-31',
+ ],
+ 2 =>
+ [
+ 'id' => '19',
+ 'date' => '2016-02-01',
+ ],
+ ],
+ ],
+ 1 =>
+ [
+ 'id' => '18',
+ 'type_id' => '1',
+ 'contact_id' => '204',
+ 'status_id' => '1',
+ 'from_date' => '2016-02-01 00:00:00',
+ 'from_date_type' => '1',
+ 'to_date' => '2016-02-03 00:00:00',
+ 'to_date_type' => '1',
+ 'request_type' => 'leave',
+ 'is_deleted' => '0',
+ 'from_date_amount' => '0.00',
+ 'to_date_amount' => '0.00',
+ 'balance_change' => -3,
+ 'dates' =>
+ [
+ 0 =>
+ [
+ 'id' => '20',
+ 'date' => '2016-02-01',
+ ],
+ 1 =>
+ [
+ 'id' => '21',
+ 'date' => '2016-02-02',
+ ],
+ 2 =>
+ [
+ 'id' => '22',
+ 'date' => '2016-02-03',
+ ],
+ ],
+ ],
+ 2 =>
+ [
+ 'id' => '24',
+ 'type_id' => '2',
+ 'contact_id' => '204',
+ 'status_id' => '1',
+ 'from_date' => '2016-10-20 00:00:00',
+ 'from_date_type' => '1',
+ 'to_date' => '2016-10-20 23:45:00',
+ 'to_date_type' => '1',
+ 'request_type' => 'leave',
+ 'is_deleted' => '0',
+ 'from_date_amount' => '0.00',
+ 'to_date_amount' => '0.00',
+ 'balance_change' => 1,
+ 'dates' =>
+ [
+ 0 =>
+ [
+ 'id' => '51',
+ 'date' => '2016-10-20',
+ ],
+ ],
+ ],
+ 3 =>
+ [
+ 'id' => '25',
+ 'type_id' => '2',
+ 'contact_id' => '208',
+ 'status_id' => '4',
+ 'from_date' => '2016-12-15 00:00:00',
+ 'from_date_type' => '1',
+ 'to_date' => '2016-12-15 23:45:00',
+ 'to_date_type' => '1',
+ 'request_type' => 'leave',
+ 'is_deleted' => '0',
+ 'from_date_amount' => '0.00',
+ 'to_date_amount' => '0.00',
+ 'balance_change' => 2,
+ 'dates' =>
+ [
+ 0 =>
+ [
+ 'id' => '52',
+ 'date' => '2016-12-15',
+ ],
+ ],
+ ],
+ 4 =>
+ [
+ 'id' => '27',
+ 'type_id' => '3',
+ 'contact_id' => '204',
+ 'status_id' => '6',
+ 'from_date' => '2017-02-01 00:00:00',
+ 'from_date_type' => '1',
+ 'to_date' => '2017-02-01 00:00:00',
+ 'to_date_type' => '1',
+ 'sickness_reason' => '2',
+ 'request_type' => 'sickness',
+ 'is_deleted' => '0',
+ 'from_date_amount' => '0.00',
+ 'to_date_amount' => '0.00',
+ 'balance_change' => -1,
+ 'dates' =>
+ [
+ 0 =>
+ [
+ 'id' => '63',
+ 'date' => '2017-02-01',
+ ],
+ ],
+ ],
+ 5 =>
+ [
+ 'id' => '28',
+ 'type_id' => '3',
+ 'contact_id' => '209',
+ 'status_id' => '6',
+ 'from_date' => '2017-02-01 00:00:00',
+ 'from_date_type' => '1',
+ 'to_date' => '2017-02-01 00:00:00',
+ 'to_date_type' => '1',
+ 'sickness_reason' => '2',
+ 'request_type' => 'sickness',
+ 'is_deleted' => '0',
+ 'from_date_amount' => '0.00',
+ 'to_date_amount' => '0.00',
+ 'balance_change' => -1,
+ 'dates' =>
+ [
+ 0 =>
+ [
+ 'id' => '63',
+ 'date' => '2017-02-01',
+ ],
+ ],
+ ],
+ ],
+ ];
+
+ public function testProcessWhenUserIsNotAnAdminUser() {
+ $absenceType = AbsenceTypeFabricator::fabricate([
+ 'hide_label' => 1
+ ]);
+ $this->setPermissions();
+ //User has access to two leave contacts.
+ $leaveRequestRights = $this->prophesize(LeaveRequestRightsService::class);
+ $leaveRequestRights->getLeaveContactsCurrentUserHasAccessTo()->willReturn([209, 208]);
+ $apiRequest = [];
+ $genericFieldHandler = new GenericLeaveFieldPermissions($apiRequest, $leaveRequestRights->reveal());
+
+ $sampleData = $this->sampleData;
+ $this->setAbsenceTypeID($sampleData, $absenceType->id);
+
+ $results = $sampleData;
+ $expectedParams = $sampleData;
+ $expectedParams['values'][1]['from_date_amount'] = '';
+ $expectedParams['values'][1]['to_date_amount'] = '';
+ $expectedParams['values'][1]['balance_change'] = '';
+ $expectedParams['values'][1]['type_id'] = '';
+ $expectedParams['values'][2]['from_date_amount'] = '';
+ $expectedParams['values'][2]['to_date_amount'] = '';
+ $expectedParams['values'][2]['balance_change'] = '';
+ $expectedParams['values'][2]['type_id'] = '';
+ $expectedParams['values'][4]['sickness_reason'] = '';
+ $expectedParams['values'][4]['from_date_amount'] = '';
+ $expectedParams['values'][4]['to_date_amount'] = '';
+ $expectedParams['values'][4]['balance_change'] = '';
+ $expectedParams['values'][4]['type_id'] = '';
+ $genericFieldHandler->process($results);
+
+ $this->assertEquals($expectedParams, $results);
+ }
+
+
+ public function testProcessWhenUserIsAdmin() {
+ //Admin User
+ $this->setPermissions(['administer leave and absences']);
+ $leaveRequestRights = $this->prophesize(LeaveRequestRightsService::class);
+ $leaveRequestRights->getLeaveContactsCurrentUserHasAccessTo()->willReturn([]);
+ $apiRequest = [];
+ $genericFieldHandler = new GenericLeaveFieldPermissions($apiRequest, $leaveRequestRights->reveal());
+
+ $results = $this->sampleData;
+ $expectedParams = $this->sampleData;
+ $genericFieldHandler->process($results);
+
+ $this->assertEquals($expectedParams, $results);
+ $this->setPermissions();
+ }
+
+ public function testProcessWillHideAccessibleFieldsWhenRowIdentifierIsAbsentEvenIfUserHasAccess() {
+ $absenceType = AbsenceTypeFabricator::fabricate([
+ 'hide_label' => 0
+ ]);
+ //User is Staff with ID of 204 and has full access to only his data
+ $this->setPermissions();
+ $leaveRequestRights = $this->prophesize(LeaveRequestRightsService::class);
+ $leaveRequestRights->getLeaveContactsCurrentUserHasAccessTo()->willReturn([204]);
+ $apiRequest = [];
+ $genericFieldHandler = new GenericLeaveFieldPermissions($apiRequest, $leaveRequestRights->reveal());
+
+ $sampleData = $this->sampleData;
+ $this->setAbsenceTypeID($sampleData, $absenceType->id);
+ unset($sampleData['values'][0], $sampleData['values'][3], $sampleData['values'][5]);
+ unset($sampleData['values'][1]['contact_id']);
+ unset($sampleData['values'][2]['contact_id']);
+ unset($sampleData['values'][4]['contact_id']);
+
+ $results = $sampleData;
+ $expectedParams = $sampleData;
+
+ //Staff will not be able to access all restricted fields for his
+ //records since the row identifier is absent
+ //The type_id field is not expected to be restricted to the user since the
+ //Absence type label is public which makes it accessible to user
+ $expectedParams['values'][1]['from_date_amount'] = '';
+ $expectedParams['values'][1]['to_date_amount'] = '';
+ $expectedParams['values'][1]['balance_change'] = '';
+ $expectedParams['values'][2]['from_date_amount'] = '';
+ $expectedParams['values'][2]['to_date_amount'] = '';
+ $expectedParams['values'][2]['balance_change'] = '';
+ $expectedParams['values'][4]['sickness_reason'] = '';
+ $expectedParams['values'][4]['from_date_amount'] = '';
+ $expectedParams['values'][4]['to_date_amount'] = '';
+ $expectedParams['values'][4]['balance_change'] = '';
+
+ $genericFieldHandler->process($results);
+
+ $this->assertEquals($expectedParams, $results);
+ }
+
+ public function testTypeIDFieldIsNotHiddenWhenAbsenceTypeIsPublicAndUserDoesNotHaveAccessToTheLeaveContact() {
+ $absenceType = AbsenceTypeFabricator::fabricate([
+ 'hide_label' => 0
+ ]);
+ $this->setPermissions();
+ //User has access to two leave contacts.
+ $leaveRequestRights = $this->prophesize(LeaveRequestRightsService::class);
+ $leaveRequestRights->getLeaveContactsCurrentUserHasAccessTo()->willReturn([209, 208]);
+ $apiRequest = [];
+ $genericFieldHandler = new GenericLeaveFieldPermissions($apiRequest, $leaveRequestRights->reveal());
+
+ $sampleData = $this->sampleData;
+ $this->setAbsenceTypeID($sampleData, $absenceType->id);
+
+ $results = $sampleData;
+ $expectedParams = $sampleData;
+
+ // All other restricted fields are hidden for the user except the type ID field
+ // because the Absence type allows the label to be public.
+ $expectedParams['values'][1]['from_date_amount'] = '';
+ $expectedParams['values'][1]['to_date_amount'] = '';
+ $expectedParams['values'][1]['balance_change'] = '';
+ $expectedParams['values'][2]['from_date_amount'] = '';
+ $expectedParams['values'][2]['to_date_amount'] = '';
+ $expectedParams['values'][2]['balance_change'] = '';
+ $expectedParams['values'][4]['sickness_reason'] = '';
+ $expectedParams['values'][4]['from_date_amount'] = '';
+ $expectedParams['values'][4]['to_date_amount'] = '';
+ $expectedParams['values'][4]['balance_change'] = '';
+ $genericFieldHandler->process($results);
+
+ $this->assertEquals($expectedParams, $results);
+ }
+
+ private function setAbsenceTypeID(&$sampleData, $absenceTypeID) {
+ foreach ($sampleData['values'] as &$data) {
+ $data['type_id'] = $absenceTypeID;
+ }
+ }
+}
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/API/Handler/GetBreakDownFieldPermissionsTest.php b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/API/Handler/GetBreakDownFieldPermissionsTest.php
new file mode 100644
index 00000000000..5cf6e407dc1
--- /dev/null
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/API/Handler/GetBreakDownFieldPermissionsTest.php
@@ -0,0 +1,127 @@
+ 0,
+ 'version' => 3,
+ 'count' => 3,
+ 'values' =>
+ [
+ 0 =>
+ [
+ 'id' => '17',
+ 'type' => 1,
+ 'label' => 'Test Label',
+ 'date' => '2016-01-30',
+ 'amount' => '0.00',
+ ],
+ 1 =>
+ [
+ 'id' => '18',
+ 'type' => 1,
+ 'label' => 'Test Label',
+ 'date' => '2016-01-31',
+ 'amount' => '0.00',
+ ],
+ 2 =>
+ [
+ 'id' => '19',
+ 'type' => 1,
+ 'label' => 'Test Label',
+ 'date' => '2016-02-01',
+ 'amount' => '-1.00',
+ ],
+ ],
+ ];
+
+ private function createLeaveRequest($contactID) {
+ return LeaveRequestFabricator::fabricateWithoutValidation([
+ 'contact_id' => $contactID,
+ 'type_id' => 1,
+ 'from_date' => CRM_Utils_Date::processDate('2016-01-30'),
+ 'to_date' => CRM_Utils_Date::processDate('2016-02-01'),
+ 'status_id' => 1
+ ]);
+ }
+
+ public function testProcessForUserRequestingBreakDownForLeaveRequestContactHeHasAccessTo() {
+ //User with contact ID of 204
+ $contactID = 204;
+ $this->setPermissions();
+ $leaveRequestRights = $this->prophesize(LeaveRequestRightsService::class);
+ $leaveRequestRights->getLeaveContactsCurrentUserHasAccessTo()->willReturn([$contactID]);
+ $leaveRequest = $this->createLeaveRequest($contactID);
+ $apiRequest = [
+ 'params' => [
+ 'leave_request_id' => $leaveRequest->id
+ ]
+ ];
+ $getBreakDownFieldHandler = new GetBreakDownFieldHandler($apiRequest, $leaveRequestRights->reveal());
+ $results = $this->sampleData;
+ $expectedParams = $this->sampleData;
+
+ $getBreakDownFieldHandler->process($results);
+ $this->assertEquals($expectedParams, $results);
+ }
+
+ public function testProcessForUserRequestingBreakdownForLeaveRequestContactHeDoesNotHaveAccessTo() {
+ //Staff with contact ID of 204 trying to access breakdown of staff with contact ID 206
+ $contactID = 204;
+ $staffID = 206;
+ $this->setPermissions();
+ $leaveRequestRights = $this->prophesize(LeaveRequestRightsService::class);
+ $leaveRequestRights->getLeaveContactsCurrentUserHasAccessTo()->willReturn([$contactID]);
+ $leaveRequest = $this->createLeaveRequest($staffID);
+ $apiRequest = [
+ 'params' => [
+ 'leave_request_id' => $leaveRequest->id
+ ]
+ ];
+ $getBreakDownFieldHandler = new GetBreakDownFieldHandler($apiRequest, $leaveRequestRights->reveal());
+ $results = $this->sampleData;
+ $expectedParams = $this->sampleData;
+ $expectedParams['values'][0]['amount'] = '';
+ $expectedParams['values'][1]['amount'] = '';
+ $expectedParams['values'][2]['amount'] = '';
+
+ $getBreakDownFieldHandler->process($results);
+ $this->assertEquals($expectedParams, $results);
+ }
+
+ public function testProcessForAdmin() {
+ //Admin can access all restricted fields for a contaxt
+ $this->setPermissions(['administer leave and absences']);
+ $staffID = 206;
+ $leaveRequestRights = $this->prophesize(LeaveRequestRightsService::class);
+ $leaveRequestRights->getLeaveContactsCurrentUserHasAccessTo()->willReturn([]);
+ $leaveRequest = $this->createLeaveRequest($staffID);
+ $apiRequest = [
+ 'params' => [
+ 'leave_request_id' => $leaveRequest->id
+ ]
+ ];
+ $getBreakDownFieldHandler = new GetBreakDownFieldHandler($apiRequest, $leaveRequestRights->reveal());
+ $results = $this->sampleData;
+ $expectedParams = $this->sampleData;
+
+ $getBreakDownFieldHandler->process($results);
+ $this->assertEquals($expectedParams, $results);
+ }
+
+}
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/BAO/LeaveBalanceChangeTest.php b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/BAO/LeaveBalanceChangeTest.php
index 4dffab58a2c..29fb5e3e1a6 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/BAO/LeaveBalanceChangeTest.php
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/BAO/LeaveBalanceChangeTest.php
@@ -992,7 +992,7 @@ public function testTheLeaveRequestBreakdownReturnsOnlyTheLeaveBalanceChangesOfT
// This balance change will not be returned because it's not linked to
// the leave request
LeaveBalanceChangeFabricator::fabricate([
- 'source_id' => 100,
+ 'source_id' => $expectedLeaveBalanceChanges[1]->source_id + 1,
'source_type' => LeaveBalanceChange::SOURCE_LEAVE_REQUEST_DAY,
]);
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/BAO/LeaveRequestTest.php b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/BAO/LeaveRequestTest.php
index cf05d7f40cb..fdcd2be3a13 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/BAO/LeaveRequestTest.php
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/BAO/LeaveRequestTest.php
@@ -3415,8 +3415,8 @@ public function testToilCanBeAccruedWhenTheCurrentBalanceForPeriodEntitlementIsZ
}
public function testToilCanBeAccruedWhenTheToilRequestHasNoWorkingDay() {
- $dateSaturday = CRM_Utils_Date::processDate('2018-05-05');
- $dateSunday = CRM_Utils_Date::processDate('2018-05-06');
+ $dateSaturday = CRM_Utils_Date::processDate('saturday next week');
+ $dateSunday = CRM_Utils_Date::processDate('sunday next week');
$period = AbsencePeriodFabricator::fabricate([
'start_date' => $dateSaturday,
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/BAO/WorkPatternTest.php b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/BAO/WorkPatternTest.php
index 6bd6db72e9a..5382e822a6b 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/BAO/WorkPatternTest.php
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/BAO/WorkPatternTest.php
@@ -692,6 +692,54 @@ public function testCannotDisableTheDefaultWorkPattern() {
WorkPattern::create($params);
}
+ public function testisActiveReturnsTrueWhenWorkPatternIsActive() {
+ $workPattern = WorkPatternFabricator::fabricate(['is_active' => 1]);
+
+ $this->assertTrue($workPattern->isActive());
+ }
+
+ public function testisActiveReturnsFalseWhenWorkPatternIsNotActive() {
+ $workPattern = WorkPatternFabricator::fabricate(['is_active' => 0]);
+
+ $this->assertFalse($workPattern->isActive());
+ }
+
+ public function testisDefaultReturnsTrueWhenWorkPatternIsTheDefault() {
+ $workPattern = WorkPatternFabricator::fabricate(['is_default' => 1]);
+
+ $this->assertTrue($workPattern->isDefault());
+ }
+
+ public function testisDefaultReturnsFalseWhenWorkPatternIsNotTheDefault() {
+ $workPattern = WorkPatternFabricator::fabricate(['is_default' => 0]);
+
+ $this->assertFalse($workPattern->isDefault());
+ }
+
+ public function testCannotMakeTheDefaultWorkPatternNonDefault() {
+ $workPattern = WorkPatternFabricator::fabricate(['is_default' => 1]);
+
+ $this->setExpectedException(
+ CRM_HRLeaveAndAbsences_Exception_InvalidWorkPatternException::class,
+ 'It is not possible to have no default Work Pattern'
+ );
+
+ $params = ['id' => $workPattern->id, 'is_default' => 0];
+ WorkPattern::create($params);
+ }
+
+ public function testCannotMakeADisabledWorkPatternAsDefault() {
+ $workPattern = WorkPatternFabricator::fabricate(['is_active' => 0]);
+
+ $this->setExpectedException(
+ CRM_HRLeaveAndAbsences_Exception_InvalidWorkPatternException::class,
+ 'You cannot set a disabled work pattern as the default'
+ );
+
+ $params = ['id' => $workPattern->id, 'is_default' => 1];
+ WorkPattern::create($params);
+ }
+
public function testWorkPatternLabelsShouldBeUnique() {
WorkPatternFabricator::fabricate(['label' => 'WorkPattern 1']);
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/Service/LeaveRequestRightsTest.php b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/Service/LeaveRequestRightsTest.php
index a31f39d90ed..224527491f4 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/Service/LeaveRequestRightsTest.php
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/CRM/HRLeaveAndAbsences/Service/LeaveRequestRightsTest.php
@@ -3,6 +3,7 @@
use CRM_HRLeaveAndAbsences_Service_LeaveRequestRights as LeaveRequestRightsService;
use CRM_HRLeaveAndAbsences_BAO_LeaveRequest as LeaveRequest;
use CRM_HRLeaveAndAbsences_Test_Fabricator_AbsenceType as AbsenceTypeFabricator;
+use CRM_HRCore_Test_Fabricator_Contact as ContactFabricator;
/**
* Class CRM_HRLeaveAndAbsences_Service_LeaveRequestRightsTest
@@ -281,6 +282,38 @@ public function testCanCancelToilWithPastDatesReturnsTrueWhenAbsenceTypeAllowsPa
$this->assertTrue($leaveRightsService->canCancelToilWithPastDates($this->leaveContact, $absenceType->id));
}
+ public function testStaffMembersShouldOnlyHaveAccessToThemselves() {
+ $staffMember1 = ContactFabricator::fabricate();
+ $staffMember2 = ContactFabricator::fabricate();
+ $this->registerCurrentLoggedInContactInSession($staffMember1['id']);
+ $leaveRequestRightsService = $this->getLeaveRightsService();
+ $accessibleContacts = $leaveRequestRightsService->getLeaveContactsCurrentUserHasAccessTo();
+ $this->assertEquals([$staffMember1['id']], $accessibleContacts);
+ }
+
+ public function testGetLeaveApproverShouldOnlyHaveAccessToManagees() {
+ $manager = ContactFabricator::fabricate();
+ $staffMember1 = ContactFabricator::fabricate();
+ $staffMember2 = ContactFabricator::fabricate();
+ $this->registerCurrentLoggedInContactInSession($manager['id']);
+ $this->setContactAsLeaveApproverOf($manager, $staffMember2);
+ $leaveRequestRightsService = $this->getLeaveRightsService();
+ $accessibleContacts = $leaveRequestRightsService->getLeaveContactsCurrentUserHasAccessTo();
+ sort($accessibleContacts);
+ //The leave approver has access to his own contact id and that of his managees.
+ $this->assertEquals([$manager['id'],$staffMember2['id']], $accessibleContacts);
+ }
+
+ public function testAdminShouldHaveAccessToAllContacts() {
+ $staffMember1 = ContactFabricator::fabricate();
+ $staffMember2 = ContactFabricator::fabricate();
+ $leaveRequestRightsService = $this->getLeaveRequestRightsForAdminAsCurrentUser();
+ $accessibleContacts = $leaveRequestRightsService->getLeaveContactsCurrentUserHasAccessTo();
+ //In reality, An admin user has access to all contacts, but an empty array is returned in
+ //this case.
+ $this->assertEquals([], $accessibleContacts);
+ }
+
private function getLeaveRightsService($isAdmin = FALSE, $isManager = FALSE) {
$leaveManagerService = $this->createLeaveManagerServiceMock($isAdmin, $isManager);
return new LeaveRequestRightsService($leaveManagerService);
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/api/v3/LeaveRequestTest.php b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/api/v3/LeaveRequestTest.php
index 828de26388d..02d94b06898 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/api/v3/LeaveRequestTest.php
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/tests/phpunit/api/v3/LeaveRequestTest.php
@@ -921,11 +921,11 @@ public function testGetFullShouldNotIncludeTheBalanceChangeIfTheReturnOptionIsNo
$expectedValues = [
[
'id' => $leaveRequest1->id,
- 'dates' => $this->createLeaveRequestDatesArray($leaveRequest1)
+ 'dates' => $this->createLeaveRequestDatesArray($leaveRequest1),
],
[
'id' => $leaveRequest2->id,
- 'dates' => $this->createLeaveRequestDatesArray($leaveRequest2)
+ 'dates' => $this->createLeaveRequestDatesArray($leaveRequest2),
]
];
@@ -971,11 +971,11 @@ public function testGetFullShouldNotIncludeTheDatesIfTheReturnOptionIsNotEmptyAn
$expectedValues = [
[
'id' => $leaveRequest1->id,
- 'balance_change' => -1
+ 'balance_change' => -1,
],
[
'id' => $leaveRequest2->id,
- 'balance_change' => -1
+ 'balance_change' => -1,
]
];
@@ -1036,17 +1036,17 @@ public function testGetFullIncludesBalanceChangesAndDatesForToilLeaveRequests()
[
'id' => $leaveRequest1->id,
'balance_change' => -1,
- 'dates' => $this->createLeaveRequestDatesArray($leaveRequest1)
+ 'dates' => $this->createLeaveRequestDatesArray($leaveRequest1),
],
[
'id' => $leaveRequest2->id,
'balance_change' => -1,
- 'dates' => $this->createLeaveRequestDatesArray($leaveRequest2)
+ 'dates' => $this->createLeaveRequestDatesArray($leaveRequest2),
],
[
'id' => $toilRequest->id,
'balance_change' => 8,
- 'dates' => $this->createLeaveRequestDatesArray($toilRequest)
+ 'dates' => $this->createLeaveRequestDatesArray($toilRequest),
]
];
@@ -1092,11 +1092,11 @@ public function testGetFullShouldNotIncludeTheBalanceChangeAndDatesIfTheReturnOp
$expectedValues = [
[
'id' => $leaveRequest1->id,
- 'type_id' => $this->absenceType->id
+ 'type_id' => $this->absenceType->id,
],
[
'id' => $leaveRequest2->id,
- 'type_id' => $this->absenceType->id
+ 'type_id' => $this->absenceType->id,
]
];
@@ -1174,7 +1174,7 @@ public function testGetAndGetFullDoesNotReturnSoftDeletedLeaveRequests() {
$this->assertNotEmpty($resultGetFull['values'][$leaveRequest3->id]);
}
- public function testGetAndGetFullReturnAllLeaveRequestsWhenTheExpiredParamIsNotPresent() {
+ public function testGetAndGetFullReturnAllNonExpiredLeaveRequestsWhenTheExpiredParamFalse() {
$type = AbsenceTypeFabricator::fabricate([
'allow_accruals_request' => TRUE,
'max_leave_accrual' => 10
@@ -1185,8 +1185,8 @@ public function testGetAndGetFullReturnAllLeaveRequestsWhenTheExpiredParamIsNotP
['period_start_date' => '2016-01-01']
);
- // This request has 3 days expired, but will be included on
- // the response anyway, since the "expired" flag is not set
+ // This request has 3 days expired, but will not be included on
+ // the response since the "expired" flag is FALSE.
$toilRequest1 = LeaveRequestFabricator::fabricateWithoutValidation([
'contact_id' => $contract['contact_id'],
'type_id' => $type->id,
@@ -1219,16 +1219,63 @@ public function testGetAndGetFullReturnAllLeaveRequestsWhenTheExpiredParamIsNotP
'request_type' => LeaveRequest::REQUEST_TYPE_LEAVE
], TRUE);
- $resultGet = civicrm_api3('LeaveRequest', 'get');
- $resultGetFull = civicrm_api3('LeaveRequest', 'getFull');
+ $resultGet = civicrm_api3('LeaveRequest', 'get', ['expired' => FALSE]);
+ $resultGetFull = civicrm_api3('LeaveRequest', 'getFull', ['expired' => FALSE]);
- $this->assertEquals(3, $resultGet['count']);
- $this->assertEquals(3, $resultGetFull['count']);
- $this->assertNotEmpty($resultGet['values'][$toilRequest1->id]);
+ $this->assertEquals(2, $resultGet['count']);
+ $this->assertEquals(2, $resultGetFull['count']);
$this->assertNotEmpty($resultGet['values'][$toilRequest2->id]);
$this->assertNotEmpty($resultGet['values'][$leaveRequest->id]);
}
+ public function testGetAndGetFullWillIncludeLeaveRequestWithExpiredBalanceOfZeroWhenTheExpiredParamIsFalse() {
+ $type = AbsenceTypeFabricator::fabricate([
+ 'allow_accruals_request' => TRUE,
+ 'max_leave_accrual' => 10
+ ]);
+
+ $contract = HRJobContractFabricator::fabricate(
+ ['contact_id' => 1],
+ ['period_start_date' => '2016-01-01']
+ );
+
+ // This request has 3 days expired, but will not be included on
+ // the response since the "expired" flag is FALSE
+ $toilRequest1 = LeaveRequestFabricator::fabricateWithoutValidation([
+ 'contact_id' => $contract['contact_id'],
+ 'type_id' => $type->id,
+ 'from_date' => CRM_Utils_Date::processDate('2016-01-01'),
+ 'to_date' => CRM_Utils_Date::processDate('2016-01-01'),
+ 'toil_duration' => 10,
+ 'toil_expiry_date' => CRM_Utils_Date::processDate('2016-01-10'),
+ 'toil_to_accrue' => 5,
+ 'request_type' => LeaveRequest::REQUEST_TYPE_TOIL
+ ], TRUE);
+ //expire balance change but with an amount of zero which means the TOIl was used up before
+ //the expiry date.
+ $this->createExpiryBalanceChangeForTOILRequest($toilRequest1->id, 0);
+
+ // this one is not expired yet
+ $toilRequest2 = LeaveRequestFabricator::fabricateWithoutValidation([
+ 'contact_id' => $contract['contact_id'],
+ 'type_id' => $type->id,
+ 'from_date' => CRM_Utils_Date::processDate('tomorrow'),
+ 'to_date' => CRM_Utils_Date::processDate('tomorrow'),
+ 'toil_duration' => 10,
+ 'toil_expiry_date' => CRM_Utils_Date::processDate('+30 days'),
+ 'toil_to_accrue' => 1,
+ 'request_type' => LeaveRequest::REQUEST_TYPE_TOIL
+ ], TRUE);
+
+ $resultGet = civicrm_api3('LeaveRequest', 'get', ['expired' => FALSE]);
+ $resultGetFull = civicrm_api3('LeaveRequest', 'getFull', ['expired' => FALSE]);
+
+ $this->assertEquals(2, $resultGet['count']);
+ $this->assertEquals(2, $resultGetFull['count']);
+ $this->assertNotEmpty($resultGet['values'][$toilRequest1->id]);
+ $this->assertNotEmpty($resultGet['values'][$toilRequest2->id]);
+ }
+
public function testGetAndGetFullReturnOnlyLeaveRequestsWithExpiredBalanceChangesWhenTheExpiredParamIsPresent() {
$type = AbsenceTypeFabricator::fabricate([
'allow_accruals_request' => TRUE,
@@ -1679,7 +1726,7 @@ public function testGetAndGetFullShouldReturnInformationForContactsWithActiveLea
$this->assertNotEmpty($resultGetFull['values'][$leaveRequest1->id]);
}
- public function testGetAndGetFullShouldReturnEmptyResponseForALoggedInLeaveManagerWhenUnassignedIsTrue() {
+ public function testGetAndGetFullHidesRestrictedFieldsForUnAssignedContactForLoggedInLeaveManagerWhenUnassignedIsTrue() {
$manager1 = ContactFabricator::fabricate();
$this->registerCurrentLoggedInContactInSession($manager1['id']);
CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access AJAX API'];
@@ -1702,6 +1749,11 @@ public function testGetAndGetFullShouldReturnEmptyResponseForALoggedInLeaveManag
['period_start_date' => '2015-10-01']
);
+ //The type ID field will be visible if the Absence type label is not hidden.
+ $absenceType = AbsenceTypeFabricator::fabricate([
+ 'hide_label' => 1
+ ]);
+
// Set Leave Approvers for staffMembers 1 and 2.
// staffMember2 does not have an active leave manager relationship
$this->setContactAsLeaveApproverOf($manager1, $staffMember1, null, null, true, 'has Leaves Approved By');
@@ -1709,7 +1761,7 @@ public function testGetAndGetFullShouldReturnEmptyResponseForALoggedInLeaveManag
$leaveRequest1 = LeaveRequestFabricator::fabricateWithoutValidation([
'contact_id' => $staffMember1['id'],
- 'type_id' => $this->absenceType->id,
+ 'type_id' => $absenceType->id,
'from_date' => CRM_Utils_Date::processDate('2016-01-01'),
'to_date' => CRM_Utils_Date::processDate('2016-01-01'),
'from_date_type' => 1,
@@ -1718,23 +1770,29 @@ public function testGetAndGetFullShouldReturnEmptyResponseForALoggedInLeaveManag
$leaveRequest2 = LeaveRequestFabricator::fabricateWithoutValidation([
'contact_id' => $staffMember2['id'],
- 'type_id' => $this->absenceType->id,
+ 'type_id' => $absenceType->id,
'from_date' => CRM_Utils_Date::processDate('2016-01-05'),
'to_date' => CRM_Utils_Date::processDate('2016-01-05'),
'from_date_type' => 1,
- 'to_date_type' => 1
+ 'to_date_type' => 1,
], true);
-
- // No results will be returned because the unassigned parameter has a true value
- // and a manager can only see contacts assigned to him that he manages, the unassigned parameter negates that.
+ // The manager will see results for the contact with the inactive leave manager relationship.
// We need to set check permissions to true here so that civi can add
// the appropriate ACL clause to the LeaveRequest queries
$result = civicrm_api3('LeaveRequest', 'get', ['unassigned' => true, 'check_permissions' => true]);
$resultGetFull = civicrm_api3('LeaveRequest', 'getFull', ['unassigned' => true, 'check_permissions' => true]);
- $this->assertEquals(0, $result['count']);
- $this->assertEquals(0, $resultGetFull['count']);
+ $this->assertEquals(1, $result['count']);
+ $contactData = array_shift($result['values']);
+ $this->assertEquals($staffMember2['id'], $contactData['contact_id']);
+ $this->assertEquals('', $contactData['type_id']);
+
+ $this->assertEquals(1, $resultGetFull['count']);
+ $contactData = array_shift($resultGetFull['values']);
+ $this->assertEquals($staffMember2['id'], $contactData['contact_id']);
+ $this->assertEquals('', $contactData['type_id']);
+ $this->assertEquals('', $contactData['balance_change']);
}
public function testGetAndGetFullShouldReturnResultsForContactsManagedByLoggedInLeaveManagerWhenUnassignedIsFalse() {
@@ -3590,7 +3648,7 @@ public function testDeleteCommentShouldThrowAnExceptionIfCommentIDIsMissing() {
civicrm_api3('LeaveRequest', 'deletecomment', []);
}
- public function testGetAndGetFullReturnsOnlyDataLinkedToLoggedInUserWhenUserIsNotALeaveApproverOrAdmin() {
+ public function testGetAndGetFullShouldHideRestrictedFieldValuesForContactsOtherThanLoggedInUserWhenUserIsNotALeaveApproverOrAdmin() {
$contact1 = ContactFabricator::fabricate();
$contact2 = ContactFabricator::fabricate();
@@ -3613,36 +3671,123 @@ public function testGetAndGetFullReturnsOnlyDataLinkedToLoggedInUserWhenUserIsNo
]
);
- LeaveRequestFabricator::fabricateWithoutValidation([
+ //The type ID field will be visible if the Absence type label is not hidden.
+ $absenceType = AbsenceTypeFabricator::fabricate([
+ 'hide_label' => 1
+ ]);
+
+ $leaveRequest1 = LeaveRequestFabricator::fabricateWithoutValidation([
'contact_id' => $contact1['id'],
- 'type_id' => $this->absenceType->id,
+ 'type_id' => $absenceType->id,
'from_date' => CRM_Utils_Date::processDate('2016-03-02'),
'to_date' => CRM_Utils_Date::processDate('2016-03-02'),
'from_date_type' => 1,
'to_date_type' => 1,
- 'status_id' => 1
+ 'status_id' => 1,
], true);
- LeaveRequestFabricator::fabricateWithoutValidation([
+ $leaveRequest2 = LeaveRequestFabricator::fabricateWithoutValidation([
'contact_id' => $contact2['id'],
- 'type_id' => $this->absenceType->id,
+ 'type_id' => $absenceType->id,
'from_date' => CRM_Utils_Date::processDate('2016-02-20'),
'to_date' => CRM_Utils_Date::processDate('2016-02-23'),
'from_date_type' => 1,
'to_date_type' => 1,
- 'status_id' => 1
+ 'status_id' => 1,
], true);
+ //The logged in contact would be able to see results for the other contact too since the Leave ACL
+ //allows it but would not be able to view field values for restricted fields.
$result = civicrm_api3('LeaveRequest', 'get', ['check_permissions' => true, 'sequential' => 1]);
- $this->assertEquals(1, $result['count']);
+ $this->assertEquals(2, $result['count']);
$this->assertEquals($contact1['id'], $result['values'][0]['contact_id']);
+ $this->assertEquals($leaveRequest1->type_id, $result['values'][0]['type_id']);
+
+ $this->assertEquals($contact2['id'], $result['values'][1]['contact_id']);
+ $this->assertEquals('', $result['values'][1]['type_id']);
$result = civicrm_api3('LeaveRequest', 'getfull', ['check_permissions' => true, 'sequential' => 1]);
- $this->assertEquals(1, $result['count']);
+ $this->assertEquals(2, $result['count']);
$this->assertEquals($contact1['id'], $result['values'][0]['contact_id']);
+ $this->assertEquals($leaveRequest1->type_id, $result['values'][0]['type_id']);
+ $this->assertNotEmpty($result['values'][0]['balance_change']);
+
+ $this->assertEquals($contact2['id'], $result['values'][1]['contact_id']);
+ $this->assertEquals('', $result['values'][1]['type_id']);
+ $this->assertEquals('', $result['values'][1]['balance_change']);
}
- public function testGetAndGetFullReturnsOnlyDataLinkedToContactsThatLoggedInUserManagesWhenLoggedInUserIsALeaveApprover() {
+ public function testGetAndGetFullShouldNotHideTypeIDFieldValueForContactsOtherThanLoggedInUserWhenUserIsStaffAndAbsenceTypeLabelIsPublic() {
+ $contact1 = ContactFabricator::fabricate();
+ $contact2 = ContactFabricator::fabricate();
+
+ $this->registerCurrentLoggedInContactInSession($contact1['id']);
+ CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access AJAX API'];
+
+ HRJobContractFabricator::fabricate(
+ [ 'contact_id' => $contact1['id'] ],
+ [
+ 'period_start_date' => '2016-01-01',
+ 'period_end_date' => '2016-10-01'
+ ]
+ );
+
+ HRJobContractFabricator::fabricate(
+ [ 'contact_id' => $contact2['id'] ],
+ [
+ 'period_start_date' => '2016-01-01',
+ 'period_end_date' => '2016-10-01'
+ ]
+ );
+
+ //The type ID field will be visible since the Absence type label is set as public
+ $absenceType = AbsenceTypeFabricator::fabricate([
+ 'hide_label' => 0
+ ]);
+
+ $leaveRequest1 = LeaveRequestFabricator::fabricateWithoutValidation([
+ 'contact_id' => $contact1['id'],
+ 'type_id' => $absenceType->id,
+ 'from_date' => CRM_Utils_Date::processDate('2016-03-02'),
+ 'to_date' => CRM_Utils_Date::processDate('2016-03-02'),
+ 'from_date_type' => 1,
+ 'to_date_type' => 1,
+ 'status_id' => 1,
+ ], true);
+
+ $leaveRequest2 = LeaveRequestFabricator::fabricateWithoutValidation([
+ 'contact_id' => $contact2['id'],
+ 'type_id' => $absenceType->id,
+ 'from_date' => CRM_Utils_Date::processDate('2016-02-20'),
+ 'to_date' => CRM_Utils_Date::processDate('2016-02-23'),
+ 'from_date_type' => 1,
+ 'to_date_type' => 1,
+ 'status_id' => 1,
+ ], true);
+
+ //The logged in contact would be able to see results for the other contact too since the Leave ACL
+ //allows it and would not be able to view field values for restricted fields but would view the
+ //type_id field for other users since the Absence type label is public
+ $result = civicrm_api3('LeaveRequest', 'get', ['check_permissions' => true, 'sequential' => 1]);
+ $this->assertEquals(2, $result['count']);
+ $this->assertEquals($contact1['id'], $result['values'][0]['contact_id']);
+ $this->assertEquals($leaveRequest1->type_id, $result['values'][0]['type_id']);
+
+ $this->assertEquals($contact2['id'], $result['values'][1]['contact_id']);
+ $this->assertEquals($leaveRequest2->type_id, $result['values'][1]['type_id']);
+
+ $result = civicrm_api3('LeaveRequest', 'getfull', ['check_permissions' => true, 'sequential' => 1]);
+ $this->assertEquals(2, $result['count']);
+ $this->assertEquals($contact1['id'], $result['values'][0]['contact_id']);
+ $this->assertEquals($leaveRequest1->type_id, $result['values'][0]['type_id']);
+ $this->assertNotEmpty($result['values'][0]['balance_change']);
+
+ $this->assertEquals($contact2['id'], $result['values'][1]['contact_id']);
+ $this->assertEquals($leaveRequest2->type_id, $result['values'][1]['type_id']);
+ $this->assertEquals('', $result['values'][1]['balance_change']);
+ }
+
+ public function testGetAndGetFullHidesRestrictedFieldValuesForNonManageesWhenLoggedInUserIsALeaveApprover() {
$manager = ContactFabricator::fabricate();
$contact1 = ContactFabricator::fabricate();
$contact2 = ContactFabricator::fabricate();
@@ -3668,50 +3813,64 @@ public function testGetAndGetFullReturnsOnlyDataLinkedToContactsThatLoggedInUser
]
);
- LeaveRequestFabricator::fabricateWithoutValidation([
+ //The type ID field will be visible if the Absence type label is not hidden.
+ $absenceType = AbsenceTypeFabricator::fabricate([
+ 'hide_label' => 1
+ ]);
+
+ $leaveRequest1 = LeaveRequestFabricator::fabricateWithoutValidation([
'contact_id' => $contact1['id'],
- 'type_id' => $this->absenceType->id,
+ 'type_id' => $absenceType->id,
'from_date' => CRM_Utils_Date::processDate('2016-03-02'),
'to_date' => CRM_Utils_Date::processDate('2016-03-02'),
'from_date_type' => 1,
'to_date_type' => 1,
- 'status_id' => 1
+ 'status_id' => 1,
], true);
- LeaveRequestFabricator::fabricateWithoutValidation([
+ $leaveRequest2 = LeaveRequestFabricator::fabricateWithoutValidation([
'contact_id' => $contact2['id'],
- 'type_id' => $this->absenceType->id,
+ 'type_id' => $absenceType->id,
'from_date' => CRM_Utils_Date::processDate('2016-02-20'),
'to_date' => CRM_Utils_Date::processDate('2016-02-23'),
'from_date_type' => 1,
'to_date_type' => 1,
- 'status_id' => 1
+ 'status_id' => 1,
], true);
+ //Results will be returned for both leave contacts even though contact1 is not being managed by
+ //the logged in manager but manager will not be able to view restricted field values for the contact
+ //which he's not a leave approver for.
$result = civicrm_api3('LeaveRequest', 'get', ['check_permissions' => true, 'sequential' => 1]);
- $this->assertEquals(1, $result['count']);
- $this->assertEquals($contact2['id'], $result['values'][0]['contact_id']);
+ $this->assertEquals(2, $result['count']);
+
+ $this->assertEquals($contact1['id'], $result['values'][0]['contact_id']);
+ $this->assertEquals('', $result['values'][0]['type_id']);
+
+ $this->assertEquals($contact2['id'], $result['values'][1]['contact_id']);
+ $this->assertEquals($leaveRequest2->type_id, $result['values'][1]['type_id']);
$result = civicrm_api3('LeaveRequest', 'getfull', ['check_permissions' => true, 'sequential' => 1]);
- $this->assertEquals(1, $result['count']);
- $this->assertEquals($contact2['id'], $result['values'][0]['contact_id']);
- }
+ $this->assertEquals(2, $result['count']);
- public function testGetAndGetFullReturnsOnlyDataLinkedToContactsThatLoggedInUserManagesWhenLoggedInUserIsALeaveApproverWithOneOfTheAvailableRelationships() {
- $this->setLeaveApproverRelationshipTypes([
- 'has leaves approved by',
- 'has things managed by',
- ]);
+ $this->assertEquals($contact1['id'], $result['values'][0]['contact_id']);
+ $this->assertEquals('', $result['values'][0]['type_id']);
+ $this->assertEquals('', $result['values'][0]['balance_change']);
- $manager1 = ContactFabricator::fabricate();
- $manager2 = ContactFabricator::fabricate();
+ $this->assertEquals($contact2['id'], $result['values'][1]['contact_id']);
+ $this->assertEquals($leaveRequest2->type_id, $result['values'][1]['type_id']);
+ $this->assertNotEmpty($result['values'][1]['balance_change']);
+ }
+
+ public function testGetAndGetFullShouldNotHideTypeIDFieldValueForNonManageesWhenLoggedInUserIsALeaveApproverAndAbsenceTypeLabelIsPublic() {
+ $manager = ContactFabricator::fabricate();
$contact1 = ContactFabricator::fabricate();
$contact2 = ContactFabricator::fabricate();
- $contact3 = ContactFabricator::fabricate();
- $this->setContactAsLeaveApproverOf($manager1, $contact2, null, null, true, 'has things managed by');
- $this->setContactAsLeaveApproverOf($manager2, $contact1, null, null, true, 'has leaves approved by');
- $this->setContactAsLeaveApproverOf($manager2, $contact3, null, null, true, 'has leaves managed by');
+ $this->registerCurrentLoggedInContactInSession($manager['id']);
+ CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access AJAX API'];
+
+ $this->setContactAsLeaveApproverOf($manager, $contact2);
HRJobContractFabricator::fabricate(
[ 'contact_id' => $contact2['id'] ],
@@ -3729,49 +3888,162 @@ public function testGetAndGetFullReturnsOnlyDataLinkedToContactsThatLoggedInUser
]
);
+ //The type ID field will be visible if the Absence type label is not hidden.
+ $absenceType = AbsenceTypeFabricator::fabricate([
+ 'hide_label' => 0
+ ]);
+
+ $leaveRequest1 = LeaveRequestFabricator::fabricateWithoutValidation([
+ 'contact_id' => $contact1['id'],
+ 'type_id' => $absenceType->id,
+ 'from_date' => CRM_Utils_Date::processDate('2016-03-02'),
+ 'to_date' => CRM_Utils_Date::processDate('2016-03-02'),
+ 'from_date_type' => 1,
+ 'to_date_type' => 1,
+ 'status_id' => 1,
+ ], true);
+
+ $leaveRequest2 = LeaveRequestFabricator::fabricateWithoutValidation([
+ 'contact_id' => $contact2['id'],
+ 'type_id' => $absenceType->id,
+ 'from_date' => CRM_Utils_Date::processDate('2016-02-20'),
+ 'to_date' => CRM_Utils_Date::processDate('2016-02-23'),
+ 'from_date_type' => 1,
+ 'to_date_type' => 1,
+ 'status_id' => 1,
+ ], true);
+
+ //Results will be returned for both leave contacts even though contact1 is not being managed by
+ //the logged in manager but manager will not be able to view restricted field values for the contact
+ //which he's not a leave approver for but would view the type_id field for other non managees since the
+ // Absence type label is public
+ $result = civicrm_api3('LeaveRequest', 'get', ['check_permissions' => true, 'sequential' => 1]);
+ $this->assertEquals(2, $result['count']);
+
+ $this->assertEquals($contact1['id'], $result['values'][0]['contact_id']);
+ $this->assertEquals($leaveRequest1->type_id, $result['values'][0]['type_id']);
+
+ $this->assertEquals($contact2['id'], $result['values'][1]['contact_id']);
+ $this->assertEquals($leaveRequest2->type_id, $result['values'][1]['type_id']);
+
+ $result = civicrm_api3('LeaveRequest', 'getfull', ['check_permissions' => true, 'sequential' => 1]);
+ $this->assertEquals(2, $result['count']);
+
+ $this->assertEquals($contact1['id'], $result['values'][0]['contact_id']);
+ $this->assertEquals($leaveRequest1->type_id, $result['values'][0]['type_id']);
+ $this->assertEquals('', $result['values'][0]['balance_change']);
+
+ $this->assertEquals($contact2['id'], $result['values'][1]['contact_id']);
+ $this->assertEquals($leaveRequest2->type_id, $result['values'][1]['type_id']);
+ $this->assertNotEmpty($result['values'][1]['balance_change']);
+ }
+
+ public function testGetAndGetFullShouldNotReturnToilRequestsForContactsOtherThanLoggedInUserWhenUserIsNotALeaveApproverOrAdmin() {
+ $contact1 = ContactFabricator::fabricate();
+ $contact2 = ContactFabricator::fabricate();
+
+ $this->registerCurrentLoggedInContactInSession($contact1['id']);
+ CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access AJAX API'];
+
HRJobContractFabricator::fabricate(
- [ 'contact_id' => $contact3['id'] ],
+ [ 'contact_id' => $contact1['id'] ],
[
'period_start_date' => '2016-01-01',
'period_end_date' => '2016-10-01'
]
);
- $leaveRequestContact1 = LeaveRequestFabricator::fabricateWithoutValidation([
+ HRJobContractFabricator::fabricate(
+ [ 'contact_id' => $contact2['id'] ],
+ [
+ 'period_start_date' => '2016-01-01',
+ 'period_end_date' => '2016-10-01'
+ ]
+ );
+
+ $leaveRequest1 = LeaveRequestFabricator::fabricateWithoutValidation([
'contact_id' => $contact1['id'],
'type_id' => $this->absenceType->id,
'from_date' => CRM_Utils_Date::processDate('2016-03-02'),
'to_date' => CRM_Utils_Date::processDate('2016-03-02'),
'from_date_type' => 1,
'to_date_type' => 1,
- 'status_id' => 1
- ], true);
+ 'status_id' => 1,
+ 'request_type' => LeaveRequest::REQUEST_TYPE_TOIL
+ ]);
- LeaveRequestFabricator::fabricateWithoutValidation([
+ $leaveRequest2 = LeaveRequestFabricator::fabricateWithoutValidation([
'contact_id' => $contact2['id'],
'type_id' => $this->absenceType->id,
'from_date' => CRM_Utils_Date::processDate('2016-02-20'),
'to_date' => CRM_Utils_Date::processDate('2016-02-23'),
'from_date_type' => 1,
'to_date_type' => 1,
- 'status_id' => 1
- ], true);
+ 'status_id' => 1,
+ 'request_type' => LeaveRequest::REQUEST_TYPE_TOIL
+ ]);
+
+ //The logged in contact would be not be able to view the other contact's TOIL request
+ //He can only view his own TOIL request
+ $result = civicrm_api3('LeaveRequest', 'get', ['check_permissions' => true, 'sequential' => 1]);
+ $this->assertEquals(1, $result['count']);
+ $this->assertEquals($contact1['id'], $result['values'][0]['contact_id']);
+
+ $result = civicrm_api3('LeaveRequest', 'getfull', ['check_permissions' => true, 'sequential' => 1]);
+ $this->assertEquals(1, $result['count']);
+ $this->assertEquals($contact1['id'], $result['values'][0]['contact_id']);
+ }
+
+ public function testGetAndGetFullShouldNotReturnToilRequestsForNonManageesWhenLoggedInUserIsALeaveApprover() {
+ $manager = ContactFabricator::fabricate();
+ $contact1 = ContactFabricator::fabricate();
+ $contact2 = ContactFabricator::fabricate();
+
+ $this->registerCurrentLoggedInContactInSession($manager['id']);
+ CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access AJAX API'];
+
+ $this->setContactAsLeaveApproverOf($manager, $contact2);
+
+ HRJobContractFabricator::fabricate(
+ [ 'contact_id' => $contact2['id'] ],
+ [
+ 'period_start_date' => '2016-01-01',
+ 'period_end_date' => '2016-10-01'
+ ]
+ );
+
+ HRJobContractFabricator::fabricate(
+ [ 'contact_id' => $contact1['id'] ],
+ [
+ 'period_start_date' => '2016-01-01',
+ 'period_end_date' => '2016-10-01'
+ ]
+ );
- $leaveRequestContact3 = LeaveRequestFabricator::fabricateWithoutValidation([
- 'contact_id' => $contact3['id'],
+ $leaveRequest1 = LeaveRequestFabricator::fabricateWithoutValidation([
+ 'contact_id' => $contact1['id'],
'type_id' => $this->absenceType->id,
- 'from_date' => CRM_Utils_Date::processDate('2016-02-20'),
- 'to_date' => CRM_Utils_Date::processDate('2016-02-20'),
+ 'from_date' => CRM_Utils_Date::processDate('2016-03-02'),
+ 'to_date' => CRM_Utils_Date::processDate('2016-03-02'),
'from_date_type' => 1,
'to_date_type' => 1,
- 'status_id' => 1
- ], true);
+ 'status_id' => 1,
+ 'request_type' => LeaveRequest::REQUEST_TYPE_TOIL
+ ]);
- CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access AJAX API'];
+ $leaveRequest2 = LeaveRequestFabricator::fabricateWithoutValidation([
+ 'contact_id' => $contact2['id'],
+ 'type_id' => $this->absenceType->id,
+ 'from_date' => CRM_Utils_Date::processDate('2016-02-20'),
+ 'to_date' => CRM_Utils_Date::processDate('2016-02-23'),
+ 'from_date_type' => 1,
+ 'to_date_type' => 1,
+ 'status_id' => 1,
+ 'request_type' => LeaveRequest::REQUEST_TYPE_TOIL
+ ]);
- // Manager1 only manages contact2 (though the 'has things managed by' relationship),
- // so only contact2 leave requests will be returned
- $this->registerCurrentLoggedInContactInSession($manager1['id']);
+ //Results will be not be returned for contact1 since contact one is not a managee of the leave
+ //approver
$result = civicrm_api3('LeaveRequest', 'get', ['check_permissions' => true, 'sequential' => 1]);
$this->assertEquals(1, $result['count']);
$this->assertEquals($contact2['id'], $result['values'][0]['contact_id']);
@@ -3779,21 +4051,63 @@ public function testGetAndGetFullReturnsOnlyDataLinkedToContactsThatLoggedInUser
$result = civicrm_api3('LeaveRequest', 'getfull', ['check_permissions' => true, 'sequential' => 1]);
$this->assertEquals(1, $result['count']);
$this->assertEquals($contact2['id'], $result['values'][0]['contact_id']);
+ }
+
+ public function testGetAndGetFullShouldReturnToilRequestsForAllContactsWhenLoggedInUserIsAnAdmin() {
+ $admin = ContactFabricator::fabricate();
+ $contact1 = ContactFabricator::fabricate();
+ $contact2 = ContactFabricator::fabricate();
+
+ $this->registerCurrentLoggedInContactInSession($admin['id']);
+ CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access AJAX API', 'view all contacts'];
+
+ HRJobContractFabricator::fabricate(
+ [ 'contact_id' => $contact2['id'] ],
+ [
+ 'period_start_date' => '2016-01-01',
+ 'period_end_date' => '2016-10-01'
+ ]
+ );
+
+ HRJobContractFabricator::fabricate(
+ [ 'contact_id' => $contact1['id'] ],
+ [
+ 'period_start_date' => '2016-01-01',
+ 'period_end_date' => '2016-10-01'
+ ]
+ );
+
+ $leaveRequest1 = LeaveRequestFabricator::fabricateWithoutValidation([
+ 'contact_id' => $contact1['id'],
+ 'type_id' => $this->absenceType->id,
+ 'from_date' => CRM_Utils_Date::processDate('2016-03-02'),
+ 'to_date' => CRM_Utils_Date::processDate('2016-03-02'),
+ 'from_date_type' => 1,
+ 'to_date_type' => 1,
+ 'status_id' => 1,
+ 'request_type' => LeaveRequest::REQUEST_TYPE_TOIL
+ ]);
+
+ $leaveRequest2 = LeaveRequestFabricator::fabricateWithoutValidation([
+ 'contact_id' => $contact2['id'],
+ 'type_id' => $this->absenceType->id,
+ 'from_date' => CRM_Utils_Date::processDate('2016-02-20'),
+ 'to_date' => CRM_Utils_Date::processDate('2016-02-23'),
+ 'from_date_type' => 1,
+ 'to_date_type' => 1,
+ 'status_id' => 1,
+ 'request_type' => LeaveRequest::REQUEST_TYPE_TOIL
+ ]);
- // Manager2 manages contact1 (through the 'has leaves approved by' relationship),
- // and contact3 (through the 'manage things for' relationship), so leave
- // requests from both should be returned
- $this->registerCurrentLoggedInContactInSession($manager2['id']);
- $result = civicrm_api3('LeaveRequest', 'get', ['check_permissions' => true]);
+ $result = civicrm_api3('LeaveRequest', 'get', ['check_permissions' => true, 'sequential' => 1]);
$this->assertEquals(2, $result['count']);
- $this->assertEquals($contact1['id'], $result['values'][$leaveRequestContact1->id]['contact_id']);
- $this->assertEquals($contact3['id'], $result['values'][$leaveRequestContact3->id]['contact_id']);
+ $this->assertEquals($contact1['id'], $result['values'][0]['contact_id']);
+ $this->assertEquals($contact2['id'], $result['values'][1]['contact_id']);
- $result = civicrm_api3('LeaveRequest', 'getfull', ['check_permissions' => true]);
+ $result = civicrm_api3('LeaveRequest', 'getfull', ['check_permissions' => true, 'sequential' => 1]);
$this->assertEquals(2, $result['count']);
- $this->assertEquals($contact1['id'], $result['values'][$leaveRequestContact1->id]['contact_id']);
- $this->assertEquals($contact3['id'], $result['values'][$leaveRequestContact3->id]['contact_id']);
- $this->assertEquals($contact3['id'], $result['values'][$leaveRequestContact3->id]['contact_id']);
+ $this->assertEquals($contact1['id'], $result['values'][0]['contact_id']);
+ $this->assertEquals($contact2['id'], $result['values'][1]['contact_id']);
}
public function testGetAndGetFullReturnsAllDataWhenLoggedInUserHasViewAllContactsPermission() {
@@ -4131,7 +4445,7 @@ public function testGetBreakdownShouldReturnEmptyIfTheGiveLeaveRequestDoesnExist
$this->assertEmpty($result['values']);
}
- public function testGetBreakdownShouldReturnEmptyIfAStaffMemberTriesToGetTheBreakdownOfAnotherStaffMember() {
+ public function testGetBreakdownHidesRestrictedFieldValuesOfAnotherStaffMemberWhenAStaffMemberTriesAccessingIt() {
$contact1 = ContactFabricator::fabricate();
$contact2 = ContactFabricator::fabricate();
@@ -4151,13 +4465,16 @@ public function testGetBreakdownShouldReturnEmptyIfAStaffMemberTriesToGetTheBrea
$this->registerCurrentLoggedInContactInSession($contact1['id']);
- // Contact1 should not be able to get the breakdown for a leave request of
- // Contact2
+ // Contact1 should also be able to get the breakdown for a leave request of
+ // Contact2 but not restricted fields
$result = civicrm_api3('LeaveRequest', 'getBreakdown', [
'leave_request_id' => $leaveRequest->id,
'check_permissions' => true,
]);
- $this->assertEmpty($result['values']);
+ $this->assertCount(3, $result['values']);
+ $this->assertEquals('', $result['values'][0]['amount']);
+ $this->assertEquals('', $result['values'][1]['amount']);
+ $this->assertEquals('', $result['values'][2]['amount']);
$this->registerCurrentLoggedInContactInSession($contact2['id']);
@@ -4167,49 +4484,34 @@ public function testGetBreakdownShouldReturnEmptyIfAStaffMemberTriesToGetTheBrea
'check_permissions' => true,
]);
$this->assertCount(3, $result['values']);
+ $this->assertNotEmpty($result['values'][0]['amount']);
+ $this->assertNotEmpty($result['values'][1]['amount']);
+ $this->assertNotEmpty($result['values'][2]['amount']);
}
- public function testGetBreakdownShouldReturnEmptyIfAManagerTriesToGetTheBreakdownOfSomeoneWhoTheyDontManage() {
+ public function testGetBreakdownHidesRestrictedFieldValuesForNonManageesOfALeaveManager() {
$manager = ContactFabricator::fabricate();
$contact1 = ContactFabricator::fabricate();
$contact2 = ContactFabricator::fabricate();
+ HRJobContractFabricator::fabricate(
+ ['contact_id' => $contact1['id']],
+ ['period_start_date' => CRM_Utils_Date::processDate('+5 days')]
+ );
+
HRJobContractFabricator::fabricate(
['contact_id' => $contact2['id']],
['period_start_date' => CRM_Utils_Date::processDate('+5 days')]
);
- $leaveRequest = LeaveRequestFabricator::fabricateWithoutValidation([
+ $leaveRequest1 = LeaveRequestFabricator::fabricateWithoutValidation([
'type_id' => $this->absenceType->id,
- 'contact_id' => $contact2['id'],
+ 'contact_id' => $contact1['id'],
'from_date' => CRM_Utils_Date::processDate('+5 days'),
'to_date' => CRM_Utils_Date::processDate('+7 days'),
], true);
- $this->registerCurrentLoggedInContactInSession($manager['id']);
- $this->setContactAsLeaveApproverOf($manager, $contact1);
- $this->setPermissions(['access AJAX API']);
-
- // Manager only manages Contact 1, so they should not be able to get the
- // breakdown for a leave request of Contact2
- $result = civicrm_api3('LeaveRequest', 'getBreakdown', [
- 'leave_request_id' => $leaveRequest->id,
- 'check_permissions' => true,
- ]);
- $this->assertEmpty($result['values']);
- }
-
- public function testGetBreakdownShouldNotReturnEmptyIfAManagerTriesToGetTheBreakdownOfSomeoneTheyManage() {
- $manager = ContactFabricator::fabricate();
- $contact1 = ContactFabricator::fabricate();
- $contact2 = ContactFabricator::fabricate();
-
- HRJobContractFabricator::fabricate(
- ['contact_id' => $contact2['id']],
- ['period_start_date' => CRM_Utils_Date::processDate('+5 days')]
- );
-
- $leaveRequest = LeaveRequestFabricator::fabricateWithoutValidation([
+ $leaveRequest2 = LeaveRequestFabricator::fabricateWithoutValidation([
'type_id' => $this->absenceType->id,
'contact_id' => $contact2['id'],
'from_date' => CRM_Utils_Date::processDate('+5 days'),
@@ -4220,11 +4522,25 @@ public function testGetBreakdownShouldNotReturnEmptyIfAManagerTriesToGetTheBreak
$this->setContactAsLeaveApproverOf($manager, $contact2);
$this->setPermissions(['access AJAX API']);
+ //Contact one is not among the managees of the manager so the restricted fields
+ //values will be hidden for the manager.
$result = civicrm_api3('LeaveRequest', 'getBreakdown', [
- 'leave_request_id' => $leaveRequest->id,
+ 'leave_request_id' => $leaveRequest1->id,
'check_permissions' => true,
]);
$this->assertCount(3, $result['values']);
+ $this->assertEquals('', $result['values'][0]['amount']);
+ $this->assertEquals('', $result['values'][1]['amount']);
+ $this->assertEquals('', $result['values'][2]['amount']);
+
+ $result = civicrm_api3('LeaveRequest', 'getBreakdown', [
+ 'leave_request_id' => $leaveRequest2->id,
+ 'check_permissions' => true,
+ ]);
+ $this->assertCount(3, $result['values']);
+ $this->assertNotEmpty($result['values'][0]['amount']);
+ $this->assertNotEmpty($result['values'][1]['amount']);
+ $this->assertNotEmpty($result['values'][2]['amount']);
}
public function testGetBreakdownReturnsResultsIfAnAdminTriesToAccessTheBreakdownOfAnyLeaveRequest() {
@@ -4595,6 +4911,53 @@ public function testTheTimeForFromAndToDateOfLeaveRequestIsAddedCorrectlyWhenLea
$this->assertEquals($toDate->format('Y-m-d') ." 23:59:00", $leaveRequest->to_date);
}
+ public function testAContactCanViewRestrictedFieldsHeHasAccessToEvenWhenTheContactIdIsNotInTheReturnParameters() {
+ $contact1 = ContactFabricator::fabricate();
+ $this->registerCurrentLoggedInContactInSession($contact1['id']);
+ CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access AJAX API'];
+
+ HRJobContractFabricator::fabricate(
+ [ 'contact_id' => $contact1['id'] ],
+ [
+ 'period_start_date' => '2016-01-01',
+ 'period_end_date' => '2016-10-01'
+ ]
+ );
+
+ $leaveRequest = LeaveRequestFabricator::fabricateWithoutValidation([
+ 'contact_id' => $contact1['id'],
+ 'type_id' => $this->absenceType->id,
+ 'from_date' => CRM_Utils_Date::processDate('2016-03-02'),
+ 'to_date' => CRM_Utils_Date::processDate('2016-03-02'),
+ 'from_date_type' => 1,
+ 'to_date_type' => 1,
+ 'status_id' => 1
+ ]);
+
+ $result = civicrm_api3('LeaveRequest', 'get', [
+ 'check_permissions' => true,
+ 'sequential' => 1,
+ 'return' => ['status_id', 'type_id']
+ ]);
+
+ $this->assertEquals(1, $result['count']);
+ //type_id is a restricted field but contact has access
+ $this->assertEquals($leaveRequest->type_id, $result['values'][0]['type_id']);
+ $this->assertEquals($leaveRequest->status_id, $result['values'][0]['status_id']);
+ $this->assertArrayNotHasKey('contact_id', $result['values'][0]);
+
+ $result = civicrm_api3('LeaveRequest', 'getfull', [
+ 'check_permissions' => true,
+ 'sequential' => 1,
+ 'return' => ['status_id', 'type_id']
+ ]);
+ $this->assertEquals(1, $result['count']);
+ //type_id is a restricted field but contact has access
+ $this->assertEquals($leaveRequest->type_id, $result['values'][0]['type_id']);
+ $this->assertEquals($leaveRequest->status_id, $result['values'][0]['status_id']);
+ $this->assertArrayNotHasKey('contact_id', $result['values'][0]);
+ }
+
/**
* @expectedException CiviCRM_API3_Exception
* @expectedExceptionMessage Mandatory key(s) missing from params array: contact_id, leave_date
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/views/absence-tab/components/absence-tab-container.html b/uk.co.compucorp.civicrm.hrleaveandabsences/views/absence-tab/components/absence-tab-container.html
index 49f2d79ae32..6b6d04f5e8b 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/views/absence-tab/components/absence-tab-container.html
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/views/absence-tab/components/absence-tab-container.html
@@ -12,7 +12,11 @@
-
+
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/views/manager-notification-badge/components/manager-notification-badge.html b/uk.co.compucorp.civicrm.hrleaveandabsences/views/manager-notification-badge/components/manager-notification-badge.html
index 5e55073536a..a8db2f13714 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/views/manager-notification-badge/components/manager-notification-badge.html
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/views/manager-notification-badge/components/manager-notification-badge.html
@@ -1,5 +1,5 @@
-
-
+
diff --git a/uk.co.compucorp.civicrm.hrleaveandabsences/views/my-leave/components/my-leave-container.html b/uk.co.compucorp.civicrm.hrleaveandabsences/views/my-leave/components/my-leave-container.html
index 98df8be2728..ef430d5c164 100644
--- a/uk.co.compucorp.civicrm.hrleaveandabsences/views/my-leave/components/my-leave-container.html
+++ b/uk.co.compucorp.civicrm.hrleaveandabsences/views/my-leave/components/my-leave-container.html
@@ -3,8 +3,7 @@
+ selected-contact-id="myleave.contactId">