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

(-)a/C4/Circulation.pm (-6 / +10 lines)
Lines 2034-2040 sub AddReturn { Link Here
2034
    # fix up the overdues in accounts...
2034
    # fix up the overdues in accounts...
2035
    if ($borrowernumber) {
2035
    if ($borrowernumber) {
2036
        my $fix = _FixOverduesOnReturn( $borrowernumber, $item->itemnumber, $exemptfine, 'RETURNED' );
2036
        my $fix = _FixOverduesOnReturn( $borrowernumber, $item->itemnumber, $exemptfine, 'RETURNED' );
2037
        defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, $item->itemnumber...) failed!";  # zero is OK, check defined
2037
        defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, ".$item->itemnumber."...) failed!";  # zero is OK, check defined
2038
2038
2039
        if ( $issue and $issue->is_overdue($return_date) ) {
2039
        if ( $issue and $issue->is_overdue($return_date) ) {
2040
        # fix fine days
2040
        # fix fine days
Lines 2406-2414 sub _FixOverduesOnReturn { Link Here
2406
            return 0 unless $accountlines->count; # no warning, there's just nothing to fix
2406
            return 0 unless $accountlines->count; # no warning, there's just nothing to fix
2407
2407
2408
            my $accountline = $accountlines->next;
2408
            my $accountline = $accountlines->next;
2409
            my $payments = $accountline->credits;
2409
2410
2410
            my $amountoutstanding = $accountline->amountoutstanding;
2411
            my $amountoutstanding = $accountline->amountoutstanding;
2411
            if ($exemptfine && ($amountoutstanding != 0)) {
2412
            if ( $accountline->amount == 0 && $payments->count == 0 ) {
2413
                $accountline->delete;
2414
            } elsif ($exemptfine && ($amountoutstanding != 0)) {
2412
                my $account = Koha::Account->new({patron_id => $borrowernumber});
2415
                my $account = Koha::Account->new({patron_id => $borrowernumber});
2413
                my $credit = $account->add_credit(
2416
                my $credit = $account->add_credit(
2414
                    {
2417
                    {
Lines 2423-2438 sub _FixOverduesOnReturn { Link Here
2423
2426
2424
                $credit->apply({ debits => [ $accountline ], offset_type => 'Forgiven' });
2427
                $credit->apply({ debits => [ $accountline ], offset_type => 'Forgiven' });
2425
2428
2426
                $accountline->status('FORGIVEN');
2427
2428
                if (C4::Context->preference("FinesLog")) {
2429
                if (C4::Context->preference("FinesLog")) {
2429
                    &logaction("FINES", 'MODIFY',$borrowernumber,"Overdue forgiven: item $item");
2430
                    &logaction("FINES", 'MODIFY',$borrowernumber,"Overdue forgiven: item $item");
2430
                }
2431
                }
2432
2433
                $accountline->status('FORGIVEN');
2434
                $accountline->store();
2431
            } else {
2435
            } else {
2432
                $accountline->status($status);
2436
                $accountline->status($status);
2433
            }
2437
                $accountline->store();
2434
2438
2435
            return $accountline->store();
2439
            }
2436
        }
2440
        }
2437
    );
2441
    );
2438
2442
(-)a/t/db_dependent/Circulation.t (-52 / +184 lines)
Lines 18-24 Link Here
18
use Modern::Perl;
18
use Modern::Perl;
19
use utf8;
19
use utf8;
20
20
21
use Test::More tests => 47;
21
use Test::More tests => 46;
22
use Test::MockModule;
22
use Test::MockModule;
23
23
24
use Data::Dumper;
24
use Data::Dumper;
Lines 2034-2040 subtest 'CanBookBeIssued + AutoReturnCheckedOutItems' => sub { Link Here
2034
2034
2035
2035
2036
subtest 'AddReturn | is_overdue' => sub {
2036
subtest 'AddReturn | is_overdue' => sub {
2037
    plan tests => 6;
2037
    plan tests => 8;
2038
2038
2039
    t::lib::Mocks::mock_preference('CalculateFinesOnReturn', 1);
2039
    t::lib::Mocks::mock_preference('CalculateFinesOnReturn', 1);
2040
    t::lib::Mocks::mock_preference('finesMode', 'production');
2040
    t::lib::Mocks::mock_preference('finesMode', 'production');
Lines 2068-2073 subtest 'AddReturn | is_overdue' => sub { Link Here
2068
2068
2069
    my $now   = dt_from_string;
2069
    my $now   = dt_from_string;
2070
    my $one_day_ago   = $now->clone->subtract( days => 1 );
2070
    my $one_day_ago   = $now->clone->subtract( days => 1 );
2071
    my $two_days_ago  = $now->clone->subtract( days => 2 );
2071
    my $five_days_ago = $now->clone->subtract( days => 5 );
2072
    my $five_days_ago = $now->clone->subtract( days => 5 );
2072
    my $ten_days_ago  = $now->clone->subtract( days => 10 );
2073
    my $ten_days_ago  = $now->clone->subtract( days => 10 );
2073
    $patron = Koha::Patrons->find( $patron->{borrowernumber} );
2074
    $patron = Koha::Patrons->find( $patron->{borrowernumber} );
Lines 2132-2140 subtest 'AddReturn | is_overdue' => sub { Link Here
2132
        Koha::Account::Lines->search({ borrowernumber => $patron->borrowernumber })->delete;
2133
        Koha::Account::Lines->search({ borrowernumber => $patron->borrowernumber })->delete;
2133
    };
2134
    };
2134
2135
2135
    subtest 'bug 25417 | backdated return + exemptfine' => sub {
2136
    subtest 'bug 8338 | backdated return resulting in zero amount fine' => sub {
2136
2137
2137
        plan tests => 6;
2138
        plan tests => 17;
2138
2139
2139
        t::lib::Mocks::mock_preference('CalculateFinesOnBackdate', 1);
2140
        t::lib::Mocks::mock_preference('CalculateFinesOnBackdate', 1);
2140
2141
Lines 2156-2175 subtest 'AddReturn | is_overdue' => sub { Link Here
2156
        is( int( $patron->account->balance() ),
2157
        is( int( $patron->account->balance() ),
2157
            1, "Overdue fine of 1 day overdue" );
2158
            1, "Overdue fine of 1 day overdue" );
2158
2159
2159
        # Backdated return (dropbox mode example - charge should exist but be zero)
2160
        # Backdated return (dropbox mode example - charge should be removed)
2160
        AddReturn( $item->{barcode}, $library->{branchcode}, 1, $one_day_ago );
2161
        AddReturn( $item->{barcode}, $library->{branchcode}, 1, $one_day_ago );
2161
        is( int( $patron->account->balance() ),
2162
        is( int( $patron->account->balance() ),
2162
            0, "Overdue fine should be annulled" );
2163
            0, "Overdue fine should be annulled" );
2163
        my $lines = Koha::Account::Lines->search({ borrowernumber => $patron->borrowernumber });
2164
        my $lines = Koha::Account::Lines->search({ borrowernumber => $patron->borrowernumber });
2164
        is( $lines->count, 1, "Overdue fine accountlines still exists");
2165
        is( $lines->count, 0, "Overdue fine accountline has been removed");
2166
2167
        $issue = AddIssue( $patron->unblessed, $item->{barcode}, $two_days_ago );    # date due was 2d ago
2168
2169
        # Fake fines cronjob on this checkout
2170
        ($fine) =
2171
          CalcFine( $item, $patron->categorycode, $library->{branchcode},
2172
            $two_days_ago, $now );
2173
        UpdateFine(
2174
            {
2175
                issue_id       => $issue->issue_id,
2176
                itemnumber     => $item->{itemnumber},
2177
                borrowernumber => $patron->borrowernumber,
2178
                amount         => $fine,
2179
                due            => output_pref($one_day_ago)
2180
            }
2181
        );
2182
        is( int( $patron->account->balance() ),
2183
            2, "Overdue fine of 2 days overdue" );
2184
2185
        # Payment made against fine
2186
        $lines = Koha::Account::Lines->search({ borrowernumber => $patron->borrowernumber });
2187
        my $debit = $lines->next;
2188
        my $credit = $patron->account->add_credit(
2189
            {
2190
                amount    => 2,
2191
                type      => 'PAYMENT',
2192
                interface => 'test',
2193
            }
2194
        );
2195
        $credit->apply(
2196
            { debits => [ $debit ], offset_type => 'Payment' } );
2197
2198
        is( int( $patron->account->balance() ),
2199
            0, "Overdue fine should be paid off" );
2200
        $lines = Koha::Account::Lines->search({ borrowernumber => $patron->borrowernumber });
2201
        is ( $lines->count, 2, "Overdue (debit) and Payment (credit) present");
2165
        my $line = $lines->next;
2202
        my $line = $lines->next;
2166
        is($line->amount+0,0, "Overdue fine amount has been reduced to 0");
2203
        is( $line->amount+0, 2, "Overdue fine amount remains as 2 days");
2167
        is($line->amountoutstanding+0,0, "Overdue fine amount outstanding has been reduced to 0");
2204
        is( $line->amountoutstanding+0, 0, "Overdue fine amountoutstanding reduced to 0");
2168
        is($line->status,'RETURNED', "Overdue fine was fixed");
2205
2206
        # Backdated return (dropbox mode example - charge should be removed)
2207
        AddReturn( $item->{barcode}, $library->{branchcode}, undef, $one_day_ago );
2208
        is( int( $patron->account->balance() ),
2209
            -1, "Refund credit has been applied" );
2210
        $lines = Koha::Account::Lines->search({ borrowernumber => $patron->borrowernumber }, { order_by => { '-asc' => 'accountlines_id' }});
2211
        is( $lines->count, 3, "Overdue (debit), Payment (credit) and Refund (credit) are all present");
2212
2213
        $line = $lines->next;
2214
        is($line->amount+0,1, "Overdue fine amount has been reduced to 1");
2215
        is($line->amountoutstanding+0,0, "Overdue fine amount outstanding remains at 0");
2216
        is($line->status,'RETURNED', "Overdue fine is fixed");
2217
        $line = $lines->next;
2218
        is($line->amount+0,-2, "Original payment amount remains as 2");
2219
        is($line->amountoutstanding+0,0, "Original payment remains applied");
2220
        $line = $lines->next;
2221
        is($line->amount+0,-1, "Refund amount correctly set to 1");
2222
        is($line->amountoutstanding+0,-1, "Refund amount outstanding unspent");
2223
2224
        # Cleanup
2225
        Koha::Account::Lines->search({ borrowernumber => $patron->borrowernumber })->delete;
2226
    };
2227
2228
    subtest 'bug 25417 | backdated return + exemptfine' => sub {
2229
2230
        plan tests => 2;
2231
2232
        t::lib::Mocks::mock_preference('CalculateFinesOnBackdate', 1);
2233
2234
        my $issue = AddIssue( $patron->unblessed, $item->{barcode}, $one_day_ago );    # date due was 1d ago
2235
2236
        # Fake fines cronjob on this checkout
2237
        my ($fine) =
2238
          CalcFine( $item, $patron->categorycode, $library->{branchcode},
2239
            $one_day_ago, $now );
2240
        UpdateFine(
2241
            {
2242
                issue_id       => $issue->issue_id,
2243
                itemnumber     => $item->{itemnumber},
2244
                borrowernumber => $patron->borrowernumber,
2245
                amount         => $fine,
2246
                due            => output_pref($one_day_ago)
2247
            }
2248
        );
2249
        is( int( $patron->account->balance() ),
2250
            1, "Overdue fine of 1 day overdue" );
2251
2252
        # Backdated return (dropbox mode example - charge should no longer exist)
2253
        AddReturn( $item->{barcode}, $library->{branchcode}, 1, $one_day_ago );
2254
        is( int( $patron->account->balance() ),
2255
            0, "Overdue fine should be annulled" );
2169
2256
2170
        # Cleanup
2257
        # Cleanup
2171
        Koha::Account::Lines->search({ borrowernumber => $patron->borrowernumber })->delete;
2258
        Koha::Account::Lines->search({ borrowernumber => $patron->borrowernumber })->delete;
2172
    };
2259
    };
2260
2261
    subtest 'bug 24075 | backdated return with return datetime matching due datetime' => sub {
2262
        plan tests => 7;
2263
2264
        t::lib::Mocks::mock_preference( 'CalculateFinesOnBackdate', 1 );
2265
2266
        my $due_date = dt_from_string;
2267
        my $issue = AddIssue( $patron->unblessed, $item->{barcode}, $due_date );
2268
2269
        # Add fine
2270
        UpdateFine(
2271
            {
2272
                issue_id       => $issue->issue_id,
2273
                itemnumber     => $item->{itemnumber},
2274
                borrowernumber => $patron->borrowernumber,
2275
                amount         => 0.25,
2276
                due            => output_pref($due_date)
2277
            }
2278
        );
2279
        is( $patron->account->balance(),
2280
            0.25, 'Overdue fine of $0.25 recorded' );
2281
2282
        # Backdate return to exact due date and time
2283
        my ( undef, $message ) =
2284
          AddReturn( $item->{barcode}, $library->{branchcode},
2285
            undef, $due_date );
2286
2287
        my $accountline =
2288
          Koha::Account::Lines->find( { issue_id => $issue->id } );
2289
        ok( !$accountline, 'accountline removed as expected' );
2290
2291
        # Re-issue
2292
        $issue = AddIssue( $patron->unblessed, $item->{barcode}, $due_date );
2293
2294
        # Add fine
2295
        UpdateFine(
2296
            {
2297
                issue_id       => $issue->issue_id,
2298
                itemnumber     => $item->{itemnumber},
2299
                borrowernumber => $patron->borrowernumber,
2300
                amount         => .25,
2301
                due            => output_pref($due_date)
2302
            }
2303
        );
2304
        is( $patron->account->balance(),
2305
            0.25, 'Overdue fine of $0.25 recorded' );
2306
2307
        # Partial pay accruing fine
2308
        my $lines = Koha::Account::Lines->search(
2309
            {
2310
                borrowernumber => $patron->borrowernumber,
2311
                issue_id       => $issue->id
2312
            }
2313
        );
2314
        my $debit  = $lines->next;
2315
        my $credit = $patron->account->add_credit(
2316
            {
2317
                amount    => .20,
2318
                type      => 'PAYMENT',
2319
                interface => 'test',
2320
            }
2321
        );
2322
        $credit->apply( { debits => [$debit], offset_type => 'Payment' } );
2323
2324
        is( $patron->account->balance(), .05, 'Overdue fine reduced to $0.05' );
2325
2326
        # Backdate return to exact due date and time
2327
        ( undef, $message ) =
2328
          AddReturn( $item->{barcode}, $library->{branchcode},
2329
            undef, $due_date );
2330
2331
        $lines = Koha::Account::Lines->search(
2332
            {
2333
                borrowernumber => $patron->borrowernumber,
2334
                issue_id       => $issue->id
2335
            }
2336
        );
2337
        $accountline = $lines->next;
2338
        is( $accountline->amountoutstanding + 0,
2339
            0, 'Partially paid fee amount outstanding was reduced to 0' );
2340
        is( $accountline->amount + 0,
2341
            0, 'Partially paid fee amount was reduced to 0' );
2342
        is( $patron->account->balance(), -0.20, 'Patron refund recorded' );
2343
2344
        # Cleanup
2345
        Koha::Account::Lines->search(
2346
            { borrowernumber => $patron->borrowernumber } )->delete;
2347
    };
2173
};
2348
};
2174
2349
2175
subtest '_FixAccountForLostAndReturned' => sub {
2350
subtest '_FixAccountForLostAndReturned' => sub {
Lines 3461-3509 subtest 'CanBookBeIssued & RentalFeesCheckoutConfirmation' => sub { Link Here
3461
    $itemtype->rentalcharge_daily('0')->store;
3636
    $itemtype->rentalcharge_daily('0')->store;
3462
};
3637
};
3463
3638
3464
subtest "Test Backdating of Returns" => sub {
3465
    plan tests => 2;
3466
3467
    my $branch = $library2->{branchcode};
3468
    my $biblio = $builder->build_sample_biblio();
3469
    my $item = $builder->build_sample_item(
3470
        {
3471
            biblionumber     => $biblio->biblionumber,
3472
            library          => $branch,
3473
            itype            => $itemtype,
3474
        }
3475
    );
3476
3477
    my %a_borrower_data = (
3478
        firstname =>  'Kyle',
3479
        surname => 'Hall',
3480
        categorycode => $patron_category->{categorycode},
3481
        branchcode => $branch,
3482
    );
3483
    my $borrowernumber = Koha::Patron->new(\%a_borrower_data)->store->borrowernumber;
3484
    my $borrower = Koha::Patrons->find( $borrowernumber )->unblessed;
3485
3486
    my $due_date = dt_from_string;
3487
    my $issue = AddIssue( $borrower, $item->barcode, $due_date );
3488
    UpdateFine(
3489
        {
3490
            issue_id          => $issue->id(),
3491
            itemnumber        => $item->itemnumber,
3492
            borrowernumber    => $borrowernumber,
3493
            amount            => .25,
3494
            amountoutstanding => .25,
3495
            type              => q{}
3496
        }
3497
    );
3498
3499
3500
    my ( undef, $message ) = AddReturn( $item->barcode, $branch, undef, $due_date );
3501
3502
    my $accountline = Koha::Account::Lines->find( { issue_id => $issue->id } );
3503
    is( $accountline->amountoutstanding+0, 0, 'Fee amount outstanding was reduced to 0' );
3504
    is( $accountline->amount+0, 0, 'Fee amount was reduced to 0' );
3505
};
3506
3507
subtest 'Filling a hold should cancel existing transfer' => sub {
3639
subtest 'Filling a hold should cancel existing transfer' => sub {
3508
    plan tests => 4;
3640
    plan tests => 4;
3509
3641
(-)a/t/db_dependent/Circulation/Returns.t (-1 / +45 lines)
Lines 394-397 subtest 'BranchTransferLimitsType' => sub { Link Here
394
    is( $doreturn, 1, 'AddReturn should have checkin the item if BranchTransferLimitsType=itemtype');
394
    is( $doreturn, 1, 'AddReturn should have checkin the item if BranchTransferLimitsType=itemtype');
395
};
395
};
396
396
397
subtest 'Backdated returns should reduce fine if needed' => sub {
398
    plan tests => 3;
399
400
    t::lib::Mocks::mock_preference( "CalculateFinesOnReturn",   0 );
401
    t::lib::Mocks::mock_preference( "CalculateFinesOnBackdate", 1 );
402
403
    my $biblio = $builder->build_object( { class => 'Koha::Biblios' } );
404
    my $item = $builder->build_object(
405
        {
406
            class  => 'Koha::Items',
407
            value  => {
408
                biblionumber => $biblio->biblionumber,
409
                notforloan => 0,
410
                itemlost   => 0,
411
                withdrawn  => 0,
412
        }
413
    }
414
    );
415
    my $patron = $builder->build_object({class => 'Koha::Patrons'});
416
    my $checkout = AddIssue( $patron->unblessed, $item->barcode );
417
    my $fine = Koha::Account::Line->new({
418
        issue_id => $checkout->id,
419
        borrowernumber => $patron->id,
420
        itemnumber => $item->id,
421
        date => dt_from_string(),
422
        amount => 100,
423
        amountoutstanding => 100,
424
        debit_type_code => 'OVERDUE',
425
        status => 'UNRETURNED',
426
        timestamp => dt_from_string(),
427
        manager_id => undef,
428
        interface => 'cli',
429
        branchcode => $patron->branchcode,
430
    })->store();
431
432
    my $account = $patron->account;
433
    is( $account->balance+0, 100, "Account balance before return is 100");
434
435
    my ( $doreturn, $messages, $issue ) = AddReturn($item->barcode, undef, undef, dt_from_string('1999-01-01') );
436
    is( $account->balance+0, 0, "Account balance after return is 0");
437
438
    $fine = $fine->get_from_storage;
439
    is( $fine, undef, "Fine was removed correctly with a backdated return" );
440
};
441
397
$schema->storage->txn_rollback;
442
$schema->storage->txn_rollback;
398
- 

Return to bug 8338