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

(-)a/C4/Circulation.pm (-1 / +44 lines)
Lines 1036-1041 sub CanBookBeIssued { Link Here
1036
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1036
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1037
                    $needsconfirmation{'resbranchcode'} = $res->{branchcode};
1037
                    $needsconfirmation{'resbranchcode'} = $res->{branchcode};
1038
                    $needsconfirmation{'reswaitingdate'} = $res->{'waitingdate'};
1038
                    $needsconfirmation{'reswaitingdate'} = $res->{'waitingdate'};
1039
                    $needsconfirmation{resreserveid} = $res->{reserve_id};
1039
                }
1040
                }
1040
                elsif ( $restype eq "Reserved" ) {
1041
                elsif ( $restype eq "Reserved" ) {
1041
                    # The item is on reserve for someone else.
1042
                    # The item is on reserve for someone else.
Lines 1046-1054 sub CanBookBeIssued { Link Here
1046
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1047
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1047
                    $needsconfirmation{'resbranchcode'} = $patron->branchcode;
1048
                    $needsconfirmation{'resbranchcode'} = $patron->branchcode;
1048
                    $needsconfirmation{'resreservedate'} = $res->{reservedate};
1049
                    $needsconfirmation{'resreservedate'} = $res->{reservedate};
1050
                    $needsconfirmation{resreserveid} = $res->{reserve_id};
1049
                }
1051
                }
1050
            }
1052
            }
1051
        }
1053
        }
1054
1055
        my $now = dt_from_string();
1056
        my $preventCheckoutOnSameReservePeriod =
1057
            C4::Context->preference("PreventCheckoutOnSameReservePeriod");
1058
        my $reserves_on_same_period =
1059
            ReservesOnSamePeriod($item_object->biblionumber, $item_object->itemnumber, $now->ymd, $duedate->ymd);
1060
        if ($preventCheckoutOnSameReservePeriod && $reserves_on_same_period) {
1061
            my $reserve = $reserves_on_same_period->[0];
1062
            my $patron = Koha::Patrons->find( $reserve->{borrowernumber} );
1063
            my $branchname = Koha::Libraries->find($reserve->{branchcode})->branchname;
1064
1065
            $needsconfirmation{RESERVED} = 1;
1066
            $needsconfirmation{resfirstname} = $patron->firstname;
1067
            $needsconfirmation{ressurname} = $patron->surname;
1068
            $needsconfirmation{rescardnumber} = $patron->cardnumber;
1069
            $needsconfirmation{resborrowernumber} = $patron->borrowernumber;
1070
            $needsconfirmation{resbranchname} = $branchname;
1071
            $needsconfirmation{resreservedate} = $reserve->{reservedate};
1072
            $needsconfirmation{resreserveid} = $reserve->{reserve_id};
1073
        }
1074
1052
    }
1075
    }
1053
1076
1054
    ## CHECK AGE RESTRICTION
1077
    ## CHECK AGE RESTRICTION
Lines 2689-2695 already renewed the loan. $error will contain the reason the renewal can not pro Link Here
2689
=cut
2712
=cut
2690
2713
2691
sub CanBookBeRenewed {
2714
sub CanBookBeRenewed {
2692
    my ( $borrowernumber, $itemnumber, $override_limit ) = @_;
2715
    my ( $borrowernumber, $itemnumber, $override_limit, $date_due ) = @_;
2693
2716
2694
    my $dbh    = C4::Context->dbh;
2717
    my $dbh    = C4::Context->dbh;
2695
    my $renews = 1;
2718
    my $renews = 1;
Lines 2766-2771 sub CanBookBeRenewed { Link Here
2766
            }
2789
            }
2767
        }
2790
        }
2768
    }
2791
    }
2792
2793
    unless ($date_due) {
2794
        my $itemtype = $item->effective_itemtype;
2795
        my $patron_unblessed = $patron->unblessed;
2796
        $date_due = (C4::Context->preference('RenewalPeriodBase') eq 'date_due') ?
2797
                                        dt_from_string( $issue->date_due, 'sql' ) :
2798
                                        DateTime->now( time_zone => C4::Context->tz());
2799
        $date_due =  CalcDateDue($date_due, $itemtype, _GetCircControlBranch($item->unblessed(), $patron_unblessed), $patron_unblessed, 'is a renewal');
2800
    }
2801
2802
    my $now = dt_from_string();
2803
    my $biblionumber = $item->biblionumber;
2804
    my $preventCheckoutOnSameReservePeriod =
2805
        C4::Context->preference("PreventCheckoutOnSameReservePeriod");
2806
    my $reserves_on_same_period =
2807
        ReservesOnSamePeriod($biblionumber, $itemnumber, $now->ymd, $date_due->ymd);
2808
    if ($preventCheckoutOnSameReservePeriod && $reserves_on_same_period) {
2809
        $resfound = 1;
2810
    }
2811
2769
    return ( 0, "on_reserve" ) if $resfound;    # '' when no hold was found
2812
    return ( 0, "on_reserve" ) if $resfound;    # '' when no hold was found
2770
2813
2771
    return ( 1, undef ) if $override_limit;
2814
    return ( 1, undef ) if $override_limit;
(-)a/C4/Reserves.pm (-5 / +68 lines)
Lines 131-136 BEGIN { Link Here
131
        &SuspendAll
131
        &SuspendAll
132
132
133
        &GetReservesControlBranch
133
        &GetReservesControlBranch
134
        &ReservesOnSamePeriod
134
135
135
        IsItemOnHoldAndFound
136
        IsItemOnHoldAndFound
136
137
Lines 279-285 sub AddReserve { Link Here
279
280
280
=head2 CanBookBeReserved
281
=head2 CanBookBeReserved
281
282
282
  $canReserve = &CanBookBeReserved($borrowernumber, $biblionumber, $branchcode)
283
  $canReserve = &CanBookBeReserved($borrowernumber, $biblionumber, $branchcode, {
284
    hold_date => $hold_date,
285
    expiration_date => $expiration_date,
286
  })
283
  if ($canReserve eq 'OK') { #We can reserve this Item! }
287
  if ($canReserve eq 'OK') { #We can reserve this Item! }
284
288
285
See CanItemBeReserved() for possible return values.
289
See CanItemBeReserved() for possible return values.
Lines 287-293 See CanItemBeReserved() for possible return values. Link Here
287
=cut
291
=cut
288
292
289
sub CanBookBeReserved{
293
sub CanBookBeReserved{
290
    my ($borrowernumber, $biblionumber, $pickup_branchcode) = @_;
294
    my ($borrowernumber, $biblionumber, $pickup_branchcode, $params) = @_;
291
295
292
    my @itemnumbers = Koha::Items->search({ biblionumber => $biblionumber})->get_column("itemnumber");
296
    my @itemnumbers = Koha::Items->search({ biblionumber => $biblionumber})->get_column("itemnumber");
293
    #get items linked via host records
297
    #get items linked via host records
Lines 298-304 sub CanBookBeReserved{ Link Here
298
302
299
    my $canReserve = { status => '' };
303
    my $canReserve = { status => '' };
300
    foreach my $itemnumber (@itemnumbers) {
304
    foreach my $itemnumber (@itemnumbers) {
301
        $canReserve = CanItemBeReserved( $borrowernumber, $itemnumber, $pickup_branchcode );
305
        $canReserve = CanItemBeReserved( $borrowernumber, $itemnumber, $pickup_branchcode, $params );
302
        return { status => 'OK' } if $canReserve->{status} eq 'OK';
306
        return { status => 'OK' } if $canReserve->{status} eq 'OK';
303
    }
307
    }
304
    return $canReserve;
308
    return $canReserve;
Lines 306-312 sub CanBookBeReserved{ Link Here
306
310
307
=head2 CanItemBeReserved
311
=head2 CanItemBeReserved
308
312
309
  $canReserve = &CanItemBeReserved($borrowernumber, $itemnumber, $branchcode)
313
  $canReserve = &CanItemBeReserved($borrowernumber, $itemnumber, $branchcode, {
314
    hold_date => $hold_date,
315
    expiration_date => $expiration_date,
316
  })
310
  if ($canReserve->{status} eq 'OK') { #We can reserve this Item! }
317
  if ($canReserve->{status} eq 'OK') { #We can reserve this Item! }
311
318
312
@RETURNS { status => OK },              if the Item can be reserved.
319
@RETURNS { status => OK },              if the Item can be reserved.
Lines 324-330 sub CanBookBeReserved{ Link Here
324
=cut
331
=cut
325
332
326
sub CanItemBeReserved {
333
sub CanItemBeReserved {
327
    my ( $borrowernumber, $itemnumber, $pickup_branchcode ) = @_;
334
    my ( $borrowernumber, $itemnumber, $pickup_branchcode, $params ) = @_;
335
336
    $params //= {};
328
337
329
    my $dbh = C4::Context->dbh;
338
    my $dbh = C4::Context->dbh;
330
    my $ruleitemtype;    # itemtype of the matching issuing rule
339
    my $ruleitemtype;    # itemtype of the matching issuing rule
Lines 513-518 sub CanItemBeReserved { Link Here
513
        }
522
        }
514
    }
523
    }
515
524
525
    if (C4::Context->preference('PreventReservesOnSamePeriod')) {
526
        my $overlapping_reserves = ReservesOnSamePeriod($item->biblionumber, $item->itemnumber, $params->{hold_date}, $params->{expiration_date});
527
        if ($overlapping_reserves && 0 < scalar @$overlapping_reserves) {
528
            return { status => 'reservesOnSamePeriod' };
529
        }
530
    }
531
516
    return { status => 'OK' };
532
    return { status => 'OK' };
517
}
533
}
518
534
Lines 2243-2248 sub GetHoldRule { Link Here
2243
    return $rules;
2259
    return $rules;
2244
}
2260
}
2245
2261
2262
=head2 ReservesOnSamePeriod
2263
2264
    my $reserve = ReservesOnSamePeriod( $biblionumber, $itemnumber, $resdate, $expdate);
2265
2266
    Return the reserve that match the period ($resdate => $expdate),
2267
    undef if no reserve match.
2268
2269
=cut
2270
2271
sub ReservesOnSamePeriod {
2272
    my ($biblionumber, $itemnumber, $resdate, $expdate) = @_;
2273
2274
    unless ($resdate && $expdate) {
2275
        return;
2276
    }
2277
2278
    my @reserves = Koha::Holds->search({ biblionumber => $biblionumber });
2279
2280
    $resdate = output_pref({ str => $resdate, dateonly => 1, dateformat => 'iso' });
2281
    $expdate = output_pref({ str => $expdate, dateonly => 1, dateformat => 'iso' });
2282
2283
    my @reserves_overlaps;
2284
    foreach my $reserve ( @reserves ) {
2285
2286
        unless ($reserve->reservedate && $reserve->expirationdate) {
2287
            next;
2288
        }
2289
2290
        if (date_ranges_overlap($resdate, $expdate,
2291
                                $reserve->reservedate,
2292
                                $reserve->expirationdate)) {
2293
2294
            # If reserve is item level and the requested periods overlap.
2295
            if ($itemnumber && $reserve->itemnumber == $itemnumber ) {
2296
                return [$reserve->unblessed];
2297
            }
2298
            push @reserves_overlaps, $reserve->unblessed;
2299
        }
2300
    }
2301
2302
    if ( @reserves_overlaps >= Koha::Items->search({ biblionumber => $biblionumber })->count() ) {
2303
        return \@reserves_overlaps;
2304
    }
2305
2306
    return;
2307
}
2308
2246
=head1 AUTHOR
2309
=head1 AUTHOR
2247
2310
2248
Koha Development Team <http://koha-community.org/>
2311
Koha Development Team <http://koha-community.org/>
(-)a/Koha/DateUtils.pm (-1 / +43 lines)
Lines 24-30 use Koha::Exceptions; Link Here
24
use base 'Exporter';
24
use base 'Exporter';
25
25
26
our @EXPORT = (
26
our @EXPORT = (
27
    qw( dt_from_string output_pref format_sqldatetime )
27
    qw( dt_from_string output_pref format_sqldatetime date_ranges_overlap )
28
);
28
);
29
29
30
=head1 DateUtils
30
=head1 DateUtils
Lines 322-325 sub format_sqldatetime { Link Here
322
    return q{};
322
    return q{};
323
}
323
}
324
324
325
=head2 date_ranges_overlap
326
327
    $bool = date_ranges_overlap($start1, $end1, $start2, $end2);
328
329
    Tells if first range ($start1 => $end1) overlaps
330
    the second one ($start2 => $end2)
331
332
=cut
333
334
sub date_ranges_overlap {
335
    my ($start1, $end1, $start2, $end2) = @_;
336
337
    $start1 = dt_from_string( $start1, 'iso' );
338
    $end1 = dt_from_string( $end1, 'iso' );
339
    $start2 = dt_from_string( $start2, 'iso' );
340
    $end2 = dt_from_string( $end2, 'iso' );
341
342
    if (
343
        # Start of range 2 is in the range 1.
344
        (
345
            DateTime->compare($start2, $start1) >= 0
346
            && DateTime->compare($start2, $end1) <= 0
347
        )
348
        ||
349
        # End of range 2 is in the range 1.
350
        (
351
            DateTime->compare($end2, $start1) >= 0
352
            && DateTime->compare($end2, $end1) <= 0
353
        )
354
        ||
355
        # Range 2 start before and end after range 1.
356
        (
357
            DateTime->compare($start2, $start1) < 0
358
            && DateTime->compare($end2, $end1) > 0
359
        )
360
    ) {
361
        return 1;
362
    }
363
364
    return;
365
}
366
325
1;
367
1;
(-)a/Koha/REST/V1/Holds.pm (-2 / +7 lines)
Lines 138-147 sub add { Link Here
138
            );
138
            );
139
        }
139
        }
140
140
141
        my $can_place_hold_params = {
142
            hold_date => dt_from_string($hold_date),
143
            expiration_date => dt_from_string($expiration_date),
144
        };
145
141
        my $can_place_hold
146
        my $can_place_hold
142
            = $item_id
147
            = $item_id
143
            ? C4::Reserves::CanItemBeReserved( $patron_id, $item_id )
148
            ? C4::Reserves::CanItemBeReserved( $patron_id, $item_id, $pickup_library_id, $can_place_hold_params )
144
            : C4::Reserves::CanBookBeReserved( $patron_id, $biblio_id );
149
            : C4::Reserves::CanBookBeReserved( $patron_id, $biblio_id, $pickup_library_id, $can_place_hold_params );
145
150
146
        my $can_override = C4::Context->preference('AllowHoldPolicyOverride');
151
        my $can_override = C4::Context->preference('AllowHoldPolicyOverride');
147
152
(-)a/circ/circulation.pl (+4 lines)
Lines 434-439 if (@$barcodes) { Link Here
434
        }
434
        }
435
        unless($confirm_required) {
435
        unless($confirm_required) {
436
            my $switch_onsite_checkout = exists $messages->{ONSITE_CHECKOUT_WILL_BE_SWITCHED};
436
            my $switch_onsite_checkout = exists $messages->{ONSITE_CHECKOUT_WILL_BE_SWITCHED};
437
            if ( $cancelreserve eq 'cancel' ) {
438
                CancelReserve({ reserve_id => $query->param('reserveid') });
439
            }
440
            $cancelreserve = $cancelreserve eq 'revert' ? 'revert' : undef;
437
            my $issue = AddIssue( $patron->unblessed, $barcode, $datedue, $cancelreserve, undef, undef, { onsite_checkout => $onsite_checkout, auto_renew => $session->param('auto_renew'), switch_onsite_checkout => $switch_onsite_checkout, } );
441
            my $issue = AddIssue( $patron->unblessed, $barcode, $datedue, $cancelreserve, undef, undef, { onsite_checkout => $onsite_checkout, auto_renew => $session->param('auto_renew'), switch_onsite_checkout => $switch_onsite_checkout, } );
438
            $template_params->{issue} = $issue;
442
            $template_params->{issue} = $issue;
439
            $session->clear('auto_renew');
443
            $session->clear('auto_renew');
(-)a/installer/data/mysql/atomicupdate/bug_15261-add_preventchechoutonsamereserveperiod_syspref.sql (+1 lines)
Line 0 Link Here
1
INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) VALUES ('PreventCheckoutOnSameReservePeriod', '0', 'Prevent to checkout a document if a reserve on same period exists', NULL, 'YesNo');
(-)a/installer/data/mysql/atomicupdate/bug_15261-add_preventreservesonsameperiod_syspref.sql (+1 lines)
Line 0 Link Here
1
INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) VALUES ('PreventReservesOnSamePeriod', '0', 'Prevent to hold a document if a reserve on same period exists', NULL, 'YesNo');
(-)a/installer/data/mysql/sysprefs.sql (+2 lines)
Lines 498-503 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
498
('PayPalUser',  '', NULL ,  'Your PayPal API username ( email address )',  'Free'),
498
('PayPalUser',  '', NULL ,  'Your PayPal API username ( email address )',  'Free'),
499
('PrefillItem','0','','When a new item is added, should it be prefilled with last created item values?','YesNo'),
499
('PrefillItem','0','','When a new item is added, should it be prefilled with last created item values?','YesNo'),
500
('PreserveSerialNotes','1','','When a new "Expected" issue is generated, should it be prefilled with last created issue notes?','YesNo'),
500
('PreserveSerialNotes','1','','When a new "Expected" issue is generated, should it be prefilled with last created issue notes?','YesNo'),
501
('PreventCheckoutOnSameReservePeriod','0','','Prevent to checkout a document if a reserve on same period exists','YesNo'),
502
('PreventReservesOnSamePeriod','0','','Prevent to hold a document if a reserve on same period exists','YesNo'),
501
('previousIssuesDefaultSortOrder','asc','asc|desc','Specify the sort order of Previous Issues on the circulation page','Choice'),
503
('previousIssuesDefaultSortOrder','asc','asc|desc','Specify the sort order of Previous Issues on the circulation page','Choice'),
502
('printcirculationslips','1','','If ON, enable printing circulation receipts','YesNo'),
504
('printcirculationslips','1','','If ON, enable printing circulation receipts','YesNo'),
503
('PrintNoticesMaxLines','0','','If greater than 0, sets the maximum number of lines an overdue notice will print. If the number of items is greater than this number, the notice will end with a warning asking the borrower to check their online account for a full list of overdue items.','Integer'),
505
('PrintNoticesMaxLines','0','','If greater than 0, sets the maximum number of lines an overdue notice will print. If the number of items is greater than this number, the notice will end with a warning asking the borrower to check their online account for a full list of overdue items.','Integer'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (+12 lines)
Lines 499-504 Circulation: Link Here
499
            - "<br />ccode: [NEWFIC,NULL,DVD]"
499
            - "<br />ccode: [NEWFIC,NULL,DVD]"
500
            - "<br />itype: [NEWBK,\"\"]"
500
            - "<br />itype: [NEWBK,\"\"]"
501
            - "<br /> Note: the word 'NULL' can be used to block renewal on undefined fields, while an empty string \"\" will block on an empty (but defined) field."
501
            - "<br /> Note: the word 'NULL' can be used to block renewal on undefined fields, while an empty string \"\" will block on an empty (but defined) field."
502
        -
503
            - pref: PreventCheckoutOnSameReservePeriod
504
              choices:
505
                  yes: Do
506
                  no: "Don't"
507
            - If yes, checkouts periods can't overlap with a reserve period.
502
    Checkin Policy:
508
    Checkin Policy:
503
        -
509
        -
504
            - pref: HoldsAutoFill
510
            - pref: HoldsAutoFill
Lines 808-813 Circulation: Link Here
808
            - pref: UpdateItemWhenLostFromHoldList
814
            - pref: UpdateItemWhenLostFromHoldList
809
              type: textarea
815
              type: textarea
810
              syntax: text/x-yaml
816
              syntax: text/x-yaml
817
        -
818
            - pref: PreventReservesOnSamePeriod
819
              choices:
820
                  yes: Do
821
                  no: "Don't"
822
            - If yes, Reserves periods for the same document can't overlap.
811
    Interlibrary Loans:
823
    Interlibrary Loans:
812
        -
824
        -
813
            - pref: ILLModule
825
            - pref: ILLModule
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt (+2 lines)
Lines 214-219 Link Here
214
                                    [% IF ( RESERVED ) %]
214
                                    [% IF ( RESERVED ) %]
215
                                        <p>
215
                                        <p>
216
                                            <input type="checkbox" id="cancelreserve" name="cancelreserve" value="cancel" />
216
                                            <input type="checkbox" id="cancelreserve" name="cancelreserve" value="cancel" />
217
                                            <input type="hidden" name="reserveid" value="[% resreserveid | html %]" />
217
                                            <label for="cancelreserve">Cancel hold</label>
218
                                            <label for="cancelreserve">Cancel hold</label>
218
                                        </p>
219
                                        </p>
219
                                    [% END %]
220
                                    [% END %]
Lines 222-227 Link Here
222
                                        <p>
223
                                        <p>
223
                                            <label for="cancelreserve">Cancel hold</label>
224
                                            <label for="cancelreserve">Cancel hold</label>
224
                                            <input type="radio" value="cancel" name="cancelreserve" id="cancelreserve" /><br />
225
                                            <input type="radio" value="cancel" name="cancelreserve" id="cancelreserve" /><br />
226
                                            <input type="hidden" name="reserveid" value="[% resreserveid | html %]" />
225
                                            <label for="revertreserve">Revert waiting status</label>
227
                                            <label for="revertreserve">Revert waiting status</label>
226
                                            <input type="radio" value="revert" name="cancelreserve" id="revertreserve" checked="checked"/>
228
                                            <input type="radio" value="revert" name="cancelreserve" id="revertreserve" checked="checked"/>
227
                                        </p>
229
                                        </p>
(-)a/opac/opac-reserve.pl (-2 / +8 lines)
Lines 276-291 if ( $query->param('place_reserve') ) { Link Here
276
276
277
        my $expiration_date = $query->param("expiration_date_$biblioNum");
277
        my $expiration_date = $query->param("expiration_date_$biblioNum");
278
278
279
        my $canreserve_params = {
280
            hold_date => dt_from_string($startdate),
281
            expiration_date => dt_from_string($expiration_date),
282
        };
283
279
        my $rank = $biblioData->{rank};
284
        my $rank = $biblioData->{rank};
280
        if ( $itemNum ne '' ) {
285
        if ( $itemNum ne '' ) {
281
            $canreserve = 1 if CanItemBeReserved( $borrowernumber, $itemNum, $branch )->{status} eq 'OK';
286
            $canreserve = 1 if CanItemBeReserved( $borrowernumber, $itemNum, $branch, $canreserve_params )->{status} eq 'OK';
282
        }
287
        }
283
        else {
288
        else {
284
            $canreserve = 1 if CanBookBeReserved( $borrowernumber, $biblioNum, $branch )->{status} eq 'OK';
289
            $canreserve = 1 if CanBookBeReserved( $borrowernumber, $biblioNum, $branch, $canreserve_params )->{status} eq 'OK';
285
290
286
            # Inserts a null into the 'itemnumber' field of 'reserves' table.
291
            # Inserts a null into the 'itemnumber' field of 'reserves' table.
287
            $itemNum = undef;
292
            $itemNum = undef;
288
        }
293
        }
294
289
        my $notes = $query->param('notes_'.$biblioNum)||'';
295
        my $notes = $query->param('notes_'.$biblioNum)||'';
290
296
291
        if (   $maxreserves
297
        if (   $maxreserves
(-)a/svc/renew (-1 / +1 lines)
Lines 57-63 $data->{borrowernumber} = $borrowernumber; Link Here
57
$data->{branchcode} = $branchcode;
57
$data->{branchcode} = $branchcode;
58
58
59
( $data->{renew_okay}, $data->{error} ) =
59
( $data->{renew_okay}, $data->{error} ) =
60
  CanBookBeRenewed( $borrowernumber, $itemnumber, $override_limit );
60
  CanBookBeRenewed( $borrowernumber, $itemnumber, $override_limit, $date_due );
61
61
62
# If we're allowing reserved items to be renewed...
62
# If we're allowing reserved items to be renewed...
63
if ( $data->{error} && $data->{error} eq 'on_reserve' && C4::Context->preference('AllowRenewalOnHoldOverride')) {
63
if ( $data->{error} && $data->{error} eq 'on_reserve' && C4::Context->preference('AllowRenewalOnHoldOverride')) {
(-)a/t/db_dependent/Circulation.t (-1 / +61 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 => 46;
21
use Test::More tests => 48;
22
use Test::MockModule;
22
use Test::MockModule;
23
use Test::Deep qw( cmp_deeply );
23
use Test::Deep qw( cmp_deeply );
24
24
Lines 379-384 subtest "CanBookBeRenewed tests" => sub { Link Here
379
    my $borrowing_borrowernumber = Koha::Checkouts->find( { itemnumber => $item_1->itemnumber } )->borrowernumber;
379
    my $borrowing_borrowernumber = Koha::Checkouts->find( { itemnumber => $item_1->itemnumber } )->borrowernumber;
380
    is ($borrowing_borrowernumber, $renewing_borrowernumber, "Item checked out to $renewing_borrower->{firstname} $renewing_borrower->{surname}");
380
    is ($borrowing_borrowernumber, $renewing_borrowernumber, "Item checked out to $renewing_borrower->{firstname} $renewing_borrower->{surname}");
381
381
382
    t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary');
382
    my ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $item_1->itemnumber, 1);
383
    my ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $item_1->itemnumber, 1);
383
    is( $renewokay, 1, 'Can renew, no holds for this title or item');
384
    is( $renewokay, 1, 'Can renew, no holds for this title or item');
384
385
Lines 1476-1481 subtest "AllowRenewalIfOtherItemsAvailable tests" => sub { Link Here
1476
    is( $error, 'onsite_checkout', 'A correct error code should be returned by CanBookBeRenewed for on-site checkout' );
1477
    is( $error, 'onsite_checkout', 'A correct error code should be returned by CanBookBeRenewed for on-site checkout' );
1477
}
1478
}
1478
1479
1480
{
1481
    # Don't allow renewing on reserve period with PreventCheckoutOnSameReservePeriod enabled
1482
    t::lib::Mocks::mock_preference( 'PreventCheckoutOnSameReservePeriod', 1 );
1483
1484
    my $start_issue_dt = DateTime->now();
1485
    $start_issue_dt->subtract( days => 15);
1486
    my $due_date = $start_issue_dt->clone;
1487
    $due_date->add( days => 17 );
1488
1489
    my $biblio = $builder->build({
1490
        source => 'Biblio',
1491
    });
1492
    my $biblionumber = $biblio->{biblionumber};
1493
1494
    my $item = $builder->build({
1495
        source => 'Item',
1496
        value => {
1497
            biblionumber => $biblionumber
1498
        }
1499
    });
1500
    my $itemnumber = $item->{itemnumber};
1501
1502
    my $issue = $builder->build({
1503
        source => 'Issue',
1504
        value => {
1505
            itemnumber => $itemnumber,
1506
            biblionumber => $biblionumber,
1507
            issuedate => $start_issue_dt->ymd,
1508
            date_due => $due_date->ymd,
1509
            onsite_checkout => 0
1510
        }
1511
    });
1512
1513
    my $reservedate = $due_date->clone;
1514
    $reservedate->add( days => 5 );
1515
    my $expirationdate = $reservedate->clone;
1516
    $expirationdate->add( days => 15 );
1517
    $builder->build({
1518
        source => 'Reserve',
1519
        value => {
1520
            biblionumber => $biblionumber,
1521
            itemnumber => $itemnumber,
1522
            reservedate => $reservedate->ymd,
1523
            expirationdate => $expirationdate->ymd
1524
        }
1525
    });
1526
1527
    my $requested_date_due = $due_date->clone;
1528
    $requested_date_due->add( days => 4 );
1529
    my ( $renewed, $error ) = CanBookBeRenewed( $issue->{borrowernumber}, $itemnumber, 1, $requested_date_due );
1530
    is( $renewed, 1, 'CanBookBeRenewed should allow to renew if no reserve date overlap' );
1531
1532
    $requested_date_due->add( days => 2 );
1533
    ( $renewed, $error ) = CanBookBeRenewed( $issue->{borrowernumber}, $itemnumber, 1, $requested_date_due );
1534
    is( $renewed, 0, 'CanBookBeRenewed should not allow to renew if reserve date overlap' );
1535
1536
    t::lib::Mocks::mock_preference( 'PreventCheckoutOnSameReservePeriod', 0 );
1537
}
1538
1479
{
1539
{
1480
    my $library = $builder->build({ source => 'Branch' });
1540
    my $library = $builder->build({ source => 'Branch' });
1481
1541
(-)a/t/db_dependent/Circulation/CanBookBeIssued.t (+106 lines)
Line 0 Link Here
1
#!/usr/bin/env perl
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Test::More tests => 1;
21
use C4::Members;
22
use C4::Reserves;
23
use C4::Circulation;
24
use Koha::DateUtils;
25
26
use t::lib::TestBuilder;
27
28
my $schema  = Koha::Database->new->schema;
29
$schema->storage->txn_begin;
30
31
my $builder = t::lib::TestBuilder->new();
32
33
subtest 'Tests for CanBookBeIssued with overlap reserves' => sub {
34
    plan tests => 6;
35
36
    my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
37
    my $branch = $builder->build({ source => 'Branch' });
38
    my $branchcode = $branch->{branchcode};
39
40
    my $borrower = $builder->build_object({
41
        class => 'Koha::Patrons',
42
        value => {
43
            branchcode   => $branchcode,
44
            categorycode => $categorycode,
45
        }
46
    });
47
    my $borrowernumber = $borrower->borrowernumber;
48
49
    my $biblio = $builder->build({source => 'Biblio'});
50
    my $biblioitem = $builder->build({
51
        source => 'Biblioitem',
52
        value => {
53
            biblionumber => $biblio->{biblionumber},
54
        },
55
    });
56
    my $item = $builder->build({
57
        source => 'Item',
58
        value => {
59
            biblionumber => $biblio->{biblionumber},
60
            biblioitemnumber => $biblioitem->{biblioitemnumber},
61
            withdrawn => 0,
62
            itemlost => 0,
63
            notforloan => 0,
64
        },
65
    });
66
67
68
    my $startdate = dt_from_string();
69
    $startdate->add_duration(DateTime::Duration->new(days => 4));
70
    my $expdate = $startdate->clone();
71
    $expdate->add_duration(DateTime::Duration->new(days => 10));
72
73
    my $reserveid = AddReserve($branchcode, $borrowernumber,
74
        $item->{biblionumber}, undef,  1, $startdate->ymd(), $expdate->ymd,
75
        undef, undef, undef, undef);
76
77
    my $non_overlap_duedate = dt_from_string();
78
    $non_overlap_duedate->add_duration(DateTime::Duration->new(days => 2));
79
    my ($error, $question, $alerts ) =
80
        CanBookBeIssued($borrower, $item->{barcode}, $non_overlap_duedate, 1, 0);
81
82
    is_deeply($error, {}, "");
83
    is_deeply($question, {}, "");
84
    is_deeply($alerts, {}, "");
85
86
    my $overlap_duedate = dt_from_string();
87
    $overlap_duedate->add_duration(DateTime::Duration->new(days => 5));
88
    ($error, $question, $alerts ) =
89
        CanBookBeIssued($borrower, $item->{barcode}, $overlap_duedate, 1, 0);
90
91
    is_deeply($error, {}, "");
92
    my $expected = {
93
        RESERVED => 1,
94
        resfirstname => $borrower->firstname,
95
        ressurname => $borrower->surname,
96
        rescardnumber => $borrower->cardnumber,
97
        resborrowernumber => $borrower->borrowernumber,
98
        resbranchname => $branch->{branchname},
99
        resreservedate => $startdate->ymd,
100
        resreserveid => $reserveid,
101
    };
102
    is_deeply($question, $expected, "");
103
    is_deeply($alerts, {}, "");
104
};
105
106
$schema->storage->txn_rollback;
(-)a/t/db_dependent/Reserves/ReserveDate.t (-1 / +108 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Test::More tests => 6;
21
use Test::Warn;
22
23
use MARC::Record;
24
use DateTime::Duration;
25
26
use C4::Biblio;
27
use C4::Items;
28
use C4::Members;
29
use C4::Circulation;
30
use Koha::Holds;
31
use t::lib::TestBuilder;
32
33
use Koha::DateUtils;
34
35
36
use_ok('C4::Reserves');
37
38
my $dbh = C4::Context->dbh;
39
40
# Start transaction
41
$dbh->{AutoCommit} = 0;
42
$dbh->{RaiseError} = 1;
43
44
my $builder = t::lib::TestBuilder->new();
45
my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
46
my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode };
47
48
my $borrower = $builder->build({
49
    source => 'Borrower',
50
    value => {
51
        branchcode   => $branchcode,
52
        categorycode => $categorycode,
53
    }
54
});
55
56
my $borrower2 = $builder->build({
57
    source => 'Borrower',
58
    value => {
59
        branchcode   => $branchcode,
60
        categorycode => $categorycode,
61
    }
62
});
63
64
my $borrowernumber = $borrower->{borrowernumber};
65
my $borrowernumber2 = $borrower2->{borrowernumber};
66
67
# Create a helper biblio
68
my $biblio = MARC::Record->new();
69
my $title = 'Alone in the Dark';
70
my $author = 'Karen Rose';
71
if( C4::Context->preference('marcflavour') eq 'UNIMARC' ) {
72
    $biblio->append_fields(
73
        MARC::Field->new('600', '', '1', a => $author),
74
        MARC::Field->new('200', '', '', a => $title),
75
    );
76
}
77
else {
78
    $biblio->append_fields(
79
        MARC::Field->new('100', '', '', a => $author),
80
        MARC::Field->new('245', '', '', a => $title),
81
    );
82
}
83
my ($bibnum, $bibitemnum);
84
($bibnum, $title, $bibitemnum) = AddBiblio($biblio, '');
85
86
my ($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem({ homebranch => $branchcode, holdingbranch => $branchcode, barcode => '333' } , $bibnum);
87
88
C4::Context->set_preference('AllowHoldDateInFuture', 1);
89
90
AddReserve($branchcode, $borrowernumber, $bibnum,
91
           $bibitemnum,  1, '2015-11-01', '2015-11-20', undef,
92
           undef, undef, undef);
93
94
is(ReservesOnSamePeriod($bibnum, undef, '2015-11-25', '2015-11-30'), undef, "Period doesn't overlaps");
95
96
ok(ReservesOnSamePeriod($bibnum, undef, '2015-11-02', '2015-11-10'), "Period overlaps");
97
98
my ($item_bibnum2, $item_bibitemnum2, $itemnumber2) = AddItem({ homebranch => $branchcode, holdingbranch => $branchcode, barcode => '444' } , $bibnum);
99
is(ReservesOnSamePeriod($bibnum, undef, '2015-11-02', '2015-11-10'), undef, "Period overlaps but there is 2 items");
100
101
AddReserve($branchcode, $borrowernumber2, $bibnum,
102
           $bibitemnum,  1, '2016-02-01', '2016-02-10', undef,
103
           undef, $itemnumber, undef);
104
is(ReservesOnSamePeriod($bibnum, $itemnumber, '02/12/2015', '10/12/2015'), undef, "Period on item does not overlap (with metric date format)");
105
106
my $reserve = ReservesOnSamePeriod($bibnum, $itemnumber, '2016-01-31', '2016-02-05');
107
is($reserve->[0]->{itemnumber}, $itemnumber, 'Period on item overlaps');
108
$dbh->rollback;

Return to bug 15261