View | Details | Raw Unified | Return to bug 23355
Collapse All | Expand All

(-)a/t/db_dependent/Koha/Account/Line.t (+892 lines)
Line 0 Link Here
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 => 8;
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
        debit_type_code    => "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
subtest 'item() tests' => sub {
66
67
    plan tests => 3;
68
69
    $schema->storage->txn_begin;
70
71
    my $library = $builder->build( { source => 'Branch' } );
72
    my $biblioitem = $builder->build( { source => 'Biblioitem' } );
73
    my $patron = $builder->build( { source => 'Borrower' } );
74
    my $item = Koha::Item->new(
75
    {
76
        biblionumber     => $biblioitem->{biblionumber},
77
        biblioitemnumber => $biblioitem->{biblioitemnumber},
78
        homebranch       => $library->{branchcode},
79
        holdingbranch    => $library->{branchcode},
80
        barcode          => 'some_barcode_12',
81
        itype            => 'BK',
82
    })->store;
83
84
    my $line = Koha::Account::Line->new(
85
    {
86
        borrowernumber => $patron->{borrowernumber},
87
        itemnumber     => $item->itemnumber,
88
        debit_type_code    => "OVERDUE",
89
        status         => "RETURNED",
90
        amount         => 10,
91
        interface      => 'commandline',
92
    })->store;
93
94
    my $account_line_item = $line->item;
95
    is( ref( $account_line_item ), 'Koha::Item', 'Koha::Account::Line->item should return a Koha::Item' );
96
    is( $line->itemnumber, $account_line_item->itemnumber, 'Koha::Account::Line->item should return the correct item' );
97
98
    $line->itemnumber(undef)->store;
99
    is( $line->item, undef, 'Koha::Account::Line->item should return undef if no item linked' );
100
101
    $schema->storage->txn_rollback;
102
};
103
104
subtest 'is_credit() and is_debit() tests' => sub {
105
106
    plan tests => 4;
107
108
    $schema->storage->txn_begin;
109
110
    my $patron  = $builder->build_object({ class => 'Koha::Patrons' });
111
    my $account = $patron->account;
112
113
    my $credit = $account->add_credit({ amount => 100, user_id => $patron->id, interface => 'commandline' });
114
115
    ok( $credit->is_credit, 'is_credit detects credits' );
116
    ok( !$credit->is_debit, 'is_debit detects credits' );
117
118
    my $debit = Koha::Account::Line->new(
119
    {
120
        borrowernumber => $patron->id,
121
        debit_type_code    => "OVERDUE",
122
        status         => "RETURNED",
123
        amount         => 10,
124
        interface      => 'commandline',
125
    })->store;
126
127
    ok( !$debit->is_credit, 'is_credit detects debits' );
128
    ok( $debit->is_debit, 'is_debit detects debits');
129
130
    $schema->storage->txn_rollback;
131
};
132
133
subtest 'apply() tests' => sub {
134
135
    plan tests => 24;
136
137
    $schema->storage->txn_begin;
138
139
    my $patron  = $builder->build_object( { class => 'Koha::Patrons' } );
140
    my $account = $patron->account;
141
142
    my $credit = $account->add_credit( { amount => 100, user_id => $patron->id, interface => 'commandline' } );
143
144
    my $debit_1 = Koha::Account::Line->new(
145
        {   borrowernumber    => $patron->id,
146
            debit_type_code       => "OVERDUE",
147
            status            => "RETURNED",
148
            amount            => 10,
149
            amountoutstanding => 10,
150
            interface         => 'commandline',
151
        }
152
    )->store;
153
154
    my $debit_2 = Koha::Account::Line->new(
155
        {   borrowernumber    => $patron->id,
156
            debit_type_code       => "OVERDUE",
157
            status            => "RETURNED",
158
            amount            => 100,
159
            amountoutstanding => 100,
160
            interface         => 'commandline',
161
        }
162
    )->store;
163
164
    $credit->discard_changes;
165
    $debit_1->discard_changes;
166
167
    my $debits = Koha::Account::Lines->search({ accountlines_id => $debit_1->id });
168
    my $remaining_credit = $credit->apply( { debits => [ $debits->as_list ], offset_type => 'Manual Credit' } );
169
    is( $remaining_credit * 1, 90, 'Remaining credit is correctly calculated' );
170
    $credit->discard_changes;
171
    is( $credit->amountoutstanding * -1, $remaining_credit, 'Remaining credit correctly stored' );
172
173
    # re-read debit info
174
    $debit_1->discard_changes;
175
    is( $debit_1->amountoutstanding * 1, 0, 'Debit has been cancelled' );
176
177
    my $offsets = Koha::Account::Offsets->search( { credit_id => $credit->id, debit_id => $debit_1->id } );
178
    is( $offsets->count, 1, 'Only one offset is generated' );
179
    my $THE_offset = $offsets->next;
180
    is( $THE_offset->amount * 1, -10, 'Amount was calculated correctly (less than the available credit)' );
181
    is( $THE_offset->type, 'Manual Credit', 'Passed type stored correctly' );
182
183
    $debits = Koha::Account::Lines->search({ accountlines_id => $debit_2->id });
184
    $remaining_credit = $credit->apply( { debits => [ $debits->as_list ] } );
185
    is( $remaining_credit, 0, 'No remaining credit left' );
186
    $credit->discard_changes;
187
    is( $credit->amountoutstanding * 1, 0, 'No outstanding credit' );
188
    $debit_2->discard_changes;
189
    is( $debit_2->amountoutstanding * 1, 10, 'Outstanding amount decremented correctly' );
190
191
    $offsets = Koha::Account::Offsets->search( { credit_id => $credit->id, debit_id => $debit_2->id } );
192
    is( $offsets->count, 1, 'Only one offset is generated' );
193
    $THE_offset = $offsets->next;
194
    is( $THE_offset->amount * 1, -90, 'Amount was calculated correctly (less than the available credit)' );
195
    is( $THE_offset->type, 'Credit Applied', 'Defaults to \'Credit Applied\' offset type' );
196
197
    $debits = Koha::Account::Lines->search({ accountlines_id => $debit_1->id });
198
    throws_ok
199
        { $credit->apply({ debits => [ $debits->as_list ] }); }
200
        'Koha::Exceptions::Account::NoAvailableCredit',
201
        '->apply() can only be used with outstanding credits';
202
203
    $debits = Koha::Account::Lines->search({ accountlines_id => $credit->id });
204
    throws_ok
205
        { $debit_1->apply({ debits => [ $debits->as_list ] }); }
206
        'Koha::Exceptions::Account::IsNotCredit',
207
        '->apply() can only be used with credits';
208
209
    $debits = Koha::Account::Lines->search({ accountlines_id => $credit->id });
210
    my $credit_3 = $account->add_credit({ amount => 1, interface => 'commandline' });
211
    throws_ok
212
        { $credit_3->apply({ debits => [ $debits->as_list ] }); }
213
        'Koha::Exceptions::Account::IsNotDebit',
214
        '->apply() can only be applied to credits';
215
216
    my $credit_2 = $account->add_credit({ amount => 20, interface => 'commandline' });
217
    my $debit_3  = Koha::Account::Line->new(
218
        {   borrowernumber    => $patron->id,
219
            debit_type_code       => "OVERDUE",
220
            status            => "RETURNED",
221
            amount            => 100,
222
            amountoutstanding => 100,
223
            interface         => 'commandline',
224
        }
225
    )->store;
226
227
    $debits = Koha::Account::Lines->search({ accountlines_id => { -in => [ $debit_1->id, $debit_2->id, $debit_3->id, $credit->id ] } });
228
    throws_ok {
229
        $credit_2->apply( { debits => [ $debits->as_list ], offset_type => 'Manual Credit' } ); }
230
        'Koha::Exceptions::Account::IsNotDebit',
231
        '->apply() rolls back if any of the passed lines is not a debit';
232
233
    is( $debit_1->discard_changes->amountoutstanding * 1,   0, 'No changes to already cancelled debit' );
234
    is( $debit_2->discard_changes->amountoutstanding * 1,  10, 'Debit cancelled' );
235
    is( $debit_3->discard_changes->amountoutstanding * 1, 100, 'Outstanding amount correctly calculated' );
236
    is( $credit_2->discard_changes->amountoutstanding * -1, 20, 'No changes made' );
237
238
    $debits = Koha::Account::Lines->search({ accountlines_id => { -in => [ $debit_1->id, $debit_2->id, $debit_3->id ] } });
239
    $remaining_credit = $credit_2->apply( { debits => [ $debits->as_list ], offset_type => 'Manual Credit' } );
240
241
    is( $debit_1->discard_changes->amountoutstanding * 1,  0, 'No changes to already cancelled debit' );
242
    is( $debit_2->discard_changes->amountoutstanding * 1,  0, 'Debit cancelled' );
243
    is( $debit_3->discard_changes->amountoutstanding * 1, 90, 'Outstanding amount correctly calculated' );
244
    is( $credit_2->discard_changes->amountoutstanding * 1, 0, 'No remaining credit' );
245
246
    $schema->storage->txn_rollback;
247
};
248
249
subtest 'Keep account info when related patron, staff, item or cash_register is deleted' => sub {
250
251
    plan tests => 4;
252
253
    $schema->storage->txn_begin;
254
255
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
256
    my $staff = $builder->build_object( { class => 'Koha::Patrons' } );
257
    my $item = $builder->build_object({ class => 'Koha::Items' });
258
    my $issue = $builder->build_object(
259
        {
260
            class => 'Koha::Checkouts',
261
            value => { itemnumber => $item->itemnumber }
262
        }
263
    );
264
    my $register = $builder->build_object({ class => 'Koha::Cash::Registers' });
265
266
    my $line = Koha::Account::Line->new(
267
    {
268
        borrowernumber => $patron->borrowernumber,
269
        manager_id     => $staff->borrowernumber,
270
        itemnumber     => $item->itemnumber,
271
        debit_type_code    => "OVERDUE",
272
        status         => "RETURNED",
273
        amount         => 10,
274
        interface      => 'commandline',
275
        register_id    => $register->id
276
    })->store;
277
278
    $issue->delete;
279
    $item->delete;
280
    $line = $line->get_from_storage;
281
    is( $line->itemnumber, undef, "The account line should not be deleted when the related item is delete");
282
283
    $staff->delete;
284
    $line = $line->get_from_storage;
285
    is( $line->manager_id, undef, "The account line should not be deleted when the related staff is delete");
286
287
    $patron->delete;
288
    $line = $line->get_from_storage;
289
    is( $line->borrowernumber, undef, "The account line should not be deleted when the related patron is delete");
290
291
    $register->delete;
292
    $line = $line->get_from_storage;
293
    is( $line->register_id, undef, "The account line should not be deleted when the related cash register is delete");
294
295
    $schema->storage->txn_rollback;
296
};
297
298
subtest 'adjust() tests' => sub {
299
300
    plan tests => 29;
301
302
    $schema->storage->txn_begin;
303
304
    # count logs before any actions
305
    my $action_logs = $schema->resultset('ActionLog')->search()->count;
306
307
    # Disable logs
308
    t::lib::Mocks::mock_preference( 'FinesLog', 0 );
309
310
    my $patron  = $builder->build_object( { class => 'Koha::Patrons' } );
311
    my $account = $patron->account;
312
313
    my $debit_1 = Koha::Account::Line->new(
314
        {   borrowernumber    => $patron->id,
315
            debit_type_code       => "OVERDUE",
316
            status            => "RETURNED",
317
            amount            => 10,
318
            amountoutstanding => 10,
319
            interface         => 'commandline',
320
        }
321
    )->store;
322
323
    my $debit_2 = Koha::Account::Line->new(
324
        {   borrowernumber    => $patron->id,
325
            debit_type_code       => "OVERDUE",
326
            status            => "UNRETURNED",
327
            amount            => 100,
328
            amountoutstanding => 100,
329
            interface         => 'commandline'
330
        }
331
    )->store;
332
333
    my $credit = $account->add_credit( { amount => 40, user_id => $patron->id, interface => 'commandline' } );
334
335
    throws_ok { $debit_1->adjust( { amount => 50, type => 'bad', interface => 'commandline' } ) }
336
    qr/Update type not recognised/, 'Exception thrown for unrecognised type';
337
338
    throws_ok { $debit_1->adjust( { amount => 50, type => 'overdue_update', interface => 'commandline' } ) }
339
    qr/Update type not allowed on this debit_type/,
340
      'Exception thrown for type conflict';
341
342
    # Increment an unpaid fine
343
    $debit_2->adjust( { amount => 150, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
344
345
    is( $debit_2->amount * 1, 150, 'Fine amount was updated in full' );
346
    is( $debit_2->amountoutstanding * 1, 150, 'Fine amountoutstanding was update in full' );
347
    isnt( $debit_2->date, undef, 'Date has been set' );
348
349
    my $offsets = Koha::Account::Offsets->search( { debit_id => $debit_2->id } );
350
    is( $offsets->count, 1, 'An offset is generated for the increment' );
351
    my $THIS_offset = $offsets->next;
352
    is( $THIS_offset->amount * 1, 50, 'Amount was calculated correctly (increment by 50)' );
353
    is( $THIS_offset->type, 'OVERDUE_INCREASE', 'Adjust type stored correctly' );
354
355
    is( $schema->resultset('ActionLog')->count(), $action_logs + 0, 'No log was added' );
356
357
    # Update fine to partially paid
358
    my $debits = Koha::Account::Lines->search({ accountlines_id => $debit_2->id });
359
    $credit->apply( { debits => [ $debits->as_list ], offset_type => 'Manual Credit' } );
360
361
    $debit_2->discard_changes;
362
    is( $debit_2->amount * 1, 150, 'Fine amount unaffected by partial payment' );
363
    is( $debit_2->amountoutstanding * 1, 110, 'Fine amountoutstanding updated by partial payment' );
364
365
    # Enable logs
366
    t::lib::Mocks::mock_preference( 'FinesLog', 1 );
367
368
    # Increment the partially paid fine
369
    $debit_2->adjust( { amount => 160, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
370
371
    is( $debit_2->amount * 1, 160, 'Fine amount was updated in full' );
372
    is( $debit_2->amountoutstanding * 1, 120, 'Fine amountoutstanding was updated by difference' );
373
374
    $offsets = Koha::Account::Offsets->search( { debit_id => $debit_2->id } );
375
    is( $offsets->count, 3, 'An offset is generated for the increment' );
376
    $THIS_offset = $offsets->last;
377
    is( $THIS_offset->amount * 1, 10, 'Amount was calculated correctly (increment by 10)' );
378
    is( $THIS_offset->type, 'OVERDUE_INCREASE', 'Adjust type stored correctly' );
379
380
    is( $schema->resultset('ActionLog')->count(), $action_logs + 1, 'Log was added' );
381
382
    # Decrement the partially paid fine, less than what was paid
383
    $debit_2->adjust( { amount => 50, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
384
385
    is( $debit_2->amount * 1, 50, 'Fine amount was updated in full' );
386
    is( $debit_2->amountoutstanding * 1, 10, 'Fine amountoutstanding was updated by difference' );
387
388
    $offsets = Koha::Account::Offsets->search( { debit_id => $debit_2->id } );
389
    is( $offsets->count, 4, 'An offset is generated for the decrement' );
390
    $THIS_offset = $offsets->last;
391
    is( $THIS_offset->amount * 1, -110, 'Amount was calculated correctly (decrement by 110)' );
392
    is( $THIS_offset->type, 'OVERDUE_DECREASE', 'Adjust type stored correctly' );
393
394
    # Decrement the partially paid fine, more than what was paid
395
    $debit_2->adjust( { amount => 30, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
396
    is( $debit_2->amount * 1, 30, 'Fine amount was updated in full' );
397
    is( $debit_2->amountoutstanding * 1, 0, 'Fine amountoutstanding was zeroed (payment was 40)' );
398
399
    $offsets = Koha::Account::Offsets->search( { debit_id => $debit_2->id } );
400
    is( $offsets->count, 5, 'An offset is generated for the decrement' );
401
    $THIS_offset = $offsets->last;
402
    is( $THIS_offset->amount * 1, -20, 'Amount was calculated correctly (decrement by 20)' );
403
    is( $THIS_offset->type, 'OVERDUE_DECREASE', 'Adjust type stored correctly' );
404
405
    my $overpayment_refund = $account->lines->last;
406
    is( $overpayment_refund->amount * 1, -10, 'A new credit has been added' );
407
    is( $overpayment_refund->description, 'Overpayment refund', 'Credit generated with the expected description' );
408
409
    $schema->storage->txn_rollback;
410
};
411
412
subtest 'checkout() tests' => sub {
413
    plan tests => 6;
414
415
    $schema->storage->txn_begin;
416
417
    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
418
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
419
    my $item = $builder->build_sample_item;
420
    my $account = $patron->account;
421
422
    t::lib::Mocks::mock_userenv({ branchcode => $library->branchcode });
423
    my $checkout = AddIssue( $patron->unblessed, $item->barcode );
424
425
    my $line = $account->add_debit({
426
        amount    => 10,
427
        interface => 'commandline',
428
        item_id   => $item->itemnumber,
429
        issue_id  => $checkout->issue_id,
430
        type      => 'OVERDUE',
431
    });
432
433
    my $line_checkout = $line->checkout;
434
    is( ref($line_checkout), 'Koha::Checkout', 'Result type is correct' );
435
    is( $line_checkout->issue_id, $checkout->issue_id, 'Koha::Account::Line->checkout should return the correct checkout');
436
437
    my ( $returned, undef, $old_checkout) = C4::Circulation::AddReturn( $item->barcode, $library->branchcode );
438
    is( $returned, 1, 'The item should have been returned' );
439
440
    $line = $line->get_from_storage;
441
    my $old_line_checkout = $line->checkout;
442
    is( ref($old_line_checkout), 'Koha::Old::Checkout', 'Result type is correct' );
443
    is( $old_line_checkout->issue_id, $old_checkout->issue_id, 'Koha::Account::Line->checkout should return the correct old_checkout' );
444
445
    $line->issue_id(undef)->store;
446
    is( $line->checkout, undef, 'Koha::Account::Line->checkout should return undef if no checkout linked' );
447
448
    $schema->storage->txn_rollback;
449
};
450
451
subtest 'credits() and debits() tests' => sub {
452
    plan tests => 10;
453
454
    $schema->storage->txn_begin;
455
456
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
457
    my $account = $patron->account;
458
459
    my $debit1 = $account->add_debit({
460
        amount    => 8,
461
        interface => 'commandline',
462
        type      => 'ACCOUNT',
463
    });
464
    my $debit2 = $account->add_debit({
465
        amount    => 12,
466
        interface => 'commandline',
467
        type      => 'ACCOUNT',
468
    });
469
    my $credit1 = $account->add_credit({
470
        amount    => 5,
471
        interface => 'commandline',
472
        type      => 'CREDIT',
473
    });
474
    my $credit2 = $account->add_credit({
475
        amount    => 10,
476
        interface => 'commandline',
477
        type      => 'CREDIT',
478
    });
479
480
    $credit1->apply({ debits => [ $debit1 ] });
481
    $credit2->apply({ debits => [ $debit1, $debit2 ] });
482
483
    my $credits = $debit1->credits;
484
    is($credits->count, 2, '2 Credits applied to debit 1');
485
    my $credit = $credits->next;
486
    is($credit->amount + 0, -5, 'Correct first credit');
487
    $credit = $credits->next;
488
    is($credit->amount + 0, -10, 'Correct second credit');
489
490
    $credits = $debit2->credits;
491
    is($credits->count, 1, '1 Credits applied to debit 2');
492
    $credit = $credits->next;
493
    is($credit->amount + 0, -10, 'Correct first credit');
494
495
    my $debits = $credit1->debits;
496
    is($debits->count, 1, 'Credit 1 applied to 1 debit');
497
    my $debit = $debits->next;
498
    is($debit->amount + 0, 8, 'Correct first debit');
499
500
    $debits = $credit2->debits;
501
    is($debits->count, 2, 'Credit 2 applied to 2 debits');
502
    $debit = $debits->next;
503
    is($debit->amount + 0, 8, 'Correct first debit');
504
    $debit = $debits->next;
505
    is($debit->amount + 0, 12, 'Correct second debit');
506
507
    $schema->storage->txn_rollback;
508
};
509
510
subtest "void() tests" => sub {
511
512
    plan tests => 16;
513
514
    $schema->storage->txn_begin;
515
516
    # Create a borrower
517
    my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
518
    my $branchcode   = $builder->build({ source => 'Branch' })->{ branchcode };
519
520
    my $borrower = Koha::Patron->new( {
521
        cardnumber => 'dariahall',
522
        surname => 'Hall',
523
        firstname => 'Daria',
524
    } );
525
    $borrower->categorycode( $categorycode );
526
    $borrower->branchcode( $branchcode );
527
    $borrower->store;
528
529
    my $account = Koha::Account->new({ patron_id => $borrower->id });
530
531
    my $line1 = Koha::Account::Line->new(
532
        {
533
            borrowernumber    => $borrower->borrowernumber,
534
            amount            => 10,
535
            amountoutstanding => 10,
536
            interface         => 'commandline',
537
            debit_type_code   => 'OVERDUE'
538
        }
539
    )->store();
540
    my $line2 = Koha::Account::Line->new(
541
        {
542
            borrowernumber    => $borrower->borrowernumber,
543
            amount            => 20,
544
            amountoutstanding => 20,
545
            interface         => 'commandline',
546
            debit_type_code   => 'OVERDUE'
547
        }
548
    )->store();
549
550
    is( $account->balance(), 30, "Account balance is 30" );
551
    is( $line1->amountoutstanding, 10, 'First fee has amount outstanding of 10' );
552
    is( $line2->amountoutstanding, 20, 'Second fee has amount outstanding of 20' );
553
554
    my $id = $account->pay(
555
        {
556
            lines  => [$line1, $line2],
557
            amount => 30,
558
        }
559
    );
560
561
    my $account_payment = Koha::Account::Lines->find( $id );
562
563
    is( $account->balance(), 0, "Account balance is 0" );
564
565
    $line1->_result->discard_changes();
566
    $line2->_result->discard_changes();
567
    is( $line1->amountoutstanding+0, 0, 'First fee has amount outstanding of 0' );
568
    is( $line2->amountoutstanding+0, 0, 'Second fee has amount outstanding of 0' );
569
570
    my $ret = $account_payment->void();
571
572
    is( ref($ret), 'Koha::Account::Line', 'Void returns the account line' );
573
    is( $account->balance(), 30, "Account balance is again 30" );
574
575
    $account_payment->_result->discard_changes();
576
    $line1->_result->discard_changes();
577
    $line2->_result->discard_changes();
578
579
    is( $account_payment->credit_type_code, 'PAYMENT', 'Voided payment credit_type_code is still PAYMENT' );
580
    is( $account_payment->status, 'VOID', 'Voided payment status is VOID' );
581
    is( $account_payment->amount+0, 0, 'Voided payment amount is 0' );
582
    is( $account_payment->amountoutstanding+0, 0, 'Voided payment amount outstanding is 0' );
583
584
    is( $line1->amountoutstanding+0, 10, 'First fee again has amount outstanding of 10' );
585
    is( $line2->amountoutstanding+0, 20, 'Second fee again has amount outstanding of 20' );
586
587
    # Accountlines that are not credits should be un-voidable
588
    my $line1_pre = $line1->unblessed();
589
    $ret = $line1->void();
590
    $line1->_result->discard_changes();
591
    my $line1_post = $line1->unblessed();
592
    is( $ret, undef, 'Attempted void on non-credit returns undef' );
593
    is_deeply( $line1_pre, $line1_post, 'Non-credit account line cannot be voided' );
594
595
    $schema->storage->txn_rollback;
596
};
597
598
subtest "payout() tests" => sub {
599
600
    plan tests => 17;
601
602
    $schema->storage->txn_begin;
603
604
    # Create a borrower
605
    my $categorycode =
606
      $builder->build( { source => 'Category' } )->{categorycode};
607
    my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode};
608
609
    my $borrower = Koha::Patron->new(
610
        {
611
            cardnumber => 'dariahall',
612
            surname    => 'Hall',
613
            firstname  => 'Daria',
614
        }
615
    );
616
    $borrower->categorycode($categorycode);
617
    $borrower->branchcode($branchcode);
618
    $borrower->store;
619
620
    my $staff = Koha::Patron->new(
621
        {
622
            cardnumber => 'bobby',
623
            surname    => 'Bloggs',
624
            firstname  => 'Bobby',
625
        }
626
    );
627
    $staff->categorycode($categorycode);
628
    $staff->branchcode($branchcode);
629
    $staff->store;
630
631
    my $account = Koha::Account->new( { patron_id => $borrower->id } );
632
633
    my $debit1 = Koha::Account::Line->new(
634
        {
635
            borrowernumber    => $borrower->borrowernumber,
636
            amount            => 10,
637
            amountoutstanding => 10,
638
            interface         => 'commandline',
639
            debit_type_code   => 'OVERDUE'
640
        }
641
    )->store();
642
    my $credit1 = Koha::Account::Line->new(
643
        {
644
            borrowernumber    => $borrower->borrowernumber,
645
            amount            => -20,
646
            amountoutstanding => -20,
647
            interface         => 'commandline',
648
            credit_type_code  => 'CREDIT'
649
        }
650
    )->store();
651
652
    is( $account->balance(), -10, "Account balance is -10" );
653
    is( $debit1->amountoutstanding,
654
        10, 'Overdue fee has an amount outstanding of 10' );
655
    is( $credit1->amountoutstanding,
656
        -20, 'Credit has an amount outstanding of -20' );
657
658
    my $pay_params = {
659
        interface   => 'intranet',
660
        staff_id    => $staff->borrowernumber,
661
        branch      => $branchcode,
662
        payout_type => 'CASH',
663
        amount      => 20
664
    };
665
666
    throws_ok { $debit1->payout($pay_params); }
667
    'Koha::Exceptions::Account::IsNotCredit',
668
      '->payout() can only be used with credits';
669
670
    my @required =
671
      ( 'interface', 'staff_id', 'branch', 'payout_type', 'amount' );
672
    for my $required (@required) {
673
        my $params = {%$pay_params};
674
        delete( $params->{$required} );
675
        throws_ok {
676
            $credit1->payout($params);
677
        }
678
        'Koha::Exceptions::MissingParameter',
679
          "->payout() requires the `$required` parameter is passed";
680
    }
681
682
    throws_ok {
683
        $credit1->payout(
684
            {
685
                interface   => 'intranet',
686
                staff_id    => $staff->borrowernumber,
687
                branch      => $branchcode,
688
                payout_type => 'CASH',
689
                amount      => 25
690
            }
691
        );
692
    }
693
    'Koha::Exceptions::ParameterTooHigh',
694
      '->payout() cannot pay out more than the amountoutstanding';
695
696
    t::lib::Mocks::mock_preference( 'UseCashRegisters', 1 );
697
    throws_ok {
698
        $credit1->payout(
699
            {
700
                interface   => 'intranet',
701
                staff_id    => $staff->borrowernumber,
702
                branch      => $branchcode,
703
                payout_type => 'CASH',
704
                amount      => 10
705
            }
706
        );
707
    }
708
    'Koha::Exceptions::Account::RegisterRequired',
709
      '->payout() requires a cash_register if payout_type is `CASH`';
710
711
    t::lib::Mocks::mock_preference( 'UseCashRegisters', 0 );
712
    my $payout = $credit1->payout(
713
        {
714
            interface   => 'intranet',
715
            staff_id    => $staff->borrowernumber,
716
            branch      => $branchcode,
717
            payout_type => 'CASH',
718
            amount      => 10
719
        }
720
    );
721
722
    is( $payout->amount(),            10, "Payout amount is 10" );
723
    is( $payout->amountoutstanding(), 0,  "Payout amountoutstanding is 0" );
724
    is( $account->balance(),          0,  "Account balance is 0" );
725
    is( $debit1->amountoutstanding,
726
        10, 'Overdue fee still has an amount outstanding of 10' );
727
    is( $credit1->amountoutstanding,
728
        -10, 'Credit has an new amount outstanding of -10' );
729
    is( $credit1->status(), 'PAID', "Credit has a new status of PAID" );
730
731
    $schema->storage->txn_rollback;
732
};
733
734
subtest "reduce() tests" => sub {
735
736
    plan tests => 25;
737
738
    $schema->storage->txn_begin;
739
740
    # Create a borrower
741
    my $categorycode =
742
      $builder->build( { source => 'Category' } )->{categorycode};
743
    my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode};
744
745
    my $borrower = Koha::Patron->new(
746
        {
747
            cardnumber => 'dariahall',
748
            surname    => 'Hall',
749
            firstname  => 'Daria',
750
        }
751
    );
752
    $borrower->categorycode($categorycode);
753
    $borrower->branchcode($branchcode);
754
    $borrower->store;
755
756
    my $staff = Koha::Patron->new(
757
        {
758
            cardnumber => 'bobby',
759
            surname    => 'Bloggs',
760
            firstname  => 'Bobby',
761
        }
762
    );
763
    $staff->categorycode($categorycode);
764
    $staff->branchcode($branchcode);
765
    $staff->store;
766
767
    my $account = Koha::Account->new( { patron_id => $borrower->id } );
768
769
    my $debit1 = Koha::Account::Line->new(
770
        {
771
            borrowernumber    => $borrower->borrowernumber,
772
            amount            => 20,
773
            amountoutstanding => 20,
774
            interface         => 'commandline',
775
            debit_type_code   => 'LOST'
776
        }
777
    )->store();
778
    my $credit1 = Koha::Account::Line->new(
779
        {
780
            borrowernumber    => $borrower->borrowernumber,
781
            amount            => -20,
782
            amountoutstanding => -20,
783
            interface         => 'commandline',
784
            credit_type_code  => 'CREDIT'
785
        }
786
    )->store();
787
788
    is( $account->balance(), 0, "Account balance is 0" );
789
    is( $debit1->amountoutstanding,
790
        20, 'Overdue fee has an amount outstanding of 20' );
791
    is( $credit1->amountoutstanding,
792
        -20, 'Credit has an amount outstanding of -20' );
793
794
    my $reduce_params = {
795
        interface      => 'commandline',
796
        reduction_type => 'REFUND',
797
        amount         => 5,
798
        staff_id       => $staff->borrowernumber,
799
        branch         => $branchcode
800
    };
801
802
    throws_ok { $credit1->reduce($reduce_params); }
803
    'Koha::Exceptions::Account::IsNotDebit',
804
      '->reduce() can only be used with debits';
805
806
    my @required = ( 'interface', 'reduction_type', 'amount' );
807
    for my $required (@required) {
808
        my $params = {%$reduce_params};
809
        delete( $params->{$required} );
810
        throws_ok {
811
            $debit1->reduce($params);
812
        }
813
        'Koha::Exceptions::MissingParameter',
814
          "->reduce() requires the `$required` parameter is passed";
815
    }
816
817
    $reduce_params->{interface} = 'intranet';
818
    my @dependant_required = ( 'staff_id', 'branch' );
819
    for my $d (@dependant_required) {
820
        my $params = {%$reduce_params};
821
        delete( $params->{$d} );
822
        throws_ok {
823
            $debit1->reduce($params);
824
        }
825
        'Koha::Exceptions::MissingParameter',
826
"->reduce() requires the `$d` parameter is passed when interface is intranet";
827
    }
828
829
    throws_ok {
830
        $debit1->reduce(
831
            {
832
                interface      => 'intranet',
833
                staff_id       => $staff->borrowernumber,
834
                branch         => $branchcode,
835
                reduction_type => 'REFUND',
836
                amount         => 25
837
            }
838
        );
839
    }
840
    'Koha::Exceptions::ParameterTooHigh',
841
      '->reduce() cannot reduce more than original amount';
842
843
    # Partial Reduction
844
    # (Refund 5 on debt of 20)
845
    my $reduction = $debit1->reduce($reduce_params);
846
847
    is( $reduction->amount() * 1, -5, "Reduce amount is -5" );
848
    is( $reduction->amountoutstanding() * 1,
849
        0, "Reduce amountoutstanding is 0" );
850
    is( $debit1->amountoutstanding() * 1,
851
        15, "Debit amountoutstanding reduced by 5 to 15" );
852
    is( $account->balance() * 1, -5,       "Account balance is -5" );
853
    is( $reduction->status(),    'APPLIED', "Reduction status is 'APPLIED'" );
854
855
    my $offsets = Koha::Account::Offsets->search(
856
        { credit_id => $reduction->id, debit_id => $debit1->id } );
857
    is( $offsets->count, 1, 'Only one offset is generated' );
858
    my $THE_offset = $offsets->next;
859
    is( $THE_offset->amount * 1,
860
        -5, 'Correct amount was applied against debit' );
861
    is( $THE_offset->type, 'REFUND', "Offset type set to 'REFUND'" );
862
863
    # Zero offset created when zero outstanding
864
    # (Refund another 5 on paid debt of 20)
865
    $credit1->apply( { debits => [ $debit1 ] } );
866
    is($debit1->amountoutstanding + 0, 0, 'Debit1 amountoutstanding reduced to 0');
867
    $reduction = $debit1->reduce($reduce_params);
868
    is( $reduction->amount() * 1, -5, "Reduce amount is -5" );
869
    is( $reduction->amountoutstanding() * 1,
870
        -5, "Reduce amountoutstanding is -5" );
871
872
    $offsets = Koha::Account::Offsets->search(
873
        { credit_id => $reduction->id, debit_id => $debit1->id } );
874
    is( $offsets->count, 1, 'Only one new offset is generated' );
875
    $THE_offset = $offsets->next;
876
    is( $THE_offset->amount * 1,
877
        0, 'Zero offset created for already paid off debit' );
878
    is( $THE_offset->type, 'REFUND', "Offset type set to 'REFUND'" );
879
880
    # Compound reduction should not allow more than original amount
881
    # (Reduction of 5 + 5 + 20 > 20)
882
    $reduce_params->{amount} = 20;
883
    throws_ok {
884
        $debit1->reduce($reduce_params);
885
    }
886
    'Koha::Exceptions::ParameterTooHigh',
887
'->reduce cannot reduce mor than the original amount (combined reductions test)';
888
889
    $schema->storage->txn_rollback;
890
};
891
892
1;
(-)a/t/db_dependent/Koha/Account/Lines.t (-865 / +6 lines)
Lines 19-32 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 15;
22
use Test::More tests => 4;
23
use Test::Exception;
23
use Test::Exception;
24
24
25
use C4::Circulation qw/AddIssue AddReturn/;
26
use Koha::Account;
25
use Koha::Account;
27
use Koha::Account::Lines;
26
use Koha::Account::Lines;
28
use Koha::Account::Offsets;
27
use Koha::Account::Offsets;
29
use Koha::Items;
30
28
31
use t::lib::Mocks;
29
use t::lib::Mocks;
32
use t::lib::TestBuilder;
30
use t::lib::TestBuilder;
Lines 34-107 use t::lib::TestBuilder; Link Here
34
my $schema = Koha::Database->new->schema;
32
my $schema = Koha::Database->new->schema;
35
my $builder = t::lib::TestBuilder->new;
33
my $builder = t::lib::TestBuilder->new;
36
34
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
        debit_type_code    => "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
        debit_type_code    => "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 {
35
subtest 'total_outstanding() tests' => sub {
106
36
107
    plan tests => 5;
37
    plan tests => 5;
Lines 115-121 subtest 'total_outstanding() tests' => sub { Link Here
115
45
116
    my $debit_1 = Koha::Account::Line->new(
46
    my $debit_1 = Koha::Account::Line->new(
117
        {   borrowernumber    => $patron->id,
47
        {   borrowernumber    => $patron->id,
118
            debit_type_code       => "OVERDUE",
48
            debit_type_code   => "OVERDUE",
119
            status            => "RETURNED",
49
            status            => "RETURNED",
120
            amount            => 10,
50
            amount            => 10,
121
            amountoutstanding => 10,
51
            amountoutstanding => 10,
Lines 125-131 subtest 'total_outstanding() tests' => sub { Link Here
125
55
126
    my $debit_2 = Koha::Account::Line->new(
56
    my $debit_2 = Koha::Account::Line->new(
127
        {   borrowernumber    => $patron->id,
57
        {   borrowernumber    => $patron->id,
128
            debit_type_code       => "OVERDUE",
58
            debit_type_code   => "OVERDUE",
129
            status            => "RETURNED",
59
            status            => "RETURNED",
130
            amount            => 10,
60
            amount            => 10,
131
            amountoutstanding => 10,
61
            amountoutstanding => 10,
Lines 138-144 subtest 'total_outstanding() tests' => sub { Link Here
138
68
139
    my $credit_1 = Koha::Account::Line->new(
69
    my $credit_1 = Koha::Account::Line->new(
140
        {   borrowernumber    => $patron->id,
70
        {   borrowernumber    => $patron->id,
141
            debit_type_code       => "OVERDUE",
71
            debit_type_code   => "OVERDUE",
142
            status            => "RETURNED",
72
            status            => "RETURNED",
143
            amount            => -10,
73
            amount            => -10,
144
            amountoutstanding => -10,
74
            amountoutstanding => -10,
Lines 151-157 subtest 'total_outstanding() tests' => sub { Link Here
151
81
152
    my $credit_2 = Koha::Account::Line->new(
82
    my $credit_2 = Koha::Account::Line->new(
153
        {   borrowernumber    => $patron->id,
83
        {   borrowernumber    => $patron->id,
154
            debit_type_code       => "OVERDUE",
84
            debit_type_code   => "OVERDUE",
155
            status            => "RETURNED",
85
            status            => "RETURNED",
156
            amount            => -10,
86
            amount            => -10,
157
            amountoutstanding => -10,
87
            amountoutstanding => -10,
Lines 164-170 subtest 'total_outstanding() tests' => sub { Link Here
164
94
165
    my $credit_3 = Koha::Account::Line->new(
95
    my $credit_3 = Koha::Account::Line->new(
166
        {   borrowernumber    => $patron->id,
96
        {   borrowernumber    => $patron->id,
167
            debit_type_code       => "OVERDUE",
97
            debit_type_code   => "OVERDUE",
168
            status            => "RETURNED",
98
            status            => "RETURNED",
169
            amount            => -100,
99
            amount            => -100,
170
            amountoutstanding => -100,
100
            amountoutstanding => -100,
Lines 406-1197 subtest 'debits_total() tests' => sub { Link Here
406
    $schema->storage->txn_rollback;
336
    $schema->storage->txn_rollback;
407
};
337
};
408
338
409
subtest 'is_credit() and is_debit() tests' => sub {
410
411
    plan tests => 4;
412
413
    $schema->storage->txn_begin;
414
415
    my $patron  = $builder->build_object({ class => 'Koha::Patrons' });
416
    my $account = $patron->account;
417
418
    my $credit = $account->add_credit({ amount => 100, user_id => $patron->id, interface => 'commandline' });
419
420
    ok( $credit->is_credit, 'is_credit detects credits' );
421
    ok( !$credit->is_debit, 'is_debit detects credits' );
422
423
    my $debit = Koha::Account::Line->new(
424
    {
425
        borrowernumber => $patron->id,
426
        debit_type_code    => "OVERDUE",
427
        status         => "RETURNED",
428
        amount         => 10,
429
        interface      => 'commandline',
430
    })->store;
431
432
    ok( !$debit->is_credit, 'is_credit detects debits' );
433
    ok( $debit->is_debit, 'is_debit detects debits');
434
435
    $schema->storage->txn_rollback;
436
};
437
438
subtest 'apply() tests' => sub {
439
440
    plan tests => 24;
441
442
    $schema->storage->txn_begin;
443
444
    my $patron  = $builder->build_object( { class => 'Koha::Patrons' } );
445
    my $account = $patron->account;
446
447
    my $credit = $account->add_credit( { amount => 100, user_id => $patron->id, interface => 'commandline' } );
448
449
    my $debit_1 = Koha::Account::Line->new(
450
        {   borrowernumber    => $patron->id,
451
            debit_type_code       => "OVERDUE",
452
            status            => "RETURNED",
453
            amount            => 10,
454
            amountoutstanding => 10,
455
            interface         => 'commandline',
456
        }
457
    )->store;
458
459
    my $debit_2 = Koha::Account::Line->new(
460
        {   borrowernumber    => $patron->id,
461
            debit_type_code       => "OVERDUE",
462
            status            => "RETURNED",
463
            amount            => 100,
464
            amountoutstanding => 100,
465
            interface         => 'commandline',
466
        }
467
    )->store;
468
469
    $credit->discard_changes;
470
    $debit_1->discard_changes;
471
472
    my $debits = Koha::Account::Lines->search({ accountlines_id => $debit_1->id });
473
    my $remaining_credit = $credit->apply( { debits => [ $debits->as_list ], offset_type => 'Manual Credit' } );
474
    is( $remaining_credit * 1, 90, 'Remaining credit is correctly calculated' );
475
    $credit->discard_changes;
476
    is( $credit->amountoutstanding * -1, $remaining_credit, 'Remaining credit correctly stored' );
477
478
    # re-read debit info
479
    $debit_1->discard_changes;
480
    is( $debit_1->amountoutstanding * 1, 0, 'Debit has been cancelled' );
481
482
    my $offsets = Koha::Account::Offsets->search( { credit_id => $credit->id, debit_id => $debit_1->id } );
483
    is( $offsets->count, 1, 'Only one offset is generated' );
484
    my $THE_offset = $offsets->next;
485
    is( $THE_offset->amount * 1, -10, 'Amount was calculated correctly (less than the available credit)' );
486
    is( $THE_offset->type, 'Manual Credit', 'Passed type stored correctly' );
487
488
    $debits = Koha::Account::Lines->search({ accountlines_id => $debit_2->id });
489
    $remaining_credit = $credit->apply( { debits => [ $debits->as_list ] } );
490
    is( $remaining_credit, 0, 'No remaining credit left' );
491
    $credit->discard_changes;
492
    is( $credit->amountoutstanding * 1, 0, 'No outstanding credit' );
493
    $debit_2->discard_changes;
494
    is( $debit_2->amountoutstanding * 1, 10, 'Outstanding amount decremented correctly' );
495
496
    $offsets = Koha::Account::Offsets->search( { credit_id => $credit->id, debit_id => $debit_2->id } );
497
    is( $offsets->count, 1, 'Only one offset is generated' );
498
    $THE_offset = $offsets->next;
499
    is( $THE_offset->amount * 1, -90, 'Amount was calculated correctly (less than the available credit)' );
500
    is( $THE_offset->type, 'Credit Applied', 'Defaults to \'Credit Applied\' offset type' );
501
502
    $debits = Koha::Account::Lines->search({ accountlines_id => $debit_1->id });
503
    throws_ok
504
        { $credit->apply({ debits => [ $debits->as_list ] }); }
505
        'Koha::Exceptions::Account::NoAvailableCredit',
506
        '->apply() can only be used with outstanding credits';
507
508
    $debits = Koha::Account::Lines->search({ accountlines_id => $credit->id });
509
    throws_ok
510
        { $debit_1->apply({ debits => [ $debits->as_list ] }); }
511
        'Koha::Exceptions::Account::IsNotCredit',
512
        '->apply() can only be used with credits';
513
514
    $debits = Koha::Account::Lines->search({ accountlines_id => $credit->id });
515
    my $credit_3 = $account->add_credit({ amount => 1, interface => 'commandline' });
516
    throws_ok
517
        { $credit_3->apply({ debits => [ $debits->as_list ] }); }
518
        'Koha::Exceptions::Account::IsNotDebit',
519
        '->apply() can only be applied to credits';
520
521
    my $credit_2 = $account->add_credit({ amount => 20, interface => 'commandline' });
522
    my $debit_3  = Koha::Account::Line->new(
523
        {   borrowernumber    => $patron->id,
524
            debit_type_code       => "OVERDUE",
525
            status            => "RETURNED",
526
            amount            => 100,
527
            amountoutstanding => 100,
528
            interface         => 'commandline',
529
        }
530
    )->store;
531
532
    $debits = Koha::Account::Lines->search({ accountlines_id => { -in => [ $debit_1->id, $debit_2->id, $debit_3->id, $credit->id ] } });
533
    throws_ok {
534
        $credit_2->apply( { debits => [ $debits->as_list ], offset_type => 'Manual Credit' } ); }
535
        'Koha::Exceptions::Account::IsNotDebit',
536
        '->apply() rolls back if any of the passed lines is not a debit';
537
538
    is( $debit_1->discard_changes->amountoutstanding * 1,   0, 'No changes to already cancelled debit' );
539
    is( $debit_2->discard_changes->amountoutstanding * 1,  10, 'Debit cancelled' );
540
    is( $debit_3->discard_changes->amountoutstanding * 1, 100, 'Outstanding amount correctly calculated' );
541
    is( $credit_2->discard_changes->amountoutstanding * -1, 20, 'No changes made' );
542
543
    $debits = Koha::Account::Lines->search({ accountlines_id => { -in => [ $debit_1->id, $debit_2->id, $debit_3->id ] } });
544
    $remaining_credit = $credit_2->apply( { debits => [ $debits->as_list ], offset_type => 'Manual Credit' } );
545
546
    is( $debit_1->discard_changes->amountoutstanding * 1,  0, 'No changes to already cancelled debit' );
547
    is( $debit_2->discard_changes->amountoutstanding * 1,  0, 'Debit cancelled' );
548
    is( $debit_3->discard_changes->amountoutstanding * 1, 90, 'Outstanding amount correctly calculated' );
549
    is( $credit_2->discard_changes->amountoutstanding * 1, 0, 'No remaining credit' );
550
551
    $schema->storage->txn_rollback;
552
};
553
554
subtest 'Keep account info when related patron, staff, item or cash_register is deleted' => sub {
555
556
    plan tests => 4;
557
558
    $schema->storage->txn_begin;
559
560
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
561
    my $staff = $builder->build_object( { class => 'Koha::Patrons' } );
562
    my $item = $builder->build_object({ class => 'Koha::Items' });
563
    my $issue = $builder->build_object(
564
        {
565
            class => 'Koha::Checkouts',
566
            value => { itemnumber => $item->itemnumber }
567
        }
568
    );
569
    my $register = $builder->build_object({ class => 'Koha::Cash::Registers' });
570
571
    my $line = Koha::Account::Line->new(
572
    {
573
        borrowernumber => $patron->borrowernumber,
574
        manager_id     => $staff->borrowernumber,
575
        itemnumber     => $item->itemnumber,
576
        debit_type_code    => "OVERDUE",
577
        status         => "RETURNED",
578
        amount         => 10,
579
        interface      => 'commandline',
580
        register_id    => $register->id
581
    })->store;
582
583
    $issue->delete;
584
    $item->delete;
585
    $line = $line->get_from_storage;
586
    is( $line->itemnumber, undef, "The account line should not be deleted when the related item is delete");
587
588
    $staff->delete;
589
    $line = $line->get_from_storage;
590
    is( $line->manager_id, undef, "The account line should not be deleted when the related staff is delete");
591
592
    $patron->delete;
593
    $line = $line->get_from_storage;
594
    is( $line->borrowernumber, undef, "The account line should not be deleted when the related patron is delete");
595
596
    $register->delete;
597
    $line = $line->get_from_storage;
598
    is( $line->register_id, undef, "The account line should not be deleted when the related cash register is delete");
599
600
    $schema->storage->txn_rollback;
601
};
602
603
subtest 'adjust() tests' => sub {
604
605
    plan tests => 29;
606
607
    $schema->storage->txn_begin;
608
609
    # count logs before any actions
610
    my $action_logs = $schema->resultset('ActionLog')->search()->count;
611
612
    # Disable logs
613
    t::lib::Mocks::mock_preference( 'FinesLog', 0 );
614
615
    my $patron  = $builder->build_object( { class => 'Koha::Patrons' } );
616
    my $account = $patron->account;
617
618
    my $debit_1 = Koha::Account::Line->new(
619
        {   borrowernumber    => $patron->id,
620
            debit_type_code       => "OVERDUE",
621
            status            => "RETURNED",
622
            amount            => 10,
623
            amountoutstanding => 10,
624
            interface         => 'commandline',
625
        }
626
    )->store;
627
628
    my $debit_2 = Koha::Account::Line->new(
629
        {   borrowernumber    => $patron->id,
630
            debit_type_code       => "OVERDUE",
631
            status            => "UNRETURNED",
632
            amount            => 100,
633
            amountoutstanding => 100,
634
            interface         => 'commandline'
635
        }
636
    )->store;
637
638
    my $credit = $account->add_credit( { amount => 40, user_id => $patron->id, interface => 'commandline' } );
639
640
    throws_ok { $debit_1->adjust( { amount => 50, type => 'bad', interface => 'commandline' } ) }
641
    qr/Update type not recognised/, 'Exception thrown for unrecognised type';
642
643
    throws_ok { $debit_1->adjust( { amount => 50, type => 'overdue_update', interface => 'commandline' } ) }
644
    qr/Update type not allowed on this debit_type/,
645
      'Exception thrown for type conflict';
646
647
    # Increment an unpaid fine
648
    $debit_2->adjust( { amount => 150, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
649
650
    is( $debit_2->amount * 1, 150, 'Fine amount was updated in full' );
651
    is( $debit_2->amountoutstanding * 1, 150, 'Fine amountoutstanding was update in full' );
652
    isnt( $debit_2->date, undef, 'Date has been set' );
653
654
    my $offsets = Koha::Account::Offsets->search( { debit_id => $debit_2->id } );
655
    is( $offsets->count, 1, 'An offset is generated for the increment' );
656
    my $THIS_offset = $offsets->next;
657
    is( $THIS_offset->amount * 1, 50, 'Amount was calculated correctly (increment by 50)' );
658
    is( $THIS_offset->type, 'OVERDUE_INCREASE', 'Adjust type stored correctly' );
659
660
    is( $schema->resultset('ActionLog')->count(), $action_logs + 0, 'No log was added' );
661
662
    # Update fine to partially paid
663
    my $debits = Koha::Account::Lines->search({ accountlines_id => $debit_2->id });
664
    $credit->apply( { debits => [ $debits->as_list ], offset_type => 'Manual Credit' } );
665
666
    $debit_2->discard_changes;
667
    is( $debit_2->amount * 1, 150, 'Fine amount unaffected by partial payment' );
668
    is( $debit_2->amountoutstanding * 1, 110, 'Fine amountoutstanding updated by partial payment' );
669
670
    # Enable logs
671
    t::lib::Mocks::mock_preference( 'FinesLog', 1 );
672
673
    # Increment the partially paid fine
674
    $debit_2->adjust( { amount => 160, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
675
676
    is( $debit_2->amount * 1, 160, 'Fine amount was updated in full' );
677
    is( $debit_2->amountoutstanding * 1, 120, 'Fine amountoutstanding was updated by difference' );
678
679
    $offsets = Koha::Account::Offsets->search( { debit_id => $debit_2->id } );
680
    is( $offsets->count, 3, 'An offset is generated for the increment' );
681
    $THIS_offset = $offsets->last;
682
    is( $THIS_offset->amount * 1, 10, 'Amount was calculated correctly (increment by 10)' );
683
    is( $THIS_offset->type, 'OVERDUE_INCREASE', 'Adjust type stored correctly' );
684
685
    is( $schema->resultset('ActionLog')->count(), $action_logs + 1, 'Log was added' );
686
687
    # Decrement the partially paid fine, less than what was paid
688
    $debit_2->adjust( { amount => 50, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
689
690
    is( $debit_2->amount * 1, 50, 'Fine amount was updated in full' );
691
    is( $debit_2->amountoutstanding * 1, 10, 'Fine amountoutstanding was updated by difference' );
692
693
    $offsets = Koha::Account::Offsets->search( { debit_id => $debit_2->id } );
694
    is( $offsets->count, 4, 'An offset is generated for the decrement' );
695
    $THIS_offset = $offsets->last;
696
    is( $THIS_offset->amount * 1, -110, 'Amount was calculated correctly (decrement by 110)' );
697
    is( $THIS_offset->type, 'OVERDUE_DECREASE', 'Adjust type stored correctly' );
698
699
    # Decrement the partially paid fine, more than what was paid
700
    $debit_2->adjust( { amount => 30, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
701
    is( $debit_2->amount * 1, 30, 'Fine amount was updated in full' );
702
    is( $debit_2->amountoutstanding * 1, 0, 'Fine amountoutstanding was zeroed (payment was 40)' );
703
704
    $offsets = Koha::Account::Offsets->search( { debit_id => $debit_2->id } );
705
    is( $offsets->count, 5, 'An offset is generated for the decrement' );
706
    $THIS_offset = $offsets->last;
707
    is( $THIS_offset->amount * 1, -20, 'Amount was calculated correctly (decrement by 20)' );
708
    is( $THIS_offset->type, 'OVERDUE_DECREASE', 'Adjust type stored correctly' );
709
710
    my $overpayment_refund = $account->lines->last;
711
    is( $overpayment_refund->amount * 1, -10, 'A new credit has been added' );
712
    is( $overpayment_refund->description, 'Overpayment refund', 'Credit generated with the expected description' );
713
714
    $schema->storage->txn_rollback;
715
};
716
717
subtest 'checkout() tests' => sub {
718
    plan tests => 6;
719
720
    $schema->storage->txn_begin;
721
722
    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
723
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
724
    my $item = $builder->build_sample_item;
725
    my $account = $patron->account;
726
727
    t::lib::Mocks::mock_userenv({ branchcode => $library->branchcode });
728
    my $checkout = AddIssue( $patron->unblessed, $item->barcode );
729
730
    my $line = $account->add_debit({
731
        amount    => 10,
732
        interface => 'commandline',
733
        item_id   => $item->itemnumber,
734
        issue_id  => $checkout->issue_id,
735
        type      => 'OVERDUE',
736
    });
737
738
    my $line_checkout = $line->checkout;
739
    is( ref($line_checkout), 'Koha::Checkout', 'Result type is correct' );
740
    is( $line_checkout->issue_id, $checkout->issue_id, 'Koha::Account::Line->checkout should return the correct checkout');
741
742
    my ( $returned, undef, $old_checkout) = C4::Circulation::AddReturn( $item->barcode, $library->branchcode );
743
    is( $returned, 1, 'The item should have been returned' );
744
745
    $line = $line->get_from_storage;
746
    my $old_line_checkout = $line->checkout;
747
    is( ref($old_line_checkout), 'Koha::Old::Checkout', 'Result type is correct' );
748
    is( $old_line_checkout->issue_id, $old_checkout->issue_id, 'Koha::Account::Line->checkout should return the correct old_checkout' );
749
750
    $line->issue_id(undef)->store;
751
    is( $line->checkout, undef, 'Koha::Account::Line->checkout should return undef if no checkout linked' );
752
753
    $schema->storage->txn_rollback;
754
};
755
756
subtest 'credits() and debits() tests' => sub {
757
    plan tests => 10;
758
759
    $schema->storage->txn_begin;
760
761
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
762
    my $account = $patron->account;
763
764
    my $debit1 = $account->add_debit({
765
        amount    => 8,
766
        interface => 'commandline',
767
        type      => 'ACCOUNT',
768
    });
769
    my $debit2 = $account->add_debit({
770
        amount    => 12,
771
        interface => 'commandline',
772
        type      => 'ACCOUNT',
773
    });
774
    my $credit1 = $account->add_credit({
775
        amount    => 5,
776
        interface => 'commandline',
777
        type      => 'CREDIT',
778
    });
779
    my $credit2 = $account->add_credit({
780
        amount    => 10,
781
        interface => 'commandline',
782
        type      => 'CREDIT',
783
    });
784
785
    $credit1->apply({ debits => [ $debit1 ] });
786
    $credit2->apply({ debits => [ $debit1, $debit2 ] });
787
788
    my $credits = $debit1->credits;
789
    is($credits->count, 2, '2 Credits applied to debit 1');
790
    my $credit = $credits->next;
791
    is($credit->amount + 0, -5, 'Correct first credit');
792
    $credit = $credits->next;
793
    is($credit->amount + 0, -10, 'Correct second credit');
794
795
    $credits = $debit2->credits;
796
    is($credits->count, 1, '1 Credits applied to debit 2');
797
    $credit = $credits->next;
798
    is($credit->amount + 0, -10, 'Correct first credit');
799
800
    my $debits = $credit1->debits;
801
    is($debits->count, 1, 'Credit 1 applied to 1 debit');
802
    my $debit = $debits->next;
803
    is($debit->amount + 0, 8, 'Correct first debit');
804
805
    $debits = $credit2->debits;
806
    is($debits->count, 2, 'Credit 2 applied to 2 debits');
807
    $debit = $debits->next;
808
    is($debit->amount + 0, 8, 'Correct first debit');
809
    $debit = $debits->next;
810
    is($debit->amount + 0, 12, 'Correct second debit');
811
812
    $schema->storage->txn_rollback;
813
};
814
815
subtest "void() tests" => sub {
816
817
    plan tests => 16;
818
819
    $schema->storage->txn_begin;
820
821
    # Create a borrower
822
    my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
823
    my $branchcode   = $builder->build({ source => 'Branch' })->{ branchcode };
824
825
    my $borrower = Koha::Patron->new( {
826
        cardnumber => 'dariahall',
827
        surname => 'Hall',
828
        firstname => 'Daria',
829
    } );
830
    $borrower->categorycode( $categorycode );
831
    $borrower->branchcode( $branchcode );
832
    $borrower->store;
833
834
    my $account = Koha::Account->new({ patron_id => $borrower->id });
835
836
    my $line1 = Koha::Account::Line->new(
837
        {
838
            borrowernumber    => $borrower->borrowernumber,
839
            amount            => 10,
840
            amountoutstanding => 10,
841
            interface         => 'commandline',
842
            debit_type_code   => 'OVERDUE'
843
        }
844
    )->store();
845
    my $line2 = Koha::Account::Line->new(
846
        {
847
            borrowernumber    => $borrower->borrowernumber,
848
            amount            => 20,
849
            amountoutstanding => 20,
850
            interface         => 'commandline',
851
            debit_type_code   => 'OVERDUE'
852
        }
853
    )->store();
854
855
    is( $account->balance(), 30, "Account balance is 30" );
856
    is( $line1->amountoutstanding, 10, 'First fee has amount outstanding of 10' );
857
    is( $line2->amountoutstanding, 20, 'Second fee has amount outstanding of 20' );
858
859
    my $id = $account->pay(
860
        {
861
            lines  => [$line1, $line2],
862
            amount => 30,
863
        }
864
    );
865
866
    my $account_payment = Koha::Account::Lines->find( $id );
867
868
    is( $account->balance(), 0, "Account balance is 0" );
869
870
    $line1->_result->discard_changes();
871
    $line2->_result->discard_changes();
872
    is( $line1->amountoutstanding+0, 0, 'First fee has amount outstanding of 0' );
873
    is( $line2->amountoutstanding+0, 0, 'Second fee has amount outstanding of 0' );
874
875
    my $ret = $account_payment->void();
876
877
    is( ref($ret), 'Koha::Account::Line', 'Void returns the account line' );
878
    is( $account->balance(), 30, "Account balance is again 30" );
879
880
    $account_payment->_result->discard_changes();
881
    $line1->_result->discard_changes();
882
    $line2->_result->discard_changes();
883
884
    is( $account_payment->credit_type_code, 'PAYMENT', 'Voided payment credit_type_code is still PAYMENT' );
885
    is( $account_payment->status, 'VOID', 'Voided payment status is VOID' );
886
    is( $account_payment->amount+0, 0, 'Voided payment amount is 0' );
887
    is( $account_payment->amountoutstanding+0, 0, 'Voided payment amount outstanding is 0' );
888
889
    is( $line1->amountoutstanding+0, 10, 'First fee again has amount outstanding of 10' );
890
    is( $line2->amountoutstanding+0, 20, 'Second fee again has amount outstanding of 20' );
891
892
    # Accountlines that are not credits should be un-voidable
893
    my $line1_pre = $line1->unblessed();
894
    $ret = $line1->void();
895
    $line1->_result->discard_changes();
896
    my $line1_post = $line1->unblessed();
897
    is( $ret, undef, 'Attempted void on non-credit returns undef' );
898
    is_deeply( $line1_pre, $line1_post, 'Non-credit account line cannot be voided' );
899
900
    $schema->storage->txn_rollback;
901
};
902
903
subtest "payout() tests" => sub {
904
905
    plan tests => 17;
906
907
    $schema->storage->txn_begin;
908
909
    # Create a borrower
910
    my $categorycode =
911
      $builder->build( { source => 'Category' } )->{categorycode};
912
    my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode};
913
914
    my $borrower = Koha::Patron->new(
915
        {
916
            cardnumber => 'dariahall',
917
            surname    => 'Hall',
918
            firstname  => 'Daria',
919
        }
920
    );
921
    $borrower->categorycode($categorycode);
922
    $borrower->branchcode($branchcode);
923
    $borrower->store;
924
925
    my $staff = Koha::Patron->new(
926
        {
927
            cardnumber => 'bobby',
928
            surname    => 'Bloggs',
929
            firstname  => 'Bobby',
930
        }
931
    );
932
    $staff->categorycode($categorycode);
933
    $staff->branchcode($branchcode);
934
    $staff->store;
935
936
    my $account = Koha::Account->new( { patron_id => $borrower->id } );
937
938
    my $debit1 = Koha::Account::Line->new(
939
        {
940
            borrowernumber    => $borrower->borrowernumber,
941
            amount            => 10,
942
            amountoutstanding => 10,
943
            interface         => 'commandline',
944
            debit_type_code   => 'OVERDUE'
945
        }
946
    )->store();
947
    my $credit1 = Koha::Account::Line->new(
948
        {
949
            borrowernumber    => $borrower->borrowernumber,
950
            amount            => -20,
951
            amountoutstanding => -20,
952
            interface         => 'commandline',
953
            credit_type_code  => 'CREDIT'
954
        }
955
    )->store();
956
957
    is( $account->balance(), -10, "Account balance is -10" );
958
    is( $debit1->amountoutstanding,
959
        10, 'Overdue fee has an amount outstanding of 10' );
960
    is( $credit1->amountoutstanding,
961
        -20, 'Credit has an amount outstanding of -20' );
962
963
    my $pay_params = {
964
        interface   => 'intranet',
965
        staff_id    => $staff->borrowernumber,
966
        branch      => $branchcode,
967
        payout_type => 'CASH',
968
        amount      => 20
969
    };
970
971
    throws_ok { $debit1->payout($pay_params); }
972
    'Koha::Exceptions::Account::IsNotCredit',
973
      '->payout() can only be used with credits';
974
975
    my @required =
976
      ( 'interface', 'staff_id', 'branch', 'payout_type', 'amount' );
977
    for my $required (@required) {
978
        my $params = {%$pay_params};
979
        delete( $params->{$required} );
980
        throws_ok {
981
            $credit1->payout($params);
982
        }
983
        'Koha::Exceptions::MissingParameter',
984
          "->payout() requires the `$required` parameter is passed";
985
    }
986
987
    throws_ok {
988
        $credit1->payout(
989
            {
990
                interface   => 'intranet',
991
                staff_id    => $staff->borrowernumber,
992
                branch      => $branchcode,
993
                payout_type => 'CASH',
994
                amount      => 25
995
            }
996
        );
997
    }
998
    'Koha::Exceptions::ParameterTooHigh',
999
      '->payout() cannot pay out more than the amountoutstanding';
1000
1001
    t::lib::Mocks::mock_preference( 'UseCashRegisters', 1 );
1002
    throws_ok {
1003
        $credit1->payout(
1004
            {
1005
                interface   => 'intranet',
1006
                staff_id    => $staff->borrowernumber,
1007
                branch      => $branchcode,
1008
                payout_type => 'CASH',
1009
                amount      => 10
1010
            }
1011
        );
1012
    }
1013
    'Koha::Exceptions::Account::RegisterRequired',
1014
      '->payout() requires a cash_register if payout_type is `CASH`';
1015
1016
    t::lib::Mocks::mock_preference( 'UseCashRegisters', 0 );
1017
    my $payout = $credit1->payout(
1018
        {
1019
            interface   => 'intranet',
1020
            staff_id    => $staff->borrowernumber,
1021
            branch      => $branchcode,
1022
            payout_type => 'CASH',
1023
            amount      => 10
1024
        }
1025
    );
1026
1027
    is( $payout->amount(),            10, "Payout amount is 10" );
1028
    is( $payout->amountoutstanding(), 0,  "Payout amountoutstanding is 0" );
1029
    is( $account->balance(),          0,  "Account balance is 0" );
1030
    is( $debit1->amountoutstanding,
1031
        10, 'Overdue fee still has an amount outstanding of 10' );
1032
    is( $credit1->amountoutstanding,
1033
        -10, 'Credit has an new amount outstanding of -10' );
1034
    is( $credit1->status(), 'PAID', "Credit has a new status of PAID" );
1035
1036
    $schema->storage->txn_rollback;
1037
};
1038
1039
subtest "reduce() tests" => sub {
1040
1041
    plan tests => 25;
1042
1043
    $schema->storage->txn_begin;
1044
1045
    # Create a borrower
1046
    my $categorycode =
1047
      $builder->build( { source => 'Category' } )->{categorycode};
1048
    my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode};
1049
1050
    my $borrower = Koha::Patron->new(
1051
        {
1052
            cardnumber => 'dariahall',
1053
            surname    => 'Hall',
1054
            firstname  => 'Daria',
1055
        }
1056
    );
1057
    $borrower->categorycode($categorycode);
1058
    $borrower->branchcode($branchcode);
1059
    $borrower->store;
1060
1061
    my $staff = Koha::Patron->new(
1062
        {
1063
            cardnumber => 'bobby',
1064
            surname    => 'Bloggs',
1065
            firstname  => 'Bobby',
1066
        }
1067
    );
1068
    $staff->categorycode($categorycode);
1069
    $staff->branchcode($branchcode);
1070
    $staff->store;
1071
1072
    my $account = Koha::Account->new( { patron_id => $borrower->id } );
1073
1074
    my $debit1 = Koha::Account::Line->new(
1075
        {
1076
            borrowernumber    => $borrower->borrowernumber,
1077
            amount            => 20,
1078
            amountoutstanding => 20,
1079
            interface         => 'commandline',
1080
            debit_type_code   => 'LOST'
1081
        }
1082
    )->store();
1083
    my $credit1 = Koha::Account::Line->new(
1084
        {
1085
            borrowernumber    => $borrower->borrowernumber,
1086
            amount            => -20,
1087
            amountoutstanding => -20,
1088
            interface         => 'commandline',
1089
            credit_type_code  => 'CREDIT'
1090
        }
1091
    )->store();
1092
1093
    is( $account->balance(), 0, "Account balance is 0" );
1094
    is( $debit1->amountoutstanding,
1095
        20, 'Overdue fee has an amount outstanding of 20' );
1096
    is( $credit1->amountoutstanding,
1097
        -20, 'Credit has an amount outstanding of -20' );
1098
1099
    my $reduce_params = {
1100
        interface      => 'commandline',
1101
        reduction_type => 'REFUND',
1102
        amount         => 5,
1103
        staff_id       => $staff->borrowernumber,
1104
        branch         => $branchcode
1105
    };
1106
1107
    throws_ok { $credit1->reduce($reduce_params); }
1108
    'Koha::Exceptions::Account::IsNotDebit',
1109
      '->reduce() can only be used with debits';
1110
1111
    my @required = ( 'interface', 'reduction_type', 'amount' );
1112
    for my $required (@required) {
1113
        my $params = {%$reduce_params};
1114
        delete( $params->{$required} );
1115
        throws_ok {
1116
            $debit1->reduce($params);
1117
        }
1118
        'Koha::Exceptions::MissingParameter',
1119
          "->reduce() requires the `$required` parameter is passed";
1120
    }
1121
1122
    $reduce_params->{interface} = 'intranet';
1123
    my @dependant_required = ( 'staff_id', 'branch' );
1124
    for my $d (@dependant_required) {
1125
        my $params = {%$reduce_params};
1126
        delete( $params->{$d} );
1127
        throws_ok {
1128
            $debit1->reduce($params);
1129
        }
1130
        'Koha::Exceptions::MissingParameter',
1131
"->reduce() requires the `$d` parameter is passed when interface is intranet";
1132
    }
1133
1134
    throws_ok {
1135
        $debit1->reduce(
1136
            {
1137
                interface      => 'intranet',
1138
                staff_id       => $staff->borrowernumber,
1139
                branch         => $branchcode,
1140
                reduction_type => 'REFUND',
1141
                amount         => 25
1142
            }
1143
        );
1144
    }
1145
    'Koha::Exceptions::ParameterTooHigh',
1146
      '->reduce() cannot reduce more than original amount';
1147
1148
    # Partial Reduction
1149
    # (Refund 5 on debt of 20)
1150
    my $reduction = $debit1->reduce($reduce_params);
1151
1152
    is( $reduction->amount() * 1, -5, "Reduce amount is -5" );
1153
    is( $reduction->amountoutstanding() * 1,
1154
        0, "Reduce amountoutstanding is 0" );
1155
    is( $debit1->amountoutstanding() * 1,
1156
        15, "Debit amountoutstanding reduced by 5 to 15" );
1157
    is( $account->balance() * 1, -5,       "Account balance is -5" );
1158
    is( $reduction->status(),    'APPLIED', "Reduction status is 'APPLIED'" );
1159
1160
    my $offsets = Koha::Account::Offsets->search(
1161
        { credit_id => $reduction->id, debit_id => $debit1->id } );
1162
    is( $offsets->count, 1, 'Only one offset is generated' );
1163
    my $THE_offset = $offsets->next;
1164
    is( $THE_offset->amount * 1,
1165
        -5, 'Correct amount was applied against debit' );
1166
    is( $THE_offset->type, 'REFUND', "Offset type set to 'REFUND'" );
1167
1168
    # Zero offset created when zero outstanding
1169
    # (Refund another 5 on paid debt of 20)
1170
    $credit1->apply( { debits => [ $debit1 ] } );
1171
    is($debit1->amountoutstanding + 0, 0, 'Debit1 amountoutstanding reduced to 0');
1172
    $reduction = $debit1->reduce($reduce_params);
1173
    is( $reduction->amount() * 1, -5, "Reduce amount is -5" );
1174
    is( $reduction->amountoutstanding() * 1,
1175
        -5, "Reduce amountoutstanding is -5" );
1176
1177
    $offsets = Koha::Account::Offsets->search(
1178
        { credit_id => $reduction->id, debit_id => $debit1->id } );
1179
    is( $offsets->count, 1, 'Only one new offset is generated' );
1180
    $THE_offset = $offsets->next;
1181
    is( $THE_offset->amount * 1,
1182
        0, 'Zero offset created for already paid off debit' );
1183
    is( $THE_offset->type, 'REFUND', "Offset type set to 'REFUND'" );
1184
1185
    # Compound reduction should not allow more than original amount
1186
    # (Reduction of 5 + 5 + 20 > 20)
1187
    $reduce_params->{amount} = 20;
1188
    throws_ok {
1189
        $debit1->reduce($reduce_params);
1190
    }
1191
    'Koha::Exceptions::ParameterTooHigh',
1192
'->reduce cannot reduce mor than the original amount (combined reductions test)';
1193
1194
    $schema->storage->txn_rollback;
1195
};
1196
1197
1;
339
1;
1198
- 

Return to bug 23355