]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
webstaff: Add a "Batch Apply" template row above call numbers
[working/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_locations = function(orgs) {
60         return egCore.pcrud.search('acpl',
61             {owning_lib : orgs},
62             null, {atomic : true}
63         );
64     };
65
66     service.get_suffixes = function(org) {
67         return egCore.pcrud.search('acns',
68             {owning_lib : egCore.org.fullPath(org, true)},
69             null, {atomic : true}
70         );
71
72     };
73
74     service.get_statuses = function() {
75         if (egCore.env.ccs)
76             return $q.when(egCore.env.ccs.list);
77
78         return egCore.pcrud.retrieveAll('ccs', {}, {atomic : true}).then(
79             function(list) {
80                 egCore.env.absorbList(list, 'ccs');
81                 return list;
82             }
83         );
84
85     };
86
87     service.get_circ_mods = function() {
88         if (egCore.env.ccm)
89             return $q.when(egCore.env.ccm.list);
90
91         return egCore.pcrud.retrieveAll('ccm', {}, {atomic : true}).then(
92             function(list) {
93                 egCore.env.absorbList(list, 'ccm');
94                 return list;
95             }
96         );
97
98     };
99
100     service.get_circ_types = function() {
101         if (egCore.env.citm)
102             return $q.when(egCore.env.citm.list);
103
104         return egCore.pcrud.retrieveAll('citm', {}, {atomic : true}).then(
105             function(list) {
106                 egCore.env.absorbList(list, 'citm');
107                 return list;
108             }
109         );
110
111     };
112
113     service.get_age_protects = function() {
114         if (egCore.env.crahp)
115             return $q.when(egCore.env.crahp.list);
116
117         return egCore.pcrud.retrieveAll('crahp', {}, {atomic : true}).then(
118             function(list) {
119                 egCore.env.absorbList(list, 'crahp');
120                 return list;
121             }
122         );
123
124     };
125
126     service.bmp_parts = {};
127     service.get_parts = function(rec) {
128         if (service.bmp_parts[rec])
129             return $q.when(service.bmp_parts[rec]);
130
131         return egCore.pcrud.search('bmp',
132             {record : rec},
133             null, {atomic : true}
134         ).then(function(list) {
135             service.bmp_parts[rec] = list;
136             return list;
137         });
138
139     };
140
141     service.flesh = {   
142         flesh : 3, 
143         flesh_fields : {
144             acp : ['call_number','parts'],
145             acn : ['label_class','prefix','suffix']
146         }
147     }
148
149     service.addCopy = function (cp) {
150
151         if (!cp.parts()) cp.parts([]); // just in case...
152
153         var lib = cp.call_number().owning_lib();
154         var cn = cp.call_number().id();
155
156         if (!service.tree[lib]) service.tree[lib] = {};
157         if (!service.tree[lib][cn]) service.tree[lib][cn] = [];
158
159         service.tree[lib][cn].push(cp);
160         service.copies.push(cp);
161     }
162
163     service.fetchIds = function(idList) {
164         service.tree = {}; // clear the tree on fetch
165         service.copies = []; // clear the copy list on fetch
166         return egCore.pcrud.search('acp', { 'id' : idList }, service.flesh).then(null,null,
167             function(copy) {
168                 service.addCopy(copy);
169             }
170         );
171     }
172
173     return service;
174 }])
175
176 .directive("egVolCopyEdit", function () {
177     return {
178         restrict: 'E',
179         replace: true,
180         template:
181             '<div class="row">'+
182                 '<div class="col-xs-6"><input type="text" ng-model="barcode" ng-change="updateBarcode()"/></div>'+
183                 '<div class="col-xs-2"><input type="number" ng-model="copy_number" ng-change="updateCopyNo()"/></div>'+
184                 '<div class="col-xs-4"><eg-basic-combo-box list="parts" selected="part"></eg-basic-combo-box></div>'+
185             '</div>',
186
187         scope: { copy: "=", callNumber: "=" },
188         controller : ['$scope','itemSvc',
189             function ( $scope , itemSvc ) {
190                 $scope.new_part_id = 0;
191
192                 $scope.updateBarcode = function () { $scope.copy.barcode($scope.barcode); $scope.copy.ischanged(1); };
193                 $scope.updateCopyNo = function () { $scope.copy.copy_number($scope.copy_number); $scope.copy.ischanged(1); };
194                 $scope.updatePart = function () {
195                     var p = $scope.part_list.filter(function (x) {
196                         return x.label() == $scope.part
197                     });
198                     if (p.length > 0) { // preexisting part
199                         $scope.copy.parts(p)
200                     } else { // create one...
201                         var part = new egCore.idl.bmp();
202                         part.id( --$scope.new_part_id );
203                         part.isnew( true );
204                         part.label( $scope.part );
205                         part.record( $scope.callNumber.owning_lib() );
206                         $scope.copy.parts([part]);
207                         $scope.copy.ischanged(1);
208                     }
209                 }
210
211                 $scope.barcode = $scope.copy.barcode();
212                 $scope.copy_number = $scope.copy.copy_number();
213
214                 if ($scope.copy.parts()) {
215                     $scope.part = $scope.copy.parts()[0];
216                     if ($scope.part) $scope.part = $scope.part.label();
217                 };
218
219                 $scope.parts = [];
220                 $scope.part_list = [];
221
222                 itemSvc.get_parts($scope.callNumber.record()).then(function(list){
223                     $scope.part_list = list;
224                     angular.forEach(list, function(p){ $scope.parts.push(p.label()) });
225                 });
226
227             }
228         ]
229
230     }
231 })
232
233 .directive("egVolRow", function () {
234     return {
235         restrict: 'E',
236         replace: true,
237         transclude: true,
238         template:
239             '<div class="row">'+
240                 '<div class="col-xs-1">'+
241                     '<select ng-model="classification" ng-options="cl.name() for cl in classification_list track by idTracker(cl)"/>'+
242                 '</div>'+
243                 '<div class="col-xs-1">'+
244                     '<select ng-model="prefix" ng-change="updatePrefix()" ng-options="p.label() for p in prefix_list track by idTracker(p)"/>'+
245                 '</div>'+
246                 '<div class="col-xs-3"><input type="text" ng-change="updateLabel()" ng-model="label"/></div>'+
247                 '<div class="col-xs-1">'+
248                     '<select ng-model="suffix" ng-change="updateSuffix()" ng-options="s.label() for s in suffix_list track by idTracker(s)"/>'+
249                 '</div>'+
250                 '<div class="col-xs-1"><input type="number" ng-model="copy_count" min="{{orig_copy_count}}" ng-change="changeCPCount()"></div>'+
251                 '<div class="col-xs-5">'+
252                     '<div class="container-fluid">'+
253                         '<eg-vol-copy-edit ng-repeat="cp in copies track by idTracker(cp)" copy="cp" call-number="callNumber"></eg-vol-copy-edit>'+
254                     '</div>'+
255                 '</div>'+
256             '</div>',
257
258         scope: {allcopies: "=", copies: "=" },
259         controller : ['$scope','itemSvc','egCore',
260             function ( $scope , itemSvc , egCore ) {
261                 $scope.new_cp_id = 0;
262                 $scope.callNumber =  $scope.copies[0].call_number();
263
264                 $scope.idTracker = function (x) { if (x) return x.id() };
265
266                 $scope.suffix_list = [];
267                 itemSvc.get_suffixes($scope.callNumber.owning_lib()).then(function(list){
268                     $scope.suffix_list = list;
269                 });
270                 $scope.updateSuffix = function () {
271                     angular.forEach($scope.copies, function(cp) {
272                         cp.call_number().suffix($scope.suffix);
273                         cp.call_number().ischanged(1);
274                     });
275                 }
276
277                 $scope.prefix_list = [];
278                 itemSvc.get_prefixes($scope.callNumber.owning_lib()).then(function(list){
279                     $scope.prefix_list = list;
280                 });
281                 $scope.updatePrefix = function () {
282                     angular.forEach($scope.copies, function(cp) {
283                         cp.call_number().prefix($scope.prefix);
284                         cp.call_number().ischanged(1);
285                     });
286                 }
287
288                 $scope.classification_list = [];
289                 itemSvc.get_classifications().then(function(list){
290                     $scope.classification_list = list;
291                 });
292                 $scope.updateClassification = function () {
293                     angular.forEach($scope.copies, function(cp) {
294                         cp.call_number().label_class($scope.classification);
295                         cp.call_number().ischanged(1);
296                     });
297                 }
298
299                 $scope.updateLabel = function () {
300                     angular.forEach($scope.copies, function(cp) {
301                         cp.call_number().label($scope.label);
302                         cp.call_number().ischanged(1);
303                     });
304                 }
305
306                 $scope.$watch('callNumber.prefix()', function (v) {
307                     if (typeof v != 'object') {
308                         $scope.prefix = $scope.prefix_list.filter(function (p) {
309                             return p.id() == v;
310                         })[0];
311                         $scope.callNumber.prefix($scope.prefix);
312                     }
313                 });
314
315                 $scope.$watch('callNumber.suffix()', function (v) {
316                     if (typeof v != 'object') {
317                         $scope.suffix = $scope.suffix_list.filter( function (s) {
318                             return s.id() == v;
319                         })[0];
320                         $scope.callNumber.suffix($scope.suffix);
321                     }
322                 });
323
324                 $scope.$watch('callNumber.label_class()', function (v) {
325                     if (typeof v != 'object') {
326                         $scope.classification = $scope.classification_list.filter(function (c) {
327                             return c.id() == v;
328                         })[0];
329                         $scope.callNumber.label_class($scope.classification);
330                     }
331                 });
332
333                 $scope.$watch('callNumber.label()', function (v) {
334                     $scope.label = v;
335                 });
336
337                 $scope.prefix = $scope.callNumber.prefix();
338                 $scope.suffix = $scope.callNumber.suffix();
339                 $scope.classification = $scope.callNumber.label_class();
340                 $scope.label = $scope.callNumber.label();
341
342                 $scope.copy_count = $scope.copies.length;
343                 $scope.orig_copy_count = $scope.copy_count;
344
345                 $scope.changeCPCount = function () {
346                     while ($scope.copy_count > $scope.copies.length) {
347                         var cp = new egCore.idl.acp();
348                         cp.id( --$scope.new_cp_id );
349                         cp.isnew( true );
350                         cp.circ_lib( $scope.lib );
351                         cp.call_number( $scope.callNumber );
352                         $scope.copies.push( cp );
353                         $scope.allcopies.push( cp );
354                     }
355
356                     var how_many = $scope.copies.length - $scope.copy_count;
357                     if (how_many > 0) {
358                         var dead = $scope.copies.splice($scope.copy_count,how_many);
359                         $scope.callNumber.copies($scope.copies);
360
361                         // Trimming the global list is a bit more tricky
362                         angular.forEach( dead, function (d) {
363                             angular.forEach( $scope.allcopies, function (l, i) { 
364                                 if (l === d) $scope.allcopies.splice(i,1);
365                             });
366                         });
367                     }
368                 }
369
370             }
371         ]
372
373     }
374 })
375
376 .directive("egVolEdit", function () {
377     return {
378         restrict: 'E',
379         replace: true,
380         template:
381             '<div class="row">'+
382                 '<div class="col-xs-1"><eg-org-selector selected="owning_lib" disableTest="cant_have_vols"></eg-org-selector></div>'+
383                 '<div class="col-xs-1"><input type="number" min="{{orig_cn_count}}" ng-model="cn_count" ng-change="changeCNCount()"/></div>'+
384                 '<div class="col-xs-10">'+
385                     '<div class="container-fluid">'+
386                         '<eg-vol-row ng-repeat="(cn,copies) in struct track by cn" copies="copies" allcopies="allcopies"></eg-vol-row>'+
387                     '</div>'+
388                 '</div>'+
389             '</div>',
390
391         scope: { allcopies: "=", struct: "=", lib: "@", record: "@" },
392         controller : ['$scope','itemSvc','egCore',
393             function ( $scope , itemSvc , egCore ) {
394                 $scope.new_cn_id = 0;
395                 $scope.first_cn = Object.keys($scope.struct)[0];
396                 $scope.full_cn = $scope.struct[$scope.first_cn][0].call_number();
397
398                 $scope.cn_count = Object.keys($scope.struct).length;
399                 $scope.orig_cn_count = $scope.cn_count;
400
401                 $scope.owning_lib = egCore.org.get($scope.lib);
402                 $scope.$watch('owning_lib', function (l) {
403                     angular.forEach( $scope.struct[$scope.first_cn], function (cp) {
404                         cp.call_number().owning_lib( $scope.owning_lib.id() );
405                     });
406                 });
407
408                 $scope.cant_have_vols = function (id) { return !egCore.org.CanHaveVolumes(id); };
409
410                 $scope.$watch('cn_count', function (n) {
411                     var o = Object.keys($scope.struct).length;
412                     if (n > o) { // adding
413                         for (var i = o; o < n; o++) {
414                             var cn = new egCore.idl.acn();
415                             cn.id( --$scope.new_cn_id );
416                             cn.isnew( true );
417                             cn.owning_lib( $scope.owning_lib.id() );
418                             cn.record( $scope.full_cn.record() );
419
420                             var cp = new egCore.idl.acp();
421                             cp.id( --$scope.new_cp_id );
422                             cp.isnew( true );
423                             cp.circ_lib( $scope.owning_lib.id() );
424                             cp.call_number( cn );
425
426                             $scope.struct[cn.id()] = [cp];
427                             $scope.allcopies.push(cp);
428                         }
429                     } else if (n < o) { // removing
430                         var how_many = o - n;
431                         var list = Object
432                                 .keys($scope.struct)
433                                 .sort(function(a, b){return a-b})
434                                 .reverse();
435                         for (var i = how_many; i > 0; i--) {
436                             // Trimming the global list is a bit more tricky
437                             angular.forEach($scope.struct[list[i]], function (d) {
438                                 angular.forEach( $scope.allcopies, function (l, j) { 
439                                     if (l === d) $scope.allcopies.splice(j,1);
440                                 });
441                             });
442                             delete $scope.struct[list[i]];
443                         }
444                     }
445                 });
446             }
447         ]
448
449     }
450 })
451
452 /**
453  * Edit controller!
454  */
455 .controller('EditCtrl', 
456        ['$scope','$q','$routeParams','$location','$timeout','egCore','egNet','egGridDataProvider','itemSvc',
457 function($scope , $q , $routeParams , $location , $timeout , egCore , egNet , egGridDataProvider , itemSvc) {
458
459     $scope.show_vols = true;
460     $scope.show_copies = true;
461
462     $scope.tracker = function (x,f) { if (x) return x[f]() };
463     $scope.idTracker = function (x) { if (x) return $scope.tracker(x,'id') };
464     $scope.cant_have_vols = function (id) { return !egCore.org.CanHaveVolumes(id); };
465
466     $scope.orgById = function (id) { return egCore.org.get(id) }
467     $scope.statusById = function (id) {
468         return $scope.status_list.filter( function (s) { return s.id() == id } )[0];
469     }
470     $scope.locationById = function (id) {
471         return $scope.location_cache[''+id];
472     }
473
474     $scope.workingToComplete = function () {
475         angular.forEach( $scope.workingGridControls.selectedItems(), function (c) {
476             angular.forEach( itemSvc.copies, function (w, i) {
477                 if (c === w)
478                     $scope.completed_copies = $scope.completed_copies.concat(itemSvc.copies.splice(i,1));
479             });
480         });
481     }
482
483     $scope.completeToWorking = function () {
484         angular.forEach( $scope.completedGridControls.selectedItems(), function (c) {
485             angular.forEach( $scope.completed_copies, function (w, i) {
486                 if (c === w)
487                     itemSvc.copies = itemSvc.copies.concat($scope.completed_copies.splice(i,1));
488             });
489         });
490     }
491
492     createSimpleUpdateWatcher = function (field) {
493         $scope.$watch('working.' + field, function () {
494             var newval = $scope.working[field];
495             if (angular.isObject(newval)) { // we'll use the pkey
496                 if (newval.id) newval = newval.id();
497                 else if (newval.code) newval = newval.code();
498             }
499
500             if ($scope.workingGridControls && $scope.workingGridControls.selectedItems) {
501                 angular.forEach(
502                     $scope.workingGridControls.selectedItems(),
503                     function (cp) { cp[field](newval); cp.ischanged(1); }
504                 );
505             }
506         });
507     }
508
509     $timeout(function(){
510
511     var dataKey = $routeParams.dataKey;
512     console.debug('dataKey: ' + dataKey);
513
514     if (dataKey && dataKey.length > 0) {
515         $scope.working = {};
516
517         $scope.copytab = 'working';
518         $scope.tab = 'edit';
519         $scope.summaryRecord = null;
520         $scope.record_id = null;
521         $scope.data = {};
522         $scope.completed_copies = [];
523         $scope.location_orgs = [];
524         $scope.location_cache = {};
525         $scope.batch = {};
526
527         $scope.applyBatchCNValues = function () {
528             if ($scope.data.tree) {
529                 angular.forEach($scope.data.tree, function(cn_hash) {
530                     angular.forEach(cn_hash, function(copies) {
531                         angular.forEach(copies, function(cp) {
532                             if (typeof $scope.batch.classification != 'undefined' && $scope.batch.classification != '')
533                                 cp.call_number().label_class($scope.batch.classification);
534                             if (typeof $scope.batch.prefix != 'undefined' && $scope.batch.prefix != '')
535                                 cp.call_number().prefix($scope.batch.prefix);
536                             if (typeof $scope.batch.label != 'undefined' && $scope.batch.label != '')
537                                 cp.call_number().label($scope.batch.label);
538                             if (typeof $scope.batch.suffix != 'undefined' && $scope.batch.suffix != '')
539                                 cp.call_number().suffix($scope.batch.suffix);
540                         });
541                     });
542                 });
543             }
544         }
545
546         $scope.completedGridDataProvider = egGridDataProvider.instance({
547             get : function(offset, count) {
548                 //return provider.arrayNotifier(itemSvc.copies, offset, count);
549                 return this.arrayNotifier($scope.completed_copies, offset, count);
550             }
551         });
552
553         $scope.completedGridControls = {};
554
555         $scope.workingGridDataProvider = egGridDataProvider.instance({
556             get : function(offset, count) {
557                 //return provider.arrayNotifier(itemSvc.copies, offset, count);
558                 return this.arrayNotifier(itemSvc.copies, offset, count);
559             }
560         });
561
562         $scope.workingGridControls = {};
563
564         egNet.request(
565             'open-ils.actor',
566             'open-ils.actor.anon_cache.get_value',
567             dataKey, 'edit-these-copies'
568         ).then(function (data) {
569
570             if (data.hide_vols) $scope.show_vols = false;
571             if (data.hide_copies) $scope.show_copies = false;
572
573             $scope.record_id = data.record_id;
574
575             if (data.copies && data.copies.length)
576                 return itemSvc.fetchIds(data.copies);
577
578             if (data.raw && data.raw.length) {
579
580                 /* data.raw must be an array of copies with (at least)
581                  * the call number fleshed on each.  For new copies
582                  * create from whole cloth, the id for each should
583                  * probably be negative and isnew() should return true.
584                  * Each /distinct/ call number must have a distinct id
585                  * as well, probably negative also if they're new. Clear?
586                  */
587
588                 angular.forEach(
589                     data.raw,
590                     function (cp) { itemSvc.addCopy(cp) }
591                 );
592
593                 return itemSvc.copies;
594             }
595
596         }).then( function() {
597             $scope.data = itemSvc;
598             $scope.workingGridDataProvider.refresh();
599         });
600
601         $scope.$watch('data.copies.length', function () {
602             if ($scope.data.copies) {
603                 var base_orgs = $scope.data.copies.map(function(cp){
604                     return cp.circ_lib()
605                 }).filter(function(e,i,a){
606                     return a.lastIndexOf(e) === i;
607                 });
608
609                 var all_orgs = [];
610                 angular.forEach(base_orgs, function(o) {
611                     all_orgs = all_orgs.concat( egCore.org.fullPath(o, true) );
612                 });
613
614                 var final_orgs = all_orgs.filter(function(e,i,a){
615                     return a.lastIndexOf(e) === i;
616                 }).sort(function(a,b){return b-a});
617
618                 if ($scope.location_orgs.toString() != final_orgs.toString()) {
619                     $scope.location_orgs = final_orgs;
620                     if ($scope.location_orgs.length) {
621                         itemSvc.get_locations($scope.location_orgs).then(function(list){
622                             angular.forEach(list, function(l) {
623                                 $scope.location_cache[ ''+l.id() ] = l;
624                             });
625                             $scope.location_list = list;
626                         });
627                     }
628                 }
629             }
630
631             $scope.workingGridDataProvider.refresh();
632         });
633
634         $scope.suffix_list = [];
635         itemSvc.get_suffixes(egCore.auth.user().ws_ou()).then(function(list){
636             $scope.suffix_list = list;
637         });
638
639         $scope.prefix_list = [];
640         itemSvc.get_prefixes(egCore.auth.user().ws_ou()).then(function(list){
641             $scope.prefix_list = list;
642         });
643
644         $scope.classification_list = [];
645         itemSvc.get_classifications().then(function(list){
646             $scope.classification_list = list;
647         });
648
649         $scope.$watch('completed_copies.length', function () {
650             $scope.completedGridDataProvider.refresh();
651         });
652
653         $scope.location_list = [];
654         itemSvc.get_locations().then(function(list){
655             $scope.location_list = list;
656         });
657         createSimpleUpdateWatcher('location');
658
659         $scope.status_list = [];
660         itemSvc.get_statuses().then(function(list){
661             $scope.status_list = list;
662         });
663         createSimpleUpdateWatcher('status');
664
665         $scope.circ_mod_list = [];
666         itemSvc.get_circ_mods().then(function(list){
667             $scope.circ_mod_list = list;
668         });
669         createSimpleUpdateWatcher('circ_modifier');
670
671         $scope.circ_type_list = [];
672         itemSvc.get_circ_types().then(function(list){
673             $scope.circ_type_list = list;
674         });
675         createSimpleUpdateWatcher('circ_as_type');
676
677         $scope.age_protect_list = [];
678         itemSvc.get_age_protects().then(function(list){
679             $scope.age_protect_list = list;
680         });
681         createSimpleUpdateWatcher('age_protect');
682
683         createSimpleUpdateWatcher('circulate');
684         createSimpleUpdateWatcher('holdable');
685         createSimpleUpdateWatcher('fine_level');
686         createSimpleUpdateWatcher('loan_duration');
687         createSimpleUpdateWatcher('cost');
688         createSimpleUpdateWatcher('deposit');
689         createSimpleUpdateWatcher('deposit_amount');
690         createSimpleUpdateWatcher('mint_condition');
691         createSimpleUpdateWatcher('opac_visible');
692         createSimpleUpdateWatcher('ref');
693
694     }
695
696     });
697
698 }])
699
700