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

(-)a/C4/Reserves.pm (-5 / +70 lines)
Lines 185-196 sub AddReserve { Link Here
185
        # Make room in reserves for this before those of a later reserve date
185
        # Make room in reserves for this before those of a later reserve date
186
        $priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority );
186
        $priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority );
187
    }
187
    }
188
    my ($waitingdate, $lastpickupdate);
188
189
189
    my $waitingdate;
190
    my $item = C4::Items::GetItem( $checkitem );
190
191
    # If the reserv had the waiting status, we had the value of the resdate
191
    # If the reserv had the waiting status, we had the value of the resdate
192
    if ( $found eq 'W' ) {
192
    if ( $found eq 'W' ) {
193
        $waitingdate = $resdate;
193
        $waitingdate = $resdate;
194
195
        #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.
196
        my $reserve = {borrowernumber => $borrowernumber, waitingdate => $waitingdate, branchcode => $branch};
197
        $lastpickupdate = GetLastPickupDate( $reserve, $item );
194
    }
198
    }
195
199
196
    # Don't add itemtype limit if specific item is selected
200
    # Don't add itemtype limit if specific item is selected
Lines 211-216 sub AddReserve { Link Here
211
            expirationdate => $expdate,
215
            expirationdate => $expdate,
212
            itemtype       => $itemtype,
216
            itemtype       => $itemtype,
213
            item_level_hold => $checkitem ? 1 : 0,
217
            item_level_hold => $checkitem ? 1 : 0,
218
            lastpickupdate => $lastpickupdate,
214
        }
219
        }
215
    )->store();
220
    )->store();
216
    $hold->set_waiting() if $found eq 'W';
221
    $hold->set_waiting() if $found eq 'W';
Lines 1022-1030 sub ModReserveStatus { Link Here
1022
    my ($itemnumber, $newstatus) = @_;
1027
    my ($itemnumber, $newstatus) = @_;
1023
    my $dbh = C4::Context->dbh;
1028
    my $dbh = C4::Context->dbh;
1024
1029
1025
    my $query = "UPDATE reserves SET found = ?, waitingdate = NOW() WHERE itemnumber = ? AND found IS NULL AND priority = 0";
1030
    my $now = dt_from_string;
1031
    my $reserve = $dbh->selectrow_hashref(q{
1032
        SELECT *
1033
        FROM reserves
1034
        WHERE itemnumber = ?
1035
            AND found IS NULL
1036
            AND priority = 0
1037
    }, {}, $itemnumber);
1038
    return unless $reserve;
1039
1040
    my $lastpickupdate = GetLastPickupDate( $reserve );
1041
1042
    my $query = q{
1043
        UPDATE reserves
1044
        SET found = ?,
1045
            waitingdate = ?,
1046
            lastpickupdate = ?
1047
        WHERE itemnumber = ?
1048
            AND found IS NULL
1049
            AND priority = 0
1050
    };
1026
    my $sth_set = $dbh->prepare($query);
1051
    my $sth_set = $dbh->prepare($query);
1027
    $sth_set->execute( $newstatus, $itemnumber );
1052
    $sth_set->execute( $newstatus, $now, $lastpickupdate, $itemnumber );
1028
1053
1029
    my $item = Koha::Items->find($itemnumber);
1054
    my $item = Koha::Items->find($itemnumber);
1030
    if ( ( $item->location eq 'CART' && $item->permanent_location ne 'CART'  ) && $newstatus ) {
1055
    if ( ( $item->location eq 'CART' && $item->permanent_location ne 'CART'  ) && $newstatus ) {
Lines 1530-1535 sub _Findgroupreserve { Link Here
1530
               reserves.borrowernumber      AS borrowernumber,
1555
               reserves.borrowernumber      AS borrowernumber,
1531
               reserves.reservedate         AS reservedate,
1556
               reserves.reservedate         AS reservedate,
1532
               reserves.branchcode          AS branchcode,
1557
               reserves.branchcode          AS branchcode,
1558
               reserves.lastpickupdate      AS lastpickupdate,
1533
               reserves.cancellationdate    AS cancellationdate,
1559
               reserves.cancellationdate    AS cancellationdate,
1534
               reserves.found               AS found,
1560
               reserves.found               AS found,
1535
               reserves.reservenotes        AS reservenotes,
1561
               reserves.reservenotes        AS reservenotes,
Lines 1565-1570 sub _Findgroupreserve { Link Here
1565
               reserves.borrowernumber      AS borrowernumber,
1591
               reserves.borrowernumber      AS borrowernumber,
1566
               reserves.reservedate         AS reservedate,
1592
               reserves.reservedate         AS reservedate,
1567
               reserves.branchcode          AS branchcode,
1593
               reserves.branchcode          AS branchcode,
1594
               reserves.lastpickupdate      AS lastpickupdate,
1568
               reserves.cancellationdate    AS cancellationdate,
1595
               reserves.cancellationdate    AS cancellationdate,
1569
               reserves.found               AS found,
1596
               reserves.found               AS found,
1570
               reserves.reservenotes        AS reservenotes,
1597
               reserves.reservenotes        AS reservenotes,
Lines 1600-1605 sub _Findgroupreserve { Link Here
1600
               reserves.reservedate                AS reservedate,
1627
               reserves.reservedate                AS reservedate,
1601
               reserves.waitingdate                AS waitingdate,
1628
               reserves.waitingdate                AS waitingdate,
1602
               reserves.branchcode                 AS branchcode,
1629
               reserves.branchcode                 AS branchcode,
1630
               reserves.lastpickupdate             AS lastpickupdate,
1603
               reserves.cancellationdate           AS cancellationdate,
1631
               reserves.cancellationdate           AS cancellationdate,
1604
               reserves.found                      AS found,
1632
               reserves.found                      AS found,
1605
               reserves.reservenotes               AS reservenotes,
1633
               reserves.reservenotes               AS reservenotes,
Lines 1820-1825 sub MoveReserve { Link Here
1820
    }
1848
    }
1821
}
1849
}
1822
1850
1851
=head MoveWaitingdate
1852
1853
  #Move waitingdate two months and fifteen days forward.
1854
  my $dateDuration = DateTime::Duration->new( months => 2, days => 15 );
1855
  $reserve = MoveWaitingdate( $reserve, $dateDuration);
1856
1857
  #Move waitingdate one year and eleven days backwards.
1858
  my $dateDuration = DateTime::Duration->new( years => -1, days => -11 );
1859
  $reserve = MoveWaitingdate( $reserve, $dateDuration);
1860
1861
Moves the waitingdate and updates the lastpickupdate to match.
1862
If waitingdate is not defined, uses today.
1863
Is intended to be used from automated tests, because under normal library
1864
operations there should be NO REASON to move the waitingdate.
1865
1866
@PARAM1 koha.reserves-row, with waitingdate set.
1867
@PARAM2 DateTime::Duration, with the desired offset.
1868
RETURNS koha.reserve-row, with keys waitingdate and lastpickupdate updated.
1869
=cut
1870
sub MoveWaitingdate {
1871
    my ($reserve, $dateDuration) = @_;
1872
1873
    my $dt = dt_from_string( $reserve->{waitingdate} );
1874
    $dt->add_duration( $dateDuration );
1875
    $reserve->{waitingdate} = $dt->ymd();
1876
1877
    GetLastPickupDate( $reserve ); #Update the $reserve->{lastpickupdate}
1878
1879
    #UPDATE the DB part
1880
    my $dbh = C4::Context->dbh();
1881
    my $sth = $dbh->prepare( "UPDATE reserves SET waitingdate=?, lastpickupdate=? WHERE reserve_id=?" );
1882
    $sth->execute( $reserve->{waitingdate}, $reserve->{lastpickupdate}, $reserve->{reserve_id} );
1883
1884
    return $reserve;
1885
}
1886
1823
=head2 MergeHolds
1887
=head2 MergeHolds
1824
1888
1825
  MergeHolds($dbh,$to_biblio, $from_biblio);
1889
  MergeHolds($dbh,$to_biblio, $from_biblio);
Lines 1918-1924 sub RevertWaitingStatus { Link Here
1918
    SET
1982
    SET
1919
      priority = 1,
1983
      priority = 1,
1920
      found = NULL,
1984
      found = NULL,
1921
      waitingdate = NULL
1985
      waitingdate = NULL,
1986
      lastpickupdate = NULL,
1922
    WHERE
1987
    WHERE
1923
      reserve_id = ?
1988
      reserve_id = ?
1924
    ";
1989
    ";
(-)a/admin/smart-rules.pl (-1 / +2 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+/;
(-)a/circ/waitingreserves.pl (+27 lines)
Lines 92-97 my $today = Date_to_Days(&Today); Link Here
92
while ( my $hold = $holds->next ) {
92
while ( my $hold = $holds->next ) {
93
    next unless ($hold->waitingdate && $hold->waitingdate ne '0000-00-00');
93
    next unless ($hold->waitingdate && $hold->waitingdate ne '0000-00-00');
94
94
95
    my $item = $hold->item;
96
    my $patron = $hold->borrower;
97
    my $biblio = $item->biblio;
98
    my $holdingbranch = $item->holdingbranch;
99
    my $homebranch = $item->homebranch;
100
101
    my %getreserv = (
102
        title             => $biblio->title,
103
        itemnumber        => $item->itemnumber,
104
        waitingdate       => $hold->waitingdate,
105
        reservedate       => $hold->reservedate,
106
        borrowernum       => $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 );
95
    my ( $expire_year, $expire_month, $expire_day ) = split (/-/, $hold->expirationdate);
121
    my ( $expire_year, $expire_month, $expire_day ) = split (/-/, $hold->expirationdate);
96
    my $calcDate = Date_to_Days( $expire_year, $expire_month, $expire_day );
122
    my $calcDate = Date_to_Days( $expire_year, $expire_month, $expire_day );
97
123
Lines 118-123 $template->param( Link Here
118
    overcount   => scalar @over_loop,
144
    overcount   => scalar @over_loop,
119
    show_date   => output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 }),
145
    show_date   => output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 }),
120
    tab => $tab,
146
    tab => $tab,
147
    show_date   => format_date(C4::Dates->today('iso')),
121
);
148
);
122
149
123
# Checking if there is a Fast Cataloging Framework
150
# Checking if there is a Fast Cataloging Framework
(-)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 1818-1823 CREATE TABLE `reserves` ( -- information related to holds/reserves in Koha Link Here
1818
  `suspend_until` DATETIME NULL DEFAULT NULL,
1819
  `suspend_until` DATETIME NULL DEFAULT NULL,
1819
  `itemtype` VARCHAR(10) NULL DEFAULT NULL, -- If record level hold, the optional itemtype of the item the patron is requesting
1820
  `itemtype` VARCHAR(10) NULL DEFAULT NULL, -- If record level hold, the optional itemtype of the item the patron is requesting
1820
  `item_level_hold` BOOLEAN NOT NULL DEFAULT 0, -- Is the hpld placed at item level
1821
  `item_level_hold` BOOLEAN NOT NULL DEFAULT 0, -- Is the hpld placed at item level
1822
  `lastpickupdate` date NULL DEFAULT NULL, -- the last day this hold is available for pickup, until it becomes problematic
1821
  PRIMARY KEY (`reserve_id`),
1823
  PRIMARY KEY (`reserve_id`),
1822
  KEY priorityfoundidx (priority,found),
1824
  KEY priorityfoundidx (priority,found),
1823
  KEY `borrowernumber` (`borrowernumber`),
1825
  KEY `borrowernumber` (`borrowernumber`),
Lines 1858-1863 CREATE TABLE `old_reserves` ( -- this table holds all holds/reserves that have b Link Here
1858
  `suspend_until` DATETIME NULL DEFAULT NULL, -- the date this hold is suspended until (NULL for infinitely)
1860
  `suspend_until` DATETIME NULL DEFAULT NULL, -- the date this hold is suspended until (NULL for infinitely)
1859
  `itemtype` VARCHAR(10) NULL DEFAULT NULL, -- If record level hold, the optional itemtype of the item the patron is requesting
1861
  `itemtype` VARCHAR(10) NULL DEFAULT NULL, -- If record level hold, the optional itemtype of the item the patron is requesting
1860
  `item_level_hold` BOOLEAN NOT NULL DEFAULT 0, -- Is the hpld placed at item level
1862
  `item_level_hold` BOOLEAN NOT NULL DEFAULT 0, -- Is the hpld placed at item level
1863
  `lastpickupdate` date NULL DEFAULT NULL, -- the last day this hold is available for pickup, until it becomes problematic
1861
  PRIMARY KEY (`reserve_id`),
1864
  PRIMARY KEY (`reserve_id`),
1862
  KEY `old_reserves_borrowernumber` (`borrowernumber`),
1865
  KEY `old_reserves_borrowernumber` (`borrowernumber`),
1863
  KEY `old_reserves_biblionumber` (`biblionumber`),
1866
  KEY `old_reserves_biblionumber` (`biblionumber`),
(-)a/installer/data/mysql/sysprefs.sql (-3 / +1 lines)
Lines 171-181 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
171
('expandedSearchOption','0',NULL,'If ON, set advanced search to be expanded by default','YesNo'),
171
('expandedSearchOption','0',NULL,'If ON, set advanced search to be expanded by default','YesNo'),
172
('ExpireReservesMaxPickUpDelay','0','','Enabling this allows holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay','YesNo'),
172
('ExpireReservesMaxPickUpDelay','0','','Enabling this allows holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay','YesNo'),
173
('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'),
173
('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'),
174
('ExpireReservesOnHolidays', '1', NULL, 'If false, reserves at a library will not be canceled on days the library is not open.', 'YesNo'),
175
('ExcludeHolidaysFromMaxPickUpDelay', '0', NULL, 'If ON, reserves max pickup delay takes into accountthe closed days.', 'YesNo'),
174
('ExcludeHolidaysFromMaxPickUpDelay', '0', NULL, 'If ON, reserves max pickup delay takes into accountthe closed days.', 'YesNo'),
176
('ExportCircHistory', 0, NULL, "Display the export circulation options",  'YesNo' ),
175
('ExportCircHistory', 0, NULL, "Display the export circulation options",  'YesNo' ),
177
('ExportRemoveFields','',NULL,'List of fields for non export in circulation.pl (separated by a space)','Free'),
176
('ExportRemoveFields','',NULL,'List of fields for non export in circulation.pl (separated by a space)','Free'),
178
('ExtendedPatronAttributes','1',NULL,'Use extended patron IDs and attributes','YesNo'),
177
('ExtendedPatronAttributes','0',NULL,'Use extended patron IDs and attributes','YesNo'),
179
('FacetLabelTruncationLength','20',NULL,'Specify the facet max length in OPAC','Integer'),
178
('FacetLabelTruncationLength','20',NULL,'Specify the facet max length in OPAC','Integer'),
180
('FacetMaxCount','20',NULL,'Specify the max facet count for each category','Integer'),
179
('FacetMaxCount','20',NULL,'Specify the max facet count for each category','Integer'),
181
('FailedLoginAttempts','','','Number of login attempts before lockout the patron account','Integer'),
180
('FailedLoginAttempts','','','Number of login attempts before lockout the patron account','Integer'),
Lines 507-513 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
507
('RequestOnOpac','1',NULL,'If ON, globally enables patron holds on OPAC','YesNo'),
506
('RequestOnOpac','1',NULL,'If ON, globally enables patron holds on OPAC','YesNo'),
508
('RequireStrongPassword','1','','Require a strong login password for staff and patrons','YesNo'),
507
('RequireStrongPassword','1','','Require a strong login password for staff and patrons','YesNo'),
509
('ReservesControlBranch','PatronLibrary','ItemHomeLibrary|PatronLibrary','Branch checked for members reservations rights','Choice'),
508
('ReservesControlBranch','PatronLibrary','ItemHomeLibrary|PatronLibrary','Branch checked for members reservations rights','Choice'),
510
('ReservesMaxPickUpDelay','7','','Define the Maximum delay to pick up an item on hold','Integer'),
511
('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'),
509
('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'),
512
('RESTBasicAuth','0',NULL,'If enabled, Basic authentication is enabled for the REST API.','YesNo'),
510
('RESTBasicAuth','0',NULL,'If enabled, Basic authentication is enabled for the REST API.','YesNo'),
513
('RESTdefaultPageSize','20','','Default page size for endpoints listing objects','Integer'),
511
('RESTdefaultPageSize','20','','Default page size for endpoints listing objects','Integer'),
(-)a/installer/data/mysql/updatedatabase.pl (+32 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 16478-16483 if( CheckVersion( $DBversion ) ) { Link Here
16478
    print "Upgrade to $DBversion done (Bug 21403 - Add Indian Amazon Affiliate option to AmazonLocale setting)\n";
16480
    print "Upgrade to $DBversion done (Bug 21403 - Add Indian Amazon Affiliate option to AmazonLocale setting)\n";
16479
}
16481
}
16480
16482
16483
$DBversion = "18.06.00.XXX";
16484
if ( CheckVersion($DBversion) ) {
16485
    my $maxpickupdelay = C4::Context->preference('ReservesMaxPickUpDelay') || 0; #MaxPickupDelay
16486
    $dbh->do(q{ DELETE FROM systempreferences WHERE variable='ReservesMaxPickUpDelay'; });
16487
    $dbh->do(q{ DELETE FROM systempreferences WHERE variable='ExpireReservesOnHolidays'; });
16488
    #        //DELETE FROM systempreferences WHERE variable='ExpireReservesMaxPickUpDelay'; #This syspref is not needed and would be better suited to be calculated from the holdspickupwait
16489
    #        //ExpireReservesMaxPickUpDelayCharge #This could be added as a column to the issuing rules.
16490
    $dbh->do(q{ ALTER TABLE issuingrules ADD COLUMN holdspickupwait INT(11) NULL default NULL AFTER reservesallowed; });
16491
    $dbh->do(q{ ALTER TABLE reserves ADD COLUMN lastpickupdate DATE NULL default NULL AFTER suspend_until; });
16492
    $dbh->do(q{ ALTER TABLE old_reserves ADD COLUMN lastpickupdate DATE NULL default NULL AFTER suspend_until; });
16493
    my $sth = $dbh->prepare(q{
16494
        UPDATE issuingrules SET holdspickupwait = ?
16495
    });
16496
    $sth->execute( $maxpickupdelay ) if $maxpickupdelay; #Don't want to accidentally nullify all!
16497
16498
    ##Populate the lastpickupdate-column from existing 'ReservesMaxPickUpDelay'
16499
    print "Populating the new lastpickupdate-column for all waiting holds. This might take a while.\n";
16500
    $sth = $dbh->prepare(q{ SELECT * FROM reserves WHERE found = 'W'; });
16501
    $sth->execute( );
16502
    my $dtdur = DateTime::Duration->new( days => 0 );
16503
16504
    while ( my $res = $sth->fetchrow_hashref ) {
16505
         C4::Reserves::MoveWaitingdate( $res, $dtdur ); #We call MoveWaitingdate with a 0 duration to simply recalculate the lastpickupdate and store the new values to DB.
16506
    }
16507
    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";
16508
    SetVersion($DBversion);
16509
}
16510
16511
# SEE bug 13068
16512
# if there is anything in the atomicupdate, read and execute it.
16481
16513
16482
$DBversion = '18.06.00.036';
16514
$DBversion = '18.06.00.036';
16483
if( CheckVersion( $DBversion ) ) {
16515
if( CheckVersion( $DBversion ) ) {
(-)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 (+3 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 307-312 Link Here
307
                    <td><input type="text" name="suspension_chargeperiod" id="suspension_chargeperiod" size="3" /> </td>
308
                    <td><input type="text" name="suspension_chargeperiod" id="suspension_chargeperiod" size="3" /> </td>
308
                    <td><input type="text" name="renewalsallowed" id="renewalsallowed" size="2" /></td>
309
                    <td><input type="text" name="renewalsallowed" id="renewalsallowed" size="2" /></td>
309
                    <td><input type="text" name="renewalperiod" id="renewalperiod" size="3" /></td>
310
                    <td><input type="text" name="renewalperiod" id="renewalperiod" size="3" /></td>
311
                    <td><input type="text" name="holdspickupwait" id="holdspickupwait" size="2" /></td>
310
                    <td><input type="text" name="norenewalbefore" id="norenewalbefore" size="3" /></td>
312
                    <td><input type="text" name="norenewalbefore" id="norenewalbefore" size="3" /></td>
311
                    <td>
313
                    <td>
312
                        <select name="auto_renew" id="auto_renew">
314
                        <select name="auto_renew" id="auto_renew">
Lines 373-378 Link Here
373
                      <th>Suspension charging interval</th>
375
                      <th>Suspension charging interval</th>
374
                      <th>Renewals allowed (count)</th>
376
                      <th>Renewals allowed (count)</th>
375
                      <th>Renewal period</th>
377
                      <th>Renewal period</th>
378
                      <th>Holds wait for pickup (day)</th>
376
                      <th>No renewal before</th>
379
                      <th>No renewal before</th>
377
                      <th>Automatic renewal</th>
380
                      <th>Automatic renewal</th>
378
                      <th>No automatic renewal after</th>
381
                      <th>No automatic renewal after</th>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt (-10 / +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>
Lines 949-964 Link Here
949
                    [% END %]
950
                    [% END %]
950
                [% END %]
951
                [% END %]
951
952
952
                [% UNLESS ( borrowers ) %]
953
                    [% IF borrowernumber and patron %]
954
                            <div class="col-sm-2 col-sm-pull-10">
955
                                <aside>
956
                            [% INCLUDE 'circ-menu.inc' %]
957
                                </aside>
958
                            </div> <!-- /.col-sm-2 col-sm-pull-10 -->
959
                    [% END %]
960
                [% END %]
961
962
            </div> <!-- /.row -->
953
            </div> <!-- /.row -->
963
        </main>
954
        </main>
964
955
(-)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/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