]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
webstaff: Clear template name after application so another can be selected
[Evergreen.git] / Open-ILS / web / js / ui / default / staff / cat / volcopy / app.js
1 /**
2  * Vol/Copy Editor
3  */
4
5 angular.module('egVolCopy',
6     ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod'])
7
8 .filter('boolText', function(){
9     return function (v) {
10         return v == 't';
11     }
12 })
13
14 .config(function($routeProvider, $locationProvider, $compileProvider) {
15     $locationProvider.html5Mode(true);
16     $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); // grid export
17
18     var resolver = {
19         delay : ['egStartup', function(egStartup) { return egStartup.go(); }]
20     };
21
22     $routeProvider.when('/cat/volcopy/:dataKey', {
23         templateUrl: './cat/volcopy/t_view',
24         controller: 'EditCtrl',
25         resolve : resolver
26     });
27
28 })
29
30 .factory('itemSvc', 
31        ['egCore','$q',
32 function(egCore , $q) {
33
34     var service = {
35         tree : {}, // holds lib->cn->copy hash stack
36         copies : [] // raw copy list
37     };
38
39     // returns a promise resolved with the list of circ mods
40     service.get_classifications = function() {
41         if (egCore.env.acnc)
42             return $q.when(egCore.env.acnc.list);
43
44         return egCore.pcrud.retrieveAll('acnc', null, {atomic : true})
45         .then(function(list) {
46             egCore.env.absorbList(list, 'acnc');
47             return list;
48         });
49     };
50
51     service.get_prefixes = function(org) {
52         return egCore.pcrud.search('acnp',
53             {owning_lib : egCore.org.fullPath(org, true)},
54             null, {atomic : true}
55         );
56
57     };
58
59     service.get_statcats = function(orgs) {
60         return egCore.pcrud.search('asc',
61             {owner : orgs},
62             { flesh : 1,
63               flesh_fields : {
64                 asc : ['owner','entries']
65               }
66             },
67             { atomic : true }
68         );
69     };
70
71     service.get_locations = function(orgs) {
72         return egCore.pcrud.search('acpl',
73             {owning_lib : orgs},
74             null, {atomic : true}
75         );
76     };
77
78     service.get_suffixes = function(org) {
79         return egCore.pcrud.search('acns',
80             {owning_lib : egCore.org.fullPath(org, true)},
81             null, {atomic : true}
82         );
83
84     };
85
86     service.get_statuses = function() {
87         if (egCore.env.ccs)
88             return $q.when(egCore.env.ccs.list);
89
90         return egCore.pcrud.retrieveAll('ccs', {}, {atomic : true}).then(
91             function(list) {
92                 egCore.env.absorbList(list, 'ccs');
93                 return list;
94             }
95         );
96
97     };
98
99     service.get_circ_mods = function() {
100         if (egCore.env.ccm)
101             return $q.when(egCore.env.ccm.list);
102
103         return egCore.pcrud.retrieveAll('ccm', {}, {atomic : true}).then(
104             function(list) {
105                 egCore.env.absorbList(list, 'ccm');
106                 return list;
107             }
108         );
109
110     };
111
112     service.get_circ_types = function() {
113         if (egCore.env.citm)
114             return $q.when(egCore.env.citm.list);
115
116         return egCore.pcrud.retrieveAll('citm', {}, {atomic : true}).then(
117             function(list) {
118                 egCore.env.absorbList(list, 'citm');
119                 return list;
120             }
121         );
122
123     };
124
125     service.get_age_protects = function() {
126         if (egCore.env.crahp)
127             return $q.when(egCore.env.crahp.list);
128
129         return egCore.pcrud.retrieveAll('crahp', {}, {atomic : true}).then(
130             function(list) {
131                 egCore.env.absorbList(list, 'crahp');
132                 return list;
133             }
134         );
135
136     };
137
138     service.bmp_parts = {};
139     service.get_parts = function(rec) {
140         if (service.bmp_parts[rec])
141             return $q.when(service.bmp_parts[rec]);
142
143         return egCore.pcrud.search('bmp',
144             {record : rec},
145             null, {atomic : true}
146         ).then(function(list) {
147             service.bmp_parts[rec] = list;
148             return list;
149         });
150
151     };
152
153     service.flesh = {   
154         flesh : 3, 
155         flesh_fields : {
156             acp : ['call_number','parts','stat_cat_entries'],
157             acn : ['label_class','prefix','suffix']
158         }
159     }
160
161     service.addCopy = function (cp) {
162
163         if (!cp.parts()) cp.parts([]); // just in case...
164
165         var lib = cp.call_number().owning_lib();
166         var cn = cp.call_number().id();
167
168         if (!service.tree[lib]) service.tree[lib] = {};
169         if (!service.tree[lib][cn]) service.tree[lib][cn] = [];
170
171         service.tree[lib][cn].push(cp);
172         service.copies.push(cp);
173     }
174
175     service.fetchIds = function(idList) {
176         service.tree = {}; // clear the tree on fetch
177         service.copies = []; // clear the copy list on fetch
178         return egCore.pcrud.search('acp', { 'id' : idList }, service.flesh).then(null,null,
179             function(copy) {
180                 service.addCopy(copy);
181             }
182         );
183     }
184
185     return service;
186 }])
187
188 .directive("egVolCopyEdit", function () {
189     return {
190         restrict: 'E',
191         replace: true,
192         template:
193             '<div class="row">'+
194                 '<div class="col-xs-5">'+
195                     '<input id="{{callNumber.id()}}.{{copy.id()}}"'+
196                     ' eg-enter="nextBarcode()" class="form-control"'+
197                     ' type="text" ng-model="barcode" ng-change="updateBarcode()"/>'+
198                 '</div>'+
199                 '<div class="col-xs-3"><input class="form-control" type="number" ng-model="copy_number" ng-change="updateCopyNo()"/></div>'+
200                 '<div class="col-xs-4"><eg-basic-combo-box list="parts" selected="part"></eg-basic-combo-box></div>'+
201             '</div>',
202
203         scope: { copy: "=", callNumber: "=", index: "@" },
204         controller : ['$scope','itemSvc',
205             function ( $scope , itemSvc ) {
206                 $scope.new_part_id = 0;
207
208                 $scope.nextBarcode = function (i) {
209                     $scope.$parent.focusNextBarcode($scope.copy.id());
210                 }
211
212                 $scope.updateBarcode = function () { $scope.copy.barcode($scope.barcode); $scope.copy.ischanged(1); };
213                 $scope.updateCopyNo = function () { $scope.copy.copy_number($scope.copy_number); $scope.copy.ischanged(1); };
214                 $scope.updatePart = function () {
215                     var p = $scope.part_list.filter(function (x) {
216                         return x.label() == $scope.part
217                     });
218                     if (p.length > 0) { // preexisting part
219                         $scope.copy.parts(p)
220                     } else { // create one...
221                         var part = new egCore.idl.bmp();
222                         part.id( --$scope.new_part_id );
223                         part.isnew( true );
224                         part.label( $scope.part );
225                         part.record( $scope.callNumber.owning_lib() );
226                         $scope.copy.parts([part]);
227                         $scope.copy.ischanged(1);
228                     }
229                 }
230
231                 $scope.barcode = $scope.copy.barcode();
232                 $scope.copy_number = $scope.copy.copy_number();
233
234                 if ($scope.copy.parts()) {
235                     $scope.part = $scope.copy.parts()[0];
236                     if ($scope.part) $scope.part = $scope.part.label();
237                 };
238
239                 $scope.parts = [];
240                 $scope.part_list = [];
241
242                 itemSvc.get_parts($scope.callNumber.record()).then(function(list){
243                     $scope.part_list = list;
244                     angular.forEach(list, function(p){ $scope.parts.push(p.label()) });
245                 });
246
247             }
248         ]
249
250     }
251 })
252
253 .directive("egVolRow", function () {
254     return {
255         restrict: 'E',
256         replace: true,
257         transclude: true,
258         template:
259             '<div class="row">'+
260                 '<div class="col-xs-2">'+
261                     '<select class="form-control" ng-model="classification" ng-options="cl.name() for cl in classification_list track by idTracker(cl)"/>'+
262                 '</div>'+
263                 '<div class="col-xs-1">'+
264                     '<select class="form-control" ng-model="prefix" ng-change="updatePrefix()" ng-options="p.label() for p in prefix_list track by idTracker(p)"/>'+
265                 '</div>'+
266                 '<div class="col-xs-2"><input class="form-control" type="text" ng-change="updateLabel()" ng-model="label"/></div>'+
267                 '<div class="col-xs-1">'+
268                     '<select class="form-control" ng-model="suffix" ng-change="updateSuffix()" ng-options="s.label() for s in suffix_list track by idTracker(s)"/>'+
269                 '</div>'+
270                 '<div class="col-xs-1"><input class="form-control" type="number" ng-model="copy_count" min="{{orig_copy_count}}" ng-change="changeCPCount()"></div>'+
271                 '<div class="col-xs-5">'+
272                     '<eg-vol-copy-edit ng-repeat="cp in copies track by idTracker(cp)" copy="cp" call-number="callNumber"></eg-vol-copy-edit>'+
273                 '</div>'+
274             '</div>',
275
276         scope: {allcopies: "=", copies: "=" },
277         controller : ['$scope','itemSvc','egCore',
278             function ( $scope , itemSvc , egCore ) {
279                 $scope.new_cp_id = 0;
280                 $scope.callNumber =  $scope.copies[0].call_number();
281
282                 $scope.idTracker = function (x) { if (x) return x.id() };
283
284                 // XXX $() is not working! arg
285                 $scope.focusNextBarcode = function (i) {
286                     var n;
287                     var yep = false;
288                     angular.forEach($scope.copies, function (cp) {
289                         if (n) return;
290
291                         if (cp.id() == i) {
292                             yep = true;
293                             return;
294                         }
295
296                         if (yep) n = cp.id();
297                     });
298
299                     if (n) {
300                         var next = '#' + $scope.callNumber.id() + '.' + n;
301                         var el = $(next).get(0);
302                         if (el) el.focus()
303                     }
304                 }
305
306                 $scope.suffix_list = [];
307                 itemSvc.get_suffixes($scope.callNumber.owning_lib()).then(function(list){
308                     $scope.suffix_list = list;
309                 });
310                 $scope.updateSuffix = function () {
311                     angular.forEach($scope.copies, function(cp) {
312                         cp.call_number().suffix($scope.suffix);
313                         cp.call_number().ischanged(1);
314                     });
315                 }
316
317                 $scope.prefix_list = [];
318                 itemSvc.get_prefixes($scope.callNumber.owning_lib()).then(function(list){
319                     $scope.prefix_list = list;
320                 });
321                 $scope.updatePrefix = function () {
322                     angular.forEach($scope.copies, function(cp) {
323                         cp.call_number().prefix($scope.prefix);
324                         cp.call_number().ischanged(1);
325                     });
326                 }
327
328                 $scope.classification_list = [];
329                 itemSvc.get_classifications().then(function(list){
330                     $scope.classification_list = list;
331                 });
332                 $scope.updateClassification = function () {
333                     angular.forEach($scope.copies, function(cp) {
334                         cp.call_number().label_class($scope.classification);
335                         cp.call_number().ischanged(1);
336                     });
337                 }
338
339                 $scope.updateLabel = function () {
340                     angular.forEach($scope.copies, function(cp) {
341                         cp.call_number().label($scope.label);
342                         cp.call_number().ischanged(1);
343                     });
344                 }
345
346                 $scope.$watch('callNumber.prefix()', function (v) {
347                     if (typeof v != 'object') {
348                         $scope.prefix = $scope.prefix_list.filter(function (p) {
349                             return p.id() == v;
350                         })[0];
351                         $scope.callNumber.prefix($scope.prefix);
352                     }
353                 });
354
355                 $scope.$watch('callNumber.suffix()', function (v) {
356                     if (typeof v != 'object') {
357                         $scope.suffix = $scope.suffix_list.filter( function (s) {
358                             return s.id() == v;
359                         })[0];
360                         $scope.callNumber.suffix($scope.suffix);
361                     }
362                 });
363
364                 $scope.$watch('callNumber.label_class()', function (v) {
365                     if (typeof v != 'object') {
366                         $scope.classification = $scope.classification_list.filter(function (c) {
367                             return c.id() == v;
368                         })[0];
369                         $scope.callNumber.label_class($scope.classification);
370                     }
371                 });
372
373                 $scope.$watch('callNumber.label()', function (v) {
374                     $scope.label = v;
375                 });
376
377                 $scope.prefix = $scope.callNumber.prefix();
378                 $scope.suffix = $scope.callNumber.suffix();
379                 $scope.classification = $scope.callNumber.label_class();
380                 $scope.label = $scope.callNumber.label();
381
382                 $scope.copy_count = $scope.copies.length;
383                 $scope.orig_copy_count = $scope.copy_count;
384
385                 $scope.changeCPCount = function () {
386                     while ($scope.copy_count > $scope.copies.length) {
387                         var cp = new egCore.idl.acp();
388                         cp.id( --$scope.new_cp_id );
389                         cp.isnew( true );
390                         cp.circ_lib( $scope.lib );
391                         cp.call_number( $scope.callNumber );
392                         $scope.copies.push( cp );
393                         $scope.allcopies.push( cp );
394                     }
395
396                     var how_many = $scope.copies.length - $scope.copy_count;
397                     if (how_many > 0) {
398                         var dead = $scope.copies.splice($scope.copy_count,how_many);
399                         $scope.callNumber.copies($scope.copies);
400
401                         // Trimming the global list is a bit more tricky
402                         angular.forEach( dead, function (d) {
403                             angular.forEach( $scope.allcopies, function (l, i) { 
404                                 if (l === d) $scope.allcopies.splice(i,1);
405                             });
406                         });
407                     }
408                 }
409
410             }
411         ]
412
413     }
414 })
415
416 .directive("egVolEdit", function () {
417     return {
418         restrict: 'E',
419         replace: true,
420         template:
421             '<div class="row">'+
422                 '<div class="col-xs-1"><eg-org-selector selected="owning_lib" disableTest="cant_have_vols"></eg-org-selector></div>'+
423                 '<div class="col-xs-1"><input class="form-control" type="number" min="{{orig_cn_count}}" ng-model="cn_count" ng-change="changeCNCount()"/></div>'+
424                 '<div class="col-xs-10">'+
425                     '<eg-vol-row ng-repeat="(cn,copies) in struct track by cn" copies="copies" allcopies="allcopies"></eg-vol-row>'+
426                 '</div>'+
427             '</div>',
428
429         scope: { allcopies: "=", struct: "=", lib: "@", record: "@" },
430         controller : ['$scope','itemSvc','egCore',
431             function ( $scope , itemSvc , egCore ) {
432                 $scope.new_cn_id = 0;
433                 $scope.first_cn = Object.keys($scope.struct)[0];
434                 $scope.full_cn = $scope.struct[$scope.first_cn][0].call_number();
435
436                 $scope.cn_count = Object.keys($scope.struct).length;
437                 $scope.orig_cn_count = $scope.cn_count;
438
439                 $scope.owning_lib = egCore.org.get($scope.lib);
440                 $scope.$watch('owning_lib', function (l) {
441                     angular.forEach( $scope.struct[$scope.first_cn], function (cp) {
442                         cp.call_number().owning_lib( $scope.owning_lib.id() );
443                     });
444                 });
445
446                 $scope.cant_have_vols = function (id) { return !egCore.org.CanHaveVolumes(id); };
447
448                 $scope.$watch('cn_count', function (n) {
449                     var o = Object.keys($scope.struct).length;
450                     if (n > o) { // adding
451                         for (var i = o; o < n; o++) {
452                             var cn = new egCore.idl.acn();
453                             cn.id( --$scope.new_cn_id );
454                             cn.isnew( true );
455                             cn.owning_lib( $scope.owning_lib.id() );
456                             cn.record( $scope.full_cn.record() );
457
458                             var cp = new egCore.idl.acp();
459                             cp.id( --$scope.new_cp_id );
460                             cp.isnew( true );
461                             cp.circ_lib( $scope.owning_lib.id() );
462                             cp.call_number( cn );
463
464                             $scope.struct[cn.id()] = [cp];
465                             $scope.allcopies.push(cp);
466                         }
467                     } else if (n < o) { // removing
468                         var how_many = o - n;
469                         var list = Object
470                                 .keys($scope.struct)
471                                 .sort(function(a, b){return a-b})
472                                 .reverse();
473                         for (var i = how_many; i > 0; i--) {
474                             // Trimming the global list is a bit more tricky
475                             angular.forEach($scope.struct[list[i]], function (d) {
476                                 angular.forEach( $scope.allcopies, function (l, j) { 
477                                     if (l === d) $scope.allcopies.splice(j,1);
478                                 });
479                             });
480                             delete $scope.struct[list[i]];
481                         }
482                     }
483                 });
484             }
485         ]
486
487     }
488 })
489
490 /**
491  * Edit controller!
492  */
493 .controller('EditCtrl', 
494        ['$scope','$q','$routeParams','$location','$timeout','egCore','egNet','egGridDataProvider','itemSvc',
495 function($scope , $q , $routeParams , $location , $timeout , egCore , egNet , egGridDataProvider , itemSvc) {
496
497     $scope.defaults = { // If defaults are not set at all, allow everything
498         statcats : true,
499         attributes : {
500             status : true,
501             loan_duration : true,
502             fine_level : true,
503             cost : true,
504             alerts : true,
505             deposit : true,
506             deposit_amount : true,
507             opac_visible : true,
508             price : true,
509             circulate : true,
510             mint_condition : true,
511             circ_lib : true,
512             ref : true,
513             circ_modifier : true,
514             circ_as_type : true,
515             location : true,
516             holdable : true,
517             age_protect : true
518         }
519     };
520
521     $scope.saveDefaults = function () {
522         egCore.hatch.setItem('cat.copy.defaults', $scope.defaults);
523     }
524
525     $scope.fetchDefaults = function () {
526         egCore.hatch.getItem('cat.copy.defaults').then(function(t) {
527             if (t) {
528                 $scope.defaults = t;
529                 if (!$scope.batch) $scope.batch = {};
530                 $scope.batch.classification = $scope.defaults.classification;
531                 $scope.batch.prefix = $scope.defaults.prefix;
532                 $scope.batch.suffix = $scope.defaults.suffix;
533                 $scope.working.statcat_filter = $scope.defaults.statcat_filter;
534                 if ($scope.defaults.always_vols) $scope.show_vols = true;
535             }
536         });
537     }
538     $scope.fetchDefaults();
539
540     $scope.dirty = false;
541
542     $scope.show_vols = true;
543     $scope.show_copies = true;
544
545     $scope.tracker = function (x,f) { if (x) return x[f]() };
546     $scope.idTracker = function (x) { if (x) return $scope.tracker(x,'id') };
547     $scope.cant_have_vols = function (id) { return !egCore.org.CanHaveVolumes(id); };
548
549     $scope.orgById = function (id) { return egCore.org.get(id) }
550     $scope.statusById = function (id) {
551         return $scope.status_list.filter( function (s) { return s.id() == id } )[0];
552     }
553     $scope.locationById = function (id) {
554         return $scope.location_cache[''+id];
555     }
556
557     $scope.workingToComplete = function () {
558         angular.forEach( $scope.workingGridControls.selectedItems(), function (c) {
559             angular.forEach( itemSvc.copies, function (w, i) {
560                 if (c === w)
561                     $scope.completed_copies = $scope.completed_copies.concat(itemSvc.copies.splice(i,1));
562             });
563         });
564     }
565
566     $scope.completeToWorking = function () {
567         angular.forEach( $scope.completedGridControls.selectedItems(), function (c) {
568             angular.forEach( $scope.completed_copies, function (w, i) {
569                 if (c === w)
570                     itemSvc.copies = itemSvc.copies.concat($scope.completed_copies.splice(i,1));
571             });
572         });
573     }
574
575     createSimpleUpdateWatcher = function (field) {
576         return $scope.$watch('working.' + field, function () {
577             var newval = $scope.working[field];
578
579             if (typeof newval != 'undefined') {
580                 if (angular.isObject(newval)) { // we'll use the pkey
581                     if (newval.id) newval = newval.id();
582                     else if (newval.code) newval = newval.code();
583                 }
584
585                 if (""+newval == "" || newval == null) {
586                     $scope.working[field] = undefined;
587                     newval = null;
588                 }
589
590                 if ($scope.workingGridControls && $scope.workingGridControls.selectedItems) {
591                     angular.forEach(
592                         $scope.workingGridControls.selectedItems(),
593                         function (cp) { cp[field](newval); cp.ischanged(1); }
594                     );
595                 }
596             }
597         });
598     }
599
600     $scope.working = {
601         statcats: {},
602         statcat_filter: undefined
603     };
604
605     $scope.statcatUpdate = function (id) {
606         var newval = $scope.working.statcats[id];
607
608         if (typeof newval != 'undefined') {
609             if (angular.isObject(newval)) { // we'll use the pkey
610                 newval = newval.id();
611             }
612     
613             if (""+newval == "" || newval == null) {
614                 $scope.working.statcats[id] = undefined;
615                 newval = null;
616             }
617     
618             if (!$scope.in_item_select && $scope.workingGridControls && $scope.workingGridControls.selectedItems) {
619                 angular.forEach(
620                     $scope.workingGridControls.selectedItems(),
621                     function (cp) {
622                         cp.stat_cat_entries(
623                             angular.forEach( cp.stat_cat_entries(), function (e) {
624                                 if (e.stat_cat() == id) { // mark deleted
625                                     e.isdeleted(1);
626                                 }
627                             })
628                         );
629     
630                         if (newval) {
631                             var e = new egCore.idl.ascecm();
632                             e.isnew( 1 );
633                             e.owning_copy( cp.id() );
634                             e.stat_cat( id );
635                             e.stat_cat_entry( newval );
636
637                             cp.stat_cat_entries(
638                                 cp.stat_cat_entries().concat([ e ])
639                             );
640
641                         }
642
643                         cp.stat_cat_entries( // trim out ephemeral deleted ones
644                             cp.stat_cat_entries().filter(function (e) {
645                                 if (Boolean(e.isnew())) {
646                                     if (Boolean(e.isdeleted())) {
647                                         return false;
648                                     }
649                                 }
650                                 return true;
651                             })
652                         );
653    
654                         cp.ischanged(1);
655                     }
656                 );
657             }
658         }
659     }
660
661     $scope.applyTemplate = function (n) {
662         angular.forEach($scope.templates[n], function (v,k) {
663             if (!angular.isObject(v)) {
664                 $scope.working[k] = angular.copy(v);
665             } else {
666                 angular.forEach(v, function (sv,sk) {
667                     $scope.working[k][sk] = angular.copy(sv);
668                     if (k == 'statcats') $scope.statcatUpdate(sk);
669                 });
670             }
671         });
672         $scope.template_name = '';
673     }
674
675     var dataKey = $routeParams.dataKey;
676     console.debug('dataKey: ' + dataKey);
677
678     if (dataKey && dataKey.length > 0) {
679
680         $scope.templates = {};
681         $scope.template_name = '';
682         $scope.template_name_list = [];
683
684         $scope.fetchTemplates = function () {
685             egCore.hatch.getItem('cat.copy.templates').then(function(t) {
686                 if (t) {
687                     $scope.templates = t;
688                     $scope.template_name_list = Object.keys(t);
689                 }
690             });
691         }
692         $scope.fetchTemplates();
693  
694         $scope.copytab = 'working';
695         $scope.tab = 'edit';
696         $scope.summaryRecord = null;
697         $scope.record_id = null;
698         $scope.data = {};
699         $scope.completed_copies = [];
700         $scope.location_orgs = [];
701         $scope.location_cache = {};
702         $scope.statcats = [];
703         if (!$scope.batch) $scope.batch = {};
704
705         $scope.applyBatchCNValues = function () {
706             if ($scope.data.tree) {
707                 angular.forEach($scope.data.tree, function(cn_hash) {
708                     angular.forEach(cn_hash, function(copies) {
709                         angular.forEach(copies, function(cp) {
710                             if (typeof $scope.batch.classification != 'undefined' && $scope.batch.classification != '')
711                                 cp.call_number().label_class($scope.batch.classification);
712                             if (typeof $scope.batch.prefix != 'undefined' && $scope.batch.prefix != '')
713                                 cp.call_number().prefix($scope.batch.prefix);
714                             if (typeof $scope.batch.label != 'undefined' && $scope.batch.label != '')
715                                 cp.call_number().label($scope.batch.label);
716                             if (typeof $scope.batch.suffix != 'undefined' && $scope.batch.suffix != '')
717                                 cp.call_number().suffix($scope.batch.suffix);
718                         });
719                     });
720                 });
721             }
722         }
723
724         $scope.clearWorking = function () {
725             angular.forEach($scope.working, function (v,k,o) {
726                 if (!angular.isObject(v)) {
727                     if (typeof v != 'undefined')
728                         $scope.working[k] = undefined;
729                 } else if (k != 'circ_lib') {
730                     angular.forEach(v, function (sv,sk) {
731                         if (typeof v != 'undefined')
732                             $scope.working[k][sk] = undefined;
733                     });
734                 }
735             });
736             $scope.working.circ_lib = undefined; // special
737         }
738
739         $scope.completedGridDataProvider = egGridDataProvider.instance({
740             get : function(offset, count) {
741                 //return provider.arrayNotifier(itemSvc.copies, offset, count);
742                 return this.arrayNotifier($scope.completed_copies, offset, count);
743             }
744         });
745
746         $scope.completedGridControls = {};
747
748         $scope.workingGridDataProvider = egGridDataProvider.instance({
749             get : function(offset, count) {
750                 //return provider.arrayNotifier(itemSvc.copies, offset, count);
751                 return this.arrayNotifier(itemSvc.copies, offset, count);
752             }
753         });
754
755         $scope.workingGridControls = {};
756
757         egNet.request(
758             'open-ils.actor',
759             'open-ils.actor.anon_cache.get_value',
760             dataKey, 'edit-these-copies'
761         ).then(function (data) {
762
763             if (data) {
764                 if (data.hide_vols && !$scope.defaults.always_vols) $scope.show_vols = false;
765                 if (data.hide_copies) $scope.show_copies = false;
766
767                 $scope.record_id = data.record_id;
768
769                 if (data.copies && data.copies.length)
770                     return itemSvc.fetchIds(data.copies);
771
772                 if (data.raw && data.raw.length) {
773
774                     /* data.raw must be an array of copies with (at least)
775                      * the call number fleshed on each.  For new copies
776                      * create from whole cloth, the id for each should
777                      * probably be negative and isnew() should return true.
778                      * Each /distinct/ call number must have a distinct id
779                      * as well, probably negative also if they're new. Clear?
780                      */
781
782                     angular.forEach(
783                         data.raw,
784                         function (cp) { itemSvc.addCopy(cp) }
785                     );
786
787                     return itemSvc.copies;
788                 }
789             }
790
791         }).then( function() {
792             $scope.data = itemSvc;
793             $scope.workingGridDataProvider.refresh();
794         });
795
796         $scope.in_item_select = false;
797         $scope.afterItemSelect = function() { $scope.in_item_select = false };
798         $scope.handleItemSelect = function (item_list) {
799             if (item_list && item_list.length > 0) {
800                 $scope.in_item_select = true;
801
802                 angular.forEach(Object.keys($scope.defaults.attributes), function (attr) {
803
804                     var value_hash = {};
805                     angular.forEach(item_list, function (item) {
806                         if (item[attr]) {
807                             var v = item[attr]()
808                             if (angular.isObject(v)) {
809                                 if (v.id) v = v.id();
810                                 else if (v.code) v = v.code();
811                             }
812                             value_hash[v] = 1;
813                         }
814                     });
815
816                     if (Object.keys(value_hash).length == 1) {
817                         if (attr == 'circ_lib') {
818                             $scope.working[attr] = egCore.org.get(item_list[0][attr]());
819                         } else {
820                             $scope.working[attr] = item_list[0][attr]();
821                         }
822                     } else {
823                         $scope.working[attr] = undefined;
824                     }
825                 });
826
827                 angular.forEach($scope.statcats, function (sc) {
828
829                     var counter = -1;
830                     var value_hash = {};
831                     var none = false;
832                     angular.forEach(item_list, function (item) {
833                         if (item.stat_cat_entries().length > 0) {
834                             var right_sc = item.stat_cat_entries().filter(function (e) {
835                                 return e.stat_cat() == sc.id() && !Boolean(e.isdeleted());
836                             });
837
838                             if (right_sc.length > 0) {
839                                 value_hash[right_sc[0].stat_cat_entry()] = right_sc[0].stat_cat_entry();
840                             } else {
841                                 none = true;
842                             }
843                         } else {
844                             none = true;
845                         }
846                     });
847
848                     if (!none && Object.keys(value_hash).length == 1) {
849                         $scope.working.statcats[sc.id()] = value_hash[Object.keys(value_hash)[0]];
850                     } else {
851                         $scope.working.statcats[sc.id()] = undefined;
852                     }
853                 });
854
855             } else {
856                 $scope.clearWorking();
857             }
858
859         }
860
861         $scope.$watch('data.copies.length', function () {
862             if ($scope.data.copies) {
863                 var base_orgs = $scope.data.copies.map(function(cp){
864                     return cp.circ_lib()
865                 }).concat(
866                     $scope.data.copies.map(function(cp){
867                         return cp.call_number().owning_lib()
868                     })
869                 ).concat(
870                     [egCore.auth.user().ws_ou()]
871                 ).filter(function(e,i,a){
872                     return a.lastIndexOf(e) === i;
873                 });
874
875                 var all_orgs = [];
876                 angular.forEach(base_orgs, function(o) {
877                     all_orgs = all_orgs.concat( egCore.org.fullPath(o, true) );
878                 });
879
880                 var final_orgs = all_orgs.filter(function(e,i,a){
881                     return a.lastIndexOf(e) === i;
882                 }).sort(function(a,b){return b-a});
883
884                 if ($scope.location_orgs.toString() != final_orgs.toString()) {
885                     $scope.location_orgs = final_orgs;
886                     if ($scope.location_orgs.length) {
887                         itemSvc.get_locations($scope.location_orgs).then(function(list){
888                             angular.forEach(list, function(l) {
889                                 $scope.location_cache[ ''+l.id() ] = l;
890                             });
891                             $scope.location_list = list;
892                         });
893
894                         $scope.statcat_filter_list = [];
895                         angular.forEach($scope.location_orgs, function (o) {
896                             $scope.statcat_filter_list.push(egCore.org.get(o));
897                         });
898
899                         itemSvc.get_statcats($scope.location_orgs).then(function(list){
900                             $scope.statcats = list;
901                             angular.forEach($scope.statcats, function (s) {
902
903                                 if (!$scope.working)
904                                     $scope.working = { statcats: {}, statcat_filter: undefined};
905                                 if (!$scope.working.statcats)
906                                     $scope.working.statcats = {};
907
908                                 if (!$scope.in_item_select) {
909                                     $scope.working.statcats[s.id()] = undefined;
910                                 }
911                                 createStatcatUpdateWatcher(s.id());
912                             });
913                             $scope.in_item_select = false;
914                         });
915                     }
916                 }
917             }
918
919             $scope.workingGridDataProvider.refresh();
920         });
921
922         $scope.suffix_list = [];
923         itemSvc.get_suffixes(egCore.auth.user().ws_ou()).then(function(list){
924             $scope.suffix_list = list;
925         });
926
927         $scope.prefix_list = [];
928         itemSvc.get_prefixes(egCore.auth.user().ws_ou()).then(function(list){
929             $scope.prefix_list = list;
930         });
931
932         $scope.classification_list = [];
933         itemSvc.get_classifications().then(function(list){
934             $scope.classification_list = list;
935         });
936
937         $scope.$watch('completed_copies.length', function () {
938             $scope.completedGridDataProvider.refresh();
939         });
940
941         $scope.location_list = [];
942         itemSvc.get_locations().then(function(list){
943             $scope.location_list = list;
944         });
945         createSimpleUpdateWatcher('location');
946
947         $scope.status_list = [];
948         itemSvc.get_statuses().then(function(list){
949             $scope.status_list = list;
950         });
951         createSimpleUpdateWatcher('status');
952
953         $scope.circ_modifier_list = [];
954         itemSvc.get_circ_mods().then(function(list){
955             $scope.circ_modifier_list = list;
956         });
957         createSimpleUpdateWatcher('circ_modifier');
958
959         $scope.circ_type_list = [];
960         itemSvc.get_circ_types().then(function(list){
961             $scope.circ_type_list = list;
962         });
963         createSimpleUpdateWatcher('circ_as_type');
964
965         $scope.age_protect_list = [];
966         itemSvc.get_age_protects().then(function(list){
967             $scope.age_protect_list = list;
968         });
969         createSimpleUpdateWatcher('age_protect');
970
971         createSimpleUpdateWatcher('circ_lib');
972         createSimpleUpdateWatcher('circulate');
973         createSimpleUpdateWatcher('holdable');
974         createSimpleUpdateWatcher('fine_level');
975         createSimpleUpdateWatcher('loan_duration');
976         createSimpleUpdateWatcher('cost');
977         createSimpleUpdateWatcher('deposit');
978         createSimpleUpdateWatcher('deposit_amount');
979         createSimpleUpdateWatcher('mint_condition');
980         createSimpleUpdateWatcher('opac_visible');
981         createSimpleUpdateWatcher('ref');
982
983     }
984
985 }])
986
987 .directive("egVolTemplate", function () {
988     return {
989         restrict: 'E',
990         replace: true,
991         template: '<div ng-include="'+"'/eg/staff/cat/volcopy/t_attr_edit'"+'"></div>',
992         scope: { },
993         controller : ['$scope','itemSvc','egCore',
994             function ( $scope , itemSvc , egCore ) {
995
996                 $scope.defaults = { // If defaults are not set at all, allow everything
997                     statcats : true,
998                     attributes : {
999                         status : true,
1000                         loan_duration : true,
1001                         fine_level : true,
1002                         cost : true,
1003                         alerts : true,
1004                         deposit : true,
1005                         deposit_amount : true,
1006                         opac_visible : true,
1007                         price : true,
1008                         circulate : true,
1009                         mint_condition : true,
1010                         circ_lib : true,
1011                         ref : true,
1012                         circ_modifier : true,
1013                         circ_as_type : true,
1014                         location : true,
1015                         holdable : true,
1016                         age_protect : true
1017                     }
1018                 };
1019
1020                 $scope.fetchDefaults = function () {
1021                     egCore.hatch.getItem('cat.copy.defaults').then(function(t) {
1022                         if (t) {
1023                             $scope.defaults = t;
1024                             $scope.working.statcat_filter = $scope.defaults.statcat_filter;
1025                         }
1026                     });
1027                 }
1028                 $scope.fetchDefaults();
1029
1030                 $scope.dirty = false;
1031                 $scope.template_controls = true;
1032
1033                 $scope.fetchTemplates = function () {
1034                     egCore.hatch.getItem('cat.copy.templates').then(function(t) {
1035                         if (t) {
1036                             $scope.templates = t;
1037                             $scope.template_name_list = Object.keys(t);
1038                         }
1039                     });
1040                 }
1041                 $scope.fetchTemplates();
1042             
1043                 $scope.applyTemplate = function (n) {
1044                     angular.forEach($scope.templates[n], function (v,k) {
1045                         if (!angular.isObject(v)) {
1046                             $scope.working[k] = angular.copy(v);
1047                         } else {
1048                             angular.forEach(v, function (sv,sk) {
1049                                 $scope.working[k][sk] = angular.copy(sv);
1050                             });
1051                         }
1052                     });
1053                     $scope.template_name = '';
1054                 }
1055
1056                 $scope.deleteTemplate = function (n) {
1057                     if (n) {
1058                         delete $scope.templates[n]
1059                         $scope.template_name_list = Object.keys($scope.templates);
1060                         $scope.template_name = '';
1061                         egCore.hatch.setItem('cat.copy.templates', $scope.templates);
1062                         $scope.$parent.fetchTemplates();
1063                     }
1064                 }
1065
1066                 $scope.saveTemplate = function (n) {
1067                     if (n) {
1068                         var tmpl = {};
1069             
1070                         angular.forEach($scope.working, function (v,k) {
1071                             if (angular.isObject(v)) { // we'll use the pkey
1072                                 if (v.id) v = v.id();
1073                                 else if (v.code) v = v.code();
1074                             }
1075             
1076                             tmpl[k] = v;
1077                         });
1078             
1079                         $scope.templates[n] = tmpl;
1080                         $scope.template_name_list = Object.keys($scope.templates);
1081             
1082                         egCore.hatch.setItem('cat.copy.templates', $scope.templates);
1083                         $scope.$parent.fetchTemplates();
1084                     }
1085                 }
1086             
1087                 $scope.templates = {};
1088                 $scope.template_name = '';
1089                 $scope.template_name_list = [];
1090             
1091                 $scope.tracker = function (x,f) { if (x) return x[f]() };
1092                 $scope.idTracker = function (x) { if (x) return $scope.tracker(x,'id') };
1093                 $scope.cant_have_vols = function (id) { return !egCore.org.CanHaveVolumes(id); };
1094             
1095                 $scope.orgById = function (id) { return egCore.org.get(id) }
1096                 $scope.statusById = function (id) {
1097                     return $scope.status_list.filter( function (s) { return s.id() == id } )[0];
1098                 }
1099                 $scope.locationById = function (id) {
1100                     return $scope.location_cache[''+id];
1101                 }
1102             
1103                 createSimpleUpdateWatcher = function (field) {
1104                     $scope.$watch('working.' + field, function () {
1105                         var newval = $scope.working[field];
1106             
1107                         if (typeof newval != 'undefined') {
1108                             $scope.dirty = true;
1109                             if (angular.isObject(newval)) { // we'll use the pkey
1110                                 if (newval.id) $scope.working[field] = newval.id();
1111                                 else if (newval.code) $scope.working[field] = newval.code();
1112                             }
1113             
1114                             if (""+newval == "" || newval == null) {
1115                                 $scope.working[field] = undefined;
1116                             }
1117             
1118                         }
1119                     });
1120                 }
1121             
1122                 $scope.working = {
1123                     statcats: {},
1124                     statcat_filter: undefined
1125                 };
1126             
1127                 createStatcatUpdateWatcher = function (id) {
1128                     return $scope.$watch('working.statcats[' + id + ']', function () {
1129                         if ($scope.working.statcats) {
1130                             var newval = $scope.working.statcats[id];
1131                 
1132                             if (typeof newval != 'undefined') {
1133                                 if (angular.isObject(newval)) { // we'll use the pkey
1134                                     newval = newval.id();
1135                                 }
1136                 
1137                                 if (""+newval == "" || newval == null) {
1138                                     $scope.working.statcats[id] = undefined;
1139                                     newval = null;
1140                                 }
1141                 
1142                             }
1143                         }
1144                     });
1145                 }
1146
1147                 $scope.clearWorking = function () {
1148                     angular.forEach($scope.working, function (v,k,o) {
1149                         if (!angular.isObject(v)) {
1150                             if (typeof v != 'undefined')
1151                                 $scope.working[k] = undefined;
1152                         } else if (k != 'circ_lib') {
1153                             angular.forEach(v, function (sv,sk) {
1154                                 $scope.working[k][sk] = undefined;
1155                             });
1156                         }
1157                     });
1158                     $scope.working.circ_lib = undefined; // special
1159                 }
1160
1161                 $scope.working = {};
1162                 $scope.location_orgs = [];
1163                 $scope.location_cache = {};
1164             
1165                 $scope.location_list = [];
1166                 itemSvc.get_locations(
1167                     egCore.org.fullPath( egCore.auth.user().ws_ou(), true )
1168                 ).then(function(list){
1169                     $scope.location_list = list;
1170                 });
1171                 createSimpleUpdateWatcher('location');
1172
1173                 $scope.statcat_filter_list = egCore.org.fullPath( egCore.auth.user().ws_ou() );
1174
1175                 $scope.statcats = [];
1176                 itemSvc.get_statcats(
1177                     egCore.org.fullPath( egCore.auth.user().ws_ou(), true )
1178                 ).then(function(list){
1179                     $scope.statcats = list;
1180                     angular.forEach($scope.statcats, function (s) {
1181
1182                         if (!$scope.working)
1183                             $scope.working = { statcats: {}, statcat_filter: undefined};
1184                         if (!$scope.working.statcats)
1185                             $scope.working.statcats = {};
1186
1187                         $scope.working.statcats[s.id()] = undefined;
1188                         createStatcatUpdateWatcher(s.id());
1189                     });
1190                 });
1191             
1192                 $scope.status_list = [];
1193                 itemSvc.get_statuses().then(function(list){
1194                     $scope.status_list = list;
1195                 });
1196                 createSimpleUpdateWatcher('status');
1197             
1198                 $scope.circ_modifier_list = [];
1199                 itemSvc.get_circ_mods().then(function(list){
1200                     $scope.circ_modifier_list = list;
1201                 });
1202                 createSimpleUpdateWatcher('circ_modifier');
1203             
1204                 $scope.circ_type_list = [];
1205                 itemSvc.get_circ_types().then(function(list){
1206                     $scope.circ_type_list = list;
1207                 });
1208                 createSimpleUpdateWatcher('circ_as_type');
1209             
1210                 $scope.age_protect_list = [];
1211                 itemSvc.get_age_protects().then(function(list){
1212                     $scope.age_protect_list = list;
1213                 });
1214                 createSimpleUpdateWatcher('age_protect');
1215             
1216                 createSimpleUpdateWatcher('circ_lib');
1217                 createSimpleUpdateWatcher('circulate');
1218                 createSimpleUpdateWatcher('holdable');
1219                 createSimpleUpdateWatcher('fine_level');
1220                 createSimpleUpdateWatcher('loan_duration');
1221                 createSimpleUpdateWatcher('cost');
1222                 createSimpleUpdateWatcher('deposit');
1223                 createSimpleUpdateWatcher('deposit_amount');
1224                 createSimpleUpdateWatcher('mint_condition');
1225                 createSimpleUpdateWatcher('opac_visible');
1226                 createSimpleUpdateWatcher('ref');
1227             }
1228         ]
1229     }
1230 })
1231
1232