Bug 34932: Patron.t - Pass borrowernumber of manager to userenv
[koha.git] / t / db_dependent / Koha / Acquisition / Order.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 => 13;
23 use Test::Exception;
24
25 use t::lib::TestBuilder;
26 use t::lib::Mocks;
27
28 use C4::Circulation qw( AddIssue AddReturn );
29
30 use Koha::Biblios;
31 use Koha::Database;
32 use Koha::DateUtils qw(dt_from_string);
33 use Koha::Items;
34
35 my $schema  = Koha::Database->schema;
36 my $builder = t::lib::TestBuilder->new;
37
38 subtest 'basket() tests' => sub {
39
40     plan tests => 2;
41
42     $schema->storage->txn_begin;
43
44     my $basket = $builder->build_object(
45         {
46             class => 'Koha::Acquisition::Baskets'
47         }
48     );
49     my $order = $builder->build_object(
50         {
51             class => 'Koha::Acquisition::Orders',
52             value => { basketno => $basket->basketno }
53         }
54     );
55
56     my $retrieved_basket = $order->basket;
57     is( ref($retrieved_basket), 'Koha::Acquisition::Basket',
58         'Type is correct for ->basket' );
59     is_deeply( $retrieved_basket->unblessed,
60         $basket->unblessed, "Correct basket found and updated" );
61
62     $schema->storage->txn_rollback;
63 };
64
65 subtest 'biblio() tests' => sub {
66
67     plan tests => 5;
68
69     $schema->storage->txn_begin;
70
71     my $order = $builder->build_object(
72         {
73             class => 'Koha::Acquisition::Orders',
74             value => { biblionumber => undef }
75         }
76     );
77
78     is( $order->biblio, undef, 'If no linked biblio, undef is returned' );
79
80     # Add and link a biblio to the order
81     my $biblio = $builder->build_sample_biblio();
82     $order->set({ biblionumber => $biblio->biblionumber })->store->discard_changes;
83
84     my $THE_biblio = $order->biblio;
85     is( ref($THE_biblio), 'Koha::Biblio', 'Returns a Koha::Biblio object' );
86     is( $THE_biblio->biblionumber, $biblio->biblionumber, 'It is not cheating about the object' );
87
88     $order->biblio->delete;
89     $order = Koha::Acquisition::Orders->find($order->ordernumber);
90     ok( $order, 'The order is not deleted if the biblio is deleted' );
91     is( $order->biblio, undef, 'order.biblio is correctly set to NULL when the biblio is deleted' );
92
93     $schema->storage->txn_rollback;
94 };
95
96 subtest 'store' => sub {
97     plan tests => 1;
98
99     $schema->storage->txn_begin;
100     my $o = $builder->build_object(
101         {
102             class => 'Koha::Acquisition::Orders'
103         }
104     );
105
106     subtest 'entrydate' => sub {
107         plan tests => 2;
108
109         my $order;
110
111         t::lib::Mocks::mock_preference( 'TimeFormat', '12hr' );
112         $order = Koha::Acquisition::Order->new(
113             {
114                 basketno     => $o->basketno,
115                 biblionumber => $o->biblionumber,
116                 budget_id    => $o->budget_id,
117                 quantity     => 1,
118             }
119         )->store;
120         $order->discard_changes;
121         like( $order->entrydate, qr|^\d{4}-\d{2}-\d{2}$| );
122
123         t::lib::Mocks::mock_preference( 'TimeFormat', '24hr' );
124         $order = Koha::Acquisition::Order->new(
125             {
126                 basketno     => $o->basketno,
127                 biblionumber => $o->biblionumber,
128                 budget_id    => $o->budget_id,
129                 quantity     => 1,
130             }
131         )->store;
132         $order->discard_changes;
133         like( $order->entrydate, qr|^\d{4}-\d{2}-\d{2}$| );
134     };
135     $schema->storage->txn_rollback;
136 };
137
138 subtest 'fund' => sub {
139     plan tests => 1;
140
141     $schema->storage->txn_begin;
142     my $o = $builder->build_object(
143         {
144             class => 'Koha::Acquisition::Orders',
145         }
146     );
147
148     my $order = Koha::Acquisition::Orders->find( $o->ordernumber );
149     is( ref( $order->fund ),
150         'Koha::Acquisition::Fund',
151         '->fund should return a Koha::Acquisition::Fund object' );
152     $schema->storage->txn_rollback;
153 };
154
155 subtest 'invoice' => sub {
156     plan tests => 2;
157
158     $schema->storage->txn_begin;
159     my $o = $builder->build_object(
160         {
161             class => 'Koha::Acquisition::Orders',
162             value => { cancellationreason => 'XXXXXXXX', invoiceid => undef }, # not received yet
163         }
164     );
165
166     my $order = Koha::Acquisition::Orders->find( $o->ordernumber );
167     is( $order->invoice, undef,
168         '->invoice should return undef if no invoice defined yet');
169
170     my $invoice = $builder->build_object(
171         {
172             class => 'Koha::Acquisition::Invoices',
173         },
174     );
175
176     $o->invoiceid( $invoice->invoiceid )->store;
177     $order = Koha::Acquisition::Orders->find( $o->ordernumber );
178     is( ref( $order->invoice ), 'Koha::Acquisition::Invoice',
179         '->invoice should return a Koha::Acquisition::Invoice object if an invoice is defined');
180
181     $schema->storage->txn_rollback;
182 };
183
184 subtest 'subscription' => sub {
185     plan tests => 2;
186
187     $schema->storage->txn_begin;
188     my $o = $builder->build_object(
189         {
190             class => 'Koha::Acquisition::Orders',
191             value => { subscriptionid => undef }, # not linked to a subscription
192         }
193     );
194
195     my $order = Koha::Acquisition::Orders->find( $o->ordernumber );
196     is( $order->subscription, undef,
197         '->subscription should return undef if not created from a subscription');
198
199     $o = $builder->build_object(
200         {
201             class => 'Koha::Acquisition::Orders',
202             # Will be linked to a subscription by TestBuilder
203         }
204     );
205
206     $order = Koha::Acquisition::Orders->find( $o->ordernumber );
207     is( ref( $order->subscription ), 'Koha::Subscription',
208         '->subscription should return a Koha::Subscription object if created from a subscription');
209
210     $schema->storage->txn_rollback;
211 };
212
213 subtest 'duplicate_to | add_item' => sub {
214     plan tests => 3;
215
216     $schema->storage->txn_begin;
217
218     my $item = $builder->build_sample_item;
219     my $order_no_sub = $builder->build_object(
220         {
221             class => 'Koha::Acquisition::Orders',
222             value =>
223               {
224                   biblionumber => $item->biblionumber,
225                   subscriptionid => undef, # not linked to a subscription
226               }
227         }
228     );
229     $order_no_sub->basket->create_items(undef)->store; # use syspref
230     $order_no_sub->add_item( $item->itemnumber );
231
232     $item = $builder->build_sample_item;
233     my $order_from_sub = $builder->build_object(
234         {
235             class => 'Koha::Acquisition::Orders',
236             value =>
237               {
238                   biblionumber => $item->biblionumber,
239                   # Will be linked to a subscription by TestBuilder
240               }
241         }
242     );
243     $order_from_sub->basket->create_items(undef)->store; # use syspref
244     $order_from_sub->add_item( $item->itemnumber );
245
246     my $basket_to = $builder->build_object(
247          { class => 'Koha::Acquisition::Baskets' });
248
249     subtest 'Create item on receiving' => sub {
250         plan tests => 2;
251
252         t::lib::Mocks::mock_preference('AcqCreateItem', 'receiving');
253
254         my $duplicated_order = $order_no_sub->duplicate_to($basket_to);
255         is( $duplicated_order->items->count, 0,
256             'Items should not be copied if the original order did not create items on ordering'
257         );
258
259         $duplicated_order = $order_from_sub->duplicate_to($basket_to);
260         is( $duplicated_order->items->count, 0,
261             'Items should not be copied if the original order is created from a subscription'
262         );
263     };
264
265     subtest 'Create item on ordering' => sub {
266         plan tests => 2;
267
268         t::lib::Mocks::mock_preference('AcqCreateItem', 'ordering');
269
270         my $duplicated_order = $order_no_sub->duplicate_to($basket_to);
271         is( $duplicated_order->items->count, 1,
272             'Items should be copied if items are created on ordering'
273         );
274
275         $duplicated_order = $order_from_sub->duplicate_to($basket_to);
276         is( $duplicated_order->items->count, 0,
277             'Items should never be copied if the original order is created from a subscription'
278         );
279     };
280
281     subtest 'Regression tests' => sub {
282         plan tests => 1;
283
284         my $duplicated_order = $order_no_sub->duplicate_to($basket_to);
285         is($duplicated_order->invoiceid, undef, "invoiceid should be set to null for a new duplicated order");
286     };
287
288     $schema->storage->txn_rollback;
289 };
290
291 subtest 'current_item_level_holds() tests' => sub {
292
293     plan tests => 5;
294
295     $schema->storage->txn_begin;
296
297     my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
298     my $biblio = $builder->build_sample_biblio();
299     my $item_1 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
300     my $item_2 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
301     my $item_3 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
302
303     C4::Reserves::AddReserve(
304         {
305             branchcode       => $patron->branchcode,
306             borrowernumber   => $patron->borrowernumber,
307             biblionumber     => $biblio->biblionumber,
308             reservation_date => dt_from_string->add( days => -2 ),
309             itemnumber       => $item_1->itemnumber,
310         }
311     );
312     C4::Reserves::AddReserve(
313         {
314             branchcode       => $patron->branchcode,
315             borrowernumber   => $patron->borrowernumber,
316             biblionumber     => $biblio->biblionumber,
317             reservation_date => dt_from_string->add( days => -2 ),
318             itemnumber       => $item_2->itemnumber,
319         }
320     );
321     # Add a hold in the future
322     C4::Reserves::AddReserve(
323         {
324             branchcode       => $patron->branchcode,
325             borrowernumber   => $patron->borrowernumber,
326             biblionumber     => $biblio->biblionumber,
327             reservation_date => dt_from_string->add( days => 2 ),
328             itemnumber       => $item_3->itemnumber,
329         }
330     );
331
332     # Add an order with no biblionumber
333     my $order = $builder->build_object(
334         {
335             class => 'Koha::Acquisition::Orders',
336             value => {
337                 biblionumber => undef
338             }
339         }
340     );
341
342     my $holds = $order->current_item_level_holds;
343
344     is( ref($holds), 'Koha::Holds', 'Koha::Holds iterator returned if no linked biblio' );
345     is( $holds->count, 0, 'Count is 0 if no linked biblio' );
346
347     $order->set({ biblionumber => $biblio->biblionumber })->store->discard_changes;
348
349     $holds = $order->current_item_level_holds;
350
351     is( ref($holds), 'Koha::Holds', 'Koha::Holds iterator returned if no linked items' );
352     is( $holds->count, 0, 'Count is 0 if no linked items' );
353
354     $order->add_item( $item_2->itemnumber );
355     $order->add_item( $item_3->itemnumber );
356
357     $holds = $order->current_item_level_holds;
358     is( $holds->count, 1, 'Only current (not future) holds are returned');
359
360     $schema->storage->txn_rollback;
361 };
362
363 subtest 'claim*' => sub {
364     plan tests => 6;
365
366     $schema->storage->txn_begin;
367     my $order = $builder->build_object(
368         {
369             class => 'Koha::Acquisition::Orders',
370         }
371     );
372
373     my $now = dt_from_string;
374     is( $order->claims->count, 0, 'No claim yet, ->claims should return an empty set');
375     is( $order->claims_count, 0, 'No claim yet, ->claims_count should return 0');
376     is( $order->claimed_date, undef, 'No claim yet, ->claimed_date should return undef');
377
378     my $claim_1 = $order->claim;
379     my $claim_2 = $order->claim;
380
381     $claim_1->claimed_on($now->clone->subtract(days => 1))->store;
382     $claim_2->claimed_on($now)->store;
383
384     is( $order->claims->count, 2, '->claims should return the correct number of claims');
385     is( $order->claims_count, 2, '->claims_count should return the correct number of claims');
386     is( dt_from_string($order->claimed_date), $now, '->claimed_date should return the date of the last claim');
387
388     $schema->storage->txn_rollback;
389 };
390
391 subtest 'filter_by_late' => sub {
392     plan tests => 17;
393
394     $schema->storage->txn_begin;
395     my $now        = dt_from_string;
396     my $bookseller = $builder->build_object(
397         {
398             class => 'Koha::Acquisition::Booksellers',
399             value => { deliverytime => 2 }
400         }
401     );
402     my $basket_1 = $builder->build_object(
403         {
404             class => 'Koha::Acquisition::Baskets',
405             value => {
406                 booksellerid => $bookseller->id,
407                 closedate    => undef,
408             }
409         }
410     );
411     my $order_1 = $builder->build_object(
412         {
413             class => 'Koha::Acquisition::Orders',
414             value => {
415                 basketno                => $basket_1->basketno,
416                 datereceived            => undef,
417                 datecancellationprinted => undef,
418                 estimated_delivery_date => undef,
419                 orderstatus             => 'ordered',
420             }
421         }
422     );
423     my $basket_2 = $builder->build_object(    # expected tomorrow
424         {
425             class => 'Koha::Acquisition::Baskets',
426             value => {
427                 booksellerid => $bookseller->id,
428                 closedate    => $now->clone->subtract( days => 1 ),
429             }
430         }
431     );
432     my $order_2 = $builder->build_object(
433         {
434             class => 'Koha::Acquisition::Orders',
435             value => {
436                 basketno                => $basket_2->basketno,
437                 datereceived            => undef,
438                 datecancellationprinted => undef,
439                 estimated_delivery_date => undef,
440                 orderstatus             => 'ordered',
441             }
442         }
443     );
444     my $basket_3 = $builder->build_object(    # expected yesterday (1 day)
445         {
446             class => 'Koha::Acquisition::Baskets',
447             value => {
448                 booksellerid => $bookseller->id,
449                 closedate    => $now->clone->subtract( days => 3 ),
450             }
451         }
452     );
453     my $order_3 = $builder->build_object(
454         {
455             class => 'Koha::Acquisition::Orders',
456             value => {
457                 basketno                => $basket_3->basketno,
458                 datereceived            => undef,
459                 datecancellationprinted => undef,
460                 estimated_delivery_date => undef,
461                 orderstatus             => 'ordered',
462             }
463         }
464     );
465     my $basket_4 = $builder->build_object(    # expected 3 days ago
466         {
467             class => 'Koha::Acquisition::Baskets',
468             value => {
469                 booksellerid => $bookseller->id,
470                 closedate    => $now->clone->subtract( days => 5 ),
471             }
472         }
473     );
474     my $order_4 = $builder->build_object(
475         {
476             class => 'Koha::Acquisition::Orders',
477             value => {
478                 basketno                => $basket_4->basketno,
479                 datereceived            => undef,
480                 datecancellationprinted => undef,
481                 estimated_delivery_date => undef,
482                 orderstatus             => 'ordered',
483             }
484         }
485     );
486     my $order_42 = $builder->build_object(
487         {
488             class => 'Koha::Acquisition::Orders',
489             value => {
490                 basketno                => $basket_4->basketno,
491                 datereceived            => undef,
492                 datecancellationprinted => undef,
493                 estimated_delivery_date => undef,
494                 orderstatus             => 'complete',
495             }
496         }
497     );
498
499     my $orders = Koha::Acquisition::Orders->search(
500         {
501             ordernumber => {
502                 -in => [
503                     $order_1->ordernumber, $order_2->ordernumber,
504                     $order_3->ordernumber, $order_4->ordernumber,
505                 ]
506             }
507         }
508     );
509
510     my $late_orders = $orders->filter_by_lates;
511     is( $late_orders->count, 3 );
512
513     $late_orders = $orders->filter_by_lates( { delay => 0 } );
514     is( $late_orders->count, 3 );
515
516     $late_orders = $orders->filter_by_lates( { delay => 1 } );
517     is( $late_orders->count, 3 );
518
519     $late_orders = $orders->filter_by_lates( { delay => 3 } );
520     is( $late_orders->count, 2 );
521
522     $late_orders = $orders->filter_by_lates( { delay => 4 } );
523     is( $late_orders->count, 1 );
524
525     $late_orders = $orders->filter_by_lates( { delay => 5 } );
526     is( $late_orders->count, 1 );
527
528     $late_orders = $orders->filter_by_lates( { delay => 6 } );
529     is( $late_orders->count, 0 );
530
531     $late_orders = $orders->filter_by_lates(
532         { estimated_from => $now->clone->subtract( days => 6 ) } );
533     is( $late_orders->count,             2 );
534     is( $late_orders->next->ordernumber, $order_3->ordernumber );
535
536     $late_orders = $orders->filter_by_lates(
537         { estimated_from => $now->clone->subtract( days => 5 ) } );
538     is( $late_orders->count,             2 );
539     is( $late_orders->next->ordernumber, $order_3->ordernumber );
540
541     $late_orders = $orders->filter_by_lates(
542         { estimated_from => $now->clone->subtract( days => 4 ) } );
543     is( $late_orders->count,             2 );
544     is( $late_orders->next->ordernumber, $order_3->ordernumber );
545
546     $late_orders = $orders->filter_by_lates(
547         { estimated_from => $now->clone->subtract( days => 3 ) } );
548     is( $late_orders->count, 2 );
549
550     $late_orders = $orders->filter_by_lates(
551         { estimated_from => $now->clone->subtract( days => 1 ) } );
552     is( $late_orders->count, 1 );
553
554     $late_orders = $orders->filter_by_lates(
555         {
556             estimated_from => $now->clone->subtract( days => 4 ),
557             estimated_to   => $now->clone->subtract( days => 3 )
558         }
559     );
560     is( $late_orders->count, 1 );
561
562     my $basket_5 = $builder->build_object(    # closed today
563         {
564             class => 'Koha::Acquisition::Baskets',
565             value => {
566                 booksellerid => $bookseller->id,
567                 closedate    => $now,
568             }
569         }
570     );
571     my $order_5 = $builder->build_object(
572         {
573             class => 'Koha::Acquisition::Orders',
574             value => {
575                 basketno                => $basket_4->basketno,
576                 datereceived            => undef,
577                 datecancellationprinted => undef,
578                 estimated_delivery_date => $now->clone->subtract( days => 2 ),
579             }
580         }
581     );
582     $late_orders = $orders->filter_by_lates(
583         {
584             estimated_from => $now->clone->subtract( days => 3 ),
585             estimated_to   => $now->clone->subtract( days => 2 )
586         }
587     );
588     is( $late_orders->count, 1 );
589
590     $schema->storage->txn_rollback;
591 };
592
593 subtest 'filter_by_current & filter_by_cancelled' => sub {
594     plan tests => 2;
595
596     $schema->storage->txn_begin;
597     my $now        = dt_from_string;
598     my $order_1 = $builder->build_object(
599         {
600             class => 'Koha::Acquisition::Orders',
601             value => {
602                 datecancellationprinted => undef,
603             }
604         }
605     );
606     my $order_2 = $builder->build_object(
607         {
608             class => 'Koha::Acquisition::Orders',
609             value => {
610                 datecancellationprinted => undef,
611             }
612         }
613     );
614     my $order_3 = $builder->build_object(
615         {
616             class => 'Koha::Acquisition::Orders',
617             value => {
618                 datecancellationprinted => dt_from_string,
619             }
620         }
621     );
622
623     my $orders = Koha::Acquisition::Orders->search(
624         {
625             ordernumber => {
626                 -in => [
627                     $order_1->ordernumber, $order_2->ordernumber,
628                     $order_3->ordernumber,
629                 ]
630             }
631         }
632     );
633
634     is( $orders->filter_by_current->count, 2);
635     is( $orders->filter_by_cancelled->count, 1);
636
637
638     $schema->storage->txn_rollback;
639 };
640
641 subtest 'creator ()' => sub {
642     plan tests => 2;
643
644     $schema->storage->txn_begin;
645
646     my $patron = $builder->build_object({ class => 'Koha::Patrons' });
647     my $order = $builder->build_object({ class => 'Koha::Acquisition::Orders', value => { created_by => $patron->borrowernumber } });
648
649     my $creator = $order->creator;
650
651     is($creator->borrowernumber, $patron->borrowernumber, 'Patron is order creator');
652
653     $creator->delete;
654
655     is( $order->get_from_storage->creator, undef );
656
657     $schema->storage->txn_rollback;
658 };
659
660 subtest 'cancel() tests' => sub {
661
662     plan tests => 56;
663
664     $schema->storage->txn_begin;
665
666     my $reason = 'Some reason';
667
668     # Scenario:
669     # * order with one item attached
670     # * the item is on loan
671     # * delete_biblio is passed
672     # => order is not cancelled
673     # => item in order is not removed
674     # => biblio in order is not removed
675     # => message about not being able to delete
676
677     my $item      = $builder->build_sample_item;
678     my $biblio_id = $item->biblionumber;
679     my $order     = $builder->build_object(
680         {
681             class => 'Koha::Acquisition::Orders',
682             value => {
683                 orderstatus             => 'new',
684                 biblionumber            => $item->biblionumber,
685                 datecancellationprinted => undef,
686                 cancellationreason      => undef,
687             }
688         }
689     );
690     $order->add_item( $item->id );
691
692     my $patron = $builder->build_object(
693         {
694             class => 'Koha::Patrons',
695             value => { branchcode => $item->homebranch, flags => 1 }
696         }
697     );
698     t::lib::Mocks::mock_userenv({ patron => $patron });
699
700     # Add a checkout so deleting the item fails because od 'book_on_loan'
701     C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
702
703     my $result = $order->cancel({ reason => $reason });
704     # refresh the order object
705     $order->discard_changes;
706
707     is( $result, $order, 'self is returned' );
708     is( $order->orderstatus, 'cancelled', 'Order is not marked as cancelled' );
709     isnt( $order->datecancellationprinted, undef, 'datecancellationprinted is not undef' );
710     is( $order->cancellationreason, $reason, 'cancellationreason is set' );
711     is( ref(Koha::Items->find($item->id)), 'Koha::Item', 'The item is present' );
712     is( ref(Koha::Biblios->find($biblio_id)), 'Koha::Biblio', 'The biblio is present' );
713     my @messages = @{ $order->object_messages };
714     is( $messages[0]->message, 'error_delitem', 'An error message is attached to the order' );
715
716     # Scenario:
717     # * order with one item attached
718     # * the item is no longer on loan
719     # * delete_biblio not passed
720     # => order is cancelled
721     # => item in order is removed
722     # => biblio remains untouched
723
724     C4::Circulation::AddReturn( $item->barcode );
725
726     $order = Koha::Acquisition::Orders->find($order->ordernumber);
727     $order->cancel({ reason => $reason })
728           ->discard_changes;
729
730     is( $order->orderstatus, 'cancelled', 'Order is marked as cancelled' );
731     isnt( $order->datecancellationprinted, undef, 'datecancellationprinted is set' );
732     is( $order->cancellationreason, $reason, 'cancellationreason is undef' );
733     is( Koha::Items->find($item->id), undef, 'The item is no longer present' );
734     is( ref(Koha::Biblios->find($biblio_id)), 'Koha::Biblio', 'The biblio is present' );
735     @messages = @{ $order->object_messages };
736     is( scalar @messages, 0, 'No messages' );
737
738     # Scenario:
739     # * order with one item attached
740     # * biblio has another item
741     # => order is cancelled
742     # => item in order is removed
743     # => the extra item remains untouched
744     # => biblio remains untouched
745
746     my $item_1 = $builder->build_sample_item;
747     $biblio_id = $item_1->biblionumber;
748     my $item_2 = $builder->build_sample_item({ biblionumber => $biblio_id });
749     $order     = $builder->build_object(
750         {
751             class => 'Koha::Acquisition::Orders',
752             value => {
753                 orderstatus             => 'new',
754                 biblionumber            => $biblio_id,
755                 datecancellationprinted => undef,
756                 cancellationreason      => undef,
757             }
758         }
759     );
760     $order->add_item( $item_1->id );
761
762     $order->cancel({ reason => $reason, delete_biblio => 1 })
763           ->discard_changes;
764
765     is( $order->orderstatus, 'cancelled', 'Order is marked as cancelled' );
766     isnt( $order->datecancellationprinted, undef, 'datecancellationprinted is set' );
767     is( $order->cancellationreason, $reason, 'cancellationreason is undef' );
768     is( Koha::Items->find($item_1->id), undef, 'The item is no longer present' );
769     is( ref(Koha::Items->find($item_2->id)), 'Koha::Item', 'The item is still present' );
770     is( ref(Koha::Biblios->find($biblio_id)), 'Koha::Biblio', 'The biblio is still present' );
771     @messages = @{ $order->object_messages };
772     is( $messages[0]->message, 'error_delbiblio_items', 'Cannot delete biblio and it gets notified' );
773
774     # Scenario:
775     # * order with one item attached
776     # * there's another order pointing to the biblio
777     # => order is cancelled
778     # => item in order is removed
779     # => biblio remains untouched
780     # => biblio delete error notified
781
782     $item      = $builder->build_sample_item;
783     $biblio_id = $item->biblionumber;
784     $order     = $builder->build_object(
785         {
786             class => 'Koha::Acquisition::Orders',
787             value => {
788                 orderstatus             => 'new',
789                 biblionumber            => $biblio_id,
790                 datecancellationprinted => undef,
791                 cancellationreason      => undef,
792             }
793         }
794     );
795     $order->add_item( $item->id );
796
797     # Add another order
798     $builder->build_object(
799         {
800             class => 'Koha::Acquisition::Orders',
801             value => {
802                 orderstatus             => 'new',
803                 biblionumber            => $biblio_id,
804                 datecancellationprinted => undef,
805                 cancellationreason      => undef,
806             }
807         }
808     );
809
810     $order->cancel({ reason => $reason, delete_biblio => 1 })
811           ->discard_changes;
812
813     is( $order->orderstatus, 'cancelled', 'Order is marked as cancelled' );
814     isnt( $order->datecancellationprinted, undef, 'datecancellationprinted is set' );
815     is( $order->cancellationreason, $reason, 'cancellationreason is undef' );
816     is( Koha::Items->find($item->id), undef, 'The item is no longer present' );
817     is( ref(Koha::Biblios->find($biblio_id)), 'Koha::Biblio', 'The biblio is still present' );
818     @messages = @{ $order->object_messages };
819     is( $messages[0]->message, 'error_delbiblio_active_orders', 'Cannot delete biblio and it gets notified' );
820
821     # Scenario:
822     # * order with one item attached
823     # * there's a subscription on the biblio
824     # => order is cancelled
825     # => item in order is removed
826     # => biblio remains untouched
827     # => biblio delete error notified
828
829     $item      = $builder->build_sample_item;
830     $biblio_id = $item->biblionumber;
831     $order     = $builder->build_object(
832         {
833             class => 'Koha::Acquisition::Orders',
834             value => {
835                 orderstatus             => 'new',
836                 biblionumber            => $biblio_id,
837                 datecancellationprinted => undef,
838                 cancellationreason      => undef,
839             }
840         }
841     );
842     $order->add_item( $item->id );
843
844     # Add a subscription
845     $builder->build_object(
846         {
847             class => 'Koha::Subscriptions',
848             value => {
849                 biblionumber => $biblio_id,
850             }
851         }
852     );
853
854     $order->cancel({ reason => $reason, delete_biblio => 1 })
855           ->discard_changes;
856
857     is( $order->orderstatus, 'cancelled', 'Order is marked as cancelled' );
858     isnt( $order->datecancellationprinted, undef, 'datecancellationprinted is set' );
859     is( $order->cancellationreason, $reason, 'cancellationreason is undef' );
860     is( Koha::Items->find($item->id), undef, 'The item is no longer present' );
861     is( ref(Koha::Biblios->find($biblio_id)), 'Koha::Biblio', 'The biblio is still present' );
862     @messages = @{ $order->object_messages };
863     is( $messages[0]->message, 'error_delbiblio_subscriptions', 'Cannot delete biblio and it gets notified' );
864
865     # Scenario:
866     # * order with one item attached
867     # * delete_biblio is passed
868     # => order is cancelled
869     # => item in order is removed
870     # => biblio in order is removed
871
872     $item      = $builder->build_sample_item;
873     $biblio_id = $item->biblionumber;
874     $order     = $builder->build_object(
875         {
876             class => 'Koha::Acquisition::Orders',
877             value => {
878                 orderstatus             => 'new',
879                 biblionumber            => $item->biblionumber,
880                 datecancellationprinted => undef,
881                 cancellationreason      => undef,
882             }
883         }
884     );
885     $order->add_item( $item->id );
886
887     $order->cancel({ reason => $reason, delete_biblio => 1 })
888           ->discard_changes;
889
890     is( $order->orderstatus, 'cancelled', 'Order is not marked as cancelled' );
891     isnt( $order->datecancellationprinted, undef, 'datecancellationprinted is not undef' );
892     is( $order->cancellationreason, $reason, 'cancellationreason is set' );
893     is( Koha::Items->find($item->id), undef, 'The item is not present' );
894     is( Koha::Biblios->find($biblio_id), undef, 'The biblio is not present' );
895     @messages = @{ $order->object_messages };
896     is( scalar @messages, 0, 'No errors' );
897
898     # Scenario:
899     # * order made from a suggestion with same biblionumber
900     # => order is cancelled
901     # => suggestion status is changed to ACCEPTED
902
903     $item      = $builder->build_sample_item;
904     $biblio_id = $item->biblionumber;
905
906     # Add the suggestion
907     my $suggestion = $builder->build_object(
908         {
909             class => 'Koha::Suggestions',
910             value => {
911                 biblionumber => $biblio_id,
912                 suggesteddate => dt_from_string,
913                 STATUS => 'ORDERED',
914                 archived => 0,
915             }
916         }
917     );
918
919     $order = $builder->build_object(
920         {
921             class => 'Koha::Acquisition::Orders',
922             value => {
923                 orderstatus             => 'new',
924                 biblionumber            => $biblio_id,
925                 datecancellationprinted => undef,
926                 cancellationreason      => undef,
927             }
928         }
929     );
930
931     $order->cancel({ reason => $reason })
932           ->discard_changes;
933
934     $suggestion = Koha::Suggestions->find( $suggestion->id );
935
936     is( $order->orderstatus, 'cancelled', 'Order is marked as cancelled' );
937     is( $suggestion->STATUS, 'ACCEPTED', 'Suggestion status is correctly reverted after order is cancelled' );
938
939     # Scenario:
940     # * order with two items attached
941     # * one of the items is on loan
942     # => order is cancelled
943     # => item on loan is kept
944     # => the other item is removed
945     # => biblio remains untouched
946     # => biblio delete error notified
947     # => item delete error notified
948
949     $item_1    = $builder->build_sample_item;
950     $item_2    = $builder->build_sample_item({ biblionumber => $item_1->biblionumber });
951     my $item_3 = $builder->build_sample_item({ biblionumber => $item_1->biblionumber });
952     $biblio_id = $item_1->biblionumber;
953     $order     = $builder->build_object(
954         {
955             class => 'Koha::Acquisition::Orders',
956             value => {
957                 orderstatus             => 'new',
958                 biblionumber            => $biblio_id,
959                 datecancellationprinted => undef,
960                 cancellationreason      => undef,
961             }
962         }
963     );
964     $order->add_item( $item_1->id );
965     $order->add_item( $item_2->id );
966     $order->add_item( $item_3->id );
967
968     # Add a checkout so deleting the item fails because od 'book_on_loan'
969     C4::Circulation::AddIssue( $patron->unblessed, $item_2->barcode );
970     C4::Reserves::AddReserve(
971         {
972             branchcode     => $item_3->holdingbranch,
973             borrowernumber => $patron->borrowernumber,
974             biblionumber   => $biblio_id,
975             itemnumber     => $item_3->id,
976             found          => 'W',
977         }
978     );
979
980     $order->cancel({ reason => $reason, delete_biblio => 1 })
981           ->discard_changes;
982
983     is( $order->orderstatus, 'cancelled', 'Order is marked as cancelled' );
984     isnt( $order->datecancellationprinted, undef, 'datecancellationprinted is set' );
985     is( $order->cancellationreason, $reason, 'cancellationreason is undef' );
986     is( Koha::Items->find($item_1->id), undef, 'The item is no longer present' );
987     is( ref(Koha::Items->find($item_2->id)), 'Koha::Item', 'The on loan item is still present' );
988     is( ref(Koha::Biblios->find($biblio_id)), 'Koha::Biblio', 'The biblio is still present' );
989     @messages = @{ $order->object_messages };
990     is( $messages[0]->message, 'error_delitem', 'Cannot delete on loan item' );
991     is( $messages[0]->payload->{item}->id, $item_2->id, 'Cannot delete on loan item' );
992     is( $messages[0]->payload->{reason}, 'book_on_loan', 'Item on loan notified' );
993     is( $messages[1]->message, 'error_delitem', 'Cannot delete reserved and found item' );
994     is( $messages[1]->payload->{item}->id, $item_3->id, 'Cannot delete reserved and found item' );
995     is( $messages[1]->payload->{reason}, 'book_reserved', 'Item reserved notified' );
996     is( $messages[2]->message, 'error_delbiblio_items', 'Cannot delete on loan item' );
997     is( $messages[2]->payload->{biblio}->id, $biblio_id, 'The right biblio is attached' );
998
999     # Call ->store with biblionumber NULL (as ->cancel does)
1000     $item_1 = $builder->build_sample_item;
1001     $biblio_id = $item_1->biblionumber;
1002     $order= $builder->build_object({
1003         class => 'Koha::Acquisition::Orders',
1004         value => {
1005             orderstatus             => 'new',
1006             biblionumber            => $biblio_id,
1007             datecancellationprinted => undef,
1008             cancellationreason      => undef,
1009         }
1010     });
1011     my $columns = {
1012         biblionumber            => undef,
1013         cancellationreason      => $reason,
1014         datecancellationprinted => \'NOW()',
1015         orderstatus             => 'cancelled',
1016     };
1017     lives_ok { $order->set($columns)->store; } 'No croak on missing biblionumber when cancelling an order';
1018     throws_ok { $order->orderstatus('new')->store; } qr/Cannot insert order: Mandatory parameter biblionumber is missing/, 'Expected croak';
1019
1020     $schema->storage->txn_rollback;
1021 };