Main Koha release repository https://koha-community.org
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

759 行
29 KiB

  1. #!/usr/bin/perl
  2. #written 2/1/00 by chris@katipo.oc.nz
  3. # Copyright 2000-2002 Katipo Communications
  4. # Parts Copyright 2011 Catalyst IT
  5. #
  6. # This file is part of Koha.
  7. #
  8. # Koha is free software; you can redistribute it and/or modify it
  9. # under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 3 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # Koha is distributed in the hope that it will be useful, but
  14. # WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with Koha; if not, see <http://www.gnu.org/licenses>.
  20. =head1 request.pl
  21. script to place reserves/requests
  22. =cut
  23. use Modern::Perl;
  24. use CGI qw ( -utf8 );
  25. use List::MoreUtils qw/uniq/;
  26. use Date::Calc qw/Date_to_Days/;
  27. use C4::Output;
  28. use C4::Auth;
  29. use C4::Reserves;
  30. use C4::Biblio;
  31. use C4::Items;
  32. use C4::Koha;
  33. use C4::Serials;
  34. use C4::Circulation;
  35. use Koha::DateUtils;
  36. use C4::Utils::DataTables::Members;
  37. use C4::Members;
  38. use C4::Search; # enabled_staff_search_views
  39. use Koha::Biblios;
  40. use Koha::DateUtils;
  41. use Koha::Checkouts;
  42. use Koha::Holds;
  43. use Koha::CirculationRules;
  44. use Koha::Items;
  45. use Koha::ItemTypes;
  46. use Koha::Libraries;
  47. use Koha::Patrons;
  48. use Koha::Clubs;
  49. my $dbh = C4::Context->dbh;
  50. my $input = CGI->new;
  51. my ( $template, $borrowernumber, $cookie, $flags ) = get_template_and_user(
  52. {
  53. template_name => "reserve/request.tt",
  54. query => $input,
  55. type => "intranet",
  56. flagsrequired => { reserveforothers => 'place_holds' },
  57. }
  58. );
  59. my $showallitems = $input->param('showallitems');
  60. my $pickup = $input->param('pickup') || C4::Context->userenv->{'branch'};
  61. my $itemtypes = { map { $_->{itemtype} => $_ } @{ Koha::ItemTypes->search_with_localization->unblessed } };
  62. # Select borrowers infos
  63. my $findborrower = $input->param('findborrower');
  64. $findborrower = '' unless defined $findborrower;
  65. $findborrower =~ s|,| |g;
  66. my $findclub = $input->param('findclub');
  67. $findclub = '' unless defined $findclub && !$findborrower;
  68. my $borrowernumber_hold = $input->param('borrowernumber') || '';
  69. my $club_hold = $input->param('club')||'';
  70. my $messageborrower;
  71. my $messageclub;
  72. my $warnings;
  73. my $messages;
  74. my $exceeded_maxreserves;
  75. my $exceeded_holds_per_record;
  76. my $date = output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
  77. my $action = $input->param('action');
  78. $action ||= q{};
  79. if ( $action eq 'move' ) {
  80. my $where = $input->param('where');
  81. my $reserve_id = $input->param('reserve_id');
  82. my $prev_priority = $input->param('prev_priority');
  83. my $next_priority = $input->param('next_priority');
  84. my $first_priority = $input->param('first_priority');
  85. my $last_priority = $input->param('last_priority');
  86. my $hold_itemnumber = $input->param('itemnumber');
  87. if ( $prev_priority == 0 && $next_priority == 1 ){
  88. C4::Reserves::RevertWaitingStatus({ itemnumber => $hold_itemnumber });
  89. } else {
  90. AlterPriority( $where, $reserve_id, $prev_priority, $next_priority, $first_priority, $last_priority );
  91. }
  92. } elsif ( $action eq 'cancel' ) {
  93. my $reserve_id = $input->param('reserve_id');
  94. my $cancellation_reason = $input->param("cancellation-reason");
  95. my $hold = Koha::Holds->find( $reserve_id );
  96. $hold->cancel({ cancellation_reason => $cancellation_reason }) if $hold;
  97. } elsif ( $action eq 'setLowestPriority' ) {
  98. my $reserve_id = $input->param('reserve_id');
  99. ToggleLowestPriority( $reserve_id );
  100. } elsif ( $action eq 'toggleSuspend' ) {
  101. my $reserve_id = $input->param('reserve_id');
  102. my $suspend_until = $input->param('suspend_until');
  103. ToggleSuspend( $reserve_id, $suspend_until );
  104. }
  105. if ($findborrower) {
  106. my $patron = Koha::Patrons->find( { cardnumber => $findborrower } );
  107. if ( $patron ) {
  108. $borrowernumber_hold = $patron->borrowernumber;
  109. } else {
  110. my $dt_params = { iDisplayLength => -1 };
  111. my $results = C4::Utils::DataTables::Members::search(
  112. {
  113. searchmember => $findborrower,
  114. dt_params => $dt_params,
  115. }
  116. );
  117. my $borrowers = $results->{patrons};
  118. if ( scalar @$borrowers == 1 ) {
  119. $borrowernumber_hold = $borrowers->[0]->{borrowernumber};
  120. } elsif ( @$borrowers ) {
  121. $template->param( borrowers => $borrowers );
  122. } else {
  123. $messageborrower = "'$findborrower'";
  124. }
  125. }
  126. }
  127. if($findclub) {
  128. my $club = Koha::Clubs->find( { name => $findclub } );
  129. if( $club ) {
  130. $club_hold = $club->id;
  131. } else {
  132. my @clubs = Koha::Clubs->search( [
  133. { name => { like => '%'.$findclub.'%' } },
  134. { description => { like => '%'.$findclub.'%' } }
  135. ] );
  136. if( scalar @clubs == 1 ) {
  137. $club_hold = $clubs[0]->id;
  138. } elsif ( @clubs ) {
  139. $template->param( clubs => \@clubs );
  140. } else {
  141. $messageclub = "'$findclub'";
  142. }
  143. }
  144. }
  145. my @biblionumbers = ();
  146. my $biblionumber = $input->param('biblionumber');
  147. my $biblionumbers = $input->param('biblionumbers');
  148. if ( $biblionumbers ) {
  149. @biblionumbers = split '/', $biblionumbers;
  150. } else {
  151. push @biblionumbers, $input->multi_param('biblionumber');
  152. }
  153. my $multi_hold = @biblionumbers > 1;
  154. $template->param(multi_hold => $multi_hold);
  155. # If we have the borrowernumber because we've performed an action, then we
  156. # don't want to try to place another reserve.
  157. if ($borrowernumber_hold && !$action) {
  158. my $patron = Koha::Patrons->find( $borrowernumber_hold );
  159. my $diffbranch;
  160. # we check the reserves of the user, and if they can reserve a document
  161. # FIXME At this time we have a simple count of reservs, but, later, we could improve the infos "title" ...
  162. my $reserves_count = $patron->holds->count;
  163. my $new_reserves_count = scalar( @biblionumbers );
  164. my $maxreserves = C4::Context->preference('maxreserves');
  165. if ( $maxreserves
  166. && ( $reserves_count + $new_reserves_count > $maxreserves ) )
  167. {
  168. my $new_reserves_allowed =
  169. $maxreserves - $reserves_count > 0
  170. ? $maxreserves - $reserves_count
  171. : 0;
  172. $warnings = 1;
  173. $exceeded_maxreserves = 1;
  174. $template->param(
  175. new_reserves_allowed => $new_reserves_allowed,
  176. new_reserves_count => $new_reserves_count,
  177. reserves_count => $reserves_count,
  178. maxreserves => $maxreserves,
  179. );
  180. }
  181. # we check the date expiry of the borrower (only if there is an expiry date, otherwise, set to 1 (warn)
  182. my $expiry_date = $patron->dateexpiry;
  183. my $expiry = 0; # flag set if patron account has expired
  184. if ($expiry_date and $expiry_date ne '0000-00-00' and
  185. Date_to_Days(split /-/,$date) > Date_to_Days(split /-/,$expiry_date)) {
  186. $expiry = 1;
  187. }
  188. # check if the borrower make the reserv in a different branch
  189. if ( $patron->branchcode ne C4::Context->userenv->{'branch'} ) {
  190. $diffbranch = 1;
  191. }
  192. my $amount_outstanding = $patron->account->balance;
  193. $template->param(
  194. patron => $patron,
  195. expiry => $expiry,
  196. diffbranch => $diffbranch,
  197. messages => $messages,
  198. warnings => $warnings,
  199. amount_outstanding => $amount_outstanding,
  200. );
  201. }
  202. if ($club_hold && !$borrowernumber_hold && !$action) {
  203. my $club = Koha::Clubs->find($club_hold);
  204. my $enrollments = $club->club_enrollments;
  205. my $maxreserves = C4::Context->preference('maxreserves');
  206. my $new_reserves_count = scalar( @biblionumbers );
  207. my @members;
  208. while(my $enrollment = $enrollments->next) {
  209. next if $enrollment->is_canceled;
  210. my $member = { patron => $enrollment->patron->unblessed };
  211. my $reserves_count = $enrollment->patron->holds->count;
  212. if ( $maxreserves
  213. && ( $reserves_count + $new_reserves_count > $maxreserves ) )
  214. {
  215. $member->{new_reserves_allowed} = $maxreserves - $reserves_count > 0
  216. ? $maxreserves - $reserves_count
  217. : 0;
  218. $member->{exceeded_maxreserves} = 1;
  219. }
  220. my $expiry_date = $enrollment->patron->dateexpiry;
  221. $member->{expiry} = 0; # flag set if patron account has expired
  222. if ($expiry_date and $expiry_date ne '0000-00-00' and
  223. Date_to_Days(split /-/,$date) > Date_to_Days(split /-/,$expiry_date)) {
  224. $member->{expiry} = 1;
  225. }
  226. $member->{amount_outstanding} = $enrollment->patron->account->balance;
  227. if ( $enrollment->patron->branchcode ne C4::Context->userenv->{'branch'} ) {
  228. $member->{diffbranch} = 1;
  229. }
  230. push @members, $member;
  231. }
  232. $template->param(
  233. club => $club,
  234. members => \@members,
  235. maxreserves => $maxreserves,
  236. new_reserves_count => $new_reserves_count
  237. );
  238. }
  239. $template->param(
  240. messageborrower => $messageborrower,
  241. messageclub => $messageclub
  242. );
  243. # FIXME launch another time GetMember perhaps until (Joubu: Why?)
  244. my $patron = Koha::Patrons->find( $borrowernumber_hold );
  245. my $logged_in_patron = Koha::Patrons->find( $borrowernumber );
  246. my $wants_check;
  247. if ($patron) {
  248. $wants_check = $patron->wants_check_for_previous_checkout;
  249. }
  250. my $itemdata_enumchron = 0;
  251. my $itemdata_ccode = 0;
  252. my @biblioloop = ();
  253. foreach my $biblionumber (@biblionumbers) {
  254. next unless $biblionumber =~ m|^\d+$|;
  255. my %biblioloopiter = ();
  256. my $biblio = Koha::Biblios->find( $biblionumber );
  257. my $force_hold_level;
  258. if ( $patron ) {
  259. { # CanBookBeReserved
  260. my $canReserve = CanBookBeReserved( $patron->borrowernumber, $biblionumber );
  261. if ( $canReserve->{status} eq 'OK' ) {
  262. #All is OK and we can continue
  263. }
  264. elsif ( $canReserve->{status} eq 'tooManyReserves' ) {
  265. $exceeded_maxreserves = 1;
  266. $template->param( maxreserves => $canReserve->{limit} );
  267. }
  268. elsif ( $canReserve->{status} eq 'tooManyHoldsForThisRecord' ) {
  269. $exceeded_holds_per_record = 1;
  270. $biblioloopiter{ $canReserve->{status} } = 1;
  271. }
  272. elsif ( $canReserve->{status} eq 'ageRestricted' ) {
  273. $template->param( $canReserve->{status} => 1 );
  274. $biblioloopiter{ $canReserve->{status} } = 1;
  275. }
  276. elsif ( $canReserve->{status} eq 'alreadypossession' ) {
  277. $template->param( $canReserve->{status} => 1);
  278. $biblioloopiter{ $canReserve->{status} } = 1;
  279. }
  280. else {
  281. $biblioloopiter{ $canReserve->{status} } = 1;
  282. }
  283. }
  284. # For multiple holds per record, if a patron has previously placed a hold,
  285. # the patron can only place more holds of the same type. That is, if the
  286. # patron placed a record level hold, all the holds the patron places must
  287. # be record level. If the patron placed an item level hold, all holds
  288. # the patron places must be item level
  289. my $holds = Koha::Holds->search(
  290. {
  291. borrowernumber => $patron->borrowernumber,
  292. biblionumber => $biblionumber,
  293. found => undef,
  294. }
  295. );
  296. $force_hold_level = $holds->forced_hold_level();
  297. $biblioloopiter{force_hold_level} = $force_hold_level;
  298. $template->param( force_hold_level => $force_hold_level );
  299. # For a librarian to be able to place multiple record holds for a patron for a record,
  300. # we must find out what the maximum number of holds they can place for the patron is
  301. my $max_holds_for_record = GetMaxPatronHoldsForRecord( $patron->borrowernumber, $biblionumber );
  302. my $remaining_holds_for_record = $max_holds_for_record - $holds->count();
  303. $biblioloopiter{remaining_holds_for_record} = $max_holds_for_record;
  304. $template->param( max_holds_for_record => $max_holds_for_record );
  305. $template->param( remaining_holds_for_record => $remaining_holds_for_record );
  306. }
  307. my $count = Koha::Holds->search( { biblionumber => $biblionumber } )->count();
  308. my $totalcount = $count;
  309. # FIXME think @optionloop, is maybe obsolete, or must be switchable by a systeme preference fixed rank or not
  310. # make priorities options
  311. my @optionloop;
  312. for ( 1 .. $count + 1 ) {
  313. push(
  314. @optionloop,
  315. {
  316. num => $_,
  317. selected => ( $_ == $count + 1 ),
  318. }
  319. );
  320. }
  321. # adding a fixed value for priority options
  322. my $fixedRank = $count+1;
  323. my %itemnumbers_of_biblioitem;
  324. my @hostitems = get_hostitemnumbers_of($biblionumber);
  325. my @itemnumbers;
  326. if (@hostitems){
  327. $template->param('hostitemsflag' => 1);
  328. push(@itemnumbers, @hostitems);
  329. }
  330. my $items = Koha::Items->search({ -or => { biblionumber => $biblionumber, itemnumber => { in => \@itemnumbers } } });
  331. unless ( $items->count ) {
  332. # FIXME Then why do we continue?
  333. $template->param('noitems' => 1);
  334. $biblioloopiter{noitems} = 1;
  335. }
  336. ## Here we go backwards again to create hash of biblioitemnumber to itemnumbers,
  337. ## when by definition all of the itemnumber have the same biblioitemnumber
  338. my ( $iteminfos_of );
  339. while ( my $item = $items->next ) {
  340. $item = $item->unblessed;
  341. my $biblioitemnumber = $item->{biblioitemnumber};
  342. my $itemnumber = $item->{itemnumber};
  343. push( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} }, $itemnumber );
  344. $iteminfos_of->{$itemnumber} = $item;
  345. }
  346. ## Should be same as biblionumber
  347. my @biblioitemnumbers = keys %itemnumbers_of_biblioitem;
  348. my $biblioiteminfos_of = {
  349. map {
  350. my $biblioitem = $_;
  351. ( $biblioitem->{biblioitemnumber} => $biblioitem )
  352. } @{ Koha::Biblioitems->search(
  353. { biblioitemnumber => { -in => \@biblioitemnumbers } },
  354. { select => ['biblioitemnumber', 'publicationyear', 'itemtype']}
  355. )->unblessed
  356. }
  357. };
  358. my @bibitemloop;
  359. my @available_itemtypes;
  360. foreach my $biblioitemnumber (@biblioitemnumbers) {
  361. my $biblioitem = $biblioiteminfos_of->{$biblioitemnumber};
  362. my $num_available = 0;
  363. my $num_override = 0;
  364. my $hiddencount = 0;
  365. my $num_alreadyheld = 0;
  366. $biblioitem->{force_hold_level} = $force_hold_level;
  367. if ( $biblioitem->{biblioitemnumber} ne $biblionumber ) {
  368. $biblioitem->{hostitemsflag} = 1;
  369. }
  370. $biblioloopiter{description} = $biblioitem->{description};
  371. $biblioloopiter{itypename} = $biblioitem->{description};
  372. if ( $biblioitem->{itemtype} ) {
  373. $biblioitem->{description} =
  374. $itemtypes->{ $biblioitem->{itemtype} }{description};
  375. $biblioloopiter{imageurl} =
  376. getitemtypeimagelocation( 'intranet',
  377. $itemtypes->{ $biblioitem->{itemtype} }{imageurl} );
  378. }
  379. # iterating through all items first to check if any of them available
  380. # to pass this value further inside down to IsAvailableForItemLevelRequest to
  381. # it's complicated logic to analyse.
  382. # (before this loop was inside that sub loop so it was O(n^2) )
  383. my $items_any_available;
  384. $items_any_available = ItemsAnyAvailableAndNotRestricted( { biblionumber => $biblioitemnumber, patron => $patron })
  385. if $patron;
  386. foreach my $itemnumber ( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} } ) {
  387. my $item = $iteminfos_of->{$itemnumber};
  388. my $do_check;
  389. if ( $patron ) {
  390. $do_check = $patron->do_check_for_previous_checkout($item) if $wants_check;
  391. if ( $do_check && $wants_check ) {
  392. $item->{checked_previously} = $do_check;
  393. if ( $multi_hold ) {
  394. $biblioloopiter{checked_previously} = $do_check;
  395. } else {
  396. $template->param( checked_previously => $do_check );
  397. }
  398. }
  399. }
  400. $item->{force_hold_level} = $force_hold_level;
  401. unless (C4::Context->preference('item-level_itypes')) {
  402. $item->{itype} = $biblioitem->{itemtype};
  403. }
  404. $item->{itypename} = $itemtypes->{ $item->{itype} }{description};
  405. $item->{imageurl} = getitemtypeimagelocation( 'intranet', $itemtypes->{ $item->{itype} }{imageurl} );
  406. $item->{homebranch} = $item->{homebranch};
  407. # if the holdingbranch is different than the homebranch, we show the
  408. # holdingbranch of the document too
  409. if ( $item->{homebranch} ne $item->{holdingbranch} ) {
  410. $item->{holdingbranch} = $item->{holdingbranch};
  411. }
  412. if($item->{biblionumber} ne $biblionumber){
  413. $item->{hostitemsflag} = 1;
  414. $item->{hosttitle} = Koha::Biblios->find( $item->{biblionumber} )->title;
  415. }
  416. # if the item is currently on loan, we display its return date and
  417. # change the background color
  418. my $issue = Koha::Checkouts->find( { itemnumber => $itemnumber } );
  419. if ( $issue ) {
  420. $item->{date_due} = $issue->date_due;
  421. $item->{backgroundcolor} = 'onloan';
  422. }
  423. # checking reserve
  424. my $item_object = Koha::Items->find( $itemnumber );
  425. my $holds = $item_object->current_holds;
  426. if ( my $first_hold = $holds->next ) {
  427. my $p = Koha::Patrons->find( $first_hold->borrowernumber );
  428. $item->{backgroundcolor} = 'reserved';
  429. $item->{reservedate} = output_pref({ dt => dt_from_string( $first_hold->reservedate ), dateonly => 1 }); # FIXME Should be formatted in the template
  430. $item->{ReservedFor} = $p;
  431. $item->{ExpectedAtLibrary} = $first_hold->branchcode;
  432. $item->{waitingdate} = $first_hold->waitingdate;
  433. }
  434. # Management of the notforloan document
  435. if ( $item->{notforloan} ) {
  436. $item->{backgroundcolor} = 'other';
  437. }
  438. # Management of lost or long overdue items
  439. if ( $item->{itemlost} ) {
  440. $item->{backgroundcolor} = 'other';
  441. if ($logged_in_patron->category->hidelostitems && !$showallitems) {
  442. $item->{hide} = 1;
  443. $hiddencount++;
  444. }
  445. }
  446. # Check the transit status
  447. my ( $transfertwhen, $transfertfrom, $transfertto ) =
  448. GetTransfers($itemnumber);
  449. if ( defined $transfertwhen && $transfertwhen ne '' ) {
  450. $item->{transfertwhen} = output_pref({ dt => dt_from_string( $transfertwhen ), dateonly => 1 });
  451. $item->{transfertfrom} = $transfertfrom;
  452. $item->{transfertto} = $transfertto;
  453. $item->{nocancel} = 1;
  454. }
  455. # If there is no loan, return and transfer, we show a checkbox.
  456. $item->{notforloan} ||= 0;
  457. # if independent branches is on we need to check if the person can reserve
  458. # for branches they arent logged in to
  459. if ( C4::Context->preference("IndependentBranches") ) {
  460. if (! C4::Context->preference("canreservefromotherbranches")){
  461. # can't reserve items so need to check if item homebranch and userenv branch match if not we can't reserve
  462. my $userenv = C4::Context->userenv;
  463. unless ( C4::Context->IsSuperLibrarian ) {
  464. $item->{cantreserve} = 1 if ( $item->{homebranch} ne $userenv->{branch} );
  465. }
  466. }
  467. }
  468. if ( $patron ) {
  469. my $patron_unblessed = $patron->unblessed;
  470. my $branch = C4::Circulation::_GetCircControlBranch($item, $patron_unblessed);
  471. my $branchitemrule = GetBranchItemRule( $branch, $item->{'itype'} );
  472. $item->{'holdallowed'} = $branchitemrule->{'holdallowed'};
  473. my $can_item_be_reserved = CanItemBeReserved( $patron->borrowernumber, $itemnumber, $pickup )->{status};
  474. $item->{not_holdable} = $can_item_be_reserved unless ( $can_item_be_reserved eq 'OK' );
  475. $item->{item_level_holds} = Koha::CirculationRules->get_opacitemholds_policy( { item => $item_object, patron => $patron } );
  476. if (
  477. !$item->{cantreserve}
  478. && !$exceeded_maxreserves
  479. && $can_item_be_reserved eq 'OK'
  480. # items_any_available defined outside of the current loop,
  481. # so we avoiding loop inside IsAvailableForItemLevelRequest:
  482. && IsAvailableForItemLevelRequest($item_object, $patron, undef, $items_any_available)
  483. )
  484. {
  485. $item->{available} = 1;
  486. $num_available++;
  487. if($branchitemrule->{'hold_fulfillment_policy'} eq 'any' ) {
  488. $item->{pickup_locations} = 'Any library';
  489. $item->{pickup_locations_code} = 'all';
  490. } else {
  491. my $arr_locations = Koha::Items->find($itemnumber)
  492. ->pickup_locations( { patron => $patron } )->as_list();
  493. $item->{pickup_locations} = join( ', ',
  494. map { $_->unblessed->{branchname} } @$arr_locations);
  495. $item->{pickup_locations_code} = join( ',',
  496. map { $_->unblessed->{branchcode} } @$arr_locations);
  497. }
  498. push( @available_itemtypes, $item->{itype} );
  499. }
  500. elsif ( C4::Context->preference('AllowHoldPolicyOverride') ) {
  501. # If AllowHoldPolicyOverride is set, it should override EVERY restriction, not just branch item rules
  502. # with the exception of itemAlreadyOnHold because, you know, the item is already on hold
  503. if ( $can_item_be_reserved ne 'itemAlreadyOnHold' ) {
  504. $item->{override} = 1;
  505. $num_override++;
  506. } else { $num_alreadyheld++ }
  507. push( @available_itemtypes, $item->{itype} );
  508. }
  509. # If none of the conditions hold true, then neither override nor available is set and the item cannot be checked
  510. # Show serial enumeration when needed
  511. if ($item->{enumchron}) {
  512. $itemdata_enumchron = 1;
  513. }
  514. # Show collection when needed
  515. if ($item->{ccode}) {
  516. $itemdata_ccode = 1;
  517. }
  518. }
  519. push @{ $biblioitem->{itemloop} }, $item;
  520. }
  521. # While we can't override an alreay held item, we should be able to override the others
  522. # Unless all items are already held
  523. if ( $num_override > 0 && ($num_override + $num_alreadyheld) == scalar( @{ $biblioitem->{itemloop} } ) ) {
  524. # That is, if all items require an override
  525. $template->param( override_required => 1 );
  526. } elsif ( $num_available == 0 ) {
  527. $template->param( none_available => 1 );
  528. $biblioloopiter{warn} = 1;
  529. $biblioloopiter{none_avail} = 1;
  530. }
  531. $template->param( hiddencount => $hiddencount);
  532. push @bibitemloop, $biblioitem;
  533. }
  534. @available_itemtypes = uniq( @available_itemtypes );
  535. $template->param( available_itemtypes => \@available_itemtypes );
  536. # existingreserves building
  537. my @reserveloop;
  538. my @reserves = Koha::Holds->search( { biblionumber => $biblionumber }, { order_by => 'priority' } );
  539. foreach my $res (
  540. sort {
  541. my $a_found = $a->found() || '';
  542. my $b_found = $a->found() || '';
  543. $a_found cmp $b_found;
  544. } @reserves
  545. )
  546. {
  547. my $priority = $res->priority();
  548. my %reserve;
  549. my @optionloop;
  550. for ( my $i = 1 ; $i <= $totalcount ; $i++ ) {
  551. push(
  552. @optionloop,
  553. {
  554. num => $i,
  555. selected => ( $i == $priority ),
  556. }
  557. );
  558. }
  559. if ( $res->is_found() ) {
  560. $reserve{'holdingbranch'} = $res->item()->holdingbranch();
  561. $reserve{'biblionumber'} = $res->item()->biblionumber();
  562. $reserve{'barcodenumber'} = $res->item()->barcode();
  563. $reserve{'wbrcode'} = $res->branchcode();
  564. $reserve{'itemnumber'} = $res->itemnumber();
  565. $reserve{'wbrname'} = $res->branch()->branchname();
  566. $reserve{'atdestination'} = $res->is_at_destination();
  567. $reserve{'desk_name'} = ( $res->desk() ) ? $res->desk()->desk_name() : '' ;
  568. $reserve{'found'} = $res->is_found();
  569. $reserve{'inprocessing'} = $res->is_in_processing();
  570. $reserve{'intransit'} = $res->is_in_transit();
  571. }
  572. elsif ( $res->priority() > 0 ) {
  573. if ( my $item = $res->item() ) {
  574. $reserve{'itemnumber'} = $item->id();
  575. $reserve{'barcodenumber'} = $item->barcode();
  576. $reserve{'item_level_hold'} = 1;
  577. }
  578. }
  579. $reserve{'expirationdate'} = $res->expirationdate;
  580. $reserve{'date'} = $res->reservedate;
  581. $reserve{'borrowernumber'} = $res->borrowernumber();
  582. $reserve{'biblionumber'} = $res->biblionumber();
  583. $reserve{'patron'} = $res->borrower;
  584. $reserve{'notes'} = $res->reservenotes();
  585. $reserve{'waiting_date'} = $res->waitingdate();
  586. $reserve{'ccode'} = $res->item() ? $res->item()->ccode() : undef;
  587. $reserve{'barcode'} = $res->item() ? $res->item()->barcode() : undef;
  588. $reserve{'priority'} = $res->priority();
  589. $reserve{'lowestPriority'} = $res->lowestPriority();
  590. $reserve{'optionloop'} = \@optionloop;
  591. $reserve{'suspend'} = $res->suspend();
  592. $reserve{'suspend_until'} = $res->suspend_until();
  593. $reserve{'reserve_id'} = $res->reserve_id();
  594. $reserve{itemtype} = $res->itemtype();
  595. $reserve{branchcode} = $res->branchcode();
  596. $reserve{non_priority} = $res->non_priority();
  597. $reserve{object} = $res;
  598. push( @reserveloop, \%reserve );
  599. }
  600. # get the time for the form name...
  601. my $time = time();
  602. $template->param(
  603. time => $time,
  604. fixedRank => $fixedRank,
  605. );
  606. # display infos
  607. $template->param(
  608. optionloop => \@optionloop,
  609. bibitemloop => \@bibitemloop,
  610. itemdata_enumchron => $itemdata_enumchron,
  611. itemdata_ccode => $itemdata_ccode,
  612. date => $date,
  613. biblionumber => $biblionumber,
  614. findborrower => $findborrower,
  615. biblio => $biblio,
  616. holdsview => 1,
  617. C4::Search::enabled_staff_search_views,
  618. );
  619. $biblioloopiter{biblionumber} = $biblionumber;
  620. $biblioloopiter{title} = $biblio->title;
  621. $biblioloopiter{rank} = $fixedRank;
  622. $biblioloopiter{reserveloop} = \@reserveloop;
  623. if (@reserveloop) {
  624. $template->param( reserveloop => \@reserveloop );
  625. }
  626. push @biblioloop, \%biblioloopiter;
  627. }
  628. $template->param( biblioloop => \@biblioloop );
  629. $template->param( biblionumbers => $biblionumbers );
  630. $template->param( exceeded_maxreserves => $exceeded_maxreserves );
  631. $template->param( exceeded_holds_per_record => $exceeded_holds_per_record );
  632. $template->param( subscriptionsnumber => CountSubscriptionFromBiblionumber($biblionumber));
  633. $template->param( pickup => $pickup );
  634. if ( C4::Context->preference( 'AllowHoldDateInFuture' ) ) {
  635. $template->param( reserve_in_future => 1 );
  636. }
  637. $template->param(
  638. SuspendHoldsIntranet => C4::Context->preference('SuspendHoldsIntranet'),
  639. AutoResumeSuspendedHolds => C4::Context->preference('AutoResumeSuspendedHolds'),
  640. );
  641. # printout the page
  642. output_html_with_http_headers $input, $cookie, $template->output;
  643. sub sort_borrowerlist {
  644. my $borrowerslist = shift;
  645. my $ref = [];
  646. push @{$ref}, sort {
  647. uc( $a->{surname} . $a->{firstname} ) cmp
  648. uc( $b->{surname} . $b->{firstname} )
  649. } @{$borrowerslist};
  650. return $ref;
  651. }