diff --git a/C4/Circulation.pm b/C4/Circulation.pm index baab76fef6..ec21a6091c 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -1219,6 +1219,36 @@ sub CanBookBeIssued { } } + ## CHECK FOR BOOKINGS + if ( + my $booking = $item_object->find_booking( + { + checkout_date => $now, + due_date => $duedate, + patron_id => $patron->borrowernumber + } + ) + ) + { + # Booked to this patron :) + if ( $booking->patron_id == $patron->borrowernumber ) { + if ( $now < dt_from_string($booking->start_date) ) { + $needsconfirmation{'BOOKED_EARLY'} = $booking; + } + else { + $alerts{'BOOKED'} = $booking; + } + } + # Booking starts before due date, reduce loan? + elsif ( $duedate > dt_from_string($booking->start_date) ) { + $needsconfirmation{'BOOKED_TO_ANOTHER'} = $booking; + } + # Loan falls inside booking + else { + $issuingimpossible{'BOOKED_TO_ANOTHER'} = $booking; + } + } + ## CHECK AGE RESTRICTION my $agerestriction = $biblioitem->agerestriction; my ($restriction_age, $daysToAgeRestriction) = GetAgeRestriction( $agerestriction, $patron->unblessed ); diff --git a/Koha/Item.pm b/Koha/Item.pm index ea2b8e39d2..1f490b86f5 100644 --- a/Koha/Item.pm +++ b/Koha/Item.pm @@ -526,6 +526,85 @@ sub bookings { return Koha::Bookings->_new_from_dbic( $bookings_rs ); } +=head3 find_booking + +Find the first booking that would conflict with the passed checkout dates + +=cut + +sub find_booking { + my ( $self, $params ) = @_; + + my $checkout_date = $params->{checkout_date}; + my $due_date = $params->{due_date}; + my $biblio = $self->biblio; + + my $dtf = Koha::Database->new->schema->storage->datetime_parser; + my $bookings = $biblio->bookings( + [ + # Checkout starts during booked period + start_date => { + '-between' => [ + $dtf->format_datetime($checkout_date), + $dtf->format_datetime($due_date) + ] + }, + + # Checkout is due during booked period + end_date => { + '-between' => [ + $dtf->format_datetime($checkout_date), + $dtf->format_datetime($due_date) + ] + }, + + # Checkout contains booked period + { + start_date => { '<' => $dtf->format_datetime($checkout_date) }, + end_date => { '>' => $dtf->format_datetime($due_date) } + } + ], + { + order_by => { '-asc' => 'start_date' } + } + ); + + my $checkouts = {}; + my $loanable_items = {}; + my $bookable_items = $biblio->bookable_items; + while ( my $item = $bookable_items->next ) { + $loanable_items->{ $item->itemnumber } = 1; + if ( my $checkout = $item->checkout ) { + $checkouts->{ $item->itemnumber } = + dt_from_string( $checkout->date_due ); + } + } + + while ( my $booking = $bookings->next ) { + + # Booking for this item + if ( defined( $booking->item_id ) + && $booking->item_id == $self->itemnumber ) + { + return $booking; + } + + # Booking for another item + elsif ( defined( $booking->item_id ) ) { + # Due for another booking, remove from pool + delete $loanable_items->{ $booking->item_id }; + next; + + } + + # Booking for any item + else { + # Can another item satisfy this booking? + } + } + return; +} + =head3 check_booking my $bookable = @@ -613,9 +692,9 @@ sub place_booking { { start_date => $params->{start_date}, end_date => $params->{end_date}, - borrowernumber => $patron->borrowernumber, - biblionumber => $self->biblionumber, - itemnumber => $self->itemnumber, + patron_id => $patron->borrowernumber, + biblio_id => $self->biblionumber, + item_id => $self->itemnumber, } )->store(); return $booking; diff --git a/circ/circulation.pl b/circ/circulation.pl index 7f8c0ef080..6a2d610ecb 100755 --- a/circ/circulation.pl +++ b/circ/circulation.pl @@ -202,6 +202,10 @@ if( $onsite_checkout && !$duedatespec_allow ) { } } } +my $reduced_datedue = $query->param('reduceddue'); +if ( $reduced_datedue ) { + $datedue = dt_from_string( $reduced_datedue ); +} my $inprocess = (@$barcodes == 0) ? '' : $query->param('inprocess'); if ( @$barcodes == 0 && $charges eq 'yes' ) { @@ -370,9 +374,16 @@ if (@$barcodes) { ? qw( UNKNOWN_BARCODE NO_OPEN_DAYS ) : ( keys %$error ); + if ( $error->{BOOKED_TO_ANOTHER} ) { + $template_params->{BOOKED_TO_ANOTHER} = $error->{BOOKED_TO_ANOTHER}; + $template_params->{IMPOSSIBLE} = 1; + $blocker = 1; + } + foreach my $code ( @blocking_error_codes ) { if ($error->{$code}) { $template_params->{$code} = $error->{$code}; + $template_params->{IMPOSSIBLE} = 1; $blocker = 1; } @@ -397,6 +408,10 @@ if (@$barcodes) { $template_params->{getBarcodeMessageIteminfo} = $item->barcode; $template_params->{NEEDSCONFIRMATION} = 1; $confirm_required = 1; + if ( $needsconfirmation eq 'BOOKED_TO_ANOTHER' ) { + my $reduceddue = dt_from_string($$question{$needsconfirmation}->start_date)->subtract( days => 1 ); + $template_params->{reduceddue} = $reduceddue; + } } } unless($confirm_required) { @@ -413,7 +428,20 @@ if (@$barcodes) { ); $recall_id = ( $recall and $recall->id ) ? $recall->id : undef; } - my $issue = AddIssue( $patron, $barcode, $datedue, $cancelreserve, undef, undef, { onsite_checkout => $onsite_checkout, auto_renew => $session->param('auto_renew'), switch_onsite_checkout => $switch_onsite_checkout, cancel_recall => $cancel_recall, recall_id => $recall_id, } ); + # If booked (alerts or confirmation) update datedue to end of booking + if ( my $booked = $question->{BOOKED_EARLY} // $alerts->{BOOKED} ) { + $datedue = $booked->end_date; + } + my $issue = AddIssue( + $patron, $barcode, $datedue, + $cancelreserve, + undef, undef, + { + onsite_checkout => $onsite_checkout, auto_renew => $session->param('auto_renew'), + switch_onsite_checkout => $switch_onsite_checkout, cancel_recall => $cancel_recall, + recall_id => $recall_id, + } + ); $template_params->{issue} = $issue; $session->clear('auto_renew'); $inprocess = 1; diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt index dfc42a6b9e..cdd223688f 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt @@ -92,6 +92,10 @@
+
+ [% END %] + + [% IF ( BOOKED_EARLY ) %] ++ Allow early collection? +
+ [% END %] + @@ -530,6 +558,10 @@ [% IF (forceallow) %]