X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=C4%2FReserves.pm;h=36175495821272f07b0c154ab8f34ac906ad02a9;hb=26a779eded6fe24abe0be904da64e0186c3d91ec;hp=e7fc3c1eb5a3ab7a666ffabe10d179e16194ab34;hpb=2c1f7d22810be37c4220566a675b3620d1ff4002;p=koha.git diff --git a/C4/Reserves.pm b/C4/Reserves.pm index e7fc3c1eb5..3617549582 100644 --- a/C4/Reserves.pm +++ b/C4/Reserves.pm @@ -48,6 +48,8 @@ use Koha::IssuingRules; use Koha::Items; use Koha::ItemTypes; use Koha::Patrons; +use Koha::CirculationRules; +use Koha::Account::Lines; use List::MoreUtils qw( firstidx any ); use Carp; @@ -265,7 +267,7 @@ sub AddReserve { =head2 CanBookBeReserved - $canReserve = &CanBookBeReserved($borrowernumber, $biblionumber) + $canReserve = &CanBookBeReserved($borrowernumber, $biblionumber, $branchcode) if ($canReserve eq 'OK') { #We can reserve this Item! } See CanItemBeReserved() for possible return values. @@ -273,64 +275,68 @@ See CanItemBeReserved() for possible return values. =cut sub CanBookBeReserved{ - my ($borrowernumber, $biblionumber) = @_; + my ($borrowernumber, $biblionumber, $pickup_branchcode) = @_; - my $items = GetItemnumbersForBiblio($biblionumber); + my @itemnumbers = Koha::Items->search({ biblionumber => $biblionumber})->get_column("itemnumber"); #get items linked via host records my @hostitems = get_hostitemnumbers_of($biblionumber); if (@hostitems){ - push (@$items,@hostitems); + push (@itemnumbers, @hostitems); } - my $canReserve; - foreach my $item (@$items) { - $canReserve = CanItemBeReserved( $borrowernumber, $item ); - return 'OK' if $canReserve eq 'OK'; + my $canReserve = { status => '' }; + foreach my $itemnumber (@itemnumbers) { + $canReserve = CanItemBeReserved( $borrowernumber, $itemnumber, $pickup_branchcode ); + return { status => 'OK' } if $canReserve->{status} eq 'OK'; } return $canReserve; } =head2 CanItemBeReserved - $canReserve = &CanItemBeReserved($borrowernumber, $itemnumber) - if ($canReserve eq 'OK') { #We can reserve this Item! } + $canReserve = &CanItemBeReserved($borrowernumber, $itemnumber, $branchcode) + if ($canReserve->{status} eq 'OK') { #We can reserve this Item! } -@RETURNS OK, if the Item can be reserved. - ageRestricted, if the Item is age restricted for this borrower. - damaged, if the Item is damaged. - cannotReserveFromOtherBranches, if syspref 'canreservefromotherbranches' is OK. - tooManyReserves, if the borrower has exceeded his maximum reserve amount. - notReservable, if holds on this item are not allowed +@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 => 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 =cut sub CanItemBeReserved { - my ( $borrowernumber, $itemnumber ) = @_; + my ( $borrowernumber, $itemnumber, $pickup_branchcode ) = @_; my $dbh = C4::Context->dbh; my $ruleitemtype; # itemtype of the matching issuing rule my $allowedreserves = 0; # Total number of holds allowed across all records my $holds_per_record = 1; # Total number of holds allowed for this one given record + my $holds_per_day; # Default to unlimited # we retrieve borrowers and items informations # # item->{itype} will come for biblioitems if necessery - my $item = GetItem($itemnumber); - my $biblio = Koha::Biblios->find( $item->{biblionumber} ); + my $item = Koha::Items->find($itemnumber); + my $biblio = $item->biblio; my $patron = Koha::Patrons->find( $borrowernumber ); my $borrower = $patron->unblessed; # If an item is damaged and we don't allow holds on damaged items, we can stop right here - return 'damaged' - if ( $item->{damaged} + return { status =>'damaged' } + if ( $item->damaged && !C4::Context->preference('AllowHoldsOnDamagedItems') ); # Check for the age restriction my ( $ageRestriction, $daysToAgeRestriction ) = C4::Circulation::GetAgeRestriction( $biblio->biblioitem->agerestriction, $borrower ); - return 'ageRestricted' if $daysToAgeRestriction && $daysToAgeRestriction > 0; + return { status => 'ageRestricted' } if $daysToAgeRestriction && $daysToAgeRestriction > 0; # Check that the patron doesn't have an item level hold on this item already - return 'itemAlreadyOnHold' + return { status =>'itemAlreadyOnHold' } if Koha::Holds->search( { borrowernumber => $borrowernumber, itemnumber => $itemnumber } )->count(); my $controlbranch = C4::Context->preference('ReservesControlBranch'); @@ -349,7 +355,7 @@ sub CanItemBeReserved { if ( $controlbranch eq "ItemHomeLibrary" ) { $branchfield = "items.homebranch"; - $branchcode = $item->{homebranch}; + $branchcode = $item->homebranch; } elsif ( $controlbranch eq "PatronLibrary" ) { $branchfield = "borrowers.branchcode"; @@ -357,16 +363,16 @@ sub CanItemBeReserved { } # we retrieve rights - if ( my $rights = GetHoldRule( $borrower->{'categorycode'}, $item->{'itype'}, $branchcode ) ) { + if ( my $rights = GetHoldRule( $borrower->{'categorycode'}, $item->effective_itemtype, $branchcode ) ) { $ruleitemtype = $rights->{itemtype}; $allowedreserves = $rights->{reservesallowed}; $holds_per_record = $rights->{holds_per_record}; + $holds_per_day = $rights->{holds_per_day}; } else { $ruleitemtype = '*'; } - $item = Koha::Items->find( $itemnumber ); my $holds = Koha::Holds->search( { borrowernumber => $borrowernumber, @@ -375,7 +381,19 @@ sub CanItemBeReserved { } ); if ( $holds->count() >= $holds_per_record ) { - return "tooManyHoldsForThisRecord"; + return { status => "tooManyHoldsForThisRecord", limit => $holds_per_record }; + } + + my $today_holds = Koha::Holds->search({ + borrowernumber => $borrowernumber, + reservedate => dt_from_string->date + }); + + if ( defined $holds_per_day && + ( ( $holds_per_day > 0 && $today_holds->count() >= $holds_per_day ) + or ( $holds_per_day == 0 ) ) + ) { + return { status => 'tooManyReservesToday', limit => $holds_per_day }; } # we retrieve count @@ -406,22 +424,40 @@ sub CanItemBeReserved { # we check if it's ok or not if ( $reservecount >= $allowedreserves ) { - return 'tooManyReserves'; + return { status => 'tooManyReserves', limit => $allowedreserves }; + } + + # Now we need to check hold limits by patron category + my $rule = Koha::CirculationRules->get_effective_rule( + { + categorycode => $borrower->{categorycode}, + branchcode => $branchcode, + rule_name => 'max_holds', + } + ); + if ( $rule && defined( $rule->rule_value ) && $rule->rule_value ne '' ) { + my $total_holds_count = Koha::Holds->search( + { + borrowernumber => $borrower->{borrowernumber} + } + )->count(); + + return { status => 'tooManyReserves', limit => $rule->rule_value} if $total_holds_count >= $rule->rule_value; } my $circ_control_branch = C4::Circulation::_GetCircControlBranch( $item->unblessed(), $borrower ); my $branchitemrule = - C4::Circulation::GetBranchItemRule( $circ_control_branch, $item->itype ); + C4::Circulation::GetBranchItemRule( $circ_control_branch, $item->itype ); # FIXME Should not be item->effective_itemtype? if ( $branchitemrule->{holdallowed} == 0 ) { - return 'notReservable'; + return { status => 'notReservable' }; } if ( $branchitemrule->{holdallowed} == 1 && $borrower->{branchcode} ne $item->homebranch ) { - return 'cannotReserveFromOtherBranches'; + return { status => 'cannotReserveFromOtherBranches' }; } # If reservecount is ok, we check item branch if IndependentBranches is ON @@ -429,13 +465,28 @@ sub CanItemBeReserved { if ( C4::Context->preference('IndependentBranches') and !C4::Context->preference('canreservefromotherbranches') ) { - my $itembranch = $item->homebranch; - if ( $itembranch ne $borrower->{branchcode} ) { - return 'cannotReserveFromOtherBranches'; + if ( $item->homebranch ne $borrower->{branchcode} ) { + return { status => 'cannotReserveFromOtherBranches' }; } } - return 'OK'; + if ($pickup_branchcode) { + my $destination = Koha::Libraries->find({ + branchcode => $pickup_branchcode, + }); + + unless ($destination) { + return { status => 'libraryNotFound' }; + } + unless ($destination->pickup_location) { + return { status => 'libraryNotPickupLocation' }; + } + unless ($item->can_be_transferred({ to => $destination })) { + return 'cannotBeTransferred'; + } + } + + return { status => 'OK' }; } =head2 CanReserveBeCanceledFromOpac @@ -475,8 +526,8 @@ sub GetOtherReserves { my $nextreservinfo; my ( undef, $checkreserves, undef ) = CheckReserves($itemnumber); if ($checkreserves) { - my $iteminfo = GetItem($itemnumber); - if ( $iteminfo->{'holdingbranch'} ne $checkreserves->{'branchcode'} ) { + my $item = Koha::Items->find($itemnumber); + if ( $item->holdingbranch ne $checkreserves->{'branchcode'} ) { $messages->{'transfert'} = $checkreserves->{'branchcode'}; #minus priorities of others reservs ModReserveMinusPriority( @@ -487,7 +538,7 @@ sub GetOtherReserves { #launch the subroutine dotransfer C4::Items::ModItemTransfer( $itemnumber, - $iteminfo->{'holdingbranch'}, + $item->holdingbranch, $checkreserves->{'branchcode'} ), ; @@ -519,13 +570,20 @@ sub GetOtherReserves { sub ChargeReserveFee { my ( $borrowernumber, $fee, $title ) = @_; - return if !$fee || $fee==0; # the last test is needed to include 0.00 - my $accquery = qq{ -INSERT INTO accountlines ( borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding ) VALUES (?, ?, NOW(), ?, ?, 'Res', ?) - }; - my $dbh = C4::Context->dbh; - my $nextacctno = &getnextacctno( $borrowernumber ); - $dbh->do( $accquery, undef, ( $borrowernumber, $nextacctno, $fee, "Reserve Charge - $title", $fee ) ); + return if !$fee || $fee == 0; # the last test is needed to include 0.00 + Koha::Account->new( { patron_id => $borrowernumber } )->add_debit( + { + amount => $fee, + description => "Reserve Charge - " . $title, + note => undef, + user_id => C4::Context->userenv ? C4::Context->userenv->{'number'} : 0, + library_id => C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef, + sip => undef, + invoice_type => undef, + type => 'reserve', + item_id => undef + } + ); } =head2 GetReserveFee @@ -712,15 +770,15 @@ sub CheckReserves { } } else { my $patron; - my $iteminfo; + my $item; my $local_hold_match; if ($LocalHoldsPriority) { $patron = Koha::Patrons->find( $res->{borrowernumber} ); - $iteminfo = C4::Items::GetItem($itemnumber); + $item = Koha::Items->find($itemnumber); my $local_holds_priority_item_branchcode = - $iteminfo->{$LocalHoldsPriorityItemControl}; + $item->$LocalHoldsPriorityItemControl; my $local_holds_priority_patron_branchcode = ( $LocalHoldsPriorityPatronControl eq 'PickupLibrary' ) ? $res->{branchcode} @@ -734,14 +792,15 @@ sub CheckReserves { # See if this item is more important than what we've got so far if ( ( $res->{'priority'} && $res->{'priority'} < $priority ) || $local_hold_match ) { - $iteminfo ||= C4::Items::GetItem($itemnumber); - next if $res->{itemtype} && $res->{itemtype} ne _get_itype( $iteminfo ); + $item ||= Koha::Items->find($itemnumber); + next if $res->{itemtype} && $res->{itemtype} ne $item->effective_itemtype; $patron ||= Koha::Patrons->find( $res->{borrowernumber} ); - my $branch = GetReservesControlBranch( $iteminfo, $patron->unblessed ); - my $branchitemrule = C4::Circulation::GetBranchItemRule($branch,$iteminfo->{'itype'}); + my $branch = GetReservesControlBranch( $item->unblessed, $patron->unblessed ); + my $branchitemrule = C4::Circulation::GetBranchItemRule($branch,$item->effective_itemtype); next if ($branchitemrule->{'holdallowed'} == 0); next if (($branchitemrule->{'holdallowed'} == 1) && ($branch ne $patron->branchcode)); - next if ( ($branchitemrule->{hold_fulfillment_policy} ne 'any') && ($res->{branchcode} ne $iteminfo->{ $branchitemrule->{hold_fulfillment_policy} }) ); + my $hold_fulfillment_policy = $branchitemrule->{hold_fulfillment_policy}; + next if ( ($branchitemrule->{hold_fulfillment_policy} ne 'any') && ($res->{branchcode} ne $item->$hold_fulfillment_policy) ); $priority = $res->{'priority'}; $highest = $res; last if $local_hold_match; @@ -804,9 +863,9 @@ Unsuspends all suspended reserves with a suspend_until date from before today. sub AutoUnsuspendReserves { my $today = dt_from_string(); - my @holds = Koha::Holds->search( { suspend_until => { '<' => $today->ymd() } } ); + my @holds = Koha::Holds->search( { suspend_until => { '<=' => $today->ymd() } } ); - map { $_->suspend(0)->suspend_until(undef)->store() } @holds; + map { $_->resume() } @holds; } =head2 ModReserve @@ -1188,7 +1247,7 @@ sub _get_itype { =head2 AlterPriority - AlterPriority( $where, $reserve_id ); + AlterPriority( $where, $reserve_id, $prev_priority, $next_priority, $first_priority, $last_priority ); This function changes a reserve's priority up, down, to the top, or to the bottom. Input: $where is 'up', 'down', 'top' or 'bottom'. Biblionumber, Date reserve was placed @@ -1196,7 +1255,7 @@ Input: $where is 'up', 'down', 'top' or 'bottom'. Biblionumber, Date reserve was =cut sub AlterPriority { - my ( $where, $reserve_id ) = @_; + my ( $where, $reserve_id, $prev_priority, $next_priority, $first_priority, $last_priority ) = @_; my $hold = Koha::Holds->find( $reserve_id ); return unless $hold; @@ -1206,21 +1265,18 @@ sub AlterPriority { return; } - if ( $where eq 'up' || $where eq 'down' ) { - - my $priority = $hold->priority; - $priority = $where eq 'up' ? $priority - 1 : $priority + 1; - _FixPriority({ reserve_id => $reserve_id, rank => $priority }) - + if ( $where eq 'up' ) { + return unless $prev_priority; + _FixPriority({ reserve_id => $reserve_id, rank => $prev_priority }) + } elsif ( $where eq 'down' ) { + return unless $next_priority; + _FixPriority({ reserve_id => $reserve_id, rank => $next_priority }) } elsif ( $where eq 'top' ) { - - _FixPriority({ reserve_id => $reserve_id, rank => '1' }) - + _FixPriority({ reserve_id => $reserve_id, rank => $first_priority }) } elsif ( $where eq 'bottom' ) { - - _FixPriority({ reserve_id => $reserve_id, rank => '999999' }); - + _FixPriority({ reserve_id => $reserve_id, rank => $last_priority }); } + # FIXME Should return the new priority } @@ -1868,13 +1924,15 @@ sub RevertWaitingStatus { =head2 ReserveSlip - ReserveSlip($args => { - branchcode, - borrowernumber, - biblionumber, - [itemnumber], - [barcode], - }) +ReserveSlip( + { + branchcode => $branchcode, + borrowernumber => $borrowernumber, + biblionumber => $biblionumber, + [ itemnumber => $itemnumber, ] + [ barcode => $barcode, ] + } + ) Returns letter hash ( see C4::Letters::GetPreparedLetter ) or undef @@ -1892,18 +1950,34 @@ available within the slip: sub ReserveSlip { my ($args) = @_; - my $patron = Koha::Patrons->find( $args->{borrowernumber} ); + my $branchcode = $args->{branchcode}; + my $borrowernumber = $args->{borrowernumber}; + my $biblionumber = $args->{biblionumber}; + my $itemnumber = $args->{itemnumber}; + my $barcode = $args->{barcode}; + + + my $patron = Koha::Patrons->find($borrowernumber); my $hold; - if ($args->{itemnumber}) { - $hold = Koha::Holds->search({biblionumber => $args->{biblionumber}, borrowernumber => $args->{borrowernumber}, itemnumber => $args->{itemnumber} })->next; - } elsif ($args->{barcode}) { - my $itemnumber = Koha::Items->find({ barcode => $args->{barcode} }); - if ($args->{itemnumber}) { - $hold = Koha::Holds->search({biblionumber => $args->{biblionumber}, borrowernumber => $args->{borrowernumber}, itemnumber => $args->{itemnumber} })->next; - } - } else { - $hold = Koha::Holds->search({biblionumber => $args->{biblionumber}, borrowernumber => $args->{borrowernumber} })->next; + if ($itemnumber || $barcode ) { + $itemnumber ||= Koha::Items->find( { barcode => $barcode } )->itemnumber; + + $hold = Koha::Holds->search( + { + biblionumber => $biblionumber, + borrowernumber => $borrowernumber, + itemnumber => $itemnumber + } + )->next; + } + else { + $hold = Koha::Holds->search( + { + biblionumber => $biblionumber, + borrowernumber => $borrowernumber + } + )->next; } return unless $hold; @@ -1912,7 +1986,7 @@ sub ReserveSlip { return C4::Letters::GetPreparedLetter ( module => 'circulation', letter_code => 'HOLD_SLIP', - branchcode => $args->{branchcode}, + branchcode => $branchcode, lang => $patron->lang, tables => { 'reserves' => $reserve, @@ -2074,7 +2148,7 @@ sub GetHoldRule { my $sth = $dbh->prepare( q{ - SELECT categorycode, itemtype, branchcode, reservesallowed, holds_per_record + SELECT categorycode, itemtype, branchcode, reservesallowed, holds_per_record, holds_per_day FROM issuingrules WHERE (categorycode in (?,'*') ) AND (itemtype IN (?,'*'))