Bug 18651: Update accountlines.issue_id is the issue_id has been changed during the...
[koha_ffzg] / t / db_dependent / Circulation / Returns.t
1 #!/usr/bin/perl
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18 use Modern::Perl;
19
20 use Test::More tests => 4;
21 use Test::MockModule;
22 use Test::Warn;
23
24 use t::lib::Mocks;
25 use t::lib::TestBuilder;
26
27 use C4::Biblio;
28 use C4::Circulation;
29 use C4::Items;
30 use C4::Members;
31 use Koha::Database;
32 use Koha::Account::Lines;
33 use Koha::DateUtils;
34 use Koha::Items;
35
36 use MARC::Record;
37 use MARC::Field;
38
39 # Mock userenv, used by AddIssue
40 my $branch;
41 my $context = Test::MockModule->new('C4::Context');
42 $context->mock( 'userenv', sub {
43     return { branch => $branch }
44 });
45
46 my $schema = Koha::Database->schema;
47 $schema->storage->txn_begin;
48
49 my $builder = t::lib::TestBuilder->new();
50
51 subtest "InProcessingToShelvingCart tests" => sub {
52
53     plan tests => 2;
54
55     $branch = $builder->build({ source => 'Branch' })->{ branchcode };
56     my $permanent_location = 'TEST';
57     my $location           = 'PROC';
58
59     # Create a biblio record with biblio-level itemtype
60     my $record = MARC::Record->new();
61     my ( $biblionumber, $biblioitemnumber ) = AddBiblio( $record, '' );
62     my $built_item = $builder->build({
63         source => 'Item',
64         value  => {
65             biblionumber  => $biblionumber,
66             homebranch    => $branch,
67             holdingbranch => $branch,
68             location      => $location,
69             permanent_location => $permanent_location
70         }
71     });
72     my $barcode = $built_item->{ barcode };
73     my $itemnumber = $built_item->{ itemnumber };
74     my $item;
75
76     t::lib::Mocks::mock_preference( "InProcessingToShelvingCart", 1 );
77     AddReturn( $barcode, $branch );
78     $item = GetItem( $itemnumber );
79     is( $item->{location}, 'CART',
80         "InProcessingToShelvingCart functions as intended" );
81
82     $item->{location} = $location;
83     ModItem( $item, undef, $itemnumber );
84
85     t::lib::Mocks::mock_preference( "InProcessingToShelvingCart", 0 );
86     AddReturn( $barcode, $branch );
87     $item = GetItem( $itemnumber );
88     is( $item->{location}, $permanent_location,
89         "InProcessingToShelvingCart functions as intended" );
90 };
91
92
93 subtest "AddReturn logging on statistics table (item-level_itypes=1)" => sub {
94
95     plan tests => 4;
96
97     # Set item-level item types
98     t::lib::Mocks::mock_preference( "item-level_itypes", 1 );
99
100     # Make sure logging is enabled
101     t::lib::Mocks::mock_preference( "IssueLog", 1 );
102     t::lib::Mocks::mock_preference( "ReturnLog", 1 );
103
104     # Create an itemtype for biblio-level item type
105     my $blevel_itemtype = $builder->build({ source => 'Itemtype' })->{ itemtype };
106     # Create an itemtype for item-level item type
107     my $ilevel_itemtype = $builder->build({ source => 'Itemtype' })->{ itemtype };
108     # Create a branch
109     $branch = $builder->build({ source => 'Branch' })->{ branchcode };
110     # Create a borrower
111     my $borrowernumber = $builder->build({
112         source => 'Borrower',
113         value => { branchcode => $branch }
114     })->{ borrowernumber };
115     # Look for the defined MARC field for biblio-level itemtype
116     my $rs = $schema->resultset('MarcSubfieldStructure')->search({
117         frameworkcode => '',
118         kohafield     => 'biblioitems.itemtype'
119     });
120     my $tagfield    = $rs->first->tagfield;
121     my $tagsubfield = $rs->first->tagsubfield;
122
123     # Create a biblio record with biblio-level itemtype
124     my $record = MARC::Record->new();
125     $record->append_fields(
126         MARC::Field->new($tagfield,'','', $tagsubfield => $blevel_itemtype )
127     );
128     my ( $biblionumber, $biblioitemnumber ) = AddBiblio( $record, '' );
129     my $item_with_itemtype = $builder->build(
130         {
131             source => 'Item',
132             value  => {
133                 biblionumber     => $biblionumber,
134                 biblioitemnumber => $biblioitemnumber,
135                 homebranch       => $branch,
136                 holdingbranch    => $branch,
137                 itype            => $ilevel_itemtype
138             }
139         }
140     );
141     my $item_without_itemtype = $builder->build(
142         {
143             source => 'Item',
144             value  => {
145                 biblionumber     => $biblionumber,
146                 biblioitemnumber => $biblioitemnumber,
147                 homebranch       => $branch,
148                 holdingbranch    => $branch,
149                 itype            => undef
150             }
151         }
152     );
153
154     my $borrower = GetMember( borrowernumber => $borrowernumber );
155     AddIssue( $borrower, $item_with_itemtype->{ barcode } );
156     AddReturn( $item_with_itemtype->{ barcode }, $branch );
157     # Test item-level itemtype was recorded on the 'statistics' table
158     my $stat = $schema->resultset('Statistic')->search({
159         branch     => $branch,
160         type       => 'return',
161         itemnumber => $item_with_itemtype->{ itemnumber }
162     }, { order_by => { -asc => 'datetime' } })->next();
163
164     is( $stat->itemtype, $ilevel_itemtype,
165         "item-level itype recorded on statistics for return");
166     warning_like { AddIssue( $borrower, $item_without_itemtype->{ barcode } ) }
167                  qr/^item-level_itypes set but no itemtype set for item/,
168                  'Item without itemtype set raises warning on AddIssue';
169     warning_like { AddReturn( $item_without_itemtype->{ barcode }, $branch ) }
170                  qr/^item-level_itypes set but no itemtype set for item/,
171                  'Item without itemtype set raises warning on AddReturn';
172     # Test biblio-level itemtype was recorded on the 'statistics' table
173     $stat = $schema->resultset('Statistic')->search({
174         branch     => $branch,
175         type       => 'return',
176         itemnumber => $item_without_itemtype->{ itemnumber }
177     }, { order_by => { -asc => 'datetime' } })->next();
178
179     is( $stat->itemtype, $blevel_itemtype,
180         "biblio-level itype recorded on statistics for return as a fallback for null item-level itype");
181
182 };
183
184 subtest "AddReturn logging on statistics table (item-level_itypes=0)" => sub {
185
186     plan tests => 2;
187
188     # Make sure logging is enabled
189     t::lib::Mocks::mock_preference( "IssueLog", 1 );
190     t::lib::Mocks::mock_preference( "ReturnLog", 1 );
191
192     # Set item-level item types
193     t::lib::Mocks::mock_preference( "item-level_itypes", 0 );
194
195     # Create an itemtype for biblio-level item type
196     my $blevel_itemtype = $builder->build({ source => 'Itemtype' })->{ itemtype };
197     # Create an itemtype for item-level item type
198     my $ilevel_itemtype = $builder->build({ source => 'Itemtype' })->{ itemtype };
199     # Create a branch
200     $branch = $builder->build({ source => 'Branch' })->{ branchcode };
201     # Create a borrower
202     my $borrowernumber = $builder->build({
203         source => 'Borrower',
204         value => { branchcode => $branch }
205     })->{ borrowernumber };
206     # Look for the defined MARC field for biblio-level itemtype
207     my $rs = $schema->resultset('MarcSubfieldStructure')->search({
208         frameworkcode => '',
209         kohafield     => 'biblioitems.itemtype'
210     });
211     my $tagfield    = $rs->first->tagfield;
212     my $tagsubfield = $rs->first->tagsubfield;
213
214     # Create a biblio record with biblio-level itemtype
215     my $record = MARC::Record->new();
216     $record->append_fields(
217         MARC::Field->new($tagfield,'','', $tagsubfield => $blevel_itemtype )
218     );
219     my ( $biblionumber, $biblioitemnumber ) = AddBiblio( $record, '' );
220     my $item_with_itemtype = $builder->build({
221         source => 'Item',
222         value  => {
223             biblionumber  => $biblionumber,
224             homebranch    => $branch,
225             holdingbranch => $branch,
226             itype         => $ilevel_itemtype
227         }
228     });
229     my $item_without_itemtype = $builder->build({
230         source => 'Item',
231         value  => {
232             biblionumber  => $biblionumber,
233             homebranch    => $branch,
234             holdingbranch => $branch,
235             itype         => undef
236         }
237     });
238
239     my $borrower = GetMember( borrowernumber => $borrowernumber );
240
241     AddIssue( $borrower, $item_with_itemtype->{ barcode } );
242     AddReturn( $item_with_itemtype->{ barcode }, $branch );
243     # Test item-level itemtype was recorded on the 'statistics' table
244     my $stat = $schema->resultset('Statistic')->search({
245         branch     => $branch,
246         type       => 'return',
247         itemnumber => $item_with_itemtype->{ itemnumber }
248     }, { order_by => { -asc => 'datetime' } })->next();
249
250     is( $stat->itemtype, $blevel_itemtype,
251         "biblio-level itype recorded on statistics for return");
252
253     AddIssue( $borrower, $item_without_itemtype->{ barcode } );
254     AddReturn( $item_without_itemtype->{ barcode }, $branch );
255     # Test biblio-level itemtype was recorded on the 'statistics' table
256     $stat = $schema->resultset('Statistic')->search({
257         branch     => $branch,
258         type       => 'return',
259         itemnumber => $item_without_itemtype->{ itemnumber }
260     }, { order_by => { -asc => 'datetime' } })->next();
261
262     is( $stat->itemtype, $blevel_itemtype,
263         "biblio-level itype recorded on statistics for return");
264 };
265
266 subtest 'Handle ids duplication' => sub {
267     plan tests => 4;
268
269     t::lib::Mocks::mock_preference( 'item-level_itypes', 1 );
270     t::lib::Mocks::mock_preference( 'CalculateFinesOnReturn', 1 );
271     t::lib::Mocks::mock_preference( 'finesMode', 'production' );
272     Koha::IssuingRules->search->update({ chargeperiod => 1, fine => 1, firstremind => 1, });
273
274     my $biblio = $builder->build( { source => 'Biblio' } );
275     my $itemtype = $builder->build( { source => 'Itemtype', value => { rentalcharge => 5 } } );
276     my $item = $builder->build(
277         {
278             source => 'Item',
279             value  => {
280                 biblionumber => $biblio->{biblionumber},
281                 notforloan => 0,
282                 itemlost   => 0,
283                 withdrawn  => 0,
284                 itype      => $itemtype->{itemtype},
285             }
286         }
287     );
288     my $patron = $builder->build({source => 'Borrower'});
289     $patron = Koha::Patrons->find( $patron->{borrowernumber} );
290
291     my $original_checkout = AddIssue( $patron->unblessed, $item->{barcode}, dt_from_string->subtract( days => 50 ) );
292     $builder->build({ source => 'OldIssue', value => { issue_id => $original_checkout->issue_id } });
293     my $old_checkout = Koha::Old::Checkouts->find( $original_checkout->issue_id );
294
295     AddRenewal( $patron->borrowernumber, $item->{itemnumber} );
296
297     my ($doreturn, $messages, $new_checkout, $borrower) = AddReturn( $item->{barcode}, undef, undef, undef, dt_from_string );
298
299     my $account_lines = Koha::Account::Lines->search({ borrowernumber => $patron->borrowernumber, issue_id => $original_checkout->issue_id });
300     is( $account_lines->count, 0, 'No account lines should exist on old issue_id' );
301
302     $account_lines = Koha::Account::Lines->search({ borrowernumber => $patron->borrowernumber, issue_id => $new_checkout->{issue_id} });
303     is( $account_lines->count, 2, 'Two account lines should exist on new issue_id' );
304
305     isnt( $original_checkout->issue_id, $new_checkout->{issue_id}, 'AddReturn should return the issue with the new issue_id' );
306     isnt( $old_checkout->itemnumber, $item->{itemnumber}, 'If an item is checked-in, it should be moved to old_issues even if the issue_id already existed in the table' );
307 };
308
309 1;