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

(-)a/C4/Circulation.pm (+23 lines)
Lines 1028-1033 sub CanBookBeIssued { Link Here
1028
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1028
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1029
                    $needsconfirmation{'resbranchcode'} = $res->{branchcode};
1029
                    $needsconfirmation{'resbranchcode'} = $res->{branchcode};
1030
                    $needsconfirmation{'reswaitingdate'} = $res->{'waitingdate'};
1030
                    $needsconfirmation{'reswaitingdate'} = $res->{'waitingdate'};
1031
                    $needsconfirmation{resreserveid} = $res->{reserve_id};
1031
                }
1032
                }
1032
                elsif ( $restype eq "Reserved" ) {
1033
                elsif ( $restype eq "Reserved" ) {
1033
                    # The item is on reserve for someone else.
1034
                    # The item is on reserve for someone else.
Lines 1038-1046 sub CanBookBeIssued { Link Here
1038
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1039
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1039
                    $needsconfirmation{'resbranchcode'} = $patron->branchcode;
1040
                    $needsconfirmation{'resbranchcode'} = $patron->branchcode;
1040
                    $needsconfirmation{'resreservedate'} = $res->{reservedate};
1041
                    $needsconfirmation{'resreservedate'} = $res->{reservedate};
1042
                    $needsconfirmation{resreserveid} = $res->{reserve_id};
1041
                }
1043
                }
1042
            }
1044
            }
1043
        }
1045
        }
1046
1047
        my $now = dt_from_string();
1048
        my $preventCheckoutOnSameReservePeriod =
1049
            C4::Context->preference("PreventCheckoutOnSameReservePeriod");
1050
        my $reserves_on_same_period =
1051
            ReservesOnSamePeriod($item_object->biblionumber, $item_object->itemnumber, $now->ymd, $duedate->ymd);
1052
        if ($preventCheckoutOnSameReservePeriod && $reserves_on_same_period) {
1053
            my $reserve = $reserves_on_same_period->[0];
1054
            my $patron = Koha::Patrons->find( $reserve->{borrowernumber} );
1055
            my $branchname = Koha::Libraries->find($reserve->{branchcode})->branchname;
1056
1057
            $needsconfirmation{RESERVED} = 1;
1058
            $needsconfirmation{resfirstname} = $patron->firstname;
1059
            $needsconfirmation{ressurname} = $patron->surname;
1060
            $needsconfirmation{rescardnumber} = $patron->cardnumber;
1061
            $needsconfirmation{resborrowernumber} = $patron->borrowernumber;
1062
            $needsconfirmation{resbranchname} = $branchname;
1063
            $needsconfirmation{resreservedate} = $reserve->{reservedate};
1064
            $needsconfirmation{resreserveid} = $reserve->{reserve_id};
1065
        }
1066
1044
    }
1067
    }
1045
1068
1046
    ## CHECK AGE RESTRICTION
1069
    ## 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 2171-2176 sub GetHoldRule { Link Here
2171
    return $sth->fetchrow_hashref();
2172
    return $sth->fetchrow_hashref();
2172
}
2173
}
2173
2174
2175
=head2 ReservesOnSamePeriod
2176
2177
    my $reserve = ReservesOnSamePeriod( $biblionumber, $itemnumber, $resdate, $expdate);
2178
2179
    Return the reserve that match the period ($resdate => $expdate),
2180
    undef if no reserve match.
2181
2182
=cut
2183
2184
sub ReservesOnSamePeriod {
2185
    my ($biblionumber, $itemnumber, $resdate, $expdate) = @_;
2186
2187
    unless ($resdate && $expdate) {
2188
        return;
2189
    }
2190
2191
    my @reserves = Koha::Holds->search({ biblionumber => $biblionumber });
2192
2193
    $resdate = output_pref({ str => $resdate, dateonly => 1, dateformat => 'iso' });
2194
    $expdate = output_pref({ str => $expdate, dateonly => 1, dateformat => 'iso' });
2195
2196
    my @reserves_overlaps;
2197
    foreach my $reserve ( @reserves ) {
2198
2199
        unless ($reserve->reservedate && $reserve->expirationdate) {
2200
            next;
2201
        }
2202
2203
        if (date_ranges_overlap($resdate, $expdate,
2204
                                $reserve->reservedate,
2205
                                $reserve->expirationdate)) {
2206
2207
            # If reserve is item level and the requested periods overlap.
2208
            if ($itemnumber && $reserve->itemnumber == $itemnumber ) {
2209
                return [$reserve->unblessed];
2210
            }
2211
            push @reserves_overlaps, $reserve->unblessed;
2212
        }
2213
    }
2214
2215
    if ( @reserves_overlaps >= Koha::Items->search({ biblionumber => $biblionumber })->count() ) {
2216
        return \@reserves_overlaps;
2217
    }
2218
2219
    return;
2220
}
2221
2174
=head1 AUTHOR
2222
=head1 AUTHOR
2175
2223
2176
Koha Development Team <http://koha-community.org/>
2224
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 419-424 if (@$barcodes) { Link Here
419
        }
419
        }
420
        unless($confirm_required) {
420
        unless($confirm_required) {
421
            my $switch_onsite_checkout = exists $messages->{ONSITE_CHECKOUT_WILL_BE_SWITCHED};
421
            my $switch_onsite_checkout = exists $messages->{ONSITE_CHECKOUT_WILL_BE_SWITCHED};
422
            if ( $cancelreserve eq 'cancel' ) {
423
                CancelReserve({ reserve_id => $query->param('reserveid') });
424
            }
425
            $cancelreserve = $cancelreserve eq 'revert' ? 'revert' : undef;
422
            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, } );
426
            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, } );
423
            $template_params->{issue} = $issue;
427
            $template_params->{issue} = $issue;
424
            $session->clear('auto_renew');
428
            $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 649-653 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
649
('XSLTListsDisplay','default','','Enable XSLT stylesheet control over lists pages display on intranet','Free'),
649
('XSLTListsDisplay','default','','Enable XSLT stylesheet control over lists pages display on intranet','Free'),
650
('XSLTResultsDisplay','default','','Enable XSL stylesheet control over results page display on intranet','Free'),
650
('XSLTResultsDisplay','default','','Enable XSL stylesheet control over results page display on intranet','Free'),
651
('z3950AuthorAuthFields','701,702,700',NULL,'Define the MARC biblio fields for Personal Name Authorities to fill biblio.author','free'),
651
('z3950AuthorAuthFields','701,702,700',NULL,'Define the MARC biblio fields for Personal Name Authorities to fill biblio.author','free'),
652
('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo')
652
('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo'),
653
('PreventCheckoutOnSameReservePeriod','0','','Prevent to checkout a document if a reserve on same period exists','YesNo'),
654
('PreventReservesOnSamePeriod','0','','Prevent to hold a document if a reserve on same period exists','YesNo')
653
;
655
;
(-)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 783-788 Circulation: Link Here
783
            - 'Example: "itemlost: 1" to set items.itemlost to 1 when the item is marked as lost'
789
            - 'Example: "itemlost: 1" to set items.itemlost to 1 when the item is marked as lost'
784
            - pref: UpdateItemWhenLostFromHoldList
790
            - pref: UpdateItemWhenLostFromHoldList
785
              type: textarea
791
              type: textarea
792
        -
793
            - pref: PreventReservesOnSamePeriod
794
              choices:
795
                  yes: Do
796
                  no: "Don't"
797
            - If yes, Reserves periods for the same document can't overlap.
786
    Interlibrary Loans:
798
    Interlibrary Loans:
787
        -
799
        -
788
            - pref: ILLModule
800
            - 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 287-292 if ( $query->param('place_reserve') ) { Link Here
287
            # Inserts a null into the 'itemnumber' field of 'reserves' table.
287
            # Inserts a null into the 'itemnumber' field of 'reserves' table.
288
            $itemNum = undef;
288
            $itemNum = undef;
289
        }
289
        }
290
291
        if ($canreserve) {
292
            if (C4::Context->preference("PreventReservesOnSamePeriod") &&
293
                ReservesOnSamePeriod($biblioNum, $itemNum, $startdate, $expiration_date)) {
294
                $canreserve = 0;
295
                $failed_holds++;
296
            }
297
        }
298
290
        my $notes = $query->param('notes_'.$biblioNum)||'';
299
        my $notes = $query->param('notes_'.$biblioNum)||'';
291
300
292
        if (   $maxreserves
301
        if (   $maxreserves
(-)a/reserve/placerequest.pl (-62 / +82 lines)
Lines 30-118 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;
79
        @bibitems = sort @bibitems;
80
        my $i2 = 1;
81
        my @realbi;
82
        $realbi[0] = $bibitems[0];
83
        for ( my $i = 1 ; $i < $count ; $i++ ) {
84
            my $i3 = $i2 - 1;
85
            if ( $realbi[$i3] ne $bibitems[$i] ) {
86
                $realbi[$i2] = $bibitems[$i];
87
                $i2++;
88
            }
89
        }
90
91
91
        if ( defined $checkitem && $checkitem ne '' ) {
92
    my ($reserve_title, $reserve_rank);
92
            my $item = Koha::Items->find($checkitem);
93
    if ($multi_hold) {
93
            if ( $item->biblionumber ne $biblionumber ) {
94
        my $bibinfo = $bibinfos{$biblionumber};
94
                $biblionumber = $item->biblionumber;
95
        $reserve_rank = $bibinfo->{rank};
95
            }
96
        $reserve_title = $bibinfo->{title};
96
        }
97
    } else {
98
        $reserve_rank = $rank[0];
99
        $reserve_title = $title;
100
    }
97
101
98
        if ($multi_hold) {
102
    if (defined $checkitem && $checkitem ne '') {
99
            my $bibinfo = $bibinfos{$biblionumber};
103
        my $item = Koha::Items->find($checkitem);
100
            if ( CanBookBeReserved($borrower->{'borrowernumber'}, $biblionumber)->{status} eq 'OK' ) {
104
        if ($item->biblionumber ne $biblionumber) {
101
                AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,[$biblionumber],
105
            $biblionumber = $item->biblionumber;
102
                           $bibinfo->{rank},$startdate,$expirationdate,$notes,$bibinfo->{title},$checkitem,$found);
103
            }
104
        } else {
105
            # place a request on 1st available
106
            for ( my $i = 0 ; $i < $holds_to_place_count ; $i++ ) {
107
                if ( CanBookBeReserved($borrower->{'borrowernumber'}, $biblionumber)->{status} eq 'OK' ) {
108
                    AddReserve( $branch, $borrower->{'borrowernumber'},
109
                        $biblionumber, \@realbi, $rank[0], $startdate, $expirationdate, $notes, $title,
110
                        $checkitem, $found, $itemtype );
111
                }
112
            }
113
        }
106
        }
114
    }
107
    }
115
108
109
    if (!$confirm &&
110
        ReservesOnSamePeriod($biblionumber, $checkitem, $startdate, $expirationdate) &&
111
        C4::Context->preference("PreventReservesOnSamePeriod")) {
112
        $overlap_reserves->{$biblionumber} = {
113
            title => $reserve_title ,
114
            checkitem => $checkitem,
115
            rank => $reserve_rank
116
        };
117
        next;
118
    }
119
120
    if ( CanBookBeReserved($borrower->{borrowernumber}, $biblionumber)->{status} eq 'OK' ) {
121
        AddReserve($branch, $borrower->{'borrowernumber'}, $biblionumber, undef,
122
            $reserve_rank, $startdate, $expirationdate, $notes, $reserve_title,
123
            $checkitem, $found);
124
    }
125
}
126
127
if (scalar keys %$overlap_reserves) {
128
    $template->param(
129
        borrowernumber => $borrowernumber,
130
        biblionumbers => $biblionumbers,
131
        biblionumber => $biblionumber,
132
        overlap_reserves => $overlap_reserves,
133
        reserve_date => $startdate,
134
        expiration_date => $expirationdate,
135
        notes => $notes,
136
        rank_request => \@rank,
137
        pickup => $branch,
138
        multi_hold => $multi_hold,
139
    );
140
141
    output_html_with_http_headers $input, $cookie, $template->output;
142
} else {
116
    if ($multi_hold) {
143
    if ($multi_hold) {
117
        if ($bad_bibs) {
144
        if ($bad_bibs) {
118
            $biblionumbers .= $bad_bibs;
145
            $biblionumbers .= $bad_bibs;
Lines 122-133 if ( $type eq 'str8' && $borrower ) { Link Here
122
    else {
149
    else {
123
        print $input->redirect("request.pl?biblionumber=$biblionumber");
150
        print $input->redirect("request.pl?biblionumber=$biblionumber");
124
    }
151
    }
125
}
152
    exit;
126
elsif ( $borrowernumber eq '' ) {
127
    print $input->header();
128
    print "Invalid borrower number please try again";
129
130
    # Not sure that Dump() does HTML escaping. Use firebug or something to trace
131
    # instead.
132
    #print $input->Dump;
133
}
153
}
(-)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