Bug 19919: Unit Tests
[koha-equinox.git] / t / db_dependent / Koha / Account / Lines.t
1 #!/usr/bin/perl
2
3 # Copyright 2018 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 => 9;
23 use Test::Exception;
24
25 use C4::Circulation qw/AddIssue AddReturn/;
26 use Koha::Account;
27 use Koha::Account::Lines;
28 use Koha::Account::Offsets;
29 use Koha::Items;
30
31 use t::lib::Mocks;
32 use t::lib::TestBuilder;
33
34 my $schema = Koha::Database->new->schema;
35 my $builder = t::lib::TestBuilder->new;
36
37 subtest 'patron() tests' => sub {
38
39     plan tests => 3;
40
41     $schema->storage->txn_begin;
42
43     my $library = $builder->build( { source => 'Branch' } );
44     my $patron = $builder->build( { source => 'Borrower' } );
45
46     my $line = Koha::Account::Line->new(
47     {
48         borrowernumber => $patron->{borrowernumber},
49         accounttype    => "OVERDUE",
50         status         => "RETURNED",
51         amount         => 10,
52         interface      => 'commandline',
53     })->store;
54
55     my $account_line_patron = $line->patron;
56     is( ref( $account_line_patron ), 'Koha::Patron', 'Koha::Account::Line->patron should return a Koha::Patron' );
57     is( $line->borrowernumber, $account_line_patron->borrowernumber, 'Koha::Account::Line->patron should return the correct borrower' );
58
59     $line->borrowernumber(undef)->store;
60     is( $line->patron, undef, 'Koha::Account::Line->patron should return undef if no patron linked' );
61
62     $schema->storage->txn_rollback;
63 };
64
65
66 subtest 'item() tests' => sub {
67
68     plan tests => 3;
69
70     $schema->storage->txn_begin;
71
72     my $library = $builder->build( { source => 'Branch' } );
73     my $biblioitem = $builder->build( { source => 'Biblioitem' } );
74     my $patron = $builder->build( { source => 'Borrower' } );
75     my $item = Koha::Item->new(
76     {
77         biblionumber     => $biblioitem->{biblionumber},
78         biblioitemnumber => $biblioitem->{biblioitemnumber},
79         homebranch       => $library->{branchcode},
80         holdingbranch    => $library->{branchcode},
81         barcode          => 'some_barcode_12',
82         itype            => 'BK',
83     })->store;
84
85     my $line = Koha::Account::Line->new(
86     {
87         borrowernumber => $patron->{borrowernumber},
88         itemnumber     => $item->itemnumber,
89         accounttype    => "OVERDUE",
90         status         => "RETURNED",
91         amount         => 10,
92         interface      => 'commandline',
93     })->store;
94
95     my $account_line_item = $line->item;
96     is( ref( $account_line_item ), 'Koha::Item', 'Koha::Account::Line->item should return a Koha::Item' );
97     is( $line->itemnumber, $account_line_item->itemnumber, 'Koha::Account::Line->item should return the correct item' );
98
99     $line->itemnumber(undef)->store;
100     is( $line->item, undef, 'Koha::Account::Line->item should return undef if no item linked' );
101
102     $schema->storage->txn_rollback;
103 };
104
105 subtest 'total_outstanding() tests' => sub {
106
107     plan tests => 5;
108
109     $schema->storage->txn_begin;
110
111     my $patron  = $builder->build_object({ class => 'Koha::Patrons' });
112
113     my $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
114     is( $lines->total_outstanding, 0, 'total_outstanding returns 0 if no lines (undef case)' );
115
116     my $debit_1 = Koha::Account::Line->new(
117         {   borrowernumber    => $patron->id,
118             accounttype       => "OVERDUE",
119             status            => "RETURNED",
120             amount            => 10,
121             amountoutstanding => 10,
122             interface         => 'commandline',
123         }
124     )->store;
125
126     my $debit_2 = Koha::Account::Line->new(
127         {   borrowernumber    => $patron->id,
128             accounttype       => "OVERDUE",
129             status            => "RETURNED",
130             amount            => 10,
131             amountoutstanding => 10,
132             interface         => 'commandline',
133         }
134     )->store;
135
136     $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
137     is( $lines->total_outstanding, 20, 'total_outstanding sums correctly' );
138
139     my $credit_1 = Koha::Account::Line->new(
140         {   borrowernumber    => $patron->id,
141             accounttype       => "OVERDUE",
142             status            => "RETURNED",
143             amount            => -10,
144             amountoutstanding => -10,
145             interface         => 'commandline',
146         }
147     )->store;
148
149     $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
150     is( $lines->total_outstanding, 10, 'total_outstanding sums correctly' );
151
152     my $credit_2 = Koha::Account::Line->new(
153         {   borrowernumber    => $patron->id,
154             accounttype       => "OVERDUE",
155             status            => "RETURNED",
156             amount            => -10,
157             amountoutstanding => -10,
158             interface         => 'commandline',
159         }
160     )->store;
161
162     $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
163     is( $lines->total_outstanding, 0, 'total_outstanding sums correctly' );
164
165     my $credit_3 = Koha::Account::Line->new(
166         {   borrowernumber    => $patron->id,
167             accounttype       => "OVERDUE",
168             status            => "RETURNED",
169             amount            => -100,
170             amountoutstanding => -100,
171             interface         => 'commandline',
172         }
173     )->store;
174
175     $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
176     is( $lines->total_outstanding, -100, 'total_outstanding sums correctly' );
177
178     $schema->storage->txn_rollback;
179 };
180
181 subtest 'is_credit() and is_debit() tests' => sub {
182
183     plan tests => 4;
184
185     $schema->storage->txn_begin;
186
187     my $patron  = $builder->build_object({ class => 'Koha::Patrons' });
188     my $account = $patron->account;
189
190     my $credit = $account->add_credit({ amount => 100, user_id => $patron->id, interface => 'commandline' });
191
192     ok( $credit->is_credit, 'is_credit detects credits' );
193     ok( !$credit->is_debit, 'is_debit detects credits' );
194
195     my $debit = Koha::Account::Line->new(
196     {
197         borrowernumber => $patron->id,
198         accounttype    => "OVERDUE",
199         status         => "RETURNED",
200         amount         => 10,
201         interface      => 'commandline',
202     })->store;
203
204     ok( !$debit->is_credit, 'is_credit detects debits' );
205     ok( $debit->is_debit, 'is_debit detects debits');
206
207     $schema->storage->txn_rollback;
208 };
209
210 subtest 'apply() tests' => sub {
211
212     plan tests => 24;
213
214     $schema->storage->txn_begin;
215
216     my $patron  = $builder->build_object( { class => 'Koha::Patrons' } );
217     my $account = $patron->account;
218
219     my $credit = $account->add_credit( { amount => 100, user_id => $patron->id, interface => 'commandline' } );
220
221     my $debit_1 = Koha::Account::Line->new(
222         {   borrowernumber    => $patron->id,
223             accounttype       => "OVERDUE",
224             status            => "RETURNED",
225             amount            => 10,
226             amountoutstanding => 10,
227             interface         => 'commandline',
228         }
229     )->store;
230
231     my $debit_2 = Koha::Account::Line->new(
232         {   borrowernumber    => $patron->id,
233             accounttype       => "OVERDUE",
234             status            => "RETURNED",
235             amount            => 100,
236             amountoutstanding => 100,
237             interface         => 'commandline',
238         }
239     )->store;
240
241     $credit->discard_changes;
242     $debit_1->discard_changes;
243
244     my $debits = Koha::Account::Lines->search({ accountlines_id => $debit_1->id });
245     my $remaining_credit = $credit->apply( { debits => $debits, offset_type => 'Manual Credit' } );
246     is( $remaining_credit * 1, 90, 'Remaining credit is correctly calculated' );
247     $credit->discard_changes;
248     is( $credit->amountoutstanding * -1, $remaining_credit, 'Remaining credit correctly stored' );
249
250     # re-read debit info
251     $debit_1->discard_changes;
252     is( $debit_1->amountoutstanding * 1, 0, 'Debit has been cancelled' );
253
254     my $offsets = Koha::Account::Offsets->search( { credit_id => $credit->id, debit_id => $debit_1->id } );
255     is( $offsets->count, 1, 'Only one offset is generated' );
256     my $THE_offset = $offsets->next;
257     is( $THE_offset->amount * 1, -10, 'Amount was calculated correctly (less than the available credit)' );
258     is( $THE_offset->type, 'Manual Credit', 'Passed type stored correctly' );
259
260     $debits = Koha::Account::Lines->search({ accountlines_id => $debit_2->id });
261     $remaining_credit = $credit->apply( { debits => $debits } );
262     is( $remaining_credit, 0, 'No remaining credit left' );
263     $credit->discard_changes;
264     is( $credit->amountoutstanding * 1, 0, 'No outstanding credit' );
265     $debit_2->discard_changes;
266     is( $debit_2->amountoutstanding * 1, 10, 'Outstanding amount decremented correctly' );
267
268     $offsets = Koha::Account::Offsets->search( { credit_id => $credit->id, debit_id => $debit_2->id } );
269     is( $offsets->count, 1, 'Only one offset is generated' );
270     $THE_offset = $offsets->next;
271     is( $THE_offset->amount * 1, -90, 'Amount was calculated correctly (less than the available credit)' );
272     is( $THE_offset->type, 'Credit Applied', 'Defaults to \'Credit Applied\' offset type' );
273
274     $debits = Koha::Account::Lines->search({ accountlines_id => $debit_1->id });
275     throws_ok
276         { $credit->apply({ debits => $debits }); }
277         'Koha::Exceptions::Account::NoAvailableCredit',
278         '->apply() can only be used with outstanding credits';
279
280     $debits = Koha::Account::Lines->search({ accountlines_id => $credit->id });
281     throws_ok
282         { $debit_1->apply({ debits => $debits }); }
283         'Koha::Exceptions::Account::IsNotCredit',
284         '->apply() can only be used with credits';
285
286     $debits = Koha::Account::Lines->search({ accountlines_id => $credit->id });
287     my $credit_3 = $account->add_credit({ amount => 1, interface => 'commandline' });
288     throws_ok
289         { $credit_3->apply({ debits => $debits }); }
290         'Koha::Exceptions::Account::IsNotDebit',
291         '->apply() can only be applied to credits';
292
293     my $credit_2 = $account->add_credit({ amount => 20, interface => 'commandline' });
294     my $debit_3  = Koha::Account::Line->new(
295         {   borrowernumber    => $patron->id,
296             accounttype       => "OVERDUE",
297             status            => "RETURNED",
298             amount            => 100,
299             amountoutstanding => 100,
300             interface         => 'commandline',
301         }
302     )->store;
303
304     $debits = Koha::Account::Lines->search({ accountlines_id => { -in => [ $debit_1->id, $debit_2->id, $debit_3->id, $credit->id ] } });
305     throws_ok {
306         $credit_2->apply( { debits => $debits, offset_type => 'Manual Credit' } ); }
307         'Koha::Exceptions::Account::IsNotDebit',
308         '->apply() rolls back if any of the passed lines is not a debit';
309
310     is( $debit_1->discard_changes->amountoutstanding * 1,   0, 'No changes to already cancelled debit' );
311     is( $debit_2->discard_changes->amountoutstanding * 1,  10, 'Debit cancelled' );
312     is( $debit_3->discard_changes->amountoutstanding * 1, 100, 'Outstanding amount correctly calculated' );
313     is( $credit_2->discard_changes->amountoutstanding * -1, 20, 'No changes made' );
314
315     $debits = Koha::Account::Lines->search({ accountlines_id => { -in => [ $debit_1->id, $debit_2->id, $debit_3->id ] } });
316     $remaining_credit = $credit_2->apply( { debits => $debits, offset_type => 'Manual Credit' } );
317
318     is( $debit_1->discard_changes->amountoutstanding * 1,  0, 'No changes to already cancelled debit' );
319     is( $debit_2->discard_changes->amountoutstanding * 1,  0, 'Debit cancelled' );
320     is( $debit_3->discard_changes->amountoutstanding * 1, 90, 'Outstanding amount correctly calculated' );
321     is( $credit_2->discard_changes->amountoutstanding * 1, 0, 'No remaining credit' );
322
323     $schema->storage->txn_rollback;
324 };
325
326 subtest 'Keep account info when related patron, staff or item is deleted' => sub {
327
328     plan tests => 3;
329
330     $schema->storage->txn_begin;
331
332     my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
333     my $staff = $builder->build_object( { class => 'Koha::Patrons' } );
334     my $item = $builder->build_object({ class => 'Koha::Items' });
335     my $issue = $builder->build_object(
336         {
337             class => 'Koha::Checkouts',
338             value => { itemnumber => $item->itemnumber }
339         }
340     );
341     my $line = Koha::Account::Line->new(
342     {
343         borrowernumber => $patron->borrowernumber,
344         manager_id     => $staff->borrowernumber,
345         itemnumber     => $item->itemnumber,
346         accounttype    => "OVERDUE",
347         status         => "RETURNED",
348         amount         => 10,
349         interface      => 'commandline',
350     })->store;
351
352     $issue->delete;
353     $item->delete;
354     $line = $line->get_from_storage;
355     is( $line->itemnumber, undef, "The account line should not be deleted when the related item is delete");
356
357     $staff->delete;
358     $line = $line->get_from_storage;
359     is( $line->manager_id, undef, "The account line should not be deleted when the related staff is delete");
360
361     $patron->delete;
362     $line = $line->get_from_storage;
363     is( $line->borrowernumber, undef, "The account line should not be deleted when the related patron is delete");
364
365     $schema->storage->txn_rollback;
366 };
367
368 subtest 'adjust() tests' => sub {
369
370     plan tests => 29;
371
372     $schema->storage->txn_begin;
373
374     # count logs before any actions
375     my $action_logs = $schema->resultset('ActionLog')->search()->count;
376
377     # Disable logs
378     t::lib::Mocks::mock_preference( 'FinesLog', 0 );
379
380     my $patron  = $builder->build_object( { class => 'Koha::Patrons' } );
381     my $account = $patron->account;
382
383     my $debit_1 = Koha::Account::Line->new(
384         {   borrowernumber    => $patron->id,
385             accounttype       => "OVERDUE",
386             status            => "RETURNED",
387             amount            => 10,
388             amountoutstanding => 10,
389             interface         => 'commandline',
390         }
391     )->store;
392
393     my $debit_2 = Koha::Account::Line->new(
394         {   borrowernumber    => $patron->id,
395             accounttype       => "OVERDUE",
396             status            => "UNRETURNED",
397             amount            => 100,
398             amountoutstanding => 100,
399             interface         => 'commandline'
400         }
401     )->store;
402
403     my $credit = $account->add_credit( { amount => 40, user_id => $patron->id, interface => 'commandline' } );
404
405     throws_ok { $debit_1->adjust( { amount => 50, type => 'bad', interface => 'commandline' } ) }
406     qr/Update type not recognised/, 'Exception thrown for unrecognised type';
407
408     throws_ok { $debit_1->adjust( { amount => 50, type => 'overdue_update', interface => 'commandline' } ) }
409     qr/Update type not allowed on this accounttype/,
410       'Exception thrown for type conflict';
411
412     # Increment an unpaid fine
413     $debit_2->adjust( { amount => 150, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
414
415     is( $debit_2->amount * 1, 150, 'Fine amount was updated in full' );
416     is( $debit_2->amountoutstanding * 1, 150, 'Fine amountoutstanding was update in full' );
417     isnt( $debit_2->date, undef, 'Date has been set' );
418
419     my $offsets = Koha::Account::Offsets->search( { debit_id => $debit_2->id } );
420     is( $offsets->count, 1, 'An offset is generated for the increment' );
421     my $THIS_offset = $offsets->next;
422     is( $THIS_offset->amount * 1, 50, 'Amount was calculated correctly (increment by 50)' );
423     is( $THIS_offset->type, 'OVERDUE_INCREASE', 'Adjust type stored correctly' );
424
425     is( $schema->resultset('ActionLog')->count(), $action_logs + 0, 'No log was added' );
426
427     # Update fine to partially paid
428     my $debits = Koha::Account::Lines->search({ accountlines_id => $debit_2->id });
429     $credit->apply( { debits => $debits, offset_type => 'Manual Credit' } );
430
431     $debit_2->discard_changes;
432     is( $debit_2->amount * 1, 150, 'Fine amount unaffected by partial payment' );
433     is( $debit_2->amountoutstanding * 1, 110, 'Fine amountoutstanding updated by partial payment' );
434
435     # Enable logs
436     t::lib::Mocks::mock_preference( 'FinesLog', 1 );
437
438     # Increment the partially paid fine
439     $debit_2->adjust( { amount => 160, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
440
441     is( $debit_2->amount * 1, 160, 'Fine amount was updated in full' );
442     is( $debit_2->amountoutstanding * 1, 120, 'Fine amountoutstanding was updated by difference' );
443
444     $offsets = Koha::Account::Offsets->search( { debit_id => $debit_2->id } );
445     is( $offsets->count, 3, 'An offset is generated for the increment' );
446     $THIS_offset = $offsets->last;
447     is( $THIS_offset->amount * 1, 10, 'Amount was calculated correctly (increment by 10)' );
448     is( $THIS_offset->type, 'OVERDUE_INCREASE', 'Adjust type stored correctly' );
449
450     is( $schema->resultset('ActionLog')->count(), $action_logs + 1, 'Log was added' );
451
452     # Decrement the partially paid fine, less than what was paid
453     $debit_2->adjust( { amount => 50, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
454
455     is( $debit_2->amount * 1, 50, 'Fine amount was updated in full' );
456     is( $debit_2->amountoutstanding * 1, 10, 'Fine amountoutstanding was updated by difference' );
457
458     $offsets = Koha::Account::Offsets->search( { debit_id => $debit_2->id } );
459     is( $offsets->count, 4, 'An offset is generated for the decrement' );
460     $THIS_offset = $offsets->last;
461     is( $THIS_offset->amount * 1, -110, 'Amount was calculated correctly (decrement by 110)' );
462     is( $THIS_offset->type, 'OVERDUE_DECREASE', 'Adjust type stored correctly' );
463
464     # Decrement the partially paid fine, more than what was paid
465     $debit_2->adjust( { amount => 30, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
466     is( $debit_2->amount * 1, 30, 'Fine amount was updated in full' );
467     is( $debit_2->amountoutstanding * 1, 0, 'Fine amountoutstanding was zeroed (payment was 40)' );
468
469     $offsets = Koha::Account::Offsets->search( { debit_id => $debit_2->id } );
470     is( $offsets->count, 5, 'An offset is generated for the decrement' );
471     $THIS_offset = $offsets->last;
472     is( $THIS_offset->amount * 1, -20, 'Amount was calculated correctly (decrement by 20)' );
473     is( $THIS_offset->type, 'OVERDUE_DECREASE', 'Adjust type stored correctly' );
474
475     my $overpayment_refund = $account->lines->last;
476     is( $overpayment_refund->amount * 1, -10, 'A new credit has been added' );
477     is( $overpayment_refund->description, 'Overpayment refund', 'Credit generated with the expected description' );
478
479     $schema->storage->txn_rollback;
480 };
481
482 subtest 'checkout() tests' => sub {
483     plan tests => 6;
484
485     $schema->storage->txn_begin;
486
487     my $library = $builder->build_object( { class => 'Koha::Libraries' } );
488     my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
489     my $item = $builder->build_sample_item;
490     my $account = $patron->account;
491
492     t::lib::Mocks::mock_userenv({ branchcode => $library->branchcode });
493     my $checkout = AddIssue( $patron->unblessed, $item->barcode );
494
495     my $line = $account->add_debit({
496         amount    => 10,
497         interface => 'commandline',
498         item_id   => $item->itemnumber,
499         issue_id  => $checkout->issue_id,
500         type      => 'overdue',
501     });
502
503     my $line_checkout = $line->checkout;
504     is( ref($line_checkout), 'Koha::Checkout', 'Result type is correct' );
505     is( $line_checkout->issue_id, $checkout->issue_id, 'Koha::Account::Line->checkout should return the correct checkout');
506
507     my ( $returned, undef, $old_checkout) = C4::Circulation::AddReturn( $item->barcode, $library->branchcode );
508     is( $returned, 1, 'The item should have been returned' );
509
510     $line = $line->get_from_storage;
511     my $old_line_checkout = $line->checkout;
512     is( ref($old_line_checkout), 'Koha::Old::Checkout', 'Result type is correct' );
513     is( $old_line_checkout->issue_id, $old_checkout->issue_id, 'Koha::Account::Line->checkout should return the correct old_checkout' );
514
515     $line->issue_id(undef)->store;
516     is( $line->checkout, undef, 'Koha::Account::Line->checkout should return undef if no checkout linked' );
517
518     $schema->storage->txn_rollback;
519 };
520
521 subtest "void() tests" => sub {
522
523     plan tests => 16;
524
525     $schema->storage->txn_begin;
526
527     # Create a borrower
528     my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
529     my $branchcode   = $builder->build({ source => 'Branch' })->{ branchcode };
530
531     my $borrower = Koha::Patron->new( {
532         cardnumber => 'dariahall',
533         surname => 'Hall',
534         firstname => 'Daria',
535     } );
536     $borrower->categorycode( $categorycode );
537     $borrower->branchcode( $branchcode );
538     $borrower->store;
539
540     my $account = Koha::Account->new({ patron_id => $borrower->id });
541
542     my $line1 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amount => 10, amountoutstanding => 10, interface => 'commandline' })->store();
543     my $line2 = Koha::Account::Line->new({ borrowernumber => $borrower->borrowernumber, amount => 20, amountoutstanding => 20, interface => 'commandline' })->store();
544
545     is( $account->balance(), 30, "Account balance is 30" );
546     is( $line1->amountoutstanding, 10, 'First fee has amount outstanding of 10' );
547     is( $line2->amountoutstanding, 20, 'Second fee has amount outstanding of 20' );
548
549     my $id = $account->pay(
550         {
551             lines  => [$line1, $line2],
552             amount => 30,
553         }
554     );
555
556     my $account_payment = Koha::Account::Lines->find( $id );
557
558     is( $account->balance(), 0, "Account balance is 0" );
559
560     $line1->_result->discard_changes();
561     $line2->_result->discard_changes();
562     is( $line1->amountoutstanding+0, 0, 'First fee has amount outstanding of 0' );
563     is( $line2->amountoutstanding+0, 0, 'Second fee has amount outstanding of 0' );
564
565     my $ret = $account_payment->void();
566
567     is( ref($ret), 'Koha::Account::Line', 'Void returns the account line' );
568     is( $account->balance(), 30, "Account balance is again 30" );
569
570     $account_payment->_result->discard_changes();
571     $line1->_result->discard_changes();
572     $line2->_result->discard_changes();
573
574     is( $account_payment->accounttype, 'Pay', 'Voided payment accounttype is still Pay' );
575     is( $account_payment->status, 'VOID', 'Voided payment status is VOID' );
576     is( $account_payment->amount+0, 0, 'Voided payment amount is 0' );
577     is( $account_payment->amountoutstanding+0, 0, 'Voided payment amount outstanding is 0' );
578
579     is( $line1->amountoutstanding+0, 10, 'First fee again has amount outstanding of 10' );
580     is( $line2->amountoutstanding+0, 20, 'Second fee again has amount outstanding of 20' );
581
582     # Accountlines that are not credits should be un-voidable
583     my $line1_pre = $line1->unblessed();
584     $ret = $line1->void();
585     $line1->_result->discard_changes();
586     my $line1_post = $line1->unblessed();
587     is( $ret, undef, 'Attempted void on non-credit returns undef' );
588     is_deeply( $line1_pre, $line1_post, 'Non-credit account line cannot be voided' );
589
590     $schema->storage->txn_rollback;
591 };
592
593 1;