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

(-)a/C4/Circulation.pm (-10 / +47 lines)
Lines 1053-1058 sub CanBookBeIssued { Link Here
1053
                }
1053
                }
1054
            }
1054
            }
1055
        }
1055
        }
1056
1057
        my $now = dt_from_string();
1058
        my $preventCheckoutOnSameReservePeriod =
1059
            C4::Context->preference("PreventCheckoutOnSameReservePeriod");
1060
        my $reserves_on_same_period =
1061
            ReservesOnSamePeriod($item_object->biblionumber, $item_object->itemnumber, $now->ymd, $duedate->ymd);
1062
        if ($preventCheckoutOnSameReservePeriod && $reserves_on_same_period) {
1063
            my $reserve = $reserves_on_same_period->[0];
1064
            my $patron = Koha::Patrons->find( $reserve->{borrowernumber} );
1065
            my $branchname = Koha::Libraries->find($reserve->{branchcode})->branchname;
1066
1067
            $needsconfirmation{RESERVED} = 1;
1068
            $needsconfirmation{resfirstname} = $patron->firstname;
1069
            $needsconfirmation{ressurname} = $patron->surname;
1070
            $needsconfirmation{rescardnumber} = $patron->cardnumber;
1071
            $needsconfirmation{resborrowernumber} = $patron->borrowernumber;
1072
            $needsconfirmation{resbranchname} = $branchname;
1073
            $needsconfirmation{resreservedate} = $reserve->{reservedate};
1074
            $needsconfirmation{reserve_id} = $reserve->{reserve_id};
1075
        }
1076
1056
    }
1077
    }
1057
1078
1058
    ## CHECK AGE RESTRICTION
1079
    ## CHECK AGE RESTRICTION
Lines 1293-1307 AddIssue does the following things : Link Here
1293
1314
1294
  - step 01: check that there is a borrowernumber & a barcode provided
1315
  - step 01: check that there is a borrowernumber & a barcode provided
1295
  - check for RENEWAL (book issued & being issued to the same patron)
1316
  - check for RENEWAL (book issued & being issued to the same patron)
1296
      - renewal YES = Calculate Charge & renew
1317
  - renewal YES = Calculate Charge & renew renewal NO = * BOOK
1297
      - renewal NO  =
1318
  - ACTUALLY ISSUED ? do a return if book is actually issued (but to
1298
          * BOOK ACTUALLY ISSUED ? do a return if book is actually issued (but to someone else)
1319
  - someone else) * RESERVE PLACED ?  fill reserve if reserve to this
1299
          * RESERVE PLACED ?
1320
  - patron cancel reserve or not, otherwise * TRANSFERT PENDING ?
1300
              - fill reserve if reserve to this patron
1321
  - complete the transfert * ISSUE THE BOOK
1301
              - cancel reserve or not, otherwise
1302
          * TRANSFERT PENDING ?
1303
              - complete the transfert
1304
          * ISSUE THE BOOK
1305
1322
1306
=back
1323
=back
1307
1324
Lines 2723-2729 already renewed the loan. $error will contain the reason the renewal can not pro Link Here
2723
=cut
2740
=cut
2724
2741
2725
sub CanBookBeRenewed {
2742
sub CanBookBeRenewed {
2726
    my ( $borrowernumber, $itemnumber, $override_limit ) = @_;
2743
    my ( $borrowernumber, $itemnumber, $override_limit, $date_due ) = @_;
2727
2744
2728
    my $dbh    = C4::Context->dbh;
2745
    my $dbh    = C4::Context->dbh;
2729
    my $renews = 1;
2746
    my $renews = 1;
Lines 2910-2915 sub CanBookBeRenewed { Link Here
2910
            }
2927
            }
2911
        }
2928
        }
2912
    }
2929
    }
2930
2931
    unless ($date_due) {
2932
        my $itemtype = $item->effective_itemtype;
2933
        my $patron_unblessed = $patron->unblessed;
2934
        $date_due = (C4::Context->preference('RenewalPeriodBase') eq 'date_due') ?
2935
                                        dt_from_string( $issue->date_due, 'sql' ) :
2936
                                        DateTime->now( time_zone => C4::Context->tz());
2937
        $date_due =  CalcDateDue($date_due, $itemtype, _GetCircControlBranch($item->unblessed(), $patron_unblessed), $patron_unblessed, 'is a renewal');
2938
    }
2939
2940
    my $now = dt_from_string();
2941
    my $biblionumber = $item->biblionumber;
2942
    my $preventCheckoutOnSameReservePeriod =
2943
        C4::Context->preference("PreventCheckoutOnSameReservePeriod");
2944
    my $reserves_on_same_period =
2945
        ReservesOnSamePeriod($biblionumber, $itemnumber, $now->ymd, $date_due->ymd);
2946
    if ($preventCheckoutOnSameReservePeriod && $reserves_on_same_period) {
2947
        $resfound = 1;
2948
    }
2949
2913
    return ( 0, "on_reserve" ) if $resfound;    # '' when no hold was found
2950
    return ( 0, "on_reserve" ) if $resfound;    # '' when no hold was found
2914
    return ( 0, "auto_renew" ) if $auto_renew && !$override_limit; # 0 if auto-renewal should not succeed
2951
    return ( 0, "auto_renew" ) if $auto_renew && !$override_limit; # 0 if auto-renewal should not succeed
2915
2952
(-)a/C4/Reserves.pm (+48 lines)
Lines 132-137 BEGIN { Link Here
132
        &SuspendAll
132
        &SuspendAll
133
133
134
        &GetReservesControlBranch
134
        &GetReservesControlBranch
135
        &ReservesOnSamePeriod
135
136
136
        IsItemOnHoldAndFound
137
        IsItemOnHoldAndFound
137
138
Lines 2264-2269 sub GetHoldRule { Link Here
2264
    return $rules;
2265
    return $rules;
2265
}
2266
}
2266
2267
2268
=head2 ReservesOnSamePeriod
2269
2270
    my $reserve = ReservesOnSamePeriod( $biblionumber, $itemnumber, $resdate, $expdate);
2271
2272
    Return the reserve that match the period ($resdate => $expdate),
2273
    undef if no reserve match.
2274
2275
=cut
2276
2277
sub ReservesOnSamePeriod {
2278
    my ($biblionumber, $itemnumber, $resdate, $expdate) = @_;
2279
2280
    unless ($resdate && $expdate) {
2281
        return;
2282
    }
2283
2284
    my @reserves = Koha::Holds->search({ biblionumber => $biblionumber });
2285
2286
    $resdate = output_pref({ str => $resdate, dateonly => 1, dateformat => 'iso' });
2287
    $expdate = output_pref({ str => $expdate, dateonly => 1, dateformat => 'iso' });
2288
2289
    my @reserves_overlaps;
2290
    foreach my $reserve ( @reserves ) {
2291
2292
        unless ($reserve->reservedate && $reserve->expirationdate) {
2293
            next;
2294
        }
2295
2296
        if (date_ranges_overlap($resdate, $expdate,
2297
                                $reserve->reservedate,
2298
                                $reserve->expirationdate)) {
2299
2300
            # If reserve is item level and the requested periods overlap.
2301
            if ($itemnumber && $reserve->itemnumber == $itemnumber ) {
2302
                return [$reserve->unblessed];
2303
            }
2304
            push @reserves_overlaps, $reserve->unblessed;
2305
        }
2306
    }
2307
2308
    if ( @reserves_overlaps >= Koha::Items->search({ biblionumber => $biblionumber })->count() ) {
2309
        return \@reserves_overlaps;
2310
    }
2311
2312
    return;
2313
}
2314
2267
=head1 AUTHOR
2315
=head1 AUTHOR
2268
2316
2269
Koha Development Team <http://koha-community.org/>
2317
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 338-341 sub format_sqldatetime { Link Here
338
    return q{};
338
    return q{};
339
}
339
}
340
340
341
=head2 date_ranges_overlap
342
343
    $bool = date_ranges_overlap($start1, $end1, $start2, $end2);
344
345
    Tells if first range ($start1 => $end1) overlaps
346
    the second one ($start2 => $end2)
347
348
=cut
349
350
sub date_ranges_overlap {
351
    my ($start1, $end1, $start2, $end2) = @_;
352
353
    $start1 = dt_from_string( $start1, 'iso' );
354
    $end1 = dt_from_string( $end1, 'iso' );
355
    $start2 = dt_from_string( $start2, 'iso' );
356
    $end2 = dt_from_string( $end2, 'iso' );
357
358
    if (
359
        # Start of range 2 is in the range 1.
360
        (
361
            DateTime->compare($start2, $start1) >= 0
362
            && DateTime->compare($start2, $end1) <= 0
363
        )
364
        ||
365
        # End of range 2 is in the range 1.
366
        (
367
            DateTime->compare($end2, $start1) >= 0
368
            && DateTime->compare($end2, $end1) <= 0
369
        )
370
        ||
371
        # Range 2 start before and end after range 1.
372
        (
373
            DateTime->compare($start2, $start1) < 0
374
            && DateTime->compare($end2, $end1) > 0
375
        )
376
    ) {
377
        return 1;
378
    }
379
380
    return;
381
}
382
341
1;
383
1;
(-)a/circ/circulation.pl (+4 lines)
Lines 410-415 if (@$barcodes) { Link Here
410
        }
410
        }
411
        unless($confirm_required) {
411
        unless($confirm_required) {
412
            my $switch_onsite_checkout = exists $messages->{ONSITE_CHECKOUT_WILL_BE_SWITCHED};
412
            my $switch_onsite_checkout = exists $messages->{ONSITE_CHECKOUT_WILL_BE_SWITCHED};
413
            if ( $cancelreserve eq 'cancel' ) {
414
                CancelReserve({ reserve_id => $query->param('reserveid') });
415
            }
416
            $cancelreserve = $cancelreserve eq 'revert' ? 'revert' : undef;
413
            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, } );
417
            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, } );
414
            $template_params->{issue} = $issue;
418
            $template_params->{issue} = $issue;
415
            $session->clear('auto_renew');
419
            $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 509-514 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
509
('PrefillGuaranteeField', 'phone,email,streetnumber,address,city,state,zipcode,country', NULL, 'Prefill these fields in guarantee member entry form from guarantor patron record', 'Multiple'),
509
('PrefillGuaranteeField', 'phone,email,streetnumber,address,city,state,zipcode,country', NULL, 'Prefill these fields in guarantee member entry form from guarantor patron record', 'Multiple'),
510
('PrefillItem','0','','When a new item is added, should it be prefilled with last created item values?','YesNo'),
510
('PrefillItem','0','','When a new item is added, should it be prefilled with last created item values?','YesNo'),
511
('PreserveSerialNotes','1','','When a new "Expected" issue is generated, should it be prefilled with last created issue notes?','YesNo'),
511
('PreserveSerialNotes','1','','When a new "Expected" issue is generated, should it be prefilled with last created issue notes?','YesNo'),
512
('PreventCheckoutOnSameReservePeriod','0','','Prevent to checkout a document if a reserve on same period exists','YesNo'),
513
('PreventReservesOnSamePeriod','0','','Prevent to hold a document if a reserve on same period exists','YesNo'),
512
('previousIssuesDefaultSortOrder','asc','asc|desc','Specify the sort order of Previous Issues on the circulation page','Choice'),
514
('previousIssuesDefaultSortOrder','asc','asc|desc','Specify the sort order of Previous Issues on the circulation page','Choice'),
513
('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'),
515
('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'),
514
('PrivacyPolicyURL','',NULL,'This URL is used in messages about GDPR consents.', 'Free'),
516
('PrivacyPolicyURL','',NULL,'This URL is used in messages about GDPR consents.', 'Free'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (+13 lines)
Lines 532-537 Circulation: Link Here
532
              class: integer
532
              class: integer
533
            - to fill holds.
533
            - to fill holds.
534
            - "(list of not for loan values separated with a pipe '|')"
534
            - "(list of not for loan values separated with a pipe '|')"
535
        -
536
            - pref: PreventCheckoutOnSameReservePeriod
537
              choices:
538
                  yes: Do
539
                  no: "Don't"
540
            - If yes, checkouts periods can't overlap with a reserve period.
535
        -
541
        -
536
            - pref: HoldsAutoFill
542
            - pref: HoldsAutoFill
537
              choices:
543
              choices:
Lines 854-859 Circulation: Link Here
854
            - pref: UpdateItemWhenLostFromHoldList
860
            - pref: UpdateItemWhenLostFromHoldList
855
              type: textarea
861
              type: textarea
856
              syntax: text/x-yaml
862
              syntax: text/x-yaml
863
        -
864
            - pref: PreventReservesOnSamePeriod
865
              choices:
866
                  yes: Do
867
                  no: "Don't"
868
            - If yes, Reserves periods for the same document can't overlap.
869
857
    Interlibrary loans:
870
    Interlibrary loans:
858
        -
871
        -
859
            - pref: ILLModule
872
            - 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/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/placerequest.tt (+88 lines)
Line 0 Link Here
1
[% USE JSON.Escape %]
2
[% USE raw %]
3
[% INCLUDE 'doc-head-open.inc' %]
4
    <title>Koha &rsaquo; Circulation &rsaquo; Holds &rsaquo; Confirm holds</title>
5
[% INCLUDE 'doc-head-close.inc' %]
6
</head>
7
<body id="circ_placerequest" class="catalog">
8
[% INCLUDE 'header.inc' %]
9
[% INCLUDE 'circ-search.inc' %]
10
11
<div id="breadcrumbs">
12
  <a href="/cgi-bin/koha/mainpage.pl">Home</a>
13
  &rsaquo;
14
  <a href="/cgi-bin/koha/catalogue/search.pl">Catalog</a>
15
  &rsaquo;
16
  Confirm holds
17
</div>
18
19
<div id="doc3" class="yui-t2">
20
21
  <div id="bd">
22
    <div id="yui-main">
23
      <div class="yui-b">
24
        <div class="modal fade" tabindex="-1" role="dialog">
25
          <div class="modal-dialog" role="document">
26
            <div class="modal-content">
27
              <div class="modal-header">
28
                <h4 class="modal-title">Confirm holds</h4>
29
              </div>
30
              <div class="modal-body">
31
                <p>
32
                  Some of the reserves you are trying to do overlaps with existing reserves.
33
                  Please tick the box confirming the reservation then click on Confirm if you want to proceed.
34
                </p>
35
36
                <form method="post" id="confirm-holds">
37
                  <input type="hidden" name="borrowernumber" value="[% borrowernumber | html %]">
38
                  [% IF multi_hold %]
39
                    <input type="hidden" name="biblionumbers" value="[% biblionumbers | html %]">
40
                    <input type="hidden" name="multi_hold" value="1">
41
                  [% ELSE %]
42
                    <input type="hidden" name="biblionumber" value="[% biblionumber | html %]">
43
                  [% END %]
44
                  <input type="hidden" name="reserve_date" value="[% reserve_date | html %]">
45
                  <input type="hidden" name="expiration_date" value="[% expiration_date | html %]">
46
                  <input type="hidden" name="pickup" value="[% pickup | html %]">
47
                  <input type="hidden" name="notes" value="[% notes | html %]">
48
                  <input type="hidden" name="confirm" value="1">
49
                  <input type="hidden" name="type" value="str8">
50
51
                  [% FOREACH biblionumber IN overlap_reserves.keys %]
52
                    [% input_id = "confirm_$biblionumber" %]
53
                    <div>
54
                      <input type="hidden" name="rank_[% biblionumber | html %]" value="[% overlap_reserves.$biblionumber.rank | html %]">
55
                      [% IF (overlap_reserves.$biblionumber.checkitem) %]
56
                        <input type="hidden" name="checkitem" value="[% overlap_reserves.$biblionumber.checkitem | html %]">
57
                      [% END %]
58
                      <input type="checkbox" name="confirm_biblionumbers" id="[% input_id | html %]"
59
                             value="[% biblionumber | html %]">
60
                      <label for="[% input_id | html %]">Confirm hold for [% overlap_reserves.$biblionumber.title | html %]</label>
61
                    </div>
62
                  [% END %]
63
                </form>
64
              </div>
65
              <div class="modal-footer">
66
                <button type="button" class="btn btn-default deny" data-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
67
                <button type="submit" class="btn btn-default approve" form="confirm-holds"><i class="fa fa-check"></i> Confirm</button>
68
              </div>
69
            </div><!-- /.modal-content -->
70
          </div><!-- /.modal-dialog -->
71
        </div><!-- /.modal -->
72
      </div>
73
    </div>
74
75
  </div>
76
  <script>
77
    $(document).ready(function () {
78
      var modal = $('.modal').modal();
79
      modal.on('hide.bs.modal', function (e) {
80
        [% IF multi_hold %]
81
          window.location = '/cgi-bin/koha/reserve/request.pl?biblionumbers=' + [% biblionumbers.json | $raw %] + '&multi_hold=1' + '&borrowernumber=' + [% borrowernumber.json | $raw %];
82
        [% ELSE %]
83
          window.location = '/cgi-bin/koha/reserve/request.pl?biblionumber=' + [% biblionumber.json | $raw %] + '&borrowernumber=' + [% borrowernumber.json | $raw %]
84
        [% END %]
85
      })
86
    });
87
  </script>
88
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/opac/opac-reserve.pl (+9 lines)
Lines 286-291 if ( $query->param('place_reserve') ) { Link Here
286
            # Inserts a null into the 'itemnumber' field of 'reserves' table.
286
            # Inserts a null into the 'itemnumber' field of 'reserves' table.
287
            $itemNum = undef;
287
            $itemNum = undef;
288
        }
288
        }
289
290
        if ($canreserve) {
291
            if (C4::Context->preference("PreventReservesOnSamePeriod") &&
292
                ReservesOnSamePeriod($biblioNum, $itemNum, $startdate, $expiration_date)) {
293
                $canreserve = 0;
294
                $failed_holds++;
295
            }
296
        }
297
289
        my $notes = $query->param('notes_'.$biblioNum)||'';
298
        my $notes = $query->param('notes_'.$biblioNum)||'';
290
299
291
        if (   $maxreserves
300
        if (   $maxreserves
(-)a/reserve/placerequest.pl (-3 / +45 lines)
Lines 23-28 Link Here
23
23
24
use Modern::Perl;
24
use Modern::Perl;
25
25
26
use List::MoreUtils qw( none );
27
26
use CGI qw ( -utf8 );
28
use CGI qw ( -utf8 );
27
use C4::Biblio;
29
use C4::Biblio;
28
use C4::Items;
30
use C4::Items;
Lines 30-46 use C4::Output; Link Here
30
use C4::Reserves;
32
use C4::Reserves;
31
use C4::Circulation;
33
use C4::Circulation;
32
use C4::Members;
34
use C4::Members;
33
use C4::Auth qw/checkauth/;
35
use C4::Auth;
34
36
35
use Koha::Items;
37
use Koha::Items;
36
use Koha::Patrons;
38
use Koha::Patrons;
37
39
38
my $input = CGI->new();
40
my $input = CGI->new();
39
41
40
checkauth($input, 0, { reserveforothers => 'place_holds' }, 'intranet');
42
my ($template, $loggedinuser, $cookie) = get_template_and_user({
43
    template_name   => 'reserve/placerequest.tt',
44
    query           => $input,
45
    type            => 'intranet',
46
    authnotrequired => 0,
47
    flagsrequired   => { reserveforothers => 'place_holds' },
48
});
41
49
42
my @bibitems       = $input->multi_param('biblioitem');
50
my @bibitems       = $input->multi_param('biblioitem');
43
my @reqbib         = $input->multi_param('reqbib');
44
my $biblionumber   = $input->param('biblionumber');
51
my $biblionumber   = $input->param('biblionumber');
45
my $borrowernumber = $input->param('borrowernumber');
52
my $borrowernumber = $input->param('borrowernumber');
46
my $notes          = $input->param('notes');
53
my $notes          = $input->param('notes');
Lines 52-57 my $title = $input->param('title'); Link Here
52
my $checkitem      = $input->param('checkitem');
59
my $checkitem      = $input->param('checkitem');
53
my $expirationdate = $input->param('expiration_date');
60
my $expirationdate = $input->param('expiration_date');
54
my $itemtype       = $input->param('itemtype') || undef;
61
my $itemtype       = $input->param('itemtype') || undef;
62
my $confirm        = $input->param('confirm');
63
my @confirm_biblionumbers = $input->multi_param('confirm_biblionumbers');
55
64
56
my $borrower = Koha::Patrons->find( $borrowernumber );
65
my $borrower = Koha::Patrons->find( $borrowernumber );
57
$borrower = $borrower->unblessed if $borrower;
66
$borrower = $borrower->unblessed if $borrower;
Lines 74-81 foreach my $bibnum (@biblionumbers) { Link Here
74
my $found;
83
my $found;
75
84
76
if ( $type eq 'str8' && $borrower ) {
85
if ( $type eq 'str8' && $borrower ) {
86
    if (C4::Context->preference('PreventReservesOnSamePeriod') && !$confirm) {
87
        my %overlap_reserves;
88
        foreach my $biblionumber ( keys %bibinfos ) {
89
            my $overlapping_reserves = ReservesOnSamePeriod($biblionumber, $checkitem, $startdate, $expirationdate);
90
            if ($overlapping_reserves && 0 < scalar @$overlapping_reserves) {
91
                $overlap_reserves{$biblionumber} = {
92
                    title => $multi_hold ? $bibinfos{$biblionumber}->{title} : $title,
93
                    checkitem => $checkitem,
94
                    rank => $multi_hold ? $bibinfos{$biblionumber}->{rank} : $rank[0],
95
                };
96
            }
97
        }
98
99
        if (scalar keys %overlap_reserves) {
100
            $template->param(
101
                borrowernumber => $borrowernumber,
102
                biblionumbers => $biblionumbers,
103
                biblionumber => $biblionumber,
104
                overlap_reserves => \%overlap_reserves,
105
                reserve_date => $startdate,
106
                expiration_date => $expirationdate,
107
                notes => $notes,
108
                rank_request => \@rank,
109
                pickup => $branch,
110
                multi_hold => $multi_hold,
111
            );
112
113
            output_html_with_http_headers $input, $cookie, $template->output;
114
            exit;
115
        }
116
    }
77
117
78
    foreach my $biblionumber ( keys %bibinfos ) {
118
    foreach my $biblionumber ( keys %bibinfos ) {
119
        next if ($confirm && none { $_ eq $biblionumber } @confirm_biblionumbers);
120
79
        my $count = @bibitems;
121
        my $count = @bibitems;
80
        @bibitems = sort @bibitems;
122
        @bibitems = sort @bibitems;
81
        my $i2 = 1;
123
        my $i2 = 1;
(-)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 (+60 lines)
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 1546-1551 subtest "AllowRenewalIfOtherItemsAvailable tests" => sub { Link Here
1546
    is( $error, 'onsite_checkout', 'A correct error code should be returned by CanBookBeRenewed for on-site checkout' );
1547
    is( $error, 'onsite_checkout', 'A correct error code should be returned by CanBookBeRenewed for on-site checkout' );
1547
}
1548
}
1548
1549
1550
{
1551
    # Don't allow renewing on reserve period with PreventCheckoutOnSameReservePeriod enabled
1552
    t::lib::Mocks::mock_preference( 'PreventCheckoutOnSameReservePeriod', 1 );
1553
1554
    my $start_issue_dt = DateTime->now();
1555
    $start_issue_dt->subtract( days => 15);
1556
    my $due_date = $start_issue_dt->clone;
1557
    $due_date->add( days => 17 );
1558
1559
    my $biblio = $builder->build({
1560
        source => 'Biblio',
1561
    });
1562
    my $biblionumber = $biblio->{biblionumber};
1563
1564
    my $item = $builder->build({
1565
        source => 'Item',
1566
        value => {
1567
            biblionumber => $biblionumber
1568
        }
1569
    });
1570
    my $itemnumber = $item->{itemnumber};
1571
1572
    my $issue = $builder->build({
1573
        source => 'Issue',
1574
        value => {
1575
            itemnumber => $itemnumber,
1576
            biblionumber => $biblionumber,
1577
            issuedate => $start_issue_dt->ymd,
1578
            date_due => $due_date->ymd,
1579
            onsite_checkout => 0
1580
        }
1581
    });
1582
1583
    my $reservedate = $due_date->clone;
1584
    $reservedate->add( days => 5 );
1585
    my $expirationdate = $reservedate->clone;
1586
    $expirationdate->add( days => 15 );
1587
    $builder->build({
1588
        source => 'Reserve',
1589
        value => {
1590
            biblionumber => $biblionumber,
1591
            itemnumber => $itemnumber,
1592
            reservedate => $reservedate->ymd,
1593
            expirationdate => $expirationdate->ymd
1594
        }
1595
    });
1596
1597
    my $requested_date_due = $due_date->clone;
1598
    $requested_date_due->add( days => 4 );
1599
    my ( $renewed, $error ) = CanBookBeRenewed( $issue->{borrowernumber}, $itemnumber, 1, $requested_date_due );
1600
    is( $renewed, 1, 'CanBookBeRenewed should allow to renew if no reserve date overlap' );
1601
1602
    $requested_date_due->add( days => 2 );
1603
    ( $renewed, $error ) = CanBookBeRenewed( $issue->{borrowernumber}, $itemnumber, 1, $requested_date_due );
1604
    is( $renewed, 0, 'CanBookBeRenewed should not allow to renew if reserve date overlap' );
1605
1606
    t::lib::Mocks::mock_preference( 'PreventCheckoutOnSameReservePeriod', 0 );
1607
}
1608
1549
{
1609
{
1550
    my $library = $builder->build({ source => 'Branch' });
1610
    my $library = $builder->build({ source => 'Branch' });
1551
1611
(-)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