]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/circ/services/item.js
LP#1685929 - Prep check-in grid for print spine labels
[working/Evergreen.git] / Open-ILS / web / js / ui / default / staff / circ / services / item.js
1 /**
2  * Shared item services for circulation
3  */
4
5 angular.module('egCoreMod')
6     .factory('egItem',
7     ['egCore','egCirc','$uibModal','$q','$timeout','$window','egConfirmDialog','egAlertDialog',
8 function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog, egAlertDialog ) {
9
10     var service = {
11         copies : [], // copy barcode search results
12         index : 0 // search grid index
13     };
14
15     service.flesh = {   
16         flesh : 3, 
17         flesh_fields : {
18             acp : ['call_number','location','status','location','floating','circ_modifier',
19                 'age_protect','circ_lib'],
20             acn : ['record','prefix','suffix','label_class'],
21             bre : ['simple_record','creator','editor']
22         },
23         select : { 
24             // avoid fleshing MARC on the bre
25             // note: don't add simple_record.. not sure why
26             bre : ['id','tcn_value','creator','editor'],
27         } 
28     }
29
30     service.circFlesh = {
31         flesh : 2,
32         flesh_fields : {
33             circ : [
34                 'usr',
35                 'workstation',
36                 'checkin_workstation',
37                 'checkin_lib',
38                 'duration_rule',
39                 'max_fine_rule',
40                 'recurring_fine_rule'
41             ],
42             au : ['card']
43         },
44         order_by : {circ : 'xact_start desc'},
45         limit :  1
46     }
47
48     //Retrieve separate copy, aacs, and accs information
49     service.getCopy = function(barcode, id) {
50         if (barcode) {
51             return egCore.pcrud
52                 .search('acp', {barcode : barcode, deleted : 'f'}, service.flesh)
53                 .then(function(copy) {return copy});
54         }
55
56         return egCore.pcrud.retrieve( 'acp', id, service.flesh)
57             .then(function(copy) {return copy});
58     }
59
60     service.getCirc = function(id) {
61         return egCore.pcrud.search('aacs', { target_copy : id },
62             service.circFlesh).then(function(circ) {return circ});
63     }
64
65     service.getSummary = function(id) {
66         return circ_summary = egCore.net.request(
67             'open-ils.circ',
68             'open-ils.circ.renewal_chain.retrieve_by_circ.summary',
69             egCore.auth.token(), id).then(
70                 function(circ_summary) {return circ_summary});
71     }
72
73     //Combine copy, circ, and accs information
74     service.retrieveCopyData = function(barcode, id) {
75         var copyData = {};
76
77         var fetchCopy = function(barcode, id) {
78             return service.getCopy(barcode, id)
79                 .then(function(copy) {
80                     copyData.copy = copy;
81                     return copyData;
82                 });
83         }
84         var fetchCirc = function(copy) {
85             return service.getCirc(copy.id())
86                 .then(function(circ) {
87                     copyData.circ = circ;
88                     return copyData;
89                 });
90         }
91         var fetchSummary = function(circ) {
92             return service.getSummary(circ.id())
93                 .then(function(summary) {
94                     copyData.circ_summary = summary;
95                     return copyData;
96                 });
97         }
98         return fetchCopy(barcode, id).then(function(res) {
99             return fetchCirc(copyData.copy).then(function(res) {
100                 if (copyData.circ) {
101                     return fetchSummary(copyData.circ).then(function() {
102                         return copyData;
103                     });
104                 } else {
105                     return copyData;
106                 }
107             });
108         });
109
110     }
111
112     // resolved with the last received copy
113     service.fetch = function(barcode, id, noListDupes) {
114         var copy;
115         var circ;
116         var circ_summary;
117         var lastRes = {};
118
119         return service.retrieveCopyData(barcode, id)
120         .then(function(copyData) {
121             //Make sure we're getting a completed copyData - no plain acp or circ objects
122             if (copyData.circ) {
123                 // flesh circ_lib locally
124                 copyData.circ.circ_lib(egCore.org.get(copyData.circ.circ_lib()));
125                 copyData.circ.checkin_workstation(
126                     egCore.org.get(copyData.circ.checkin_workstation()));
127             }
128             var flatCopy;
129
130             if (noListDupes) {
131                 // use the existing copy if possible
132                 flatCopy = service.copies.filter(
133                     function(c) {return c.id == copyData.copy.id()})[0];
134             }
135
136             if (!flatCopy) {
137                 flatCopy = egCore.idl.toHash(copyData.copy, true);
138
139                 if (copyData.circ) {
140                     flatCopy._circ = egCore.idl.toHash(copyData.circ, true);
141                     flatCopy._circ_summary = egCore.idl.toHash(copyData.circ_summary, true);
142                 }
143                 flatCopy.index = service.index++;
144                 service.copies.unshift(flatCopy);
145             }
146
147             //Get in-house use count
148             egCore.pcrud.search('aihu',
149                 {item : flatCopy.id}, {}, {idlist : true, atomic : true})
150             .then(function(uses) {
151                 flatCopy._inHouseUseCount = uses.length;
152                 copyData.copy._inHouseUseCount = uses.length;
153             });
154
155             return lastRes = {
156                 copy : copyData.copy,
157                 index : flatCopy.index
158             }
159         });
160
161
162     }
163
164     service.add_copies_to_bucket = function(copy_list) {
165         if (copy_list.length == 0) return;
166
167         return $uibModal.open({
168             templateUrl: './cat/catalog/t_add_to_bucket',
169             animation: true,
170             size: 'md',
171             controller:
172                    ['$scope','$uibModalInstance',
173             function($scope , $uibModalInstance) {
174
175                 $scope.bucket_id = 0;
176                 $scope.newBucketName = '';
177                 $scope.allBuckets = [];
178
179                 egCore.net.request(
180                     'open-ils.actor',
181                     'open-ils.actor.container.retrieve_by_class.authoritative',
182                     egCore.auth.token(), egCore.auth.user().id(),
183                     'copy', 'staff_client'
184                 ).then(function(buckets) { $scope.allBuckets = buckets; });
185
186                 $scope.add_to_bucket = function() {
187                     var promises = [];
188                     angular.forEach(copy_list, function (cp) {
189                         var item = new egCore.idl.ccbi()
190                         item.bucket($scope.bucket_id);
191                         item.target_copy(cp);
192                         promises.push(
193                             egCore.net.request(
194                                 'open-ils.actor',
195                                 'open-ils.actor.container.item.create',
196                                 egCore.auth.token(), 'copy', item
197                             )
198                         );
199
200                         return $q.all(promises).then(function() {
201                             $uibModalInstance.close();
202                         });
203                     });
204                 }
205
206                 $scope.add_to_new_bucket = function() {
207                     var bucket = new egCore.idl.ccb();
208                     bucket.owner(egCore.auth.user().id());
209                     bucket.name($scope.newBucketName);
210                     bucket.description('');
211                     bucket.btype('staff_client');
212
213                     return egCore.net.request(
214                         'open-ils.actor',
215                         'open-ils.actor.container.create',
216                         egCore.auth.token(), 'copy', bucket
217                     ).then(function(bucket) {
218                         $scope.bucket_id = bucket;
219                         $scope.add_to_bucket();
220                     });
221                 }
222
223                 $scope.cancel = function() {
224                     $uibModalInstance.dismiss();
225                 }
226             }]
227         });
228     }
229
230     service.make_copies_bookable = function(items) {
231
232         var copies_by_record = {};
233         var record_list = [];
234         angular.forEach(
235             items,
236             function (item) {
237                 var record_id = item['call_number.record.id'];
238                 if (typeof copies_by_record[ record_id ] == 'undefined') {
239                     copies_by_record[ record_id ] = [];
240                     record_list.push( record_id );
241                 }
242                 copies_by_record[ record_id ].push(item.id);
243             }
244         );
245
246         var promises = [];
247         var combined_results = [];
248         angular.forEach(record_list, function(record_id) {
249             promises.push(
250                 egCore.net.request(
251                     'open-ils.booking',
252                     'open-ils.booking.resources.create_from_copies',
253                     egCore.auth.token(),
254                     copies_by_record[record_id]
255                 ).then(function(results) {
256                     if (results && results['brsrc']) {
257                         combined_results = combined_results.concat(results['brsrc']);
258                     }
259                 })
260             );
261         });
262
263         $q.all(promises).then(function() {
264             if (combined_results.length > 0) {
265                 $uibModal.open({
266                     template: '<eg-embed-frame url="booking_admin_url" handlers="funcs"></eg-embed-frame>',
267                     animation: true,
268                     size: 'md',
269                     controller:
270                            ['$scope','$location','egCore','$uibModalInstance',
271                     function($scope , $location , egCore , $uibModalInstance) {
272
273                         $scope.funcs = {
274                             ses : egCore.auth.token(),
275                             resultant_brsrc : combined_results.map(function(o) { return o[0]; })
276                         }
277
278                         var booking_path = '/eg/conify/global/booking/resource';
279
280                         $scope.booking_admin_url =
281                             $location.absUrl().replace(/\/eg\/staff.*/, booking_path);
282                     }]
283                 });
284             }
285         });
286     }
287
288     service.book_copies_now = function(items) {
289         var copies_by_record = {};
290         var record_list = [];
291         angular.forEach(
292             items,
293             function (item) {
294                 var record_id = item['call_number.record.id'];
295                 if (typeof copies_by_record[ record_id ] == 'undefined') {
296                     copies_by_record[ record_id ] = [];
297                     record_list.push( record_id );
298                 }
299                 copies_by_record[ record_id ].push(item.id);
300             }
301         );
302
303         var promises = [];
304         var combined_brt = [];
305         var combined_brsrc = [];
306         angular.forEach(record_list, function(record_id) {
307             promises.push(
308                 egCore.net.request(
309                     'open-ils.booking',
310                     'open-ils.booking.resources.create_from_copies',
311                     egCore.auth.token(),
312                     copies_by_record[record_id]
313                 ).then(function(results) {
314                     if (results && results['brt']) {
315                         combined_brt = combined_brt.concat(results['brt']);
316                     }
317                     if (results && results['brsrc']) {
318                         combined_brsrc = combined_brsrc.concat(results['brsrc']);
319                     }
320                 })
321             );
322         });
323
324         $q.all(promises).then(function() {
325             if (combined_brt.length > 0 || combined_brsrc.length > 0) {
326                 $uibModal.open({
327                     template: '<eg-embed-frame url="booking_admin_url" handlers="funcs"></eg-embed-frame>',
328                     animation: true,
329                     size: 'md',
330                     controller:
331                            ['$scope','$location','egCore','$uibModalInstance',
332                     function($scope , $location , egCore , $uibModalInstance) {
333
334                         $scope.funcs = {
335                             ses : egCore.auth.token(),
336                             bresv_interface_opts : {
337                                 booking_results : {
338                                      brt : combined_brt
339                                     ,brsrc : combined_brsrc
340                                 }
341                             }
342                         }
343
344                         var booking_path = '/eg/booking/reservation';
345
346                         $scope.booking_admin_url =
347                             $location.absUrl().replace(/\/eg\/staff.*/, booking_path);
348
349                     }]
350                 });
351             }
352         });
353     }
354
355     service.requestItems = function(copy_list) {
356         if (copy_list.length == 0) return;
357
358         return $uibModal.open({
359             templateUrl: './cat/catalog/t_request_items',
360             animation: true,
361             controller:
362                    ['$scope','$uibModalInstance','egUser',
363             function($scope , $uibModalInstance , egUser) {
364                 $scope.user = null;
365                 $scope.first_user_fetch = true;
366
367                 $scope.hold_data = {
368                     hold_type : 'C',
369                     copy_list : copy_list,
370                     pickup_lib: egCore.org.get(egCore.auth.user().ws_ou()),
371                     user      : egCore.auth.user().id()
372                 };
373
374                 egUser.get( $scope.hold_data.user ).then(function(u) {
375                     $scope.user = u;
376                     $scope.barcode = u.card().barcode();
377                     $scope.user_name = egUser.format_name(u);
378                     $scope.hold_data.user = u.id();
379                 });
380
381                 $scope.user_name = '';
382                 $scope.barcode = '';
383                 $scope.$watch('barcode', function (n) {
384                     if (!$scope.first_user_fetch) {
385                         egUser.getByBarcode(n).then(function(u) {
386                             $scope.user = u;
387                             $scope.user_name = egUser.format_name(u);
388                             $scope.hold_data.user = u.id();
389                         }, function() {
390                             $scope.user = null;
391                             $scope.user_name = '';
392                             delete $scope.hold_data.user;
393                         });
394                     }
395                     $scope.first_user_fetch = false;
396                 });
397
398                 $scope.ok = function(h) {
399                     var args = {
400                         patronid  : h.user,
401                         hold_type : h.hold_type,
402                         pickup_lib: h.pickup_lib.id(),
403                         depth     : 0
404                     };
405
406                     egCore.net.request(
407                         'open-ils.circ',
408                         'open-ils.circ.holds.test_and_create.batch.override',
409                         egCore.auth.token(), args, h.copy_list
410                     );
411
412                     $uibModalInstance.close();
413                 }
414
415                 $scope.cancel = function($event) {
416                     $uibModalInstance.dismiss();
417                     $event.preventDefault();
418                 }
419             }]
420         });
421     }
422
423     service.attach_to_peer_bib = function(items) {
424         if (items.length == 0) return;
425
426         egCore.hatch.getItem('eg.cat.marked_conjoined_record').then(function(target_record) {
427             if (!target_record) return;
428
429             return $uibModal.open({
430                 templateUrl: './cat/catalog/t_conjoined_selector',
431                 animation: true,
432                 controller:
433                        ['$scope','$uibModalInstance',
434                 function($scope , $uibModalInstance) {
435                     $scope.update = false;
436
437                     $scope.peer_type = null;
438                     $scope.peer_type_list = [];
439
440                     get_peer_types = function() {
441                         if (egCore.env.bpt)
442                             return $q.when(egCore.env.bpt.list);
443
444                         return egCore.pcrud.retrieveAll('bpt', null, {atomic : true})
445                         .then(function(list) {
446                             egCore.env.absorbList(list, 'bpt');
447                             return list;
448                         });
449                     }
450
451                     get_peer_types().then(function(list){
452                         $scope.peer_type_list = list;
453                     });
454
455                     $scope.ok = function(type) {
456                         var promises = [];
457
458                         angular.forEach(items, function (cp) {
459                             var n = new egCore.idl.bpbcm();
460                             n.isnew(true);
461                             n.peer_record(target_record);
462                             n.target_copy(cp.id);
463                             n.peer_type(type);
464                             promises.push(egCore.pcrud.create(n).then(function(){service.add_barcode_to_list(cp.barcode)}));
465                         });
466
467                         return $q.all(promises).then(function(){$uibModalInstance.close()});
468                     }
469
470                     $scope.cancel = function($event) {
471                         $uibModalInstance.dismiss();
472                         $event.preventDefault();
473                     }
474                 }]
475             });
476         });
477     }
478
479     service.selectedHoldingsCopyDelete = function (items) {
480         if (items.length == 0) return;
481
482         var copy_objects = [];
483         egCore.pcrud.search('acp',
484             {deleted : 'f', id : items.map(function(el){return el.id;}) },
485             { flesh : 1, flesh_fields : { acp : ['call_number'] } }
486         ).then(function(copy) {
487             copy_objects.push(copy);
488         }).then(function() {
489
490             var cnHash = {};
491             var perCnCopies = {};
492
493             var cn_count = 0;
494             var cp_count = 0;
495
496             angular.forEach(
497                 copy_objects,
498                 function (cp) {
499                     cp.isdeleted(1);
500                     cp_count++;
501                     var cn_id = cp.call_number().id();
502                     if (!cnHash[cn_id]) {
503                         cnHash[cn_id] = cp.call_number();
504                         perCnCopies[cn_id] = [cp];
505                     } else {
506                         perCnCopies[cn_id].push(cp);
507                     }
508                     cp.call_number(cn_id); // prevent loops in JSON-ification
509                 }
510             );
511
512             angular.forEach(perCnCopies, function (v, k) {
513                 cnHash[k].copies(v);
514             });
515
516             cnList = [];
517             angular.forEach(cnHash, function (v, k) {
518                 cnList.push(v);
519             });
520
521             if (cnList.length == 0) return;
522
523             var flags = {};
524
525             egConfirmDialog.open(
526                 egCore.strings.CONFIRM_DELETE_COPIES_VOLUMES,
527                 egCore.strings.CONFIRM_DELETE_COPIES_VOLUMES_MESSAGE,
528                 {copies : cp_count, volumes : cn_count}
529             ).result.then(function() {
530                 egCore.net.request(
531                     'open-ils.cat',
532                     'open-ils.cat.asset.volume.fleshed.batch.update.override',
533                     egCore.auth.token(), cnList, 1, flags
534                 ).then(function(){
535                     angular.forEach(items, function(cp){service.add_barcode_to_list(cp.barcode)});
536                 });
537             });
538         });
539     }
540
541     service.checkin = function (items) {
542         angular.forEach(items, function (cp) {
543             egCirc.checkin({copy_barcode:cp.barcode}).then(
544                 function() { service.add_barcode_to_list(cp.barcode) }
545             );
546         });
547     }
548
549     service.renew = function (items) {
550         angular.forEach(items, function (cp) {
551             egCirc.renew({copy_barcode:cp.barcode}).then(
552                 function() { service.add_barcode_to_list(cp.barcode) }
553             );
554         });
555     }
556
557     service.cancel_transit = function (items) {
558         angular.forEach(items, function(cp) {
559             egCirc.find_copy_transit(null, {copy_barcode:cp.barcode})
560                 .then(function(t) { return egCirc.abort_transit(t.id())    })
561                 .then(function()  { return service.add_barcode_to_list(cp.barcode) });
562         });
563     }
564
565     service.selectedHoldingsDamaged = function (items) {
566         egCirc.mark_damaged(items.map(function(el){return el.id;})).then(function(){
567             angular.forEach(items, function(cp){service.add_barcode_to_list(cp.barcode)});
568         });
569     }
570
571     service.selectedHoldingsMissing = function (items) {
572         egCirc.mark_missing(items.map(function(el){return el.id;})).then(function(){
573             angular.forEach(items, function(cp){service.add_barcode_to_list(cp.barcode)});
574         });
575     }
576
577     service.gatherSelectedRecordIds = function (items) {
578         var rid_list = [];
579         angular.forEach(
580             items,
581             function (item) {
582                 if (rid_list.indexOf(item['call_number.record.id']) == -1)
583                     rid_list.push(item['call_number.record.id'])
584             }
585         );
586         return rid_list;
587     }
588
589     service.gatherSelectedVolumeIds = function (items,rid) {
590         var cn_id_list = [];
591         angular.forEach(
592             items,
593             function (item) {
594                 if (rid && item['call_number.record.id'] != rid) return;
595                 if (cn_id_list.indexOf(item['call_number.id']) == -1)
596                     cn_id_list.push(item['call_number.id'])
597             }
598         );
599         return cn_id_list;
600     }
601
602     service.gatherSelectedHoldingsIds = function (items,rid) {
603         var cp_id_list = [];
604         angular.forEach(
605             items,
606             function (item) {
607                 if (rid && item['call_number.record.id'] != rid) return;
608                 cp_id_list.push(item.id)
609             }
610         );
611         return cp_id_list;
612     }
613
614     service.spawnHoldingsAdd = function (items,use_vols,use_copies){
615         angular.forEach(service.gatherSelectedRecordIds(items), function (r) {
616             var raw = [];
617             if (use_copies) { // just a copy on existing volumes
618                 angular.forEach(service.gatherSelectedVolumeIds(items,r), function (v) {
619                     raw.push( {callnumber : v} );
620                 });
621             } else if (use_vols) {
622                 angular.forEach(
623                     service.gatherSelectedHoldingsIds(items,r),
624                     function (i) {
625                         angular.forEach(items, function(item) {
626                             if (i == item.id) raw.push({owner : item['call_number.owning_lib']});
627                         });
628                     }
629                 );
630             }
631
632             if (raw.length == 0) raw.push({});
633
634             egCore.net.request(
635                 'open-ils.actor',
636                 'open-ils.actor.anon_cache.set_value',
637                 null, 'edit-these-copies', {
638                     record_id: r,
639                     raw: raw,
640                     hide_vols : false,
641                     hide_copies : false
642                 }
643             ).then(function(key) {
644                 if (key) {
645                     var url = egCore.env.basePath + 'cat/volcopy/' + key;
646                     $timeout(function() { $window.open(url, '_blank') });
647                 } else {
648                     alert('Could not create anonymous cache key!');
649                 }
650             });
651         });
652     }
653
654     service.spawnHoldingsEdit = function (items,hide_vols,hide_copies){
655         angular.forEach(service.gatherSelectedRecordIds(items), function (r) {
656             egCore.net.request(
657                 'open-ils.actor',
658                 'open-ils.actor.anon_cache.set_value',
659                 null, 'edit-these-copies', {
660                     record_id: r,
661                     copies: service.gatherSelectedHoldingsIds(items,r),
662                     raw: {},
663                     hide_vols : hide_vols,
664                     hide_copies : hide_copies
665                 }
666             ).then(function(key) {
667                 if (key) {
668                     var url = egCore.env.basePath + 'cat/volcopy/' + key;
669                     $timeout(function() { $window.open(url, '_blank') });
670                 } else {
671                     alert('Could not create anonymous cache key!');
672                 }
673             });
674         });
675     }
676
677     service.replaceBarcodes = function(items) {
678         angular.forEach(items, function (cp) {
679             $uibModal.open({
680                 templateUrl: './cat/share/t_replace_barcode',
681                 animation: true,
682                 controller:
683                            ['$scope','$uibModalInstance',
684                     function($scope , $uibModalInstance) {
685                         $scope.isModal = true;
686                         $scope.focusBarcode = false;
687                         $scope.focusBarcode2 = true;
688                         $scope.barcode1 = cp.barcode;
689
690                         $scope.updateBarcode = function() {
691                             $scope.copyNotFound = false;
692                             $scope.updateOK = false;
693
694                             egCore.pcrud.search('acp',
695                                 {deleted : 'f', barcode : $scope.barcode1})
696                             .then(function(copy) {
697
698                                 if (!copy) {
699                                     $scope.focusBarcode = true;
700                                     $scope.copyNotFound = true;
701                                     return;
702                                 }
703
704                                 $scope.copyId = copy.id();
705                                 copy.barcode($scope.barcode2);
706
707                                 egCore.pcrud.update(copy).then(function(stat) {
708                                     $scope.updateOK = stat;
709                                     $scope.focusBarcode = true;
710                                     if (stat) service.add_barcode_to_list(copy.barcode());
711                                 });
712
713                             });
714                             $uibModalInstance.close();
715                         }
716
717                         $scope.cancel = function($event) {
718                             $uibModalInstance.dismiss();
719                             $event.preventDefault();
720                         }
721                     }
722                 ]
723             });
724         });
725     }
726
727     // this "transfers" selected copies to a new owning library,
728     // auto-creating volumes and deleting unused volumes as required.
729     service.changeItemOwningLib = function(items) {
730         var xfer_target = egCore.hatch.getLocalItem('eg.cat.volume_transfer_target');
731         if (!xfer_target || !items.length) {
732             return;
733         }
734         var vols_to_move   = {};
735         var copies_to_move = {};
736         angular.forEach(items, function(item) {
737             if (item['call_number.owning_lib'] != xfer_target) {
738                 if (item['call_number.id'] in vols_to_move) {
739                     copies_to_move[item['call_number.id']].push(item.id);
740                 } else {
741                     vols_to_move[item['call_number.id']] = {
742                         label       : item['call_number.label'],
743                         label_class : item['call_number.label_class'],
744                         record      : item['call_number.record.id'],
745                         prefix      : item['call_number.prefix.id'],
746                         suffix      : item['call_number.suffix.id']
747                     };
748                     copies_to_move[item['call_number.id']] = new Array;
749                     copies_to_move[item['call_number.id']].push(item.id);
750                 }
751             }
752         });
753
754         var promises = [];
755         angular.forEach(vols_to_move, function(vol) {
756             promises.push(egCore.net.request(
757                 'open-ils.cat',
758                 'open-ils.cat.call_number.find_or_create',
759                 egCore.auth.token(),
760                 vol.label,
761                 vol.record,
762                 xfer_target,
763                 vol.prefix,
764                 vol.suffix,
765                 vol.label_class
766             ).then(function(resp) {
767                 var evt = egCore.evt.parse(resp);
768                 if (evt) return;
769                 return egCore.net.request(
770                     'open-ils.cat',
771                     'open-ils.cat.transfer_copies_to_volume',
772                     egCore.auth.token(),
773                     resp.acn_id,
774                     copies_to_move[vol.id]
775                 );
776             }));
777         });
778
779         angular.forEach(
780             items,
781             function(cp){
782                 promises.push(
783                     function(){ service.add_barcode_to_list(cp.barcode) }
784                 )
785             }
786         );
787         $q.all(promises);
788     }
789
790     service.transferItems = function (items){
791         var xfer_target = egCore.hatch.getLocalItem('eg.cat.item_transfer_target');
792         var copy_ids = service.gatherSelectedHoldingsIds(items);
793         if (xfer_target && copy_ids.length > 0) {
794             egCore.net.request(
795                 'open-ils.cat',
796                 'open-ils.cat.transfer_copies_to_volume',
797                 egCore.auth.token(),
798                 xfer_target,
799                 copy_ids
800             ).then(
801                 function(resp) { // oncomplete
802                     var evt = egCore.evt.parse(resp);
803                     if (evt) {
804                         egConfirmDialog.open(
805                             egCore.strings.OVERRIDE_TRANSFER_COPIES_TO_MARKED_VOLUME_TITLE,
806                             egCore.strings.OVERRIDE_TRANSFER_COPIES_TO_MARKED_VOLUME_BODY,
807                             {'evt_desc': evt}
808                         ).result.then(function() {
809                             egCore.net.request(
810                                 'open-ils.cat',
811                                 'open-ils.cat.transfer_copies_to_volume.override',
812                                 egCore.auth.token(),
813                                 xfer_target,
814                                 copy_ids,
815                                 { events: ['TITLE_LAST_COPY', 'COPY_DELETE_WARNING'] }
816                             );
817                         }).then(function() {
818                             angular.forEach(items, function(cp){service.add_barcode_to_list(cp.barcode)});
819                         });
820                     } else {
821                         angular.forEach(items, function(cp){service.add_barcode_to_list(cp.barcode)});
822                     }
823
824                 },
825                 null, // onerror
826                 null // onprogress
827             );
828         }
829     }
830
831     service.mark_missing_pieces = function(copy) {
832         var b = copy.barcode();
833         var t = egCore.idl.toHash(copy.call_number()).record.title;
834         egConfirmDialog.open(
835             egCore.strings.CONFIRM_MARK_MISSING_TITLE,
836             egCore.strings.CONFIRM_MARK_MISSING_BODY,
837             { barcode : b, title : t }
838         ).result.then(function() {
839
840             // kick off mark missing
841             return egCore.net.request(
842                 'open-ils.circ',
843                 'open-ils.circ.mark_item_missing_pieces',
844                 egCore.auth.token(), copy.id()
845             )
846
847         }).then(function(resp) {
848             var evt = egCore.evt.parse(resp); // should always produce event
849
850             if (evt.textcode == 'ACTION_CIRCULATION_NOT_FOUND') {
851                 return egAlertDialog.open(
852                     egCore.strings.CIRC_NOT_FOUND, {barcode : copy.barcode()});
853             }
854
855             var payload = evt.payload;
856
857             // TODO: open copy editor inline?  new tab?
858
859             // print the missing pieces slip
860             var promise = $q.when();
861             if (payload.slip) {
862                 // wait for completion, since it may spawn a confirm dialog
863                 promise = egCore.print.print({
864                     context : 'default',
865                     content_type : 'text/html',
866                     content : payload.slip.template_output().data()
867                 });
868             }
869
870             if (payload.letter) {
871                 $scope.letter = payload.letter.template_output().data();
872             }
873
874             // apply patron penalty
875             if (payload.circ) {
876                 promise.then(function() {
877                     egCirc.create_penalty(payload.circ.usr())
878                 });
879             }
880
881         });
882     }
883
884     service.print_spine_labels = function(copies){
885
886         // delete me:
887         console.warn("Print Spine Labels is not implemented yet! See LP 1704873");
888         $timeout(function() { $window.open(egCore.env.basePath, '_blank') });
889
890         // TODO : LP# 1704873 needs to be in master
891         // for the below to work. Commented out until then
892
893         /*
894         egCore.net.request(
895             'open-ils.actor',
896             'open-ils.actor.anon_cache.set_value',
897             null, 'print-labels-these-copies', {
898                 copies : service.gatherSelectedHoldingsIds(copies)
899             }
900         ).then(function(key) {
901             if (key) {
902                 var url = egCore.env.basePath + 'cat/printlabels/' + key;
903                 $timeout(function() { $window.open(url, '_blank') });
904             } else {
905                 alert('Could not create anonymous cache key!');
906             }
907         });
908         */
909     }
910
911     return service;
912 }])