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

(-)a/C4/Letters.pm (-15 lines)
Lines 622-642 sub _parseletter_sth { Link Here
622
sub _parseletter {
622
sub _parseletter {
623
    my ( $letter, $table, $values ) = @_;
623
    my ( $letter, $table, $values ) = @_;
624
624
625
    if ( $table eq 'reserves' && $values->{'waitingdate'} ) {
626
        my @waitingdate = split /-/, $values->{'waitingdate'};
627
628
        $values->{'expirationdate'} = '';
629
        if( C4::Context->preference('ExpireReservesMaxPickUpDelay') &&
630
        C4::Context->preference('ReservesMaxPickUpDelay') ) {
631
            my $dt = dt_from_string();
632
            $dt->add( days => C4::Context->preference('ReservesMaxPickUpDelay') );
633
            $values->{'expirationdate'} = output_pref({ dt => $dt, dateonly => 1 });
634
        }
635
636
        $values->{'waitingdate'} = output_pref({ dt => dt_from_string( $values->{'waitingdate'} ), dateonly => 1 });
637
638
    }
639
640
    if ($letter->{content} && $letter->{content} =~ /<<today>>/) {
625
    if ($letter->{content} && $letter->{content} =~ /<<today>>/) {
641
        my $todaysdate = output_pref( DateTime->now() );
626
        my $todaysdate = output_pref( DateTime->now() );
642
        $letter->{content} =~ s/<<today>>/$todaysdate/go;
627
        $letter->{content} =~ s/<<today>>/$todaysdate/go;
(-)a/C4/Reserves.pm (-41 / +197 lines)
Lines 168-178 sub AddReserve { Link Here
168
	# Make room in reserves for this before those of a later reserve date
168
	# Make room in reserves for this before those of a later reserve date
169
	$priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority );
169
	$priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority );
170
    }
170
    }
171
    my $waitingdate;
171
    my ($waitingdate, $lastpickupdate);
172
172
173
    my $item = C4::Items::GetItem( $checkitem );
173
    # If the reserv had the waiting status, we had the value of the resdate
174
    # If the reserv had the waiting status, we had the value of the resdate
174
    if ( $found eq 'W' ) {
175
    if ( $found eq 'W' ) {
175
        $waitingdate = $resdate;
176
        $waitingdate = $resdate;
177
178
        #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.
179
        my $reserve = {borrowernumber => $borrowernumber, waitingdate => $waitingdate, branchcode => $branch};
180
        $lastpickupdate = GetLastPickupDate( $reserve, $item );
176
    }
181
    }
177
182
178
    #eval {
183
    #eval {
Lines 194-209 sub AddReserve { Link Here
194
    my $query = qq/
199
    my $query = qq/
195
        INSERT INTO reserves
200
        INSERT INTO reserves
196
            (borrowernumber,biblionumber,reservedate,branchcode,constrainttype,
201
            (borrowernumber,biblionumber,reservedate,branchcode,constrainttype,
197
            priority,reservenotes,itemnumber,found,waitingdate,expirationdate)
202
            priority,reservenotes,itemnumber,found,waitingdate,expirationdate,lastpickupdate)
198
        VALUES
203
        VALUES
199
             (?,?,?,?,?,
204
             (?,?,?,?,?,
200
             ?,?,?,?,?,?)
205
             ?,?,?,?,?,?,?)
201
    /;
206
    /;
202
    my $sth = $dbh->prepare($query);
207
    my $sth = $dbh->prepare($query);
203
    $sth->execute(
208
    $sth->execute(
204
        $borrowernumber, $biblionumber, $resdate, $branch,
209
        $borrowernumber, $biblionumber, $resdate, $branch,
205
        $const,          $priority,     $notes,   $checkitem,
210
        $const,          $priority,     $notes,   $checkitem,
206
        $found,          $waitingdate,	$expdate
211
        $found,          $waitingdate,  $expdate, $lastpickupdate
207
    );
212
    );
208
213
209
    # Send e-mail to librarian if syspref is active
214
    # Send e-mail to librarian if syspref is active
Lines 806-815 sub GetReservesToBranch { Link Here
806
811
807
sub GetReservesForBranch {
812
sub GetReservesForBranch {
808
    my ($frombranch) = @_;
813
    my ($frombranch) = @_;
809
    my $dbh = C4::Context->dbh;
810
814
811
    my $query = "
815
    my $dbh          = C4::Context->dbh;
812
        SELECT reserve_id,borrowernumber,reservedate,itemnumber,waitingdate
816
    my $query        = "
817
        SELECT reserve_id, borrowernumber, reservedate, itemnumber, waitingdate, lastpickupdate
813
        FROM   reserves 
818
        FROM   reserves 
814
        WHERE   priority='0'
819
        WHERE   priority='0'
815
        AND found='W'
820
        AND found='W'
Lines 874-879 sub GetReserveStatus { Link Here
874
    return ''; # empty string here will remove need for checking undef, or less log lines
879
    return ''; # empty string here will remove need for checking undef, or less log lines
875
}
880
}
876
881
882
=head2 GetLastPickupDate
883
884
  my $lastpickupdate = GetLastPickupDate($reserve, $item);
885
  my $lastpickupdate = GetLastPickupDate($reserve, $item, $borrower);
886
  my $lastpickupdate = GetLastPickupDate(undef,    $item);
887
888
Gets the last pickup date from the issuingrules for the given reserves-row and sets the
889
$reserve->{lastpickupdate}.-value.
890
If the reserves-row is not passed, function tries to figure it out from the item-row.
891
892
Calculating the last pickup date respects Calendar holidays and skips to the next open day.
893
894
If the issuingrule's holdspickupwait is 0 or less, means that the lastpickupdate-feature
895
is disabled for this kind of material.
896
897
@PARAM1 koha.reserves-row
898
@PARAM2 koha.items-row, If the reserve is not given, an item must be given to be
899
                        able to find a reservation
900
@PARAM3 koha.borrowers-row, OPTIONAL
901
RETURNS DateTime, depicting the last pickup date.
902
        OR undef if there is an error or if the issuingrules disable this feature for this material.
903
=cut
904
905
sub GetLastPickupDate {
906
    my ($reserve, $item, $borrower) = @_;
907
908
    ##Verify parameters
909
    if ( not defined $reserve and not defined $item ) {
910
        warn "C4::Reserves::GetMaxPickupDate(), is called without a reserve and an item";
911
        return;
912
    }
913
    if ( defined $reserve and not defined $item ) {
914
        $item = C4::Items::GetItem( $reserve->{itemnumber} );
915
    }
916
    unless ( defined $reserve ) {
917
        my $reserve = GetReservesFromItemnumber( $item->{itemnumber} );
918
    }
919
920
    my $date = $reserve->{waitingdate};
921
    unless ( $date ) { #It is possible that a reserve is just caught and it doesn't have a waitingdate yet.
922
        $date = DateTime->now( time_zone => C4::Context->tz() ); #So default to NOW()
923
    }
924
    else {
925
        $date = (ref $reserve->{waitingdate} eq 'DateTime') ? $reserve->{waitingdate}  :  dt_from_string($reserve->{waitingdate});
926
    }
927
    $borrower = C4::Members::GetMember( 'borrowernumber' => $reserve->{borrowernumber} ) unless $borrower;
928
929
    ##Get churning the LastPickupDate
930
    my $controlbranch = GetReservesControlBranch( $item, $borrower );
931
932
    my $issuingrule = C4::Circulation::GetIssuingRule( $borrower->{categorycode}, $item->{itype}, $controlbranch );
933
934
    if ( defined($issuingrule)
935
        && defined $issuingrule->{holdspickupwait}
936
        && $issuingrule->{holdspickupwait} > 0 ) { #If holdspickupwait is <= 0, it means this feature is disabled for this type of material.
937
938
        $date->add( days => $issuingrule->{holdspickupwait} );
939
940
        my $calendar = Koha::Calendar->new( branchcode => $reserve->{'branchcode'} );
941
        my $is_holiday = $calendar->is_holiday( $date );
942
943
        while ( $is_holiday ) {
944
            $date->add( days => 1 );
945
            $is_holiday = $calendar->is_holiday( $date );
946
        }
947
948
        $reserve->{lastpickupdate} = $date->ymd();
949
        return $date;
950
    }
951
    #Without explicitly setting the return value undef, the lastpickupdate-column is
952
    #  set as 0000-00-00 instead of NULL in DBD::MySQL.
953
    #This causes this hold to always expire any lastpickupdate check,
954
    #  effectively canceling it as soon as cancel_expired_holds.pl is ran.
955
    #This is exactly the opposite of disabling the autoexpire feature.
956
    #So please let me explicitly return undef, because Perl cant always handle everything.
957
    delete $reserve->{lastpickupdate};
958
    return undef;
959
}
960
=head2 GetReservesControlBranch
961
962
  $branchcode = &GetReservesControlBranch($borrower, $item)
963
964
Returns the branchcode to consider to check hold rules against
965
966
=cut
967
968
sub GetReservesControlBranch {
969
    my ( $borrower, $item ) = @_;
970
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
971
    my $hbr           = C4::Context->preference('HomeOrHoldingBranch') || "homebranch";
972
    my $branchcode    = "*";
973
    if ( $controlbranch eq "ItemHomeLibrary" ) {
974
        $branchcode = $item->{$hbr};
975
    } elsif ( $controlbranch eq "PatronLibrary" ) {
976
        $branchcode = $borrower->{branchcode};
977
    }
978
    return $branchcode;
979
}
980
877
=head2 CheckReserves
981
=head2 CheckReserves
878
982
879
  ($status, $reserve, $all_reserves) = &CheckReserves($itemnumber);
983
  ($status, $reserve, $all_reserves) = &CheckReserves($itemnumber);
Lines 1017-1051 sub CancelExpiredReserves { Link Here
1017
    }
1121
    }
1018
  
1122
  
1019
    # Cancel reserves that have been waiting too long
1123
    # Cancel reserves that have been waiting too long
1020
    if ( C4::Context->preference("ExpireReservesMaxPickUpDelay") ) {
1124
    if (C4::Context->preference("ExpireReservesMaxPickUpDelay")) {
1021
        my $max_pickup_delay = C4::Context->preference("ReservesMaxPickUpDelay");
1022
        my $charge = C4::Context->preference("ExpireReservesMaxPickUpDelayCharge");
1023
        my $cancel_on_holidays = C4::Context->preference('ExpireReservesOnHolidays');
1024
1125
1025
        my $today = dt_from_string();
1126
        my $today = dt_from_string();
1127
        my $charge = C4::Context->preference("ExpireReservesMaxPickUpDelayCharge");
1026
1128
1027
        my $query = "SELECT * FROM reserves WHERE TO_DAYS( NOW() ) - TO_DAYS( waitingdate ) > ? AND found = 'W' AND priority = 0";
1129
        my $query = "SELECT * FROM reserves WHERE NOW() > lastpickupdate AND found = 'W' AND priority = 0";
1028
        $sth = $dbh->prepare( $query );
1130
        $sth = $dbh->prepare( $query );
1029
        $sth->execute( $max_pickup_delay );
1131
        $sth->execute();
1030
1132
1031
        while ( my $res = $sth->fetchrow_hashref ) {
1133
        while ( my $res = $sth->fetchrow_hashref ) {
1032
            my $do_cancel = 1;
1033
            unless ( $cancel_on_holidays ) {
1034
                my $calendar = Koha::Calendar->new( branchcode => $res->{'branchcode'} );
1035
                my $is_holiday = $calendar->is_holiday( $today );
1036
1134
1037
                if ( $is_holiday ) {
1135
            if ( $charge ) {
1038
                    $do_cancel = 0;
1136
                manualinvoice($res->{'borrowernumber'}, $res->{'itemnumber'}, 'Hold waiting too long', 'F', $charge);
1039
                }
1040
            }
1137
            }
1041
1138
1042
            if ( $do_cancel ) {
1139
            CancelReserve({ reserve_id => $res->{'reserve_id'} });
1043
                if ( $charge ) {
1044
                    manualinvoice($res->{'borrowernumber'}, $res->{'itemnumber'}, 'Hold waiting too long', 'F', $charge);
1045
                }
1046
1047
                CancelReserve({ reserve_id => $res->{'reserve_id'} });
1048
            }
1049
        }
1140
        }
1050
    }
1141
    }
1051
1142
Lines 1284-1292 sub ModReserveStatus { Link Here
1284
    my ($itemnumber, $newstatus) = @_;
1375
    my ($itemnumber, $newstatus) = @_;
1285
    my $dbh = C4::Context->dbh;
1376
    my $dbh = C4::Context->dbh;
1286
1377
1287
    my $query = "UPDATE reserves SET found = ?, waitingdate = NOW() WHERE itemnumber = ? AND found IS NULL AND priority = 0";
1378
    my $now = dt_from_string;
1379
    my $reserve = $dbh->selectrow_hashref(q{
1380
        SELECT *
1381
        FROM reserves
1382
        WHERE itemnumber = ?
1383
            AND found IS NULL
1384
            AND priority = 0
1385
    }, {}, $itemnumber);
1386
    return unless $reserve;
1387
1388
    my $lastpickupdate = GetLastPickupDate( $reserve );
1389
    my $query = q{
1390
        UPDATE reserves
1391
        SET found = ?,
1392
            waitingdate = ?,
1393
            maxpickupdate = ?
1394
        WHERE itemnumber = ?
1395
            AND found IS NULL
1396
            AND priority = 0
1397
    };
1288
    my $sth_set = $dbh->prepare($query);
1398
    my $sth_set = $dbh->prepare($query);
1289
    $sth_set->execute( $newstatus, $itemnumber );
1399
    $sth_set->execute( $newstatus, $now, $lastpickupdate, $itemnumber );
1290
1400
1291
    if ( C4::Context->preference("ReturnToShelvingCart") && $newstatus ) {
1401
    if ( C4::Context->preference("ReturnToShelvingCart") && $newstatus ) {
1292
      CartToShelf( $itemnumber );
1402
      CartToShelf( $itemnumber );
Lines 1331-1359 sub ModReserveAffect { Link Here
1331
    # If we affect a reserve that has to be transfered, don't set to Waiting
1441
    # If we affect a reserve that has to be transfered, don't set to Waiting
1332
    my $query;
1442
    my $query;
1333
    if ($transferToDo) {
1443
    if ($transferToDo) {
1334
    $query = "
1444
        $query = "
1335
        UPDATE reserves
1445
            UPDATE reserves
1336
        SET    priority = 0,
1446
            SET    priority = 0,
1337
               itemnumber = ?,
1447
                   itemnumber = ?,
1338
               found = 'T'
1448
                   found = 'T'
1339
        WHERE borrowernumber = ?
1449
            WHERE borrowernumber = ?
1340
          AND biblionumber = ?
1450
              AND biblionumber = ?
1341
    ";
1451
        ";
1452
        $sth = $dbh->prepare($query);
1453
        $sth->execute( $itemnumber, $borrowernumber,$biblionumber);
1342
    }
1454
    }
1343
    else {
1455
    else {
1344
    # affect the reserve to Waiting as well.
1456
        # affect the reserve to Waiting as well.
1457
        my $item = C4::Items::GetItem( $itemnumber );
1458
        my $lastpickupdate = GetLastPickupDate( $request, $item );
1345
        $query = "
1459
        $query = "
1346
            UPDATE reserves
1460
            UPDATE reserves
1347
            SET     priority = 0,
1461
            SET     priority = 0,
1348
                    found = 'W',
1462
                    found = 'W',
1349
                    waitingdate = NOW(),
1463
                    waitingdate = NOW(),
1464
                    lastpickupdate = ?,
1350
                    itemnumber = ?
1465
                    itemnumber = ?
1351
            WHERE borrowernumber = ?
1466
            WHERE borrowernumber = ?
1352
              AND biblionumber = ?
1467
              AND biblionumber = ?
1353
        ";
1468
        ";
1469
        $sth = $dbh->prepare($query);
1470
        $sth->execute( $lastpickupdate, $itemnumber, $borrowernumber,$biblionumber);
1354
    }
1471
    }
1355
    $sth = $dbh->prepare($query);
1356
    $sth->execute( $itemnumber, $borrowernumber,$biblionumber);
1357
    _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber ) if ( !$transferToDo && !$already_on_shelf );
1472
    _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber ) if ( !$transferToDo && !$already_on_shelf );
1358
    _FixPriority( { biblionumber => $biblionumber } );
1473
    _FixPriority( { biblionumber => $biblionumber } );
1359
    if ( C4::Context->preference("ReturnToShelvingCart") ) {
1474
    if ( C4::Context->preference("ReturnToShelvingCart") ) {
Lines 1429-1434 sub GetReserveInfo { Link Here
1429
                   reserves.biblionumber,
1544
                   reserves.biblionumber,
1430
                   reserves.branchcode,
1545
                   reserves.branchcode,
1431
                   reserves.waitingdate,
1546
                   reserves.waitingdate,
1547
                   reserves.lastpickupdate,
1432
                   notificationdate,
1548
                   notificationdate,
1433
                   reminderdate,
1549
                   reminderdate,
1434
                   priority,
1550
                   priority,
Lines 1840-1845 sub _Findgroupreserve { Link Here
1840
               reserves.borrowernumber      AS borrowernumber,
1956
               reserves.borrowernumber      AS borrowernumber,
1841
               reserves.reservedate         AS reservedate,
1957
               reserves.reservedate         AS reservedate,
1842
               reserves.branchcode          AS branchcode,
1958
               reserves.branchcode          AS branchcode,
1959
               reserves.lastpickupdate      AS lastpickupdate,
1843
               reserves.cancellationdate    AS cancellationdate,
1960
               reserves.cancellationdate    AS cancellationdate,
1844
               reserves.found               AS found,
1961
               reserves.found               AS found,
1845
               reserves.reservenotes        AS reservenotes,
1962
               reserves.reservenotes        AS reservenotes,
Lines 1872-1877 sub _Findgroupreserve { Link Here
1872
               reserves.borrowernumber      AS borrowernumber,
1989
               reserves.borrowernumber      AS borrowernumber,
1873
               reserves.reservedate         AS reservedate,
1990
               reserves.reservedate         AS reservedate,
1874
               reserves.branchcode          AS branchcode,
1991
               reserves.branchcode          AS branchcode,
1992
               reserves.lastpickupdate      AS lastpickupdate,
1875
               reserves.cancellationdate    AS cancellationdate,
1993
               reserves.cancellationdate    AS cancellationdate,
1876
               reserves.found               AS found,
1994
               reserves.found               AS found,
1877
               reserves.reservenotes        AS reservenotes,
1995
               reserves.reservenotes        AS reservenotes,
Lines 1904-1909 sub _Findgroupreserve { Link Here
1904
               reserves.reservedate                AS reservedate,
2022
               reserves.reservedate                AS reservedate,
1905
               reserves.waitingdate                AS waitingdate,
2023
               reserves.waitingdate                AS waitingdate,
1906
               reserves.branchcode                 AS branchcode,
2024
               reserves.branchcode                 AS branchcode,
2025
               reserves.lastpickupdate             AS lastpickupdate,
1907
               reserves.cancellationdate           AS cancellationdate,
2026
               reserves.cancellationdate           AS cancellationdate,
1908
               reserves.found                      AS found,
2027
               reserves.found                      AS found,
1909
               reserves.reservenotes               AS reservenotes,
2028
               reserves.reservenotes               AS reservenotes,
Lines 2117-2122 sub MoveReserve { Link Here
2117
    }
2236
    }
2118
}
2237
}
2119
2238
2239
=head MoveWaitingdate
2240
2241
  #Move waitingdate two months and fifteen days forward.
2242
  my $dateDuration = DateTime::Duration->new( months => 2, days => 15 );
2243
  $reserve = MoveWaitingdate( $reserve, $dateDuration);
2244
2245
  #Move waitingdate one year and eleven days backwards.
2246
  my $dateDuration = DateTime::Duration->new( years => -1, days => -11 );
2247
  $reserve = MoveWaitingdate( $reserve, $dateDuration);
2248
2249
Moves the waitingdate and updates the lastpickupdate to match.
2250
If waitingdate is not defined, uses today.
2251
Is intended to be used from automated tests, because under normal library
2252
operations there should be NO REASON to move the waitingdate.
2253
2254
@PARAM1 koha.reserves-row, with waitingdate set.
2255
@PARAM2 DateTime::Duration, with the desired offset.
2256
RETURNS koha.reserve-row, with keys waitingdate and lastpickupdate updated.
2257
=cut
2258
sub MoveWaitingdate {
2259
    my ($reserve, $dateDuration) = @_;
2260
2261
    my $dt = dt_from_string( $reserve->{waitingdate} );
2262
    $dt->add_duration( $dateDuration );
2263
    $reserve->{waitingdate} = $dt->ymd();
2264
2265
    GetLastPickupDate( $reserve ); #Update the $reserve->{lastpickupdate}
2266
2267
    #UPDATE the DB part
2268
    my $dbh = C4::Context->dbh();
2269
    my $sth = $dbh->prepare( "UPDATE reserves SET waitingdate=?, lastpickupdate=? WHERE reserve_id=?" );
2270
    $sth->execute( $reserve->{waitingdate}, $reserve->{lastpickupdate}, $reserve->{reserve_id} );
2271
2272
    return $reserve;
2273
}
2274
2120
=head2 MergeHolds
2275
=head2 MergeHolds
2121
2276
2122
  MergeHolds($dbh,$to_biblio, $from_biblio);
2277
  MergeHolds($dbh,$to_biblio, $from_biblio);
Lines 2213-2219 sub RevertWaitingStatus { Link Here
2213
    SET
2368
    SET
2214
      priority = 1,
2369
      priority = 1,
2215
      found = NULL,
2370
      found = NULL,
2216
      waitingdate = NULL
2371
      waitingdate = NULL,
2372
      lastpickupdate = NULL,
2217
    WHERE
2373
    WHERE
2218
      reserve_id = ?
2374
      reserve_id = ?
2219
    ";
2375
    ";
(-)a/Koha/Schema/Result/Reserve.pm (+7 lines)
Lines 139-144 __PACKAGE__->table("reserves"); Link Here
139
  datetime_undef_if_invalid: 1
139
  datetime_undef_if_invalid: 1
140
  is_nullable: 1
140
  is_nullable: 1
141
141
142
=head2 lastpickupdate
143
  date_type: 'date'
144
  datetime_undef_if_invalid: 1
145
  is_nullable: 1
146
142
=cut
147
=cut
143
148
144
__PACKAGE__->add_columns(
149
__PACKAGE__->add_columns(
Lines 199-204 __PACKAGE__->add_columns( Link Here
199
    datetime_undef_if_invalid => 1,
204
    datetime_undef_if_invalid => 1,
200
    is_nullable => 1,
205
    is_nullable => 1,
201
  },
206
  },
207
  "lastpickupdate",
208
  { data_type => "date", datetime_undef_if_invalid => 1, is_nullable => 1 },
202
);
209
);
203
210
204
=head1 PRIMARY KEY
211
=head1 PRIMARY KEY
(-)a/admin/smart-rules.pl (-4 / +5 lines)
Lines 101-108 elsif ($op eq 'delete-branch-item') { Link Here
101
# save the values entered
101
# save the values entered
102
elsif ($op eq 'add') {
102
elsif ($op eq 'add') {
103
    my $sth_search = $dbh->prepare('SELECT COUNT(*) AS total FROM issuingrules WHERE branchcode=? AND categorycode=? AND itemtype=?');
103
    my $sth_search = $dbh->prepare('SELECT COUNT(*) AS total FROM issuingrules WHERE branchcode=? AND categorycode=? AND itemtype=?');
104
    my $sth_insert = $dbh->prepare('INSERT INTO issuingrules (branchcode, categorycode, itemtype, maxissueqty, renewalsallowed, renewalperiod, norenewalbefore, auto_renew, reservesallowed, issuelength, lengthunit, hardduedate, hardduedatecompare, fine, finedays, maxsuspensiondays, firstremind, chargeperiod,rentaldiscount, overduefinescap) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)');
104
    my $sth_insert = $dbh->prepare('INSERT INTO issuingrules (branchcode, categorycode, itemtype, maxissueqty, renewalsallowed, renewalperiod, norenewalbefore, auto_renew, reservesallowed, holdspickupwait, issuelength, lengthunit, hardduedate, hardduedatecompare, fine, finedays, maxsuspensiondays, firstremind, chargeperiod,rentaldiscount, overduefinescap) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)');
105
    my $sth_update=$dbh->prepare("UPDATE issuingrules SET fine=?, finedays=?, maxsuspensiondays=?, firstremind=?, chargeperiod=?, maxissueqty=?, renewalsallowed=?, renewalperiod=?, norenewalbefore=?, auto_renew=?, reservesallowed=?, issuelength=?, lengthunit = ?, hardduedate=?, hardduedatecompare=?, rentaldiscount=?, overduefinescap=?  WHERE branchcode=? AND categorycode=? AND itemtype=?");
105
    my $sth_update=$dbh->prepare("UPDATE issuingrules SET fine=?, finedays=?, maxsuspensiondays=?, firstremind=?, chargeperiod=?, maxissueqty=?, renewalsallowed=?, renewalperiod=?, norenewalbefore=?, auto_renew=?, reservesallowed=?, holdspickupwait=?, issuelength=?, lengthunit = ?, hardduedate=?, hardduedatecompare=?, rentaldiscount=?, overduefinescap=?  WHERE branchcode=? AND categorycode=? AND itemtype=?");
106
    
106
    
107
    my $br = $branch; # branch
107
    my $br = $branch; # branch
108
    my $bor  = $input->param('categorycode'); # borrower category
108
    my $bor  = $input->param('categorycode'); # borrower category
Lines 120-125 elsif ($op eq 'add') { Link Here
120
    $norenewalbefore = undef if $norenewalbefore eq '0' or $norenewalbefore =~ /^\s*$/;
120
    $norenewalbefore = undef if $norenewalbefore eq '0' or $norenewalbefore =~ /^\s*$/;
121
    my $auto_renew = $input->param('auto_renew') eq 'yes' ? 1 : 0;
121
    my $auto_renew = $input->param('auto_renew') eq 'yes' ? 1 : 0;
122
    my $reservesallowed  = $input->param('reservesallowed');
122
    my $reservesallowed  = $input->param('reservesallowed');
123
    my $holdspickupwait = $input->param('holdspickupwait');
123
    $maxissueqty =~ s/\s//g;
124
    $maxissueqty =~ s/\s//g;
124
    $maxissueqty = undef if $maxissueqty !~ /^\d+/;
125
    $maxissueqty = undef if $maxissueqty !~ /^\d+/;
125
    my $issuelength  = $input->param('issuelength');
126
    my $issuelength  = $input->param('issuelength');
Lines 134-142 elsif ($op eq 'add') { Link Here
134
    $sth_search->execute($br,$bor,$cat);
135
    $sth_search->execute($br,$bor,$cat);
135
    my $res = $sth_search->fetchrow_hashref();
136
    my $res = $sth_search->fetchrow_hashref();
136
    if ($res->{total}) {
137
    if ($res->{total}) {
137
        $sth_update->execute($fine, $finedays, $maxsuspensiondays, $firstremind, $chargeperiod, $maxissueqty, $renewalsallowed, $renewalperiod, $norenewalbefore, $auto_renew, $reservesallowed, $issuelength,$lengthunit, $hardduedate,$hardduedatecompare,$rentaldiscount,$overduefinescap, $br,$bor,$cat);
138
        $sth_update->execute($fine, $finedays, $maxsuspensiondays, $firstremind, $chargeperiod, $maxissueqty, $renewalsallowed, $renewalperiod, $norenewalbefore, $auto_renew, $reservesallowed, $holdspickupwait, $issuelength,$lengthunit, $hardduedate,$hardduedatecompare,$rentaldiscount,$overduefinescap,   $br,$bor,$cat);
138
    } else {
139
    } else {
139
        $sth_insert->execute($br,$bor,$cat,$maxissueqty,$renewalsallowed, $renewalperiod, $norenewalbefore, $auto_renew, $reservesallowed,$issuelength,$lengthunit,$hardduedate,$hardduedatecompare,$fine,$finedays, $maxsuspensiondays, $firstremind,$chargeperiod,$rentaldiscount,$overduefinescap);
140
        $sth_insert->execute($br,$bor,$cat,   $maxissueqty, $renewalsallowed, $renewalperiod, $norenewalbefore, $auto_renew, $reservesallowed, $holdspickupwait, $issuelength, $lengthunit, $hardduedate, $hardduedatecompare, $fine, $finedays, $maxsuspensiondays, $firstremind, $chargeperiod, $rentaldiscount, $overduefinescap);
140
    }
141
    }
141
} 
142
} 
142
elsif ($op eq "set-branch-defaults") {
143
elsif ($op eq "set-branch-defaults") {
(-)a/circ/waitingreserves.pl (-31 / +25 lines)
Lines 27-43 use C4::Branch; # GetBranchName Link Here
27
use C4::Auth;
27
use C4::Auth;
28
use C4::Dates qw/format_date/;
28
use C4::Dates qw/format_date/;
29
use C4::Circulation;
29
use C4::Circulation;
30
use C4::Reserves;
30
use C4::Members;
31
use C4::Members;
31
use C4::Biblio;
32
use C4::Biblio;
32
use C4::Items;
33
use C4::Items;
33
34
34
use Date::Calc qw(
35
  Today
36
  Add_Delta_Days
37
  Date_to_Days
38
);
39
use C4::Reserves;
35
use C4::Reserves;
40
use C4::Koha;
36
use C4::Koha;
37
use Koha::DateUtils;
41
38
42
my $input = new CGI;
39
my $input = new CGI;
43
40
Lines 86-116 my ($reservcount, $overcount); Link Here
86
my @getreserves = $all_branches ? GetReservesForBranch() : GetReservesForBranch($default);
83
my @getreserves = $all_branches ? GetReservesForBranch() : GetReservesForBranch($default);
87
# get reserves for the branch we are logged into, or for all branches
84
# get reserves for the branch we are logged into, or for all branches
88
85
89
my $today = Date_to_Days(&Today);
86
my $today = dt_from_string;
90
foreach my $num (@getreserves) {
87
foreach my $num (@getreserves) {
91
    next unless ($num->{'waitingdate'} && $num->{'waitingdate'} ne '0000-00-00');
88
    next unless ($num->{'waitingdate'} && $num->{'waitingdate'} ne '0000-00-00');
92
89
93
    my $itemnumber = $num->{'itemnumber'};
90
    my $itemnumber = $num->{'itemnumber'};
94
    my $gettitle     = GetBiblioFromItemNumber( $itemnumber );
91
    my $gettitle     = GetBiblioFromItemNumber( $itemnumber );
95
    my $borrowernum = $num->{'borrowernumber'};
92
    my $borrowernumber = $num->{'borrowernumber'};
96
    my $holdingbranch = $gettitle->{'holdingbranch'};
93
    my $holdingbranch = $gettitle->{'holdingbranch'};
97
    my $homebranch = $gettitle->{'homebranch'};
94
    my $homebranch = $gettitle->{'homebranch'};
98
95
99
    my %getreserv = (
96
    my %getreserv = (
100
        itemnumber => $itemnumber,
97
        itemnumber => $itemnumber,
101
        borrowernum => $borrowernum,
98
        borrowernum => $borrowernumber,
102
    );
99
    );
103
100
104
    # fix up item type for display
101
    # fix up item type for display
105
    $gettitle->{'itemtype'} = C4::Context->preference('item-level_itypes') ? $gettitle->{'itype'} : $gettitle->{'itemtype'};
102
    $gettitle->{'itemtype'} = C4::Context->preference('item-level_itypes') ? $gettitle->{'itype'} : $gettitle->{'itemtype'};
106
    my $getborrower = GetMember(borrowernumber => $num->{'borrowernumber'});
103
    my $getborrower = GetMember(borrowernumber => $num->{'borrowernumber'});
107
    my $itemtypeinfo = getitemtypeinfo( $gettitle->{'itemtype'} );  # using the fixed up itype/itemtype
104
    my $itemtypeinfo = getitemtypeinfo( $gettitle->{'itemtype'} );  # using the fixed up itype/itemtype
108
    $getreserv{'waitingdate'} = $num->{'waitingdate'};
105
109
    my ( $waiting_year, $waiting_month, $waiting_day ) = split (/-/, $num->{'waitingdate'});
106
    if ( $num->{waitingdate} ) {
110
    ( $waiting_year, $waiting_month, $waiting_day ) =
107
        my $lastpickupdate = dt_from_string($num->{lastpickupdate});
111
      Add_Delta_Days( $waiting_year, $waiting_month, $waiting_day,
108
        $getreserv{waitingdate} = $num->{waitingdate};
112
        C4::Context->preference('ReservesMaxPickUpDelay'));
109
        $getreserv{lastpickupdate} = $num->{lastpickupdate};
113
    my $calcDate = Date_to_Days( $waiting_year, $waiting_month, $waiting_day );
110
        if ( DateTime->compare( $today, $lastpickupdate ) == 1 ) {
111
            if ($cancelall) {
112
                my $res = cancel( $itemnumber, $borrowernumber, $holdingbranch, $homebranch, !$transfer_when_cancel_all );
113
                push @cancel_result, $res if $res;
114
                next;
115
            } else {
116
                push @overloop,   \%getreserv;
117
                $overcount++;
118
            }
119
        }else{
120
            push @reservloop, \%getreserv;
121
            $reservcount++;
122
        }
123
    }
114
124
115
    $getreserv{'itemtype'}       = $itemtypeinfo->{'description'};
125
    $getreserv{'itemtype'}       = $itemtypeinfo->{'description'};
116
    $getreserv{'title'}          = $gettitle->{'title'};
126
    $getreserv{'title'}          = $gettitle->{'title'};
Lines 129-154 foreach my $num (@getreserves) { Link Here
129
    $getreserv{'borrowerfirstname'} = $getborrower->{'firstname'};
139
    $getreserv{'borrowerfirstname'} = $getborrower->{'firstname'};
130
    $getreserv{'borrowerphone'}     = $getborrower->{'phone'};
140
    $getreserv{'borrowerphone'}     = $getborrower->{'phone'};
131
141
132
    my $borEmail = GetFirstValidEmailAddress( $borrowernum );
142
    my $borEmail = GetFirstValidEmailAddress( $borrowernumber );
133
143
134
    if ( $borEmail ) {
144
    if ( $borEmail ) {
135
        $getreserv{'borrowermail'}  = $borEmail;
145
        $getreserv{'borrowermail'}  = $borEmail;
136
    }
146
    }
137
138
    if ($today > $calcDate) {
139
        if ($cancelall) {
140
            my $res = cancel( $itemnumber, $borrowernum, $holdingbranch, $homebranch, !$transfer_when_cancel_all );
141
            push @cancel_result, $res if $res;
142
            next;
143
        } else {
144
            push @overloop,   \%getreserv;
145
            $overcount++;
146
        }
147
    }else{
148
        push @reservloop, \%getreserv;
149
        $reservcount++;
150
    }
151
    
152
}
147
}
153
148
154
$template->param(cancel_result => \@cancel_result) if @cancel_result;
149
$template->param(cancel_result => \@cancel_result) if @cancel_result;
Lines 158-164 $template->param( Link Here
158
    overloop    => \@overloop,
153
    overloop    => \@overloop,
159
    overcount   => $overcount,
154
    overcount   => $overcount,
160
    show_date   => format_date(C4::Dates->today('iso')),
155
    show_date   => format_date(C4::Dates->today('iso')),
161
    ReservesMaxPickUpDelay => C4::Context->preference('ReservesMaxPickUpDelay')
162
);
156
);
163
157
164
if ($cancelall) {
158
if ($cancelall) {
(-)a/installer/data/mysql/kohastructure.sql (+3 lines)
Lines 1165-1170 CREATE TABLE `issuingrules` ( -- circulation and fine rules Link Here
1165
  `norenewalbefore` int(4) default NULL, -- no renewal allowed until X days or hours before due date. In the unit set in issuingrules.lengthunit
1165
  `norenewalbefore` int(4) default NULL, -- no renewal allowed until X days or hours before due date. In the unit set in issuingrules.lengthunit
1166
  `auto_renew` BOOLEAN default FALSE, -- automatic renewal
1166
  `auto_renew` BOOLEAN default FALSE, -- automatic renewal
1167
  `reservesallowed` smallint(6) NOT NULL default "0", -- how many holds are allowed
1167
  `reservesallowed` smallint(6) NOT NULL default "0", -- how many holds are allowed
1168
  `holdspickupwait` int(11)  default NULL, -- How many open library days a hold can wait in the pickup shelf until it becomes problematic
1168
  `branchcode` varchar(10) NOT NULL default '', -- the branch this rule is for (branches.branchcode)
1169
  `branchcode` varchar(10) NOT NULL default '', -- the branch this rule is for (branches.branchcode)
1169
  overduefinescap decimal(28,6) default NULL, -- the maximum amount of an overdue fine
1170
  overduefinescap decimal(28,6) default NULL, -- the maximum amount of an overdue fine
1170
  PRIMARY KEY  (`branchcode`,`categorycode`,`itemtype`),
1171
  PRIMARY KEY  (`branchcode`,`categorycode`,`itemtype`),
Lines 1636-1641 CREATE TABLE `old_reserves` ( -- this table holds all holds/reserves that have b Link Here
1636
  `lowestPriority` tinyint(1) NOT NULL, -- has this hold been pinned to the lowest priority in the holds queue (1 for yes, 0 for no)
1637
  `lowestPriority` tinyint(1) NOT NULL, -- has this hold been pinned to the lowest priority in the holds queue (1 for yes, 0 for no)
1637
  `suspend` BOOLEAN NOT NULL DEFAULT 0, -- in this hold suspended (1 for yes, 0 for no)
1638
  `suspend` BOOLEAN NOT NULL DEFAULT 0, -- in this hold suspended (1 for yes, 0 for no)
1638
  `suspend_until` DATETIME NULL DEFAULT NULL, -- the date this hold is suspended until (NULL for infinitely)
1639
  `suspend_until` DATETIME NULL DEFAULT NULL, -- the date this hold is suspended until (NULL for infinitely)
1640
  `lastpickupdate` date NULL DEFAULT NULL, -- the last day this hold is available for pickup, until it becomes problematic
1639
  PRIMARY KEY (`reserve_id`),
1641
  PRIMARY KEY (`reserve_id`),
1640
  KEY `old_reserves_borrowernumber` (`borrowernumber`),
1642
  KEY `old_reserves_borrowernumber` (`borrowernumber`),
1641
  KEY `old_reserves_biblionumber` (`biblionumber`),
1643
  KEY `old_reserves_biblionumber` (`biblionumber`),
Lines 1838-1843 CREATE TABLE `reserves` ( -- information related to holds/reserves in Koha Link Here
1838
  `lowestPriority` tinyint(1) NOT NULL,
1840
  `lowestPriority` tinyint(1) NOT NULL,
1839
  `suspend` BOOLEAN NOT NULL DEFAULT 0,
1841
  `suspend` BOOLEAN NOT NULL DEFAULT 0,
1840
  `suspend_until` DATETIME NULL DEFAULT NULL,
1842
  `suspend_until` DATETIME NULL DEFAULT NULL,
1843
  `lastpickupdate` date NULL DEFAULT NULL, -- the last day this hold is available for pickup, until it becomes problematic
1841
  PRIMARY KEY (`reserve_id`),
1844
  PRIMARY KEY (`reserve_id`),
1842
  KEY priorityfoundidx (priority,found),
1845
  KEY priorityfoundidx (priority,found),
1843
  KEY `borrowernumber` (`borrowernumber`),
1846
  KEY `borrowernumber` (`borrowernumber`),
(-)a/installer/data/mysql/sysprefs.sql (-2 lines)
Lines 116-122 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
116
('expandedSearchOption','0',NULL,'If ON, set advanced search to be expanded by default','YesNo'),
116
('expandedSearchOption','0',NULL,'If ON, set advanced search to be expanded by default','YesNo'),
117
('ExpireReservesMaxPickUpDelay','0','','Enabling this allows holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay','YesNo'),
117
('ExpireReservesMaxPickUpDelay','0','','Enabling this allows holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay','YesNo'),
118
('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'),
118
('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'),
119
('ExpireReservesOnHolidays', '1', NULL, 'If false, reserves at a library will not be canceled on days the library is not open.', 'YesNo'),
120
('ExtendedPatronAttributes','0',NULL,'Use extended patron IDs and attributes','YesNo'),
119
('ExtendedPatronAttributes','0',NULL,'Use extended patron IDs and attributes','YesNo'),
121
('FacetLabelTruncationLength','20',NULL,'Specify the facet max length in OPAC','Integer'),
120
('FacetLabelTruncationLength','20',NULL,'Specify the facet max length in OPAC','Integer'),
122
('FacetMaxCount','20',NULL,'Specify the max facet count for each category','Integer'),
121
('FacetMaxCount','20',NULL,'Specify the max facet count for each category','Integer'),
Lines 339-345 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
339
('RentalsInNoissuesCharge','1',NULL,'Rental charges block checkouts (added to noissuescharge).','YesNo'),
338
('RentalsInNoissuesCharge','1',NULL,'Rental charges block checkouts (added to noissuescharge).','YesNo'),
340
('RequestOnOpac','1',NULL,'If ON, globally enables patron holds on OPAC','YesNo'),
339
('RequestOnOpac','1',NULL,'If ON, globally enables patron holds on OPAC','YesNo'),
341
('ReservesControlBranch','PatronLibrary','ItemHomeLibrary|PatronLibrary','Branch checked for members reservations rights','Choice'),
340
('ReservesControlBranch','PatronLibrary','ItemHomeLibrary|PatronLibrary','Branch checked for members reservations rights','Choice'),
342
('ReservesMaxPickUpDelay','7','','Define the Maximum delay to pick up an item on hold','Integer'),
343
('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'),
341
('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'),
344
('ReturnBeforeExpiry','0',NULL,'If ON, checkout will be prevented if returndate is after patron card expiry','YesNo'),
342
('ReturnBeforeExpiry','0',NULL,'If ON, checkout will be prevented if returndate is after patron card expiry','YesNo'),
345
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
343
('ReturnLog','1',NULL,'If ON, enables the circulation (returns) log','YesNo'),
(-)a/installer/data/mysql/updatedatabase.pl (+31 lines)
Lines 37-42 use Getopt::Long; Link Here
37
use C4::Context;
37
use C4::Context;
38
use C4::Installer;
38
use C4::Installer;
39
use C4::Dates;
39
use C4::Dates;
40
use C4::Reserves;
41
use DateTime::Duration;
40
42
41
use MARC::Record;
43
use MARC::Record;
42
use MARC::File::XML ( BinaryEncoding => 'utf8' );
44
use MARC::File::XML ( BinaryEncoding => 'utf8' );
Lines 8891-8896 if ( CheckVersion($DBversion) ) { Link Here
8891
    SetVersion($DBversion);
8893
    SetVersion($DBversion);
8892
}
8894
}
8893
8895
8896
$DBversion = "3.17.00.XXX";
8897
if ( CheckVersion($DBversion) ) {
8898
    my $maxpickupdelay = C4::Context->preference('ReservesMaxPickUpDelay') || 0; #MaxPickupDelay
8899
    $dbh->do(q{ DELETE FROM systempreferences WHERE variable='ReservesMaxPickUpDelay'; });
8900
    $dbh->do(q{ DELETE FROM systempreferences WHERE variable='ExpireReservesOnHolidays'; });
8901
#        //DELETE FROM systempreferences WHERE variable='ExpireReservesMaxPickUpDelay'; #This syspref is not needed and would be better suited to be calculated from the holdspickupwait
8902
#        //ExpireReservesMaxPickUpDelayCharge #This could be added as a column to the issuing rules.
8903
    $dbh->do(q{ ALTER TABLE issuingrules ADD COLUMN holdspickupwait INT(11) NULL default NULL AFTER reservesallowed; });
8904
    $dbh->do(q{ ALTER TABLE reserves ADD COLUMN lastpickupdate DATE NULL default NULL AFTER suspend_until; });
8905
    $dbh->do(q{ ALTER TABLE old_reserves ADD COLUMN lastpickupdate DATE NULL default NULL AFTER suspend_until; });
8906
8907
    my $sth = $dbh->prepare(q{
8908
        UPDATE issuingrules SET holdspickupwait = ?
8909
    });
8910
    $sth->execute( $maxpickupdelay ) if $maxpickupdelay; #Don't want to accidentally nullify all!
8911
8912
    ##Populate the lastpickupdate-column from existing 'ReservesMaxPickUpDelay'
8913
    print "Populating the new lastpickupdate-column for all waiting holds. This might take a while.\n";
8914
    $sth = $dbh->prepare(q{ SELECT * FROM reserves WHERE found = 'W'; });
8915
    $sth->execute( );
8916
    my $dtdur = DateTime::Duration->new( days => 0 );
8917
    while ( my $res = $sth->fetchrow_hashref ) {
8918
        C4::Reserves::MoveWaitingdate( $res, $dtdur ); #We call MoveWaitingdate with a 0 duration to simply recalculate the lastpickupdate and store the new values to DB.
8919
    }
8920
8921
    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";
8922
    SetVersion($DBversion);
8923
}
8924
8894
=head1 FUNCTIONS
8925
=head1 FUNCTIONS
8895
8926
8896
=head2 TableExists($table)
8927
=head2 TableExists($table)
(-)a/koha-tmpl/intranet-tmpl/prog/en/js/holds.js (+3 lines)
Lines 11-16 $(document).ready(function() { Link Here
11
                        "mDataProp": "reservedate_formatted"
11
                        "mDataProp": "reservedate_formatted"
12
                    },
12
                    },
13
                    {
13
                    {
14
                        "mDataProp": "lastpickupdate_formatted"
15
                    },
16
                    {
14
                        "mDataProp": function ( oObj ) {
17
                        "mDataProp": function ( oObj ) {
15
                            title = "<a href='/cgi-bin/koha/reserve/request.pl?biblionumber="
18
                            title = "<a href='/cgi-bin/koha/reserve/request.pl?biblionumber="
16
                                  + oObj.biblionumber
19
                                  + oObj.biblionumber
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (-12 / +1 lines)
Lines 427-442 Circulation: Link Here
427
                  PatronLibrary: "patron's home library"
427
                  PatronLibrary: "patron's home library"
428
            - to see if the patron can place a hold on the item.    
428
            - to see if the patron can place a hold on the item.    
429
        -
429
        -
430
            - Mark a hold as problematic if it has been waiting for more than
431
            - pref: ReservesMaxPickUpDelay
432
              class: integer
433
            - days.
434
        -
435
            - pref: ExpireReservesMaxPickUpDelay
430
            - pref: ExpireReservesMaxPickUpDelay
436
              choices:
431
              choices:
437
                  yes: Allow
432
                  yes: Allow
438
                  no: "Don't allow"
433
                  no: "Don't allow"
439
            - "holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay"
434
            - "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"
440
        -
435
        -
441
            - If using ExpireReservesMaxPickUpDelay, charge a borrower who allows his or her waiting hold to expire a fee of
436
            - If using ExpireReservesMaxPickUpDelay, charge a borrower who allows his or her waiting hold to expire a fee of
442
            - pref: ExpireReservesMaxPickUpDelayCharge
437
            - pref: ExpireReservesMaxPickUpDelayCharge
Lines 511-522 Circulation: Link Here
511
                  no: "Don't allow"
506
                  no: "Don't allow"
512
            - holds to be suspended from the OPAC.
507
            - holds to be suspended from the OPAC.
513
        -
508
        -
514
            - pref: ExpireReservesOnHolidays
515
              choices:
516
                  yes: Allow
517
                  no: "Don't allow"
518
            - expired holds to be canceled on days the library is closed.
519
        -
520
            - pref: decreaseLoanHighHolds
509
            - pref: decreaseLoanHighHolds
521
              choices:
510
              choices:
522
                  yes: Enable
511
                  yes: Enable
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt (+4 lines)
Lines 117-122 $(document).ready(function() { Link Here
117
            <li>default (all libraries), all patron types, same item type</li>
117
            <li>default (all libraries), all patron types, same item type</li>
118
            <li>default (all libraries), all patron types, all item types</li>
118
            <li>default (all libraries), all patron types, all item types</li>
119
        </ul>
119
        </ul>
120
        <p>To get more information about how these settings affect Koha, hover your cursor over a column header.</p>
120
        <p>To modify a rule, create a new one with the same patron type and item type.</p>
121
        <p>To modify a rule, create a new one with the same patron type and item type.</p>
121
    </div>
122
    </div>
122
    <div>
123
    <div>
Lines 153-158 for="tobranch"><strong>Clone these rules to:</strong></label> <input type="hidde Link Here
153
                <th>Max. suspension duration (day)</th>
154
                <th>Max. suspension duration (day)</th>
154
                <th>Renewals allowed (count)</th>
155
                <th>Renewals allowed (count)</th>
155
                <th>Renewal period</th>
156
                <th>Renewal period</th>
157
                <th>Holds wait for pickup (day)</th>
156
                <th>No renewal before</th>
158
                <th>No renewal before</th>
157
                <th>Automatic renewal</th>
159
                <th>Automatic renewal</th>
158
                <th>Holds allowed (count)</th>
160
                <th>Holds allowed (count)</th>
Lines 209-214 for="tobranch"><strong>Clone these rules to:</strong></label> <input type="hidde Link Here
209
                            <td>[% rule.maxsuspensiondays %]</td>
211
                            <td>[% rule.maxsuspensiondays %]</td>
210
							<td>[% rule.renewalsallowed %]</td>
212
							<td>[% rule.renewalsallowed %]</td>
211
                            <td>[% rule.renewalperiod %]</td>
213
                            <td>[% rule.renewalperiod %]</td>
214
                            <td>[% rule.holdspickupwait %]</td>
212
                            <td>[% rule.norenewalbefore %]</td>
215
                            <td>[% rule.norenewalbefore %]</td>
213
                            <td>
216
                            <td>
214
                                [% IF ( rule.auto_renew ) %]
217
                                [% IF ( rule.auto_renew ) %]
Lines 267-272 for="tobranch"><strong>Clone these rules to:</strong></label> <input type="hidde Link Here
267
                    <td><input type="text" name="maxsuspensiondays" id="maxsuspensiondays" size="3" /> </td>
270
                    <td><input type="text" name="maxsuspensiondays" id="maxsuspensiondays" size="3" /> </td>
268
                    <td><input type="text" name="renewalsallowed" id="renewalsallowed" size="2" /></td>
271
                    <td><input type="text" name="renewalsallowed" id="renewalsallowed" size="2" /></td>
269
                    <td><input type="text" name="renewalperiod" id="renewalperiod" size="3" /></td>
272
                    <td><input type="text" name="renewalperiod" id="renewalperiod" size="3" /></td>
273
                    <td><input type="text" name="holdspickupwait" id="holdspickupwait" size="2" /></td>
270
                    <td><input type="text" name="norenewalbefore" id="norenewalbefore" size="3" /></td>
274
                    <td><input type="text" name="norenewalbefore" id="norenewalbefore" size="3" /></td>
271
                    <td>
275
                    <td>
272
                        <select name="auto_renew" id="auto_renew">
276
                        <select name="auto_renew" id="auto_renew">
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt (+1 lines)
Lines 776-781 No patron matched <span class="ex">[% message %]</span> Link Here
776
            <thead>
776
            <thead>
777
                <tr>
777
                <tr>
778
                    <th>Hold date</th>
778
                    <th>Hold date</th>
779
                    <th>Last pickup date</th>
779
                    <th>Title</th>
780
                    <th>Title</th>
780
                    <th>Call number</th>
781
                    <th>Call number</th>
781
                    <th>Barcode</th>
782
                    <th>Barcode</th>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/waitingreserves.tt (-4 / +4 lines)
Lines 77-83 Link Here
77
            [% IF ( reserveloop ) %]
77
            [% IF ( reserveloop ) %]
78
               <table id="holdst">
78
               <table id="holdst">
79
               <thead><tr>
79
               <thead><tr>
80
                    <th class="title-string">Available since</th>
80
                    <th class="title-string">Available since-until</th>
81
                    <th class="anti-the">Title</th>
81
                    <th class="anti-the">Title</th>
82
                    <th>Patron</th>
82
                    <th>Patron</th>
83
                    <th>Location</th>
83
                    <th>Location</th>
Lines 87-93 Link Here
87
               </tr></thead>
87
               </tr></thead>
88
               <tbody>[% FOREACH reserveloo IN reserveloop %]
88
               <tbody>[% FOREACH reserveloo IN reserveloop %]
89
                <tr>
89
                <tr>
90
                    <td><span title="[% reserveloo.waitingdate %]">[% reserveloo.waitingdate | $KohaDates %]</span></td>
90
                    <td><span title="[% reserveloo.waitingdate %]">[% reserveloo.waitingdate | $KohaDates %] - [% reserveloo.lastpickupdate | $KohaDates %]</span></td>
91
                    <td>[% INCLUDE 'biblio-default-view.inc' biblionumber = reserveloo.biblionumber %]
91
                    <td>[% INCLUDE 'biblio-default-view.inc' biblionumber = reserveloo.biblionumber %]
92
                        [% reserveloo.title |html %] [% reserveloo.subtitle |html %]
92
                        [% reserveloo.title |html %] [% reserveloo.subtitle |html %]
93
                        </a>
93
                        </a>
Lines 137-143 Link Here
137
               <br/>
137
               <br/>
138
               <table id="holdso">
138
               <table id="holdso">
139
               <thead><tr>
139
               <thead><tr>
140
                    <th class="title-string">Available since</th>
140
                    <th class="title-string">Available since-until</th>
141
                    <th class="anti-the">Title</th>
141
                    <th class="anti-the">Title</th>
142
                    <th>Patron</th>
142
                    <th>Patron</th>
143
                    <th>Location</th>
143
                    <th>Location</th>
Lines 147-153 Link Here
147
               </tr></thead>
147
               </tr></thead>
148
               <tbody>[% FOREACH overloo IN overloop %]
148
               <tbody>[% FOREACH overloo IN overloop %]
149
                    <tr>
149
                    <tr>
150
                        <td><p><span title="[% overloo.waitingdate %]">[% overloo.waitingdate | $KohaDates %]</span></p></td>
150
                        <td><p><span title="[% overloo.waitingdate %]">[% overloo.waitingdate | $KohaDates %] - [% overloo.lastpickupdate | $KohaDates %]</span></p></td>
151
                        <td>[% INCLUDE 'biblio-default-view.inc' biblionumber = overloo.biblionumber %][% overloo.title |html %] [% overloo.subtitle |html %]
151
                        <td>[% INCLUDE 'biblio-default-view.inc' biblionumber = overloo.biblionumber %][% overloo.title |html %] [% overloo.subtitle |html %]
152
                        </a>
152
                        </a>
153
                            [% UNLESS ( item_level_itypes ) %][% IF ( overloo.itemtype ) %]&nbsp; (<b>[% overloo.itemtype %]</b>)[% END %][% END %]
153
                            [% UNLESS ( item_level_itypes ) %][% IF ( overloo.itemtype ) %]&nbsp; (<b>[% overloo.itemtype %]</b>)[% END %][% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/help/admin/smart-rules.tt (+1 lines)
Lines 57-62 Link Here
57
</li>
57
</li>
58
    <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>
58
    <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>
59
    <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>
59
    <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>
60
    <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>
60
    <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.
61
    <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.
61
    <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>
62
    <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>
62
    <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>
63
    <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>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/help/circ/waitingreserves.tt (-1 / +1 lines)
Lines 4-10 Link Here
4
4
5
<p>This report will show all of the holds that are waiting for patrons to pick them up.</p>
5
<p>This report will show all of the holds that are waiting for patrons to pick them up.</p>
6
6
7
<p>Items that have been on the hold shelf longer than you normally allow (based on the ReservesMaxPickUpDelay preference value) will appear on the 'Holds Over' tab, they will not automatically be cancelled unless you have set the cron job to do that for you, but you can cancel all holds using the button at the top of the list.</p>
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
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>
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
10
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt (+1 lines)
Lines 522-527 function validate1(date) { Link Here
522
            <thead>
522
            <thead>
523
                <tr>
523
                <tr>
524
                    <th>Hold date</th>
524
                    <th>Hold date</th>
525
                    <th>Last pickup date</th>
525
                    <th>Title</th>
526
                    <th>Title</th>
526
                    <th>Call number</th>
527
                    <th>Call number</th>
527
                    <th>Barcode</th>
528
                    <th>Barcode</th>
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt (-1 / +1 lines)
Lines 560-566 Link Here
560
                                                [% IF ( RESERVE.wait ) %]
560
                                                [% IF ( RESERVE.wait ) %]
561
                                                    [% IF ( RESERVE.atdestination ) %]
561
                                                    [% IF ( RESERVE.atdestination ) %]
562
                                                        [% IF ( RESERVE.found ) %]
562
                                                        [% IF ( RESERVE.found ) %]
563
                                                            Item waiting at <b> [% RESERVE.wbrname %]</b>[% IF ( RESERVE.waitingdate ) %] since [% RESERVE.waitingdate | $KohaDates %][% END %]
563
                                                            Item waiting at <b> [% RESERVE.wbrname %]</b>[% IF ( RESERVE.waitingdate ) %] since [% RESERVE.waitingdate | $KohaDates %] until [% RESERVE.lastpickupdate | $KohaDates %][% END %]
564
                                                            <input type="hidden" name="pickup" value="[% RESERVE.wbrcd %]" />
564
                                                            <input type="hidden" name="pickup" value="[% RESERVE.wbrcd %]" />
565
                                                        [% ELSE %]
565
                                                        [% ELSE %]
566
                                                            Item waiting to be pulled from <b> [% RESERVE.wbrname %]</b>
566
                                                            Item waiting to be pulled from <b> [% RESERVE.wbrname %]</b>
(-)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 81-86 while ( my $h = $holds_rs->next() ) { Link Here
81
        author         => $h->biblio()->author(),
81
        author         => $h->biblio()->author(),
82
        reserve_id     => $h->reserve_id(),
82
        reserve_id     => $h->reserve_id(),
83
        reservedate    => $h->reservedate(),
83
        reservedate    => $h->reservedate(),
84
        lastpickupdate => $h->lastpickupdate(),
84
        expirationdate => $h->expirationdate(),
85
        expirationdate => $h->expirationdate(),
85
        suspend        => $h->suspend(),
86
        suspend        => $h->suspend(),
86
        suspend_until  => $h->suspend_until(),
87
        suspend_until  => $h->suspend_until(),
Lines 97-102 while ( my $h = $holds_rs->next() ) { Link Here
97
            { dt => dt_from_string( $h->reservedate() ), dateonly => 1 }
98
            { dt => dt_from_string( $h->reservedate() ), dateonly => 1 }
98
          )
99
          )
99
        : q{},
100
        : q{},
101
        lastpickupdate_formatted => $h->lastpickupdate() ? output_pref(
102
            { dt => dt_from_string( $h->lastpickupdate() ), dateonly => 1 }
103
          )
104
        : q{},
100
        suspend_until_formatted => $h->suspend_until() ? output_pref(
105
        suspend_until_formatted => $h->suspend_until() ? output_pref(
101
            { dt => dt_from_string( $h->suspend_until() ), dateonly => 1 }
106
            { dt => dt_from_string( $h->suspend_until() ), dateonly => 1 }
102
          )
107
          )
(-)a/t/db_dependent/Holds.t (-38 / +203 lines)
Lines 6-17 use t::lib::Mocks; Link Here
6
use C4::Context;
6
use C4::Context;
7
use C4::Branch;
7
use C4::Branch;
8
8
9
use Test::More tests => 38;
9
use Test::More tests => 46;
10
use MARC::Record;
10
use MARC::Record;
11
use C4::Biblio;
11
use C4::Biblio;
12
use C4::Items;
12
use C4::Items;
13
use C4::Members;
13
use C4::Members;
14
use C4::Calendar;
14
use C4::Calendar;
15
use Koha::Calendar;
16
use DateTime;
17
use DateTime::Duration;
15
18
16
use Koha::DateUtils qw( dt_from_string output_pref );
19
use Koha::DateUtils qw( dt_from_string output_pref );
17
20
Lines 328-372 ok( Link Here
328
    "cannot request item if policy that matches on bib-level item type forbids it (bug 9532)"
331
    "cannot request item if policy that matches on bib-level item type forbids it (bug 9532)"
329
);
332
);
330
333
331
# Test CancelExpiredReserves
334
332
C4::Context->set_preference('ExpireReservesMaxPickUpDelay', 1);
335
##Setting duration variables
333
C4::Context->set_preference('ReservesMaxPickUpDelay', 1);
336
my $now = DateTime->now();
334
337
my $minus4days = DateTime::Duration->new(days => -4);
335
my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time);
338
my $minus1days = DateTime::Duration->new(days => -1);
336
$year += 1900;
339
my $plus1days = DateTime::Duration->new(days => 1);
337
$mon += 1;
340
my $plus4days = DateTime::Duration->new(days => 4);
338
$reserves = $dbh->selectall_arrayref('SELECT * FROM reserves', { Slice => {} });
341
##Setting some test prerequisites testing environment
339
$reserve = $reserves->[0];
342
setSimpleCircPolicy();
340
my $calendar = C4::Calendar->new(branchcode => $reserve->{branchcode});
343
setCalendars();
341
$calendar->insert_single_holiday(
344
#Running more tests
342
    day         => $mday,
345
testGetLastPickupDate();
343
    month       => $mon,
346
testMoveWaitingdate();
344
    year        => $year,
347
testCancelExpiredReserves();
345
    title       => 'Test',
348
346
    description => 'Test',
349
## Environment should be the following
347
);
350
## Holidays: days from today; -2,-3,-4
348
$reserve_id = $reserve->{reserve_id};
351
sub testCancelExpiredReserves {
349
$dbh->do("UPDATE reserves SET waitingdate = DATE_SUB( NOW(), INTERVAL 5 DAY ), found = 'W', priority = 0 WHERE reserve_id = ?", undef, $reserve_id );
352
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 1'); #Make holds problematize after 1 day
350
C4::Context->set_preference('ExpireReservesOnHolidays', 0);
353
351
CancelExpiredReserves();
354
    $reserves = $dbh->selectall_arrayref('SELECT * FROM reserves WHERE found IS NULL', { Slice => {} });
352
my $count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve_id );
355
    $reserve = $reserves->[0];
353
is( $count, 1, "Waiting reserve beyond max pickup delay *not* canceled on holiday" );
356
    #Catch this hold and make it Waiting for pickup today.
354
C4::Context->set_preference('ExpireReservesOnHolidays', 1);
357
    C4::Reserves::ModReserveAffect( $reserve->{itemnumber}, $reserve->{borrowernumber} );
355
CancelExpiredReserves();
358
    $reserve = C4::Reserves::GetReserve( $reserve->{reserve_id} ); #UPDATE DB changes to local scope.
356
$count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve_id );
359
357
is( $count, 0, "Waiting reserve beyond max pickup delay canceled on holiday" );
360
    CancelExpiredReserves();
358
361
    my $count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
359
# Test expirationdate
362
    is( $count, 1, "Waiting reserve with lastpickupdate for ".$reserve->{lastpickupdate}." not canceled" );
360
$reserve = $reserves->[1];
363
361
$reserve_id = $reserve->{reserve_id};
364
    C4::Reserves::MoveWaitingdate( $reserve, DateTime::Duration->new(days => -4) );
362
$dbh->do("UPDATE reserves SET expirationdate = DATE_SUB( NOW(), INTERVAL 1 DAY ) WHERE reserve_id = ?", undef, $reserve_id );
365
    CancelExpiredReserves();
363
CancelExpiredReserves();
366
    $count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
364
$count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve_id );
367
    is( $count, 0, "Waiting reserve with lastpickupdate for ".$reserve->{lastpickupdate}." totally canceled" );
365
is( $count, 0, "Reserve with manual expiration date canceled correctly" );
368
369
    # Test expirationdate
370
    $reserve = $reserves->[1];
371
    $dbh->do("UPDATE reserves SET expirationdate = DATE_SUB( NOW(), INTERVAL 1 DAY ) WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
372
    CancelExpiredReserves();
373
    $count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
374
    is( $count, 0, "Reserve with manual expiration date canceled correctly" );
375
376
    #This test verifies that reserves with holdspickupwait disabled are not ćanceled!
377
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 0'); #Make holds never problematize
378
    $reserve = $reserves->[2];
379
    #Catch this hold and make it Waiting for pickup today.
380
    C4::Reserves::ModReserveAffect( $reserve->{itemnumber}, $reserve->{borrowernumber} );
381
    $reserve = C4::Reserves::GetReserve( $reserve->{reserve_id} ); #UPDATE DB changes to local scope.
382
    #Move the caught reserve 4 days to past and try to cancel it.
383
    C4::Reserves::MoveWaitingdate( $reserve, $minus4days );
384
    CancelExpiredReserves();
385
    $count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
386
    is( $count, 1, "CancelExpiredReserves(): not canceling lastpickupdate-less hold." );
387
}
388
389
## Environment should be the following
390
## Holidays: days from today; -2,-3,-4
391
sub testMoveWaitingdate {
392
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 1'); #Make holds problematize after 1 day
393
394
    $reserves = $dbh->selectall_arrayref('SELECT * FROM reserves WHERE found IS NULL', { Slice => {} }); #Get reserves not waiting for pickup
395
    $reserve = $reserves->[0];
396
397
    C4::Reserves::ModReserveAffect( $reserve->{itemnumber}, $reserve->{borrowernumber} ); #Catch the reserve and put it to wait for pickup, now we get a waitingdate generated.
398
399
    C4::Reserves::MoveWaitingdate( $reserve, $minus1days );
400
    $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.
401
    is( ($reserve->{waitingdate} eq $now->clone()->add_duration($minus1days)->ymd() &&
402
         $reserve->{lastpickupdate} eq $now->ymd()),
403
         1, "MoveWaitingdate(): Moving to past");
404
    C4::Reserves::MoveWaitingdate( $reserve, $plus1days );
405
406
    C4::Reserves::MoveWaitingdate( $reserve, $plus4days );
407
    $reserve = C4::Reserves::GetReserve( $reserve_id );
408
    is( ($reserve->{waitingdate} eq $now->clone()->add_duration($plus4days)->ymd() &&
409
         $reserve->{lastpickupdate} eq $now->clone()->add_duration($plus4days)->add_duration($plus1days)->ymd()),
410
         1, "MoveWaitingdate(): Moving to future");
411
    C4::Reserves::MoveWaitingdate( $reserve, $minus4days );
412
}
413
414
## Environment should be the following
415
## Holidays: days from today; -2,-3,-4
416
sub testGetLastPickupDate {
417
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 1'); #Make holds problematize after 1 day
418
419
    my $now = DateTime->now();
420
    my $minus4days = DateTime::Duration->new(days => -4);
421
    my $minus1days = DateTime::Duration->new(days => -1);
422
    my $plus1days = DateTime::Duration->new(days => 1);
423
    my $plus4days = DateTime::Duration->new(days => 4);
424
425
    $reserves = $dbh->selectall_arrayref('SELECT * FROM reserves', { Slice => {} }); #Get reserves not waiting for pickup
426
    $reserve = $reserves->[0];
427
428
    $reserve->{waitingdate} = $now->clone()->add_duration($minus4days)->ymd();
429
    my $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve )->ymd();
430
    $reserve = C4::Reserves::GetReserve( $reserve_id ); #UPDATE DB changes to local scope
431
    is( $lastpickupdate, $now->clone()->add_duration($minus1days)->ymd(),
432
         "GetLastPickupDate(): Calendar finds the next open day for lastpickupdate.");
433
434
    $reserve->{waitingdate} = $now->clone()->add_duration($minus1days)->ymd();
435
    $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve )->ymd();
436
    is( $lastpickupdate, $now->ymd(),
437
         "GetLastPickupDate(): Not using Calendar");
438
439
    $reserve->{waitingdate} = $now->clone()->add_duration($plus4days)->ymd();
440
    $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve )->ymd();
441
    is( $lastpickupdate, $now->clone()->add_duration($plus4days)->add_duration($plus1days)->ymd(),
442
         "GetLastPickupDate(): Moving to future");
443
444
    #This test catches moving lastpickupdate for each holiday, instead of just moving the last date to an open library day
445
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 4'); #Make holds problematize after 4 days
446
    $reserve->{waitingdate} = $now->clone()->add_duration($minus4days)->ymd();
447
    $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve )->ymd();
448
    is( $lastpickupdate, $now->ymd(),
449
         "GetLastPickupDate(): Moving lastpickupdate over holidays, but not affected by them");
450
451
    #This test verifies that this feature is disabled and an undef is returned
452
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 0'); #Make holds never problematize
453
    $reserve->{waitingdate} = $now->clone()->add_duration($minus4days)->ymd();
454
    $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve );
455
    is( $reserve->{lastpickupdate}, undef,
456
         "GetLastPickupDate(): holdspickupwait disabled");
457
}
366
458
367
# Helper method to set up a Biblio.
459
# Helper method to set up a Biblio.
368
sub create_helper_biblio {
460
sub create_helper_biblio {
369
    my $itemtype = shift;
461
    my $itemtype = $_[0] ? $_[0] : 'BK';
370
    my $bib = MARC::Record->new();
462
    my $bib = MARC::Record->new();
371
    my $title = 'Silence in the library';
463
    my $title = 'Silence in the library';
372
    $bib->append_fields(
464
    $bib->append_fields(
Lines 376-378 sub create_helper_biblio { Link Here
376
    );
468
    );
377
    return ($bibnum, $title, $bibitemnum) = AddBiblio($bib, '');
469
    return ($bibnum, $title, $bibitemnum) = AddBiblio($bib, '');
378
}
470
}
379
- 
471
472
sub setSimpleCircPolicy {
473
    $dbh->do('DELETE FROM issuingrules');
474
    $dbh->do(
475
        q{INSERT INTO issuingrules (categorycode, branchcode, itemtype, reservesallowed,
476
                                    maxissueqty, issuelength, lengthunit,
477
                                    renewalsallowed, renewalperiod,
478
                                    norenewalbefore, auto_renew,
479
                                    fine, chargeperiod, holdspickupwait)
480
          VALUES (?, ?, ?, ?,
481
                  ?, ?, ?,
482
                  ?, ?,
483
                  ?, ?,
484
                  ?, ?, ?
485
                 )
486
        },
487
        {},
488
        '*', '*', '*', 25,
489
        20, 14, 'days',
490
        1, 7,
491
        '', 0,
492
        .10, 1,1
493
    );
494
}
495
496
###Set C4::Calendar and Koha::Calendar holidays for
497
# today -2 days
498
# today -3 days
499
# today -4 days
500
#
501
## Koha::Calendar for caching purposes (supposedly) doesn't work from the DB in this script
502
## So we must set the cache for Koha::calnder as well as the DB modifications for C4::Calendar.
503
## When making date comparisons with Koha::Calendar, using DateTime::Set, DateTime-objects
504
## need to match by the nanosecond and time_zone.
505
sub setCalendars {
506
507
    ##Set the C4::Calendar
508
    my $now = DateTime->now(time_zone => C4::Context->tz())->truncate(to => 'day');
509
    my $c4calendar = C4::Calendar->new(branchcode => $reserve->{branchcode});
510
    $now->add_duration( DateTime::Duration->new(days => -2) );
511
    $c4calendar->insert_single_holiday(
512
        day         => $now->day(),
513
        month       => $now->month(),
514
        year        => $now->year(),
515
        title       => 'Test',
516
        description => 'Test',
517
    );
518
    $now->add_duration( DateTime::Duration->new(days => -1) );
519
    $c4calendar->insert_single_holiday(
520
        day         => $now->day(),
521
        month       => $now->month(),
522
        year        => $now->year(),
523
        title       => 'Test',
524
        description => 'Test',
525
    );
526
    $now->add_duration( DateTime::Duration->new(days => -1) );
527
    $c4calendar->insert_single_holiday(
528
        day         => $now->day(),
529
        month       => $now->month(),
530
        year        => $now->year(),
531
        title       => 'Test',
532
        description => 'Test',
533
    );
534
535
    #Set the Koha::Calendar
536
    my $kohaCalendar = Koha::Calendar->new(branchcode => $reserve->{branchcode});
537
    $now = DateTime->now(time_zone => C4::Context->tz())->truncate(to => 'day');
538
    $now->add_duration( DateTime::Duration->new(days => -2) );
539
    $kohaCalendar->add_holiday( $now );
540
    $now->add_duration( DateTime::Duration->new(days => -1) );
541
    $kohaCalendar->add_holiday( $now );
542
    $now->add_duration( DateTime::Duration->new(days => -1) );
543
    $kohaCalendar->add_holiday( $now );
544
}

Return to bug 8367