From 9896d830ca753e188606426016e7f678786a3a9a Mon Sep 17 00:00:00 2001 From: Thibaud Guillot Date: Fri, 23 Feb 2024 10:14:40 +0100 Subject: [PATCH] Bug 8137: Add 'global' has_priority rules for checkouts The aim here is to avoid the accumulation of checkouts possibilities through the rules of the various branches, in order to have a global quota. Test plan: 1) - Set for library "A" a max checkouts allowed to 3 for any categorycode on "Default checkout, hold and return policy" - Set for library "B" a max checkouts allowed to 2 for any categorycode on "Default checkout, hold and return policy" 2) Currently you can accumulate on each branch to get 5 checkouts in this test. 3) Apply this patch 4) Run updatedatabase script and rebuild schema, restart_all.. 5) Now you have a "Priority checkouts rule" checkboxes on "Default checkouts[..]" table and the same by categorycode below (only on "Standard rules for all librairies" section). 6) If checked on save, these rules will override global max checkouts allowed even if you try to check out from different librairy. 7) According to this test plan, if you set a "global" max checkouts to 2 you can only perform 2 checkouts and not more if you try on another lib. Try on the both tables (by defaults and by defaults/categorycode). Sponsored-by: BibLibre --- C4/Circulation.pm | 51 ++++++++++++++++--- Koha/CirculationRules.pm | 27 ++++++++-- admin/smart-rules.pl | 16 +++++- .../Bug_8137-add-column-has_priority.pl | 13 +++++ .../prog/en/modules/admin/smart-rules.tt | 40 ++++++++++----- 5 files changed, 122 insertions(+), 25 deletions(-) create mode 100644 installer/data/mysql/atomicupdate/Bug_8137-add-column-has_priority.pl diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 8ad7cfa08c2..ef4cb628614 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -461,6 +461,40 @@ sub TooMany { } ); + my $default_priority_no_branch_no_cat_patron_maxissueqty = Koha::CirculationRules->get_effective_rule( + { + rule_name => 'patron_maxissueqty', + has_priority => 1, + } + ); + my $default_priority_no_branch_patron_maxissueqty = Koha::CirculationRules->get_effective_rule( + { + categorycode => $cat_borrower, + rule_name => 'patron_maxissueqty', + has_priority => 1, + } + ); + + my $default_priority_patron_maxissueqty = $default_priority_no_branch_patron_maxissueqty // $default_priority_no_branch_no_cat_patron_maxissueqty; + + $maxissueqty_rule = $default_priority_patron_maxissueqty ? $default_priority_patron_maxissueqty : $maxissueqty_rule; + + my $default_priority_no_branch_no_cat_patron_maxonsiteissueqty = Koha::CirculationRules->get_effective_rule( + { + rule_name => 'patron_maxonsiteissueqty', + has_priority => 1, + } + ); + my $default_priority_no_branch_patron_maxonsiteissueqty = Koha::CirculationRules->get_effective_rule( + { + categorycode => $cat_borrower, + rule_name => 'patron_maxonsiteissueqty', + has_priority => 1, + } + ); + + my $default_priority_patron_maxonsiteissueqty = $default_priority_no_branch_patron_maxonsiteissueqty // $default_priority_no_branch_no_cat_patron_maxonsiteissueqty; + # if a rule is found and has a loan limit set, count # how many loans the patron already has that meet that # rule @@ -534,15 +568,20 @@ sub TooMany { my $checkout_count = $sum_checkouts->{total} || 0; my $onsite_checkout_count = $sum_checkouts->{onsite_checkouts} || 0; + my $max_checkouts_allowed = $default_priority_patron_maxissueqty->rule_value // $maxissueqty_rule->rule_value; + my $max_onsite_checkouts_allowed = $default_priority_patron_maxonsiteissueqty->rule_value // $maxonsiteissueqty_rule->rule_value; + my $circulation_rule = $default_priority_patron_maxissueqty // $maxissueqty_rule; + my $onsite_circulation_rule = $default_priority_patron_maxonsiteissueqty // $maxonsiteissueqty_rule; + my $checkout_rules = { checkout_count => $checkout_count, onsite_checkout_count => $onsite_checkout_count, onsite_checkout => $onsite_checkout, - max_checkouts_allowed => $maxissueqty_rule ? $maxissueqty_rule->rule_value : undef, - max_onsite_checkouts_allowed => $maxonsiteissueqty_rule ? $maxonsiteissueqty_rule->rule_value : undef, + max_checkouts_allowed => $max_checkouts_allowed, + max_onsite_checkouts_allowed => $max_onsite_checkouts_allowed, switch_onsite_checkout => $switch_onsite_checkout, - circulation_rule => $maxissueqty_rule, - onsite_circulation_rule => $maxonsiteissueqty_rule, + circulation_rule => $circulation_rule, + onsite_circulation_rule => $onsite_circulation_rule, }; # If parent rules exists if ( defined($parent_maxissueqty_rule) and defined($parent_maxissueqty_rule->rule_value) ){ @@ -581,8 +620,8 @@ sub TooMany { my $checkout_count = $checkouts->count; my $onsite_checkout_count = $checkouts->search({ onsite_checkout => 1 })->count; - my $max_checkouts_allowed = $branch_borrower_circ_rule->{patron_maxissueqty}; - my $max_onsite_checkouts_allowed = $branch_borrower_circ_rule->{patron_maxonsiteissueqty} || undef; + my $max_checkouts_allowed = $default_priority_patron_maxissueqty->rule_value ? $default_priority_patron_maxissueqty->rule_value : $branch_borrower_circ_rule->{patron_maxissueqty}; + my $max_onsite_checkouts_allowed = $default_priority_patron_maxonsiteissueqty->rule_value ? $default_priority_patron_maxonsiteissueqty->rule_value : $branch_borrower_circ_rule->{patron_maxonsiteissueqty}; my $qty_over = _check_max_qty( { diff --git a/Koha/CirculationRules.pm b/Koha/CirculationRules.pm index 1d074768754..03df0a51e73 100644 --- a/Koha/CirculationRules.pm +++ b/Koha/CirculationRules.pm @@ -216,6 +216,9 @@ our $RULE_KINDS = { holds_pickup_period => { scope => [ 'branchcode', 'categorycode', 'itemtype' ], }, + has_priority => { + scope => [ 'branchcode', 'categorycode' ], + }, # Not included (deprecated?): # * accountsent # * reservecharge @@ -248,11 +251,13 @@ sub get_effective_rule { $params->{categorycode} //= undef; $params->{branchcode} //= undef; $params->{itemtype} //= undef; + $params->{has_priority} //= undef; my $rule_name = $params->{rule_name}; my $categorycode = $params->{categorycode}; my $itemtype = $params->{itemtype}; my $branchcode = $params->{branchcode}; + my $has_priority = $params->{has_priority}; Koha::Exceptions::MissingParameter->throw( "Required parameter 'rule_name' missing") @@ -271,6 +276,7 @@ sub get_effective_rule { $search_params->{categorycode} = defined $categorycode ? [ $categorycode, undef ] : undef; $search_params->{itemtype} = defined $itemtype ? [ $itemtype, undef ] : undef; $search_params->{branchcode} = defined $branchcode ? [ $branchcode, undef ] : undef; + $search_params->{has_priority} = defined $has_priority ? $has_priority : 0; my $rule = $self->search( $search_params, @@ -308,10 +314,10 @@ sub get_effective_rule_value { my $categorycode = $params->{categorycode}; my $itemtype = $params->{itemtype}; my $branchcode = $params->{branchcode}; - + my $has_priority = $params->{has_priority}; my $memory_cache = Koha::Cache::Memory::Lite->get_instance; my $cache_key = sprintf "CircRules:%s:%s:%s:%s", $rule_name // q{}, - $categorycode // q{}, $branchcode // q{}, $itemtype // q{}; + $categorycode // q{}, $branchcode // q{}, $itemtype // q{} ,$has_priority // q{}; my $cached = $memory_cache->get_from_cache($cache_key); return $cached if $cached; @@ -334,6 +340,7 @@ sub get_effective_rules { my $categorycode = $params->{categorycode}; my $itemtype = $params->{itemtype}; my $branchcode = $params->{branchcode}; + my $has_priority = $params->{has_priority}; my $r; foreach my $rule (@$rules) { @@ -343,6 +350,7 @@ sub get_effective_rules { categorycode => $categorycode, itemtype => $itemtype, branchcode => $branchcode, + has_priority => $has_priority, } ); @@ -386,6 +394,8 @@ sub set_rule { my $itemtype = $params->{itemtype}; my $rule_name = $params->{rule_name}; my $rule_value = $params->{rule_value}; + my $has_priority = $params->{has_priority}; + my $can_be_blank = defined $kind_info->{can_be_blank} ? $kind_info->{can_be_blank} : 1; $rule_value = undef if defined $rule_value && $rule_value eq "" && !$can_be_blank; my $is_monetary = defined $kind_info->{is_monetary} ? $kind_info->{is_monetary} : 0; @@ -407,6 +417,7 @@ sub set_rule { if ($rule) { if ( defined $rule_value ) { $rule->rule_value($rule_value); + $rule->has_priority($has_priority); $rule->update(); } else { @@ -422,6 +433,7 @@ sub set_rule { itemtype => $itemtype, rule_name => $rule_name, rule_value => $rule_value, + has_priority => $has_priority, } ); $rule->store(); @@ -444,10 +456,15 @@ sub set_rules { my ( $self, $params ) = @_; my %set_params; - $set_params{branchcode} = $params->{branchcode} if exists $params->{branchcode}; - $set_params{categorycode} = $params->{categorycode} if exists $params->{categorycode}; + $set_params{branchcode} = $params->{branchcode} + if exists $params->{branchcode}; + $set_params{categorycode} = $params->{categorycode} + if exists $params->{categorycode}; $set_params{itemtype} = $params->{itemtype} if exists $params->{itemtype}; - my $rules = $params->{rules}; + $set_params{has_priority} = $params->{has_priority} + if exists $params->{has_priority}; + my $rules = $params->{rules}; + my $rule_objects = []; while ( my ( $rule_name, $rule_value ) = each %$rules ) { diff --git a/admin/smart-rules.pl b/admin/smart-rules.pl index efe1b91dfb5..458656c616d 100755 --- a/admin/smart-rules.pl +++ b/admin/smart-rules.pl @@ -365,6 +365,7 @@ elsif ($op eq "cud-set-branch-defaults") { my $hold_fulfillment_policy = $input->param('hold_fulfillment_policy'); my $returnbranch = $input->param('returnbranch'); my $max_holds = strip_non_numeric( scalar $input->param('max_holds') ); + my $has_priority = $input->param('has_priority_defaults') ? 1 : 0; if ($branch eq "*") { Koha::CirculationRules->set_rules( @@ -382,6 +383,7 @@ elsif ($op eq "cud-set-branch-defaults") { { categorycode => undef, branchcode => undef, + has_priority => $has_priority, rules => { patron_maxissueqty => $patron_maxissueqty, patron_maxonsiteissueqty => $patron_maxonsiteissueqty, @@ -404,6 +406,7 @@ elsif ($op eq "cud-set-branch-defaults") { { categorycode => undef, branchcode => $branch, + has_priority => $has_priority, rules => { patron_maxissueqty => $patron_maxissueqty, patron_maxonsiteissueqty => $patron_maxonsiteissueqty, @@ -427,6 +430,7 @@ elsif ($op eq "cud-add-branch-cat") { $patron_maxonsiteissueqty = strip_non_numeric($patron_maxonsiteissueqty); my $max_holds = $input->param('max_holds'); $max_holds = strip_non_numeric($max_holds); + my $has_priority = $input->param('has_priority_by_cat') ? 1 : 0; if ($branch eq "*") { if ($categorycode eq "*") { @@ -447,7 +451,16 @@ elsif ($op eq "cud-add-branch-cat") { categorycode => $categorycode, branchcode => undef, rules => { - max_holds => $max_holds, + max_holds => $max_holds, + } + } + ); + Koha::CirculationRules->set_rules( + { + categorycode => $categorycode, + branchcode => undef, + has_priority => $has_priority, + rules => { patron_maxissueqty => $patron_maxissueqty, patron_maxonsiteissueqty => $patron_maxonsiteissueqty, } @@ -758,7 +771,6 @@ while ( my $r = $all_rules->next ) { } $template->param(show_branch_cat_rule_form => 1); - $template->param( used_categorycodes => \@used_categorycodes, used_itemtypes => \@used_itemtypes, diff --git a/installer/data/mysql/atomicupdate/Bug_8137-add-column-has_priority.pl b/installer/data/mysql/atomicupdate/Bug_8137-add-column-has_priority.pl new file mode 100644 index 00000000000..05a6e898862 --- /dev/null +++ b/installer/data/mysql/atomicupdate/Bug_8137-add-column-has_priority.pl @@ -0,0 +1,13 @@ +use Modern::Perl; + +return { + bug_number => "8137", + description => "Add column has_priority", + up => sub { + my ($args) = @_; + my ( $dbh, $out ) = @$args{qw(dbh out)}; + + $dbh->do('ALTER TABLE circulation_rules ADD IF NOT EXISTS has_priority INT(1) DEFAULT 0'); + say $out "Added column 'has_priority'"; + }, +}; diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt index 960854b831c..e8580a997f4 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt @@ -622,6 +622,7 @@ + @@ -637,6 +638,7 @@ [% SET hold_fulfillment_policy = CirculationRules.Search( current_branch, undef, undef, 'hold_fulfillment_policy', { want_rule => 1 }) %] [% SET returnbranch = CirculationRules.Search( current_branch, undef, undef, 'returnbranch', { want_rule => 1 }) %] [% SET default_checkout_hold_and_return_policy = ( patron_maxissueqty || patron_maxonsiteissueqty || rule_value || holdallowed || hold_fulfillment_policy || returnbranch ) %] + [% SET has_priority_defaults = patron_maxissueqty.has_priority && patron_maxonsiteissueqty.has_priority %] + @@ -817,6 +822,7 @@
 Priority checkouts rule Total current checkouts allowed Total current on-site checkouts allowed Maximum total holds allowed (count)
[% IF ( default_checkout_hold_and_return_policy ) %] @@ -647,6 +649,9 @@ Not set [% END %] + +
+ @@ -825,11 +831,11 @@ [% FOREACH c IN categorycodes %] [% NEXT UNLESS c %] [% SET i = undef %] - [% SET patron_maxissueqty = all_rules.$c.$i.patron_maxissueqty %] - [% SET patron_maxonsiteissueqty = all_rules.$c.$i.patron_maxonsiteissueqty %] - [% SET max_holds = all_rules.$c.$i.max_holds %] - - [% IF ( patron_maxissueqty.defined && patron_maxissueqty != '' ) || ( patron_maxonsiteissueqty.defined && patron_maxonsiteissueqty != '' ) || ( max_holds.defined && max_holds != '' ) %] + [% SET patron_maxissueqty = CirculationRules.Search( current_branch, c, undef, 'patron_maxissueqty', { want_rule => 1 } ) %] + [% SET patron_maxonsiteissueqty = CirculationRules.Search( current_branch, c, undef, 'patron_maxonsiteissueqty', { want_rule => 1 } ) %] + [% SET max_holds = CirculationRules.Search( current_branch, c, undef, 'max_holds', { want_rule => 1 } ) %] + [% SET has_priority_by_cat = patron_maxissueqty.has_priority && patron_maxonsiteissueqty.has_priority %] + [% IF ( patron_maxissueqty.rule_value && patron_maxissueqty.rule_value != '' ) || ( patron_maxonsiteissueqty.rule_value && patron_maxonsiteissueqty.rule_value != '' ) || ( max_holds.rule_value && max_holds.rule_value != '' ) %] + - @@ -874,6 +882,9 @@ [% END %] + @@ -1678,6 +1689,11 @@ return f.submit(); }); + if($("#branch").val() != '*') { + $(".hasPriority").each(function() { + $(this).remove(); + }); + } }); [% END %] -- 2.30.2
Patron categoryPriority checkouts rule Total current checkouts allowed Total current on-site checkouts allowed Total holds allowed
[% IF c == undef %] @@ -838,28 +844,30 @@ [% Categories.GetName(c) | html %] [% END %] + + - [% IF patron_maxissueqty.defined && patron_maxissueqty != '' %] - [% patron_maxissueqty | html %] + [% IF patron_maxissueqty.rule_value && patron_maxissueqty.rule_value != '' %] + [% patron_maxissueqty.rule_value | html %] [% ELSE %] Unlimited [% END %] - [% IF patron_maxonsiteissueqty.defined && patron_maxonsiteissueqty != '' %] - [% patron_maxonsiteissueqty | html %] + [% IF patron_maxonsiteissueqty.rule_value && patron_maxonsiteissueqty.rule_value != '' %] + [% patron_maxonsiteissueqty.rule_value | html %] [% ELSE %] Unlimited [% END %] - [% IF max_holds.defined && max_holds != '' %] - [% max_holds | html %] + [% IF max_holds.rule_value && max_holds.rule_value != '' %] + [% max_holds.rule_value | html %] [% ELSE %] Unlimited [% END %] Delete + +