Lines 370-388
sub CanBookBeReserved{
Link Here
|
370 |
return { status =>'alreadypossession' }; |
370 |
return { status =>'alreadypossession' }; |
371 |
} |
371 |
} |
372 |
|
372 |
|
|
|
373 |
my @circulation_rules; |
374 |
|
373 |
if ( $params->{itemtype} ) { |
375 |
if ( $params->{itemtype} ) { |
374 |
|
376 |
|
375 |
# biblio-level, item type-contrained |
377 |
# biblio-level, item type-contrained |
376 |
my $patron = Koha::Patrons->find($borrowernumber); |
378 |
my $patron = Koha::Patrons->find($borrowernumber); |
377 |
my $reservesallowed = Koha::CirculationRules->get_effective_rule( |
379 |
my $reservesallowed_rule = Koha::CirculationRules->get_effective_rule( |
378 |
{ |
380 |
{ |
379 |
itemtype => $params->{itemtype}, |
381 |
itemtype => $params->{itemtype}, |
380 |
categorycode => $patron->categorycode, |
382 |
categorycode => $patron->categorycode, |
381 |
branchcode => $pickup_branchcode, |
383 |
branchcode => $pickup_branchcode, |
382 |
rule_name => 'reservesallowed', |
384 |
rule_name => 'reservesallowed', |
383 |
} |
385 |
} |
384 |
)->rule_value; |
386 |
); |
|
|
387 |
push @circulation_rules, $reservesallowed_rule if $reservesallowed_rule; |
385 |
|
388 |
|
|
|
389 |
my $reservesallowed = $reservesallowed_rule ? $reservesallowed_rule->rule_value : undef; |
386 |
$reservesallowed = ( $reservesallowed eq '' ) ? undef : $reservesallowed; |
390 |
$reservesallowed = ( $reservesallowed eq '' ) ? undef : $reservesallowed; |
387 |
|
391 |
|
388 |
my $count = $patron->holds->search( |
392 |
my $count = $patron->holds->search( |
Lines 397-403
sub CanBookBeReserved{
Link Here
|
397 |
} |
401 |
} |
398 |
)->count; |
402 |
)->count; |
399 |
|
403 |
|
400 |
return { status => '' } |
404 |
return { status => '', circulation_rules => \@circulation_rules } |
401 |
if defined $reservesallowed and $reservesallowed < $count + 1; |
405 |
if defined $reservesallowed and $reservesallowed < $count + 1; |
402 |
} |
406 |
} |
403 |
|
407 |
|
Lines 415-450
sub CanBookBeReserved{
Link Here
|
415 |
$items = Koha::Items->search({ biblionumber => $biblionumber}); |
419 |
$items = Koha::Items->search({ biblionumber => $biblionumber}); |
416 |
} |
420 |
} |
417 |
|
421 |
|
418 |
my $canReserve = { status => '' }; |
422 |
my $response = { status => '', item_responses => [] }; |
419 |
my $patron = Koha::Patrons->find( $borrowernumber ); |
423 |
my $patron = Koha::Patrons->find( $borrowernumber ); |
420 |
while ( my $item = $items->next ) { |
424 |
while ( my $item = $items->next ) { |
421 |
$canReserve = CanItemBeReserved( $patron, $item, $pickup_branchcode, $params ); |
425 |
my $itemResponse = CanItemBeReserved( $patron, $item, $pickup_branchcode, $params ); |
422 |
return { status => 'OK' } if $canReserve->{status} eq 'OK'; |
426 |
push @{ $response->{item_responses} }, { %$itemResponse, item => $item }; |
|
|
427 |
|
428 |
if ($itemResponse->{status} eq 'OK') { |
429 |
$response->{status} = 'OK'; |
430 |
|
431 |
return $response; |
432 |
} |
423 |
} |
433 |
} |
424 |
return $canReserve; |
434 |
|
|
|
435 |
return $response; |
425 |
} |
436 |
} |
426 |
|
437 |
|
427 |
=head2 CanItemBeReserved |
438 |
=head2 CanItemBeReserved |
428 |
|
439 |
|
429 |
$canReserve = &CanItemBeReserved($patron, $item, $branchcode, $params) |
440 |
$canReserve = CanItemBeReserved($patron, $item, $branchcode, $params) |
430 |
if ($canReserve->{status} eq 'OK') { #We can reserve this Item! } |
441 |
if ($canReserve->{status} eq 'OK') { #We can reserve this Item! } |
431 |
|
442 |
|
432 |
current params are: |
443 |
Current params are: |
433 |
'ignore_hold_counts' - we use this routine to check if an item can fill a hold - on this case we |
444 |
|
434 |
should not check if there are too many holds as we only care about reservability |
445 |
=over |
435 |
|
446 |
|
436 |
@RETURNS { status => OK }, if the Item can be reserved. |
447 |
=item * C<ignore_hold_counts> |
437 |
{ status => ageRestricted }, if the Item is age restricted for this borrower. |
448 |
|
438 |
{ status => damaged }, if the Item is damaged. |
449 |
we use this routine to check if an item can |
439 |
{ status => cannotReserveFromOtherBranches }, if syspref 'canreservefromotherbranches' is OK. |
450 |
fill a hold - on this case we should not check if there are too many holds as |
440 |
{ status => branchNotInHoldGroup }, if borrower home library is not in hold group, and holds are only allowed from hold groups. |
451 |
we only care about reservability |
441 |
{ status => tooManyReserves, limit => $limit }, if the borrower has exceeded their maximum reserve amount. |
452 |
|
442 |
{ status => notReservable }, if holds on this item are not allowed |
453 |
=back |
443 |
{ status => libraryNotFound }, if given branchcode is not an existing library |
454 |
|
444 |
{ status => libraryNotPickupLocation }, if given branchcode is not configured to be a pickup location |
455 |
Returns a hashref with the following keys: |
445 |
{ status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode |
456 |
|
446 |
{ status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups. |
457 |
=over |
447 |
{ status => recall }, if the borrower has already placed a recall on this item |
458 |
|
|
|
459 |
=item * C<status> |
460 |
|
461 |
A string that can have the following values: |
462 |
|
463 |
=over |
464 |
|
465 |
=item * C<OK> if the item can be reserved |
466 |
|
467 |
=item * C<ageRestricted> if the item is aged restricted for this borrower |
468 |
|
469 |
=item * C<damaged> if the item is damaged |
470 |
|
471 |
=item * C<cannotReserveFromOtherBranches> if syspref |
472 |
'canreservefromotherbranches' is OK |
473 |
|
474 |
=item * C<branchNotInHoldGroup> if borrower home library is not in hold group, |
475 |
and holds are only allowed from hold groups. |
476 |
|
477 |
=item * C<tooManyReserves> if the borrower has exceeded their maximum reserve |
478 |
amount. |
479 |
|
480 |
=item * C<tooManyHoldsForThisRecord> if the borrower has exceeded their maximum |
481 |
reserve amount for this biblio record |
482 |
|
483 |
=item * C<tooManyReservesToday> if the borrower has exceeded their maximum |
484 |
reserve amount for today |
485 |
|
486 |
=item * C<notReservable> if holds on this item are not allowed |
487 |
|
488 |
=item * C<libraryNotFound> if given branchcode is not an existing library |
489 |
|
490 |
=item * C<libraryNotPickupLocation> if given branchcode is not configured to be |
491 |
a pickup location |
492 |
|
493 |
=item * C<cannotBeTransferred> if branch transfer limit applies on given item |
494 |
and branchcode |
495 |
|
496 |
=item * C<pickupNotInHoldGroup> if pickup location is not in hold group, and |
497 |
pickup locations are only allowed from hold groups. |
498 |
|
499 |
=item * C<recall> if the borrower has already placed a recall on this item |
500 |
|
501 |
=back |
502 |
|
503 |
=item * C<limit> |
504 |
|
505 |
Only if C<status> is equal to C<tooManyReserves>, C<tooManyHoldsForThisRecord>, |
506 |
or C<tooManyReservesToday>. |
507 |
|
508 |
It contains the number of reserves allowed. |
509 |
|
510 |
=item * C<circulation_rules> |
511 |
|
512 |
An arrayref containing all circulations rules (L<Koha::CirculationRule>) that |
513 |
have been checked |
514 |
|
515 |
=back |
448 |
|
516 |
|
449 |
=cut |
517 |
=cut |
450 |
|
518 |
|
Lines 467-474
sub CanItemBeReserved {
Link Here
|
467 |
} |
535 |
} |
468 |
|
536 |
|
469 |
my $dbh = C4::Context->dbh; |
537 |
my $dbh = C4::Context->dbh; |
470 |
my $ruleitemtype; # itemtype of the matching issuing rule |
|
|
471 |
my $allowedreserves = 0; # Total number of holds allowed across all records, default to none |
472 |
|
538 |
|
473 |
# We check item branch if IndependentBranches is ON |
539 |
# We check item branch if IndependentBranches is ON |
474 |
# and canreservefromotherbranches is OFF |
540 |
# and canreservefromotherbranches is OFF |
Lines 520-553
sub CanItemBeReserved {
Link Here
|
520 |
$reserves_control_branch = $patron->branchcode; |
586 |
$reserves_control_branch = $patron->branchcode; |
521 |
} |
587 |
} |
522 |
|
588 |
|
523 |
# we retrieve rights |
589 |
my @circulation_rules; |
524 |
if ( |
|
|
525 |
my $reservesallowed = Koha::CirculationRules->get_effective_rule({ |
526 |
itemtype => $item->effective_itemtype, |
527 |
categorycode => $patron->categorycode, |
528 |
branchcode => $reserves_control_branch, |
529 |
rule_name => 'reservesallowed', |
530 |
}) |
531 |
) { |
532 |
$ruleitemtype = $reservesallowed->itemtype; |
533 |
$allowedreserves = $reservesallowed->rule_value // 0; #undefined is 0, blank is unlimited |
534 |
} |
535 |
else { |
536 |
$ruleitemtype = undef; |
537 |
} |
538 |
|
590 |
|
539 |
my $rights = Koha::CirculationRules->get_effective_rules({ |
591 |
my $holds_per_record_rule = Koha::CirculationRules->get_effective_rule({ |
540 |
categorycode => $patron->categorycode, |
592 |
categorycode => $patron->categorycode, |
541 |
itemtype => $item->effective_itemtype, |
593 |
itemtype => $item->effective_itemtype, |
542 |
branchcode => $reserves_control_branch, |
594 |
branchcode => $reserves_control_branch, |
543 |
rules => ['holds_per_record','holds_per_day'] |
595 |
rule_name => 'holds_per_record', |
544 |
}); |
596 |
}); |
545 |
my $holds_per_record = $rights->{holds_per_record} // 1; |
597 |
push @circulation_rules, $holds_per_record_rule if $holds_per_record_rule; |
546 |
my $holds_per_day = $rights->{holds_per_day}; |
598 |
|
|
|
599 |
my $holds_per_record = $holds_per_record_rule ? $holds_per_record_rule->rule_value : 1; |
547 |
|
600 |
|
548 |
if ( defined $holds_per_record && $holds_per_record ne '' ){ |
601 |
if ( defined $holds_per_record && $holds_per_record ne '' ){ |
549 |
if ( $holds_per_record == 0 ) { |
602 |
if ( $holds_per_record == 0 ) { |
550 |
return _cache { status => "noReservesAllowed" }; |
603 |
return _cache { status => "noReservesAllowed", circulation_rules => \@circulation_rules }; |
551 |
} |
604 |
} |
552 |
if ( !$params->{ignore_hold_counts} ) { |
605 |
if ( !$params->{ignore_hold_counts} ) { |
553 |
my $search_params = { |
606 |
my $search_params = { |
Lines 555-578
sub CanItemBeReserved {
Link Here
|
555 |
biblionumber => $item->biblionumber, |
608 |
biblionumber => $item->biblionumber, |
556 |
}; |
609 |
}; |
557 |
my $holds = Koha::Holds->search($search_params); |
610 |
my $holds = Koha::Holds->search($search_params); |
558 |
return _cache { status => "tooManyHoldsForThisRecord", limit => $holds_per_record } if $holds->count() >= $holds_per_record; |
611 |
|
|
|
612 |
if ($holds->count() >= $holds_per_record) { |
613 |
return _cache { |
614 |
status => "tooManyHoldsForThisRecord", |
615 |
limit => $holds_per_record, |
616 |
circulation_rules => \@circulation_rules, |
617 |
}; |
618 |
} |
559 |
} |
619 |
} |
560 |
} |
620 |
} |
561 |
|
621 |
|
|
|
622 |
my $holds_per_day_rule = Koha::CirculationRules->get_effective_rule({ |
623 |
categorycode => $patron->categorycode, |
624 |
itemtype => $item->effective_itemtype, |
625 |
branchcode => $reserves_control_branch, |
626 |
rule_name => 'holds_per_day', |
627 |
}); |
628 |
push @circulation_rules, $holds_per_day_rule if $holds_per_day_rule; |
629 |
|
630 |
my $holds_per_day = $holds_per_day_rule ? $holds_per_day_rule->rule_value : undef; |
562 |
if (!$params->{ignore_hold_counts} && defined $holds_per_day && $holds_per_day ne '') |
631 |
if (!$params->{ignore_hold_counts} && defined $holds_per_day && $holds_per_day ne '') |
563 |
{ |
632 |
{ |
564 |
my $today_holds = Koha::Holds->search({ |
633 |
my $today_holds = Koha::Holds->search({ |
565 |
borrowernumber => $patron->borrowernumber, |
634 |
borrowernumber => $patron->borrowernumber, |
566 |
reservedate => dt_from_string->date |
635 |
reservedate => dt_from_string->date |
567 |
}); |
636 |
}); |
568 |
return _cache { status => 'tooManyReservesToday', limit => $holds_per_day } if $today_holds->count() >= $holds_per_day; |
637 |
|
|
|
638 |
if ($today_holds->count() >= $holds_per_day) { |
639 |
return _cache { |
640 |
status => 'tooManyReservesToday', |
641 |
limit => $holds_per_day, |
642 |
circulation_rules => \@circulation_rules, |
643 |
}; |
644 |
} |
645 |
} |
646 |
|
647 |
my $reservesallowed_rule = Koha::CirculationRules->get_effective_rule({ |
648 |
itemtype => $item->effective_itemtype, |
649 |
categorycode => $patron->categorycode, |
650 |
branchcode => $reserves_control_branch, |
651 |
rule_name => 'reservesallowed', |
652 |
}); |
653 |
push @circulation_rules, $reservesallowed_rule if $reservesallowed_rule; |
654 |
|
655 |
my $ruleitemtype; # itemtype of the matching issuing rule |
656 |
my $allowedreserves = 0; # Total number of holds allowed across all records, default to none |
657 |
if ($reservesallowed_rule) { |
658 |
$ruleitemtype = $reservesallowed_rule->itemtype; |
659 |
$allowedreserves = $reservesallowed_rule->rule_value // 0; #undefined is 0, blank is unlimited |
569 |
} |
660 |
} |
570 |
|
661 |
|
571 |
# we check if it's ok or not |
662 |
# we check if it's ok or not |
572 |
if ( defined $allowedreserves && $allowedreserves ne '' ){ |
663 |
if ( defined $allowedreserves && $allowedreserves ne '' ){ |
573 |
if( $allowedreserves == 0 ){ |
664 |
if ( $allowedreserves == 0 ) { |
574 |
return _cache { status => 'noReservesAllowed' }; |
665 |
return _cache { |
|
|
666 |
status => 'noReservesAllowed', |
667 |
circulation_rules => \@circulation_rules, |
668 |
}; |
575 |
} |
669 |
} |
|
|
670 |
|
576 |
if ( !$params->{ignore_hold_counts} ) { |
671 |
if ( !$params->{ignore_hold_counts} ) { |
577 |
# we retrieve count |
672 |
# we retrieve count |
578 |
my $querycount = q{ |
673 |
my $querycount = q{ |
Lines 616-660
sub CanItemBeReserved {
Link Here
|
616 |
$reservecount = $rowcount->{count}; |
711 |
$reservecount = $rowcount->{count}; |
617 |
} |
712 |
} |
618 |
|
713 |
|
619 |
return _cache { status => 'tooManyReserves', limit => $allowedreserves } if $reservecount >= $allowedreserves; |
714 |
if ($reservecount >= $allowedreserves) { |
|
|
715 |
return _cache { |
716 |
status => 'tooManyReserves', |
717 |
limit => $allowedreserves, |
718 |
circulation_rules => \@circulation_rules, |
719 |
}; |
720 |
} |
620 |
} |
721 |
} |
621 |
} |
722 |
} |
622 |
|
723 |
|
623 |
# Now we need to check hold limits by patron category |
724 |
# Now we need to check hold limits by patron category |
624 |
my $rule = Koha::CirculationRules->get_effective_rule( |
725 |
my $max_holds_rule = Koha::CirculationRules->get_effective_rule( |
625 |
{ |
726 |
{ |
626 |
categorycode => $patron->categorycode, |
727 |
categorycode => $patron->categorycode, |
627 |
branchcode => $reserves_control_branch, |
728 |
branchcode => $reserves_control_branch, |
628 |
rule_name => 'max_holds', |
729 |
rule_name => 'max_holds', |
629 |
} |
730 |
} |
630 |
); |
731 |
); |
631 |
if (!$params->{ignore_hold_counts} && $rule && defined( $rule->rule_value ) && $rule->rule_value ne '' ) { |
732 |
push @circulation_rules, $max_holds_rule if $max_holds_rule; |
|
|
733 |
|
734 |
if (!$params->{ignore_hold_counts} && $max_holds_rule && defined( $max_holds_rule->rule_value ) && $max_holds_rule->rule_value ne '' ) { |
632 |
my $total_holds_count = Koha::Holds->search( |
735 |
my $total_holds_count = Koha::Holds->search( |
633 |
{ |
736 |
{ |
634 |
borrowernumber => $patron->borrowernumber |
737 |
borrowernumber => $patron->borrowernumber |
635 |
} |
738 |
} |
636 |
)->count(); |
739 |
)->count(); |
637 |
|
740 |
|
638 |
return _cache { status => 'tooManyReserves', limit => $rule->rule_value} if $total_holds_count >= $rule->rule_value; |
741 |
if ($total_holds_count >= $max_holds_rule->rule_value) { |
|
|
742 |
return _cache { |
743 |
status => 'tooManyReserves', |
744 |
limit => $max_holds_rule->rule_value, |
745 |
circulation_rules => \@circulation_rules, |
746 |
}; |
747 |
} |
639 |
} |
748 |
} |
640 |
|
749 |
|
|
|
750 |
my $holdallowed_rule = Koha::CirculationRules->get_effective_rule({ |
751 |
branchcode => $reserves_control_branch, |
752 |
itemtype => $item->effective_itemtype, |
753 |
rule_name => 'holdallowed', |
754 |
}); |
755 |
push @circulation_rules, $holdallowed_rule if $holdallowed_rule; |
756 |
|
641 |
my $branchitemrule = |
757 |
my $branchitemrule = |
642 |
C4::Circulation::GetBranchItemRule( $reserves_control_branch, $item->effective_itemtype ); |
758 |
C4::Circulation::GetBranchItemRule( $reserves_control_branch, $item->effective_itemtype ); |
643 |
|
759 |
|
644 |
if ( $branchitemrule->{holdallowed} eq 'not_allowed' ) { |
760 |
if ( $branchitemrule->{holdallowed} eq 'not_allowed' ) { |
645 |
return _cache { status => 'notReservable' }; |
761 |
return _cache { status => 'notReservable', circulation_rules => \@circulation_rules }; |
646 |
} |
762 |
} |
647 |
|
763 |
|
648 |
if ( $branchitemrule->{holdallowed} eq 'from_home_library' |
764 |
if ( $branchitemrule->{holdallowed} eq 'from_home_library' |
649 |
&& $patron->branchcode ne $item->homebranch ) |
765 |
&& $patron->branchcode ne $item->homebranch ) |
650 |
{ |
766 |
{ |
651 |
return _cache { status => 'cannotReserveFromOtherBranches' }; |
767 |
return _cache { status => 'cannotReserveFromOtherBranches', circulation_rules => \@circulation_rules }; |
652 |
} |
768 |
} |
653 |
|
769 |
|
654 |
my $item_library = Koha::Libraries->find( {branchcode => $item->homebranch} ); |
770 |
my $item_library = Koha::Libraries->find( {branchcode => $item->homebranch} ); |
655 |
if ( $branchitemrule->{holdallowed} eq 'from_local_hold_group') { |
771 |
if ( $branchitemrule->{holdallowed} eq 'from_local_hold_group') { |
656 |
if($patron->branchcode ne $item->homebranch && !$item_library->validate_hold_sibling( {branchcode => $patron->branchcode} )) { |
772 |
if($patron->branchcode ne $item->homebranch && !$item_library->validate_hold_sibling( {branchcode => $patron->branchcode} )) { |
657 |
return _cache { status => 'branchNotInHoldGroup' }; |
773 |
return _cache { status => 'branchNotInHoldGroup', circulation_rules => \@circulation_rules }; |
658 |
} |
774 |
} |
659 |
} |
775 |
} |
660 |
|
776 |
|
Lines 672-686
sub CanItemBeReserved {
Link Here
|
672 |
unless ($item->can_be_transferred({ to => $destination })) { |
788 |
unless ($item->can_be_transferred({ to => $destination })) { |
673 |
return _cache { status => 'cannotBeTransferred' }; |
789 |
return _cache { status => 'cannotBeTransferred' }; |
674 |
} |
790 |
} |
|
|
791 |
|
792 |
my $hold_fulfillment_policy_rule = Koha::CirculationRules->get_effective_rule({ |
793 |
branchcode => $reserves_control_branch, |
794 |
itemtype => $item->effective_itemtype, |
795 |
rule_name => 'hold_fulfillment_policy', |
796 |
}); |
797 |
push @circulation_rules, $hold_fulfillment_policy_rule if $hold_fulfillment_policy_rule; |
798 |
|
675 |
if ($branchitemrule->{hold_fulfillment_policy} eq 'holdgroup' && !$item_library->validate_hold_sibling( {branchcode => $pickup_branchcode} )) { |
799 |
if ($branchitemrule->{hold_fulfillment_policy} eq 'holdgroup' && !$item_library->validate_hold_sibling( {branchcode => $pickup_branchcode} )) { |
676 |
return _cache { status => 'pickupNotInHoldGroup' }; |
800 |
return _cache { status => 'pickupNotInHoldGroup', circulation_rules => \@circulation_rules }; |
677 |
} |
801 |
} |
678 |
if ($branchitemrule->{hold_fulfillment_policy} eq 'patrongroup' && !Koha::Libraries->find({branchcode => $patron->branchcode})->validate_hold_sibling({branchcode => $pickup_branchcode})) { |
802 |
if ($branchitemrule->{hold_fulfillment_policy} eq 'patrongroup' && !Koha::Libraries->find({branchcode => $patron->branchcode})->validate_hold_sibling({branchcode => $pickup_branchcode})) { |
679 |
return _cache { status => 'pickupNotInHoldGroup' }; |
803 |
return _cache { status => 'pickupNotInHoldGroup', circulation_rules => \@circulation_rules }; |
680 |
} |
804 |
} |
681 |
} |
805 |
} |
682 |
|
806 |
|
683 |
return _cache { status => 'OK' }; |
807 |
return _cache { status => 'OK', circulation_rules => \@circulation_rules }; |
684 |
} |
808 |
} |
685 |
|
809 |
|
686 |
=head2 ChargeReserveFee |
810 |
=head2 ChargeReserveFee |