<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>
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;
# 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/ ) {
}
}
+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) = @_;
--- /dev/null
+#!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.
<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') %]"
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') %]"
<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>
<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>
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>
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>
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>
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>
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"
"{{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") %]';
});
}
+ $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();
});
}]);
}
+ $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
}]);
}
itemSvc.selectedHoldingsDamaged(copyGrid.selectedItems());
}
+ $scope.selectedHoldingsDiscard = function () {
+ itemSvc.selectedHoldingsDiscard(copyGrid.selectedItems());
+ }
+
$scope.selectedHoldingsMissing = function () {
itemSvc.selectedHoldingsMissing(copyGrid.selectedItems());
}
}
+ $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) {
}
}
+ $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(
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);
}).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);
+ });
});
}
});
}
+ 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) {
});
}
+ 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)});
});
}
--- /dev/null
+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.