]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/cat/volcopy/app.js
webstaff: Update all instances of the call number, mainly for copy display
[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         if (egCore.env.acnp)
53             return $q.when(egCore.env.acnp.list);
54
55         return egCore.pcrud.search('acnp',
56             {owning_lib : egCore.org.fullPath(org, true)},
57             null, {atomic : true}
58         ).then(function(list) {
59             egCore.env.absorbList(list, 'acnp');
60             return list;
61         });
62
63     };
64
65     service.get_locations = function(orgs) {
66         return egCore.pcrud.search('acpl',
67             {owning_lib : orgs},
68             null, {atomic : true}
69         );
70     };
71
72     service.get_suffixes = function(org) {
73         if (egCore.env.acns)
74             return $q.when(egCore.env.acns.list);
75
76         return egCore.pcrud.search('acns',
77             {owning_lib : egCore.org.fullPath(org, true)},
78             null, {atomic : true}
79         ).then(function(list) {
80             egCore.env.absorbList(list, 'acns');
81             return list;
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'],
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-6"><input type="text" ng-model="barcode" ng-change="updateBarcode()"/></div>'+
195                 '<div class="col-xs-2"><input type="number" ng-model="copy_number" ng-change="updateCopyNo()"/></div>'+
196                 '<div class="col-xs-4"><eg-basic-combo-box list="parts" selected="part"></eg-basic-combo-box></div>'+
197             '</div>',
198
199         scope: { copy: "=", callNumber: "=" },
200         controller : ['$scope','itemSvc',
201             function ( $scope , itemSvc ) {
202                 $scope.new_part_id = 0;
203
204                 $scope.updateBarcode = function () { $scope.copy.barcode($scope.barcode); $scope.copy.ischanged(1); };
205                 $scope.updateCopyNo = function () { $scope.copy.copy_number($scope.copy_number); $scope.copy.ischanged(1); };
206                 $scope.updatePart = function () {
207                     var p = angular.filter($scope.part_list, function (x) {
208                         return x.label() == $scope.part
209                     });
210                     if (p.length > 0) { // preexisting part
211                         $scope.copy.parts(p)
212                     } else { // create one...
213                         var part = new egCore.idl.bmp();
214                         part.id( --$scope.new_part_id );
215                         part.isnew( true );
216                         part.label( $scope.part );
217                         part.record( $scope.callNumber.owning_lib() );
218                         $scope.copy.parts([part]);
219                         $scope.copy.ischanged(1);
220                     }
221                 }
222
223                 $scope.barcode = $scope.copy.barcode();
224                 $scope.copy_number = $scope.copy.copy_number();
225
226                 if ($scope.copy.parts()) {
227                     $scope.part = $scope.copy.parts()[0];
228                     if ($scope.part) $scope.part = $scope.part.label();
229                 };
230
231                 $scope.parts = [];
232                 $scope.part_list = [];
233
234                 itemSvc.get_parts($scope.callNumber.record()).then(function(list){
235                     $scope.part_list = list;
236                     angular.forEach(list, function(p){ $scope.parts.push(p.label()) });
237                 });
238
239             }
240         ]
241
242     }
243 })
244
245 .directive("egVolRow", function () {
246     return {
247         restrict: 'E',
248         replace: true,
249         transclude: true,
250         template:
251             '<div class="row">'+
252                 '<div class="col-xs-1">'+
253                     '<select ng-model="classification" ng-options="cl.name() for cl in classification_list track by idTracker(cl)"/>'+
254                 '</div>'+
255                 '<div class="col-xs-1">'+
256                     '<select ng-model="prefix" ng-change="updatePrefix()" ng-options="p.label() for p in prefix_list track by idTracker(p)"/>'+
257                 '</div>'+
258                 '<div class="col-xs-3"><input type="text" ng-change="updateLabel()" ng-model="label"/></div>'+
259                 '<div class="col-xs-1">'+
260                     '<select ng-model="suffix" ng-change="updateSuffix()" ng-options="s.label() for s in suffix_list track by idTracker(s)"/>'+
261                 '</div>'+
262                 '<div class="col-xs-1"><input type="number" ng-model="copy_count" min="{{orig_copy_count}}" ng-change="changeCPCount()"></div>'+
263                 '<div class="col-xs-5">'+
264                     '<div class="container-fluid">'+
265                         '<eg-vol-copy-edit ng-repeat="cp in copies track by idTracker(cp)" copy="cp" call-number="callNumber"></eg-vol-copy-edit>'+
266                     '</div>'+
267                 '</div>'+
268             '</div>',
269
270         scope: {allcopies: "=", copies: "=" },
271         controller : ['$scope','itemSvc','egCore',
272             function ( $scope , itemSvc , egCore ) {
273                 $scope.new_cp_id = 0;
274                 $scope.callNumber =  $scope.copies[0].call_number();
275
276                 $scope.idTracker = function (x) { if (x) return x.id() };
277
278                 $scope.suffix_list = [];
279                 itemSvc.get_suffixes($scope.callNumber.owning_lib()).then(function(list){
280                     $scope.suffix_list = list;
281                 });
282                 $scope.updateSuffix = function () {
283                     angular.forEach($scope.copies, function(cp) {
284                         cp.call_number().suffix($scope.suffix);
285                         cp.call_number().ischanged(1);
286                     });
287                 }
288
289                 $scope.prefix_list = [];
290                 itemSvc.get_prefixes($scope.callNumber.owning_lib()).then(function(list){
291                     $scope.prefix_list = list;
292                 });
293                 $scope.updatePrefix = function () {
294                     angular.forEach($scope.copies, function(cp) {
295                         cp.call_number().prefix($scope.prefix);
296                         cp.call_number().ischanged(1);
297                     });
298                 }
299
300                 $scope.classification_list = [];
301                 itemSvc.get_classifications().then(function(list){
302                     $scope.classification_list = list;
303                 });
304                 $scope.updateClassification = function () {
305                     angular.forEach($scope.copies, function(cp) {
306                         cp.call_number().label_class($scope.classification);
307                         cp.call_number().ischanged(1);
308                     });
309                 }
310
311                 $scope.classification = $scope.callNumber.label_class();
312                 $scope.prefix = $scope.callNumber.prefix();
313                 $scope.suffix = $scope.callNumber.suffix();
314
315                 $scope.label = $scope.callNumber.label();
316                 $scope.updateLabel = function () {
317                     angular.forEach($scope.copies, function(cp) {
318                         cp.call_number().label($scope.label);
319                         cp.call_number().ischanged(1);
320                     });
321                 }
322
323                 $scope.copy_count = $scope.copies.length;
324                 $scope.orig_copy_count = $scope.copy_count;
325
326                 $scope.changeCPCount = function () {
327                     while ($scope.copy_count > $scope.copies.length) {
328                         var cp = new egCore.idl.acp();
329                         cp.id( --$scope.new_cp_id );
330                         cp.isnew( true );
331                         cp.circ_lib( $scope.lib );
332                         cp.call_number( $scope.callNumber );
333                         $scope.copies.push( cp );
334                         $scope.allcopies.push( cp );
335                     }
336
337                     var how_many = $scope.copies.length - $scope.copy_count;
338                     if (how_many > 0) {
339                         var dead = $scope.copies.splice($scope.copy_count,how_many);
340                         $scope.callNumber.copies($scope.copies);
341
342                         // Trimming the global list is a bit more tricky
343                         angular.forEach( dead, function (d) {
344                             angular.forEach( $scope.allcopies, function (l, i) { 
345                                 if (l === d) $scope.allcopies.splice(i,1);
346                             });
347                         });
348                     }
349                 }
350
351             }
352         ]
353
354     }
355 })
356
357 .directive("egVolEdit", function () {
358     return {
359         restrict: 'E',
360         replace: true,
361         template:
362             '<div class="row">'+
363                 '<div class="col-xs-1"><eg-org-selector selected="owning_lib" disableTest="cant_have_vols"></eg-org-selector></div>'+
364                 '<div class="col-xs-1"><input type="number" min="{{orig_cn_count}}" ng-model="cn_count" ng-change="changeCNCount()"/></div>'+
365                 '<div class="col-xs-10">'+
366                     '<div class="container-fluid">'+
367                         '<eg-vol-row ng-repeat="(cn,copies) in struct track by cn" copies="copies" allcopies="allcopies"></eg-vol-row>'+
368                     '</div>'+
369                 '</div>'+
370             '</div>',
371
372         scope: { allcopies: "=", struct: "=", lib: "@", record: "@" },
373         controller : ['$scope','itemSvc','egCore',
374             function ( $scope , itemSvc , egCore ) {
375                 $scope.new_cn_id = 0;
376                 $scope.first_cn = Object.keys($scope.struct)[0];
377                 $scope.full_cn = $scope.struct[$scope.first_cn][0].call_number();
378
379                 $scope.cn_count = Object.keys($scope.struct).length;
380                 $scope.orig_cn_count = $scope.cn_count;
381
382                 $scope.owning_lib = egCore.org.get($scope.lib);
383                 $scope.$watch('owning_lib', function (l) {
384                     angular.forEach( $scope.struct[$scope.first_cn], function (cp) {
385                         cp.call_number().owning_lib( $scope.owning_lib.id() );
386                     });
387                 });
388
389                 $scope.cant_have_vols = function (id) { return !egCore.org.CanHaveVolumes(id); };
390
391                 $scope.$watch('cn_count', function (n) {
392                     var o = Object.keys($scope.struct).length;
393                     if (n > o) { // adding
394                         for (var i = o; o < n; o++) {
395                             var cn = new egCore.idl.acn();
396                             cn.id( --$scope.new_cn_id );
397                             cn.isnew( true );
398                             cn.owning_lib( $scope.owning_lib.id() );
399                             cn.record( $scope.full_cn.record() );
400
401                             var cp = new egCore.idl.acp();
402                             cp.id( --$scope.new_cp_id );
403                             cp.isnew( true );
404                             cp.circ_lib( $scope.owning_lib.id() );
405                             cp.call_number( cn );
406
407                             $scope.struct[cn.id()] = [cp];
408                             $scope.allcopies.push(cp);
409                         }
410                     } else if (n < o) { // removing
411                         var how_many = o - n;
412                         var list = Object
413                                 .keys($scope.struct)
414                                 .sort(function(a, b){return a-b})
415                                 .reverse();
416                         for (var i = how_many; i > 0; i--) {
417                             // Trimming the global list is a bit more tricky
418                             angular.forEach($scope.struct[list[i]], function (d) {
419                                 angular.forEach( $scope.allcopies, function (l, j) { 
420                                     if (l === d) $scope.allcopies.splice(j,1);
421                                 });
422                             });
423                             delete $scope.struct[list[i]];
424                         }
425                     }
426                 });
427             }
428         ]
429
430     }
431 })
432
433 /**
434  * Edit controller!
435  */
436 .controller('EditCtrl', 
437        ['$scope','$q','$routeParams','$location','$timeout','egCore','egNet','egGridDataProvider','itemSvc',
438 function($scope , $q , $routeParams , $location , $timeout , egCore , egNet , egGridDataProvider , itemSvc) {
439
440     $scope.show_vols = true;
441     $scope.show_copies = true;
442
443     $scope.tracker = function (x,f) { if (x) return x[f]() };
444     $scope.idTracker = function (x) { if (x) return $scope.tracker(x,'id') };
445     $scope.cant_have_vols = function (id) { return !egCore.org.CanHaveVolumes(id); };
446
447     $scope.orgById = function (id) { return egCore.org.get(id) }
448     $scope.statusById = function (id) {
449         return $scope.status_list.filter( function (s) { return s.id() == id } )[0];
450     }
451     $scope.locationById = function (id) {
452         return $scope.location_cache[''+id];
453     }
454
455     $scope.workingToComplete = function () {
456         angular.forEach( $scope.workingGridControls.selectedItems(), function (c) {
457             angular.forEach( itemSvc.copies, function (w, i) {
458                 if (c === w)
459                     $scope.completed_copies = $scope.completed_copies.concat(itemSvc.copies.splice(i,1));
460             });
461         });
462     }
463
464     $scope.completeToWorking = function () {
465         angular.forEach( $scope.completedGridControls.selectedItems(), function (c) {
466             angular.forEach( $scope.completed_copies, function (w, i) {
467                 if (c === w)
468                     itemSvc.copies = itemSvc.copies.concat($scope.completed_copies.splice(i,1));
469             });
470         });
471     }
472
473     createSimpleUpdateWatcher = function (field) {
474         $scope.$watch('working.' + field, function () {
475             var newval = $scope.working[field];
476             if (angular.isObject(newval)) { // we'll use the pkey
477                 if (newval.id) newval = newval.id();
478                 else if (newval.code) newval = newval.code();
479             }
480
481             if ($scope.workingGridControls && $scope.workingGridControls.selectedItems) {
482                 angular.forEach(
483                     $scope.workingGridControls.selectedItems(),
484                     function (cp) { cp[field](newval); cp.ischanged(1); }
485                 );
486             }
487         });
488     }
489
490     $timeout(function(){
491
492     var dataKey = $routeParams.dataKey;
493     console.debug('dataKey: ' + dataKey);
494
495     if (dataKey && dataKey.length > 0) {
496         $scope.working = {};
497
498         $scope.copytab = 'working';
499         $scope.tab = 'edit';
500         $scope.summaryRecord = null;
501         $scope.record_id = null;
502         $scope.data = {};
503         $scope.completed_copies = [];
504         $scope.location_orgs = [];
505         $scope.location_cache = {};
506
507         $scope.completedGridDataProvider = egGridDataProvider.instance({
508             get : function(offset, count) {
509                 //return provider.arrayNotifier(itemSvc.copies, offset, count);
510                 return this.arrayNotifier($scope.completed_copies, offset, count);
511             }
512         });
513
514         $scope.completedGridControls = {};
515
516         $scope.workingGridDataProvider = egGridDataProvider.instance({
517             get : function(offset, count) {
518                 //return provider.arrayNotifier(itemSvc.copies, offset, count);
519                 return this.arrayNotifier(itemSvc.copies, offset, count);
520             }
521         });
522
523         $scope.workingGridControls = {};
524
525         egNet.request(
526             'open-ils.actor',
527             'open-ils.actor.anon_cache.get_value',
528             dataKey, 'edit-these-copies'
529         ).then(function (data) {
530
531             if (data.hide_vols) $scope.show_vols = false;
532             if (data.hide_copies) $scope.show_copies = false;
533
534             $scope.record_id = data.record_id;
535
536             if (data.copies && data.copies.length)
537                 return itemSvc.fetchIds(data.copies);
538
539             if (data.raw && data.raw.length) {
540
541                 /* data.raw must be an array of copies with (at least)
542                  * the call number fleshed on each.  For new copies
543                  * create from whole cloth, the id for each should
544                  * probably be negative and isnew() should return true.
545                  * Each /distinct/ call number must have a distinct id
546                  * as well, probably negative also if they're new. Clear?
547                  */
548
549                 angular.forEach(
550                     data.raw,
551                     function (cp) { itemSvc.addCopy(cp) }
552                 );
553
554                 return itemSvc.copies;
555             }
556
557         }).then( function() {
558             $scope.data = itemSvc;
559             $scope.workingGridDataProvider.refresh();
560         });
561
562         $scope.$watch('data.copies.length', function () {
563             if ($scope.data.copies) {
564                 var base_orgs = $scope.data.copies.map(function(cp){
565                     return cp.circ_lib()
566                 }).filter(function(e,i,a){
567                     return a.lastIndexOf(e) === i;
568                 });
569
570                 var all_orgs = [];
571                 angular.forEach(base_orgs, function(o) {
572                     all_orgs = all_orgs.concat( egCore.org.fullPath(o, true) );
573                 });
574
575                 var final_orgs = all_orgs.filter(function(e,i,a){
576                     return a.lastIndexOf(e) === i;
577                 }).sort(function(a,b){return b-a});
578
579                 if ($scope.location_orgs.toString() != final_orgs.toString()) {
580                     $scope.location_orgs = final_orgs;
581                     if ($scope.location_orgs.length) {
582                         itemSvc.get_locations($scope.location_orgs).then(function(list){
583                             angular.forEach(list, function(l) {
584                                 $scope.location_cache[ ''+l.id() ] = l;
585                             });
586                             $scope.location_list = list;
587                         });
588                     }
589                 }
590             }
591
592             $scope.workingGridDataProvider.refresh();
593         });
594
595         $scope.$watch('completed_copies.length', function () {
596             $scope.completedGridDataProvider.refresh();
597         });
598
599         $scope.location_list = [];
600         itemSvc.get_locations().then(function(list){
601             $scope.location_list = list;
602         });
603         createSimpleUpdateWatcher('location');
604
605         $scope.status_list = [];
606         itemSvc.get_statuses().then(function(list){
607             $scope.status_list = list;
608         });
609         createSimpleUpdateWatcher('status');
610
611         $scope.circ_mod_list = [];
612         itemSvc.get_circ_mods().then(function(list){
613             $scope.circ_mod_list = list;
614         });
615         createSimpleUpdateWatcher('circ_modifier');
616
617         $scope.circ_type_list = [];
618         itemSvc.get_circ_types().then(function(list){
619             $scope.circ_type_list = list;
620         });
621         createSimpleUpdateWatcher('circ_as_type');
622
623         $scope.age_protect_list = [];
624         itemSvc.get_age_protects().then(function(list){
625             $scope.age_protect_list = list;
626         });
627         createSimpleUpdateWatcher('age_protect');
628
629         createSimpleUpdateWatcher('circulate');
630         createSimpleUpdateWatcher('holdable');
631         createSimpleUpdateWatcher('fine_level');
632         createSimpleUpdateWatcher('loan_duration');
633         createSimpleUpdateWatcher('cost');
634         createSimpleUpdateWatcher('deposit');
635         createSimpleUpdateWatcher('deposit_amount');
636         createSimpleUpdateWatcher('mint_condition');
637         createSimpleUpdateWatcher('opac_visible');
638         createSimpleUpdateWatcher('ref');
639
640     }
641
642     });
643
644 }])
645
646