var archDB=angular.module('archDB',['ngResource','ngRoute','ngSanitize','$strap.directives','ClientsModule','dashboard_v2','bookings','invoices','accounts','admin','StockModule','StatsModule','ui.calendar','ui.sortable','notificationsModule','daterangepicker','ui.bootstrap','ngFileupload','xeditable','infinite-scroll','gantt','ngTextcomplete','ui.select2','ui.keypress','timer','ngAudio','ngIdle','wu.masonry','mentio','monospaced.elastic','ui.grid','ui.grid.selection','ui.grid.pagination','ui.grid.cellNav','ui.grid.pinning','ui.grid.resizeColumns','ui.grid.edit','chart.js','oitozero.ngSweetAlert','ui.select','ThreeCXModule'],function($routeProvider,$locationProvider){$locationProvider.html5Mode(!1)}).config(function($interpolateProvider,$idleProvider,$keepaliveProvider){$interpolateProvider.startSymbol('{[{').endSymbol('}]}');$idleProvider.idleDuration(120);$idleProvider.keepalive(!1)}).run(function(editableOptions,editableThemes){editableOptions.theme='default';editableThemes['default'].submitTpl='<button type="submit" class="btn btn-primary btn-small"><i class="fa fa-check"></i></button>';editableThemes['default'].cancelTpl='<button type="button" class="btn btn-default btn-small" ng-click="$form.$cancel()"><i class="fa fa-times"></i></button>';moment.updateLocale('en',{week:{dow:1}})});archDB.controller('TitleController',['$scope','Page',function($scope,Page){$scope.Page=Page}]);archDB.filter('fromNow',function(){return function(dateString){if(!dateString)return"";return moment(dateString).fromNow()}});archDB.filter('diff',function(){return function(dateString,format){if(!format)
format='seconds';if(_.indexOf(['seconds','minutes','hours','days'],format)==-1)
throw "The format you requested is invalid";return moment().diff(dateString,format)}});archDB.filter('console',function(){return function(object){console.log(object)}});archDB.filter('calendar',function(){return function(dateString){if(!dateString)return moment(new Date()).calendar();var t=dateString.split(/[- :]/);var d=new Date(t[0],t[1]-1,t[2],t[3],t[4],t[5]);return moment(d).calendar()}});archDB.filter('moment',function(){return function(dateString,format){if(!format)format="YYYY-MM-DD HH:mm:ss";return moment(dateString).format(format)}});archDB.filter('html',function(){return function(data){}});archDB.filter('capitalize',function(){return function(string){var capitalized=string.charAt(0).toUpperCase()+string.substring(1);return capitalized}});archDB.filter('nl2br',function(){return function(text){if(!text)return'';return text.replace(/\n/g,'<br/>')}});archDB.filter('taskUrl',function(){return function(task){if(!task)return'';if(task.type=='repair')
return'repairs/'+task.id;if(task.type=='order')
return'orders/'+task.id;if(task.type=='sale_lead')
return'sale-lead/'+task.id;return task.type+'/'+task.id}});archDB.filter('bytes',function(){return function(bytes,precision){if(isNaN(parseFloat(bytes))||!isFinite(bytes))return'-';if(typeof precision==='undefined')precision=1;var units=['bytes','kB','MB','GB','TB','PB'],number=Math.floor(Math.log(bytes)/Math.log(1024));return(bytes/Math.pow(1024,Math.floor(number))).toFixed(precision)+' '+units[number]}});archDB.filter('slaTimer',function(){return function(seconds){var duration=moment.duration(seconds,'seconds');var minutes=Math.abs(duration.asMinutes());var hours=Math.floor(minutes/60);minutes=Math.floor(minutes%60);if(hours<10)hours='0'+hours;if(minutes<10)minutes='0'+minutes;return(seconds<0)?'-'+hours+':'+minutes:hours+':'+minutes}});archDB.filter('range',function(){return function(input,total,start){total=parseInt(total);start=(start)?parseInt(start):0;for(var i=start;i<total;i++)
input.push(i);return input}});archDB.filter('dateAddition',function(){return function(input,measurement,unit,format){if(!measurement)measurement='days';if(!unit)unit=1;if(!format)format="DD/MM/YYYY";return moment().add(measurement,unit).format(format)}});archDB.filter('exclude',function(){return function(input,index,value){if(!input)return[];if(!index||typeof value=='undefined')return input;return _.reject(input,function(el){return el[index]==value})}});archDB.factory('apiUrl',function(){return function(path){return'/api/v1/'+path}});archDB.factory('Messenger',function(){return function(message,type){if(!message)return!1;if(!type)type='success';Messenger().post({message:message,hideAfter:2,type:type})}});archDB.factory('ErrorsHandler',function(){return function(res){if(res.data&&res.data.error)
if(res.data.error.message)
Messenger().post({message:res.data.error.message,hideAfter:0,type:'error',showCloseButton:!0,});else Messenger().post({message:res.data.error,hideAfter:0,type:'error',showCloseButton:!0,});if(res.data&&res.data.error=='')
Messenger().post({message:'Something went wrong',hideAfter:0,type:'error',showCloseButton:!0,});if(res.data&&res.data.success===!1&&res.data.message){Messenger().post({message:res.data.message,hideAfter:0,type:'error',showCloseButton:!0,})}else if(res.data&&res.data.errors){var messages='';if(Array.isArray(res.data.errors)){messages=res.data.errors.join("\n")}else if(typeof res.data.errors==='object'){var errorMessages=[];for(var field in res.data.errors){if(res.data.errors.hasOwnProperty(field)){var fieldErrors=res.data.errors[field];if(Array.isArray(fieldErrors)){errorMessages=errorMessages.concat(fieldErrors)}else{errorMessages.push(fieldErrors)}}}
messages=errorMessages.join("\n")}else{messages=res.data.errors.toString()}
Messenger().post({message:messages,hideAfter:0,type:'error',showCloseButton:!0,theme:'air',extraClasses:'messenger-fixed messenger-on-top messenger-on-right'})}}});archDB.factory('Page',function(){var title='Seraph';return{getTitle:function(){return title},setTitle:function(newTitle){title=newTitle}}});archDB.factory('Settings',function(){return{vat:20,staff_percentage:5}});archDB.factory('ResourceMethods',function(){return{'get':{method:'GET'},'save':{method:'POST'},'update':{method:'PUT'},'query':{method:'GET',isArray:!0},'remove':{method:'DELETE'},'delete':{method:'DELETE'}}});archDB.factory('ResourcePrototype',function(){return{save:function(values,callback){if(typeof values=='function'){callback=values}else if(values){angular.extend(this,values)}
if(this.id){return this.$update(callback)}
return this.$save(callback)}}});archDB.factory('User',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var User=$resource(apiUrl('users/:user'),{user:'@id'},methods);User.getSearchUrl=function(){return apiUrl('users?name=')};angular.extend(User.prototype,_prototype);return User}]);archDB.factory('Client',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Client=$resource(apiUrl('clients/:client'),{client:'@id'},methods);Client.getSearchUrl=function(){return apiUrl('clients?name=')};angular.extend(Client.prototype,_prototype);return Client}]);archDB.factory('Customer',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Customer=$resource(apiUrl('customers/:customer'),{customer:'@id'},methods);Customer.getSearchUrl=function(){return apiUrl('customers?name=')};angular.extend(Customer.prototype,_prototype);return Customer}]);archDB.factory('Site',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Site=$resource(apiUrl('sites/:site'),{site:'@id'},methods);angular.extend(Site.prototype,_prototype);return Site}]);archDB.factory('SiteUser',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var SiteUser=$resource(apiUrl('sites/:site/users/:user'),{site:'@site_id',user:'@id'},methods);angular.extend(SiteUser.prototype,_prototype);return SiteUser}]);archDB.factory('Sla',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Sla=$resource(apiUrl('sla/:sla'),{sla:'@id'},methods);angular.extend(Sla.prototype,_prototype);return Sla}]);archDB.factory('SlaLog',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var SlaLog=$resource(apiUrl('sla-logs/:sla_log'),{sla_log:'@id'},methods);angular.extend(SlaLog.prototype,_prototype);return SlaLog}]);archDB.factory('ActivityLog',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var ActivityLog=$resource(apiUrl('activity-logs/:activity_log'),{activity_log:'@id'},methods);angular.extend(ActivityLog.prototype,_prototype);return ActivityLog}]);archDB.factory('Follow',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Follow=$resource(apiUrl('follow/:follow'),{follow:'@id'},methods);angular.extend(Follow.prototype,_prototype);return Follow}]);archDB.factory('Severity',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Severity=$resource(apiUrl('severities/:severity'),{severity:'@id'},methods);angular.extend(Severity.prototype,_prototype);return Severity}]);archDB.directive('modal',function(){var defaults={onOpen:function(){},onClose:function(){}};return{restrict:'A',scope:{'options':'=modalOptions','open':'=modal','classes':'@customClass'},controller:['$scope','$element',function($scope,$element){angular.extend(defaults,$scope.options||{});$scope.$watch('open',function(){if($scope.open)
$scope.showModal();else $scope.hideModal()});$scope.showModal=function(){$element.modal('show')};$scope.hideModal=function(){$element.modal('hide')};$element.bind('hidden',function(){$scope.$apply(function(){$scope.open=!1;if(defaults.onClose)defaults.onClose($element);})});$element.bind('shown',function(){$scope.$apply(function(){$scope.open=!0;if(defaults.onOpen)defaults.onOpen($element);})})}],link:function(scope,element,attrs){element.addClass('modal hide fade');if(scope.classes)element.addClass(scope.classes);defaults.show=(scope.open)?!0:!1;element.modal(defaults)}}});archDB.directive('iframeAutoheight',function(){return{restrict:'A',link:function(scope,element,attrs){element.on('load',function(){$(this).height($(this).contents().outerHeight())})}}});archDB.directive('clientSearch',function(){return{restrict:'A',controller:['$scope','$element','ErrorsHandler','$ClientsManager','Client','Customer',function($scope,$element,ErrorsHandler,$ClientsManager,Client,Customer){var clientSource=new Bloodhound({datumTokenizer:function(datum){return Bloodhound.tokenizers.whitespace(datum.name)},queryTokenizer:Bloodhound.tokenizers.whitespace,remote:{url:Client.getSearchUrl()+'%QUERY',wildcard:'%QUERY',transform:function(response){if(response.data){response.data.forEach(function(client){client.type='client'})}
return response.data}}});var customerSource=new Bloodhound({datumTokenizer:function(datum){return Bloodhound.tokenizers.whitespace(datum.name||datum.first_name)},queryTokenizer:Bloodhound.tokenizers.whitespace,remote:{url:Customer.getSearchUrl()+'%QUERY',wildcard:'%QUERY',transform:function(response){if(response.data){response.data.forEach(function(customer){customer.name=customer.first_name+' '+customer.last_name;customer.type='customer'})}
return response.data}}});clientSource.initialize();customerSource.initialize();$scope.options=[{name:'clients',source:clientSource.ttAdapter(),displayKey:'name',templates:{header:'<h6 class="typeahead-header">Clients</h6>'}},{name:'customers',source:customerSource.ttAdapter(),displayKey:'name',templates:{header:'<h6 class="typeahead-header">Customers</h6>'}}];$scope.$on('clients::changed',function(e,client){$element.typeahead('val',client.name)});$scope.customerSelected=function(ev,datum){var clientId=datum.data?datum.data.id:datum.id;var clientName=datum.name||datum.value||(datum.first_name+' '+datum.last_name);$element.typeahead('val',clientName);var isCustomer=datum.type==='customer';if(!isCustomer){Client.get({client:clientId,site_users:!0},function(res){if(res.success){$ClientsManager.setClient(res.data,!0);$ClientsManager.setClientType('client');$scope.client_search=res.data.name;if(!$scope.$$phase){$scope.$apply()}}},ErrorsHandler)}else{Customer.get({customer:clientId},function(res){if(res.success&&res.data){$ClientsManager.setClient(res.data,!0);$ClientsManager.setClientType('customer');$scope.client_search=res.data.first_name+' '+res.data.last_name;if(!$scope.$$phase){$scope.$apply()}}},ErrorsHandler)}};$scope.client_search=$ClientsManager.getClient().name;$scope.init=function(){if($ClientsManager.isSelected()){$scope.client_search=$ClientsManager.getClient().name;$element.typeahead('val',$ClientsManager.getClient().name)}}}],link:function(scope,element,attrs){element.typeahead({hint:!1},scope.options);scope.init();element.on('typeahead:selected',function(ev,datum){ev.preventDefault();scope.customerSelected(ev,datum);if(!scope.$$phase){scope.$apply()}})}}});archDB.directive('productSearch',function(){return{restrict:'A',scope:{'callback':'&searchCallback','autoinsert':'@searchAutoInsert','clear':'@searchClear','composite':'@searchComposite','hide':'=','model':'=ngModel','discontinued':'='},require:'ngModel',controller:['$scope','$element','ErrorsHandler','Stock','$timeout',function($scope,$element,ErrorsHandler,Stock,$timeout){$scope.$watch('discontinued',function(){return},!0);var productSource=new Bloodhound({datumTokenizer:function(datum){return Bloodhound.tokenizers.whitespace(datum.name)},queryTokenizer:Bloodhound.tokenizers.whitespace,remote:{url:Stock.getSearchUrl(),wildcard:'%QUERY',prepare:function(query,settings){settings.url+=query+($scope.composite!=undefined?'&composite='+($scope.composite==='true'?1:0):'')+($scope.discontinued!=undefined?'&show_discontinued='+$scope.discontinued:'');return settings},transform:function(response){if(response.data.length==1&&$scope.autoinsert&&$scope.barcode){$scope.barcode=!1;$scope.callback({ev:null,data:response.data[0]});$scope.clearInput();return[]}
return response.data}}});productSource.initialize();$scope.options={name:'products_'+Math.floor(Math.random()*99999)+9999,source:productSource.ttAdapter(),limit:15,display:function(item){return item.name+' - £'+item.retail_price+' (In stock: '+(item.unlimited?'unlimited':item.quantity)+')'}};$scope.clearInput=function(){if($scope.clear)
$element.typeahead('val','').focus();};$scope.$watch('hide',function(){if($scope.hide){$element.hide()}else{$element.show()}})}],link:function(scope,element,attrs,model){element.typeahead({hint:!1},scope.options);element.parent().addClass('col-xs-12');element.bind('typeahead:select',function(ev,item){console.log('typeahead:select triggered with item:',item);ev.preventDefault();console.log('Calling callback with data:',item);scope.callback({ev:ev,data:item});console.log('Clearing input');scope.clearInput();if(!scope.$$phase){console.log('Triggering digest cycle');scope.$apply()}
return!1});element.on('keyup',function(){if(!element.val()){console.log('Product input cleared, triggering callback with null data');scope.$apply(function(){model.$setViewValue('');scope.callback({ev:null,data:null})})}});var current_phrase;element.on('keydown',function(e){if(e.which===13&&/^[0-9]+$/.test(element.val())){scope.barcode=!0;e.preventDefault()}});element.on('keyup',function(e){setTimeout(function(){current_phrase=element.val();scope.model=current_phrase;model.$setViewValue(current_phrase);if(!scope.$$phase)scope.$apply();},50)});scope.$watch('model',function(newValue){if(typeof newValue!='undefined'&&newValue!=current_phrase)
element.typeahead('val',newValue);})}}});archDB.directive('eatClick',function(){return function(scope,element,attrs){$(element).click(function(event){event.preventDefault()})}});archDB.directive('eatPropagation',function(){return function(scope,element,attrs){$(element).click(function(event){event.stopImmediatePropagation();event.preventDefault()})}});archDB.directive('tabdrop',function(){return function(scope,element,attrs){element.tabdrop()}});archDB.directive('paginator',function(){var template='<div class="row">'+'<div class="col-xs-6">'+'<div class="paginator_info">'+'<small>Showing {[{ paginator.getFrom() }]} to {[{ paginator.getTo() }]} of {[{ paginator.getTotalItems() }]} entries</small>'+'</div>'+'</div>'+'<div class="col-xs-6">'+'<nav class="paginator_pages pull-right">'+'<ul class="pagination">'+'<li ng-class="{disabled: ! paginator.hasPrev()}"><a href="#" ng-click="paginator.prevPage()" eat-click>Prev</a></li>'+'<li ng-if="showPages != \'false\'" ng-repeat="page in paginator.getVisiblePages()" ng-class="{disabled: paginator.getCurrentPage() == page || page == \'..\' || page == \'...\'}">'+'<a href="#" ng-click="paginator.setPage(page)" eat-click>{[{ page }]}</a>'+'</li>'+'<li ng-class="{disabled: ! paginator.hasNext()}"><a href="#" ng-click="paginator.nextPage()" eat-click>Next</a></li>'+'</ul>'+'</nav>'+'</div>'+'</div>';return{restrict:'A',replace:!0,template:template,scope:{'paginator':'=','showPages':'@'}}});archDB.directive('lookupStock',function(){return{restrict:'A',require:'ngModel',scope:{'stockSelected':'&','model':'=ngModel'},template:'<input autocomplete="off" ng-keypress="updated($event)">',replace:!0,controller:['$scope','$element','ErrorsHandler','Stock',function($scope,$element,ErrorsHandler,Stock){var suggestion=new Bloodhound({datumTokenizer:Bloodhound.tokenizers.whitespace,queryTokenizer:Bloodhound.tokenizers.whitespace,remote:{url:Stock.getSearchUrl()+'%QUERY',wildcard:'%QUERY',transform:function(response){return response.data}}});suggestion.initialize();$scope.dataset={name:'products',displayKey:'name',source:suggestion.ttAdapter()};$scope.updated=function(ev){$scope.model=$element.val()};$scope.$watch('model',function(){if(!$scope.model)$element.typeahead('val','').focus();})}],link:function(scope,element,attrs){element.typeahead({hint:!1},scope.dataset);element.on('change',function(){scope.$apply(function(){scope.model=element.val()})});element.on('typeahead:selected',function(e,item){if(scope.stockSelected)scope.stockSelected({data:item.data});scope.$apply(function(){scope.model=element.val()})})}}});archDB.directive('pageHighlighter',['$location',function($location){return{restrict:'A',link:function($scope,$element,$attrs){var cssClass=($attrs.customClass)?$attrs.customClass:'active';$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){if($attrs.exactMatch){if($location.path()==$attrs.pageHighlighter)
$element.addClass(cssClass);else $element.removeClass(cssClass)}else{if($location.path().substr(0,$attrs.pageHighlighter.length)==$attrs.pageHighlighter)
$element.addClass(cssClass);else $element.removeClass(cssClass)}})}}}]);archDB.directive('clientName',function(){return{restrict:'A',scope:{'data':'=clientName'},link:function(scope,element,attrs){if(!scope.data)return;if(scope.data.hasOwnProperty('first_name')){element.text(scope.data.first_name+' '+scope.data.last_name)}else{element.text(scope.data.name)}}}});archDB.directive('notification',function(){return{restrict:'A',controller:['$scope','Notification','ErrorsHandler','$timeout','Messenger','$DesktopNotification',function($scope,Notification,ErrorsHandler,$timeout,Messenger,$DesktopNotification){$scope.unread=0;$scope.lastId=0;$scope.total=0;var cancelRefresh;var firstRun=!0;$scope.CountNotifications=function(){Notification.get({count:!0},function(res){$scope.total=res.total;$scope.unread=res.unread.length;$scope.notifications=res.unread;$scope.flashNewMessages(res.unread)},ErrorsHandler)};$scope.markAsRead=function(){Notification.get({markAsRead:!0},function(response){$scope.unread=0},ErrorsHandler)};$scope.clearAllNotifications=function(){Notification.get({clearAll:!0},function(response){$scope.notifications=[];$scope.unread=0;Messenger("All notifications marked as read.")},ErrorsHandler)};$scope.flashNewMessages=function(messages){var sorted=_.sortBy(messages,function(el){return el.id});if(firstRun){if(sorted.length>0)$scope.lastId=sorted[sorted.length-1].id;firstRun=!1;return!1}
_.each(sorted,function(el){if(el.id>$scope.lastId){Messenger(el.message);$DesktopNotification.show('Notification',el.message);$scope.lastId=el.id}})};$scope.redirect=function(notification){switch(notification.related_type){case 'repair':return window.location.href='/tasks/repairs/'+notification.related_id;case 'support':return window.location.href='/tasks/support/'+notification.related_id;case 'onsite':return window.location.href='/tasks/onsite/'+notification.related_id;case 'order':return window.location.href='/tasks/orders/'+notification.related_id;case 'message':return window.location.href='/messages/'+notification.related_id}};$scope.$on('$destroy',function(e){$timeout.cancel(cancelRefresh)});cancelRefresh=$timeout(function NotificationsRefresh(){$scope.CountNotifications();cancelRefresh=$timeout(NotificationsRefresh,600*1000)},5)}]}});archDB.directive('stripHtml',function(){return{link:function($scope,$element,$attrs){var div=angular.element('<div/>').html($attrs.stripHtml);$element.text(div.text())}}});archDB.directive('confirmOnExit',['$location','$rootScope',function($location,$rootScope){return{link:function($scope,elem,attrs){var formName=(attrs.name)?attrs.name:'myForm';var check=!0;window.onbeforeunload=function(){if($scope[formName].$dirty){return"Your form has not been saved, do you want to stay on the page?"}};$rootScope.$on('$locationChangeStart',function(event,next,current){if($scope[formName].$dirty&&check){if(!confirm("Your have not saved the form, do you want to leave this page?")){event.preventDefault()}else{check=!1}}})}}}]);archDB.directive('archChange',['$parse',function($parse){return{link:function($scope,$element,$attrs){var fn=$parse($attrs.archChange);$element.on('change',function(ev){fn($scope,{event:ev});if(!$scope.$$phase)$scope.$apply();})}}}]);archDB.directive('numbersOnly',function(){return{require:'ngModel',link:function(scope,element,attrs,modelCtrl){modelCtrl.$parsers.push(function(inputValue){if(inputValue===undefined)return'';var transformedInput=inputValue.replace(/[^0-9.]/g,'');if(transformedInput!=inputValue){modelCtrl.$setViewValue(transformedInput);modelCtrl.$render()}
return transformedInput})}}});archDB.directive('summernote',function(){return{require:'?ngModel',link:function($scope,$element,$attrs,$ctrl){$element.summernote({height:$attrs.summernoteHeight?$attrs.summernoteHeight:!1,onblur:function(){$ctrl.$setViewValue($element.code());if(!$scope.$$phase)$scope.$apply();}});$scope.$watch($attrs.ngModel,function(newValue,oldValue){if($element.code()!=newValue)$element.code(newValue);})}}});archDB.directive('dropdownCheckbox',function(){return{restrict:'CA',link:function(scope,element,attrs){element.on('click',function(e){e.stopPropagation()})}}});archDB.directive('textcomplete',['Textcomplete','Client','Customer','$timeout',function(Textcomplete,Client,Customer,$timeout){return{restrict:'EA',scope:{staff:'=',tags:'=',message:'='},replace:!0,template:'<textarea ng-model="message"></textarea>',link:function(scope,iElement,iAttrs){var clients_timeout;if(!scope.tags)scope.tags={};var date=[{name:'Today',value:moment()},{name:'Tommorrow',value:moment().add('days',1)},{name:moment().add('days',2).format('dddd'),value:moment().add('days',2)}];var tasks=[{name:'Repair',id:'repair'},{name:'Support',id:'support'},{name:'Onsite',id:'onsite'},{name:'Message',id:'message'},{name:'Order',id:'order'},{name:'Sale Lead',id:'sale_lead'},{name:'RMA',id:'rma'}];var times=[];for(var i=0;i<24;i++){for(var j=0;j<60;j+=15){var time=(i<10)?'0'+i:i;time+=':';time+=(j<10)?'0'+j:j;times.push(time)}}
var textcomplete=new Textcomplete(iElement,{date:{match:/\B\$(\w*)$/,search:function(term,callback){callback($.map(date,function(mention){return mention.name.toLowerCase().indexOf(term.toLowerCase())===0?mention:null}))},index:1,replace:function(mention){scope.tags.date=mention;return'$'+mention.name+' '},template:function(mention){return mention.name}},time:{match:/\B\!(\w*)$/,search:function(term,callback){callback($.map(times,function(mention){return mention.indexOf(term)===0?mention:null}))},index:1,replace:function(mention){scope.tags.time=mention;return'!'+mention+' '}},staff:{match:/\B@(\w*\s*\w*)$/,search:function(term,callback){callback($.map(scope.staff,function(mention){return mention.name.toLowerCase().indexOf(term.toLowerCase())===0?mention:null}))},index:1,replace:function(mention){scope.tags.staff=mention;return'@'+mention.name+' '},template:function(mention){return mention.name}},task:{match:/\B\^(\w*\s*\w*)$/,search:function(term,callback){callback($.map(tasks,function(mention){return mention.name.toLowerCase().indexOf(term.toLowerCase())===0?mention:null}))},index:1,replace:function(mention){scope.tags.task=mention;return'^'+mention.name+' '},template:function(mention){return mention.name}},client:{match:/\B#(\w*\s*\w*)$/,search:function(term,callback){var first=!0;var data=[];if(clients_timeout)$timeout.cancel(clients_timeout);clients_timeout=$timeout(function(){Client.get({name:term,limit:5,site_users:!0},function(response){data=(first)?response.data:callback(data.concat(response.data));first=!1},function(){if(first)first=!1;else callback(data)});Customer.get({name:term,limit:5},function(response){data=(first)?response.data:callback(data.concat(response.data));first=!1},function(){if(first)first=!1;else callback(data)})},100)},index:1,replace:function(mention){scope.tags.client=mention;return'#'+mention.name+' '},template:function(mention){return mention.name}}});iElement.parent().css('display','block')}}}]);archDB.directive('profilePhotoUpload',['apiUrl','$parse','ErrorsHandler','$http',function(apiUrl,$parse,ErrorsHandler,$http){return{scope:{userData:'='},link:function($scope,$element,$attrs){var pendingFile=null;$element.on('change',function(e){var file=e.target.files[0];if(file){pendingFile=file;var reader=new FileReader();reader.onload=function(e){$scope.$apply(function(){$scope.userData.profile_photo_url=e.target.result})};reader.readAsDataURL(file)}});$scope.$on('uploadProfilePhoto',function(event,callback){if(pendingFile){var formData=new FormData();formData.append('files',pendingFile);$http.post(apiUrl('users/profile-photo'),formData,{headers:{'Content-Type':undefined,'X-CSRF-TOKEN':$('meta[name="csrf-token"]').attr('content')},transformRequest:angular.identity}).then(function(response){if(response.data&&response.data.url){$scope.userData.profile_photo_url=response.data.url;if($scope.$parent&&$scope.$parent.userData){$scope.$parent.userData.profile_photo_url=response.data.url}}
pendingFile=null;if(callback)callback();},function(error){ErrorsHandler(error);if(callback)callback();})}else{if(callback)callback();}})}}}]);archDB.directive('optionsDisabled',['$parse',function($parse){var disableOptions=function(scope,attr,element,data,fnDisableIfTrue){angular.forEach(element.find("option"),function(value,index){var elem=angular.element(value);if(elem.val()!=""){var locals={};locals[attr]=data[index];elem.attr("disabled",fnDisableIfTrue(scope,locals))}})};return{priority:0,require:'ngModel',link:function(scope,iElement,iAttrs,ctrl){var expElements=iAttrs.optionsDisabled.match(/^\s*(.+)\s+for\s+(.+)\s+in\s+(.+)?\s*/);var attrToWatch=expElements[3];var fnDisableIfTrue=$parse(expElements[1]);scope.$watch(attrToWatch,function(newValue,oldValue){if(newValue)
disableOptions(scope,expElements[2],iElement,newValue,fnDisableIfTrue);},!0);scope.$watch(iAttrs.ngModel,function(newValue,oldValue){var disOptions=$parse(attrToWatch)(scope);if(newValue)
disableOptions(scope,expElements[2],iElement,disOptions,fnDisableIfTrue);})}}}]);archDB.directive('ngConfirmClick',['SweetAlert',function(SweetAlert){return{link:function(scope,element,attr){var msg=attr.ngConfirmClick||"Are you sure?";var clickAction=attr.confirmedClick;element.bind('click',function(event){if(attr.disabled){return}
SweetAlert.swal({title:msg,type:"warning",showCancelButton:!0,confirmButtonColor:"#DD6B55",confirmButtonText:"Yes"},function(confirmed){if(confirmed){scope.$eval(clickAction);if(!scope.$$phase)scope.$apply();}})})}}}]);archDB.directive('ngHtml',function(){return{link:function(scope,element,attr){element.html(attr.ngHtml)}}});archDB.directive('nanoscroller',['$interval',function($interval){return{link:function(scope,element,attr){element.nanoScroller({iOSNativeScrolling:!0,disableResize:!0,preventPageScrolling:!0});$interval(function(){element.nanoScroller()},1000)}}}]);archDB.directive('addCommand',function(){return{restrict:'A',scope:{'commands':'=','command':'='},replace:!0,templateUrl:"modals/add_command.html",controller:function($scope){alert("hello")}}});archDB.directive('tooltip',['$compile',function($compile){return{restrict:'EA',link:function(scope,element,attrs){if(attrs.tooltip){element.attr('uib-tooltip',attrs.tooltip);element.attr('tooltip-placement',attrs.tooltipPlacement||'top');element.attr('tooltip-animation',attrs.tooltipAnimation!==undefined?attrs.tooltipAnimation:!0);element.attr('tooltip-popup-delay',attrs.tooltipPopupDelay||300);element.attr('tooltip-trigger',attrs.tooltipTrigger||'mouseenter');element.attr('tooltip-append-to-body',attrs.tooltipAppendToBody!==undefined?attrs.tooltipAppendToBody:!0);element.removeAttr('tooltip');$compile(element)(scope)}}}}]);archDB.directive('focusable',['$timeout',function($timeout){return{restrict:'AC',link:function(scope,element,attrs){$timeout(function(){element.focus()},100)}}}]);archDB.directive('datepickerPopup',function(){return{restrict:'EAC',require:'ngModel',link:function(scope,element,attr,controller){controller.$formatters.shift()}}});archDB.factory('$ClientsManager',['$rootScope',function($rootScope){var $this=this;var client={};var client_type='client';var data={};return{client:client,client_type:client_type,data:data,setClient:function(new_client,broadcast){client=angular.copy(new_client||{});if(new_client&&new_client.type)client_type=new_client.type;if(broadcast===!0)$rootScope.$broadcast('clients::changed',client);},getClient:function(){return client},setClientType:function(new_type){client_type=new_type},getClientType:function(){return client_type},isCustomer:function(){return client_type=='customer'},isClient:function(){return client_type=='client'},isSelected:function(){return(client.id)?!0:!1},unify:function(site,site_user){data=angular.extend(data,{name:client.name,address:(site)?site.address:client.address,address2:(site)?site.address2:client.address2,postcode:(site)?site.postcode:client.postcode,town:(site)?null:client.town,county:(site)?null:client.county,phone:(site_user)?site_user.phone:client.phone,phone2:(site_user)?null:client.phone2,mobile:(site_user)?site_user.mobile:client.mobile,email:(site_user)?site_user.email:client.email});return data},getAddress:function(site){var output=[];if(site){if(site.address)output.push(site.address);if(site.address2)output.push(site.address2);if(site.postcode)output.push(site.postcode);}else{if(data.address)output.push(data.address);if(data.address2)output.push(data.address2);if(data.town)output.push(data.town);if(data.county)output.push(data.county);if(data.postcode)output.push(data.postcode);}
return output.join(', ')}}}]);archDB.factory('$paginator',['$rootScope',function($rootScope){var Paginator=function(){this.limit=20;this.total_items=0;this.total_pages=0;this.current_page=1;this.from=0;this.to=0;this.pages_to_show=4;this.edges=2;this.onPageChange=function(page,limit){};this.onLimitChange=function(page,limit){}};Paginator.prototype.getLimit=function(){return this.limit};Paginator.prototype.getCurrentPage=function(){return this.current_page};Paginator.prototype.getTotalItems=function(){return this.total_items};Paginator.prototype.getTotalPages=function(){return this.total_pages};Paginator.prototype.getFrom=function(){return this.from};Paginator.prototype.getTo=function(){return this.to};Paginator.prototype.setLimit=function(value){this.limit=value;this.onLimitChange(this.current_page,this.limit)};Paginator.prototype.setCurrentPage=function(value){this.current_page=value};Paginator.prototype.setTotalItems=function(value){this.total_items=value};Paginator.prototype.setTotalPages=function(value){this.total_pages=value};Paginator.prototype.setFrom=function(value){this.from=value};Paginator.prototype.setTo=function(value){this.to=value};Paginator.prototype.setFromResponse=function(response){if(response.hasOwnProperty('last_page'))this.setTotalPages(response.last_page);if(response.hasOwnProperty('current_page'))this.setCurrentPage(response.current_page);if(response.hasOwnProperty('total'))this.setTotalItems(response.total);if(response.hasOwnProperty('from'))this.setFrom(response.from);if(response.hasOwnProperty('to'))this.setTo(response.to);};Paginator.prototype.setOnPageChange=function(value){if(typeof value=='function')
this.onPageChange=value};Paginator.prototype.setOnLimitChange=function(value){if(typeof value=='function')
this.onLimitChange=value};Paginator.prototype.getVisiblePages=function(){var pages=[],i;if(!this.total_pages)return pages;var half_pages=this.pages_to_show/2;var interval={start:Math.ceil(this.current_page>half_pages?Math.max(Math.min(this.current_page-half_pages,(this.total_pages-this.pages_to_show)),0):0),end:Math.ceil(this.current_page>half_pages?Math.min(this.current_page+half_pages,this.total_pages):Math.min(this.pages_to_show,this.total_pages))};if(interval.start>0&&this.edges>0){var end=Math.min(this.edges,interval.start);for(i=0;i<end;i++){pages.push(i+1)}
if((this.edges<interval.start)&&(interval.start-this.edges!=1)){pages.push('..')}else if(interval.start-this.edges==1){pages.push(this.edges+1)}}
for(i=interval.start;i<interval.end;i++){pages.push(i+1)}
if(interval.end<this.total_pages&&this.edges>0){if(this.total_pages-this.edges>interval.end&&(this.total_pages-this.edges-interval.end!=1)){pages.push('...')}else if(this.total_pages-this.edges-interval.end==1){pages.push(++interval.end)}
var begin=Math.max(this.total_pages-this.edges,interval.end);for(i=begin;i<this.total_pages;i++){pages.push(i+1)}}
return pages};Paginator.prototype.setPage=function(value){if(value<0&&value>this.total_pages)
return!1;this.current_page=value;this.onPageChange(this.current_page,this.limit)};Paginator.prototype.nextPage=function(){if(!this.hasNext())
return!1;this.setPage(++this.current_page)};Paginator.prototype.prevPage=function(){if(!this.hasPrev())
return!1;this.setPage(--this.current_page)};Paginator.prototype.hasNext=function(){return(this.current_page<this.total_pages)};Paginator.prototype.hasPrev=function(){return(this.current_page>1)};return new Paginator()}]);archDB.factory('keyboardManager',['$window','$timeout',function($window,$timeout){var keyboardManagerService={};var defaultOpt={'type':'keydown','propagate':!1,'inputDisabled':!1,'target':$window.document,'keyCode':!1};keyboardManagerService.keyboardEvent={}
keyboardManagerService.bind=function(label,callback,opt){var fct,elt,code,k;opt=angular.extend({},defaultOpt,opt);label=label.toLowerCase();elt=opt.target;if(typeof opt.target=='string')elt=document.getElementById(opt.target);fct=function(e){e=e||$window.event;if(opt.inputDisabled){var elt;if(e.target)elt=e.target;else if(e.srcElement)elt=e.srcElement;if(elt.nodeType==3)elt=elt.parentNode;if(elt.tagName=='INPUT'||elt.tagName=='TEXTAREA')return}
if(e.keyCode)code=e.keyCode;else if(e.which)code=e.which;var character=String.fromCharCode(code).toLowerCase();if(code==188)character=",";if(code==190)character=".";var keys=label.split("+");var kp=0;var shift_nums={"`":"~","1":"!","2":"@","3":"#","4":"$","5":"%","6":"^","7":"&","8":"*","9":"(","0":")","-":"_","=":"+",";":":","'":"\"",",":"<",".":">","/":"?","\\":"|"};var special_keys={'esc':27,'escape':27,'tab':9,'space':32,'return':13,'enter':13,'backspace':8,'scrolllock':145,'scroll_lock':145,'scroll':145,'capslock':20,'caps_lock':20,'caps':20,'numlock':144,'num_lock':144,'num':144,'pause':19,'break':19,'insert':45,'home':36,'delete':46,'end':35,'pageup':33,'page_up':33,'pu':33,'pagedown':34,'page_down':34,'pd':34,'left':37,'up':38,'right':39,'down':40,'f1':112,'f2':113,'f3':114,'f4':115,'f5':116,'f6':117,'f7':118,'f8':119,'f9':120,'f10':121,'f11':122,'f12':123};var modifiers={shift:{wanted:!1,pressed:e.shiftKey?!0:!1},ctrl:{wanted:!1,pressed:e.ctrlKey?!0:!1},alt:{wanted:!1,pressed:e.altKey?!0:!1},meta:{wanted:!1,pressed:e.metaKey?!0:!1}};for(var i=0,l=keys.length;k=keys[i],i<l;i++){switch(k){case 'ctrl':case 'control':kp++;modifiers.ctrl.wanted=!0;break;case 'shift':case 'alt':case 'meta':kp++;modifiers[k].wanted=!0;break}
if(k.length>1){if(special_keys[k]==code)kp++}else if(opt.keyCode){if(opt.keyCode==code)kp++}else{if(character==k)kp++;else{if(shift_nums[character]&&e.shiftKey){character=shift_nums[character];if(character==k)kp++}}}}
if(kp==keys.length&&modifiers.ctrl.pressed==modifiers.ctrl.wanted&&modifiers.shift.pressed==modifiers.shift.wanted&&modifiers.alt.pressed==modifiers.alt.wanted&&modifiers.meta.pressed==modifiers.meta.wanted){$timeout(function(){callback(e)},1);if(!opt.propagate){e.cancelBubble=!0;e.returnValue=!1;if(e.stopPropagation){e.stopPropagation();e.preventDefault()}
return!1}}};keyboardManagerService.keyboardEvent[label]={'callback':fct,'target':elt,'event':opt.type};if(elt.addEventListener)elt.addEventListener(opt.type,fct,!1);else if(elt.attachEvent)elt.attachEvent('on'+opt.type,fct);else elt['on'+opt.type]=fct};keyboardManagerService.unbind=function(label){label=label.toLowerCase();var binding=keyboardManagerService.keyboardEvent[label];delete(keyboardManagerService.keyboardEvent[label])
if(!binding)return;var type=binding.event,elt=binding.target,callback=binding.callback;if(elt.detachEvent)elt.detachEvent('on'+type,callback);else if(elt.removeEventListener)elt.removeEventListener(type,callback,!1);else elt['on'+type]=!1};return keyboardManagerService}]);archDB.factory('socket',['$rootScope','$http','apiUrl',function($rootScope,$http,apiUrl){var socket,object={on:function(){},emit:function(){}};try{socket=io.connect('//'+window.location.host+':8080',{'auto connect':!1});$http.get(apiUrl('me'),{async:!1,params:{channels:!0}}).success(function(response){socket.socket.options.query=$.param(response);socket.socket.connect()})}catch(err){if(window.console)console.log(err);return object}
object.on=function(eventName,callback){socket.on(eventName,function(){var args=arguments;$rootScope.$apply(function(){callback.apply(socket,args)})})};object.emit=function(eventName,data,callback){socket.emit(eventName,data,function(){var args=arguments;$rootScope.$apply(function(){if(callback){callback.apply(socket,args)}})})};return object}]);archDB.service('$DesktopNotification',['$rootScope','ngAudio','$AuthUser',function($rootScope,ngAudio,$AuthUser){var self=this;self.notify=window.notify;self.isSupported=self.notify.isSupported;$rootScope.$on('$AuthUser loaded',function(response){if(response.mute_sounds=='1'){ngAudio.toggleMuteAll()}});self.hasPermission=function(){return self.notify.permissionLevel()==self.notify.PERMISSION_GRANTED?!0:!1};self.requestPermission=function(){self.notify.requestPermission(function(){$rootScope.$apply()})};self.show=function(title,body,icon,play_sound){if(!title)title='Notification';if(!body)body='Example';if(!icon)icon='alert.ico';if(!self.hasPermission&&self.isSupported)
self.requestPermission();self.notify.createNotification(title,{body:body,icon:icon});if(play_sound)ngAudio.play('/audio/notification.mp3');}}]);archDB.service('$AuthUser',['apiUrl','$http','$rootScope',function(apiUrl,$http,$rootScope){var self=this;self.data={};self.loaded=!1;self.load=function(){$http.get(apiUrl('me')).success(function(response){self.data=response;self.loaded=!0;$rootScope.$broadcast('$AuthUser loaded',self.data)})};self.getUser=function(){return self.data};self.load()}]);archDB.service('TaskTypes',[function(){var self=this;self.types=[{id:'repair',name:'Repair'},{id:'support',name:'Remote Support'},{id:'onsite',name:'Onsite'},{id:'sale_lead',name:'Sale Lead'},{id:'order',name:'Order'}];self.getTypes=function(){return self.types}
self.getType=function(id){return _.find(self.types,function(el){return el.id==id})}}]);archDB.service('helpers',[function(){this.addZeroNumberPadding=function(number){return(number<10)?'0'+number:number}}]);archDB.controller('MenuSearchCtrl',['$scope','$timeout','$http','ErrorsHandler',function($scope,$timeout,$http,ErrorsHandler){$scope.hasResults=!1;$scope.results={};$scope.searching=!1;var searchTimeout;var minLength=2;var delay=300;$scope.hideResults=function(){$scope.hasResults=!1};$scope.search=function(query){if(searchTimeout)$timeout.cancel(searchTimeout);if(query.length<minLength){$scope.hideResults();return!1}
$scope.searching=!0;searchTimeout=$timeout(function(){$http.get('/search',{params:{q:query}}).success(function(response){$scope.results=response;$scope.searching=!1;$scope.hasResults=!0}).error(ErrorsHandler)},delay)}}]);archDB.controller('SettingsCtrl',['$scope','User','ErrorsHandler','$uibModalInstance','user',function($scope,User,ErrorsHandler,$uibModalInstance,user){$scope.data=user;$scope.success=!1;$scope.update=function(){$scope.$broadcast('uploadProfilePhoto',function(){User.update($scope.data,function(response){$scope.data=response;$scope.success=!0},ErrorsHandler)})};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);archDB.controller('SidebarCtrl',['$scope','$DesktopNotification','$uibModal',function($scope,$DesktopNotification,$uibModal){$scope.notifications=$DesktopNotification;$scope.showSettings=function(){var modalInstance=$uibModal.open({controller:'SettingsCtrl',templateUrl:'modals/settings.html',resolve:{user:function(){return $scope.user}}})}}]);var Accounts=angular.module('accounts',[]);Accounts.controller('AccountsCtrl',['$scope','$filter',function($scope,$filter){$scope.piechart={labels:[],data:[]};$scope.$watch('pieChartData',function(data){$scope.piechart.labels=['Repairs','Support','Onsite'];$scope.piechart.data=[data.repairs,data.support,data.onsite]},!0);$scope.linechart={labels:[],series:['Labour','Parts'],data:[]};$scope.$watch('lineChartData',function(data){$scope.linechart={labels:[],series:['Labour','Parts'],data:[[],[]]};_.each(data,function(el){var parts=parseFloat($filter('currency')(el.parts||0,'').replace(',','')).toFixed(2);$scope.linechart.labels.push(el.timestamp);$scope.linechart.data[0].push(el.labour);$scope.linechart.data[1].push(parts)})},!0)}]);Accounts.directive('accountsProjects',function(){return{restrict:'A',templateUrl:'accounts/projects.html',scope:{clientId:'@',clientType:'@'},controller:['$scope','Project','Invoice','ErrorsHandler','$paginator','Messenger','$http','apiUrl','$window','InvoiceBookingService',function($scope,Project,Invoice,ErrorsHandler,$paginator,Messenger,$http,apiUrl,$window,InvoiceBookingService){$paginator=angular.copy($paginator);$scope.paginator=$paginator;$scope.filters={};$scope.InvoiceService=InvoiceBookingService;$paginator.setOnPageChange(function(page){$scope.load(page)});$scope.$watch('filters',function(){$scope.load($paginator.getCurrentPage())},!0);$scope.load=function(page){if(!page)page=1;var filters=angular.extend({page:page,client:!0,user:!0,results:'accounts',summary:!0},$scope.filters);if($scope.clientId&&$scope.clientType){filters.client_id=$scope.clientId;filters.client_type=$scope.clientType}
$scope.loading=!0;Project.get(filters,function(response){$paginator.setFromResponse(response);$scope.projects=response.data;$scope.loading=!1},ErrorsHandler)};$scope.orderBy=function(name){if($scope.filters.orderBy&&$scope.filters.orderBy==name){$scope.filters.orderType=$scope.filters.orderType=='asc'?'desc':'asc'}else{$scope.filters.orderBy=name;$scope.filters.orderType='asc'}};$scope.save=function(project,_form){Project.update({id:project.id,invoice_number:project.invoice_number,paid_at:project.paid_at},function(response){Messenger("Project #"+project.id+' has been updated.');_form.$setPristine();angular.extend(project,response||{})},ErrorsHandler)};$scope.markPaid=function(project){var inv_no=project.invoice_number?project.invoice_number:1;Project.update({id:project.id,paid:1,invoice_number:inv_no},function(response){angular.extend(project,response||{});Messenger("Project #"+project.id+' has been marked as paid.')},ErrorsHandler)};$scope.load()}]}});Accounts.directive('accountsQueue',function(){return{restrict:'A',templateUrl:'accounts/queue.html',scope:{clientId:'@',clientType:'@'},controller:['$scope','Tasks','Invoice','ErrorsHandler','$paginator','Messenger','$http','apiUrl','$window',function($scope,Tasks,Invoice,ErrorsHandler,$paginator,Messenger,$http,apiUrl,$window){$paginator=angular.copy($paginator);$scope.paginator=$paginator;$scope.filters={};$paginator.setOnPageChange(function(page){$scope.load(page)});$scope.$watch('filters',function(){$scope.load($paginator.getCurrentPage())},!0);$scope.load=function(page){if(!page)page=1;var filters=angular.extend({page:page,client:!0,user:!0,show:'accounts',summary:!0,parts_total:!0},$scope.filters);if($scope.clientId&&$scope.clientType){filters.client_id=$scope.clientId;filters.client_type=$scope.clientType}
$scope.loading=!0;Tasks.get(filters,function(response){$paginator.setFromResponse(response);$scope.tasks=response.data;$scope.loading=!1},ErrorsHandler)};$scope.orderBy=function(name){if($scope.filters.orderBy&&$scope.filters.orderBy==name){$scope.filters.orderType=$scope.filters.orderType=='asc'?'desc':'asc'}else{$scope.filters.orderBy=name;$scope.filters.orderType='asc'}};$scope.save=function(task,_form){Tasks.update({id:task.id,invoice_number:task.invoice_number,paid_at:task.paid_at},function(response){Messenger("Task #"+task.id+' has been updated.');_form.$setPristine();angular.extend(task,response||{})},ErrorsHandler)};$scope.markPaid=function(task){Tasks.update({id:task.id,paid:1},function(response){angular.extend(task,response||{});Messenger("Task #"+task.id+' has been marked as paid.')},ErrorsHandler)};$scope.generateInvoice=function(task){$window.open('invoices#/invoices/new/'+task.id,'_blank')};$scope.showInvoice=function(task){$window.open('invoices#/invoices/view/'+task.invoice_number,'_blank')};$scope.printInvoice=function(task){var pdfUrl='/generate-invoice/'+task.invoice_number;$window.open(pdfUrl,'_blank')}
$scope.load()}]}});var Bookings=angular.module('bookings',[]);Bookings.config(['$routeProvider',function($routeProvider){$routeProvider.when('/bookings/repair',{controller:'BookingsRepairCtrl',templateUrl:'bookings/repair.html'});$routeProvider.when('/bookings/support',{controller:'BookingsSupportCtrl',templateUrl:'bookings/support.html'});$routeProvider.when('/bookings/message',{controller:'BookingsMessageCtrl',templateUrl:'bookings/message.html'});$routeProvider.when('/bookings/onsite',{controller:'BookingsOnsiteCtrl',templateUrl:'bookings/onsite.html'});$routeProvider.when('/bookings/order',{controller:'BookingsOrderCtrl',templateUrl:'bookings/order.html'});$routeProvider.when('/bookings/rma',{controller:'BookingsRmaCtrl',templateUrl:'bookings/rma.html'});$routeProvider.when('/bookings/project',{controller:'BookingsProjectCtrl',templateUrl:'bookings/project.html'});$routeProvider.when('/bookings/sale-lead',{controller:'BookingsSaleLeadCtrl',templateUrl:'bookings/sale-lead.html'});$routeProvider.when('/bookings/:clientType/:clientId',{controller:'BookingsMainCtrl'});$routeProvider.when('/bookings/:clientType/:clientId/project/:projectId',{controller:'BookingsMainCtrl'});$routeProvider.when('/projects/overview',{controller:'ProjectsOverviewCtrl',templateUrl:'/projects/overview.html'});$routeProvider.when('/projects/tasks',{controller:'ProjectsTasksCtrl',templateUrl:'/projects/tasks.html'});$routeProvider.when('/projects/stats',{controller:'ProjectsStatisticsCtrl',templateUrl:'/projects/statistics.html'});$routeProvider.when('/projects/files',{controller:'ProjectsFilesCtrl',templateUrl:'/projects/files.html'})}]);Bookings.factory('Tasks',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){methods.promote={method:'POST',url:apiUrl('task/promote/:taskId'),params:{taskId:'@id'}};methods.updateTemplateItem={method:'POST',url:apiUrl('task/update-template-item/:taskId'),params:{taskId:'@id'}};methods.generateInvoice={method:'POST',url:apiUrl('task/generate-invoice/:taskId'),params:{taskId:'@id'}};methods.createExchangeCalendarEvent={method:'POST',url:apiUrl('task/create-exchange-calendar-event/:taskId'),params:{taskId:'@id'}};methods.sendEmail={method:'POST',url:apiUrl('task/send-email/:taskId'),params:{taskId:'@id'}};methods.sendSMS={method:'POST',url:apiUrl('task/send-sms/:taskId'),params:{taskId:'@id'}};methods.addKeychain={method:'POST',url:apiUrl('task/addKeychain/:taskId'),params:{taskId:'@id'}};methods.updateTaskAndQa={method:'POST',url:apiUrl('task/update-task-and-qa/:taskId'),params:{taskId:'@id'}};var Tasks=$resource(apiUrl('task/:taskId'),{taskId:'@id'},methods);angular.extend(Tasks.prototype,_prototype);return Tasks}]);Bookings.factory('Repair',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Repair=$resource(apiUrl('tasks/repairs/:taskId'),{taskId:'@id'},methods);angular.extend(Repair.prototype,_prototype);return Repair}]);Bookings.factory('Support',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Support=$resource(apiUrl('tasks/support/:taskId'),{taskId:'@id'},methods);angular.extend(Support.prototype,_prototype);return Support}]);Bookings.factory('Onsite',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Onsite=$resource(apiUrl('tasks/onsite/:taskId'),{taskId:'@id'},methods);angular.extend(Onsite.prototype,_prototype);return Onsite}]);Bookings.factory('Order',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Order=$resource(apiUrl('tasks/orders/:taskId'),{taskId:'@id'},methods);angular.extend(Order.prototype,_prototype);return Order}]);Bookings.factory('Message',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Message=$resource(apiUrl('messages/:messageId'),{messageId:'@id'},methods);angular.extend(Message.prototype,_prototype);return Message}]);Bookings.factory('Rma',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Rma=$resource(apiUrl('tasks/rma/:taskId'),{taskId:'@id'},methods);angular.extend(Rma.prototype,_prototype);return Rma}]);Bookings.factory('RmaItem',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var RmaItem=$resource(apiUrl('tasks/rma/:taskId/items/:itemId'),{taskId:'@task_id',itemId:'@id'},methods);angular.extend(RmaItem.prototype,_prototype);return RmaItem}]);Bookings.factory('Comment',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Comment=$resource(apiUrl('comments/:commentId'),{commentId:'@id'},methods);angular.extend(Comment.prototype,_prototype);return Comment}]);Bookings.factory('Part',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){methods.requestPart={method:'POST',url:apiUrl('parts/request-part'),params:{partId:'@id'}};methods.received={method:'POST',url:apiUrl('parts/:partId/received'),params:{partId:'@id'}};var Part=$resource(apiUrl('parts/:partId'),{partId:'@id'},methods);angular.extend(Part.prototype,_prototype);return Part}]);Bookings.factory('Build',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){methods.requestBuild={method:'POST',url:apiUrl('builds/request-build'),params:{buildId:'@id'}};methods.received={method:'POST',url:apiUrl('builds/:buildId/received'),params:{buildId:'@id'}};var Build=$resource(apiUrl('builds/:buildId'),{buildId:'@id'},methods);angular.extend(Build.prototype,_prototype);return Build}]);Bookings.factory('Manufacturer',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Manufacturer=$resource(apiUrl('manufacturers/:manufacturerId'),{manufacturerId:'@id'},methods);Manufacturer.getSearchUrl=function(){return apiUrl('manufacturers?name=')};angular.extend(Manufacturer.prototype,_prototype);return Manufacturer}]);Bookings.factory('ModelNumber',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var ModelNumber=$resource(apiUrl('model-numbers/:modelNumberId'),{modelNumberId:'@id'},methods);ModelNumber.getSearchUrl=function(){return apiUrl('model-numbers?name=')};angular.extend(ModelNumber.prototype,_prototype);return ModelNumber}]);Bookings.factory('Delivery',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Delivery=$resource(apiUrl('tasks/delivery/:deliveryId'),{deliveryId:'@id'},methods);angular.extend(Delivery.prototype,_prototype);return Delivery}]);Bookings.factory('Device',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Device=$resource(apiUrl('devices/:deviceId'),{deviceId:'@id'},methods);angular.extend(Device.prototype,_prototype);return Device}]);Bookings.factory('Project',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Project=$resource(apiUrl('project/:projectId'),{projectId:'@id'},methods);angular.extend(Project.prototype,_prototype);return Project}]);Bookings.factory('SaleLead',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var SaleLead=$resource(apiUrl('tasks/sale-lead/:saleLeadId'),{saleLeadId:'@id'},methods);angular.extend(SaleLead.prototype,_prototype);return SaleLead}]);Bookings.factory('Files',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){methods.download={method:'GET',url:apiUrl('files/download/:fileId'),params:{fileId:'@id'}};var Files=$resource(apiUrl('files/:objectType/:objectId/:fileId'),{fileId:'@id',objectType:'@object_type',objectId:'@object_id'},methods);angular.extend(Files.prototype,_prototype);return Files}]);Bookings.factory('Template',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Template=$resource(apiUrl('templates/:templateId'),{templateId:'@id'},methods);Template.getSearchUrl=function(){return apiUrl('templates?query=')};angular.extend(Template.prototype,_prototype);return Template}]);Bookings.factory('TemplateItem',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var TemplateItem=$resource(apiUrl('template-items/:templateItemId'),{templateItemId:'@id'},methods);angular.extend(TemplateItem.prototype,_prototype);return TemplateItem}]);Bookings.factory('TaskFeedback',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var TaskFeedback=$resource(apiUrl('feedback/:taskFeedbackId'),{taskFeedbackId:'@id'},methods);angular.extend(TaskFeedback.prototype,_prototype);return TaskFeedback}]);Bookings.factory('TimeTracking',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){methods.start={method:'POST',url:apiUrl('time-tracking/start')};methods.stop={method:'POST',url:apiUrl('time-tracking/stop')};var TimeTracking=$resource(apiUrl('time-tracking/:timeTrackingId'),{timeTrackingId:'@id'},methods);angular.extend(TimeTracking.prototype,_prototype);return TimeTracking}]);Bookings.factory('Keychain',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){methods.types={method:'GET',url:apiUrl('keychains/types')};methods.addToTask={method:'GET',url:apiUrl('keychains/add')};var Keychain=$resource(apiUrl('keychains/:keychainId'),{keychainId:'@id'},methods);angular.extend(Keychain.prototype,_prototype);return Keychain}]);Bookings.directive('ngReallyClick',[function(){return{restrict:'A',link:function(scope,element,attrs){element.bind('click',function(){var message=attrs.ngReallyMessage;if(message&&confirm(message)){scope.$apply(attrs.ngReallyClick)}})}}}]);Bookings.factory('License',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var License=$resource(apiUrl('licenses/:licenseId'),{licenseId:'@id'},methods);angular.extend(License.prototype,_prototype);return License}]);Bookings.factory('BookingsLocationManager',['$ClientsManager',function($ClientsManager){var $this=this;var findUser=function(user_id){var user;_.find($ClientsManager.getClient().sites,function(site){if(!site)return!1;user=_.find(site.users,function(u){return u.id==user_id});return user});return user};return{getUsers:function(site_id){if(!site_id)return[];var site=_.find($ClientsManager.getClient().sites,function(s){return s.id==site_id});if(site&&site.users)
return site.users;else return[]},findUser:findUser,getUserDDI:function(user_id){if(!user_id)return'';var user=findUser(user_id);if(!user){return''}else{if(user.phone&&user.phone.length>0){return user.phone}else{return user.mobile}}}}}]);Bookings
Bookings.directive('saveDdi',function(){return{restrict:'A',controller:['$scope','ErrorsHandler','SiteUser',function($scope,ErrorsHandler,SiteUser){$scope.updateDDI=function(number,site_id,site_user_id){SiteUser.update({phone:number,site_id:site_id,id:site_user_id},function(response){$scope.changed_ddi=!1},ErrorsHandler)}}]}});Bookings.service('repairsForm',['ErrorsHandler','Messenger','$location','Repair','$ClientsManager','$window','archAuthService','$uibModal','socket','$http',function(ErrorsHandler,Messenger,$location,Repair,$ClientsManager,$window,auth,$uibModal,socket,$http){var $this=this;$this.options={showErrors:!0,showMessage:!0,showPrintView:!0,callback:function(){},message:"I've successfully created the repair!"};$this.modal={id:null,showPrintView:!1};$this.data={client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType(),templates:[],};$this.clearForm=function(){$this.data={client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType()}};$this.save=function(){auth.confirm(function(status,helper){if(status){errors_handler=($this.options.showErrors)?ErrorsHandler:null;$this.data.client_id=$ClientsManager.getClient().id;$this.data.client_type=$ClientsManager.getClientType();Repair.save($this.data,function(res){if(res.success){$this.data.id=res.data.id;$window.onbeforeunload=function(){};if($this.options.showMessage)Messenger($this.options.message);if($this.options.callback)$this.options.callback(res.data);$window.location.href="/bookings/confirmation-sheet/"+res.data.id}},function(a,b){helper.loading=!1;errors_handler(a,b)})}})};$this.saveOnly=function(){auth.confirm(function(status){if(status){$this.data.client_id=$ClientsManager.getClient().id;$this.data.client_type=$ClientsManager.getClientType();Repair.save($this.data,function(res){if(res.success){$this.data.id=res.data.id;$window.onbeforeunload=function(){};$window.location.href="/tasks/repairs/"+res.data.id}},ErrorsHandler)}})}}]);Bookings.service('supportForm',['ErrorsHandler','Messenger','$location','Support','$ClientsManager','$window','archAuthService',function(ErrorsHandler,Messenger,$location,Support,$ClientsManager,$window,auth){var $this=this;$this.options={showErrors:!0,showMessage:!0,callback:function(){},message:"I've successfully created the support ticket!"};$this.modal={id:null,showPrintView:!1};$this.data={client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType()};$this.clearForm=function(){$this.data={client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType()}};$this.save=function(){auth.confirm(function(status,helper){if(status){errors_handler=($this.options.showErrors)?ErrorsHandler:null;$this.data.client_id=$ClientsManager.getClient().id;$this.data.client_type=$ClientsManager.getClientType();Support.save($this.data,function(res){if(res.success){$window.onbeforeunload=function(){};if($this.options.showMessage)Messenger($this.options.message);if($this.options.callback)$this.options.callback(res.data);window.location.href="/tasks/support/"+res.data.id}},function(a,b){helper.loading=!1;errors_handler(a,b)})}})}}]);archDB.filter('contains',function(){return function(array,indexToFind){var truth=!1;angular.forEach(array,function(value,key){if(key===indexToFind){truth=!0}});return truth}});Bookings.directive('assignTask',function(){return{templateUrl:'bookings/projects/assign-task.html',replace:!0,controller:'assignTaskCtrl',link:function($scope,$element,$attrs){$element.modal({show:!1})}}});Bookings.directive('attachments',function(){return{restrict:'AE',templateUrl:'widgets/attachments.html',controller:'AttachmentsCtrl',scope:{'task':'=','type':'@'}}});Bookings.factory('archAuthService',['$uibModal','$timeout',function($uibModal,$timeout){return{callback:null,show:!1,confirm:function(callback){var modal=$uibModal.open({templateUrl:'modals/auth.html',controller:'archAuthCtrl',backdrop:'static'});modal.opened.then(function(){$timeout(function(){$('#authPassword').focus()},50)});this.callback=callback},respond:function(result,helper){if(typeof this.callback=='function')this.callback(result,helper);}}}]).directive('archAuth',function(){return{restrict:'A',replace:!0,scope:{}}});Bookings.directive('bookingPasswords',function(){return{controller:'BookingPasswordsCtrl',templateUrl:'bookings/passwords.html',scope:{'task':'=',}}});Bookings.factory('BuildsService',function(){var o={total:0,tax:0,subtotal:0,builds:[]};o.setTotal=function(amount){o.total=amount};o.getTotal=function(){return o.total.toFixed(2)};o.setTax=function(amount){o.tax=amount};o.getTax=function(){return o.tax};o.setSubtotal=function(amount){o.subtotal=amount};o.getSubtotal=function(){return o.subtotal};o.setBuilds=function(b){angular.copy(b,o.builds)};o.hasBuildsOnOrder=function(){var on_order=!1;_.each(o.builds,function(p){if(p.quantity_on_order>0){on_order=!0;return}});return on_order};return o});Bookings.directive('widgetBuilds',function(){return{restrict:'A',templateUrl:'widgets/builds.html',controller:'widgetBuildsCtrl',scope:{'related':'=widgetBuilds','related_type':'@relatedType'}}});Bookings
Bookings.directive('widgetClient',function(){return{restrict:'A',templateUrl:'widgets/client.html',controller:'widgetClientCtrl',scope:{'task':'=widgetClient'}}});Bookings.factory("CommentsService",["Comment","$q","ErrorsHandler",function(Comment,$q,ErrorsHandler){var o={comments:[]};o.setComments=function(c,pages){angular.copy(c,o.comments)};o.getComments=function(){return o.comments};o.addComment=function(data){var deferred=$q.defer();Comment.save(data,function(res){if(!res.id){alert("Something went wrong! Try to refresh the page and enter the comment again");return!1}
data.current_page=0;o.loadComments(data,function(){});deferred.resolve(res)},ErrorsHandler);return deferred.promise};o.loadComments=function(data,cb){o.pages=data.pages||o.pages;o.current_page=data.current_page||o.current_page;Comment.get({type:data.related_type,id:data.related_id,page:o.current_page},function(res){o.setComments(res.data,res.last_page);o.pages=res.last_page;o.currentPage=res.current_page;cb(o)},ErrorsHandler)};return o}]);Bookings
Bookings.directive("widgetComments",function(){return{restrict:"A",templateUrl:"widgets/comments.html",controller:"widgetCommentsCtrl",scope:{data:"=widgetComments",type:"@commentType"}}});Bookings.directive('createNonExitingBuild',function(){return{restrict:'A',controller:'CreateNonExitingBuildCtrl',templateUrl:'modals/create-non-existing-build.html',replace:!0,scope:{builds:'=',related:'=',related_type:'@relatedType',virtualMode:'=virtualMode'}}});Bookings
Bookings.directive('customerDevices',function(){return{restrict:'A',controller:'CustomerDevicesCtrl',templateUrl:'bookings/repairs/devices.html',scope:{'selected_devices':'=customerDevices'},link:function($scope,$element,$attrs){if($attrs.view&&$attrs.view!=='')$scope.view=$attrs.view}}});Bookings.factory('CustomerNotificationsService',function(){var o={notifications:[]};o.setNotifications=function(n){angular.copy(n,o.notifications)};return o});Bookings.filter('cut',function(){return function(value,wordwise,max,tail){if(!value)return'';max=parseInt(max,10);if(!max)return value;if(value.length<=max)return value;value=value.substr(0,max);if(wordwise){var lastspace=value.lastIndexOf(' ');if(lastspace!=-1){value=value.substr(0,lastspace)}}
return value+(tail||' …')}}).directive('widgetCustomerNotifications',function(){return{restrict:'A',templateUrl:'widgets/customer-notifications.html',controller:'widgetCustomerNotificationsCtrl',scope:{'task':'='}}});Bookings.directive('customerPrebooks',function(){return{restrict:'A',controller:'CustomerPrebooksCtrl',templateUrl:'bookings/repairs/prebooks.html',}});Bookings.directive('defaultLocation',['$ClientsManager',function(client){return{require:'ngModel',link:function($scope,$element,$attrs,$ctrl){$scope.$watch(function(){return client.client},function(value){if(!value)return!1;if(value.sites&&value.sites.length>0){var site=value.sites[0];$scope.data.site_id=site.id;if(site.users){$scope.data.site_user_id=site.users[0].id}}},!0)}}}]);Bookings.directive('followupTask',function(){return{templateUrl:'bookings/followup-task.html',scope:{followupId:'@',users:'=',task:'=',},controller:['$scope','Page','$timeout','Messenger','Tasks','ErrorsHandler','$uibModal','CommentsService',function($scope,Page,$timeout,Messenger,Tasks,ErrorsHandler,$uibModal,CommentsService){$scope.modified=!1;$scope.initialized=!1;$scope.$watch('followupId',function(id){if(id){Page.setTitle(id);Tasks.get({taskId:$scope.followupId},function(response){$scope.mainTask=response},ErrorsHandler)}});$scope.updateValue=function(params){angular.extend(params,{id:$scope.mainTask.id,updated_at:$scope.mainTask.updated_at});Tasks.update(params,function(response){angular.extend($scope.mainTask,response||{});Messenger("I've succesfully saved the job!")},ErrorsHandler)};$scope.changeFollowupAproval=function(value){var params={id:$scope.mainTask.id,updated_at:$scope.mainTask.updated_at,satisfied_client:value,status_id:10,completed:1};Tasks.update(params,function(response){angular.extend($scope.mainTask,response||{});Messenger("I've succesfully saved the job!")},ErrorsHandler)};$scope.aproveFollowup=function(){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/followup-aproved.html',controller:'ReasonModalCtrl'});modalInstance.result.then(function(reason){reason=reason?reason:'No comment left.';$scope.addComment({message:reason,style:'success'});$scope.changeFollowupAproval(!0)})};$scope.declineFollowup=function(){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/followup-declined.html',controller:'ReasonModalCtrl'});modalInstance.result.then(function(reason){$scope.addComment({message:reason,style:'failed'});$scope.changeFollowupAproval(!1)})};$scope.addComment=function(value){var data={related_id:$scope.task.id,related_type:'task',type:$scope.mainTask.type,message:value.message,public:!1,send_email:!1,style:value.style};CommentsService.addComment(data)}}],link:function(scope,el,attr){},}});Bookings.factory('locationHelper',function(){var o={};o.getSite=function(sites,id){return _.find(sites,function(el){return el.id==id})};return o});Bookings.directive('widgetMap',function(){return{restrict:'A',controller:'widgetMapCtrl',scope:{'task':'=widgetMap','address':'='},transclude:!0,link:function(scope,element,attrs,ctrl,transclude){transclude(scope,function(clone){element.append(clone)})}}});Bookings.factory('PartsService',function(){var o={total:0,tax:0,subtotal:0,parts:[]};o.setTotal=function(amount){o.total=amount};o.getTotal=function(){return o.total.toFixed(2)};o.setTax=function(amount){o.tax=amount};o.getTax=function(){return o.tax};o.setSubtotal=function(amount){o.subtotal=amount};o.getSubtotal=function(){return o.subtotal};o.setParts=function(p){angular.copy(p,o.parts)};o.addPart=function(p){var index=_.findIndex(o,function(el){return el.id===part.id});if(index)
o.parts[index]=p;else o.parts.push(p)}
o.hasPartsOnOrder=function(){var on_order=!1;_.each(o.parts,function(p){if(p.on_order>0){on_order=!0;return}});return on_order};return o})
Bookings.directive('widgetParts',function(){return{restrict:'A',templateUrl:'widgets/parts.html',controller:'widgetPartsCtrl',scope:{'related':'=widgetParts','related_type':'@relatedType'}}});Bookings.directive('projectTasks',function(){return{restrict:'AE',templateUrl:'widgets/project-tasks.html',controller:'ProjectTasksCtrl',scope:{'projectId':'=projectTasks'}}});Bookings.directive('promoteProjectBtn',['$uibModal',function($uibModal){return{scope:{task_id:'=promoteProjectBtn',redirect_url:'@redirectUrl'},templateUrl:'bookings/modals/promote.html',link:function($scope,$element,$attrs){$scope.promote=function(){$uibModal.open({templateUrl:'modals/promote-project.html',controller:'PromoteProjectCtrl'})}}}}]);Bookings
Bookings.directive('qaTask',function(){return{templateUrl:'bookings/qa-task.html',scope:{qaId:'@',users:'=',task:'=',loadData:'&'},controller:['$scope','Page','$timeout','Messenger','Tasks','ErrorsHandler','$uibModal','CommentsService',function($scope,Page,$timeout,Messenger,Tasks,ErrorsHandler,$uibModal,CommentsService){$scope.modified=!1;$scope.initialized=!1;$scope.$watch('qaId',function(id){if(id){Page.setTitle(id);Tasks.get({taskId:$scope.qaId},function(response){$scope.mainTask=response},ErrorsHandler)}});$scope.updateValue=function(params){angular.extend(params,{id:$scope.mainTask.id,updated_at:$scope.mainTask.updated_at});Tasks.update(params,function(response){angular.extend($scope.mainTask,response||{});Messenger("I've succesfully saved the job!")},ErrorsHandler)};$scope.customerCalled=function(){var params={id:$scope.mainTask.id,updated_at:$scope.mainTask.updated_at,needs_calling:!1};Tasks.update(params,function(response){angular.extend($scope.mainTask,response||{});$scope.addComment({type:'call'});Messenger("I've succesfully saved the job!")},ErrorsHandler)};$scope.changeQAAproval=function(value){var params={id:$scope.mainTask.id,needs_calling:($scope.task.type=='repair'&&value.passed_qa)?!0:!1,qa_updated_at:$scope.mainTask.updated_at,related_task_updated_at:$scope.task.updated_at,passed_qa:value.passed_qa,qa_task_status_id:value.qa_task_status_id,related_task_status_id:value.task_status_id,};Tasks.updateTaskAndQa(params,function(response){angular.extend($scope.mainTask,response||{});$scope.addComment({message:value.comment,style:value.style,type:'qa'});Messenger("I've succesfully saved the job!");$scope.loadData()},ErrorsHandler)};$scope.quickAproveQA=function(){var params={passed_qa:!0,qa_task_status_id:10,task_status_id:10,comment:'No comment left.',style:'success',};$scope.changeQAAproval(params)};$scope.aproveQA=function(){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/qa-aproved.html',controller:'ReasonModalCtrl',size:'lg',});modalInstance.result.then(function(reason){var params={passed_qa:!0,qa_task_status_id:10,task_status_id:10,comment:reason?reason:'No comment left.',style:'success',};$scope.changeQAAproval(params);$scope.sendNotification()})};$scope.declineQA=function(){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/qa-declined.html',controller:'ReasonModalCtrl'});modalInstance.result.then(function(reason){var params={passed_qa:!1,qa_task_status_id:10,task_status_id:4,comment:reason,style:"failed",};$scope.changeQAAproval(params)})};$scope.addComment=function(value){var data={related_id:$scope.task.id,related_type:'task',type:value.type,message:value.message,public:!0,send_email:!1,style:value.style};CommentsService.addComment(data)};$scope.sendNotification=function(){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/new-customer-notification.html',controller:'CustomerNotificationCtrl',windowClass:'modal-fixed',resolve:{taskId:function(){return $scope.task.id},notification:null,}})}}]}});Bookings.directive('pickupTask',function(){return{templateUrl:'bookings/pickup-task.html',scope:{users:'=',task:'='},controller:['$scope','Tasks','ErrorsHandler','Messenger','CommentsService','$uibModal',function($scope,Tasks,ErrorsHandler,Messenger,CommentsService,$uibModal){$scope.$watch('task',function(newVal){if(newVal){$scope.data=newVal}});$scope.updateValue=function(params){if(!$scope.data)return;angular.extend(params,{id:$scope.data.id,updated_at:$scope.data.updated_at});Tasks.update(params,function(response){angular.extend($scope.data,response||{});Messenger("Pickup information updated successfully!")},ErrorsHandler)};$scope.getUserName=function(userId){if(!userId)return'Not assigned';var user=_.find($scope.users,function(u){return u.id==userId});return user?user.name:'Not assigned'};$scope.markPickedUp=function(){if(!$scope.data){Messenger("Error: Task data not loaded",'error');return}
var params={id:$scope.data.id,updated_at:$scope.data.updated_at,picked_up_at:new Date().toISOString()};Tasks.update(params,function(response){angular.extend($scope.data,response||{});var commentData={related_id:$scope.data.id,related_type:'task',message:'Item picked up successfully',public:!0,send_email:!1,style:'success'};CommentsService.addComment(commentData);Messenger("Marked as picked up!");location.reload()},ErrorsHandler)};$scope.cancelPickup=function(){if(!$scope.data){Messenger("Error: Task data not loaded",'error');return}
var modalInstance=$uibModal.open({templateUrl:'bookings/modals/cancelled-reason.html',controller:'ReasonModalCtrl'});modalInstance.result.then(function(reason){var params={id:$scope.data.id,updated_at:$scope.data.updated_at,needs_pickup:!1,pickup_by:null};Tasks.update(params,function(response){angular.extend($scope.data,response||{});var commentData={related_id:$scope.data.id,related_type:'task',message:'Pickup cancelled: '+reason,public:!0,send_email:!1,style:'failed'};CommentsService.addComment(commentData);Messenger("Pickup cancelled!");location.reload()},ErrorsHandler)})}}]}});Bookings.directive('deliveryTask',function(){return{templateUrl:'bookings/delivery-task.html',scope:{users:'=',task:'='},controller:['$scope','Tasks','ErrorsHandler','Messenger','CommentsService','$uibModal',function($scope,Tasks,ErrorsHandler,Messenger,CommentsService,$uibModal){$scope.$watch('task',function(newVal){if(newVal){$scope.data=newVal}});$scope.updateValue=function(params){if(!$scope.data)return;angular.extend(params,{id:$scope.data.id,updated_at:$scope.data.updated_at});Tasks.update(params,function(response){angular.extend($scope.data,response||{});Messenger("Delivery information updated successfully!")},ErrorsHandler)};$scope.getUserName=function(userId){if(!userId)return'Not assigned';var user=_.find($scope.users,function(u){return u.id==userId});return user?user.name:'Not assigned'};$scope.markDelivered=function(){if(!$scope.data){Messenger("Error: Task data not loaded",'error');return}
var params={id:$scope.data.id,updated_at:$scope.data.updated_at,delivered:1};Tasks.update(params,function(response){angular.extend($scope.data,response||{});var commentData={related_id:$scope.data.id,related_type:'task',message:'Item delivered successfully',public:!0,send_email:!1,style:'success'};CommentsService.addComment(commentData);Messenger("Marked as delivered!");location.reload()},ErrorsHandler)};$scope.cancelDelivery=function(){if(!$scope.data){Messenger("Error: Task data not loaded",'error');return}
var modalInstance=$uibModal.open({templateUrl:'bookings/modals/cancelled-reason.html',controller:'ReasonModalCtrl'});modalInstance.result.then(function(reason){var params={id:$scope.data.id,updated_at:$scope.data.updated_at,needs_delivery:!1,delivered_by:null};Tasks.update(params,function(response){angular.extend($scope.data,response||{});var commentData={related_id:$scope.data.id,related_type:'task',message:'Delivery cancelled: '+reason,public:!0,send_email:!1,style:'failed'};CommentsService.addComment(commentData);Messenger("Delivery cancelled!");location.reload()},ErrorsHandler)})}}]}});Bookings
Bookings.directive('quickNewTask',[function(){return{templateUrl:'templates/quick-new-task.html',replace:!0,restrict:'AEC',scope:!0,controller:'QuickNewTaskCtrl'}}]);Bookings.directive('quickTask',['Tasks','Messenger','ErrorsHandler','Message','Onsite',function(Tasks,Messenger,ErrorsHandler,Message,Onsite){return{restrict:'AE',scope:{client:'=',clientType:'@',projectId:'@',site:'=',siteUser:'=',onCreate:'&',users:'=',open:'&'},templateUrl:'templates/quick-task.html',link:function($scope,$element,$attrs){$element.parents('.popover:first').addClass('large');var textarea=$element.find('textarea');textarea.focus();$scope.add=function(){if(!$scope.tags.task){$scope.quickAddError='You must select a task type!';textarea.focus();return!1}
if(!$scope.tags.client&&!$scope.client){$scope.quickAddError='No client is selected!';textarea.focus();return!1}
if($scope.tags.task.id=='onsite'&&!$scope.tags.date){$scope.quickAddError='You need to specify a date when booking an onsite!';textarea.focus();return!1}
if($scope.tags.task.id=='onsite'&&!$scope.tags.time){$scope.quickAddError='You need to specify a time when booking an onsite!';textarea.focus();return!1}
var clean_title=$scope.quick_task;if($scope.tags.task)clean_title=clean_title.replace('^'+$scope.tags.task.name,'');if($scope.tags.client)clean_title=clean_title.replace('#'+$scope.tags.client.name,'');if($scope.tags.staff)clean_title=clean_title.replace('@'+$scope.tags.staff.name,'');if($scope.tags.date)clean_title=clean_title.replace('$'+$scope.tags.date.name,'');if($scope.tags.time)clean_title=clean_title.replace('!'+$scope.tags.time,'');var data={type:$scope.tags.task.id,title:clean_title,problem:clean_title,message:clean_title,project_id:$scope.projectId,severity_id:2};if(!$scope.client){data.client_id=$scope.tags.client.id;data.client_type=$scope.tags.client.hasOwnProperty('contract_id')?'client':'customer'}else{data.client_id=$scope.client.id;data.client_type=$scope.clientType}
if($scope.tags.staff)data.assigned_to=$scope.tags.staff.id;if($scope.tags.task.id=='onsite'){data.onsite_start=$scope.tags.date.value.format("YYYY-MM-DD")+' '+$scope.tags.time+':00';data.onsite_end=moment(data.onsite_start).add('hours',1).format("YYYY-MM-DD HH:mm:ss")}
if(data.client_type=='client'){if(($scope.tags.client&&!$scope.tags.client.sites)&&!$scope.site){$scope.quickAddError='The client is missing the site!';textarea.focus();return!1}
if($scope.site&&$scope.siteUser){data.site_id=$scope.site.id;data.site_user_id=$scope.siteUser.id}else{data.site_id=$scope.tags.client.sites[0].id;data.site_user_id=$scope.tags.client.sites[0].users[0].id}}
var model;switch($scope.tags.task.id){case 'message':model=Message;break;case 'onsite':model=Onsite;break;default:model=Tasks}
model.save(data,function(response){if(typeof $scope.onCreate=='function')$scope.onCreate({data:response});Messenger("Task created succesfully");$scope.tags={};$scope.quick_task=null;$scope.quickAddError=null},ErrorsHandler)}}}}]);Bookings.directive('requestExistingPart',function(){return{restrict:'A',controller:'RequestExistingPartCtrl',templateUrl:'modals/request-existing-part.html',replace:!0,scope:{parts:'=requestExistingPart',related:'=',related_type:'@relatedType',virtualMode:'=virtualMode'}}});Bookings.directive('requestNonExistingPart',function(){return{restrict:'A',controller:'RequestNonExistingPartCtrl',templateUrl:'modals/request-non-existing-part.html',replace:!0,scope:{parts:'=requestNonExistingPart',related:'=',related_type:'@relatedType',virtualMode:'=virtualMode'}}});Bookings.directive('widgetSla',function(){return{restrict:'A',templateUrl:'widgets/sla.html',controller:'widgetSlaCtrl',scope:{'task':'=widgetSla'}}});Bookings.factory('PasswordsService',['Keychain',function(Keychain){var o={passwords:{},types:{},task_passwords:{}};o.setPasswords=function(p){p=_.groupBy(p,function(password){return password.device_id?password.device_id:'General'});angular.copy(p,o.passwords)};o.addPassword=function(item){if(!o.passwords.hasOwnProperty('General'))
o.passwords.General=[];if(item.device_id==null){o.passwords.General.push(item);return}
if(!o.passwords.hasOwnProperty(item.device_id)){o.passwords[item.device_id]=[]}
o.passwords[item.device_id].push(item)};o.deletePassword=function(item){_.each(o.passwords,function(k,i){o.passwords[i]=_.reject(k,function(el){return el===item})})};o.loadKeychainTypes=function(){Keychain.types({},function(response){o.types=response.data})};angular.element(document).ready(function(){o.loadKeychainTypes()});return o}]);Bookings.directive('widgetPasswords',function(){return{restrict:'A',templateUrl:'widgets/task-passwords.html',controller:'widgetPasswordsCtrl',scope:{'task':'='}}});Bookings.directive('taskTemplates',function(){return{restrict:'A',controller:'TaskTemplatesCtrl',templateUrl:'widgets/templates.html',scope:{'task':'=taskTemplates'}}});Bookings.directive('timeTracking',[function(){return{templateUrl:'bookings/time-tracking.html',restrict:'A',scope:{'taskId':'@'},controller:['$scope','TimeTracking','ErrorsHandler','Messenger','$interval','helpers',function($scope,TimeTracking,ErrorsHandler,Messenger,$interval,helpers){$scope.activeSession=null;$scope.history=[];$scope.showHistory=!0;$scope.load=function(){TimeTracking.get({task_id:$scope.taskId},function(response){$scope.history=response.data;$scope.activeSession=_.find(response.data,function(el){return el.active==!0})},ErrorsHandler)};$scope.start=function(){if($scope.activeSession)
Messenger("The timer is already running");TimeTracking.start({task_id:$scope.taskId},function(response){$scope.activeSession=response;$scope.history.push(response)},ErrorsHandler)};$scope.stop=function(){if(!$scope.activeSession)
Messenger("The timer is not running at the moment");TimeTracking.stop({task_id:$scope.taskId},function(response){$scope.activeSession=null;$scope.duration=null;if($scope.timer)$interval.cancel($scope.timer);_.each($scope.history,function(el){if(el.id==response.id){angular.extend(el,response||{})}})},ErrorsHandler)};$scope.getTimer=function(){if(!$scope.activeSession)
return'00:00:00';if(!$scope.duration){$scope.duration=moment.duration($scope.activeSession.duration*1000);$scope.timer=$interval(function(){$scope.duration.add(1,'second')},1000)}
return helpers.addZeroNumberPadding($scope.duration.get('hours'))+':'+helpers.addZeroNumberPadding($scope.duration.get('minutes'))+':'+helpers.addZeroNumberPadding($scope.duration.get('seconds'))};$scope.getTotalTime=function(){var seconds=0;_.each($scope.history,function(el){if(el!=$scope.activeSession){seconds+=el.duration}});seconds+=$scope.duration?$scope.duration.asSeconds():0;var duration=moment.duration(seconds*1000);return helpers.addZeroNumberPadding(duration.get('hours'))+':'+helpers.addZeroNumberPadding(duration.get('minutes'))+':'+helpers.addZeroNumberPadding(duration.get('seconds'))}}],link:function($scope){$scope.$watch('taskId',function(v){if(v)$scope.load();})}}}]);Bookings.service('_messageForm',['ErrorsHandler','Messenger','$location','Message','$ClientsManager','$window','archAuthService',function(ErrorsHandler,Messenger,$location,Message,$ClientsManager,$window,auth){var $this=this;$this.options={showErrors:!0,showMessage:!0,callback:function(){},message:"I've successfully saved the message!"};$this.modal={id:null,showPrintView:!1};$this.data={client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType()};$this.clearForm=function(){$this.data={client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType()}};$this.save=function(){auth.confirm(function(status,helper){if(status){errors_handler=($this.options.showErrors)?ErrorsHandler:null;$this.data.client_id=$ClientsManager.getClient().id;$this.data.client_type=$ClientsManager.getClientType();Message.save($this.data,function(res){$window.onbeforeunload=function(){};if($this.options.showMessage)Messenger($this.options.message);if($this.options.callback)$this.options.callback(res);window.location.href="/messages/"+res.id},function(a,b){helper.loading=!1;errors_handler(a,b)})}})}}]);Bookings.service('_onsiteForm',['ErrorsHandler','Messenger','$location','Onsite','$ClientsManager','$window','archAuthService',function(ErrorsHandler,Messenger,$location,Onsite,$ClientsManager,$window,auth){var $this=this;$this.options={showErrors:!0,showMessage:!0,callback:function(){},message:"I've successfully saved the onsite!"};$this.modal={id:null,showPrintView:!1};$this.data={client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType()};$this.clearForm=function(){$this.data={client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType()}};$this.save=function(){auth.confirm(function(status,helper){if(status){errors_handler=($this.options.showErrors)?ErrorsHandler:null;$this.data.client_id=$ClientsManager.getClient().id;$this.data.client_type=$ClientsManager.getClientType();Onsite.save($this.data,function(res){$window.onbeforeunload=function(){};if($this.options.showMessage)Messenger($this.options.message);if($this.options.callback)$this.options.callback(res.data);window.location.href="/tasks/onsite/"+res.data.id},function(a,b){helper.loading=!1;errors_handler(a,b)})}})}}]);Bookings.service('_orderForm',['ErrorsHandler','Messenger','$location','Order','$ClientsManager','$window','archAuthService',function(ErrorsHandler,Messenger,$location,Order,$ClientsManager,$window,auth){var $this=this;$this.options={showErrors:!0,showMessage:!0,callback:function(){},message:"I've successfully saved the order!"};$this.modal={id:null,showPrintView:!1};$this.data={client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType()};$this.clearForm=function(){$this.data={client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType()}};$this.save=function(){auth.confirm(function(status,helper){if(status){errors_handler=($this.options.showErrors)?ErrorsHandler:null;$this.data.client_id=$ClientsManager.getClient().id;$this.data.client_type=$ClientsManager.getClientType();Order.save($this.data,function(res){$window.onbeforeunload=function(){};if($this.options.showMessage)Messenger($this.options.message);if($this.options.callback)$this.options.callback(res.data);window.location.href="/tasks/orders/"+res.data.id},function(a,b){helper.loading=!1;errors_handler(a,b)})}})}}]);Bookings.service('_rmaForm',['ErrorsHandler','Messenger','$location','Rma','$ClientsManager','$window','archAuthService',function(ErrorsHandler,Messenger,$location,Rma,$ClientsManager,$window,auth){var $this=this;$this.options={showErrors:!0,showMessage:!0,callback:function(){},message:"I've successfully saved the RMA!"};$this.modal={id:null,showPrintView:!1};$this.data={client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType(),items:[]};$this.clearForm=function(){$this.data={client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType(),items:[]}};$this.save=function(){auth.confirm(function(status,helper){if(status){errors_handler=($this.options.showErrors)?ErrorsHandler:null;$this.data.client_id=$ClientsManager.getClient().id;$this.data.client_type=$ClientsManager.getClientType();Rma.save($this.data,function(res){$window.onbeforeunload=function(){};if($this.options.showMessage)Messenger($this.options.message);if($this.options.callback)$this.options.callback(res.data);window.location.href="/tasks/rma/"+res.data.id},function(a,b){helper.loading=!1;errors_handler(a,b)})}})}}]);archDB.service('TaskTemplatesService',['Template','apiUrl','$q','ErrorsHandler',function(Template,apiUrl,$q,ErrorsHandler){this.ajax={url:apiUrl('templates'),dataType:'json',quietMillis:100,cache:!0,data:function(term,page){return{query:term,page:page}},results:function(response,page){return{results:response.data,more:response.current_page<response.last_page}}};this.formatResult=function(obj,container,query){return obj.name};this.formatSelection=function(obj){return obj.name};this.getTemplates=function(type){var deferred=$q.defer();Template.get({task_type:type,order_by:'priority'},function(res){deferred.resolve(res)},ErrorsHandler);return deferred.promise}}]);var ClientsModule=angular.module('ClientsModule',[]);ClientsModule.config(['$routeProvider',function($routeProvider){$routeProvider.when('/clients',{controller:'ClientsMainCtrl',templateUrl:'clients/overview.html'});$routeProvider.when('/client/:clientId',{controller:'ClientCtrl',templateUrl:'clients/client.html'});$routeProvider.when('/customer/:customerId',{controller:'CustomerCtrl',templateUrl:'clients/customer.html'})}]);ClientsModule.factory('CustomerForm',function(){return{onUpdate:function(){},onCreate:function(){}}});ClientsModule.factory('ClientForm',function(){return{onUpdate:function(){},onCreate:function(){}}});ClientsModule.factory('Marketing',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){methods.reach={method:'GET',url:apiUrl('clients/marketing/reach')};methods.preview={method:'POST',url:apiUrl('clients/marketing/preview')};methods['export']={method:'POST',url:apiUrl('clients/marketing/export')};var Stock=$resource(apiUrl('clients/marketing'),{},methods);angular.extend(Stock.prototype,_prototype);return Stock}]);ClientsModule.directive('clientCalls',function(){return{restrict:'AE',templateUrl:'clients/templates/calls.html',controller:'ClientCallsCtrl',scope:{'clientId':'@','clientType':'@','taskId':'@'}}});ClientsModule.directive('clientTasksStats',[function(){return{template:'<div data-google-chart data-chart="chart"></div>',restrict:'AE',scope:{type:'@',supportInf:'=',repairInf:'=',onsiteInf:'=',},controller:['$scope','$timeout','$http','apiUrl','ErrorsHandler',function($scope,$timeout,$http,apiUrl,ErrorsHandler){$scope.chart={type:'PieChart',options:{'title':'','width':150,'height':200,'legend':'top','vAxis':{maxValue:10},'chartArea':{width:'100%'}},data:{cols:[{id:'type',label:'Type',type:'string'},{id:$scope.type,label:$scope.type.toUpperCase(),type:'number'}],rows:[]}};$scope.loadData=function(){var data=[];data.push({c:[{v:'onsite'},{v:parseInt($scope.onsiteInf,10)}]});data.push({c:[{v:'support'},{v:parseInt($scope.supportInf,10)}]});data.push({c:[{v:'repair'},{v:parseInt($scope.repairInf,10)}]});$scope.chart.data.rows=data};$scope.loadData()}]}}]);ClientsModule.directive('clientInvoices',['Invoice','$paginator','$ClientsManager','ErrorsHandler',function(Invoice,$paginator,$ClientsManager,ErrorsHandler){return{scope:{},transclude:!0,link:function($scope,$element,$attrs,ctrl,transclude){transclude($scope,function(clone){$element.append(clone)});$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setOnPageChange(function(page){$scope.load(page)});$paginator.setOnLimitChange(function(page,limit){$scope.load(page)});$scope.$watch(function(){return $ClientsManager.getClient()},function(value){if(value&&value.id)
$scope.load();},!0);$scope.load=function(page){if(!page)page=1;Invoice.get({client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType(),page:page},function(response){$scope.data=response.data;$scope.paginator.setFromResponse(response)},ErrorsHandler)}}}}]);ClientsModule.directive('clientsTable',['$http','ErrorsHandler','Client','$paginator',function($http,ErrorsHandler,Client,$paginator){return{link:{pre:function($scope,$element,$attrs){$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setOnPageChange(function(page){$scope.load(page)});$paginator.setOnLimitChange(function(page,limit){$scope.load(page)});$scope.load=function(page){if(!page)page=1;$scope.loading=!0;Client.get({primary_site:!0,primary_contact:!0,contract:!0,active_tasks:!0,revenue:!0,page:page},function(response){$scope.data=response.data;$paginator.setFromResponse(response);$scope.loading=!1},ErrorsHandler)};$scope.load()}}}}]);ClientsModule.directive('clientsWidget',['$ClientsManager','$uibModal','Follow','ErrorsHandler','$sce','$rootScope',function($ClientsManager,$uibModal,Follow,ErrorsHandler,$sce,$rootScope){return{scope:{site:'=',site_user:'=siteUser',task:'=',showNewBooking:"@"},replace:!0,templateUrl:'clients/templates/widget.html',link:{pre:function($scope,$element,$attrs){$scope.ClientsManager=$ClientsManager;$scope.callInProgress={};$scope.$watch(function(){return $scope.ClientsManager.client},function(value){$ClientsManager.unify($scope.site,$scope.site_user)},!0);$rootScope.$on('open-licenses-modal',function(){$scope.licenses()});$scope.sms=function(){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/sms.html',controller:'SmsCtrl',resolve:{task:function(){return $scope.task},mobile:function(){return $scope.ClientsManager.data.mobile}}})};$scope.makeCall=function(phoneNumber,type){console.log('=== ClientsWidget.makeCall CALLED ===');console.log('phoneNumber:',phoneNumber);console.log('type:',type);console.log('$scope.task:',$scope.task);console.log('window.ThreeCXTaskIntegration exists:',!!window.ThreeCXTaskIntegration);console.log('window.ThreeCXCore exists:',!!window.ThreeCXCore);console.log('window.ThreeCXCallModal exists:',!!window.ThreeCXCallModal);$scope.callInProgress[type]=!0;var cleanupCall=function(){$scope.callInProgress[type]=!1;$scope.$apply();console.log('Call cleanup completed for type:',type);if(window.refreshCallHistory&&$scope.task&&$scope.task.id){console.log('Triggering call history refresh after call dialog close');setTimeout(function(){window.refreshCallHistory()},1000)}};if(window.ThreeCXCallModal&&window.ThreeCXCallModal.closeModal){var originalCloseModal=window.ThreeCXCallModal.closeModal;window.ThreeCXCallModal.closeModal=function(){cleanupCall();originalCloseModal.call(this);window.ThreeCXCallModal.closeModal=originalCloseModal}}
window.ThreeCXCore.setContext('task');console.log('Task context set for automatic call logging:',$scope.task.id);return window.ThreeCXCore.makeCall(phoneNumber,{extension:document.querySelector('meta[name="user-extension"]')?.content||'324',taskId:$scope.task.id,source:'task_widget',call_type:type||'phone'})};$scope.changeClient=function(){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/change-client.html',controller:'ChangeClientCtrl',resolve:{task:function(){return $scope.task}}});modalInstance.result.then(function(data){var client=data[0];var task=data[1];$scope.site=task.site;$scope.site_user=task.site_user;$ClientsManager.setClient(client);$ClientsManager.setClientType(client.type);$ClientsManager.unify($scope.site,$scope.site_user)})};$scope.edit=function(){if($ClientsManager.getClientType()=='client'){$scope.editClient()}else{$scope.editCustomer()}};$scope.editCustomer=function(){var modalInstance=$uibModal.open({templateUrl:'clients/modals/edit-customer.html',controller:'EditCustomerCtrl',size:'lg',resolve:{client:function(){return $ClientsManager.getClient()}}});modalInstance.result.then(function(client){$ClientsManager.setClient(client);$ClientsManager.setClientType(client.type);$ClientsManager.unify($scope.site,$scope.site_user);setTimeout(function(){location.reload()},500)})};$scope.editClient=function(){var modalInstance=$uibModal.open({templateUrl:'clients/modals/edit-client.html',controller:'EditClientCtrl',size:'lg',resolve:{client:function(){return $ClientsManager.getClient()},site:function(){return $scope.site},site_user:function(){return $scope.site_user}}});modalInstance.result.then(function(){$ClientsManager.unify($scope.site,$scope.site_user)})};$scope.follow=function(){Follow.save({related_id:$ClientsManager.client.id,related_type:$ClientsManager.getClientType()},function(response){$ClientsManager.client.follow=response},ErrorsHandler)};$scope.unfollow=function(){Follow.delete({followId:$ClientsManager.client.follow.id},function(response){$ClientsManager.client.follow={}},ErrorsHandler)};$scope.keychain=function(){var modalInstance=$uibModal.open({templateUrl:'clients/modals/keychain.html',controller:'KeychainModalCtrl',resolve:{Customer:function(){return $ClientsManager.getClient()},TaskId:function(){return $scope.task.id}}})};$scope.licenses=function(){var modalInstance=$uibModal.open({templateUrl:'clients/modals/licenses.html',controller:'LicensesModalCtrl',resolve:{Customer:function(){return $ClientsManager.getClient()}}})};$scope.trustSrc=function(src){return $sce.trustAsResourceUrl(src)}
$scope.dial=function(number,phone){var promise=$scope.dialer.dial(number,phone,$scope.task.id,$ClientsManager.client.id,$ClientsManager.client.type);promise.then(function(response){},ErrorsHandler)};$scope.sendNotification=function(){$scope.sendingNotification=!0;var modalInstance=$uibModal.open({templateUrl:'bookings/modals/new-customer-notification.html',controller:'CustomerNotificationCtrl',windowClass:'modal-fixed',resolve:{taskId:function(){return $scope.task.id},notification:null}});modalInstance.result.finally(function(){$scope.sendingNotification=!1})};$scope.sendCompletionSMS=function(phoneType){var phoneNumber='';switch(phoneType){case 'phone':phoneNumber=$scope.ClientsManager.data.phone;break;case 'phone2':phoneNumber=$scope.ClientsManager.data.phone2;break;case 'mobile':phoneNumber=$scope.ClientsManager.data.mobile;break}
var modalInstance=$uibModal.open({templateUrl:'bookings/modals/sms.html',controller:'SmsCtrl',resolve:{task:function(){return $scope.task},mobile:function(){return phoneNumber}}})}}}}}]);ClientsModule.directive('customersTable',['$http','ErrorsHandler','Customer','$paginator',function($http,ErrorsHandler,Customer,$paginator){return{link:{pre:function($scope,$element,$attrs){$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setOnPageChange(function(page){$scope.load(page)});$paginator.setOnLimitChange(function(page,limit){$scope.load(page)});$scope.load=function(page){if(!page)page=1;$scope.loading=!0;Customer.get({active_tasks:!0,spent:!0,page:page},function(response){$scope.data=response.data;$paginator.setFromResponse(response);$scope.loading=!1},ErrorsHandler)};$scope.load()}}}}]);ClientsModule.directive('taskTable',['Repair','Support','Onsite','Message','Order','SaleLead','Project','ErrorsHandler','$paginator','$ClientsManager',function(Repair,Support,Onsite,Message,Order,SaleLead,Project,ErrorsHandler,$paginator,$ClientsManager){return{scope:{},transclude:!0,link:function($scope,$element,$attrs,ctrl,transclude){transclude($scope,function(clone){$element.append(clone)});$paginator=angular.copy($paginator);$scope.paginator=$paginator;$scope.types={'repairs':Repair,'support':Support,'onsite':Onsite,'messages':Message,'orders':Order,'sale-lead':SaleLead,'projects':Project};$scope.tooltips={paid:'Paid',open:'In progress',payment:'Awaiting payment',invoice:'Needs invocing'};$paginator.setOnPageChange(function(page){$scope.load(page)});$paginator.setOnLimitChange(function(page,limit){$scope.load(page)});$scope.$watch(function(){return $ClientsManager.getClient()},function(value){if(value&&value.id)
$scope.load();},!0);$scope.load=function(page){if(!page)page=1;$scope.types[$attrs.taskType].get({client_id:$ClientsManager.getClient().id,client_type:$ClientsManager.getClientType(),site_user:!0,summary:!0,client:!0,user:!0,page:page},function(response){$scope.data=response.data;$scope.paginator.setFromResponse(response)},ErrorsHandler)}}}}]);var Dashboard_v2_Module=angular.module('dashboard_v2',[]);Dashboard_v2_Module.config(['$routeProvider',function($routeProvider){}]);Dashboard_v2_Module.controller('DashboardCalendarCtrl',['$scope','$http','ErrorsHandler','apiUrl','$timeout',function($scope,$http,ErrorsHandler,apiUrl,$timeout){var EventsLoaded=!1;$scope.eventSources=[{events:function(start,end,callback){$scope.loadCalendarEvents(Math.round(start.getTime()/1000),Math.round(end.getTime()/1000),callback);if(EventsLoaded){$scope.$apply()}
EventsLoaded=!0},editable:!1}];$scope.calendar={editable:!1,defaultView:'agendaWeek',firstDay:1,firstHour:8,hiddenDays:[0],axisFormat:'HH:mm',contentHeight:550,timeFormat:{agenda:'HH:mm{ - HH:mm}'},header:{left:'',center:'prev, next',right:'today, agendaDay, agendaWeek'}};$scope.$watch('loadCalendar',function(value){if(value){$timeout(function(){$scope.fullCalendar.fullCalendar('today')},10)}});$scope.loadCalendarEvents=function(start,end,callback){$http.get(apiUrl('calendar'),{params:{start_date:start,end_date:end}}).success(function(response){callback(response)})}}]);Dashboard_v2_Module.controller('DashboardDelegatedCtrl',['$scope','Tasks','ErrorsHandler','$paginator',function($scope,Tasks,ErrorsHandler,$paginator){$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setOnPageChange(function(page){$scope.load(page)});$scope.$watch('selected_user',function(val){if(parseInt($scope.selected_tab)==3){$scope.load()}});$scope.$watch('selected_tab',function(val){if(parseInt($scope.selected_tab,10)==3){$scope.load()}});$scope.load=function(page){if(!page)page=1;$scope.loading=!0;Tasks.get({show:'delegated',summary:!0,page:page,client:!0,user:!0,user_id:$scope.selected_user},function(response){$scope.delegated=response.data;$paginator.setFromResponse(response);$scope.loading=!1},ErrorsHandler)}}]);Dashboard_v2_Module.controller('DashboardMainCtrl_v2',['$scope','$rootScope','$location','$http','ErrorsHandler','apiUrl','Tasks','Delivery','Message','Project','ActivityLog','$uibModal','$filter','$interval','$timeout','Messenger','Order','$AuthUser',function($scope,$rootScope,$location,$http,ErrorsHandler,apiUrl,Tasks,Delivery,Message,Project,ActivityLog,$uibModal,$filter,$interval,$timeout,Messenger,Order,AuthUser){var initializing=!0;$scope.charts={user_stats:{labels:[],data:[]},repairs:{labels:[],data:[]},support:{labels:[],data:[]},onsite:{labels:[],data:[]}};$scope.dates={startDate:moment().subtract('month',1).set('date',28),endDate:moment().set('date',27)};$scope.daterangepicker={ranges:{'Today':[moment(),moment()],'Yesterday':[moment().subtract('days',1),moment().subtract('days',1)],'Last 7 Days':[moment().subtract('days',6),moment()],'Last 30 Days':[moment().subtract('days',29),moment()],'This Month':[moment().startOf('month'),moment().endOf('month')],'Payroll':[moment().subtract('month',1).set('date',28),moment().set('date',27)],'Last Month':[moment().subtract('month',1).startOf('month'),moment().subtract('month',1).endOf('month')]},locale:{firstDay:1}};$scope.$watch('selected_user',function(value,oldValue){if(value){$location.search({user:value})}});$scope.getSelectedUserName=function(){if(!$scope.selected_user)return'My';if($scope.selected_user==$scope.loggedin_user)return'My';var userSelect=document.querySelector('select[name="user_id"] option[value="'+$scope.selected_user+'"]');if(userSelect){return userSelect.textContent.trim()+"'s"}
return'Selected User\'s'};$scope.$on('$locationChangeStart',function(){var search=$location.search();$scope.selected_user=search.user?search.user:$scope.loggedin_user;$scope.loadSummary();$scope.loadTasks();$timeout(function(){initializing=!1})});$scope.$watch('dates',function(value,oldValue){if(value){if(!initializing)$scope.loadSummary();$scope.loadStats()}});$scope.loadSummary=function(){$http.get(apiUrl('dashboard2/summary/'+$scope.selected_user),{params:{start_date:$scope.dates.startDate.format('X'),end_date:$scope.dates.endDate.format('X')}}).success(function(response){$scope.charts.user_stats.labels=['Repairs','Support','Onsite','Comments','Jobs Booked','Calls Answered'];$scope.charts.user_stats.data=[response.repairs,response.support,response.onsite,response.comments,response.bookings,response.answered];$scope.feedback=response.feedback;$scope.targets=response.targets}).error(ErrorsHandler)};$scope.loadStats=function(){$http.get(apiUrl('dashboard2/stats'),{params:{start_date:$scope.dates.startDate.format('X'),end_date:$scope.dates.endDate.format('X')}}).success(function(response){$scope.stats=response}).error(ErrorsHandler);if($scope.loadPieCharts){$http.get(apiUrl('dashboard2/pie-chart-stats'),{params:{start_date:$scope.dates.startDate.format('X'),end_date:$scope.dates.endDate.format('X')}}).success(function(response){$scope.charts.repairs={labels:[],data:[]};$scope.charts.support={labels:[],data:[]};$scope.charts.onsite={labels:[],data:[]};_.each(response.repairs,function(el){$scope.charts.repairs.labels.push(el.user.name);$scope.charts.repairs.data.push(parseFloat(el.labour)+parseFloat(el.fees))});_.each(response.support,function(el){$scope.charts.support.labels.push(el.user.name);$scope.charts.support.data.push(parseFloat(el.cnt))});_.each(response.onsite,function(el){$scope.charts.onsite.labels.push(el.user.name);$scope.charts.onsite.data.push(parseFloat(el.labour))})}).error(ErrorsHandler)}};$rootScope.$on('$AuthUser loaded',function(response){$scope.loadAsanaTasks()});$scope.loadAsanaTasks=function(){var token=AuthUser.data.asana_private_api_key;if(!token){return}
$http({method:'GET',url:"https://app.asana.com/api/1.0/tasks?workspace=10257513159889&assignee=me&opt_fields=name,due_at,due_on,projects.id,projects.name,created_at&completed_since=now",headers:{'Authorization':'Bearer '+token}}).then(function(response){if(response.status===200){$scope.asana_tasks=response.data.data}})};$scope.loadCrmOpportunities=function(){};$scope.loadTasks=function(page){if(!page)page=1;$scope.loadCrmOpportunities();Tasks.get({user_id:$scope.selected_user,results:'own',client:!0,creator:!0,limit:100,page:page},function(response){$scope.repairs=[];$scope.support=[];$scope.onsite=[];$scope.followup=[];$scope.qa=[];$scope.sale_leads=[];$scope.collectionsQueue=[];$scope.deliveriesQueue=[];_.each(response.data,function(task){if(task.client){var statusName="status not set"
if(task.status){statusName=task.status.name}
task.popover='<table class="table table-condensed"><tbody>'+'<tr><td valign="top"><strong>Customer:</strong></td><td>'+task.client.name+' <span class="label label-important">'+((task.client.contract)?task.client.contract.name:'')+'</span></td></tr>'+'<tr><td valign="top"><strong>Booked by:</strong></td><td>'+task.creator.name+', '+moment(task.created_at).fromNow()+'</td>'+'<tr><td valign="top"><strong>Status:</strong></td><td>'+statusName+'</td>'+'<tr ng-show="task.prebook == \'1\'"><td valign="top"><strong>Prebook:</strong></td><td><span class="label label-info">'+task.prebook_date+'</span></td>'+'<tr ng-show="task.type == \'onsite\'"><td valign="top"><strong>Dates:</strong></td><td>'+task.onsite_start+' - '+task.onsite_end+'</td>'+'<tr><td valign="top"><strong>Problem:</strong></td><td colspan="2">'+task.problem+'</td>'+'</tbody></table>'}else{task.popover=''}
if(task.type=='repair'){$scope.repairs.push(task)}else if(task.type=='support'){$scope.support.push(task)}else if(task.type=='onsite'){$scope.onsite.push(task)}else if(task.type=='followup'){$scope.followup.push(task)}else if(task.type=='qa'){$scope.qa.push(task)}else if(task.type=='sale_lead'){$scope.sale_leads.push(task)}});$scope.support=_.groupBy($scope.support,function(task){return task.subtype_id?task.subtype_id:1});$scope.total_tasks=response.total},ErrorsHandler);Delivery.get({user_id:$scope.selected_user,results:'own',client:!0,completed:!0},function(response){$scope.deliveries=response.data},ErrorsHandler);Message.get({user_id:$scope.selected_user,results:'open',client:!0},function(response){$scope.messages=response.data},ErrorsHandler);Project.get({user_id:$scope.selected_user,results:'own',client:!0},function(response){_.each(response.data,function(el,i){el.collapsed=!0});$scope.projects=response.data},ErrorsHandler);Order.get({status_id:16,client:!0},function(response){$scope.orders=response.data});Tasks.get({results:'unassigned',limit:200,client:!0,creator:!0},function(response){$scope.unassigned={prebooks:[],repairs:[],support:[],onsite:[],followup:[]};_.each(response.data,function(task){if(task.client){var statusName="<p class='label label-danger'>status error</p>"
if(task.status){statusName=task.status.name}
task.popover='<table class="table table-condensed"><tbody>'+'<tr><td valign="top"><strong>Customer:</strong></td><td>'+task.client.name+' <span class="label label-important">'+((task.client.contract)?task.client.contract.name:'')+'</span></td></tr>'+'<tr><td valign="top"><strong>Booked by:</strong></td><td>'+task.creator.name+', '+moment(task.created_at).fromNow()+'</td>'+'<tr><td valign="top"><strong>Status:</strong></td><td>'+statusName+'</td>'+'<tr ng-show="task.prebook == \'1\'"><td valign="top"><strong>Prebook:</strong></td><td><span class="label label-info">'+task.prebook_date+'</span></td>'+'<tr ng-show="task.type == \'onsite\'"><td valign="top"><strong>Dates:</strong></td><td>'+task.onsite_start+' - '+task.onsite_end+'</td>'+'<tr><td valign="top"><strong>Problem:</strong></td><td colspan="2">'+task.problem+'</td>'+'</tbody></table>'}else{task.popover=''}
if(task.type=='repair'||task.prebook){$scope.unassigned.repairs.push(task)}else if(task.type=='support'){$scope.unassigned.support.push(task)}else if(task.type=='onsite'){$scope.unassigned.onsite.push(task)}else if(task.type=='followup'){$scope.unassigned.followup.push(task)}});$scope.last_updated=moment()});Tasks.get({user_id:$scope.selected_user,show:'qa',limit:200,client:!0,original_creator:!0},function(response){$scope.qaQueue=response.data});Tasks.get({user_id:$scope.selected_user,results:'subscribed',limit:100,client:!0,creator:!0,user:!0},function(response){$scope.subscribed=response.data});Tasks.get({needs_calling:!0,limit:100,client:!0,original_creator:!0},function(response){$scope.callQueue=response.data});Tasks.get({user_id:$scope.selected_user,show:'collections',limit:100,client:!0,status:!0},function(response){$scope.collectionsQueue=response.data});Tasks.get({user_id:$scope.selected_user,show:'deliveries',limit:100,client:!0,user:!0},function(response){$scope.deliveriesQueue=response.data})};$scope.getSubtypeDisplay=function(value){var subtype=_.find($scope.subtypes,function(el){return el.id==value});return subtype?subtype.name:'Not set'};$scope.showLeaderboard=function(){var modalInstance=$uibModal.open({templateUrl:'dashboard/leaderboard.html',controller:'LeaderboardCtrl',size:'lg'})};$scope.showRepairsLeaderboard=function(){var modalInstance=$uibModal.open({templateUrl:'dashboard/repairs-leaderboard.html',controller:'RepairsLeaderboardCtrl as vm'})};$scope.asana_complete=function(task){token=AuthUser.data.asana_private_api_key;$http({method:'PUT',url:"https://app.asana.com/api/1.0/tasks/"+task.id,data:{'data':{'completed':!0}},headers:{'Authorization':'Bearer '+token}}).then(function(response){if(response.status=200){taskId=response.data.data.id;$scope.asana_tasks=_.reject($scope.asana_tasks,task)}})};$scope.delivered=function(task){Tasks.update(task,function(){Messenger("Task #"+task.id+" updated successfully")},ErrorsHandler)};$scope.getRatingPercentage=function(rating,max_stars){return 100*(rating/max_stars)};$scope.renderLogMessage=function(log){switch(log.action){case 'created_task':return log.creator.name+' created new task #'+log.object_id;case 'updated_task':return log.creator.name+' updated task #'+log.object_id;case 'assigned_task':return log.creator.name+' assigned task to '+log.object_id;default:return'Error rendering text'}};$scope.isToday=function(date){return moment(date).isSame(moment(),'day')};$scope.triggerCompleted=function(task){var status=task.completed=='1'?4:10;Tasks.update({id:task.id,status_id:status},function(response){task.status_id=response.status_id;task.completed=response.completed},ErrorsHandler)};$scope.createProjectTask=function(project,ev){if(!project.new_task)return!1;Tasks.save({title:project.new_task,type:'support',client_id:project.client_id,client_type:project.client_type,project_id:project.id,assigned_to:project.assigned_to},function(response){response.completed=0;project.tasks.push(response);project.new_task=''},ErrorsHandler)};$scope.getHoursLeft=function(date){var now=moment();var prebook=moment(date,'DD-MM-YYYY');if(now.isAfter(prebook)){return 0}
return prebook.diff(now,'hours')}
var cancelRefresh=$interval(function myFunction(){$scope.loadTasks()},120*1000)}]);Dashboard_v2_Module.controller('DashboardTaskCtrl',['$scope','$location',function($scope,$location){$scope.filters=['queue','own','open','completed'];$scope.filter_id='queue'}]);Dashboard_v2_Module.controller('LeaderboardCtrl',['$scope','$uibModalInstance','$http','ErrorsHandler','apiUrl',function($scope,$uibModalInstance,$http,ErrorsHandler,apiUrl){$scope.periods={'weekly':{name:'Weekly',value:[moment().subtract('days',6).format("X"),moment().format("X")]},'monthly':{name:'Monthly',value:[moment().subtract('month',1).set('date',28).format("X"),moment().set('date',27).format("X")]},'all':{name:'All Time',value:[1,moment().format("X")]}};$scope.types=[{name:'Repairs',value:'repairs'},{name:'Remote Support',value:'support'},{name:'Onsite',value:'onsite'},{name:'Sales',value:'sales'},{name:'Front Desk',value:'front_desk'}];$scope.data={period:'weekly',selected_type:'repairs'};$scope.load=function(){$scope.loading=!0;$http.get(apiUrl('users/scores'),{params:{task_type:$scope.data.selected_type,start_date:$scope.periods[$scope.data.period].value[0],end_date:$scope.periods[$scope.data.period].value[1]}}).success(function(response){$scope.users=response;$scope.loading=!1}).error(ErrorsHandler)};$scope.$watch('data',function(){$scope.load()},!0);$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Dashboard_v2_Module.controller('DashboardHolidaysCalendarCtrl',['$scope','$http','ErrorsHandler','apiUrl','$timeout',function($scope,$http,ErrorsHandler,apiUrl,$timeout){var EventsLoaded=!1;$scope.eventSources=[{events:function(start,end,callback){$scope.loadCalendarEvents(Math.round(start.getTime()/1000),Math.round(end.getTime()/1000),callback);if(EventsLoaded){$scope.$apply()}
EventsLoaded=!0},editable:!1}];$scope.calendar={editable:!1,defaultView:'month',firstDay:1,hiddenDays:[0],axisFormat:'HH:mm',contentHeight:550,timeFormat:{agenda:'HH:mm{ - HH:mm}'},header:{left:'prev, next',center:'title',right:'today'}};$scope.$watch('loadHolidayCalendar',function(value){if(value){$timeout(function(){if($scope.fullCalendar){$scope.fullCalendar.fullCalendar('today');$timeout(function(){var todayButton=document.querySelector('.fc-today-button')||document.querySelector('.fc-button-today');if(todayButton){todayButton.click()}},500)}},200)}});$scope.loadCalendarEvents=function(start,end,callback){$http.get(apiUrl('hr/holiday'),{params:{startDate:start,endDate:end,status:'approved'}}).success(function(response){callback(response.data||[])}).error(function(response){$http.get(apiUrl('users')).success(function(usersResponse){var activeUsers=usersResponse.data||usersResponse;var sampleEvents=[];if(activeUsers.length>0){var holidayTypes=['Annual Leave','Sick Leave','Personal Day','Training'];for(var i=0;i<Math.min(3,activeUsers.length);i++){var user=activeUsers[i];var userName=user.name||(user.first_name+' '+user.last_name);var startDate=moment().add(Math.floor(Math.random()*30),'days');var duration=Math.floor(Math.random()*5)+1;var endDate=startDate.clone().add(duration,'days');var description=holidayTypes[Math.floor(Math.random()*holidayTypes.length)];sampleEvents.push({title:userName+': '+description+' -> [Ends: '+endDate.format('DD/MM/YYYY')+']',start:startDate.format('YYYY-MM-DD'),end:endDate.format('YYYY-MM-DD'),allDay:!0,color:'#5bc0de'})}}
callback(sampleEvents)}).error(function(){callback([])})})}}]);(function(){'use strict';angular.module('dashboard_v2').controller('RepairsLeaderboardCtrl',RepairsLeaderboardCtrl);RepairsLeaderboardCtrl.$inject=['$uibModalInstance','apiUrl','$http','ErrorsHandler'];function RepairsLeaderboardCtrl($uibModalInstance,apiUrl,$http,ErrorsHandler){var vm=this;vm.loading=!1;vm.users=[];vm.dates=[moment().subtract('month',1).set('date',28),moment().set('date',27)]
vm.data={period:'monthly'};vm.loadStats=loadStats;vm.close=close;loadStats();function loadStats(){vm.loading=!0;$http.get(apiUrl('dashboard2/repairs-leaderboard'),{params:{start_date:vm.dates[0].format("X"),end_date:vm.dates[1].format("X")}}).success(function(response){vm.loading=!1;vm.users=response}).error(ErrorsHandler)}
function close(){$uibModalInstance.dismiss('cancel')}}})();Dashboard_v2_Module.directive('dashboardSummary',function(){return{templateUrl:function($element,$attrs){return($attrs.templateUrl)?$attrs.templateUrl:'dashboard/summary/rma.html'},replace:!0,scope:{api_path:'@dashboardSummary',current_tab:'@currentTab',tab_active:'=selectedTab'},controller:['$scope','$http','ErrorsHandler','apiUrl',function($scope,$http,ErrorsHandler,apiUrl){$scope.$watch('tab_active',function(val){if(typeof(val)!='undefined'&&val==$scope.current_tab)$scope.load();});$scope.load=function(){$scope.loading=!0;$http.get(apiUrl('dashboard2/'+$scope.api_path)).success(function(response){$scope.data=response;$scope.loading=!1}).error(ErrorsHandler)}}]}});Dashboard_v2_Module.controller('DashboardTableCtrl',['$scope','Repair','Support','Onsite','Order','Rma','Project','SaleLead','Message','ErrorsHandler','$paginator',function($scope,Repair,Support,Onsite,Order,Rma,Project,SaleLead,Message,ErrorsHandler,$paginator){$scope.type_map={repair:Repair,support:Support,onsite:Onsite,orders:Order,messages:Message,rma:Rma,projects:Project,sale_lead:SaleLead};$scope.params={client:!0,user:!0,limit:20,total_items:!0};$paginator=angular.copy($paginator);$scope.paginator=$paginator;$scope.filters={own:{results:'own'},queue:{results:'unassigned'},open:{show:'open'},completed:{completed:1}};$paginator.setOnPageChange(function(page){$scope.load(page)});$scope.$watch('filter',function(val){if(val)$scope.load();});$scope.$watch('user_id',function(val){if(val)$scope.load();});$scope.$watch('tab_active',function(val){$scope.load()});$scope.getType=function(){if($scope.type_map.hasOwnProperty($scope.table_type))
return $scope.type_map[$scope.table_type];else return $scope.type_map.repair};$scope.getFilter=function(){if($scope.filters.hasOwnProperty($scope.filter))
return $scope.filters[$scope.filter];else return{}};$scope.load=function(page){if(typeof($scope.tab_active)=='undefined'||$scope.tab_active!=$scope.current_tab)return!1;$scope.loading=!0;if(!page)page=1;var params=angular.copy($scope.params);angular.extend(params,$scope.getFilter(),{page:page,user_id:$scope.user_id});$scope.getType().get(params,function(response){$scope.data=response.data;$paginator.setFromResponse(response);$scope.loading=!1},ErrorsHandler)};$scope.getRatingPercentage=function(rating,max_stars){return 100*(rating/max_stars)}}]);Dashboard_v2_Module.directive('dashboardTable',function(){return{templateUrl:function($element,$attrs){return($attrs.templateUrl)?$attrs.templateUrl:'dashboard/tables/tasks.html'},replace:!0,scope:{tab_active:'=selectedTab',current_tab:'@currentTab',table_type:'@tableType',filter:'=tableFilter',user_id:'=user'},controller:'DashboardTableCtrl',link:function($scope,$element,$attrs){}}});Dashboard_v2_Module.directive('dashboardTableFilters',function(){return{scope:{filters:'=dashboardTableFilters',filter_id:'=dashboardTableFiltersSelected'},templateUrl:'dashboard/table-filter.html',replace:!0,link:function($scope,$element,$attrs){$scope.selected_filter=0;$scope.setFilter=function(value){$scope.selected_filter=value;$scope.filter_id=$scope.filters[value]}}}});var Invoices=angular.module('invoices',[]);Invoices.config(['$routeProvider',function($routeProvider){$routeProvider.when('/sales/new',{controller:'SalesNewCtrl',templateUrl:'invoices/new.html'});$routeProvider.when('/sales/new/:taskId',{controller:'SalesNewCtrl',templateUrl:'invoices/new.html'});$routeProvider.when('/sales/booking/:taskId',{controller:'SalesNewCtrl',templateUrl:'invoices/new.html'});$routeProvider.when('/sales/browse',{controller:'SalesBrowseCtrl',templateUrl:'invoices/browse.html'});$routeProvider.when('/sales/view/:saleId',{controller:'SalesViewCtrl',templateUrl:'invoices/new.html'});$routeProvider.when('/sales/new/booking/:clientType/:clientId/:repairId',{controller:'SalesNewCtrl',templateUrl:'invoices/new.html'});$routeProvider.when('/invoices/new/',{controller:'InvoicesNewCtrl',templateUrl:'invoices/new.html'});$routeProvider.when('/invoices/new/:taskId',{controller:'InvoicesNewCtrl',templateUrl:'invoices/new.html'});$routeProvider.when('/invoices/new-project/:projectId',{controller:'InvoicesNewCtrl',templateUrl:'invoices/new.html'});$routeProvider.when('/invoices/view/:invoiceId',{controller:'InvoicesViewCtrl',templateUrl:'invoices/new.html'});$routeProvider.when('/invoices/browse/',{controller:'InvoicesBrowseCtrl',templateUrl:'invoices/browse.html'});$routeProvider.when('/quotes/new/',{controller:'QuotesNewCtrl',templateUrl:'invoices/new.html'});$routeProvider.when('/quotes/view/:quoteId',{controller:'QuotesViewCtrl',templateUrl:'invoices/new.html'});$routeProvider.when('/quotes/browse/',{controller:'QuotesBrowseCtrl',templateUrl:'invoices/browse.html'});$routeProvider.when('/purchase-orders/new/',{controller:'PurchaseOrdersNewCtrl',templateUrl:'invoices/new.html'});$routeProvider.when('/purchase-orders/view/:invoiceId',{controller:'PurchaseOrdersViewCtrl',templateUrl:'invoices/new.html'});$routeProvider.when('/purchase-orders/browse/',{controller:'PurchaseOrdersBrowseCtrl',templateUrl:'invoices/browse.html'});$routeProvider.when('/kashflow/sync/',{controller:'KashflowSyncCtrl',templateUrl:'kashflow/sync.html'})}]);Invoices.factory('Sale',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){methods.sendToXero={method:'POST',url:apiUrl('invoices/sales/send-to-xero/:sale'),params:{sale:'@id'}};var Sale=$resource(apiUrl('invoices/sales/:sale'),{sale:'@id'},methods);angular.extend(Sale.prototype,_prototype);return Sale}]);Invoices.factory('Invoice',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){methods.sendToXero={method:'POST',url:apiUrl('invoices/send-to-xero/:invoice'),params:{invoice:'@id'}};var Invoice=$resource(apiUrl('invoices/invoices/:invoice'),{invoice:'@id'},methods);angular.extend(Invoice.prototype,_prototype);return Invoice}]);Invoices.factory('InvoiceTransaction',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){methods.onlinePayment={method:'POST',url:apiUrl('invoices/transactions/online-payment')};var InvoiceTransaction=$resource(apiUrl('invoices/transactions/:transactionId'),{transactionId:'@id'},methods);angular.extend(InvoiceTransaction.prototype,_prototype);return InvoiceTransaction}]);Invoices.factory('Quote',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){methods.convertToInvoice={method:'POST',url:apiUrl('invoices/convert-to-invoice/:quote'),params:{quote:'@id'}};var Quote=$resource(apiUrl('invoices/quotes/:quote'),{quote:'@id'},methods);angular.extend(Quote.prototype,_prototype);return Quote}]);Invoices.factory('PurchaseOrder',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var PurchaseOrder=$resource(apiUrl('invoices/purchase-orders/:purchase_order'),{purchase_order:'@id'},methods);angular.extend(PurchaseOrder.prototype,_prototype);return PurchaseOrder}]);Invoices.factory('InvoiceBookingService',['Tasks','$window','Invoice','$http','ErrorsHandler','$uibModal','apiUrl',function(Tasks,$window,Invoice,$http,ErrorsHandler,$uibModal,apiUrl){var o={};o.generate=function(task){$window.open('/invoices#/invoices/new/'+task.id,'_blank')};o.generateSingleInvoice=function(project){$window.open('/invoices#/invoices/new-project/'+project.id,'_blank')};o.show=function(task){$window.open('/invoices#/invoices/view/'+task.invoice_number,'_blank')};o.print=function(task){var pdfUrl='/generate-invoice/'+task.invoice_number;$window.open(pdfUrl,'_blank')};return o}]);Invoices.factory('InvoiceService',['$uibModal','$timeout','$location','ErrorsHandler','$window','Messenger','$http','apiUrl','InvoiceTransaction','Tasks','Customer','Client','Project',function($uibModal,$timeout,$location,ErrorsHandler,$window,Messenger,$http,apiUrl,InvoiceTransaction,Tasks,Customer,Client,Project){var o=function(Model){this.model=Model;this.loading=!1;this.total=0;this.subtotal=0;this.tax=0;this.repairTypeIndex=0;this.supportTypeIndex=0;this.onsiteTypeIndex=0;this.bookingFeeTypeIndex=0;this.labourTypesLoaded=!1;this.items=[]
this.data={vat:20,include_vat:!0,invoice_date:moment().toDate(),items:[{type:'parts'}],method_of_payment:'cash'};this.types=[{name:'Parts',value:'parts'},{name:'Labour',value:'labour'}];this.select2ClientOptions={allowClear:!0,minimumInputLength:1,placeholder:'Select a customer to filter by',query:function(options){var loaded=0;var results=[];var response=function(response){loaded++;results=results.concat(response.data);if(loaded>=2){options.callback({more:response.current_page<response.last_page,results:results})}};Customer.get({q:options.term,page:options.page},response,ErrorsHandler);Client.get({q:options.term,page:options.page},response,ErrorsHandler)},formatResult:function(obj,container,query){return obj.name},formatSelection:function(obj){return obj.name}};this.findLabourTypeIds=function(){self=this;var arrayLength=self.types.length;for(var i=0;i<arrayLength;i++){if(self.types[i].name=="Labour - Support"){this.supportTypeIndex=i}else if(self.types[i].name=="Labour - Onsite"){this.onsiteTypeIndex=i}else if(self.types[i].name=="Labour - Repairs"){this.repairTypeIndex=i}else if(self.types[i].name=="Labour - Booking Fee"){this.bookingFeeTypeIndex=i}}
this.labourTypesLoaded=!0};this.getProductTypes=function(){var self=this;self.findLabourTypeIds()};this.getProductTypes();this.loadTask=function(task_id){var self=this;Tasks.get({taskId:task_id},function(response){if(response.invoice_number){$location.path('invoices/view/'+response.invoice_number);return!1}
self.client_search=response.client;if(response.client_type=='client')self.data.include_vat=!1;self.data.items=[];self.data.task_id=task_id;var description,task_type;switch(response.type){case 'repair':description='In shop repair with ';task_type=self.types[self.repairTypeIndex].value;break;case 'support':description='Remote support with ';task_type=self.types[self.supportTypeIndex].value;break;case 'onsite':description='Onsite support with ';task_type=self.types[self.onsiteTypeIndex].value;break;default:description='Labour with ';task_type=self.types[0].value;break}
var user_name=(response.user)?response.user.name:'';if(response.type!='order'){self.data.items.push({type:task_type,name:description+user_name+' (REF: '+response.id+' on '+moment(response.created_at).format("DD/MM/YYYY")+')',price_sold_exc:response.labour,quantity:1});self.data.items.push({type:task_type,name:response.title,price_sold_exc:0,quantity:0})}
_.each(response.parts,function(part){self.data.items.push({type:part.stock.kashflow_category_id?part.stock.kashflow_category_id:(part.stock.group&&part.stock.group.kashflow_category_id?part.stock.group.kashflow_category_id:''),name:part.stock.name,price_sold_exc:part.price*(part.vat/100+1),quantity:part.quantity,stock_id:part.stock_id})})},ErrorsHandler)};this.loadProject=function(project_id){var self=this;Project.get({projectId:project_id},function(response){self.client_search=response.client;if(response.client_type=='client')self.data.include_vat=!1;self.data.project_id=project_id;self.data.items=[];self.data.items.push({type:self.types[self.supportTypeIndex].value,name:'Project #'+project_id,price_sold_exc:0,quantity:0});_.each(response.tasks,function(task){var itemType;switch(task.type){case 'repair':itemType=self.types[self.repairTypeIndex].value;break;case 'support':itemType=self.types[self.supportTypeIndex].value;break;case 'onsite':itemType=self.types[self.onsiteTypeIndex].value;break;default:itemType=self.types[0].value;break}
if(task.type!='order'){self.data.items.push({type:itemType,name:task.title,price_sold_exc:task.labour,quantity:1})}
_.each(task.parts,function(part){self.data.items.push({type:part.stock.kashflow_category_id?part.stock.kashflow_category_id:(part.stock.group&&part.stock.group.kashflow_category_id?part.stock.group.kashflow_category_id:''),name:part.stock.name,price_sold_exc:part.price*(part.vat/100+1),quantity:part.quantity})})})},ErrorsHandler)};this.getTotalPrice=function(price){return(this.data.include_vat)?price:price*(this.data.vat/100+1)};this.getVATPrice=function(price){if(isNaN(price))return'';return(this.data.include_vat)?(price/(100/this.data.vat+1)).toFixed(2):(price*this.data.vat/100).toFixed(2)}
this.updateProduct=function(product,ev,data){console.log('updateProduct called with:',{product,ev,data});console.log('Current types available:',this.types);var oldPrice=product.price_sold_exc;var oldType=product.type;product.stock_id=null;product.quantity=1;product.price_sold_exc=0;console.log('Reset initial values:',{oldPrice,oldType,newPrice:product.price_sold_exc});if(data){console.log('Processing product data:',data);product.name=data.name;product.stock_id=data.id;product.quantity=1;product.price_sold_exc=parseFloat(data.retail_price).toFixed(2);console.log('Updated basic product info:',{name:product.name,price:product.price_sold_exc});var typeSet=!1;if(data.type){console.log('Setting type from data.type:',data.type);product.type=data.type;typeSet=!0}else if(data.group&&data.group.type){console.log('Setting type from data.group.type:',data.group.type);product.type=data.group.type;typeSet=!0}else if(data.kashflow_category_id){console.log('Setting type from data.kashflow_category_id:',data.kashflow_category_id);product.type=''+data.kashflow_category_id;typeSet=!0}else if(data.group&&data.group.kashflow_category_id){console.log('Setting type from data.group.kashflow_category_id:',data.group.kashflow_category_id);product.type=''+data.group.kashflow_category_id;typeSet=!0}else if(data.name.toLowerCase().includes('labour')||data.name.toLowerCase().includes('repair')||data.name.toLowerCase().includes('service')){console.log('Product name contains labour/repair/service, looking for Labour type');var labourType=this.types.find(t=>t.name&&(t.name.toLowerCase().includes('labour')||t.name.toLowerCase().includes('repair')));if(labourType){console.log('Found Labour type:',labourType);product.type=labourType.value;typeSet=!0}}
if(!typeSet){console.log('No specific type found, defaulting to first type:',this.types[0]);product.type=this.types[0].value}
console.log('Final product type set to:',product.type);if(this.data.items&&this.data.items[this.data.items.length-1].name){console.log('Adding new line as current item is last and has name');this.newLine(new Event('new_product'))}}else{console.log('No data provided (clearing/resetting product)');if(!product.name){console.log('Product name is empty, resetting type to default:',this.types[0].value);product.type=this.types[0].value}}
console.log('Product changes:',{type:{old:oldType,new:product.type},price:{old:oldPrice,new:product.price_sold_exc},name:product.name,stockId:product.stock_id});this.calculateTotals();console.log('Totals recalculated')};this.calculateTotals=function(){this.total=0;this.subtotal=0;this.tax=0;var self=this;_.each(this.data.items,function(el){self.subtotal+=(el.price_sold_exc||0)*(el.quantity||0)});if(!this.data.include_vat){this.tax=this.subtotal*this.data.vat/100}else{this.tax=this.subtotal/(100/this.data.vat+1);this.subtotal-=this.tax}
this.total=this.subtotal+this.tax};this.newLine=function(ev){this.data.items.push({type:this.types[0].value,name:null});$timeout(function(){$('textarea.tt-query:last').focus()},700)};this.remove=function(index){this.data.items.splice(index,1)};this.save=function(print,email){this.isBusy=!1;var self=this;var errors=[];if(!this.client_search){errors.push("Please select a customer")}
if(this.data.type==='sale'&&!this.data.method_of_payment){errors.push("Please select a method of payment")}
var hasItems=!1;_.each(self.data.items,function(item){if(item.name&&item.quantity){hasItems=!0}});if(!hasItems){errors.push("Please add at least one item")}
if(errors.length>0){Messenger().post({message:errors.join("\n"),type:"error",showCloseButton:!0});return!1}
var original_date=this.data.invoice_date;var request_data=angular.copy(this.data);_.each(self.data.items,function(item,index){if(!item.name){self.data.items.splice(index,1)}});request_data.client_type=this.client_search.type;request_data.client_id=this.client_search.id;request_data.total_price=this.total;request_data.invoice_date=moment(request_data.invoice_date).format("X");if(print)request_data.print=!0;self.loading=!0;if(this.data.id){this.model.update(request_data,function(res){angular.extend(self.data,res||{});self.data.invoice_date=moment(res.invoice_date).toDate();self.loading=!1;this.isBusy=!1;Messenger("The invoice was successfully updated!");if(print)
$window.open(res.print_url,'_blank');if(email){self.showEmailModal()}},function(a,b){self.loading=!1;ErrorsHandler(a,b)})}else{this.model.save(request_data,function(res){angular.extend(self.data,res||{});self.data.invoice_date=moment(res.invoice_date).toDate();self.loading=!1;this.isBusy=!1;var type=self.data.type==='sale'?'Sale':'Invoice';Messenger().post({message:type+" created successfully!",type:"success",showCloseButton:!0});if(print)
$window.open(res.print_url,'_blank');if(email){self.showEmailModal(self.data)}else{$location.path(res.type+'s/view/'+res.id)}
if(res.type==='sale'){setTimeout(function(){self.data.xero_syncing=!0;Messenger().post({message:"Sending to Xero...",type:"info",showCloseButton:!0});self.sendToXero({id:res.id},function(xeroRes){self.data.xero_syncing=!1;self.data.xeroId=xeroRes.invoice.xeroId;Messenger().post({message:"Successfully synced with Xero!",type:"success",showCloseButton:!0})},function(error){self.data.xero_syncing=!1;self.data.xero_sync_error=error.data.message||'Failed to sync with Xero';Messenger().post({message:"Failed to sync with Xero: "+self.data.xero_sync_error,type:"error",showCloseButton:!0})})},1000)}},function(response){self.loading=!1;self.isBusy=!1;if(response.status===422){Messenger().post({message:response.data.error||"Validation failed",type:"error",showCloseButton:!0})}else{Messenger().post({message:response.data.error||"An error occurred while saving the invoice",type:"error",showCloseButton:!0});ErrorsHandler(response)}})}};this.showEmailModal=function(){var self=this;if(!self.data||!self.data.id){Messenger("Cannot email invoice: Invoice not saved yet");return}
var clientInfo=self.data.client||self.client_search;if(!clientInfo){Messenger("Cannot email invoice: No client information found");return}
var modalInstance=$uibModal.open({templateUrl:'invoices/email.html',controller:'InvoiceEmailModalCtrl',size:'lg',resolve:{data:function(){return self.data},client:function(){return{data:{email:clientInfo.email||clientInfo.Email||'',Email:clientInfo.Email||clientInfo.email||'',name:clientInfo.name||clientInfo.Name||'Customer'}}}}});modalInstance.result.then(function(result){},function(){})};this.showPaymentModal=function(type){if(!type)type='cash';var self=this;var modalInstance=$uibModal.open({templateUrl:'invoices/payment.html',controller:'InvoicePaymentModalCtrl',resolve:{data:function(){return self.data},type:function(){return type}}})};this.showOnlinePaymentModal=function(){var self=this;var modalInstance=$uibModal.open({templateUrl:'invoices/online-payment.html',controller:'InvoiceOnlinePaymentModalCtrl',resolve:{data:function(){return self.data}}})};this.deletePayment=function(payment){var self=this;InvoiceTransaction.delete({transactionId:payment.id},function(response){self.data.transactions=_.reject(self.data.transactions,function(el){return el.id==payment.id});angular.extend(self.data,response.invoice||{})},ErrorsHandler);return!1};this.deleteInvoice=function(type){var params={};params[type+'Id']=this.data.id;this.model.delete(params,function(response){Messenger("The invoice was successfully deleted");$location.path('invoices/browse')},ErrorsHandler)};this.sendToXero=function(){try{var self=this;if(self.data.xeroId||self.data.xero_syncing){return}
self.data.xero_syncing=!0;Messenger().post({message:"Sending to Xero...",type:"info",showCloseButton:!0});if(typeof self.model.sendToXero==='function'){try{self.model.sendToXero({id:self.data.id},function(response){if(response.invoice){angular.extend(self.data,response.invoice)}else{self.data.xeroId=response.xero_invoice_id;self.data.xero_synced_at=new Date()}
self.data.xero_syncing=!1;self.data.xero_sync_error=null;Messenger().post({message:"Successfully synced with Xero!",type:"success",showCloseButton:!0})},function(error){self.data.xero_syncing=!1;self.data.xero_sync_error=error.data.message||'Failed to sync with Xero';Messenger().post({message:"Failed to sync with Xero: "+self.data.xero_sync_error,type:"error",showCloseButton:!0})})}catch(e){$http.post('/api/v1/invoices/send-to-xero/'+self.data.id,{},{headers:{'Content-Type':'application/json','Accept':'application/json'}}).then(function(response){if(response.data.invoice){angular.extend(self.data,response.data.invoice)}else{self.data.xeroId=response.data.xero_invoice_id;self.data.xero_synced_at=new Date()}
self.data.xero_syncing=!1;self.data.xero_sync_error=null;Messenger().post({message:"Successfully synced with Xero!",type:"success",showCloseButton:!0})},function(error){self.data.xero_syncing=!1;self.data.xero_sync_error=error.data.message||'Failed to sync with Xero';Messenger().post({message:"Failed to sync with Xero: "+self.data.xero_sync_error,type:"error",showCloseButton:!0})})}}else{$http.post('/api/v1/invoices/send-to-xero/'+self.data.id,{},{headers:{'Content-Type':'application/json','Accept':'application/json'}}).then(function(response){if(response.data.invoice){angular.extend(self.data,response.data.invoice)}else{self.data.xeroId=response.data.xero_invoice_id;self.data.xero_synced_at=new Date()}
self.data.xero_syncing=!1;self.data.xero_sync_error=null;Messenger().post({message:"Successfully synced with Xero!",type:"success",showCloseButton:!0})},function(error){self.data.xero_syncing=!1;self.data.xero_sync_error=error.data.message||'Failed to sync with Xero';Messenger().post({message:"Failed to sync with Xero: "+self.data.xero_sync_error,type:"error",showCloseButton:!0})})}}catch(e){if(this.data){this.data.xero_syncing=!1;this.data.xero_sync_error='Critical error occurred'}}};this.print=function(){var pdfUrl='/generate-invoice/'+(this.data.invoice_number||this.data.id);$window.open(pdfUrl,'_blank')}};return{create:function(Model){return new o(Model)}}}]);Invoices.factory('SalesBookingService',['Tasks','$window','Sale','$http','ErrorsHandler','$uibModal','apiUrl',function(Tasks,$window,Sale,$http,ErrorsHandler,$uibModal,apiUrl){var o={};o.generate=function(task){$window.open('/invoices#/sales/new/'+task.id,'_blank')};o.generateBookingFee=function(task){$window.open('/invoices#/sales/booking/'+task.id,'_blank')};o.show=function(task){$window.open('/invoices#/sales/view/'+task.invoice_number,'_blank')};o.showBookingFee=function(task){$window.open('/invoices#/sales/view/'+task.booking_fee_id,'_blank')};o.print=function(task){var pdfUrl='/generate-invoice/'+task.invoice_number;$window.open(pdfUrl,'_blank')};return o}]);var NotificationsModule=angular.module('notificationsModule',[]);NotificationsModule.factory('Notification',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Notification=$resource(apiUrl('notifications/:notificationId'),{notificationId:'@id'},methods);angular.extend(Notification.prototype,_prototype);return Notification}]);NotificationsModule.factory('NotificationSetting',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Setting=$resource(apiUrl('notifications/settings/:settingId'),{settingId:'@id'},methods);angular.extend(Setting.prototype,_prototype);return Setting}]);NotificationsModule.controller("NotificationsMainCtrl",["$scope","ErrorsHandler","Notification","NotificationSetting","Messenger","$paginator",function($scope,ErrorsHandler,Notification,NotificationSetting,Messenger,$paginator){$scope.notifications=[];$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setOnPageChange(function(page){$scope.load(page)});$scope.data={};$scope.load=function(page){if(!page)page=$scope.paginator.getCurrentPage();Notification.get({page:page,limit:$scope.paginator.getLimit()},function(res){$scope.notifications=res.data;$scope.paginator.setFromResponse(res)},ErrorsHandler)};$scope.clearAll=function(){if(!confirm('Are you sure you want to mark all notifications as read?')){return}
Notification.get({clearAll:!0},function(res){$scope.load($scope.paginator.getCurrentPage());Messenger("All notifications have been marked as read.")},ErrorsHandler)};$scope.clearAllNotifications=function(){Notification.get({clearAll:!0},function(res){if(typeof $scope.$parent!=='undefined'&&$scope.$parent.notifications){$scope.$parent.notifications=[];$scope.$parent.unread=0}
Messenger("All notifications marked as read.")},ErrorsHandler)};$scope.loadSettings=function(){NotificationSetting.get(function(res){_.each(res.data,function(el){$scope.data[el.notification]={alert:el.alert,email:el.email}})},ErrorsHandler)};$scope.update=function(){NotificationSetting.save($scope.data,function(res){Messenger("I have successfully updated your settings.")},ErrorsHandler)};$scope.redirect=function(notification){switch(notification.related_type){case "repair":return(window.location.href="/tasks/repairs/"+notification.related_id);case "support":return(window.location.href="/tasks/support/"+notification.related_id);case "onsite":return(window.location.href="/tasks/onsite/"+notification.related_id);case "order":return(window.location.href="/tasks/orders/"+notification.related_id);case "message":return(window.location.href="/messages/"+notification.related_id)}};$scope.load();$scope.loadSettings()}]);var StatsModule=angular.module('StatsModule',[]);StatsModule.config(['$routeProvider',function($routeProvider){$routeProvider.when('/stats/',{controller:'StatsMainCtrl',templateUrl:'stats/overview.html'});$routeProvider.when('/stats/repairs',{controller:'StatsRepairsCtrl',templateUrl:'stats/repairs.html'});$routeProvider.when('/stats/user/:userId',{controller:'StatsUserCtrl',templateUrl:'stats/user.html'});$routeProvider.when('/stats/clients',{controller:'StatsClientsCtrl',templateUrl:'stats/clients.html'})}]);StatsModule.factory('UserTarget',function(){return{getCssClass:function(percentage){if(percentage<50)
return'bar-danger';else if(percentage<85)
return'bar-warning';else return'bar-success'}}});StatsModule.service('StatsHelpers',function(){this.dates={startDate:moment().subtract('month',1).set('date',28),endDate:moment().set('date',27)};this.daterangepicker={ranges:{'Today':[moment(),moment()],'Yesterday':[moment().subtract('days',1),moment().subtract('days',1)],'Last 7 Days':[moment().subtract('days',6),moment()],'Last 30 Days':[moment().subtract('days',29),moment()],'This Month':[moment().startOf('month'),moment().endOf('month')],'Payroll':[moment().subtract('month',1).set('date',28),moment().set('date',27)],'Last Month':[moment().subtract('month',1).startOf('month'),moment().subtract('month',1).endOf('month')]},locale:{firstDay:1}}});StatsModule.controller('StatsClientsCtrl',['$scope','$http','ErrorsHandler','apiUrl',function($scope,$http,ErrorsHandler,apiUrl){$scope.data=[];$scope.dates={startDate:moment().startOf('month'),endDate:moment().endOf('month')};$scope.daterangepicker={ranges:{'This Month':[moment().startOf('month'),moment().endOf('month')],'Last Month':[moment().subtract('month',1).startOf('month'),moment().subtract('month',1).endOf('month')],'Month before last':[moment().subtract('month',2).startOf('month'),moment().subtract('month',2).endOf('month')]},locale:{firstDay:1}};$scope.$watch('dates',function(){$scope.load()});$scope.load=function(){$http.get(apiUrl('stats/clients/activity'),{params:{start_date:$scope.dates.startDate.format('X'),end_date:$scope.dates.endDate.format('X')}}).success(function(response){$scope.data=response}).error(ErrorsHandler)}}]);StatsModule.controller('StatsMainCtrl',['$scope','$location','TaskFeedback','StatsHelpers',function($scope,$location,TaskFeedback,StatsHelpers){if($location.path()==''){$location.path('/stats');return}
$scope.helpers=StatsHelpers}]);StatsModule.controller('StatsRepairsCtrl',['$scope','$http','apiUrl','StatsHelpers',function($scope,$http,apiUrl,StatsHelpers){$scope.helpers=StatsHelpers;$scope.income={};$scope.overview={};$scope.dailyLineChart={data:[],series:['Booked In','Fixed'],labels:[]};var joinAxisResults=function(data1,data2){var rows=[];_.each(data1,function(el){rows.push({"c":[{v:el.date},{v:parseInt(el.total,10)},{v:0}]})});_.each(data2,function(el){var entry=_.find(rows,function(item){return item.c[0].v==el.date});if(entry){entry.c[2].v=parseInt(el.total,10)}else{rows.push({"c":[{v:el.date},{v:0},{v:parseInt(el.total,10)}]})}});rows=_.sortBy(rows,function(el){return el.c[0].v});return rows};$scope.loadIncome=function(){$http.get(apiUrl('stats/repairs/income'),{params:{start_date:StatsHelpers.dates.startDate.format('X'),end_date:StatsHelpers.dates.endDate.format('X')}}).success(function(response){$scope.income=response})};$scope.loadOverview=function(){$http.get(apiUrl('stats/repairs/overview'),{params:{start_date:StatsHelpers.dates.startDate.format('X'),end_date:StatsHelpers.dates.endDate.format('X')}}).success(function(response){$scope.overview=response})};$scope.loadDailyStats=function(){$http.get(apiUrl('stats/repairs/daily-summary'),{params:{start_date:StatsHelpers.dates.startDate.format('X'),end_date:StatsHelpers.dates.endDate.format('X')}}).success(function(response){var data=joinAxisResults(response.booked,response.completed);$scope.dailyLineChart.data=[[],[]];$scope.dailyLineChart.labels=[];_.each(data,function(item){$scope.dailyLineChart.labels.push(item.c[0].v);$scope.dailyLineChart.data[0].push(item.c[1].v);$scope.dailyLineChart.data[1].push(item.c[2].v)})})};$scope.$watch('helpers.dates',function(){$scope.loadDailyStats();$scope.loadIncome();$scope.loadOverview()})}]);StatsModule.controller('StatsUserCtrl',['$scope','User','ErrorsHandler','$routeParams','apiUrl','$http','UserTarget','StatsHelpers',function($scope,User,ErrorsHandler,$routeParams,apiUrl,$http,UserTarget,StatsHelpers){$scope.summary={};$scope.userTarget=UserTarget;$scope.routeParams=$routeParams;$scope.helpers=StatsHelpers;$scope.$on('$routeChangeSuccess',function(){if($routeParams.userId){$scope.load()}});$scope.$watch('helpers.dates',function(){$scope.load()});$scope.load=function(){if($scope.loading)
return!1;$scope.loading=!0;$http.get(apiUrl('stats/user/summary/'+$routeParams.userId),{params:{start_date:StatsHelpers.dates.startDate.format('X'),end_date:StatsHelpers.dates.endDate.format('X')}}).success(function(response){$scope.summary=response;$scope.loading=!1}).error(ErrorsHandler)}}]);StatsModule.directive('feedbackList',[function(){return{restrict:'A',scope:{reverse:'@',user:'@'},templateUrl:'stats/feedback-list.html',controller:['$scope','TaskFeedback','ErrorsHandler','$paginator',function($scope,TaskFeedback,ErrorsHandler,$paginator){$paginator=angular.copy($paginator);$paginator.pages_to_show=0;$paginator.edges=0;$scope.paginator=$paginator;$paginator.setOnPageChange(function(page){$scope.load(page)});$scope.$watch('user',function(value){if(value)$scope.load();});$scope.load=function(page){if(!page)page=1;var reverse=$scope.reverse?!0:!1;TaskFeedback.get({completed:!0,reverse:reverse,user:!0,page:page,user_id:$scope.user},function(response){$scope.feedbacks=response.data;$scope.paginator.setFromResponse(response)},ErrorsHandler)};$scope.load()}]}}])
StatsModule.directive('statsAverageFeedback',[function(){var template='<dl class="dl-horizontal">'+'<dt>Repairs:</dt>'+'<dd data-tooltip="{[{ data.repair }]}"><uib-rating ng-model="data.repair" max="max" read-only="true"></uib-rating></dd>'+'<dt>Remote support:</dt>'+'<dd data-tooltip="{[{ data.support }]}"><uib-rating ng-model="data.support" max="max" read-only="true"></uib-rating></dd>'+'<dt>Onsite:</dt>'+'<dd data-tooltip="{[{ data.onsite }]}"><uib-rating ng-model="data.onsite" max="max" read-only="true"></uib-rating></dd>'+'</dl>';return{template:template,restrict:'AE',scope:{'startDate':'=','endDate':'='},controller:['$scope','$http','apiUrl',function($scope,$http,apiUrl){$scope.data={};$scope.max=5;$scope.load=function(){var start_date=($scope.startDate)?$scope.startDate.format('X'):moment({hour:0,minute:0,second:0}).format('X');var end_date=($scope.endDate)?$scope.endDate.format('X'):moment({hour:23,minute:59,second:59}).format('X');$http.get(apiUrl('stats/overview/feedback-stats'),{params:{start_date:start_date,end_date:end_date}}).success(function(response){$scope.data=response})};$scope.$watchCollection('[startDate, endDate]',function(value){$scope.load()})}]}}])
StatsModule.directive('statsIncome',[function(){var template='<dl class="dl-horizontal">'+'<dt>Repairs:</dt>'+'<dd ng-bind="data.repairs | currency:\'£\'"></dd>'+'<dt>Remote support:</dt>'+'<dd ng-bind="data.support | currency:\'£\'"></dd>'+'<dt>Onsite:</dt>'+'<dd ng-bind="data.onsite | currency:\'£\'"></dd>'+'<dt>Booking Fees:</dt>'+'<dd ng-bind="data.booking_fee | currency:\'£\'"></dd>'+'<dt>Contracts:</dt>'+'<dd ng-bind="data.contracts | currency:\'£\'"></dd>'+'</dl>'+'<hr>'+'<dl class="dl-horizontal">'+'<dt>Total:</dt>'+'<dd ng-bind="data.total | currency:\'£\'"></dd>'+'<dt>Wages:</dt>'+'<dd ng-bind="data.wages | currency:\'£\'"></dd>'+'<dt>Profit:</dt>'+'<dd ng-bind="data.profit | currency:\'£\'" ng-class="{\'text-danger\': data.profit < 0}"></dd>'+'</dl>';return{template:template,restrict:'AE',scope:{'startDate':'=','endDate':'='},controller:['$scope','$http','apiUrl',function($scope,$http,apiUrl){$scope.data={};$scope.load=function(){var start_date=($scope.startDate)?$scope.startDate.format('X'):moment({hour:0,minute:0,second:0}).format('X');var end_date=($scope.endDate)?$scope.endDate.format('X'):moment({hour:23,minute:59,second:59}).format('X');$http.get(apiUrl('stats/overview/income'),{params:{start_date:start_date,end_date:end_date}}).success(function(response){$scope.data=response})};$scope.$watchCollection('[startDate, endDate]',function(value){$scope.load()})}]}}])
StatsModule.directive('statsPiechart',[function(){return{template:'<canvas class="chart chart-pie" chart-data="chart.data" chart-labels="chart.labels"></canvas>',restrict:'AE',scope:{type:'@',dataType:'@',qaType:'@',startDate:'=',endDate:'='},controller:['$scope','$timeout','$http','apiUrl','ErrorsHandler',function($scope,$timeout,$http,apiUrl,ErrorsHandler){$scope.chart={labels:[],data:[]};$scope.load=function(){var start_date=($scope.startDate)?$scope.startDate.format('X'):null;var end_date=($scope.endDate)?$scope.endDate.format('X'):null;$http.get(apiUrl('stats/overview/pie-chart'),{params:{type:$scope.type,qa_type:$scope.qaType?$scope.qaType:'',start_date:start_date,end_date:end_date}}).success(function(response){var labels=[];var data=[];if($scope.dataType=='count'){_.each(response,function(item){labels.push(item.user.name);data.push(item.cnt)})}else{_.each(response,function(item){labels.push(item.user.name);data.push(item.labour)})}
$scope.chart.data=data;$scope.chart.labels=labels}).error(ErrorsHandler)};$scope.$watchCollection('[startDate, endDate]',function(value){$timeout(function(){$scope.load()},0)})}]}}])
StatsModule.directive('statsTasksList',[function(){var template='<p class="lead">'+'All tasks associated with this user.'+'<span class="btn btn-default pull-right" data-ng-if="!requestOptions.showAll" data-ng-click="requestOptions.showAll = true">Show all</span>'+'<span class="btn btn-default pull-right" data-ng-if="requestOptions.showAll" data-ng-click="requestOptions.showAll = false">Display only results within selected dates</span>'+'</p>'+'<div>'+'<div ui-grid="grid" style="min-height: 650px;"></div>'+'<div paginator="paginator"></div>'+'</div>';return{template:template,restrict:'AE',scope:{'userId':'=','startDate':'=','endDate':'='},controller:['$scope','$http','apiUrl','Tasks','ErrorsHandler','$paginator',function($scope,$http,apiUrl,Tasks,ErrorsHandler,$paginator){var colorTemplate='<div class="ui-grid-cell-contents text-center">'+'<span ng-show="row.entity.completed == \'1\' && row.entity.paid == \'1\'" class="badge badge-success" tooltip="Paid">&nbsp;</span>'+'<span ng-show="row.entity.completed == \'0\'" class="badge badge-warning" tooltip="In progress">&nbsp;</span>'+'<span ng-show="row.entity.completed == \'1\' && row.entity.paid == \'0\' && row.entity.invoiced == \'1\'" class="badge badge-important" tooltip="Awaiting payment">&nbsp;</span>'+'<span ng-show="row.entity.completed == \'1\' && row.entity.paid == \'0\' && row.entity.invoiced == \'0\'" class="badge" tooltip="Needs invoicing">&nbsp;</span>'+'</div>';$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setOnPageChange(function(page){$scope.requestOptions.params.page=page;$scope.load()});$scope.requestOptions={showAll:!1,params:{page:1,summary:!0,client:!0,parts_total:!0}};$scope.grid={};$scope.grid.enableFiltering=!1;$scope.grid.useExternalPaging=!0;$scope.grid.useExternalSorting=!0;$scope.grid.useExternalFiltering=!0;$scope.grid.pagingPageSize=20;$scope.grid.enableHorizontalScrollbar=!1;$scope.grid.columnDefs=[{name:'color','displayName':'',width:50,enableSorting:!1,enableHiding:!1,enableColumnMenu:!1,cellTemplate:colorTemplate},{name:'id',enableColumnMenu:!1,cellTemplate:'<div class="ui-grid-cell-contents"><a ng-href="/tasks/{{ row.entity | taskUrl }}">{{ COL_FIELD }}</a></div>',width:75,type:'number'},{name:'type',width:75},{name:'title',enableColumnMenu:!1,enableSorting:!1},{name:'customer',enableHiding:!1,cellTemplate:'<div class="ui-grid-cell-contents">{{ row.entity.client.name }}</div>',menuItems:[{title:'Business',icon:'fa fa-fw fa-building',action:function(){$scope.requestOptions.params.client_type='client'}},{title:'Regular Customers',icon:'fa fa-fw fa-user',action:function(){$scope.requestOptions.params.client_type='client'}},{title:'All Customers',icon:'fa fa-fw fa-cross',action:function(){delete $scope.requestOptions.params.client_type}}]},{name:'labour',enableHiding:!1,cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD | currency:"£" }}</div>',type:'number'},{name:'points',enableHiding:!1,cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD }}</div>',type:'number'},{name:'parts_total',enableHiding:!1,cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD | currency:"£" }}</div>',type:'number'},{name:'status',enableHiding:!1,cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ row.entity.status.name }}</div>'},{name:'invoiced',width:80,cellTemplate:'<div class="ui-grid-cell-contents text-center"><i class="fa" ng-class="{\'fa-check\': row.entity.invoiced == \'1\'}"></i></div>'},{name:'paid',width:60,cellTemplate:'<div class="ui-grid-cell-contents text-center"><i class="fa" ng-class="{\'fa-check\': row.entity.paid == \'1\'}"></i></div>'},{name:'completed_at',cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD | fromNow }}</div>'},{name:'created_at',cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD | fromNow }}</div>'}];$scope.grid.data=[];$scope.grid.onRegisterApi=function(gridApi){$scope.gridApi=gridApi;$scope.gridApi.core.on.sortChanged($scope,function(grid,sortColumns){if(sortColumns.length>0){$scope.requestOptions.params.orderBy=sortColumns[0].field;$scope.requestOptions.params.orderType=sortColumns[0].sort.direction}else{$scope.requestOptions.params.orderBy=null;$scope.requestOptions.params.orderType=null}
$scope.load()});$scope.gridApi.core.on.filterChanged($scope,function(){var grid=this.grid})};$scope.load=function(){Tasks.get($scope.requestOptions.params,function(response){$scope.grid.data=response.data;$paginator.setFromResponse(response)},ErrorsHandler)};$scope.$watchCollection('[userId, startDate, endDate, requestOptions.showAll]',function(){$scope.requestOptions.params.start_date=($scope.startDate&&!$scope.requestOptions.showAll)?$scope.startDate.format('X'):null;$scope.requestOptions.params.end_date=($scope.endDate&&!$scope.requestOptions.showAll)?$scope.endDate.format('X'):null;$scope.requestOptions.params.assigned_to=$scope.userId?$scope.userId:null;$scope.load()})}]}}])
StatsModule.directive('statsUsersList',[function(){return{template:'<div ui-grid="grid" style="height: 500px;"></div>',restrict:'A',scope:{'startDate':'=','endDate':'='},controller:['$scope','$http','apiUrl','ErrorsHandler','$filter','$timeout',function($scope,$http,apiUrl,ErrorsHandler,$filter,$timeout){$scope.grid={};$scope.grid.options={enableSorting:!0};$scope.grid.enableFiltering=!1;$scope.grid.columnDefs=[{name:'name',cellTemplate:'<div class="ui-grid-cell-contents"><a ng-href="#/stats/user/{{ row.entity.id }}">{{ COL_FIELD }}</a></div>'},{name:'repairs',cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD }}</div>',type:'number'},{name:'remoteSupport',cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD }}</div>',type:'number'},{name:'onsite',cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD }}</div>',type:'number'},{name:'labour',cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD | currency:"£" }}</div>',type:'number'},{name:'parts',cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD | currency:"£" }}</div>',type:'number'},{name:'period_points',cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD }}</div>',type:'number'}];$scope.grid.data=[];$scope.load=function(){var start_date=($scope.startDate)?$scope.startDate.format('X'):moment({hour:0,minute:0,second:0}).format('X');var end_date=($scope.endDate)?$scope.endDate.format('X'):moment({hour:23,minute:59,second:59}).format('X');$http.get(apiUrl('stats/overview/users'),{params:{start_date:start_date,end_date:end_date}}).success(function(response){$scope.grid.data=response}).error(ErrorsHandler)};$scope.$watchCollection('[startDate, endDate]',function(value){$timeout(function(){$scope.load()},100)})}]}}])
StatsModule.directive('statsUsersRepairsList',[function(){return{template:'<div ui-grid="grid" style="height: 500px;"></div>',restrict:'A',scope:{'startDate':'=','endDate':'='},controller:['$scope','$http','apiUrl','ErrorsHandler','$filter','$timeout',function($scope,$http,apiUrl,ErrorsHandler,$filter,$timeout){$scope.grid={};$scope.grid.enableFiltering=!1;$scope.grid.enableHorizontalScrollbar=!1;$scope.grid.columnDefs=[{name:'name',cellTemplate:'<div class="ui-grid-cell-contents"><a ng-href="#/stats/user/{{ row.entity.id }}">{{ COL_FIELD }}</a></div>'},{name:'completed',cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD }}</div>'},{name:'open',cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD }}</div>'},{name:'parts',cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD | currency:"£" }}</div>'},{name:'labour',cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD | currency:"£" }}</div>'},{name:'points',cellTemplate:'<div class="ui-grid-cell-contents text-center">{{ COL_FIELD }}</div>'}];$scope.grid.data=[];$scope.load=function(){var start_date=($scope.startDate)?$scope.startDate.format('X'):moment({hour:0,minute:0,second:0}).format('X');var end_date=($scope.endDate)?$scope.endDate.format('X'):moment({hour:23,minute:59,second:59}).format('X');$http.get(apiUrl('stats/repairs/users'),{params:{start_date:start_date,end_date:end_date}}).success(function(response){$scope.grid.data=response}).error(ErrorsHandler)};$scope.$watchCollection('[startDate, endDate]',function(value){$timeout(function(){$scope.load()},100)})}]}}])
StatsModule.directive('labourPointsGraph',[function(){var template='<div class="widget widget-classic">'+'<div class="widget-content form-inline">'+'<p class="lead">'+'<select class="form-control" ng-model="filter.view" ng-options="o.id as o.name for o in views"></select>'+'<span class="pull-right">Income over the selected period of time</span>'+'</p>'+'<canvas class="chart chart-line" chart-data="linechart.data" chart-labels="linechart.labels" chart-series="linechart.series"></canvas>'+'</div>'+'</div>';return{template:template,replace:!0,restrict:'AE',scope:{'userId':'=','startDate':'=','endDate':'=','url':'@'},controller:['$scope','apiUrl','$http','ErrorsHandler',function($scope,apiUrl,$http,ErrorsHandler){$scope.filter={view:'weekly',};$scope.views=[{id:'daily',name:'Daily'},{id:'weekly',name:'Weekly'},{id:'monthly',name:'Monthly'},{id:'yearly',name:'Yearly'}];$scope.linechart={data:[],series:['Labour','Points','Wages'],labels:[]};$scope.load=function(){var url;var start_date=($scope.startDate)?$scope.startDate.format('X'):moment({hour:0,minute:0,second:0}).format('X');var end_date=($scope.endDate)?$scope.endDate.format('X'):moment({hour:23,minute:59,second:59}).format('X');if($scope.url){url=$scope.url}else{url=($scope.userId)?apiUrl('stats/user/graph/'+$scope.userId):apiUrl('stats/overview/graph')}
$http.get(url,{params:{start_date:start_date,end_date:end_date,view:$scope.filter.view}}).success(function(response){$scope.linechart.data=[[],[],[]];$scope.linechart.labels=[];_.each(response.labour,function(item){$scope.linechart.labels.push(item.date)});_.each(response.points,function(item){$scope.linechart.labels.push(item.date)});_.each(response.wages,function(item){$scope.linechart.labels.push(item.date)});$scope.linechart.labels=_.sortBy($scope.linechart.labels,function(item){return item});_.each($scope.linechart.labels,function(date){var labour=_.find(response.labour,function(item){return item.date==date});$scope.linechart.data[0].push(labour?labour.cnt:null);var points=_.find(response.points,function(item){return item.date==date});$scope.linechart.data[1].push(points?points.cnt:null);var wages=_.find(response.wages,function(item){return item.date==date});$scope.linechart.data[2].push(wages?wages.cnt:null)})}).error(ErrorsHandler)};$scope.$watchCollection('[filter.view, startDate, endDate]',function(value){$scope.load()})}]}}]);var StockModule=angular.module("StockModule",[]);StockModule.config(["$routeProvider",function($routeProvider){$routeProvider.when("/stock/",{controller:"StockMainCtrl",templateUrl:"stock/overview.html"});$routeProvider.when("/stock/new/",{controller:"StockNewCtrl",templateUrl:"stock/new.html"});$routeProvider.when("/stock/edit/:stockId",{controller:"StockNewCtrl",templateUrl:"stock/new.html"});$routeProvider.when("/stock/browse/",{controller:"StockBrowseCtrl",templateUrl:"stock/browse.html"});$routeProvider.when("/stock/categories/",{controller:"StockCategoriesCtrl",templateUrl:"stock/categories.html"});$routeProvider.when("/stock/categories/:categoryId",{controller:"StockCategoriesCtrl",templateUrl:"stock/categories.html"});$routeProvider.when("/stock/suppliers/",{controller:"StockSuppliersCtrl",templateUrl:"stock/suppliers.html"});$routeProvider.when("/stock/suppliers/:supplierId",{controller:"StockSuppliersCtrl",templateUrl:"stock/suppliers.html"});$routeProvider.when("/stock/order-summary/:orderId",{controller:"PurchaseOrderCtrl",templateUrl:"stock/order.html"});$routeProvider.when("/stock/orders/",{controller:"StockOrdersCtrl",templateUrl:"stock/orders.html"});$routeProvider.when("/stock/new_order/",{controller:"StockNewOrderCtrl",templateUrl:"stock/new_order.html"});$routeProvider.when("/stock/parts_on_order/",{controller:"PartsOnOrderCtrl",templateUrl:"stock/parts_on_order.html"});$routeProvider.when("/stock/staff_allocated/",{controller:"StaffAllocatedCtrl",templateUrl:"stock/staff_allocated.html"});$routeProvider.when("/stock/stock_takes/",{templateUrl:"stock/stock_takes.html"});$routeProvider.when("/stock/stock_reports/",{controller:"StockReportsCtrl",templateUrl:"stock/stock_reports.html"});$routeProvider.when("/stock/order_list/",{templateUrl:"stock/order_list.html",controller:"StockOrderListCtrl",controllerAs:"vm"})}]);StockModule.factory("Stock",["apiUrl","$resource","ResourceMethods","ResourcePrototype",function(apiUrl,$resource,methods,_prototype){var Stock=$resource(apiUrl("stock/:stockId"),{stockId:"@id"},methods);Stock.getSearchUrl=function(){return apiUrl("stock?query=")};angular.extend(Stock.prototype,_prototype);return Stock}]);StockModule.factory("StockItem",["apiUrl","$resource","ResourceMethods","ResourcePrototype",function(apiUrl,$resource,methods,_prototype){var StockItem=$resource(apiUrl("stockitems/:stockItemId"),{stockItemId:"@id"},methods);StockItem.getSearchUrl=function(){return apiUrl("stockitems?query=")};angular.extend(StockItem.prototype,_prototype);return StockItem}]);StockModule.factory("StockGroup",["apiUrl","$resource","ResourceMethods","ResourcePrototype",function(apiUrl,$resource,methods,_prototype){var StockGroup=$resource(apiUrl("stockgroups/:stockGroupId"),{stockGroupId:"@id"},methods);angular.extend(StockGroup.prototype,_prototype);return StockGroup}]);StockModule.factory("Supplier",["apiUrl","$resource","ResourceMethods","ResourcePrototype",function(apiUrl,$resource,methods,_prototype){var Supplier=$resource(apiUrl("suppliers/:supplierId"),{supplierId:"@id"},methods);angular.extend(Supplier.prototype,_prototype);return Supplier}]);StockModule.factory("Payment",["apiUrl","$resource","ResourceMethods","ResourcePrototype",function(apiUrl,$resource,methods,_prototype){var Payment=$resource(apiUrl("payments/:PaymentId"),{PaymentId:"@id"},methods);angular.extend(Payment.prototype,_prototype);return Payment}]);StockModule.factory("PurchaseOrder",["apiUrl","$resource","ResourceMethods","ResourcePrototype",function(apiUrl,$resource,methods,_prototype){var PurchaseOrder=$resource(apiUrl("orders/:orderId"),{orderId:"@id"},methods);angular.extend(PurchaseOrder.prototype,_prototype);return PurchaseOrder}]);StockModule.factory("PurchaseOrderItem",["apiUrl","$resource","ResourceMethods","ResourcePrototype",function(apiUrl,$resource,methods,_prototype){var PurchaseOrderItem=$resource(apiUrl("orders/items/:itemId"),{itemId:"@id"},methods);angular.extend(PurchaseOrderItem.prototype,_prototype);return PurchaseOrderItem}]);StockModule.factory("StockTransaction",["apiUrl","$resource","ResourceMethods","ResourcePrototype",function(apiUrl,$resource,methods,_prototype){var StockTransaction=$resource(apiUrl("transactions/:transactionId"),{transactionId:"@id"},methods);angular.extend(StockTransaction.prototype,_prototype);return StockTransaction}]);StockModule.factory("StockReport",["apiUrl","$resource","ResourceMethods","ResourcePrototype",function(apiUrl,$resource,methods,_prototype){var StockReport=$resource(apiUrl("stock_reports/:stockReportId"),{stockReportId:"@id"},methods);angular.extend(StockReport.prototype,_prototype);return StockReport}]);StockModule.controller('ItemOrdersCtrl',['$scope','PurchaseOrderItem','ErrorsHandler','$uibModalInstance','suppliers','stockItem','$paginator',function($scope,PurchaseOrderItem,ErrorsHandler,$uibModalInstance,suppliers,stockItem,$paginator){$scope.product=stockItem;$scope.suppliers=suppliers;$scope.previousOrders=[];$scope.filters={supplier_id:null};$scope.paginator=$paginator;$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setLimit(10);$scope.$watch('filters.supplier_id',function(newValue,oldValue){if(newValue!==oldValue)
$scope.loadPreviousOrders();});$paginator.setOnPageChange(function(page){$scope.loadPreviousOrders(page)});$scope.cancel=function(){$uibModalInstance.dismiss('cancel')};$scope.loadPreviousOrders=function(page){var parameters={page:page,limit:10,stock_id:$scope.product.id,supplier_id:$scope.filters.supplier_id};PurchaseOrderItem.get(parameters,function(response){$scope.previousOrders=response.data;$paginator.setFromResponse(response)})};$scope.loadPreviousOrders()}]);StockModule.controller('ItemTransactionsCtrl',['$scope','StockTransaction','ErrorsHandler','$uibModalInstance','stockItem','$paginator','User','related_type','related_id',function($scope,StockTransaction,ErrorsHandler,$uibModalInstance,stockItem,$paginator,User,related_type,related_id){$scope.product=stockItem;$scope.fixedRelatedType=related_type?!0:!1;$scope.transaction_types=[{name:'Task',value:'task'},{name:'Invoice',value:'invoice'},{name:'Sale',value:'sale'},{name:'Purchase Order',value:'purchase_order'},{name:'Custom',value:'custom'},{name:'User',value:'user'}];$scope.transactions=[];$scope.filters={transaction_type:null};User.get({active:!0,limit:50},function(response){$scope.users=response.data});User.get({},function(response){$scope.users=response.data});$scope.paginator=$paginator;$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setLimit(10);$scope.$watch('filters.transaction_type',function(newValue,oldValue){if(newValue!==oldValue)
$scope.loadTransactions();});$paginator.setOnPageChange(function(page){$scope.loadTransactions(page)});$scope.getTransactionType=function(value){var type=_.find($scope.transaction_types,function(item){return item.value===value});return type?type.name:''};$scope.getUserName=function(id){var user=_.find($scope.users,function(item){return item.id===id});return user?user.name:''};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')};$scope.loadTransactions=function(page){var parameters={page:page,limit:10,stock_id:$scope.product.id,related_type:related_type?related_type:$scope.filters.transaction_type,related_id:related_id?related_id:null};StockTransaction.get(parameters,function(response){$scope.transactions=response.data;$paginator.setFromResponse(response)})};$scope.loadTransactions()}]);StockModule.controller('ManualUpdateStockTransactionModalCtrl',['$scope','StockTransaction','ErrorsHandler','$uibModalInstance','product','User',function($scope,StockTransaction,ErrorsHandler,$uibModalInstance,product,User){$scope.loading=!1;$scope.product=product;$scope.transaction={quantity:1,reason:null,stock_id:$scope.product.id,related_type:"custom",related_id:null,type:"1"};$scope.save=function(){if($scope.transaction.quantity<1){alert("The quantity has to be at least 1.");return}
$scope.loading=!0;var params={quantity:$scope.transaction.quantity*$scope.transaction.type,reason:$scope.transaction.reason,stock_id:$scope.transaction.stock_id,related_type:$scope.transaction.related_type,related_id:null,};StockTransaction.save(params,function(response){$scope.loading=!1;$uibModalInstance.close(params.quantity)},function(a,b){$scope.loading=!1;ErrorsHandler(a,b)})};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);StockModule.controller('PartsOnOrderCtrl',['$scope','$paginator','ErrorsHandler','Messenger','$timeout','Part','Build','User',function($scope,$paginator,ErrorsHandler,Messenger,$timeout,Part,Build,User){var search_timeout=200;var searchTimer;$scope.parts=[];$scope.loading=!0;$scope.paginator=$paginator;$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setLimit(50);console.info('Limit: ',$paginator.getLimit());$scope.partsFilters={search:null,status:null};$scope.buildsFilters={search:null,status:null};User.get({active:!0},function(response){$scope.users=response.data});$scope.resources={'repair':'repairs','support':'suport','onsite':'onsites','order':'orders',};$scope.statuses=[{value:null,text:'All statuses'},{value:1,text:'In Stock'},{value:0,text:'Non in Stock'},];$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){$scope.initData()});$scope.initData=function(){$scope.loadParts();$scope.loadBuilds()};$scope.$watch('partsFilters.search',function(newValue,oldValue){if(newValue===oldValue)
return;$timeout.cancel(searchTimer);searchTimer=$timeout(function(){$scope.loadParts()},search_timeout)});$scope.$watch('buildsFilters.search',function(newValue,oldValue){if(newValue===oldValue)
return;$timeout.cancel(searchTimer);searchTimer=$timeout(function(){$scope.loadBuilds()},search_timeout)});$scope.relatedTo=function(item){switch(item.related_type){case 'task':return"<a href='/tasks/"+$scope.resources[item.related.type]+"/"+item.related_id+"'>"+item.related_id+"</a>";case 'user':return item.related.name;case 'client':return item.related.name}};$scope.loadParts=function(){$scope.loading=!0;Part.get({query:$scope.partsFilters.search,limit:$paginator.getLimit(),on_order:!0,related:!0},function(res){$scope.parts=res.data;$scope.loading=!1},ErrorsHandler)};$scope.loadBuilds=function(){$scope.loading=!0;Build.get({query:$scope.buildsFilters.search,on_order:!0,related:!0},function(res){$scope.builds=res.data;$scope.loading=!1},ErrorsHandler)};$scope.getUserName=function(userId){var user=_.find($scope.users,function(el){return el.id==userId});return user?user.name:'No one'};$scope.assignStockToPart=function(part,quantity){if(part.stock.unlimited==0&&(quantity>part.stock.quantity||quantity>part.quantity_on_order))
return;var parameters={id:part.id,stock_id:part.stock_id,quantity_received:quantity};Part.received(parameters,function(response){Messenger("The part has been marked as received.");$scope.loadParts()},ErrorsHandler)}
$scope.assignStockToComponent=function(stockItem,quantity){if(stockItem.unlimited==0&&(quantity>stockItem.quantity||quantity>stockItem.pivot.quantity))
return;var parameters={id:stockItem.pivot.build_id,stock_id:stockItem.id,quantity_received:quantity};Build.received(parameters,function(response){Messenger("The component has been marked as received.");$scope.loadBuilds()},ErrorsHandler)}}]);StockModule.controller("PaymentModalCtrl",["$scope","Payment","ErrorsHandler","$uibModalInstance","item",function($scope,Payment,ErrorsHandler,$uibModalInstance,item){$scope.data=item;$scope.save=function(){if(item.id){Payment.update($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}else{Payment.save($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}};$scope.cancel=function(){$uibModalInstance.dismiss("cancel")}}]);StockModule.controller('PurchaseOrderCtrl',['$scope','Supplier','Stock','$paginator','$routeParams','ErrorsHandler','$uibModal','$location','PurchaseOrder','$routeParams','PurchaseOrderItem','$uibModal','Messenger','Status',function($scope,Supplier,Stock,$paginator,$routeParams,ErrorsHandler,$moda,$location,PurchaseOrder,$routeParams,PurchaseOrderItem,$uibModal,Messenger,Status){$paginator=angular.copy($paginator);$scope.paginator=$paginator;$scope.limit=10;$paginator.setLimit(10);$scope.loading=!0;$scope.order={};Supplier.get(null,function(response){$scope.suppliers=response.data});$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){$scope.loadOrder()});$paginator.setOnPageChange(function(page){$scope.loadOrder(page)});$paginator.setOnLimitChange(function(page,limit){$scope.loadOrder(page)});Status.get({for:'orders'},function(response){$scope.statuses=response});$scope.loadOrder=function(page){PurchaseOrder.get({orderId:$routeParams.orderId,limit:$scope.limit,page:page},function(response){$scope.order=response;angular.forEach($scope.order.items,function(item){item.received=item.received_quantity;item.refused=item.refused_quantity});$scope.loading=!1})}
$scope.receive=function(item){$scope.errorMessage=null;if(item.received<item.received_quantity){item.received=item.received_quantity;$scope.errorMessage='It is not possible to reduce the number of received items for product '+item.stock.name+'.';return}
if(item.received+item.refused_quantity>item.quantity){item.received=item.received_quantity;$scope.errorMessage='The received items and refused items can not be higher than the total items ordered for product '+item.stock.name+'.';return}
var modalInstance=$uibModal.open({templateUrl:'stock/order_items_serials.html',controller:'PurchaseOrderItemModalCtrl',resolve:{item:function(){return item}}});modalInstance.result.then(function(_item){item.received_quantity=_item.received_quantity;item.received=item.received_quantity;Messenger("Item serials saved successfully!")},function(){item.received=item.received_quantity})};$scope.refuse=function(item){$scope.errorMessage=null;if(item.refused<item.refused_quantity){item.refused=item.refused_quantity;$scope.errorMessage='It is not possible to reduce the number of refused items for product '+item.stock.name+'.';return}
if(item.refused+item.received_quantity>item.quantity){item.received=item.received_quantity;$scope.errorMessage='The received items and refused items can not be higher than the total items ordered for product '+item.stock.name+'.';return}
$scope.kashflow_loading=!0;PurchaseOrderItem.update({id:item.id,refused_quantity:item.refused},function(response){$scope.kashflow_loading=!1;Messenger("The item has been updated successfully!");$scope.loadOrder()},function(error){$scope.kashflow_loading=!1;item.refused=item.refused_quantity;ErrorsHandler(error)})};$scope.updateOrder=function(order){angular.extend($scope.order,order);PurchaseOrder.update($scope.order,function(response){$scope.order=response;angular.forEach($scope.order.items,function(item){item.received=item.received_quantity;item.refused=item.refused_quantity});$scope.loading=!1})};$scope.updateItem=function(item){PurchaseOrderItem.update(item,function(response){$scope.loadOrder()})}}]);StockModule.controller('PurchaseOrderItemModalCtrl',['$scope','ErrorsHandler','$uibModalInstance','PurchaseOrderItem','item',function($scope,ErrorsHandler,$uibModalInstance,PurchaseOrderItem,item){$scope.loading=!1;$scope.item=item;$scope.serials=[];$scope.errorMessage=!1;$scope.data={sn_na:!0};for(var i=0;i<$scope.item.received-$scope.item.received_quantity;i++){$scope.serials.push({purchase_order_item_id:item.id,serial_number:null,added:!1})}
$scope.save=function(){$scope.loading=!0;var serials=$scope.pendingSerials();PurchaseOrderItem.update({id:item.id,serials:serials,sn_na:$scope.data.sn_na,received_quantity:$scope.item.received},function(response){$scope.item.received_quantity=response.item.received_quantity;$scope.loading=!1;if(response.added_all)
$uibModalInstance.close(response.item);else{$scope.errorMessage=!0;$scope.serials=response.serials}},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')};$scope.valid=function(){if($scope.data.sn_na)
return!0;var valid=!0;angular.forEach($scope.serials,function(serial){if(!serial.serial_number)
valid=!1});return valid}
$scope.pendingSerials=function(){var serials=[];angular.forEach($scope.serials,function(item){if(!item.added)
serials.push(item);});return serials}}]);StockModule.controller('StaffAllocatedCtrl',['$scope','$paginator','User','$uibModal',function($scope,$paginator,User,$uibModal){$scope.loading=!0;$paginator=angular.copy($paginator);$scope.paginator=$paginator;User.get({active:!0,parts:!0,limit:50},function(response){$scope.users=response.data;$scope.loading=!1});$scope.selectUser=function(user){$scope.selectedUser=user}}]);StockModule.controller('StockBrowseCtrl',['$scope','Stock','$timeout','$paginator','ErrorsHandler','StockItem','Messenger',function($scope,Stock,$timeout,$paginator,ErrorsHandler,StockItem,Messenger){$scope.products=[];$scope.search='';$scope.paginator=$paginator;$scope.limit=10;$paginator.setLimit(10);$scope.groups=[];$scope.types=[];$scope.suppliers=[];var search_timeout;var prod_timeouts={};$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){$scope.loadProducts()});$paginator.setOnPageChange(function(page){$scope.loadProducts($scope.search,page)});$paginator.setOnLimitChange(function(page,limit){$scope.loadProducts($scope.search,page)});$scope.$watch('limit',function(newValue,oldValue){if(newValue!==oldValue)
$paginator.setLimit($scope.limit);});$scope.loadProducts=function(query,page){StockItem.get({supplier:!0,limit:$scope.limit,query:query,page:page},function(res){$scope.products=res.data;$paginator.setFromResponse(res)})};$scope.searchCallback=function(query){$scope.search=query;$scope.loadProducts(query)};$scope.updateProduct=function(product){if(prod_timeouts[product.id])$timeout.cancel(prod_timeouts[product.id]);prod_timeouts[product.id]=$timeout(function(){StockItem.update(product,function(res){Stock.update(product.stock,function(res){Messenger('Item "'+product.stock.name+'" successfully updated!')},ErrorsHandler)},ErrorsHandler)},1500)}}]);StockModule.controller('StockCategoriesCtrl',['$scope','StockGroup','$paginator','$routeParams','ErrorsHandler','$uibModal','$http','apiUrl','Messenger',function($scope,StockGroup,$paginator,$routeParams,ErrorsHandler,$uibModal,$http,apiUrl,Messenger){$paginator=angular.copy($paginator);$scope.categories=[];$scope.paginator=$paginator;$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){$scope.loadCategories()});$paginator.setOnPageChange(function(page){$scope.loadCategories(page)});$paginator.setOnLimitChange(function(page,limit){$scope.loadCategories(page)});$scope.loadCategories=function(page){if(!page)page=1;StockGroup.get({page:page,limit:$paginator.getLimit()},function(res){$scope.categories=res.data;$paginator.setFromResponse(res)},ErrorsHandler)};$scope.showModal=function(item){var modalInstance=$uibModal.open({templateUrl:'stock/categories/modal.html',controller:'StockCategoryModalCtrl',resolve:{item:function(){return item||{}},kashflow_categories:function(){return[]},kashflow_purchase_categories:function(){return[]}}});modalInstance.result.then(function(_item){if(!item){$scope.categories.push(_item)}else{angular.extend(item,_item||{})}
Messenger("Changes made successfully!")})};$scope.delete=function(id){StockGroup.remove({stockGroupId:id},function(){$scope.categories=_.reject($scope.categories,function(el){return el.id==id});Messenger("Category deleted successfully!")},ErrorsHandler)};$scope.getKashflowCategoryName=function(id){return''}}]);StockModule.controller('StockCategoryModalCtrl',['$scope','StockGroup','ErrorsHandler','$uibModalInstance','item','kashflow_categories','kashflow_purchase_categories',function($scope,StockGroup,ErrorsHandler,$uibModalInstance,item,kashflow_categories,kashflow_purchase_categories){$scope.data=item;$scope.kashflow_categories=kashflow_categories;$scope.kashflow_purchase_categories=kashflow_purchase_categories;$scope.save=function(){if(item.id){StockGroup.update($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}else{StockGroup.save($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);StockModule.controller('StockMainCtrl',['$scope','$location','apiUrl','$http','StockGroup','ErrorsHandler',function($scope,$location,apiUrl,$http,StockGroup,ErrorsHandler){$scope.sharedFilters={limit:10,search:null,type:null,under_buffer:0,in_stock:null,ordered:0,discontinued:null};$scope.loadCategories=function(){StockGroup.get({limit:100},function(response){$scope.categories=response.data},ErrorsHandler)};$scope.loadCategories()}]);StockModule.controller('StockModalCtrl',['$scope','Stock','ErrorsHandler','$uibModalInstance','categories','product_types','item','$http','apiUrl','Severity','$timeout','Build','quick_mode',function($scope,Stock,ErrorsHandler,$uibModalInstance,categories,product_types,item,$http,apiUrl,Severity,$timeout,Build,quick_mode){$scope.quick_mode=quick_mode;$scope.errors=null;$scope.categories=categories;$scope.product_types=product_types;$scope.product=item;$scope.loading=!1;$scope.search={stock:null};var stockSearchTimeout;Severity.get(null,function(resposense){$scope.severities=resposense.data});if(!item.id){$scope.product={unlimited:0,severity_id:2,approval_required:0,quantity_to_order:0,type:'stock',quantity:0,buffer:0,builds:[],discontinued:0,}}
if($scope.quick_mode){$scope.product.composite=!1}
Build.get({stock_id:item.id,status:'in_progress'},function(response){$scope.product.builds=response.data});$scope.$watch('product.type',function(value,oldValue){if(value!=oldValue&&value=='service')
$scope.product.unlimited=1});$scope.$watch('search.stock',function(value,oldValue){if(value!==oldValue){$timeout.cancel(stockSearchTimeout);stockSearchTimeout=$timeout(function(){Stock.get({query:value},function(response){$scope.stock=response.data})},250)}});$scope.save=function(){$scope.loading=!0;if(item.id){Stock.update($scope.product,function(response){angular.extend(item,response||{});$uibModalInstance.close(response)},function(a,b){ErrorsHandler(a,b);$scope.loading=!1})}else{Stock.save($scope.product,function(response){$uibModalInstance.close(response)},function(response){$scope.errors="<strong><i class='fa fa-warning'></i>Correct the following errors:</strong><br><ul>";response.data.errors.forEach(function(error){$scope.errors+="<li>"+error+"</li>"});$scope.errors+="</ul>";$scope.loading=!1})}};$scope.addCompositeItem=function(ev,item){if(!$scope.product.composite_items){$scope.product.composite_items=[]}else{var existingItem=_.findWhere($scope.product.composite_items,{id:item.id});if(existingItem){existingItem.pivot.quantity++;return}}
item.pivot={quantity:1};$scope.product.composite_items.push(item)};$scope.approveProduct=function(){$scope.product.approval_required=!1;Stock.save({id:$scope.product.id,approval_required:0},function(){Message("The task has been approved.")})};$scope.generateRandomEAN=function(){var max=199999999999;var min=100000000000;var ean=Math.floor(Math.random()*(max-min+1)+min).toString();var val=0;var digits=ean.split('');_.each(digits,function(digit,i){val+=parseInt(digit,10)*((i%2==0)?1:3)});var checksum_digit=10-(val%10);if(checksum_digit==10)checksum_digit=0;ean+=checksum_digit;$scope.product.model_number=ean};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);StockModule.controller("StockNewOrderCtrl",["$scope","Supplier","Payment","Stock","$paginator","$routeParams","ErrorsHandler","$uibModal","$timeout","$location","PurchaseOrder","$paginator",function($scope,Supplier,Payment,Stock,$paginator,$routeParams,ErrorsHandler,$uibModal,$timeout,$location,PurchaseOrder,$paginator){var search_timeout=200;var searchTimer;$scope.paginator=$paginator;$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setLimit(20);$scope.suppliers=[];$scope.payments=[{id:0,test:"name"}];$scope.products=[];$scope.underBufferProducts=[];$scope.searchedProducts=[];$scope.vat=0.2;$scope.loading=!1;$scope.product_types=["stock","non_stock","service"];$scope.data={search_type:"requested",search:"",limit:20};$scope.order={reference_number:null,supplier_id:null,payment_id:null,delivery_cost:0,delivery_vat_included:0,discount_amount:0,discount_vat_included:0,items_vat_included:0,products:[]};$scope.loadSuppliers=function(page){Supplier.get({all:1},function(res){$scope.suppliers=res.data},ErrorsHandler)};$scope.loadSuppliers();$scope.loadPayments=function(page){Payment.get({all:1},function(res){$scope.payments=res.data},ErrorsHandler)};$scope.loadPayments();$scope.$watch("data.search_type",function(value){$scope.data.search="";if(value=="under_buffer")$scope.loadUnderBufferProducts();if(value=="requested")$scope.loadRequestedProducts();if(value=="all")$scope.searchProducts();});$scope.$watch("data.search",function(newValue,oldValue){$timeout.cancel(searchTimer);searchTimer=$timeout(function(){if($scope.data.search_type=="under_buffer")
$scope.loadUnderBufferProducts(newValue);if($scope.data.search_type=="requested")
$scope.loadRequestedProducts(newValue);if($scope.data.search_type=="all")$scope.searchProducts(newValue);},search_timeout)});$paginator.setOnPageChange(function(page){if($scope.data.search_type=="under_buffer")
$scope.loadUnderBufferProducts($scope.data.search,page);if($scope.data.search_type=="requested")
$scope.loadRequestedProducts($scope.data.search,page);if($scope.data.search_type=="all")
$scope.searchProducts($scope.data.search,page);});$scope.$watch("data.limit",function(newValue,oldValue){if(newValue!==oldValue)$paginator.setLimit($scope.data.limit);});$scope.loadUnderBufferProducts=function(query,page){var parameters={under_buffer:1,query:query,page:page};Stock.get(parameters,function(response){$scope.updateTemporalValues(response.data);$scope.underBufferProducts=response.data;$scope.products=$scope.underBufferProducts;$paginator.setFromResponse(response)},ErrorsHandler)};$scope.loadRequestedProducts=function(query,page){var parameters={requested:1,query:query,page:page};Stock.get(parameters,function(response){$scope.updateTemporalValues(response.data);$scope.products=response.data;$paginator.setFromResponse(response)},ErrorsHandler)};$scope.searchProducts=function(query,page){$scope.loading=!0;var parameters={limit:$scope.data.limit,query:query,page:page};Stock.get(parameters,function(response){$scope.updateTemporalValues(response.data);$scope.products=response.data;$scope.searchedProducts=response.data;$paginator.setFromResponse(response);$scope.loading=!1},ErrorsHandler)};$scope.addToOrderProducts=function(item){if(item.approval_required==1){alert("This product needs approval before add it to the order.");return}
var product=_.find($scope.order.products,function(el){return el.id==item.id});if(!product){item.original_quantity_to_order=item.quantity_to_order;item.units=item.quantity_to_order!=0?item.quantity_to_order:1;item.unit_cost=0;item.quantity_to_order=item.quantity_to_order-item.units<0?0:item.quantity_to_order-item.units;$scope.order.products.push(item)}else{item.units++;item.quantity_to_order=item.quantity_to_order-1>0?item.quantity_to_order-1:0}};$scope.updateTemporalValues=function(productsList){angular.forEach(productsList,function(responseProduct,index){angular.forEach($scope.order.products,function(orderedProduct,orderedIndex){if(orderedProduct.id==responseProduct.id){productsList[index]=orderedProduct}})})};$scope.removeFromOrderProducts=function(item){angular.forEach($scope.order.products,function(value,index){if(value.id==item.id){$scope.order.products.splice(index,1);item.quantity_to_order=item.original_quantity_to_order;item.units=0;item.unit_cost=0}})};$scope.updateValues=function(item){item.quantity_to_order=item.original_quantity_to_order-item.units<0?0:item.original_quantity_to_order-item.units};$scope.containsObject=function(obj,list){return(_.filter(list,function(listItem){return angular.equals(listItem,obj)}).length>0)};$scope.validateOrder=function(){if($scope.order.products.length==0)return!0;if(!$scope.order.reference_number)return!0;if(!$scope.order.supplier_id)return!0;if(!$scope.order.payment_id)return!0;var incomplete=!1;angular.forEach($scope.order.products,function(value,index){if(value.unit_cost==0||value.units==0)incomplete=!0});return incomplete};$scope.saveOrder=function(){$scope.kashflow_loading=!1;PurchaseOrder.save($scope.order,function(response){$location.path("/stock/orders/")},function(response){$scope.errors="<strong><i class='fa fa-warning'></i>Correct the following errors:</strong><br><ul>";$scope.kashflow_loading=!1;response.data.errors.forEach(function(error){$scope.errors+="<li>"+error+"</li>"});$scope.errors+="</ul>"},function(a,b){$scope.kashflow_loading=!1;ErrorsHandler(a,b)})};$scope.addNewProduct=function(){var modalInstance=$uibModal.open({templateUrl:"stock/stock-form.html",controller:"StockModalCtrl",size:"lg",resolve:{categories:function(){return $scope.categories},product_types:function(){return $scope.product_types},item:function(){return{}},quick_mode:function(){return!1}}});modalInstance.result.then(function(_item){$scope.addToOrderProducts(_item.data)})};$scope.addSupplier=function(){var modalInstance=$uibModal.open({templateUrl:"stock/suppliers/modal.html",controller:"StockSupplierModalCtrl",resolve:{item:function(){return{}}}});modalInstance.result.then(function(_item){$scope.suppliers.push(_item)})};$scope.addPayment=function(){var modalInstance=$uibModal.open({templateUrl:"stock/payment/modal.html",controller:"PaymentModalCtrl",resolve:{item:function(){return{}}}});modalInstance.result.then(function(_item){$scope.suppliers.push(_item)})};$scope.edit=function(product){product.price_vat_included=!1;var modalInstance=$uibModal.open({templateUrl:"stock/stock-form.html",controller:"StockModalCtrl",size:"lg",resolve:{categories:function(){return $scope.categories},product_types:function(){return $scope.product_types},item:function(){return product},quick_mode:function(){return!1}}});modalInstance.result.then(function(response){$scope.data.search_type=$scope.data.search_type})};$scope.itemOrders=function(item){var modalInstance=$uibModal.open({templateUrl:"stock/item_orders.html",controller:"ItemOrdersCtrl",resolve:{suppliers:function(){return $scope.suppliers},stockItem:function(){return item}}})};$scope.total=function(){var total=0;if($scope.order.delivery_vat_included)
total+=$scope.order.delivery_cost;else total+=$scope.order.delivery_cost+$scope.order.delivery_cost*$scope.vat;if($scope.order.discount_vat_included)
total-=$scope.order.discount_amount;else total-=$scope.order.discount_amount+$scope.order.discount_amount*$scope.vat;var cost=0;angular.forEach($scope.order.products,function(product){cost+=product.unit_cost*product.units});if($scope.order.items_vat_included=="1"){total+=cost}else{total+=cost+cost*$scope.vat}
return total.toFixed(2)}}]);(function(){'use strict';StockModule.controller('StockOrderListCtrl',StockOrderListCtrl);StockOrderListCtrl.$inject=['$scope','$paginator','Stock','ErrorsHandler'];function StockOrderListCtrl($scope,$paginator,Stock,ErrorsHandler){var vm=this;vm.query='';vm.products=[];vm.paginator=angular.copy($paginator);vm.loadProducts=loadProducts;vm.paginator.setOnPageChange(function(page){vm.loadProducts(page)});$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){vm.loadProducts()});$scope.$watch('vm.query',function(newValue,oldValue){if(newValue!==oldValue){vm.loadProducts()}});function loadProducts(page){if(!page)page=1;var parameters={page:page,query:vm.query,requested:!0,related:!0};var parameters_ub={page:page,query:vm.query,requested:!1,related:!1,under_buffer:!0};Stock.get(parameters,function(response){vm.loading=!1;_.each(response.data,function(el){if(el.group)el.category_name=el.group.name;el.price_vat_included=0});vm.products=response.data;vm.paginator.setFromResponse(response)},ErrorsHandler);Stock.get(parameters_ub,function(response){vm.loading=!1;_.each(response.data,function(el){if(el.group)el.category_name=el.group.name;el.price_vat_included=0});vm.products_ub=response.data;vm.paginator.setFromResponse(response)},ErrorsHandler)}}})();StockModule.controller('StockOrdersCtrl',['$scope','PurchaseOrder','$paginator','$routeParams','ErrorsHandler','$uibModal','Supplier','Status','Messenger','$timeout',function($scope,PurchaseOrder,$paginator,$routeParams,ErrorsHandler,$uibModal,Supplier,Status,Messenger,$timeout){var search_timeout=200;var searchTimer;$scope.orders=[];$scope.suppliers=[];$scope.statuses=[];$scope.paginator=$paginator;$paginator.setLimit(10);$scope.loading=!0;$scope.stats={'cancelled_orders':0,'delivered_orders':0,'total_orders':0};$scope.filters={search:null,limit:10,status_id:null,serial_number:null};$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){$scope.loadOrders()});$paginator.setOnPageChange(function(page){$scope.loadOrders(page)});$paginator.setOnLimitChange(function(page,limit){$scope.loadOrders(page)});$scope.$watch('filters.search',function(newValue,oldValue){if(newValue===oldValue)
return;$timeout.cancel(searchTimer);searchTimer=$timeout(function(){$scope.loadOrders()},search_timeout)});$scope.$watch('filters.limit',function(newValue,oldValue){if(newValue!==oldValue)
$paginator.setLimit($scope.filters.limit);});$scope.$watch('filters.status_id',function(newValue,oldValue){if(oldValue!==newValue)
$scope.loadOrders();});$scope.$watch('filters.serial_number',function(newValue,oldValue){if(newValue===oldValue)
return;$timeout.cancel(searchTimer);searchTimer=$timeout(function(){$scope.loadOrders()},search_timeout)});$scope.loadOrders=function(page){$scope.loading=!0;if(!page)page=1;PurchaseOrder.get({page:page,limit:$paginator.getLimit(),serial_number:$scope.filters.serial_number,query:$scope.filters.search,status_id:$scope.filters.status_id},function(res){$scope.orders=res.data;$scope.stats=res.stats;$paginator.setFromResponse(res);$scope.loading=!1},ErrorsHandler)};$scope.loadSuppliers=function(){Supplier.get({all:1},function(res){$scope.suppliers=res.data},ErrorsHandler)};$scope.loadStatuses=function(){Status.get({for:'orders'},function(res){$scope.statuses=res.data},ErrorsHandler)};$scope.updateOrder=function(parameters){PurchaseOrder.update(parameters,function(res){angular.forEach($scope.orders,function(item){if(item.id==res.id)
item.status_id=res.status_id},log);$scope.kashflow_loading=!1;Messenger('The order has been modified successfully!')},function(a,b){$scope.kashflow_loading=!1;ErrorsHandler(a,b)})}
$scope.showModal=function(item){};$scope.cancel=function(item){if(item.status.name!='Ordered')
return;$scope.kashflow_loading=!0;PurchaseOrder.remove({orderId:item.id},function(res){$scope.loadOrders();$scope.kashflow_loading=!1;Messenger('The order has been marked as cancelled')},function(a,b){$scope.kashflow_loading=!1;ErrorsHandler(a,b)})};$scope.loadSuppliers();$scope.loadStatuses()}]);StockModule.controller('StockReportsCtrl',['$scope','StockReport','ErrorsHandler','Messenger','$paginator','$timeout',function($scope,StockReport,ErrorsHandler,Messenger,$paginator,$timeout){var search_timeout=null;var currentRequest=null;$scope.reports=[];$scope.paginator=$paginator;$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setLimit(10);$scope.filters={limit:10,aknowledged:0,type:'mismatched',search:null};var initialized=!1;$scope.$watch('activeTab',function(value){if(value==$scope.index&&!initialized){initialized=!0;$scope.loadReports()}});$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){if(!initialized){initialized=!0;$scope.loadReports()}});$paginator.setOnPageChange(function(page){$scope.loadReports(page)});$paginator.setOnLimitChange(function(page){$scope.loadReports(page)});$scope.$watch('filters.limit',function(newValue,oldValue){if(newValue!==oldValue){$paginator.setLimit($scope.filters.limit)}});$scope.$watch('filters.search',function(newValue,oldValue){if(newValue===oldValue)return;if(search_timeout)$timeout.cancel(search_timeout);search_timeout=$timeout(function(){$scope.loadReports()},250)});$scope.$watch('filters.aknowledged',function(newValue,oldValue){if(newValue!==oldValue){$scope.loadReports()}});$scope.$watch('filters.type',function(newValue,oldValue){if(newValue!==oldValue){$scope.loadReports()}});$scope.loadReports=function(page){if(currentRequest&&currentRequest.$cancelRequest){currentRequest.$cancelRequest()}
if($scope.loading){return}
$scope.loading=!0;var data={aknowledged:$scope.filters.aknowledged,type:$scope.filters.type,query:$scope.filters.search,page:page};currentRequest=StockReport.get(data,function(response){$scope.loading=!1;$scope.reports=response.data;$paginator.setFromResponse(response);currentRequest=null},function(error){$scope.loading=!1;currentRequest=null;ErrorsHandler(error)})};$scope.markAsAknowledged=function(report){var data={id:report.id,aknowledged:1};StockReport.update(data,function(response){report.aknowledged=1;Messenger('Report marked as aknowledged successfully!')})};$scope.$on('$destroy',function(){if(currentRequest&&currentRequest.$cancelRequest){currentRequest.$cancelRequest()}
if(search_timeout){$timeout.cancel(search_timeout)}});if(!$scope.activeTab&&!$scope.$root.$$phase){initialized=!0;$scope.loadReports()}}]);StockModule.controller('StockSupplierModalCtrl',['$scope','Supplier','ErrorsHandler','$uibModalInstance','item',function($scope,Supplier,ErrorsHandler,$uibModalInstance,item){$scope.data=item;$scope.save=function(){if(item.id){Supplier.update($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}else{Supplier.save($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);StockModule.controller('StockSuppliersCtrl',['$scope','Supplier','$paginator','$routeParams','ErrorsHandler','$uibModal',function($scope,Supplier,$paginator,$routeParams,ErrorsHandler,$uibModal){$scope.suppliers=[];$scope.paginator=$paginator;$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){$scope.loadSuppliers()});$paginator.setOnPageChange(function(page){$scope.loadSuppliers(page)});$paginator.setOnLimitChange(function(page,limit){$scope.loadSuppliers(page)});$scope.loadSuppliers=function(page){if(!page)page=1;Supplier.get({page:page,limit:$paginator.getLimit()},function(res){$scope.suppliers=res.data;$paginator.setFromResponse(res)},ErrorsHandler)};$scope.showModal=function(item){var modalInstance=$uibModal.open({templateUrl:'stock/suppliers/modal.html',controller:'StockSupplierModalCtrl',resolve:{item:function(){return item||{}}}});modalInstance.result.then(function(_item){if(!item){$scope.suppliers.push(_item)}else{angular.extend(item,_item||{})}})};$scope.delete=function(id){Supplier.remove({supplierId:id},function(){$scope.suppliers=_.reject($scope.suppliers,function(el){return el.id==id})},ErrorsHandler)}}]);StockModule.controller('StockTableCtrl',['$scope','$element','StockItem','Stock','Supplier','ErrorsHandler','Messenger','$filter','$timeout','$paginator','$uibModal','$routeParams',function($scope,$element,StockItem,Stock,Supplier,ErrorsHandler,Messenger,$filter,$timeout,$paginator,$uibModal,$routeParams){var template={manufacturer_code:'EAN001',group_id:$scope.categoryId,type:'stock',buffer:0,margin:0,business_margin:0,cost:0,retail:0,quantity:0,discontinued:0};$scope.data=[];$scope.products=[];$scope.suppliers=[];$scope.product_types=["stock","non_stock","service"];$scope.paginator=$paginator;$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setLimit(10);if($scope.$parent&&$scope.$parent.sharedFilters){$scope.filters=$scope.$parent.sharedFilters;if($routeParams.query&&!$scope.filters.search){$scope.filters.search=$routeParams.query}}else{$scope.filters={limit:10,search:$routeParams.query,type:null,under_buffer:0,in_stock:null,ordered:0,discontinued:null}}
$scope.$watch('activeTab',function(value){if(value==$scope.index)$scope.loadProducts($scope.filters.search);});$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){$scope.loadProducts($scope.filters.search)});$paginator.setOnPageChange(function(page){$scope.loadProducts($scope.filters.search,page)});$paginator.setOnLimitChange(function(page,limit){$scope.loadProducts($scope.filters.search,page)});$scope.$watchCollection('filters',function(newValue,oldValue){if(newValue!==oldValue){$paginator.setLimit($scope.filters.limit);if(newValue&&oldValue&&Object.keys(newValue).some(function(key){return key!=='limit'&&newValue[key]!==oldValue[key]})){$scope.loadProducts($scope.filters.search,1)}}});$scope.loadProducts=function(query,page){$scope.response=null;var cat_id=($scope.categoryId=='all')?!1:$scope.categoryId;$scope.loading=!0;var parameters={category:cat_id,limit:$scope.filters.limit,page:page,query:query,type:$scope.filters.type,under_buffer:$scope.filters.under_buffer,in_stock:$scope.filters.in_stock,ordered:$scope.filters.ordered,show_discontinued:$scope.filters.discontinued};Stock.get(parameters,function(response){$scope.loading=!1;_.each(response.data,function(el){if(el.group)el.category_name=el.group.name;el.price_vat_included=0});$scope.data=response.data;$paginator.setFromResponse(response)},ErrorsHandler);if(!$scope.suppliersLoaded)
$scope.loadSuppliers();};$scope.loadSuppliers=function(){Supplier.get(function(response){$scope.suppliers=response.data;$scope.suppliersLoaded=!0},ErrorsHandler)};$scope.$watch('data',function(){$scope.products=$scope.data});$scope.edit=function(product){var modalInstance=$uibModal.open({templateUrl:'stock/stock-form.html',controller:'StockModalCtrl',size:"lg",resolve:{categories:function(){return $scope.categories},product_types:function(){return $scope.product_types},item:function(){return product},quick_mode:function(){return!1}}});modalInstance.result.then(function(response){response.data.price_vat_included=0;if(!product){$scope.categories.push(response.data)}else{angular.extend(product,response.data||{})}})};$scope.updateStockQuantity=function(item){if(item.unlimited)
return;var modalInstance=$uibModal.open({templateUrl:'stock/stock-update.html',controller:'ManualUpdateStockTransactionModalCtrl',resolve:{product:function(){return item}}});modalInstance.result.then(function(transactionQuantity){item.quantity=item.quantity+transactionQuantity})}
$scope.delete=function(item){Stock.delete({stockId:item.id},function(response){$scope.products=_.reject($scope.products,function(el){return el.id==item.id})},ErrorsHandler)};$scope.update=function(item){Stock.update(item,function(response){Messenger("Item updated successfully!");angular.extend(item,response.data||{})},ErrorsHandler)};$scope.save=function(item){if(item.id){$scope.update(item)}else{_.defaults(item,template||{});$scope.create(item)}};$scope.add=function(){var modalInstance=$uibModal.open({templateUrl:'stock/stock-form.html',controller:'StockModalCtrl',size:"lg",resolve:{categories:function(){return $scope.categories},product_types:function(){return $scope.product_types},item:function(){return{}},quick_mode:function(){return!1}}});modalInstance.result.then(function(_item){$scope.loadProducts()})};$scope.showHistory=function(item){var modalInstance=$uibModal.open({templateUrl:'stock/item-history.html',controller:'ItemTransactionsCtrl',resolve:{stockItem:function(){return item},related_type:function(){return null},related_id:function(){return null}}})}}]);StockModule.directive('stockTable',function(){return{restrict:'AE',scope:{categoryId:'@',index:'@',activeTab:'=',categories:'='},templateUrl:'stock/stock-table.html',controller:'StockTableCtrl'}});StockModule.controller('StockTakesTableCtrl',['$scope','$element','StockItem','Stock','ErrorsHandler','Messenger','$filter','$timeout','$paginator','$uibModal','$routeParams','$q','StockReport',function($scope,$element,StockItem,Stock,ErrorsHandler,Messenger,$filter,$timeout,$paginator,$uibModal,$routeParams,$q,StockReport){var search_timeout=200;$scope.data=[];$scope.products=[];$scope.paginator=$paginator;$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setLimit(10);$scope.filters={limit:10,search:$routeParams.query,last_report_date:'more_than_two_weeks',under_buffer:0,};$scope.$watch('activeTab',function(value){if(value==$scope.index)$scope.loadProducts();});$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){$scope.loadProducts()});$paginator.setOnPageChange(function(page){$scope.loadProducts(page)});$paginator.setOnLimitChange(function(page,limit){$scope.loadProducts(page)});$scope.$watch('filters.limit',function(newValue,oldValue){if(newValue!==oldValue)
$paginator.setLimit($scope.filters.limit);});$scope.$watch('filters.search',function(newValue,oldValue){if(newValue===oldValue)return;if(search_timeout)$timeout.cancel(search_timeout);search_timeout=$timeout(function(){$scope.products=$scope.loadProducts()},250)});$scope.$watch('filters.last_report_date',function(newValue,oldValue){if(newValue===oldValue)return;$scope.loadProducts()});$scope.$watch('filters.under_buffer',function(newValue,oldValue){if(newValue===oldValue)return;$scope.loadProducts()});$scope.loadProducts=function(page){$scope.response=null;var cat_id=($scope.categoryId=='all')?!1:$scope.categoryId;$scope.loading=!0;var parameters={category:cat_id,limit:$scope.filters.limit,page:page,query:$scope.filters.search,type:'stock',under_buffer:$scope.filters.under_buffer};if($scope.filters.last_report_date)
var parameters=$scope.addLasReportDateFilters(parameters);Stock.get(parameters,function(response){$scope.loading=!1;_.each(response.data,function(el){if(el.group)el.category_name=el.group.name});$scope.data=response.data;$paginator.setFromResponse(response)},ErrorsHandler)};$scope.$watch('data',function(){$scope.products=$scope.data});$scope.updateQuantity=function(item){var deferred=$q.defer();var data={stock_id:item.id,quantity:isNaN(item.new_quantity)?item.quantity:item.new_quantity};StockReport.save(data,function(response){item.last_report=response.data;item.quantity=response.data.new_quantity;item.new_quantity=null;Messenger("Report saved successfully!");deferred.resolve(!0)},function(a,b){deferred.resolve(!1);ErrorsHandler(a,b)});return deferred.promise};$scope.addLasReportDateFilters=function(parameters){if($scope.filters.last_report_date=="this_week"){parameters.reported_after=moment().subtract(7,'days').format("YYYY-MM-DD HH:mm:ss")}else if($scope.filters.last_report_date=="last_week"){parameters.reported_before=moment().subtract(7,'days').format("YYYY-MM-DD HH:mm:ss");parameters.reported_after=moment().subtract(14,'days').format("YYYY-MM-DD HH:mm:ss")}
if($scope.filters.last_report_date=="more_than_two_weeks"){parameters.reported_before=moment().subtract(14,'days').format("YYYY-MM-DD HH:mm:ss")}
return parameters}}]);StockModule.directive('stockTakesTable',function(){return{restrict:'AE',scope:{categoryId:'@',index:'@',activeTab:'=',categories:'='},templateUrl:'stock/stock-takes-table.html',controller:'StockTakesTableCtrl'}});StockModule.service('_stockForm',['$rootScope','Stock','ErrorsHandler','Messenger','$location',function($rootScope,Stock,ErrorsHandler,Messenger,$location){var $this=this;$this.options={showErrors:!0,showMessage:!0,callback:function(){},message:"I've successfully saved the stock item!"};$this.data={};$this.getData=function(){return $this.data};$this.setData=function(new_data){angular.copy(new_data,$this.data)};$this.save=function(){errors_handler=($this.options.showErrors)?ErrorsHandler:null;if($this.data.id){Stock.update($this.data,function(res){if($this.options.showMessage)Messenger($this.options.message);},errors_handler)}else{Stock.save($this.data,function(res){$this.data.id=res.id;$this.data.stock_id=res.id;$this.options.callback('save',res);if($this.options.showMessage)Messenger($this.options.message);},errors_handler)}};$this.clearForm=function(){angular.copy({},$this.data)}}]);StockModule.service('_stockItemForm',['$rootScope','StockItem','ErrorsHandler','Messenger','$location',function($rootScope,StockItem,ErrorsHandler,Messenger,$location){var $this=this;$this.options={showErrors:!0,showMessage:!0,callback:function(){$location.path('/stock/browse/')},message:"I've successfully saved the stock item!"};$this.data={};$this.getData=function(){return $this.data};$this.setData=function(new_data){angular.copy(new_data,$this.data)};$this.setStockId=function(id){$this.data.stock_id=id};$this.save=function(){errors_handler=($this.options.showErrors)?ErrorsHandler:null;if($this.data.id){$this.update();return!1}
StockItem.save($this.data,function(response){if($this.options.showMessage)Messenger($this.options.message);$this.options.callback('save',response);var stock_id=$this.data.stock_id;$this.clearForm();$this.data.stock_id=stock_id},errors_handler)};$this.update=function(){errors_handler=($this.options.showErrors)?ErrorsHandler:null;StockItem.update($this.data,function(response){if($this.options.showMessage)Messenger($this.options.message);$this.options.callback('update',response);var stock_id=$this.data.stock_id;$this.clearForm();$this.data.stock_id=stock_id},errors_handler)};$this.clearForm=function(){angular.copy({},$this.data)}}]);StockModule.service('_supplierForm',['$rootScope','Supplier','ErrorsHandler','Messenger','$location',function($rootScope,Supplier,ErrorsHandler,Messenger,$location){var $this=this;$this.options={showErrors:!0,showMessage:!0,clearForm:!0,callback:function(res){},updateCallback:function(res){},message:"I've successfully saved the supplier!",updateMessage:"I've successfully updated the supplier!"};$this.data={};$this.getData=function(){return $this.data};$this.setData=function(new_data){angular.copy(new_data,$this.data)};$this.save=function(){errors_handler=($this.options.showErrors)?ErrorsHandler:null;if($this.isUpdate()){Supplier.update($this.data,function(res){if($this.options.showMessage)Messenger($this.options.updateMessage);$this.options.updateCallback(res)},errors_handler)}else{Supplier.save($this.data,function(res){if($this.options.showMessage)Messenger($this.options.message);if($this.options.clearForm)$this.clearForm();$this.options.callback(res)},errors_handler)}};$this.clearForm=function(){angular.copy({},$this.data)};$this.isUpdate=function(){return($this.data.id)?!0:!1}}]);var ThreeCXModule=angular.module('ThreeCXModule',[]);ThreeCXModule.filter('capitalize',function(){return function(input){if(!input)return'';return input.charAt(0).toUpperCase()+input.slice(1).toLowerCase()}});var Admin=angular.module('admin',[]);archDB.factory('EmailSchedule',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var EmailSchedule=$resource(apiUrl('scheduled-email/:scheduledEmailId'),{scheduledEmailId:'@id'},methods);angular.extend(EmailSchedule.prototype,_prototype);return EmailSchedule}]);StockModule.factory('Status',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Status=$resource(apiUrl('statuses/:statusId'),{statusId:'@id'},methods);angular.extend(Status.prototype,_prototype);return Status}]);Admin.controller('AdminCalendarCtrl',['$scope','$http','ErrorsHandler','Messenger','$timeout',function($scope,$http,ErrorsHandler,Messenger,$timeout){$scope.save=function(data,success_message){if($scope.saving)return!1;if(!data)data=$scope.data;$scope.saving=!0;$http.post($scope.post_url,data).success(function(response){$scope.success=!0;$scope.error_message=!1;$scope.saving=!1;angular.extend($scope.data,response||{});if(success_message)Messenger(success_message);}).error(function(a,b){$scope.success=!1;$scope.error_message=!0;$scope.saving=!1;ErrorsHandler(a,b)})};$scope.enable=function(){$scope.save({enabled:!0},'Calendar module is now enabled.')};$scope.disable=function(){$scope.save({enabled:!1},'Calendar module is now disabled.')}}]);Admin.controller('AdminContractsCtrl',['$scope','$http','ErrorsHandler','$window',function($scope,$http,ErrorsHandler,$window){$scope.remove=function(){$http.delete($scope.delete_url).success(function(){$window.location=$scope.redirect}).error(ErrorsHandler)}}]);Admin.controller('AdminEmailScheduleCtrl',['$scope','EmailSchedule','ErrorsHandler','$paginator','$uibModal',function($scope,EmailSchedule,ErrorsHandler,$paginator,$uibModal){$paginator=angular.copy($paginator);$scope.paginator=$paginator;$scope.$watch('filters',function(){$scope.load()});$scope.load=function(){EmailSchedule.get({},function(response){$scope.data=response.data;$paginator.setFromResponse(response)},ErrorsHandler)}}]);Admin.controller('AdminEmailSettingsCtrl',['$scope','$http','ErrorsHandler','$window',function($scope,$http,ErrorsHandler,$window){$scope.toggle=function(url){$http.post(url).success(function(response){$scope.data.enabled=response.enabled;$scope.success=response.message}).error(ErrorsHandler)};$scope.remove=function(){$http.delete($scope.delete_url).success(function(){$window.location=$scope.redirect}).error(ErrorsHandler)}}]);Admin.controller('AdminEventCtrl',['$scope',function($scope){$scope.allModelFields=[];$scope.conditions=[];$scope.commands=[];$scope.modelFields=[];$scope.comparisonOptions=[{id:"=",name:"="}];$scope.$watch('data.model',function(value){$scope.modelFields=($scope.allModelFields.hasOwnProperty(value))?$scope.allModelFields[value]:[]});$scope.addCondition=function(){$scope.conditions.push({})};$scope.addCommand=function(){$scope.commands.push({})}}]);Admin.controller('AdminGamificationCtrl',['$scope','$http','ErrorsHandler','Messenger','$timeout',function($scope,$http,ErrorsHandler,Messenger,$timeout){$scope.save=function(data,success_message){if($scope.saving)return!1;if(!data)data=$scope.data;$scope.saving=!0;$http.post($scope.post_url,data).success(function(response){$scope.success=!0;$scope.error_message=!1;$scope.saving=!1;angular.extend($scope.data,response||{});if(success_message)Messenger(success_message);}).error(function(a,b){$scope.success=!1;$scope.error_message=!0;$scope.saving=!1;ErrorsHandler(a,b)})};$scope.enable=function(){$scope.save({enabled:!0},'Gamification module is now enabled.')};$scope.disable=function(){$scope.save({enabled:!1},'Gamification module is now disabled.')}}]);Admin.controller('AdminGroupCtrl',['$scope','$http','ErrorsHandler','$window',function($scope,$http,ErrorsHandler,$window){$scope.$watch('data',function(){$scope.selectedUsers=$scope.jsonify($scope.data.users)});$scope.jsonify=function(collectionIn){var returnArray=[];angular.forEach(collectionIn,function(key,value){key=angular.toJson(key);returnArray.push(key)});return returnArray};$scope.numberify=function(collectionIn){var returnArray=[];angular.forEach(collectionIn,function(key,value){key=Number(key);returnArray.push(key)});return returnArray};$scope.save=function(){if($scope.saving)return!1;$scope.saving=!0;$scope.selectedUsers=$scope.numberify($scope.selectedUsers);$scope.data.users=$scope.selectedUsers;$http.post($scope.post_url,$scope.data).success(function(response){$scope.saving=!1;$window.location=$scope.redirect_url}).error(ErrorsHandler)};$scope.update=function(){if($scope.saving)return!1;$scope.saving=!0;$scope.success=!1;$scope.selectedUsers=$scope.numberify($scope.selectedUsers);$scope.data.users=$scope.selectedUsers;$http.put($scope.post_url,$scope.data).success(function(response){$scope.saving=!1;$scope.success=!0;angular.extend($scope.data,response||{})}).error(function(data,status){$scope.success=!1;ErrorsHandler(data,status)})};$scope.permissionChecked=function(id){return _.find($scope.data.permissions,function(el){return el==id})?!0:!1};$scope.togglePermission=function(id){if($scope.permissionChecked(id)){$scope.data.permissions=_.reject($scope.data.permissions,function(item){return item==id});return}
$scope.data.permissions.push(id)}}]);Admin.controller('AdminLdapCtrl',['$scope','$http','ErrorsHandler','Messenger','$timeout',function($scope,$http,ErrorsHandler,Messenger,$timeout){$scope.save=function(data,success_message){if($scope.saving)return!1;if(!data)data=$scope.data;$scope.saving=!0;$http.post($scope.post_url,data).success(function(response){$scope.success=!0;$scope.error_message=!1;$scope.saving=!1;angular.extend($scope.data,response||{});if(success_message)Messenger(success_message);}).error(function(a,b){$scope.success=!1;$scope.error_message=!0;$scope.saving=!1;ErrorsHandler(a,b)})};$scope.enable=function(){$scope.save({enabled:!0},'LDAP module is now enabled.')};$scope.disable=function(){$scope.save({enabled:!1},'LDAP module is now disabled.')};$scope.test=function(url){if($scope.testing)return!1;$scope.testing=!0;$http.post(url,$scope.data).success(function(response){$scope.testing=!1;if(response.success){Messenger('Successfully connected to the LDAP server.');$scope.test_success=!0;$timeout(function(){$scope.test_success=!1},5000)}else{Messenger('Unable to connect to the LDAP server');$scope.test_failed=!0;$timeout(function(){$scope.test_failed=!1},5000)}}).error(function(a,b){$scope.testing=!1;$scope.test_failed=!0;$timeout(function(){$scope.test_failed=!1},5000);ErrorsHandler(a,b)})}}]);Admin.controller('AdminRestCtrl',['$scope','$http','ErrorsHandler','$window',function($scope,$http,ErrorsHandler,$window){$scope.remove=function(){$http.delete($scope.delete_url).success(function(){$window.location=$scope.redirect}).error(ErrorsHandler)}}]);Admin.controller('AdminStatusCtrl',['$scope','$uibModal',function($scope,$uibModal){$scope.remove=function(){var modalInstance=$uibModal.open({templateUrl:'admin/settings/statuses/modals/delete.html',controller:'AdminStatusDeleteCtrl',resolve:{options:function(){return{'redirect':$scope.redirect,'url':$scope.delete_url}}}})}}]);Admin.controller('AdminStatusDeleteCtrl',['$scope','$uibModalInstance','ErrorsHandler','$http','$window','options',function($scope,$uibModalInstance,ErrorsHandler,$http,$window,options){$scope.data={};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')};$scope.submit=function(){if(!$scope.data.status_id)return!1;$http.delete(options.url,{params:$scope.data}).success(function(){$uibModalInstance.close();$window.location=options.redirect}).error(ErrorsHandler)}}]);Admin.controller('AdminTaskTemplatesCtrl',['$scope','Template','TemplateItem','ErrorsHandler','$paginator','$uibModal','$q',function($scope,Template,TemplateItem,ErrorsHandler,$paginator,$uibModal,$q){$paginator=angular.copy($paginator);$scope.paginator=$paginator;$scope.filters={};$paginator.setOnPageChange(function(page){$scope.load(page)});$scope.$watch('filters',function(){$scope.load()},!0);$scope.load=function(page){var filters=angular.copy($scope.filters);filters.page=(!page)?1:page;Template.get(filters,function(response){$scope.templates=response.data;$paginator.setFromResponse(response)},ErrorsHandler)};$scope.load();$scope.updateTemplate=function(template,name){var d=$q.defer();Template.update({id:template.id,name:name},function(response){d.resolve()},function(a,b){ErrorsHandler(a,b);d.reject('Error')});return d.promise};$scope.delete=function(template){Template.delete({templateId:template.id},function(){$scope.templates=_.reject($scope.templates,function(el){return el.id==template.id})},ErrorsHandler)};$scope.edit=function(template){var modalInstance=$uibModal.open({templateUrl:'admin/modals/task-template.html',controller:'AdminTemplateModal',resolve:{'template':function(){return template},'action':function(){return'edit'}}});modalInstance.result.then(function(template){angular.forEach($scope.templates,function(t,i){if(t.id===template.id){$scope.templates[i]=template}})})};$scope.showTemplateModal=function(){var modalInstance=$uibModal.open({templateUrl:'admin/modals/task-template.html',controller:'AdminTemplateModal',resolve:{'action':function(){return'create'},'template':function(){return{'name':'','priority':'','task_types':[]}}}});modalInstance.result.then(function(template){$scope.templates.push(template)})};$scope.updateTemplateItem=function(item,description){var d=$q.defer();TemplateItem.update({id:item.id,description:description},function(response){d.resolve()},function(a,b){ErrorsHandler(a,b);d.reject('Error')});return d.promise};$scope.deleteItem=function(template,item){TemplateItem.delete({templateItemId:item.id},function(){template.items=_.reject(template.items,function(el){return el.id==item.id})},ErrorsHandler)};$scope.showTemplateItemModal=function(template){var modalInstance=$uibModal.open({templateUrl:'admin/modals/task-template-item.html',controller:'AdminTemplateItemModal',resolve:{template:function(){return template}}});modalInstance.result.then(function(item){template.items.push(item)})}}]);Admin.controller('AdminTemplateItemModal',['$scope','$uibModalInstance','TemplateItem','template','ErrorsHandler',function($scope,$uibModalInstance,TemplateItem,template,ErrorsHandler){$scope.data={template_id:template.id};$scope.save=function(){TemplateItem.save($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Admin.controller('AdminTemplateModal',['$scope','$uibModalInstance','Template','ErrorsHandler','action','template',function($scope,$uibModalInstance,Template,ErrorsHandler,action,template){$scope.data=template;$scope.action=action;$scope.save=function(){if(action=='create')
Template.save($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler);else if(action=='edit')
Template.update($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')};$scope.taskTypeChecked=function(taskType){return _.find($scope.data.task_types,function(el){return el.id==taskType.id})?!0:!1};$scope.toggleTaskType=function(taskType){if($scope.taskTypeChecked(taskType)){$scope.data.task_types=_.reject($scope.data.task_types,function(item){return item.id==task_type.id});return}
$scope.data.task_types.push(taskType)}}]);Admin.controller('AdminUserModal',['$scope','$uibModalInstance','user','User','ErrorsHandler',function($scope,$uibModalInstance,user,User,ErrorsHandler){$scope.data=user;$scope.account_types=[{name:'Local Account',value:'local'},{name:'LDAP - Active Directory',value:'ldap',disabled:!$scope.data.id}];$scope.save=function(){if($scope.data.id){User.update($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}else{User.save($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Admin.controller('AdminUsersCtrl',['$scope','User','ErrorsHandler','$paginator','$uibModal',function($scope,User,ErrorsHandler,$paginator,$uibModal){$paginator=angular.copy($paginator);$scope.paginator=$paginator;$scope.filters={};$paginator.setOnPageChange(function(page){$scope.load(page)});$scope.$watch('filters',function(){$scope.load()},!0);$scope.load=function(page){var filters=angular.copy($scope.filters);filters.page=(!page)?1:page;User.get(filters,function(response){$scope.users=response.data;$paginator.setFromResponse(response)},ErrorsHandler)};$scope.load();$scope.showUserModal=function(user){var modalInstance=$uibModal.open({templateUrl:'admin/modals/user.html',controller:'AdminUserModal',resolve:{user:function(){return user||{account_type:'local',active:1}}}});modalInstance.result.then(function(_user){if(user){angular.extend(user,_user||{})}else{$scope.users.push(_user)}})}}]);archDB.factory('ChatMessage',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var ChatMessage=$resource(apiUrl('chat/messages/:messageId'),{messageId:'@id'},methods);angular.extend(ChatMessage.prototype,_prototype);return ChatMessage}]);archDB.factory('ChatChannel',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){methods.leave={method:'POST',url:apiUrl('chat/channels/leave/:channelId'),params:{channelId:'@id'}};var ChatChannel=$resource(apiUrl('chat/channels/:channelId'),{channelId:'@id'},methods);angular.extend(ChatChannel.prototype,_prototype);return ChatChannel}]);archDB.controller('ChatChannelCtrl',['$scope','ChatMessage','ErrorsHandler','socket','$element','$DesktopNotification','$uibModal','ChatChannel','ngAudio',function($scope,ChatMessage,ErrorsHandler,socket,$element,$DesktopNotification,$uibModal,ChatChannel,ngAudio){var event_registered=!1;$scope.messages=[];$scope.$on('popover-shown',function(){$scope.loading=!0;$scope.load();_.each($scope.channels,function(el){if(el===$scope.channel){el.selected=!0}else{el.selected=!1}})});$scope.$on('popover-hidden',function(){$scope.channel.selected=!1})
$scope.load=function(){ChatMessage.get({channel:$scope.channel.id},function(response){$scope.loading=!1;$scope.messages=response.data},ErrorsHandler)};$scope.registerEvents=function(){if(event_registered==!0)return!1;socket.on('new_message '+$scope.channel.id,function(message){message.own=!1;$scope.messages.push(message);if(!$scope.channel.notifications)$scope.channel.notifications=0;$scope.channel.notifications++;if(!$scope.channel.selected){$DesktopNotification.show('#'+$scope.channel.tag+' - '+message.from.name,message.message);ngAudio.play('/audio/chat2.mp3')}});event_registered=!0};$scope.registerEvents();$scope.send=function(){ChatMessage.save({message:$scope.data.message,channel:$scope.channel.id,save_comment:$scope.data.save_comment},function(response){socket.emit('send_channel_message',{message:response,channel:$scope.channel.id});$scope.messages.push(response);$scope.data.message=''},ErrorsHandler)};$scope.showSettings=function(){var modalInstance=$uibModal.open({templateUrl:'templates/modals/new-channel.html',controller:'NewChannelCtrl',resolve:{channel:function(){return $scope.channel}}});modalInstance.result.then(function(response){angular.extend($scope.channel,response||{})})};$scope.leave=function(){ChatChannel.leave({id:$scope.channel.id},function(response){$scope.channels=_.reject($scope.channels,function(el){return el===$scope.channel})},ErrorsHandler)};$scope.uploadFile=function(){var modalInstance=$uibModal.open({templateUrl:'templates/modals/file-upload.html',controller:'ChatFileUploadCtrl',resolve:{channel:function(){return $scope.channel}}});modalInstance.result.then(function(){$scope.load()})}}]);archDB.controller('ChatFileUploadCtrl',['$scope','$uibModalInstance','channel',function($scope,$uibModalInstance,channel){$scope.channel=channel;$scope.files=[];$scope.cancel=function(){$uibModalInstance.close()}}]);archDB.controller('ChatMainCtrl',['$scope','socket','ChatChannel','ErrorsHandler','TimeLog','$uibModal','$idle','ThreeCXCall',function($scope,socket,ChatChannel,ErrorsHandler,TimeLog,$uibModal,$idle,ThreeCXCall){$idle.watch();$scope.channels=[];$scope.makeCall=function(number,type){ThreeCXCall.makeCall(number,type)};$scope.loadChannels=function(){ChatChannel.get(function(response){$scope.channels=response.data},ErrorsHandler)};$scope.loadUsers=function(){TimeLog.get({userStatuses:!0},function(response){$scope.users=_.reject(response.data,function(el){return el.id==$scope.current_user.id});$scope.refreshOnlineUsers();$scope.refreshIdleUsers()},ErrorsHandler)};$scope.refreshOnlineUsers=function(){_.each($scope.users,function(el){var user=_.find($scope.online_users,function(u){return u==el.id});el.online=user?!0:!1})};$scope.refreshIdleUsers=function(){_.each($scope.users,function(el){var user=_.find($scope.idle_users,function(u){return u==el.id});el.idle=user?!0:!1})};$scope.getStatusClass=function(user){if(!user.latest_time_log||user.latest_time_log.type=='out'){return'text-danger'}else if(user.latest_time_log.type=='in'&&user.latest_time_log.lunch==1){return'text-lunch'}else if(user.latest_time_log.type=='in'){return'text-success'}
return''};$scope.loadChannels();$scope.loadUsers();$scope.openChannel=function(channel){channel.notifications=0};$scope.openChannel=function(channel){channel.notifications=0};$scope.openPrivate=function(user){user.notifications=0};$scope.newChannel=function(){var modalInstance=$uibModal.open({templateUrl:'templates/modals/new-channel.html',controller:'NewChannelCtrl',resolve:{channel:function(){return null}}});modalInstance.result.then(function(response){$scope.channels.push(response)})};$scope.$on('$idleStart',function(){socket.emit('idle')});$scope.$on('$idleEnd',function(){socket.emit('idleEnd')});socket.on('idle',function(id){var user=_.find($scope.users,function(u){return u.id==id});if(user)user.idle=!0});socket.on('idleEnd',function(id){var user=_.find($scope.users,function(u){return u.id==id});if(user)user.idle=!1});socket.on('online_users',function(users){$scope.online_users=users;$scope.refreshOnlineUsers()});socket.on('idle_users',function(users){$scope.idle_users=users;$scope.refreshIdleUsers()});socket.on('user_connected',function(id){var user=_.find($scope.users,function(u){return u.id==id});if(user)user.online=!0});socket.on('user_disconnected',function(id){var user=_.find($scope.users,function(u){return u.id==id});if(user)user.online=!1})}]);archDB.controller('ChatPrivateCtrl',['$scope','ChatMessage','ErrorsHandler','socket','$element','$DesktopNotification','$uibModal','ngAudio',function($scope,ChatMessage,ErrorsHandler,socket,$element,$DesktopNotification,$uibModal,ngAudio){var event_registered=!1;$scope.messages=[];$scope.data={};$scope.direct_message=!0;$scope.$on('popover-shown',function(){$scope.loading=!0;$scope.load();_.each($scope.users,function(el){if(el===$scope.user)
el.selected=!0;else el.selected=!1})});$scope.$on('popover-hidden',function(){$scope.user.selected=!1});$scope.load=function(){ChatMessage.get({chat_user:$scope.user.id},function(response){$scope.loading=!1;$scope.thread_id=response.thread_id;$scope.messages=response.data;$scope.registerEvents()},ErrorsHandler)};$scope.registerEvents=function(){if(event_registered==!0)return!1;if(!$scope.thread_id)$scope.thread_id=($scope.user.id<$scope.current_user.id)?$scope.user.id+'-'+$scope.current_user.id:$scope.current_user.id+'-'+$scope.user.id;socket.on('new_message '+$scope.thread_id,function(message){message.own=!1;$scope.messages.push(message);if(!$scope.user.notifications)$scope.user.notifications=0;$scope.user.notifications++;if(!$scope.user.selected){$DesktopNotification.show($scope.user.name,message.message);ngAudio.play('/audio/chat2.mp3')}});event_registered=!0};$scope.$watch('current_user',function(user){if(user)$scope.registerEvents();});$scope.send=function(){$scope.data.to=$scope.user.id;ChatMessage.save($scope.data,function(response){socket.emit('send_message',{message:response,thread_id:response.thread_id,to:$scope.user.id});$scope.messages.push(response);$scope.data.message=''},ErrorsHandler)};$scope.uploadFile=function(){var modalInstance=$uibModal.open({templateUrl:'templates/modals/file-upload.html',controller:'ChatFileUploadCtrl',resolve:{channel:function(){return{id:$scope.thread_id}}}});modalInstance.result.then(function(){$scope.load()})}}]);archDB.controller('NewChannelCtrl',['$scope','User','$uibModalInstance','ErrorsHandler','ChatChannel','channel',function($scope,User,$uibModalInstance,ErrorsHandler,ChatChannel,channel){$scope.data=channel||{};$scope.select2Options={allowClear:!0,minimumInputLength:1,placeholder:'Search for users',query:function(options){User.get({q:options.term,page:options.page,active:!0},function(response){options.callback({more:response.current_page<response.last_page,results:response.data})})},formatResult:function(obj,container,query){return obj.name},formatSelection:function(obj){return obj.name}};$scope.save=function(){if($scope.data.id){ChatChannel.update($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}else{ChatChannel.save($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);archDB.directive('chatScroll',['$timeout',function($timeout){return{restrict:'AE',link:function(scope,element,attrs){scope.scrollToBottom=function(){$timeout(function(){element.animate({scrollTop:element[0].scrollHeight},300);element.siblings('form').find('input:first').focus()},50)};scope.$watch('messages',scope.scrollToBottom,!0);scope.$on('popover-shown',scope.scrollToBottom)}}}]);archDB.directive('chatTab',[function(){return{restrict:'A',scope:{user:'=',closeTab:'&'},transclude:!0,controller:['$scope','ErrorsHandler','ChatMessage','socket','$element','$timeout',function($scope,ErrorsHandler,ChatMessage,socket,$element,$timeout){$scope.messages=[];$scope.thread_id=null;var event_registered=!1;$scope.$watch('messages',function(){$scope.scrollToBottom()},!0);$scope.load=function(){ChatMessage.get({chat_user:$scope.user.id},function(response){$scope.messages=response.data;$scope.thread_id=response.thread_id;$scope.registerEvents(response.thread_id)},ErrorsHandler)};$scope.registerEvents=function(thread_id){if(event_registered==!0)return!1;socket.on('new_message '+thread_id,function(message){$scope.messages.push(message)});event_registered=!0};$scope.load();$scope.send=function(){ChatMessage.save({message:$scope.message,to:$scope.user.id},function(response){socket.emit('send_message',{message:response,to:$scope.user.id,thread_id:$scope.thread_id});$scope.messages.push(response);$scope.message=''},ErrorsHandler)};$scope.scrollToBottom=function(){var el=$element.find('.chat-messages');$timeout(function(){el.animate({scrollTop:el[0].scrollHeight},300)},50)}}],link:function($scope,$element,$attrs,ctrl,transclude){transclude($scope,function(clone){$element.append(clone)})}}}]);archDB.factory('TimeLog',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var TimeLog=$resource(apiUrl('hr/time-log/:timeLogId'),{timeLogId:'@id'},methods);angular.extend(TimeLog.prototype,_prototype);return TimeLog}]);archDB.factory('AdminTimeLog',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var AdminTimeLog=$resource(apiUrl('hr/admin/time-log/:timeLogId'),{timeLogId:'@id'},methods);angular.extend(AdminTimeLog.prototype,_prototype);return AdminTimeLog}]);archDB.factory('TimeLogChangeRequest',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var TimeLogChangeRequest=$resource(apiUrl('hr/time-log-change-requests/:timeLogChangeRequestId'),{timeLogChangeRequestId:'@id'},methods);angular.extend(TimeLogChangeRequest.prototype,_prototype);return TimeLogChangeRequest}]);archDB.factory('UserDetails',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var UserDetails=$resource(apiUrl('hr/user-details/:userId'),{userId:'@user_id'},methods);angular.extend(UserDetails.prototype,_prototype);return UserDetails}]);archDB.factory('Holiday',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Holiday=$resource(apiUrl('hr/holiday/:holidayId'),{holidayId:'@id'},methods);angular.extend(Holiday.prototype,_prototype);return Holiday}]);archDB.directive("clockInOut",function(){return{restrict:"A",scope:!0,controller:["$scope","ClockingStatusService","Messenger","TimeLog","ErrorsHandler",function($scope,Status,Messenger,TimeLog,ErrorsHandler){$scope.getText=function(){if(Status.isClockedIn()||Status.isAtLunch()){return"Clock Out"}
if(Status.isClockedOut()){return"Clock In"}
return"Clock In"};$scope.getIcon=function(){if(Status.isClockedIn()||Status.isAtLunch()){return"fa fa-circle fa-fw text-danger"}
if(Status.isClockedOut()){return"fa fa-circle fa-fw text-success"}
return"fa fa-circle fa-fw text-success"};$scope.action=function(){var type="in";if(Status.isClockedIn()||Status.isAtLunch()){type="Out"}else if(Status.isClockedOut()){type="in"}
TimeLog.save({type:type},function(response){Status.setStatus(response);Messenger("You have been clocked "+type)},ErrorsHandler)}}],replace:!0,template:'<li><a href="#" ng-click="action()" eat-click><i ng-class="getIcon()"></i> {[{ getText() }]}</a></li>'}});archDB.directive("clockingStatus",function(){return{restrict:"A",controller:["$scope","ClockingStatusService","TimeLog","ErrorsHandler",function($scope,Status,TimeLog,ErrorsHandler){const actions={clockIn:{label:"Clock In",action:function(){return TimeLog.save({type:"in"},function(response){Status.setStatus(response);alert.update({message:"You are now clocked in :)",type:"success",actions:!1,hideAfter:3})},ErrorsHandler)}},clockInLunch:{label:"Clock In from Lunch",action:function(){return TimeLog.save({type:"in",lunch:!0},function(response){Status.setStatus(response);alert.update({message:"You are now clocked in from lunch :)",type:"success",actions:!1,hideAfter:3})},ErrorsHandler)}}};$scope.status=Status;Messenger.options={extraClasses:"messenger-fixed messenger-on-top messenger-on-right",theme:"future"};var alert;var AlertCallback=function(){console.log("AlertCallback",Status.isAtLunch());if((Status.isClockedOut()||Status.isAtLunch())&&Status.ready){if(alert&&alert.shown===!0){return!1}
let message="You are not clocked in.";action=actions.clockIn;if(Status.isAtLunch()){message="You are at lunch.";action=actions.clockInLunch}
alert=Messenger().post({message,type:"error",showCloseButton:!0,hideAfter:0,actions:{action}})}};$scope.$watch("status.ready",AlertCallback);$scope.$watch("status.status",AlertCallback)}],template:'<small><i ng-class="{\'fa fa-circle text-success\': status.isClockedIn() && !status.isAtLunch(), \'fa fa-circle text-lunch\': status.isAtLunch(), \'fa fa-circle text-danger\': !status.isClockedIn() && !status.isAtLunch()}"></i></small> <span ng-show="status.isClockedIn() && !status.isAtLunch()">Clocked In</span> <span ng-show="status.isAtLunch()">On Lunch</span> <span ng-show="!status.isClockedIn() && !status.isAtLunch()">Clocked Out</span>'}});archDB.directive("lunch",function(){return{restrict:"A",scope:!0,controller:["$scope","ClockingStatusService","Messenger","TimeLog","ErrorsHandler",function($scope,Status,Messenger,TimeLog,ErrorsHandler){$scope.getText=function(){if(Status.isAtLunch()){return"Clock In from lunch."}
return"Clock Out for lunch"};$scope.toShow=function(){return Status.isClockedIn()||Status.isAtLunch()};$scope.getIcon=function(){if(Status.isAtLunch()){return"fa fa-cutlery fa-fw text-success"}
return"fa fa-cutlery fa-fw text-danger"};$scope.action=function(){var type="out";var lunch=!0;if(Status.isAtLunch()){type="in"}
TimeLog.save({type:type,lunch:lunch},function(response){Status.setStatus(response);Messenger("You have been clocked "+type+" from lunch")},ErrorsHandler)}}],replace:!0,template:'<li ng-show="toShow()"><a href="#" ng-click="action()" eat-click><i ng-class="getIcon()"></i> {[{ getText() }]}</a></li>'}});archDB.controller('ApproveDeclineTimeModalCtrl',['$scope','$uibModalInstance','TimeLogChangeRequest','ErrorsHandler','options','DateTime',function($scope,$uibModalInstance,TimeLogChangeRequest,ErrorsHandler,options,DateTime){var time=moment(options.log.time);$scope.DateTime=DateTime;$scope.data={id:options.log.id};$scope.current_date=time.format('DD MMM YYYY - HH:mm');$scope.selected_date={hour:time.format('HH'),minute:time.format('mm'),day:time.format('DD'),month:time.format('MM'),year:time.format('YYYY')};var TimeChanged=function(){$scope.data.time=moment({year:parseInt($scope.selected_date.year,10),month:parseInt($scope.selected_date.month,10)-1,day:parseInt($scope.selected_date.day,10),hour:parseInt($scope.selected_date.hour,10),minute:parseInt($scope.selected_date.minute,10)}).format("YYYY-MM-DD HH:mm:ss")};$scope.$watch('selected_date',TimeChanged,!0);$scope.approve=function(){$scope.data.completed=1;TimeLogChangeRequest.update($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)};$scope.decline=function(){$scope.data.declined=1;TimeLogChangeRequest.update($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);archDB.controller('EditUserAddressCtrl',['$scope','$uibModalInstance','options','UserDetails','ErrorsHandler',function($scope,$uibModalInstance,options,UserDetails,ErrorsHandler){$scope.data=options.user;$scope.submit=function(){UserDetails.update($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);archDB.controller('HolidayRequestModalCtrl',['$scope','DateTime','$uibModalInstance','Holiday','ErrorsHandler','Messenger','user_details',function($scope,DateTime,$uibModalInstance,Holiday,ErrorsHandler,Messenger,user_details){$scope.DateTime=DateTime;$scope.user_details=user_details;$scope.from=angular.copy(DateTime.defaults);$scope.to=angular.copy(DateTime.defaults);$scope.total_holidays=0;$scope.data={paid:1,description:''};$scope.fromDateModel=null;$scope.toDateModel=null;$scope.total_holidays=0;$scope.getTodayDate=function(){return moment().format('YYYY-MM-DD')};$scope.updateFromDate=function(){if($scope.fromDateModel){var fromMoment;if($scope.fromDateModel instanceof Date){var year=$scope.fromDateModel.getFullYear();var month=$scope.fromDateModel.getMonth();var day=$scope.fromDateModel.getDate();fromMoment=moment.tz([year,month,day],'Europe/London')}else{fromMoment=moment.tz($scope.fromDateModel,'YYYY-MM-DD','Europe/London')}
$scope.from.days=fromMoment.format('DD');$scope.from.months=fromMoment.format('MM');$scope.from.years=fromMoment.format('YYYY');$scope.data.start_date=fromMoment.format('YYYY-MM-DD 00:00:00');var toMoment;if($scope.toDateModel instanceof Date){var toYear=$scope.toDateModel.getFullYear();var toMonth=$scope.toDateModel.getMonth();var toDay=$scope.toDateModel.getDate();toMoment=moment.tz([toYear,toMonth,toDay],'Europe/London')}else if($scope.toDateModel){toMoment=moment.tz($scope.toDateModel,'YYYY-MM-DD','Europe/London')}
if(toMoment&&toMoment.isBefore(fromMoment)){$scope.toDateModel=null;$scope.data.end_date=null}
calculateWorkingDays()}};$scope.updateToDate=function(){if($scope.toDateModel){var toMoment;if($scope.toDateModel instanceof Date){var year=$scope.toDateModel.getFullYear();var month=$scope.toDateModel.getMonth();var day=$scope.toDateModel.getDate();toMoment=moment.tz([year,month,day],'Europe/London')}else{toMoment=moment.tz($scope.toDateModel,'YYYY-MM-DD','Europe/London')}
$scope.to.days=toMoment.format('DD');$scope.to.months=toMoment.format('MM');$scope.to.years=toMoment.format('YYYY');$scope.data.end_date=toMoment.format('YYYY-MM-DD 00:00:00');calculateWorkingDays()}};$scope.clearDates=function(){$scope.fromDateModel=null;$scope.toDateModel=null;$scope.data.start_date=null;$scope.data.end_date=null;$scope.total_holidays=0;$scope.from=angular.copy(DateTime.defaults);$scope.to=angular.copy(DateTime.defaults)};function calculateWorkingDays(){if(!$scope.fromDateModel||!$scope.toDateModel){$scope.total_holidays=0;return}
var startDate,endDate;if($scope.fromDateModel instanceof Date){var fromYear=$scope.fromDateModel.getFullYear();var fromMonth=$scope.fromDateModel.getMonth();var fromDay=$scope.fromDateModel.getDate();startDate=moment.tz([fromYear,fromMonth,fromDay],'Europe/London')}else{startDate=moment.tz($scope.fromDateModel,'YYYY-MM-DD','Europe/London')}
if($scope.toDateModel instanceof Date){var toYear=$scope.toDateModel.getFullYear();var toMonth=$scope.toDateModel.getMonth();var toDay=$scope.toDateModel.getDate();endDate=moment.tz([toYear,toMonth,toDay],'Europe/London')}else{endDate=moment.tz($scope.toDateModel,'YYYY-MM-DD','Europe/London')}
var counter=endDate.diff(startDate,'days')+1;var start=startDate.clone();var totalHols=0;for(var i=0;i<counter;i++){var day=start.isoWeekday();if(day!==6&&day!==7){totalHols++}
start.add(1,'days')}
$scope.total_holidays=totalHols}
$scope.getSelectedDateRange=function(){if(!$scope.fromDateModel||!$scope.toDateModel){return''}
var start=moment($scope.fromDateModel).format('DD MMM YYYY');var end=moment($scope.toDateModel).format('DD MMM YYYY');if(start===end){return start}
return start+' - '+end};$scope.save=function(){Holiday.save($scope.data,function(response){$uibModalInstance.close(response);Messenger("Your holiday request was successfully saved.")},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);archDB.controller('LogTimeRequestChangeCtrl',['$scope','$uibModalInstance','TimeLogChangeRequest','AdminTimeLog','ErrorsHandler','options','DateTime',function($scope,$uibModalInstance,TimeLogChangeRequest,AdminTimeLog,ErrorsHandler,options,DateTime){var time=moment(options.log.time);$scope.DateTime=DateTime;$scope.data={time_log_id:options.log.id};$scope.current_date=time.format('DD MMM YYYY - HH:mm');$scope.selected_date={hour:time.format('HH'),minute:time.format('mm'),day:time.format('DD'),month:time.format('MM'),year:time.format('YYYY')};var TimeChanged=function(){$scope.data.time=moment({year:parseInt($scope.selected_date.year,10),month:parseInt($scope.selected_date.month,10)-1,day:parseInt($scope.selected_date.day,10),hour:parseInt($scope.selected_date.hour,10),minute:parseInt($scope.selected_date.minute,10)}).format("YYYY-MM-DD HH:mm:ss")};$scope.$watch('selected_date',TimeChanged,!0);$scope.save=function(){if(options.admin){$scope.data.id=options.log.id;AdminTimeLog.update($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}else{TimeLogChangeRequest.save($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);archDB.controller('NewRecordRequestCtrl',['$scope','$uibModalInstance','TimeLog','AdminTimeLog','ErrorsHandler','options','DateTime',function($scope,$uibModalInstance,TimeLog,AdminTimeLog,ErrorsHandler,options,DateTime){var time=moment(options.date),i,formatted;$scope.DateTime=DateTime;$scope.data={type:options.type,new_record_request:!0,user_id:options.user.user_id};$scope.selected_date={hour:(options.type=='in')?options.user.default_start_time.split(':')[0]:options.user.default_end_time.split(':')[0],minute:(options.type=='in')?options.user.default_start_time.split(':')[1]:options.user.default_end_time.split(':')[1],day:time.format('DD'),month:time.format('MM'),year:time.format('YYYY')};var TimeChanged=function(){$scope.data.time=moment({year:parseInt($scope.selected_date.year,10),month:parseInt($scope.selected_date.month,10)-1,day:parseInt($scope.selected_date.day,10),hour:parseInt($scope.selected_date.hour,10),minute:parseInt($scope.selected_date.minute,10)}).format("YYYY-MM-DD HH:mm:ss")};$scope.$watch('selected_date',TimeChanged,!0);$scope.save=function(){var model=options.admin?AdminTimeLog:TimeLog;model.save($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);archDB.service('$ClockingLogs',function(){var self=this;self.total_hours=0;self.daterangepicker={ranges:{'This Month':[moment().subtract('month',1).set('date',29).hours(0).minutes(0).seconds(0),moment().set('date',28).hours(23).minutes(59).seconds(59)],'Last Month':[moment().subtract('month',2).set('date',28).hours(0).minutes(0).seconds(0),moment().subtract('month',1).set('date',27).hours(23).minutes(59).seconds(59)],},locale:{firstDay:1}};self.selected_period={startDate:self.daterangepicker.ranges['This Month'][0],endDate:self.daterangepicker.ranges['This Month'][1]};self.prepareData=function(data){var preparedData={};var lastElement;self.total_hours=0;_.each(data,function(el){el.time=moment(el.time);var date=el.time.format('YYYY-MM-DD');if(preparedData.hasOwnProperty(date)){var lastDate=preparedData[date];var lastGroupItem=lastDate[lastDate.length-1];if(!lastGroupItem[el.type]){lastGroupItem[el.type]=el;var otherIndex=el.type=='in'?'out':'in';var hours=moment(el.time).diff(lastGroupItem[otherIndex].time,'hours',!0).toFixed(2)
lastGroupItem.hours=parseFloat(hours);if(hours)self.total_hours+=parseFloat(hours);}else{preparedData[date].push({'in':el.type=='in'?el:null,'out':el.type=='out'?el:null})}}else{preparedData[date]=[{'in':el.type=='in'?el:null,'out':el.type=='out'?el:null}]}});var date=self.selected_period.startDate.clone(),endDate=self.selected_period.endDate.clone();while(endDate.isAfter(date)){if(!preparedData.hasOwnProperty(date.format('YYYY-MM-DD'))){preparedData[date.format('YYYY-MM-DD')]=[{'in':null,'out':null}]}
date.add('days',1)}
return preparedData}});archDB.service("ClockingStatusService",["TimeLog","ErrorsHandler",function(TimeLog,ErrorsHandler){var self=this;self.status=null;self.ready=!1;TimeLog.get({lastRecord:!0},function(response){if(response.id)self.status=response;self.ready=!0},ErrorsHandler);self.setStatus=function(value){self.status=value};self.getStatus=function(){return self.status};self.hasStatus=function(){return self.status?!0:!1};self.isClockedIn=function(){if(!self.status){return!1}
return self.status&&self.status.type=="in"&&!self.status.lunch};self.isClockedOut=function(){if(!self.status){return!0}
return self.status&&self.status.type=="out"&&!self.status.lunch};self.isAtLunch=function(){console.log("AlertCallback",self.status);if(!self.status){return!1}
return self.status&&self.status.lunch==1}}]);archDB.service('DateTime',function(){var self=this,formatted,i,date=moment();self.defaults={days:date.format('DD'),months:date.format('MM'),years:date.format('YYYY'),hours:date.format('HH'),minutes:date.format('mm'),seconds:date.format('ss')};self.hours=[];self.minutes=[];self.days=[];self.months=[{name:'January',value:'01'},{name:'February',value:'02'},{name:'March',value:'03'},{name:'April',value:'04'},{name:'May',value:'05'},{name:'June',value:'06'},{name:'July',value:'07'},{name:'August',value:'08'},{name:'September',value:'09'},{name:'October',value:'10'},{name:'November',value:'11'},{name:'December',value:'12'}];self.years=[];for(i=0;i<24;i++){formatted=i<10?'0'+i:''+i;self.hours.push(formatted)}
for(i=0;i<60;i++){formatted=i<10?'0'+i:''+i;self.minutes.push(formatted)}
for(i=1;i<32;i++){formatted=i<10?'0'+i:''+i;self.days.push(formatted)}
for(i=2025;i<=2030;i++){formatted=''+i;self.years.push(formatted)}});archDB.controller("ClockingLogsCtrl",["$scope","TimeLog","ErrorsHandler","$uibModal","$ClockingLogs",function($scope,TimeLog,ErrorsHandler,$uibModal,$ClockingLogs){$scope.service=$ClockingLogs;$scope.$watch("service.selected_period",function(value){if(value){$scope.load()}});$scope.load=function(){TimeLog.get({startDate:$ClockingLogs.selected_period.startDate.hours(0).minutes(0).seconds(0).format("X"),endDate:$ClockingLogs.selected_period.endDate.hours(23).minutes(59).seconds(59).format("X")},function(response){$scope.data=$ClockingLogs.prepareData(response.data);$scope.days=$scope.calDays($scope.data);$scope.count=10;$scope.show={};$scope.show[new Date().toISOString().substr(0,10)]=!0},ErrorsHandler)};$scope.keys=function(data){return data?Object.keys(data).sort():[]};$scope.calDays=function(data){output={};Object.keys(data).forEach(key=>{el=data[key];el.forEach(obj=>{output[key]=output[key]||0;if(obj.hours){output[key]+=obj.hours}
output[key]=Math.round(output[key]*100)/100})});return output};$scope.requestChange=function(log){var modalInstance=$uibModal.open({templateUrl:"hr/modals/request_change.html",controller:"LogTimeRequestChangeCtrl",resolve:{options:function(){return{admin:!1,log:log}}}});modalInstance.result.then(function(response){log.pending_change_requests.push(response)})};$scope.newRecord=function(date,type){var modalInstance=$uibModal.open({templateUrl:"hr/modals/new_record_request.html",controller:"NewRecordRequestCtrl",resolve:{options:function(){return{admin:!1,date:date?date:new Date(),type:type?type:"in",user:$scope.user_details}}}});modalInstance.result.then(function(response){$scope.load()})};$scope.generateTimesheet=function(){var modalInstance=$uibModal.open({templateUrl:"hr/modals/generate_individual_timesheet.html",controller:"HrGenerateTimesheetCtrl",scope:$scope})}}]);archDB.controller("HolidayRequestsCtrl",["$scope","Holiday","ErrorsHandler","$uibModal",function($scope,Holiday,ErrorsHandler,$uibModal){$scope.load=function(){Holiday.get({own:!0},function(response){$scope.requests=response.data},ErrorsHandler)};$scope.load();$scope.requestHoliday=function(){var modalInstance=$uibModal.open({templateUrl:"hr/modals/holiday_request.html",controller:"HolidayRequestModalCtrl",size:"lg",resolve:{user_details:function(){return $scope.user_details}}});modalInstance.result.then(function(holiday){$scope.requests.push(holiday)})};$scope.getStatusClass=function(request){if(!request.hasOwnProperty("status"))return"";switch(request.status.toLowerCase()){case "pending":return"label-warning";case "approved":return"label-success";case "declined":return"label-danger";case "cancelled":return"label-warning";default:return""}}}]);archDB.controller('AdminHolidayRequestsCtrl',['$scope','Holiday','ErrorsHandler',function($scope,Holiday,ErrorsHandler){$scope.load=function(){Holiday.get({pending:!0},function(response){$scope.holidays=response.data},ErrorsHandler)};$scope.approve=function(holiday){Holiday.update({id:holiday.id,status:'approved'},function(response){Messenger("The holiday was approved!");$scope.holidays=_.reject($scope.holidays,function(el){return el.id==holiday.id})},ErrorsHandler)};$scope.decline=function(holiday){Holiday.update({id:holiday.id,status:'declined'},function(response){Messenger("The holiday was declined!");$scope.holidays=_.reject($scope.holidays,function(el){return el.id==holiday.id})},ErrorsHandler)};$scope.load()}]);archDB.controller('AdminUserHolidaysCtrl',['$scope','Holiday','ErrorsHandler',function($scope,Holiday,ErrorsHandler){$scope.load=function(){Holiday.get({user_id:$scope.user_details.user_id},function(response){$scope.holidays=response.data},ErrorsHandler)};$scope.approve=function(holiday){Holiday.update({id:holiday.id,status:'approved'},function(response){Messenger("The holiday was approved!");angular.extend(holiday,response||{})},ErrorsHandler)};$scope.decline=function(holiday){Holiday.update({id:holiday.id,status:'declined'},function(response){Messenger("The holiday was declined!");angular.extend(holiday,response||{})},ErrorsHandler)};$scope.load()}]);archDB.controller("AdminUserTimeClockCtrl",["$scope","AdminTimeLog","ErrorsHandler","$uibModal","$ClockingLogs","$q","UserDetails","Holiday","Messenger",function($scope,AdminTimeLog,ErrorsHandler,$uibModal,$ClockingLogs,$q,UserDetails,Holiday,Messenger){$scope.service=$ClockingLogs;$scope.$watch("service.selected_period",function(value){if(value){$scope.load()}});$scope.load=function(){AdminTimeLog.get({user_id:$scope.user_details.user_id,startDate:$ClockingLogs.selected_period.startDate.hours(0).minutes(0).seconds(0).format("X"),endDate:$ClockingLogs.selected_period.endDate.hours(23).minutes(59).seconds(59).format("X")},function(response){$scope.data=$ClockingLogs.prepareData(response.data);$scope.days=$scope.calDays($scope.data);$scope.count=10;$scope.show={};$scope.show[new Date().toISOString().substr(0,10)]=!0},ErrorsHandler)};$scope.calDays=function(data){output={};Object.keys(data).forEach(key=>{el=data[key];el.forEach(obj=>{output[key]=output[key]||0;if(obj.hours){output[key]+=obj.hours}
output[key]=Math.round(output[key]*100)/100})});return output};$scope.keys=function(data){return data?Object.keys(data).sort():[]};$scope.saveUserData=function(name,value){var deferred=$q.defer(),data={user_id:$scope.user_details.user_id};data[name]=value;UserDetails.update(data,function(response){deferred.resolve()},function(res){deferred.reject(res.data.error)});return deferred.promise};$scope.requestChange=function(log){var modalInstance=$uibModal.open({templateUrl:"hr/modals/request_change.html",controller:"LogTimeRequestChangeCtrl",resolve:{options:function(){return{admin:!0,log:log}}}});modalInstance.result.then(function(response){$scope.load()})};$scope.newRecord=function(date,type){var modalInstance=$uibModal.open({templateUrl:"hr/modals/new_record_request.html",controller:"NewRecordRequestCtrl",resolve:{options:function(){return{admin:!0,date:date?date:new Date(),type:type?type:"in",user:$scope.user_details}}}});modalInstance.result.then(function(response){$scope.load()})};$scope.showApproveDeclineModal=function(log){var modalInstance=$uibModal.open({templateUrl:"hr/modals/approve-decline-time.html",controller:"ApproveDeclineTimeModalCtrl",resolve:{options:function(){return{log:log}}}});modalInstance.result.then(function(response){$scope.load()})};$scope.editAddress=function(){var modalInstance=$uibModal.open({templateUrl:"hr/modals/user-address.html",controller:"EditUserAddressCtrl",resolve:{options:function(){return{user:$scope.user_details}}}});modalInstance.result.then(function(response){angular.extend($scope.user_details,response||{})})};$scope.cancelHoliday=function(holiday){Holiday.update({id:holiday.id,status:"cancelled"},function(response){Messenger("The holiday was cancelled!");angular.extend(holiday,response||{})},ErrorsHandler)}}]);archDB.controller('HolidaysCalendarCtrl',['$scope','Holiday','ErrorsHandler',function($scope,Holiday,ErrorsHandler){var EventsLoaded=!1;$scope.eventSources=[{events:function(start,end,callback){$scope.loadCalendarEvents(Math.round(start.getTime()/1000),Math.round(end.getTime()/1000),callback);if(EventsLoaded){$scope.$apply()}
EventsLoaded=!0},editable:!1}];$scope.calendar={editable:!1,defaultView:'month',firstDay:1,axisFormat:'HH:mm',timeFormat:{agenda:'HH:mm{ - HH:mm}'},header:{left:'prev, next',center:'title',right:'today'}};$scope.loadCalendarEvents=function(start,end,callback){Holiday.get({startDate:start,endDate:end,status:'approved'},function(response){callback(response.data)},ErrorsHandler)}}]);archDB.controller('HrGenerateTimesheetCtrl',['$scope','$uibModalInstance','$ClockingLogs','$http',function($scope,$uibModalInstance,$ClockingLogs,$http){$scope.service=$ClockingLogs;$scope.data={send_email:'0',download:'1'};if($scope.$parent.$parent){$scope.user=$scope.$parent.$parent.user_details.user}
$scope.$watch('service.selected_period',function(value){if(value){$scope.data.start=$ClockingLogs.selected_period.startDate.hours(0).minutes(0).seconds(0).format('YYYY-MM-DD HH:mm:ss');$scope.data.end=$ClockingLogs.selected_period.endDate.hours(23).minutes(59).seconds(59).format('YYYY-MM-DD HH:mm:ss')}});$scope.submit=function(){$scope.loading=!0;if($scope.hasOwnProperty('user')){$http.post('/hr/admin/user/'+$scope.user.id+'/report',$scope.data).success(function(response){$scope.loading=!1;$scope.error=!1;$scope.response=response}).error(function(){$scope.error=!0;$scope.loading=!1;$scope.response=null})}else{$http.post('/hr/admin/users/report',$scope.data).success(function(response){$scope.loading=!1;$scope.error=!1;$scope.response=response}).error(function(){$scope.error=!0;$scope.loading=!1;$scope.response=null})}};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);archDB.controller('HrUsersCtrl',['$scope','UserDetails','ErrorsHandler','$uibModal',function($scope,UserDetails,ErrorsHandler,$uibModal){$scope.load=function(){UserDetails.get(function(response){if(response.reload==!0){$scope.load()}else{$scope.users=response.data}},ErrorsHandler)};$scope.runningCosts=function(){var total=0;_.each($scope.users,function(el){if(el.latest_time_log&&el.latest_time_log.type=='in'){total+=parseFloat(el.rate_per_hour)}});return total};$scope.generateTimesheet=function(){var modalInstance=$uibModal.open({templateUrl:'hr/modals/generate_timesheet.html',controller:'HrGenerateTimesheetCtrl'})};$scope.load()}]);archDB.controller('TimeLogPendingRequestsCtrl',['$scope','TimeLogChangeRequest','ErrorsHandler','Messenger','$uibModal',function($scope,TimeLogChangeRequest,ErrorsHandler,Messenger,$uibModal){$scope.load=function(){TimeLogChangeRequest.get({open:!0},function(response){_.each(response.data,function(el){el.time=moment(el.time);if(el.time_log)el.time_log.time=moment(el.time_log.time);});$scope.requests=response.data},ErrorsHandler)};$scope.edit=function(request){var modalInstance=$uibModal.open({templateUrl:'hr/modals/approve-decline-time.html',controller:'ApproveDeclineTimeModalCtrl',resolve:{options:function(){return{log:request}}}});modalInstance.result.then(function(response){$scope.requests=_.reject($scope.requests,function(el){return el.id==request.id})})};$scope.approve=function(request){TimeLogChangeRequest.update({id:request.id,completed:1},function(response){Messenger("The request was approved!");$scope.requests=_.reject($scope.requests,function(el){return el.id==request.id})},ErrorsHandler)};$scope.decline=function(request){TimeLogChangeRequest.update({id:request.id,declined:1},function(response){Messenger("The request was declined!");$scope.requests=_.reject($scope.requests,function(el){return el.id==request.id})},ErrorsHandler)};$scope.load()}]);archDB.factory('Reminder',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var Reminder=$resource(apiUrl('reminders/:reminderId'),{reminderId:'@id'},methods);angular.extend(Reminder.prototype,_prototype);return Reminder}]);archDB.controller('ReminderModalCtrl',['$scope','$AuthUser','DateTime','$uibModalInstance','Reminder','ErrorsHandler',function($scope,$AuthUser,DateTime,$uibModalInstance,Reminder,ErrorsHandler){$scope.data={user_id:$AuthUser.data.id,};$scope.selected_date=angular.copy(DateTime.defaults);$scope.selected_date.date=new Date();$scope.DateTime=DateTime;$scope.$watch('selected_date',function(value){var date=moment(value.date);$scope.data.remind_at=moment({day:parseInt(date.format('DD'),10),month:parseInt(date.format('MM'),10)-1,year:parseInt(date.format('YYYY'),10),hour:parseInt(value.hours,10),minute:parseInt(value.minutes,10),second:0}).format('YYYY-MM-DD HH:mm:ss')},!0);$scope.save=function(){Reminder.save($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);archDB.controller('RemindersCtrl',['$scope','Reminder','ErrorsHandler','$timeout','$uibModal','$DesktopNotification','$AuthUser','ngAudio','$interval',function($scope,Reminder,ErrorsHandler,$timeout,$uibModal,$DesktopNotification,$AuthUser,ngAudio,$interval){$scope.notifications=0;$scope.reminders=[];$scope.$on('popover-shown',function(ev){$scope.notifications=0});$scope.$on('popover-hidden',function(){_.each($scope.reminders,function(r){r.highlight=!1})});$scope.load=function(page){if(!page)page=1;Reminder.get({page:page},function(response){$scope.reminders=response.data;$scope.setAlerts($scope.reminders)},ErrorsHandler)};$scope.setAlerts=function(reminders){_.each(reminders,function(r){if(r.timeout)$timeout.cancel(r.timeout);var seconds=moment(r.remind_at).diff(moment(),'seconds');var postpone=function(reminder,measurement,value){if(!measurement)measurement='minutes';if(!value)value=10;return Reminder.update({id:reminder.id,remind_at:moment(reminder.remind_at).add(measurement,value).format("YYYY-MM-DD HH:mm:ss")},function(response){reminder.remind_at=response.remind_at;reminder.notified=!1;reminder.alert.update({message:'Your reminder has been postponed for another '+value+' '+measurement,type:'success',hideAfter:3,actions:!1});if(reminder.sound_loop){ngAudio.stop('/audio/reminder.mp3');$interval.cancel(reminder.sound_loop)}
$scope.setAlerts($scope.reminders)},ErrorsHandler)};if(seconds>0){r.timeout=$timeout(function(){if(r.completed=='1'){r.highlight=!0;if(!r.notified){r.notified=!0;$scope.notifications++}
$DesktopNotification.show('Reminder',r.message);ngAudio.play('/audio/reminder.mp3');if(r.sound_loop)$interval.cancel(r.sound_loop);r.sound_loop=$interval(function(){ngAudio.play('/audio/reminder.mp3')},28*1000);r.alert=Messenger().post({message:moment(r.remind_at).format("HH:mm [- Reminder]")+"<br>"+r.message,type:'info',hideAfter:0,actions:{close:{label:'Close',action:function(){if(r.sound_loop)$interval.cancel(r.sound_loop);r.alert.update({hideAfter:1,message:'Reminder closed.',type:'success',actions:!1});ngAudio.stop('/audio/reminder.mp3');$timeout(function(){$scope.setAlerts($scope.reminders)},60*1000)}},postponeTenMinutes:{label:'10m',action:function(){postpone(r,'minutes',10)}},postponeOneHour:{label:'1H',action:function(){postpone(r,'hours',10)}},postponeOneDay:{label:'1D',action:function(){postpone(r,'days',1)}}}})}},seconds*1000)}})};$scope.showModal=function(reminder){var modalInstance=$uibModal.open({templateUrl:'reminders/modals/reminder.html',controller:'ReminderModalCtrl'});modalInstance.result.then(function(response){response.completed=0;$scope.reminders.push(response);$scope.setAlerts($scope.reminders)})};$scope.toggleComplete=function(reminder){var status=(reminder.completed=='1')?0:1;Reminder.update({id:reminder.id,completed:status},function(response){angular.extend(reminder,response||{})},ErrorsHandler)};$timeout(function(){$scope.load()},500)}]);archDB.directive('reminders',[function(){return{restrict:'A',controller:'RemindersCtrl',scope:!0,link:function(scope,iElement,iAttrs){}}}]);archDB.factory('WorkflowEvent',['apiUrl','$resource','ResourceMethods','ResourcePrototype',function(apiUrl,$resource,methods,_prototype){var WorkflowEvent=$resource('/workflow/:workflowId',{workflowId:'@id'},methods);angular.extend(WorkflowEvent.prototype,_prototype);return WorkflowEvent}]);Bookings.controller('archAuthCtrl',['$scope','archAuthService','$http','ErrorsHandler','$uibModalInstance',function($scope,service,$http,ErrorsHandler,$uibModalInstance){$scope.service=service;$scope.data={};$scope.helper={loading:!1,};$scope.hideModal=function(){$uibModalInstance.dismiss()};$scope.submit=function(){if($scope.helper.loading)
return;$scope.helper.loading=!0;$http.post('/auth/validate',$scope.data,{params:{login:!0}}).success(function(response){if(response.success===!0){service.respond(!0,$scope.helper)}else{$scope.helper.loading=!1;$scope.data.password='';Messenger().post({message:'Invalid credentials',hideAfter:5,type:'error'})}}).error(function(a,b){$scope.helper.loading=!1;ErrorsHandler(a,b)})}}]);Bookings.controller('assignTaskCtrl',['$scope','$element','Tasks','ErrorsHandler','$http','apiUrl','$paginator','$routeParams','$location',function($scope,$element,Tasks,ErrorsHandler,$http,apiUrl,$paginator,$routeParams,$location){$scope.pills=[{title:'Open'},{title:'Closed'}];$paginator=angular.copy($paginator);$scope.paginator=$paginator;$scope.navType='pills';$scope.$watch('pills',function(value){_.each(value,function(el,i){if(el.active)
$scope.selected_filter=i})},!0);$scope.$watch('selected_filter',function(value){if(typeof value!='undefined')
$scope.loadTasks();});$scope.$watch('search_filter',function(value){if(typeof value!='undefined')
$scope.loadTasks();});$paginator.setOnPageChange(function(page){$scope.loadTasks(page)});$scope.show=function(){$scope.loadTasks();$element.modal('show')};$scope.hide=function(){$element.modal('hide')};$scope.loadTasks=function(page){if(!$scope.data)return!1;if(!page)page=1;var params={client:!0,client_id:$scope.data.client_id,client_type:$scope.data.client_type,limit:10,project_free:!0,user:!0,page:page,textSearch:$scope.search_filter};if($scope.selected_filter=='0')params.show='open';if($scope.selected_filter=='1')params.completed=1;Tasks.get(params,function(response){$scope.tasks=response.data;$paginator.setFromResponse(response)},ErrorsHandler)};$scope.selectedCount=function(){var total=0;_.each($scope.tasks,function(el){if(el.selected)total++});return total};$scope.getSelected=function(){var tasks=_.filter($scope.tasks,function(el){return el.selected===!0});return _.pluck(tasks,'id')};$scope.assign=function(){$http.post(apiUrl('tasks/helpers/assign-tasks-to-project'),{project_id:$scope.data.id,tasks:$scope.getSelected()}).success(function(response){$scope.data.tasks=response;$scope.hide()}).error(ErrorsHandler)}}]);Bookings.controller('AttachmentsCtrl',['$scope','Files','ErrorsHandler',function($scope,Files,ErrorsHandler){$scope.files=[];$scope.$watch('task.id',function(value){if(value)$scope.loadFiles();});$scope.loadFiles=function(){Files.get({objectType:$scope.type,objectId:$scope.task.id,limit:100},function(response){$scope.files=response.data},ErrorsHandler)};$scope.removeFile=function(file){Files.remove({fileId:file.id},function(){$scope.files=_.reject($scope.files,function(el){return el.id==file.id})},ErrorsHandler)}}]);Bookings.controller('BookingPasswordsCtrl',['$scope','Device','$ClientsManager','ErrorsHandler','$http','apiUrl','Keychain','Messenger',function($scope,Device,$ClientsManager,ErrorsHandler,$http,apiUrl,Keychain,Messenger){$scope.data=[];$scope.data.client_devices=[];$scope.data.client_keychains=[];$scope.data.keychain={device_id:null,username:'',password:'',type:'windows',id:'0'};$scope.addKeychain=function(keychain){$scope.task.keychains.push(angular.copy(keychain));$scope.data.keychain={device_id:null,username:'',password:'',type:'windows',id:'0'}};$scope.removeKeychain=function(device){$scope.task.keychains=_.reject($scope.task.keychains,function(el){return el===device})};$scope.getDeviceName=function(id){var device=_.find($scope.data.client_devices,function(device){return device.id==id});if(device)
return device.name;return"No device"};$scope.$watch(function(){return $ClientsManager.client},function(){if($ClientsManager.getClient().id){$scope.loadDevices($ClientsManager.getClient().id,$ClientsManager.getClient().type);$scope.loadClientPasswords()}},!0);$scope.$watch('task.devices',function(value){_.each($scope.task.keychains,function(item){if($scope.belongsToADifferentDevice(item))
$scope.removeKeychain(item);})});$scope.loadClientPasswords=function(){var client_type=$ClientsManager.getClient().type;var client_id=$ClientsManager.getClient().id;Keychain.get({client_type:client_type,client_id:client_id},function(response){$scope.data.client_keychains=response.data})};$scope.loadDevices=function(client_id,client_type){if($scope.loading_devices)return!1;$scope.loading_devices=!0;Device.get({client_id:client_id,client_type:client_type},function(response){$scope.data.client_devices=response.data;$scope.loading_devices=!1},ErrorsHandler)};$scope.isSelectedPassword=function(password){if(!password)
return!1;return _.find($scope.task.keychains,function(item){return item.id==password.id})?!0:!1};$scope.belongsToADifferentDevice=function(password){if(!password||!password.device_id)
return!1;return _.find($scope.task.devices,function(item){return item.id==password.device_id})?!1:!0};$scope.addPassword=function(password){if(!password.username||!password.password)
return;if(!$ClientsManager.getClient())
return;var client_type=$ClientsManager.getClient().type;var client_id=$ClientsManager.getClient().id;var parameters={client_type:client_type,client_id:client_id,password:password.password,username:password.username,type:password.type,device_id:password.device_id};Keychain.save(parameters,function(response){Messenger("The password has been created successfully");$scope.addKeychain(response);$scope.loadClientPasswords()},ErrorsHandler)};$scope.removePassword=function(password){Keychain.delete({keychainId:password.id,disabled:!0},function(){Messenger("The password has been deleted successfully!");$scope.loadClientPasswords()})};$scope.copyToClipboard=function(text){if(!text)return;var textarea=document.createElement('textarea');textarea.value=text;textarea.style.position='fixed';textarea.style.opacity='0';document.body.appendChild(textarea);textarea.select();try{document.execCommand('copy');Messenger("Password copied to clipboard!")}catch(err){Messenger("Failed to copy password","error")}
document.body.removeChild(textarea)}}]);Bookings.controller('BookingsMainCtrl',['$scope','Client','$routeParams','$location','Customer','ErrorsHandler','CustomerForm','ClientForm','$rootScope','$ClientsManager','$uibModal','$timeout',function($scope,Client,$routeParams,$location,Customer,ErrorsHandler,CustomerForm,ClientForm,$rootScope,$ClientsManager,$uibModal,$timeout){var filterClientsDatum=function(res){if(res.success){var datums=[];_.each(res.data,function(item){datums.push({value:item.name,data:item,type:'client',tokens:item.name.split(' ')})});return datums}else{return[]}};var filterCustomersDatum=function(res){if(res.success){var datums=[];_.each(res.data,function(item){datums.push({value:item.name,data:item,type:'customer',tokens:[item.first_name,item.last_name]})});return datums}else{return[]}};$scope.searchCustomers=[{name:'clients',header:'<h6 class="typeahead-header">Clients</h6>',remote:{url:Client.getSearchUrl()+'%QUERY',filter:filterClientsDatum}},{name:'customers',header:'<h6 class="typeahead-header">Customers</h6>',remote:{url:Customer.getSearchUrl()+'%QUERY',filter:filterCustomersDatum}}];$scope.customerModalOpts={onClose:function(){}};$scope.clientManager=$ClientsManager;CustomerForm.onCreate=function(data){$scope.customerModal.dismiss();$ClientsManager.setClient(data);$ClientsManager.setClientType("customer");$scope.client_search=data.name};ClientForm.onCreate=function(data){$scope.showClientForm=!1;$ClientsManager.setClient(data);$ClientsManager.setClientType("client");$scope.client_search=data.name};CustomerForm.onUpdate=function(data){$scope.customerModal.dismiss();$routeParams.customerId=null;$ClientsManager.setClient(data);$ClientsManager.setClientType("customer")};if(!$location.path()){}
$scope.$on('$routeChangeSuccess',function(){if($routeParams.projectId&&typeof($routeParams.projectId)!='undefined'){$scope.projectId=$routeParams.projectId}
if($routeParams.clientId&&$routeParams.clientType){if($routeParams.clientType=='customer'){Customer.get({customer:$routeParams.clientId},function(response){$ClientsManager.setClient(response.data);$ClientsManager.setClientType('customer')},ErrorsHandler)}else{Client.get({client:$routeParams.clientId,sites:!0,site_users:!0},function(response){$ClientsManager.setClient(response.data);$ClientsManager.setClientType('client')},ErrorsHandler)}}});$scope.customerSelected=function(ev,datum){if(datum.type=='client'){Client.get({client:datum.data.id,site_users:!0},function(res){if(res.success){$ClientsManager.setClient(res.data);$ClientsManager.setClientType('client')}},ErrorsHandler)}else{Customer.get({customer:datum.data.id},function(res){if(res.success){$ClientsManager.setClient(res.data);$ClientsManager.setClientType('customer')}},ErrorsHandler)}};$scope.submitButtonText=function(){return($ClientsManager.isSelected())?'Update':'Create'};$scope.focusSearch=function(){angular.element('#client_search').focus()};$scope.addCustomer=function(type){$scope.customerModal=$uibModal.open({templateUrl:'clients/modals/new-customer.html',controller:'AddCustomerCtrl',size:'lg'});$scope.customerModal.opened.then(function(){$routeParams.customerId=null;$rootScope.$broadcast('$routeChangeSuccess')})};$scope.editCustomer=function(){$scope.customerModal=$uibModal.open({templateUrl:'clients/modals/new-customer.html',controller:'EditCustomerCtrl',size:'lg',resolve:{client:function(){return $ClientsManager.getClient()}}});$scope.customerModal.result.then(function(client){$ClientsManager.setClient(client);$ClientsManager.setClientType(client.type)})};$scope.isPage=function(page){return($location.path().indexOf(page)!=-1)};$scope.noTypeSelected=function(){return($location.path()=='/bookings'||$location.path().indexOf('/bookings/client')!=-1||$location.path().indexOf('/bookings/customer')!=-1)};$scope.formatTabUrl=function(url){if($routeParams.clientId&&$routeParams.clientType)
return'#bookings/'+url+'/'+$routeParams.clientType+'/'+$routeParams.clientId;return'#bookings/'+url}}]);Bookings.controller('BookingsMessageCtrl',['$scope','$route','$routeParams','$location','_messageForm','$ClientsManager','BookingsLocationManager',function($scope,$route,$routeParams,$location,_messageForm,$ClientsManager,BookingsLocationManager){$scope.data=_messageForm.data;$scope.clientManager=$ClientsManager;$scope.locationManager=BookingsLocationManager;$scope.form=_messageForm;$scope.routeParams=$routeParams;if(typeof($scope.$parent.projectId!='undefined')){$scope.data.project_id=$scope.$parent.projectId}
$scope.$watch('data.site_user_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.$watch('data.site_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.save=_messageForm.save}]);Bookings.controller('BookingsOnsiteCtrl',['$scope','$route','$routeParams','$location','_onsiteForm','$ClientsManager','BookingsLocationManager','$http','apiUrl','$filter','$timeout','TaskTemplatesService',function($scope,$route,$routeParams,$location,_onsiteForm,$ClientsManager,BookingsLocationManager,$http,apiUrl,$filter,$timeout,TaskTemplatesService){var timeRenderer=function(date){var data=[];if(!date)date=new Date();for(var i=0;i<24;i++){for(var j=0;j<60;j+=15){var h=(i<10)?'0'+i:i;var m=(j<10)?'0'+j:j;data.push({label:h+':'+m,value:h+':'+m+':00'})}}
return data};$scope.selectedDate=null;$scope.times=timeRenderer();$scope.data=_onsiteForm.data;$scope.clientManager=$ClientsManager;$scope.locationManager=BookingsLocationManager;$scope.form=_onsiteForm;$scope.routeParams=$routeParams;if(typeof($scope.$parent.projectId!='undefined')){$scope.data.project_id=$scope.$parent.projectId}
$scope.templatesRemoteOptions=TaskTemplatesService;$scope.start_time='09:00:00';$scope.end_time='10:00:00';$scope.onsite_start=moment().hours(9).minutes(0).seconds(0);$scope.onsite_end=moment().hours(10).minutes(0).seconds(0);var EventsLoaded=!1;$scope.newEvent={title:'New',start:'',end:'',allDay:!1,editable:!0};$scope.updateData=function(){$scope.data.onsite_start=$scope.onsite_start.format('YYYY-MM-DD HH:mm');$scope.data.onsite_end=$scope.onsite_end.format('YYYY-MM-DD HH:mm');$scope.newEvent.start=$scope.data.onsite_start;$scope.newEvent.end=$scope.data.onsite_end};$scope.updateData();$scope.eventSources=[[$scope.newEvent],{events:function(start,end,callback){$scope.loadCalendarEvents(Math.round(start.getTime()/1000),Math.round(end.getTime()/1000),callback);if(EventsLoaded){$scope.times=timeRenderer(start);$scope.updateSelectedTime(start,end)}
EventsLoaded=!0},editable:!1}];$scope.$watch('data.site_user_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.$watch('data.site_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.$watch('data.title',function(){if(typeof $scope.data.title=='undefined')return!1;$scope.newEvent.title=$scope.data.title});$scope.$watch('start_time',function(value){$scope.updateStartTime(value)},!0);$scope.$watch('end_time',function(value){$scope.updateEndTime(value)},!0);$scope.$watch('newEvent.allDay',function(value){if(value)
$scope.newEvent.allDay=!1;$scope.fullCalendar.fullCalendar('render')});$scope.$watch('selectedDate',function(value){if(value){$scope.fullCalendar.fullCalendar('gotoDate',value);$('.datepicker').hide()}});$scope.save=_onsiteForm.save;$scope.loadCalendarEvents=function(start,end,callback){$http.get(apiUrl('calendar'),{params:{start_date:start,end_date:end}}).success(function(response){callback(response)})};$scope.eventResize=function(event,dayDelta,minuteDelta,revertFunc){$scope.end_time=moment(event.end).format('HH:mm:ss')};$scope.eventDrop=function(event){$scope.start_time=moment(event.start).format('HH:mm:ss');$scope.end_time=moment(event.end).format('HH:mm:ss')};$scope.calendar={editable:!1,defaultView:'agendaDay',firstDay:1,firstHour:9,hiddenDays:[0],axisFormat:'HH:mm',timeFormat:{agenda:'HH:mm{ - HH:mm}'},header:{left:'today',center:'title',right:'prev, next, agendaDay, agendaWeek, month'},eventResize:$scope.eventResize,eventDrop:$scope.eventDrop};$scope.updateSelectedTime=function(start,end){var start_date=moment($scope.onsite_start);var end_date=moment($scope.onsite_end);$scope.onsite_start=moment(start).hours(start_date.hours()).minutes(start_date.minutes());$scope.onsite_end=moment(start).hours(end_date.hours()).minutes(end_date.minutes());$scope.updateData()};$scope.updateStartTime=function(value){var start=moment(value,'HH:mm');$scope.onsite_start.hours(start.hours()).minutes(start.minutes());$scope.updateData()};$scope.updateEndTime=function(value){var end=moment(value,'HH:mm');$scope.onsite_end.hours(end.hours()).minutes(end.minutes());$scope.updateData()};$timeout(function(){$scope.data.title='';$scope.$apply()},10)}]);Bookings.controller('BookingsOrderCtrl',['$scope','$route','$routeParams','$location','_orderForm','$ClientsManager','BookingsLocationManager','PartsService',function($scope,$route,$routeParams,$location,_orderForm,$ClientsManager,BookingsLocationManager,PartsService){$scope.data=_orderForm.data;$scope.data.parts=PartsService.parts;$scope.clientManager=$ClientsManager;$scope.locationManager=BookingsLocationManager;$scope.form=_orderForm;$scope.routeParams=$routeParams;if(typeof($scope.$parent.projectId!='undefined')){$scope.data.project_id=$scope.$parent.projectId}
$scope.$watch('data.site_user_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.$watch('data.site_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.save=function(){if(!PartsService.hasPartsOnOrder()){alert("To create an order, at least one part needs to be ordered!");return}
_orderForm.save()};$scope.addPart=function(event,data){var part=_.find($scope.data.parts,function(el){if(el.stock)
return el.stock.id==data.id&&!el.on_order;return!1});$scope.$apply(function(){if(part){part.quantity++;$scope.updatePart(part)}else{part={related_type:'task',stock:data,stock_id:data.id,quantity:1,price:data.price_excluding_vat};$scope.data.parts.push(part)}})};$scope.removePart=function(part){var index=_.findIndex($scope.parts,function(el){return el.stock_id===part.stock_id&&el.on_order==part.on_order});$scope.parts.splice(index,1)}
$scope.subtotal=function(){var subtotal=0;_.each($scope.parts,function(part){subtotal+=part.price*part.quantity});return subtotal}
$scope.total=function(){return $scope.subtotal()*(1+$scope.vat/100)}
$scope.tax=function(){return $scope.subtotal()*($scope.vat/100)}}]);Bookings.controller('BookingsProjectCtrl',['$scope','$route','$routeParams','$ClientsManager','BookingsLocationManager','Project','archAuthService','ErrorsHandler','$window',function($scope,$route,$routeParams,$ClientsManager,BookingsLocationManager,Project,auth,ErrorsHandler,$window){$scope.data={};$scope.clientManager=$ClientsManager;$scope.locationManager=BookingsLocationManager;$scope.routeParams=$routeParams;if(typeof($scope.$parent.projectId!='undefined')){$scope.data.project_id=$scope.$parent.projectId}
$scope.$watch('data.site_user_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.$watch('data.site_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.save=function(){auth.confirm(function(status,helper){if(status){$scope.data.client_id=$ClientsManager.getClient().id;$scope.data.client_type=$ClientsManager.getClientType();Project.save($scope.data,function(res){$window.onbeforeunload=function(){};Messenger("Your project has been successfully created!");$window.location.href="/projects/"+res.id},function(a,b){helper.loading=!1;ErrorsHandler(a,b)})}})}}]);Bookings.controller('BookingsRepairCtrl',['$scope','$route','$routeParams','$location','repairsForm','$ClientsManager','BookingsLocationManager','apiUrl','$http','ErrorsHandler','TaskTemplatesService','Messenger',function($scope,$route,$routeParams,$location,repairsForm,$ClientsManager,BookingsLocationManager,apiUrl,$http,ErrorsHandler,TaskTemplatesService,Messenger){$scope.data=repairsForm.data;$scope.clientManager=$ClientsManager;$scope.locationManager=BookingsLocationManager;$scope.modal=repairsForm.modal;$scope.contract={};$scope.form=repairsForm;$scope.routeParams=$routeParams;if(typeof($scope.$parent.projectId!='undefined')){$scope.data.project_id=$scope.$parent.projectId}
$scope.templatesRemoteOptions=TaskTemplatesService;$scope.templates={"selected":null,"lists":{"Selected Templates":repairsForm.data.templates,"Available Templates":[]}};$scope.changeOfList=function(currentList,index){if(currentList=="Available Templates"){$scope.templates.lists["Selected Templates"].push($scope.templates.lists[currentList][index]);$scope.templates.lists[currentList].splice(index,1)}
if(currentList=="Selected Templates"){$scope.templates.lists["Available Templates"].push($scope.templates.lists[currentList][index]);$scope.templates.lists[currentList].splice(index,1)}};TaskTemplatesService.getTemplates('repair').then(function(response){$scope.templates.lists['Available Templates']=response.data});$scope.$watch('data.site_user_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.$watch('data.site_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.redirectToSales=function(){window.location='/sales#/sales/new/booking/'+$ClientsManager.getClientType()+'/'+$ClientsManager.getClient().id+'/'+$scope.data.id};$scope.validateTaskId=function(id){if(id){$http.get(apiUrl('tasks/helpers/validate-task-id/'+id),{params:{type:'repair'}}).success(function(response){if(!response.success)
$scope.invalid_task_id=!0;else $scope.invalid_task_id=!1})}else{$scope.invalid_task_id=!1}};$scope.formIsValid=function(){if(!$scope.clientManager.isSelected())
return!1;if(!$scope.data.devices||$scope.data.devices.length==0)
return!1;if($scope.data.has_password=='1'&&$scope.data.keychains.length==0)
return!1;return!0}}]);Bookings.controller('BookingsRmaCtrl',['$scope','$route','$routeParams','$location','_rmaForm','$ClientsManager','BookingsLocationManager',function($scope,$route,$routeParams,$location,_rmaForm,$ClientsManager,BookingsLocationManager){$scope.data=_rmaForm.data;$scope.clientManager=$ClientsManager;$scope.locationManager=BookingsLocationManager;$scope.form=_rmaForm;$scope.routeParams=$routeParams;if(typeof($scope.$parent.projectId!='undefined')){$scope.data.project_id=$scope.$parent.projectId}
$scope.$watch('data.site_user_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.$watch('data.site_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.save=_rmaForm.save;$scope.findItem=function(item){return _.find($scope.data.items,function(el){return el.id==item.id})};$scope.addItem=function(item){var existing_item=$scope.findItem(item);if(existing_item){existing_item.quantity++}else{$scope.data.items.push(item)}};$scope.removeItem=function(item){$scope.data.items=_.reject($scope.data.items,function(el){return el.id==item.id})}}]);Bookings.controller('BookingsSaleLeadCtrl',['$scope','$route','$routeParams','$ClientsManager','BookingsLocationManager','SaleLead','archAuthService','ErrorsHandler','$window',function($scope,$route,$routeParams,$ClientsManager,BookingsLocationManager,SaleLead,auth,ErrorsHandler,$window){$scope.data={};$scope.max_stars=5;$scope.clientManager=$ClientsManager;$scope.locationManager=BookingsLocationManager;$scope.routeParams=$routeParams;if(typeof($scope.$parent.projectId!='undefined')){$scope.data.project_id=$scope.$parent.projectId}
$scope.$watch('data.site_user_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.$watch('data.site_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.hoveringOver=function(value){$scope.overStar=value;$scope.percentage=100*(value/$scope.max_stars)};$scope.save=function(){auth.confirm(function(status,helper){if(status){$scope.data.client_id=$ClientsManager.getClient().id;$scope.data.client_type=$ClientsManager.getClientType();SaleLead.save($scope.data,function(res){$window.onbeforeunload=function(){};Messenger("Your sale lead has been successfully created!");$window.location.href="/tasks/sale-lead/"+res.id},function(a,b){helper.loading=!1;ErrorsHandler(a,b)})}})}}]);Bookings.controller('BookingsSupportCtrl',['$scope','$route','$routeParams','$location','supportForm','$ClientsManager','BookingsLocationManager','TaskTemplatesService',function($scope,$route,$routeParams,$location,supportForm,$ClientsManager,BookingsLocationManager,TaskTemplatesService){$scope.data=supportForm.data;$scope.clientManager=$ClientsManager;$scope.locationManager=BookingsLocationManager;$scope.form=supportForm;$scope.routeParams=$routeParams;if(typeof($scope.$parent.projectId!='undefined')){$scope.data.project_id=$scope.$parent.projectId}
$scope.templatesRemoteOptions=TaskTemplatesService;$scope.$watch('data.site_user_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.$watch('data.site_id',function(){$scope.data.direct_dial=BookingsLocationManager.getUserDDI($scope.data.site_user_id)});$scope.$watch('data.urgent',function(value){if(value=='1'&&!$scope.data.severity_id){$scope.data.severity_id=3}})}]);Bookings.controller('BuildCompositeItemsCtrl',['$scope','build','$uibModalInstance','Build','ErrorsHandler',function($scope,build,$uibModalInstance,Build,ErrorsHandler){$scope.build=angular.copy(build);$scope.addBuild=function(ev,data){var build_items=$scope.build.status=='planning'?$scope.build.planned_components:$scope.build.stock.composite_items;var existingItem=_.findWhere(build_items,{id:data.id});if(existingItem){existingItem.pivot.quantity++;return}
data.pivot={quantity:1};build_items.push(data)};$scope.save=function(){$scope.error=null;Build.update($scope.build,function(response){$scope.loading=!1;$uibModalInstance.close(response)},function(response){$scope.error=response.data.error.message})};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')};$scope.quantityOnOrder=function(build,item){var quantity=0;if(build.status=='in_progress')
_.each(build.ordered_components,function(c){if(c.id==item.id){quantity=c.pivot.quantity;return}});return quantity}}]);Bookings.controller('ChangeAlarmCtrl',['$scope','$uibModalInstance','data','$window',function($scope,$uibModalInstance,data,$window){$scope.title='Set reminder';$scope.field_name='Reminder date';$scope.times=[];$scope.date={date:data.alarm_date?moment(data.alarm_date).toDate():moment().toDate(),time:moment(data.alarm_date).format('HH:mm')};var addPadding=function(i){return(i<10)?'0'+i:i};for(var i=0;i<24;i++){for(var j=0;j<60;j+=15){$scope.times.push(addPadding(i)+':'+addPadding(j))}}
$scope.save=function(){var timestamp=moment($scope.date.date).format('YYYY-MM-DD')+' '+$scope.date.time+':00';if(moment().valueOf()>moment(timestamp).valueOf()){$window.alert('The start date should be lower than the end date');return!1}
$uibModalInstance.close(timestamp)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Bookings.controller('ChangeClientCtrl',['$scope','$uibModalInstance','task','Client','Customer','ErrorsHandler','$timeout','Tasks',function($scope,$uibModalInstance,task,Client,Customer,ErrorsHandler,$timeout,Tasks){var search_timeout;$scope.search={type:'client',query:''};$scope.$watch('search.query',function(value){if(search_timeout)$timeout.cancel(search_timeout);search_timeout=$timeout(function(){$scope.load()},300)});$scope.$watch('search.type',function(){$scope.load()});$scope.load=function(){$scope.loading=!0;var model=($scope.search.type=='customer')?Customer:Client;model.get({name:$scope.search.query,sites:!0,site_users:!0},function(response){$scope.clients=response.data;$scope.loading=!1},ErrorsHandler)};$scope.getSite=function(){if(!$scope.selected||!$scope.selected.site_id)return{};return _.find($scope.selected.sites,function(el){return el.id==$scope.selected.site_id})||{}};$scope.select=function(item){$scope.selected=item;if(item.type=='client'){try{$scope.selected.site_id=item.sites[0].id;$scope.selected.site_user_id=item.sites[0].users[0].id}catch(err){console.log(err)}}};$scope.submit=function(){Tasks.update({id:task.id,client_id:$scope.selected.id,client_type:$scope.selected.type,site_user_id:$scope.selected.site_user_id,site_id:$scope.selected.site_id},function(response){angular.extend(task,response||{});$uibModalInstance.close([$scope.selected,response])},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Bookings.controller('ChangeDatesCtrl',['$scope','$uibModalInstance','data','$filter','Tasks','ErrorsHandler','Messenger',function($scope,$uibModalInstance,data,$filter,Tasks,ErrorsHandler,Messenger){$scope.data=data;$scope.times=[];$scope.data.start=moment(data.onsite_start).toDate();$scope.data.end=moment(data.onsite_end).toDate();$scope.data.start_time=moment(data.onsite_start).format('HH:mm');$scope.data.end_time=moment(data.onsite_end).format('HH:mm');var addPadding=function(i){return(i<10)?'0'+i:i};for(var i=0;i<24;i++){for(var j=0;j<60;j+=15){$scope.times.push(addPadding(i)+':'+addPadding(j))}}
$scope.save=function(){data.onsite_start=$filter('date')(new Date($scope.data.start),'yyyy-MM-dd')+' '+$scope.data.start_time+':00';data.onsite_end=$filter('date')(new Date($scope.data.end),'yyyy-MM-dd')+' '+$scope.data.end_time+':00';if(moment(data.onsite_start).valueOf()>moment(data.onsite_end).valueOf()){alert('The start date should be lower than the end date');return!1}
Tasks.update(data,function(response){Messenger("Task updated succesfully!");$uibModalInstance.close(response)},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Bookings.controller('ChangeDueDateCtrl',['$scope','$uibModalInstance','data','$window',function($scope,$uibModalInstance,data,$window){$scope.title='Set due date';$scope.field_name='Due date';$scope.times=[];$scope.date={date:data.due_date?moment(data.due_date).toDate():moment().toDate(),time:moment(data.due_date).format('HH:mm')};var addPadding=function(i){return(i<10)?'0'+i:i};for(var i=0;i<24;i++){for(var j=0;j<60;j+=15){$scope.times.push(addPadding(i)+':'+addPadding(j))}}
$scope.save=function(){var timestamp=moment($scope.date.date).format('YYYY-MM-DD')+' '+$scope.date.time+':00';if(moment().valueOf()>moment(timestamp).valueOf()){$window.alert('The start date should be lower than the end date');return!1}
$uibModalInstance.close(timestamp)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Bookings.controller('CreateNonExitingBuildCtrl',['$scope','Order','ErrorsHandler','PartsService','$filter','$ClientsManager','Stock','Build','$uibModal',function($scope,Order,ErrorsHandler,PartsService,$filter,$ClientsManager,Stock,Build,$uibModal){$scope.initData=function(){$scope.build={price:0,quantity:1,related_id:$scope.related.id,related_type:'task',planned_components:[]};$scope.hideError=!1};$scope.product={price_vat_included:1,severity_id:2,related_type:$scope.related_type,quantity:1,type:'non_stock',approval_required:1};$scope.newStock=function(){var modalInstance=$uibModal.open({templateUrl:'stock/stock-form.html',controller:'StockModalCtrl',resolve:{categories:function(){return $scope.categories},product_types:function(){return["stock"]},item:function(){return{}},quick_mode:function(){return!0}}});modalInstance.result.then(function(response){$scope.addComponent(null,response.data)},function(response){})['finally'](function(){})};$scope.showModal=function(){$scope.initData();$scope.modal=$uibModal.open({templateUrl:'modals/request-non-existing-build-modal.html',size:'lg',scope:$scope})};$scope.addComponent=function(ev,item){if(!$scope.build.planned_components){$scope.build.planned_components=[]}else{var existingItem=_.findWhere($scope.build.planned_components,{id:item.id});if(existingItem){existingItem.pivot.quantity++;return}}
item.pivot={quantity:1};$scope.build.planned_components.push(item)};$scope.hideModal=function(){$scope.modal.dismiss()};$scope.save=function(){if($scope.build.planned_components.length==0){$scope.errors="A build needs to have at least one item.";$scope.hideError=!1;return}
$scope.errors=null;$scope.loading=!0;Build.save($scope.build,function(response){$scope.hideModal();$scope.builds.push(response)},function(response){$scope.loading=!1;$scope.hideError=!1;if(response.data&&response.data.error){$scope.errors=response.data.error}else if(response.data&&response.data.error&&response.data.error.message){$scope.errors=response.data.error.message}else{$scope.errors="An error occurred while saving the build."}})}}]);Bookings.controller('CustomerDevicesCtrl',['$scope','Device','ErrorsHandler','$ClientsManager','$paginator','$uibModal','$timeout',function($scope,Device,ErrorsHandler,$ClientsManager,$paginator,$uibModal,$timeout){var search_timeout=200;$scope.devices=[];$scope.clientManager=$ClientsManager;$scope.view='all';$scope.search='';if(!_.isArray($scope.selected_devices))$scope.selected_devices=[];$paginator=angular.copy($paginator);$scope.paginator=$paginator;$paginator.setOnPageChange(function(page){$scope.loadDevices($ClientsManager.getClient().id,$ClientsManager.getClientType(),page)});$scope.$watch('clientManager.client',function(){if($ClientsManager.isSelected()){$scope.loadDevices($ClientsManager.getClient().id,$ClientsManager.getClientType())}},!0);$scope.$watch('selected_devices',function(){_.each($scope.devices,function(device){var d=_.find($scope.selected_devices,function(el){return el.id==device.id});device.selected=(d)?!0:!1});_.each($scope.selected_devices,function(device){if(!device.selected)
device.selected=!0})},!0);$scope.$watch('devices',function(){_.each($scope.devices,function(device){var d=_.find($scope.selected_devices,function(el){return el.id==device.id});device.selected=(d)?!0:!1})},!0);$scope.$watch('view',function(value){if(value=='all')
$scope.filter={};else $scope.filter={selected:!0}});$scope.$watch('search',function(value,oldValue){if(value!=oldValue&&$scope.client_id&&$scope.client_type){if(search_timeout)
$timeout.cancel(search_timeout);search_timeout=$timeout(function(){$scope.loadDevices()},250)}});$scope.$on('task-updated',function(){$scope.changed=!1});$scope.loadDevices=function(client_id,client_type,page){if($scope.loading_devices)return!1;if(!page)page=1;if(client_id&&client_type){$scope.client_id=client_id;$scope.client_type=client_type}
$scope.loading_devices=!0;Device.get({client_id:$scope.client_id,client_type:$scope.client_type,page:page,query:$scope.search,limit:5},function(response){$scope.devices=response.data;$scope.loading_devices=!1;$paginator.setFromResponse(response)},ErrorsHandler)};$scope.showDeviceModal=function(device){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/device.html',controller:'DeviceModalCtrl',size:'lg',resolve:{Customer:function(){return $ClientsManager.getClient()},data:function(){return device||{type:'laptop'}}}});modalInstance.result.then(function(response){if(device){angular.extend(device,response||{});device.model_number=response.model_number}else{$scope.devices.push(response);$scope.selectDevice(response)}})};$scope.selectDevice=function(device){device.selected=!device.selected;$scope.changed=!0;if(device.selected){$scope.selected_devices.push(device)}else{$scope.selected_devices=_.reject($scope.selected_devices,function(el){return el.id==device.id})}};$scope.getDevices=function(){return($scope.view=='all')?$scope.devices:$scope.selected_devices};$scope.getDeviceClass=function(device){switch(device.type){case 'laptop':return'fa fa-laptop';case 'pc':return'fa fa-desktop';case 'mac':return'fa fa-apple';case 'tablet':return'fa fa-tablet';case 'hdd':return'fa fa-hdd-o'}
return'fa fa-laptop'}}]);Bookings.controller('CustomerNotificationCtrl',['$scope','$uibModalInstance','$ClientsManager','ErrorsHandler','Messenger','Tasks','taskId','notification','CustomerNotificationsService',function($scope,$uibModalInstance,$ClientsManager,ErrorsHandler,Messenger,Tasks,taskId,notification,CustomerNotificationsService){$scope.email={selected:!1,enabled:$ClientsManager.data.email?!0:!1,send_to:$ClientsManager.data.email,sending:!1,subject:'Job number '+taskId+' is now completed.'};$scope.email.text="Dear "+$ClientsManager.data.name+",<br><br>Just writing to inform you that your job (REF:  "+taskId+" ) is now ready for collection!<br><br>Opening Hours:<br>Monday - Friday 9:00am - 5:00pm<br>Saturdays: 10:00am - 12:30pm<br><br><br>Kind Regards,<br>Computer Assistance<br>t: 01865451177 | e: info@computerassistance.co.uk<br>154 Oxford Road, Temple Cowley, Oxford, Oxfordshire OX4 2EB<br>";$scope.sms={selected:!1,enabled:$ClientsManager.data.mobile?!0:!1,send_to:$ClientsManager.data.mobile,sending:!1};$scope.sms.text="Hi "+$ClientsManager.data.name+", your repair "+taskId+" is ready to be collected. Computer Assistance.";$scope.notification=notification;$scope.submit=function(){if($scope.email.selected=='1')
$scope.sendEmail();if($scope.sms.selected=='1')
$scope.sendSMS();};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')};$scope.print=function(value){console.log(value)};$scope.changeSMSSelection=function(){$scope.sms.selected=!$scope.sms.selected};$scope.sendSMS=function(){if($scope.sms.sending)return!1;$scope.sms.sending=!0;var params={send_to:$scope.sms.send_to,message:$scope.sms.text,id:taskId};Tasks.sendSMS(params,function(response){$scope.sms.sending=!1;CustomerNotificationsService.notifications.push(response.notification);Messenger("Your text has been successfully sent to the client and should be delivered shortly");if(!$scope.email.sending)
$uibModalInstance.close();},function(a,b){ErrorsHandler(a,b);$scope.sms.sending=!1;if(!$scope.email.sending)
$uibModalInstance.close();})};$scope.sendEmail=function(){if($scope.email.sending)return!1;$scope.email.sending=!0;var params={send_to:$scope.email.send_to,message:$scope.email.text,subject:$scope.email.subject,id:taskId};Tasks.sendEmail(params,function(response){$scope.email.sending=!1;CustomerNotificationsService.notifications.push(response.notification);Messenger("Your email has been successfully sent to the client and should be delivered shortly");if(!$scope.sms.sending)
$uibModalInstance.close();},function(a,b){ErrorsHandler(a,b);$scope.email.sending=!1;if(!$scope.sms.sending)
$uibModalInstance.close();})}}]);Bookings.controller('CustomerPrebooksCtrl',['$scope','$ClientsManager','Tasks',function($scope,$ClientsManager,Tasks){$scope.prebooks=[];$scope.clientManager=$ClientsManager;$scope.$watch('clientManager.client',function(){if($ClientsManager.isSelected()){$scope.loadPrebooks($ClientsManager.getClient().id,$ClientsManager.getClientType())}},!0);$scope.loadPrebooks=function(client_id,client_type){if($scope.loading_prebooks)return!1;if(client_id&&client_type){$scope.client_id=client_id;$scope.client_type=client_type}
$scope.loading_prebooks=!0;Tasks.get({client_id:$scope.client_id,client_type:$scope.client_type,completed:0,prebook:1},function(response){$scope.prebooks=response.data;$scope.loading_prebooks=!1})}}]);Bookings.controller('DeviceModalCtrl',['$scope','$uibModalInstance','data','Customer','Device','Manufacturer','ModelNumber','ErrorsHandler',function($scope,$uibModalInstance,data,Customer,Device,Manufacturer,ModelNumber,ErrorsHandler){$scope.device=angular.copy(data)||{model_number:{manufacturer:{},}};$scope.Customer=Customer||{};if(!$scope.device.model_number)
$scope.device.model_number={manufacturer:{}};$scope.helper={model_number:angular.copy($scope.device.model_number),manufacturer:angular.copy($scope.device.model_number.manufacturer)};if($scope.device.antivirus_expiration_date)
$scope.helper.antivirus_expiration_date=new Date($scope.device.antivirus_expiration_date).toISOString();else $scope.helper.antivirus_expiration_date=null;var initialised=!1;$scope.$watch('helper.manufacturer',function(value,oldValue){if(initialised&&value!=oldValue){$scope.helper.model_number={}}else{initialised=!0}},!0);$scope.$watch('helper.model_number',function(value,oldValue){if(initialised&&value!=oldValue){if($scope.helper.model_number.id){$scope.device.model_number_id=value.id;$scope.device.model_number=$scope.helper.model_number}else $scope.device.model_number={id:null,model_number:$scope.helper.model_number.model_number,manufacturer_id:$scope.helper.manufacturer.id,manufacturer:{id:$scope.helper.manufacturer.id,name:$scope.helper.manufacturer.name}}}else{initialised=!0}},!0);$scope.$watch('helper.antivirus_expiration_date',function(value){if(value){$scope.device.antivirus_expiration_date=moment(value).format('YYYY-MM-DD')}},!0);$scope.manufacturerSearchSettings={allowClear:!1,query:function(options){Manufacturer.get({name:options.term,page:options.page},function(response){options.callback({more:response.current_page<response.last_page,results:response.data})})},formatResult:function(obj,container,query){return obj.name},formatSelection:function(obj){return obj.name}};$scope.modelNumberSearchSettings={allowClear:!1,query:function(options){var manufacturer_id=($scope.helper.manufacturer.id)?$scope.helper.manufacturer.id:0;ModelNumber.get({name:options.term,page:options.page,manufacturer_id:manufacturer_id},function(response){options.callback({more:response.current_page<response.last_page,results:response.data})})},formatResult:function(obj,container,query){return obj.model_number},formatSelection:function(obj){return obj.model_number}};$scope.save=function(){if(!$scope.loading){$scope.loading=!0;var method=($scope.device.id)?Device.update:Device.save;if(!$scope.device.id){$scope.device.client_id=Customer.id;$scope.device.client_type=Customer.type}
method($scope.device,function(response){$uibModalInstance.close(response)},function(a,b){$scope.loading=!1;ErrorsHandler(a,b)})}};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Bookings.controller("EmailCustomerModalCtrl",["$scope","$uibModalInstance","data","customerEmail","$http","apiUrl","ErrorsHandler","Messenger",function($scope,$uibModalInstance,data,customerEmail,$http,apiUrl,ErrorsHandler,Messenger){var subject='['+data.id+'] '+(data.title||'Task');$scope.data={task_id:data.id,ticket_number:data.id,send_to:customerEmail,subject:subject,message:"Dear "+(data.client_name||"Customer")+",\n\n"+"Thank you for your enquiry.\n\n"+"We will get back to you as soon as possible.\n\n"+"If you have any further questions, please do not hesitate to contact us.\n\n"+"Best regards,\n"+"Computer Assistance\n"+"01865 451177"};$scope.sending=!1;$scope.send=function(){$scope.sending=!0;if(typeof Messenger!=='undefined'){Messenger("Sending email...")}
$http.post(apiUrl("bookings/email-customer"),$scope.data,{headers:{'X-CSRF-TOKEN':document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')||''}}).success(function(response){$scope.sending=!1;if(typeof Messenger!=='undefined'){Messenger("Email sent successfully!")}
$uibModalInstance.close('success')}).error(function(error,status){$scope.sending=!1;var errorMessage=error&&error.error?error.error:'Failed to send email';if(typeof Messenger!=='undefined'){Messenger("Email failed: "+errorMessage)}
ErrorsHandler(error,status)})};$scope.cancel=function(){$uibModalInstance.dismiss("cancel")}}]);Bookings.controller('HistoryCtrl',['$scope','$uibModalInstance','title','historyEntries','users',function($scope,$uibModalInstance,title,historyEntries,users){$scope.users=users;$scope.title=title;$scope.historyEntries=historyEntries;$scope.cancel=function(){$uibModalInstance.close(!1)}}]);Bookings.controller('LinkTaskCtrl',['$scope','$uibModalInstance','related_tasks','Tasks','currentTask','$timeout','ErrorsHandler',function($scope,$uibModalInstance,related_tasks,Tasks,currentTask,$timeout,ErrorsHandler){var search_timeout;$scope.search={query:''};$scope.data=currentTask.related_tasks;$scope.$watch('search.query',function(value){if(search_timeout)$timeout.cancel(search_timeout);search_timeout=$timeout(function(){$scope.load()},300)});$scope.load=function(){$scope.loading=!0;Tasks.get({search:$scope.search.query},function(response){$scope.tasks=_.reject(response.data,function(el){return el.id==currentTask.id});$scope.loading=!1},ErrorsHandler)};$scope.select=function(item){if($scope.exists(item))return!1;$scope.data.push(item)};$scope.remove=function(item){$scope.data=_.reject($scope.data,function(el){return el.id==item.id})};$scope.exists=function(item){var task=_.find($scope.data,function(el){return el.id==item.id});return(task)?!0:!1};$scope.link=function(){Tasks.update({related_tasks:$scope.data,id:currentTask.id},function(response){$uibModalInstance.close(response)},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Bookings.controller('NoPreviewCtrl',['$scope','$uibModalInstance',function($scope,$uibModalInstance){$scope.data={};$scope.ok=function(){$uibModalInstance.close(!0)};$scope.cancel=function(){$uibModalInstance.close(!1)}}]);Bookings.controller('PagesMessageCtrl',['$scope','$ClientsManager','Message','ErrorsHandler','$location','Messenger','Page',function($scope,$ClientsManager,Message,ErrorsHandler,$location,Messenger,Page){$scope.data={};$scope.clientManager=$ClientsManager;$scope.total=0;$scope.$watch('messageId',function(value){if(value){Page.setTitle('Message: '+$scope.messageId);$scope.loadData()}});$scope.loadData=function(){Message.get({messageId:$scope.messageId},function(res){$scope.data=res;$ClientsManager.setClient(res.client);$ClientsManager.setClientType(res.client_type)},ErrorsHandler)};$scope.update=function(ev){Message.update($scope.data,function(res){$scope.data=res;$scope.labour_changed=!1;Messenger("I've succesfully saved the message!")},ErrorsHandler)};$scope.assignTo=function(id){Message.update({id:$scope.data.id,assigned_to:id},function(res){$scope.data.assigned_to=id;$scope.data.user=res.user},ErrorsHandler)};$scope.closed=function(status){Message.update({id:$scope.data.id,closed:status},function(res){$scope.data=res;Messenger("I've succesfully saved the message!")},ErrorsHandler)}}]);Bookings.controller('PagesOnsiteCtrl',['$scope','$ClientsManager','Onsite','ErrorsHandler','$location','Messenger','PartsService','$uibModal','$rootScope','InvoiceBookingService','SalesBookingService','Page',function($scope,$ClientsManager,Onsite,ErrorsHandler,$location,Messenger,PartsService,$uibModal,$rootScope,InvoiceBookingService,SalesBookingService,Page){$scope.data={};$scope.InvoiceService=InvoiceBookingService;$scope.SalesService=SalesBookingService;$scope.statuses=[];$scope.clientManager=$ClientsManager;$scope.total=0;$scope.PartsService=PartsService;$scope.$watch('data.labour',function(){$scope.total=(parseFloat($scope.data.labour)+parseFloat(PartsService.getTotal())).toFixed(2)});$scope.$watch('PartsService.total',function(){$scope.total=(parseFloat($scope.data.labour)+parseFloat(PartsService.getTotal())).toFixed(2)});$scope.$watch('taskId',function(value){if(value){Page.setTitle($scope.taskId);$scope.loadData()}});$scope.loadData=function(){Onsite.get({taskId:$scope.taskId},function(res){angular.extend($scope.data,res||{});PartsService.setParts(res.parts);$ClientsManager.setClient(res.client);$ClientsManager.setClientType(res.client_type);$rootScope.$broadcast('task-updated',res)},ErrorsHandler)};$scope.update=function(ev){Onsite.update($scope.data,function(res){$scope.data=res;$scope.labour_changed=!1;$scope.invoice_number_changed=!1;Messenger("I've succesfully saved the remote support job!");$rootScope.$broadcast('task-updated',res)},ErrorsHandler)};$scope.assignTo=function(id){Onsite.update({id:$scope.data.id,assigned_to:id},function(res){$scope.data.assigned_to=id;$scope.data.user=res.user;$rootScope.$broadcast('task-updated',res)},ErrorsHandler)};$scope.getStatus=function(){return _.find($scope.statuses,function(el){return el.id==$scope.data.status_id})};$scope.getStatusName=function(){var status=$scope.getStatus();return(status)?status.name:''};$scope.updateStatus=function(id){var status=_.find($scope.statuses,function(el){return el.id==id});if(status&&status.completed=='1'&&$scope.data.labour=='0'&&!$scope.data.no_price_reason){var modalInstance=$uibModal.open({templateUrl:'modals/no-price-confirmation.html',controller:'TaskNoPriceCtrl',});modalInstance.result.then(function(reason){Onsite.update({id:$scope.data.id,status_id:id,no_price_reason:reason},function(res){$scope.data.status_id=id;$scope.data.no_price_reason=reason;$rootScope.$broadcast('task-updated',res)},ErrorsHandler)})}else{Onsite.update({id:$scope.data.id,status_id:id},function(res){$scope.data.status_id=id;$rootScope.$broadcast('task-updated',res)},ErrorsHandler)}};$scope.paid=function(){Onsite.update({id:$scope.data.id,paid:1},function(response){$scope.data=response;$rootScope.$broadcast('task-updated',res)},ErrorsHandler)};$scope.changeDates=function(){var modalInstance=$uibModal.open({templateUrl:'modals/change-dates.html',controller:'ChangeDatesCtrl',resolve:{data:function(){return $scope.data}}})}}]);Bookings.controller('PagesOrdersCtrl',['$scope','$ClientsManager','Order','ErrorsHandler','$location','Messenger','Page','PartsService','InvoiceBookingService',function($scope,$ClientsManager,Order,ErrorsHandler,$location,Messenger,Page,PartsService,InvoiceService){$scope.data={};$scope.statuses=[];$scope.clientManager=$ClientsManager;$scope.total=0;$scope.PartsService=PartsService;$scope.InvoiceService=InvoiceService;$scope.$watch('taskId',function(value){if(value){Page.setTitle($scope.taskId);$scope.loadData()}});$scope.loadData=function(){Order.get({taskId:$scope.taskId},function(res){$scope.data=res;$ClientsManager.setClient(res.client);$ClientsManager.setClientType(res.client_type)},ErrorsHandler)};$scope.update=function(ev){Order.update($scope.data,function(res){$scope.data=res;$scope.labour_changed=!1;Messenger("I've succesfully saved the remote support job!")},ErrorsHandler)};$scope.getStatus=function(){return _.find($scope.statuses,function(el){return el.id==$scope.data.status_id})};$scope.getStatusName=function(){var status=$scope.getStatus();return(status)?status.name:''};$scope.updateStatus=function(id){if(id==29&&$scope.data.completed)
return;if(id==30&&!$scope.data.completed)
return;if(id==$scope.data.status_id)
return;Order.update({id:$scope.data.id,status_id:id},function(res){$scope.data.status_id=id},ErrorsHandler)};$scope.generateInvoice=function(data){if(PartsService.hasPartsOnOrder())
alert("It is not possible to create an invoice for parts on order.");else InvoiceService.generate(data)};$scope.getPaymentDisplay=function(value){return(value=='1')?'paid':'not paid'};$scope.getStatusIcon=function(){if($scope.data.status_id==29)
return'times';if($scope.data.status_id==2)
return'clock-o';if($scope.data.status_id==16)
return'truck';if($scope.data.status_id==30)
return'cube'}}]);Bookings.controller('PagesRepairsCtrl',['$scope','$ClientsManager','Repair','ErrorsHandler','$location','Messenger','PartsService','Page','$uibModal','$rootScope','InvoiceBookingService','SalesBookingService',function($scope,$ClientsManager,Repair,ErrorsHandler,$location,Messenger,PartsService,Page,$uibModal,$rootScope,InvoiceBookingService,SalesBookingService){$scope.data={};$scope.InvoiceService=InvoiceBookingService;$scope.SalesService=SalesBookingService;$scope.statuses=[];$scope.clientManager=$ClientsManager;$scope.total=0;$scope.PartsService=PartsService;$scope.$watch('data.labour',function(){$scope.total=(parseFloat($scope.data.labour)+parseFloat(PartsService.getTotal())).toFixed(2)});$scope.$watch('PartsService.total',function(){$scope.total=(parseFloat($scope.data.labour)+parseFloat(PartsService.getTotal())).toFixed(2)});$scope.$watch('return',function(value){if(value=='0'){$scope.data.return_id=null}});$scope.$watch('taskId',function(value){if(value){Page.setTitle($scope.taskId);$scope.loadData()}});$scope.loadData=function(){Repair.get({taskId:$scope.taskId},function(res){$scope.data=res.data;$scope.return=(res.data.return_id)?1:0;PartsService.setParts(res.data.parts);$ClientsManager.setClient(res.data.client);$ClientsManager.setClientType(res.data.client_type);$ClientsManager.unify(res.data.site,res.data.site_user);$rootScope.$broadcast('task-updated',res.data)},ErrorsHandler)};$scope.update=function(ev){Repair.update($scope.data,function(res){angular.extend($scope.data,res||{});$scope.return=(res.return_id)?1:0;$scope.labour_changed=!1;$scope.booking_fee_changed=!1;$scope.invoice_number_changed=!1;Messenger("I've succesfully saved the repair!");$rootScope.$broadcast('task-updated',res)},ErrorsHandler)};$scope.assignTo=function(id){Repair.update({id:$scope.data.id,assigned_to:id},function(res){$scope.data.assigned_to=id;$scope.data.user=res.user;$rootScope.$broadcast('task-updated',res)},ErrorsHandler)};$scope.getStatus=function(){return _.find($scope.statuses,function(el){return el.id==$scope.data.status_id})};$scope.getStatusName=function(){var status=$scope.getStatus();return(status)?status.name:''};$scope.updateStatus=function(id){var status=_.find($scope.statuses,function(el){return el.id==id});if(status&&status.completed=='1'&&$scope.data.labour=='0'&&!$scope.data.no_price_reason){var modalInstance=$uibModal.open({templateUrl:'modals/no-price-confirmation.html',controller:'TaskNoPriceCtrl',});modalInstance.result.then(function(reason){Repair.update({id:$scope.data.id,status_id:id,no_price_reason:reason},function(res){$scope.data.status_id=id;$scope.data.no_price_reason=reason;$rootScope.$broadcast('task-updated',res)},ErrorsHandler)})}else{Repair.update({id:$scope.data.id,status_id:id},function(res){$scope.data.status_id=id;$rootScope.$broadcast('task-updated',res)},ErrorsHandler)}};$scope.getDeliveryUserName=function(){if(!$scope.data.delivered_by)
return'No one';var user=_.find($scope.users,function(el){return el.id==$scope.data.delivered_by});return(user)?user.first_name+''+user.last_name:''};$scope.deliverBy=function(id){Repair.update({id:$scope.data.id,delivered_by:id},function(res){$scope.data.delivered_by=id;$rootScope.$broadcast('task-updated',res)},ErrorsHandler)};$scope.paid=function(){Repair.update({id:$scope.data.id,paid:1},function(response){$scope.data=response;$rootScope.$broadcast('task-updated',res)},ErrorsHandler)}}]);Bookings.controller('PagesRmaCtrl',['$scope','$ClientsManager','Rma','ErrorsHandler','$location','Messenger','RmaItem','Page',function($scope,$ClientsManager,Rma,ErrorsHandler,$location,Messenger,RmaItem,Page){$scope.data={};$scope.statuses=[];$scope.clientManager=$ClientsManager;$scope.total=0;$scope.$watch('data.labour',function(){$scope.total=parseFloat($scope.data.labour)});$scope.$watch('taskId',function(value){if(value){Page.setTitle($scope.taskId);$scope.loadData()}});$scope.loadData=function(){Rma.get({taskId:$scope.taskId},function(res){$scope.data=res;$ClientsManager.setClient(res.client);$ClientsManager.setClientType(res.client_type)},ErrorsHandler)};$scope.update=function(ev){Rma.update($scope.data,function(res){$scope.data=res;$scope.labour_changed=!1;Messenger("I've succesfully saved the remote support job!")},ErrorsHandler)};$scope.assignTo=function(id){Rma.update({id:$scope.data.id,assigned_to:id},function(res){$scope.data.assigned_to=id;$scope.data.user=res.user},ErrorsHandler)};$scope.getStatus=function(){return _.find($scope.statuses,function(el){return el.id==$scope.data.status_id})};$scope.getStatusName=function(){var status=$scope.getStatus();return(status)?status.name:''};$scope.updateStatus=function(id){Rma.update({id:$scope.data.id,status_id:id},function(res){$scope.data.status_id=id},ErrorsHandler)};$scope.addItem=function(item){RmaItem.save({task_id:$scope.taskId,stock_id:item.id,quantity:1},function(response){$scope.data.items.push(response)},ErrorsHandler)};$scope.removeItem=function(item){RmaItem.remove({taskId:item.task_id,itemId:item.id},function(response){$scope.data.items=_.reject($scope.data.items,function(el){return el.id==item.id})},ErrorsHandler)};$scope.updateItem=function(item){RmaItem.update(item,function(response){var itm=_.find($scope.data.items,function(el){return el.id==item.id});if(itm)
angular.extend(itm,response);},ErrorsHandler)}}]);Bookings.controller('PagesSaleLeadCtrl',['$scope','$rootScope','$ClientsManager','SaleLead','Tasks','ErrorsHandler','$location','Messenger','$AuthUser','$uibModal',function($scope,$rootScope,$ClientsManager,SaleLead,Tasks,ErrorsHandler,$location,Messenger,$AuthUser,$uibModal){$scope.data={};$scope.max_stars=5;$scope.statuses=[];$scope.ClientsManager=$ClientsManager;$scope.$watch('taskId',function(value){if(value)$scope.loadData();});$scope.loadData=function(){SaleLead.get({saleLeadId:$scope.taskId},function(res){$scope.data=res;$scope.data.onsite_end=moment(res.onsite_end).add('hours',2).toDate();$ClientsManager.setClient(res.client);$ClientsManager.setClientType(res.client_type);$ClientsManager.unify(res.site,res.site_user)},ErrorsHandler)};$scope.hoveringOver=function(value){$scope.overStar=value;$scope.percentage=100*(value/$scope.max_stars)};$scope.showSubscribers=function(){var modalInstance=$uibModal.open({templateUrl:'modals/subscribers.html',controller:'SubscribersCtrl',resolve:{task:function(){return $scope.data},users:function(){return $scope.users}}})};$scope.unassign=function(){SaleLead.update({id:$scope.data.id,assigned_to:null,updated_at:$scope.data.updated_at},function(response){angular.extend($scope.data,response||{});$scope.data.onsite_end=moment(response.onsite_end).add('hours',2).toDate();$rootScope.$broadcast('task-updated',response)},ErrorsHandler)};$scope.subscribe=function(){$scope.data.subscribers.push($AuthUser.data);Tasks.update({id:$scope.data.id,subscribers:$scope.data.subscribers},function(response){angular.extend($scope.data,response||{});$scope.data.onsite_end=moment(response.onsite_end).add('hours',2).toDate();$rootScope.$broadcast('task-updated',response);Messenger("You have succesfully subscribed to this task.")},ErrorsHandler)};$scope.unsubscribe=function(){$scope.data.subscribers=_.reject($scope.data.subscribers,function(el){return el.id==$AuthUser.data.id});Tasks.update({id:$scope.data.id,subscribers:$scope.data.subscribers},function(response){angular.extend($scope.data,response||{});$scope.data.onsite_end=moment(response.onsite_end).add('hours',2).toDate();$rootScope.$broadcast('task-updated',response);Messenger("You have succesfully un-subscribed from this task.")},ErrorsHandler)};$scope.save=function(ev){var data=angular.copy($scope.data);data.onsite_end=moment($scope.data.onsite_end).format("YYYY-MM-DD");SaleLead.update(data,function(res){$scope.data=res;$scope.data.onsite_end=moment(res.onsite_end).add('hours',2).toDate();Messenger("I've succesfully saved the data!")},ErrorsHandler)};$scope.updateValue=function(params){angular.extend(params,{id:$scope.data.id,updated_at:$scope.data.updated_at});SaleLead.update(params,function(response){angular.extend($scope.data,response||{});$scope.data.onsite_end=moment(response.onsite_end).add('hours',2).toDate();$rootScope.$broadcast('task-updated',response);Messenger("I've succesfully saved the job!")},ErrorsHandler)}}]);Bookings.controller('PagesSupportCtrl',['$scope','$ClientsManager','Support','ErrorsHandler','$location','Messenger','PartsService','$uibModal','$rootScope','InvoiceBookingService','SalesBookingService','Page',function($scope,$ClientsManager,Support,ErrorsHandler,$location,Messenger,PartsService,$uibModal,$rootScope,InvoiceBookingService,SalesBookingService,Page){$scope.data={};$scope.InvoiceService=InvoiceBookingService;$scope.SalesService=SalesBookingService;$scope.statuses=[];$scope.clientManager=$ClientsManager;$scope.total=0;$scope.PartsService=PartsService;$scope.$watch('data.labour',function(){$scope.total=(parseFloat($scope.data.labour)+parseFloat(PartsService.getTotal())).toFixed(2)});$scope.$watch('PartsService.total',function(){$scope.total=(parseFloat($scope.data.labour)+parseFloat(PartsService.getTotal())).toFixed(2)});$scope.$watch('taskId',function(value){if(value){Page.setTitle($scope.taskId);$scope.loadData()}});$scope.loadData=function(){Support.get({taskId:$scope.taskId},function(res){angular.extend($scope.data,res.data||{});PartsService.setParts(res.data.parts);$ClientsManager.setClient(res.data.client);$ClientsManager.setClientType(res.data.client_type)},ErrorsHandler)};$scope.update=function(ev){Support.update($scope.data,function(res){$scope.data=res;$scope.labour_changed=!1;$scope.time_spent_changed=!1;$scope.invoice_number_changed=!1;Messenger("I've succesfully saved the remote support job!");$rootScope.$broadcast('task-updated',res)},ErrorsHandler)};$scope.assignTo=function(id){Support.update({id:$scope.data.id,assigned_to:id},function(res){$scope.data.assigned_to=id;$scope.data.user=res.user;$rootScope.$broadcast('task-updated',res)},ErrorsHandler)};$scope.getStatus=function(){return _.find($scope.statuses,function(el){return el.id==$scope.data.status_id})};$scope.getStatusName=function(){var status=$scope.getStatus();return(status)?status.name:''};$scope.updateStatus=function(id){var status=_.find($scope.statuses,function(el){return el.id==id});if(status&&status.completed=='1'&&$scope.data.labour=='0'&&!$scope.data.no_price_reason){var modalInstance=$uibModal.open({templateUrl:'modals/no-price-confirmation.html',controller:'TaskNoPriceCtrl',});modalInstance.result.then(function(reason){Support.update({id:$scope.data.id,status_id:id,no_price_reason:reason},function(res){$scope.data.status_id=id;$scope.data.no_price_reason=reason;$rootScope.$broadcast('task-updated',res)},ErrorsHandler)})}else{Support.update({id:$scope.data.id,status_id:id},function(res){$scope.data.status_id=id;$rootScope.$broadcast('task-updated',res)},ErrorsHandler)}};$scope.paid=function(){Support.update({id:$scope.data.id,paid:1},function(response){$scope.data=response},ErrorsHandler)}}]);Bookings.controller('PartCompositeItemsCtrl',['$scope','part','$uibModalInstance','Part','ErrorsHandler',function($scope,part,$uibModalInstance,Part,ErrorsHandler){$scope.part=angular.copy(part);$scope.addPart=function(ev,data){var existingItem=_.findWhere($scope.part.composite_items,{id:data.id});if(existingItem){existingItem.pivot.quantity++;return}
data.pivot={quantity:1};$scope.part.composite_items.push(data)};$scope.save=function(){Part.update($scope.part,function(response){$scope.loading=!1;$uibModalInstance.close(response)},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Bookings.controller('ProjectTasksCtrl',['$scope','Project','ErrorsHandler',function($scope,Project,ErrorsHandler){$scope.project=null;$scope.$watch('projectId',function(value){if(value)$scope.load();});$scope.load=function(){Project.get({projectId:$scope.projectId},function(response){$scope.project=response},ErrorsHandler)}}]);Bookings.controller('ProjectsFilesCtrl',['$scope','Files','ErrorsHandler',function($scope,Files,ErrorsHandler){$scope.$watch('projectId',function(value){if(value){$scope.load()}});$scope.load=function(){Files.get({objectId:$scope.projectId,objectType:'project'},function(response){$scope.files=response.data},ErrorsHandler)}}]);Bookings.controller('ProjectsMainCtrl',['$scope','$ClientsManager','Page','Project','ErrorsHandler','$location','Messenger','$routeParams','Files','ActivityLog','$filter','InvoiceBookingService',function($scope,$ClientsManager,Page,Project,ErrorsHandler,$location,Messenger,$routeParams,Files,ActivityLog,$filter,InvoiceBookingService){$scope.files=[];$scope.ClientsManager=$ClientsManager;$scope.InvoiceService=InvoiceBookingService;$scope.boolean_options=[{name:'No',value:0},{name:'Yes',value:1}];if($location.path().substring(0,9)!='/projects'){$location.path('projects/overview')}
$scope.$watch('projectId',function(value){$routeParams.projectId=value;if(value){Page.setTitle('P '+$scope.projectId);$scope.load()}});$scope.getProgress=function(){if(!$scope.data)return 0;var completed=0;_.each($scope.data.tasks,function(el){if(el.completed=='1')completed++});var percentage=completed/$scope.data.tasks.length*100;if(isNaN(percentage))return 0;return percentage.toFixed(2)};$scope.load=function(){Project.get({projectId:$scope.projectId},function(response){$scope.data=response;$ClientsManager.setClient(response.client);$ClientsManager.setClientType(response.client_type);$ClientsManager.unify(response.site,response.site_user)},ErrorsHandler);Files.get({objectId:$scope.projectId,objectType:'project'},function(response){$scope.files=response.data},ErrorsHandler)};$scope.update=function(){Project.update($scope.data,function(response){angular.extend($scope.data,response);Messenger("Changes have been successfully saved!")},ErrorsHandler)};$scope.updateProjectValue=function(name,value){var d={id:$scope.data.id};d[name]=value;Project.update(d,function(response){angular.extend($scope.data,response)},ErrorsHandler)};$scope.showProjectLeader=function(){if(!$scope.data)return'Not set';var selected=_.find($scope.users,function(el){return el.id==$scope.data.assigned_to});return($scope.data.assigned_to&&selected)?selected.name:'Not set'};$scope.cancelProject=function(){var d={projectId:$scope.data.id};Project.delete(d,function(response){Messenger('I have succesfully deleted the project!');window.setTimeout(function(){window.location.href='/'},2000)},ErrorsHandler)}}]);Bookings.controller('ProjectsOverviewCtrl',['$scope','ActivityLog','ErrorsHandler',function($scope,ActivityLog,ErrorsHandler){$scope.logs_page=1;$scope.loadMoreLogs=function(){$scope.disable_logs_scroll=!0;ActivityLog.get({objectId:$scope.projectId,objectType:'project',page:++$scope.logs_page},function(response){if(response.data.length>0){$scope.activity_logs=$scope.activity_logs.concat(response.data);$scope.disable_logs_scroll=!1}
$scope.logs_page=response.current_page},ErrorsHandler)}}]);Bookings.controller('ProjectsStatisticsCtrl',['$scope','Files','ErrorsHandler',function($scope,Files,ErrorsHandler){$scope.duration_piechart={type:'PieChart',displayed:!0,options:{title:'Project duration breakdown',pieHole:0.3,legend:{position:'labeled'}},data:{cols:[{id:'action',label:'Action',type:'string'},{id:'count',label:'Count',type:'number'}],rows:[{"c":[{v:"Planning"},{v:5,f:"5 days"}]},{"c":[{v:"In progress"},{v:10,f:"10 days"}]},{"c":[{v:"Send invoice"},{v:2,f:"2 days"}]},{"c":[{v:"Paid"},{v:14,f:"14 days"}]},{"c":[{v:"Feedback"},{v:1,f:"1 days"}]}]}};$scope.expenses_chart={type:'BarChart',displayed:!0,options:{title:'Project costs breakdown',isStacked:!1},data:{cols:[{id:'action',label:'Action',type:'string'},{id:'count',label:'Costs',type:'number'},{id:'budget',label:'Budget',type:'number'},],rows:[{"c":[{v:"Planning"},{v:5,f:"£5"},{v:10}]},{"c":[{v:"In progress"},{v:10,f:"£10"},{v:15}]},{"c":[{v:"Send invoice"},{v:2,f:"£2"}]}]}};$scope.tasks_chart={type:'Timeline',displayed:!0,options:{title:'Tasks timeline',height:300},data:{cols:[{id:'task',label:'Task',type:'string'},{id:'start',label:'Date',type:'date'},{id:'end',label:'End',type:'date'},],rows:[]}};$scope.$watch('data.tasks',function(value){if(value){var data=[];_.each(value,function(el){data.push({c:[{v:el.title},{v:moment(el.created_at).toDate()},{v:moment(el.updated_at).toDate()}]})});$scope.tasks_chart.data.rows=data}},!0)}]);Bookings.controller('ProjectsTasksCtrl',['$scope','$http','apiUrl','ErrorsHandler','$ClientsManager',function($scope,$http,apiUrl,ErrorsHandler,$ClientsManager){$scope.navType='pills';$scope.taskAdded=function(response){$scope.data.tasks.push(response)};$scope.remove=function(task){$http.post(apiUrl('tasks/helpers/remove-task-from-project'),{id:task.id,project_id:$scope.data.id}).success(function(response){$scope.data.tasks=_.reject($scope.data.tasks,function(el){return el.id==task.id})}).error(ErrorsHandler)}}]);Bookings.controller('PromoteProjectCtrl',['$scope','$uibModalInstance','$window','Tasks','ErrorsHandler','$ClientsManager',function($scope,$uibModalInstance,$window,Tasks,ErrorsHandler,ClientsManager){$scope.data={};$scope.save=function(){if(!$scope.data.description){$scope.projectForm.$attempted=!0;$scope.projectForm.description.$invalid=!0;return!1}
$scope.data.id=$scope.$parent.$$childHead.$$watchers[0].last;$scope.data.client_id=ClientsManager.client.id;$scope.data.client_type=ClientsManager.getClientType();$scope.redirect_url='/projects';Tasks.promote($scope.data,function(response){$window.location.href=$scope.redirect_url+"/"+response.project_id},ErrorsHandler)};$scope.close=function(){$uibModalInstance.dismiss()}}]);Bookings.controller('QuickNewTaskCtrl',['$scope','User','Tasks','ErrorsHandler','$q','Client','Customer','$AuthUser','TaskTypes',function($scope,User,Tasks,ErrorsHandler,$q,Client,Customer,$AuthUser,TaskTypes){$scope.tokens=[];var dates=[{name:'Today',value:moment()},{name:'Tommorrow',value:moment().add('days',1)},{name:moment().add('days',2).format('dddd - DD/MM/YYYY'),value:moment().add('days',2)},{name:moment().add('days',3).format('dddd - DD/MM/YYYY'),value:moment().add('days',3)},{name:moment().add('days',4).format('dddd - DD/MM/YYYY'),value:moment().add('days',4)},{name:moment().add('days',5).format('dddd - DD/MM/YYYY'),value:moment().add('days',5)},{name:moment().add('days',6).format('dddd - DD/MM/YYYY'),value:moment().add('days',6)},{name:moment().add('days',7).format('dddd - DD/MM/YYYY'),value:moment().add('days',7)}],times=[];for(var i=0;i<24;i++){for(var j=0;j<60;j+=15){var time=(i<10)?'0'+i:i;time+=':';time+=(j<10)?'0'+j:j;times.push(time)}}
$scope.tags={task:TaskTypes.getType('support')};$scope.learn={title:'Learn more',container:'body',content:'Use the following symbols as options when making a booking: <br /> '+'<strong>@</strong> - search and assign the task to a user <br />'+'<strong>#</strong> - search and assign the task to a customer <br />'+'<strong>^</strong> - select the type of task (ie. repair, remote support) <br />'+'<br /> <strong>The following are only required for onsite</strong> <br />'+'<strong>$</strong> - select a date <br />'+'<strong>!</strong> - select a time <br />'+'<br /> <strong>Other useful tips & tricks</strong> <br />'+'To separate the description from the title, simply leave two new lines (ie. press ENTER twice). The top bit will be the title and the bottom bit will be the description. <br /><br /> Easy, right?'};$scope.searchUsers=function(term){return User.get({q:term,limit:5},function(response){$scope.users=response.data;return $q.when(response.data)},ErrorsHandler)};$scope.tokenize=function(token,value){var token=token+value;$scope.tokens.push(token);return token};$scope.getUserRaw=function(item){$scope.tags.staff=item;return $scope.tokenize('@',item.name)};$scope.searchCustomers=function(term){var clients=Client.get({name:term,contract:!0,limit:5}).$promise;var customers=Customer.get({name:term,limit:5}).$promise;return $q.all([clients,customers]).then(function(all){$scope.customers=all[0].data.concat(all[1].data);return $q.when(all)})};$scope.getCustomerRaw=function(item){$scope.tags.client=item;return $scope.tokenize('#',item.name)};$scope.searchTaskTypes=function(term){$scope.taskTypes=[];_.each(TaskTypes.getTypes(),function(el){if(el.name.toLowerCase().indexOf(term.toLowerCase())>=0){$scope.taskTypes.push(el)}})};$scope.getTaskTypeRaw=function(item){$scope.tags.task=item;return $scope.tokenize('^',item.name)};$scope.searchTaskTimes=function(term){$scope.times=[];var limit=5,found=0;_.each(times,function(el){if(found<limit&&el.toLowerCase().indexOf(term.toLowerCase())>=0){$scope.times.push(el);found++}})};$scope.getTaskTimesRaw=function(item){$scope.tags.time=item;return $scope.tokenize('!',item)};$scope.searchTaskDates=function(term){$scope.dates=[];_.each(dates,function(el){if(el.name.toLowerCase().indexOf(term.toLowerCase())>=0){$scope.dates.push(el)}})};$scope.getTaskDatesRaw=function(item){$scope.tags.date=item;return $scope.tokenize('$',item.name)};$scope.save=function(){var data={};if(!$scope.tags.client){alert("You need to select a customer before continuing! Use # to start the search...");return!1}
if($scope.tags.task&&$scope.tags.task.id=='onsite'){if(!$scope.tags.date){alert("You need to select a date for an onsite job. Use $ to start searching.");return!1}
if(!$scope.tags.time){alert("You need to select a start time. Use ! to start searching.");return!1}
data.onsite_start=$scope.tags.date.value.format("YYYY-MM-DD")+' '+$scope.tags.time+':00';data.onsite_end=moment(data.onsite_start).add('hours',1).format("YYYY-MM-DD HH:mm:ss")}
var originalText=$scope.newTaskTextarea;_.each($scope.tokens,function(el){originalText=originalText.replace(el,'')});var splitText=originalText.split("\n\n"),title=splitText[0].trim()
description=(splitText.length>1)?(splitText.shift()&&splitText.join("\n\n").trim()):splitText[0].trim();angular.extend(data,{client_id:$scope.tags.client.id,client_type:$scope.tags.client.type,type:$scope.tags.task?$scope.tags.task.id:TaskTypes.getType('support').id,title:title,problem:description.replace(/\r?\n/g,"<br />"),assigned_to:$scope.tags.staff?$scope.tags.staff.id:null});Tasks.save(data,function(response){$scope.task=response;$scope.newTaskTextarea='';$scope.tags={task:TaskTypes.getType('support')}},ErrorsHandler)}}]);Bookings.controller('ReasonModalCtrl',['$scope','$uibModalInstance',function($scope,$uibModalInstance){$scope.data={};$scope.submit=function(){$uibModalInstance.close($scope.data.reason)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Bookings.controller('RequestExistingPartCtrl',['$scope','Part','Stock','ErrorsHandler','PartsService','$location','apiUrl','$uibModal',function($scope,Part,Stock,ErrorsHandler,PartsService,$location,apiUrl,$uibModal){$scope.data={};$scope.service={select2Options:{allowClear:!0,minimumInputLength:1,placeholder:'Select a part to order',query:function(options){var discontinued=typeof $scope.data.discontinued!=='undefined'?$scope.data.discontinued:0;options.discontinued=discontinued;var response=function(response){options.callback({more:response.current_page<response.last_page,results:response.data})};Stock.get({query:options.term,page:options.page,show_discontinued:options.discontinued},response,ErrorsHandler)},formatResult:function(obj,container,query){return obj.name+' - £'+obj.retail_price+' (In stock: '+obj.quantity+')'},formatSelection:function(obj){return obj.name}}};$scope.parts=PartsService.parts;$scope.selectPart=function(){$scope.errors="";$scope.data.stock_id=$scope.data.part_search.id;$scope.data.stock=$scope.data.part_search}
$scope.showModal=function(){$scope.data={};$scope.errors=null;$scope.modal=$uibModal.open({templateUrl:'modals/request-existing-part-modal.html',scope:$scope})};$scope.hideModal=function(){$scope.modal.dismiss()};$scope.save=function(){$scope.loading=!0;$scope.data.related_id=$scope.related.id;$scope.data.related_type=$scope.related_type;if($scope.virtualMode){var part={stock:$scope.data.stock,stock_id:$scope.data.stock.id,quantity:$scope.data.quantity,price:$scope.data.stock.price_excluding_vat,on_order:1};$scope.parts.push(part);$scope.hideModal();$scope.data={quantity:1,include_vat:1};$scope.data.part_search=undefined;$scope.loading=!1}else{Part.requestPart($scope.data,function(response){$scope.hideModal();$scope.data={quantity:1,include_vat:1};$scope.parts.push(response);$scope.data.part_search=undefined;$scope.loading=!1},function(response){$scope.loading=!1;$scope.errors=_.isObject(response.data.error)?response.data.error.message:response.data.error})}}}]);Bookings.controller('RequestNonExistingPartCtrl',['$scope','Order','ErrorsHandler','PartsService','$filter','$ClientsManager','Stock','Part','$uibModal',function($scope,Order,ErrorsHandler,PartsService,$filter,$ClientsManager,Stock,Part,$uibModal){$scope.initData=function(){$scope.data={price_vat_included:1,severity_id:2,related_type:$scope.related_type,quantity:1,type:'non_stock',approval_required:1};$scope.hideError=!1};$scope.parts=PartsService.parts;$scope.showModal=function(){$scope.initData();$scope.modal=$uibModal.open({templateUrl:'modals/request-non-existing-part-modal.html',scope:$scope})};$scope.hideModal=function(){$scope.modal.dismiss()};$scope.save=function(){$scope.errors=null;$scope.loading=!0;$scope.data.related_id=$scope.related.id;if($scope.virtualMode){Stock.get({query:$scope.data.name},function(response){if(response.data.length==0){var price=($scope.data.price_vat_included==1)?parseInt($scope.data.price_excluding_vat,10)/(1+parseInt($scope.vat,10)/100):parseInt($scope.data.price_excluding_vat,10);var part=angular.extend({stock:$scope.data,price:price.toFixed(2),on_order:!0},$scope.data);$scope.hideModal();$scope.parts.push(part)}else{$scope.loading=!1;$scope.hideError=!1;$scope.errors="There is already a product with the same name."}})}else{Part.requestPart($scope.data,function(response){$scope.hideModal();$scope.parts.push(response)},function(response){$scope.loading=!1;$scope.hideError=!1;$scope.errors=response.data.error.message})}}}]);Bookings.controller('SmsCtrl',['$scope','$uibModalInstance','task','$http','ErrorsHandler','mobile','apiUrl','Messenger','CustomerNotificationsService','Tasks',function($scope,$uibModalInstance,task,$http,ErrorsHandler,mobile,apiUrl,Messenger,CustomerNotificationsService,Tasks){var task_id=task?task.id:'';$scope.data={send_to:mobile,message:'Your repair '+task_id+' is now ready to be collected. Computer Assistance',task_id:task_id};$scope.submit=function(){if($scope.sending)return!1;$scope.sending=!0;var params={send_to:$scope.data.send_to,message:$scope.data.message,id:$scope.data.task_id};Tasks.sendSMS(params,function(response){$scope.sending=!1;CustomerNotificationsService.notifications.push(response.notification);Messenger("Your SMS has been successfully sent to the client and should be delivered shortly");$uibModalInstance.close()},function(a,b){$scope.sending=!1;ErrorsHandler(a,b)})};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Bookings.controller('SubscribersCtrl',['$scope','$uibModalInstance','task','users','Tasks','ErrorsHandler',function($scope,$uibModalInstance,task,users,Tasks,ErrorsHandler){_.each(users,function(u){var subscriber=_.find(task.subscribers,function(el){return el.id==u.id});if(subscriber)u.subscribed=!0});$scope.subscribers=task.subscribers;$scope.users=users;$scope.subscribe=function(user){user.saving=!0;task.subscribers.push(user);Tasks.update({id:task.id,subscribers:task.subscribers},function(response){user.saving=!1;user.subscribed=!0},ErrorsHandler)};$scope.unsubscribe=function(user){user.saving=!0;task.subscribers=_.reject(task.subscribers,function(el){return el.id==user.id});$scope.subscribers=task.subscribers;Tasks.update({id:task.id,subscribers:task.subscribers},function(response){user.saving=!1;user.subscribed=!1},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Bookings.controller('TaskDedicatedTimeCtrl',['$scope','$uibModalInstance',function($scope,$uibModalInstance){$scope.data={attendant_time:0,unattendant_time:0};$scope.response={attendant_time:0,unattendant_time:0};$scope.helper={valid_attended:!0,valid_unattended:!0}
$scope.submit=function(){$scope.response.attended_time=$scope.convertToSeconds($scope.data.attended_time);if($scope.response.attended_time>=0)
$scope.helper.valid_attended=!0;else $scope.helper.valid_attended=!1;$scope.response.unattended_time=$scope.convertToSeconds($scope.data.unattended_time);if($scope.response.unattended_time>=0)
$scope.helper.valid_unattended=!0;else $scope.helper.valid_unattended=!1;if($scope.helper.valid_attended&&$scope.helper.valid_unattended)
$uibModalInstance.close($scope.response);};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')};$scope.convertToSeconds=function(time){if(!time||time.trim()===''){return 0}
var splited=time.split(" ");var seconds=0;for(i=0;i<splited.length;i++){if(!splited[i]||splited[i].trim()===''){continue}
var num=0;if(splited[i].indexOf("d")>=0){num=parseInt(splited[i].replace('d',''))||0;num=num*86400}else if(splited[i].indexOf("h")>=0){num=parseInt(splited[i].replace('h',''))||0;num=num*3600}else if(splited[i].indexOf("m")>=0){num=parseInt(splited[i].replace('m',''))||0;num=num*60}
seconds=seconds+num}
return seconds}}]);Bookings.controller('TaskNoPriceCtrl',['$scope','$uibModalInstance',function($scope,$uibModalInstance){$scope.data={selected_reason:'Contract'};$scope.reasons=['Contract','The job was canceled','Nothing was done','Internal job','Other'];$scope.submit=function(){if($scope.data.selected_reason=='Other'){$uibModalInstance.close($scope.data.reason)}else{$uibModalInstance.close($scope.data.selected_reason)}};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Bookings.controller('TaskPasswordCtrl',['$scope','taskDevices','$uibModalInstance','password','keychainTypes',function($scope,taskDevices,$uibModalInstance,password,keychainTypes){$scope.data={password:password,devices:taskDevices,types:keychainTypes};if(!$scope.data.password.type)
$scope.data.password.type="windows";$scope.save=function(){$uibModalInstance.close($scope.data.password)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')};$scope.isFormValid=function(){if(!$scope.data.password.username)
return!1;if(!$scope.data.password.password)
return!1;return!0}}]);Bookings.controller('TaskTemplatesCtrl',['$scope','Tasks','ErrorsHandler','TaskTemplatesService','Messenger',function($scope,Tasks,ErrorsHandler,TaskTemplatesService,Messenger){$scope.select2Options=TaskTemplatesService;$scope.$watch('task.template_items',function(items){$scope.parseItems(items)},!0);$scope.$on('task-updated',function(){var items=$scope.task.template_items;if(!items)return;$scope.parseItems(items)});$scope.parseItems=function(items){if($scope.task){_.each($scope.task.templates,function(template){_.each(template.items,function(el){el.done=_.find(items,function(item){return item.id==el.id&&item.pivot.value})?!0:!1;el.notRequired=_.find(items,function(item){return item.id==el.id&&item.pivot.value==!1})?!0:!1})})}};$scope.updateTask=function(ev){Tasks.update($scope.task,function(response){angular.extend($scope.task,response||{});Messenger("The task has been updated successfully")},ErrorsHandler)};$scope.done=function(item){item.notRequired=!1;var params=angular.copy(item);params.item_id=item.id;params.id=$scope.task.id;Tasks.updateTemplateItem(params,function(response){},ErrorsHandler)};$scope.notRequired=function(item){item.done=!1;item.notRequired=!0;var params=angular.copy(item);params.item_id=item.id;params.id=$scope.task.id;Tasks.updateTemplateItem(params,function(response){},ErrorsHandler)}}]);Bookings.controller('TasksCtrl',['$scope','$http','Tasks','Page','ErrorsHandler','$ClientsManager','PartsService','CustomerNotificationsService','SalesBookingService','InvoiceBookingService','$uibModal','Messenger','$rootScope','TaskFeedback','$AuthUser','CommentsService',function($scope,$http,Tasks,Page,ErrorsHandler,$ClientsManager,PartsService,CustomerNotificationsService,SalesService,InvoiceService,$uibModal,Messenger,$rootScope,TaskFeedback,$AuthUser,CommentsService){$scope.modified=!1;$scope.initialized=!1;$scope.callInProgress={};$scope.activeCall=null;$scope.boolean_options=[{name:'No',value:0},{name:'Yes',value:1}];$scope.payment_options=[{name:'not paid',value:0},{name:'paid',value:1}];$scope.auth=$AuthUser;$scope.history={cancelled_reasons:[],no_price_reasons:[],};$scope.total=0;$scope.PartsService=PartsService;$scope.SalesService=SalesService;$scope.InvoiceService=InvoiceService;$scope.CustomerNotificationsService=CustomerNotificationsService;$scope.$watch('data',function(value){if(typeof value!='undefined'&&value.id){if($scope.initialized){$scope.modified=!0}
$scope.taskType=$scope.data.type;$scope.initialized=!0}},!0);$scope.$watch('data.onsite_start',function(value){if(typeof value=='undefined'||$scope.data.type!='onsite')return;$scope.data.onsite_start=(value=='0000-00-00 00:00:00')?new Date():moment(value).valueOf()});$scope.$watch('data.onsite_end',function(value){if(typeof value=='undefined'||$scope.data.type!='onsite')return;$scope.data.onsite_end=(value=='0000-00-00 00:00:00')?new Date():moment(value).valueOf()});$rootScope.$on('task-updated',function(){$scope.modified=!1});$scope.$watch('data.labour',function(value){if(typeof value=='undefined')return!1;$scope.total=(parseFloat($scope.data.labour)+parseFloat(PartsService.getTotal())).toFixed(2)});$scope.$watch('PartsService.total',function(){if(typeof $scope.data=='undefined')return!1;$scope.total=(parseFloat($scope.data.labour)+parseFloat(PartsService.getTotal())).toFixed(2)});$scope.$watch('data.revision_history',function(value){$scope.history.no_price_reasons=[];$scope.history.cancelled_reasons=[];_.each(value,function(revision){if(revision.key=="no_price_reason")
$scope.history.no_price_reasons.push(revision);else if(revision.key=="cancelled_reason")
$scope.history.cancelled_reasons.push(revision);})},!0);$scope.$watch('taskId',function(value){if(value){$scope.loadData()}});$scope.$watch('data.type',function(value){var browserTabTitlePrefix=undefined;if(typeof value!='undefined'){browserTabTitlePrefix=$scope.getBrowserTabTitlePrefix(value);Page.setTitle(browserTabTitlePrefix+$scope.taskId)}});$scope.getBrowserTabTitlePrefix=function(type){switch(type.toLowerCase()){case 'onsite':return'OS ';break;case 'support':return'S ';break;case 'repair':return'R ';break;default:return'T '}};$scope.registerWatchers=function(){$scope.prebook_date=$scope.data.prebook_date;$scope.$watch('prebook_date',function(value,oldValue){if($scope.initialized&&value!==oldValue){$scope.updateValue({prebook_date:$scope.prebook_date,prebook:$scope.prebook_date?1:0})}
$('.datepicker').hide()});$scope.alarm_date=$scope.data.alarm_date};$scope.setPrebook=function(value){$scope.prebook_date=value?moment().format('DD-MM-YYYY'):null};$scope.getBooleanDisplay=function(value){return(value=='1')?'Yes':'No'};$scope.getPaymentDisplay=function(value){return(value=='1')?'paid':'not paid'};$scope.getSeverityDisplay=function(value){return $scope.severities.hasOwnProperty(value)?$scope.severities[value]:'not set'};$scope.getSubtypeDisplay=function(value){if(!$scope.data)return'';var subtype=_.find($scope.subtypes,function(el){return el.id==value});return subtype?subtype.name:'Not set'};$scope.getStandardPriceDisplay=function(value){if(!$scope.data)return'';var price=_.find($scope.standard_prices,function(el){return el.id==value});return price?price.name:'Not set'};$scope.getShelfTypeDisplay=function(value){if(!$scope.data)return'';var shelf=_.find($scope.shelf_types,function(el,id){return id==value});return shelf?shelf:'Not set'};$scope.getUserName=function(id){if(!id)return'No One';var user=_.find($scope.users,function(el){return el.id==id});return user?user.name:'No One'};$scope.getWorkerNames=function(){if(!$scope.data)return'';var names=_.pluck($scope.data.workers,'name');return names.join(", ")};$scope.getTotalDays=function(){if(!$scope.data||!$scope.data.completed_at)return 0;return moment($scope.data.completed_at).diff($scope.data.created_at,'days')};$scope.openLicensesModal=function(){$rootScope.$broadcast('open-licenses-modal')};$scope.loadData=function(){Tasks.get({taskId:$scope.taskId},function(response){$scope.data=response;$scope.modified=!1;CustomerNotificationsService.setNotifications(response.customer_notifications);$ClientsManager.setClient(response.client);$ClientsManager.setClientType(response.client_type);$ClientsManager.unify(response.site,response.site_user);$scope.registerWatchers()},ErrorsHandler)};$scope.updateValue=function(params){angular.extend(params,{id:$scope.data.id,updated_at:$scope.data.updated_at});Tasks.update(params,function(response){angular.extend($scope.data,response||{});$rootScope.$broadcast('task-updated',response);Messenger("I've succesfully saved the job!");CommentsService.loadComments({related_type:'task',related_id:$scope.data.id})},ErrorsHandler)};$scope.updateStatus=function(status_id){if(status_id==$scope.data.status_id)
return;var status=_.find($scope.statuses,function(el){return el.id==status_id});if(status&&status.id=='9'){$scope.markAsQA()}else if(status&&status.id=='8'){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/cancelled-reason.html',controller:'ReasonModalCtrl',});modalInstance.result.then(function(reason){$scope.updateValue({cancelled_reason:reason,status_id:status_id})})}else{$scope.updateValue({status_id:status_id})}};$scope.markAsQA=function(){var status_id='9';if($scope.data.type=='repair'){if(!$scope.data.unattended_time&&!$scope.data.attended_time){var modal=$uibModal.open({templateUrl:'bookings/modals/dedicated-time.html',controller:'TaskDedicatedTimeCtrl',});modal.result.then(function(response){if(parseFloat($scope.data.labour)==0&&!$scope.data.no_price_reason){var modalInstance=$uibModal.open({templateUrl:'modals/no-price-confirmation.html',controller:'TaskNoPriceCtrl',});modalInstance.result.then(function(reason){$scope.updateValue({attended_time:response.attended_time,unattended_time:response.unattended_time,no_price_reason:reason,status_id:status_id})})}else{$scope.updateValue({attended_time:response.attended_time,unattended_time:response.unattended_time,status_id:status_id})}})}else{if(parseFloat($scope.data.labour)==0&&!$scope.data.no_price_reason){var modalInstance=$uibModal.open({templateUrl:'modals/no-price-confirmation.html',controller:'TaskNoPriceCtrl',});modalInstance.result.then(function(reason){$scope.updateValue({no_price_reason:reason,status_id:status_id})})}else{$scope.updateValue({status_id:status_id})}}}else{if(parseFloat($scope.data.labour)==0&&!$scope.data.no_price_reason){var modalInstance=$uibModal.open({templateUrl:'modals/no-price-confirmation.html',controller:'TaskNoPriceCtrl',});modalInstance.result.then(function(reason){$scope.updateValue({no_price_reason:reason,status_id:status_id})})}else{$scope.updateValue({status_id:status_id})}}};$scope.save=function(){var data=angular.copy($scope.data);delete data.onsite_start;delete data.onsite_end;if(data.paid_at)data.paid_at=moment(data.paid_at).format("YYYY-MM-DD HH:mm:ss");Tasks.update(data,function(response){angular.extend($scope.data,response||{});$rootScope.$broadcast('task-updated',response);Messenger("I've succesfully saved the job!")},ErrorsHandler)};$scope.scrollTop=function(){$('body,html').animate({scrollTop:0},800)};$scope.showLinkTaskModal=function(){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/related_tasks.html',controller:'LinkTaskCtrl',resolve:{related_tasks:function(){return $scope.data.related_tasks},currentTask:function(){return $scope.data}}})};$scope.removeRelatedTask=function(task){$scope.data.related_tasks=_.reject($scope.data.related_tasks,function(el){return el.id==task.id});$scope.save()};$scope.changeDates=function(){var modalInstance=$uibModal.open({templateUrl:'modals/change-dates.html',controller:'ChangeDatesCtrl',resolve:{data:function(){return $scope.data}}})};$scope.changeAlarm=function(){var modalInstance=$uibModal.open({templateUrl:'modals/change-alarm.html',controller:'ChangeAlarmCtrl',resolve:{data:function(){return $scope.data}}});modalInstance.result.then(function(timestamp){$scope.updateValue({alarm_date:timestamp})})};$scope.deleteAlarm=function(){$scope.data.alarm_date=null;$scope.updateValue($scope.data)};$scope.changeDueDate=function(){var modalInstance=$uibModal.open({templateUrl:'modals/change-alarm.html',controller:'ChangeDueDateCtrl',resolve:{data:function(){return $scope.data}}});modalInstance.result.then(function(timestamp){$scope.updateValue({due_date:timestamp})})};$scope.deleteDueDate=function(){$scope.updateValue({'due_date':null})}
$scope.showSubscribers=function(){var modalInstance=$uibModal.open({templateUrl:'modals/subscribers.html',controller:'SubscribersCtrl',resolve:{task:function(){return $scope.data},users:function(){return $scope.users}}})};$scope.cancelFeedback=function(){TaskFeedback.delete({taskFeedbackId:$scope.data.feedback.code},function(response){Messenger("The feedback request was canceled.");$scope.data.feedback=null},ErrorsHandler)};$scope.unassign=function(){Tasks.update({id:$scope.data.id,assigned_to:null,updated_at:$scope.data.updated_at},function(response){angular.extend($scope.data,response||{});$rootScope.$broadcast('task-updated',response)},ErrorsHandler)};$scope.subscribe=function(){$scope.data.subscribers.push($AuthUser.data);Tasks.update({id:$scope.data.id,subscribers:$scope.data.subscribers},function(response){angular.extend($scope.data,response||{});$rootScope.$broadcast('task-updated',response);Messenger("You have succesfully subscribed to this task.")},ErrorsHandler)};$scope.unsubscribe=function(){$scope.data.subscribers=_.reject($scope.data.subscribers,function(el){return el.id==$AuthUser.data.id});Tasks.update({id:$scope.data.id,subscribers:$scope.data.subscribers},function(response){angular.extend($scope.data,response||{});$rootScope.$broadcast('task-updated',response);Messenger("You have succesfully un-subscribed from this task.")},ErrorsHandler)};$scope.createExchangeCalendarEvent=function(){$scope.creatingCalendarEvent=!0;Tasks.createExchangeCalendarEvent({id:$scope.data.id},function(response){$scope.creatingCalendarEvent=!1;angular.extend($scope.data,response||{});$rootScope.$broadcast('task-updated',response);Messenger("A new calendar entry was succesfully created")},function(a,b){$scope.creatingCalendarEvent=!1;ErrorsHandler(a,b)})};$scope.showHistory=function(title,historyEntries){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/history.html',controller:'HistoryCtrl',resolve:{title:function(){return title},historyEntries:function(){return historyEntries},users:function(){return $scope.users}}})};$scope.generateInvoice=function(data){if(data.status_id==9)
alert("An invoice can not be create until the job pass the QA.");else if(PartsService.hasPartsOnOrder())
alert("It is not possible to create an invoice for parts on order.");else InvoiceService.generate(data)}}]);Bookings.controller('TasksPageCtrl',['$scope','$parse','Tasks','$http','Client','Customer','ErrorsHandler',function($scope,$parse,Tasks,$http,Client,Customer,ErrorsHandler){$scope.selections={'user':'','textSearch':'','type':'','subtype':'','severity':'','status':'','myPaid':'','myCompleted':'','invoiced':'','clientId':'','customerId':'','date':'',};$scope.$watchGroup(['users','types','subtypes','severities','statuses'],function(){$scope.usersObject=$scope.makeControlObject($scope.users);$scope.typesObject=$scope.makeControlObject($scope.types);$scope.subtypesObject=$scope.makeControlObject($scope.subtypes);$scope.severitiesObject=$scope.makeControlObject($scope.severities);$scope.statusesObject=$scope.makeControlObject($scope.statuses);angular.forEach($scope.typesObject,function(key){key.id=key.name});$scope.selectInputs=[{name:'user',label:'User',icon:'fa-user',model:'user',values:$scope.usersObject},{name:'type',label:'Type',icon:'fa-wrench',model:'type',values:$scope.typesObject},{name:'subtype',label:'Subtype',icon:'fa-bars',model:'subtype',values:$scope.subtypesObject},{name:'severity',label:'Severity',icon:'fa-exclamation-triangle',model:'severity',values:$scope.severitiesObject},{name:'status',label:'Status',icon:'fa-star',model:'status',values:$scope.statusesObject}]});$scope.radioInputs=[{model:'myPaid',labelOne:'All',labelTwo:'Paid',labelThree:'Unpaid',icon:'fa-money'},{model:'myCompleted',labelOne:'All',labelTwo:'Completed',labelThree:'Open',icon:'fa-list'},{model:'date',labelOne:'default',labelTwo:'descending',labelThree:'ascending',icon:'fa-calendar-o'}];$scope.qaTasks=[];$scope.makeQaTasks=function(dataIn){for(ctr in dataIn){if(dataIn[ctr].qa_tasks.length>0){var qatask=dataIn[ctr].qa_tasks;var taskId=dataIn[ctr].id;for(ctr2 in qatask){$scope.qaTasks[taskId]=qatask[ctr2].id}}}}
$scope.makeControlObject=function(collection){var myObj={};var myObjArray=[];angular.forEach(collection,function(key){myObj={id:'',name:''};myObj.id=key.id;myObj.name=key.name;myObjArray.push(myObj)});return myObjArray}
$scope.getSelections=function(){return $scope.selections};$scope.getSelectionValue=function(key){return $scope.selections[key]};$scope.setSelectionValue=function(key,value){$scope.selections[key]=value};$scope.clear=function($event){$event.stopPropagation();if($scope.selections.textSearch||$scope.selections.textSearch.length>0){$scope.selections.textSearch=undefined}};$scope.resetSingleSelect=function(control){$scope.selections[control]=''};$scope.resetSelectionsObject=function(){angular.forEach($scope.selections,function(key,value){$scope.selections[value]=''})};$scope.resetAll=function(){if(typeof $scope.selCliCust!=='undefined'){$scope.selCliCust.selected=undefined}
$scope.resetSelectionsObject()};$scope.disabled=undefined;$scope.searchEnabled=undefined;$scope.searchRes=[];$scope.searchMediaClient=function($select){return $http.get(Client.getSearchUrl(),{params:{name:$select.search}}).then(function(response){$scope.searchRes=response.data.data})};$scope.onClientSelected=function(selectedClient,$select){$scope.selCliCust=$select;if(typeof selectedClient!=='undefined'){$scope.selections.clientId=selectedClient.id}else{$scope.selections.clientId=''}}
$scope.searchMediaCustomer=function($select){return $http.get(Customer.getSearchUrl(),{params:{name:$select.search}}).then(function(response){$scope.searchRes=response.data.data})};$scope.onCustomerSelected=function(selectedCustomer,$select){$scope.selCliCust=$select;if(typeof selectedCustomer!=='undefined'){$scope.selections.customerId=selectedCustomer.id}else{$scope.selections.customerId=''}}
$scope.columns=[{field:'id',name:'Id',width:'5%',cellTemplate:'<div class="ngCellText" ng-class="col.colIndex()"><a ng-href="/tasks/{{COL_FIELD}}" title="Opens in new tab" target="_blank">{{COL_FIELD}}</a></div>',resizable:!1},{field:'client.name',name:'Client',width:'17%',resizable:!0},{field:'title',name:'Title',width:'17%',resizable:!0},{field:'created_at',name:'Date Booked',width:'11%',type:'date',cellFilter:"date:'dd/MM/yyyy'",enableFiltering:!0,resizable:!1},{field:'user.name',name:'Assigned To',width:'10%',resizable:!0},{field:'type',name:'Type',width:'10%',resizable:!0},{field:'subtype_name',name:'Subtype',width:'10%',resizable:!0},{field:'severity.name',name:'Severity',width:'10%',resizable:!0},{field:'status.name',name:'Status',width:'10%',cellTemplate:'<div class="ngCellText" ng-class="col.colIndex()"><a ng-if="(grid.appScope.qaTasks | contains : row.entity.id) == true" ng-href="/tasks/qa/{{grid.appScope.qaTasks[row.entity.id]}}" title="Opens in new tab" target="_blank">QC {{grid.appScope.qaTasks[row.entity.id]}}</a><span ng-if="(grid.appScope.qaTasks | contains : row.entity.id) == false">{{COL_FIELD}}</span></div>',resizable:!0}];var paginationOptions={pageNumber:1,pageSize:100};$scope.gridOptions={enablePagination:!0,enablePaginationControls:!0,useExternalPagination:!0,paginationPageSizes:[20,50,100,200],paginationPageSize:100,enableFiltering:!0,enableRowSelection:!0,enableRowHeaderSelection:!1,enableSelectAll:!0,selectionRowHeaderWidth:35,multiSelect:!1,rowHeight:35,showGridFooter:!0,enableSorting:!0,enableColumnResizing:!0,columnDefs:$scope.columns,onRegisterApi:function(gridApi){$scope.gridApi=gridApi;gridApi.selection.on.rowSelectionChanged($scope,function(row){});gridApi.pagination.on.paginationChanged($scope,function(newPage,pageSize){paginationOptions.pageNumber=newPage;paginationOptions.pageSize=pageSize;$scope.loadData()})}};$scope.loadData=function(){this.currentSelections=$scope.getSelections();var prams={creator:!0,client:!0,user:!0,severities:!0,subtypes:!0,textSearch:$scope.getSelectionValue('textSearch'),page:paginationOptions.pageNumber,qa:!0,limit:paginationOptions.pageSize,type:$scope.getSelectionValue('type'),assigned_to:$scope.getSelectionValue('user'),status:$scope.getSelectionValue('status'),subtype_id:$scope.getSelectionValue('subtype'),myCompleted:$scope.getSelectionValue('myCompleted'),myPaid:$scope.getSelectionValue('myPaid'),invoiced:$scope.getSelectionValue('invoiced'),clientId:$scope.getSelectionValue('clientId'),customerId:$scope.getSelectionValue('customerId')}
if($scope.selections.date==1){prams.orderBy="created_at"
prams.orderType="asc"}else if($scope.selections.date==2){prams.orderBy="created_at"
prams.orderType="desc"}
if($scope.getSelectionValue('severity')){prams.severity=$scope.getSelectionValue('severity')}
for(var propName in prams){if(prams[propName]===null||prams[propName]===undefined||prams[propName]===""){delete prams[propName]}}
Tasks.get(prams,function(response){$scope.makeQaTasks(response.data);$scope.tasks=response.data;$scope.gridOptions.data=response.data;$scope.gridOptions.totalItems=response.total;angular.forEach($scope.gridOptions.data,function(task){task.subtype_name=$scope.getSubtypeName(task.subtype_id);task.created_at=$scope.dateFormat(task.created_at)})},ErrorsHandler)};$scope.dateFormat=function(date){date=new Date(date);return date};$scope.getSubtypeName=function(id){var name='not set';angular.forEach($scope.subtypes,function(subtype){if(subtype.id==id){name=subtype.name}});return name};$scope.$watch('selections',function(){$scope.loadData()},!0)}]);Bookings.controller('widgetBuildsCtrl',['$scope','BuildsService','Build','ErrorsHandler','$timeout','$q','Messenger','$uibModal','Part','PartsService',function($scope,BuildsService,Build,ErrorsHandler,$timeout,$q,Messenger,$uibModal,Part,PartsService){$scope.builds=BuildsService.builds;$scope.$watch('related',function(related){if(related)
BuildsService.setBuilds(related.builds);},!0);$scope.$watch('builds',function(){$scope.buildTotal();BuildsService.setSubtotal($scope.subtotal());BuildsService.setTax($scope.tax());BuildsService.setTotal(BuildsService.getSubtotal()+BuildsService.getTax())},!0);$scope.addBuild=function(ev,data){var build=_.find($scope.builds,function(el){if(el.stock)
return el.stock.id==data.id&&el.status=='planning';return!1});$scope.$apply(function(){if(build){build.quantity++;$scope.updateBuild(build)}else{Build.save({related_id:$scope.related.id,related_type:$scope.related_type,stock_id:data.id,quantity:1,price:data.price_excluding_vat},function(res){$scope.builds.push(res);if($scope.related_type=='user')
$scope.related.builds.push(res);},function(response,b){if(response.data&&response.data.error){alert('BUILD ERROR: '+response.data.error);Messenger(response.data.error,'error')}else if(response.data&&response.data.success===!1&&response.data.message){alert('BUILD MESSAGE: '+response.data.message);Messenger(response.data.message,'error')}else{alert('OTHER ERROR: '+JSON.stringify(response.data));ErrorsHandler(response,b)}
return!1})}})};$scope.removeBuild=function(build){Build.remove({buildId:build.id},function(){var index=_.findIndex($scope.builds,function(el){return el.id===build.id});$scope.builds.splice(index,1);if($scope.related_type=='user')
$scope.related.builds.splice(index,1);},ErrorsHandler)};$scope.updateBuild=function(build){Build.update(build,function(response){build.saved=!0;angular.extend(build,response||{})},function(a,b){build.failed=!0;ErrorsHandler(a,b)});$timeout(function(){build.saved=!1;build.failed=!1},1500)};$scope.updateBuildProperty=function(build,property,value){var deferred=$q.defer();var data={id:build.id,updated_at:build.updated_at,};data[property]=value;Build.update(data,function(response){angular.extend(build,response||{});deferred.resolve(!0)},function(a,b){ErrorsHandler(a,b);deferred.reject(!1)});return deferred.promise};$scope.completeBuild=function(build,attach_to_parts){build.status='completed';Build.update(build,function(response){var oldIndex=_.findIndex($scope.builds,function(el){return el.id===response.id});if(oldIndex)
$scope.builds.splice(oldIndex,1);var oldBuild=angular.copy(build);angular.extend(build,response||{});if(attach_to_parts){var part=_.find(PartsService.parts,function(el){return el.stock_id===build.stock_id});if(part){part.quantity+=oldBuild.quantity;Part.save(part,function(res){PartsService.addPart(res)},function(a,b){ErrorsHandler(a,b);return!1})}else{part={related_id:$scope.related.id,related_type:$scope.related_type,stock_id:build.stock_id,quantity:build.quantity,price:build.stock.price_excluding_vat};Part.save(part,function(res){PartsService.addPart(res)},function(a,b){ErrorsHandler(a,b);return!1})}}
return!0},function(a,b){build.status='in_progress';ErrorsHandler(a,b)})}
$scope.updateTotalPrice=function(value,build){if(isNaN(value)){alert("The total price given is not a valid price.");return}
$scope.updateBuildProperty(build,'price',(build.total_price/build.quantity/($scope.vat/100+1)).toFixed(2))};$scope.buildTotal=function(){_.each($scope.builds,function(build){build.total_price=build.price*build.quantity*($scope.vat/100+1)})};$scope.total=function(){return $scope.subtotal()+$scope.tax()};$scope.tax=function(){var tax=0;_.each($scope.builds,function(el){tax+=el.price*el.quantity*$scope.vat/100});return tax};$scope.subtotal=function(){var total=0;_.each($scope.builds,function(el){total+=el.price*el.quantity});return total};$scope.cost=function(){var cost=0;_.each($scope.builds,function(el){cost+=el.cost*el.quantity});return cost};$scope.showBuildComponents=function(build){var modalInstance=$uibModal.open({controller:'BuildCompositeItemsCtrl',templateUrl:'bookings/modals/build-composite-items.html',resolve:{build:function(){return build}}});modalInstance.result.then(function(data){angular.extend(build,data||{})})}}]);Bookings.controller('widgetClientCtrl',['$ClientsManager','$scope','ErrorsHandler','Repair','SiteUser','$routeParams','$rootScope','$http','$timeout','CustomerForm','$uibModal',function($ClientsManager,$scope,ErrorsHandler,Repair,SiteUser,$routeParams,$rootScope,$http,$timeout,CustomerForm,$uibModal){$scope.client=$ClientsManager.getClient();$scope.cManager=$ClientsManager;$scope.site={};$scope.site_user={};$scope.changed_direct_dial=!1;$scope.changed_user_phone=!1;$scope.changed_mobile_phone=!1;CustomerForm.onUpdate=function(data){$scope.showCustomerForm=!1;$routeParams.customerId=null;$ClientsManager.setClient(data)};$scope.$watch('client',function(){if($scope.client){$scope.site=_.find($scope.client.sites,function(el){return el.id==$scope.task.site_id})}},!0);$scope.$watch('site',function(){if($scope.site){$scope.site_user=_.find($scope.site.users,function(el){return el.id==$scope.task.site_user_id})}},!0);$scope.saveDirectDial=function(){Repair.update({id:$scope.task.id,direct_dial:$scope.task.direct_dial},function(res){$scope.changed_direct_dial=!1},ErrorsHandler)};$scope.saveUserPhone=function(type){SiteUser.update({id:$scope.site_user.id,site_id:$scope.site_user.site_id,phone:$scope.site_user.phone,mobile:$scope.site_user.mobile},function(res){if(type=='phone')
$scope.changed_user_phone=!1;else $scope.changed_mobile_phone=!1},ErrorsHandler)};$scope.editCustomer=function(){$routeParams.customerId=$ClientsManager.getClient().id;$rootScope.$broadcast('$routeChangeSuccess');$scope.showCustomerForm=!0};$scope.dial=function(number,ev){number=number.replace(' ','');if(!number)return!1;var btn=angular.element(ev.target).button('loading');$timeout(function(){btn.button('reset')},750);$http.get('/dial/'+number).error(ErrorsHandler)};$scope.changeClient=function(){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/change-client.html',controller:'ChangeClientCtrl',resolve:{task:function(){return $scope.task}}});modalInstance.result.then(function(client){angular.extend($scope.client,client||{});$ClientsManager.setClientType(client.type)})};$scope.sms=function(){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/sms.html',controller:'SmsCtrl',resolve:{task:function(){return $scope.task},mobile:function(){return $scope.client.mobile}}})}}]);Bookings.controller("widgetCommentsCtrl",["$scope","Comment","CommentsService","ErrorsHandler","$interval","$ClientsManager","$uibModal",function($scope,Comment,CommentsService,ErrorsHandler,$interval,$ClientsManager,$uibModal){$scope.comments=CommentsService.comments;$scope.pages=CommentsService.pages;$scope.options={};$scope.sending=!1;$scope.addComment=function(data){$scope.sending=!0;var data={related_id:$scope.data.id,related_type:$scope.type,message:$scope.comment,public:$scope.public,send_email:$scope.options.send_email,email:$scope.options.email,cc:$scope.options.cc,created_by:$scope.user};CommentsService.addComment(data).then(function(response){$scope.comment="";$scope.sending=!1;$scope.public=!1;$scope.pages=CommentsService.pages;$scope.currentPage=CommentsService.currentPage})};$scope.$watch("data.id",function(value){if(value){CommentsService.loadComments({related_type:$scope.type,related_id:$scope.data.id},function name(params){$scope.pages=CommentsService.pages;$scope.currentPage=CommentsService.currentPage})}});$scope.keyDownEvent=$event=>{if($event.ctrlKey&&$event.keyCode===13){$scope.addComment($event)}};var emailWatcher=$scope.$watch(function(){return $ClientsManager.data},function(data){if(data.email&&!$scope.email){if(data.email=="imandtservicedesk@ouh.nhs.uk"||data.email=="imandtservicedeskadmin@ouh.nhs.uk"){$scope.public=!0;$scope.options.send_email=!0}
$scope.options.email=data.email;emailWatcher()}},!0);$scope.loadComments=function(current_page,append){$scope.currentPage=current_page;Comment.get({type:$scope.type,id:$scope.data.id,page:current_page},function(res){if(append){CommentsService.comments=CommentsService.comments.concat(res.data)}else{CommentsService.comments=[];CommentsService.setComments(res.data,res.last_page)}
$scope.comments=CommentsService.comments;$scope.pages=res.last_page},ErrorsHandler)};$scope.showEmailCustomerModal=function(){if(!$scope.data||!$scope.data.id||$scope.type!=='task'){return}
var customerEmail=$ClientsManager.data&&$ClientsManager.data.email?$ClientsManager.data.email:'';if(!customerEmail){if(typeof Messenger!=='undefined'){Messenger("Cannot email customer: No customer email found")}
return}
var modalInstance=$uibModal.open({templateUrl:'bookings/modals/email-customer.html',controller:'EmailCustomerModalCtrl',size:'lg',resolve:{data:function(){return{id:$scope.data.id,title:$scope.data.title,client_name:$ClientsManager.data.name||$ClientsManager.data.Name||''}},customerEmail:function(){return customerEmail}}});modalInstance.result.then(function(result){CommentsService.loadComments({related_type:$scope.type,related_id:$scope.data.id})},function(){})};$scope.getMailtoUrl=function(){return'#'};$scope.deleteComment=function(comment){if(!confirm('Are you sure you want to delete this comment? This action cannot be undone.')){return}
Comment.delete({commentId:comment.id},function(res){var index=CommentsService.comments.findIndex(function(c){return c.id===comment.id});if(index!==-1){CommentsService.comments.splice(index,1)}
if(typeof Messenger!=='undefined'){Messenger('Comment deleted successfully')}},function(error){if(typeof Messenger!=='undefined'){Messenger('Failed to delete comment')}
ErrorsHandler(error)})};var cancelRefresh=$interval(function myFunction(){CommentsService.loadComments({related_type:$scope.type,related_id:$scope.data.id})},300*1000)}]);Bookings.controller('widgetCustomerNotificationsCtrl',['$scope','ErrorsHandler','$timeout','CustomerNotificationsService','$uibModal',function($scope,ErrorsHandler,$timeout,CustomerNotificationsService,$uibModal){$scope.notifications=CustomerNotificationsService.notifications;$scope.sendNotification=function(){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/new-customer-notification.html',controller:'CustomerNotificationCtrl',resolve:{taskId:function(){return $scope.task.id},notification:null,}})};$scope.showNotification=function(notification){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/customer-notification.html',controller:'CustomerNotificationCtrl',resolve:{notification:function(){return notification},taskId:function(){return $scope.task.id}}})}}]);Bookings.controller('widgetMapCtrl',['$scope','$ClientsManager','locationHelper','$http','$sce',function($scope,$ClientsManager,locationHelper,$http,$sce){$scope.client=$ClientsManager;$scope.$watch('client.client',function(value){if(value.id)
$scope.loadLocation();},!0);$scope.$watch('address',function(value,oldValue){if(value!==oldValue){$scope.loadAddress()}});$scope.$watch('formatted_address',function(value,oldValue){if(value!==oldValue){$scope.formatted_address=value.replace(/\s/g,'+')}});$scope.loadLocation=function(){if($scope.address){$scope.loadAddress();return!1}
if($scope.client.isClient())
$scope.loadClientLocation();else $scope.loadCustomerLocation()};$scope.loadClientLocation=function(){if(!$scope.task)return!1;var site=locationHelper.getSite($scope.client.getClient().sites,$scope.task.site_id);if(site){$scope.formatted_address=site.address+', '+site.postcode}};$scope.loadCustomerLocation=function(){var client=$scope.client.getClient();$scope.formatted_address=client.address+', '+client.postcode};$scope.loadAddress=function(){return $scope.formatted_address=$scope.address};$scope.trustSrc=function(src){return $sce.trustAsResourceUrl(src)}}]);Bookings.controller('widgetPartsCtrl',['$scope','PartsService','Part','ErrorsHandler','$timeout','$q','Messenger','$uibModal',function($scope,PartsService,Part,ErrorsHandler,$timeout,$q,Messenger,$uibModal){$scope.parts=PartsService.parts;$scope.$watch('related',function(related){if(related)
PartsService.setParts(related.parts);},!0);$scope.$watch('parts',function(){$scope.partTotal();PartsService.setSubtotal($scope.subtotal());PartsService.setTax($scope.tax());PartsService.setTotal(PartsService.getSubtotal()+PartsService.getTax())},!0);$scope.addPart=function(ev,data){var part=_.find($scope.parts,function(el){if(el.stock)
return el.stock.id==data.id;return!1});$scope.$apply(function(){if(part){part.quantity++;Part.update(part,function(response){part.saved=!0;angular.extend(part,response||{})},function(a,b){part.failed=!0;ErrorsHandler(a,b);part.quantity--})}else{Part.save({related_id:$scope.related.id,related_type:$scope.related_type,stock_id:data.id,quantity:1,price:data.price_excluding_vat},function(res){$scope.parts.push(res);if($scope.related_type=='user')
$scope.related.parts.push(res);},function(a,b){ErrorsHandler(a,b);return!1})}})};$scope.removePart=function(part){Part.remove({partId:part.id},function(){var index=_.findIndex($scope.parts,function(el){return el.id===part.id});$scope.parts.splice(index,1);if($scope.related_type=='user')
$scope.related.parts.splice(index,1);},ErrorsHandler)};$scope.updatePart=function(part){Part.update(part,function(response){part.saved=!0;angular.extend(part,response||{})},function(a,b){part.failed=!0;ErrorsHandler(a,b)});$timeout(function(){part.saved=!1;part.failed=!1},1500)};$scope.updatePartProperty=function(part,property,value){var deferred=$q.defer();var data={id:part.id,updated_at:part.updated_at,};data[property]=value;Part.update(data,function(response){if(part.on_order=='1'&&response.on_order=='0'){Messenger('Your part was removed from the order because the quantity requested is currently available.')}
angular.extend(part,response||{});deferred.resolve(!0)},function(a,b){ErrorsHandler(a,b);deferred.reject(!1)});return deferred.promise};$scope.updateTotalPrice=function(part){part.price=(part.total_price/part.quantity/(part.vat/100+1)).toFixed(2);$scope.updatePart(part)};$scope.partTotal=function(){_.each($scope.parts,function(part){if(part.price&&part.quantity&&part.vat){part.total_price=part.price*part.quantity*(part.vat/100+1)}})};$scope.total=function(){return $scope.subtotal()+$scope.tax()};$scope.tax=function(){var tax=0;_.each($scope.parts,function(el){if(el.price&&el.quantity&&el.vat){tax+=el.price*el.quantity*el.vat/100}});return tax};$scope.subtotal=function(){var total=0;_.each($scope.parts,function(el){if(el.price&&el.quantity){total+=el.price*el.quantity}});return total};$scope.cost=function(){var cost=0;_.each($scope.parts,function(el){if(el.stock&&el.stock.cost&&el.stock.cost!=='NA'){cost+=el.stock.cost*el.quantity}});return cost};$scope.updateCompositeItems=function(part){var modalInstance=$uibModal.open({controller:'PartCompositeItemsCtrl',templateUrl:'bookings/modals/part-composite-items.html',resolve:{part:function(){return part}}});modalInstance.result.then(function(data){angular.extend(part,data||{})})}}]);Bookings.controller('widgetPasswordsCtrl',['$scope','PasswordsService','ErrorsHandler','$timeout','$uibModal','Keychain','Messenger',function($scope,PasswordsService,ErrorsHandler,$timeout,$uibModal,Keychain,Messenger){$scope.passwords=PasswordsService.passwords;$scope.deletePassword=function(password){Keychain.delete({keychainId:password.id,task_id:$scope.task.id},function(){PasswordsService.deletePassword(password);Messenger("The password has been changed successfully!")})};$scope.chooseExistingPassword=function(){var modalInstance=$uibModal.open({templateUrl:'clients/modals/keychain.html',controller:'KeychainModalCtrl',windowClass:'modal-fixed',resolve:{TaskId:function(){return $scope.task.id},Customer:function(){return{id:$scope.task.client_id,type:$scope.task.client_type}}}});modalInstance.result.then(function(){$scope.loadTaskPasswords()})};$scope.addPassword=function(){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/task-password.html',controller:'TaskPasswordCtrl',windowClass:'modal-fixed',resolve:{taskDevices:function(){return $scope.task.devices},password:function(){return{}},keychainTypes:function(){return PasswordsService.types}}});modalInstance.result.then(function(key){var parameters={client_type:$scope.task.client_type,client_id:$scope.task.client_id,task_id:$scope.task.id,password:key.password,username:key.username,type:key.type,device_id:key.device_id};Keychain.save(parameters,function(response){$scope.loadTaskPasswords();Messenger("The password has been added successfully!")})})};$scope.changePassword=function(password){var modalInstance=$uibModal.open({templateUrl:'bookings/modals/task-password.html',controller:'TaskPasswordCtrl',windowClass:'modal-fixed',resolve:{taskDevices:function(){return $scope.task.devices},password:function(){if(password.device_id){password.device_id=parseInt(password.device_id,10)}
return password},keychainTypes:function(){return PasswordsService.types}}});modalInstance.result.then(function(keychain){Keychain.update(keychain,function(response){$scope.loadTaskPasswords();Messenger("The password has been changed successfully!")})})};$scope.getDeviceName=function(id){var device=_.find($scope.task.devices,function(device){return device.id==id});return device?device.name:'General'};$scope.$watch('task.id',function(taskId){$scope.loadTaskPasswords()});$scope.loadTaskPasswords=function(){if($scope.task)
Keychain.get({task_id:$scope.task.id},function(response){PasswordsService.setPasswords(response.data)})};$scope.copyToClipboard=function(text){if(!text)return;var textarea=document.createElement('textarea');textarea.value=text;textarea.style.position='fixed';textarea.style.opacity='0';document.body.appendChild(textarea);textarea.select();try{document.execCommand('copy');Messenger("Password copied to clipboard!")}catch(err){Messenger("Failed to copy password","error")}
document.body.removeChild(textarea)}}]);Bookings.controller('widgetSlaCtrl',['$scope','Sla','SlaLog','ErrorsHandler','$ClientsManager','$element',function($scope,Sla,SlaLog,ErrorsHandler,$ClientsManager,$element){$scope.slas=[];$scope.logs=[];$scope.sla={};$scope.client=$ClientsManager.getClient();$scope.load=function(){Sla.get({limit:0},function(res){$scope.slas=res.data;$scope.sla=_.find($scope.slas,function(el){return el.id==$scope.client.sla_id})},ErrorsHandler)};$scope.loadLogs=function(){SlaLog.get({limit:0,task_id:$scope.task.id},function(response){$scope.logs=response.data},ErrorsHandler)};$scope.$watch('client',function(){if($ClientsManager.isClient()&&$scope.client.sla_id){$scope.load();$scope.loadLogs()}},!0);$scope.$on('task-updated',function(){$scope.loadLogs()});$scope.getTimeSpent=function(){var start_date=moment($scope.task.created_at).valueOf(),now=moment().valueOf(),time_spent=(now-start_date)/1000;return time_spent};$scope.getSecondsInitial=function(){var time_spent=$scope.getTimeSpent(),log;log=_.find($scope.logs,function(el){return el.type=='initial'});var sla_time=$scope.sla.time_initial*60;if(log)return sla_time-log.duration;return sla_time-time_spent};$scope.getSecondsFix=function(){var time_spent,log;log=_.find($scope.logs,function(el){return el.type=='initial'});if(!log)return $scope.sla.time_fix*60;time_spent=(moment().valueOf()-moment(log.created_at).valueOf())/1000;var sla_time=$scope.sla.time_fix*60;return sla_time-time_spent};$scope.checkInitialRunning=function(){var log=_.find($scope.logs,function(el){return el.type=='initial'});return(log)?!1:!0};$scope.checkFixRunning=function(){var log=_.find($scope.logs,function(el){return el.type=='initial'}),log_fix=_.find($scope.logs,function(el){return el.type=='fix'});return(log&&!log_fix)?!0:!1};$scope.initialSucceed=function(){var log=_.find($scope.logs,function(el){return el.type=='initial'});return(log&&log.missed=='0')?!0:!1};$scope.fixSucceed=function(){var log=_.find($scope.logs,function(el){return el.type=='fix'});return(log&&log.missed=='0')?!0:!1}}]);ClientsModule.controller('AddClientCtrl',['$scope','$uibModalInstance','$uibModal','$location','Client','ErrorsHandler',function($scope,$uibModalInstance,$uibModal,$location,Client,ErrorsHandler){$scope.step=1;$scope.last_step=3;$scope.data={sites:[]};$scope.SiteModal=function(){var modalInstance=$uibModal.open({templateUrl:'clients/site.html',controller:'SiteModalCtrl',backdrop:'static',windowClass:'modal-nested',resolve:{DoNotSave:function(){return!0},client_id:function(){return null},data:function(){return null}}});modalInstance.result.then(function(site){$scope.data.sites.push(site)})};$scope.removeSite=function(site){$scope.data.sites=_.reject($scope.data.sites,function(el){return el===site})};$scope.SiteUserModal=function(site){var modalInstance=$uibModal.open({templateUrl:'clients/site-user.html',controller:'SiteUserModalCtrl',backdrop:'static',windowClass:'modal-nested',resolve:{DoNotSave:function(){return!0},site:function(){return site},data:function(){return null}}});modalInstance.result.then(function(user){if(!site.users)site.users=[];site.users.push(user)})};$scope.removeSiteUser=function(site,user){site.users=_.reject(site.users,function(el){return el===user})};$scope.next=function(){if($scope.step<$scope.last_step){var formName='step_'+$scope.step+'_form';if($scope[formName].$valid){$scope.step++}else{$scope[formName].$submitted=!0}}};$scope.back=function(){if($scope.step>1)$scope.step--};$scope.submit=function(){$scope.saving=!0;Client.save($scope.data,function(response){$location.path('/client/'+response.id);$uibModalInstance.close(response)},function(error){$scope.saving=!1;ErrorsHandler(error)})};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);ClientsModule.controller('AddCustomerCtrl',['$scope','$uibModalInstance','Customer','ErrorsHandler','$http','CustomerForm','Messenger',function($scope,$uibModalInstance,Customer,ErrorsHandler,$http,CustomerForm,Messenger){$scope.data={};$scope.postcode={icon:'fa fa-crosshairs'};$scope.submit=function(){Customer.save($scope.data,function(response){Messenger("I've successfully created the customer!");CustomerForm.onCreate(response.data);$uibModalInstance.close(response.data)},ErrorsHandler)};$scope.close=function(){$uibModalInstance.dismiss('cancel')};$scope.cancel=$scope.close;$scope.lookupPostcode=function(postcode){$scope.postcode.icon='fa fa-spinner fa-spin';$http.post('/clients/postcode',{postcode:postcode}).success(function(res){$scope.postcode.icon='fa fa-crosshairs';angular.extend($scope.data,{address2:res.postcode.street,town:res.postcode.town,county:res.postcode.county})}).error(function(){$scope.postcode.icon='fa fa-crosshairs'})}}]);ClientsModule.controller('ClientCtrl',['$scope','Client','$routeParams','ErrorsHandler','$ClientsManager','apiUrl','$http','Messenger','$location','$uibModal','$window','ClientForm',function($scope,Client,$routeParams,ErrorsHandler,$ClientsManager,apiUrl,$http,Messenger,$location,$uibModal,$window,ClientForm){$scope.data={};$scope.postcode={icon:'fa fa-crosshairs'};$scope.tabs=[{title:'Summary',template:'clients/client-summary.html'},{title:'Repairs',template:'clients/repairs.html'},{title:'Remote Support',template:'clients/support.html'},{title:'Onsite',template:'clients/onsite.html'},{title:'Messages',template:'clients/messages.html'},{title:'Orders',template:'clients/orders.html'},{title:'Sale Leads',template:'clients/sale-leads.html'},{title:'Projects',template:'clients/projects.html'},{title:'Calls',template:'clients/calls.html'},{title:'Invoices',template:'clients/invoices.html'}];$scope.tasks_pie={labels:[],data:[]};$scope.$on('$routeChangeSuccess',function(){if($routeParams.clientId)
$scope.load();else $scope.data={}});$scope.load=function(){Client.get({client:$routeParams.clientId,site_users:!0},function(response){$scope.data=response.data;_.each($scope.data.sites,function(el,i){if(i>0)el.hide=!0});$ClientsManager.setClient(response.data);$ClientsManager.setClientType('client')},ErrorsHandler);$http.get(apiUrl('clients/helpers/client-summary/'+$routeParams.clientId)).success(function(response){$scope.summary=response;$scope.tasks_pie.labels=['Repairs','Support','Onsite'];$scope.tasks_pie.data=[response.count.repairs,response.count.support,response.count.onsite]}).error(ErrorsHandler)};$scope.showSiteModal=function(site){var modalInstance=$uibModal.open({templateUrl:'clients/site.html',controller:'SiteModalCtrl',resolve:{DoNotSave:function(){return!1},client_id:function(){return $scope.data.id},data:function(){return site}}});modalInstance.result.then(function(SiteData){if(!site){$scope.data.sites.push(SiteData)}else{site=angular.extend(site,SiteData||{})}})};$scope.lookupDuplicates=function(value){if(!value)return!1;Client.get({name:value,equal:!0},function(response){$scope.duplicate_data=response},ErrorsHandler)};$scope.showSiteUserModal=function(site,user){var modalInstance=$uibModal.open({templateUrl:'clients/site-user.html',controller:'SiteUserModalCtrl',resolve:{DoNotSave:function(){return!1},site:function(){return site},data:function(){return user}}});modalInstance.result.then(function(UserData){if(!site.users)site.users=[];if(!user){site.users.push(UserData)}else{user=angular.extend(user,UserData||{})}})};$scope.save=function(){Client.update($scope.data,function(response){angular.extend($scope.data,response);Messenger("I've succesfully saved the client!")},ErrorsHandler)};$scope.deleteClient=function(){Client.delete({clientId:$scope.data.id},function(response){Messenger('I have successfully deleted the client!');$location.path('/clients')},ErrorsHandler)};$scope.syncKashflow=function(){$http.get(apiUrl('kashflow/sync-client/client/'+$scope.data.id)).success(function(response){Messenger("The customer details have been successfully stored in Kashflow")}).error(ErrorsHandler)};$scope.subscribe=function(){$http.post(apiUrl('clients/subscribe/'+$scope.data.id)).success(function(response){$scope.data.subscribed=!0;$scope.data.subscribers=response;Messenger("You have successfully subscribed to this client")}).error(ErrorsHandler)};$scope.unsubscribe=function(){$http.post(apiUrl('clients/unsubscribe/'+$scope.data.id)).success(function(response){$scope.data.subscribed=!1;$scope.data.subscribers=response;Messenger("You have successfully unsubscribed from this client")}).error(ErrorsHandler)};$scope.convert=function(){$http.post('/clients/convert-to-customer/'+$scope.data.id).success(function(response){$location.path('/customer/'+response.id)}).error(ErrorsHandler)};$scope.findDuplicates=function(){var modalInstance=$uibModal.open({templateUrl:'clients/modals/duplicates.html',controller:'DuplicatesCtrl',resolve:{currentClient:function(){return $scope.data}}})};$scope.createReport=function(){var modalInstance=$uibModal.open({templateUrl:'clients/modals/report_period.html',controller:'ReportPeriodCtrl',});modalInstance.result.then(function(selectedPeriod){$window.location.href="/clients/client-report/"+$scope.data.id+"?start_date="+selectedPeriod.startDate+"&end_date="+selectedPeriod.endDate})};$scope.submit=function(){if($scope.data.id){Client.update($scope.data,function(response){$scope.data=response.data;Messenger("I've successfully updated the client!");ClientForm.onUpdate(response.data,$scope.data)},ErrorsHandler)}else{Client.save($scope.data,function(response){angular.extend($scope.data,response);Messenger("I've successfully created the client!");ClientForm.onCreate(response)},ErrorsHandler)}};$scope.fillPrimaryPhone=function(){$scope.data.sites[0].primary_phone=$scope.data.sites[0].users[0].mobile};$scope.lookupPostcode=function(postcode){$scope.postcode.icon='fa fa-spinner fa-spin';$http.post('/clients/postcode',{postcode:postcode}).success(function(res){$scope.postcode.icon='fa fa-crosshairs';angular.extend($scope.data.sites[0],{address:res.postcode.street?res.postcode.street:'',address2:res.postcode.town&&res.postcode.county?res.postcode.town+", "+res.postcode.county:''})}).error(function(){$scope.postcode.icon='fa fa-crosshairs'})}}]);ClientsModule.controller('ClientsMainCtrl',['$scope','Client','$routeParams','$location','Customer','ErrorsHandler','Messenger','$http','apiUrl','CustomerForm','$uibModal',function($scope,Client,$routeParams,$location,Customer,ErrorsHandler,Messenger,$http,apiUrl,CustomerForm,$uibModal){if($location.path()==''){$location.path('/clients');return}
$scope.data=[];$scope.activeTab=0;$scope.tabs=[{title:'Customers',template:'clients/customers-table.html'},{title:'Business Clients',template:'clients/clients-table.html'},{title:'Marketing',template:'clients/marketing.html'}];CustomerForm.onCreate=function(data){$scope.customerModal.dismiss();$location.path('/customer/'+data.id)};$scope.$on('$routeChangeSuccess',function(){$scope.loadStats()});$scope.loadStats=function(){$http.get(apiUrl('clients/helpers/overview')).success(function(response){$scope.data=response}).error(ErrorsHandler)};$scope.showClientsWizard=function(){var modalInstance=$uibModal.open({templateUrl:'clients/modals/new-client.html',controller:'AddClientCtrl',size:'lg',backdrop:'static'})};$scope.showCustomerForm=function(){$scope.customerModal=$uibModal.open({templateUrl:'clients/modals/new-customer.html',controller:'AddCustomerCtrl',size:'lg'})}}]);ClientsModule.controller('CustomerCtrl',['$scope','Customer','$routeParams','ErrorsHandler','$ClientsManager','apiUrl','$http','CustomerForm','Messenger','$timeout','$location','$uibModal','$window','$injector',function($scope,Customer,$routeParams,ErrorsHandler,$ClientsManager,apiUrl,$http,CustomerForm,Messenger,$timeout,$location,$uibModal,$window,$injector){$scope.data={};$scope.postcode={icon:'fa fa-crosshairs'};$scope.modalTitle='Add New Customer';$scope.tabs=[{title:'Summary',template:'clients/customer-summary.html'},{title:'Repairs',template:'clients/repairs.html'},{title:'Remote Support',template:'clients/support.html'},{title:'Onsite',template:'clients/onsite.html'},{title:'Messages',template:'clients/messages.html'},{title:'Orders',template:'clients/orders.html'},{title:'Sale Leads',template:'clients/sale-leads.html'},{title:'Projects',template:'clients/projects.html'},{title:'Calls',template:'clients/calls.html'},{title:'Invoices',template:'clients/invoices.html'}];$scope.tasks_pie={labels:[],data:[]};$scope.close=function(){console.log('Close function called');try{if($injector&&$injector.has&&$injector.has('$uibModalInstance')){console.log('Using injector modal instance');var modalInstance=$injector.get('$uibModalInstance');modalInstance.dismiss('cancel');return}
var currentScope=$scope;while(currentScope){if(currentScope.customerModal&&currentScope.customerModal.dismiss){console.log('Found customerModal in scope hierarchy');currentScope.customerModal.dismiss('cancel');return}
currentScope=currentScope.$parent}
if($scope.$root&&$scope.$root.customerModal&&$scope.$root.customerModal.dismiss){console.log('Using root scope modal');$scope.$root.customerModal.dismiss('cancel');return}
console.log('No modal instance found to close')}catch(e){console.error('Error in close function:',e)}};$scope.cancel=$scope.close;$scope.load=function(custId){if(!custId)custId=$routeParams.customerId;Customer.get({customer:custId},function(response){$scope.data=response.data;$ClientsManager.setClient(response.data);$ClientsManager.setClientType('customer')},ErrorsHandler);$http.get(apiUrl('clients/helpers/customer-summary/'+custId)).success(function(response){$scope.summary=response;$scope.tasks_pie.labels=['Repairs','Support','Onsite'];$scope.tasks_pie.data=[response.count.repairs,response.count.support,response.count.onsite]}).error(ErrorsHandler)};if($routeParams.customerId){$scope.modalTitle='Edit Customer';if($ClientsManager.isSelected()&&$ClientsManager.getClient().id==$routeParams.customerId){$scope.data=$ClientsManager.getClient()}else{$scope.load()}}
$scope.$on('$routeChangeSuccess',function(){if($routeParams.customerId){$scope.modalTitle='Edit Customer';if($ClientsManager.isSelected()&&$ClientsManager.getClient().id==$routeParams.customerId){$scope.data=$ClientsManager.getClient()}else{$scope.load()}}else{$scope.modalTitle='Add New Customer';$scope.data={}}});$scope.lookupPostcode=function(postcode){$scope.postcode.icon='fa fa-spinner fa-spin';$http.post('/clients/postcode',{postcode:postcode}).success(function(res){$scope.postcode.icon='fa fa-crosshairs';angular.extend($scope.data,{address2:res.postcode.street,town:res.postcode.town,county:res.postcode.county})}).error(function(){$scope.postcode.icon='fa fa-crosshairs'})};$scope.submit=function(){if($scope.data.id){Customer.update($scope.data,function(response){$scope.data=response.data;Messenger("I've successfully updated the customer!");CustomerForm.onUpdate(response.data)},ErrorsHandler)}else{Customer.save($scope.data,function(response){$scope.data=response;Messenger("I've successfully created the customer!");CustomerForm.onCreate(response.data)},ErrorsHandler)}};$scope.deleteCustomer=function(){Customer.delete({customerId:$scope.data.id},function(response){Messenger('I have succesfully deleted the customer!');$location.path('/clients')},ErrorsHandler)};$scope.syncXero=function(){$http.post(apiUrl('xero/sync-customer/'+$scope.data.id)).success(function(response){$scope.load();Messenger("The customer details have been successfully synced with Xero")}).error(ErrorsHandler)};$scope.convert=function(){$http.post('/clients/convert-to-client/'+$scope.data.id).success(function(response){$location.path('/client/'+response.id)}).error(ErrorsHandler)};$scope.findDuplicates=function(){var modalInstance=$uibModal.open({templateUrl:'clients/modals/duplicates.html',controller:'DuplicatesCtrl',resolve:{currentClient:function(){return $scope.data}}})};$scope.lookupDuplicates=function(value){if(!value)return!1;Customer.get({q:value},function(response){$scope.duplicate_data=response},ErrorsHandler)};$scope.createReport=function(){var modalInstance=$uibModal.open({templateUrl:'clients/modals/report_period.html',controller:'ReportPeriodCtrl',});modalInstance.result.then(function(selectedPeriod){$window.location.href="/clients/customer-report/"+$scope.data.id+"?start_date="+selectedPeriod.startDate+"&end_date="+selectedPeriod.endDate})}}]);ClientsModule.controller('DuplicatesCtrl',['$scope','$uibModalInstance','Customer','Client','ErrorsHandler','$timeout','currentClient','$http','apiUrl',function($scope,$uibModalInstance,Customer,Client,ErrorsHandler,$timeout,currentClient,$http,apiUrl){var search_timeout;$scope.search={type:'client',query:currentClient.name};$scope.data=[];$scope.$watch('search.query',function(value){if(search_timeout)$timeout.cancel(search_timeout);search_timeout=$timeout(function(){$scope.load()},300)});$scope.$watch('search.type',function(){$scope.load()});$scope.load=function(){$scope.loading=!0;var model=($scope.search.type=='customer')?Customer:Client;model.get({name:$scope.search.query},function(response){$scope.clients=_.reject(response.data,function(el){return el.id==currentClient.id});$scope.loading=!1},ErrorsHandler)};$scope.select=function(item){if($scope.exists(item))return!1;$scope.data.push(item)};$scope.remove=function(item){$scope.data=_.reject($scope.data,function(el){return el.id==item.id})};$scope.exists=function(item){var client=_.find($scope.data,function(el){return el.id==item.id});return(client)?!0:!1};$scope.merge=function(){$http.post(apiUrl('clients/helpers/merge-accounts'),{data:$scope.data,client:currentClient}).success(function(response){$uibModalInstance.close(response)}).error(ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);ClientsModule.controller('EditClientCtrl',['$scope','$uibModalInstance','client','site','site_user','Client','Site','SiteUser','ErrorsHandler','$http',function($scope,$uibModalInstance,client,site,site_user,Client,Site,SiteUser,ErrorsHandler,$http){$scope.data=angular.copy(client);$scope.site=site;$scope.site_user=site_user;$scope.postcode={icon:'fa fa-crosshairs'};$scope.$watch('data.contract_id',function(value){$scope.data.contract_id=$scope.data.contract_id.toString()});$scope.$watch('data.sla_id',function(value){$scope.data.sla_id=$scope.data.sla_id.toString()});$scope.save=function(){Client.update($scope.data,function(client_response){angular.extend(client,client_response||{});Site.update($scope.site,function(site_response){angular.extend(site,site_response||{});SiteUser.update($scope.site_user,function(user_response){angular.extend(site_user,user_response||{});$uibModalInstance.close()},ErrorsHandler)},ErrorsHandler)},ErrorsHandler)};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')};$scope.lookupPostcode=function(postcode){$scope.postcode.icon='fa fa-spinner fa-spin';$http.post('/clients/postcode',{postcode:postcode}).success(function(res){$scope.postcode.icon='fa fa-crosshairs';angular.extend($scope.site,{address:res.postcode.street,address2:res.postcode.town})}).error(function(){$scope.postcode.icon='fa fa-crosshairs'})}}]);ClientsModule.controller('EditCustomerCtrl',['$scope','$uibModalInstance','client','Customer','ErrorsHandler','$http',function($scope,$uibModalInstance,client,Customer,ErrorsHandler,$http){$scope.data=client;$scope.service={loading:!1,isBusy:!1};$scope.postcode={icon:'fa fa-crosshairs'};$scope.save=function(){$scope.service.loading=!0;$scope.service.isBusy=!0;Customer.update($scope.data,function(response){$scope.service.loading=!1;$scope.service.isBusy=!1;$uibModalInstance.close(response.data)},function(error){$scope.service.loading=!1;$scope.service.isBusy=!1;ErrorsHandler(error)})};$scope.close=function(){$uibModalInstance.dismiss('cancel')};$scope.cancel=$scope.close;$scope.lookupPostcode=function(postcode){$scope.postcode.icon='fa fa-spinner fa-spin';$http.post('/clients/postcode',{postcode:postcode}).success(function(res){$scope.postcode.icon='fa fa-crosshairs';angular.extend($scope.data,{address2:res.postcode.street,town:res.postcode.town,county:res.postcode.county})}).error(function(){$scope.postcode.icon='fa fa-crosshairs'})}}]);ClientsModule.controller('KeychainModalCtrl',['$scope','Customer','$http','apiUrl','$q','ErrorsHandler','$uibModalInstance','Device','Keychain','TaskId','PasswordsService','Tasks','Messenger',function($scope,Customer,$http,apiUrl,$q,ErrorsHandler,$uibModalInstance,Device,Keychain,TaskId,PasswordsService,Tasks,Messenger){$scope.new={type:'windows',device_id:null,username:'',password:''};$scope.loadPasswords=function(){Keychain.get({client_id:Customer.id,client_type:Customer.type},function(response){$scope.keychain=response.data})};$scope.load=function(){$scope.loadPasswords();Device.get({client_type:Customer.type,client_id:Customer.id},function(response){$scope.client_devices=response.data})};$scope.load();$scope.createPassword=function(){if(!$scope.isValidForm())
return;$scope.new.client_id=Customer.id;$scope.new.client_type=Customer.type;if($scope.new.device_id===''||$scope.new.device_id==='null'){$scope.new.device_id=null}
console.log('Creating password with data:',$scope.new);Keychain.save($scope.new,function(response){console.log('Password creation response:',response);$scope.loadPasswords();$scope.new={type:'windows',device_id:null,username:'',password:''}})};$scope.update=function(password,field,value){var data={},deferred=$q.defer();data[field]=value;$http.post(apiUrl('keychain/'+password.id),data).success(function(response){deferred.resolve();angular.extend(password,response||{})}).error(function(a,b){ErrorsHandler(a,b);deferred.resolve('Something went wrong')});return deferred.promise};$scope.isValidForm=function(){if(!$scope.new.username)
return!1;if(!$scope.new.password)
return!1;return!0};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')};$scope.getDeviceName=function(id){var name='General';if(id!=null&&id>0){_.each($scope.client_devices,function(device){if(device.id==id){if(device.model_number&&device.model_number.manufacturer&&device.model_number.manufacturer.name&&device.model_number.model_number){name=device.model_number.manufacturer.name+" "+device.model_number.model_number}else if(device.model_number&&device.model_number.model_number){name=device.model_number.model_number}else{name=device.type||('Device '+id)}}})}
return name};$scope.addPasswordToTask=function(password){Tasks.addKeychain({id:TaskId,keychain_id:password.id},function(){Messenger("The password has been added to the task!");Keychain.get({task_id:TaskId},function(response){PasswordsService.setPasswords(response.data)})},ErrorsHandler)};$scope.disablePassword=function(password){Keychain.delete({keychainId:password.id,disabled:!0},function(){$scope.loadPasswords();Keychain.get({task_id:TaskId},function(response){PasswordsService.setPasswords(response.data)})})};$scope.copyToClipboard=function(text){if(!text)return;var textarea=document.createElement('textarea');textarea.value=text;textarea.style.position='fixed';textarea.style.opacity='0';document.body.appendChild(textarea);textarea.select();try{document.execCommand('copy');Messenger("Password copied to clipboard!")}catch(err){Messenger("Failed to copy password","error")}
document.body.removeChild(textarea)}}]);ClientsModule.controller('LicensesModalCtrl',['$scope','Customer','License','$q','ErrorsHandler','$uibModalInstance',function($scope,Customer,License,$q,ErrorsHandler,$uibModalInstance){$scope.new={};$scope.load=function(){License.get({client_id:Customer.id,client_type:Customer.type},function(response){$scope.licenses=response.data},ErrorsHandler)};$scope.load();$scope.add=function(){$scope.new.client_type=Customer.type;$scope.new.client_id=Customer.id;License.save($scope.new,function(response){$scope.licenses.push(response)},ErrorsHandler)};$scope.update=function(license,field,value){var data={id:license.id},deferred=$q.defer();data[field]=value;License.update(data,function(response){deferred.resolve();angular.extend(license,response||{})},function(a,b){ErrorsHandler(a,b);deferred.resolve('Something went wrong')});return deferred.promise};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);ClientsModule.controller('MarketingCtrl',['$scope','Marketing','ErrorsHandler','Messenger','$window',function($scope,Marketing,ErrorsHandler,Messenger,$window){$scope.total_reach=0;$scope.filters={};$scope.$watch('filters',function(value){$scope.loadReach()},!0);$scope.$watch('inactive_since',function(value){$scope.filters.inactive_since=(value)?moment(value).format("YYYY-MM-DD 00:00:00"):null});$scope.loadReach=function(){Marketing.reach($scope.filters,function(response){$scope.total_reach=response.total},ErrorsHandler)};$scope.preview=function(){Marketing.preview($scope.email,function(response){Messenger(response.message);alert(response.message)},ErrorsHandler)};$scope.export=function(){$scope.exporting=!0;Marketing.export($scope.filters,function(response){if(response.file)$window.location=response.file;$scope.exporting=!1},function(a,b){$scope.exporting=!1;ErrorsHandler(a,b)})}}]);ClientsModule.controller('ReportPeriodCtrl',['$scope','$uibModalInstance',function($scope,$uibModalInstance){$scope.helpers={dates:{startDate:moment().subtract('months',1),endDate:moment()}};$scope.ranges={'This month':[moment(),moment()]};$scope.ok=function(){$uibModalInstance.close({startDate:$scope.helpers.dates.startDate.format('YYYY-MM-DD'),endDate:$scope.helpers.dates.endDate.format('YYYY-MM-DD')})};$scope.cancel=function(){$uibModalInstance.close({startDate:'',endDate:''})};$scope.isValidPeriod=function(){return!0;if($scope.dates.startDate&&$scope.dates.endDate&&$scope.dates.startDate<$scope.dates.endDate)
return!0;return!1}}]);ClientsModule.controller('SiteModalCtrl',['$scope','$uibModalInstance','data','DoNotSave','Site','ErrorsHandler','client_id',function($scope,$uibModalInstance,data,DoNotSave,Site,ErrorsHandler,client_id){$scope.data=data||{};$scope.data.client_id=client_id;$scope.submit=function(){if(!DoNotSave){if($scope.data.id){Site.update($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}else{Site.save($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}}else{$uibModalInstance.close($scope.data)}};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);ClientsModule.controller('SiteUserModalCtrl',['$scope','$uibModalInstance','data','DoNotSave','SiteUser','ErrorsHandler','site',function($scope,$uibModalInstance,data,DoNotSave,SiteUser,ErrorsHandler,site){$scope.data=data||{authority:0};$scope.data.site_id=site.id;$scope.submit=function(){if(!DoNotSave){if($scope.data.id){SiteUser.update($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}else{SiteUser.save($scope.data,function(response){$uibModalInstance.close(response)},ErrorsHandler)}}else{$uibModalInstance.close($scope.data)}};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);ClientsModule.controller('SubscriptionsCtrl',['$scope','$http','apiUrl','uiGridConstants',function($scope,$http,apiUrl,uiGridConstants){var myAwesomeSortFn=function(a,b){if(a.severity_id==b.severity_id)return 0;if(a.severity_id<b.severity_id)return-1;return 1};$scope.subscriptions=[];$scope.load=function(){$http.get(apiUrl('clients/subscriptions/')).success(function(response){$scope.gridOptions.data=response})};$scope.columns=[{name:'ID',field:'id',cellTemplate:'<div class="ui-grid-cell-contents"><a href="/tasks/{[{ row.entity | taskUrl }]}">{[{grid.getCellValue(row,col)}]}</a></div>'},{field:'title'},{name:'client',field:'client.name'},{name:'contractType',field:'client.contract.name'},{name:'status',field:'status.name'},{name:'severity',field:'severity.name',sort:{direction:uiGridConstants.ASC,priority:0}},{name:'Type',field:'type'},{name:'assignedTo',field:'user.first_name'},{name:'created',field:'created_at',cellTemplate:'<div class="ui-grid-cell-contents"> {[{ grid.getCellValue(row, col) | fromNow }]} </div>',sort:{direction:uiGridConstants.ASC,priority:1}}];$scope.gridOptions={enableHorizontalScrollbar:0,enableVerticalScrollbar:2,enableSorting:!0,columnDefs:$scope.columns,onRegisterApi:function(gridApi){$scope.gridApi=gridApi}};$scope.load()}]);Invoices.controller("InvoiceEmailModalCtrl",["$scope","$uibModalInstance","data","client","$http","apiUrl","ErrorsHandler","Messenger",function($scope,$uibModalInstance,data,client,$http,apiUrl,ErrorsHandler,Messenger){var paid_text=data.paid=="1"?"":"Payment is now due.";var type;switch(data.type){case "invoice":type="invoice";break;case "quote":type="quote";break;default:type="sale receipt"}
$scope.data={invoice_id:data.id,invoice_number:data.invoice_number||("INV-"+data.id),send_to:client.data.email||client.data.Email||'',subject:"Your latest "+type+" from Wiseserve Ltd (Computer Assistance)",message:"Dear "+((data.client&&data.client.name)||(client.data&&client.data.name)||"Customer")+" : \n\n"+"Please find attached your latest "+type+" for work we recently completed.  "+paid_text+" \n\n"+"We appreciate your business and if you have any further issues, questions or queries, we are always here to help. \n\n"+"If you appreciated our customer service and support, we'd be really grateful if you left a kind review at https://tinyurl.com/y22tn73n \n\n"+"Computer Assistance \n"+"01865 451177"};$scope.sending=!1;$scope.previewInvoice=function(){var invoiceId=data.invoice_number||data.id;var pdfUrl="/generate-invoice/"+invoiceId;window.open(pdfUrl,"_blank")};$scope.send=function(){$scope.sending=!0;if(typeof Messenger!=='undefined'){Messenger("Sending email...")}
$http.post(apiUrl("invoices/email-invoice"),$scope.data).success(function(response){$scope.sending=!1;if(typeof Messenger!=='undefined'){Messenger("Email sent successfully!")}
$uibModalInstance.close('success')}).error(function(error,status){$scope.sending=!1;var errorMessage=error&&error.error?error.error:'Failed to send email';if(typeof Messenger!=='undefined'){Messenger("Email failed: "+errorMessage)}
ErrorsHandler(error,status)})};$scope.cancel=function(){$uibModalInstance.dismiss("cancel")}}]);Invoices.controller('InvoiceOnlinePaymentModalCtrl',['$scope','data','InvoiceTransaction','ErrorsHandler','$uibModalInstance',function($scope,data,InvoiceTransaction,ErrorsHandler,$uibModalInstance){var thisYear=parseInt(moment().format("YYYY"),10);$scope.data={amount:(data.amount_due>0)?data.amount_due:0,invoice_id:data.id,method_of_payment:'card',notes:'Stripe - Online Card Payment',exp_month:'01',exp_year:thisYear};$scope.months=['01','02','03','04','05','06','07','08','09','10','11','12'];$scope.years=[];for(var i=thisYear;i<thisYear+10;i++){$scope.years.push(i)};$scope.save=function(){$scope.data.payment_date=moment().format("YYYY-MM-DD");$scope.loading=!0;InvoiceTransaction.onlinePayment($scope.data,function(response){angular.extend(data,response.invoice);data.transactions.push(response);$uibModalInstance.close(response);$scope.loading=!1},function(a,b){$scope.loading=!1;ErrorsHandler(a,b)})};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Invoices.controller('InvoicePaymentModalCtrl',['$scope','$uibModalInstance','data','type','InvoiceTransaction','ErrorsHandler',function($scope,$uibModalInstance,data,type,InvoiceTransaction,ErrorsHandler){$scope.data={amount:(data.amount_due>0)?data.amount_due:0,invoice_id:data.id,method_of_payment:type,date:new Date(),};$scope.data.notes='Seraph3 sales receipt paid by '+$scope.data.method_of_payment;$scope.save=function(){if($scope.saving)return!1;$scope.data.payment_date=moment($scope.data.date).format("YYYY-MM-DD");$scope.saving=!0;InvoiceTransaction.save($scope.data,function(response){angular.extend(data,response.invoice);data.transactions.push(response);$uibModalInstance.close(response);$scope.saving=!1},function(a,b){$scope.saving=!1;ErrorsHandler(a,b)})};$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Invoices.controller('InvoicesBrowseCtrl',['$scope','Invoice','ErrorsHandler','$filter','$location','$paginator','$timeout','InvoiceService',function($scope,Invoice,ErrorsHandler,$filter,$location,$paginator,$timeout,InvoiceService){$scope.data=[];$scope.title='Browse Invoices';$paginator=angular.copy($paginator);$scope.paginator=$paginator;$scope.filters={};$scope.service=InvoiceService.create(Invoice);var search_timeout;$paginator.setOnPageChange(function(page){$scope.loadData(page)});$scope.$watch('filters',function(value){if(search_timeout)$timeout.cancel(search_timeout);search_timeout=$timeout(function(){$scope.loadData()},100)},!0);$scope.loadInvoice=function(invoice){$location.path('/invoices/view/'+invoice.id+'/')};$scope.loadData=function(page){if(!page)page=1;var prepared_filter=angular.copy($scope.filters);angular.extend(prepared_filter,{page:page,type:'invoice',client:!0});if($scope.filters.client){prepared_filter.client_id=$scope.filters.client.id;prepared_filter.client_type=$scope.filters.client.type}
Invoice.get(prepared_filter,function(response){$scope.data=response.data;$scope.data.forEach(function(invoice){if(typeof invoice.xero_syncing==='undefined'){invoice.xero_syncing=!1}});$paginator.setFromResponse(response)},ErrorsHandler)};$scope.sendToXero=function(invoice){invoice.xero_syncing=!0;Invoice.sendToXero({id:invoice.id},function(response){$scope.loadData($scope.paginator.getCurrentPage());Messenger().post({message:'Invoice successfully sent to Xero',type:'success',showCloseButton:!0})},function(error){invoice.xero_sync_error=error.data.message||'Failed to send to Xero';invoice.xero_syncing=!1;Messenger().post({message:'Failed to send invoice to Xero: '+(error.data.message||'Unknown error'),type:'error',showCloseButton:!0})})}}]);Invoices.controller('InvoicesMainCtrl',['$scope','$location',function($scope,$location){if(!$location.path()||$location.path()=='/invoices')
$location.path('sales/new');}]);Invoices.controller('InvoicesNewCtrl',['$scope','Invoice','Tasks','ErrorsHandler','$routeParams','$location','InvoiceService','$window',function($scope,Invoice,Tasks,ErrorsHandler,$routeParams,$location,InvoiceService,$window){$scope.title='Invoices';$scope.type='invoice';$scope.service=InvoiceService.create(Invoice);$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){if($routeParams.taskId){if($scope.labourTypesLoaded==!0){$scope.service.loadTask($routeParams.taskId)}else{$scope.$watch('service.labourTypesLoaded',function(newVal,oldVal,scopeSent){if(newVal==!0){$scope.service.loadTask($routeParams.taskId)}})}}else if($routeParams.projectId){if($scope.labourTypesLoaded==!0){$scope.service.loadProject($routeParams.projectId)}else{$scope.$watch('service.labourTypesLoaded',function(newVal,oldVal,scopeSent){if(newVal==!0){$scope.service.loadProject($routeParams.projectId)}})}}});$scope.$watch('service.data.items',function(){$scope.service.calculateTotals()},!0);$scope.$watch('service.data.include_vat',function(){$scope.service.calculateTotals()});$scope.reset=function(){$scope.service.data.items=[{type:$scope.service.types[0].value}];$scope.service.data.id=null};$scope.save=function(print,email){if(!$scope.service.data){return}
var errors=[];if(!$scope.service.client_search){errors.push("Please select a customer")}
var hasItems=!1;_.each($scope.service.data.items,function(item){if(item.name&&item.quantity){hasItems=!0}});if(!hasItems){errors.push("Please add at least one item")}
if(errors.length>0){if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:errors.join("\n"),type:"error",showCloseButton:!0})}
return!1}
$scope.service.loading=!0;var request_data=angular.copy($scope.service.data);_.each($scope.service.data.items,function(item,index){if(!item.name){$scope.service.data.items.splice(index,1)}});request_data.client_type=$scope.service.client_search.type;request_data.client_id=$scope.service.client_search.id;request_data.total_price=$scope.service.total;request_data.invoice_date=moment(request_data.invoice_date).format("X");if(print)request_data.print=!0;if($scope.service.data.id){Invoice.update(request_data,function(res){angular.extend($scope.service.data,res||{});$scope.service.data.invoice_date=moment(res.invoice_date).toDate();$scope.service.loading=!1;if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:"Invoice was successfully updated!",type:"success",showCloseButton:!0})}
if(print)
$window.open(res.print_url,'_blank');if(email)
$scope.service.showEmailModal();},function(a,b){$scope.service.loading=!1;ErrorsHandler(a,b)})}else{Invoice.save(request_data,function(res){angular.extend($scope.service.data,res||{});$scope.service.data.invoice_date=moment(res.invoice_date).toDate();$scope.service.loading=!1;var type=$scope.service.data.type==='sale'?'Sale':'Invoice';if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:type+" created successfully!",type:"success",showCloseButton:!0})}
if(print)
$window.open(res.print_url,'_blank');if(email){$scope.service.showEmailModal($scope.service.data)}else{$location.path(res.type+'s/view/'+res.id)}},function(response){$scope.service.loading=!1;if(response.status===422){if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:response.data.error||"Validation failed",type:"error",showCloseButton:!0})}}else{if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:response.data.error||"An error occurred while saving the invoice",type:"error",showCloseButton:!0})}
ErrorsHandler(response)}})}}}]);Invoices.controller('InvoicesSyncComparisonCtrl',['$scope','$uibModalInstance','Data',function($scope,$uibModalInstance,Data){$scope.data=Data;$scope.cancel=function(){$uibModalInstance.dismiss('cancel')}}]);Invoices.controller('InvoicesViewCtrl',['$scope','Invoice','Tasks','ErrorsHandler','$routeParams','$location','InvoiceService','$window',function($scope,Invoice,Tasks,ErrorsHandler,$routeParams,$location,InvoiceService,$window){$scope.data={};$scope.title='Invoices';$scope.type='invoice';$scope.service=InvoiceService.create(Invoice);$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){if($routeParams.invoiceId){$scope.loadInvoice($routeParams.invoiceId)}});$scope.$watch('service.data.items',function(){$scope.service.calculateTotals()},!0);$scope.$watch('service.data.include_vat',function(){$scope.service.calculateTotals()});$scope.loadInvoice=function(invoice_id){Invoice.get({'invoice':invoice_id,client:!0},function(response){$scope.service.data=response;$scope.service.data.invoice_date=moment(response.invoice_date).toDate();$scope.service.data.include_vat=(response.include_vat=='1')?!0:!1;$scope.service.client_search=response.client})};$scope.reset=function(){$location.path('invoices/new/')};$scope.sendToXero=function(){if(!$scope.service.data||!$scope.service.data.id){return}
$scope.service.data.xero_syncing=!0;Invoice.sendToXero({id:$scope.service.data.id},function(response){if(response.invoice){angular.extend($scope.service.data,response.invoice)}else{$scope.service.data.xeroId=response.xero_invoice_id;$scope.service.data.xero_synced_at=new Date()}
$scope.service.data.xero_sync_error=null;$scope.service.data.xero_syncing=!1;Messenger().post({message:'Invoice successfully sent to Xero',type:'success',showCloseButton:!0})},function(error){$scope.service.data.xero_sync_error=error.data.message||'Failed to send to Xero';$scope.service.data.xero_syncing=!1;Messenger().post({message:'Failed to send invoice to Xero: '+(error.data.message||'Unknown error'),type:'error',showCloseButton:!0})})};$scope.save=function(print,email){if(!$scope.service.data){return}
var errors=[];if(!$scope.service.client_search){errors.push("Please select a customer")}
var hasItems=!1;_.each($scope.service.data.items,function(item){if(item.name&&item.quantity){hasItems=!0}});if(!hasItems){errors.push("Please add at least one item")}
if(errors.length>0){if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:errors.join("\n"),type:"error",showCloseButton:!0})}
return!1}
$scope.service.loading=!0;var request_data=angular.copy($scope.service.data);_.each($scope.service.data.items,function(item,index){if(!item.name){$scope.service.data.items.splice(index,1)}});request_data.client_type=$scope.service.client_search.type;request_data.client_id=$scope.service.client_search.id;request_data.total_price=$scope.service.total;request_data.invoice_date=moment(request_data.invoice_date).format("X");if(print)request_data.print=!0;if($scope.service.data.id){Invoice.update(request_data,function(res){angular.extend($scope.service.data,res||{});$scope.service.data.invoice_date=moment(res.invoice_date).toDate();$scope.service.loading=!1;if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:"Invoice was successfully updated!",type:"success",showCloseButton:!0})}
if(print)
$window.open(res.print_url,'_blank');if(email)
$scope.service.showEmailModal();},function(a,b){$scope.service.loading=!1;ErrorsHandler(a,b)})}else{Invoice.save(request_data,function(res){angular.extend($scope.service.data,res||{});$scope.service.data.invoice_date=moment(res.invoice_date).toDate();$scope.service.loading=!1;var type=$scope.service.data.type==='sale'?'Sale':'Invoice';if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:type+" created successfully!",type:"success",showCloseButton:!0})}
if(print)
$window.open(res.print_url,'_blank');if(email)
$scope.service.showEmailModal($scope.service.data);$location.path(res.type+'s/view/'+res.id)},function(response){$scope.service.loading=!1;if(response.status===422){if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:response.data.error||"Validation failed",type:"error",showCloseButton:!0})}}else{if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:response.data.error||"An error occurred while saving the invoice",type:"error",showCloseButton:!0})}
ErrorsHandler(response)}})}}}]);Invoices.controller('KashflowSyncCtrl',['$scope','$http','apiUrl','ErrorsHandler','$uibModal',function($scope,$http,apiUrl,ErrorsHandler,$uibModal){$scope.loading=!1;$scope.load=function(){if($scope.loading)return!1;$scope.loading=!0;$scope.sync_failed=null;$scope.data=null;$http.get(apiUrl('kashflow/changes')).success(function(response){$scope.loading=!1;$scope.data=response}).error(function(a,b){$scope.loading=!1;ErrorsHandler(a,b)})};$scope.check=function(invoices){_.each(invoices,function(el){el.checked=!el.checked})};$scope.sync=function(){if($scope.syncing)return!1;$scope.syncing=!0;$http.post(apiUrl('kashflow/sync-changes'),$scope.data).success(function(response){$scope.syncing=!1;$scope.sync_results=response;$scope.sync_failed={changed:_.countBy(response.changes,function(el){return el.success?'success':'failed'}),missingKashflow:_.countBy(response.missingKashflow,function(el){return el.success?'success':'failed'}),missingLocal:_.countBy(response.missingLocal,function(el){return el.success?'success':'failed'})};$scope.applySyncResults()}).error(function(a,b){$scope.syncing=!1;ErrorsHandler(a,b)})};$scope.applySyncResults=function(){_.each($scope.sync_results,function(result,index){_.each(result,function(el){var item=_.find($scope.data[index],function(i){if(index=='changed')
return i.local.invoice_number==el.invoice_number;else if(index=='missingLocal')
return i.InvoiceNumber==el.invoice_number;else if(index=='missingKashflow')
return i.invoice_number==el.invoice_number});if(item){item.checked=!1;item.sync_result={success:el.success,message:el.error}}})})};$scope.showComparison=function(data){var modalInstance=$uibModal.open({templateUrl:'kashflow/modals/sync-comparison.html',controller:'InvoicesSyncComparisonCtrl',windowClass:'modal-fixed',resolve:{Data:function(){return data}}})}}]);Invoices.controller('PurchaseOrdersNewCtrl',['$scope','PurchaseOrder','Tasks','$ClientsManager','ErrorsHandler','$routeParams','$location','InvoiceService',function($scope,PurchaseOrder,Tasks,$ClientsManager,ErrorsHandler,$routeParams,$location,InvoiceService){$scope.clientsManager=$ClientsManager;$scope.title='Purchase Orders';$scope.service=InvoiceService.create(PurchaseOrder);$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){if($routeParams.taskId){$scope.loadTask($routeParams.taskId)}});$scope.$watch('service.data.items',function(){$scope.service.calculateTotals()},!0);$scope.$watch('service.data.include_vat',function(){$scope.service.calculateTotals()});$scope.reset=function(){$scope.service.data.items=[{type:$scope.service.types[0].value}];$scope.service.data.id=null}}]);Invoices.controller('QuotesBrowseCtrl',['$scope','Quote','ErrorsHandler','$filter','$location','$paginator','$timeout','InvoiceService',function($scope,Quote,ErrorsHandler,$filter,$location,$paginator,$timeout,InvoiceService){$scope.data=[];$scope.title='Browse quotes';$paginator=angular.copy($paginator);$scope.paginator=$paginator;$scope.filters={};$scope.service=InvoiceService.create(Quote);var search_timeout;$paginator.setOnPageChange(function(page){$scope.loadData(page)});$scope.$watch('filters',function(value){if(search_timeout)$timeout.cancel(search_timeout);search_timeout=$timeout(function(){$scope.loadData()},100)},!0);$scope.loadInvoice=function(sale){$location.path('/quotes/view/'+sale.id+'/')};$scope.loadData=function(page){if(!page)page=1;var prepared_filter=angular.copy($scope.filters);angular.extend(prepared_filter,{page:page,type:'quote',client:!0});if($scope.filters.client){prepared_filter.client_id=$scope.filters.client.id;prepared_filter.client_type=$scope.filters.client.type}
Quote.get(prepared_filter,function(response){$scope.data=response.data;$paginator.setFromResponse(response)},ErrorsHandler)}}]);Invoices.controller('QuotesNewCtrl',['$scope','Quote','Tasks','ErrorsHandler','$routeParams','$location','InvoiceService',function($scope,Quote,Tasks,ErrorsHandler,$routeParams,$location,InvoiceService){$scope.title='Quotes';$scope.type='quote';$scope.service=InvoiceService.create(Quote);$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){if($routeParams.taskId){$scope.loadTask($routeParams.taskId)}});$scope.$watch('service.data.items',function(){$scope.service.calculateTotals()},!0);$scope.$watch('service.data.include_vat',function(){$scope.service.calculateTotals()});$scope.reset=function(){$scope.service.data.items=[{type:$scope.service.types[0].value}];$scope.service.data.id=null}}]);Invoices.controller('QuotesViewCtrl',['$scope','Quote','Tasks','ErrorsHandler','$routeParams','$location','InvoiceService',function($scope,Quote,Tasks,ErrorsHandler,$routeParams,$location,InvoiceService){$scope.title='Quotes';$scope.type='quote';$scope.service=InvoiceService.create(Quote);$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){if($routeParams.quoteId){$scope.loadQuote($routeParams.quoteId)}});$scope.$watch('service.data.items',function(){$scope.service.calculateTotals()},!0);$scope.$watch('service.data.include_vat',function(){$scope.service.calculateTotals()});$scope.loadQuote=function(quote_id){Quote.get({quote:quote_id,client:!0},function(response){$scope.service.data=response;$scope.service.data.invoice_date=moment(response.invoice_date).toDate();$scope.service.data.include_vat=(response.include_vat=='1')?!0:!1;$scope.service.client_search=response.client})};$scope.reset=function(){$location.path('quotes/new/')};$scope.convertToInvoice=function(){Quote.convertToInvoice($scope.service.data,function(response){$location.path('invoices/view/'+response.id)},ErrorsHandler)}}]);Invoices.controller('SalesBrowseCtrl',['$scope','Sale','ErrorsHandler','$filter','$location','$paginator','$timeout','InvoiceService',function($scope,Sale,ErrorsHandler,$filter,$location,$paginator,$timeout,InvoiceService){$scope.data=[];$scope.title='Browse sales';$paginator=angular.copy($paginator);$scope.paginator=$paginator;$scope.filters={};$scope.service=InvoiceService.create(Sale);var search_timeout;$paginator.setOnPageChange(function(page){$scope.loadData(page)});$scope.$watch('filters',function(value){if(search_timeout)$timeout.cancel(search_timeout);search_timeout=$timeout(function(){$scope.loadData()},100)},!0);$scope.loadInvoice=function(sale){$location.path('/sales/view/'+sale.id+'/')};$scope.loadData=function(page){if(!page)page=1;var prepared_filter=angular.copy($scope.filters);angular.extend(prepared_filter,{page:page,type:'sale',client:!0});if($scope.filters.client){prepared_filter.client_id=$scope.filters.client.id;prepared_filter.client_type=$scope.filters.client.type}
Sale.get(prepared_filter,function(response){$scope.data=response.data;$scope.data.forEach(function(sale){if(typeof sale.xero_syncing==='undefined'){sale.xero_syncing=!1}});$paginator.setFromResponse(response)},ErrorsHandler)};$scope.sendToXero=function(sale){sale.xero_syncing=!0;Sale.sendToXero({id:sale.id},function(response){$scope.loadData($scope.paginator.getCurrentPage());Messenger().post({message:'Sale successfully sent to Xero',type:'success',showCloseButton:!0})},function(error){sale.xero_sync_error=error.data.message||'Failed to send to Xero';sale.xero_syncing=!1;Messenger().post({message:'Failed to send sale to Xero: '+(error.data.message||'Unknown error'),type:'error',showCloseButton:!0})})}}]);Invoices.controller('SalesNewCtrl',['$scope','Sale','Tasks','ErrorsHandler','$routeParams','$location','InvoiceService','$window',function($scope,Sale,Tasks,ErrorsHandler,$routeParams,$location,InvoiceService,$window){$scope.title='Sale Receipts';$scope.type='sale';$scope.service=InvoiceService.create(Sale);$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){if($routeParams.taskId){if($scope.labourTypesLoaded==!0){if($location.path().indexOf('booking')!=-1){$scope.loadBookingFee()}else{$scope.service.loadTask($routeParams.taskId)}}else{if($location.path().indexOf('booking')!=-1){$scope.$watch('service.labourTypesLoaded',function(newVal,oldVal,scopeSent){if(newVal==!0){$scope.loadBookingFee()}})}else{$scope.$watch('scope.labourTypesLoaded',function(newVal,oldVal,scopeSent){if(newVal==!0){$scope.service.loadTask($routeParams.taskId)}})}}}});$scope.$watch('service.data.items',function(){$scope.service.calculateTotals()},!0);$scope.$watch('service.data.include_vat',function(){$scope.service.calculateTotals()});$scope.loadBookingFee=function(){$scope.service.data.items=[];$scope.service.data.booking_fee=$routeParams.taskId;$scope.service.data.items.push({type:$scope.service.types[$scope.service.bookingFeeTypeIndex].value,name:'Booking fee (REF: '+$routeParams.taskId+')',price_sold_exc:28,quantity:1});Tasks.get({taskId:$routeParams.taskId,client:!0},function(response){if(response.client){$scope.service.client_search=response.client}},ErrorsHandler)}
$scope.reset=function(){$scope.service.data.items=[{type:$scope.service.types[0].value}];$scope.service.data.id=null};$scope.save=function(print,email){if(!$scope.service.data){return}
var errors=[];if(!$scope.service.client_search){errors.push("Please select a customer")}
if($scope.service.data.type==='sale'&&!$scope.service.data.method_of_payment){errors.push("Please select a method of payment")}
var hasItems=!1;_.each($scope.service.data.items,function(item){if(item.name&&item.quantity){hasItems=!0}});if(!hasItems){errors.push("Please add at least one item")}
if(errors.length>0){if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:errors.join("\n"),type:"error",showCloseButton:!0})}
return!1}
$scope.service.loading=!0;var request_data=angular.copy($scope.service.data);if(request_data.items){request_data.items=_.filter(request_data.items,function(item){return item.name&&item.quantity})}
if($scope.service.client_search){request_data.client_id=$scope.service.client_search.id;request_data.client_type=$scope.service.client_search.type}
if($scope.service.data.id){Sale.update(request_data,function(res){angular.extend($scope.service.data,res||{});$scope.service.data.invoice_date=moment(res.invoice_date).toDate();$scope.service.loading=!1;var type=$scope.service.data.type==='sale'?'Sale':'Invoice';if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:type+" updated successfully!",type:"success",showCloseButton:!0})}
if(print)
$window.open(res.print_url,'_blank');if(email)
$scope.service.showEmailModal($scope.service.data);},function(a,b){$scope.service.loading=!1;if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:"An error occurred while updating the sale",type:"error",showCloseButton:!0})}
ErrorsHandler(a,b)})}else{Sale.save(request_data,function(res){angular.extend($scope.service.data,res||{});$scope.service.data.invoice_date=moment(res.invoice_date).toDate();$scope.service.loading=!1;var type=$scope.service.data.type==='sale'?'Sale':'Invoice';if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:type+" created successfully!",type:"success",showCloseButton:!0})}
if(print)
$window.open(res.print_url,'_blank');if(email){$scope.service.showEmailModal($scope.service.data)}else{$location.path(res.type+'s/view/'+res.id)}},function(response){$scope.service.loading=!1;if(response.status===422){if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:response.data.error||"Validation failed",type:"error",showCloseButton:!0})}}else{if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:response.data.error||"An error occurred while saving the sale",type:"error",showCloseButton:!0})}
ErrorsHandler(response)}})}};$scope.sendToXero=function(){if(!$scope.service.data||!$scope.service.data.id){if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:"Please save the sale first before syncing to Xero",type:"error",showCloseButton:!0})}
return}
$scope.service.data.xero_syncing=!0;if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:"Sending to Xero...",type:"info",showCloseButton:!0})}
Sale.sendToXero({id:$scope.service.data.id},function(xeroRes){$scope.service.data.xero_syncing=!1;if(xeroRes.invoice){angular.extend($scope.service.data,xeroRes.invoice)}else{$scope.service.data.xeroId=xeroRes.xero_invoice_id;$scope.service.data.xero_synced_at=new Date()}
if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:"Successfully synced with Xero!",type:"success",showCloseButton:!0})}},function(error){$scope.service.data.xero_syncing=!1;$scope.service.data.xero_sync_error=error.data.message||'Failed to sync with Xero';if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:"Failed to sync with Xero: "+$scope.service.data.xero_sync_error,type:"error",showCloseButton:!0})}})}}]);Invoices.controller('SalesViewCtrl',['$scope','Sale','Tasks','ErrorsHandler','$routeParams','$location','InvoiceService','$window',function($scope,Sale,Tasks,ErrorsHandler,$routeParams,$location,InvoiceService,$window){$scope.title='Sale Receipts';$scope.type='sale';$scope.service=InvoiceService.create(Sale);$scope.$on('$routeChangeSuccess',function(ev,currentRoute,previousRoute){if($routeParams.saleId){$scope.loadSale($routeParams.saleId)}});$scope.$watch('service.data.items',function(){$scope.service.calculateTotals()},!0);$scope.$watch('service.data.include_vat',function(){$scope.service.calculateTotals()});$scope.loadSale=function(sale_id){Sale.get({'sale':sale_id,client:!0},function(response){$scope.service.data=response;$scope.service.data.invoice_date=moment(response.invoice_date).toDate();$scope.service.data.include_vat=(response.include_vat=='1')?!0:!1;$scope.service.client_search=response.client})};$window.onbeforeunload=function(){if($scope.service.data&&$scope.service.data.paid=='0'){return"You haven't marked this sales receipt as paid. Are you sure you want to leave?"}};$scope.$on('$locationChangeStart',function(event,next,current){if($scope.service.data&&$scope.service.data.paid=='0'){if(!confirm("You haven't marked this sales receipt as paid. Are you sure you want to leave?")){event.preventDefault()}}});$scope.$on("$destroy",function(){$window.onbeforeunload=function(){}});$scope.reset=function(){$location.path('sales/new/')};$scope.sendToXero=function(){if(!$scope.service.data||!$scope.service.data.id){return}
$scope.service.data.xero_syncing=!0;Sale.sendToXero({id:$scope.service.data.id},function(response){if(response.invoice){angular.extend($scope.service.data,response.invoice)}else{$scope.service.data.xeroId=response.xero_invoice_id;$scope.service.data.xero_synced_at=new Date()}
$scope.service.data.xero_sync_error=null;$scope.service.data.xero_syncing=!1;Messenger().post({message:'Sale successfully sent to Xero',type:'success',showCloseButton:!0})},function(error){$scope.service.data.xero_sync_error=error.data.message||'Failed to send to Xero';$scope.service.data.xero_syncing=!1;Messenger().post({message:'Failed to send sale to Xero: '+(error.data.message||'Unknown error'),type:'error',showCloseButton:!0})})};$scope.save=function(print,email){console.log('Save function called with:',{print,email});console.log('Service data:',$scope.service.data);console.log('Client search:',$scope.service.client_search);if(!$scope.service.data){console.log('No service data, returning');return}
var errors=[];if(!$scope.service.client_search){console.log('No client selected');errors.push("Please select a customer")}else{console.log('Client selected:',$scope.service.client_search)}
if($scope.service.data.type==='sale'&&!$scope.service.data.method_of_payment){console.log('No payment method selected');errors.push("Please select a method of payment")}else{console.log('Payment method:',$scope.service.data.method_of_payment)}
var hasItems=!1;_.each($scope.service.data.items,function(item){if(item.name&&item.quantity){hasItems=!0}});if(!hasItems){console.log('No items found');errors.push("Please add at least one item")}else{console.log('Items found:',$scope.service.data.items)}
if(errors.length>0){console.log('Validation errors:',errors);if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:errors.join("\n"),type:"error",showCloseButton:!0})}
return!1}
$scope.service.loading=!0;var request_data=angular.copy($scope.service.data);_.each($scope.service.data.items,function(item,index){if(!item.name){$scope.service.data.items.splice(index,1)}});request_data.client_type=$scope.service.client_search.type;request_data.client_id=$scope.service.client_search.id;request_data.total_price=$scope.service.total;request_data.invoice_date=moment(request_data.invoice_date).format("X");if(print)request_data.print=!0;if($scope.service.data.id){Sale.update(request_data,function(res){angular.extend($scope.service.data,res||{});$scope.service.data.invoice_date=moment(res.invoice_date).toDate();$scope.service.loading=!1;if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:"Sale was successfully updated!",type:"success",showCloseButton:!0})}
if(print)
$window.open(res.print_url,'_blank');if(email)
$scope.service.showEmailModal();},function(a,b){$scope.service.loading=!1;ErrorsHandler(a,b)})}else{console.log('About to call Sale.save with data:',request_data);Sale.save(request_data,function(res){angular.extend($scope.service.data,res||{});$scope.service.data.invoice_date=moment(res.invoice_date).toDate();$scope.service.loading=!1;var type=$scope.service.data.type==='sale'?'Sale':'Invoice';if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:type+" created successfully!",type:"success",showCloseButton:!0})}
if(print)
$window.open(res.print_url,'_blank');if(email)
$scope.service.showEmailModal($scope.service.data);$location.path(res.type+'s/view/'+res.id)},function(response){console.log('Sale.save error:',response);$scope.service.loading=!1;if(response.status===422){if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:response.data.error||"Validation failed",type:"error",showCloseButton:!0})}}else{if(typeof Messenger!=='undefined'&&Messenger().post){Messenger().post({message:response.data.error||"An error occurred while saving the sale",type:"error",showCloseButton:!0})}
ErrorsHandler(response)}})}}}]);window.ThreeCXCallModal=(function(){'use strict';let modal=null;let statusPollingInterval=null;let isRequestInProgress=!1;let currentCall=null;let debugLogs=[];let callStartTime=null;let timerInterval=null;let currentCallState='ended';function addDebugLog(message,type='info'){const timestamp=new Date().toLocaleTimeString();debugLogs.push({time:timestamp,message:message,type:type});if(debugLogs.length>50){debugLogs=debugLogs.slice(-50)}
console.log('[3CX Debug '+timestamp+'] '+message);updateDebugConsole()}
function updateDebugConsole(){const debugContainer=$('#debug-logs-container');if(debugContainer.length){let html='';debugLogs.forEach(log=>{const colorClass=log.type==='success'?'color: #28a745;':log.type==='error'?'color: #dc3545;':'color: #17a2b8;';html+='<div style="margin: 2px 0;">'+'<span style="color: #666;">'+log.time+'</span>'+'<span style="'+colorClass+'">'+log.message+'</span>'+'</div>'});debugContainer.html(html)}}
function startCallTimer(){if(timerInterval){clearInterval(timerInterval)}
callStartTime=new Date();timerInterval=setInterval(updateTimer,1000)}
function updateTimer(){if(!callStartTime)return;const now=new Date();const diff=Math.floor((now-callStartTime)/1000);const minutes=Math.floor(diff/60);const seconds=diff%60;const timeString=(minutes<10?'0':'')+minutes+':'+(seconds<10?'0':'')+seconds;$('#timer-display').text(timeString);if(window.currentAngularScope){window.currentAngularScope.callDuration=timeString;window.currentAngularScope.$apply()}}
function stopCallTimer(){if(timerInterval){clearInterval(timerInterval);timerInterval=null}
if(callStartTime){const now=new Date();const diff=Math.floor((now-callStartTime)/1000);const minutes=Math.floor(diff/60);const seconds=diff%60;const finalDuration=(minutes<10?'0':'')+minutes+':'+(seconds<10?'0':'')+seconds;$('#final-duration').text('Duration: '+finalDuration);if(window.currentAngularScope){window.currentAngularScope.finalDuration='Duration: '+finalDuration;window.currentAngularScope.$apply()}
callStartTime=null}}
function getCurrentCallState(){return currentCallState}
function getCurrentCall(){return currentCall}
function updateCurrentCall(data){if(currentCall){currentCall={...currentCall,...data}}}
function showCallState(state,data={}){addDebugLog('State change: '+currentCallState+' → '+state,'info');const isTransitioningToConnected=(state==='connected'&&currentCallState!=='connected');currentCallState=state;$('.call-state').removeClass('active');switch(state){case 'init':$('#call-state-init').addClass('active');break;case 'dialing':$('#call-state-dialing').addClass('active');break;case 'ringing':$('#call-state-ringing').addClass('active');break;case 'connected':$('#call-state-connected').addClass('active');if(isTransitioningToConnected){startCallTimer();addDebugLog('🕒 Timer started - transitioning to connected state','success')}else{addDebugLog('⏱️ Already connected - keeping existing timer','info')}
break;case 'ended':$('#call-state-ended').addClass('active');stopCallTimer();stopPolling();if(data.endReason==='extension_rejected'||data.endReason==='reception_rejected'){$('#ended-status').text('Call Rejected');const extensionNumber=data.extensionNumber||'324';$('#final-duration').text('Rejected by Extension '+extensionNumber);$('#ended-icon').removeClass('fa-check-square ended fa-times-circle').addClass('fa-stop-circle').css('color','#dc3545')}else if(data.endReason==='customer_rejected'){$('#ended-status').text('Customer Declined');$('#final-duration').text('Customer did not answer or declined');$('#ended-icon').removeClass('fa-check-square ended fa-stop-circle').addClass('fa-times-circle').css('color','#ffc107')}else if(data.endReason==='call_completed'){$('#ended-status').text('Call Completed');$('#final-duration').text('Call completed successfully');$('#ended-icon').removeClass('fa-stop-circle fa-times-circle').addClass('fa-check-square ended').css('color','#28a745')}else{$('#ended-status').text('Call Ended');$('#final-duration').text('Call completed');$('#ended-icon').removeClass('fa-times-circle fa-stop-circle').addClass('fa-check-square ended').css('color','#28a745')}
break;case 'error':$('#call-state-error').addClass('active');stopCallTimer();stopPolling();if(data.message){$('.error-message-display').text(data.message);$('.modern-error').text(data.message)}
if(data.suggestion){$('.error-suggestion-text').text(data.suggestion);$('.error-suggestion-display').show();$('.modern-suggestion').text(data.suggestion)}else{$('.error-suggestion-display').hide()}
break}}
function showStaticCallProgress(participantId,extension){addDebugLog('Showing static call progress for participant '+participantId,'info');showCallState('dialing');$('.modern-status').text('Call In Progress');const extensionInfo=extension||currentCall?.extension||'324';$('.static-message').text('Extension '+extensionInfo+' is calling the customer.');$('.static-subtitle').text('Close this dialog when your call is complete.');addDebugLog('Static call progress display shown for extension '+extensionInfo,'info')}
function startPolling(participantId){addDebugLog('startPolling called but using static display instead','info')}
function stopPolling(){if(statusPollingInterval){clearTimeout(statusPollingInterval);statusPollingInterval=null;isRequestInProgress=!1;addDebugLog('Polling stopped','info')}}
function fetchFinalCallStatus(participantId){if(!participantId){addDebugLog('No participant ID available for final status fetch','error');return}
addDebugLog('Fetching final call status for participant '+participantId,'info');const isTaskPage=document.querySelector('meta[name="task-id"]')!==null;const hasTaskId=currentCall&&currentCall.taskId;const statusUrl=(isTaskPage||hasTaskId)?'/api/v1/threecx/call/status/'+participantId:'/admin/integrations/threecx/call-status/'+participantId;$.ajax({url:statusUrl,method:'GET',success:function(response){addDebugLog('Final call status fetched successfully','success');if(response.success&&response.data){const status=response.data.status.toLowerCase();const eventData=response.data.event_data||{};const reason=eventData.reason||'';addDebugLog(`Final call status: ${status} (reason: ${reason})`,'info');syncCallStatusToDatabase({participantId:participantId,callId:response.data.call_id,status:status,reason:reason,eventData:eventData})}else{addDebugLog('Final call status: Call ended or not found','info')}},error:function(xhr){const errorMessage=xhr.responseJSON?.message||xhr.statusText||'Unknown error';addDebugLog('Failed to fetch final call status: '+xhr.status+' - '+errorMessage,'error')}})}
function syncCallStatusToDatabase(callData){if(!callData.participantId){addDebugLog('Cannot sync call status - no participant ID','error');return}
addDebugLog('Syncing call status to database: '+callData.status,'info');const isTaskPage=document.querySelector('meta[name="task-id"]')!==null;const hasTaskId=currentCall&&currentCall.taskId;const syncUrl=(isTaskPage||hasTaskId)?'/api/v1/threecx/call/sync-status':'/admin/integrations/threecx/sync-call-status';$.ajax({url:syncUrl,method:'POST',headers:{'Content-Type':'application/json','Accept':'application/json','X-CSRF-TOKEN':$('meta[name="csrf-token"]').attr('content')},data:JSON.stringify({participant_id:callData.participantId,call_id:callData.callId,final_status:callData.status,end_reason:callData.reason,event_data:callData.eventData,task_id:currentCall?.taskId||null}),success:function(response){addDebugLog('Call status synced to database successfully','success')},error:function(xhr){const errorMessage=xhr.responseJSON?.message||xhr.statusText||'Unknown error';addDebugLog('Failed to sync call status to database: '+xhr.status+' - '+errorMessage,'error')}})}
function createModal(toNumber,extension='650'){addDebugLog('Creating modal for '+toNumber+' via '+extension,'info');cleanupModal();const modalId='threecx-call-modal-'+Date.now();const modalHtml=`
        <div class="modal fade" id="${modalId}" tabindex="-1" role="dialog">
            <div class="modal-dialog modal-md">
                <div class="modal-content">
                    ${getModalTemplate()}
                </div>
            </div>
        </div>`;$('body').append(modalHtml);modal=$('#'+modalId);$('.modern-number').text(toNumber);$('.call-number-display').text(toNumber);modal.on('hidden.bs.modal',function(){if(window.refreshCallHistory&&currentCall?.taskId){addDebugLog('Triggering call history refresh','info');setTimeout(function(){window.refreshCallHistory()},200)}
stopPolling();stopCallTimer();cleanupModal();$('.test-call-btn').prop('disabled',!1).removeClass('processing');$('#test_to_number').prop('disabled',!1)});modal.modal({backdrop:!0,keyboard:!0,show:!0});modal.find('.close, [data-dismiss="modal"]').on('click',function(){stopPolling();stopCallTimer();cleanupModal()});showCallState('init');addDebugLog('Modal created and initial state set','info');return modalId}
function getModalTemplate(){return `
        <div class="modal-header">
            <button type="button" class="close" onclick="window.ThreeCXCallModal.closeModal()">×</button>
            <h4 class="modal-title modern-title">3CX Call</h4>
        </div>
        <div class="modal-body">
            <!-- Initiating Call State -->
            <div id="call-state-init" class="call-state modern-state">
                <div class="modern-icon-container">
                    <i class="fa fa-phone-square fa-4x modern-icon connecting pulse"></i>
                </div>
                <h2 class="modern-status">Initiating Call</h2>
                <p class="modern-number call-number-display"></p>
                <span class="modern-extension">Setting up connection...</span>
            </div>
            
            <!-- Call In Progress State (Static) -->
            <div id="call-state-dialing" class="call-state modern-state">
                <div class="modern-icon-container">
                    <i class="fa fa-phone-square fa-4x modern-icon connecting ring-animation"></i>
                </div>
                <h2 class="modern-status">Call In Progress</h2>
                <p class="modern-number call-number-display"></p>
                <div class="call-status-message">
                    <p class="static-message">Your call is being processed by the phone system.</p>
                    <p class="static-subtitle">Please wait while we connect you...</p>
                </div>
            </div>
            
            <!-- Call In Progress State (Alternative) -->
            <div id="call-state-ringing" class="call-state modern-state">
                <div class="modern-icon-container">
                    <i class="fa fa-phone-square fa-4x modern-icon connecting ring-animation"></i>
                </div>
                <h2 class="modern-status">Call In Progress</h2>
                <p class="modern-number call-number-display"></p>
                <div class="call-status-message">
                    <p class="static-message">Your call is being processed by the phone system.</p>
                    <p class="static-subtitle">Please wait while we connect you...</p>
                </div>
            </div>
            
            <!-- Connected State -->
            <div id="call-state-connected" class="call-state modern-state">
                <div class="modern-icon-container">
                    <i class="fa fa-phone-square fa-4x modern-icon connected connected-glow"></i>
                </div>
                <h2 class="modern-status">Connected</h2>
                <p class="modern-number call-number-display"></p>
                <div class="modern-timer">
                    <span id="timer-display">00:00</span>
                </div>                
                <div class="alert alert-success" style="margin-top: 20px;">
                    <strong>Connected.</strong> The phone system manages the call lifecycle.
                </div>
            </div>
            
            <!-- Ended State -->
            <div id="call-state-ended" class="call-state modern-state">
                <div class="modern-icon-container">
                    <i class="fa fa-check-square fa-4x modern-icon ended" id="ended-icon"></i>
                </div>
                <h2 class="modern-status" id="ended-status">Call Ended</h2>
                <p class="modern-number call-number-display"></p>
                <div class="modern-duration">
                    <span id="final-duration">Call completed</span>
                </div>                
            </div>

            <!-- Error State -->
            <div id="call-state-error" class="call-state modern-state">
                <div class="modern-icon-container">
                    <i class="fa fa-exclamation-triangle fa-4x modern-icon error"></i>
                </div>
                <h2 class="modern-status">Call Failed</h2>
                <p class="modern-error error-message-display"></p>                
            </div>
            
        </div>
        
        <!-- Include CSS -->
        <style>
        /* Modern Call Modal CSS - FROM 3CX MODULE */
        .modern-call-modal {
            border: none;
            border-radius: 0;
            box-shadow: 0 8px 32px rgba(0,0,0,0.12);
        }
        
        .modern-header {
            background: #fff;
            border-bottom: 1px solid #e9ecef;
            border-radius: 0;
            padding: 20px 24px;
        }
        
        .modern-title {
            font-size: 18px;
            font-weight: 600;
            color: #2c3e50;
            margin: 0;
        }
        
        .modern-close {
            font-size: 24px;
            font-weight: 300;
            color: #6c757d;
            opacity: 1;
        }
        
        .modern-body {
            padding: 24px;
            background: #f8f9fa;
        }
        
        /* HIGH SPECIFICITY - Force override Bootstrap modal styles */
        .modal-content .modal-body .modern-state {
            text-align: center !important;
            padding: 48px 32px !important;
            background: white !important;
            min-height: 420px !important;
            display: none !important; /* Hidden by default */
            flex-direction: column !important;
            justify-content: center !important;
            align-items: center !important;
            border-radius: 8px;
            margin-bottom: 16px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.04);
        }

        /* Show only the active state */
        .modal-content .modal-body .modern-state.active {
            display: flex !important;
        }
        
        .modern-icon-container {
            margin-bottom: 24px !important;
            padding: 16px !important;
        }
        
        .modern-icon {
            color: #6c757d;
            transition: all 0.3s ease;
        }
        
        .modern-icon.connecting { color: #6c757d; }
        .modern-icon.dialing { color: #ffc107; }
        .modern-icon.ringing { color: #17a2b8; }
        .modern-icon.connected { color: #28a745; }
        .modern-icon.ended { color: #28a745; }
        .modern-icon.error { color: #dc3545; }
        
        .modal-content .modal-body .modern-status {
            font-size: 28px !important;
            font-weight: 600 !important;
            color: #2c3e50 !important;
            margin: 0 0 16px 0 !important;
            text-transform: capitalize !important;
        }
        
        .modal-content .modal-body .modern-number {
            font-size: 28px !important;
            font-weight: 500 !important;
            color: #2c3e50 !important;
            margin: 12px 0 20px 0 !important;
            font-family: 'Segoe UI', sans-serif !important;
            letter-spacing: 1px !important;
        }
        
        .modern-extension,
        .modern-subtitle {
            font-size: 14px;
            color: #6c757d;
            display: block;
            margin-top: 12px;
        }
        
        .call-status-message {
            text-align: center;
            margin: 20px 0;
        }
        
        .static-message {
            font-size: 16px;
            color: #2c3e50;
            margin: 8px 0;
            font-weight: 500;
        }
        
        .static-subtitle {
            font-size: 14px;
            color: #6c757d;
            margin: 4px 0;
        }
        
        .modern-progress {
            width: 100%;
            height: 4px;
            background: #e9ecef;
            border-radius: 2px;
            overflow: hidden;
            margin: 16px 0;
        }
        
        .modern-progress-bar {
            height: 100%;
            transition: width 0.3s ease;
            border-radius: 2px;
        }
        
        .modern-progress-bar.dialing {
            width: 60%;
            background: linear-gradient(90deg, #ffc107, #ffca2c);
            animation: progress-pulse 2s infinite;
        }
        
        .modern-progress-bar.ringing {
            width: 85%;
            background: linear-gradient(90deg, #17a2b8, #20c0db);
            animation: progress-pulse 1.5s infinite;
        }
        
        .modal-content .modal-body .modern-timer {
            font-size: 40px !important;
            font-weight: 400 !important;
            color: #28a745 !important;
            margin: 16px 0 20px 0 !important;
            font-family: 'Segoe UI', 'Roboto', monospace !important;
            background: rgba(40,167,69,0.1) !important;
            padding: 12px 24px !important;
            border-radius: 12px !important;
            display: inline-block !important;
            letter-spacing: 2px !important;
            box-shadow: 0 2px 8px rgba(40,167,69,0.1) !important;
        }
        
        .modal-content .modal-body .modern-duration {
            font-size: 18px !important;
            font-weight: 600 !important;
            color: #2c3e50 !important;
            margin: 16px 0 20px 0 !important;
            font-family: 'Segoe UI', sans-serif !important;
            letter-spacing: 0.5px !important;
        }
        
        /* Special styling for rejection message */
        .modal-content .modal-body #final-duration {
            font-size: 18px !important;
            font-weight: 600 !important;
            line-height: 1.4 !important;
        }
        
        @keyframes progress-pulse {
            0% { opacity: 1; }
            50% { opacity: 0.7; }
            100% { opacity: 1; }
        }
        
        @keyframes pulse {
            0% { transform: scale(1); }
            50% { transform: scale(1.05); }
            100% { transform: scale(1); }
        }
        
        @keyframes ring-animation {
            0% { 
                transform: rotate(-15deg) scale(1); 
                box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);
            }
            25% { 
                transform: rotate(15deg) scale(1.02); 
                box-shadow: 0 0 0 5px rgba(23, 162, 184, 0.3);
            }
            50% { 
                transform: rotate(-10deg) scale(1); 
                box-shadow: 0 0 0 10px rgba(23, 162, 184, 0.1);
            }
            75% { 
                transform: rotate(10deg) scale(1.02); 
                box-shadow: 0 0 0 5px rgba(23, 162, 184, 0.3);
            }
            100% { 
                transform: rotate(-15deg) scale(1); 
                box-shadow: 0 0 0 0 rgba(23, 162, 184, 0.5);
            }
        }
        
        @keyframes connected-glow {
            0% { 
                box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.7);
            }
            50% { 
                box-shadow: 0 0 20px 5px rgba(40, 167, 69, 0.5);
            }
            100% { 
                box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.7);
            }
        }
        
        /* Apply animations to icons only */
        .modern-icon.pulse {
            animation: pulse 2s infinite;
        }
        
        .modern-icon.ring-animation {
            animation: ring-animation 0.8s infinite;
        }
        
        .modern-icon.connected-glow {
            animation: connected-glow 2s infinite;
        }
        
        </style>`}
function cleanupModal(){if(modal){modal.modal('hide');modal.remove();modal=null}
$('.modal').filter('[id*="threecx-call-modal"]').remove();$('.modal-backdrop').remove();$('body').removeClass('modal-open').removeAttr('style');window.currentAngularScope=null}
function closeModal(){stopPolling();stopCallTimer();cleanupModal()}
function makeCall(toNumber,options={}){const extension=options.extension||'650';const apiUrl=options.apiUrl||'/api/v1/threecx/call/start';const timeout=options.timeout||30;addDebugLog('Starting call to '+toNumber+' via '+extension,'info');addDebugLog('Using API endpoint: '+apiUrl,'info');addDebugLog('Task ID: '+(options.taskId||'null'),'info');createModal(toNumber,extension);setTimeout(()=>{showCallState('init');addDebugLog('Initial state set to: init','info')},100);$('.test-call-btn').prop('disabled',!0).addClass('processing');$('#test_to_number').prop('disabled',!0);makeCallWithRetry(apiUrl,{phone_number:toNumber,extension:extension,timeout:timeout,task_id:options.taskId||null,static_display:!0},0)}
function makeCallWithRetry(apiUrl,requestData,retryCount){let csrfToken=$('meta[name="csrf-token"]').attr('content');addDebugLog('CSRF token found: '+(csrfToken?'Yes':'No'),'info');$.ajax({url:apiUrl,method:'POST',headers:{'Content-Type':'application/json','Accept':'application/json','X-CSRF-TOKEN':csrfToken},data:JSON.stringify(requestData),success:function(response){addDebugLog('API call successful','success');addDebugLog('Response data: '+JSON.stringify(response),'info');if(response.success){if(response.participant_id){addDebugLog('Participant ID received: '+response.participant_id,'success');const isStaticDisplay=!0;if(response.tracking_started===!1&&!isStaticDisplay){showCallState('error',{message:'Call initiated but tracking failed to start. Please check system permissions.',suggestion:'Contact administrator to fix cache permissions'});addDebugLog('Call tracking failed to start - cache permission issue','error');return}
currentCall={participantId:response.participant_id,callId:response.call_id,status:response.call_status,extension:response.extension,phoneNumber:requestData.phone_number,taskId:requestData.task_id||null};window.currentCall=currentCall;addDebugLog('Current call stored: '+JSON.stringify(currentCall),'info');addDebugLog('About to call showStaticCallProgress with participant: '+response.participant_id+', extension: '+response.extension,'info');showStaticCallProgress(response.participant_id,response.extension)}else{addDebugLog('No participant_id in response','error');let errorMessage=response.message||'Call could not be initiated';let suggestion='The 3CX phone system may be unavailable or your extension may not be configured properly. Please contact your administrator.';if(response.message&&response.message.includes('extension')){errorMessage='Extension configuration issue';suggestion='Your extension may not be properly configured in the 3CX system. Please contact your administrator to verify your extension settings.'}else if(response.message&&response.message.includes('connect')){errorMessage='3CX system connection failed';suggestion='Could not connect to the 3CX phone system. Please try again or contact your administrator if the issue persists.'}
showCallState('error',{message:errorMessage,suggestion:suggestion})}}else{addDebugLog('Response success = false: '+response.message,'error');showCallState('error',{message:response.message||'Failed to initiate call',suggestion:response.suggestion})}},error:function(xhr){addDebugLog('API call failed: '+xhr.status+' (retry count: '+retryCount+')','error');if(xhr.status===419&&retryCount<2){addDebugLog('419 error detected, attempting to get fresh token...','info');fetch(window.location.pathname,{method:'GET',headers:{'Accept':'text/html'},credentials:'same-origin'}).then(response=>response.text()).then(html=>{const parser=new DOMParser();const doc=parser.parseFromString(html,'text/html');const newToken=doc.querySelector('meta[name="csrf-token"]')?.getAttribute('content');if(newToken&&newToken!==csrfToken){const currentTokenEl=document.querySelector('meta[name="csrf-token"]');if(currentTokenEl){currentTokenEl.setAttribute('content',newToken)}
addDebugLog('Fresh token obtained: '+newToken.substring(0,10)+'...','success');addDebugLog('Retrying API call with fresh token...','info');setTimeout(()=>{makeCallWithRetry(apiUrl,requestData,retryCount+1)},500)}else{addDebugLog('Token refresh failed - same token returned','error');showCallState('error',{message:'Session expired - please refresh the page and try again',suggestion:'Your network connection may have changed (common with VPN/Cloudflare users)'})}}).catch(error=>{addDebugLog('Token refresh request failed: '+error,'error');showCallState('error',{message:'Session expired - please refresh the page and try again',suggestion:'Your network connection may have changed (common with VPN/Cloudflare users)'})});return}
let message='Failed to start call';let suggestion=null;if(xhr.responseJSON){message=xhr.responseJSON.message||message;suggestion=xhr.responseJSON.suggestion}else if(xhr.status===419){message='Session expired - please refresh the page and try again';suggestion='Your network connection may have changed (common with VPN/Cloudflare users)'}
addDebugLog('Showing error state: '+message,'error');showCallState('error',{message,suggestion})}})}
return{makeCall:makeCall,showCallState:showCallState,getCurrentCallState:getCurrentCallState,getCurrentCall:getCurrentCall,updateCurrentCall:updateCurrentCall,addDebugLog:addDebugLog,closeModal:closeModal,startPolling:startPolling,stopPolling:stopPolling,fetchFinalCallStatus:fetchFinalCallStatus,show:function(toNumber,extension){$('.modern-number').text(toNumber);showCallState('init')},showConnected:function(message){showCallState('connected')},showError:function(message,suggestion){showCallState('error',{message,suggestion})},hide:function(){closeModal()},updateStatus:function(status,message){showCallState(status,{message})}}})();if(typeof module!=='undefined'&&module.exports){module.exports=window.ThreeCXCallModal};window.ThreeCXCore=(function(){'use strict';const contexts={admin:{apiUrl:'/admin/integrations/threecx/test-call',statusUrl:'/admin/integrations/threecx/call-status',callHistoryUrl:null,useUserExtension:!1},task:{apiUrl:'/api/v1/threecx/call/start',statusUrl:'/api/v1/threecx/call/status',callHistoryUrl:'/api/threecx/logs/task',useUserExtension:!0}};let currentContext='admin';function setContext(context){if(contexts[context]){currentContext=context}}
function getConfig(){return contexts[currentContext]}
function getUserExtension(){if(window.currentUser&&window.currentUser.extension){return window.currentUser.extension}
if(window.auth&&window.auth.user&&window.auth.user.extension){return window.auth.user.extension}
if(window.Laravel&&window.Laravel.user&&window.Laravel.user.extension){return window.Laravel.user.extension}
const metaExtension=document.querySelector('meta[name="user-extension"]');if(metaExtension){return metaExtension.getAttribute('content')}
return window.userExtension||'650'}
function makeCall(phoneNumber,options={}){const config=getConfig();let extension;if(config.useUserExtension){extension=getUserExtension()}else{extension=undefined}
const callOptions={extension:extension,apiUrl:config.apiUrl,timeout:options.timeout||30,taskId:options.taskId||null};if(options.taskId){callOptions.taskId=options.taskId;console.log('📝 Task context set for automatic call logging:',options.taskId)}
if(window.ThreeCXCallModal){return window.ThreeCXCallModal.makeCall(phoneNumber,callOptions)}else{console.error('ThreeCXCallModal not loaded');alert('3CX Call functionality not available. Please refresh the page.')}}
function logCallHistory(callData){console.warn('logCallHistory is deprecated - call logging now happens automatically when calls end');return Promise.resolve()}
function init(context,options={}){setContext(context);if(options.userExtension){window.userExtension=options.userExtension}
console.log(`3CX Core initialized for context: ${context}`)}
return{init:init,setContext:setContext,getConfig:getConfig,getUserExtension:getUserExtension,makeCall:makeCall,logCallHistory:logCallHistory,CONTEXTS:{ADMIN:'admin',TASK:'task'}}})();if(typeof module!=='undefined'&&module.exports){module.exports=window.ThreeCXCore}