From f53920e1bdada25e0161cf4a703b498502200c25 Mon Sep 17 00:00:00 2001 From: Alex Arnaud Date: Wed, 12 Dec 2018 11:08:43 +0100 Subject: [PATCH] Bug 23732: Hold rules checker Test plan: - apply patch - try to place a hold, on the Hold details page you should see a "Holding rules" button. Click on it - check that the info is consistent with the holding rules, the syspref and the current and future number of holds of the patron - place the hold - set maxreserves syspref to 1 - try to place a hold - see in the holds rules checker that the maxreserves is highlighted in red - restore maxreserves syspref - test that sabotaging the matched circulation rule or hold policy is reflected in the hold rules checker Co-authored-by: Victor Grousset/tuxayo Co-authored-by: Arthur Suzuki Rebased-by: Julian Maurice Co-authored-by: Julian Maurice Rebased-by: Victor Grousset/tuxayo --- C4/Reserves.pm | 274 +- .../prog/en/modules/reserve/request.tt | 2394 +++++++++-------- reserve/request.pl | 60 +- t/db_dependent/Reserves/HoldRulesChecker.t | 110 + 4 files changed, 1641 insertions(+), 1197 deletions(-) create mode 100644 t/db_dependent/Reserves/HoldRulesChecker.t diff --git a/C4/Reserves.pm b/C4/Reserves.pm index 432ae461dc..bd4747751c 100644 --- a/C4/Reserves.pm +++ b/C4/Reserves.pm @@ -373,19 +373,23 @@ sub CanBookBeReserved { return { status => 'alreadypossession' }; } + my @circulation_rules; + if ( $params->{itemtype} ) { # biblio-level, item type-contrained - my $patron = Koha::Patrons->find($borrowernumber); - my $reservesallowed = Koha::CirculationRules->get_effective_rule( + my $patron = Koha::Patrons->find($borrowernumber); + my $reservesallowed_rule = Koha::CirculationRules->get_effective_rule( { itemtype => $params->{itemtype}, categorycode => $patron->categorycode, branchcode => $pickup_branchcode, rule_name => 'reservesallowed', } - )->rule_value; + ); + push @circulation_rules, $reservesallowed_rule if $reservesallowed_rule; + my $reservesallowed = $reservesallowed_rule ? $reservesallowed_rule->rule_value : undef; $reservesallowed = ( $reservesallowed eq '' ) ? undef : $reservesallowed; my $count = $patron->holds->search( @@ -398,7 +402,7 @@ sub CanBookBeReserved { { join => ['item'] } )->count; - return { status => '' } + return { status => '', circulation_rules => \@circulation_rules } if defined $reservesallowed and $reservesallowed < $count + 1; } @@ -419,36 +423,100 @@ sub CanBookBeReserved { $items = Koha::Items->search( { biblionumber => $biblionumber } ); } - my $canReserve = { status => '' }; - my $patron = Koha::Patrons->find($borrowernumber); + my $response = { status => '', item_responses => [] }; + my $patron = Koha::Patrons->find($borrowernumber); while ( my $item = $items->next ) { - $canReserve = CanItemBeReserved( $patron, $item, $pickup_branchcode, $params ); - return { status => 'OK' } if $canReserve->{status} eq 'OK'; + my $itemResponse = CanItemBeReserved( $patron, $item, $pickup_branchcode, $params ); + push @{ $response->{item_responses} }, { %$itemResponse, item => $item }; + + if ( $itemResponse->{status} eq 'OK' ) { + $response->{status} = 'OK'; + + return $response; + } } - return $canReserve; + + return $response; } =head2 CanItemBeReserved - $canReserve = &CanItemBeReserved($patron, $item, $branchcode, $params) + $canReserve = CanItemBeReserved($patron, $item, $branchcode, $params) if ($canReserve->{status} eq 'OK') { #We can reserve this Item! } - current params are: - 'ignore_hold_counts' - we use this routine to check if an item can fill a hold - on this case we - should not check if there are too many holds as we only care about reservability - -@RETURNS { status => OK }, if the Item can be reserved. - { status => ageRestricted }, if the Item is age restricted for this borrower. - { status => damaged }, if the Item is damaged. - { status => cannotReserveFromOtherBranches }, if syspref 'canreservefromotherbranches' is OK. - { status => branchNotInHoldGroup }, if borrower home library is not in hold group, and holds are only allowed from hold groups. - { status => tooManyReserves, limit => $limit }, if the borrower has exceeded their maximum reserve amount. - { status => notReservable }, if holds on this item are not allowed - { status => libraryNotFound }, if given branchcode is not an existing library - { status => libraryNotPickupLocation }, if given branchcode is not configured to be a pickup location - { status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode - { status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups. - { status => recall }, if the borrower has already placed a recall on this item +Current params are: + +=over + +=item * C + +we use this routine to check if an item can +fill a hold - on this case we should not check if there are too many holds as +we only care about reservability + +=back + +Returns a hashref with the following keys: + +=over + +=item * C + +A string that can have the following values: + +=over + +=item * C if the item can be reserved + +=item * C if the item is aged restricted for this borrower + +=item * C if the item is damaged + +=item * C if syspref +'canreservefromotherbranches' is OK + +=item * C if borrower home library is not in hold group, +and holds are only allowed from hold groups. + +=item * C if the borrower has exceeded their maximum reserve +amount. + +=item * C if the borrower has exceeded their maximum +reserve amount for this biblio record + +=item * C if the borrower has exceeded their maximum +reserve amount for today + +=item * C if holds on this item are not allowed + +=item * C if given branchcode is not an existing library + +=item * C if given branchcode is not configured to be +a pickup location + +=item * C if branch transfer limit applies on given item +and branchcode + +=item * C if pickup location is not in hold group, and +pickup locations are only allowed from hold groups. + +=item * C if the borrower has already placed a recall on this item + +=back + +=item * C + +Only if C is equal to C, C, +or C. + +It contains the number of reserves allowed. + +=item * C + +An arrayref containing all circulations rules (L) that +have been checked + +=back =cut @@ -473,8 +541,6 @@ sub CanItemBeReserved { } my $dbh = C4::Context->dbh; - my $ruleitemtype; # itemtype of the matching issuing rule - my $allowedreserves = 0; # Total number of holds allowed across all records, default to none # We check item branch if IndependentBranches is ON # and canreservefromotherbranches is OFF @@ -530,38 +596,23 @@ sub CanItemBeReserved { $reserves_control_branch = $patron->branchcode; } - # we retrieve rights - if ( - my $reservesallowed = Koha::CirculationRules->get_effective_rule( - { - itemtype => $item->effective_itemtype, - categorycode => $patron->categorycode, - branchcode => $reserves_control_branch, - rule_name => 'reservesallowed', - } - ) - ) - { - $ruleitemtype = $reservesallowed->itemtype; - $allowedreserves = $reservesallowed->rule_value // 0; #undefined is 0, blank is unlimited - } else { - $ruleitemtype = undef; - } + my @circulation_rules; - my $rights = Koha::CirculationRules->get_effective_rules( + my $holds_per_record_rule = Koha::CirculationRules->get_effective_rule( { categorycode => $patron->categorycode, itemtype => $item->effective_itemtype, branchcode => $reserves_control_branch, - rules => [ 'holds_per_record', 'holds_per_day' ] + rule_name => 'holds_per_record', } ); - my $holds_per_record = $rights->{holds_per_record} // 1; - my $holds_per_day = $rights->{holds_per_day}; + push @circulation_rules, $holds_per_record_rule if $holds_per_record_rule; + + my $holds_per_record = $holds_per_record_rule ? $holds_per_record_rule->rule_value : 1; if ( defined $holds_per_record && $holds_per_record ne '' ) { if ( $holds_per_record == 0 ) { - return _cache { status => "noReservesAllowed" }; + return _cache { status => "noReservesAllowed", circulation_rules => \@circulation_rules }; } if ( !$params->{ignore_hold_counts} ) { my $search_params = { @@ -569,11 +620,28 @@ sub CanItemBeReserved { biblionumber => $item->biblionumber, }; my $holds = Koha::Holds->search($search_params); - return _cache { status => "tooManyHoldsForThisRecord", limit => $holds_per_record } - if $holds->count() >= $holds_per_record; + + if ( $holds->count() >= $holds_per_record ) { + return _cache { + status => "tooManyHoldsForThisRecord", + limit => $holds_per_record, + circulation_rules => \@circulation_rules, + }; + } } } + my $holds_per_day_rule = Koha::CirculationRules->get_effective_rule( + { + categorycode => $patron->categorycode, + itemtype => $item->effective_itemtype, + branchcode => $reserves_control_branch, + rule_name => 'holds_per_day', + } + ); + push @circulation_rules, $holds_per_day_rule if $holds_per_day_rule; + + my $holds_per_day = $holds_per_day_rule ? $holds_per_day_rule->rule_value : undef; if ( !$params->{ignore_hold_counts} && defined $holds_per_day && $holds_per_day ne '' ) { my $today_holds = Koha::Holds->search( { @@ -581,15 +649,42 @@ sub CanItemBeReserved { reservedate => dt_from_string->date } ); - return _cache { status => 'tooManyReservesToday', limit => $holds_per_day } - if $today_holds->count() >= $holds_per_day; + + if ( $today_holds->count() >= $holds_per_day ) { + return _cache { + status => 'tooManyReservesToday', + limit => $holds_per_day, + circulation_rules => \@circulation_rules, + }; + } + } + + my $reservesallowed_rule = Koha::CirculationRules->get_effective_rule( + { + itemtype => $item->effective_itemtype, + categorycode => $patron->categorycode, + branchcode => $reserves_control_branch, + rule_name => 'reservesallowed', + } + ); + push @circulation_rules, $reservesallowed_rule if $reservesallowed_rule; + + my $ruleitemtype; # itemtype of the matching issuing rule + my $allowedreserves = 0; # Total number of holds allowed across all records, default to none + if ($reservesallowed_rule) { + $ruleitemtype = $reservesallowed_rule->itemtype; + $allowedreserves = $reservesallowed_rule->rule_value // 0; #undefined is 0, blank is unlimited } # we check if it's ok or not if ( defined $allowedreserves && $allowedreserves ne '' ) { if ( $allowedreserves == 0 ) { - return _cache { status => 'noReservesAllowed' }; + return _cache { + status => 'noReservesAllowed', + circulation_rules => \@circulation_rules, + }; } + if ( !$params->{ignore_hold_counts} ) { # we retrieve count @@ -632,36 +727,61 @@ sub CanItemBeReserved { $reservecount = $rowcount->{count}; } - return _cache { status => 'tooManyReserves', limit => $allowedreserves } - if $reservecount >= $allowedreserves; + if ( $reservecount >= $allowedreserves ) { + return _cache { + status => 'tooManyReserves', + limit => $allowedreserves, + circulation_rules => \@circulation_rules, + }; + } } } # Now we need to check hold limits by patron category - my $rule = Koha::CirculationRules->get_effective_rule( + my $max_holds_rule = Koha::CirculationRules->get_effective_rule( { categorycode => $patron->categorycode, branchcode => $reserves_control_branch, rule_name => 'max_holds', } ); - if ( !$params->{ignore_hold_counts} && $rule && defined( $rule->rule_value ) && $rule->rule_value ne '' ) { + push @circulation_rules, $max_holds_rule if $max_holds_rule; + + if ( !$params->{ignore_hold_counts} + && $max_holds_rule + && defined( $max_holds_rule->rule_value ) + && $max_holds_rule->rule_value ne '' ) + { my $total_holds_count = Koha::Holds->search( { borrowernumber => $patron->borrowernumber } )->count(); - return _cache { status => 'tooManyReserves', limit => $rule->rule_value } - if $total_holds_count >= $rule->rule_value; + if ( $total_holds_count >= $max_holds_rule->rule_value ) { + return _cache { + status => 'tooManyReserves', + limit => $max_holds_rule->rule_value, + circulation_rules => \@circulation_rules, + }; + } } + my $holdallowed_rule = Koha::CirculationRules->get_effective_rule( + { + branchcode => $reserves_control_branch, + itemtype => $item->effective_itemtype, + rule_name => 'holdallowed', + } + ); + push @circulation_rules, $holdallowed_rule if $holdallowed_rule; + my $branchitemrule = C4::Circulation::GetBranchItemRule( $reserves_control_branch, $item->effective_itemtype ); if ( $branchitemrule->{holdallowed} eq 'not_allowed' ) { - return _cache { status => 'notReservable' }; + return _cache { status => 'notReservable', circulation_rules => \@circulation_rules }; } if ( $branchitemrule->{holdallowed} eq 'from_home_library' && $patron->branchcode ne $item->homebranch ) { - return _cache { status => 'cannotReserveFromOtherBranches' }; + return _cache { status => 'cannotReserveFromOtherBranches', circulation_rules => \@circulation_rules }; } my $item_library = Koha::Libraries->find( { branchcode => $item->homebranch } ); @@ -669,7 +789,7 @@ sub CanItemBeReserved { if ( $patron->branchcode ne $item->homebranch && !$item_library->validate_hold_sibling( { branchcode => $patron->branchcode } ) ) { - return _cache { status => 'branchNotInHoldGroup' }; + return _cache { status => 'branchNotInHoldGroup', circulation_rules => \@circulation_rules }; } } @@ -689,20 +809,30 @@ sub CanItemBeReserved { unless ( $item->can_be_transferred( { to => $destination } ) ) { return _cache { status => 'cannotBeTransferred' }; } + + my $hold_fulfillment_policy_rule = Koha::CirculationRules->get_effective_rule( + { + branchcode => $reserves_control_branch, + itemtype => $item->effective_itemtype, + rule_name => 'hold_fulfillment_policy', + } + ); + push @circulation_rules, $hold_fulfillment_policy_rule if $hold_fulfillment_policy_rule; + if ( $branchitemrule->{hold_fulfillment_policy} eq 'holdgroup' && !$item_library->validate_hold_sibling( { branchcode => $pickup_branchcode } ) ) { - return _cache { status => 'pickupNotInHoldGroup' }; + return _cache { status => 'pickupNotInHoldGroup', circulation_rules => \@circulation_rules }; } if ( $branchitemrule->{hold_fulfillment_policy} eq 'patrongroup' && !Koha::Libraries->find( { branchcode => $patron->branchcode } ) ->validate_hold_sibling( { branchcode => $pickup_branchcode } ) ) { - return _cache { status => 'pickupNotInHoldGroup' }; + return _cache { status => 'pickupNotInHoldGroup', circulation_rules => \@circulation_rules }; } } - return _cache { status => 'OK' }; + return _cache { status => 'OK', circulation_rules => \@circulation_rules }; } =head2 ChargeReserveFee @@ -1831,9 +1961,8 @@ sub _koha_notify_reserve { } ); - my $library = Koha::Libraries->find( $hold->branchcode ); - my $from_email_address = $library->from_email_address; - my $reply_email_address = $library->inbound_email_address; + my $library = Koha::Libraries->find( $hold->branchcode ); + my $from_email_address = $library->from_email_address; my %letter_params = ( module => 'reserves', @@ -1869,7 +1998,6 @@ sub _koha_notify_reserve { letter => $letter, borrowernumber => $borrowernumber, from_address => $from_email_address, - reply_address => $reply_email_address, message_transport_type => $mtt, } ); @@ -2128,8 +2256,6 @@ sub RevertWaitingStatus { } )->next; - my $original = C4::Context->preference('HoldsLog') ? $hold->unblessed : undef; - ## Increment the priority of all other non-waiting ## reserves for this bib record my $holds = Koha::Holds->search( { biblionumber => $hold->biblionumber, priority => { '>' => 0 } } ) @@ -2146,7 +2272,7 @@ sub RevertWaitingStatus { } )->store( { hold_reverted => 1 } ); - logaction( 'HOLDS', 'MODIFY', $hold->id, $hold, undef, $original ) + logaction( 'HOLDS', 'MODIFY', $hold->id, $hold ) if C4::Context->preference('HoldsLog'); _FixPriority( { biblionumber => $hold->biblionumber } ); diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt index 558b5e0d9f..74e07c438d 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt @@ -22,26 +22,23 @@ [% SET categories = Categories.all.unblessed %] [% SET columns = ['name', 'cardnumber', 'dateofbirth', 'category', 'branch', 'address', 'phone'] %] [% PROCESS "patron-search.inc" %] -[% FILTER collapse %] - [% UNLESS ( multi_hold ) %] - [% title_in_title = INCLUDE 'biblio-title-head.inc' %] - [% tx("Place a hold on {title}", { title = title_in_title }) | html %] - › - [% ELSE %] - [% t("Confirm holds") | html %] - › - [% END %] - [% t("Holds") | html %] - › [% t("Circulation") | html %] › [% t("Koha") | html %] - [% END %] +[% FILTER collapse %] + [% UNLESS ( multi_hold ) %] + [% title_in_title = INCLUDE 'biblio-title-head.inc' %] + [% tx("Place a hold on {title}", { title = title_in_title }) | html %] › + [% ELSE %] + [% t("Confirm holds") | html %] › + [% END %] + [% t("Holds") | html %] › + [% t("Circulation") | html %] › + [% t("Koha") | html %] +[% END %] [% INCLUDE 'doc-head-close.inc' %] [% FILTER collapse %] [% END %] @@ -147,36 +144,43 @@ [% END #/ WRAPPER breadcrumbs %] [% END #/ WRAPPER sub-header.inc %] -[%# No sidebar menu when placing multiple holds or biblio not found %] -[% SET aside = (multi_hold || nobiblio) ? '' : 'biblio-view-menu' %] -[% WRAPPER 'main-container.inc' aside=aside wide_centered=1 %] -

Holds

+
+
+ [% IF ( multi_hold || nobiblio ) # No sidebar menu when placing multiple holds or biblio not found %] +
+ [% ELSE %] +
+ [% END %] - [% IF ( nobiblio ) %] -
- [% IF (multi_hold) %] - Cannot place hold: one or more records don't exist. - [% ELSE %] - Cannot place hold: this record doesn't exist. +
+ [% INCLUDE 'messages.inc' %] +

Holds

+ + [% IF ( nobiblio ) %] +
+ [%IF (multi_hold) %] + Cannot place hold: one or more records don't exist. + [% ELSE %] + Cannot place hold: this record doesn't exist. + [% END %] +
[% END %] -
- [% END %] - [% IF ( noitems ) %] -
- [% IF (multi_hold) %] - Cannot place hold: one or more records without items attached. - [% ELSE %] - Cannot place hold: this record has no items attached. + [% IF ( noitems ) %] +
+ [%IF (multi_hold) %] + Cannot place hold: one or more records without items attached. + [% ELSE %] + Cannot place hold: this record has no items attached. + [% END %] +
[% END %] -
- [% END %] - [% IF ( failed_holds ) %] -
- One or more holds were not placed due to following errors: -
    - [% FOREACH fail IN failed_holds %] -
  • + [% IF ( failed_holds ) %] +
    + One or more holds were not placed due to following errors: +
      + [% FOREACH fail IN failed_holds %] +
    • [% SWITCH fail %] [% CASE 'damaged' %] Item is damaged @@ -215,436 +219,372 @@ [% CASE %] Error: [% fail | html %] [% END %] -
    • - [% END %] -
    -
    - [% END %] +
  • + [% END %] +
+
+ [% END %] - [% UNLESS ( multi_hold ) %] -

Place a hold on [% INCLUDE 'biblio-title.inc' link = 1 %] [% IF biblio.author %]by [% biblio.author | html %][% END %]

- [% ELSE %] -

- [% IF ( patron ) %] - Place holds + [% UNLESS ( multi_hold ) %] +

Place a hold on [% INCLUDE 'biblio-title.inc' link = 1 %] [% IF biblio.author %] by [% biblio.author | html %][% END %]

[% ELSE %] - [% IF clubcount %] - Search patrons or clubs - [% ELSE %] - Search patrons - [% END %] +

+ [% IF ( patron ) %] + Place holds + [% ELSE %] + [% IF clubcount %] + Search patrons or clubs + [% ELSE %] + Search patrons + [% END %] + [% END %] +

[% END %] - - [% END %] - [% UNLESS club OR patron OR patron.borrowernumber OR noitems OR nobiblio %] - [% IF ( messageborrower ) %] -
-

Patron not found

-

No patron with this name, please, try another

-
- [% END %] + [% IF canbookbereserved_responses %] + + [% END %] - [% IF ( messageclub ) %] -
-

Club not found

-

No club with this name, please, try another

-
- [% END %] -
- [% UNLESS multi_hold %] - [% IF clubcount %] -

Search patrons or clubs

- [% ELSE %] -

Search patrons

+ [% UNLESS club OR patron OR patron.borrowernumber OR noitems OR nobiblio %] + [% IF ( messageborrower ) %] +
+

Patron not found

+

No patron with this name, please, try another

+
[% END %] - [% END %] - [% WRAPPER tabs id= "circ_holds_select" %] - [% WRAPPER tabs_nav %] - [% WRAPPER tab_item tabname= "holds_patronsearch_pane" bt_active= 1 %]Patrons[% END %] - [% IF clubcount %] - [% WRAPPER tab_item tabname= "holds_clubsearch_pane" %]Clubs[% END %] - [% END %] - [% END # /WRAPPER tabs_nav %] - - [% WRAPPER tab_panels %] - [% WRAPPER tab_panel tabname="holds_patronsearch_pane" bt_active= 1 %] - [% PROCESS patron_search_filters_simple %] - - [% PROCESS patron_search_table table_id => 'table_borrowers', open_on_row_click => 1 %] - [% END # /tab_panel# %] - [% IF clubcount %] - [% WRAPPER tab_panel tabname="holds_clubsearch_pane" %] -
-
Enter club ID or partial name:
- - - - [% FOREACH biblionumber IN biblionumbers %] - - [% END %] -
- - [% IF clubs %] - [% INCLUDE 'clubs-table.inc' destination = "holds" %] - [% END %] - [% END # /tab_panel# %] - [% END # /IF clubcount %] - [% END # /WRAPPER tab_panels %] - [% END # /WRAPPER tabs %] -
- [% ELSIF club %] -
-
- Hold details -
- [% INCLUDE 'csrf-token.inc' %] - - - [% IF ( multi_hold ) %] - - [% FOREACH biblioloo IN biblioloop %] - [% UNLESS biblioloo.none_avail %] - - - + [% IF ( messageclub ) %] +
+

Club not found

+

No club with this name, please, try another

+
+ [% END %] +
+ [% UNLESS multi_hold %] + [% IF clubcount %] +

Search patrons or clubs

+ [% ELSE %] +

Search patrons

[% END %] [% END %] - [% ELSE %] - - - - [% END # /IF multi_hold %] -
    -
  1. Club: [% club.name | html %]
  2. -
  3. Description: [% club.description | html %]
  4. -
  5. - - -
  6. -
  7. - - -
  8. -
-

Members

-
    - [% FOREACH member IN members %] - [% SET patron = member.patron %] -
  1. -
    [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %]
    - [% IF member.exceeded_maxreserves %] -
    - - Too many holds: Patron can only place a maximum of [% maxreserves | html %] total holds. -
    - [% END %] - [% IF ( patron.is_expired ) %] -
    - - Account has expired -
    - [% END %] - [% IF patron.is_debarred %] -
    - - Patron has restrictions -
    - [% END %] - [% IF member.amount_outstanding && Koha.Preference('maxoutstanding') && member.amount_outstanding > Koha.Preference('maxoutstanding') %] -
    - - Patron has outstanding fines: [% member.amount_outstanding | $Price %] -
    - [% END %] - [% IF ( member.diffbranch ) %] -
    - - Pickup library is different. Patron's home library: ([% Branches.GetName(patron.branchcode) | html %] / [% patron.branchcode | html %] ) -
    + [% WRAPPER tabs id= "circ_holds_select" %] + [% WRAPPER tabs_nav %] + [% WRAPPER tab_item tabname= "holds_patronsearch_pane" bt_active= 1 %] Patrons [% END %] + [% IF clubcount %] + [% WRAPPER tab_item tabname= "holds_clubsearch_pane" %] Clubs [% END %] [% END %] -
  2. - [% END %] -
- [% UNLESS ( multi_hold ) %] -
- -
- [% ELSE %] - - - - - [% UNLESS Koha.Preference('item-level_itypes') %] - - [% END %] - - - - [% FOREACH biblioloo IN biblioloop %] - - - - [% UNLESS Koha.Preference('item-level_itypes') %] - + [% END # /tab_panel# %] + [% END # /IF clubcount %] + [% END # /WRAPPER tab_panels %] + [% END # /WRAPPER tabs %] + + [% ELSIF club %] +
+
+
+ Hold details +
+ [% INCLUDE 'csrf-token.inc' %] + + + [% IF ( multi_hold ) %] + + [% FOREACH biblioloo IN biblioloop %] + [% UNLESS biblioloo.none_avail %] + + + [% END %] -
- - - [% END # /FOREACH biblioloo %] -
 TitleItem typePriorityInformation
- [% UNLESS ( biblioloo.warn ) %] - - [% END %] - -
    -
  • - [% biblioloo.title | html %] - [% IF biblioloo.author %]by [% biblioloo.author | html %][% END %] -
  • - [% IF ( biblioloo.publicationyear ) %] -
  • Publication year: [% biblioloo.publicationyear | html %]
  • + [% END # /WRAPPER tabs_nav %] + + [% WRAPPER tab_panels %] + [% WRAPPER tab_panel tabname="holds_patronsearch_pane" bt_active= 1 %] + [% PROCESS patron_search_filters_simple %] + + [% PROCESS patron_search_table table_id => 'table_borrowers', open_on_row_click => 1 %] + [% END # /tab_panel# %] + [% IF clubcount %] + [% WRAPPER tab_panel tabname="holds_clubsearch_pane" %] + +
    Enter club ID or partial name:
    + + + [% FOREACH biblionumber IN biblionumbers %] + [% END %] -
- [% IF ( biblioloo.warn ) %] - + + + [% IF clubs %] + [% INCLUDE 'clubs-table.inc' destination = "holds" %] [% END %] -
- [% biblioloo.itemtype.translated_description | html %] - [% biblioloo.rank | html %] - [% IF ( biblioloo.checked_previously ) %] - Patron has previously checked out this title
+ [% END %] + [% ELSE %] + + + + [% END # /IF multi_hold %] +
    +
  1. + Club: [% club.name | html %] +
  2. +
  3. + Description: [% club.description | html %] +
  4. +
  5. + + +
  6. +
  7. + + +
  8. +
+

Members

+
    + [% FOREACH member IN members %] + [% SET patron = member.patron %] +
  1. +
    [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %]
    + [% IF member.exceeded_maxreserves %] +
    + + Too many holds: Patron can only place a maximum of [% maxreserves | html %] total holds. +
    [% END %] - [% IF ( biblioloo.alreadyres ) %] -
      -
    • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] already has a hold on this item
    • -
    + [% IF ( patron.is_expired ) %] +
    + + Account has expired +
    [% END %] - [% IF ( biblioloo.none_avail || biblioloo.noitems ) %] -
    • No items are available to be placed on hold
    + [% IF patron.is_debarred %] +
    + + Patron has restrictions +
    + [% END %] + [% IF member.amount_outstanding && Koha.Preference('maxoutstanding') && member.amount_outstanding > Koha.Preference('maxoutstanding') %] +
    + + Patron has outstanding fines: [% member.amount_outstanding | $Price %] +
    [% END %] -
- - [% END %] - -
- [% ELSIF NOT ( noitems || nobiblio ) # /UNLESS patron %] - - [% IF ( checked_previously && !multi_hold ) %] -
-
    -
  • Patron has previously checked out this title
  • -
-
- [% END %] - [% IF ( no_reserves_allowed || exceeded_maxreserves || exceeded_holds_per_record || alreadyreserved || none_available || alreadypossession || ageRestricted || recall ) %] -
- [% UNLESS ( multi_hold ) %] -

Cannot place hold

-
    - [% IF ( no_reserves_allowed ) %] -
  • No holds allowed: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] cannot place a hold on any of these items.
  • - [% ELSIF ( exceeded_maxreserves ) %] -
  • Too many holds: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] can only place a maximum of [% maxreserves | html %] total - holds.
  • - [% ELSIF ( exceeded_holds_per_record ) %] -
  • Too many holds for this record: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] can only place a maximum of - [% max_holds_for_record | html %] hold(s) on this record.
  • - [% ELSIF ( alreadypossession ) %] -
  • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 hide_patron_infos_if_needed => 1 %] is already in possession of one item.
  • - [% ELSIF ( alreadyreserved ) %] -
  • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] already has a hold on this item.
  • - [% ELSIF ( ageRestricted ) %] -
  • Age restricted
  • - [% ELSIF ( none_available ) %] -
  • No items are available to be placed on hold.
  • - [% ELSIF ( maxreserves ) %] -
  • Too many holds: - [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] has too many holds.
  • - [% ELSIF ( recall ) %] -
  • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] has already placed a recall on this item.
  • - [% END # /IF exceeded_maxreserves %] -
- [% ELSE # UNLESS multi_hold %] -

Cannot place hold on some items

- [% IF (no_reserves_allowed ) %] -
  • No holds allowed: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] cannot place holds on some of these title's items.
  • - [% ELSIF ( exceeded_maxreserves ) %] -
  • Too many holds: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] can place [% new_reserves_allowed | html %] of the requested - [% new_reserves_count | html %] holds for a maximum of [% maxreserves | html %] total holds.
  • - [% ELSIF ( exceeded_holds_per_record ) %] - [% FOREACH biblioloo IN biblioloop %] - [% IF (biblioloo.tooManyHoldsForThisRecord) %] -
  • Too many holds for [% biblioloo.title | html %]: - [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] can only place a maximum of [% max_holds_for_record | html %] hold(s) on this - record.
  • + [% IF ( member.diffbranch ) %] +
    + + Pickup library is different. Patron's home library: ([% Branches.GetName(patron.branchcode) | html %] / [% patron.branchcode | html %] ) +
    + [% END %] + [% END %] - [% END %] - [% ELSIF ( none_available ) %] -
  • No items available: One or more records have no items that can be held
  • - [% END # /IF exceeded_maxreserves %] - [% END # /UNLESS multi_hold %] -
    - [% END # /IF ( exceeded_maxreserves || ... %] + [% UNLESS ( multi_hold ) %] +
    + +
    + [% ELSE %] + + + + + [% UNLESS Koha.Preference('item-level_itypes') %] + + [% END %] + + + + [% FOREACH biblioloo IN biblioloop %] + [% IF ( biblioloo.warn ) %] + + [% ELSE %] + + [% END %] + + [% END %] + + [% UNLESS Koha.Preference('item-level_itypes') %] + + [% END %] + + + + [% END # /FOREACH biblioloo %] +
     TitleItem typePriorityInformation
    + [% UNLESS ( biblioloo.warn ) %] + + +
      +
    • + [% biblioloo.title | html %] + [% IF biblioloo.author %] by [% biblioloo.author | html %][% END %] +
    • + [% IF ( biblioloo.publicationyear ) %] +
    • + Publication year: [% biblioloo.publicationyear | html %] +
    • + [% END %] +
    + [% IF ( biblioloo.warn ) %] + + [% END %] +
    + [% biblioloo.itemtype.translated_description | html %] + [% biblioloo.rank | html %] + [% IF ( biblioloo.checked_previously ) %] + Patron has previously checked out this title
    + [% END %] + [% IF ( biblioloo.alreadyres ) %] +
      + [% ELSE %] + [% IF ( biblioloo.none_avail || biblioloo.noitems ) %] +
        + [% END %] + [% END %] - [% IF ( patron.is_expired || diffbranch || patron.is_debarred || ( amount_outstanding && Koha.Preference('maxoutstanding') && amount_outstanding > Koha.Preference('maxoutstanding') ) ) %] -
        -
          - [% IF ( patron.is_expired ) %] -
        • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %]: Account has expired
        • - [% END %] + [% IF ( biblioloo.alreadyres ) %] +
        • + [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] already has a hold on this item +
        • + [% END %] + [% IF ( biblioloo.none_avail || biblioloo.noitems ) %] +
        • No items are available to be placed on hold
        • + [% END %] - [% IF patron.is_debarred %] -
        • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %]: Patron has restrictions
        • - [% END %] + [% IF ( biblioloo.alreadyres ) %] +
        + [% ELSE %] + [% IF ( biblioloo.none_avail || biblioloo.noitems ) %] +
      + [% END %] + [% END %] +
    + [% END %] + +
    + [% ELSIF NOT ( noitems || nobiblio ) # /UNLESS patron %] - [% IF amount_outstanding && Koha.Preference('maxoutstanding') && amount_outstanding > Koha.Preference('maxoutstanding') %] -
  • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 link_to => 'members_pay' %]: - Patron has outstanding fines: [% amount_outstanding | $Price %]
  • - [% END %] + [% IF ( checked_previously && !multi_hold ) %] +
    +
      +
    • Patron has previously checked out this title
    • +
    +
    + [% END %] - [% IF ( diffbranch ) %] -
  • Pickup library is different. Patron: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] Patron's home library: - ([% Branches.GetName(patron.branchcode) | html %] / [% patron.branchcode | html %] )
  • - [% END %] - - -
    - [% END # /IF patron.is_expired || diffbranch ... %] + [% IF ( no_reserves_allowed || exceeded_maxreserves || exceeded_holds_per_record || alreadyreserved || none_available || alreadypossession || ageRestricted || recall ) %] +
    + + [% UNLESS ( multi_hold ) %] +

    Cannot place hold

    +
      + [% IF ( no_reserves_allowed ) %] +
    • No holds allowed: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] cannot place a hold on any of these items.
    • + [% ELSIF ( exceeded_maxreserves ) %] +
    • Too many holds: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] can only place a maximum of [% maxreserves | html %] total holds.
    • + [% ELSIF ( exceeded_holds_per_record ) %] +
    • Too many holds for this record: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] can only place a maximum of [% max_holds_for_record | html %] hold(s) on this record.
    • + [% ELSIF ( alreadypossession ) %] +
    • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 hide_patron_infos_if_needed => 1 %] is already in possession of one item.
    • + [% ELSIF ( alreadyreserved ) %] +
    • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] already has a hold on this item.
    • + [% ELSIF ( ageRestricted ) %] +
    • Age restricted
    • + [% ELSIF ( none_available ) %] +
    • No items are available to be placed on hold.
    • + [% ELSIF ( maxreserves ) %] +
    • Too many holds: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] has too many holds.
    • + [% ELSIF ( recall ) %] +
    • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] has already placed a recall on this item.
    • + [% END # /IF exceeded_maxreserves %] +
    + [% ELSE # UNLESS multi_hold %] +

    Cannot place hold on some items

    + [% IF (no_reserves_allowed ) %] +
  • No holds allowed: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] cannot place holds on some of these title's items.
  • + [% ELSIF ( exceeded_maxreserves ) %] +
  • Too many holds: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] can place [% new_reserves_allowed | html %] of the requested [% new_reserves_count | html %] holds for a maximum of [% maxreserves | html %] total holds.
  • + [% ELSIF ( exceeded_holds_per_record ) %] + [% FOREACH biblioloo IN biblioloop %] + [% IF (biblioloo.tooManyHoldsForThisRecord) %] +
  • Too many holds for [% biblioloo.title | html %]: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] can only place a maximum of [% max_holds_for_record | html %] hold(s) on this record.
  • + [% END %] + [% END %] + [% ELSIF ( none_available ) %] +
  • No items available: One or more records have no items that can be held
  • + [% END # /IF exceeded_maxreserves %] + [% END # /UNLESS multi_hold %] - [% IF ( messageborrower ) %] -
    -

    Patron not found:

    -

    Name or barcode not found. Please try an other

    -
    - [% END %] +
    + [% END # /IF ( exceeded_maxreserves || ... %] -
    + [% IF ( patron.is_expired || diffbranch || patron.is_debarred || ( amount_outstanding && Koha.Preference('maxoutstanding') && amount_outstanding > Koha.Preference('maxoutstanding') ) ) %] +
    +
      + [% IF ( patron.is_expired ) %] +
    • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %]: Account has expired
    • + [% END %] - [% UNLESS ( multi_hold ) %] -
      - [% INCLUDE 'csrf-token.inc' %] - -
      - Hold details + [% IF patron.is_debarred %] +
    • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %]: Patron has restrictions
    • + [% END %] - - [% FOREACH biblionumber IN biblionumbers %] - - [% END %] - - - + [% IF amount_outstanding && Koha.Preference('maxoutstanding') && amount_outstanding > Koha.Preference('maxoutstanding') %] +
    • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 link_to => 'members_pay' %]: Patron has outstanding fines: [% amount_outstanding | $Price %]
    • + [% END %] -
        -
      1. - Patron: - [% IF ( patron.borrowernumber ) %] - [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 hide_patron_infos_if_needed => 1 %] - [% ELSE %] - Not defined yet + [% IF ( diffbranch ) %] +
      2. Pickup library is different. Patron: [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] Patron's home library: ([% Branches.GetName(patron.branchcode) | html %] / [% patron.branchcode | html %] )
      3. [% END %] - +
    +
    + [% END # /IF patron.is_expired || diffbranch ... %] -
  • - Estimated priority: - [% fixedRank | html %] -
  • + [% IF ( messageborrower ) %] +
    +

    Patron not found:

    +

    Name or barcode not found. Please try an other

    +
    + [% END %] -
  • - - -
  • +
    +
    - [% IF Koha.Preference('AllowHoldItemTypeSelection') %] -
  • - - -
  • - [% END %] + [% UNLESS ( multi_hold ) %] + + [% INCLUDE 'csrf-token.inc' %] + +
    + Hold details + + + [% FOREACH biblionumber IN biblionumbers %] + + [% END %] + + + - [% IF ( Koha.Preference('AllowHoldDateInFuture') ) %] -
  • - - -
  • - [% END %] +
      +
    1. + Patron: + [% IF ( patron.borrowernumber ) %] + [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 hide_patron_infos_if_needed => 1 %] + [% ELSE %] + Not defined yet + [% END %] +
    2. -
    3. - - -
    4. +
    5. + Estimated priority: + [% fixedRank | html %] +
    6. -
    7. - - - A non priority hold doesn't prevent a current checkout from renewing -
    8. -
    -
    -
    - - [% IF force_hold_level == 'item' || force_hold_level == 'item_group' %] - - [% ELSIF force_hold_level == 'record' %] - - - (Required) - [% ELSE %] - - [% END %] - - - -
    - [% IF force_hold_level == 'item' # Patron has placed a item level hold previously for this record %] - - - Hold must be item level - - [% ELSIF force_hold_level == 'item_group' # Patron has placed an item group level hold previously for this record %] - - - Hold must be item group level - - [% ELSE %] -
    1. - - + +
    2. [% IF Koha.Preference('AllowHoldItemTypeSelection') %] @@ -658,50 +598,124 @@ [% END %] - [% UNLESS remaining_holds_for_record == 1 %] -
    3. - - -
    4. - [% ELSE %] - - [% END %] -
    - [% END %] -
    - [% IF ( patron.borrowernumber ) %] - [% IF ( override_required ) %] - - [% ELSIF ( none_available ) %] - + [% IF ( Koha.Preference('AllowHoldDateInFuture') ) %] +
  • + + +
  • + [% END %] + +
  • + + +
  • + +
  • + + + A non priority hold doesn't prevent a current checkout from renewing +
  • + +
    +
    + + [% IF force_hold_level == 'item' || force_hold_level == 'item_group' %] + + [% ELSIF force_hold_level == 'record' %] + + + (Required) + [% ELSE %] + + [% END %] + + + +
    + [% IF force_hold_level == 'item' # Patron has placed a item level hold previously for this record %] + + + Hold must be item level + + [% ELSIF force_hold_level == 'item_group' # Patron has placed an item group level hold previously for this record %] + + + Hold must be item group level + [% ELSE %] - +
      + +
    1. + + +
    2. + + [% IF Koha.Preference('AllowHoldItemTypeSelection') %] +
    3. + + +
    4. + [% END %] + [% UNLESS remaining_holds_for_record == 1 %] +
    5. + + +
    6. + [% ELSE %] + + [% END %] +
    [% END %] - [% END %] + + +
    + [% IF ( patron.borrowernumber ) %] + [% IF ( override_required ) %] + + [% ELSIF ( none_available ) %] + + [% ELSE %] + + [% END %] + [% END %] +
    +
    -
    -
    -
    - - [% biblio_info = biblioloop.0 %] - - [% IF Koha.Preference('EnableItemGroupHolds') && biblio_info.object.item_groups.count %] -
    - - [% IF force_hold_level == 'item_group' %] - - (Required) - [% ELSIF force_hold_level == 'item' || force_hold_level == 'record' %] - - [% ELSE %] - - [% END %] - - +
    + + [% biblio_info = biblioloop.0 %] + + [% IF Koha.Preference('EnableItemGroupHolds') && biblio_info.object.item_groups.count %] +
    + + [% IF force_hold_level == 'item_group' %] + + (Required) + [% ELSIF force_hold_level == 'item' || force_hold_level == 'record' %] + + [% ELSE %] + + [% END %] + + -
    +
    [% IF force_hold_level == 'record' # Patron has placed a record level hold previously for this record %] @@ -716,7 +730,10 @@
    • - [% PROCESS options_for_libraries libraries => Branches.pickup_locations({ search_params => { biblio => biblionumber, patron => patron }, selected => pickup }) %]
    • @@ -764,673 +781,720 @@
    [% END %] -
    - [% IF ( patron.borrowernumber ) %] - [% IF ( override_required ) %] - - [% ELSIF ( none_available ) %] - - [% ELSE %] - +
    + [% IF ( patron.borrowernumber ) %] + [% IF ( override_required ) %] + + [% ELSIF ( none_available ) %] + + [% ELSE %] + + [% END %] [% END %] - [% END %] +
    -
    - [% END %] - - -
    - - [% IF force_hold_level == 'item' %] - - (Required) - [% ELSIF force_hold_level == 'record' || force_hold_level == 'item_group' %] - - [% ELSE %] - [% END %] - - + + +
    + + [% IF force_hold_level == 'item' %] + + (Required) + [% ELSIF force_hold_level == 'record' || force_hold_level == 'item_group' %] + + [% ELSE %] + + [% END %] + + + +
    +
      + [% UNLESS Koha.Preference('item-level_itypes') %] +
    1. + Item type: + [% biblio_info.itemtype.translated_description | html %] +
    2. + [% END %] -
      -
        - [% UNLESS Koha.Preference('item-level_itypes') %] -
      1. - Item type: - [% biblio_info.itemtype.translated_description | html %] -
      2. - [% END %] + [% IF ( biblio_info.biblioitem.publicationyear ) %] +
      3. + Publication year: + [% biblio_info.biblioitem.publicationyear | html %] +
      4. + [% END %] +
      - [% IF ( biblio_info.biblioitem.publicationyear ) %] -
    3. - Publication year: - [% biblio_info.biblioitem.publicationyear | html %] -
    4. - [% END %] -
    + + + + + + [% IF Koha.Preference('item-level_itypes') %] + + [% END %] + + [% IF Koha.Preference('EnableItemGroupHolds') && biblio_info.object.item_groups.count %] + + [% END %] + + + [% IF itemdata_ccode %] + + [% END %] + + + [% IF itemdata_enumchron %] + + [% END %] + + + + + [% SET selected = 0 %] + [% FOREACH itemloo IN biblio_info.itemloop %] + [% UNLESS ( itemloo.hide ) %] + + + + [% IF Koha.Preference('item-level_itypes') %] + + [% END %] + + [% IF Koha.Preference('EnableItemGroupHolds') && biblio_info.object.item_groups.count %] + + [% END %] + + + [% IF itemdata_ccode %] + + [% END %] + + + [% IF itemdata_enumchron %] + + [% END %] + [% IF ( itemloo.onloan ) %] + - [% IF Koha.Preference('item-level_itypes') %] - [% END %] - - [% IF Koha.Preference('EnableItemGroupHolds') && biblio_info.object.item_groups.count %] - + +
      + +
    1. + Patron: + [% IF ( patron.borrowernumber ) %] + [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 hide_patron_infos_if_needed => 1 %] + [% ELSE %] + Not defined yet + [% END %] +
    2. + + +
    3. + + +
    4. +
    5. + + +
    6. + [% IF ( Koha.Preference('AllowHoldDateInFuture') ) %] +
    7. + + +
    8. [% END %] -
    - - [% IF itemdata_ccode %] - + +
  • + + +
  • + + +
    HoldAllowed pickup locationsItem typeBarcodeItem groupHome libraryLast locationCollectionCall numberCopy numberVol no.Information
    + [% IF force_hold_level == 'record' # Patron has placed a record level hold previously for this record %] + + + Hold must be record level + + [% ELSIF force_hold_level == 'item_group' %] + + + Hold must be item group level + + [% ELSIF ( itemloo.available ) %] + + [% ELSIF ( itemloo.override ) %] + + + [% ELSE %] + + + [% IF itemloo.not_holdable %] + [% IF itemloo.not_holdable == 'damaged' %] + Item damaged + [% ELSIF itemloo.not_holdable == 'ageRestricted' %] + Age restricted + [% ELSIF itemloo.not_holdable == 'tooManyHoldsForThisRecord' %] + Exceeded max holds per record + [% ELSIF itemloo.not_holdable == 'tooManyReservesToday' %] + Daily hold limit reached for patron + [% ELSIF itemloo.not_holdable == 'tooManyReserves' %] + Too many holds + [% ELSIF itemloo.not_holdable == 'notReservable' %] + Not holdable + [% ELSIF itemloo.not_holdable == 'cannotReserveFromOtherBranches' %] + Patron is from different library + [% ELSIF itemloo.not_holdable == 'branchNotInHoldGroup' %] + Cannot place hold from patron's library + [% ELSIF itemloo.not_holdable == 'itemAlreadyOnHold' %] + Patron already has hold for this item + [% ELSIF itemloo.not_holdable == 'cannotBeTransferred' %] + Cannot be transferred to pickup library + [% ELSIF itemloo.not_holdable == 'pickupNotInHoldGroup' %] + Only pickup locations within the same hold group are allowed + [% ELSIF itemloo.not_holdable == 'noReservesAllowed' %] + No holds are allowed on this item + [% ELSIF itemloo.not_holdable == 'libraryNotPickupLocation' %] + Library is not a pickup location + [% ELSIF itemloo.not_holdable == 'no_valid_pickup_location' %] + No valid pickup location + [% ELSIF itemloo.not_holdable == 'notforloan' %] + Not for loan + [% ELSE %] + [% itemloo.not_holdable | html %] + [% END %] + [% END %] + + [% END # /IF force_hold_level %] + + [% IF (itemloo.pickup_locations_count > 0) || Koha.Preference('AllowHoldPolicyOverride') %] + + [% END %] + + [% UNLESS ( noItemTypeImages ) %] + [% IF ( itemloo.itemtype.image_location) %]
    [% END %] + [% END %] + [% itemloo.itemtype.translated_description | html %] +
    + [% itemloo.barcode | html %] + + [% itemloo.object.item_group.description | html %] + + [% Branches.GetName( itemloo.homebranch ) | html %] + + [% Branches.GetName( itemloo.holdingbranch ) | html %] + + [% IF ( itemloo.ccode ) %][% AuthorisedValues.GetDescriptionByKohaField( kohafield => 'items.ccode', authorised_value => itemloo.ccode ) | html %][% END %] + + [% itemloo.itemcallnumber | html %] + + [% IF ( itemloo.copynumber ) %][% itemloo.copynumber | html %][% ELSE %] [% END %] + + [% itemloo.enumchron | html %] + + Due [% itemloo.date_due | $KohaDates as_due_date => 1 %] + [% ELSE %] + + [% IF ( itemloo.transfertwhen ) %] + In transit from [% Branches.GetName( itemloo.transfertfrom ) | html %], + to [% Branches.GetName( itemloo.transfertto ) | html %], since [% itemloo.transfertwhen | $KohaDates %] + [% END %] + [% END %] - - - - - - [% IF Koha.Preference('item-level_itypes') %] - - [% END %] - - [% IF Koha.Preference('EnableItemGroupHolds') && biblio_info.object.item_groups.count %] - - [% END %] - - - [% IF itemdata_ccode %] - - [% END %] - - - [% IF itemdata_enumchron %] - - [% END %] - - - - - [% SET selected = 0 %] - [% FOREACH itemloo IN biblio_info.itemloop %] - [% UNLESS ( itemloo.hide ) %] - - - + + [% END # / UNLESS itemloo.hide %] + [% END # /FOREACH itemloo %] + +
    HoldAllowed pickup locationsItem typeBarcodeItem groupHome libraryLast locationCollectionCall numberCopy numberVol no.Information
    - [% IF force_hold_level == 'record' # Patron has placed a record level hold previously for this record %] - - - Hold must be record level - - [% ELSIF force_hold_level == 'item_group' %] - - - Hold must be item group level - - [% ELSIF ( itemloo.available ) %] - - [% ELSIF ( itemloo.override ) %] - - - [% ELSE %] - - - [% IF itemloo.not_holdable %] - [% IF itemloo.not_holdable == 'damaged' %] - Item damaged - [% ELSIF itemloo.not_holdable == 'ageRestricted' %] - Age restricted - [% ELSIF itemloo.not_holdable == 'tooManyHoldsForThisRecord' %] - Exceeded max holds per record - [% ELSIF itemloo.not_holdable == 'tooManyReservesToday' %] - Daily hold limit reached for patron - [% ELSIF itemloo.not_holdable == 'tooManyReserves' %] - Too many holds - [% ELSIF itemloo.not_holdable == 'notReservable' %] - Not holdable - [% ELSIF itemloo.not_holdable == 'cannotReserveFromOtherBranches' %] - Patron is from different library - [% ELSIF itemloo.not_holdable == 'branchNotInHoldGroup' %] - Cannot place hold from patron's library - [% ELSIF itemloo.not_holdable == 'itemAlreadyOnHold' %] - Patron already has hold for this item - [% ELSIF itemloo.not_holdable == 'cannotBeTransferred' %] - Cannot be transferred to pickup library - [% ELSIF itemloo.not_holdable == 'pickupNotInHoldGroup' %] - Only pickup locations within the same hold group are allowed - [% ELSIF itemloo.not_holdable == 'noReservesAllowed' %] - No holds are allowed on this item - [% ELSIF itemloo.not_holdable == 'libraryNotPickupLocation' %] - Library is not a pickup location - [% ELSIF itemloo.not_holdable == 'no_valid_pickup_location' %] - No valid pickup location - [% ELSIF itemloo.not_holdable == 'notforloan' %] - Not for loan + [% IF ( itemloo.reservedate ) %] + [% IF ( itemloo.nocancel ) %] + Can't be cancelled when item is in transit [% ELSE %] - [% itemloo.not_holdable | html %] - [% END %] + [% IF ( itemloo.waitingdate ) %] + [% IF ( itemloo.canreservefromotherbranches ) %] + Waiting for [% INCLUDE 'patron-title.inc' patron=itemloo.ReservedFor %] at [% Branches.GetName( itemloo.ExpectedAtLibrary ) | html %] since [% itemloo.waitingdate | $KohaDates %] + [% ELSE %] + Waiting at [% Branches.GetName( itemloo.ExpectedAtLibrary ) | html %] since [% itemloo.waitingdate | $KohaDates %] + [% END %] + [% ELSE %] + [% IF ( itemloo.canreservefromotherbranches ) %] + [% IF itemloo.reservedate %] + On hold for [% INCLUDE 'patron-title.inc' patron=itemloo.ReservedFor %] expected at [% Branches.GetName( itemloo.ExpectedAtLibrary ) | html %] since + [% ELSE %] + On hold expected at [% Branches.GetName( itemloo.ExpectedAtLibrary ) | html %] + [% END %] + [% ELSIF itemloo.reservedate %] + On hold expected at [% Branches.GetName( itemloo.ExpectedAtLibrary ) | html %] since [% itemloo.reservedate | $KohaDates %] + [% ELSE %] + On hold expected at [% Branches.GetName( itemloo.ExpectedAtLibrary ) | html %] + [% END %] + [% END %] + [% END # /IF itemloo.nocancel %] + [% ELSE %] + Not on hold + [% END # /IF itemloo.reservedate %] + + [% IF itemloo.item_level_holds == "N" %] +
    Item level hold not allowed from OPAC + [% ELSIF itemloo.item_level_holds == "F" %] +
    Item level hold forced from OPAC [% END %] -
    - [% END # /IF force_hold_level %] -
    - [% IF (itemloo.pickup_locations_count > 0) || Koha.Preference('AllowHoldPolicyOverride') %] - + + [% IF ( itemloo.withdrawn ) %] + [% AuthorisedValues.GetDescriptionByKohaField( kohafield => 'items.withdrawn', authorised_value => itemloo.withdrawn ) | html %] + [% END %] + + [% IF ( itemloo.notforloan ) %] + Not for loan ([% AuthorisedValues.GetDescriptionByKohaField( kohafield => 'items.notforloan', authorised_value => itemloo.notforloan ) | html %]) + [% ELSIF ( itemloo.notforloanitype ) %] + Not for loan (Itemtype not for loan) + [% END %] +
    + + [% IF hiddencount %] +

    + Show all items ([% hiddencount | html %] hidden) +

    + [% END # /IF hiddencount %] +
    + [% IF ( patron.borrowernumber ) %] + [% IF ( override_required ) %] + + [% ELSIF ( none_available ) %] + + [% ELSE %] + + [% END %] + [% END %] +
    + + [% ELSE # /UNLESS multi_hold %] +
    + Hold details + + [% INCLUDE 'csrf-token.inc' %] + + + + + [% FOREACH biblioloo IN biblioloop %] + + [% UNLESS biblioloo.none_avail %] + + [% END %] -
    - [% UNLESS ( noItemTypeImages ) %] - [% IF ( itemloo.itemtype.image_location) %]
    [% END %] - [% END %] - [% itemloo.itemtype.translated_description | html %] -
    [% itemloo.barcode | html %] [% itemloo.object.item_group.description | html %] [% Branches.GetName( itemloo.homebranch ) | html %] [% Branches.GetName( itemloo.holdingbranch ) | html %] [% IF ( itemloo.ccode ) %][% AuthorisedValues.GetDescriptionByKohaField( kohafield => 'items.ccode', authorised_value => itemloo.ccode ) | html %][% END %]
    + + + + + [% UNLESS Koha.Preference('item-level_itypes') %] + [% END %] - - - [% IF itemdata_enumchron %] - + + + + [% FOREACH biblioloo IN biblioloop %] + [% IF ( biblioloo.warn ) %] + + [% ELSE %] + [% END %] - + + + [% UNLESS Koha.Preference('item-level_itypes') %] + [% END %] - - [% IF ( itemloo.reservedate ) %] - [% IF ( itemloo.nocancel ) %] - Can't be cancelled when item is in transit + + + + [% END # /FOREACH biblioloo %] +
     Pickup locationTitleItem type [% itemloo.itemcallnumber | html %] [% IF ( itemloo.copynumber ) %][% itemloo.copynumber | html %][% ELSE %] [% END %] [% itemloo.enumchron | html %] PriorityInformation
    - [% IF ( itemloo.onloan ) %] - Due [% itemloo.date_due | $KohaDates as_due_date => 1 %] - [% ELSE %] - [% IF ( itemloo.transfertwhen ) %] - In transit from [% Branches.GetName( itemloo.transfertfrom ) | html %], to [% Branches.GetName( itemloo.transfertto ) | html %], since - [% itemloo.transfertwhen | $KohaDates %] + + [% UNLESS ( biblioloo.warn ) %] + + [% END %] + + [% UNLESS ( biblioloo.none_avail || biblioloo.noitems ) %] + + [% END %] + +
      +
    • + [% biblioloo.title | html %] + [% IF biblioloo.author %] by [% biblioloo.author | html %][% END %] +
    • + [% IF ( biblioloo.publicationyear ) %] +
    • + Publication year: [% biblioloo.publicationyear | html %] +
    • + [% END %] +
    + [% IF ( biblioloo.warn ) %] + [% END %] +
    + [% biblioloo.itemtype.translated_description | html %] + [% biblioloo.rank | html %] + [% IF ( biblioloo.checked_previously ) %] + Patron has previously checked out this title
    + [% END %] + [% IF ( biblioloo.alreadyres ) %] +
      [% ELSE %] - [% IF ( itemloo.waitingdate ) %] - [% IF ( itemloo.canreservefromotherbranches ) %] - Waiting for [% INCLUDE 'patron-title.inc' patron=itemloo.ReservedFor %] at [% Branches.GetName( itemloo.ExpectedAtLibrary ) | html %] since - [% itemloo.waitingdate | $KohaDates %] - [% ELSE %] - Waiting at [% Branches.GetName( itemloo.ExpectedAtLibrary ) | html %] since [% itemloo.waitingdate | $KohaDates %] - [% END %] - [% ELSE %] - [% IF ( itemloo.canreservefromotherbranches ) %] - [% IF itemloo.reservedate %] - On hold for [% INCLUDE 'patron-title.inc' patron=itemloo.ReservedFor %] expected at [% Branches.GetName( itemloo.ExpectedAtLibrary ) | html %] since - [% ELSE %] - On hold expected at [% Branches.GetName( itemloo.ExpectedAtLibrary ) | html %] - [% END %] - [% ELSIF itemloo.reservedate %] - On hold expected at [% Branches.GetName( itemloo.ExpectedAtLibrary ) | html %] since [% itemloo.reservedate | $KohaDates %] - [% ELSE %] - On hold expected at [% Branches.GetName( itemloo.ExpectedAtLibrary ) | html %] - [% END %] + [% IF ( biblioloo.none_avail || biblioloo.noitems ) %] +
        [% END %] - [% END # /IF itemloo.nocancel %] - [% ELSE %] - Not on hold - [% END # /IF itemloo.reservedate %] + [% END %] - [% IF itemloo.item_level_holds == "N" %] -
        Item level hold not allowed from OPAC - [% ELSIF itemloo.item_level_holds == "F" %] -
        Item level hold forced from OPAC - [% END %] + [% IF ( biblioloo.alreadyres ) %] +
      • + [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] already has a hold on this item +
      • + [% END %] + [% IF ( biblioloo.none_avail || biblioloo.noitems ) %] +
      • No items are available to be placed on hold
      • + [% END %] - [% IF ( itemloo.itemlost ) %] - [% AuthorisedValues.GetDescriptionByKohaField( kohafield => 'items.itemlost', authorised_value => itemloo.itemlost ) | html %] - [% END %] + [% IF ( biblioloo.alreadyres ) %] +
      + [% ELSE %] + [% IF ( biblioloo.none_avail || biblioloo.noitems ) %] +
    + [% END %] + [% END %] +
    +
    +
    + [% IF ( patron AND patron.borrowernumber ) %] + [% IF ( override_required ) %] + + [% ELSIF ( no_bibs_available ) %] + + [% ELSIF ( none_available ) %] + + [% ELSE %] + + [% END %] + [% END # /IF patron %] +
    - [% IF ( itemloo.damaged ) %] - [% AuthorisedValues.GetDescriptionByKohaField( kohafield => 'items.damaged', authorised_value => itemloo.damaged ) | html %] - [% END %] + [% END # /UNLESS multi_hold %] - [% IF ( itemloo.withdrawn ) %] - [% AuthorisedValues.GetDescriptionByKohaField( kohafield => 'items.withdrawn', authorised_value => itemloo.withdrawn ) | html %] - [% END %] +
    +
    + + [% END %] - [% IF ( itemloo.notforloan ) %] - Not for loan ([% AuthorisedValues.GetDescriptionByKohaField( kohafield => 'items.notforloan', authorised_value => itemloo.notforloan ) | html %]) - [% ELSIF ( itemloo.notforloanitype ) %] - Not for loan (Itemtype not for loan) - [% END %] - - - [% END # / UNLESS itemloo.hide %] - [% END # /FOREACH itemloo %] - - - - - [% IF hiddencount %] -

    - Show all items ([% hiddencount | html %] hidden) -

    - [% END # /IF hiddencount %] -
    - [% IF ( patron.borrowernumber ) %] - [% IF ( override_required ) %] - - [% ELSIF ( none_available ) %] - - [% ELSE %] - - [% END %] + [% UNLESS ( patron ) %] + [% UNLESS borrowers %] + [% SET hold_count = biblio.holds.count | html %] + [% IF hold_count %] + + [% IF always_show_holds == 'DONT' %] + + [% UNLESS reserveloop %] + Show holds ([% hold_count | html %]) [% END %] -
    -
    -
    - - - - [% ELSE # /UNLESS multi_hold %] -
    -
    - Hold details - [% INCLUDE 'csrf-token.inc' %] - - - - - [% FOREACH biblioloo IN biblioloop %] - - [% UNLESS biblioloo.none_avail %] - - + [% ELSE %] + [% END %] [% END %] - -
      -
    1. - Patron: - [% IF ( patron.borrowernumber ) %] - [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 hide_patron_infos_if_needed => 1 %] - [% ELSE %] - Not defined yet + [% END %] + [% IF ( reserveloop ) %] + + [% INCLUDE 'csrf-token.inc' %] + [% IF ( multi_hold ) %] + [% FOREACH biblionumber IN biblionumbers %] + [% END %] -
    2. + [% END %] -
    3. - - -
    4. -
    5. - - -
    6. - [% IF ( Koha.Preference('AllowHoldDateInFuture') ) %] -
    7. - - -
    8. + [% IF enqueued %] +
      +

      The job has been enqueued! It will be processed as soon as possible.

      +

      View detail of the enqueued job

      +
      [% END %] -
    9. - - -
    10. -
    - - - - - - [% UNLESS Koha.Preference('item-level_itypes') %] - +

    Existing holds

    +
    +
    + + +
    +
    + +
    +
    - - + + +
    + [% FOREACH biblioloo IN biblioloop %] -
    - - - - [% UNLESS Koha.Preference('item-level_itypes') %] - - [% END %] - - - - [% END # /FOREACH biblioloo %] -
     Pickup locationTitleItem typePriorityInformation
    - [% UNLESS ( biblioloo.warn ) %] - - [% END %] - - [% UNLESS ( biblioloo.none_avail || biblioloo.noitems ) %] - - [% END %] - -
      -
    • - [% biblioloo.title | html %] - [% IF biblioloo.author %]by [% biblioloo.author | html %][% END %] -
    • - [% IF ( biblioloo.publicationyear ) %] -
    • Publication year: [% biblioloo.publicationyear | html %]
    • - [% END %] -
    - [% IF ( biblioloo.warn ) %] - - [% END %] -
    - [% biblioloo.itemtype.translated_description | html %] - [% biblioloo.rank | html %] - [% IF ( biblioloo.checked_previously ) %] - Patron has previously checked out this title
    + [% IF ( biblioloo.reserveloop ) %] +
    + [% IF ( multi_hold ) %] +

    + + [% biblioloo.title | html %] + [% biblioloo.reserveloop.size | html %] [% tn('Hold', 'Holds', biblioloo.reserveloop.size) | $raw %] +

    [% END %] - [% IF ( biblioloo.alreadyres ) %] -
      -
    • [% INCLUDE 'patron-title.inc' patron => patron no_title => 1 no_cardnumber => 1 hide_patron_infos_if_needed => 1 %] already has a hold on this item
    • -
    - [% END %] - [% IF ( biblioloo.none_avail || biblioloo.noitems ) %] -
    • No items are available to be placed on hold
    - [% END %] -
    - -
    -
    - [% IF ( patron AND patron.borrowernumber ) %] - [% IF ( override_required ) %] - - [% ELSIF ( no_bibs_available ) %] - - [% ELSIF ( none_available ) %] - - [% ELSE %] - - [% END %] - [% END # /IF patron %] -
    - -
    - - [% END # /UNLESS multi_hold %] - [% END %] - [% UNLESS ( patron ) %] - [% UNLESS borrowers %] - [% SET hold_count = biblio.holds.count | html %] - [% IF hold_count %] - - [% IF always_show_holds == 'DONT' %] - - [% UNLESS reserveloop %] - Show holds ([% hold_count | html %]) - [% END %] - [% ELSE %] - - [% END %] - [% END %] - [% END %] - [% IF ( reserveloop ) %] -
    - [% INCLUDE 'csrf-token.inc' %] - [% IF ( multi_hold ) %] - [% FOREACH biblionumber IN biblionumbers %] - - [% END %] - [% END %] + [% IF Koha.Preference('HoldsSplitQueue') == 'branch' %] - [% IF enqueued %] -
    -

    The job has been enqueued! It will be processed as soon as possible.

    -

    View detail of the enqueued job

    -
    - [% END %] + [% SET branchcodes = [] %] -

    Existing holds

    -
    -
    - - -
    -
    - -
    - -
    -
    - [% FOREACH biblioloo IN biblioloop %] - [% IF ( biblioloo.reserveloop ) %] -
    - [% IF ( multi_hold ) %] -

    - [% biblioloo.title | html %] - [% biblioloo.reserveloop.size | html %] [% tn('Hold', 'Holds', biblioloo.reserveloop.size) | $raw %] -

    - [% END %] + [% FOREACH h IN biblioloo.reserveloop %] + [% branchcodes.push( h.branchcode ) %] + [% END %] + [% branchcodes = branchcodes.unique %] + [% IF ( branchcodes.empty ) %] +
    + There are no holds on this title. +
    + [% ELSE %] - [% IF Koha.Preference('HoldsSplitQueue') == 'branch' %] + [% FOREACH b IN branchcodes.sort %] + [% SET holds_by_branch = [] %] + [% FOREACH h IN biblioloo.reserveloop %] + [% IF h.branchcode == b %] + [% holds_by_branch.push( h ) %] + [% END %] + [% END %] +
    +

    [% Branches.GetName( b ) | html %]

    - [% SET branchcodes = [] %] + [% INCLUDE holds_table.inc holds=holds_by_branch %] +
    + [% END # /FOREACh b %] + [% END # /IF ( branchcodes.empty ) %] - [% FOREACH h IN biblioloo.reserveloop %] - [% branchcodes.push( h.branchcode ) %] - [% END %] - [% branchcodes = branchcodes.unique %] - [% IF ( branchcodes.empty ) %] -
    There are no holds on this title.
    - [% ELSE %] + [% ELSIF Koha.Preference('HoldsSplitQueue') == 'itemtype' %] - [% FOREACH b IN branchcodes.sort %] - [% SET holds_by_branch = [] %] - [% FOREACH h IN biblioloo.reserveloop %] - [% IF h.branchcode == b %] - [% holds_by_branch.push( h ) %] - [% END %] - [% END %] -
    -

    [% Branches.GetName( b ) | html %]

    + [% SET itemtypes = [] %] - [% INCLUDE holds_table.inc holds=holds_by_branch %] + [% FOREACH h IN biblioloo.reserveloop %] + [% SET hold_itemtype = h.object.item.effective_itemtype || h.itemtype %] + [% itemtypes.push( hold_itemtype ) %] + [% END %] + [% itemtypes = itemtypes.unique %] + [% IF ( itemtypes.empty ) %] +
    + There are no holds on this title.
    - [% END # /FOREACh b %] - [% END # /IF ( branchcodes.empty ) %] - [% ELSIF Koha.Preference('HoldsSplitQueue') == 'itemtype' %] + [% ELSE %] - [% SET itemtypes = [] %] + [% FOREACH i IN itemtypes.sort %] + [% SET holds_by_itemtype = [] %] + [% FOREACH h IN biblioloo.reserveloop %] + [% SET hold_itemtype = h.object.item.effective_itemtype || h.itemtype %] + [% IF hold_itemtype == i %] + [% holds_by_itemtype.push( h ) %] + [% END %] + [% END %] - [% FOREACH h IN biblioloo.reserveloop %] - [% SET hold_itemtype = h.object.item.effective_itemtype || h.itemtype %] - [% itemtypes.push( hold_itemtype ) %] - [% END %] - [% itemtypes = itemtypes.unique %] - [% IF ( itemtypes.empty ) %] -
    There are no holds on this title.
    - [% ELSE %] +
    + [% IF i %] +

    [% ItemTypes.GetDescription( i ) | html %]

    + [% ELSE %] +

    Any item type

    + [% END %] + [% INCLUDE holds_table.inc holds=holds_by_itemtype %] +
    + [% END # /FOREACH i %] + [% END # /IF ( itemtypes.empty ) %] - [% FOREACH i IN itemtypes.sort %] - [% SET holds_by_itemtype = [] %] - [% FOREACH h IN biblioloo.reserveloop %] - [% SET hold_itemtype = h.object.item.effective_itemtype || h.itemtype %] - [% IF hold_itemtype == i %] - [% holds_by_itemtype.push( h ) %] - [% END %] - [% END %] + [% ELSIF Koha.Preference('HoldsSplitQueue') == 'branch_itemtype' %] + [% SET branchcodes = [] %] -
    - [% IF i %] -

    [% ItemTypes.GetDescription( i ) | html %]

    - [% ELSE %] -

    Any item type

    - [% END %] - [% INCLUDE holds_table.inc holds=holds_by_itemtype %] + [% FOREACH h IN biblioloo.reserveloop %] + [% branchcodes.push( h.branchcode ) %] + [% END %] + [% branchcodes = branchcodes.unique %] + [% IF ( branchcodes.empty ) %] +
    + There are no holds on this title.
    - [% END # /FOREACH i %] - [% END # /IF ( itemtypes.empty ) %] - [% ELSIF Koha.Preference('HoldsSplitQueue') == 'branch_itemtype' %] - [% SET branchcodes = [] %] - - [% FOREACH h IN biblioloo.reserveloop %] - [% branchcodes.push( h.branchcode ) %] - [% END %] - [% branchcodes = branchcodes.unique %] - [% IF ( branchcodes.empty ) %] -
    There are no holds on this title.
    - [% ELSE %] + [% ELSE %] + + [% FOREACH b IN branchcodes.sort %] +
    +

    [% Branches.GetName( b ) | html %]

    + [% SET holds_by_branch = [] %] + [% FOREACH h IN biblioloo.reserveloop %] + [% IF h.branchcode == b %] + [% holds_by_branch.push( h ) %] + [% END %] + [% END %] - [% FOREACH b IN branchcodes.sort %] -
    -

    [% Branches.GetName( b ) | html %]

    - [% SET holds_by_branch = [] %] - [% FOREACH h IN biblioloo.reserveloop %] - [% IF h.branchcode == b %] - [% holds_by_branch.push( h ) %] + [% SET itemtypes = [] %] + [% FOREACH h IN holds_by_branch %] + [% SET hold_itemtype = h.object.item.effective_itemtype || h.itemtype %] + [% itemtypes.push( hold_itemtype ) %] [% END %] - [% END %] + [% itemtypes = itemtypes.unique %] - [% SET itemtypes = [] %] - [% FOREACH h IN holds_by_branch %] - [% SET hold_itemtype = h.object.item.effective_itemtype || h.itemtype %] - [% itemtypes.push( hold_itemtype ) %] - [% END %] - [% itemtypes = itemtypes.unique %] + [% FOREACH i IN itemtypes.sort %] +
    +
    + [% IF i %] + [% ItemTypes.GetDescription( i ) | html %] + [% ELSE %] + Any item type + [% END %] +
    - [% FOREACH i IN itemtypes.sort %] -
    -
    - [% IF i %] - [% ItemTypes.GetDescription( i ) | html %] - [% ELSE %] - Any item type + [% SET holds_by_itemtype = [] %] + [% FOREACH h IN holds_by_branch %] + [% SET hold_itemtype = h.object.item.effective_itemtype || h.itemtype %] + [% IF hold_itemtype == i %] + [% holds_by_itemtype.push( h ) %] + [% END %] [% END %] -
    + [% INCLUDE holds_table.inc holds=holds_by_itemtype %] +
    + [% END %] +
    + [% END # /FOREACH b %] + [% END # /IF ( branchcodes.empty ) %] - [% SET holds_by_itemtype = [] %] - [% FOREACH h IN holds_by_branch %] - [% SET hold_itemtype = h.object.item.effective_itemtype || h.itemtype %] - [% IF hold_itemtype == i %] - [% holds_by_itemtype.push( h ) %] - [% END %] - [% END %] - [% INCLUDE holds_table.inc holds=holds_by_itemtype %] -
    - - [% END %] + [% ELSE %] + + [% IF ( biblioloo.reserveloop.size ) %] + [% INCLUDE holds_table.inc holds=biblioloo.reserveloop %] + [% ELSE %] +
    + There are no holds on this title.
    - - [% END # /FOREACH b %] - [% END # /IF ( branchcodes.empty ) %] - [% ELSE %] + [% END %] - [% IF ( biblioloo.reserveloop.size ) %] - [% INCLUDE holds_table.inc holds=biblioloo.reserveloop %] - [% ELSE %] -
    There are no holds on this title.
    - [% END %] - [% END # /IF HoldsSplitQueue %] -
    - - [% END # /IF biblioloo.reserveloop %] - [% END # FOREACH biblioloo %] -
    - - - [% END # IF reserveloop %] - [% END # UNLESS patron %] -[% END %] + [% END # /IF HoldsSplitQueue %] +
    + [% END # /IF biblioloo.reserveloop %] + [% END # FOREACH biblioloo %] +
    + + [% END # IF reserveloop %] + [% END # UNLESS patron %] - -
    -
    [% INCLUDE 'csrf-token.inc' %]
    -
    +
    +
    + [% INCLUDE 'csrf-token.inc' %] +
    +
    [% MACRO jsinclude BLOCK %] [% INCLUDE 'datatables.inc' %] [% INCLUDE 'calendar.inc' %] [% INCLUDE 'select2.inc' %] - [% Asset.js("js/holds.js") | $raw %] - [% Asset.js("js/form-submit.js") | $raw %] + [% Asset.js("js/holds.js") | $raw%] + [% Asset.js("js/form-submit.js") | $raw%] [% SET url_biblio_params = "biblionumber=" _ biblionumbers.join("&biblionumber=") %] [% IF multi_hold %] [% SET url_biblio_params = url_biblio_params _ "&multi_hold=1" %] [% END %] + + + - [% UNLESS patron %] - [% SET search_results_block_id = 'holds_patronsearch_pane_panel' %] - [%# adjusting variable for patron-search.inc %] - [% PROCESS patron_search_js table_id => 'table_borrowers', categories => categories, libraries => libraries, columns => columns, open_on_row_click => 1, on_click_url => '/cgi-bin/koha/reserve/request.pl?' _ url_biblio_params, redirect_if_one_result => 1, redirect_url => '/cgi-bin/koha/reserve/request.pl?' _ url_biblio_params, redirect_if_attribute_equal => 'cardnumber' %] - - [% END %] + + [% SET search_results_block_id = 'holds_patronsearch_pane_panel' %] [%# adjusting variable for patron-search.inc %] + [% PROCESS patron_search_js table_id => 'table_borrowers', categories => categories, libraries => libraries, columns => columns, open_on_row_click => 1, on_click_url => '/cgi-bin/koha/reserve/request.pl?' _ url_biblio_params, redirect_if_one_result => 1, redirect_url => '/cgi-bin/koha/reserve/request.pl?' _ url_biblio_params, redirect_if_attribute_equal => 'cardnumber' %] [% END %] -[% INCLUDE 'intranet-bottom.inc' %] + + +[% INCLUDE 'intranet-bottom.inc' %] \ No newline at end of file diff --git a/reserve/request.pl b/reserve/request.pl index c5e354aa7d..bafa6defbf 100755 --- a/reserve/request.pl +++ b/reserve/request.pl @@ -88,8 +88,7 @@ my $warnings; my $messages; my $exceeded_maxreserves; my $exceeded_holds_per_record; -my @failed_holds = $input->multi_param('failed_holds'); -my $form_submitted = $input->param('form_submitted'); +my @failed_holds = $input->multi_param('failed_holds'); my $op = $input->param('op') || q{}; @@ -146,29 +145,24 @@ if ($findborrower) { $borrowernumber_hold = $patron->borrowernumber if $patron; } -if ($form_submitted) { - if ($findclub) { - my $club = Koha::Clubs->find( { name => $findclub } ); - if ($club) { - $club_hold = $club->id; +if ($findclub) { + my $club = Koha::Clubs->find( { name => $findclub } ); + if ($club) { + $club_hold = $club->id; + } else { + my @clubs = Koha::Clubs->search( + [ + { name => { like => '%' . $findclub . '%' } }, + { description => { like => '%' . $findclub . '%' } } + ] + )->as_list; + if ( scalar @clubs == 1 ) { + $club_hold = $clubs[0]->id; + } elsif (@clubs) { + $template->param( clubs => \@clubs ); } else { - my @clubs = Koha::Clubs->search( - [ - { name => { like => '%' . $findclub . '%' } }, - { description => { like => '%' . $findclub . '%' } } - ] - )->as_list; - if ( scalar @clubs == 1 ) { - $club_hold = $clubs[0]->id; - } elsif (@clubs) { - $template->param( clubs => \@clubs ); - } else { - $messageclub = "'$findclub'"; - } + $messageclub = "'$findclub'"; } - } else { - my @clubs = Koha::Clubs->search()->as_list; - $template->param( clubs => \@clubs ); } } @@ -189,8 +183,10 @@ if ( $borrowernumber_hold && !$op ) { # FIXME At this time we have a simple count of reservs, but, later, we could improve the infos "title" ... my $reserves_count = $patron->holds->count; + $template->param( reserves_count => $reserves_count ); my $new_reserves_count = scalar(@biblionumbers); + $template->param( new_reserves_count => $new_reserves_count ); my $maxreserves = C4::Context->preference('maxreserves'); $template->param( maxreserves => $maxreserves ); @@ -206,8 +202,6 @@ if ( $borrowernumber_hold && !$op ) { $exceeded_maxreserves = 1; $template->param( new_reserves_allowed => $new_reserves_allowed, - new_reserves_count => $new_reserves_count, - reserves_count => $reserves_count, maxreserves => $maxreserves, ); } @@ -302,6 +296,7 @@ if ( ( $findborrower && $borrowernumber_hold || $findclub && $club_hold ) my @biblioloop = (); my $no_reserves_allowed = 0; my $num_bibs_available = 0; + my @canbookbereserved_responses; foreach my $biblionumber (@biblionumbers) { next unless $biblionumber =~ m|^\d+$|; @@ -320,6 +315,9 @@ if ( ( $findborrower && $borrowernumber_hold || $findclub && $club_hold ) if ($patron) { { # CanBookBeReserved my $canReserve = CanBookBeReserved( $patron->borrowernumber, $biblionumber ); + + push @canbookbereserved_responses, { %$canReserve, biblio => $biblio }; + if ( $canReserve->{status} eq 'OK' ) { #All is OK and we can continue @@ -719,10 +717,14 @@ if ( ( $findborrower && $borrowernumber_hold || $findclub && $club_hold ) $template->param( no_bibs_available => 1 ) unless $num_bibs_available > 0; - $template->param( biblioloop => \@biblioloop ); - $template->param( no_reserves_allowed => $no_reserves_allowed ); - $template->param( exceeded_maxreserves => $exceeded_maxreserves ); - $template->param( exceeded_holds_per_record => $exceeded_holds_per_record ); + $Data::Dumper::Maxdepth = 3; + use Data::Dumper; + warn Dumper \@canbookbereserved_responses; + $template->param( canbookbereserved_responses => \@canbookbereserved_responses ); + $template->param( biblioloop => \@biblioloop ); + $template->param( no_reserves_allowed => $no_reserves_allowed ); + $template->param( exceeded_maxreserves => $exceeded_maxreserves ); + $template->param( exceeded_holds_per_record => $exceeded_holds_per_record ); # FIXME: getting just the first bib's result doesn't seem right $template->param( subscriptionsnumber => CountSubscriptionFromBiblionumber( $biblionumbers[0] ) ); diff --git a/t/db_dependent/Reserves/HoldRulesChecker.t b/t/db_dependent/Reserves/HoldRulesChecker.t new file mode 100644 index 0000000000..0e41b46038 --- /dev/null +++ b/t/db_dependent/Reserves/HoldRulesChecker.t @@ -0,0 +1,110 @@ +#!/usr/bin/perl + +# Copyright 2019 Biblibre +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use Test::More tests => 13; +use t::lib::TestBuilder; + +use C4::Reserves qw( CanBookBeReserved ); + +my $schema = Koha::Database->new->schema; +$schema->storage->txn_begin; + +my $builder = t::lib::TestBuilder->new(); + +my $library = $builder->build_object( { class => 'Koha::Libraries' } ); +my $category = $builder->build_object( { class => 'Koha::Patron::Categories' } ); +my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { + categorycode => $category->categorycode, + branchcode => $library->branchcode, + }, + } +); + +my $itemtype = $builder->build_object( { class => 'Koha::ItemTypes' } ); + +my $biblio = $builder->build_sample_biblio( + { + title => 'Title 1', + } +); +my $item = $builder->build_sample_item( + { + biblionumber => $biblio->biblionumber, + library => $library->branchcode, + itype => $itemtype->itemtype, + damaged => 0, + } +); + +my $rules_rs = Koha::Database->new()->schema()->resultset('CirculationRule'); +$rules_rs->delete(); + +my $default_reservesallowed_rule = $rules_rs->create( + { + categorycode => undef, + itemtype => undef, + branchcode => undef, + rule_name => 'reservesallowed', + rule_value => 10, + } +); + +my $reservesallowed_rule = $rules_rs->create( + { + categorycode => $patron->categorycode, + itemtype => undef, + branchcode => $library->branchcode, + rule_name => 'reservesallowed', + rule_value => 0, + } +); + +my $can = CanBookBeReserved( $patron->borrowernumber, $biblio->biblionumber ); +is( $can->{status}, '' ); +is( scalar @{ $can->{circulation_rules} }, 0 ); +is( scalar @{ $can->{item_responses} }, 1 ); +is( $can->{item_responses}->[0]->{status}, 'noReservesAllowed' ); +is( scalar @{ $can->{item_responses}->[0]->{circulation_rules} }, 1 ); +is( $can->{item_responses}->[0]->{circulation_rules}->[0]->id, $reservesallowed_rule->id ); + +my $holds_per_record_rule = $rules_rs->create( + { + categorycode => $patron->categorycode, + itemtype => undef, + branchcode => $library->branchcode, + rule_name => 'holds_per_record', + rule_value => 1, + } +); + +$can = CanBookBeReserved( $patron->borrowernumber, $biblio->biblionumber ); +is( $can->{status}, '' ); +is( scalar @{ $can->{circulation_rules} }, 0 ); +is( scalar @{ $can->{item_responses} }, 1 ); +is( $can->{item_responses}->[0]->{status}, 'noReservesAllowed' ); +is( scalar @{ $can->{item_responses}->[0]->{circulation_rules} }, 2 ); +is( $can->{item_responses}->[0]->{circulation_rules}->[0]->id, $holds_per_record_rule->id ); +is( $can->{item_responses}->[0]->{circulation_rules}->[1]->id, $reservesallowed_rule->id ); + +$schema->storage->txn_rollback; -- 2.48.1