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

(-)a/C4/Circulation.pm (+23 lines)
Lines 1014-1019 sub CanBookBeIssued { Link Here
1014
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1014
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1015
                    $needsconfirmation{'resbranchcode'} = $res->{branchcode};
1015
                    $needsconfirmation{'resbranchcode'} = $res->{branchcode};
1016
                    $needsconfirmation{'reswaitingdate'} = $res->{'waitingdate'};
1016
                    $needsconfirmation{'reswaitingdate'} = $res->{'waitingdate'};
1017
                    $needsconfirmation{resreserveid} = $res->{reserve_id};
1017
                }
1018
                }
1018
                elsif ( $restype eq "Reserved" ) {
1019
                elsif ( $restype eq "Reserved" ) {
1019
                    # The item is on reserve for someone else.
1020
                    # The item is on reserve for someone else.
Lines 1024-1032 sub CanBookBeIssued { Link Here
1024
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1025
                    $needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
1025
                    $needsconfirmation{'resbranchcode'} = $patron->branchcode;
1026
                    $needsconfirmation{'resbranchcode'} = $patron->branchcode;
1026
                    $needsconfirmation{'resreservedate'} = $res->{reservedate};
1027
                    $needsconfirmation{'resreservedate'} = $res->{reservedate};
1028
                    $needsconfirmation{resreserveid} = $res->{reserve_id};
1027
                }
1029
                }
1028
            }
1030
            }
1029
        }
1031
        }
1032
1033
        my $now = dt_from_string();
1034
        my $preventCheckoutOnSameReservePeriod =
1035
            C4::Context->preference("PreventCheckoutOnSameReservePeriod");
1036
        my $reserves_on_same_period =
1037
            ReservesOnSamePeriod($item->{biblionumber}, $item->{itemnumber}, $now->ymd, $duedate->ymd);
1038
        if ($preventCheckoutOnSameReservePeriod && $reserves_on_same_period) {
1039
            my $reserve = $reserves_on_same_period->[0];
1040
            my $patron = Koha::Patrons->find( $reserve->{borrowernumber} );
1041
            my $branchname = Koha::Libraries->find($reserve->{branchcode})->branchname;
1042
1043
            $needsconfirmation{RESERVED} = 1;
1044
            $needsconfirmation{resfirstname} = $patron->firstname;
1045
            $needsconfirmation{ressurname} = $patron->surname;
1046
            $needsconfirmation{rescardnumber} = $patron->cardnumber;
1047
            $needsconfirmation{resborrowernumber} = $patron->borrowernumber;
1048
            $needsconfirmation{resbranchname} = $branchname;
1049
            $needsconfirmation{resreservedate} = $reserve->{reservedate};
1050
            $needsconfirmation{resreserveid} = $reserve->{reserve_id};
1051
        }
1052
1030
    }
1053
    }
1031
1054
1032
    ## CHECK AGE RESTRICTION
1055
    ## CHECK AGE RESTRICTION
(-)a/C4/Reserves.pm (+48 lines)
Lines 137-142 BEGIN { Link Here
137
        &SuspendAll
137
        &SuspendAll
138
138
139
        &GetReservesControlBranch
139
        &GetReservesControlBranch
140
		&ReservesOnSamePeriod
140
141
141
        IsItemOnHoldAndFound
142
        IsItemOnHoldAndFound
142
143
Lines 2164-2169 sub GetHoldRule { Link Here
2164
    return $sth->fetchrow_hashref();
2165
    return $sth->fetchrow_hashref();
2165
}
2166
}
2166
2167
2168
=head2 ReservesOnSamePeriod
2169
2170
    my $reserve = ReservesOnSamePeriod( $biblionumber, $itemnumber, $resdate, $expdate);
2171
2172
    Return the reserve that match the period ($resdate => $expdate),
2173
    undef if no reserve match.
2174
2175
=cut
2176
2177
sub ReservesOnSamePeriod {
2178
    my ($biblionumber, $itemnumber, $resdate, $expdate) = @_;
2179
2180
    unless ($resdate && $expdate) {
2181
        return;
2182
    }
2183
2184
    my @reserves = Koha::Holds->search({ biblionumber => $biblionumber });
2185
2186
    $resdate = output_pref({ str => $resdate, dateonly => 1, dateformat => 'iso' });
2187
    $expdate = output_pref({ str => $expdate, dateonly => 1, dateformat => 'iso' });
2188
2189
    my @reserves_overlaps;
2190
    foreach my $reserve ( @reserves ) {
2191
2192
        unless ($reserve->reservedate && $reserve->expirationdate) {
2193
            next;
2194
        }
2195
2196
        if (date_ranges_overlap($resdate, $expdate,
2197
                                $reserve->reservedate,
2198
                                $reserve->expirationdate)) {
2199
2200
            # If reserve is item level and the requested periods overlap.
2201
            if ($itemnumber && $reserve->itemnumber == $itemnumber ) {
2202
                return [$reserve];
2203
            }
2204
            push @reserves_overlaps, $reserve->unblessed;
2205
        }
2206
    }
2207
2208
    if ( @reserves_overlaps >= Koha::Items->search({ biblionumber => $biblionumber })->count() ) {
2209
        return \@reserves_overlaps;
2210
    }
2211
2212
    return;
2213
}
2214
2167
=head1 AUTHOR
2215
=head1 AUTHOR
2168
2216
2169
Koha Development Team <http://koha-community.org/>
2217
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 297-300 sub format_sqldatetime { Link Here
297
    return q{};
297
    return q{};
298
}
298
}
299
299
300
=head2 date_ranges_overlap
301
302
    $bool = date_ranges_overlap($start1, $end1, $start2, $end2);
303
304
    Tells if first range ($start1 => $end1) overlaps
305
    the second one ($start2 => $end2)
306
307
=cut
308
309
sub date_ranges_overlap {
310
    my ($start1, $end1, $start2, $end2) = @_;
311
312
    $start1 = dt_from_string( $start1, 'iso' );
313
    $end1 = dt_from_string( $end1, 'iso' );
314
    $start2 = dt_from_string( $start2, 'iso' );
315
    $end2 = dt_from_string( $end2, 'iso' );
316
317
    if (
318
        # Start of range 2 is in the range 1.
319
        (
320
            DateTime->compare($start2, $start1) >= 0
321
            && DateTime->compare($start2, $end1) <= 0
322
        )
323
        ||
324
        # End of range 2 is in the range 1.
325
        (
326
            DateTime->compare($end2, $start1) >= 0
327
            && DateTime->compare($end2, $end1) <= 0
328
        )
329
        ||
330
        # Range 2 start before and end after range 1.
331
        (
332
            DateTime->compare($start2, $start1) < 0
333
            && DateTime->compare($end2, $end1) > 0
334
        )
335
    ) {
336
        return 1;
337
    }
338
339
    return;
340
}
341
300
1;
342
1;
(-)a/circ/circulation.pl (+4 lines)
Lines 404-409 if (@$barcodes) { Link Here
404
        }
404
        }
405
        unless($confirm_required) {
405
        unless($confirm_required) {
406
            my $switch_onsite_checkout = exists $messages->{ONSITE_CHECKOUT_WILL_BE_SWITCHED};
406
            my $switch_onsite_checkout = exists $messages->{ONSITE_CHECKOUT_WILL_BE_SWITCHED};
407
            if ( $cancelreserve eq 'cancel' ) {
408
                CancelReserve({ reserve_id => $query->param('reserveid') });
409
            }
410
            $cancelreserve = $cancelreserve eq 'revert' ? 'revert' : undef;
407
            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, } );
411
            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, } );
408
            $template_params->{issue} = $issue;
412
            $template_params->{issue} = $issue;
409
            $session->clear('auto_renew');
413
            $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 594-598 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
594
('XSLTListsDisplay','default','','Enable XSLT stylesheet control over lists pages display on intranet','Free'),
594
('XSLTListsDisplay','default','','Enable XSLT stylesheet control over lists pages display on intranet','Free'),
595
('XSLTResultsDisplay','default','','Enable XSL stylesheet control over results page display on intranet','Free'),
595
('XSLTResultsDisplay','default','','Enable XSL stylesheet control over results page display on intranet','Free'),
596
('z3950AuthorAuthFields','701,702,700',NULL,'Define the MARC biblio fields for Personal Name Authorities to fill biblio.author','free'),
596
('z3950AuthorAuthFields','701,702,700',NULL,'Define the MARC biblio fields for Personal Name Authorities to fill biblio.author','free'),
597
('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo')
597
('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo'),
598
('PreventCheckoutOnSameReservePeriod','0','','Prevent to checkout a document if a reserve on same period exists','YesNo'),
599
('PreventReservesOnSamePeriod','0','','Prevent to hold a document if a reserve on same period exists','YesNo')
598
;
600
;
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (+12 lines)
Lines 459-464 Circulation: Link Here
459
                  yes: Block
459
                  yes: Block
460
                  no: Allow
460
                  no: Allow
461
            - his/her auto renewals.
461
            - his/her auto renewals.
462
        -
463
            - pref: PreventCheckoutOnSameReservePeriod
464
              choices:
465
                  yes: Do
466
                  no: "Don't"
467
            - If yes, checkouts periods can't overlap with a reserve period.
462
    Checkin Policy:
468
    Checkin Policy:
463
        -
469
        -
464
            - pref: BlockReturnOfWithdrawnItems
470
            - pref: BlockReturnOfWithdrawnItems
Lines 690-695 Circulation: Link Here
690
            - "Patron categories not affected by OPACHoldsIfAvailableAtPickup"
696
            - "Patron categories not affected by OPACHoldsIfAvailableAtPickup"
691
            - pref: OPACHoldsIfAvailableAtPickupExceptions
697
            - pref: OPACHoldsIfAvailableAtPickupExceptions
692
            - "(list of patron categories separated with a pipe '|')"
698
            - "(list of patron categories separated with a pipe '|')"
699
        -
700
            - pref: PreventReservesOnSamePeriod
701
              choices:
702
                  yes: Do
703
                  no: "Don't"
704
            - If yes, Reserves periods for the same document can't overlap.
693
    Fines Policy:
705
    Fines Policy:
694
        -
706
        -
695
            - Calculate fines based on days overdue
707
            - Calculate fines based on days overdue
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt (+2 lines)
Lines 337-342 $(document).ready(function() { Link Here
337
[% IF ( RESERVED ) %]
337
[% IF ( RESERVED ) %]
338
    <p>
338
    <p>
339
    <input type="checkbox" id="cancelreserve" name="cancelreserve" value="cancel" />
339
    <input type="checkbox" id="cancelreserve" name="cancelreserve" value="cancel" />
340
    <input type="hidden" name="reserveid" value="[% resreserveid %]" />
340
    <label for="cancelreserve">Cancel hold</label>
341
    <label for="cancelreserve">Cancel hold</label>
341
    </p>
342
    </p>
342
[% END %]
343
[% END %]
Lines 345-350 $(document).ready(function() { Link Here
345
<p>
346
<p>
346
    <label for="cancelreserve">Cancel hold</label>
347
    <label for="cancelreserve">Cancel hold</label>
347
    <input type="radio" value="cancel" name="cancelreserve" id="cancelreserve" /><br />
348
    <input type="radio" value="cancel" name="cancelreserve" id="cancelreserve" /><br />
349
    <input type="hidden" name="reserveid" value="[% resreserveid %]" />
348
    <label for="revertreserve">Revert waiting status</label>
350
    <label for="revertreserve">Revert waiting status</label>
349
    <input type="radio" value="revert" name="cancelreserve" id="revertreserve" checked="checked"/>
351
    <input type="radio" value="revert" name="cancelreserve" id="revertreserve" checked="checked"/>
350
</p>
352
</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 %]">
34
          [% IF multi_hold %]
35
            <input type="hidden" name="biblionumbers" value="[% biblionumbers%]">
36
            <input type="hidden" name="multi_hold" value="1">
37
          [% ELSE %]
38
            <input type="hidden" name="biblionumber" value="[% biblionumber%]">
39
          [% END %]
40
          <input type="hidden" name="reserve_date" value="[% reserve_date %]">
41
          <input type="hidden" name="expiration_date" value="[% expiration_date %]">
42
          <input type="hidden" name="pickup" value="[% pickup%]">
43
          <input type="hidden" name="notes" value="[% notes %]">
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 %]" value="[% overlap_reserves.$biblionumber.rank %]">
50
              [% IF (overlap_reserves.$biblionumber.checkitem) %]
51
                <input type="hidden" name="checkitem" value="[% overlap_reserves.$biblionumber.checkitem%]">
52
              [% END %]
53
              <input type="checkbox" name="confirm_biblionumbers" id="[% input_id %]"
54
                     value="[% biblionumber %]">
55
              <label for="[% input_id %]">Confirm hold for [% overlap_reserves.$biblionumber.title %]</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 284-289 if ( $query->param('place_reserve') ) { Link Here
284
            # Inserts a null into the 'itemnumber' field of 'reserves' table.
284
            # Inserts a null into the 'itemnumber' field of 'reserves' table.
285
            $itemNum = undef;
285
            $itemNum = undef;
286
        }
286
        }
287
288
        if ($canreserve) {
289
            if (C4::Context->preference("PreventReservesOnSamePeriod") &&
290
                ReservesOnSamePeriod($biblioNum, $itemNum, $startdate, $expiration_date)) {
291
                $canreserve = 0;
292
                $failed_holds++;
293
            }
294
        }
295
287
        my $notes = $query->param('notes_'.$biblioNum)||'';
296
        my $notes = $query->param('notes_'.$biblioNum)||'';
288
297
289
        if (   $maxreserves
298
        if (   $maxreserves
(-)a/reserve/placerequest.pl (-60 / +82 lines)
Lines 31-78 use C4::Output; Link Here
31
use C4::Reserves;
31
use C4::Reserves;
32
use C4::Circulation;
32
use C4::Circulation;
33
use C4::Members;
33
use C4::Members;
34
use C4::Auth qw/checkauth/;
34
use C4::Auth;
35
use Koha::Patrons;
35
use Koha::Patrons;
36
36
37
my $input = CGI->new();
37
my $input = CGI->new();
38
38
39
checkauth($input, 0, { reserveforothers => 'place_holds' }, 'intranet');
39
my ( $template, $borrowernumber, $cookie, $flags ) = get_template_and_user(
40
40
    {
41
my @bibitems       = $input->multi_param('biblioitem');
41
        template_name   => "reserve/placerequest.tt",
42
my @reqbib         = $input->multi_param('reqbib');
42
        query           => $input,
43
my $biblionumber   = $input->param('biblionumber');
43
        type            => "intranet",
44
my $borrowernumber = $input->param('borrowernumber');
44
        authnotrequired => 0,
45
my $notes          = $input->param('notes');
45
        flagsrequired   => { reserveforothers => 'place_holds' },
46
my $branch         = $input->param('pickup');
46
    }
47
my $startdate      = $input->param('reserve_date') || '';
47
);
48
my @rank           = $input->multi_param('rank-request');
48
49
my $type           = $input->param('type');
49
my $biblionumber=$input->param('biblionumber');
50
my $title          = $input->param('title');
50
my $borrowernumber=$input->param('borrowernumber');
51
my $checkitem      = $input->param('checkitem');
51
my $notes=$input->param('notes');
52
my $branch=$input->param('pickup');
53
my $startdate=$input->param('reserve_date') || '';
54
my @rank=$input->param('rank-request');
55
my $title=$input->param('title');
56
my $checkitem=$input->param('checkitem');
52
my $expirationdate = $input->param('expiration_date');
57
my $expirationdate = $input->param('expiration_date');
53
my $itemtype       = $input->param('itemtype') || undef;
58
my $itemtype       = $input->param('itemtype') || undef;
54
59
my $confirm = $input->param('confirm');
55
my $borrower = Koha::Patrons->find( $borrowernumber );
60
my @confirm_biblionumbers = $input->param('confirm_biblionumbers');
56
$borrower = $borrower->unblessed if $borrower;
57
61
58
my $multi_hold = $input->param('multi_hold');
62
my $multi_hold = $input->param('multi_hold');
59
my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ($biblionumber . '/');
63
my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ($biblionumber . '/');
60
my $bad_bibs = $input->param('bad_bibs');
64
my $bad_bibs = $input->param('bad_bibs');
61
my $holds_to_place_count = $input->param('holds_to_place_count') || 1;
65
my $holds_to_place_count = $input->param('holds_to_place_count') || 1;
62
66
67
my $borrower = Koha::Patrons->find( $borrowernumber );
68
$borrower = $borrower->unblessed if $borrower;
69
unless ($borrower) {
70
    print $input->header();
71
    print "Invalid borrower number please try again";
72
    exit;
73
}
74
63
my %bibinfos = ();
75
my %bibinfos = ();
64
my @biblionumbers = split '/', $biblionumbers;
76
my @biblionumbers = split '/', $biblionumbers;
65
foreach my $bibnum (@biblionumbers) {
77
foreach my $bibnum (@biblionumbers) {
66
    my %bibinfo = ();
78
    my %bibinfo;
67
    $bibinfo{title} = $input->param("title_$bibnum");
79
    $bibinfo{title} = $input->param("title_$bibnum");
80
68
    $bibinfo{rank} = $input->param("rank_$bibnum");
81
    $bibinfo{rank} = $input->param("rank_$bibnum");
69
    $bibinfos{$bibnum} = \%bibinfo;
82
    $bibinfos{$bibnum} = \%bibinfo;
70
}
83
}
71
84
72
my $found;
85
my $found;
73
86
74
# if we have an item selectionned, and the pickup branch is the same as the holdingbranch
87
# if we have an item selectionned, and the pickup branch is the same as the
75
# of the document, we force the value $rank and $found .
88
# holdingbranch of the document, we force the value $rank and $found .
76
if (defined $checkitem && $checkitem ne ''){
89
if (defined $checkitem && $checkitem ne ''){
77
    $holds_to_place_count = 1;
90
    $holds_to_place_count = 1;
78
    $rank[0] = '0' unless C4::Context->preference('ReservesNeedReturns');
91
    $rank[0] = '0' unless C4::Context->preference('ReservesNeedReturns');
Lines 83-125 if (defined $checkitem && $checkitem ne ''){ Link Here
83
    }
96
    }
84
}
97
}
85
98
86
if ( $type eq 'str8' && $borrower ) {
99
my $overlap_reserves = {};
87
100
foreach my $biblionumber (keys %bibinfos) {
88
    foreach my $biblionumber ( keys %bibinfos ) {
101
    next if ($confirm && !grep { $_ eq $biblionumber } @confirm_biblionumbers);
89
        my $count = @bibitems;
90
        @bibitems = sort @bibitems;
91
        my $i2 = 1;
92
        my @realbi;
93
        $realbi[0] = $bibitems[0];
94
        for ( my $i = 1 ; $i < $count ; $i++ ) {
95
            my $i3 = $i2 - 1;
96
            if ( $realbi[$i3] ne $bibitems[$i] ) {
97
                $realbi[$i2] = $bibitems[$i];
98
                $i2++;
99
            }
100
        }
101
102
102
        if ( defined $checkitem && $checkitem ne '' ) {
103
    my ($reserve_title, $reserve_rank);
103
            my $item = GetItem($checkitem);
104
    if ($multi_hold) {
104
            if ( $item->{'biblionumber'} ne $biblionumber ) {
105
        my $bibinfo = $bibinfos{$biblionumber};
105
                $biblionumber = $item->{'biblionumber'};
106
        $reserve_rank = $bibinfo->{rank};
106
            }
107
        $reserve_title = $bibinfo->{title};
107
        }
108
    } else {
109
        $reserve_rank = $rank[0];
110
        $reserve_title = $title;
111
    }
108
112
109
        if ($multi_hold) {
113
    if (defined $checkitem && $checkitem ne '') {
110
            my $bibinfo = $bibinfos{$biblionumber};
114
        my $item = GetItem($checkitem);
111
            AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,[$biblionumber],
115
        if ($item->{'biblionumber'} ne $biblionumber) {
112
                       $bibinfo->{rank},$startdate,$expirationdate,$notes,$bibinfo->{title},$checkitem,$found);
116
            $biblionumber = $item->{'biblionumber'};
113
        } else {
114
            # place a request on 1st available
115
            for ( my $i = 0 ; $i < $holds_to_place_count ; $i++ ) {
116
                AddReserve( $branch, $borrower->{'borrowernumber'},
117
                    $biblionumber, \@realbi, $rank[0], $startdate, $expirationdate, $notes, $title,
118
                    $checkitem, $found, $itemtype );
119
            }
120
        }
117
        }
121
    }
118
    }
122
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
    AddReserve($branch, $borrower->{'borrowernumber'}, $biblionumber, undef,
132
        $reserve_rank, $startdate, $expirationdate, $notes, $reserve_title,
133
        $checkitem, $found);
134
}
135
136
if (scalar keys %$overlap_reserves) {
137
    $template->param(
138
        borrowernumber => $borrowernumber,
139
        biblionumbers => $biblionumbers,
140
        biblionumber => $biblionumber,
141
        overlap_reserves => $overlap_reserves,
142
        reserve_date => $startdate,
143
        expiration_date => $expirationdate,
144
        notes => $notes,
145
        rank_request => \@rank,
146
        pickup => $branch,
147
        multi_hold => $multi_hold,
148
    );
149
150
    output_html_with_http_headers $input, $cookie, $template->output;
151
} else {
123
    if ($multi_hold) {
152
    if ($multi_hold) {
124
        if ($bad_bibs) {
153
        if ($bad_bibs) {
125
            $biblionumbers .= $bad_bibs;
154
            $biblionumbers .= $bad_bibs;
Lines 129-140 if ( $type eq 'str8' && $borrower ) { Link Here
129
    else {
158
    else {
130
        print $input->redirect("request.pl?biblionumber=$biblionumber");
159
        print $input->redirect("request.pl?biblionumber=$biblionumber");
131
    }
160
    }
132
}
161
    exit;
133
elsif ( $borrowernumber eq '' ) {
134
    print $input->header();
135
    print "Invalid borrower number please try again";
136
137
    # Not sure that Dump() does HTML escaping. Use firebug or something to trace
138
    # instead.
139
    #print $input->Dump;
140
}
162
}
(-)a/t/db_dependent/Circulation/CanBookBeIssued.t (+107 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 C4::Branch;
25
use Koha::DateUtils;
26
27
use t::lib::TestBuilder;
28
29
my $schema  = Koha::Database->new->schema;
30
$schema->storage->txn_begin;
31
32
my $builder = t::lib::TestBuilder->new();
33
34
subtest 'Tests for CanBookBeIssued with overlap reserves' => sub {
35
    plan tests => 6;
36
37
    my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
38
    my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode };
39
40
    my $borrower = $builder->build({
41
        source => 'Borrower',
42
        value => {
43
            branchcode   => $branchcode,
44
            categorycode => $categorycode,
45
        }
46
    });
47
    my $borrowernumber = $borrower->{borrowernumber};
48
    $borrower = GetMemberDetails($borrowernumber);
49
50
    my $biblio = $builder->build({source => 'Biblio'});
51
    my $biblioitem = $builder->build({
52
        source => 'Biblioitem',
53
        value => {
54
            biblionumber => $biblio->{biblionumber},
55
        },
56
    });
57
    my $item = $builder->build({
58
        source => 'Item',
59
        value => {
60
            biblionumber => $biblio->{biblionumber},
61
            biblioitemnumber => $biblioitem->{biblioitemnumber},
62
            withdrawn => 0,
63
            itemlost => 0,
64
            notforloan => 0,
65
        },
66
    });
67
68
69
    my $startdate = dt_from_string();
70
    $startdate->add_duration(DateTime::Duration->new(days => 4));
71
    my $expdate = $startdate->clone();
72
    $expdate->add_duration(DateTime::Duration->new(days => 10));
73
74
    my $reserveid = AddReserve($branchcode, $borrowernumber,
75
        $item->{biblionumber}, undef,  1, $startdate->ymd(), $expdate->ymd,
76
        undef, undef, undef, undef);
77
78
    my $non_overlap_duedate = dt_from_string();
79
    $non_overlap_duedate->add_duration(DateTime::Duration->new(days => 2));
80
    my ($error, $question, $alerts ) =
81
        CanBookBeIssued($borrower, $item->{barcode}, $non_overlap_duedate, 1, 0);
82
83
    is_deeply($error, {}, "");
84
    is_deeply($question, {}, "");
85
    is_deeply($alerts, {}, "");
86
87
    my $overlap_duedate = dt_from_string();
88
    $overlap_duedate->add_duration(DateTime::Duration->new(days => 5));
89
    ($error, $question, $alerts ) =
90
        CanBookBeIssued($borrower, $item->{barcode}, $overlap_duedate, 1, 0);
91
92
    is_deeply($error, {}, "");
93
    my $expected = {
94
        RESERVED => 1,
95
        resfirstname => $borrower->{firstname},
96
        ressurname => $borrower->{surname},
97
        rescardnumber => $borrower->{cardnumber},
98
        resborrowernumber => $borrower->{borrowernumber},
99
        resbranchname => GetBranchName($branchcode),
100
        resreservedate => $startdate->ymd,
101
        resreserveid => $reserveid,
102
    };
103
    is_deeply($question, $expected, "");
104
    is_deeply($alerts, {}, "");
105
};
106
107
$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::Branch;
27
use C4::Biblio;
28
use C4::Items;
29
use C4::Members;
30
use C4::Circulation;
31
use Koha::Holds;
32
use t::lib::TestBuilder;
33
34
use Koha::DateUtils;
35
36
37
use_ok('C4::Reserves');
38
39
my $dbh = C4::Context->dbh;
40
41
# Start transaction
42
$dbh->{AutoCommit} = 0;
43
$dbh->{RaiseError} = 1;
44
45
my $builder = t::lib::TestBuilder->new();
46
my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
47
my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode };
48
49
my $borrower = $builder->build({
50
    source => 'Borrower',
51
    value => {
52
        branchcode   => $branchcode,
53
        categorycode => $categorycode,
54
    }
55
});
56
57
my $borrower2 = $builder->build({
58
    source => 'Borrower',
59
    value => {
60
        branchcode   => $branchcode,
61
        categorycode => $categorycode,
62
    }
63
});
64
65
my $borrowernumber = $borrower->{borrowernumber};
66
my $borrowernumber2 = $borrower2->{borrowernumber};
67
68
# Create a helper biblio
69
my $biblio = MARC::Record->new();
70
my $title = 'Alone in the Dark';
71
my $author = 'Karen Rose';
72
if( C4::Context->preference('marcflavour') eq 'UNIMARC' ) {
73
    $biblio->append_fields(
74
        MARC::Field->new('600', '', '1', a => $author),
75
        MARC::Field->new('200', '', '', a => $title),
76
    );
77
}
78
else {
79
    $biblio->append_fields(
80
        MARC::Field->new('100', '', '', a => $author),
81
        MARC::Field->new('245', '', '', a => $title),
82
    );
83
}
84
my ($bibnum, $bibitemnum);
85
($bibnum, $title, $bibitemnum) = AddBiblio($biblio, '');
86
87
my ($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem({ homebranch => $branchcode, holdingbranch => $branchcode, barcode => '333' } , $bibnum);
88
89
C4::Context->set_preference('AllowHoldDateInFuture', 1);
90
91
AddReserve($branchcode, $borrowernumber, $bibnum,
92
           $bibitemnum,  1, '2015-11-01', '2015-11-20', undef,
93
           undef, undef, undef);
94
95
is(ReservesOnSamePeriod($bibnum, undef, '2015-11-25', '2015-11-30'), undef, "Period doesn't overlaps");
96
97
ok(ReservesOnSamePeriod($bibnum, undef, '2015-11-02', '2015-11-10'), "Period overlaps");
98
99
my ($item_bibnum2, $item_bibitemnum2, $itemnumber2) = AddItem({ homebranch => $branchcode, holdingbranch => $branchcode, barcode => '444' } , $bibnum);
100
is(ReservesOnSamePeriod($bibnum, undef, '2015-11-02', '2015-11-10'), undef, "Period overlaps but there is 2 items");
101
102
AddReserve($branchcode, $borrowernumber2, $bibnum,
103
           $bibitemnum,  1, '2016-02-01', '2016-02-10', undef,
104
           undef, $itemnumber, undef);
105
is(ReservesOnSamePeriod($bibnum, $itemnumber, '02/12/2015', '10/12/2015'), undef, "Period on item does not overlap (with metric date format)");
106
107
ok(ReservesOnSamePeriod($bibnum, $itemnumber, '2016-01-31', '2016-02-05'), "Period on item overlaps");
108
$dbh->rollback;

Return to bug 15261