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

(-)a/C4/Circulation.pm (-7 / +3 lines)
Lines 20-26 package C4::Circulation; Link Here
20
20
21
use Modern::Perl;
21
use Modern::Perl;
22
use DateTime;
22
use DateTime;
23
use POSIX qw( floor );
23
use POSIX qw( floor ceil );
24
use YAML::XS;
24
use YAML::XS;
25
use Encode;
25
use Encode;
26
26
Lines 2699-2712 sub _calculate_new_debar_dt { Link Here
2699
    );
2699
    );
2700
    my $finedays = $issuing_rule ? $issuing_rule->{finedays} : undef;
2700
    my $finedays = $issuing_rule ? $issuing_rule->{finedays} : undef;
2701
    my $unit     = $issuing_rule ? $issuing_rule->{lengthunit} : undef;
2701
    my $unit     = $issuing_rule ? $issuing_rule->{lengthunit} : undef;
2702
    my $chargeable_units = C4::Overdues::get_chargeable_units($unit, $dt_due, $return_date, $branchcode);
2702
    my $chargeable_units = C4::Overdues::get_chargeable_units('days', $dt_due, $return_date, $branchcode);
2703
2703
2704
    return unless $finedays;
2704
    return unless $finedays;
2705
2705
2706
    # finedays is in days, so hourly loans must multiply by 24
2707
    # thus 1 hour late equals 1 day suspension * finedays rate
2708
    $finedays = $finedays * 24 if ( $unit eq 'hours' );
2709
2710
    # grace period is measured in the same units as the loan
2706
    # grace period is measured in the same units as the loan
2711
    my $grace =
2707
    my $grace =
2712
      DateTime::Duration->new( $unit => $issuing_rule->{firstremind} // 0);
2708
      DateTime::Duration->new( $unit => $issuing_rule->{firstremind} // 0);
Lines 2721-2727 sub _calculate_new_debar_dt { Link Here
2721
        if ( defined $issuing_rule->{suspension_chargeperiod} && $issuing_rule->{suspension_chargeperiod} > 1 ) {
2717
        if ( defined $issuing_rule->{suspension_chargeperiod} && $issuing_rule->{suspension_chargeperiod} > 1 ) {
2722
            # No need to / 1 and do not consider / 0
2718
            # No need to / 1 and do not consider / 0
2723
            $suspension_days = DateTime::Duration->new(
2719
            $suspension_days = DateTime::Duration->new(
2724
                days => floor( $suspension_days->in_units('days') / $issuing_rule->{suspension_chargeperiod} )
2720
                days => ceil( $suspension_days->in_units('days') / $issuing_rule->{suspension_chargeperiod} )
2725
            );
2721
            );
2726
        }
2722
        }
2727
2723
(-)a/C4/Overdues.pm (-19 / +14 lines)
Lines 321-346 sub get_chargeable_units { Link Here
321
321
322
    my $charge_units = 0;
322
    my $charge_units = 0;
323
    my $charge_duration;
323
    my $charge_duration;
324
    if ($unit eq 'hours') {
324
    if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
325
        if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
325
        my $calendar = Koha::Calendar->new( branchcode => $branchcode );
326
            my $calendar = Koha::Calendar->new( branchcode => $branchcode );
326
        $charge_duration = $calendar->hours_between( $date_due, $date_returned );
327
            $charge_duration = $calendar->hours_between( $date_due, $date_returned );
327
    } else {
328
        } else {
328
        $charge_duration = $date_returned->delta_ms( $date_due );
329
            $charge_duration = $date_returned->delta_ms( $date_due );
330
        }
331
        if($charge_duration->in_units('hours') == 0 && $charge_duration->in_units('seconds') > 0){
332
            return 1;
333
        }
334
        return $charge_duration->in_units('hours');
335
    }
329
    }
336
    else { # days
330
    if($charge_duration->in_units('hours') == 0 && $charge_duration->in_units('seconds') > 0){
337
        if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
331
        return 1;
338
            my $calendar = Koha::Calendar->new( branchcode => $branchcode );
332
    }
339
            $charge_duration = $calendar->days_between( $date_due, $date_returned );
333
340
        } else {
334
    my $hours = $charge_duration->in_units('hours');
341
            $charge_duration = $date_returned->delta_days( $date_due );
335
    if ($unit eq 'hours') {
342
        }
336
        return $hours;
343
        return $charge_duration->in_units('days');
337
    } else {
338
        return ( $hours > 0 ? ceil($hours / 24) : 0 );
344
    }
339
    }
345
}
340
}
346
341
(-)a/t/db_dependent/Circulation/maxsuspensiondays.t (-6 / +116 lines)
Lines 15-20 use Koha::Patrons; Link Here
15
use t::lib::TestBuilder;
15
use t::lib::TestBuilder;
16
use t::lib::Mocks;
16
use t::lib::Mocks;
17
17
18
use POSIX qw( ceil );
19
18
my $schema = Koha::Database->schema;
20
my $schema = Koha::Database->schema;
19
$schema->storage->txn_begin;
21
$schema->storage->txn_begin;
20
my $builder = t::lib::TestBuilder->new;
22
my $builder = t::lib::TestBuilder->new;
Lines 129-137 subtest "suspension_chargeperiod" => sub { Link Here
129
    my $last_year = dt_from_string->clone->subtract( years => 1 );
131
    my $last_year = dt_from_string->clone->subtract( years => 1 );
130
    my $today = dt_from_string;
132
    my $today = dt_from_string;
131
    my $new_debar_dt = C4::Circulation::_calculate_new_debar_dt( $patron, $item, $last_year, $today );
133
    my $new_debar_dt = C4::Circulation::_calculate_new_debar_dt( $patron, $item, $last_year, $today );
132
    is( $new_debar_dt->truncate( to => 'day' ),
134
    is(
133
        $today->clone->add( days => 365 / 15 * 7 )->truncate( to => 'day' ) );
135
        $new_debar_dt->truncate( to => 'day' ),
134
136
        $today->clone->add( days => ceil( 365 / 15 * 7 ) )
137
          ->truncate( to => 'day' )
138
      ),
139
      'the truncated dates are equal.'
135
};
140
};
136
141
137
subtest "maxsuspensiondays" => sub {
142
subtest "maxsuspensiondays" => sub {
Lines 156-162 subtest "maxsuspensiondays" => sub { Link Here
156
    my $today = dt_from_string;
161
    my $today = dt_from_string;
157
    my $new_debar_dt = C4::Circulation::_calculate_new_debar_dt( $patron, $item, $last_year, $today );
162
    my $new_debar_dt = C4::Circulation::_calculate_new_debar_dt( $patron, $item, $last_year, $today );
158
    is( $new_debar_dt->truncate( to => 'day' ),
163
    is( $new_debar_dt->truncate( to => 'day' ),
159
        $today->clone->add( days => 333 )->truncate( to => 'day' ) );
164
        $today->clone->add( days => 333 )->truncate( to => 'day' ));
160
};
165
166
167
    Koha::CirculationRules->set_rules(
168
        {
169
            categorycode => undef,
170
            itemtype     => undef,
171
            branchcode   => undef,
172
            rules        => {
173
                firstremind             => 0,
174
                finedays                => 2,
175
                lengthunit              => 'hours',
176
                suspension_chargeperiod => 1,
177
                maxsuspensiondays       => 100,
178
            }
179
        }
180
    );
181
182
    my $yesterday  = dt_from_string->clone->subtract( days => 1 );
183
    my $daysafter2 = dt_from_string->clone->add( days => 2 );
184
    AddIssue( $patron, $barcode, $yesterday );
185
    AddReturn( $barcode, $branchcode );
186
    $debarments    = $patron->restrictions;
187
    $THE_debarment = $debarments->next;
188
    is(
189
        $THE_debarment->expiration,
190
        output_pref(
191
            { dt => $daysafter2, dateformat => 'iso', dateonly => 1 }
192
        ),
193
        'calculate suspension with lengthunit hours.'
194
    );
195
196
    DelDebarment( $THE_debarment->borrower_debarment_id );
197
198
    my $hoursago2 = dt_from_string->clone->subtract( hours => 2 );
199
    AddIssue( $patron, $barcode, $hoursago2 );
200
    AddReturn( $barcode, $branchcode );
201
    $debarments    = $patron->restrictions;
202
    $THE_debarment = $debarments->next;
203
    is(
204
        $THE_debarment->expiration,
205
        output_pref(
206
            { dt => $daysafter2, dateformat => 'iso', dateonly => 1 }
207
        ),
208
        'calculate suspension with lengthunit hours.'
209
    );
161
210
211
    DelDebarment( $THE_debarment->borrower_debarment_id );
212
213
    my $hoursafter2 = dt_from_string->clone->add( hours => 2 );
214
    AddIssue( $patron, $barcode, $hoursafter2 );
215
    AddReturn( $barcode, $branchcode );
216
    $debarments    = $patron->restrictions;
217
    $THE_debarment = $debarments->next;
218
    is_deeply( $THE_debarment, undef, 'No debarment if in time' );
219
220
    # Add 1 day every 2 days
221
    Koha::CirculationRules->set_rules(
222
        {
223
            categorycode => undef,
224
            itemtype     => undef,
225
            branchcode   => undef,
226
            rules        => {
227
                firstremind             => 0,
228
                finedays                => 1,
229
                lengthunit              => 'hours',
230
                suspension_chargeperiod => 2,
231
                maxsuspensiondays       => 100,
232
            }
233
        }
234
    );
235
236
    # 20 days late => 20/2 * 1 => 10 days of suspension
237
    AddIssue( $patron, $barcode, $daysago20 );
238
    AddReturn( $barcode, $branchcode );
239
    $debarments    = $patron->restrictions;
240
    $THE_debarment = $debarments->next;
241
    is(
242
        $THE_debarment->expiration,
243
        output_pref(
244
            { dt => $daysafter10, dateformat => 'iso', dateonly => 1 }
245
        ),
246
        'calculate suspension with lengthunit hours.'
247
    );
248
249
    DelDebarment( $THE_debarment->borrower_debarment_id );
250
251
    # 1 day late => ceil(1/2) * 1 => 1 day of suspension
252
    my $tomorrow = dt_from_string->clone->add( days => 1 );
253
    AddIssue( $patron, $barcode, $yesterday );
254
    AddReturn( $barcode, $branchcode );
255
    $debarments    = $patron->restrictions;
256
    $THE_debarment = $debarments->next;
257
    is(
258
        $THE_debarment->expiration,
259
        output_pref( { dt => $tomorrow, dateformat => 'iso', dateonly => 1 } ),
260
        'calculate suspension with lengthunit hours.'
261
    );
262
263
    DelDebarment( $THE_debarment->borrower_debarment_id );
264
265
    # 2 hours late => ceil(.x/2) * 1 => 1 day of suspension
266
    AddIssue( $patron, $barcode, $hoursafter2 );
267
    AddReturn( $barcode, $branchcode );
268
    $debarments    = $patron->restrictions;
269
    $THE_debarment = $debarments->next;
270
    is_deeply( $THE_debarment, undef, 'No debarment if in time' );
271
272
};
162
$schema->storage->txn_rollback;
273
$schema->storage->txn_rollback;
163
- 

Return to bug 14293