Bug 24769: Regression tests
[koha.git] / t / db_dependent / SIP / Transaction.t
1 #!/usr/bin/perl
2
3 # Tests for SIP::ILS::Transaction
4 # Current state is very rudimentary. Please help to extend it!
5
6 use Modern::Perl;
7 use Test::More tests => 10;
8
9 use Koha::Database;
10 use t::lib::TestBuilder;
11 use t::lib::Mocks;
12 use C4::SIP::ILS;
13 use C4::SIP::ILS::Patron;
14 use C4::SIP::ILS::Transaction::RenewAll;
15 use C4::SIP::ILS::Transaction::Checkout;
16 use C4::SIP::ILS::Transaction::FeePayment;
17 use C4::SIP::ILS::Transaction::Hold;
18 use C4::SIP::ILS::Transaction::Checkout;
19 use C4::SIP::ILS::Transaction::Checkin;
20
21 use C4::Reserves;
22 use Koha::CirculationRules;
23 use Koha::DateUtils qw( dt_from_string output_pref );
24
25 my $schema = Koha::Database->new->schema;
26 $schema->storage->txn_begin;
27
28 my $builder = t::lib::TestBuilder->new();
29 my $borr1 = $builder->build({ source => 'Borrower' });
30 my $card = $borr1->{cardnumber};
31 my $sip_patron = C4::SIP::ILS::Patron->new( $card );
32
33 # Create transaction RenewAll, assign patron, and run (no items)
34 my $transaction = C4::SIP::ILS::Transaction::RenewAll->new();
35 is( ref $transaction, "C4::SIP::ILS::Transaction::RenewAll", "New transaction created" );
36 is( $transaction->patron( $sip_patron ), $sip_patron, "Patron assigned to transaction" );
37 isnt( $transaction->do_renew_all, undef, "RenewAll on zero items" );
38
39 subtest fill_holds_at_checkout => sub {
40     plan tests => 6;
41
42
43     my $category = $builder->build({ source => 'Category', value => { category_type => 'A' }});
44     my $branch   = $builder->build({ source => 'Branch' });
45     my $borrower = $builder->build({ source => 'Borrower', value =>{
46         branchcode => $branch->{branchcode},
47         categorycode=>$category->{categorycode}
48         }
49     });
50     t::lib::Mocks::mock_userenv({ branchcode => $branch->{branchcode}, flags => 1 });
51
52     my $itype = $builder->build({ source => 'Itemtype', value =>{notforloan=>0} });
53     my $item1 = $builder->build_sample_item({
54         barcode       => 'barcode4test',
55         homebranch    => $branch->{branchcode},
56         holdingbranch => $branch->{branchcode},
57         itype         => $itype->{itemtype},
58         notforloan       => 0,
59     })->unblessed;
60     my $item2 = $builder->build_sample_item({
61         homebranch    => $branch->{branchcode},
62         holdingbranch => $branch->{branchcode},
63         biblionumber  => $item1->{biblionumber},
64         itype         => $itype->{itemtype},
65         notforloan       => 0,
66     })->unblessed;
67
68     Koha::CirculationRules->set_rules(
69         {
70             categorycode => $borrower->{categorycode},
71             branchcode   => $branch->{branchcode},
72             itemtype     => $itype->{itemtype},
73             rules        => {
74                 onshelfholds     => 1,
75                 reservesallowed  => 3,
76                 holds_per_record => 3,
77                 issuelength      => 5,
78                 lengthunit       => 'days',
79             }
80         }
81     );
82
83     my $reserve1 = AddReserve(
84         {
85             branchcode     => $branch->{branchcode},
86             borrowernumber => $borrower->{borrowernumber},
87             biblionumber   => $item1->{biblionumber}
88         }
89     );
90     my $reserve2 = AddReserve(
91         {
92             branchcode     => $branch->{branchcode},
93             borrowernumber => $borrower->{borrowernumber},
94             biblionumber   => $item1->{biblionumber}
95         }
96     );
97
98     my $bib = Koha::Biblios->find( $item1->{biblionumber} );
99     is( $bib->holds->count(), 2, "Bib has 2 holds");
100
101     my $sip_patron = C4::SIP::ILS::Patron->new( $borrower->{cardnumber} );
102     my $sip_item   = C4::SIP::ILS::Item->new( $item1->{barcode} );
103     my $transaction = C4::SIP::ILS::Transaction::Checkout->new();
104     is( ref $transaction, "C4::SIP::ILS::Transaction::Checkout", "New transaction created" );
105     is( $transaction->patron( $sip_patron ), $sip_patron, "Patron assigned to transaction" );
106     is( $transaction->item( $sip_item ), $sip_item, "Item assigned to transaction" );
107     my $checkout = $transaction->do_checkout();
108     use Data::Dumper; # Temporary debug statement
109     is( $bib->holds->count(), 1, "Bib has 1 holds remaining") or diag Dumper $checkout;
110
111     t::lib::Mocks::mock_preference('itemBarcodeInputFilter', 'whitespace');
112     $sip_item   = C4::SIP::ILS::Item->new( ' barcode 4 test ');
113     $transaction = C4::SIP::ILS::Transaction::Checkout->new();
114     is( $sip_item->{barcode}, $item1->{barcode}, "Item assigned to transaction" );
115 };
116
117 subtest "FeePayment->pay tests" => sub {
118
119     plan tests => 5;
120
121     # Create a borrower and add some outstanding debts to their account
122     my $patron = $builder->build( { source => 'Borrower' } );
123     my $account =
124       Koha::Account->new( { patron_id => $patron->{borrowernumber} } );
125     my $debt1 = $account->add_debit(
126         { type => 'ACCOUNT', amount => 100, interface => 'commandline' } );
127     my $debt2 = $account->add_debit(
128         { type => 'ACCOUNT', amount => 200, interface => 'commandline' } );
129
130     # Instantiate a new FeePayment transaction object
131     my $trans = C4::SIP::ILS::Transaction::FeePayment->new();
132     is(
133         ref $trans,
134         "C4::SIP::ILS::Transaction::FeePayment",
135         "New fee transaction created"
136     );
137
138     # Test the 'pay' method
139     # FIXME: pay should not require a borrowernumber
140     # (we should reach out to the transaction which should contain a patron object)
141     my $pay_type = '00';    # 00 - Cash, 01 - VISA, 02 - Creditcard
142     my $ok =
143       $trans->pay( $patron->{borrowernumber}, 100, $pay_type, $debt1->id, 0,
144         0 );
145     ok( $ok, "FeePayment transaction succeeded" );
146     $debt1->discard_changes;
147     is( $debt1->amountoutstanding + 0, 0,
148         "Debt1 was reduced to 0 as expected" );
149     my $offsets = Koha::Account::Offsets->search(
150         { debit_id => $debt1->id, credit_id => { '!=' => undef } } );
151     is( $offsets->count, 1, "FeePayment produced an offset line correctly" );
152     my $credit = $offsets->next->credit;
153     is( $credit->payment_type, 'SIP00', "Payment type was set correctly" );
154 };
155
156 subtest cancel_hold => sub {
157     plan tests => 7;
158
159     my $library = $builder->build_object ({ class => 'Koha::Libraries' });
160     my $patron = $builder->build_object(
161         {
162             class => 'Koha::Patrons',
163             value => {
164                 branchcode => $library->branchcode,
165             }
166         }
167     );
168     t::lib::Mocks::mock_userenv({ branchcode => $library->branchcode, flags => 1 });
169
170     my $item = $builder->build_sample_item({
171         library       => $library->branchcode,
172     });
173
174     Koha::CirculationRules->set_rules(
175         {
176             categorycode => $patron->categorycode,
177             branchcode   => $library->branchcode,
178             itemtype     => $item->effective_itemtype,
179             rules        => {
180                 onshelfholds     => 1,
181                 reservesallowed  => 3,
182                 holds_per_record => 3,
183                 issuelength      => 5,
184                 lengthunit       => 'days',
185             }
186         }
187     );
188
189     my $reserve1 = AddReserve(
190         {
191             branchcode     => $library->branchcode,
192             borrowernumber => $patron->borrowernumber,
193             biblionumber   => $item->biblio->biblionumber,
194             itemnumber     => $item->itemnumber,
195         }
196     );
197     is( $item->biblio->holds->count(), 1, "Hold was placed on bib");
198     is( $item->holds->count(),1,"Hold was placed on specific item");
199
200     my $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
201     my $sip_item   = C4::SIP::ILS::Item->new( $item->barcode );
202     my $transaction = C4::SIP::ILS::Transaction::Hold->new();
203     is( ref $transaction, "C4::SIP::ILS::Transaction::Hold", "New transaction created" );
204     is( $transaction->patron( $sip_patron ), $sip_patron, "Patron assigned to transaction" );
205     is( $transaction->item( $sip_item ), $sip_item, "Item assigned to transaction" );
206     my $hold = $transaction->drop_hold();
207     is( $item->biblio->holds->count(), 0, "Bib has 0 holds remaining");
208     is( $item->holds->count(), 0,  "Item has 0 holds remaining");
209 };
210
211 subtest do_hold => sub {
212     plan tests => 8;
213
214     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
215     my $patron_1 = $builder->build_object(
216         {
217             class => 'Koha::Patrons',
218             value => {
219                 branchcode => $library->branchcode,
220             }
221         }
222     );
223     my $patron_2 = $builder->build_object(
224         {
225             class => 'Koha::Patrons',
226             value => {
227                 branchcode   => $library->branchcode,
228                 categorycode => $patron_1->categorycode,
229             }
230         }
231     );
232
233     t::lib::Mocks::mock_userenv(
234         { branchcode => $library->branchcode, flags => 1 } );
235
236     my $item = $builder->build_sample_item(
237         {
238             library => $library->branchcode,
239         }
240     );
241
242     my $reserve1 = AddReserve(
243         {
244             branchcode     => $library->branchcode,
245             borrowernumber => $patron_1->borrowernumber,
246             biblionumber   => $item->biblio->biblionumber,
247             itemnumber     => $item->itemnumber,
248         }
249     );
250     is( $item->biblio->holds->count(), 1, "Hold was placed on bib" );
251     is( $item->holds->count(),         1, "Hold was placed on specific item" );
252
253     my $sip_patron  = C4::SIP::ILS::Patron->new( $patron_2->cardnumber );
254     my $sip_item    = C4::SIP::ILS::Item->new( $item->barcode );
255     my $transaction = C4::SIP::ILS::Transaction::Hold->new();
256     is(
257         ref $transaction,
258         "C4::SIP::ILS::Transaction::Hold",
259         "New transaction created"
260     );
261     is( $transaction->patron($sip_patron),
262         $sip_patron, "Patron assigned to transaction" );
263     is( $transaction->item($sip_item),
264         $sip_item, "Item assigned to transaction" );
265     my $hold = $transaction->do_hold();
266     is( $item->biblio->holds->count(), 2, "Bib has 2 holds" );
267
268     my $THE_hold = $patron_2->holds->next;
269     is( $THE_hold->priority, 2, 'Hold placed from SIP should have a correct priority of 2');
270     is( $THE_hold->branchcode, $patron_2->branchcode, 'Hold placed from SIP should have the branchcode set' );
271 };
272
273 subtest do_checkin => sub {
274     plan tests => 8;
275
276     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
277     my $patron = $builder->build_object(
278         {
279             class => 'Koha::Patrons',
280             value => {
281                 branchcode => $library->branchcode,
282             }
283         }
284     );
285
286     t::lib::Mocks::mock_userenv(
287         { branchcode => $library->branchcode, flags => 1 } );
288
289     my $item = $builder->build_sample_item(
290         {
291             library => $library->branchcode,
292         }
293     );
294
295
296     # Checkout
297     my $sip_patron  = C4::SIP::ILS::Patron->new( $patron->cardnumber );
298     my $sip_item    = C4::SIP::ILS::Item->new( $item->barcode );
299     my $co_transaction = C4::SIP::ILS::Transaction::Checkout->new();
300     is( $co_transaction->patron($sip_patron),
301         $sip_patron, "Patron assigned to transaction" );
302     is( $co_transaction->item($sip_item),
303         $sip_item, "Item assigned to transaction" );
304     my $checkout = $co_transaction->do_checkout();
305     is( $patron->checkouts->count, 1, 'Checkout should have been done successfully');
306
307     # Checkin
308     my $ci_transaction = C4::SIP::ILS::Transaction::Checkin->new();
309     is( $ci_transaction->patron($sip_patron),
310         $sip_patron, "Patron assigned to transaction" );
311     is( $ci_transaction->item($sip_item),
312         $sip_item, "Item assigned to transaction" );
313
314     my $checkin = $ci_transaction->do_checkin($library->branchcode, C4::SIP::Sip::timestamp);
315     is( $patron->checkouts->count, 0, 'Checkin should have been done successfully');
316
317     # Test checkin without return date
318     $co_transaction->do_checkout;
319     is( $patron->checkouts->count, 1, 'Checkout should have been done successfully');
320     $ci_transaction->do_checkin($library->branchcode, undef);
321     is( $patron->checkouts->count, 0, 'Checkin should have been done successfully');
322 };
323
324 subtest checkin_lost => sub {
325     plan tests => 2;
326
327     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
328
329     t::lib::Mocks::mock_userenv(
330         { branchcode => $library->branchcode, flags => 1 } );
331
332     my $item = $builder->build_sample_item(
333         {
334             library     => $library->branchcode,
335         }
336     );
337
338     $item->itemlost(1)->itemlost_on(dt_from_string)->store();
339
340     my $instituation = {
341         id             => $library->id,
342         implementation => "ILS",
343         policy         => {
344             checkin  => "true",
345             renewal  => "true",
346             checkout => "true",
347             timeout  => 100,
348             retries  => 5,
349         }
350     };
351     my $ils = C4::SIP::ILS->new( $instituation );
352
353     t::lib::Mocks::mock_preference('BlockReturnOfLostItems', '1');
354     my $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp );
355     is( $circ->{screen_msg}, 'Item lost, return not allowed', "Got correct screen message" );
356
357     t::lib::Mocks::mock_preference('BlockReturnOfLostItems', '0');
358     $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp );
359     is( $circ->{screen_msg}, 'Item not checked out', "Got 'Item not checked out' screen message" );
360 };
361
362 subtest checkin_withdrawn => sub {
363     plan tests => 2;
364
365     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
366
367     t::lib::Mocks::mock_userenv(
368         { branchcode => $library->branchcode, flags => 1 } );
369
370     my $item = $builder->build_sample_item(
371         {
372             library     => $library->branchcode,
373         }
374     );
375
376     $item->withdrawn(1)->withdrawn_on(dt_from_string)->store();
377
378     my $instituation = {
379         id             => $library->id,
380         implementation => "ILS",
381         policy         => {
382             checkin  => "true",
383             renewal  => "true",
384             checkout => "true",
385             timeout  => 100,
386             retries  => 5,
387         }
388     };
389     my $ils = C4::SIP::ILS->new( $instituation );
390
391     t::lib::Mocks::mock_preference('BlockReturnOfWithdrawnItems', '1');
392     my $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp );
393     is( $circ->{screen_msg}, 'Item withdrawn, return not allowed', "Got correct screen message" );
394
395     t::lib::Mocks::mock_preference('BlockReturnOfWithdrawnItems', '0');
396     $circ = $ils->checkin( $item->barcode, C4::SIP::Sip::timestamp );
397     is( $circ->{screen_msg}, 'Item not checked out', "Got 'Item not checked out' screen message" );
398 };
399 $schema->storage->txn_rollback;