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

(-)a/C4/Reserves.pm (-5 / +70 lines)
Lines 186-197 sub AddReserve { Link Here
186
        # Make room in reserves for this before those of a later reserve date
186
        # Make room in reserves for this before those of a later reserve date
187
        $priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority );
187
        $priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority );
188
    }
188
    }
189
    my ($waitingdate, $lastpickupdate);
189
190
190
    my $waitingdate;
191
    my $item = C4::Items::GetItem( $checkitem );
191
192
    # If the reserv had the waiting status, we had the value of the resdate
192
    # If the reserv had the waiting status, we had the value of the resdate
193
    if ( $found eq 'W' ) {
193
    if ( $found eq 'W' ) {
194
        $waitingdate = $resdate;
194
        $waitingdate = $resdate;
195
196
        #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.
197
        my $reserve = {borrowernumber => $borrowernumber, waitingdate => $waitingdate, branchcode => $branch};
198
        $lastpickupdate = GetLastPickupDate( $reserve, $item );
195
    }
199
    }
196
200
197
    # Don't add itemtype limit if specific item is selected
201
    # Don't add itemtype limit if specific item is selected
Lines 211-216 sub AddReserve { Link Here
211
            waitingdate    => $waitingdate,
215
            waitingdate    => $waitingdate,
212
            expirationdate => $expdate,
216
            expirationdate => $expdate,
213
            itemtype       => $itemtype,
217
            itemtype       => $itemtype,
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 1004-1012 sub ModReserveStatus { Link Here
1004
    my ($itemnumber, $newstatus) = @_;
1009
    my ($itemnumber, $newstatus) = @_;
1005
    my $dbh = C4::Context->dbh;
1010
    my $dbh = C4::Context->dbh;
1006
1011
1007
    my $query = "UPDATE reserves SET found = ?, waitingdate = NOW() WHERE itemnumber = ? AND found IS NULL AND priority = 0";
1012
    my $now = dt_from_string;
1013
    my $reserve = $dbh->selectrow_hashref(q{
1014
        SELECT *
1015
        FROM reserves
1016
        WHERE itemnumber = ?
1017
            AND found IS NULL
1018
            AND priority = 0
1019
    }, {}, $itemnumber);
1020
    return unless $reserve;
1021
1022
    my $lastpickupdate = GetLastPickupDate( $reserve );
1023
1024
    my $query = q{
1025
        UPDATE reserves
1026
        SET found = ?,
1027
            waitingdate = ?,
1028
            lastpickupdate = ?
1029
        WHERE itemnumber = ?
1030
            AND found IS NULL
1031
            AND priority = 0
1032
    };
1008
    my $sth_set = $dbh->prepare($query);
1033
    my $sth_set = $dbh->prepare($query);
1009
    $sth_set->execute( $newstatus, $itemnumber );
1034
    $sth_set->execute( $newstatus, $now, $lastpickupdate, $itemnumber );
1010
1035
1011
    if ( C4::Context->preference("ReturnToShelvingCart") && $newstatus ) {
1036
    if ( C4::Context->preference("ReturnToShelvingCart") && $newstatus ) {
1012
      CartToShelf( $itemnumber );
1037
      CartToShelf( $itemnumber );
Lines 1503-1508 sub _Findgroupreserve { Link Here
1503
               reserves.borrowernumber      AS borrowernumber,
1528
               reserves.borrowernumber      AS borrowernumber,
1504
               reserves.reservedate         AS reservedate,
1529
               reserves.reservedate         AS reservedate,
1505
               reserves.branchcode          AS branchcode,
1530
               reserves.branchcode          AS branchcode,
1531
               reserves.lastpickupdate      AS lastpickupdate,
1506
               reserves.cancellationdate    AS cancellationdate,
1532
               reserves.cancellationdate    AS cancellationdate,
1507
               reserves.found               AS found,
1533
               reserves.found               AS found,
1508
               reserves.reservenotes        AS reservenotes,
1534
               reserves.reservenotes        AS reservenotes,
Lines 1538-1543 sub _Findgroupreserve { Link Here
1538
               reserves.borrowernumber      AS borrowernumber,
1564
               reserves.borrowernumber      AS borrowernumber,
1539
               reserves.reservedate         AS reservedate,
1565
               reserves.reservedate         AS reservedate,
1540
               reserves.branchcode          AS branchcode,
1566
               reserves.branchcode          AS branchcode,
1567
               reserves.lastpickupdate      AS lastpickupdate,
1541
               reserves.cancellationdate    AS cancellationdate,
1568
               reserves.cancellationdate    AS cancellationdate,
1542
               reserves.found               AS found,
1569
               reserves.found               AS found,
1543
               reserves.reservenotes        AS reservenotes,
1570
               reserves.reservenotes        AS reservenotes,
Lines 1573-1578 sub _Findgroupreserve { Link Here
1573
               reserves.reservedate                AS reservedate,
1600
               reserves.reservedate                AS reservedate,
1574
               reserves.waitingdate                AS waitingdate,
1601
               reserves.waitingdate                AS waitingdate,
1575
               reserves.branchcode                 AS branchcode,
1602
               reserves.branchcode                 AS branchcode,
1603
               reserves.lastpickupdate             AS lastpickupdate,
1576
               reserves.cancellationdate           AS cancellationdate,
1604
               reserves.cancellationdate           AS cancellationdate,
1577
               reserves.found                      AS found,
1605
               reserves.found                      AS found,
1578
               reserves.reservenotes               AS reservenotes,
1606
               reserves.reservenotes               AS reservenotes,
Lines 1795-1800 sub MoveReserve { Link Here
1795
    }
1823
    }
1796
}
1824
}
1797
1825
1826
=head MoveWaitingdate
1827
1828
  #Move waitingdate two months and fifteen days forward.
1829
  my $dateDuration = DateTime::Duration->new( months => 2, days => 15 );
1830
  $reserve = MoveWaitingdate( $reserve, $dateDuration);
1831
1832
  #Move waitingdate one year and eleven days backwards.
1833
  my $dateDuration = DateTime::Duration->new( years => -1, days => -11 );
1834
  $reserve = MoveWaitingdate( $reserve, $dateDuration);
1835
1836
Moves the waitingdate and updates the lastpickupdate to match.
1837
If waitingdate is not defined, uses today.
1838
Is intended to be used from automated tests, because under normal library
1839
operations there should be NO REASON to move the waitingdate.
1840
1841
@PARAM1 koha.reserves-row, with waitingdate set.
1842
@PARAM2 DateTime::Duration, with the desired offset.
1843
RETURNS koha.reserve-row, with keys waitingdate and lastpickupdate updated.
1844
=cut
1845
sub MoveWaitingdate {
1846
    my ($reserve, $dateDuration) = @_;
1847
1848
    my $dt = dt_from_string( $reserve->{waitingdate} );
1849
    $dt->add_duration( $dateDuration );
1850
    $reserve->{waitingdate} = $dt->ymd();
1851
1852
    GetLastPickupDate( $reserve ); #Update the $reserve->{lastpickupdate}
1853
1854
    #UPDATE the DB part
1855
    my $dbh = C4::Context->dbh();
1856
    my $sth = $dbh->prepare( "UPDATE reserves SET waitingdate=?, lastpickupdate=? WHERE reserve_id=?" );
1857
    $sth->execute( $reserve->{waitingdate}, $reserve->{lastpickupdate}, $reserve->{reserve_id} );
1858
1859
    return $reserve;
1860
}
1861
1798
=head2 MergeHolds
1862
=head2 MergeHolds
1799
1863
1800
  MergeHolds($dbh,$to_biblio, $from_biblio);
1864
  MergeHolds($dbh,$to_biblio, $from_biblio);
Lines 1891-1897 sub RevertWaitingStatus { Link Here
1891
    SET
1955
    SET
1892
      priority = 1,
1956
      priority = 1,
1893
      found = NULL,
1957
      found = NULL,
1894
      waitingdate = NULL
1958
      waitingdate = NULL,
1959
      lastpickupdate = NULL,
1895
    WHERE
1960
    WHERE
1896
      reserve_id = ?
1961
      reserve_id = ?
1897
    ";
1962
    ";
(-)a/admin/smart-rules.pl (+1 lines)
Lines 147-152 elsif ($op eq 'add') { Link Here
147
    $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 );
147
    $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 );
148
    my $reservesallowed  = $input->param('reservesallowed');
148
    my $reservesallowed  = $input->param('reservesallowed');
149
    my $holds_per_record  = $input->param('holds_per_record');
149
    my $holds_per_record  = $input->param('holds_per_record');
150
    my $holdspickupwait = $input->param('holdspickupwait');
150
    my $onshelfholds     = $input->param('onshelfholds') || 0;
151
    my $onshelfholds     = $input->param('onshelfholds') || 0;
151
    $maxissueqty =~ s/\s//g;
152
    $maxissueqty =~ s/\s//g;
152
    $maxissueqty = undef if $maxissueqty !~ /^\d+/;
153
    $maxissueqty = undef if $maxissueqty !~ /^\d+/;
(-)a/circ/waitingreserves.pl (+2 lines)
Lines 115-120 while ( my $hold = $holds->next ) { Link Here
115
        borrowername      => $patron->surname, # FIXME Let's send $patron to the template
115
        borrowername      => $patron->surname, # FIXME Let's send $patron to the template
116
        borrowerfirstname => $patron->firstname,
116
        borrowerfirstname => $patron->firstname,
117
        borrowerphone     => $patron->phone,
117
        borrowerphone     => $patron->phone,
118
        lastpickupdate    => $hold->lastpickupdate,
118
    );
119
    );
119
120
120
    my $itemtype = Koha::ItemTypes->find( $item->effective_itemtype );
121
    my $itemtype = Koha::ItemTypes->find( $item->effective_itemtype );
Lines 158-163 $template->param( Link Here
158
    show_date   => output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 }),
159
    show_date   => output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 }),
159
    ReservesMaxPickUpDelay => $max_pickup_delay,
160
    ReservesMaxPickUpDelay => $max_pickup_delay,
160
    tab => $tab,
161
    tab => $tab,
162
    show_date   => format_date(C4::Dates->today('iso')),
161
);
163
);
162
164
163
# Checking if there is a Fast Cataloging Framework
165
# Checking if there is a Fast Cataloging Framework
(-)a/installer/data/mysql/kohastructure.sql (+3 lines)
Lines 860-865 CREATE TABLE `issuingrules` ( -- circulation and fine rules Link Here
860
  `no_auto_renewal_after_hard_limit` date default NULL, -- no auto renewal allowed after a given date
860
  `no_auto_renewal_after_hard_limit` date default NULL, -- no auto renewal allowed after a given date
861
  `reservesallowed` smallint(6) NOT NULL default "0", -- how many holds are allowed
861
  `reservesallowed` smallint(6) NOT NULL default "0", -- how many holds are allowed
862
  `holds_per_record` SMALLINT(6) NOT NULL DEFAULT 1, -- How many holds a patron can have on a given bib
862
  `holds_per_record` SMALLINT(6) NOT NULL DEFAULT 1, -- How many holds a patron can have on a given bib
863
  `holdspickupwait` int(11)  default NULL, -- How many open library days a hold can wait in the pickup shelf until it becomes problematic
863
  `branchcode` varchar(10) NOT NULL default '', -- the branch this rule is for (branches.branchcode)
864
  `branchcode` varchar(10) NOT NULL default '', -- the branch this rule is for (branches.branchcode)
864
  overduefinescap decimal(28,6) default NULL, -- the maximum amount of an overdue fine
865
  overduefinescap decimal(28,6) default NULL, -- the maximum amount of an overdue fine
865
  cap_fine_to_replacement_price BOOLEAN NOT NULL DEFAULT  '0', -- cap the fine based on item's replacement price
866
  cap_fine_to_replacement_price BOOLEAN NOT NULL DEFAULT  '0', -- cap the fine based on item's replacement price
Lines 1882-1887 CREATE TABLE `reserves` ( -- information related to holds/reserves in Koha Link Here
1882
  `suspend` BOOLEAN NOT NULL DEFAULT 0,
1883
  `suspend` BOOLEAN NOT NULL DEFAULT 0,
1883
  `suspend_until` DATETIME NULL DEFAULT NULL,
1884
  `suspend_until` DATETIME NULL DEFAULT NULL,
1884
  `itemtype` VARCHAR(10) NULL DEFAULT NULL, -- If record level hold, the optional itemtype of the item the patron is requesting
1885
  `itemtype` VARCHAR(10) NULL DEFAULT NULL, -- If record level hold, the optional itemtype of the item the patron is requesting
1886
  `lastpickupdate` date NULL DEFAULT NULL, -- the last day this hold is available for pickup, until it becomes problematic
1885
  PRIMARY KEY (`reserve_id`),
1887
  PRIMARY KEY (`reserve_id`),
1886
  KEY priorityfoundidx (priority,found),
1888
  KEY priorityfoundidx (priority,found),
1887
  KEY `borrowernumber` (`borrowernumber`),
1889
  KEY `borrowernumber` (`borrowernumber`),
Lines 1921-1926 CREATE TABLE `old_reserves` ( -- this table holds all holds/reserves that have b Link Here
1921
  `suspend` BOOLEAN NOT NULL DEFAULT 0, -- in this hold suspended (1 for yes, 0 for no)
1923
  `suspend` BOOLEAN NOT NULL DEFAULT 0, -- in this hold suspended (1 for yes, 0 for no)
1922
  `suspend_until` DATETIME NULL DEFAULT NULL, -- the date this hold is suspended until (NULL for infinitely)
1924
  `suspend_until` DATETIME NULL DEFAULT NULL, -- the date this hold is suspended until (NULL for infinitely)
1923
  `itemtype` VARCHAR(10) NULL DEFAULT NULL, -- If record level hold, the optional itemtype of the item the patron is requesting
1925
  `itemtype` VARCHAR(10) NULL DEFAULT NULL, -- If record level hold, the optional itemtype of the item the patron is requesting
1926
  `lastpickupdate` date NULL DEFAULT NULL, -- the last day this hold is available for pickup, until it becomes problematic
1924
  PRIMARY KEY (`reserve_id`),
1927
  PRIMARY KEY (`reserve_id`),
1925
  KEY `old_reserves_borrowernumber` (`borrowernumber`),
1928
  KEY `old_reserves_borrowernumber` (`borrowernumber`),
1926
  KEY `old_reserves_biblionumber` (`biblionumber`),
1929
  KEY `old_reserves_biblionumber` (`biblionumber`),
(-)a/installer/data/mysql/sysprefs.sql (-3 / +1 lines)
Lines 161-171 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
161
('expandedSearchOption','0',NULL,'If ON, set advanced search to be expanded by default','YesNo'),
161
('expandedSearchOption','0',NULL,'If ON, set advanced search to be expanded by default','YesNo'),
162
('ExpireReservesMaxPickUpDelay','0','','Enabling this allows holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay','YesNo'),
162
('ExpireReservesMaxPickUpDelay','0','','Enabling this allows holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay','YesNo'),
163
('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'),
163
('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'),
164
('ExpireReservesOnHolidays', '1', NULL, 'If false, reserves at a library will not be canceled on days the library is not open.', 'YesNo'),
165
('ExcludeHolidaysFromMaxPickUpDelay', '0', NULL, 'If ON, reserves max pickup delay takes into accountthe closed days.', 'YesNo'),
164
('ExcludeHolidaysFromMaxPickUpDelay', '0', NULL, 'If ON, reserves max pickup delay takes into accountthe closed days.', 'YesNo'),
166
('ExportCircHistory', 0, NULL, "Display the export circulation options",  'YesNo' ),
165
('ExportCircHistory', 0, NULL, "Display the export circulation options",  'YesNo' ),
167
('ExportRemoveFields','',NULL,'List of fields for non export in circulation.pl (separated by a space)','Free'),
166
('ExportRemoveFields','',NULL,'List of fields for non export in circulation.pl (separated by a space)','Free'),
168
('ExtendedPatronAttributes','1',NULL,'Use extended patron IDs and attributes','YesNo'),
167
('ExtendedPatronAttributes','0',NULL,'Use extended patron IDs and attributes','YesNo'),
169
('FacetLabelTruncationLength','20',NULL,'Specify the facet max length in OPAC','Integer'),
168
('FacetLabelTruncationLength','20',NULL,'Specify the facet max length in OPAC','Integer'),
170
('FacetMaxCount','20',NULL,'Specify the max facet count for each category','Integer'),
169
('FacetMaxCount','20',NULL,'Specify the max facet count for each category','Integer'),
171
('FailedLoginAttempts','','','Number of login attempts before lockout the patron account','Integer'),
170
('FailedLoginAttempts','','','Number of login attempts before lockout the patron account','Integer'),
Lines 470-476 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
470
('RequestOnOpac','1',NULL,'If ON, globally enables patron holds on OPAC','YesNo'),
469
('RequestOnOpac','1',NULL,'If ON, globally enables patron holds on OPAC','YesNo'),
471
('RequireStrongPassword','1','','Require a strong login password for staff and patrons','YesNo'),
470
('RequireStrongPassword','1','','Require a strong login password for staff and patrons','YesNo'),
472
('ReservesControlBranch','PatronLibrary','ItemHomeLibrary|PatronLibrary','Branch checked for members reservations rights','Choice'),
471
('ReservesControlBranch','PatronLibrary','ItemHomeLibrary|PatronLibrary','Branch checked for members reservations rights','Choice'),
473
('ReservesMaxPickUpDelay','7','','Define the Maximum delay to pick up an item on hold','Integer'),
474
('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'),
472
('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'),
475
('RESTdefaultPageSize','20','','Default page size for endpoints listing objects','Integer'),
473
('RESTdefaultPageSize','20','','Default page size for endpoints listing objects','Integer'),
476
('RESTOAuth2ClientCredentials','0',NULL,'If enabled, the OAuth2 client credentials flow is enabled for the REST API.','YesNo'),
474
('RESTOAuth2ClientCredentials','0',NULL,'If enabled, the OAuth2 client credentials flow is enabled for the REST API.','YesNo'),
(-)a/installer/data/mysql/updatedatabase.pl (+30 lines)
Lines 38-43 use Getopt::Long; Link Here
38
# Koha modules
38
# Koha modules
39
use C4::Context;
39
use C4::Context;
40
use C4::Installer;
40
use C4::Installer;
41
use C4::Reserves;
42
use DateTime::Duration;
41
use Koha::Database;
43
use Koha::Database;
42
use Koha;
44
use Koha;
43
use Koha::DateUtils;
45
use Koha::DateUtils;
Lines 16483-16488 if( CheckVersion( $DBversion ) ) { Link Here
16483
    print "Upgrade to $DBversion done (Bug 21403 - Add Indian Amazon Affiliate option to AmazonLocale setting)\n";
16485
    print "Upgrade to $DBversion done (Bug 21403 - Add Indian Amazon Affiliate option to AmazonLocale setting)\n";
16484
}
16486
}
16485
16487
16488
$DBversion = "18.06.00.XXX";
16489
if ( CheckVersion($DBversion) ) {
16490
    my $maxpickupdelay = C4::Context->preference('ReservesMaxPickUpDelay') || 0; #MaxPickupDelay
16491
    $dbh->do(q{ DELETE FROM systempreferences WHERE variable='ReservesMaxPickUpDelay'; });
16492
    $dbh->do(q{ DELETE FROM systempreferences WHERE variable='ExpireReservesOnHolidays'; });
16493
    #        //DELETE FROM systempreferences WHERE variable='ExpireReservesMaxPickUpDelay'; #This syspref is not needed and would be better suited to be calculated from the holdspickupwait
16494
    #        //ExpireReservesMaxPickUpDelayCharge #This could be added as a column to the issuing rules.
16495
    $dbh->do(q{ ALTER TABLE issuingrules ADD COLUMN holdspickupwait INT(11) NULL default NULL AFTER reservesallowed; });
16496
    $dbh->do(q{ ALTER TABLE reserves ADD COLUMN lastpickupdate DATE NULL default NULL AFTER suspend_until; });
16497
    $dbh->do(q{ ALTER TABLE old_reserves ADD COLUMN lastpickupdate DATE NULL default NULL AFTER suspend_until; });
16498
    my $sth = $dbh->prepare(q{
16499
        UPDATE issuingrules SET holdspickupwait = ?
16500
    });
16501
    $sth->execute( $maxpickupdelay ) if $maxpickupdelay; #Don't want to accidentally nullify all!
16502
16503
    ##Populate the lastpickupdate-column from existing 'ReservesMaxPickUpDelay'
16504
    print "Populating the new lastpickupdate-column for all waiting holds. This might take a while.\n";
16505
    $sth = $dbh->prepare(q{ SELECT * FROM reserves WHERE found = 'W'; });
16506
    $sth->execute( );
16507
    my $dtdur = DateTime::Duration->new( days => 0 );
16508
16509
    while ( my $res = $sth->fetchrow_hashref ) {
16510
         C4::Reserves::MoveWaitingdate( $res, $dtdur ); #We call MoveWaitingdate with a 0 duration to simply recalculate the lastpickupdate and store the new values to DB.
16511
    }
16512
    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";
16513
    SetVersion($DBversion);
16514
}
16515
16486
# SEE bug 13068
16516
# SEE bug 13068
16487
# if there is anything in the atomicupdate, read and execute it.
16517
# if there is anything in the atomicupdate, read and execute it.
16488
16518
(-)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 589-606 Circulation: Link Here
589
              choices:
589
              choices:
590
                  ItemHomeLibrary: "item's home library"
590
                  ItemHomeLibrary: "item's home library"
591
                  PatronLibrary: "patron's home library"
591
                  PatronLibrary: "patron's home library"
592
            - to see if the patron can place a hold on the item.    
592
            - to see if the patron can place a hold on the item.
593
        -
594
            - Mark a hold as problematic if it has been waiting for more than
595
            - pref: ReservesMaxPickUpDelay
596
              class: integer
597
            - days.
598
        -
593
        -
599
            - pref: ExpireReservesMaxPickUpDelay
594
            - pref: ExpireReservesMaxPickUpDelay
600
              choices:
595
              choices:
601
                  yes: Allow
596
                  yes: Allow
602
                  no: "Don't allow"
597
                  no: "Don't allow"
603
            - "holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay"
598
            - "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"
604
        -
599
        -
605
            - If using ExpireReservesMaxPickUpDelay, charge a borrower who allows his or her waiting hold to expire a fee of
600
            - If using ExpireReservesMaxPickUpDelay, charge a borrower who allows his or her waiting hold to expire a fee of
606
            - pref: ExpireReservesMaxPickUpDelayCharge
601
            - pref: ExpireReservesMaxPickUpDelayCharge
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt (+3 lines)
Lines 83-88 Link Here
83
                <th>Suspension charging interval</th>
83
                <th>Suspension charging interval</th>
84
                <th>Renewals allowed (count)</th>
84
                <th>Renewals allowed (count)</th>
85
                <th>Renewal period</th>
85
                <th>Renewal period</th>
86
                <th>Holds wait for pickup (day)</th>
86
                <th>No renewal before</th>
87
                <th>No renewal before</th>
87
                <th>Automatic renewal</th>
88
                <th>Automatic renewal</th>
88
                <th>No automatic renewal after</th>
89
                <th>No automatic renewal after</th>
Lines 276-281 Link Here
276
                    <td><input type="text" name="suspension_chargeperiod" id="suspension_chargeperiod" size="3" /> </td>
277
                    <td><input type="text" name="suspension_chargeperiod" id="suspension_chargeperiod" size="3" /> </td>
277
                    <td><input type="text" name="renewalsallowed" id="renewalsallowed" size="2" /></td>
278
                    <td><input type="text" name="renewalsallowed" id="renewalsallowed" size="2" /></td>
278
                    <td><input type="text" name="renewalperiod" id="renewalperiod" size="3" /></td>
279
                    <td><input type="text" name="renewalperiod" id="renewalperiod" size="3" /></td>
280
                    <td><input type="text" name="holdspickupwait" id="holdspickupwait" size="2" /></td>
279
                    <td><input type="text" name="norenewalbefore" id="norenewalbefore" size="3" /></td>
281
                    <td><input type="text" name="norenewalbefore" id="norenewalbefore" size="3" /></td>
280
                    <td>
282
                    <td>
281
                        <select name="auto_renew" id="auto_renew">
283
                        <select name="auto_renew" id="auto_renew">
Lines 340-345 Link Here
340
                      <th>Suspension charging interval</th>
342
                      <th>Suspension charging interval</th>
341
                      <th>Renewals allowed (count)</th>
343
                      <th>Renewals allowed (count)</th>
342
                      <th>Renewal period</th>
344
                      <th>Renewal period</th>
345
                      <th>Holds wait for pickup (day)</th>
343
                      <th>No renewal before</th>
346
                      <th>No renewal before</th>
344
                      <th>Automatic renewal</th>
347
                      <th>Automatic renewal</th>
345
                      <th>No automatic renewal after</th>
348
                      <th>No automatic renewal after</th>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt (+1 lines)
Lines 883-888 No patron matched <span class="ex">[% message | html %]</span> Link Here
883
            <thead>
883
            <thead>
884
                <tr>
884
                <tr>
885
                    <th>Hold date</th>
885
                    <th>Hold date</th>
886
                    <th>Last pickup date</th>
886
                    <th>Title</th>
887
                    <th>Title</th>
887
                    <th>Call number</th>
888
                    <th>Call number</th>
888
                    <th>Barcode</th>
889
                    <th>Barcode</th>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/waitingreserves.tt (-2 / +2 lines)
Lines 84-90 Link Here
84
               </tr></thead>
84
               </tr></thead>
85
               <tbody>[% FOREACH reserveloo IN reserveloop %]
85
               <tbody>[% FOREACH reserveloo IN reserveloop %]
86
                <tr>
86
                <tr>
87
                    <td><span title="[% reserveloo.waitingdate | html %]">[% reserveloo.waitingdate | $KohaDates %]</span></td>
87
                    <td><span title="[% reserveloo.waitingdate | html %]">[% reserveloo.waitingdate | $KohaDates %] - [% reserveloo.lastpickupdate | $KohaDates %]</span></td>
88
                    <td><span title="[% reserveloo.reservedate | html %]">[% reserveloo.reservedate | $KohaDates %]</span></td>
88
                    <td><span title="[% reserveloo.reservedate | html %]">[% reserveloo.reservedate | $KohaDates %]</span></td>
89
                    <td>[% INCLUDE 'biblio-default-view.inc' biblionumber = reserveloo.biblionumber %]
89
                    <td>[% INCLUDE 'biblio-default-view.inc' biblionumber = reserveloo.biblionumber %]
90
                        [% reserveloo.title | html %] [% FOREACH subtitl IN reserveloo.subtitle %] [% subtitl.subfield | html %][% END %]
90
                        [% reserveloo.title | html %] [% FOREACH subtitl IN reserveloo.subtitle %] [% subtitl.subfield | html %][% END %]
Lines 155-161 Link Here
155
               </tr></thead>
155
               </tr></thead>
156
               <tbody>[% FOREACH overloo IN overloop %]
156
               <tbody>[% FOREACH overloo IN overloop %]
157
                    <tr>
157
                    <tr>
158
                        <td><span title="[% overloo.waitingdate | html %]">[% overloo.waitingdate | $KohaDates %]</span></td>
158
                        <td><span title="[% overloo.waitingdate | html %]">[% overloo.waitingdate | $KohaDates %] - [% overloo.lastpickupdate | $KohaDates %]</span></td>
159
                        <td><span title="[% overloo.reservedate | html %]">[% overloo.reservedate | $KohaDates %]</span></td>
159
                        <td><span title="[% overloo.reservedate | html %]">[% overloo.reservedate | $KohaDates %]</span></td>
160
                        <td>[% INCLUDE 'biblio-default-view.inc' biblionumber = overloo.biblionumber %][% overloo.title | html %]
160
                        <td>[% INCLUDE 'biblio-default-view.inc' biblionumber = overloo.biblionumber %][% overloo.title | html %]
161
                            [% FOREACH subtitl IN overloo.subtitle %] [% subtitl.subfield | html %][% END %]
161
                            [% FOREACH subtitl IN overloo.subtitle %] [% subtitl.subfield | html %][% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/help/admin/smart-rules.tt (+165 lines)
Line 0 Link Here
1
[% INCLUDE 'help-top.inc' %]
2
3
<h1>Circulation and Fines Rules</h1>
4
5
<p>These rules define how your items are circulated, how/when fines are calculated and how holds are handled.</p>
6
7
<p>The rules are applied from most specific to less specific, using the first found in this order:</p>
8
9
<ul>
10
	<li>same library, same patron type, same item type</li>
11
	<li>same library, same patron type, all item type</li>
12
	<li>same library, all patron types, same item type</li>
13
	<li>same library, all patron types, all item types</li>
14
	<li>all libraries, same patron type, same item type</li>
15
	<li>all libraries, same patron type, all item types</li>
16
	<li>all libraries, all patron types, same item type</li>
17
	<li>all libraries, all patron types, all item types</li>
18
</ul>
19
20
<p>The CircControl and HomeOrHoldingBranch also come in to play when figuring out which circulation rule to follow.</p>
21
22
<ul>
23
	<li>If CircControl is set to "the library you are logged in at" circ rules will be selected based on the library you are logged in at</li>
24
	<li>If CircControl is set to "the library the patron is from" circ rules will be selected based on the patron's library</li>
25
	<li>If CircControl is set to "the library the item is from" circ rules will be selected based on the item's library where HomeOrHoldingBranch chooses if item's home library is used or holding library is used.</li>
26
	<li>If IndependentBranches is set to 'Prevent' then the value of HomeOrHoldingBranch is used in figuring out if the item can be checked out. If the item's home library does not match the logged in library, the item cannot be checked out unless you are a superlibrarian.</li>
27
</ul>
28
29
<p>If you are a single library system choose your branch name before creating rules (sometimes having only rules for the 'all libraries' option can cause issues with holds)</p>
30
31
<p style="color: #990000;">Important: At the very least you will need to set a default circulation rule. This rule should be set for all item types, all libraries and all patron categories. That will catch all instances that do not match a specific rule. When checking out if you do not have a rule for all libraries, all item types and all patron types then you may see patrons getting blocked from placing holds. You will also want a rule for your specific library set for all item types and all patron types to avoid this holds issue. Koha needs to know what rule to fall back on.</p>
32
33
<h4>Default Circulation Rules</h4>
34
35
<p>Using the issuing rules matrix you can define rules that depend on patron/item type combinations. To set your rules, choose a library from the pull down (or 'all libraries' if you want to apply these rules to all libraries)</p>
36
37
<p>From the matrix you can choose any combination of patron categories and item types to apply the rules to</p>
38
39
<ul>
40
	<li>First choose which patron category you'd like the rule to be applied to. If you leave this to 'All' it will apply to all patron categories</li>
41
	<li>Choose the 'Item Type' you would like this rule to apply to. If you leave this to 'All' it will apply to all item types</li>
42
	<li>Limit the number of items a patron can have checked out at the same time by entering a number in the 'Current Checkouts Allowed' field</li>
43
    <li>Define the period of time an item can be checked out to a patron by entering the number of units (days or hours) in the 'Loan Period' box.</li>
44
    <li>Choose which unit of time, Days or Hours, that the loan period and fines will be calculate in</li>
45
    <li>You can also define a hard due date for a specific patron category and item type. A hard due date ignores your usual circulation rules and makes it so that all items of the type defined are due on, before or after the date you specify.</li>
46
    <li>'Fine Amount' should have the amount you would like to charge for overdue items
47
<ul>
48
    <li style="color: #990000;">Important: Enter only numbers and decimal points (no currency symbols).</li>
49
</ul>
50
</li>
51
    <li>Enter the 'Fine Charging Interval' in the unit you set (ex. charge fines every 1 day, or every 2 hours)</li>
52
    <li>The 'Fine Grace Period' is the period of time an item can be overdue before you start charging fines.
53
<ul>
54
    <li style="color: #990000;">Important: This can only be set for the Day unit, not in Hours</li>
55
</ul>
56
</li>
57
    <li>The 'Overdue Fines Cap' is the maximum fine for this patron and item combination
58
<ul>
59
    <li style="color: #990000;">Important: If this field is left blank then Koha will not put a limit on the fines this item will accrue. A maximum fine amount can be set using the MaxFinesystem preference.</li>
60
</ul>
61
</li>
62
    <li>If your library 'fines' patrons by suspending their account you can enter the number of days their fine should be suspended in the 'Suspension in Days' field
63
<ul>
64
    <li style="color: #990000;">Important: This can only be set for the Day unit, not in Hours</li>
65
</ul>
66
</li>
67
    <li>You can also define the maximum number of days a patron will be suspended in the 'Max suspension duration' setting</li>
68
    <li>Next decide if the patron can renew this item type and if so, enter how many times they can renew it in the 'Renewals Allowed' box</li>
69
    <li>If you're allowing renewals you can control how long the renewal loan period will be (in the units you have chosen) in the 'Renewal period' box</li>
70
    <li><i>Holds wait for pickup (day)</i> - After a hold is caught and put waiting for pickup, the hold will wait for this many days until it becomes problematic. Set it to 0 or less to disable the expiration of waiting holds. This value respects the Calendar holidays, skipping the last pickup date to the next available open library day.</li>
71
    <li>If you're allowing renewals you can control how soon before the due date patrons can renew their materials with the 'No renewals before' box.
72
    <ul><li>Items can be renewed at any time if this value is left blank. Otherwise items can only be renewed if the item is before the number in units (days/hours) entered in this box.</li></ul></li>
73
    <li>You can enable automatic renewals for certain items/patrons if you'd like. This will renew automatically following your circulation rules unless there is a hold on the item
74
    <ul>
75
    <li style="color: #990000;">Important: You will need to enable the automatic renewal cron job for this to work.</li>
76
    <li style="color: #990000;">Important: This feature needs to have the "no renewal before" column filled in or it will auto renew everyday after the due date.</li>
77
    </ul>
78
    </li>
79
    <li>If the patron can place holds on this item type, enter the total numbers of items (of this type) that can be put on hold in the 'Holds Allowed' field</li>
80
    <li>Next you can decide if this patron/item combo are allowed to place holds on items that are on the shelf (or available in the library) or not. If you choose 'no' then items can only be placed on hold if checked out</li>
81
    <li>You can also decide if patrons are allowed to place item specific holds on the item type in question. The options are:
82
    <ul>
83
    <li>Allow: Will allow patrons the option to choose next available or item specific</li>
84
    <li>Don't allow: Will only allow patrons to choose next available</li>
85
    <li>Force: Will only allow patrons to choose an specific item</li>
86
    </ul></li>
87
    <li>Finally, if you charge a rental fee for the item type and want to give a specific patron type a discount on that fee, enter the percentage discount (without the % symbol) in the 'Rental Discount' field</li>
88
</ul>
89
90
<p>When finished, click 'Add' to save your changes. To modify a rule, create a new one with the same patron type and item type. If you would like to delete your rule, simply click the 'Delete' link to the right of the rule.</p>
91
92
<p>To save time you can clone rules from one library to another by choosing the clone option above the rules matrix.</p>
93
94
<p>After choosing to clone you will be presented with a confirmation message.</p>
95
96
<h4>Default Checkouts and Hold Policy</h4>
97
98
<p>You can set a default maximum number of checkouts and hold policy that will be used if none is defined below for a particular item type or category.</p>
99
100
<p>From this menu you can set a default to apply to all item types and patrons in the library.</p>
101
102
<ul>
103
    <li>In 'Total Current Checkouts Allowed' enter the total number of items patrons can have checked out at one time</li>
104
    <li>Control where patrons can place holds from using the 'Hold Policy' menu
105
<ul>
106
    <li>From Any Library: Patrons from any library may put this item on hold. (default if none is defined)</li>
107
    <li>From Home Library: Only patrons from the item's home library may put this book on hold.</li>
108
    <li>No Holds Allowed: No patron may put this book on hold.</li>
109
</ul>
110
</li>
111
    <li>Control where the item returns to once it is checked in
112
<ul>
113
    <li>Item returns home</li>
114
    <li>Item returns to issuing library</li>
115
    <li>Item floats
116
<ul>
117
    <li>When an item floats it stays where it was checked in and does not ever return 'home'</li>
118
</ul>
119
</li>
120
</ul>
121
</li>
122
	<li>Once your policy is set, you can unset it by clicking the 'Unset' link to the right of the rule</li>
123
</ul>
124
125
<h4>Checkouts Per Patron</h4>
126
127
<p>For this library, you can specify the maximum number of loans that a patron of a given category can make, regardless of the item type.</p>
128
129
<p>Tip: If the total amount loanable for a given patron category is left blank, no limit applies, except possibly for a limit you define for a specific item type.</p>
130
131
<h4>Item Hold Policies</h4>
132
133
<p>For this library, you can edit rules for given itemtypes, regardless of the patron's category. Currently, this means hold policies.</p>
134
135
<p>The various Hold Policies have the following effects:</p>
136
137
<ul>
138
    <li>From Any Library: Patrons from any library may put this item on hold. (default if none is defined)</li>
139
    <li>From Home Library: Only patrons from the item's home library may put this book on hold.</li>
140
    <li>No Holds Allowed: No patron may put this book on hold.</li>
141
</ul>
142
143
<p style="color: #990000;">Important: Note that if the system preference AllowHoldPolicyOverrideset to 'allow', these policies can be overridden by your circulation staff.</p>
144
145
<p style="color: #990000;">Important: These policies are based on the patron's home library, not the library that the reserving staff member is from.</p>
146
147
<p>The various Return Policies have the following effects:</p>
148
149
<ul>
150
    <li>Item returns home: The item will prompt the librarian to transfer the item to its home library
151
<ul>
152
    <li style="color: #990000;">Important: If the AutomaticItemReturnpreference is set to automatically transfer the items home, then a prompt will not appear</li>
153
</ul>
154
</li>
155
    <li>Item returns to issuing library: The item will prompt the librarian to transfer the item back to the library where it was checked out
156
<ul>
157
    <li style="color: #990000;">Important: If the AutomaticItemReturnpreference is set to automatically transfer the items home, then a prompt will not appear</li>
158
</ul>
159
</li>
160
    <li>Item floats: The item will not be transferred from the library it was checked in at, instead it will remain there until transferred manually or checked in at another library</li>
161
</ul>
162
163
<p><strong>See the full documentation for Circulation and Fine Rules in the <a href="http://manual.koha-community.org/[% helpVersion %]/en/patscirc.html#circfinerules">manual</a> (online).</strong></p>
164
165
[% INCLUDE 'help-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/help/circ/waitingreserves.tt (+11 lines)
Line 0 Link Here
1
[% INCLUDE 'help-top.inc' %]
2
3
<h1>Holds awaiting pickup</h1>
4
5
<p>This report will show all of the holds that are waiting for patrons to pick them up.</p>
6
7
<p>Items that have been on the hold shelf longer than you normally allow (based on the holds pickup delay defined in the issuing rules) will appear on the 'Holds Over' tab, they will not automatically be cancelled.</p>
8
9
<p><strong>See the full documentation for Holds Awaiting Pickup in the <a href="http://manual.koha-community.org/[% helpVersion %]/en/circreports.html#holdspickup">manual</a> (online).</strong></p>
10
11
[% INCLUDE 'help-bottom.inc' %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt (-4 / +1 lines)
Lines 675-684 Link Here
675
                                                        [% IF ( RESERVE.found ) %]
675
                                                        [% IF ( RESERVE.found ) %]
676
                                                            Item waiting at <b> [% RESERVE.branch.branchname | html %]</b>
676
                                                            Item waiting at <b> [% RESERVE.branch.branchname | html %]</b>
677
                                                            [% IF ( RESERVE.waitingdate ) %]
677
                                                            [% IF ( RESERVE.waitingdate ) %]
678
                                                                since [% RESERVE.waitingdate | $KohaDates %]
678
                                                                since [% RESERVE.waitingdate | $KohaDates %] until [% RESERVE.lastpickupdate | $KohaDates %]
679
                                                                [% IF RESERVE.expirationdate %]
680
                                                                    until [% RESERVE.expirationdate | $KohaDates %]
681
                                                                [% END %]
682
                                                            [% END %]
679
                                                            [% END %]
683
                                                            <input type="hidden" name="pickup" value="[% RESERVE.branchcode | html %]" />
680
                                                            <input type="hidden" name="pickup" value="[% RESERVE.branchcode | html %]" />
684
                                                        [% ELSE %]
681
                                                        [% ELSE %]
(-)a/svc/holds (-1 / +6 lines)
Lines 48-54 my $branch = C4::Context->userenv->{'branch'}; Link Here
48
my $schema = Koha::Database->new()->schema();
48
my $schema = Koha::Database->new()->schema();
49
49
50
my @sort_columns =
50
my @sort_columns =
51
  qw/reservedate title itemcallnumber barcode expirationdate priority/;
51
  qw/reservedate lastpickupdate title itemcallnumber barcode expirationdate priority/;
52
52
53
my $borrowernumber    = $input->param('borrowernumber');
53
my $borrowernumber    = $input->param('borrowernumber');
54
my $offset            = $input->param('iDisplayStart');
54
my $offset            = $input->param('iDisplayStart');
Lines 93-98 while ( my $h = $holds_rs->next() ) { Link Here
93
        branchcode     => $h->branch()->branchname(),
93
        branchcode     => $h->branch()->branchname(),
94
        branches       => $libraries,
94
        branches       => $libraries,
95
        reservedate    => $h->reservedate(),
95
        reservedate    => $h->reservedate(),
96
        lastpickupdate => $h->lastpickupdate(),
96
        expirationdate => $h->expirationdate(),
97
        expirationdate => $h->expirationdate(),
97
        suspend        => $h->suspend(),
98
        suspend        => $h->suspend(),
98
        suspend_until  => $h->suspend_until(),
99
        suspend_until  => $h->suspend_until(),
Lines 111-116 while ( my $h = $holds_rs->next() ) { Link Here
111
            { dt => dt_from_string( $h->reservedate() ), dateonly => 1 }
112
            { dt => dt_from_string( $h->reservedate() ), dateonly => 1 }
112
          )
113
          )
113
        : q{},
114
        : q{},
115
        lastpickupdate_formatted => $h->lastpickupdate() ? output_pref(
116
            { dt => dt_from_string( $h->lastpickupdate() ), dateonly => 1 }
117
          )
118
        : q{},
114
        suspend_until_formatted => $h->suspend_until() ? output_pref(
119
        suspend_until_formatted => $h->suspend_until() ? output_pref(
115
            { dt => dt_from_string( $h->suspend_until() ), dateonly => 1 }
120
            { dt => dt_from_string( $h->suspend_until() ), dateonly => 1 }
116
          )
121
          )
(-)a/t/db_dependent/Holds.t (-6 / +205 lines)
Lines 7-13 use t::lib::TestBuilder; Link Here
7
7
8
use C4::Context;
8
use C4::Context;
9
9
10
use Test::More tests => 56;
10
use Test::More tests => 59;
11
use MARC::Record;
11
use MARC::Record;
12
use Koha::Patrons;
12
use Koha::Patrons;
13
use C4::Items;
13
use C4::Items;
Lines 382-390 is(CanItemBeReserved($borrowernumbers[0], $itemnumber)->{status}, Link Here
382
    "CanItemBeReserved should return 'cannotReserveFromOtherBranches'");
382
    "CanItemBeReserved should return 'cannotReserveFromOtherBranches'");
383
383
384
($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem(
384
($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem(
385
    { homebranch => $branch_1, holdingbranch => $branch_1, itype => 'CAN' } , $bibnum);
385
    { homebranch => 'CPL', holdingbranch => 'CPL', itype => 'CAN' } , $bibnum);
386
is(CanItemBeReserved($borrowernumbers[0], $itemnumber)->{status}, 'OK',
386
is(CanItemBeReserved($borrowernumbers[0], $itemnumber), 'OK',
387
    "CanItemBeReserved should return 'OK'");
387
    "CanItemBeReserved should returns 'OK'");
388
389
##Setting duration variables
390
my $now = DateTime->now();
391
my $minus4days = DateTime::Duration->new(days => -4);
392
my $minus1days = DateTime::Duration->new(days => -1);
393
my $plus1days = DateTime::Duration->new(days => 1);
394
my $plus4days = DateTime::Duration->new(days => 4);
395
##Setting some test prerequisites testing environment
396
C4::Context->set_preference( 'ExpireReservesMaxPickUpDelay', 1 );
397
setSimpleCircPolicy();
398
setCalendars();
399
#Running more tests
400
testGetLastPickupDate();
401
testMoveWaitingdate();
402
testCancelExpiredReserves();
403
C4::Context->set_preference( 'ExpireReservesMaxPickUpDelay', 0 );
404
405
## Environment should be the following
406
## Holidays: days from today; -2,-3,-4
407
sub testCancelExpiredReserves {
408
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 1'); #Make holds problematize after 1 day
409
410
    $reserves = $dbh->selectall_arrayref('SELECT * FROM reserves WHERE found IS NULL', { Slice => {} });
411
    $reserve = $reserves->[0];
412
    #Catch this hold and make it Waiting for pickup today.
413
    C4::Reserves::ModReserveAffect( $reserve->{itemnumber}, $reserve->{borrowernumber} );
414
    $reserve = C4::Reserves::GetReserve( $reserve->{reserve_id} ); #UPDATE DB changes to local scope.
415
416
    CancelExpiredReserves();
417
    my $count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
418
    is( $count, 1, "Waiting reserve with lastpickupdate for ".$reserve->{lastpickupdate}." not canceled" );
419
420
    C4::Reserves::MoveWaitingdate( $reserve, DateTime::Duration->new(days => -4) );
421
    CancelExpiredReserves();
422
    $count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
423
    is( $count, 0, "Waiting reserve with lastpickupdate for ".$reserve->{lastpickupdate}." totally canceled" );
424
425
    # Test expirationdate
426
    $reserve = $reserves->[1];
427
    $dbh->do("UPDATE reserves SET expirationdate = DATE_SUB( NOW(), INTERVAL 1 DAY ) WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
428
    CancelExpiredReserves();
429
    $count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
430
    is( $count, 0, "Reserve with manual expiration date canceled correctly" );
431
432
    #This test verifies that reserves with holdspickupwait disabled are not ćanceled!
433
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 0'); #Make holds never problematize
434
    $reserve = $reserves->[2];
435
    #Catch this hold and make it Waiting for pickup today.
436
    C4::Reserves::ModReserveAffect( $reserve->{itemnumber}, $reserve->{borrowernumber} );
437
    $reserve = C4::Reserves::GetReserve( $reserve->{reserve_id} ); #UPDATE DB changes to local scope.
438
    #Move the caught reserve 4 days to past and try to cancel it.
439
    C4::Reserves::MoveWaitingdate( $reserve, $minus4days );
440
    CancelExpiredReserves();
441
    $count = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE reserve_id = ?", undef, $reserve->{reserve_id} );
442
    is( $count, 1, "CancelExpiredReserves(): not canceling lastpickupdate-less hold." );
443
}
444
445
## Environment should be the following
446
## Holidays: days from today; -2,-3,-4
447
sub testMoveWaitingdate {
448
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 1'); #Make holds problematize after 1 day
449
450
    $reserves = $dbh->selectall_arrayref('SELECT * FROM reserves WHERE found IS NULL', { Slice => {} }); #Get reserves not waiting for pickup
451
    $reserve = $reserves->[0];
452
453
    C4::Reserves::ModReserveAffect( $reserve->{itemnumber}, $reserve->{borrowernumber} ); #Catch the reserve and put it to wait for pickup, now we get a waitingdate generated.
454
455
    C4::Reserves::MoveWaitingdate( $reserve, $minus1days );
456
    $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.
457
    is( ($reserve->{waitingdate} eq $now->clone()->add_duration($minus1days)->ymd() &&
458
         $reserve->{lastpickupdate} eq $now->ymd()),
459
         1, "MoveWaitingdate(): Moving to past");
460
    C4::Reserves::MoveWaitingdate( $reserve, $plus1days );
461
462
    C4::Reserves::MoveWaitingdate( $reserve, $plus4days );
463
    $reserve = C4::Reserves::GetReserve( $reserve_id );
464
    is( ($reserve->{waitingdate} eq $now->clone()->add_duration($plus4days)->ymd() &&
465
         $reserve->{lastpickupdate} eq $now->clone()->add_duration($plus4days)->add_duration($plus1days)->ymd()),
466
         1, "MoveWaitingdate(): Moving to future");
467
    C4::Reserves::MoveWaitingdate( $reserve, $minus4days );
468
}
469
470
## Environment should be the following
471
## Holidays: days from today; -2,-3,-4
472
sub testGetLastPickupDate {
473
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 1'); #Make holds problematize after 1 day
474
475
    my $now = DateTime->now();
476
    my $minus4days = DateTime::Duration->new(days => -4);
477
    my $minus1days = DateTime::Duration->new(days => -1);
478
    my $plus1days = DateTime::Duration->new(days => 1);
479
    my $plus4days = DateTime::Duration->new(days => 4);
480
481
    $reserves = $dbh->selectall_arrayref('SELECT * FROM reserves', { Slice => {} }); #Get reserves not waiting for pickup
482
    $reserve = $reserves->[0];
483
484
    $reserve->{waitingdate} = $now->clone()->add_duration($minus4days)->ymd();
485
    my $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve )->ymd();
486
    $reserve = C4::Reserves::GetReserve( $reserve_id ); #UPDATE DB changes to local scope
487
    is( $lastpickupdate, $now->clone()->add_duration($minus1days)->ymd(),
488
         "GetLastPickupDate(): Calendar finds the next open day for lastpickupdate.");
489
490
    $reserve->{waitingdate} = $now->clone()->add_duration($minus1days)->ymd();
491
    $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve )->ymd();
492
    is( $lastpickupdate, $now->ymd(),
493
         "GetLastPickupDate(): Not using Calendar");
494
495
    $reserve->{waitingdate} = $now->clone()->add_duration($plus4days)->ymd();
496
    $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve )->ymd();
497
    is( $lastpickupdate, $now->clone()->add_duration($plus4days)->add_duration($plus1days)->ymd(),
498
         "GetLastPickupDate(): Moving to future");
499
500
    #This test catches moving lastpickupdate for each holiday, instead of just moving the last date to an open library day
501
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 4'); #Make holds problematize after 4 days
502
    $reserve->{waitingdate} = $now->clone()->add_duration($minus4days)->ymd();
503
    $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve )->ymd();
504
    is( $lastpickupdate, $now->ymd(),
505
         "GetLastPickupDate(): Moving lastpickupdate over holidays, but not affected by them");
506
507
    #This test verifies that this feature is disabled and an undef is returned
508
    $dbh->do('UPDATE issuingrules SET holdspickupwait = 0'); #Make holds never problematize
509
    $reserve->{waitingdate} = $now->clone()->add_duration($minus4days)->ymd();
510
    $lastpickupdate = C4::Reserves::GetLastPickupDate( $reserve );
511
    is( $reserve->{lastpickupdate}, undef,
512
         "GetLastPickupDate(): holdspickupwait disabled");
513
}
388
514
389
# Bug 12632
515
# Bug 12632
390
t::lib::Mocks::mock_preference( 'item-level_itypes',     1 );
516
t::lib::Mocks::mock_preference( 'item-level_itypes',     1 );
Lines 513-519 subtest 'Pickup location availability tests' => sub { Link Here
513
639
514
# Helper method to set up a Biblio.
640
# Helper method to set up a Biblio.
515
sub create_helper_biblio {
641
sub create_helper_biblio {
516
    my $itemtype = shift;
642
    my $itemtype = $_[0] ? $_[0] : 'BK';
517
    my $bib = MARC::Record->new();
643
    my $bib = MARC::Record->new();
518
    my $title = 'Silence in the library';
644
    my $title = 'Silence in the library';
519
    $bib->append_fields(
645
    $bib->append_fields(
Lines 523-525 sub create_helper_biblio { Link Here
523
    );
649
    );
524
    return ($bibnum, $title, $bibitemnum) = AddBiblio($bib, '');
650
    return ($bibnum, $title, $bibitemnum) = AddBiblio($bib, '');
525
}
651
}
526
- 
652
653
sub setSimpleCircPolicy {
654
    $dbh->do('DELETE FROM issuingrules');
655
    $dbh->do(
656
        q{INSERT INTO issuingrules (categorycode, branchcode, itemtype, reservesallowed,
657
                                    maxissueqty, issuelength, lengthunit,
658
                                    renewalsallowed, renewalperiod,
659
                                    norenewalbefore, auto_renew,
660
                                    fine, chargeperiod, holdspickupwait)
661
          VALUES (?, ?, ?, ?,
662
                  ?, ?, ?,
663
                  ?, ?,
664
                  ?, ?,
665
                  ?, ?, ?
666
                 )
667
        },
668
        {},
669
        '*', '*', '*', 25,
670
        20, 14, 'days',
671
        1, 7,
672
        '', 0,
673
        .10, 1,1
674
    );
675
}
676
677
###Set C4::Calendar and Koha::Calendar holidays for
678
# today -2 days
679
# today -3 days
680
# today -4 days
681
#
682
## Koha::Calendar for caching purposes (supposedly) doesn't work from the DB in this script
683
## So we must set the cache for Koha::calnder as well as the DB modifications for C4::Calendar.
684
## When making date comparisons with Koha::Calendar, using DateTime::Set, DateTime-objects
685
## need to match by the nanosecond and time_zone.
686
sub setCalendars {
687
688
    ##Set the C4::Calendar
689
    my $now = DateTime->now(time_zone => C4::Context->tz())->truncate(to => 'day');
690
    my $c4calendar = C4::Calendar->new(branchcode => $reserve->{branchcode});
691
    $now->add_duration( DateTime::Duration->new(days => -2) );
692
    $c4calendar->insert_single_holiday(
693
        day         => $now->day(),
694
        month       => $now->month(),
695
        year        => $now->year(),
696
        title       => 'Test',
697
        description => 'Test',
698
    );
699
    $now->add_duration( DateTime::Duration->new(days => -1) );
700
    $c4calendar->insert_single_holiday(
701
        day         => $now->day(),
702
        month       => $now->month(),
703
        year        => $now->year(),
704
        title       => 'Test',
705
        description => 'Test',
706
    );
707
    $now->add_duration( DateTime::Duration->new(days => -1) );
708
    $c4calendar->insert_single_holiday(
709
        day         => $now->day(),
710
        month       => $now->month(),
711
        year        => $now->year(),
712
        title       => 'Test',
713
        description => 'Test',
714
    );
715
716
    #Set the Koha::Calendar
717
    my $kohaCalendar = Koha::Calendar->new(branchcode => $reserve->{branchcode});
718
    $now = DateTime->now(time_zone => C4::Context->tz())->truncate(to => 'day');
719
    $now->add_duration( DateTime::Duration->new(days => -2) );
720
    $kohaCalendar->add_holiday( $now );
721
    $now->add_duration( DateTime::Duration->new(days => -1) );
722
    $kohaCalendar->add_holiday( $now );
723
    $now->add_duration( DateTime::Duration->new(days => -1) );
724
    $kohaCalendar->add_holiday( $now );
725
}

Return to bug 8367