Lines 341-359
sub CanBookBeReserved{
Link Here
|
341 |
return { status =>'alreadypossession' }; |
341 |
return { status =>'alreadypossession' }; |
342 |
} |
342 |
} |
343 |
|
343 |
|
|
|
344 |
my @circulation_rules; |
345 |
|
344 |
if ( $params->{itemtype} ) { |
346 |
if ( $params->{itemtype} ) { |
345 |
|
347 |
|
346 |
# biblio-level, item type-contrained |
348 |
# biblio-level, item type-contrained |
347 |
my $patron = Koha::Patrons->find($borrowernumber); |
349 |
my $patron = Koha::Patrons->find($borrowernumber); |
348 |
my $reservesallowed = Koha::CirculationRules->get_effective_rule( |
350 |
my $reservesallowed_rule = Koha::CirculationRules->get_effective_rule( |
349 |
{ |
351 |
{ |
350 |
itemtype => $params->{itemtype}, |
352 |
itemtype => $params->{itemtype}, |
351 |
categorycode => $patron->categorycode, |
353 |
categorycode => $patron->categorycode, |
352 |
branchcode => $pickup_branchcode, |
354 |
branchcode => $pickup_branchcode, |
353 |
rule_name => 'reservesallowed', |
355 |
rule_name => 'reservesallowed', |
354 |
} |
356 |
} |
355 |
)->rule_value; |
357 |
); |
|
|
358 |
push @circulation_rules, $reservesallowed_rule if $reservesallowed_rule; |
356 |
|
359 |
|
|
|
360 |
my $reservesallowed = $reservesallowed_rule ? $reservesallowed_rule->rule_value : undef; |
357 |
$reservesallowed = ( $reservesallowed eq '' ) ? undef : $reservesallowed; |
361 |
$reservesallowed = ( $reservesallowed eq '' ) ? undef : $reservesallowed; |
358 |
|
362 |
|
359 |
my $count = $patron->holds->search( |
363 |
my $count = $patron->holds->search( |
Lines 368-374
sub CanBookBeReserved{
Link Here
|
368 |
} |
372 |
} |
369 |
)->count; |
373 |
)->count; |
370 |
|
374 |
|
371 |
return { status => '' } |
375 |
return { status => '', circulation_rules => \@circulation_rules } |
372 |
if defined $reservesallowed and $reservesallowed < $count + 1; |
376 |
if defined $reservesallowed and $reservesallowed < $count + 1; |
373 |
} |
377 |
} |
374 |
|
378 |
|
Lines 386-421
sub CanBookBeReserved{
Link Here
|
386 |
$items = Koha::Items->search({ biblionumber => $biblionumber}); |
390 |
$items = Koha::Items->search({ biblionumber => $biblionumber}); |
387 |
} |
391 |
} |
388 |
|
392 |
|
389 |
my $canReserve = { status => '' }; |
393 |
my $response = { status => '', item_responses => [] }; |
390 |
my $patron = Koha::Patrons->find( $borrowernumber ); |
394 |
my $patron = Koha::Patrons->find( $borrowernumber ); |
391 |
while ( my $item = $items->next ) { |
395 |
while ( my $item = $items->next ) { |
392 |
$canReserve = CanItemBeReserved( $patron, $item, $pickup_branchcode, $params ); |
396 |
my $itemResponse = CanItemBeReserved( $patron, $item, $pickup_branchcode, $params ); |
393 |
return { status => 'OK' } if $canReserve->{status} eq 'OK'; |
397 |
push @{ $response->{item_responses} }, { %$itemResponse, item => $item }; |
|
|
398 |
|
399 |
if ($itemResponse->{status} eq 'OK') { |
400 |
$response->{status} = 'OK'; |
401 |
|
402 |
return $response; |
403 |
} |
394 |
} |
404 |
} |
395 |
return $canReserve; |
405 |
|
|
|
406 |
return $response; |
396 |
} |
407 |
} |
397 |
|
408 |
|
398 |
=head2 CanItemBeReserved |
409 |
=head2 CanItemBeReserved |
399 |
|
410 |
|
400 |
$canReserve = &CanItemBeReserved($patron, $item, $branchcode, $params) |
411 |
$canReserve = CanItemBeReserved($patron, $item, $branchcode, $params) |
401 |
if ($canReserve->{status} eq 'OK') { #We can reserve this Item! } |
412 |
if ($canReserve->{status} eq 'OK') { #We can reserve this Item! } |
402 |
|
413 |
|
403 |
current params are: |
414 |
Current params are: |
404 |
'ignore_hold_counts' - we use this routine to check if an item can fill a hold - on this case we |
415 |
|
405 |
should not check if there are too many holds as we only care about reservability |
416 |
=over |
406 |
|
417 |
|
407 |
@RETURNS { status => OK }, if the Item can be reserved. |
418 |
=item * C<ignore_hold_counts> |
408 |
{ status => ageRestricted }, if the Item is age restricted for this borrower. |
419 |
|
409 |
{ status => damaged }, if the Item is damaged. |
420 |
we use this routine to check if an item can |
410 |
{ status => cannotReserveFromOtherBranches }, if syspref 'canreservefromotherbranches' is OK. |
421 |
fill a hold - on this case we should not check if there are too many holds as |
411 |
{ status => branchNotInHoldGroup }, if borrower home library is not in hold group, and holds are only allowed from hold groups. |
422 |
we only care about reservability |
412 |
{ status => tooManyReserves, limit => $limit }, if the borrower has exceeded their maximum reserve amount. |
423 |
|
413 |
{ status => notReservable }, if holds on this item are not allowed |
424 |
=back |
414 |
{ status => libraryNotFound }, if given branchcode is not an existing library |
425 |
|
415 |
{ status => libraryNotPickupLocation }, if given branchcode is not configured to be a pickup location |
426 |
Returns a hashref with the following keys: |
416 |
{ status => cannotBeTransferred }, if branch transfer limit applies on given item and branchcode |
427 |
|
417 |
{ status => pickupNotInHoldGroup }, pickup location is not in hold group, and pickup locations are only allowed from hold groups. |
428 |
=over |
418 |
{ status => recall }, if the borrower has already placed a recall on this item |
429 |
|
|
|
430 |
=item * C<status> |
431 |
|
432 |
A string that can have the following values: |
433 |
|
434 |
=over |
435 |
|
436 |
=item * C<OK> if the item can be reserved |
437 |
|
438 |
=item * C<ageRestricted> if the item is aged restricted for this borrower |
439 |
|
440 |
=item * C<damaged> if the item is damaged |
441 |
|
442 |
=item * C<cannotReserveFromOtherBranches> if syspref |
443 |
'canreservefromotherbranches' is OK |
444 |
|
445 |
=item * C<branchNotInHoldGroup> if borrower home library is not in hold group, |
446 |
and holds are only allowed from hold groups. |
447 |
|
448 |
=item * C<tooManyReserves> if the borrower has exceeded their maximum reserve |
449 |
amount. |
450 |
|
451 |
=item * C<tooManyHoldsForThisRecord> if the borrower has exceeded their maximum |
452 |
reserve amount for this biblio record |
453 |
|
454 |
=item * C<tooManyReservesToday> if the borrower has exceeded their maximum |
455 |
reserve amount for today |
456 |
|
457 |
=item * C<notReservable> if holds on this item are not allowed |
458 |
|
459 |
=item * C<libraryNotFound> if given branchcode is not an existing library |
460 |
|
461 |
=item * C<libraryNotPickupLocation> if given branchcode is not configured to be |
462 |
a pickup location |
463 |
|
464 |
=item * C<cannotBeTransferred> if branch transfer limit applies on given item |
465 |
and branchcode |
466 |
|
467 |
=item * C<pickupNotInHoldGroup> if pickup location is not in hold group, and |
468 |
pickup locations are only allowed from hold groups. |
469 |
|
470 |
=item * C<recall> if the borrower has already placed a recall on this item |
471 |
|
472 |
=back |
473 |
|
474 |
=item * C<limit> |
475 |
|
476 |
Only if C<status> is equal to C<tooManyReserves>, C<tooManyHoldsForThisRecord>, |
477 |
or C<tooManyReservesToday>. |
478 |
|
479 |
It contains the number of reserves allowed. |
480 |
|
481 |
=item * C<circulation_rules> |
482 |
|
483 |
An arrayref containing all circulations rules (L<Koha::CirculationRule>) that |
484 |
have been checked |
485 |
|
486 |
=back |
419 |
|
487 |
|
420 |
=cut |
488 |
=cut |
421 |
|
489 |
|
Lines 423-430
sub CanItemBeReserved {
Link Here
|
423 |
my ( $patron, $item, $pickup_branchcode, $params ) = @_; |
491 |
my ( $patron, $item, $pickup_branchcode, $params ) = @_; |
424 |
|
492 |
|
425 |
my $dbh = C4::Context->dbh; |
493 |
my $dbh = C4::Context->dbh; |
426 |
my $ruleitemtype; # itemtype of the matching issuing rule |
|
|
427 |
my $allowedreserves = 0; # Total number of holds allowed across all records, default to none |
428 |
|
494 |
|
429 |
# We check item branch if IndependentBranches is ON |
495 |
# We check item branch if IndependentBranches is ON |
430 |
# and canreservefromotherbranches is OFF |
496 |
# and canreservefromotherbranches is OFF |
Lines 481-514
sub CanItemBeReserved {
Link Here
|
481 |
$reserves_control_branch = $borrower->{branchcode}; |
547 |
$reserves_control_branch = $borrower->{branchcode}; |
482 |
} |
548 |
} |
483 |
|
549 |
|
484 |
# we retrieve rights |
550 |
my @circulation_rules; |
485 |
if ( |
|
|
486 |
my $reservesallowed = Koha::CirculationRules->get_effective_rule({ |
487 |
itemtype => $item->effective_itemtype, |
488 |
categorycode => $borrower->{categorycode}, |
489 |
branchcode => $reserves_control_branch, |
490 |
rule_name => 'reservesallowed', |
491 |
}) |
492 |
) { |
493 |
$ruleitemtype = $reservesallowed->itemtype; |
494 |
$allowedreserves = $reservesallowed->rule_value // 0; #undefined is 0, blank is unlimited |
495 |
} |
496 |
else { |
497 |
$ruleitemtype = undef; |
498 |
} |
499 |
|
551 |
|
500 |
my $rights = Koha::CirculationRules->get_effective_rules({ |
552 |
my $holds_per_record_rule = Koha::CirculationRules->get_effective_rule({ |
501 |
categorycode => $borrower->{'categorycode'}, |
553 |
categorycode => $borrower->{categorycode}, |
502 |
itemtype => $item->effective_itemtype, |
554 |
itemtype => $item->effective_itemtype, |
503 |
branchcode => $reserves_control_branch, |
555 |
branchcode => $reserves_control_branch, |
504 |
rules => ['holds_per_record','holds_per_day'] |
556 |
rule_name => 'holds_per_record', |
505 |
}); |
557 |
}); |
506 |
my $holds_per_record = $rights->{holds_per_record} // 1; |
558 |
push @circulation_rules, $holds_per_record_rule if $holds_per_record_rule; |
507 |
my $holds_per_day = $rights->{holds_per_day}; |
559 |
|
|
|
560 |
my $holds_per_record = $holds_per_record_rule ? $holds_per_record_rule->rule_value : 1; |
508 |
|
561 |
|
509 |
if ( defined $holds_per_record && $holds_per_record ne '' ){ |
562 |
if ( defined $holds_per_record && $holds_per_record ne '' ){ |
510 |
if ( $holds_per_record == 0 ) { |
563 |
if ( $holds_per_record == 0 ) { |
511 |
return { status => "noReservesAllowed" }; |
564 |
return { status => "noReservesAllowed", circulation_rules => \@circulation_rules }; |
512 |
} |
565 |
} |
513 |
if ( !$params->{ignore_hold_counts} ) { |
566 |
if ( !$params->{ignore_hold_counts} ) { |
514 |
my $search_params = { |
567 |
my $search_params = { |
Lines 516-539
sub CanItemBeReserved {
Link Here
|
516 |
biblionumber => $item->biblionumber, |
569 |
biblionumber => $item->biblionumber, |
517 |
}; |
570 |
}; |
518 |
my $holds = Koha::Holds->search($search_params); |
571 |
my $holds = Koha::Holds->search($search_params); |
519 |
return { status => "tooManyHoldsForThisRecord", limit => $holds_per_record } if $holds->count() >= $holds_per_record; |
572 |
|
|
|
573 |
if ($holds->count() >= $holds_per_record) { |
574 |
return { |
575 |
status => "tooManyHoldsForThisRecord", |
576 |
limit => $holds_per_record, |
577 |
circulation_rules => \@circulation_rules, |
578 |
}; |
579 |
} |
520 |
} |
580 |
} |
521 |
} |
581 |
} |
522 |
|
582 |
|
|
|
583 |
my $holds_per_day_rule = Koha::CirculationRules->get_effective_rule({ |
584 |
categorycode => $borrower->{'categorycode'}, |
585 |
itemtype => $item->effective_itemtype, |
586 |
branchcode => $reserves_control_branch, |
587 |
rule_name => 'holds_per_day', |
588 |
}); |
589 |
push @circulation_rules, $holds_per_day_rule if $holds_per_day_rule; |
590 |
|
591 |
my $holds_per_day = $holds_per_day_rule ? $holds_per_day_rule->rule_value : undef; |
523 |
if (!$params->{ignore_hold_counts} && defined $holds_per_day && $holds_per_day ne '') |
592 |
if (!$params->{ignore_hold_counts} && defined $holds_per_day && $holds_per_day ne '') |
524 |
{ |
593 |
{ |
525 |
my $today_holds = Koha::Holds->search({ |
594 |
my $today_holds = Koha::Holds->search({ |
526 |
borrowernumber => $patron->borrowernumber, |
595 |
borrowernumber => $patron->borrowernumber, |
527 |
reservedate => dt_from_string->date |
596 |
reservedate => dt_from_string->date |
528 |
}); |
597 |
}); |
529 |
return { status => 'tooManyReservesToday', limit => $holds_per_day } if $today_holds->count() >= $holds_per_day; |
598 |
|
|
|
599 |
if ($today_holds->count() >= $holds_per_day) { |
600 |
return { |
601 |
status => 'tooManyReservesToday', |
602 |
limit => $holds_per_day, |
603 |
circulation_rules => \@circulation_rules, |
604 |
}; |
605 |
} |
606 |
} |
607 |
|
608 |
my $reservesallowed_rule = Koha::CirculationRules->get_effective_rule({ |
609 |
itemtype => $item->effective_itemtype, |
610 |
categorycode => $borrower->{categorycode}, |
611 |
branchcode => $reserves_control_branch, |
612 |
rule_name => 'reservesallowed', |
613 |
}); |
614 |
push @circulation_rules, $reservesallowed_rule if $reservesallowed_rule; |
615 |
|
616 |
my $ruleitemtype; # itemtype of the matching issuing rule |
617 |
my $allowedreserves = 0; # Total number of holds allowed across all records, default to none |
618 |
if ($reservesallowed_rule) { |
619 |
$ruleitemtype = $reservesallowed_rule->itemtype; |
620 |
$allowedreserves = $reservesallowed_rule->rule_value // 0; #undefined is 0, blank is unlimited |
530 |
} |
621 |
} |
531 |
|
622 |
|
532 |
# we check if it's ok or not |
623 |
# we check if it's ok or not |
533 |
if ( defined $allowedreserves && $allowedreserves ne '' ){ |
624 |
if ( defined $allowedreserves && $allowedreserves ne '' ){ |
534 |
if( $allowedreserves == 0 ){ |
625 |
if ( $allowedreserves == 0 ) { |
535 |
return { status => 'noReservesAllowed' }; |
626 |
return { |
|
|
627 |
status => 'noReservesAllowed', |
628 |
circulation_rules => \@circulation_rules, |
629 |
}; |
536 |
} |
630 |
} |
|
|
631 |
|
537 |
if ( !$params->{ignore_hold_counts} ) { |
632 |
if ( !$params->{ignore_hold_counts} ) { |
538 |
# we retrieve count |
633 |
# we retrieve count |
539 |
my $querycount = q{ |
634 |
my $querycount = q{ |
Lines 577-621
sub CanItemBeReserved {
Link Here
|
577 |
$reservecount = $rowcount->{count}; |
672 |
$reservecount = $rowcount->{count}; |
578 |
} |
673 |
} |
579 |
|
674 |
|
580 |
return { status => 'tooManyReserves', limit => $allowedreserves } if $reservecount >= $allowedreserves; |
675 |
if ($reservecount >= $allowedreserves) { |
|
|
676 |
return { |
677 |
status => 'tooManyReserves', |
678 |
limit => $allowedreserves, |
679 |
circulation_rules => \@circulation_rules, |
680 |
}; |
681 |
} |
581 |
} |
682 |
} |
582 |
} |
683 |
} |
583 |
|
684 |
|
584 |
# Now we need to check hold limits by patron category |
685 |
# Now we need to check hold limits by patron category |
585 |
my $rule = Koha::CirculationRules->get_effective_rule( |
686 |
my $max_holds_rule = Koha::CirculationRules->get_effective_rule( |
586 |
{ |
687 |
{ |
587 |
categorycode => $patron->categorycode, |
688 |
categorycode => $patron->categorycode, |
588 |
branchcode => $reserves_control_branch, |
689 |
branchcode => $reserves_control_branch, |
589 |
rule_name => 'max_holds', |
690 |
rule_name => 'max_holds', |
590 |
} |
691 |
} |
591 |
); |
692 |
); |
592 |
if (!$params->{ignore_hold_counts} && $rule && defined( $rule->rule_value ) && $rule->rule_value ne '' ) { |
693 |
push @circulation_rules, $max_holds_rule if $max_holds_rule; |
|
|
694 |
|
695 |
if (!$params->{ignore_hold_counts} && $max_holds_rule && defined( $max_holds_rule->rule_value ) && $max_holds_rule->rule_value ne '' ) { |
593 |
my $total_holds_count = Koha::Holds->search( |
696 |
my $total_holds_count = Koha::Holds->search( |
594 |
{ |
697 |
{ |
595 |
borrowernumber => $patron->borrowernumber |
698 |
borrowernumber => $patron->borrowernumber |
596 |
} |
699 |
} |
597 |
)->count(); |
700 |
)->count(); |
598 |
|
701 |
|
599 |
return { status => 'tooManyReserves', limit => $rule->rule_value} if $total_holds_count >= $rule->rule_value; |
702 |
if ($total_holds_count >= $max_holds_rule->rule_value) { |
|
|
703 |
return { |
704 |
status => 'tooManyReserves', |
705 |
limit => $max_holds_rule->rule_value, |
706 |
circulation_rules => \@circulation_rules, |
707 |
}; |
708 |
} |
600 |
} |
709 |
} |
601 |
|
710 |
|
|
|
711 |
my $holdallowed_rule = Koha::CirculationRules->get_effective_rule({ |
712 |
branchcode => $reserves_control_branch, |
713 |
itemtype => $item->effective_itemtype, |
714 |
rule_name => 'holdallowed', |
715 |
}); |
716 |
push @circulation_rules, $holdallowed_rule if $holdallowed_rule; |
717 |
|
602 |
my $branchitemrule = |
718 |
my $branchitemrule = |
603 |
C4::Circulation::GetBranchItemRule( $reserves_control_branch, $item->effective_itemtype ); |
719 |
C4::Circulation::GetBranchItemRule( $reserves_control_branch, $item->effective_itemtype ); |
604 |
|
720 |
|
605 |
if ( $branchitemrule->{holdallowed} eq 'not_allowed' ) { |
721 |
if ( $branchitemrule->{holdallowed} eq 'not_allowed' ) { |
606 |
return { status => 'notReservable' }; |
722 |
return { status => 'notReservable', circulation_rules => \@circulation_rules }; |
607 |
} |
723 |
} |
608 |
|
724 |
|
609 |
if ( $branchitemrule->{holdallowed} eq 'from_home_library' |
725 |
if ( $branchitemrule->{holdallowed} eq 'from_home_library' |
610 |
&& $borrower->{branchcode} ne $item->homebranch ) |
726 |
&& $borrower->{branchcode} ne $item->homebranch ) |
611 |
{ |
727 |
{ |
612 |
return { status => 'cannotReserveFromOtherBranches' }; |
728 |
return { status => 'cannotReserveFromOtherBranches', circulation_rules => \@circulation_rules }; |
613 |
} |
729 |
} |
614 |
|
730 |
|
615 |
my $item_library = Koha::Libraries->find( {branchcode => $item->homebranch} ); |
731 |
my $item_library = Koha::Libraries->find( {branchcode => $item->homebranch} ); |
616 |
if ( $branchitemrule->{holdallowed} eq 'from_local_hold_group') { |
732 |
if ( $branchitemrule->{holdallowed} eq 'from_local_hold_group') { |
617 |
if($patron->branchcode ne $item->homebranch && !$item_library->validate_hold_sibling( {branchcode => $patron->branchcode} )) { |
733 |
if($patron->branchcode ne $item->homebranch && !$item_library->validate_hold_sibling( {branchcode => $patron->branchcode} )) { |
618 |
return { status => 'branchNotInHoldGroup' }; |
734 |
return { status => 'branchNotInHoldGroup', circulation_rules => \@circulation_rules }; |
619 |
} |
735 |
} |
620 |
} |
736 |
} |
621 |
|
737 |
|
Lines 633-647
sub CanItemBeReserved {
Link Here
|
633 |
unless ($item->can_be_transferred({ to => $destination })) { |
749 |
unless ($item->can_be_transferred({ to => $destination })) { |
634 |
return { status => 'cannotBeTransferred' }; |
750 |
return { status => 'cannotBeTransferred' }; |
635 |
} |
751 |
} |
|
|
752 |
|
753 |
my $hold_fulfillment_policy_rule = Koha::CirculationRules->get_effective_rule({ |
754 |
branchcode => $reserves_control_branch, |
755 |
itemtype => $item->effective_itemtype, |
756 |
rule_name => 'hold_fulfillment_policy', |
757 |
}); |
758 |
push @circulation_rules, $hold_fulfillment_policy_rule if $hold_fulfillment_policy_rule; |
759 |
|
636 |
if ($branchitemrule->{hold_fulfillment_policy} eq 'holdgroup' && !$item_library->validate_hold_sibling( {branchcode => $pickup_branchcode} )) { |
760 |
if ($branchitemrule->{hold_fulfillment_policy} eq 'holdgroup' && !$item_library->validate_hold_sibling( {branchcode => $pickup_branchcode} )) { |
637 |
return { status => 'pickupNotInHoldGroup' }; |
761 |
return { status => 'pickupNotInHoldGroup', circulation_rules => \@circulation_rules }; |
638 |
} |
762 |
} |
639 |
if ($branchitemrule->{hold_fulfillment_policy} eq 'patrongroup' && !Koha::Libraries->find({branchcode => $borrower->{branchcode}})->validate_hold_sibling({branchcode => $pickup_branchcode})) { |
763 |
if ($branchitemrule->{hold_fulfillment_policy} eq 'patrongroup' && !Koha::Libraries->find({branchcode => $borrower->{branchcode}})->validate_hold_sibling({branchcode => $pickup_branchcode})) { |
640 |
return { status => 'pickupNotInHoldGroup' }; |
764 |
return { status => 'pickupNotInHoldGroup', circulation_rules => \@circulation_rules }; |
641 |
} |
765 |
} |
642 |
} |
766 |
} |
643 |
|
767 |
|
644 |
return { status => 'OK' }; |
768 |
return { status => 'OK', circulation_rules => \@circulation_rules }; |
645 |
} |
769 |
} |
646 |
|
770 |
|
647 |
=head2 CanReserveBeCanceledFromOpac |
771 |
=head2 CanReserveBeCanceledFromOpac |