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

(-)a/C4/Circulation.pm (+23 lines)
Lines 1032-1037 sub CanBookBeIssued { Link Here
1032
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1032
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1033
                    $needsconfirmation{'resbranchcode'} = $res->{branchcode};
1033
                    $needsconfirmation{'resbranchcode'} = $res->{branchcode};
1034
                    $needsconfirmation{'reswaitingdate'} = $res->{'waitingdate'};
1034
                    $needsconfirmation{'reswaitingdate'} = $res->{'waitingdate'};
1035
                    $needsconfirmation{resreserveid} = $res->{reserve_id};
1035
                }
1036
                }
1036
                elsif ( $restype eq "Reserved" ) {
1037
                elsif ( $restype eq "Reserved" ) {
1037
                    # The item is on reserve for someone else.
1038
                    # The item is on reserve for someone else.
Lines 1042-1050 sub CanBookBeIssued { Link Here
1042
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1043
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1043
                    $needsconfirmation{'resbranchcode'} = $patron->branchcode;
1044
                    $needsconfirmation{'resbranchcode'} = $patron->branchcode;
1044
                    $needsconfirmation{'resreservedate'} = $res->{reservedate};
1045
                    $needsconfirmation{'resreservedate'} = $res->{reservedate};
1046
                    $needsconfirmation{resreserveid} = $res->{reserve_id};
1045
                }
1047
                }
1046
            }
1048
            }
1047
        }
1049
        }
1050
1051
        my $now = dt_from_string();
1052
        my $preventCheckoutOnSameReservePeriod =
1053
            C4::Context->preference("PreventCheckoutOnSameReservePeriod");
1054
        my $reserves_on_same_period =
1055
            ReservesOnSamePeriod($item_object->biblionumber, $item_object->itemnumber, $now->ymd, $duedate->ymd);
1056
        if ($preventCheckoutOnSameReservePeriod && $reserves_on_same_period) {
1057
            my $reserve = $reserves_on_same_period->[0];
1058
            my $patron = Koha::Patrons->find( $reserve->{borrowernumber} );
1059
            my $branchname = Koha::Libraries->find($reserve->{branchcode})->branchname;
1060
1061
            $needsconfirmation{RESERVED} = 1;
1062
            $needsconfirmation{resfirstname} = $patron->firstname;
1063
            $needsconfirmation{ressurname} = $patron->surname;
1064
            $needsconfirmation{rescardnumber} = $patron->cardnumber;
1065
            $needsconfirmation{resborrowernumber} = $patron->borrowernumber;
1066
            $needsconfirmation{resbranchname} = $branchname;
1067
            $needsconfirmation{resreservedate} = $reserve->{reservedate};
1068
            $needsconfirmation{resreserveid} = $reserve->{reserve_id};
1069
        }
1070
1048
    }
1071
    }
1049
1072
1050
    ## CHECK AGE RESTRICTION
1073
    ## CHECK AGE RESTRICTION
(-)a/C4/Reserves.pm (+48 lines)
Lines 133-138 BEGIN { Link Here
133
        &SuspendAll
133
        &SuspendAll
134
134
135
        &GetReservesControlBranch
135
        &GetReservesControlBranch
136
        &ReservesOnSamePeriod
136
137
137
        IsItemOnHoldAndFound
138
        IsItemOnHoldAndFound
138
139
Lines 2174-2179 sub GetHoldRule { Link Here
2174
    return $sth->fetchrow_hashref();
2175
    return $sth->fetchrow_hashref();
2175
}
2176
}
2176
2177
2178
=head2 ReservesOnSamePeriod
2179
2180
    my $reserve = ReservesOnSamePeriod( $biblionumber, $itemnumber, $resdate, $expdate);
2181
2182
    Return the reserve that match the period ($resdate => $expdate),
2183
    undef if no reserve match.
2184
2185
=cut
2186
2187
sub ReservesOnSamePeriod {
2188
    my ($biblionumber, $itemnumber, $resdate, $expdate) = @_;
2189
2190
    unless ($resdate && $expdate) {
2191
        return;
2192
    }
2193
2194
    my @reserves = Koha::Holds->search({ biblionumber => $biblionumber });
2195
2196
    $resdate = output_pref({ str => $resdate, dateonly => 1, dateformat => 'iso' });
2197
    $expdate = output_pref({ str => $expdate, dateonly => 1, dateformat => 'iso' });
2198
2199
    my @reserves_overlaps;
2200
    foreach my $reserve ( @reserves ) {
2201
2202
        unless ($reserve->reservedate && $reserve->expirationdate) {
2203
            next;
2204
        }
2205
2206
        if (date_ranges_overlap($resdate, $expdate,
2207
                                $reserve->reservedate,
2208
                                $reserve->expirationdate)) {
2209
2210
            # If reserve is item level and the requested periods overlap.
2211
            if ($itemnumber && $reserve->itemnumber == $itemnumber ) {
2212
                return [$reserve->unblessed];
2213
            }
2214
            push @reserves_overlaps, $reserve->unblessed;
2215
        }
2216
    }
2217
2218
    if ( @reserves_overlaps >= Koha::Items->search({ biblionumber => $biblionumber })->count() ) {
2219
        return \@reserves_overlaps;
2220
    }
2221
2222
    return;
2223
}
2224
2177
=head1 AUTHOR
2225
=head1 AUTHOR
2178
2226
2179
Koha Development Team <http://koha-community.org/>
2227
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/circ/circulation.pl (+4 lines)
Lines 430-435 if (@$barcodes) { Link Here
430
        }
430
        }
431
        unless($confirm_required) {
431
        unless($confirm_required) {
432
            my $switch_onsite_checkout = exists $messages->{ONSITE_CHECKOUT_WILL_BE_SWITCHED};
432
            my $switch_onsite_checkout = exists $messages->{ONSITE_CHECKOUT_WILL_BE_SWITCHED};
433
            if ( $cancelreserve eq 'cancel' ) {
434
                CancelReserve({ reserve_id => $query->param('reserveid') });
435
            }
436
            $cancelreserve = $cancelreserve eq 'revert' ? 'revert' : undef;
433
            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, } );
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, } );
434
            $template_params->{issue} = $issue;
438
            $template_params->{issue} = $issue;
435
            $session->clear('auto_renew');
439
            $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 (-1 / +3 lines)
Lines 665-669 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
665
('XSLTListsDisplay','default','','Enable XSLT stylesheet control over lists pages display on intranet','Free'),
665
('XSLTListsDisplay','default','','Enable XSLT stylesheet control over lists pages display on intranet','Free'),
666
('XSLTResultsDisplay','default','','Enable XSL stylesheet control over results page display on intranet','Free'),
666
('XSLTResultsDisplay','default','','Enable XSL stylesheet control over results page display on intranet','Free'),
667
('z3950AuthorAuthFields','701,702,700',NULL,'Define the MARC biblio fields for Personal Name Authorities to fill biblio.author','free'),
667
('z3950AuthorAuthFields','701,702,700',NULL,'Define the MARC biblio fields for Personal Name Authorities to fill biblio.author','free'),
668
('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo')
668
('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo'),
669
('PreventCheckoutOnSameReservePeriod','0','','Prevent to checkout a document if a reserve on same period exists','YesNo'),
670
('PreventReservesOnSamePeriod','0','','Prevent to hold a document if a reserve on same period exists','YesNo')
669
;
671
;
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (+12 lines)
Lines 498-503 Circulation: Link Here
498
            - "<br />ccode: [NEWFIC,NULL,DVD]"
498
            - "<br />ccode: [NEWFIC,NULL,DVD]"
499
            - "<br />itype: [NEWBK,\"\"]"
499
            - "<br />itype: [NEWBK,\"\"]"
500
            - "<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."
500
            - "<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
        -
502
            - pref: PreventCheckoutOnSameReservePeriod
503
              choices:
504
                  yes: Do
505
                  no: "Don't"
506
            - If yes, checkouts periods can't overlap with a reserve period.
501
    Checkin Policy:
507
    Checkin Policy:
502
        -
508
        -
503
            - pref: HoldsAutoFill
509
            - pref: HoldsAutoFill
Lines 801-806 Circulation: Link Here
801
            - pref: UpdateItemWhenLostFromHoldList
807
            - pref: UpdateItemWhenLostFromHoldList
802
              type: textarea
808
              type: textarea
803
              syntax: text/x-yaml
809
              syntax: text/x-yaml
810
        -
811
            - pref: PreventReservesOnSamePeriod
812
              choices:
813
                  yes: Do
814
                  no: "Don't"
815
            - If yes, Reserves periods for the same document can't overlap.
804
    Interlibrary Loans:
816
    Interlibrary Loans:
805
        -
817
        -
806
            - pref: ILLModule
818
            - pref: ILLModule
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt (+2 lines)
Lines 213-218 Link Here
213
                                    [% IF ( RESERVED ) %]
213
                                    [% IF ( RESERVED ) %]
214
                                        <p>
214
                                        <p>
215
                                            <input type="checkbox" id="cancelreserve" name="cancelreserve" value="cancel" />
215
                                            <input type="checkbox" id="cancelreserve" name="cancelreserve" value="cancel" />
216
                                            <input type="hidden" name="reserveid" value="[% resreserveid | html %]" />
216
                                            <label for="cancelreserve">Cancel hold</label>
217
                                            <label for="cancelreserve">Cancel hold</label>
217
                                        </p>
218
                                        </p>
218
                                    [% END %]
219
                                    [% END %]
Lines 221-226 Link Here
221
                                        <p>
222
                                        <p>
222
                                            <label for="cancelreserve">Cancel hold</label>
223
                                            <label for="cancelreserve">Cancel hold</label>
223
                                            <input type="radio" value="cancel" name="cancelreserve" id="cancelreserve" /><br />
224
                                            <input type="radio" value="cancel" name="cancelreserve" id="cancelreserve" /><br />
225
                                            <input type="hidden" name="reserveid" value="[% resreserveid | html %]" />
224
                                            <label for="revertreserve">Revert waiting status</label>
226
                                            <label for="revertreserve">Revert waiting status</label>
225
                                            <input type="radio" value="revert" name="cancelreserve" id="revertreserve" checked="checked"/>
227
                                            <input type="radio" value="revert" name="cancelreserve" id="revertreserve" checked="checked"/>
226
                                        </p>
228
                                        </p>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/placerequest.tt (+66 lines)
Line 0 Link Here
1
[% INCLUDE 'doc-head-open.inc' %]
2
    <title>Koha &rsaquo; Circulation &rsaquo; Holds &rsaquo; Confirm holds</title>
3
[% INCLUDE 'doc-head-close.inc' %]
4
</head>
5
<body id="circ_placerequest" class="catalog">
6
[% INCLUDE 'header.inc' %]
7
[% INCLUDE 'circ-search.inc' %]
8
9
<div id="breadcrumbs">
10
  <a href="/cgi-bin/koha/mainpage.pl">Home</a>
11
  &rsaquo;
12
  <a href="/cgi-bin/koha/catalogue/search.pl">Catalog</a>
13
  &rsaquo;
14
  Confirm holds
15
</div>
16
17
<div id="doc3" class="yui-t2">
18
19
  <div id="bd">
20
    <div id="yui-main">
21
      <div class="yui-b">
22
23
        <h1>Confirm holds</h1>
24
25
        <div class="alert">
26
          <p>
27
            Some of the reserves you are trying to do overlaps with existing reserves.
28
            Please confirm you want to proceed.
29
          </p>
30
        </div>
31
32
        <form method="post">
33
          <input type="hidden" name="borrowernumber" value="[% borrowernumber | html %]">
34
          [% IF multi_hold %]
35
            <input type="hidden" name="biblionumbers" value="[% biblionumbers | html %]">
36
            <input type="hidden" name="multi_hold" value="1">
37
          [% ELSE %]
38
            <input type="hidden" name="biblionumber" value="[% biblionumber | html %]">
39
          [% END %]
40
          <input type="hidden" name="reserve_date" value="[% reserve_date | html %]">
41
          <input type="hidden" name="expiration_date" value="[% expiration_date | html %]">
42
          <input type="hidden" name="pickup" value="[% pickup | html %]">
43
          <input type="hidden" name="notes" value="[% notes | html %]">
44
          <input type="hidden" name="confirm" value="1">
45
46
          [% FOREACH biblionumber IN overlap_reserves.keys %]
47
            [% input_id = "confirm_$biblionumber" %]
48
            <div>
49
              <input type="hidden" name="rank_[% biblionumber | html %]" value="[% overlap_reserves.$biblionumber.rank | html %]">
50
              [% IF (overlap_reserves.$biblionumber.checkitem) %]
51
                <input type="hidden" name="checkitem" value="[% overlap_reserves.$biblionumber.checkitem | html %]">
52
              [% END %]
53
              <input type="checkbox" name="confirm_biblionumbers" id="[% input_id | html %]"
54
                     value="[% biblionumber | html %]">
55
              <label for="[% input_id | html %]">Confirm hold for [% overlap_reserves.$biblionumber.title | html %]</label>
56
            </div>
57
          [% END %]
58
59
          <input type="submit" value="Continue">
60
        </form>
61
62
      </div>
63
    </div>
64
65
  </div>
66
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/opac/opac-reserve.pl (+9 lines)
Lines 282-287 if ( $query->param('place_reserve') ) { Link Here
282
            # Inserts a null into the 'itemnumber' field of 'reserves' table.
282
            # Inserts a null into the 'itemnumber' field of 'reserves' table.
283
            $itemNum = undef;
283
            $itemNum = undef;
284
        }
284
        }
285
286
        if ($canreserve) {
287
            if (C4::Context->preference("PreventReservesOnSamePeriod") &&
288
                ReservesOnSamePeriod($biblioNum, $itemNum, $startdate, $expiration_date)) {
289
                $canreserve = 0;
290
                $failed_holds++;
291
            }
292
        }
293
285
        my $notes = $query->param('notes_'.$biblioNum)||'';
294
        my $notes = $query->param('notes_'.$biblioNum)||'';
286
295
287
        if (   $maxreserves
296
        if (   $maxreserves
(-)a/reserve/placerequest.pl (-72 / +93 lines)
Lines 30-128 use C4::Output; Link Here
30
use C4::Reserves;
30
use C4::Reserves;
31
use C4::Circulation;
31
use C4::Circulation;
32
use C4::Members;
32
use C4::Members;
33
use C4::Auth qw/checkauth/;
33
use C4::Auth;
34
34
35
use Koha::Items;
35
use Koha::Items;
36
use Koha::Patrons;
36
use Koha::Patrons;
37
37
38
my $input = CGI->new();
38
my $input = CGI->new();
39
39
40
checkauth($input, 0, { reserveforothers => 'place_holds' }, 'intranet');
40
my ( $template, $loggedinuser, $cookie, $flags ) = get_template_and_user(
41
41
    {
42
my @bibitems       = $input->multi_param('biblioitem');
42
        template_name   => "reserve/placerequest.tt",
43
my @reqbib         = $input->multi_param('reqbib');
43
        query           => $input,
44
my $biblionumber   = $input->param('biblionumber');
44
        type            => "intranet",
45
my $borrowernumber = $input->param('borrowernumber');
45
        authnotrequired => 0,
46
my $notes          = $input->param('notes');
46
        flagsrequired   => { reserveforothers => 'place_holds' },
47
my $branch         = $input->param('pickup');
47
    }
48
my $startdate      = $input->param('reserve_date') || '';
48
);
49
my @rank           = $input->multi_param('rank-request');
49
50
my $type           = $input->param('type');
50
my $biblionumber=$input->param('biblionumber');
51
my $title          = $input->param('title');
51
my $borrowernumber=$input->param('borrowernumber');
52
my $checkitem      = $input->param('checkitem');
52
my $notes=$input->param('notes');
53
my $branch=$input->param('pickup');
54
my $startdate=$input->param('reserve_date') || '';
55
my @rank=$input->param('rank-request');
56
my $title=$input->param('title');
57
my $checkitem=$input->param('checkitem');
53
my $expirationdate = $input->param('expiration_date');
58
my $expirationdate = $input->param('expiration_date');
54
my $itemtype       = $input->param('itemtype') || undef;
59
my $itemtype       = $input->param('itemtype') || undef;
55
60
my $confirm = $input->param('confirm');
56
my $borrower = Koha::Patrons->find( $borrowernumber );
61
my @confirm_biblionumbers = $input->param('confirm_biblionumbers');
57
$borrower = $borrower->unblessed if $borrower;
58
62
59
my $multi_hold = $input->param('multi_hold');
63
my $multi_hold = $input->param('multi_hold');
60
my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ($biblionumber . '/');
64
my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ($biblionumber . '/');
61
my $bad_bibs = $input->param('bad_bibs');
65
my $bad_bibs = $input->param('bad_bibs');
62
my $holds_to_place_count = $input->param('holds_to_place_count') || 1;
66
my $holds_to_place_count = $input->param('holds_to_place_count') || 1;
63
67
68
my $borrower = Koha::Patrons->find( $borrowernumber );
69
$borrower = $borrower->unblessed if $borrower;
70
unless ($borrower) {
71
    print $input->header();
72
    print "Invalid borrower number please try again";
73
    exit;
74
}
75
64
my %bibinfos = ();
76
my %bibinfos = ();
65
my @biblionumbers = split '/', $biblionumbers;
77
my @biblionumbers = split '/', $biblionumbers;
66
foreach my $bibnum (@biblionumbers) {
78
foreach my $bibnum (@biblionumbers) {
67
    my %bibinfo = ();
79
    my %bibinfo;
68
    $bibinfo{title} = $input->param("title_$bibnum");
80
    $bibinfo{title} = $input->param("title_$bibnum");
81
69
    $bibinfo{rank} = $input->param("rank_$bibnum");
82
    $bibinfo{rank} = $input->param("rank_$bibnum");
70
    $bibinfos{$bibnum} = \%bibinfo;
83
    $bibinfos{$bibnum} = \%bibinfo;
71
}
84
}
72
85
73
my $found;
86
my $found;
74
87
75
if ( $type eq 'str8' && $borrower ) {
88
my $overlap_reserves = {};
76
89
foreach my $biblionumber (keys %bibinfos) {
77
    foreach my $biblionumber ( keys %bibinfos ) {
90
    next if ($confirm && !grep { $_ eq $biblionumber } @confirm_biblionumbers);
78
        my $count = @bibitems;
91
79
        @bibitems = sort @bibitems;
92
    my ($reserve_title, $reserve_rank);
80
        my $i2 = 1;
93
81
        my @realbi;
94
    my $can_override = C4::Context->preference('AllowHoldPolicyOverride');
82
        $realbi[0] = $bibitems[0];
95
    if (defined $checkitem && $checkitem ne '') {
83
        for ( my $i = 1 ; $i < $count ; $i++ ) {
96
84
            my $i3 = $i2 - 1;
97
        my $item = Koha::Items->find($checkitem);
85
            if ( $realbi[$i3] ne $bibitems[$i] ) {
98
86
                $realbi[$i2] = $bibitems[$i];
99
        if ($item->biblionumber ne $biblionumber) {
87
                $i2++;
100
            $biblionumber = $item->biblionumber;
88
            }
89
        }
101
        }
90
102
91
        my $can_override = C4::Context->preference('AllowHoldPolicyOverride');
103
        my $can_item_be_reserved = CanItemBeReserved($borrower->{'borrowernumber'}, $item->itemnumber, $branch)->{status};
92
        if ( defined $checkitem && $checkitem ne '' ) {
104
93
105
        if ( $can_item_be_reserved eq 'OK' || ( $can_item_be_reserved ne 'itemAlreadyOnHold' && $can_override ) ) {
94
            my $item = Koha::Items->find($checkitem);
106
            AddReserve( $branch, $borrower->{'borrowernumber'},
95
107
                $biblionumber, \@realbi, $rank[0], $startdate, $expirationdate, $notes, $title,
96
            if ( $item->biblionumber ne $biblionumber ) {
108
                $checkitem, $found, $itemtype );
97
                $biblionumber = $item->biblionumber;
98
            }
99
100
            my $can_item_be_reserved = CanItemBeReserved($borrower->{'borrowernumber'}, $item->itemnumber, $branch)->{status};
101
102
            if ( $can_item_be_reserved eq 'OK' || ( $can_item_be_reserved ne 'itemAlreadyOnHold' && $can_override ) ) {
103
                AddReserve( $branch, $borrower->{'borrowernumber'},
104
                    $biblionumber, \@realbi, $rank[0], $startdate, $expirationdate, $notes, $title,
105
                    $checkitem, $found, $itemtype );
106
            }
107
108
        } elsif ($multi_hold) {
109
            my $bibinfo = $bibinfos{$biblionumber};
110
            if ( $can_override || CanBookBeReserved($borrower->{'borrowernumber'}, $biblionumber)->{status} eq 'OK' ) {
111
                AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,[$biblionumber],
112
                           $bibinfo->{rank},$startdate,$expirationdate,$notes,$bibinfo->{title},$checkitem,$found);
113
            }
114
        } else {
115
            # place a request on 1st available
116
            for ( my $i = 0 ; $i < $holds_to_place_count ; $i++ ) {
117
                if ( $can_override || CanBookBeReserved($borrower->{'borrowernumber'}, $biblionumber)->{status} eq 'OK' ) {
118
                    AddReserve( $branch, $borrower->{'borrowernumber'},
119
                        $biblionumber, \@realbi, $rank[0], $startdate, $expirationdate, $notes, $title,
120
                        $checkitem, $found, $itemtype );
121
                }
122
            }
123
        }
109
        }
110
111
    } elsif ($multi_hold) {
112
        my $bibinfo = $bibinfos{$biblionumber};
113
        $reserve_rank = $bibinfo->{rank};
114
        $reserve_title = $bibinfo->{title};
115
    } else {
116
        $reserve_rank = $rank[0];
117
        $reserve_title = $title;
124
    }
118
    }
125
119
120
    if (!$confirm &&
121
        ReservesOnSamePeriod($biblionumber, $checkitem, $startdate, $expirationdate) &&
122
        C4::Context->preference("PreventReservesOnSamePeriod")) {
123
        $overlap_reserves->{$biblionumber} = {
124
            title => $reserve_title ,
125
            checkitem => $checkitem,
126
            rank => $reserve_rank
127
        };
128
        next;
129
    }
130
131
    if ( $can_override || CanBookBeReserved($borrower->{borrowernumber}, $biblionumber)->{status} eq 'OK' ) {
132
        AddReserve($branch, $borrower->{'borrowernumber'}, $biblionumber, undef,
133
            $reserve_rank, $startdate, $expirationdate, $notes, $reserve_title,
134
            $checkitem, $found);
135
    }
136
}
137
138
if (scalar keys %$overlap_reserves) {
139
    $template->param(
140
        borrowernumber => $borrowernumber,
141
        biblionumbers => $biblionumbers,
142
        biblionumber => $biblionumber,
143
        overlap_reserves => $overlap_reserves,
144
        reserve_date => $startdate,
145
        expiration_date => $expirationdate,
146
        notes => $notes,
147
        rank_request => \@rank,
148
        pickup => $branch,
149
        multi_hold => $multi_hold,
150
    );
151
152
    output_html_with_http_headers $input, $cookie, $template->output;
153
} else {
126
    if ($multi_hold) {
154
    if ($multi_hold) {
127
        if ($bad_bibs) {
155
        if ($bad_bibs) {
128
            $biblionumbers .= $bad_bibs;
156
            $biblionumbers .= $bad_bibs;
Lines 132-143 if ( $type eq 'str8' && $borrower ) { Link Here
132
    else {
160
    else {
133
        print $input->redirect("request.pl?biblionumber=$biblionumber");
161
        print $input->redirect("request.pl?biblionumber=$biblionumber");
134
    }
162
    }
135
}
163
    exit;
136
elsif ( $borrowernumber eq '' ) {
137
    print $input->header();
138
    print "Invalid borrower number please try again";
139
140
    # Not sure that Dump() does HTML escaping. Use firebug or something to trace
141
    # instead.
142
    #print $input->Dump;
143
}
164
}
(-)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