3 # This file is part of Koha.
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.
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.
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>.
20 use Test::More tests => 4;
25 use t::lib::TestBuilder;
32 use Koha::Account::Lines;
39 # Mock userenv, used by AddIssue
41 my $context = Test::MockModule->new('C4::Context');
42 $context->mock( 'userenv', sub {
43 return { branch => $branch }
46 my $schema = Koha::Database->schema;
47 $schema->storage->txn_begin;
49 my $builder = t::lib::TestBuilder->new();
51 subtest "InProcessingToShelvingCart tests" => sub {
55 $branch = $builder->build({ source => 'Branch' })->{ branchcode };
56 my $permanent_location = 'TEST';
57 my $location = 'PROC';
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({
65 biblionumber => $biblionumber,
66 homebranch => $branch,
67 holdingbranch => $branch,
68 location => $location,
69 permanent_location => $permanent_location
72 my $barcode = $built_item->{ barcode };
73 my $itemnumber = $built_item->{ itemnumber };
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" );
82 $item->{location} = $location;
83 ModItem( $item, undef, $itemnumber );
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" );
93 subtest "AddReturn logging on statistics table (item-level_itypes=1)" => sub {
97 # Set item-level item types
98 t::lib::Mocks::mock_preference( "item-level_itypes", 1 );
100 # Make sure logging is enabled
101 t::lib::Mocks::mock_preference( "IssueLog", 1 );
102 t::lib::Mocks::mock_preference( "ReturnLog", 1 );
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 };
109 $branch = $builder->build({ source => 'Branch' })->{ branchcode };
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({
118 kohafield => 'biblioitems.itemtype'
120 my $tagfield = $rs->first->tagfield;
121 my $tagsubfield = $rs->first->tagsubfield;
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 )
128 my ( $biblionumber, $biblioitemnumber ) = AddBiblio( $record, '' );
129 my $item_with_itemtype = $builder->build(
133 biblionumber => $biblionumber,
134 biblioitemnumber => $biblioitemnumber,
135 homebranch => $branch,
136 holdingbranch => $branch,
137 itype => $ilevel_itemtype
141 my $item_without_itemtype = $builder->build(
145 biblionumber => $biblionumber,
146 biblioitemnumber => $biblioitemnumber,
147 homebranch => $branch,
148 holdingbranch => $branch,
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({
161 itemnumber => $item_with_itemtype->{ itemnumber }
162 }, { order_by => { -asc => 'datetime' } })->next();
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({
176 itemnumber => $item_without_itemtype->{ itemnumber }
177 }, { order_by => { -asc => 'datetime' } })->next();
179 is( $stat->itemtype, $blevel_itemtype,
180 "biblio-level itype recorded on statistics for return as a fallback for null item-level itype");
184 subtest "AddReturn logging on statistics table (item-level_itypes=0)" => sub {
188 # Make sure logging is enabled
189 t::lib::Mocks::mock_preference( "IssueLog", 1 );
190 t::lib::Mocks::mock_preference( "ReturnLog", 1 );
192 # Set item-level item types
193 t::lib::Mocks::mock_preference( "item-level_itypes", 0 );
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 };
200 $branch = $builder->build({ source => 'Branch' })->{ branchcode };
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({
209 kohafield => 'biblioitems.itemtype'
211 my $tagfield = $rs->first->tagfield;
212 my $tagsubfield = $rs->first->tagsubfield;
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 )
219 my ( $biblionumber, $biblioitemnumber ) = AddBiblio( $record, '' );
220 my $item_with_itemtype = $builder->build({
223 biblionumber => $biblionumber,
224 homebranch => $branch,
225 holdingbranch => $branch,
226 itype => $ilevel_itemtype
229 my $item_without_itemtype = $builder->build({
232 biblionumber => $biblionumber,
233 homebranch => $branch,
234 holdingbranch => $branch,
239 my $borrower = GetMember( borrowernumber => $borrowernumber );
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({
247 itemnumber => $item_with_itemtype->{ itemnumber }
248 }, { order_by => { -asc => 'datetime' } })->next();
250 is( $stat->itemtype, $blevel_itemtype,
251 "biblio-level itype recorded on statistics for return");
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({
259 itemnumber => $item_without_itemtype->{ itemnumber }
260 }, { order_by => { -asc => 'datetime' } })->next();
262 is( $stat->itemtype, $blevel_itemtype,
263 "biblio-level itype recorded on statistics for return");
266 subtest 'Handle ids duplication' => sub {
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, });
274 my $biblio = $builder->build( { source => 'Biblio' } );
275 my $itemtype = $builder->build( { source => 'Itemtype', value => { rentalcharge => 5 } } );
276 my $item = $builder->build(
280 biblionumber => $biblio->{biblionumber},
284 itype => $itemtype->{itemtype},
288 my $patron = $builder->build({source => 'Borrower'});
289 $patron = Koha::Patrons->find( $patron->{borrowernumber} );
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 );
295 AddRenewal( $patron->borrowernumber, $item->{itemnumber} );
297 my ($doreturn, $messages, $new_checkout, $borrower) = AddReturn( $item->{barcode}, undef, undef, undef, dt_from_string );
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' );
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' );
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' );