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

(-)a/C4/Reserves.pm (+3 lines)
Lines 921-926 sub CancelExpiredReserves { Link Here
921
        if ( $hold->found eq 'W' ) {
921
        if ( $hold->found eq 'W' ) {
922
            $cancel_params->{charge_cancel_fee} = 1;
922
            $cancel_params->{charge_cancel_fee} = 1;
923
        }
923
        }
924
925
        $cancel_params->{pickupexpired} = dt_from_string($hold->expirationdate);
926
924
        $hold->cancel( $cancel_params );
927
        $hold->cancel( $cancel_params );
925
    }
928
    }
926
}
929
}
(-)a/Koha/Hold.pm (+11 lines)
Lines 37-42 use Koha::Old::Holds; Link Here
37
use Koha::Calendar;
37
use Koha::Calendar;
38
38
39
use Koha::Exceptions::Hold;
39
use Koha::Exceptions::Hold;
40
use Koha::Exceptions::BadParameter;
40
41
41
use base qw(Koha::Object);
42
use base qw(Koha::Object);
42
43
Lines 371-376 sub cancel { Link Here
371
    my ( $self, $params ) = @_;
372
    my ( $self, $params ) = @_;
372
    $self->_result->result_source->schema->txn_do(
373
    $self->_result->result_source->schema->txn_do(
373
        sub {
374
        sub {
375
376
            my $pickupexpired = $params->{pickupexpired};
377
            if ($pickupexpired) {
378
                unless ($pickupexpired && $pickupexpired->isa('DateTime')) {
379
                    Koha::Exceptions::BadParameter->throw(error => "cancel():> Parameter 'pickupexpired' is not a DateTime-object or undef!");
380
                } else {
381
                    $self->pickupexpired($pickupexpired->ymd());
382
                }
383
            }
384
374
            $self->cancellationdate( dt_from_string->strftime( '%Y-%m-%d %H:%M:%S' ) );
385
            $self->cancellationdate( dt_from_string->strftime( '%Y-%m-%d %H:%M:%S' ) );
375
            $self->priority(0);
386
            $self->priority(0);
376
            $self->cancellation_reason( $params->{cancellation_reason} );
387
            $self->cancellation_reason( $params->{cancellation_reason} );
(-)a/Koha/Old/Holds.pm (+59 lines)
Lines 20-27 package Koha::Old::Holds; Link Here
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Carp;
22
use Carp;
23
use Scalar::Util qw(blessed);
23
24
24
use Koha::Database;
25
use Koha::Database;
26
use Koha::Exceptions::BadParameter;
25
27
26
use Koha::Old::Hold;
28
use Koha::Old::Hold;
27
29
Lines 39-44 This object represents a set of holds that have been filled or canceled Link Here
39
41
40
=cut
42
=cut
41
43
44
=head3
45
46
my $expired_holds = Koha::Old::Holds->expired();
47
48
Returns all holds that have already expired and moved to old_reserves during period
49
of time given in 'PickupExpiredHoldsOverReportDuration'.
50
51
If branchcode is given returns expired holds for that branch and take into account
52
its holidays.
53
54
=cut
55
56
sub expired {
57
    my ($self, $params) = @_;
58
59
    my $pickupExpiredHoldsOverReportDuration = C4::Context->preference('PickupExpiredHoldsOverReportDuration');
60
    return undef unless $pickupExpiredHoldsOverReportDuration;
61
62
    my $branchcode = $params->{branchcode};
63
    if ($params->{from}) {
64
        unless (blessed($params->{from}) && $params->{from}->isa('DateTime')) {
65
            Koha::Exceptions::BadParameter->throw(error => "expired():> Parameter 'from' is not a DateTime-object or undef!");
66
        }
67
    }
68
    if ($params->{to}) {
69
        unless (blessed($params->{from}) && $params->{from}->isa('DateTime')) {
70
            Koha::Exceptions::BadParameter->throw(error => "expired():> Parameter 'from' is not a DateTime-object or undef!");
71
        }
72
    }
73
74
    #Calculate the days for which we get the expired reserves.
75
    my $fromDate   = $params->{from};
76
    my $toDate     = $params->{to}   || DateTime->now(time_zone => C4::Context->tz());
77
78
    unless ($fromDate) {
79
        $fromDate = DateTime->now( time_zone => C4::Context->tz() );
80
        #Look for previous open days
81
        if ($branchcode) {
82
            my $calendar = Koha::Calendar->new( branchcode => $branchcode, days_mode => 'Default');
83
            foreach my $i (1..$pickupExpiredHoldsOverReportDuration) {
84
                $fromDate = $calendar->prev_open_days($fromDate, 1);
85
            }
86
        }
87
        #If no branch has been specified we cannot use a calendar, so simply just go back in time.
88
        else {
89
            $fromDate = DateTime->now(time_zone => C4::Context->tz())->subtract(days => $pickupExpiredHoldsOverReportDuration);
90
        }
91
    }
92
93
    my $search_params;
94
    $search_params->{priority} = 0;
95
    $search_params->{pickupexpired} = { -between => [ $fromDate->ymd(), $toDate->ymd() ] };
96
    $search_params->{branchcode} = $branchcode if defined $branchcode;
97
98
    return $self->search( $search_params, { order_by => 'waitingdate' } );
99
}
100
42
=head3 type
101
=head3 type
43
102
44
=cut
103
=cut
(-)a/circ/waitingreserves.pl (-1 / +8 lines)
Lines 39-44 use Koha::BiblioFrameworks; Link Here
39
use Koha::Items;
39
use Koha::Items;
40
use Koha::ItemTypes;
40
use Koha::ItemTypes;
41
use Koha::Patrons;
41
use Koha::Patrons;
42
use Koha::Old::Holds;
42
43
43
my $input = new CGI;
44
my $input = new CGI;
44
45
Lines 83-89 $template->param( all_branches => 1 ) if $all_branches; Link Here
83
my (@reserve_loop, @over_loop);
84
my (@reserve_loop, @over_loop);
84
# FIXME - Is priority => 0 useful? If yes it must be moved to waiting, otherwise we need to remove it from here.
85
# FIXME - Is priority => 0 useful? If yes it must be moved to waiting, otherwise we need to remove it from here.
85
my $holds = Koha::Holds->waiting->search({ priority => 0, ( $all_branches ? () : ( branchcode => $default ) ) }, { order_by => ['waitingdate'] });
86
my $holds = Koha::Holds->waiting->search({ priority => 0, ( $all_branches ? () : ( branchcode => $default ) ) }, { order_by => ['waitingdate'] });
86
87
my $expired_holds = $all_branches ? Koha::Old::Holds->expired() : Koha::Old::Holds->expired({branchcode => $default});
87
# get reserves for the branch we are logged into, or for all branches
88
# get reserves for the branch we are logged into, or for all branches
88
89
89
my $today = Date_to_Days(&Today);
90
my $today = Date_to_Days(&Today);
Lines 108-113 while ( my $hold = $holds->next ) { Link Here
108
    
109
    
109
}
110
}
110
111
112
if($expired_holds){
113
    while (my $expired_hold = $expired_holds->next){
114
        push @over_loop, $expired_hold;
115
    }
116
}
117
111
$template->param(cancel_result => \@cancel_result) if @cancel_result;
118
$template->param(cancel_result => \@cancel_result) if @cancel_result;
112
119
113
$template->param(
120
$template->param(
(-)a/installer/data/mysql/atomicupdate/bug_10744-expire_reserves_conflict_hold_over_report.perl (+16 lines)
Line 0 Link Here
1
$DBversion = 'XXX'; # will be replaced by the RM
2
if( CheckVersion( $DBversion ) ) {
3
    # you can use $dbh here like:
4
    # $dbh->do( "ALTER TABLE biblio ADD COLUMN badtaste int" );
5
6
    $dbh->do("ALTER TABLE reserves ADD `pickupexpired` DATE DEFAULT NULL AFTER `expirationdate`");
7
    $dbh->do("ALTER TABLE reserves ADD KEY `reserves_pickupexpired` (`pickupexpired`)");
8
    $dbh->do("ALTER TABLE old_reserves ADD `pickupexpired` DATE DEFAULT NULL AFTER `expirationdate`");
9
    $dbh->do("ALTER TABLE old_reserves ADD KEY `old_reserves_pickupexpired` (`pickupexpired`)");
10
11
    $dbh->do("INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('PickupExpiredHoldsOverReportDuration','1',NULL,\"For how many days holds expired by the 'ExpireReservesMaxPickUpDelay'-syspref are visible in the 'Hold Over'-tab in /circ/waitingreserves.pl ?\",'Integer')");
12
13
    # Always end with this (adjust the bug info)
14
    SetVersion( $DBversion );
15
    print "Upgrade to $DBversion done (Bug 10744 - ExpireReservesMaxPickUpDelay works with hold(s) over report)\n";
16
}
(-)a/installer/data/mysql/kohastructure.sql (+4 lines)
Lines 1774-1779 CREATE TABLE `reserves` ( -- information related to holds/reserves in Koha Link Here
1774
  `itemnumber` int(11) default NULL, -- foreign key from the items table defining the specific item the patron has placed on hold or the item this hold was filled with
1774
  `itemnumber` int(11) default NULL, -- foreign key from the items table defining the specific item the patron has placed on hold or the item this hold was filled with
1775
  `waitingdate` date default NULL, -- the date the item was marked as waiting for the patron at the library
1775
  `waitingdate` date default NULL, -- the date the item was marked as waiting for the patron at the library
1776
  `expirationdate` DATE DEFAULT NULL, -- the date the hold expires (usually the date entered by the patron to say they don't need the hold after a certain date)
1776
  `expirationdate` DATE DEFAULT NULL, -- the date the hold expires (usually the date entered by the patron to say they don't need the hold after a certain date)
1777
  `pickupexpired` DATE DEFAULT NULL, -- if hold has been waiting but it expired before it was picked up, the expiration date is set here
1777
  `lowestPriority` tinyint(1) NOT NULL DEFAULT 0,
1778
  `lowestPriority` tinyint(1) NOT NULL DEFAULT 0,
1778
  `suspend` tinyint(1) NOT NULL DEFAULT 0,
1779
  `suspend` tinyint(1) NOT NULL DEFAULT 0,
1779
  `suspend_until` DATETIME NULL DEFAULT NULL,
1780
  `suspend_until` DATETIME NULL DEFAULT NULL,
Lines 1786-1791 CREATE TABLE `reserves` ( -- information related to holds/reserves in Koha Link Here
1786
  KEY `biblionumber` (`biblionumber`),
1787
  KEY `biblionumber` (`biblionumber`),
1787
  KEY `itemnumber` (`itemnumber`),
1788
  KEY `itemnumber` (`itemnumber`),
1788
  KEY `branchcode` (`branchcode`),
1789
  KEY `branchcode` (`branchcode`),
1790
  KEY `reserves_pickupexpired` (`pickupexpired`),
1789
  KEY `itemtype` (`itemtype`),
1791
  KEY `itemtype` (`itemtype`),
1790
  CONSTRAINT `reserves_ibfk_1` FOREIGN KEY (`borrowernumber`) REFERENCES `borrowers` (`borrowernumber`) ON DELETE CASCADE ON UPDATE CASCADE,
1792
  CONSTRAINT `reserves_ibfk_1` FOREIGN KEY (`borrowernumber`) REFERENCES `borrowers` (`borrowernumber`) ON DELETE CASCADE ON UPDATE CASCADE,
1791
  CONSTRAINT `reserves_ibfk_2` FOREIGN KEY (`biblionumber`) REFERENCES `biblio` (`biblionumber`) ON DELETE CASCADE ON UPDATE CASCADE,
1793
  CONSTRAINT `reserves_ibfk_2` FOREIGN KEY (`biblionumber`) REFERENCES `biblio` (`biblionumber`) ON DELETE CASCADE ON UPDATE CASCADE,
Lines 1816-1821 CREATE TABLE `old_reserves` ( -- this table holds all holds/reserves that have b Link Here
1816
  `itemnumber` int(11) default NULL, -- foreign key from the items table defining the specific item the patron has placed on hold or the item this hold was filled with
1818
  `itemnumber` int(11) default NULL, -- foreign key from the items table defining the specific item the patron has placed on hold or the item this hold was filled with
1817
  `waitingdate` date default NULL, -- the date the item was marked as waiting for the patron at the library
1819
  `waitingdate` date default NULL, -- the date the item was marked as waiting for the patron at the library
1818
  `expirationdate` DATE DEFAULT NULL, -- the date the hold expires (usually the date entered by the patron to say they don't need the hold after a certain date)
1820
  `expirationdate` DATE DEFAULT NULL, -- the date the hold expires (usually the date entered by the patron to say they don't need the hold after a certain date)
1821
  `pickupexpired` DATE DEFAULT NULL, -- if hold has been waiting but it expired before it was picked up, the expiration date is set here
1819
  `lowestPriority` tinyint(1) NOT NULL DEFAULT 0, -- has this hold been pinned to the lowest priority in the holds queue (1 for yes, 0 for no)
1822
  `lowestPriority` tinyint(1) NOT NULL DEFAULT 0, -- has this hold been pinned to the lowest priority in the holds queue (1 for yes, 0 for no)
1820
  `suspend` tinyint(1) NOT NULL DEFAULT 0, -- in this hold suspended (1 for yes, 0 for no)
1823
  `suspend` tinyint(1) NOT NULL DEFAULT 0, -- in this hold suspended (1 for yes, 0 for no)
1821
  `suspend_until` DATETIME NULL DEFAULT NULL, -- the date this hold is suspended until (NULL for infinitely)
1824
  `suspend_until` DATETIME NULL DEFAULT NULL, -- the date this hold is suspended until (NULL for infinitely)
Lines 1827-1832 CREATE TABLE `old_reserves` ( -- this table holds all holds/reserves that have b Link Here
1827
  KEY `old_reserves_biblionumber` (`biblionumber`),
1830
  KEY `old_reserves_biblionumber` (`biblionumber`),
1828
  KEY `old_reserves_itemnumber` (`itemnumber`),
1831
  KEY `old_reserves_itemnumber` (`itemnumber`),
1829
  KEY `old_reserves_branchcode` (`branchcode`),
1832
  KEY `old_reserves_branchcode` (`branchcode`),
1833
  KEY `old_reserves_pickupexpired` (`pickupexpired`),
1830
  KEY `old_reserves_itemtype` (`itemtype`),
1834
  KEY `old_reserves_itemtype` (`itemtype`),
1831
  CONSTRAINT `old_reserves_ibfk_1` FOREIGN KEY (`borrowernumber`) REFERENCES `borrowers` (`borrowernumber`)
1835
  CONSTRAINT `old_reserves_ibfk_1` FOREIGN KEY (`borrowernumber`) REFERENCES `borrowers` (`borrowernumber`)
1832
    ON DELETE SET NULL ON UPDATE SET NULL,
1836
    ON DELETE SET NULL ON UPDATE SET NULL,
(-)a/installer/data/mysql/sysprefs.sql (+1 lines)
Lines 190-195 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
190
('EnhancedMessagingPreferencesOPAC', '1', NULL, 'If ON, show patrons messaging setting on the OPAC.', 'YesNo'),
190
('EnhancedMessagingPreferencesOPAC', '1', NULL, 'If ON, show patrons messaging setting on the OPAC.', 'YesNo'),
191
('expandedSearchOption','0',NULL,'If ON, set advanced search to be expanded by default','YesNo'),
191
('expandedSearchOption','0',NULL,'If ON, set advanced search to be expanded by default','YesNo'),
192
('ExpireReservesMaxPickUpDelay','0','','Enabling this allows holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay','YesNo'),
192
('ExpireReservesMaxPickUpDelay','0','','Enabling this allows holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay','YesNo'),
193
('PickupExpiredHoldsOverReportDuration','1',NULL,"For how many days holds expired by the 'ExpireReservesMaxPickUpDelay'-syspref are visible in the 'Hold Over'-tab in /circ/waitingreserves.pl ?",'Integer'),
193
('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'),
194
('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'),
194
('ExpireReservesOnHolidays', '1', NULL, 'If false, reserves at a library will not be canceled on days the library is not open.', 'YesNo'),
195
('ExpireReservesOnHolidays', '1', NULL, 'If false, reserves at a library will not be canceled on days the library is not open.', 'YesNo'),
195
('ExcludeHolidaysFromMaxPickUpDelay', '0', NULL, 'If ON, reserves max pickup delay takes into accountthe closed days.', 'YesNo'),
196
('ExcludeHolidaysFromMaxPickUpDelay', '0', NULL, 'If ON, reserves max pickup delay takes into accountthe closed days.', 'YesNo'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (+4 lines)
Lines 707-712 Circulation: Link Here
707
            - pref: ExpireReservesMaxPickUpDelayCharge
707
            - pref: ExpireReservesMaxPickUpDelayCharge
708
              class: currency
708
              class: currency
709
            - "."
709
            - "."
710
        -
711
            - pref: PickupExpiredHoldsOverReportDuration
712
              class: integer
713
            - "For how many days holds expired by the 'ExpireReservesMaxPickUpDelay'-syspref are visible in the 'Hold Over'-tab in /circ/waitingreserves.pl ?"
710
        -
714
        -
711
            - Satisfy holds using items from the libraries
715
            - Satisfy holds using items from the libraries
712
            - pref: StaticHoldsQueueWeight
716
            - pref: StaticHoldsQueueWeight
(-)a/t/db_dependent/Koha/Old.t (-2 / +102 lines)
Lines 19-36 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 2;
22
use Test::More tests => 3;
23
23
24
use DateTime;
25
use DateTime::TimeZone;
26
27
use C4::Reserves;
24
use Koha::Database;
28
use Koha::Database;
25
use Koha::Old::Patrons;
29
use Koha::Old::Patrons;
26
use Koha::Old::Biblios;
30
use Koha::Old::Biblios;
27
use Koha::Old::Items;
31
use Koha::Old::Items;
32
use Koha::Old::Holds;
33
use Koha::Calendar;
28
34
29
use t::lib::TestBuilder;
35
use t::lib::TestBuilder;
36
use t::lib::Mocks;
30
37
31
my $schema = Koha::Database->new->schema;
38
my $schema = Koha::Database->new->schema;
32
$schema->storage->txn_begin;
39
$schema->storage->txn_begin;
33
40
41
#since we use calendar here, flush caches
42
Koha::Caches->get_instance()->flush_all();
43
34
my $builder = t::lib::TestBuilder->new;
44
my $builder = t::lib::TestBuilder->new;
35
45
36
subtest 'Koha::Old::Patrons' => sub {
46
subtest 'Koha::Old::Patrons' => sub {
Lines 54-57 subtest 'Koha::Old::Biblios and Koha::Old::Items' => sub { Link Here
54
    # Cannot be tested in a meaningful way so far
64
    # Cannot be tested in a meaningful way so far
55
    ok(1);
65
    ok(1);
56
};
66
};
67
57
$schema->storage->txn_rollback;
68
$schema->storage->txn_rollback;
58
- 
69
70
subtest 'Koha::Old::Holds' => sub {
71
72
    $schema->storage->txn_begin;
73
74
    plan tests => 5;
75
76
    my $branch = $builder->build_object({ class => 'Koha::Libraries' });
77
    my $calendar = Koha::Calendar->new( branchcode => $branch->branchcode );
78
    my $holiday_date = DateTime->now(time_zone => C4::Context->tz())->subtract(days => 2);
79
    my $holiday = $builder->build({
80
        source => 'SpecialHoliday',
81
        value  => {
82
            branchcode  => $branch->branchcode,
83
            day         => $holiday_date->day,
84
            month       => $holiday_date->month,
85
            year        => $holiday_date->year,
86
        },
87
    });
88
89
    t::lib::Mocks::mock_preference('ExpireReservesMaxPickUpDelay', 1);
90
91
    t::lib::Mocks::mock_preference('ReservesMaxPickUpDelay', 6);
92
    t::lib::Mocks::mock_preference('PickupExpiredHoldsOverReportDuration', 2);
93
94
    my $hold_1 = $builder->build_object({
95
        class => 'Koha::Holds',
96
        value => {
97
            branchcode => $branch->branchcode,
98
            expirationdate => DateTime->now(time_zone => C4::Context->tz())->subtract(days => 3)->iso8601(),
99
        }
100
    });
101
    my $hold_2 = $builder->build_object({
102
        class => 'Koha::Holds',
103
        value => {
104
            branchcode => $branch->branchcode,
105
            expirationdate => DateTime->now(time_zone => C4::Context->tz())->subtract(days => 2)->iso8601(),
106
        }
107
    });
108
    my $hold_3 = $builder->build_object({
109
        class => 'Koha::Holds',
110
        value => {
111
            branchcode => $branch->branchcode,
112
            expirationdate => DateTime->now(time_zone => C4::Context->tz())->subtract(days => 1)->iso8601(),
113
        }
114
    });
115
    my $hold_4 = $builder->build_object({
116
        class => 'Koha::Holds',
117
        value => {
118
            branchcode => $branch->branchcode,
119
            expirationdate => DateTime->now(time_zone => C4::Context->tz())->subtract(days => 1)->iso8601(),
120
        }
121
    });
122
    my $hold_5 = $builder->build_object({
123
        class => 'Koha::Holds',
124
        value => {
125
            branchcode => $branch->branchcode,
126
            expirationdate => DateTime->now(time_zone => C4::Context->tz()),
127
        }
128
    });
129
    my $hold_6 = $builder->build_object({
130
        class => 'Koha::Holds',
131
        value => {
132
            branchcode => $branch->branchcode,
133
            expirationdate => DateTime->now(time_zone => C4::Context->tz())->add(days => 1)->iso8601(),
134
        }
135
    });
136
137
    C4::Reserves::CancelExpiredReserves();
138
139
    my $expired_holds = Koha::Old::Holds->expired({ branchcode => $branch->branchcode });
140
    is($expired_holds->count(), 3, 'expired() returns 3 holds');
141
142
    my $expired_hold_1 = $expired_holds->find($hold_1->id);
143
    my $expired_hold_2 = $expired_holds->find($hold_2->id);
144
    my $expired_hold_3 = $expired_holds->find($hold_3->id);
145
    my $expired_hold_4 = $expired_holds->find($hold_4->id);
146
    my $expired_hold_5 = $expired_holds->find($hold_5->id);
147
    my $expired_hold_6 = $expired_holds->find($hold_6->id);
148
149
    is($expired_hold_1, undef, "Hold 1 not found with expired().");
150
    ok($expired_hold_2->pickupexpired eq DateTime->now(time_zone => C4::Context->tz())->subtract(days => 2)->ymd()
151
    , "Pick up for hold 2 expired 2 days ago.");
152
    ok($expired_hold_3->pickupexpired && $expired_hold_4->pickupexpired eq DateTime->now(time_zone => C4::Context->tz())->subtract(days => 1)->ymd()
153
    , "Pick up for holds 3 and 4 expired yesterday.");
154
    is($expired_hold_5 && $expired_hold_6, undef, "Holds 5 and 6 not expired.");
155
156
    $schema->storage->txn_rollback;
157
158
};

Return to bug 10744