[open-ils-commits] [GIT] Evergreen ILS branch master updated. dd47e20baf130adb211bc90c3d0026694906b326

Evergreen Git git at git.evergreen-ils.org
Wed Jul 31 11:34:33 EDT 2019


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, master has been updated
       via  dd47e20baf130adb211bc90c3d0026694906b326 (commit)
       via  2315c14ced7f7f44940bb8924aebb3ebb2bf1227 (commit)
       via  3e6c4007cff24c9c5a580e329a78a9f9b9df8c4d (commit)
       via  cf88a1bfba5d9476388cea8d763b96455a0d3bb6 (commit)
      from  a34245c7b12d95a8a9632f6e64a88493d02805e7 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit dd47e20baf130adb211bc90c3d0026694906b326
Author: Jason Stephenson <jason at sigio.com>
Date:   Sun Feb 3 11:44:09 2019 -0500

    LP1779467: Fix bug with charging fees when marking item damaged.
    
    Revert the changes made to the mark_damaged function in the web staff
    client code (Open-ILS/web/js/ui/default/staff/circ/services/circ.js)
    earlier in this branch.  The new code does not handle the mark damaged
    process correctly, but the original code does.
    
    Signed-off-by: Jason Stephenson <jason at sigio.com>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>
    Signed-off-by: Terran McCanna <tmccanna at georgialibraries.org>

diff --git a/Open-ILS/web/js/ui/default/staff/circ/services/circ.js b/Open-ILS/web/js/ui/default/staff/circ/services/circ.js
index 93a5d03faf..acf9d9420f 100644
--- a/Open-ILS/web/js/ui/default/staff/circ/services/circ.js
+++ b/Open-ILS/web/js/ui/default/staff/circ/services/circ.js
@@ -1361,26 +1361,16 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,  egAddCopyAl
                                 handle_checkin: !$scope.applyFine
                         }).then(function(resp) {
                             if (evt = egCore.evt.parse(resp)) {
-                                egCore.pcrud.retrieve('ccs', 14)
-                                    .then(function(resp) {
-                                        service.handle_mark_item_event(
-                                            {id : params.id, barcode : params.barcode},
-                                            resp,
-                                            {
-                                                apply_fines: $scope.applyFine,
-                                                override_amount: $scope.billArgs.charge,
-                                                override_btype: $scope.billArgs.type,
-                                                override_note: $scope.billArgs.note,
-                                                handle_checkin: !$scope.applyFine
-                                            },
-                                            evt);
-                                    }).then(function(resp) {
-                                        // noop?
-                                        //if (doRefresh) egItem.add_barcode_to_list(params.barcode);
-                                    }, function(resp) {
-                                        doRefresh = false;
-                                        console.error('mark damaged failed: ' + evt);
-                                    });
+                                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);

commit 2315c14ced7f7f44940bb8924aebb3ebb2bf1227
Author: Jason Stephenson <jason at sigio.com>
Date:   Mon Nov 12 09:17:11 2018 -0500

    LP 1779467: Fix Error When Marking Item on Hold as Discard/Weed
    
    Fix the following error when marking an on-hold item as Discard/Weed:
    
    Can't use an undefined value as an ARRAY reference at
    /usr/local/share/perl/5.18.2/OpenILS/Application/Circ.pm line 1393.
    
    Signed-off-by: Jason Stephenson <jason at sigio.com>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    
    LP#1779467: Fix 'Dicard' typo
    
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Jason Stephenson <jason at sigio.com>
    Signed-off-by: Terran McCanna <tmccanna at georgialibraries.org>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
index f3cec222d0..0dfebd3bf6 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
@@ -1376,21 +1376,21 @@ sub mark_item {
     return $evt if $evt;
 
     # Retrieving holds for later use.
-    my $holds = $e->search_action_hold_request(
+    my $holds = $e->search_action_hold_request([
         {
             current_copy => $copy->id,
             fulfillment_time => undef,
             cancel_time => undef,
         },
         {flesh=>1, flesh_fields=>{ahr=>['eligible_copies']}}
-    );
+    ]);
 
     # Throw event if attempting to  mark discard the only copy to fill a hold.
     if ($self->api_name =~ /discard/) {
         if (!$args->{handle_last_hold_copy}) {
             for my $hold (@$holds) {
                 my $eligible = $hold->eligible_copies();
-                if (scalar(@{$eligible}) < 2) {
+                if (!defined($eligible) || scalar(@{$eligible}) < 2) {
                     $evt = OpenILS::Event->new('ITEM_TO_MARK_LAST_HOLD_COPY');
                     last;
                 }
diff --git a/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2 b/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2
index e5b1e1bce8..5e40fb520d 100644
--- a/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2
+++ b/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2
@@ -27,7 +27,7 @@ s.LOCATION_ALERT_MSG =
     "{{copy.barcode()}}","{{copy.location().name()}}") %]';
 s.MARK_DAMAGED_CONFIRM = '[% l("Mark {{num_items}} items as DAMAGED?") %]';
 s.MARK_MISSING_CONFIRM = '[% l("Mark {{num_items}} items as MISSING?") %]';
-s.MARK_DISCARD_CONFIRM = '[% l("Mark {{num_items}} items as DICARD/WEED?") %]';
+s.MARK_DISCARD_CONFIRM = '[% l("Mark {{num_items}} items as DISCARD/WEED?") %]';
 s.MARK_ITEM_CHECKED_OUT = '[% l("Item {{barcode}} is checked out.") %]';
 s.MARK_ITEM_IN_TRANSIT = '[% l("Item {{barcode}} is in transit.") %]';
 s.MARK_ITEM_RESTRICT_DELETE = '[% l("Item {{barcode}} is in a status with a copy delete warning.") %]';

commit 3e6c4007cff24c9c5a580e329a78a9f9b9df8c4d
Author: Jason Stephenson <jason at sigio.com>
Date:   Mon Nov 5 16:38:48 2018 -0500

    LP 1779467: Fix SyntaxError: missing ) after argument list
    
    Missing parenthesis in Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
    caused the titular error when searching the catalog.
    
    Signed-off-by: Jason Stephenson <jason at sigio.com>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    
    LP 1779467: Fix typo in circ/renew/app.js.
    
    Had copyies for copies in one location and this prevented marking an
    item as Discard/Weed from working on the renew item interface.
    
    Signed-off-by: Jason Stephenson <jason at sigio.com>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Terran McCanna <tmccanna at georgialibraries.org>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
index 8bd06adc82..c853cb8686 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
@@ -1675,22 +1675,22 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
         var copy_list = gatherSelectedRawCopies();
         if (copy_list.length == 0) return;
         egCirc.mark_discard(copy_list.map(function(cp) {
-            return {id: cp.id(), barcode: cp.barcode()};}).then(function() {
-            holdinsSvcInst.fetchAgain().then(function() {
-                $scop.holdingsGridDataProvider.refresh();
+            return {id: cp.id(), barcode: cp.barcode()};})).then(function() {
+                holdingsSvcInst.fetchAgain().then(function() {
+                    $scope.holdingsGridDataProvider.refresh();
+                });
             });
-        });
     }
 
     $scope.selectedHoldingsMissing = function () {
         var copy_list = gatherSelectedRawCopies();
         if (copy_list.length == 0) return;
         egCirc.mark_missing(copy_list.map(function(cp) {
-            return {id: cp.id(), barcode: cp.barcode()};}).then(function() {
-            holdingsSvcInst.fetchAgain().then(function() {
-                $scope.holdingsGridDataProvider.refresh();
+            return {id: cp.id(), barcode: cp.barcode()};})).then(function() {
+                holdingsSvcInst.fetchAgain().then(function() {
+                    $scope.holdingsGridDataProvider.refresh();
+                });
             });
-        });
     }
 
     $scope.selectedHoldingsCopyAlertsAdd = function() {
diff --git a/Open-ILS/web/js/ui/default/staff/circ/renew/app.js b/Open-ILS/web/js/ui/default/staff/circ/renew/app.js
index e4b7858faf..86665574da 100644
--- a/Open-ILS/web/js/ui/default/staff/circ/renew/app.js
+++ b/Open-ILS/web/js/ui/default/staff/circ/renew/app.js
@@ -183,7 +183,7 @@ function($scope , $window , $location , egCore , egGridDataProvider , egCirc) {
     }
 
     $scope.showMarkDiscard = function(items) {
-        var copyies = [];
+        var copies = [];
         angular.forEach(items, function(item) {
             if (item.acp) copies.push(egCore.idl.toHash(item.acp));
         });

commit cf88a1bfba5d9476388cea8d763b96455a0d3bb6
Author: Jason Stephenson <jason at sigio.com>
Date:   Sat Oct 27 15:48:54 2018 -0400

    LP 1779467: Enhance Mark Items Functionality
    
    A new option to "Mark Item as Discard/Weed" is added to many actions
    menus in the staff client.  This command is connected to the back end
    function open-ils.circ.mark_item_discard.
    
    The back end functionality for the open-ils.circ.mark_item_* family of
    functions is altered to provide more consistent behavior and to avoid
    some strange situations that have come up in the past, such as items
    with the Missing status having active transits or open circulations.
    
    The code for "Mark Item as Damaged" and "Mark Item as Missing" are
    altered to take advantage of the back end changes.  NB: These changes
    do not affect the "Mark Item as Missing Pieces" function, as that is
    handled by different back end code.
    
    Perl live tests are added for the backend functionality changes to
    test that certain conditions works.  Like most of our tests these
    could be expanded to cover more potential situations.
    
    See the release notes for more detail on changes in functionality.
    
    Signed-off-by: Jason Stephenson <jason at sigio.com>
    Signed-off-by: Kathy Lussier <klussier at masslnc.org>
    Signed-off-by: Terran McCanna <tmccanna at georgialibraries.org>
    Signed-off-by: Chris Sharp <csharp at georgialibraries.org>

diff --git a/Open-ILS/src/extras/ils_events.xml b/Open-ILS/src/extras/ils_events.xml
index 9f92944cc5..362fff563d 100644
--- a/Open-ILS/src/extras/ils_events.xml
+++ b/Open-ILS/src/extras/ils_events.xml
@@ -1060,6 +1060,18 @@
     <event code='11010' textcode='SERIAL_CAPTION_AND_PATTERN_NOT_EMPTY'>
         <desc xml:lang="en-US">The prediction pattern still has dependent objects</desc>
     </event>
+
+	<!-- ================================================================ -->
+
+    <event code='12000' textcode='ITEM_TO_MARK_CHECKED_OUT'>
+      <desc xml:lang="en-US">The item to be marked is checked out to a patron.</desc>
+    </event>
+    <event code='12001' textcode='ITEM_TO_MARK_IN_TRANSIT'>
+      <desc xml:lang="en-US">The item to be marked is in transit.</desc>
+    </event>
+    <event code='12002' textcode='ITEM_TO_MARK_LAST_HOLD_COPY'>
+      <desc xml:lang="en-US">The item to be marked is the last possible target for a hold.</desc>
+    </event>
 </ils_events>
 
 
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
index bad3be3819..f3cec222d0 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ.pm
@@ -1307,31 +1307,24 @@ sub mark_item {
     my( $self, $conn, $auth, $copy_id, $args ) = @_;
     $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);
+    my $e = new_editor(authtoken=>$auth);
     return $e->die_event unless $e->checkauth;
     my $copy = $e->retrieve_asset_copy([
         $copy_id,
-        {flesh => 1, flesh_fields => {'acp' => ['call_number']}}])
+        {flesh => 1, flesh_fields => {'acp' => ['call_number','status']}}])
             or return $e->die_event;
 
-    my $owning_lib = 
+    my $owning_lib =
         ($copy->call_number->id == OILS_PRECAT_CALL_NUMBER) ? 
             $copy->circ_lib : $copy->call_number->owning_lib;
 
+    my $evt; # For later.
     my $perm = 'MARK_ITEM_MISSING';
     my $stat = OILS_COPY_STATUS_MISSING;
 
     if( $self->api_name =~ /damaged/ ) {
         $perm = 'MARK_ITEM_DAMAGED';
         $stat = OILS_COPY_STATUS_DAMAGED;
-        my $evt = handle_mark_damaged($e, $copy, $owning_lib, $args);
-        return $evt if $evt;
-
     } elsif ( $self->api_name =~ /bindery/ ) {
         $perm = 'MARK_ITEM_BINDERY';
         $stat = OILS_COPY_STATUS_BINDERY;
@@ -1355,20 +1348,73 @@ sub mark_item {
     # caller may proceed if either perm is allowed
     return $e->die_event unless $e->allowed([$perm, 'UPDATE_COPY'], $owning_lib);
 
-    $copy->status($stat);
-    $copy->edit_date('now');
-    $copy->editor($e->requestor->id);
-
-    $e->update_asset_copy($copy) or return $e->die_event;
+    # Copy status checks.
+    if ($copy->status->id() == OILS_COPY_STATUS_CHECKED_OUT) {
+        # Items must be checked in before any attempt is made to change its status.
+        if ($args->{handle_checkin}) {
+            $evt = try_checkin($auth, $copy_id);
+        } else {
+            $evt = OpenILS::Event->new('ITEM_TO_MARK_CHECKED_OUT');
+        }
+    } elsif ($copy->status->id() == OILS_COPY_STATUS_IN_TRANSIT) {
+        # Items in transit need to have the transit aborted before being marked.
+        if ($args->{handle_transit}) {
+            $evt = try_abort_transit($auth, $copy_id);
+        } else {
+            $evt = OpenILS::Event->new('ITEM_TO_MARK_IN_TRANSIT');
+        }
+    } elsif ($U->is_true($copy->status->restrict_copy_delete()) && $self->api_name =~ /discard/) {
+        # Items with restrict_copy_delete status require the
+        # COPY_DELETE_WARNING.override permission to be marked for
+        # discard.
+        if ($args->{handle_copy_delete_warning}) {
+            $evt = $e->event unless $e->allowed(['COPY_DELETE_WARNING.override'], $owning_lib);
+        } else {
+            $evt = OpenILS::Event->new('COPY_DELETE_WARNING');
+        }
+    }
+    return $evt if $evt;
 
+    # Retrieving holds for later use.
     my $holds = $e->search_action_hold_request(
-        { 
+        {
             current_copy => $copy->id,
             fulfillment_time => undef,
             cancel_time => undef,
-        }
+        },
+        {flesh=>1, flesh_fields=>{ahr=>['eligible_copies']}}
     );
 
+    # Throw event if attempting to  mark discard the only copy to fill a hold.
+    if ($self->api_name =~ /discard/) {
+        if (!$args->{handle_last_hold_copy}) {
+            for my $hold (@$holds) {
+                my $eligible = $hold->eligible_copies();
+                if (scalar(@{$eligible}) < 2) {
+                    $evt = OpenILS::Event->new('ITEM_TO_MARK_LAST_HOLD_COPY');
+                    last;
+                }
+            }
+        }
+    }
+    return $evt if $evt;
+
+    # Things below here require a transaction and there is nothing left to interfere with it.
+    $e->xact_begin;
+
+    # Handle extra mark damaged charges, etc.
+    if ($self->api_name =~ /damaged/) {
+        $evt = handle_mark_damaged($e, $copy, $owning_lib, $args);
+        return $evt if $evt;
+    }
+
+    # Mark the copy.
+    $copy->status($stat);
+    $copy->edit_date('now');
+    $copy->editor($e->requestor->id);
+
+    $e->update_asset_copy($copy) or return $e->die_event;
+
     $e->commit;
 
     if( $self->api_name =~ /damaged/ ) {
@@ -1408,6 +1454,19 @@ sub try_checkin {
     }
 }
 
+sub try_abort_transit {
+    my ($auth, $copy_id) = @_;
+
+    my $abort = $U->simplereq(
+        'open-ils.circ',
+        'open-ils.circ.transit.abort',
+        $auth, {copyid => $copy_id}
+    );
+    # Above returns 1 or an event.
+    return $abort if (ref $abort);
+    return undef;
+}
+
 sub handle_mark_damaged {
     my($e, $copy, $owning_lib, $args) = @_;
 
diff --git a/Open-ILS/src/perlmods/live_t/zz-lp1779467-mark-item-discard.t b/Open-ILS/src/perlmods/live_t/zz-lp1779467-mark-item-discard.t
new file mode 100644
index 0000000000..08f99b1c3d
--- /dev/null
+++ b/Open-ILS/src/perlmods/live_t/zz-lp1779467-mark-item-discard.t
@@ -0,0 +1,238 @@
+#!perl
+use strict; use warnings;
+use Test::More tests => 17;
+use OpenILS::Utils::TestUtils;
+use OpenILS::Const qw(:const);
+
+my $script = OpenILS::Utils::TestUtils->new();
+my $U = 'OpenILS::Application::AppUtils';
+
+diag("Test LP 1779467 Enhance Mark Item Discard/Weed.");
+
+use constant {
+    BR1_ID => 4,
+    BR3_ID => 6,
+    WORKSTATION => 'BR1-lp1779467-test-mark-item-discard'
+};
+
+# We are deliberately NOT using the admin user to check for a perm failure.
+my $credentials = {
+    username => 'br1mtownsend',
+    password => 'maryt1234',
+    type => 'staff'
+};
+
+# Log in as staff.
+my $authtoken = $script->authenticate($credentials);
+ok(
+    $authtoken,
+    'Logged in'
+) or BAIL_OUT('Must log in');
+
+# Find or register workstation.
+my $ws = $script->find_or_register_workstation(WORKSTATION, BR1_ID);
+ok(
+    ! ref $ws,
+    'Found or registered workstation'
+) or BAIL_OUT('Need Workstation');
+
+# Logout.
+$script->logout();
+ok(
+    ! $script->authtoken,
+    'Logged out'
+);
+
+# Login with workstation.
+$credentials->{workstation} = WORKSTATION;
+$credentials->{password} = 'maryt1234';
+$authtoken = $script->authenticate($credentials);
+ok(
+    $script->authtoken,
+    'Logged in with workstation'
+) or BAIL_OUT('Must log in');
+
+# Find available copy at BR1
+my $acps = $U->simplereq(
+    'open-ils.pcrud',
+    'open-ils.pcrud.search.acp.atomic',
+    $authtoken,
+    {circ_lib => BR1_ID, status => OILS_COPY_STATUS_AVAILABLE},
+    {limit => 1}
+);
+my $copy = $acps->[0];
+isa_ok(
+    ref $copy,
+    'Fieldmapper::asset::copy',
+    'Got available copy from BR1'
+);
+
+# Mark it discard/weed.
+my $result = $U->simplereq(
+    'open-ils.circ',
+    'open-ils.circ.mark_item_discard',
+    $authtoken,
+    $copy->id()
+);
+is(
+    $result,
+    1,
+    'Mark available copy Discard/Weed'
+);
+
+# Check its status.
+$copy = $U->simplereq(
+    'open-ils.pcrud',
+    'open-ils.pcrud.retrieve.acp',
+    $authtoken,
+    $copy->id()
+);
+is(
+    $copy->status(),
+    OILS_COPY_STATUS_DISCARD,
+    'Copy has Discard/Weed status'
+);
+
+# Find available copy at BR3.
+$acps = $U->simplereq(
+    'open-ils.pcrud',
+    'open-ils.pcrud.search.acp.atomic',
+    $authtoken,
+    {circ_lib => BR3_ID, status => OILS_COPY_STATUS_AVAILABLE},
+    {limit => 1}
+);
+$copy = $acps->[0];
+isa_ok(
+    ref $copy,
+    'Fieldmapper::asset::copy',
+    'Got available copy from BR3'
+);
+
+# Attempt to mark it discard/weed.
+# Should fail with a perm error.
+$result = $U->simplereq(
+    'open-ils.circ',
+    'open-ils.circ.mark_item_discard',
+    $authtoken,
+    $copy->id()
+);
+is(
+    $result->{textcode},
+    'PERM_FAILURE',
+    'Mark BR3 copy Discard/Weed'
+);
+
+# Find checked out copy at BR1.
+$acps = $U->simplereq(
+    'open-ils.pcrud',
+    'open-ils.pcrud.search.acp.atomic',
+    $authtoken,
+    {circ_lib => BR1_ID, status => OILS_COPY_STATUS_CHECKED_OUT},
+    {limit => 1}
+);
+$copy = $acps->[0];
+isa_ok(
+    ref $copy,
+    'Fieldmapper::asset::copy',
+    'Got checked out copy from BR1'
+);
+
+# Mark it discard/weed with handle_checkin: 1.
+$result = $U->simplereq(
+    'open-ils.circ',
+    'open-ils.circ.mark_item_discard',
+    $authtoken,
+    $copy->id(),
+    {handle_checkin => 1}
+);
+ok(
+    $result == 1,
+    'Mark checked out item discard'
+);
+
+# Check its status.
+$copy = $U->simplereq(
+    'open-ils.pcrud',
+    'open-ils.pcrud.retrieve.acp',
+    $authtoken,
+    $copy->id()
+);
+is(
+    $copy->status(),
+    OILS_COPY_STATUS_DISCARD,
+    'Checked out copy has Discard/Weed status'
+);
+
+# Check that it is no longer checked out.
+my $circ = $U->simplereq(
+    'open-ils.pcrud',
+    'open-ils.pcrud.search.circ',
+    $authtoken,
+    {target_copy => $copy->id(), checkin_time => undef}
+);
+ok(
+    ! defined $circ,
+    'No circulation for marked copy'
+);
+
+# Find another checked out copy at BR1.
+$acps = $U->simplereq(
+    'open-ils.pcrud',
+    'open-ils.pcrud.search.acp.atomic',
+    $authtoken,
+    {circ_lib => BR1_ID, status => OILS_COPY_STATUS_CHECKED_OUT},
+    {limit => 1}
+);
+$copy = $acps->[0];
+isa_ok(
+    ref $copy,
+    'Fieldmapper::asset::copy',
+    'Got another checked out copy from BR1'
+);
+
+# Mark it discard/weed with handle_checkin: 0.
+$result = $U->simplereq(
+    'open-ils.circ',
+    'open-ils.circ.mark_item_discard',
+    $authtoken,
+    $copy->id(),
+    {handle_checkin => 0}
+);
+# Check that we got the appropriate event: ITEM_TO_MARK_CHECKED_OUT
+is(
+    $result->{textcode},
+    'ITEM_TO_MARK_CHECKED_OUT',
+    'Mark second checked out item discard'
+);
+
+# Check its status.
+$copy = $U->simplereq(
+    'open-ils.pcrud',
+    'open-ils.pcrud.retrieve.acp',
+    $authtoken,
+    $copy->id()
+);
+is(
+    $copy->status(),
+    OILS_COPY_STATUS_CHECKED_OUT,
+    'Second checked out copy has Checked Out status'
+);
+
+# Check that it is still checked out.
+$circ = $U->simplereq(
+    'open-ils.pcrud',
+    'open-ils.pcrud.search.circ',
+    $authtoken,
+    {target_copy => $copy->id(), checkin_time => undef}
+);
+isa_ok(
+    $circ,
+    'Fieldmapper::action::circulation',
+    'Second copy still has a circulation'
+);
+
+# We could add more tests for other conditions, i.e. a copy in transit
+# and for marking other statuses.
+
+# Logout
+$script->logout(); # Not a test, just to be pedantic.
diff --git a/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2 b/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
index b694d6bc6c..d30ad1ab67 100644
--- a/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
+++ b/Open-ILS/src/templates/staff/cat/catalog/t_holdings.tt2
@@ -62,6 +62,8 @@
 
     <eg-grid-action handler="selectedHoldingsDamaged" group="[% l('Mark') %]"
       label="[% l('Item as Damaged') %]"></eg-grid-action>
+    <eg-grid-action handler="selectedHoldingsDiscard" group="[% l('Mark') %]"
+      label="[% l('Item as Discard/Weed') %]"></eg-grid-action>
     <eg-grid-action handler="selectedHoldingsMissing" group="[% l('Mark') %]"
       label="[% l('Item as Missing') %]"></eg-grid-action>
     <eg-grid-action handler="markFromSelectedAsHoldingsTarget" group="[% l('Mark') %]"
diff --git a/Open-ILS/src/templates/staff/cat/catalog/t_holds.tt2 b/Open-ILS/src/templates/staff/cat/catalog/t_holds.tt2
index c39953fdc8..f50be4cd93 100644
--- a/Open-ILS/src/templates/staff/cat/catalog/t_holds.tt2
+++ b/Open-ILS/src/templates/staff/cat/catalog/t_holds.tt2
@@ -49,6 +49,8 @@
       label="[% l('Transfer To Marked Title') %]"></eg-grid-action>
     <eg-grid-action handler="grid_actions.mark_damaged_wide" group="[% l('Item') %]"
       label="[% l('Mark Item Damaged') %]"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.mark_discard_wide" group="[% l('Item') %]"
+      label="[% l('Mark Item Discard/Weed') %]"></eg-grid-action>
     <eg-grid-action handler="grid_actions.mark_missing_wide" group="[% l('Item') %]"
       label="[% l('Mark Item Missing') %]"></eg-grid-action>
     <eg-grid-action handler="grid_actions.retarget_wide" group="[% l('Hold') %]"
diff --git a/Open-ILS/src/templates/staff/cat/item/index.tt2 b/Open-ILS/src/templates/staff/cat/item/index.tt2
index c77ced848a..96c402c043 100644
--- a/Open-ILS/src/templates/staff/cat/item/index.tt2
+++ b/Open-ILS/src/templates/staff/cat/item/index.tt2
@@ -101,6 +101,7 @@
 
         <p><b>[% l('Mark') %]</b></p>    
         <li><a href ng-click="selectedHoldingsDamaged()">[% l('Item as Damaged') %]</a></li>
+        <li><a href ng-click="selectedHoldingsDiscard()">[% l('Item as Discard/Weed') %]</a></li>
         <li><a href ng-click="selectedHoldingsMissing()">[% l('Item as Missing') %]</a></li>
     
         <p><b>[% l('Add') %]</b></p>    
diff --git a/Open-ILS/src/templates/staff/cat/item/t_list.tt2 b/Open-ILS/src/templates/staff/cat/item/t_list.tt2
index c0ac0c25d5..024271e767 100644
--- a/Open-ILS/src/templates/staff/cat/item/t_list.tt2
+++ b/Open-ILS/src/templates/staff/cat/item/t_list.tt2
@@ -47,6 +47,8 @@
 
   <eg-grid-action handler="selectedHoldingsDamaged" group="[% l('Mark') %]"
     label="[% l('Item as Damaged') %]"></eg-grid-action>
+  <eg-grid-action handler="selectedHoldingsDiscard" group="[% l('Mark') %]"
+    label="[% l('Item as Discard/Weed') %]"></eg-grid-action>
   <eg-grid-action handler="selectedHoldingsMissing" group="[% l('Mark') %]"
     label="[% l('Item as Missing') %]"></eg-grid-action>
     
diff --git a/Open-ILS/src/templates/staff/circ/checkin/t_checkin_table.tt2 b/Open-ILS/src/templates/staff/circ/checkin/t_checkin_table.tt2
index eff1448663..85fd44ec97 100644
--- a/Open-ILS/src/templates/staff/circ/checkin/t_checkin_table.tt2
+++ b/Open-ILS/src/templates/staff/circ/checkin/t_checkin_table.tt2
@@ -24,6 +24,10 @@
     label="[% l('Mark Items Damaged') %]">
   </eg-grid-action>
   <eg-grid-action
+    handler="showMarkDiscard"
+    label="[% l('Mark Items Discard/Weed') %]">
+  </eg-grid-action>
+  <eg-grid-action
     handler="show_mark_missing_pieces"
     label="[% l('Mark Missing Pieces') %]">
   </eg-grid-action>
diff --git a/Open-ILS/src/templates/staff/circ/holds/t_pull_list.tt2 b/Open-ILS/src/templates/staff/circ/holds/t_pull_list.tt2
index 4bd3762cab..2348f992cd 100644
--- a/Open-ILS/src/templates/staff/circ/holds/t_pull_list.tt2
+++ b/Open-ILS/src/templates/staff/circ/holds/t_pull_list.tt2
@@ -45,6 +45,8 @@
     label="[% l('Transfer To Marked Title') %]"></eg-grid-action>
   <eg-grid-action handler="grid_actions.mark_damaged"
     label="[% l('Mark Item Damaged') %]"></eg-grid-action>
+  <eg-grid-action handler="grid_actions.mark_discard"
+    label="[% l('Mark Item Discard/Weed') %]"></eg-grid-action>
   <eg-grid-action handler="grid_actions.mark_missing"
     label="[% l('Mark Item Missing') %]"></eg-grid-action>
   <eg-grid-action divider="true"></eg-grid-action>
diff --git a/Open-ILS/src/templates/staff/circ/holds/t_shelf_list.tt2 b/Open-ILS/src/templates/staff/circ/holds/t_shelf_list.tt2
index 083edec8be..c385a6f8a2 100644
--- a/Open-ILS/src/templates/staff/circ/holds/t_shelf_list.tt2
+++ b/Open-ILS/src/templates/staff/circ/holds/t_shelf_list.tt2
@@ -48,6 +48,8 @@
     label="[% l('Transfer To Marked Title') %]"></eg-grid-action>
   <eg-grid-action handler="grid_actions.mark_damaged_wide"
     label="[% l('Mark Item Damaged') %]"></eg-grid-action>
+  <eg-grid-action handler="grid_actions.mark_discard_wide"
+    label="[% l('Mark Item Discard/Weed') %]"></eg-grid-action>
   <eg-grid-action handler="grid_actions.mark_missing_wide"
     label="[% l('Mark Item Missing') %]"></eg-grid-action>
   <eg-grid-action divider="true"></eg-grid-action>
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_holds_list.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_holds_list.tt2
index 14a6569653..48700fcc22 100644
--- a/Open-ILS/src/templates/staff/circ/patron/t_holds_list.tt2
+++ b/Open-ILS/src/templates/staff/circ/patron/t_holds_list.tt2
@@ -35,6 +35,8 @@
     label="[% l('Transfer To Marked Title') %]"></eg-grid-action>
   <eg-grid-action handler="grid_actions.mark_damaged"
     label="[% l('Mark Item Damaged') %]"></eg-grid-action>
+  <eg-grid-action handler="grid_actions.mark_discard"
+    label="[% l('Mark Item Discard/Weed') %]"></eg-grid-action>
   <eg-grid-action handler="grid_actions.mark_missing"
     label="[% l('Mark Item Missing') %]"></eg-grid-action>
   <eg-grid-action divider="true"></eg-grid-action>
diff --git a/Open-ILS/src/templates/staff/circ/renew/t_renew.tt2 b/Open-ILS/src/templates/staff/circ/renew/t_renew.tt2
index 886e93fdbb..209570973d 100644
--- a/Open-ILS/src/templates/staff/circ/renew/t_renew.tt2
+++ b/Open-ILS/src/templates/staff/circ/renew/t_renew.tt2
@@ -61,6 +61,10 @@
     handler="showMarkDamaged"
     label="[% l('Mark Items Damaged') %]">
   </eg-grid-action>
+  <eg-grid-action
+    handler="showMarkDiscard"
+    label="[% l('Mark Items Discard/Weed') %]">
+  </eg-grid-action>
   <eg-grid-action divider="true"></eg-grid-action>
   <eg-grid-action 
     handler="abortTransit"
diff --git a/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2 b/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2
index 592365ba7f..e5b1e1bce8 100644
--- a/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2
+++ b/Open-ILS/src/templates/staff/circ/share/circ_strings.tt2
@@ -27,6 +27,15 @@ s.LOCATION_ALERT_MSG =
     "{{copy.barcode()}}","{{copy.location().name()}}") %]';
 s.MARK_DAMAGED_CONFIRM = '[% l("Mark {{num_items}} items as DAMAGED?") %]';
 s.MARK_MISSING_CONFIRM = '[% l("Mark {{num_items}} items as MISSING?") %]';
+s.MARK_DISCARD_CONFIRM = '[% l("Mark {{num_items}} items as DICARD/WEED?") %]';
+s.MARK_ITEM_CHECKED_OUT = '[% l("Item {{barcode}} is checked out.") %]';
+s.MARK_ITEM_IN_TRANSIT = '[% l("Item {{barcode}} is in transit.") %]';
+s.MARK_ITEM_RESTRICT_DELETE = '[% l("Item {{barcode}} is in a status with a copy delete warning.") %]';
+s.MARK_ITEM_LAST_HOLD_COPY = '[% l("Item {{barcode}} is the last item to fill a hold.") %]';
+s.MARK_ITEM_CONTINUE = '[% l("Do you wish to continue marking it {{status}}?") %]';
+s.MARK_ITEM_CHECKIN_CONTINUE = '[% l("Do you wish to check it in and continue marking it {{status}}?") %]';
+s.MARK_ITEM_ABORT_CONTINUE = '[% l("Do you wish to abort the transit and continue marking it {{status}}?") %]';
+s.MARK_ITEM_FAILURE = '[% l("Marking of item {{barcode}} with status {{status}} failed: {{textcode}}") %]'
 s.ABORT_TRANSIT_CONFIRM = '[% l("Cancel {{num_transits}} transits?") %]';
 s.ROUTE_TO_HOLDS_SHELF = '[% l("Holds Shelf") %]';
 s.ROUTE_TO_CATALOGING = '[% l("Cataloging") %]';
diff --git a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
index 2988d9504c..8bd06adc82 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
@@ -1671,8 +1671,22 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
         });
     }
 
+    $scope.selectedHoldingsDiscard = function () {
+        var copy_list = gatherSelectedRawCopies();
+        if (copy_list.length == 0) return;
+        egCirc.mark_discard(copy_list.map(function(cp) {
+            return {id: cp.id(), barcode: cp.barcode()};}).then(function() {
+            holdinsSvcInst.fetchAgain().then(function() {
+                $scop.holdingsGridDataProvider.refresh();
+            });
+        });
+    }
+
     $scope.selectedHoldingsMissing = function () {
-        egCirc.mark_missing(gatherSelectedHoldingsIds()).then(function() {
+        var copy_list = gatherSelectedRawCopies();
+        if (copy_list.length == 0) return;
+        egCirc.mark_missing(copy_list.map(function(cp) {
+            return {id: cp.id(), barcode: cp.barcode()};}).then(function() {
             holdingsSvcInst.fetchAgain().then(function() {
                 $scope.holdingsGridDataProvider.refresh();
             });
diff --git a/Open-ILS/web/js/ui/default/staff/cat/item/app.js b/Open-ILS/web/js/ui/default/staff/cat/item/app.js
index af59baf45b..4426ad007c 100644
--- a/Open-ILS/web/js/ui/default/staff/cat/item/app.js
+++ b/Open-ILS/web/js/ui/default/staff/cat/item/app.js
@@ -189,10 +189,17 @@ function($scope , $q , $window , $location , $timeout , egCore , egNet , egGridD
         }]);
     }
 
+    $scope.selectedHoldingsDiscard = function () {
+        itemSvc.selectedHoldingsDiscard([{
+            id : $scope.args.copyId,
+            barcode : $scope.args.barcode
+        }]);
+    }
+
     $scope.selectedHoldingsMissing = function () {
         itemSvc.selectedHoldingsMissing([{
             id : $scope.args.copyId,
-            barcode : $scope.args.copyBarcode
+            barcode : $scope.args.barcode
         }]);
     }
 
@@ -527,6 +534,10 @@ function($scope , $q , $window , $location , $timeout , egCore , egNet , egGridD
         itemSvc.selectedHoldingsDamaged(copyGrid.selectedItems());
     }
 
+    $scope.selectedHoldingsDiscard = function () {
+        itemSvc.selectedHoldingsDiscard(copyGrid.selectedItems());
+    }
+
     $scope.selectedHoldingsMissing = function () {
         itemSvc.selectedHoldingsMissing(copyGrid.selectedItems());
     }
diff --git a/Open-ILS/web/js/ui/default/staff/circ/checkin/app.js b/Open-ILS/web/js/ui/default/staff/circ/checkin/app.js
index 965529b4c5..d0e5106248 100644
--- a/Open-ILS/web/js/ui/default/staff/circ/checkin/app.js
+++ b/Open-ILS/web/js/ui/default/staff/circ/checkin/app.js
@@ -337,6 +337,20 @@ function($scope , $q , $window , $location , $timeout , egCore , checkinSvc , eg
 
     }
 
+    $scope.showMarkDiscard = function(items) {
+        var copies = [];
+        angular.forEach(items, function(item) {
+            if (item.acp) {
+                copies.push(egCore.idl.toHash(item.acp));
+            }
+        });
+        if (copies.length) {
+            egCirc.mark_discard(copies).then(function() {
+                // update grid items?
+            });
+        }
+    }
+
     $scope.abortTransit = function(items) {
         var transit_ids = [];
         angular.forEach(items, function(item) {
diff --git a/Open-ILS/web/js/ui/default/staff/circ/renew/app.js b/Open-ILS/web/js/ui/default/staff/circ/renew/app.js
index 2c6ba639ea..e4b7858faf 100644
--- a/Open-ILS/web/js/ui/default/staff/circ/renew/app.js
+++ b/Open-ILS/web/js/ui/default/staff/circ/renew/app.js
@@ -182,6 +182,19 @@ function($scope , $window , $location , egCore , egGridDataProvider , egCirc) {
         }
     }
 
+    $scope.showMarkDiscard = function(items) {
+        var copyies = [];
+        angular.forEach(items, function(item) {
+            if (item.acp) copies.push(egCore.idl.toHash(item.acp));
+        });
+
+        if (copies.length) {
+            egCirc.mark_discard(copies).then(function() {
+                // update grid items?
+            });
+        }
+    }
+
     $scope.showLastFewCircs = function(items) {
         if (items.length && (copy = items[0].acp)) {
             var url = $location.path(
diff --git a/Open-ILS/web/js/ui/default/staff/circ/services/circ.js b/Open-ILS/web/js/ui/default/staff/circ/services/circ.js
index 6c2d30d50e..93a5d03faf 100644
--- a/Open-ILS/web/js/ui/default/staff/circ/services/circ.js
+++ b/Open-ILS/web/js/ui/default/staff/circ/services/circ.js
@@ -1361,16 +1361,26 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,  egAddCopyAl
                                 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);
+                                egCore.pcrud.retrieve('ccs', 14)
+                                    .then(function(resp) {
+                                        service.handle_mark_item_event(
+                                            {id : params.id, barcode : params.barcode},
+                                            resp,
+                                            {
+                                                apply_fines: $scope.applyFine,
+                                                override_amount: $scope.billArgs.charge,
+                                                override_btype: $scope.billArgs.type,
+                                                override_note: $scope.billArgs.note,
+                                                handle_checkin: !$scope.applyFine
+                                            },
+                                            evt);
+                                    }).then(function(resp) {
+                                        // noop?
+                                        //if (doRefresh) egItem.add_barcode_to_list(params.barcode);
+                                    }, function(resp) {
+                                        doRefresh = false;
+                                        console.error('mark damaged failed: ' + evt);
+                                    });
                             }
                         }).then(function() {
                             if (doRefresh) egItem.add_barcode_to_list(params.barcode);
@@ -1381,30 +1391,146 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,  egAddCopyAl
         }).result;
     }
 
-    service.mark_missing = function(copy_ids) {
+    service.handle_mark_item_event = function(copy, status, args, event) {
+        var dlogTitle, dlogMessage;
+        switch (event.textcode) {
+        case 'ITEM_TO_MARK_CHECKED_OUT':
+            dlogTitle = egCore.strings.MARK_ITEM_CHECKED_OUT;
+            dlogMessage = egCore.strings.MARK_ITEM_CHECKIN_CONTINUE;
+            args.handle_checkin = 1;
+            break;
+        case 'ITEM_TO_MARK_IN_TRANSIT':
+            dlogTitle = egCore.strings.MARK_ITEM_IN_TRANSIT;
+            dlogMessage = egCore.strings.MARK_ITEM_ABORT_CONTINUE;
+            args.handle_transit = 1;
+            break;
+        case 'ITEM_TO_MARK_LAST_HOLD_COPY':
+            dlogTitle = egCore.strings.MARK_ITEM_LAST_HOLD_COPY;
+            dlogMessage = egCore.strings.MARK_ITEM_CONTINUE;
+            args.handle_last_hold_copy = 1;
+            break;
+        case 'COPY_DELETE_WARNING':
+            dlogTitle = egCore.strings.MARK_ITEM_RESTRICT_DELETE;
+            dlogMessage = egCore.strings.MARK_ITEM_CONTINUE;
+            args.handle_copy_delete_warning = 1;
+            break;
+        case 'PERM_FAILURE':
+            console.error('Mark item ' + status.name() + ' for ' + copy.barcode + ' failed: ' +
+                          event);
+            return service.exit_alert(egCore.strings.PERMISSION_DENIED,
+                                      {permission : event.ilsperm});
+            break;
+        default:
+            console.error('Mark item ' + status.name() + ' for ' + copy.barcode + ' failed: ' +
+                          event);
+            return service.exit_alert(egCore.strings.MARK_ITEM_FAILURE,
+                                      {status : status.name(), barcode : copy.barcode,
+                                       textcode : event.textcode});
+            break;
+        }
         return egConfirmDialog.open(
-            egCore.strings.MARK_MISSING_CONFIRM, '',
-            {   num_items : copy_ids.length,
+            dlogTitle, dlogMessage,
+            {
+                barcode : copy.barcode,
+                status : status.name(),
+                ok : function () {},
+                cancel : function () {}
+            }
+        ).result.then(function() {
+            return service.mark_item(copy, status, args);
+        });
+    }
+
+    service.mark_item = function(copy, markstatus, args) {
+        if (!copy) return $q.when();
+
+        // If any new back end mark_item calls are added, also add
+        // them here to use them from the staff client.
+        // TODO: I didn't find any JS constants for copy status.
+        var req;
+        switch (markstatus.id()) {
+        case 2:
+            // Not implemented in the staff client, yet.
+            // req = "open-ils.circ.mark_item_bindery";
+            break;
+        case 4:
+            req = "open-ils.circ.mark_item_missing";
+            break;
+        case 9:
+            // Not implemented in the staff client, yet.
+            // req = "open-ils.circ.mark_item_on_order";
+            break;
+        case 10:
+            // Not implemented in the staff client, yet.
+            // req = "open-ils.circ.mark_item_ill";
+            break;
+        case 11:
+            // Not implemented in the staff client, yet.
+            // req = "open-ils.circ.mark_item_cataloging";
+            break;
+        case 12:
+            // Not implemented in the staff client, yet.
+            // req = "open-ils.circ.mark_item_reserves";
+            break;
+        case 13:
+            req = "open-ils.circ.mark_item_discard";
+            break;
+        case 14:
+            // Damaged is for handling of events. It's main handler is elsewhere.
+            req = "open-ils.circ.mark_item_damaged";
+            break;
+        }
+
+        return egCore.net.request(
+            'open-ils.circ',
+            req,
+            egCore.auth.token(),
+            copy.id,
+            args
+        ).then(function(resp) {
+            if (evt = egCore.evt.parse(resp)) {
+                return service.handle_mark_item_event(copy, markstatus, args, evt);
+            }
+        });
+    }
+
+    service.mark_discard = function(copies) {
+        return egConfirmDialog.open(
+            egCore.strings.MARK_DISCARD_CONFIRM, '',
+            {
+                num_items : copies.length,
                 ok : function() {},
                 cancel : function() {}
             }
         ).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_missing',
-                        egCore.auth.token(), copy_id
-                    ).then(function(resp) {
-                        if (evt = egCore.evt.parse(resp)) {
-                            console.error('mark missing failed: ' + evt);
-                        }
-                    })
-                );
-            });
+            return egCore.pcrud.retrieve('ccs', 13)
+                .then(function(resp) {
+                    var promises = [];
+                    angular.forEach(copies, function(copy) {
+                        promises.push(service.mark_item(copy, resp, {}))
+                    });
+                    return $q.all(promises);
+                });
+        });
+    }
 
-            return $q.all(promises);
+    service.mark_missing = function(copies) {
+        return egConfirmDialog.open(
+            egCore.strings.MARK_MISSING_CONFIRM, '',
+            {
+                num_items : copies.length,
+                ok : function() {},
+                cancel : function() {}
+            }
+        ).result.then(function() {
+            return egCore.pcrud.retrieve('ccs', 4)
+                .then(function(resp) {
+                    var promises = [];
+                    angular.forEach(copies, function(copy) {
+                        promises.push(service.mark_item(copy, resp, {}))
+                    });
+                    return $q.all(promises);
+                });
         });
     }
 
diff --git a/Open-ILS/web/js/ui/default/staff/circ/services/holds.js b/Open-ILS/web/js/ui/default/staff/circ/services/holds.js
index 393aeb308d..dcc1d11e6c 100644
--- a/Open-ILS/web/js/ui/default/staff/circ/services/holds.js
+++ b/Open-ILS/web/js/ui/default/staff/circ/services/holds.js
@@ -744,20 +744,40 @@ function($window , $location , $timeout , egCore , egHolds , egCirc) {
         });
     }
 
+    service.mark_discard = function(items) {
+        var copies = items
+            .filter(function(item) { return Boolean(item.copy) })
+            .map(function(item) {
+                return {id: item.copy.id(), barcode: item.copy.barcode()}
+            });
+        if (copies.length)
+            egCirc.mark_discard(copies).then(service.refresh);
+    }
+
     service.mark_missing = function(items) {
-        var copy_ids = items
+        var copies = items
             .filter(function(item) { return Boolean(item.copy) })
-            .map(function(item) { return item.copy.id() });
-        if (copy_ids.length) 
-            egCirc.mark_missing(copy_ids).then(service.refresh);
+            .map(function(item) {
+                return {id: item.copy.id(), barcode: item.copy.barcode()}
+            });
+        if (copies.length)
+            egCirc.mark_missing(copies).then(service.refresh);
     }
 
     service.mark_missing_wide = function(items) {
-        var copy_ids = items
+        var copies = items
+            .filter(function(item) { return Boolean(item.hold.cp_id) })
+            .map(function(item) { return {id: item.hold.cp_id, barcode: item.hold.cp_barcode}; });
+        if (copies.length)
+            egCirc.mark_missing(copies).then(service.refresh);
+    }
+
+    service.mark_discard_wide = function(items) {
+        var copies = items
             .filter(function(item) { return Boolean(item.hold.cp_id) })
-            .map(function(item) { return item.hold.cp_id });
-        if (copy_ids.length) 
-            egCirc.mark_missing(copy_ids).then(service.refresh);
+            .map(function(item) { return {id: item.hold.cp_id, barcode: item.hold.cp_barcode}; });
+        if (copies.length)
+            egCirc.mark_discard(copies).then(service.refresh);
     }
 
     service.retarget = function(items) {
diff --git a/Open-ILS/web/js/ui/default/staff/circ/services/item.js b/Open-ILS/web/js/ui/default/staff/circ/services/item.js
index c39f0380be..6382852d32 100644
--- a/Open-ILS/web/js/ui/default/staff/circ/services/item.js
+++ b/Open-ILS/web/js/ui/default/staff/circ/services/item.js
@@ -644,8 +644,14 @@ function(egCore , egCirc , $uibModal , $q , $timeout , $window , egConfirmDialog
         });
     }
 
+    service.selectedHoldingsDiscard = function (items) {
+        egCirc.mark_discard(items.map(function(el){return {id : el.id, barcode : el.barcode};})).then(function(){
+            angular.forEach(items, function(cp){service.add_barcode_to_list(cp.barcode)});
+        });
+    }
+
     service.selectedHoldingsMissing = function (items) {
-        egCirc.mark_missing(items.map(function(el){return el.id;})).then(function(){
+        egCirc.mark_missing(items.map(function(el){return {id : el.id, barcode : el.barcode};})).then(function(){
             angular.forEach(items, function(cp){service.add_barcode_to_list(cp.barcode)});
         });
     }
diff --git a/docs/RELEASE_NOTES_NEXT/Circulation/enhanced-mark-item-functionality.adoc b/docs/RELEASE_NOTES_NEXT/Circulation/enhanced-mark-item-functionality.adoc
new file mode 100644
index 0000000000..9e02269012
--- /dev/null
+++ b/docs/RELEASE_NOTES_NEXT/Circulation/enhanced-mark-item-functionality.adoc
@@ -0,0 +1,112 @@
+Enhanced Mark Item Functionality
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Evergreen's Mark Item Damaged and Mark Item Missing functionality has
+been enhanced, and the ability to mark an item with the Discard/Weed
+status has been added.  This enhancement affects both the Evergreen
+back end code and the staff client.
+
+Staff Client Changes
+++++++++++++++++++++
+
+The option to "Mark Item as Discard/Weed" has been added to areas
+where the option(s) to "Mark Item as Missing" and/or "Mark Item as
+Damaged" appear.  This is primarily in the action menus on the
+following interfaces:
+
+ * Item Status
+ * Checkin
+ * Renew
+ * Holds Pull List
+ * Patron Holds List
+ * Record Holds List
+ * Holds Shelf
+ * Holdings Edit
+
+This new option allows staff to mark a copy with the Discard/Weed
+status quickly and easily without necessarily requiring the
+intervention of cataloging staff.  In order to mark an item with the
+Discard/Weed status, staff will require either the `MARK_ITEM_DISCARD`
+or `UPDATE_COPY status` at the item's owning library.  (NOTE: This
+permission choice is consistent with the permission requirements for
+the current Mark Item Damaged or Missing functionality.)
+
+If the item to be marked Discard/Weed is checked out to a patron, the
+staff will be presented with a dialog informing them that the item is
+checked out and asking if they would like to check it in and proceed.
+If they choose to continue, the item will be checked in and then
+marked with the Discard/Weed status.  If the staff person chooses to
+cancel, then the item will not be checked in, and it will not be
+marked Discard/Weed.  The Mark Item Missing functionality has also
+been changed to exhibit this behavior with checked out items.  The
+Mark Item Damaged functionality already handles checked out item.
+
+Should the item have a status of In Transit at the time it is to be
+marked, then staff will be prompted to abort the transit before
+proceeding with changing the item's status.  If they choose to abort
+the transit and they have the permission to do so, the transit will be
+aborted and the item's status changed.  If they choose to cancel, then
+the transit will not be aborted and the item's status will remain
+unchanged.  This change applies to all three of the current Mark Item
+statuses: Missing, Damaged, and Discard/Weed.
+
+Marking an item Discard/Weed is typically one step away from deleting
+the item.  For this reason, if the item to be marked Discard/Weed is
+not in a Checked Out or In Transit status, but it is in a status that
+restricts item deletion, the staff will be presented with a dialog
+notifying them of the item's status and asking if they wish to
+proceed.  If staff choose to proceed and they have the
+`COPY_DELETE_WARNING.override` permission, then the item will be
+marked with the Discard/Weed status.  Naturally, the item's status
+will be unchanged if they choose not to proceed.  This change does not
+affect the marking of an item as Missing or Damaged.
+
+Marking an item as Discard/Weed has one more additional check that the
+other statuses do not.  If the item being marked as Discard/Weed is
+the last copy that can fill a hold, then staff will also be notified
+of this condition and asked if they wish to continue.  In this case,
+there is no permission required.  Whether or not the item is marked as
+Discard/Weed in this case depends solely on the staff's choice.
+
+Back End Changes
+++++++++++++++++
+
+In order to accommodate the presentation of dialogs and overrides in
+the staff client, the `OpenILS::Application::Circ` module's method for
+marking item statuses has had a few changes made.  Firstly, the code
+of the `mark_item` function has been rearranged to a more logical
+flow.  Most of the condition and permission checks are made before
+creating a transaction.  Secondly, it has been modified to return 3
+new events when certain conditions are met:
+
+ * `ITEM_TO_MARK_CHECKED_OUT`
+ * `ITEM_TO_MARK_IN_TRANSIT`
+ * `ITEM_TO_MARK_LAST_HOLD_COPY`
+
+The `COPY_DELETE_WARNING` event will be returned when attempting to
+mark an item with the Discard/Weed status and the status has the
+`restrict_copy_delete` flag set to true.
+
+The function now also recognizes a hash of extra arguments for all
+statuses and not just for the mark Damaged functionality.  This
+argument hash can be used to bypass or override any or all of the
+above mentioned events.  Each event has a corresponding argument that
+if set to a "true" value will cause the `mark_item` to bypass the
+given event.  These argument flags are, respectively:
+
+ * `handle_checkin`
+ * `handle_transit`
+ * `handle_last_hold_copy`
+ * `handle_copy_delete_warning`
+
+The code to mark an item damaged still accepts its previous hash
+arguments in addition to these new ones.
+
+The function still returns other errors and events as before.  It
+still returns 1 on success.
+
+It is also worth noting here that the staff client can be easily
+extended with the ability to mark items into the other statuses
+offered by the back end functions.  Most of the staff client
+functionality is implemented in two functions with placeholders in the
+main function (`egCirc.mark_item`) for the unimplemented statuses.

-----------------------------------------------------------------------

Summary of changes:
 Open-ILS/src/extras/ils_events.xml                 |  12 ++
 .../src/perlmods/lib/OpenILS/Application/Circ.pm   |  97 +++++++--
 .../live_t/zz-lp1779467-mark-item-discard.t        | 238 +++++++++++++++++++++
 .../src/templates/staff/cat/catalog/t_holdings.tt2 |   2 +
 .../src/templates/staff/cat/catalog/t_holds.tt2    |   2 +
 Open-ILS/src/templates/staff/cat/item/index.tt2    |   1 +
 Open-ILS/src/templates/staff/cat/item/t_list.tt2   |   2 +
 .../staff/circ/checkin/t_checkin_table.tt2         |   4 +
 .../src/templates/staff/circ/holds/t_pull_list.tt2 |   2 +
 .../templates/staff/circ/holds/t_shelf_list.tt2    |   2 +
 .../templates/staff/circ/patron/t_holds_list.tt2   |   2 +
 .../src/templates/staff/circ/renew/t_renew.tt2     |   4 +
 .../templates/staff/circ/share/circ_strings.tt2    |   9 +
 .../web/js/ui/default/staff/cat/catalog/app.js     |  22 +-
 Open-ILS/web/js/ui/default/staff/cat/item/app.js   |  13 +-
 .../web/js/ui/default/staff/circ/checkin/app.js    |  14 ++
 Open-ILS/web/js/ui/default/staff/circ/renew/app.js |  13 ++
 .../web/js/ui/default/staff/circ/services/circ.js  | 152 +++++++++++--
 .../web/js/ui/default/staff/circ/services/holds.js |  36 +++-
 .../web/js/ui/default/staff/circ/services/item.js  |   8 +-
 .../enhanced-mark-item-functionality.adoc          | 112 ++++++++++
 21 files changed, 696 insertions(+), 51 deletions(-)
 create mode 100644 Open-ILS/src/perlmods/live_t/zz-lp1779467-mark-item-discard.t
 create mode 100644 docs/RELEASE_NOTES_NEXT/Circulation/enhanced-mark-item-functionality.adoc


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list