LP##1661688 - Want easy way to clear a hold when picked up by other patron.
[working/Evergreen.git] / Open-ILS / web / js / ui / default / staff / circ / services / circ.js
index 06166df..3bf866c 100644 (file)
@@ -5,9 +5,10 @@
 angular.module('egCoreMod')
 
 .factory('egCirc',
-       ['$uibModal','$q','egCore','egAlertDialog','egConfirmDialog',
+
+       ['$uibModal','$q','egCore','egAlertDialog','egConfirmDialog','egAddCopyAlertDialog','egCopyAlertManagerDialog','egCopyAlertEditorDialog',
         'egWorkLog',
-function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
+function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,  egAddCopyAlertDialog , egCopyAlertManagerDialog,  egCopyAlertEditorDialog ,
          egWorkLog) {
 
     var service = {
@@ -26,9 +27,12 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
             'ui.staff.require_initials.patron_standing_penalty',
             'ui.admin.work_log.max_entries',
             'ui.admin.patron_log.max_entries',
-            'circ.staff_client.do_not_auto_attempt_print'
+            'circ.staff_client.do_not_auto_attempt_print',
+            'circ.clear_hold_on_checkout'
         ]).then(function(set) {
             service.require_initials = Boolean(set['ui.staff.require_initials.patron_standing_penalty']);
+           service.clearHold = Boolean(set['circ.clear_hold_on_checkout']);
+
             if (angular.isArray(set['circ.staff_client.do_not_auto_attempt_print'])) {
                 if (set['circ.staff_client.do_not_auto_attempt_print'].indexOf('Hold Slip') > 1)
                     service.never_auto_print['hold_shelf_slip'] = true;
@@ -101,6 +105,7 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
         'CIRC_CLAIMS_RETURNED',
         'COPY_ALERT_MESSAGE',
         'COPY_STATUS_LOST',
+        'COPY_STATUS_LOST_AND_PAID',
         'COPY_STATUS_LONG_OVERDUE',
         'COPY_STATUS_MISSING',
         'PATRON_EXCEEDS_FINES'
@@ -122,6 +127,8 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
     // options : non-parameter controls.  e.g. "override", "check_barcode"
     service.checkout = function(params, options) {
         if (!options) options = {};
+        params.new_copy_alerts = 1;
+
         console.debug('egCirc.checkout() : ' 
             + js2JSON(params) + ' : ' + js2JSON(options));
 
@@ -176,6 +183,7 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
     // Rejected if the renewal cannot be completed.
     service.renew = function(params, options) {
         if (!options) options = {};
+        params.new_copy_alerts = 1;
 
         console.debug('egCirc.renew() : ' 
             + js2JSON(params) + ' : ' + js2JSON(options));
@@ -223,6 +231,7 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
     // Rejected if the checkin cannot be completed.
     service.checkin = function(params, options) {
         if (!options) options = {};
+        params.new_copy_alerts = 1;
 
         console.debug('egCirc.checkin() : ' 
             + js2JSON(params) + ' : ' + js2JSON(options));
@@ -277,6 +286,14 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
             return;
         }
 
+        // retrieve call number affixes prior to sending payload data to the grid
+        if (payload.volume && typeof payload.volume.prefix() != 'object') {
+            egCore.pcrud.retrieve('acnp',payload.volume.prefix()).then(function(p) {payload.volume.prefix(p)});
+        };
+        if (payload.volume && typeof payload.volume.suffix() != 'object') {
+            egCore.pcrud.retrieve('acns',payload.volume.suffix()).then(function(s) {payload.volume.suffix(s)});
+        };
+
         data.circ = payload.circ;
         data.parent_circ = payload.parent_circ;
         data.hold = payload.hold;
@@ -351,7 +368,7 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
 
         switch(evt[0].textcode) {
             case 'COPY_NOT_AVAILABLE':
-                return service.copy_not_avail_dialog(evt[0], params, options);
+                return service.copy_not_avail_dialog(evt, params, options);
             case 'COPY_ALERT_MESSAGE':
                 return service.copy_alert_dialog(evt[0], params, options, 'checkout');
             default: 
@@ -704,6 +721,13 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
         if (!angular.isArray(evt)) evt = [evt];
 
         egCore.audio.play('warning.circ.event_override');
+        var copy_alert = evt.filter(function(e) {
+            return e.textcode == 'COPY_ALERT_MESSAGE';
+        });
+        evt = evt.filter(function(e) {
+            return e.textcode !== 'COPY_ALERT_MESSAGE';
+        });
+
         return $uibModal.open({
             templateUrl: './circ/share/t_event_override_dialog',
             backdrop: 'static',
@@ -711,12 +735,53 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
                 ['$scope', '$uibModalInstance', 
                 function($scope, $uibModalInstance) {
                 $scope.events = evt;
+
+                // Find the event, if any, that is for ITEM_ON_HOLDS_SHELF
+                //  and grab the patron name of the owner. 
+                $scope.holdEvent = evt.filter(
+                    function(e) {
+                        return e.textcode === 'ITEM_ON_HOLDS_SHELF'
+                     }
+                );
+
+                if ($scope.holdEvent) {
+                  // Ensure we have a scalar here 
+                   if (angular.isArray($scope.holdEvent)) {
+                       $scope.holdEvent = $scope.holdEvent[0];
+                   }
+
+                   $scope.patronName = $scope.holdEvent.payload.patron_name;
+                   $scope.holdID = $scope.holdEvent.payload.hold_id;
+                }
+
                 $scope.auto_override =
                     evt.filter(function(e){
                         return service.checkout_auto_override_after_first.indexOf(evt.textcode) > -1;
                     }).length > 0;
                 $scope.copy_barcode = params.copy_barcode; // may be null
-                $scope.ok = function() { $uibModalInstance.close() }
+
+                // Implementation note: Why not use a primitive here? It
+                // doesn't work.  See: 
+                // http://stackoverflow.com/questions/18642371/checkbox-not-binding-to-scope-in-angularjs
+                $scope.formdata = {clearHold : service.clearHold};
+
+                $scope.ok = function() { 
+                        $uibModalInstance.close();
+
+                        // Handle the cancellation of the assciated hold here
+                        if ($scope.formdata.clearHold && $scope.holdID) {
+                            $scope.args = {
+                            cancel_reason : 5,
+                                note: 'Item checked out by other patron'
+                           };
+                            egCore.net.request(
+                                'open-ils.circ', 'open-ils.circ.hold.cancel',
+                                egCore.auth.token(), $scope.holdID,
+                                $scope.args.cancel_reason,
+                                $scope.args.note);
+                        }
+               }
+
                 $scope.cancel = function ($event) { 
                     $uibModalInstance.dismiss();
                     $event.preventDefault();
@@ -726,6 +791,10 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
             function() {
                 options.override = true;
 
+                if (copy_alert.length > 0) {
+                    return service.copy_alert_dialog(copy_alert, params, options, action);
+                }
+
                 if (action == 'checkin') {
                     return service.checkin(params, options);
                 }
@@ -742,7 +811,16 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
     }
 
     service.copy_not_avail_dialog = function(evt, params, options) {
-        if (angular.isArray(evt)) evt = evt[0];
+        if (!angular.isArray(evt)) evt = [evt];
+
+        var copy_alert = evt.filter(function(e) {
+            return e.textcode == 'COPY_ALERT_MESSAGE';
+        });
+        evt = evt.filter(function(e) {
+            return e.textcode !== 'COPY_ALERT_MESSAGE';
+        });
+        evt = evt[0];
+
         return $uibModal.open({
             templateUrl: './circ/share/t_copy_not_avail_dialog',
             backdrop: 'static',
@@ -762,6 +840,11 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
         }).result.then(
             function() {
                 options.override = true;
+
+                if (copy_alert.length > 0) {
+                    return service.copy_alert_dialog(copy_alert, params, options, 'checkout');
+                }
+
                 return service.checkout(params, options);
             }
         );
@@ -938,12 +1021,24 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
         if (angular.isArray(evt)) evt = evt[0];
 
         if (!evt.payload.old_circ) {
-            return egCore.pcrud.search('circ',
-                {target_copy : evt.payload.copy.id(), checkin_time : null},
-                {limit : 1} // should only ever be 1
-            ).then(function(old_circ) {
-                evt.payload.old_circ = old_circ;
-               return service.circ_exists_dialog_impl(evt, params, options);
+            return egCore.net.request(
+                'open-ils.search',
+                'open-ils.search.asset.copy.fleshed2.find_by_barcode',
+                params.copy_barcode
+            ).then(function(resp){
+                console.log(resp);
+                if (egCore.evt.parse(resp)) {
+                    console.error(egCore.evt.parse(resp));
+                } else {
+                    return egCore.net.request(
+                         'open-ils.circ',
+                         'open-ils.circ.copy_checkout_history.retrieve',
+                         egCore.auth.token(), resp.id(), 1
+                    ).then( function (circs) {
+                        evt.payload.old_circ = circs[0];
+                        return service.circ_exists_dialog_impl( evt, params, options );
+                    });
+                }
             });
         } else {
             return service.circ_exists_dialog_impl( evt, params, options );
@@ -974,14 +1069,12 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
             function(args) {
                 if (sameUser) {
                     params.void_overdues = args.forgive_fines;
-                    options.override = true;
                     return service.renew(params, options);
                 }
 
                 return service.checkin({
                     barcode : params.copy_barcode,
                     noop : true,
-                    override : true,
                     void_overdues : args.forgive_fines
                 }).then(function(checkin_resp) {
                     if (checkin_resp.evt[0].textcode == 'SUCCESS') {
@@ -1174,32 +1267,80 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
         return deferred.promise;
     }
 
-    service.mark_damaged = function(copy_ids) {
-        return egConfirmDialog.open(
-            egCore.strings.MARK_DAMAGED_CONFIRM, '',
-            {   num_items : copy_ids.length,
-                ok : function() {},
-                cancel : function() {}
-            }
+    service.mark_damaged = function(params) {
+        if (!params) return $q.when();
+        return $uibModal.open({
+            templateUrl: './circ/share/t_mark_damaged',
+            controller:
+                ['$scope', '$uibModalInstance', 'egCore', 'egBilling', 'egItem',
+                function($scope, $uibModalInstance, egCore, egBilling, egItem) {
+                    var doRefresh = params.refresh;
+                    
+                    $scope.billArgs = {charge: params.charge};
+                    $scope.mode = 'charge';
+                    $scope.barcode = params.barcode;
+                    if (params.charge && params.charge > 0) {
+                        $scope.applyFine = "apply";
+                    }
+                    if (params.circ) {
+                        $scope.circ = params.circ;
+                        $scope.circ_checkin_time = params.circ.checkin_time();
+                        $scope.circ_patron_name = params.circ.usr().family_name() + ", "
+                            + params.circ.usr().first_given_name() + " "
+                            + params.circ.usr().second_given_name();
+                    }
+                    egBilling.fetchBillingTypes().then(function(res) {
+                        $scope.billingTypes = res;
+                    });
 
-        ).result.then(function() {
-            var promises = [];
-            angular.forEach(copy_ids, function(copy_id) {
-                promises.push(
-                    egCore.net.request(
-                        'open-ils.circ',
-                        'open-ils.circ.mark_item_damaged',
-                        egCore.auth.token(), copy_id
-                    ).then(function(resp) {
-                        if (evt = egCore.evt.parse(resp)) {
-                            console.error('mark damaged failed: ' + evt);
-                        }
-                    })
-                );
-            });
+                    $scope.btnChargeFees = function() {
+                        $scope.mode = 'charge';
+                        $scope.billArgs.charge = params.charge;
+                        $scope.applyFine = "apply";
+                    }
+                    $scope.btnWaiveFees = function() {
+                        $scope.mode = 'waive';
+                        $scope.billArgs.charge = 0;
+                        $scope.applyFine = "noapply";
+                    }
 
-            return $q.all(promises);
-        });
+                    $scope.cancel = function ($event) { 
+                        $uibModalInstance.dismiss();
+                    }
+                    $scope.ok = function() {
+                        handle_mark_item_damaged();
+                    }
+
+                    var handle_mark_item_damaged = function() {
+                        egCore.net.request(
+                            'open-ils.circ',
+                            'open-ils.circ.mark_item_damaged',
+                            egCore.auth.token(), params.id, {
+                                apply_fines: $scope.applyFine,
+                                override_amount: $scope.billArgs.charge,
+                                override_btype: $scope.billArgs.type,
+                                override_note: $scope.billArgs.note,
+                                handle_checkin: !$scope.applyFine
+                        }).then(function(resp) {
+                            if (evt = egCore.evt.parse(resp)) {
+                                doRefresh = false;
+                                console.debug("mark damaged more information required. Pushing back.");
+                                service.mark_damaged({
+                                    id: params.id,
+                                    barcode: params.barcode,
+                                    charge: evt.payload.charge,
+                                    circ: evt.payload.circ,
+                                    refresh: params.refresh
+                                });
+                                console.error('mark damaged failed: ' + evt);
+                            }
+                        }).then(function() {
+                            if (doRefresh) egItem.add_barcode_to_list(params.barcode);
+                        });
+                        $uibModalInstance.close();
+                    }
+                }]
+        }).result;
     }
 
     service.mark_missing = function(copy_ids) {
@@ -1287,7 +1428,21 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
         });
     }
 
+    service.add_copy_alerts = function(item_ids) {
+        return egAddCopyAlertDialog.open({
+            copy_ids : item_ids,
+            ok : function() { },
+            cancel : function() {}
+        }).result.then(function() { });
+    }
 
+    service.manage_copy_alerts = function(item_ids) {
+        return egCopyAlertEditorDialog.open({
+            copy_id : item_ids[0],
+            ok : function() { },
+            cancel : function() {}
+        }).result.then(function() { });
+    }
 
     // alert when copy location alert_message is set.
     // This does not affect processing, it only produces a click-through
@@ -1382,8 +1537,8 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
                         return $q.when(final_resp);
 
                     default:
-                        egCore.audio.play('error.checkin.unknown');
-                        console.error('Unhandled checkin copy status: ' 
+                        egCore.audio.play('success.checkin');
+                        console.debug('Unusual checkin copy status (may have been set via copy alert): '
                             + copy.status().id() + ' : ' + copy.status().name());
                         return $q.when(final_resp);
                 }
@@ -1506,11 +1661,9 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
                 print_context.hold = egCore.idl.toHash(evt.payload.hold);
                 var notes = print_context.hold.notes;
                 if(notes.length > 0){
-                    print_context.holdslip_notes = [];
+                    print_context.hold_notes = [];
                     angular.forEach(notes, function(n){
-                        if (n.slip){
-                            print_context.holdslip_notes.push(n);
-                        }
+                        print_context.hold_notes.push(n);
                     });
                 }
                 print_context.patron = egCore.idl.toHash(data.patron);
@@ -1562,17 +1715,34 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,
     // action == what action to take if the user confirms the alert
     service.copy_alert_dialog = function(evt, params, options, action) {
         if (angular.isArray(evt)) evt = evt[0];
-        return egConfirmDialog.open(
-            egCore.strings.COPY_ALERT_MSG_DIALOG_TITLE, 
-            evt.payload,  // payload == alert message text
-            {   copy_barcode : params.copy_barcode,
-                ok : function() {},
+        if (!angular.isArray(evt.payload)) {
+            return egConfirmDialog.open(
+                egCore.strings.COPY_ALERT_MSG_DIALOG_TITLE, 
+                evt.payload,  // payload == alert message text
+                {   copy_barcode : params.copy_barcode,
+                    ok : function() {},
+                    cancel : function() {}
+                }
+            ).result.then(function() {
+                options.override = true;
+                return service[action](params, options);
+            });
+        } else { // we got a list of copy alert objects ...
+            return egCopyAlertManagerDialog.open({
+                alerts : evt.payload,
+                mode : action,
+                ok : function(the_next_status) {
+                        if (the_next_status !== null) {
+                            params.next_copy_status = [ the_next_status ];
+                            params.capture = 'nocapture';
+                        }
+                     },
                 cancel : function() {}
-            }
-        ).result.then(function() {
-            options.override = true;
-            return service[action](params, options);
-        });
+            }).result.then(function() {
+                options.override = true;
+                return service[action](params, options);
+            });
+        }
     }
 
     // action == what action to take if the user confirms the alert