Bug 18651: Update issue_id in AddReturn
[koha.git] / 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::DateUtils;
33 use Koha::Items;
34
35 use MARC::Record;
36 use MARC::Field;
37
38 # Mock userenv, used by AddIssue
39 my $branch;
40 my $context = Test::MockModule->new('C4::Context');
41 $context->mock( 'userenv', sub {
42     return { branch => $branch }
43 });
44
45 my $schema = Koha::Database->schema;
46 $schema->storage->txn_begin;
47
48 my $builder = t::lib::TestBuilder->new();
49
50 subtest "InProcessingToShelvingCart tests" => sub {
51
52     plan tests => 2;
53
54     $branch = $builder->build({ source => 'Branch' })->{ branchcode };
55     my $permanent_location = 'TEST';
56     my $location           = 'PROC';
57
58     # Create a biblio record with biblio-level itemtype
59     my $record = MARC::Record->new();
60     my ( $biblionumber, $biblioitemnumber ) = AddBiblio( $record, '' );
61     my $built_item = $builder->build({
62         source => 'Item',
63         value  => {
64             biblionumber  => $biblionumber,
65             homebranch    => $branch,
66             holdingbranch => $branch,
67             location      => $location,
68             permanent_location => $permanent_location
69         }
70     });
71     my $barcode = $built_item->{ barcode };
72     my $itemnumber = $built_item->{ itemnumber };
73     my $item;
74
75     t::lib::Mocks::mock_preference( "InProcessingToShelvingCart", 1 );
76     AddReturn( $barcode, $branch );
77     $item = GetItem( $itemnumber );
78     is( $item->{location}, 'CART',
79         "InProcessingToShelvingCart functions as intended" );
80
81     $item->{location} = $location;
82     ModItem( $item, undef, $itemnumber );
83
84     t::lib::Mocks::mock_preference( "InProcessingToShelvingCart", 0 );
85     AddReturn( $barcode, $branch );
86     $item = GetItem( $itemnumber );
87     is( $item->{location}, $permanent_location,
88         "InProcessingToShelvingCart functions as intended" );
89 };
90
91
92 subtest "AddReturn logging on statistics table (item-level_itypes=1)" => sub {
93
94     plan tests => 4;
95
96     # Set item-level item types
97     t::lib::Mocks::mock_preference( "item-level_itypes", 1 );
98
99     # Make sure logging is enabled
100     t::lib::Mocks::mock_preference( "IssueLog", 1 );
101     t::lib::Mocks::mock_preference( "ReturnLog", 1 );
102
103     # Create an itemtype for biblio-level item type
104     my $blevel_itemtype = $builder->build({ source => 'Itemtype' })->{ itemtype };
105     # Create an itemtype for item-level item type
106     my $ilevel_itemtype = $builder->build({ source => 'Itemtype' })->{ itemtype };
107     # Create a branch
108     $branch = $builder->build({ source => 'Branch' })->{ branchcode };
109     # Create a borrower
110     my $borrowernumber = $builder->build({
111         source => 'Borrower',
112         value => { branchcode => $branch }
113     })->{ borrowernumber };
114     # Look for the defined MARC field for biblio-level itemtype
115     my $rs = $schema->resultset('MarcSubfieldStructure')->search({
116         frameworkcode => '',
117         kohafield     => 'biblioitems.itemtype'
118     });
119     my $tagfield    = $rs->first->tagfield;
120     my $tagsubfield = $rs->first->tagsubfield;
121
122     # Create a biblio record with biblio-level itemtype
123     my $record = MARC::Record->new();
124     $record->append_fields(
125         MARC::Field->new($tagfield,'','', $tagsubfield => $blevel_itemtype )
126     );
127     my ( $biblionumber, $biblioitemnumber ) = AddBiblio( $record, '' );
128     my $item_with_itemtype = $builder->build(
129         {
130             source => 'Item',
131             value  => {
132                 biblionumber     => $biblionumber,
133                 biblioitemnumber => $biblioitemnumber,
134                 homebranch       => $branch,
135                 holdingbranch    => $branch,
136                 itype            => $ilevel_itemtype
137             }
138         }
139     );
140     my $item_without_itemtype = $builder->build(
141         {
142             source => 'Item',
143             value  => {
144                 biblionumber     => $biblionumber,
145                 biblioitemnumber => $biblioitemnumber,
146                 homebranch       => $branch,
147                 holdingbranch    => $branch,
148                 itype            => undef
149             }
150         }
151     );
152
153     my $borrower = GetMember( borrowernumber => $borrowernumber );
154     AddIssue( $borrower, $item_with_itemtype->{ barcode } );
155     AddReturn( $item_with_itemtype->{ barcode }, $branch );
156     # Test item-level itemtype was recorded on the 'statistics' table
157     my $stat = $schema->resultset('Statistic')->search({
158         branch     => $branch,
159         type       => 'return',
160         itemnumber => $item_with_itemtype->{ itemnumber }
161     }, { order_by => { -asc => 'datetime' } })->next();
162
163     is( $stat->itemtype, $ilevel_itemtype,
164         "item-level itype recorded on statistics for return");
165     warning_like { AddIssue( $borrower, $item_without_itemtype->{ barcode } ) }
166                  qr/^item-level_itypes set but no itemtype set for item/,
167                  'Item without itemtype set raises warning on AddIssue';
168     warning_like { AddReturn( $item_without_itemtype->{ barcode }, $branch ) }
169                  qr/^item-level_itypes set but no itemtype set for item/,
170                  'Item without itemtype set raises warning on AddReturn';
171     # Test biblio-level itemtype was recorded on the 'statistics' table
172     $stat = $schema->resultset('Statistic')->search({
173         branch     => $branch,
174         type       => 'return',
175         itemnumber => $item_without_itemtype->{ itemnumber }
176     }, { order_by => { -asc => 'datetime' } })->next();
177
178     is( $stat->itemtype, $blevel_itemtype,
179         "biblio-level itype recorded on statistics for return as a fallback for null item-level itype");
180
181 };
182
183 subtest "AddReturn logging on statistics table (item-level_itypes=0)" => sub {
184
185     plan tests => 2;
186
187     # Make sure logging is enabled
188     t::lib::Mocks::mock_preference( "IssueLog", 1 );
189     t::lib::Mocks::mock_preference( "ReturnLog", 1 );
190
191     # Set item-level item types
192     t::lib::Mocks::mock_preference( "item-level_itypes", 0 );
193
194     # Create an itemtype for biblio-level item type
195     my $blevel_itemtype = $builder->build({ source => 'Itemtype' })->{ itemtype };
196     # Create an itemtype for item-level item type
197     my $ilevel_itemtype = $builder->build({ source => 'Itemtype' })->{ itemtype };
198     # Create a branch
199     $branch = $builder->build({ source => 'Branch' })->{ branchcode };
200     # Create a borrower
201     my $borrowernumber = $builder->build({
202         source => 'Borrower',
203         value => { branchcode => $branch }
204     })->{ borrowernumber };
205     # Look for the defined MARC field for biblio-level itemtype
206     my $rs = $schema->resultset('MarcSubfieldStructure')->search({
207         frameworkcode => '',
208         kohafield     => 'biblioitems.itemtype'
209     });
210     my $tagfield    = $rs->first->tagfield;
211     my $tagsubfield = $rs->first->tagsubfield;
212
213     # Create a biblio record with biblio-level itemtype
214     my $record = MARC::Record->new();
215     $record->append_fields(
216         MARC::Field->new($tagfield,'','', $tagsubfield => $blevel_itemtype )
217     );
218     my ( $biblionumber, $biblioitemnumber ) = AddBiblio( $record, '' );
219     my $item_with_itemtype = $builder->build({
220         source => 'Item',
221         value  => {
222             biblionumber  => $biblionumber,
223             homebranch    => $branch,
224             holdingbranch => $branch,
225             itype         => $ilevel_itemtype
226         }
227     });
228     my $item_without_itemtype = $builder->build({
229         source => 'Item',
230         value  => {
231             biblionumber  => $biblionumber,
232             homebranch    => $branch,
233             holdingbranch => $branch,
234             itype         => undef
235         }
236     });
237
238     my $borrower = GetMember( borrowernumber => $borrowernumber );
239
240     AddIssue( $borrower, $item_with_itemtype->{ barcode } );
241     AddReturn( $item_with_itemtype->{ barcode }, $branch );
242     # Test item-level itemtype was recorded on the 'statistics' table
243     my $stat = $schema->resultset('Statistic')->search({
244         branch     => $branch,
245         type       => 'return',
246         itemnumber => $item_with_itemtype->{ itemnumber }
247     }, { order_by => { -asc => 'datetime' } })->next();
248
249     is( $stat->itemtype, $blevel_itemtype,
250         "biblio-level itype recorded on statistics for return");
251
252     AddIssue( $borrower, $item_without_itemtype->{ barcode } );
253     AddReturn( $item_without_itemtype->{ barcode }, $branch );
254     # Test biblio-level itemtype was recorded on the 'statistics' table
255     $stat = $schema->resultset('Statistic')->search({
256         branch     => $branch,
257         type       => 'return',
258         itemnumber => $item_without_itemtype->{ itemnumber }
259     }, { order_by => { -asc => 'datetime' } })->next();
260
261     is( $stat->itemtype, $blevel_itemtype,
262         "biblio-level itype recorded on statistics for return");
263 };
264
265 subtest 'Handle ids duplication' => sub {
266     plan tests => 2;
267
268     my $biblio = $builder->build( { source => 'Biblio' } );
269     my $item = $builder->build(
270         {
271             source => 'Item',
272             value  => {
273                 biblionumber => $biblio->{biblionumber},
274                 notforloan => 0,
275                 itemlost   => 0,
276                 withdrawn  => 0,
277
278             }
279         }
280     );
281     my $patron = $builder->build({source => 'Borrower'});
282
283     my $checkout = AddIssue( $patron, $item->{barcode} );
284     $builder->build({ source => 'OldIssue', value => { issue_id => $checkout->issue_id } });
285
286     my ($doreturn, $messages, $issue, $borrower) = AddReturn( $item->{barcode} );
287     my $old_checkout = Koha::Old::Checkouts->find( $checkout->issue_id );
288
289     isnt( $checkout->issue_id, $issue->{issue_id}, 'AddReturn should return the issue with the new issue_id' );
290     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' );
291 };
292
293 1;