Bug 8995: (follow-up) Add tests, move open_url/coins routines to Koha namespace
[koha.git] / t / db_dependent / Koha / Holds.t
1 #!/usr/bin/perl
2
3 # Copyright 2017 Koha Development team
4 #
5 # This file is part of Koha
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21
22 use Test::More tests => 1;
23 use Test::Warn;
24
25 use C4::Reserves;
26 use Koha::Holds;
27 use Koha::Database;
28
29 use t::lib::Mocks;
30 use t::lib::TestBuilder;
31
32 my $schema = Koha::Database->new->schema;
33 $schema->storage->txn_begin;
34
35 my $builder = t::lib::TestBuilder->new;
36
37 subtest 'cancel' => sub {
38     plan tests => 12;
39     my $biblioitem = $builder->build_object( { class => 'Koha::Biblioitems' } );
40     my $library    = $builder->build_object( { class => 'Koha::Libraries' } );
41     my $itemtype   = $builder->build_object( { class => 'Koha::ItemTypes', value => { rentalcharge => 0 } } );
42     my $item_info  = {
43         biblionumber     => $biblioitem->biblionumber,
44         biblioitemnumber => $biblioitem->biblioitemnumber,
45         homebranch       => $library->branchcode,
46         holdingbranch    => $library->branchcode,
47         itype            => $itemtype->itemtype,
48     };
49     my $item = $builder->build_object( { class => 'Koha::Items', value => $item_info } );
50     my $manager = $builder->build_object({ class => "Koha::Patrons" });
51     t::lib::Mocks::mock_userenv({ patron => $manager,branchcode => $manager->branchcode });
52
53     my ( @patrons, @holds );
54     for my $i ( 0 .. 2 ) {
55         my $priority = $i + 1;
56         my $patron   = $builder->build_object(
57             {
58                 class => 'Koha::Patrons',
59                 value => { branchcode => $library->branchcode, }
60             }
61         );
62         my $reserve_id = C4::Reserves::AddReserve(
63             $library->branchcode, $patron->borrowernumber,
64             $item->biblionumber,  '',
65             $priority,            undef,
66             undef,                '',
67             "title for fee",      $item->itemnumber,
68         );
69         my $hold = Koha::Holds->find($reserve_id);
70         push @patrons, $patron;
71         push @holds,   $hold;
72     }
73
74     # There are 3 holds on this records
75     my $nb_of_holds =
76       Koha::Holds->search( { biblionumber => $item->biblionumber } )->count;
77     is( $nb_of_holds, 3,
78         'There should have 3 holds placed on this biblio record' );
79     my $first_hold  = $holds[0];
80     my $second_hold = $holds[1];
81     my $third_hold  = $holds[2];
82     is( ref($second_hold), 'Koha::Hold',
83         'We should play with Koha::Hold objects' );
84     is( $second_hold->priority, 2,
85         'Second hold should have a priority set to 3' );
86
87     # Remove the second hold, only 2 should still exist in DB and priorities must have been updated
88     my $is_cancelled = $second_hold->cancel;
89     is( ref($is_cancelled), 'Koha::Hold',
90         'Koha::Hold->cancel should return the Koha::Hold (?)' )
91       ;    # This is can reconsidered
92     is( $second_hold->in_storage, 0,
93         'The hold has been cancelled and does not longer exist in DB' );
94     $nb_of_holds =
95       Koha::Holds->search( { biblionumber => $item->biblionumber } )->count;
96     is( $nb_of_holds, 2,
97         'a hold has been cancelled, there should have only 2 holds placed on this biblio record'
98     );
99
100     # discard_changes to refetch
101     is( $first_hold->discard_changes->priority, 1, 'First hold should still be first' );
102     is( $third_hold->discard_changes->priority, 2, 'Third hold should now be second' );
103
104     subtest 'charge_cancel_fee parameter' => sub {
105         plan tests => 4;
106         my $patron_category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { reservefee => 0 } } );
107         my $patron = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $patron_category->categorycode } });
108         is( $patron->account->balance, 0, 'A new patron does not have any charges' );
109
110         my @hold_info = (
111             $library->branchcode, $patron->borrowernumber,
112             $item->biblionumber,  '',
113             1,                    undef,
114             undef,                '',
115             "title for fee",      $item->itemnumber,
116         );
117
118         # First, test cancelling a reserve when there's no charge configured.
119         t::lib::Mocks::mock_preference('ExpireReservesMaxPickUpDelayCharge', 0);
120         my $reserve_id = C4::Reserves::AddReserve( @hold_info );
121         Koha::Holds->find( $reserve_id )->cancel( { charge_cancel_fee => 1 } );
122         is( $patron->account->balance, 0, 'ExpireReservesMaxPickUpDelayCharge=0 - The patron should not have been charged' );
123
124         # Then, test cancelling a reserve when there's no charge desired.
125         t::lib::Mocks::mock_preference('ExpireReservesMaxPickUpDelayCharge', 42);
126         $reserve_id = C4::Reserves::AddReserve( @hold_info );
127         Koha::Holds->find( $reserve_id )->cancel(); # charge_cancel_fee => 0
128         is( $patron->account->balance, 0, 'ExpireReservesMaxPickUpDelayCharge=42, but charge_cancel_fee => 0, The patron should not have been charged' );
129
130
131         # Finally, test cancelling a reserve when there's a charge desired and configured.
132         t::lib::Mocks::mock_preference('ExpireReservesMaxPickUpDelayCharge', 42);
133         $reserve_id = C4::Reserves::AddReserve( @hold_info );
134         Koha::Holds->find( $reserve_id )->cancel( { charge_cancel_fee => 1 } );
135         is( int($patron->account->balance), 42, 'ExpireReservesMaxPickUpDelayCharge=42 and charge_cancel_fee => 1, The patron should have been charged!' );
136     };
137
138     subtest 'waiting hold' => sub {
139         plan tests => 1;
140         my $patron = $builder->build_object({ class => 'Koha::Patrons' });
141         my $reserve_id = C4::Reserves::AddReserve(
142             $library->branchcode, $patron->borrowernumber,
143             $item->biblionumber,  '',
144             1,                    undef,
145             undef,                '',
146             "title for fee",      $item->itemnumber,
147             'W',
148         );
149         Koha::Holds->find( $reserve_id )->cancel;
150         my $hold_old = Koha::Old::Holds->find( $reserve_id );
151         is( $hold_old->found, 'W', 'The found column should have been kept and a hold is cancelled' );
152     };
153
154     subtest 'HoldsLog' => sub {
155         plan tests => 2;
156         my $patron = $builder->build_object({ class => 'Koha::Patrons' });
157         my @hold_info = (
158             $library->branchcode, $patron->borrowernumber,
159             $item->biblionumber,  '',
160             1,                    undef,
161             undef,                '',
162             "title for fee",      $item->itemnumber,
163         );
164
165         t::lib::Mocks::mock_preference('HoldsLog', 0);
166         my $reserve_id = C4::Reserves::AddReserve(@hold_info);
167         Koha::Holds->find( $reserve_id )->cancel;
168         my $number_of_logs = $schema->resultset('ActionLog')->search( { module => 'HOLDS', action => 'CANCEL', object => $reserve_id } )->count;
169         is( $number_of_logs, 0, 'Without HoldsLog, Koha::Hold->cancel should not have logged' );
170
171         t::lib::Mocks::mock_preference('HoldsLog', 1);
172         $reserve_id = C4::Reserves::AddReserve(@hold_info);
173         Koha::Holds->find( $reserve_id )->cancel;
174         $number_of_logs = $schema->resultset('ActionLog')->search( { module => 'HOLDS', action => 'CANCEL', object => $reserve_id } )->count;
175         is( $number_of_logs, 1, 'With HoldsLog, Koha::Hold->cancel should have logged' );
176     };
177
178     subtest 'rollback' => sub {
179         plan tests => 3;
180         my $patron_category = $builder->build_object(
181             {
182                 class => 'Koha::Patron::Categories',
183                 value => { reservefee => 0 }
184             }
185         );
186         my $patron = $builder->build_object(
187             {
188                 class => 'Koha::Patrons',
189                 value => { categorycode => $patron_category->categorycode }
190             }
191         );
192         my @hold_info = (
193             $library->branchcode, $patron->borrowernumber,
194             $item->biblionumber,  '',
195             1,                    undef,
196             undef,                '',
197             "title for fee",      $item->itemnumber,
198         );
199
200         t::lib::Mocks::mock_preference( 'ExpireReservesMaxPickUpDelayCharge',42 );
201         my $reserve_id = C4::Reserves::AddReserve(@hold_info);
202         my $hold       = Koha::Holds->find($reserve_id);
203
204         # Add a row with the same id to make the cancel fails
205         Koha::Old::Hold->new( $hold->unblessed )->store;
206
207         warning_like {
208             eval { $hold->cancel( { charge_cancel_fee => 1 } ) };
209         }
210         qr{.*DBD::mysql::st execute failed: Duplicate entry.*},
211           'DBD should have raised an error about dup primary key';
212
213         $hold = Koha::Holds->find($reserve_id);
214         is( ref($hold), 'Koha::Hold', 'The hold should not have been deleted' );
215         is( $patron->account->balance, 0,
216 'If the hold has not been cancelled, the patron should not have been charged'
217         );
218     };
219
220 };
221
222 $schema->storage->txn_rollback;
223
224 1;