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

(-)a/C4/Items.pm (-1 / +1 lines)
Lines 295-301 sub AddItem { Link Here
295
295
296
    $item->{'itemnumber'} = $itemnumber;
296
    $item->{'itemnumber'} = $itemnumber;
297
297
298
    ModZebra( $item->{biblionumber}, "specialUpdate", "biblioserver" );
298
    C4::Biblio::ModZebra( $item->{biblionumber}, "specialUpdate", "biblioserver" );
299
299
300
    logaction( "CATALOGUING", "ADD", $itemnumber, "item" )
300
    logaction( "CATALOGUING", "ADD", $itemnumber, "item" )
301
      if C4::Context->preference("CataloguingLog");
301
      if C4::Context->preference("CataloguingLog");
(-)a/C4/Reserves.pm (-3 / +40 lines)
Lines 826-831 sub CancelExpiredReserves { Link Here
826
        if ( $hold->found eq 'W' ) {
826
        if ( $hold->found eq 'W' ) {
827
            $cancel_params->{charge_cancel_fee} = 1;
827
            $cancel_params->{charge_cancel_fee} = 1;
828
        }
828
        }
829
        $cancel_params->{autofill} = C4::Context->preference('ExpireReservesAutoFill');
829
        $hold->cancel( $cancel_params );
830
        $hold->cancel( $cancel_params );
830
    }
831
    }
831
}
832
}
Lines 1015-1021 sub ModReserveStatus { Link Here
1015
1016
1016
=head2 ModReserveAffect
1017
=head2 ModReserveAffect
1017
1018
1018
  &ModReserveAffect($itemnumber,$borrowernumber,$diffBranchSend,$reserve_id);
1019
&ModReserveAffect(
1020
    $itemnumber, $borrowernumber, $diffBranchSend,
1021
    $reserve_id, $notify_library
1022
);
1019
1023
1020
This function affect an item and a status for a given reserve, either fetched directly
1024
This function affect an item and a status for a given reserve, either fetched directly
1021
by record_id, or by borrowernumber and itemnumber or biblionumber. If only biblionumber
1025
by record_id, or by borrowernumber and itemnumber or biblionumber. If only biblionumber
Lines 1029-1035 take care of the waiting status Link Here
1029
=cut
1033
=cut
1030
1034
1031
sub ModReserveAffect {
1035
sub ModReserveAffect {
1032
    my ( $itemnumber, $borrowernumber, $transferToDo, $reserve_id ) = @_;
1036
    my ( $itemnumber, $borrowernumber, $transferToDo, $reserve_id, $notify_library ) = @_;
1033
    my $dbh = C4::Context->dbh;
1037
    my $dbh = C4::Context->dbh;
1034
1038
1035
    # we want to attach $itemnumber to $borrowernumber, find the biblionumber
1039
    # we want to attach $itemnumber to $borrowernumber, find the biblionumber
Lines 1056-1062 sub ModReserveAffect { Link Here
1056
    $hold->itemnumber($itemnumber);
1060
    $hold->itemnumber($itemnumber);
1057
    $hold->set_waiting($transferToDo);
1061
    $hold->set_waiting($transferToDo);
1058
1062
1059
    _koha_notify_reserve( $hold->reserve_id )
1063
    _koha_notify_reserve( $hold->reserve_id, $notify_library )
1060
      if ( !$transferToDo && !$already_on_shelf );
1064
      if ( !$transferToDo && !$already_on_shelf );
1061
1065
1062
    _FixPriority( { biblionumber => $biblionumber } );
1066
    _FixPriority( { biblionumber => $biblionumber } );
Lines 1628-1633 The following tables are availalbe witin the notice: Link Here
1628
1632
1629
sub _koha_notify_reserve {
1633
sub _koha_notify_reserve {
1630
    my $reserve_id = shift;
1634
    my $reserve_id = shift;
1635
    my $notify_library = shift;
1636
1631
    my $hold = Koha::Holds->find($reserve_id);
1637
    my $hold = Koha::Holds->find($reserve_id);
1632
    my $borrowernumber = $hold->borrowernumber;
1638
    my $borrowernumber = $hold->borrowernumber;
1633
1639
Lines 1694-1699 sub _koha_notify_reserve { Link Here
1694
        &$send_notification('print', 'HOLD');
1700
        &$send_notification('print', 'HOLD');
1695
    }
1701
    }
1696
1702
1703
    if ($notify_library) {
1704
        my $letter = C4::Letters::GetPreparedLetter(
1705
            module      => 'reserves',
1706
            letter_code => 'HOLD_CHANGED',
1707
            branchcode  => $hold->branchcode,
1708
            substitute  => { today => output_pref( dt_from_string ) },
1709
            tables      => {
1710
                'branches'    => $library,
1711
                'borrowers'   => $patron->unblessed,
1712
                'biblio'      => $hold->biblionumber,
1713
                'biblioitems' => $hold->biblionumber,
1714
                'reserves'    => $hold->unblessed,
1715
                'items'       => $hold->itemnumber,
1716
            },
1717
        );
1718
1719
        my $email =
1720
             C4::Context->preference('ExpireReservesAutoFillEmail')
1721
          || $library->{branchemail}
1722
          || C4::Context->preference('KohaAdminEmailAddress');
1723
1724
        C4::Letters::EnqueueLetter(
1725
            {
1726
                letter                 => $letter,
1727
                borrowernumber         => $borrowernumber,
1728
                message_transport_type => 'email',
1729
                from_address           => $email,
1730
                to_address             => $email,
1731
            }
1732
        );
1733
    }
1697
}
1734
}
1698
1735
1699
=head2 _ShiftPriorityByDateAndPriority
1736
=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 338-343 Cancel a hold: Link Here
338
339
339
sub cancel {
340
sub cancel {
340
    my ( $self, $params ) = @_;
341
    my ( $self, $params ) = @_;
342
343
    my $autofill_next = $params->{autofill} && $self->itemnumber && $self->found && $self->found eq 'W';
344
341
    $self->_result->result_source->schema->txn_do(
345
    $self->_result->result_source->schema->txn_do(
342
        sub {
346
        sub {
343
            $self->cancellationdate(dt_from_string);
347
            $self->cancellationdate(dt_from_string);
Lines 358-363 sub cancel { Link Here
358
                if C4::Context->preference('HoldsLog');
362
                if C4::Context->preference('HoldsLog');
359
        }
363
        }
360
    );
364
    );
365
366
    if ($autofill_next) {
367
        my ( undef, $next_hold ) = C4::Reserves::CheckReserves( $self->itemnumber );
368
        if ($next_hold) {
369
            my $is_transfer = $self->branchcode ne $next_hold->{branchcode};
370
371
            ModReserveAffect( $self->itemnumber, $self->borrowernumber, $is_transfer, $next_hold->{reserve_id}, $autofill_next );
372
            ModItemTransfer( $self->itemnumber, $self->branchcode, $next_hold->{branchcode} ) if $is_transfer;
373
        }
374
    }
375
361
    return $self;
376
    return $self;
362
}
377
}
363
378
(-)a/installer/data/mysql/atomicupdate/bug_14364.sql (+13 lines)
Line 0 Link Here
1
INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type` ) VALUES
2
('ExpireReservesAutoFill','0',NULL,'Automatically fill the next hold with a automatically canceled expired waiting hold.','YesNo'),
3
('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');
4
5
INSERT INTO letter(module,code,branchcode,name,is_html,title,content,message_transport_type)
6
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>>).
7
8
Please update the hold information for this item.
9
10
Title: <<biblio.title>>
11
Author: <<biblio.author>>
12
Copy: <<items.copynumber>>
13
Pickup location: <<branches.branchname>>', 'email');
(-)a/installer/data/mysql/en/mandatory/sample_notices.sql (+10 lines)
Lines 181-183 INSERT INTO `letter` (`module`, `code`, `branchcode`, `name`, `is_html`, `title` Link Here
181
    VALUES
181
    VALUES
182
        ('circulation', 'ACCOUNT_PAYMENT', '', 'Account payment', 0, 'Account payment', '[%- USE Price -%]\r\nA payment of [% credit.amount * -1 | $Price %] has been applied to your account.\r\n\r\nThis payment affected the following fees:\r\n[%- FOREACH o IN offsets %]\r\nDescription: [% o.debit.description %]\r\nAmount paid: [% o.amount * -1 | $Price %]\r\nAmount remaining: [% o.debit.amountoutstanding | $Price %]\r\n[% END %]', 'email', 'default'),
182
        ('circulation', 'ACCOUNT_PAYMENT', '', 'Account payment', 0, 'Account payment', '[%- USE Price -%]\r\nA payment of [% credit.amount * -1 | $Price %] has been applied to your account.\r\n\r\nThis payment affected the following fees:\r\n[%- FOREACH o IN offsets %]\r\nDescription: [% o.debit.description %]\r\nAmount paid: [% o.amount * -1 | $Price %]\r\nAmount remaining: [% o.debit.amountoutstanding | $Price %]\r\n[% END %]', 'email', 'default'),
183
            ('circulation', 'ACCOUNT_WRITEOFF', '', 'Account writeoff', 0, 'Account writeoff', '[%- USE Price -%]\r\nAn account writeoff of [% credit.amount * -1 | $Price %] has been applied to your account.\r\n\r\nThis writeoff affected the following fees:\r\n[%- FOREACH o IN offsets %]\r\nDescription: [% o.debit.description %]\r\nAmount paid: [% o.amount * -1 | $Price %]\r\nAmount remaining: [% o.debit.amountoutstanding | $Price %]\r\n[% END %]', 'email', 'default');
183
            ('circulation', 'ACCOUNT_WRITEOFF', '', 'Account writeoff', 0, 'Account writeoff', '[%- USE Price -%]\r\nAn account writeoff of [% credit.amount * -1 | $Price %] has been applied to your account.\r\n\r\nThis writeoff affected the following fees:\r\n[%- FOREACH o IN offsets %]\r\nDescription: [% o.debit.description %]\r\nAmount paid: [% o.amount * -1 | $Price %]\r\nAmount remaining: [% o.debit.amountoutstanding | $Price %]\r\n[% END %]', 'email', 'default');
184
185
INSERT INTO letter(module,code,branchcode,name,is_html,title,content,message_transport_type)
186
    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>>).
187
188
Please update the hold information for this item.
189
190
Title: <<biblio.title>>
191
Author: <<biblio.author>>
192
Copy: <<items.copynumber>>
193
Pickup location: <<branches.branchname>>', 'email');
(-)a/installer/data/mysql/sysprefs.sql (+2 lines)
Lines 159-164 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
159
('EnhancedMessagingPreferences','1','','If ON, allows patrons to select to receive additional messages about items due or nearly due.','YesNo'),
159
('EnhancedMessagingPreferences','1','','If ON, allows patrons to select to receive additional messages about items due or nearly due.','YesNo'),
160
('EnhancedMessagingPreferencesOPAC', '1', NULL, 'If ON, show patrons messaging setting on the OPAC.', 'YesNo'),
160
('EnhancedMessagingPreferencesOPAC', '1', NULL, 'If ON, show patrons messaging setting on the OPAC.', 'YesNo'),
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
('ExpireReservesAutoFill','0',NULL,'Automatically fill the next hold with a automatically canceled expired waiting hold.','YesNo'),
163
('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'),
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'),
164
('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'),
165
('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'),
166
('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 (+10 lines)
Lines 601-606 Circulation: Link Here
601
                  yes: Allow
601
                  yes: Allow
602
                  no: "Don't allow"
602
                  no: "Don't allow"
603
            - "holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay"
603
            - "holds to expire automatically if they have not been picked by within the time period specified in ReservesMaxPickUpDelay"
604
        -
605
            - pref: ExpireReservesAutoFill
606
              choices:
607
                  yes: "Do"
608
                  no: "Don't"
609
            - 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.
610
        -
611
            - Send email notification of the new hold filled with a canceled item to
612
            - pref: ExpireReservesAutoFillEmail
613
            - . 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.
604
        -
614
        -
605
            - If using ExpireReservesMaxPickUpDelay, charge a borrower who allows his or her waiting hold to expire a fee of
615
            - If using ExpireReservesMaxPickUpDelay, charge a borrower who allows his or her waiting hold to expire a fee of
606
            - pref: ExpireReservesMaxPickUpDelayCharge
616
            - pref: ExpireReservesMaxPickUpDelayCharge
(-)a/t/db_dependent/Holds/ExpireReservesAutoFill.t (-1 / +137 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
use Modern::Perl;
4
5
use Test::More tests => 9;
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 = $builder->build( { source => 'Branch' } );
32
my $branchcode = $library->{branchcode};
33
34
$dbh->do('DELETE FROM reserves');
35
$dbh->do('DELETE FROM message_queue');
36
37
# Create a biblio instance for testing
38
my ($biblionumber) = create_helper_biblio('DUMMY');
39
40
# Create item instance for testing.
41
my ( $item_bibnum, $item_bibitemnum, $itemnumber ) =
42
  AddItem( { homebranch => $branchcode, holdingbranch => $branchcode },
43
    $biblionumber );
44
45
my $patron_1 = $builder->build( { source => 'Borrower' } );
46
my $patron_2 = $builder->build( { source => 'Borrower' } );
47
my $patron_3 = $builder->build( { source => 'Borrower' } );
48
49
# Add a hold on the item for each of our patrons
50
my $hold_1 = Koha::Hold->new(
51
    {
52
        priority       => 0,
53
        borrowernumber => $patron_1->{borrowernumber},
54
        branchcode     => $library->{branchcode},
55
        biblionumber   => $biblionumber,
56
        itemnumber     => $itemnumber,
57
        found          => 'W',
58
        reservedate    => '1900-01-01',
59
        waitingdate    => '1900-01-01',
60
        expirationdate => '1900-01-01',
61
        lowestPriority => 0,
62
        suspend        => 0,
63
    }
64
)->store();
65
my $hold_2 = Koha::Hold->new(
66
    {
67
        priority       => 1,
68
        borrowernumber => $patron_2->{borrowernumber},
69
        branchcode     => $library->{branchcode},
70
        biblionumber   => $biblionumber,
71
        itemnumber     => $itemnumber,
72
        reservedate    => '1900-01-01',
73
        expirationdate => '9999-01-01',
74
        lowestPriority => 0,
75
        suspend        => 0,
76
    }
77
)->store();
78
my $hold_3 = Koha::Hold->new(
79
    {
80
        priority       => 2,
81
        borrowernumber => $patron_2->{borrowernumber},
82
        branchcode     => $library->{branchcode},
83
        biblionumber   => $biblionumber,
84
        itemnumber     => $itemnumber,
85
        reservedate    => '1900-01-01',
86
        expirationdate => '9999-01-01',
87
        lowestPriority => 0,
88
        suspend        => 0,
89
    }
90
)->store();
91
92
# Test CancelExpiredReserves
93
t::lib::Mocks::mock_preference( 'ExpireReservesMaxPickUpDelay', 1 );
94
t::lib::Mocks::mock_preference( 'ReservesMaxPickUpDelay',       1 );
95
t::lib::Mocks::mock_preference( 'ExpireReservesOnHolidays',     1 );
96
t::lib::Mocks::mock_preference( 'ExpireReservesAutoFill',       1 );
97
t::lib::Mocks::mock_preference( 'ExpireReservesAutoFillEmail',
98
    'kyle@example.com' );
99
100
CancelExpiredReserves();
101
102
my @holds = Koha::Holds->search( {}, { order_by => 'priority' } );
103
$hold_2 = $holds[0];
104
$hold_3 = $holds[1];
105
106
is( @holds,            2,   'Found 2 holds' );
107
is( $hold_2->priority, 0,   'Next hold in line now has priority of 0' );
108
is( $hold_2->found,    'W', 'Next hold in line is now set to waiting' );
109
110
my @messages = $schema->resultset('MessageQueue')
111
  ->search( { letter_code => 'HOLD_CHANGED' } );
112
is( @messages, 1, 'Found 1 message in the message queue' );
113
is( $messages[0]->to_address, 'kyle@example.com', 'Message sent to correct email address' );
114
115
$hold_2->expirationdate('1900-01-01')->store();
116
117
CancelExpiredReserves();
118
119
@holds = Koha::Holds->search( {}, { order_by => 'priority' } );
120
$hold_3 = $holds[0];
121
122
is( @holds,            1,   'Found 1 hold' );
123
is( $hold_3->priority, 0,   'Next hold in line now has priority of 0' );
124
is( $hold_3->found,    'W', 'Next hold in line is now set to waiting' );
125
126
# Helper method to set up a Biblio.
127
sub create_helper_biblio {
128
    my $itemtype = shift;
129
    my $bib      = MARC::Record->new();
130
    my $title    = 'Silence in the library';
131
    $bib->append_fields(
132
        MARC::Field->new( '100', ' ', ' ', a => 'Moffat, Steven' ),
133
        MARC::Field->new( '245', ' ', ' ', a => $title ),
134
        MARC::Field->new( '942', ' ', ' ', c => $itemtype ),
135
    );
136
    return my ( $b, $t, $bi ) = AddBiblio( $bib, '' );
137
}

Return to bug 14364