my( $self, $conn, $auth, $args ) = @_;
translate_legacy_args($args);
$args->{override_args} = { all => 1 } unless defined $args->{override_args};
+ $args->{new_copy_alerts} ||= $self->api_level > 1 ? 1 : 0;
my $api = $self->api_name;
my $circulator =
$circulator->is_renewal(1) if $api =~ /renew/;
$circulator->is_checkin(1) if $api =~ /checkin/;
+ $circulator->is_checkout(1) if $api =~ /checkout/;
+ $circulator->override(1) if $api =~ /override/o;
$circulator->mk_env();
$circulator->noop(1) if $circulator->claims_never_checked_out;
return circ_events($circulator) if $circulator->bail_out;
-
- $circulator->override(1) if $api =~ /override/o;
if( $api =~ /checkout\.permit/ ) {
$circulator->do_permit();
return $data;
} elsif( $api =~ /checkout/ ) {
- $circulator->is_checkout(1);
$circulator->do_checkout();
} elsif( $circulator->is_res_checkin ) {
$circulator->do_checkin();
} elsif( $api =~ /renew/ ) {
- $circulator->is_renewal(1);
$circulator->do_renew();
}
copy
copy_id
copy_barcode
+ user_copy_alerts
+ system_copy_alerts
+ next_copy_status
+ copy_state
patron
patron_id
patron_barcode
}
}
+sub collect_user_copy_alerts {
+ my $self = shift;
+ my $e = $self->editor;
+
+ if($self->copy) {
+ my $alerts = $e->search_asset_copy_alert([
+ {copy => $self->copy->id, ack_time => undef}
+ {flesh => 1, flesh_fields => { ccat => [ qw/ alert_type / ] }}
+ ]);
+ if (ref $alerts eq "ARRAY") {
+ $logger->info("circulator: found " . scalar(@$alerts) . " alerts for copy" .
+ $self->copy->id);
+ $self->user_copy_alerts($alerts);
+ }
+ }
+}
+
+sub filter_user_copy_alerts {
+ my $self = shift;
+
+ if(my $alerts = $self->user_copy_alerts) {
+
+ my $suppress_orgs = $U->get_org_descendants($self->circ_lib);
+ my $suppressions = $e->search_actor_copy_alert_suppress(
+ {org => $suppress_orgs}
+ );
+
+ my @final_alerts;
+ foreach my $a (@$alerts) {
+ # filter on event type
+ if (defined $a->alert_type) {
+ next if ($a->alert_type->event eq 'CHECKIN' && !$self->is_checkin);
+ next if ($a->alert_type->event eq 'CHECKOUT' && !$self->is_checkout);
+ next if (defined $a->alert_type->in_renew && $U->is_true($a->in_renew) && !$self->is_renewal);
+ }
+
+ # filter on suppression
+ next if (grep { $a->alert_type->id == $_->alert_type} @$suppressions);
+
+ # filter on "only at circ lib"
+ if (defined $a->alert_type->at_circ) {
+ my $copy_circ_lib = (ref $self->copy->circ_lib) ?
+ $self->copy->circ_lib->id : $self->copy->circ_lib;
+ my $orgs = $U->get_org_descendants($copy_circ_lib);
+
+ if ($U->is_true($a->alert_type->invert_location)) {
+ next if (grep {$_ == $copy_circ_lib} @$orgs);
+ } else {
+ next unless (grep {$_ == $copy_circ_lib} @$orgs);
+ }
+ }
+
+ # filter on "only at owning lib"
+ if (defined $a->alert_type->at_owning) {
+ my $copy_owning_lib = (ref $self->volume->owning_lib) ?
+ $self->volume->owning_lib->id : $self->volume->owning_lib;
+ my $orgs = $U->get_org_descendants($copy_owning_lib);
+
+ if ($U->is_true($a-->alert_type>invert_location)) {
+ next if (grep {$_ == $copy_owning_lib} @$orgs);
+ } else {
+ next unless (grep {$_ == $copy_owning_lib} @$orgs);
+ }
+ }
+
+ $a->alert_type->next_status($U->unique_unnested_numbers($a->alert_type->next_status));
+
+ push @final_alerts, $a;
+ }
+
+ $self->user_copy_alerts(\@final_alerts);
+ }
+}
+
+sub generate_system_copy_alerts {
+ my $self = shift;
+ return unless($self->copy);
+
+ my $e = $self->editor;
+
+ my $suppress_orgs = $U->get_org_descendants($self->circ_lib);
+ my $suppressions = $e->search_actor_copy_alert_suppress(
+ {org => $suppress_orgs}
+ );
+
+ # events we care about ...
+ my $event = [];
+ push(@$event, 'CHECKIN') if $self->is_checkin;
+ push(@$event, 'CHECKOUT') if $self->is_checkout;
+ return unless scalar(@$event);
+
+ my $alert_orgs = $U->get_org_ancestors($self->circ_lib);
+ my $alert_types = $e->search_config_copy_alert_type({
+ active => 't'
+ scope_org => $alert_orgs,
+ event => $event,
+ state => $self->copy_state,
+ '-or' => [ { in_renew => $self->is_renewal }, { in_renew => undef } ],
+ });
+
+ my @final_types;
+ foreach my $a (@$alert_types) {
+ # filter on "only at circ lib"
+ if (defined $a->at_circ) {
+ my $copy_circ_lib = (ref $self->copy->circ_lib) ?
+ $self->copy->circ_lib->id : $self->copy->circ_lib;
+ my $orgs = $U->get_org_descendants($copy_circ_lib);
+
+ if ($U->is_true($a->invert_location)) {
+ next if (grep {$_ == $copy_circ_lib} @$orgs);
+ } else {
+ next unless (grep {$_ == $copy_circ_lib} @$orgs);
+ }
+ }
+
+ # filter on "only at owning lib"
+ if (defined $a->at_owning) {
+ my $copy_owning_lib = (ref $self->volume->owning_lib) ?
+ $self->volume->owning_lib->id : $self->volume->owning_lib;
+ my $orgs = $U->get_org_descendants($copy_owning_lib);
+
+ if ($U->is_true($a->invert_location)) {
+ next if (grep {$_ == $copy_owning_lib} @$orgs);
+ } else {
+ next unless (grep {$_ == $copy_owning_lib} @$orgs);
+ }
+ }
+
+ push @final_types, $a;
+ }
+
+ if (@final_types) {
+ $logger->info("circulator: found " . scalar(@final_types) . " system alert types for copy" .
+ $self->copy->id);
+ }
+
+ my @alerts;
+ foreach my $t (@final_types) {
+ $t->next_status($U->unique_unnested_numbers($t->next_status));
+
+ my $alert = new Fieldmapper::asset::copy_alert;
+ $alert->alert_type($t->id);
+ $alert->copy($self->copy->id);
+ $alert->temp(1);
+ $alert->create_staff($e->requestor->id);
+ $alert->create_time('now');
+ $alert->ack_staff($e->requestor->id);
+ $alert->ack_time('now');
+
+ $alert = $e->create_asset_copy_alert($alert);
+
+ next unless $alert;
+
+ $alert->alert_type($t->clone);
+
+ push(@{$self->next_copy_status}, $t->next_status) if ($t->next_status);
+ push(@alerts, $alert) unless (grep {$_->alert_type == $t->id} @$suppressions);
+ }
+
+ $self->system_copy_alerts(\@alerts);
+}
+
sub mk_env {
my $self = shift;
my $e = $self->editor;
+ $self->next_copy_status([]) unless (defined $self->next_copy_status);
+
# --------------------------------------------------------------------------
# Grab the fleshed copy
# --------------------------------------------------------------------------
}
},
"where" => {
+ deleted => 'f',
"+bresv" => {
"id" => (ref $self->reservation) ?
$self->reservation->id : $self->reservation
if($copy) {
$self->save_trimmed_copy($copy);
+
+ # alerts!
+ $self->copy_state(
+ $e->json_query(
+ {from => ['asset.copy_state', $copy->id]}
+ )->[0]{'asset.copy_state'}
+ );
+
+ $self->generate_system_copy_alerts;
+ $self->collect_user_copy_alerts;
+ $self->filter_user_copy_alerts;
+
} else {
# We can't renew if there is no copy
return $self->bail_on_events(OpenILS::Event->new('ASSET_COPY_NOT_FOUND'))
sub check_copy_alert {
my $self = shift;
+
+ if ($self->new_copy_alerts) {
+ my @alerts;
+ push @alerts, @{$self->user_copy_alerts}) # we have preexisting alerts
+ if ($self->user_copy_alerts && @{$self->user_copy_alerts});
+
+ push @alerts, @{$self->system_copy_alerts}) # we have new dynamic alerts
+ if ($self->system_copy_alerts && @{$self->system_copy_alerts});
+
+ if (@alerts) {
+ $self->bail_out(1) if (!$self->override);
+ return OpenILS::Event->new( 'COPY_ALERT_MESSAGE', payload => \@alerts);
+ }
+ }
+
return undef if $self->is_renewal;
return OpenILS::Event->new(
'COPY_ALERT_MESSAGE', payload => $self->copy->alert_message)
$U->ou_ancestor_setting_value($self->circ->circ_lib, 'circ.claim_never_checked_out.mark_missing')) {
# the item was not supposed to be checked out to the user and should now be marked as missing
- $self->copy->status(OILS_COPY_STATUS_MISSING);
+ my $next_status = $self->next_copy_status->[0] || OILS_COPY_STATUS_MISSING;
+ $self->copy->status($next_status);
$self->update_copy;
} else {
my $stat = $U->copy_status($copy->status)->id;
+ my $next_status = $self->next_copy_status->[0] || OILS_COPY_STATUS_RESHELVING;
+
if($force || (
$stat != OILS_COPY_STATUS_ON_HOLDS_SHELF and
$stat != OILS_COPY_STATUS_CATALOGING and
$stat != OILS_COPY_STATUS_IN_TRANSIT and
- $stat != OILS_COPY_STATUS_RESHELVING )) {
+ $stat != $next_status )) {
- $copy->status( OILS_COPY_STATUS_RESHELVING );
+ $copy->status( $next_status );
$self->update_copy;
$self->checkin_changed(1);
}
} elsif ($circ_lib != $self->circ_lib and $stat == OILS_COPY_STATUS_MISSING) {
$logger->info("circulator: not updating copy status on checkin because copy is missing");
} else {
- $self->copy->status($U->copy_status(OILS_COPY_STATUS_RESHELVING));
+ my $next_status = $self->next_copy_status->[0] || OILS_COPY_STATUS_RESHELVING;
+ $self->copy->status($U->copy_status($next_status));
$self->update_copy;
}
if ($immediately_available) {
# item status does not need to be retained, so give it a
# reshelving status as if it were a normal checkin
- $self->copy->status($U->copy_status(OILS_COPY_STATUS_RESHELVING));
+ my $next_status = $self->next_copy_status->[0] || OILS_COPY_STATUS_RESHELVING;
+ $self->copy->status($U->copy_status($next_status));
$self->update_copy;
} else {
$logger->info("circulator: leaving lost/longoverdue copy".
} else {
# lost/longoverdue item is home and processed, treat like a normal
# checkin from this point on
- $self->copy->status($U->copy_status(OILS_COPY_STATUS_RESHELVING));
+ my $next_status = $self->next_copy_status->[0] || OILS_COPY_STATUS_RESHELVING;
+ $self->copy->status($U->copy_status($next_status));
$self->update_copy;
}
}
// options : non-parameter controls. e.g. "override", "check_barcode"
service.checkout = function(params, options) {
if (!options) options = {};
+ params.new_copy_alerts = 1;
console.debug('egCirc.checkout() : '
+ js2JSON(params) + ' : ' + js2JSON(options));
// Rejected if the renewal cannot be completed.
service.renew = function(params, options) {
if (!options) options = {};
+ params.new_copy_alerts = 1;
console.debug('egCirc.renew() : '
+ js2JSON(params) + ' : ' + js2JSON(options));
// Rejected if the checkin cannot be completed.
service.checkin = function(params, options) {
if (!options) options = {};
+ params.new_copy_alerts = 1;
console.debug('egCirc.checkin() : '
+ js2JSON(params) + ' : ' + js2JSON(options));
// action == what action to take if the user confirms the alert
service.copy_alert_dialog = function(evt, params, options, action) {
if (angular.isArray(evt)) evt = evt[0];
- return egConfirmDialog.open(
- egCore.strings.COPY_ALERT_MSG_DIALOG_TITLE,
- evt.payload, // payload == alert message text
- { copy_barcode : params.copy_barcode,
- ok : function() {},
- cancel : function() {}
- }
- ).result.then(function() {
- options.override = true;
- return service[action](params, options);
- });
+ if (!angular.isArray(evt.payload)) {
+ return egConfirmDialog.open(
+ egCore.strings.COPY_ALERT_MSG_DIALOG_TITLE,
+ evt.payload, // payload == alert message text
+ { copy_barcode : params.copy_barcode,
+ ok : function() {},
+ cancel : function() {}
+ }
+ ).result.then(function() {
+ options.override = true;
+ return service[action](params, options);
+ });
+ } else { // we got a list of copy alert objects ...
+ // TODO: build a chain of alert ack (and maybe status selection) dialogs
+ }
}
// check the barcode. If it's no good, show the warning dialog