View | Details | Raw Unified | Return to bug 8367
Collapse All | Expand All

(-)a/C4/Circulation.pm (-2 / +2 lines)
Lines 2002-2008 sub AddReturn { Link Here
2002
        }
2002
        }
2003
        $validTransfert = 1;
2003
        $validTransfert = 1;
2004
    }
2004
    }
2005
2006
    # fix up the accounts.....
2005
    # fix up the accounts.....
2007
    if ( $item->itemlost ) {
2006
    if ( $item->itemlost ) {
2008
        $messages->{'WasLost'} = 1;
2007
        $messages->{'WasLost'} = 1;
Lines 2058-2065 sub AddReturn { Link Here
2058
    my $lookahead= C4::Context->preference('ConfirmFutureHolds'); #number of days to look for future holds
2057
    my $lookahead= C4::Context->preference('ConfirmFutureHolds'); #number of days to look for future holds
2059
    ($resfound, $resrec, undef) = C4::Reserves::CheckReserves( $item->itemnumber, undef, $lookahead ) unless ( $item->withdrawn );
2058
    ($resfound, $resrec, undef) = C4::Reserves::CheckReserves( $item->itemnumber, undef, $lookahead ) unless ( $item->withdrawn );
2060
    if ($resfound) {
2059
    if ($resfound) {
2061
          $resrec->{'ResFound'} = $resfound;
2060
        $resrec->{'ResFound'} = $resfound;
2062
        $messages->{'ResFound'} = $resrec;
2061
        $messages->{'ResFound'} = $resrec;
2062
        C4::Reserves::ModReserveStatus($item->{'itemnumber'}, 'W');
2063
    }
2063
    }
2064
2064
2065
    # Record the fact that this book was returned.
2065
    # Record the fact that this book was returned.
(-)a/C4/Letters.pm (-5 / +1 lines)
Lines 786-801 sub _parseletter { Link Here
786
786
787
    # Work on a local copy of $values_in (passed by reference) to avoid side effects
787
    # Work on a local copy of $values_in (passed by reference) to avoid side effects
788
    # in callers ( by changing / formatting values )
788
    # in callers ( by changing / formatting values )
789
    my $values = $values_in ? { %$values_in } : {};
789
   my $values = $values_in ? { %$values_in } : {};
790
790
791
    if ( $table eq 'borrowers' && $values->{'dateexpiry'} ){
791
    if ( $table eq 'borrowers' && $values->{'dateexpiry'} ){
792
        $values->{'dateexpiry'} = output_pref({ dt => dt_from_string( $values->{'dateexpiry'} ), dateonly => 1 });
792
        $values->{'dateexpiry'} = output_pref({ dt => dt_from_string( $values->{'dateexpiry'} ), dateonly => 1 });
793
    }
793
    }
794
794
795
    if ( $table eq 'reserves' && $values->{'waitingdate'} ) {
796
        $values->{'waitingdate'} = output_pref({ dt => dt_from_string( $values->{'waitingdate'} ), dateonly => 1 });
797
    }
798
799
    if ($letter->{content} && $letter->{content} =~ /<<today>>/) {
795
    if ($letter->{content} && $letter->{content} =~ /<<today>>/) {
800
        my $todaysdate = output_pref( DateTime->now() );
796
        my $todaysdate = output_pref( DateTime->now() );
801
        $letter->{content} =~ s/<<today>>/$todaysdate/go;
797
        $letter->{content} =~ s/<<today>>/$todaysdate/go;
(-)a/C4/Reserves.pm (-12 / +189 lines)
Lines 120-126 BEGIN { Link Here
120
        &CanItemBeReserved
120
        &CanItemBeReserved
121
        &CanReserveBeCanceledFromOpac
121
        &CanReserveBeCanceledFromOpac
122
        &CancelExpiredReserves
122
        &CancelExpiredReserves
123
124
        &AutoUnsuspendReserves
123
        &AutoUnsuspendReserves
125
124
126
        &IsAvailableForItemLevelRequest
125
        &IsAvailableForItemLevelRequest
Lines 185-201 sub AddReserve { Link Here
185
        # Make room in reserves for this before those of a later reserve date
184
        # Make room in reserves for this before those of a later reserve date
186
        $priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority );
185
        $priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority );
187
    }
186
    }
187
    my ($waitingdate, $lastpickupdate);
188
188
189
    my $waitingdate;
189
    my $item = C4::Items::GetItem( $checkitem );
190
191
    # If the reserv had the waiting status, we had the value of the resdate
190
    # If the reserv had the waiting status, we had the value of the resdate
192
    if ( $found eq 'W' ) {
191
    if ( $found eq 'W' ) {
193
        $waitingdate = $resdate;
192
        $waitingdate = $resdate;
194
    }
195
193
194
        #The reserve-object doesn't exist yet in DB, so we must supply what information we have to GetLastPickupDate() so it can do it's work.
195
        my $reserve = {borrowernumber => $borrowernumber, waitingdate => $waitingdate, branchcode => $branch};
196
        my $patron = Koha::Patrons->find( {borrowernumber => $borrowernumber} );
197
        $lastpickupdate = GetLastPickupDate( $reserve, $item, $patron );
198
    }
196
    # Don't add itemtype limit if specific item is selected
199
    # Don't add itemtype limit if specific item is selected
197
    $itemtype = undef if $checkitem;
200
    $itemtype = undef if $checkitem;
198
199
    # updates take place here
201
    # updates take place here
200
    my $hold = Koha::Hold->new(
202
    my $hold = Koha::Hold->new(
201
        {
203
        {
Lines 211-220 sub AddReserve { Link Here
211
            expirationdate => $expdate,
213
            expirationdate => $expdate,
212
            itemtype       => $itemtype,
214
            itemtype       => $itemtype,
213
            item_level_hold => $checkitem ? 1 : 0,
215
            item_level_hold => $checkitem ? 1 : 0,
216
            lastpickupdate => $lastpickupdate,
214
        }
217
        }
215
    )->store();
218
    )->store();
216
    $hold->set_waiting() if $found eq 'W';
219
    $hold->set_waiting() if $found eq 'W';
217
218
    logaction( 'HOLDS', 'CREATE', $hold->id, Dumper($hold->unblessed) )
220
    logaction( 'HOLDS', 'CREATE', $hold->id, Dumper($hold->unblessed) )
219
        if C4::Context->preference('HoldsLog');
221
        if C4::Context->preference('HoldsLog');
220
222
Lines 661-666 sub GetReserveStatus { Link Here
661
    return ''; # empty string here will remove need for checking undef, or less log lines
663
    return ''; # empty string here will remove need for checking undef, or less log lines
662
}
664
}
663
665
666
667
=head2 GetLastPickupDate
668
669
  my $lastpickupdate = GetLastPickupDate($reserve, $item);
670
  my $lastpickupdate = GetLastPickupDate($reserve, $item, $borrower);
671
  my $lastpickupdate = GetLastPickupDate(undef,    $item);
672
673
  Gets the last pickup date from the issuingrules for the given reserves-row and sets the
674
  $reserve->{lastpickupdate}.-value.
675
676
  If the reserves-row is not passed, function tries to figure it out from the item-row.
677
  Calculating the last pickup date respects Calendar holidays and skips to the next open day.
678
  If the issuingrule's holdspickupwait is 0 or less, means that the lastpickupdate-feature
679
  is disabled for this kind of material.
680
681
  @PARAM1 koha.reserves-row
682
  @PARAM2 koha.items-row, If the reserve is not given, an item must be given to be
683
  able to find a reservation
684
  @PARAM3 koha.borrowers-row, OPTIONAL
685
  RETURNS DateTime, depicting the last pickup date.
686
  OR undef if there is an error or if the issuingrules disable this feature for this material.
687
688
=cut
689
690
sub GetLastPickupDate {
691
    my ($reserve, $item, $borrower) = @_;
692
    ##Verify parameters
693
    if ( not defined $reserve and not defined $item ) {
694
        warn "C4::Reserves::GetMaxPickupDate(), is called without a reserve and an item";
695
        return;
696
    }
697
    if ( defined $reserve and not defined $item ) {
698
        $item = C4::Items::GetItem( $reserve->{itemnumber} );
699
    }
700
701
    unless ( defined $borrower) {
702
        my $borrower = Koha::Patrons->find( $reserve->{borrowernumber} );
703
        $borrower = $borrower->unblessed;
704
    }
705
706
    unless ( defined $reserve ) {
707
        my $reserve = GetReservesFromItemnumber( $item->{itemnumber} );
708
    }
709
    my $date = $reserve->{waitingdate};
710
    unless ( $date ) { #It is possible that a reserve is just caught and it doesn't have a waitingdate yet.
711
       $date = DateTime->now( time_zone => C4::Context->tz() ); #So default to NOW()
712
    }
713
    else {
714
       $date = (ref $reserve->{waitingdate} eq 'DateTime') ? $reserve->{waitingdate}  :  dt_from_string($reserve->{waitingdate});
715
    }
716
    ##Get churning the LastPickupDate
717
718
    # Get the controlbranch
719
    my $controlbranch = GetReservesControlBranch( $item, $borrower );
720
    my $hbr = C4::Context->preference('HomeOrHoldingBranch') || "homebranch";
721
    my $branchcode    = "*";
722
    if ( $controlbranch eq "ItemHomeLibrary" ) {
723
         $branchcode = $item->{$hbr};
724
    } elsif ( $controlbranch eq "PatronLibrary" ) {
725
         $branchcode = $borrower->branchcode;
726
    }
727
    warn 'getlastpickup';
728
    warn $branchcode;
729
    warn $borrower->{'categorycode'};
730
    warn $item->{itype};
731
    my $issuingrule = Koha::IssuingRules->get_effective_issuing_rule({
732
            branchcode   => $branchcode,
733
            categorycode => $borrower->{'categorycode'},
734
            itemtype     => $item->{itype},
735
    });
736
737
    my $reservebranch;
738
    if ($reserve->{'branchcode'}) {
739
        $reservebranch = $reserve->{'branchcode'};
740
    } else {
741
        $reservebranch = $reserve->branchcode;
742
    }
743
    if ( defined($issuingrule) && defined $issuingrule->holdspickupwait && $issuingrule->holdspickupwait > 0 ) { #If holdspickupwait is <= 0, it means this feature is disabled for this type of material.
744
        $date->add( days => $issuingrule->holdspickupwait );
745
        my $calendar = Koha::Calendar->new( branchcode => $reservebranch );
746
        my $is_holiday = $calendar->is_holiday( $date );
747
        while ( $is_holiday ) {
748
               $date->add( days => 1 );
749
               $is_holiday = $calendar->is_holiday( $date );
750
        }
751
        $reserve->{lastpickupdate} = $date->ymd();
752
        return $date;
753
     }
754
     #Without explicitly setting the return value undef, the lastpickupdate-column is
755
     #  set as 0000-00-00 instead of NULL in DBD::MySQL.
756
     #This causes this hold to always expire any lastpickupdate check,
757
     ##  effectively canceling it as soon as cancel_expired_holds.pl is ran.
758
     ##This is exactly the opposite of disabling the autoexpire feature.
759
     ##So please let me explicitly return undef, because Perl cant always handle everything.
760
     delete $reserve->{lastpickupdate};
761
     return undef;
762
}
763
664
=head2 CheckReserves
764
=head2 CheckReserves
665
765
666
  ($status, $matched_reserve, $possible_reserves) = &CheckReserves($itemnumber);
766
  ($status, $matched_reserve, $possible_reserves) = &CheckReserves($itemnumber);
Lines 1006-1012 sub ModReserveFill { Link Here
1006
1106
1007
=head2 ModReserveStatus
1107
=head2 ModReserveStatus
1008
1108
1009
  &ModReserveStatus($itemnumber, $newstatus);
1109
  &ModReserveStatus($itemnumber, $newstatus, $holdtype);
1010
1110
1011
Update the reserve status for the active (priority=0) reserve.
1111
Update the reserve status for the active (priority=0) reserve.
1012
1112
Lines 1017-1030 $newstatus is the new status. Link Here
1017
=cut
1117
=cut
1018
1118
1019
sub ModReserveStatus {
1119
sub ModReserveStatus {
1020
1021
    #first : check if we have a reservation for this item .
1120
    #first : check if we have a reservation for this item .
1022
    my ($itemnumber, $newstatus) = @_;
1121
    my ($itemnumber, $newstatus) = @_;
1023
    my $dbh = C4::Context->dbh;
1122
    my $dbh = C4::Context->dbh;
1024
1123
1025
    my $query = "UPDATE reserves SET found = ?, waitingdate = NOW() WHERE itemnumber = ? AND found IS NULL AND priority = 0";
1124
    my $now = dt_from_string;
1125
1126
    my $reserve = $dbh->selectrow_hashref(q{
1127
        SELECT *
1128
        FROM reserves
1129
        WHERE itemnumber = ?
1130
            AND found IS NULL
1131
            AND priority = 0
1132
    }, {}, $itemnumber);
1133
1134
    return unless $reserve;
1135
    my $borrower = Koha::Patrons->find( $reserve->{borrowernumber} );
1136
    $borrower = $borrower->unblessed;
1137
    my $item = C4::Items::GetItem( $reserve->{itemnumber} );
1138
1139
    my $lastpickupdate = GetLastPickupDate( $reserve, $item, $borrower );
1140
    my $query = q{
1141
        UPDATE reserves
1142
        SET found = ?,
1143
            waitingdate = ?,
1144
            lastpickupdate = ?
1145
        WHERE itemnumber = ?
1146
            AND found IS NULL
1147
            AND priority = 0
1148
    };
1026
    my $sth_set = $dbh->prepare($query);
1149
    my $sth_set = $dbh->prepare($query);
1027
    $sth_set->execute( $newstatus, $itemnumber );
1150
    $sth_set->execute( $newstatus, $now, $lastpickupdate, $itemnumber );
1028
1151
1029
    my $item = Koha::Items->find($itemnumber);
1152
    my $item = Koha::Items->find($itemnumber);
1030
    if ( ( $item->location eq 'CART' && $item->permanent_location ne 'CART'  ) && $newstatus ) {
1153
    if ( ( $item->location eq 'CART' && $item->permanent_location ne 'CART'  ) && $newstatus ) {
Lines 1075-1080 sub ModReserveAffect { Link Here
1075
    $hold->itemnumber($itemnumber);
1198
    $hold->itemnumber($itemnumber);
1076
    $hold->set_waiting($transferToDo);
1199
    $hold->set_waiting($transferToDo);
1077
1200
1201
    my $item = C4::Items::GetItem( $itemnumber );
1202
    my $borrower = Koha::Patrons->find( $borrowernumber );
1203
    $borrower = $borrower->unblessed;
1204
    my $lastpickupdate = GetLastPickupDate( $hold, $item, $borrower );
1205
    warn $lastpickupdate;
1206
    my $query = "
1207
        UPDATE reserves
1208
        SET lastpickupdate = ?
1209
        WHERE borrowernumber = ?
1210
        AND itemnumber = ?
1211
    ";
1212
     $sth = $dbh->prepare($query);
1213
     $sth->execute( $lastpickupdate, $borrowernumber,$itemnumber);
1214
1078
    _koha_notify_reserve( $hold->reserve_id )
1215
    _koha_notify_reserve( $hold->reserve_id )
1079
      if ( !$transferToDo && !$already_on_shelf );
1216
      if ( !$transferToDo && !$already_on_shelf );
1080
1217
Lines 1530-1535 sub _Findgroupreserve { Link Here
1530
               reserves.borrowernumber      AS borrowernumber,
1667
               reserves.borrowernumber      AS borrowernumber,
1531
               reserves.reservedate         AS reservedate,
1668
               reserves.reservedate         AS reservedate,
1532
               reserves.branchcode          AS branchcode,
1669
               reserves.branchcode          AS branchcode,
1670
               reserves.lastpickupdate      AS lastpickupdate,
1533
               reserves.cancellationdate    AS cancellationdate,
1671
               reserves.cancellationdate    AS cancellationdate,
1534
               reserves.found               AS found,
1672
               reserves.found               AS found,
1535
               reserves.reservenotes        AS reservenotes,
1673
               reserves.reservenotes        AS reservenotes,
Lines 1565-1570 sub _Findgroupreserve { Link Here
1565
               reserves.borrowernumber      AS borrowernumber,
1703
               reserves.borrowernumber      AS borrowernumber,
1566
               reserves.reservedate         AS reservedate,
1704
               reserves.reservedate         AS reservedate,
1567
               reserves.branchcode          AS branchcode,
1705
               reserves.branchcode          AS branchcode,
1706
               reserves.lastpickupdate      AS lastpickupdate,
1568
               reserves.cancellationdate    AS cancellationdate,
1707
               reserves.cancellationdate    AS cancellationdate,
1569
               reserves.found               AS found,
1708
               reserves.found               AS found,
1570
               reserves.reservenotes        AS reservenotes,
1709
               reserves.reservenotes        AS reservenotes,
Lines 1600-1605 sub _Findgroupreserve { Link Here
1600
               reserves.reservedate                AS reservedate,
1739
               reserves.reservedate                AS reservedate,
1601
               reserves.waitingdate                AS waitingdate,
1740
               reserves.waitingdate                AS waitingdate,
1602
               reserves.branchcode                 AS branchcode,
1741
               reserves.branchcode                 AS branchcode,
1742
               reserves.lastpickupdate             AS lastpickupdate,
1603
               reserves.cancellationdate           AS cancellationdate,
1743
               reserves.cancellationdate           AS cancellationdate,
1604
               reserves.found                      AS found,
1744
               reserves.found                      AS found,
1605
               reserves.reservenotes               AS reservenotes,
1745
               reserves.reservenotes               AS reservenotes,
Lines 1820-1825 sub MoveReserve { Link Here
1820
    }
1960
    }
1821
}
1961
}
1822
1962
1963
=head MoveWaitingdate
1964
1965
  #Move waitingdate two months and fifteen days forward.
1966
  my $dateDuration = DateTime::Duration->new( months => 2, days => 15 );
1967
  $reserve = MoveWaitingdate( $reserve, $dateDuration);
1968
1969
  #Move waitingdate one year and eleven days backwards.
1970
  my $dateDuration = DateTime::Duration->new( years => -1, days => -11 );
1971
  $reserve = MoveWaitingdate( $reserve, $dateDuration);
1972
1973
Moves the waitingdate and updates the lastpickupdate to match.
1974
If waitingdate is not defined, uses today.
1975
Is intended to be used from automated tests, because under normal library
1976
operations there should be NO REASON to move the waitingdate.
1977
1978
@PARAM1 koha.reserves-row, with waitingdate set.
1979
@PARAM2 DateTime::Duration, with the desired offset.
1980
RETURNS koha.reserve-row, with keys waitingdate and lastpickupdate updated.
1981
=cut
1982
sub MoveWaitingdate {
1983
    my ($reserve, $dateDuration) = @_;
1984
1985
    my $dt = dt_from_string( $reserve->{waitingdate} );
1986
    $dt->add_duration( $dateDuration );
1987
    $reserve->{waitingdate} = $dt->ymd();
1988
1989
    GetLastPickupDate( $reserve ); #Update the $reserve->{lastpickupdate}
1990
1991
    #UPDATE the DB part
1992
    my $dbh = C4::Context->dbh();
1993
    my $sth = $dbh->prepare( "UPDATE reserves SET waitingdate=?, lastpickupdate=? WHERE reserve_id=?" );
1994
    $sth->execute( $reserve->{waitingdate}, $reserve->{lastpickupdate}, $reserve->{reserve_id} );
1995
1996
    return $reserve;
1997
}
1998
1823
=head2 MergeHolds
1999
=head2 MergeHolds
1824
2000
1825
  MergeHolds($dbh,$to_biblio, $from_biblio);
2001
  MergeHolds($dbh,$to_biblio, $from_biblio);
Lines 1918-1924 sub RevertWaitingStatus { Link Here
1918
    SET
2094
    SET
1919
      priority = 1,
2095
      priority = 1,
1920
      found = NULL,
2096
      found = NULL,
1921
      waitingdate = NULL
2097
      waitingdate = NULL,
2098
      lastpickupdate = NULL,
1922
    WHERE
2099
    WHERE
1923
      reserve_id = ?
2100
      reserve_id = ?
1924
    ";
2101
    ";
Lines 1992-1998 sub ReserveSlip { Link Here
1992
2169
1993
    return unless $hold;
2170
    return unless $hold;
1994
    my $reserve = $hold->unblessed;
2171
    my $reserve = $hold->unblessed;
1995
2172
warn Dumper($reserve);
1996
    return  C4::Letters::GetPreparedLetter (
2173
    return  C4::Letters::GetPreparedLetter (
1997
        module => 'circulation',
2174
        module => 'circulation',
1998
        letter_code => 'HOLD_SLIP',
2175
        letter_code => 'HOLD_SLIP',
(-)a/Koha/Hold.pm (-2 / +30 lines)
Lines 33-38 use Koha::Items; Link Here
33
use Koha::Libraries;
33
use Koha::Libraries;
34
use Koha::Old::Holds;
34
use Koha::Old::Holds;
35
use Koha::Calendar;
35
use Koha::Calendar;
36
use Koha::IssuingRules;
36
37
37
use Koha::Exceptions::Hold;
38
use Koha::Exceptions::Hold;
38
39
Lines 181-190 sub set_waiting { Link Here
181
    my $calendar = Koha::Calendar->new( branchcode => $self->branchcode );
182
    my $calendar = Koha::Calendar->new( branchcode => $self->branchcode );
182
183
183
    my $expirationdate = $today->clone;
184
    my $expirationdate = $today->clone;
184
    $expirationdate->add(days => $max_pickup_delay);
185
186
    my $patron = Koha::Patrons->find( {borrowernumber => $self->borrowernumber} );
187
    my $item = Koha::Items->find( $self->itemnumber() );
188
    my $hold = Koha::Holds->find( $self->reserve_id );
189
#    my $lastpickupdate = C4::Reserves->GetLastPickupDate( $hold, $item, $patron);
190
191
    # Get the controlbranch
192
    my $controlbranch = C4::Reserves->GetReservesControlBranch( $item, $patron );
193
    my $hbr = C4::Context->preference('HomeOrHoldingBranch') || "homebranch";
194
    my $branchcode    = "*";
195
    if ( $controlbranch eq "ItemHomeLibrary" ) {
196
        $branchcode = $item->{$hbr};
197
    } elsif ( $controlbranch eq "PatronLibrary" ) {
198
        $branchcode = $patron->branchcode;
199
    }
200
201
     warn $branchcode;
202
     warn $patron->categorycode;
203
     warn $item->itype;
204
     my $issuingrule = Koha::IssuingRules->get_effective_issuing_rule({
205
             branchcode   => $branchcode,
206
             categorycode => $patron->categorycode,
207
             itemtype     => $item->itype,
208
     });
209
210
    if ( defined $issuingrule->holdspickupwait && $issuingrule->holdspickupwait > 0) {
211
        $expirationdate->add(days => $issuingrule->holdspickupwait);
212
    }
185
213
186
    if ( C4::Context->preference("ExcludeHolidaysFromMaxPickUpDelay") ) {
214
    if ( C4::Context->preference("ExcludeHolidaysFromMaxPickUpDelay") ) {
187
        $expirationdate = $calendar->days_forward( dt_from_string(), $max_pickup_delay );
215
        $expirationdate = $calendar->days_forward( dt_from_string(), $issuingrule->{holdspickupwait} );
188
    }
216
    }
189
217
190
    # If patron's requested expiration date is prior to the
218
    # If patron's requested expiration date is prior to the
(-)a/Koha/IssuingRules.pm (-1 / +1 lines)
Lines 22-28 use Modern::Perl; Link Here
22
22
23
use Koha::Database;
23
use Koha::Database;
24
use Koha::Caches;
24
use Koha::Caches;
25
25
use Data::Dumper;
26
use Koha::IssuingRule;
26
use Koha::IssuingRule;
27
27
28
use base qw(Koha::Objects);
28
use base qw(Koha::Objects);
(-)a/admin/smart-rules.pl (-1 / +3 lines)
Lines 240-249 elsif ($op eq 'add') { Link Here
240
    $no_auto_renewal_after_hard_limit = eval { dt_from_string( $input->param('no_auto_renewal_after_hard_limit') ) } if ( $no_auto_renewal_after_hard_limit );
240
    $no_auto_renewal_after_hard_limit = eval { dt_from_string( $input->param('no_auto_renewal_after_hard_limit') ) } if ( $no_auto_renewal_after_hard_limit );
241
    $no_auto_renewal_after_hard_limit = output_pref( { dt => $no_auto_renewal_after_hard_limit, dateonly => 1, dateformat => 'iso' } ) if ( $no_auto_renewal_after_hard_limit );
241
    $no_auto_renewal_after_hard_limit = output_pref( { dt => $no_auto_renewal_after_hard_limit, dateonly => 1, dateformat => 'iso' } ) if ( $no_auto_renewal_after_hard_limit );
242
    my $reservesallowed  = $input->param('reservesallowed');
242
    my $reservesallowed  = $input->param('reservesallowed');
243
    my $holds_per_record = $input->param('holds_per_record');
244
    my $holds_per_day    = $input->param('holds_per_day');
243
    my $holds_per_day    = $input->param('holds_per_day');
245
    $holds_per_day =~ s/\s//g;
244
    $holds_per_day =~ s/\s//g;
246
    $holds_per_day = undef if $holds_per_day !~ /^\d+/;
245
    $holds_per_day = undef if $holds_per_day !~ /^\d+/;
246
    my $holds_per_record  = $input->param('holds_per_record');
247
    my $holdspickupwait = $input->param('holdspickupwait');
247
    my $onshelfholds     = $input->param('onshelfholds') || 0;
248
    my $onshelfholds     = $input->param('onshelfholds') || 0;
248
    $maxissueqty =~ s/\s//g;
249
    $maxissueqty =~ s/\s//g;
249
    $maxissueqty = '' if $maxissueqty !~ /^\d+/;
250
    $maxissueqty = '' if $maxissueqty !~ /^\d+/;
Lines 277-282 elsif ($op eq 'add') { Link Here
277
        chargeperiod_charge_at        => $chargeperiod_charge_at,
278
        chargeperiod_charge_at        => $chargeperiod_charge_at,
278
        renewalsallowed               => $renewalsallowed,
279
        renewalsallowed               => $renewalsallowed,
279
        renewalperiod                 => $renewalperiod,
280
        renewalperiod                 => $renewalperiod,
281
        holdspickupwait               => $holdspickupwait,
280
        norenewalbefore               => $norenewalbefore,
282
        norenewalbefore               => $norenewalbefore,
281
        auto_renew                    => $auto_renew,
283
        auto_renew                    => $auto_renew,
282
        no_auto_renewal_after         => $no_auto_renewal_after,
284
        no_auto_renewal_after         => $no_auto_renewal_after,
(-)a/circ/returns.pl (+1 lines)
Lines 424-429 if ( $messages->{'ResFound'}) { Link Here
424
424
425
        my $diffBranchSend = !$branchCheck ? $reserve->{branchcode} : undef;
425
        my $diffBranchSend = !$branchCheck ? $reserve->{branchcode} : undef;
426
        ModReserveAffect( $reserve->{itemnumber}, $reserve->{borrowernumber}, $diffBranchSend, $reserve->{reserve_id} );
426
        ModReserveAffect( $reserve->{itemnumber}, $reserve->{borrowernumber}, $diffBranchSend, $reserve->{reserve_id} );
427
        ModReserveStatus($reserve->{itemnumber}, 'W');
427
        my ( $messages, $nextreservinfo ) = GetOtherReserves($reserve->{itemnumber});
428
        my ( $messages, $nextreservinfo ) = GetOtherReserves($reserve->{itemnumber});
428
429
429
        my $patron = Koha::Patrons->find( $nextreservinfo );
430
        my $patron = Koha::Patrons->find( $nextreservinfo );
(-)a/circ/waitingreserves.pl (-20 / +59 lines)
Lines 27-37 use C4::Circulation; Link Here
27
use C4::Members;
27
use C4::Members;
28
use C4::Biblio;
28
use C4::Biblio;
29
use C4::Items;
29
use C4::Items;
30
use Date::Calc qw(
30
#use Date::Calc qw(
31
  Today
31
#  Today
32
  Add_Delta_Days
32
#  Add_Delta_Days
33
  Date_to_Days
33
#  Date_to_Days
34
);
34
#);
35
use C4::Reserves;
35
use C4::Reserves;
36
use C4::Koha;
36
use C4::Koha;
37
use Koha::DateUtils;
37
use Koha::DateUtils;
Lines 39-44 use Koha::BiblioFrameworks; Link Here
39
use Koha::Items;
39
use Koha::Items;
40
use Koha::ItemTypes;
40
use Koha::ItemTypes;
41
use Koha::Patrons;
41
use Koha::Patrons;
42
use Data::Dumper;
42
43
43
my $input = new CGI;
44
my $input = new CGI;
44
45
Lines 87-112 my $holds = Koha::Holds->waiting->search({ priority => 0, ( $all_branches ? () : Link Here
87
88
88
# get reserves for the branch we are logged into, or for all branches
89
# get reserves for the branch we are logged into, or for all branches
89
90
90
my $today = Date_to_Days(&Today);
91
my $today = dt_from_string;
92
my $max_pickup_delay = C4::Context->preference('ReservesMaxPickUpDelay');
91
93
92
while ( my $hold = $holds->next ) {
94
while ( my $hold = $holds->next ) {
93
    next unless ($hold->waitingdate && $hold->waitingdate ne '0000-00-00');
95
    next unless ($hold->waitingdate && $hold->waitingdate ne '0000-00-00');
96
    my $item = $hold->item;
97
    my $patron = $hold->borrower;
98
    my $biblio = $item->biblio;
99
    my $holdingbranch = $item->holdingbranch;
100
    my $homebranch = $item->homebranch;
101
    my %getreserv = (
102
        title             => $biblio->title,
103
        itemnumber        => $item->itemnumber,
104
        waitingdate       => $hold->waitingdate,
105
        reservedate       => $hold->reservedate,
106
        borrowernumber       => $patron->borrowernumber,
107
        biblionumber      => $biblio->biblionumber,
108
        barcode           => $item->barcode,
109
        homebranch        => $homebranch,
110
        holdingbranch     => $item->holdingbranch,
111
        itemcallnumber    => $item->itemcallnumber,
112
        enumchron         => $item->enumchron,
113
        copynumber        => $item->copynumber,
114
        borrowername      => $patron->surname, # FIXME Let's send $patron to the template
115
        borrowerfirstname => $patron->firstname,
116
        borrowerphone     => $patron->phone,
117
        lastpickupdate    => $hold->lastpickupdate,
118
    );
119
120
    my $itemtype = Koha::ItemTypes->find( $item->effective_itemtype );
121
    $getreserv{'itemtype'}       = $itemtype->description; # FIXME Should not it be translated_description?
122
    $getreserv{'subtitle'}       = GetRecordValue(
123
        'subtitle',
124
        GetMarcBiblio({ biblionumber => $biblio->biblionumber }),
125
        $biblio->frameworkcode);
126
    if ( $homebranch ne $holdingbranch ) {
127
        $getreserv{'dotransfer'} = 1;
128
    }
94
129
95
    my ( $expire_year, $expire_month, $expire_day ) = split (/-/, $hold->expirationdate);
130
    $getreserv{patron} = $patron;
96
    my $calcDate = Date_to_Days( $expire_year, $expire_month, $expire_day );
131
    warn $getreserv{'waitingdate'};
97
132
    warn $getreserv{'lastpickupdate'};
98
    if ($today > $calcDate) {
133
    if ( $getreserv{'waitingdate'} ) {
99
        if ($cancelall) {
134
        my $lastpickupdate = dt_from_string($getreserv{'lastpickupdate'});
100
            my $res = cancel( $hold->item->itemnumber, $hold->borrowernumber, $hold->item->holdingbranch, $hold->item->homebranch, !$transfer_when_cancel_all );
135
        if ( DateTime->compare( $today, $lastpickupdate ) == 1 ) {
101
            push @cancel_result, $res if $res;
136
            if ($cancelall) {
102
            next;
137
                my $res = cancel( $item->itemnumber, $patron->borrowernumber, $item->holdingbranch, $homebranch, !$transfer_when_cancel_all );
103
        } else {
138
                push @cancel_result, $res if $res;
104
            push @over_loop, $hold;
139
                next;
140
            } else {
141
                push @overloop,   \%getreserv;
142
                $overcount++;
143
            }
144
        }else{
145
            push @reservloop, \%getreserv;
146
            $reservcount++;
105
        }
147
        }
106
    }else{
107
        push @reserve_loop, $hold;
108
    }
148
    }
109
    
110
}
149
}
111
150
112
$template->param(cancel_result => \@cancel_result) if @cancel_result;
151
$template->param(cancel_result => \@cancel_result) if @cancel_result;
Lines 118-123 $template->param( Link Here
118
    overcount   => scalar @over_loop,
157
    overcount   => scalar @over_loop,
119
    show_date   => output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 }),
158
    show_date   => output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 }),
120
    tab => $tab,
159
    tab => $tab,
160
    show_date   => $today,
121
);
161
);
122
162
123
# Checking if there is a Fast Cataloging Framework
163
# Checking if there is a Fast Cataloging Framework
Lines 139-145 sub cancel { Link Here
139
    my $transfer = $fbr ne $tbr; # XXX && !$nextreservinfo;
179
    my $transfer = $fbr ne $tbr; # XXX && !$nextreservinfo;
140
180
141
    return if $transfer && $skip_transfers;
181
    return if $transfer && $skip_transfers;
142
143
    my ( $messages, $nextreservinfo ) = ModReserveCancelAll( $item, $borrowernumber );
182
    my ( $messages, $nextreservinfo ) = ModReserveCancelAll( $item, $borrowernumber );
144
183
145
# 	if the document is not in his homebranch location and there is not reservation after, we transfer it
184
# 	if the document is not in his homebranch location and there is not reservation after, we transfer it
(-)a/installer/data/mysql/atomicupdate/bug_8367.perl (+21 lines)
Line 0 Link Here
1
my $maxpickupdelay = C4::Context->preference('ReservesMaxPickUpDelay') || 0; #MaxPickupDelay
2
$dbh->do(q{ DELETE FROM systempreferences WHERE variable='ReservesMaxPickUpDelay'; });
3
$dbh->do(q{ DELETE FROM systempreferences WHERE variable='ExpireReservesOnHolidays'; });
4
#        //DELETE FROM systempreferences WHERE variable='ExpireReservesMaxPickUpDelay'; #This syspref is not needed and would be better suited to be calculated from the holdspickupwait
5
#        //ExpireReservesMaxPickUpDelayCharge #This could be added as a column to the issuing rules.
6
$dbh->do(q{ ALTER TABLE issuingrules ADD COLUMN holdspickupwait INT(11) NULL default NULL AFTER reservesallowed; });
7
$dbh->do(q{ ALTER TABLE reserves ADD COLUMN lastpickupdate DATE NULL default NULL AFTER suspend_until; });
8
$dbh->do(q{ ALTER TABLE old_reserves ADD COLUMN lastpickupdate DATE NULL default NULL AFTER suspend_until; });
9
my $sth = $dbh->prepare(q{
10
    UPDATE issuingrules SET holdspickupwait = ?
11
});
12
$sth->execute( $maxpickupdelay ) if $maxpickupdelay; #Don't want to accidentally nullify all!
13
##Populate the lastpickupdate-column from existing 'ReservesMaxPickUpDelay'
14
print "Populating the new lastpickupdate-column for all waiting holds. This might take a while.\n";
15
$sth = $dbh->prepare(q{ SELECT * FROM reserves WHERE found = 'W'; });
16
$sth->execute( );
17
my $dtdur = DateTime::Duration->new( days => 0 );
18
while ( my $res = $sth->fetchrow_hashref ) {
19
     C4::Reserves::MoveWaitingdate( $res, $dtdur ); #We call MoveWaitingdate with a 0 duration to simply recalculate the lastpickupdate and store the new values to DB.
20
}
21
print "Upgrade to $DBversion done (8367: Add colum issuingrules.holdspickupwait and reserves.lastpickupdate. Populates introduced columns from the expiring ReservesMaxPickUpDelay. Deletes the ReservesMaxPickUpDelay and ExpireReservesOnHolidays -sysprefs)\n";>>>>>>> Bug 8367: Template fixes + fixes to C4/Reserves.pm and Koha/Hold.pm
(-)a/installer/data/mysql/kohastructure.sql (+3 lines)
Lines 832-837 CREATE TABLE `issuingrules` ( -- circulation and fine rules Link Here
832
  `reservesallowed` smallint(6) NOT NULL default "0", -- how many holds are allowed
832
  `reservesallowed` smallint(6) NOT NULL default "0", -- how many holds are allowed
833
  `holds_per_record` SMALLINT(6) NOT NULL DEFAULT 1, -- How many holds a patron can have on a given bib
833
  `holds_per_record` SMALLINT(6) NOT NULL DEFAULT 1, -- How many holds a patron can have on a given bib
834
  `holds_per_day` SMALLINT(6) DEFAULT NULL, -- How many holds a patron can have on a day
834
  `holds_per_day` SMALLINT(6) DEFAULT NULL, -- How many holds a patron can have on a day
835
  `holdspickupwait` int(11)  default NULL, -- How many open library days a hold can wait in the pickup shelf until it becomes problematic
835
  `branchcode` varchar(10) NOT NULL default '', -- the branch this rule is for (branches.branchcode)
836
  `branchcode` varchar(10) NOT NULL default '', -- the branch this rule is for (branches.branchcode)
836
  overduefinescap decimal(28,6) default NULL, -- the maximum amount of an overdue fine
837
  overduefinescap decimal(28,6) default NULL, -- the maximum amount of an overdue fine
837
  cap_fine_to_replacement_price BOOLEAN NOT NULL DEFAULT  '0', -- cap the fine based on item's replacement price
838
  cap_fine_to_replacement_price BOOLEAN NOT NULL DEFAULT  '0', -- cap the fine based on item's replacement price
Lines 1817-1822 CREATE TABLE `reserves` ( -- information related to holds/reserves in Koha Link Here
1817
  `suspend_until` DATETIME NULL DEFAULT NULL,
1818
  `suspend_until` DATETIME NULL DEFAULT NULL,
1818
  `itemtype` VARCHAR(10) NULL DEFAULT NULL, -- If record level hold, the optional itemtype of the item the patron is requesting
1819
  `itemtype` VARCHAR(10) NULL DEFAULT NULL, -- If record level hold, the optional itemtype of the item the patron is requesting
1819
  `item_level_hold` BOOLEAN NOT NULL DEFAULT 0, -- Is the hpld placed at item level
1820
  `item_level_hold` BOOLEAN NOT NULL DEFAULT 0, -- Is the hpld placed at item level
1821
  `lastpickupdate` date NULL DEFAULT NULL, -- the last day this hold is available for pickup, until it becomes problematic
1820
  PRIMARY KEY (`reserve_id`),
1822
  PRIMARY KEY (`reserve_id`),
1821
  KEY priorityfoundidx (priority,found),
1823
  KEY priorityfoundidx (priority,found),
1822
  KEY `borrowernumber` (`borrowernumber`),
1824
  KEY `borrowernumber` (`borrowernumber`),
Lines 1857-1862 CREATE TABLE `old_reserves` ( -- this table holds all holds/reserves that have b Link Here
1857
  `suspend_until` DATETIME NULL DEFAULT NULL, -- the date this hold is suspended until (NULL for infinitely)
1859
  `suspend_until` DATETIME NULL DEFAULT NULL, -- the date this hold is suspended until (NULL for infinitely)
1858
  `itemtype` VARCHAR(10) NULL DEFAULT NULL, -- If record level hold, the optional itemtype of the item the patron is requesting
1860
  `itemtype` VARCHAR(10) NULL DEFAULT NULL, -- If record level hold, the optional itemtype of the item the patron is requesting
1859
  `item_level_hold` BOOLEAN NOT NULL DEFAULT 0, -- Is the hpld placed at item level
1861
  `item_level_hold` BOOLEAN NOT NULL DEFAULT 0, -- Is the hpld placed at item level
1862
  `lastpickupdate` date NULL DEFAULT NULL, -- the last day this hold is available for pickup, until it becomes problematic
1860
  PRIMARY KEY (`reserve_id`),
1863
  PRIMARY KEY (`reserve_id`),
1861
  KEY `old_reserves_borrowernumber` (`borrowernumber`),
1864
  KEY `old_reserves_borrowernumber` (`borrowernumber`),
1862
  KEY `old_reserves_biblionumber` (`biblionumber`),
1865
  KEY `old_reserves_biblionumber` (`biblionumber`),
(-)a/installer/data/mysql/sysprefs.sql (-3 / +1 lines)
Lines 175-185 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
175
('expandedSearchOption','0',NULL,'If ON, set advanced search to be expanded by default','YesNo'),
175
('expandedSearchOption','0',NULL,'If ON, set advanced search to be expanded by default','YesNo'),
176
('ExpireReservesMaxPickUpDelay','0','','Enabling this allows holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay','YesNo'),
176
('ExpireReservesMaxPickUpDelay','0','','Enabling this allows holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay','YesNo'),
177
('ExpireReservesMaxPickUpDelayCharge','0',NULL,'If ExpireReservesMaxPickUpDelay is enabled, and this field has a non-zero value, than a borrower whose waiting hold has expired will be charged this amount.','free'),
177
('ExpireReservesMaxPickUpDelayCharge','0',NULL,'If ExpireReservesMaxPickUpDelay is enabled, and this field has a non-zero value, than a borrower whose waiting hold has expired will be charged this amount.','free'),
178
('ExpireReservesOnHolidays', '1', NULL, 'If false, reserves at a library will not be canceled on days the library is not open.', 'YesNo'),
179
('ExcludeHolidaysFromMaxPickUpDelay', '0', NULL, 'If ON, reserves max pickup delay takes into accountthe closed days.', 'YesNo'),
178
('ExcludeHolidaysFromMaxPickUpDelay', '0', NULL, 'If ON, reserves max pickup delay takes into accountthe closed days.', 'YesNo'),
180
('ExportCircHistory', 0, NULL, "Display the export circulation options",  'YesNo' ),
179
('ExportCircHistory', 0, NULL, "Display the export circulation options",  'YesNo' ),
181
('ExportRemoveFields','',NULL,'List of fields for non export in circulation.pl (separated by a space)','Free'),
180
('ExportRemoveFields','',NULL,'List of fields for non export in circulation.pl (separated by a space)','Free'),
182
('ExtendedPatronAttributes','1',NULL,'Use extended patron IDs and attributes','YesNo'),
181
('ExtendedPatronAttributes','0',NULL,'Use extended patron IDs and attributes','YesNo'),
183
('FacetLabelTruncationLength','20',NULL,'Specify the facet max length in OPAC','Integer'),
182
('FacetLabelTruncationLength','20',NULL,'Specify the facet max length in OPAC','Integer'),
184
('FacetMaxCount','20',NULL,'Specify the max facet count for each category','Integer'),
183
('FacetMaxCount','20',NULL,'Specify the max facet count for each category','Integer'),
185
('FailedLoginAttempts','','','Number of login attempts before lockout the patron account','Integer'),
184
('FailedLoginAttempts','','','Number of login attempts before lockout the patron account','Integer'),
Lines 511-517 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
511
('RequestOnOpac','1',NULL,'If ON, globally enables patron holds on OPAC','YesNo'),
510
('RequestOnOpac','1',NULL,'If ON, globally enables patron holds on OPAC','YesNo'),
512
('RequireStrongPassword','1','','Require a strong login password for staff and patrons','YesNo'),
511
('RequireStrongPassword','1','','Require a strong login password for staff and patrons','YesNo'),
513
('ReservesControlBranch','PatronLibrary','ItemHomeLibrary|PatronLibrary','Branch checked for members reservations rights','Choice'),
512
('ReservesControlBranch','PatronLibrary','ItemHomeLibrary|PatronLibrary','Branch checked for members reservations rights','Choice'),
514
('ReservesMaxPickUpDelay','7','','Define the Maximum delay to pick up an item on hold','Integer'),
515
('ReservesNeedReturns','1','','If ON, a hold placed on an item available in this library must be checked-in, otherwise, a hold on a specific item, that is in the library & available is considered available','YesNo'),
513
('ReservesNeedReturns','1','','If ON, a hold placed on an item available in this library must be checked-in, otherwise, a hold on a specific item, that is in the library & available is considered available','YesNo'),
516
('RESTBasicAuth','0',NULL,'If enabled, Basic authentication is enabled for the REST API.','YesNo'),
514
('RESTBasicAuth','0',NULL,'If enabled, Basic authentication is enabled for the REST API.','YesNo'),
517
('RESTdefaultPageSize','20','','Default page size for endpoints listing objects','Integer'),
515
('RESTdefaultPageSize','20','','Default page size for endpoints listing objects','Integer'),
(-)a/installer/data/mysql/updatedatabase.pl (+4 lines)
Lines 37-42 use Getopt::Long; Link Here
37
# Koha modules
37
# Koha modules
38
use C4::Context;
38
use C4::Context;
39
use C4::Installer;
39
use C4::Installer;
40
use C4::Reserves;
41
use DateTime::Duration;
40
use Koha::Database;
42
use Koha::Database;
41
use Koha;
43
use Koha;
42
use Koha::DateUtils;
44
use Koha::DateUtils;
Lines 16486-16491 if( CheckVersion( $DBversion ) ) { Link Here
16486
    print "Upgrade to $DBversion done (Bug 21403 - Add Indian Amazon Affiliate option to AmazonLocale setting)\n";
16488
    print "Upgrade to $DBversion done (Bug 21403 - Add Indian Amazon Affiliate option to AmazonLocale setting)\n";
16487
}
16489
}
16488
16490
16491
# SEE bug 13068
16492
# if there is anything in the atomicupdate, read and execute it.
16489
16493
16490
$DBversion = '18.06.00.036';
16494
$DBversion = '18.06.00.036';
16491
if( CheckVersion( $DBversion ) ) {
16495
if( CheckVersion( $DBversion ) ) {
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/waiting_holds.inc (-1 / +1 lines)
Lines 19-25 Link Here
19
    <tbody>
19
    <tbody>
20
        [% FOREACH reserveloo IN reserveloop %]
20
        [% FOREACH reserveloo IN reserveloop %]
21
            <tr>
21
            <tr>
22
                <td><span title="[% reserveloo.waitingdate | html %]">[% reserveloo.waitingdate | $KohaDates %]</span></td>
22
                <td><span title="[% reserveloo.waitingdate | html %]">[% reserveloo.waitingdate | $KohaDates %] - [% reserveloo.lastpickupdate | $KohaDates %]</span></td>
23
                <td><span title="[% reserveloo.reservedate | html %]">[% reserveloo.reservedate | $KohaDates %]</span></td>
23
                <td><span title="[% reserveloo.reservedate | html %]">[% reserveloo.reservedate | $KohaDates %]</span></td>
24
                <td>[% INCLUDE 'biblio-default-view.inc' biblionumber = reserveloo.biblionumber %]
24
                <td>[% INCLUDE 'biblio-default-view.inc' biblionumber = reserveloo.biblionumber %]
25
                        [% INCLUDE 'biblio-title.inc' biblio=reserveloo.biblio %]
25
                        [% INCLUDE 'biblio-title.inc' biblio=reserveloo.biblio %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/js/holds.js (+148 lines)
Line 0 Link Here
1
$(document).ready(function() {
2
    var holdsTable;
3
4
    // Don't load holds table unless it is clicked on
5
    $("#holds-tab").on( "click", function(){ load_holds_table() } );
6
7
    // If the holds tab is preselected on load, we need to load the table
8
    if ( $("#holds-tab").parent().hasClass('ui-state-active') ) { load_holds_table() }
9
10
    function load_holds_table() {
11
        if ( ! holdsTable ) {
12
            holdsTable = $("#holds-table").dataTable({
13
                "bAutoWidth": false,
14
                "sDom": "rt",
15
                "aoColumns": [
16
                    {
17
                        "mDataProp": "reservedate_formatted"
18
                    },
19
                    {
20
                        "mDataProp": "lastpickupdate_formatted"
21
                    },
22
                    {
23
                        "mDataProp": function ( oObj ) {
24
                            title = "<a href='/cgi-bin/koha/reserve/request.pl?biblionumber="
25
                                  + oObj.biblionumber
26
                                  + "'>"
27
                                  + oObj.title;
28
29
                            $.each(oObj.subtitle, function( index, value ) {
30
                                      title += " " + value.subfield;
31
                            });
32
33
                            title += "</a>";
34
35
                            if ( oObj.author ) {
36
                                title += " " + BY.replace( "_AUTHOR_",  oObj.author );
37
                            }
38
39
                            if ( oObj.itemnotes ) {
40
                                var span_class = "";
41
                                if ( $.datepicker.formatDate('yy-mm-dd', new Date(oObj.issuedate) ) == ymd ) {
42
                                    span_class = "circ-hlt";
43
                                }
44
                                title += " - <span class='" + span_class + "'>" + oObj.itemnotes + "</span>"
45
                            }
46
47
                            return title;
48
                        }
49
                    },
50
                    {
51
                        "mDataProp": function( oObj ) {
52
                            return oObj.itemcallnumber || "";
53
                        }
54
                    },
55
                    {
56
                        "mDataProp": function( oObj ) {
57
                            var data = "";
58
59
                            if ( oObj.suspend == 1 ) {
60
                                data += "<p>" + HOLD_IS_SUSPENDED;
61
                                if ( oObj.suspend_until ) {
62
                                    data += " " + UNTIL.format( oObj.suspend_until_formatted );
63
                                }
64
                                data += "</p>";
65
                            }
66
67
                            if ( oObj.barcode ) {
68
                                data += "<em>";
69
                                if ( oObj.found == "W" ) {
70
71
                                    if ( oObj.waiting_here ) {
72
                                        data += ITEM_IS_WAITING_HERE;
73
                                    } else {
74
                                        data += ITEM_IS_WAITING;
75
                                        data += " " + AT.format( oObj.waiting_at );
76
                                    }
77
78
                                } else if ( oObj.transferred ) {
79
                                    data += ITEM_IS_IN_TRANSIT.format( oObj.from_branch, oObj.date_sent );
80
                                } else if ( oObj.not_transferred ) {
81
                                    data += NOT_TRANSFERRED_YET.format( oObj.not_transferred_by );
82
                                }
83
                                data += "</em>";
84
85
                                data += " <a href='/cgi-bin/koha/catalogue/detail.pl?biblionumber="
86
                                  + oObj.biblionumber
87
                                  + "&itemnumber="
88
                                  + oObj.itemnumber
89
                                  + "#"
90
                                  + oObj.itemnumber
91
                                  + "'>"
92
                                  + oObj.barcode
93
                                  + "</a>";
94
                            }
95
96
                            return data;
97
                        }
98
                    },
99
                    {
100
                        "mDataProp": function( oObj ) {
101
                            return oObj.branchcode || "";
102
                        }
103
                    },
104
                    { "mDataProp": "expirationdate_formatted" },
105
                    {
106
                        "mDataProp": function( oObj ) {
107
                            if ( oObj.priority && parseInt( oObj.priority ) && parseInt( oObj.priority ) > 0 ) {
108
                                return oObj.priority;
109
                            } else {
110
                                return "";
111
                            }
112
                        }
113
                    },
114
                    {
115
                        "bSortable": false,
116
                        "mDataProp": function( oObj ) {
117
                            return "<select name='rank-request'>"
118
                                 + "<option value='n'>" + NO + "</option>"
119
                                 + "<option value='del'>" + YES  + "</option>"
120
                                 + "</select>"
121
                                 + "<input type='hidden' name='biblionumber' value='" + oObj.biblionumber + "'>"
122
                                 + "<input type='hidden' name='borrowernumber' value='" + borrowernumber + "'>"
123
                                 + "<input type='hidden' name='reserve_id' value='" + oObj.reserve_id + "'>";
124
                        }
125
                    }
126
                ],
127
                "bPaginate": false,
128
                "bProcessing": true,
129
                "bServerSide": false,
130
                "sAjaxSource": '/cgi-bin/koha/svc/holds',
131
                "fnServerData": function ( sSource, aoData, fnCallback ) {
132
                    aoData.push( { "name": "borrowernumber", "value": borrowernumber } );
133
134
                    $.getJSON( sSource, aoData, function (json) {
135
                        fnCallback(json)
136
                    } );
137
                },
138
            });
139
140
            if ( $("#holds-table").length ) {
141
                $("#holds-table_processing").position({
142
                    of: $( "#holds-table" ),
143
                    collision: "none"
144
                });
145
            }
146
        }
147
    }
148
});
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (-7 / +2 lines)
Lines 629-646 Circulation: Link Here
629
              choices:
629
              choices:
630
                  ItemHomeLibrary: "item's home library"
630
                  ItemHomeLibrary: "item's home library"
631
                  PatronLibrary: "patron's home library"
631
                  PatronLibrary: "patron's home library"
632
            - to see if the patron can place a hold on the item.    
632
            - to see if the patron can place a hold on the item.
633
        -
634
            - Mark a hold as problematic if it has been waiting for more than
635
            - pref: ReservesMaxPickUpDelay
636
              class: integer
637
            - days.
638
        -
633
        -
639
            - pref: ExpireReservesMaxPickUpDelay
634
            - pref: ExpireReservesMaxPickUpDelay
640
              choices:
635
              choices:
641
                  yes: Allow
636
                  yes: Allow
642
                  no: "Don't allow"
637
                  no: "Don't allow"
643
            - "holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay.<br><strong>NOTE:</strong> This system preference requires the <code>misc/cronjobs/holds/cancel_expired_holds.pl</code> cronjob. Ask your system administrator to schedule it."
638
            - "holds to expire automatically if they have not been picked by within the time period specified in hold pickup delay defined in the issuing rules"
644
        -
639
        -
645
            - If using ExpireReservesMaxPickUpDelay, charge a borrower who allows their waiting hold to expire a fee of
640
            - If using ExpireReservesMaxPickUpDelay, charge a borrower who allows their waiting hold to expire a fee of
646
            - pref: ExpireReservesMaxPickUpDelayCharge
641
            - pref: ExpireReservesMaxPickUpDelayCharge
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt (+4 lines)
Lines 97-102 Link Here
97
                <th>Suspension charging interval</th>
97
                <th>Suspension charging interval</th>
98
                <th>Renewals allowed (count)</th>
98
                <th>Renewals allowed (count)</th>
99
                <th>Renewal period</th>
99
                <th>Renewal period</th>
100
                <th>Holds wait for pickup (day)</th>
100
                <th>No renewal before</th>
101
                <th>No renewal before</th>
101
                <th>Automatic renewal</th>
102
                <th>Automatic renewal</th>
102
                <th>No automatic renewal after</th>
103
                <th>No automatic renewal after</th>
Lines 201-206 Link Here
201
                            <td>[% rule.suspension_chargeperiod | html %]</td>
202
                            <td>[% rule.suspension_chargeperiod | html %]</td>
202
							<td>[% rule.renewalsallowed | html %]</td>
203
							<td>[% rule.renewalsallowed | html %]</td>
203
                            <td>[% rule.renewalperiod | html %]</td>
204
                            <td>[% rule.renewalperiod | html %]</td>
205
                            <td>[% rule.holdspickupwait | html %]</td>
204
                            <td>[% rule.norenewalbefore | html %]</td>
206
                            <td>[% rule.norenewalbefore | html %]</td>
205
                            <td>
207
                            <td>
206
                                [% IF ( rule.auto_renew ) %]
208
                                [% IF ( rule.auto_renew ) %]
Lines 313-318 Link Here
313
                    <td><input type="text" name="suspension_chargeperiod" id="suspension_chargeperiod" size="3" /> </td>
315
                    <td><input type="text" name="suspension_chargeperiod" id="suspension_chargeperiod" size="3" /> </td>
314
                    <td><input type="text" name="renewalsallowed" id="renewalsallowed" size="2" /></td>
316
                    <td><input type="text" name="renewalsallowed" id="renewalsallowed" size="2" /></td>
315
                    <td><input type="text" name="renewalperiod" id="renewalperiod" size="3" /></td>
317
                    <td><input type="text" name="renewalperiod" id="renewalperiod" size="3" /></td>
318
                    <td><input type="text" name="holdspickupwait" id="holdspickupwait" size="2" /></td>
316
                    <td><input type="text" name="norenewalbefore" id="norenewalbefore" size="3" /></td>
319
                    <td><input type="text" name="norenewalbefore" id="norenewalbefore" size="3" /></td>
317
                    <td>
320
                    <td>
318
                        <select name="auto_renew" id="auto_renew">
321
                        <select name="auto_renew" id="auto_renew">
Lines 379-384 Link Here
379
                      <th>Suspension charging interval</th>
382
                      <th>Suspension charging interval</th>
380
                      <th>Renewals allowed (count)</th>
383
                      <th>Renewals allowed (count)</th>
381
                      <th>Renewal period</th>
384
                      <th>Renewal period</th>
385
                      <th>Holds wait for pickup (day)</th>
382
                      <th>No renewal before</th>
386
                      <th>No renewal before</th>
383
                      <th>Automatic renewal</th>
387
                      <th>Automatic renewal</th>
384
                      <th>No automatic renewal after</th>
388
                      <th>No automatic renewal after</th>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt (+1 lines)
Lines 876-881 Link Here
876
                                        <thead>
876
                                        <thead>
877
                                            <tr>
877
                                            <tr>
878
                                                <th>Hold date</th>
878
                                                <th>Hold date</th>
879
                                                <th>Last pickup date</th>
879
                                                <th>Title</th>
880
                                                <th>Title</th>
880
                                                <th>Call number</th>
881
                                                <th>Call number</th>
881
                                                <th>Barcode</th>
882
                                                <th>Barcode</th>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/help/admin/smart-rules.tt (+165 lines)
Line 0 Link Here
1
[% INCLUDE 'help-top.inc' %]
2
3
<h1>Circulation and Fines Rules</h1>
4
5
<p>These rules define how your items are circulated, how/when fines are calculated and how holds are handled.</p>
6
7
<p>The rules are applied from most specific to less specific, using the first found in this order:</p>
8
9
<ul>
10
 <li>same library, same patron type, same item type</li>
11
 <li>same library, same patron type, all item type</li>
12
 <li>same library, all patron types, same item type</li>
13
 <li>same library, all patron types, all item types</li>
14
 <li>all libraries, same patron type, same item type</li>
15
 <li>all libraries, same patron type, all item types</li>
16
 <li>all libraries, all patron types, same item type</li>
17
 <li>all libraries, all patron types, all item types</li>
18
</ul>
19
20
<p>The CircControl and HomeOrHoldingBranch also come in to play when figuring out which circulation rule to follow.</p>
21
22
<ul>
23
 <li>If CircControl is set to "the library you are logged in at" circ rules will be selected based on the library you are logged in at</li>
24
 <li>If CircControl is set to "the library the patron is from" circ rules will be selected based on the patron's library</li>
25
 <li>If CircControl is set to "the library the item is from" circ rules will be selected based on the item's library where HomeOrHoldingBranch chooses if item's home library is used or holding library is used.</li>
26
 <li>If IndependentBranches is set to 'Prevent' then the value of HomeOrHoldingBranch is used in figuring out if the item can be checked out. If the item's home library does not match the logged in library, the item cannot be checked out unless you are a superlibrarian.</li>
27
</ul>
28
29
<p>If you are a single library system choose your branch name before creating rules (sometimes having only rules for the 'all libraries' option can cause issues with holds)</p>
30
31
<p style="color: #990000;">Important: At the very least you will need to set a default circulation rule. This rule should be set for all item types, all libraries and all patron categories. That will catch all instances that do not match a specific rule. When checking out if you do not have a rule for all libraries, all item types and all patron types then you may see patrons getting blocked from placing holds. You will also want a rule for your specific library set for all item types and all patron types to avoid this holds issue. Koha needs to know what rule to fall back on.</p>
32
33
<h4>Default Circulation Rules</h4>
34
35
<p>Using the issuing rules matrix you can define rules that depend on patron/item type combinations. To set your rules, choose a library from the pull down (or 'all libraries' if you want to apply these rules to all libraries)</p>
36
37
<p>From the matrix you can choose any combination of patron categories and item types to apply the rules to</p>
38
39
<ul>
40
 <li>First choose which patron category you'd like the rule to be applied to. If you leave this to 'All' it will apply to all patron categories</li>
41
 <li>Choose the 'Item Type' you would like this rule to apply to. If you leave this to 'All' it will apply to all item types</li>
42
 <li>Limit the number of items a patron can have checked out at the same time by entering a number in the 'Current Checkouts Allowed' field</li>
43
    <li>Define the period of time an item can be checked out to a patron by entering the number of units (days or hours) in the 'Loan Period' box.</li>
44
    <li>Choose which unit of time, Days or Hours, that the loan period and fines will be calculate in</li>
45
    <li>You can also define a hard due date for a specific patron category and item type. A hard due date ignores your usual circulation rules and makes it so that all items of the type defined are due on, before or after the date you specify.</li>
46
    <li>'Fine Amount' should have the amount you would like to charge for overdue items
47
<ul>
48
    <li style="color: #990000;">Important: Enter only numbers and decimal points (no currency symbols).</li>
49
</ul>
50
</li>
51
    <li>Enter the 'Fine Charging Interval' in the unit you set (ex. charge fines every 1 day, or every 2 hours)</li>
52
    <li>The 'Fine Grace Period' is the period of time an item can be overdue before you start charging fines.
53
<ul>
54
    <li style="color: #990000;">Important: This can only be set for the Day unit, not in Hours</li>
55
</ul>
56
</li>
57
    <li>The 'Overdue Fines Cap' is the maximum fine for this patron and item combination
58
<ul>
59
    <li style="color: #990000;">Important: If this field is left blank then Koha will not put a limit on the fines this item will accrue. A maximum fine amount can be set using the MaxFinesystem preference.</li>
60
</ul>
61
</li>
62
    <li>If your library 'fines' patrons by suspending their account you can enter the number of days their fine should be suspended in the 'Suspension in Days' field
63
<ul>
64
    <li style="color: #990000;">Important: This can only be set for the Day unit, not in Hours</li>
65
</ul>
66
</li>
67
    <li>You can also define the maximum number of days a patron will be suspended in the 'Max suspension duration' setting</li>
68
    <li>Next decide if the patron can renew this item type and if so, enter how many times they can renew it in the 'Renewals Allowed' box</li>
69
    <li>If you're allowing renewals you can control how long the renewal loan period will be (in the units you have chosen) in the 'Renewal period' box</li>
70
    <li><i>Holds wait for pickup (day)</i> - After a hold is caught and put waiting for pickup, the hold will wait for this many days until it becomes problematic. Set it to 0 or less to disable the expiration of waiting holds. This value respects the Calendar holidays, skipping the last pickup date to the next available open library day.</li>
71
    <li>If you're allowing renewals you can control how soon before the due date patrons can renew their materials with the 'No renewals before' box.
72
    <ul><li>Items can be renewed at any time if this value is left blank. Otherwise items can only be renewed if the item is before the number in units (days/hours) entered in this box.</li></ul></li>
73
    <li>You can enable automatic renewals for certain items/patrons if you'd like. This will renew automatically following your circulation rules unless there is a hold on the item
74
    <ul>
75
    <li style="color: #990000;">Important: You will need to enable the automatic renewal cron job for this to work.</li>
76
    <li style="color: #990000;">Important: This feature needs to have the "no renewal before" column filled in or it will auto renew everyday after the due date.</li>
77
    </ul>
78
    </li>
79
    <li>If the patron can place holds on this item type, enter the total numbers of items (of this type) that can be put on hold in the 'Holds Allowed' field</li>
80
    <li>Next you can decide if this patron/item combo are allowed to place holds on items that are on the shelf (or available in the library) or not. If you choose 'no' then items can only be placed on hold if checked out</li>
81
    <li>You can also decide if patrons are allowed to place item specific holds on the item type in question. The options are:
82
    <ul>
83
    <li>Allow: Will allow patrons the option to choose next available or item specific</li>
84
    <li>Don't allow: Will only allow patrons to choose next available</li>
85
    <li>Force: Will only allow patrons to choose an specific item</li>
86
    </ul></li>
87
    <li>Finally, if you charge a rental fee for the item type and want to give a specific patron type a discount on that fee, enter the percentage discount (without the % symbol) in the 'Rental Discount' field</li>
88
</ul>
89
90
<p>When finished, click 'Add' to save your changes. To modify a rule, create a new one with the same patron type and item type. If you would like to delete your rule, simply click the 'Delete' link to the right of the rule.</p>
91
92
<p>To save time you can clone rules from one library to another by choosing the clone option above the rules matrix.</p>
93
94
<p>After choosing to clone you will be presented with a confirmation message.</p>
95
96
<h4>Default Checkouts and Hold Policy</h4>
97
98
<p>You can set a default maximum number of checkouts and hold policy that will be used if none is defined below for a particular item type or category.</p>
99
100
<p>From this menu you can set a default to apply to all item types and patrons in the library.</p>
101
102
<ul>
103
    <li>In 'Total Current Checkouts Allowed' enter the total number of items patrons can have checked out at one time</li>
104
    <li>Control where patrons can place holds from using the 'Hold Policy' menu
105
<ul>
106
    <li>From Any Library: Patrons from any library may put this item on hold. (default if none is defined)</li>
107
    <li>From Home Library: Only patrons from the item's home library may put this book on hold.</li>
108
    <li>No Holds Allowed: No patron may put this book on hold.</li>
109
</ul>
110
</li>
111
    <li>Control where the item returns to once it is checked in
112
<ul>
113
    <li>Item returns home</li>
114
    <li>Item returns to issuing library</li>
115
    <li>Item floats
116
<ul>
117
    <li>When an item floats it stays where it was checked in and does not ever return 'home'</li>
118
</ul>
119
</li>
120
</ul>
121
</li>
122
 <li>Once your policy is set, you can unset it by clicking the 'Unset' link to the right of the rule</li>
123
</ul>
124
125
<h4>Checkouts Per Patron</h4>
126
127
<p>For this library, you can specify the maximum number of loans that a patron of a given category can make, regardless of the item type.</p>
128
129
<p>Tip: If the total amount loanable for a given patron category is left blank, no limit applies, except possibly for a limit you define for a specific item type.</p>
130
131
<h4>Item Hold Policies</h4>
132
133
<p>For this library, you can edit rules for given itemtypes, regardless of the patron's category. Currently, this means hold policies.</p>
134
135
<p>The various Hold Policies have the following effects:</p>
136
137
<ul>
138
    <li>From Any Library: Patrons from any library may put this item on hold. (default if none is defined)</li>
139
    <li>From Home Library: Only patrons from the item's home library may put this book on hold.</li>
140
    <li>No Holds Allowed: No patron may put this book on hold.</li>
141
</ul>
142
143
<p style="color: #990000;">Important: Note that if the system preference AllowHoldPolicyOverrideset to 'allow', these policies can be overridden by your circulation staff.</p>
144
145
<p style="color: #990000;">Important: These policies are based on the patron's home library, not the library that the reserving staff member is from.</p>
146
147
<p>The various Return Policies have the following effects:</p>
148
149
<ul>
150
    <li>Item returns home: The item will prompt the librarian to transfer the item to its home library
151
<ul>
152
    <li style="color: #990000;">Important: If the AutomaticItemReturnpreference is set to automatically transfer the items home, then a prompt will not appear</li>
153
</ul>
154
</li>
155
    <li>Item returns to issuing library: The item will prompt the librarian to transfer the item back to the library where it was checked out
156
<ul>
157
    <li style="color: #990000;">Important: If the AutomaticItemReturnpreference is set to automatically transfer the items home, then a prompt will not appear</li>
158
</ul>
159
</li>
160
    <li>Item floats: The item will not be transferred from the library it was checked in at, instead it will remain there until transferred manually or checked in at another library</li>
161
</ul>
162
163
<p><strong>See the full documentation for Circulation and Fine Rules in the <a href="http://manual.koha-community.org/[% helpVersion %]/en/patscirc.html#circfinerules">manual</a> (online).</strong></p>
164
165
[% INCLUDE 'help-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/help/circ/waitingreserves.tt (+11 lines)
Line 0 Link Here
1
[% INCLUDE 'help-top.inc' %]
2
3
<h1>Holds awaiting pickup</h1>
4
5
<p>This report will show all of the holds that are waiting for patrons to pick them up.</p>
6
7
<p>Items that have been on the hold shelf longer than you normally allow (based on the holds pickup delay defined in the issuing rules) will appear on the 'Holds Over' tab, they will not automatically be cancelled.</p>
8
9
<p><strong>See the full documentation for Holds Awaiting Pickup in the <a href="http://manual.koha-community.org/[% helpVersion %]/en/circreports.html#holdspickup">manual</a> (online).</strong></p>
10
11
[% INCLUDE 'help-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt (+1 lines)
Lines 766-771 Link Here
766
                                            <thead>
766
                                            <thead>
767
                                                <tr>
767
                                                <tr>
768
                                                    <th>Hold date</th>
768
                                                    <th>Hold date</th>
769
                                                    <th>Last pickup date</th>
769
                                                    <th>Title</th>
770
                                                    <th>Title</th>
770
                                                    <th>Call number</th>
771
                                                    <th>Call number</th>
771
                                                    <th>Barcode</th>
772
                                                    <th>Barcode</th>
(-)a/koha-tmpl/intranet-tmpl/prog/js/holds.js (+3 lines)
Lines 18-23 $(document).ready(function() { Link Here
18
                        "data": { _: "reservedate_formatted", "sort": "reservedate" }
18
                        "data": { _: "reservedate_formatted", "sort": "reservedate" }
19
                    },
19
                    },
20
                    {
20
                    {
21
                        "mDataProp": "lastpickupdate_formatted"
22
                    },
23
                    {
21
                        "mDataProp": function ( oObj ) {
24
                        "mDataProp": function ( oObj ) {
22
                            title = "<a href='/cgi-bin/koha/reserve/request.pl?biblionumber="
25
                            title = "<a href='/cgi-bin/koha/reserve/request.pl?biblionumber="
23
                                  + oObj.biblionumber
26
                                  + oObj.biblionumber
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/holds-table.inc (-4 / +1 lines)
Lines 107-116 Link Here
107
                                    [% IF ( HOLD.found ) %]
107
                                    [% IF ( HOLD.found ) %]
108
                                        Item waiting at <b> [% HOLD.branch.branchname | html %]</b>
108
                                        Item waiting at <b> [% HOLD.branch.branchname | html %]</b>
109
                                        [% IF ( HOLD.waitingdate ) %]
109
                                        [% IF ( HOLD.waitingdate ) %]
110
                                            since [% HOLD.waitingdate | $KohaDates %]
110
                                            since [% HOLD.waitingdate | $KohaDates %] until [% RESERVE.lastpickupdate | $KohaDates %]
111
                                            [% IF HOLD.expirationdate %]
112
                                                until [% HOLD.expirationdate | $KohaDates %]
113
                                            [% END %]
114
                                        [% END %]
111
                                        [% END %]
115
                                        <input type="hidden" name="pickup" value="[% HOLD.branchcode | html %]" />
112
                                        <input type="hidden" name="pickup" value="[% HOLD.branchcode | html %]" />
116
                                    [% ELSE %]
113
                                    [% ELSE %]
(-)a/svc/holds (-1 / +6 lines)
Lines 47-53 my $branch = C4::Context->userenv->{'branch'}; Link Here
47
my $schema = Koha::Database->new()->schema();
47
my $schema = Koha::Database->new()->schema();
48
48
49
my @sort_columns =
49
my @sort_columns =
50
  qw/reservedate title itemcallnumber barcode expirationdate priority/;
50
  qw/reservedate lastpickupdate title itemcallnumber barcode expirationdate priority/;
51
51
52
my $borrowernumber    = $input->param('borrowernumber');
52
my $borrowernumber    = $input->param('borrowernumber');
53
my $offset            = $input->param('iDisplayStart');
53
my $offset            = $input->param('iDisplayStart');
Lines 99-104 while ( my $h = $holds_rs->next() ) { Link Here
99
        branchcode     => $h->branch()->branchname(),
99
        branchcode     => $h->branch()->branchname(),
100
        branches       => $libraries,
100
        branches       => $libraries,
101
        reservedate    => $h->reservedate(),
101
        reservedate    => $h->reservedate(),
102
        lastpickupdate => $h->lastpickupdate(),
102
        expirationdate => $h->expirationdate(),
103
        expirationdate => $h->expirationdate(),
103
        suspend        => $h->suspend(),
104
        suspend        => $h->suspend(),
104
        suspend_until  => $h->suspend_until(),
105
        suspend_until  => $h->suspend_until(),
Lines 112-117 while ( my $h = $holds_rs->next() ) { Link Here
112
            { dt => dt_from_string( $h->reservedate() ), dateonly => 1 }
113
            { dt => dt_from_string( $h->reservedate() ), dateonly => 1 }
113
          )
114
          )
114
        : q{},
115
        : q{},
116
        lastpickupdate_formatted => $h->lastpickupdate() ? output_pref(
117
            { dt => dt_from_string( $h->lastpickupdate() ), dateonly => 1 }
118
          )
119
        : q{},
115
        suspend_until_formatted => $h->suspend_until() ? output_pref(
120
        suspend_until_formatted => $h->suspend_until() ? output_pref(
116
            { dt => dt_from_string( $h->suspend_until() ), dateonly => 1 }
121
            { dt => dt_from_string( $h->suspend_until() ), dateonly => 1 }
117
          )
122
          )
(-)a/t/db_dependent/Holds.t (-1 / +219 lines)
Lines 399-407 is(CanItemBeReserved($borrowernumbers[0], $itemnumber)->{status}, Link Here
399
    "CanItemBeReserved should use item home library rule when ReservesControlBranch set to 'ItemsHomeLibrary'");
399
    "CanItemBeReserved should use item home library rule when ReservesControlBranch set to 'ItemsHomeLibrary'");
400
400
401
($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem(
401
($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem(
402
<<<<<<< HEAD
402
    { homebranch => $branch_1, holdingbranch => $branch_1, itype => 'CAN' } , $biblio->biblionumber);
403
    { homebranch => $branch_1, holdingbranch => $branch_1, itype => 'CAN' } , $biblio->biblionumber);
403
is(CanItemBeReserved($borrowernumbers[0], $itemnumber)->{status}, 'OK',
404
is(CanItemBeReserved($borrowernumbers[0], $itemnumber)->{status}, 'OK',
404
    "CanItemBeReserved should return 'OK'");
405
    "CanItemBeReserved should return 'OK'");
406
=======
407
    { homebranch => 'CPL', holdingbranch => 'CPL', itype => 'CAN' } , $bibnum);
408
is(CanItemBeReserved($borrowernumbers[0], $itemnumber), 'OK',
409
    "CanItemBeReserved should returns 'OK'");
410
411
##Setting duration variables
412
my $now = DateTime->now();
413
my $minus4days = DateTime::Duration->new(days => -4);
414
my $minus1days = DateTime::Duration->new(days => -1);
415
my $plus1days = DateTime::Duration->new(days => 1);
416
my $plus4days = DateTime::Duration->new(days => 4);
417
##Setting some test prerequisites testing environment
418
C4::Context->set_preference( 'ExpireReservesMaxPickUpDelay', 1 );
419
setSimpleCircPolicy();
420
setCalendars();
421
#Running more tests
422
testGetLastPickupDate();
423
testMoveWaitingdate();
424
testCancelExpiredReserves();
425
C4::Context->set_preference( 'ExpireReservesMaxPickUpDelay', 0 );
426
427
## Environment should be the following
428
## Holidays: days from today; -2,-3,-4
429
sub testCancelExpiredReserves {
430
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 1'); #Make holds problematize after 1 day
431
432
    $reserves = $dbh->selectall_arrayref('SELECT * FROM reserves WHERE found IS NULL', { Slice => {} });
433
    $reserve = $reserves->[0];
434
    #Catch this hold and make it Waiting for pickup today.
435
    C4::Reserves::ModReserveAffect( $reserve->{itemnumber}, $reserve->{borrowernumber} );
436
    $reserve = C4::Reserves::GetReserve( $reserve->{reserve_id} ); #UPDATE DB changes to local scope.
437
438
    CancelExpiredReserves();
439
    my $count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
440
    is( $count, 1, "Waiting reserve with lastpickupdate for ".$reserve->{lastpickupdate}." not canceled" );
441
442
    C4::Reserves::MoveWaitingdate( $reserve, DateTime::Duration->new(days => -4) );
443
    CancelExpiredReserves();
444
    $count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
445
    is( $count, 0, "Waiting reserve with lastpickupdate for ".$reserve->{lastpickupdate}." totally canceled" );
446
447
    # Test expirationdate
448
    $reserve = $reserves->[1];
449
    $dbh->do("UPDATE reserves SET expirationdate = DATE_SUB( NOW(), INTERVAL 1 DAY ) WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
450
    CancelExpiredReserves();
451
    $count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
452
    is( $count, 0, "Reserve with manual expiration date canceled correctly" );
453
454
    #This test verifies that reserves with holdspickupwait disabled are not ćanceled!
455
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 0'); #Make holds never problematize
456
    $reserve = $reserves->[2];
457
    #Catch this hold and make it Waiting for pickup today.
458
    C4::Reserves::ModReserveAffect( $reserve->{itemnumber}, $reserve->{borrowernumber} );
459
    $reserve = C4::Reserves::GetReserve( $reserve->{reserve_id} ); #UPDATE DB changes to local scope.
460
    #Move the caught reserve 4 days to past and try to cancel it.
461
    C4::Reserves::MoveWaitingdate( $reserve, $minus4days );
462
    CancelExpiredReserves();
463
    $count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
464
    is( $count, 1, "CancelExpiredReserves(): not canceling lastpickupdate-less hold." );
465
}
466
467
## Environment should be the following
468
## Holidays: days from today; -2,-3,-4
469
sub testMoveWaitingdate {
470
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 1'); #Make holds problematize after 1 day
471
472
    $reserves = $dbh->selectall_arrayref('SELECT * FROM reserves WHERE found IS NULL', { Slice => {} }); #Get reserves not waiting for pickup
473
    $reserve = $reserves->[0];
474
475
    C4::Reserves::ModReserveAffect( $reserve->{itemnumber}, $reserve->{borrowernumber} ); #Catch the reserve and put it to wait for pickup, now we get a waitingdate generated.
476
477
    C4::Reserves::MoveWaitingdate( $reserve, $minus1days );
478
    $reserve = C4::Reserves::GetReserve( $reserve_id ); #UPDATE DB changes to local scope. Actually MoveWaitingdate already updates changes to DB, but just making sure it does.
479
    is( ($reserve->{waitingdate} eq $now->clone()->add_duration($minus1days)->ymd() &&
480
         $reserve->{lastpickupdate} eq $now->ymd()),
481
         1, "MoveWaitingdate(): Moving to past");
482
    C4::Reserves::MoveWaitingdate( $reserve, $plus1days );
483
484
    C4::Reserves::MoveWaitingdate( $reserve, $plus4days );
485
    $reserve = C4::Reserves::GetReserve( $reserve_id );
486
    is( ($reserve->{waitingdate} eq $now->clone()->add_duration($plus4days)->ymd() &&
487
         $reserve->{lastpickupdate} eq $now->clone()->add_duration($plus4days)->add_duration($plus1days)->ymd()),
488
         1, "MoveWaitingdate(): Moving to future");
489
    C4::Reserves::MoveWaitingdate( $reserve, $minus4days );
490
}
491
492
## Environment should be the following
493
## Holidays: days from today; -2,-3,-4
494
sub testGetLastPickupDate {
495
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 1'); #Make holds problematize after 1 day
496
497
    my $now = DateTime->now();
498
    my $minus4days = DateTime::Duration->new(days => -4);
499
    my $minus1days = DateTime::Duration->new(days => -1);
500
    my $plus1days = DateTime::Duration->new(days => 1);
501
    my $plus4days = DateTime::Duration->new(days => 4);
502
503
    $reserves = $dbh->selectall_arrayref('SELECT * FROM reserves', { Slice => {} }); #Get reserves not waiting for pickup
504
    $reserve = $reserves->[0];
505
506
    $reserve->{waitingdate} = $now->clone()->add_duration($minus4days)->ymd();
507
    my $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve )->ymd();
508
    $reserve = C4::Reserves::GetReserve( $reserve_id ); #UPDATE DB changes to local scope
509
    is( $lastpickupdate, $now->clone()->add_duration($minus1days)->ymd(),
510
         "GetLastPickupDate(): Calendar finds the next open day for lastpickupdate.");
511
512
    $reserve->{waitingdate} = $now->clone()->add_duration($minus1days)->ymd();
513
    $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve )->ymd();
514
    is( $lastpickupdate, $now->ymd(),
515
         "GetLastPickupDate(): Not using Calendar");
516
517
    $reserve->{waitingdate} = $now->clone()->add_duration($plus4days)->ymd();
518
    $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve )->ymd();
519
    is( $lastpickupdate, $now->clone()->add_duration($plus4days)->add_duration($plus1days)->ymd(),
520
         "GetLastPickupDate(): Moving to future");
521
522
    #This test catches moving lastpickupdate for each holiday, instead of just moving the last date to an open library day
523
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 4'); #Make holds problematize after 4 days
524
    $reserve->{waitingdate} = $now->clone()->add_duration($minus4days)->ymd();
525
    $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve )->ymd();
526
    is( $lastpickupdate, $now->ymd(),
527
         "GetLastPickupDate(): Moving lastpickupdate over holidays, but not affected by them");
528
529
    #This test verifies that this feature is disabled and an undef is returned
530
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 0'); #Make holds never problematize
531
    $reserve->{waitingdate} = $now->clone()->add_duration($minus4days)->ymd();
532
    $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve );
533
    is( $reserve->{lastpickupdate}, undef,
534
         "GetLastPickupDate(): holdspickupwait disabled");
535
}
536
>>>>>>> Bug 8367: How long is a hold waiting for pickup at a more granular level
405
537
406
# Bug 12632
538
# Bug 12632
407
t::lib::Mocks::mock_preference( 'item-level_itypes',     1 );
539
t::lib::Mocks::mock_preference( 'item-level_itypes',     1 );
Lines 687-689 subtest 'CanItemBeReserved / holds_per_day tests' => sub { Link Here
687
819
688
    $schema->storage->txn_rollback;
820
    $schema->storage->txn_rollback;
689
};
821
};
690
- 
822
823
# Helper method to set up a Biblio.
824
sub create_helper_biblio {
825
    my $itemtype = $_[0] ? $_[0] : 'BK';
826
    my $bib = MARC::Record->new();
827
    my $title = 'Silence in the library';
828
    $bib->append_fields(
829
        MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'),
830
        MARC::Field->new('245', ' ', ' ', a => $title),
831
        MARC::Field->new('942', ' ', ' ', c => $itemtype),
832
    );
833
    return ($bibnum, $title, $bibitemnum) = AddBiblio($bib, '');
834
}
835
836
sub setSimpleCircPolicy {
837
    $dbh->do('DELETE FROM issuingrules');
838
    $dbh->do(
839
        q{INSERT INTO issuingrules (categorycode, branchcode, itemtype, reservesallowed,
840
                                    maxissueqty, issuelength, lengthunit,
841
                                    renewalsallowed, renewalperiod,
842
                                    norenewalbefore, auto_renew,
843
                                    fine, chargeperiod, holdspickupwait)
844
          VALUES (?, ?, ?, ?,
845
                  ?, ?, ?,
846
                  ?, ?,
847
                  ?, ?,
848
                  ?, ?, ?
849
                 )
850
        },
851
        {},
852
        '*', '*', '*', 25,
853
        20, 14, 'days',
854
        1, 7,
855
        '', 0,
856
        .10, 1,1
857
    );
858
}
859
860
###Set C4::Calendar and Koha::Calendar holidays for
861
# today -2 days
862
# today -3 days
863
# today -4 days
864
#
865
## Koha::Calendar for caching purposes (supposedly) doesn't work from the DB in this script
866
## So we must set the cache for Koha::calnder as well as the DB modifications for C4::Calendar.
867
## When making date comparisons with Koha::Calendar, using DateTime::Set, DateTime-objects
868
## need to match by the nanosecond and time_zone.
869
sub setCalendars {
870
871
    ##Set the C4::Calendar
872
    my $now = DateTime->now(time_zone => C4::Context->tz())->truncate(to => 'day');
873
    my $c4calendar = C4::Calendar->new(branchcode => $reserve->{branchcode});
874
    $now->add_duration( DateTime::Duration->new(days => -2) );
875
    $c4calendar->insert_single_holiday(
876
        day         => $now->day(),
877
        month       => $now->month(),
878
        year        => $now->year(),
879
        title       => 'Test',
880
        description => 'Test',
881
    );
882
    $now->add_duration( DateTime::Duration->new(days => -1) );
883
    $c4calendar->insert_single_holiday(
884
        day         => $now->day(),
885
        month       => $now->month(),
886
        year        => $now->year(),
887
        title       => 'Test',
888
        description => 'Test',
889
    );
890
    $now->add_duration( DateTime::Duration->new(days => -1) );
891
    $c4calendar->insert_single_holiday(
892
        day         => $now->day(),
893
        month       => $now->month(),
894
        year        => $now->year(),
895
        title       => 'Test',
896
        description => 'Test',
897
    );
898
899
    #Set the Koha::Calendar
900
    my $kohaCalendar = Koha::Calendar->new(branchcode => $reserve->{branchcode});
901
    $now = DateTime->now(time_zone => C4::Context->tz())->truncate(to => 'day');
902
    $now->add_duration( DateTime::Duration->new(days => -2) );
903
    $kohaCalendar->add_holiday( $now );
904
    $now->add_duration( DateTime::Duration->new(days => -1) );
905
    $kohaCalendar->add_holiday( $now );
906
    $now->add_duration( DateTime::Duration->new(days => -1) );
907
    $kohaCalendar->add_holiday( $now );
908
}

Return to bug 8367