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

(-)a/C4/Reserves.pm (-4 / +40 lines)
Lines 901-906 sub CancelExpiredReserves { Link Here
901
        if ( $hold->found eq 'W' ) {
901
        if ( $hold->found eq 'W' ) {
902
            $cancel_params->{charge_cancel_fee} = 1;
902
            $cancel_params->{charge_cancel_fee} = 1;
903
        }
903
        }
904
        $cancel_params->{autofill} = C4::Context->preference('ExpireReservesAutoFill');
904
        $hold->cancel( $cancel_params );
905
        $hold->cancel( $cancel_params );
905
    }
906
    }
906
}
907
}
Lines 1101-1107 sub ModReserveStatus { Link Here
1101
1102
1102
=head2 ModReserveAffect
1103
=head2 ModReserveAffect
1103
1104
1104
  &ModReserveAffect($itemnumber,$borrowernumber,$diffBranchSend,$reserve_id);
1105
&ModReserveAffect(
1106
    $itemnumber, $borrowernumber, $diffBranchSend,
1107
    $reserve_id, $notify_library
1108
);
1105
1109
1106
This function affect an item and a status for a given reserve, either fetched directly
1110
This function affect an item and a status for a given reserve, either fetched directly
1107
by record_id, or by borrowernumber and itemnumber or biblionumber. If only biblionumber
1111
by record_id, or by borrowernumber and itemnumber or biblionumber. If only biblionumber
Lines 1115-1121 take care of the waiting status Link Here
1115
=cut
1119
=cut
1116
1120
1117
sub ModReserveAffect {
1121
sub ModReserveAffect {
1118
    my ( $itemnumber, $borrowernumber, $transferToDo, $reserve_id ) = @_;
1122
    my ( $itemnumber, $borrowernumber, $transferToDo, $reserve_id, $notify_library ) = @_;
1119
    my $dbh = C4::Context->dbh;
1123
    my $dbh = C4::Context->dbh;
1120
1124
1121
    # we want to attach $itemnumber to $borrowernumber, find the biblionumber
1125
    # we want to attach $itemnumber to $borrowernumber, find the biblionumber
Lines 1142-1149 sub ModReserveAffect { Link Here
1142
    $hold->itemnumber($itemnumber);
1146
    $hold->itemnumber($itemnumber);
1143
    $hold->set_waiting($transferToDo);
1147
    $hold->set_waiting($transferToDo);
1144
1148
1149
    _koha_notify_reserve( $hold->reserve_id, $notify_library ) unless $already_on_shelf;
1145
    if( !$transferToDo ){
1150
    if( !$transferToDo ){
1146
        _koha_notify_reserve( $hold->reserve_id ) unless $already_on_shelf;
1147
        my $transfers = Koha::Item::Transfers->search({
1151
        my $transfers = Koha::Item::Transfers->search({
1148
            itemnumber => $itemnumber,
1152
            itemnumber => $itemnumber,
1149
            datearrived => undef
1153
            datearrived => undef
Lines 1153-1159 sub ModReserveAffect { Link Here
1153
        };
1157
        };
1154
    }
1158
    }
1155
1159
1156
1157
    _FixPriority( { biblionumber => $biblionumber } );
1160
    _FixPriority( { biblionumber => $biblionumber } );
1158
    my $item = Koha::Items->find($itemnumber);
1161
    my $item = Koha::Items->find($itemnumber);
1159
    if ( $item->location && $item->location eq 'CART'
1162
    if ( $item->location && $item->location eq 'CART'
Lines 1750-1755 The following tables are availalbe witin the notice: Link Here
1750
1753
1751
sub _koha_notify_reserve {
1754
sub _koha_notify_reserve {
1752
    my $reserve_id = shift;
1755
    my $reserve_id = shift;
1756
    my $notify_library = shift;
1757
1753
    my $hold = Koha::Holds->find($reserve_id);
1758
    my $hold = Koha::Holds->find($reserve_id);
1754
    my $borrowernumber = $hold->borrowernumber;
1759
    my $borrowernumber = $hold->borrowernumber;
1755
1760
Lines 1816-1821 sub _koha_notify_reserve { Link Here
1816
        &$send_notification('print', 'HOLD');
1821
        &$send_notification('print', 'HOLD');
1817
    }
1822
    }
1818
1823
1824
    if ($notify_library) {
1825
        my $letter = C4::Letters::GetPreparedLetter(
1826
            module      => 'reserves',
1827
            letter_code => 'HOLD_CHANGED',
1828
            branchcode  => $hold->branchcode,
1829
            substitute  => { today => output_pref( dt_from_string ) },
1830
            tables      => {
1831
                'branches'    => $library,
1832
                'borrowers'   => $patron->unblessed,
1833
                'biblio'      => $hold->biblionumber,
1834
                'biblioitems' => $hold->biblionumber,
1835
                'reserves'    => $hold->unblessed,
1836
                'items'       => $hold->itemnumber,
1837
            },
1838
        );
1839
1840
        my $email =
1841
             C4::Context->preference('ExpireReservesAutoFillEmail')
1842
          || $library->{branchemail}
1843
          || C4::Context->preference('KohaAdminEmailAddress');
1844
1845
        C4::Letters::EnqueueLetter(
1846
            {
1847
                letter                 => $letter,
1848
                borrowernumber         => $borrowernumber,
1849
                message_transport_type => 'email',
1850
                from_address           => $email,
1851
                to_address             => $email,
1852
            }
1853
        );
1854
    }
1819
}
1855
}
1820
1856
1821
=head2 _ShiftPriorityByDateAndPriority
1857
=head2 _ShiftPriorityByDateAndPriority
(-)a/Koha/Hold.pm (+15 lines)
Lines 25-30 use Data::Dumper qw(Dumper); Link Here
25
25
26
use C4::Context qw(preference);
26
use C4::Context qw(preference);
27
use C4::Log;
27
use C4::Log;
28
use C4::Reserves;
28
29
29
use Koha::DateUtils qw(dt_from_string output_pref);
30
use Koha::DateUtils qw(dt_from_string output_pref);
30
use Koha::Patrons;
31
use Koha::Patrons;
Lines 352-357 Cancel a hold: Link Here
352
353
353
sub cancel {
354
sub cancel {
354
    my ( $self, $params ) = @_;
355
    my ( $self, $params ) = @_;
356
357
    my $autofill_next = $params->{autofill} && $self->itemnumber && $self->found && $self->found eq 'W';
358
355
    $self->_result->result_source->schema->txn_do(
359
    $self->_result->result_source->schema->txn_do(
356
        sub {
360
        sub {
357
            $self->cancellationdate( dt_from_string->strftime( '%Y-%m-%d %H:%M:%S' ) );
361
            $self->cancellationdate( dt_from_string->strftime( '%Y-%m-%d %H:%M:%S' ) );
Lines 383-388 sub cancel { Link Here
383
                if C4::Context->preference('HoldsLog');
387
                if C4::Context->preference('HoldsLog');
384
        }
388
        }
385
    );
389
    );
390
391
    if ($autofill_next) {
392
        my ( undef, $next_hold ) = C4::Reserves::CheckReserves( $self->itemnumber );
393
        if ($next_hold) {
394
            my $is_transfer = $self->branchcode ne $next_hold->{branchcode};
395
396
            C4::Reserves::ModReserveAffect( $self->itemnumber, $self->borrowernumber, $is_transfer, $next_hold->{reserve_id}, $autofill_next );
397
            C4::Reserves::ModItemTransfer( $self->itemnumber, $self->branchcode, $next_hold->{branchcode} ) if $is_transfer;
398
        }
399
    }
400
386
    return $self;
401
    return $self;
387
}
402
}
388
403
(-)a/installer/data/mysql/atomicupdate/bug_14364.perl (+22 lines)
Line 0 Link Here
1
$DBversion = 'XXX'; # will be replaced by the RM
2
if( CheckVersion( $DBversion ) ) {
3
    $dbh->do(q{
4
        INSERT IGNORE INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type` ) VALUES
5
        ('ExpireReservesAutoFill','0',NULL,'Automatically fill the next hold with a automatically canceled expired waiting hold.','YesNo'),
6
        ('ExpireReservesAutoFillEmail','', NULL,'If ExpireReservesAutoFill and an email is defined here, the email notification for the change in the hold will be sent to this address.','Free');
7
    });
8
9
    $dbh->do(q{
10
        INSERT INTO letter(module,code,branchcode,name,is_html,title,content,message_transport_type)
11
        VALUES ( 'reserves', 'HOLD_CHANGED', '', 'Canceled Hold Available for Different Patron', '0', 'Canceled Hold Available for Different Patron', 'The patron picking up <<biblio.title>> (<<items.barcode>>) has changed to <<borrowers.firstname>> <<borrowers.surname>> (<<borrowers.cardnumber>>).
12
13
Please update the hold information for this item.
14
15
Title: <<biblio.title>>
16
Author: <<biblio.author>>
17
Copy: <<items.copynumber>>
18
Pickup location: <<branches.branchname>>', 'email');
19
    });
20
21
    NewVersion( $DBversion, 14364, "Allow automatically canceled expired waiting holds to fill the next hold");
22
}
(-)a/installer/data/mysql/pl-PL/mandatory/sample_notices.sql (+10 lines)
Lines 376-378 INSERT IGNORE INTO `letter` (`module`, `code`, `branchcode`, `name`, `is_html`, Link Here
376
    </tr>
376
    </tr>
377
</tfoot>
377
</tfoot>
378
</table>', 'print', 'default');
378
</table>', 'print', 'default');
379
380
INSERT INTO letter(module,code,branchcode,name,is_html,title,content,message_transport_type)
381
    VALUES ( 'reserves', 'HOLD_CHANGED', '', 'Canceled Hold Available for Different Patron', '0', 'Canceled Hold Available for Different Patron', 'The patron picking up <<biblio.title>> (<<items.barcode>>) has changed to <<borrowers.firstname>> <<borrowers.surname>> (<<borrowers.cardnumber>>).
382
383
Please update the hold information for this item.
384
385
Title: <<biblio.title>>
386
Author: <<biblio.author>>
387
Copy: <<items.copynumber>>
388
Pickup location: <<branches.branchname>>', 'email');
(-)a/installer/data/mysql/sysprefs.sql (+2 lines)
Lines 184-189 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
184
('EnhancedMessagingPreferences','1','','If ON, allows patrons to select to receive additional messages about items due or nearly due.','YesNo'),
184
('EnhancedMessagingPreferences','1','','If ON, allows patrons to select to receive additional messages about items due or nearly due.','YesNo'),
185
('EnhancedMessagingPreferencesOPAC', '1', NULL, 'If ON, show patrons messaging setting on the OPAC.', 'YesNo'),
185
('EnhancedMessagingPreferencesOPAC', '1', NULL, 'If ON, show patrons messaging setting on the OPAC.', 'YesNo'),
186
('expandedSearchOption','0',NULL,'If ON, set advanced search to be expanded by default','YesNo'),
186
('expandedSearchOption','0',NULL,'If ON, set advanced search to be expanded by default','YesNo'),
187
('ExpireReservesAutoFill','0',NULL,'Automatically fill the next hold with a automatically canceled expired waiting hold.','YesNo'),
188
('ExpireReservesAutoFillEmail','', NULL,'If ExpireReservesAutoFill and an email is defined here, the email notification for the change in the hold will be sent to this address.','Free'),
187
('ExpireReservesMaxPickUpDelay','0','','Enabling this allows holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay','YesNo'),
189
('ExpireReservesMaxPickUpDelay','0','','Enabling this allows holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay','YesNo'),
188
('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'),
190
('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'),
189
('ExpireReservesOnHolidays', '1', NULL, 'If false, reserves at a library will not be canceled on days the library is not open.', 'YesNo'),
191
('ExpireReservesOnHolidays', '1', NULL, 'If false, reserves at a library will not be canceled on days the library is not open.', 'YesNo'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (+9 lines)
Lines 676-681 Circulation: Link Here
676
                  yes: Allow
676
                  yes: Allow
677
                  no: "Don't allow"
677
                  no: "Don't allow"
678
            - "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."
678
            - "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."
679
            - pref: ExpireReservesAutoFill
680
              choices:
681
                  yes: "Do"
682
                  no: "Don't"
683
            - automatically fill the next hold using the item. If enabled, an email notification will be sent to either the email address defined in ExpireReservesAutoFillEmail, the item's holding library's email address, or the email address defined in KohaAdminEmailAddress in that order.
684
        -
685
            - Send email notification of the new hold filled with a canceled item to
686
            - pref: ExpireReservesAutoFillEmail
687
            - . If no address is defined here, the email will be sent to either the item's holding library or the email address defined in KohaAdminEmailAddress in that order.
679
        -
688
        -
680
            - If using ExpireReservesMaxPickUpDelay, charge a borrower who allows their waiting hold to expire a fee of
689
            - If using ExpireReservesMaxPickUpDelay, charge a borrower who allows their waiting hold to expire a fee of
681
            - pref: ExpireReservesMaxPickUpDelayCharge
690
            - pref: ExpireReservesMaxPickUpDelayCharge
(-)a/t/db_dependent/Holds/ExpireReservesAutoFill.t (-1 / +186 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
use Modern::Perl;
4
5
use Test::More tests => 3;
6
7
use t::lib::Mocks;
8
use t::lib::TestBuilder;
9
10
use MARC::Record;
11
12
use C4::Context;
13
use C4::Biblio;
14
use C4::Items;
15
use Koha::Database;
16
use Koha::Holds;
17
18
BEGIN {
19
    use FindBin;
20
    use lib $FindBin::Bin;
21
    use_ok('C4::Reserves');
22
}
23
24
my $schema = Koha::Database->new->schema;
25
$schema->storage->txn_begin;
26
27
my $builder = t::lib::TestBuilder->new();
28
my $dbh     = C4::Context->dbh;
29
30
# Create two random branches
31
my $library_1 = $builder->build({ source => 'Branch' })->{ branchcode };
32
my $library_2 = $builder->build({ source => 'Branch' })->{ branchcode };
33
34
my $biblio = $builder->build_sample_biblio({ itemtype => 'DUMMY' });
35
my $biblionumber = $biblio->id;
36
37
# Create item instance for testing.
38
my $itemnumber = $builder->build_sample_item({ library => $library_1, biblionumber => $biblio->biblionumber })->itemnumber;
39
40
my $patron_1 = $builder->build( { source => 'Borrower' } );
41
my $patron_2 = $builder->build( { source => 'Borrower' } );
42
my $patron_3 = $builder->build( { source => 'Borrower' } );
43
44
subtest 'Test automatically canceled expired waiting holds to fill the next hold, without a transfer' => sub {
45
    plan tests => 8;
46
47
    $dbh->do('DELETE FROM reserves');
48
    $dbh->do('DELETE FROM message_queue');
49
50
    # Add a hold on the item for each of our patrons
51
    my $hold_1 = Koha::Hold->new(
52
        {
53
            priority       => 0,
54
            borrowernumber => $patron_1->{borrowernumber},
55
            branchcode     => $library_1,
56
            biblionumber   => $biblionumber,
57
            itemnumber     => $itemnumber,
58
            found          => 'W',
59
            reservedate    => '1900-01-01',
60
            waitingdate    => '1900-01-01',
61
            expirationdate => '1900-01-01',
62
            lowestPriority => 0,
63
            suspend        => 0,
64
        }
65
    )->store();
66
    my $hold_2 = Koha::Hold->new(
67
        {
68
            priority       => 1,
69
            borrowernumber => $patron_2->{borrowernumber},
70
            branchcode     => $library_1,
71
            biblionumber   => $biblionumber,
72
            itemnumber     => $itemnumber,
73
            reservedate    => '1900-01-01',
74
            expirationdate => '9999-01-01',
75
            lowestPriority => 0,
76
            suspend        => 0,
77
        }
78
    )->store();
79
    my $hold_3 = Koha::Hold->new(
80
        {
81
            priority       => 2,
82
            borrowernumber => $patron_2->{borrowernumber},
83
            branchcode     => $library_1,
84
            biblionumber   => $biblionumber,
85
            itemnumber     => $itemnumber,
86
            reservedate    => '1900-01-01',
87
            expirationdate => '9999-01-01',
88
            lowestPriority => 0,
89
            suspend        => 0,
90
        }
91
    )->store();
92
93
    # Test CancelExpiredReserves
94
    t::lib::Mocks::mock_preference( 'ExpireReservesMaxPickUpDelay', 1 );
95
    t::lib::Mocks::mock_preference( 'ReservesMaxPickUpDelay',       1 );
96
    t::lib::Mocks::mock_preference( 'ExpireReservesOnHolidays',     1 );
97
    t::lib::Mocks::mock_preference( 'ExpireReservesAutoFill',       1 );
98
    t::lib::Mocks::mock_preference( 'ExpireReservesAutoFillEmail',
99
        'kyle@example.com' );
100
101
    CancelExpiredReserves();
102
103
    my @holds = Koha::Holds->search( {}, { order_by => 'priority' } );
104
    $hold_2 = $holds[0];
105
    $hold_3 = $holds[1];
106
107
    is( @holds,            2,   'Found 2 holds' );
108
    is( $hold_2->priority, 0,   'Next hold in line now has priority of 0' );
109
    is( $hold_2->found,    'W', 'Next hold in line is now set to waiting' );
110
111
    my @messages = $schema->resultset('MessageQueue')
112
      ->search( { letter_code => 'HOLD_CHANGED' } );
113
    is( @messages, 1, 'Found 1 message in the message queue' );
114
    is( $messages[0]->to_address, 'kyle@example.com', 'Message sent to correct email address' );
115
116
    $hold_2->expirationdate('1900-01-01')->store();
117
118
    CancelExpiredReserves();
119
120
    @holds = Koha::Holds->search( {}, { order_by => 'priority' } );
121
    $hold_3 = $holds[0];
122
123
    is( @holds,            1,   'Found 1 hold' );
124
    is( $hold_3->priority, 0,   'Next hold in line now has priority of 0' );
125
    is( $hold_3->found,    'W', 'Next hold in line is now set to waiting' );
126
};
127
128
subtest 'Test automatically canceled expired waiting holds to fill the next hold, with a transfer' => sub {
129
    plan tests => 6;
130
131
    $dbh->do('DELETE FROM reserves');
132
    $dbh->do('DELETE FROM message_queue');
133
134
    # Add a hold on the item for each of our patrons
135
    my $hold_1 = Koha::Hold->new(
136
        {
137
            priority       => 0,
138
            borrowernumber => $patron_1->{borrowernumber},
139
            branchcode     => $library_1,
140
            biblionumber   => $biblionumber,
141
            itemnumber     => $itemnumber,
142
            found          => 'W',
143
            reservedate    => '1900-01-01',
144
            waitingdate    => '1900-01-01',
145
            expirationdate => '1900-01-01',
146
            lowestPriority => 0,
147
            suspend        => 0,
148
        }
149
    )->store();
150
    my $hold_2 = Koha::Hold->new(
151
        {
152
            priority       => 1,
153
            borrowernumber => $patron_2->{borrowernumber},
154
            branchcode     => $library_2,
155
            biblionumber   => $biblionumber,
156
            itemnumber     => $itemnumber,
157
            reservedate    => '1900-01-01',
158
            expirationdate => '9999-01-01',
159
            lowestPriority => 0,
160
            suspend        => 0,
161
        }
162
    )->store();
163
164
    # Test CancelExpiredReserves
165
    t::lib::Mocks::mock_preference( 'ExpireReservesMaxPickUpDelay', 1 );
166
    t::lib::Mocks::mock_preference( 'ReservesMaxPickUpDelay',       1 );
167
    t::lib::Mocks::mock_preference( 'ExpireReservesOnHolidays',     1 );
168
    t::lib::Mocks::mock_preference( 'ExpireReservesAutoFill',       1 );
169
    t::lib::Mocks::mock_preference( 'ExpireReservesAutoFillEmail',
170
        'kyle@example.com' );
171
172
    CancelExpiredReserves();
173
174
    my @holds = Koha::Holds->search( {}, { order_by => 'priority' } );
175
    $hold_2 = $holds[0];
176
177
    is( @holds,            1,   'Found 1 hold' );
178
    is( $hold_2->priority, 0,   'Next hold in line now has priority of 0' );
179
    is( $hold_2->found,    'T', 'Next hold in line is now set to in transit' );
180
    is( $hold_2->branchcode, $library_2, "Next hold in line has correct branchcode" );
181
182
    my @messages = $schema->resultset('MessageQueue')
183
      ->search( { letter_code => 'HOLD_CHANGED' } );
184
    is( @messages, 1, 'Found 1 message in the message queue' );
185
    is( $messages[0]->to_address, 'kyle@example.com', 'Message sent to correct email address' );
186
};

Return to bug 14364