LP#1659181 Mark Items Damaged Prompt
authorKyle Huckins <khuckins@catalyte.io>
Fri, 15 Sep 2017 21:06:24 +0000 (21:06 +0000)
committerKathy Lussier <klussier@masslnc.org>
Wed, 18 Oct 2017 15:21:29 +0000 (11:21 -0400)
- Add new prompt for marking an item as damaged.
- If there is a fee when marking an item as damaged, offer the
option to charge as normal, manually bill the patron, or to waive
the charge.
-Affected interfaces: Holds pull list, Holds Shelf, Patron Holds,
Record Holds, Record Holdings, Item Status(List), Item Status(Detail)

Signed-off-by: Kyle Huckins <khuckins@catalyte.io>

 Changes to be committed:
modified:   Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
modified:   Open-ILS/src/templates/staff/cat/catalog/index.tt2
modified:   Open-ILS/src/templates/staff/circ/checkin/index.tt2
modified:   Open-ILS/src/templates/staff/circ/holds/index.tt2
new file:   Open-ILS/src/templates/staff/circ/share/t_mark_damaged.tt2
modified:   Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
modified:   Open-ILS/web/js/ui/default/staff/cat/item/app.js
modified:   Open-ILS/web/js/ui/default/staff/circ/checkin/app.js
modified:   Open-ILS/web/js/ui/default/staff/circ/services/circ.js
modified:   Open-ILS/web/js/ui/default/staff/circ/services/holds.js
modified:   Open-ILS/web/js/ui/default/staff/circ/services/item.js

Signed-off-by: Kathy Lussier <klussier@masslnc.org>

Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
Open-ILS/src/templates/staff/cat/catalog/index.tt2
Open-ILS/src/templates/staff/circ/checkin/index.tt2
Open-ILS/src/templates/staff/circ/holds/index.tt2
Open-ILS/src/templates/staff/circ/share/t_mark_damaged.tt2 [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
Open-ILS/web/js/ui/default/staff/cat/item/app.js
Open-ILS/web/js/ui/default/staff/circ/checkin/app.js
Open-ILS/web/js/ui/default/staff/circ/services/circ.js
Open-ILS/web/js/ui/default/staff/circ/services/holds.js
Open-ILS/web/js/ui/default/staff/circ/services/item.js

index 280ecdd..3a59bf9 100644 (file)
@@ -1269,10 +1269,15 @@ __PACKAGE__->register_method(
 
 sub mark_item {
     my( $self, $conn, $auth, $copy_id, $args ) = @_;
-    my $e = new_editor(authtoken=>$auth, xact =>1);
-    return $e->die_event unless $e->checkauth;
     $args ||= {};
 
+    # Items must be checked in before any attempt is made to mark damaged
+    my $evt = try_checkin($auth, $copy_id) if
+        ($self->api_name=~ /damaged/ && $args->{handle_checkin});
+    return $evt if $evt;
+
+    my $e = new_editor(authtoken=>$auth, xact =>1);
+    return $e->die_event unless $e->checkauth;
     my $copy = $e->retrieve_asset_copy([
         $copy_id,
         {flesh => 1, flesh_fields => {'acp' => ['call_number']}}])
@@ -1342,6 +1347,31 @@ sub mark_item {
     return 1;
 }
 
+sub try_checkin {
+    my($auth, $copy_id) = @_;
+
+    my $checkin = $U->simplereq(
+        'open-ils.circ',
+        'open-ils.circ.checkin.override',
+        $auth, {
+            copy_id => $copy_id,
+            noop => 1
+        }
+    );
+    if(ref $checkin ne 'ARRAY') { $checkin = [$checkin]; }
+
+    my $evt_code = $checkin->[0]->{textcode};
+    $logger->info("try_checkin() received event: $evt_code");
+
+    if($evt_code eq 'SUCCESS' || $evt_code eq 'NO_CHANGE') {
+        $logger->info('try_checkin() successful checkin');
+        return undef;
+    } else {
+        $logger->warn('try_checkin() un-successful checkin');
+        return $checkin;
+    }
+}
+
 sub handle_mark_damaged {
     my($e, $copy, $owning_lib, $args) = @_;
 
@@ -1422,8 +1452,6 @@ sub handle_mark_damaged {
         my $evt2 = OpenILS::Utils::Penalty->calculate_penalties($e, $circ->usr->id, $e->requestor->ws_ou);
         return $evt2 if $evt2;
 
-        return undef;
-
     } else {
         return OpenILS::Event->new('DAMAGE_CHARGE', 
             payload => {
index 3d19ca2..a50822e 100644 (file)
@@ -18,6 +18,7 @@
 [% INCLUDE 'staff/cat/share/marcedit_strings.tt2' %]
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/cat/services/marcedit.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/circ.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/billing.js"></script>
 [% INCLUDE 'staff/circ/share/circ_strings.tt2' %]
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/holds.js"></script>
 [% INCLUDE 'staff/circ/share/hold_strings.tt2' %]
index cfd0a1b..64e5510 100644 (file)
@@ -8,6 +8,7 @@
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/grid.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/ui.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/user.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/billing.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/circ.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/item.js"></script>
 <script>
index b327738..fd4ade7 100644 (file)
@@ -8,6 +8,7 @@
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/grid.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/ui.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/services/user.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/billing.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/circ.js"></script>
 [% INCLUDE 'staff/circ/share/circ_strings.tt2' %]
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/holds.js"></script>
diff --git a/Open-ILS/src/templates/staff/circ/share/t_mark_damaged.tt2 b/Open-ILS/src/templates/staff/circ/share/t_mark_damaged.tt2
new file mode 100644 (file)
index 0000000..7506217
--- /dev/null
@@ -0,0 +1,53 @@
+<div class="modal-header">
+  <button type="button" class="close" 
+    ng-click="cancel()" aria-hidden="true">&times;</button>
+  <h4 class="modal-title">
+    [% l('Mark Item(s) Damaged') %]
+  </h4>
+</div>
+<div class="modal-body">
+  <div class="pad-vert row">
+    <div class="col-md-12">
+      [% l("Item [_1] will be marked damaged. ", "{{barcode}}") %]
+      <span ng-if="circ && billArgs.charge != null">[% l("Was returned ") %]
+        <span ng-if="circ_checkin_time">[% l("on [_1] ","{{circ_checkin_time | date:'MM/dd/yy H:mm a'}}") %]</span>
+      [% l("for [_1] : [_2]. ",
+      "{{circ_patron_name}}", "{{circ.usr().usrname()}}") %]
+      [% l("Confirmation required to charge this patron [_1] for the damage.", "{{billArgs.charge | currency}}") %]</span>
+    </div>
+  </div>
+  <div class="pad-vert row" ng-if="circ && billArgs.charge != null">
+    <div class="col-md-3">
+      <label>[% l("Fee") %]
+      <input type="number" min="0" step="any" class="form-control" ng-disabled="applyFine == 'noapply'"
+          focus-me='focus' required ng-model="billArgs.charge"></label>
+    </div>
+    <div class="col-md-6">
+      <label>[% l("Type") %]
+      <select class="form-control" ng-model="billArgs.type">
+        <option ng-repeat="type in billingTypes | orderBy:'name()'" value="{{type.id()}}" 
+          ng-disabled="applyFine == 'noapply'">
+          {{type.name()}}
+        </option>
+      </select>
+      </label>
+    </div>
+  </div>
+  <div class="pad-vert row" ng-if="circ && billArgs.charge != null">
+    <div class="col-md-6">
+      <label>[% l("Note") %]
+      <textarea rows="3" class="form-control" placeholder="[% l('Note...') %]"  ng-disabled="applyFine == 'noapply'"
+        ng-model="billArgs.note"></textarea></label>
+    </div>
+  </div>
+</div>
+<div class="modal-footer">
+  <div class="btn-group pull-left" ng-if="circ && billArgs.charge != null">
+    <label class="btn btn-primary" ng-model="mode" btn-radio="'charge'"
+      ng-class="{active: mode == 'charge'}" ng-click="btnChargeFees()">Charge Fees</label>
+    <label class="btn btn-primary" ng-model="mode" btn-radio="'waive'"
+      ng-class="{active: mode == 'waive'}" ng-click="btnWaiveFees()">No Charge</label>
+  </div>
+  <button class="btn btn-primary" ng-disabled="applyFine == 'apply' && circ && (billArgs.charge == null || !billArgs.type)" ng-click="ok()">[% l('Submit') %]</button>
+  <button class="btn btn-warning" ng-if="!billArgs.charge && applyFine != 'noapply'" ng-click="cancel()">[% l('Cancel') %]</button>
+</div>
\ No newline at end of file
index 33af100..95c1dd3 100644 (file)
@@ -1542,9 +1542,18 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
     }
 
     $scope.selectedHoldingsDamaged = function () {
-        egCirc.mark_damaged(gatherSelectedHoldingsIds()).then(function() {
-            holdingsSvcInst.fetchAgain().then(function() {
-                $scope.holdingsGridDataProvider.refresh();
+        var copy_list = gatherSelectedRawCopies();
+        if (copy_list.length == 0) return;
+
+        angular.forEach(copy_list, function(cp) {
+            egCirc.mark_damaged({
+                id: cp.id(),
+                barcode: cp.barcode(),
+                circ_lib: cp.circ_lib().id()
+            }).then(function() {
+                holdingsSvcInst.fetchAgain().then(function() {
+                    $scope.holdingsGridDataProvider.refresh();
+                });
             });
         });
     }
index 2eac888..3624b2a 100644 (file)
@@ -142,7 +142,8 @@ function($scope , $location , $timeout , egCore , egGridDataProvider , itemSvc)
     $scope.selectedHoldingsDamaged = function () {
         itemSvc.selectedHoldingsDamaged([{
             id : $scope.args.copyId,
-            barcode : $scope.args.copyBarcode
+            barcode : $scope.args.copyBarcode,
+            refresh : true
         }]);
     }
 
index 46afb5b..7be19fb 100644 (file)
@@ -307,14 +307,15 @@ function($scope , $q , $window , $location , $timeout , egCore , checkinSvc , eg
     $scope.showMarkDamaged = function(items) {
         var copy_ids = [];
         angular.forEach(items, function(item) {
-            if (item.acp) copy_ids.push(item.acp.id());
+            if (item.acp) {
+                egCirc.mark_damaged({
+                    id: item.acp.id(),
+                    barcode: item.acp.barcode()
+                })
+
+            }
         });
 
-        if (copy_ids.length) {
-            egCirc.mark_damaged(copy_ids).then(function() {
-                // update grid items?
-            });
-        }
     }
 
     $scope.abortTransit = function(items) {
index bb544d0..4a5130b 100644 (file)
@@ -1174,32 +1174,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) {
index b8f67d9..0254690 100644 (file)
@@ -590,11 +590,14 @@ function($window , $location , $timeout , egCore , egHolds , egCirc) {
         generic_update(items, 'transfer_to_marked_title'); }
 
     service.mark_damaged = function(items) {
-        var copy_ids = items
-            .filter(function(item) { return Boolean(item.copy) })
-            .map(function(item) { return item.copy.id() });
-        if (copy_ids.length) 
-            egCirc.mark_damaged(copy_ids).then(service.refresh);
+        angular.forEach(items, function(item) {
+            if (item.copy) {
+                egCirc.mark_damaged({
+                    id: item.copy.id(),
+                    barcode: item.copy.barcode()
+                }).then(service.refresh);
+            }
+        });
     }
 
     service.mark_missing = function(items) {
index 44c9aa1..afbc474 100644 (file)
@@ -579,8 +579,14 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
     }
 
     service.selectedHoldingsDamaged = function (items) {
-        egCirc.mark_damaged(items.map(function(el){return el.id;})).then(function(){
-            angular.forEach(items, function(cp){service.add_barcode_to_list(cp.barcode)});
+        angular.forEach(items, function(cp) {
+            if (cp) {
+                egCirc.mark_damaged({
+                    id: cp.id,
+                    barcode: cp.barcode,
+                    refresh: cp.refresh
+                });
+            }
         });
     }