1 package OpenILS::Utils::Penalty;
2 use strict; use warnings;
5 use OpenSRF::EX qw(:try);
6 use OpenSRF::Utils::Cache;
7 use OpenILS::Utils::DateTime qw/:datetime/;
8 use OpenILS::Application::AppUtils;
9 use OpenSRF::Utils::Logger qw(:logger);
10 use OpenILS::Utils::CStoreEditor qw/:funcs/;
11 use OpenILS::Utils::Fieldmapper;
12 use OpenILS::Const qw/:const/;
13 my $U = "OpenILS::Application::AppUtils";
15 # calculate and update the well-known penalties, limited to the list supplied
16 sub calculate_penalties {
17 my($class, $e, $user_id, $context_org, @only_penalties) = @_;
21 $e = new_editor(xact =>1);
25 my $penalties = $e->json_query({from => ['actor.calculate_system_penalties',$user_id, $context_org]});
27 if (@only_penalties) {
28 my $all_penalties = $penalties;
31 my @only_penalties_id_list = grep {/^\d+$/} @only_penalties;
33 if (my @name_penalties = grep {/\D/} @only_penalties) { # has at least one non-numeric character
34 my $only_these_penalties = $e->search_config_standing_penalty({name => \@name_penalties});
35 my %penalty_override_map = $U->ou_ancestor_setting_batch_insecure(
37 [ map { 'circ.custom_penalty_override.'. $_ } @name_penalties ]
40 push @only_penalties_id_list, map { $_->id } @$only_these_penalties;
41 push @only_penalties_id_list, map { $_->{value} } values %penalty_override_map;
44 for my $p (@$all_penalties) {
45 if (grep {$p->{standing_penalty} eq $_} @only_penalties_id_list) {
51 my $user = $e->retrieve_actor_user( $user_id );
52 my @existing_penalties = grep { defined $_->{id} } @$penalties;
53 my @wanted_penalties = grep { !defined $_->{id} } @$penalties;
57 for my $pen_obj (@wanted_penalties) {
59 my $pen = Fieldmapper::actor::user_standing_penalty->new;
60 $pen->$_($pen_obj->{$_}) for keys %$pen_obj;
62 # let's see if this penalty is accounted for already
63 my ($existing) = grep {
64 $_->{org_unit} == $pen_obj->{org_unit} and
65 $_->{standing_penalty} == $pen_obj->{standing_penalty}
66 } @existing_penalties;
69 # we have one of these already. Leave it be, but remove it from the
70 # existing set so it's not deleted in the subsequent loop
71 @existing_penalties = grep { $_->{id} ne $existing->{id} } @existing_penalties;
75 # this is a new penalty
76 $e->create_actor_user_standing_penalty($pen) or return $e->die_event;
78 my $csp_obj = $csp{$pen->standing_penalty} ||
79 $e->retrieve_config_standing_penalty( $pen->standing_penalty );
82 $csp{$pen->standing_penalty} = $csp_obj;
84 push(@trigger_events, ['penalty.' . $csp_obj->name, $pen, $pen->org_unit]);
88 # at this point, any penalties remaining in the existing
89 # penalty set are unaccounted for and should be removed
90 for my $pen_obj (@existing_penalties) {
91 my $pen = Fieldmapper::actor::user_standing_penalty->new;
92 $pen->$_($pen_obj->{$_}) for keys %$pen_obj;
93 $e->delete_actor_user_standing_penalty($pen) or return $e->die_event;
96 $e->commit if $commit;
98 $U->create_events_for_hook($$_[0], $$_[1], $$_[2]) for @trigger_events;
102 # any penalties whose block_list has an item from @fatal_mask will be sorted
103 # into the fatal_penalties set. Others will be sorted into the info_penalties set
104 sub retrieve_penalties {
105 my($class, $e, $user_id, $context_org, @fatal_mask) = @_;
108 my $penalties = $class->retrieve_usr_penalties($e, $user_id, $context_org);
110 for my $p (@$penalties) {
112 if($p->standing_penalty->block_list) {
113 for my $m (@fatal_mask) {
114 if($p->standing_penalty->block_list =~ /$m/) {
115 push(@fatal, $p->standing_penalty);
121 push(@info, $p->standing_penalty) unless $pushed;
124 return {fatal_penalties => \@fatal, info_penalties => \@info};
128 # Returns a list of actor_user_standing_penalty objects
129 sub retrieve_usr_penalties {
130 my($class, $e, $user_id, $context_org) = @_;
132 return $e->search_actor_user_standing_penalty([
135 org_unit => $U->get_org_full_path($context_org),
137 {stop_date => undef},
138 {stop_date => {'>' => 'now'}}
141 {flesh => 1, flesh_fields => {ausp => ['standing_penalty']}}