@@ -, +, @@
overlap...
PreventChechoutOnSameReservePeriod and PreventReservesOnSamePeriod
---
C4/Circulation.pm | 23 +++
C4/Reserves.pm | 48 ++++++
Koha/DateUtils.pm | 44 +++++-
circ/circulation.pl | 4 +
...entchechoutonsamereserveperiod_syspref.sql | 1 +
...dd_preventreservesonsameperiod_syspref.sql | 1 +
installer/data/mysql/sysprefs.sql | 4 +-
.../admin/preferences/circulation.pref | 12 ++
.../prog/en/modules/circ/circulation.tt | 2 +
.../prog/en/modules/reserve/placerequest.tt | 66 ++++++++
opac/opac-reserve.pl | 9 ++
reserve/placerequest.pl | 144 ++++++++++--------
t/db_dependent/Circulation/CanBookBeIssued.t | 106 +++++++++++++
t/db_dependent/Reserves/ReserveDate.t | 108 +++++++++++++
14 files changed, 508 insertions(+), 64 deletions(-)
create mode 100644 installer/data/mysql/atomicupdate/bug_15261-add_preventchechoutonsamereserveperiod_syspref.sql
create mode 100644 installer/data/mysql/atomicupdate/bug_15261-add_preventreservesonsameperiod_syspref.sql
create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/reserve/placerequest.tt
create mode 100644 t/db_dependent/Circulation/CanBookBeIssued.t
create mode 100644 t/db_dependent/Reserves/ReserveDate.t
--- a/C4/Circulation.pm
+++ a/C4/Circulation.pm
@@ -1028,6 +1028,7 @@ sub CanBookBeIssued {
$needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
$needsconfirmation{'resbranchcode'} = $res->{branchcode};
$needsconfirmation{'reswaitingdate'} = $res->{'waitingdate'};
+ $needsconfirmation{resreserveid} = $res->{reserve_id};
}
elsif ( $restype eq "Reserved" ) {
# The item is on reserve for someone else.
@@ -1038,9 +1039,31 @@ sub CanBookBeIssued {
$needsconfirmation{'resborrowernumber'} = $patron->borrowernumber;
$needsconfirmation{'resbranchcode'} = $patron->branchcode;
$needsconfirmation{'resreservedate'} = $res->{reservedate};
+ $needsconfirmation{resreserveid} = $res->{reserve_id};
}
}
}
+
+ my $now = dt_from_string();
+ my $preventCheckoutOnSameReservePeriod =
+ C4::Context->preference("PreventCheckoutOnSameReservePeriod");
+ my $reserves_on_same_period =
+ ReservesOnSamePeriod($item_object->biblionumber, $item_object->itemnumber, $now->ymd, $duedate->ymd);
+ if ($preventCheckoutOnSameReservePeriod && $reserves_on_same_period) {
+ my $reserve = $reserves_on_same_period->[0];
+ my $patron = Koha::Patrons->find( $reserve->{borrowernumber} );
+ my $branchname = Koha::Libraries->find($reserve->{branchcode})->branchname;
+
+ $needsconfirmation{RESERVED} = 1;
+ $needsconfirmation{resfirstname} = $patron->firstname;
+ $needsconfirmation{ressurname} = $patron->surname;
+ $needsconfirmation{rescardnumber} = $patron->cardnumber;
+ $needsconfirmation{resborrowernumber} = $patron->borrowernumber;
+ $needsconfirmation{resbranchname} = $branchname;
+ $needsconfirmation{resreservedate} = $reserve->{reservedate};
+ $needsconfirmation{resreserveid} = $reserve->{reserve_id};
+ }
+
}
## CHECK AGE RESTRICTION
--- a/C4/Reserves.pm
+++ a/C4/Reserves.pm
@@ -133,6 +133,7 @@ BEGIN {
&SuspendAll
&GetReservesControlBranch
+ &ReservesOnSamePeriod
IsItemOnHoldAndFound
@@ -2171,6 +2172,53 @@ sub GetHoldRule {
return $sth->fetchrow_hashref();
}
+=head2 ReservesOnSamePeriod
+
+ my $reserve = ReservesOnSamePeriod( $biblionumber, $itemnumber, $resdate, $expdate);
+
+ Return the reserve that match the period ($resdate => $expdate),
+ undef if no reserve match.
+
+=cut
+
+sub ReservesOnSamePeriod {
+ my ($biblionumber, $itemnumber, $resdate, $expdate) = @_;
+
+ unless ($resdate && $expdate) {
+ return;
+ }
+
+ my @reserves = Koha::Holds->search({ biblionumber => $biblionumber });
+
+ $resdate = output_pref({ str => $resdate, dateonly => 1, dateformat => 'iso' });
+ $expdate = output_pref({ str => $expdate, dateonly => 1, dateformat => 'iso' });
+
+ my @reserves_overlaps;
+ foreach my $reserve ( @reserves ) {
+
+ unless ($reserve->reservedate && $reserve->expirationdate) {
+ next;
+ }
+
+ if (date_ranges_overlap($resdate, $expdate,
+ $reserve->reservedate,
+ $reserve->expirationdate)) {
+
+ # If reserve is item level and the requested periods overlap.
+ if ($itemnumber && $reserve->itemnumber == $itemnumber ) {
+ return [$reserve->unblessed];
+ }
+ push @reserves_overlaps, $reserve->unblessed;
+ }
+ }
+
+ if ( @reserves_overlaps >= Koha::Items->search({ biblionumber => $biblionumber })->count() ) {
+ return \@reserves_overlaps;
+ }
+
+ return;
+}
+
=head1 AUTHOR
Koha Development Team
--- a/Koha/DateUtils.pm
+++ a/Koha/DateUtils.pm
@@ -24,7 +24,7 @@ use Koha::Exceptions;
use base 'Exporter';
our @EXPORT = (
- qw( dt_from_string output_pref format_sqldatetime )
+ qw( dt_from_string output_pref format_sqldatetime date_ranges_overlap )
);
=head1 DateUtils
@@ -322,4 +322,46 @@ sub format_sqldatetime {
return q{};
}
+=head2 date_ranges_overlap
+
+ $bool = date_ranges_overlap($start1, $end1, $start2, $end2);
+
+ Tells if first range ($start1 => $end1) overlaps
+ the second one ($start2 => $end2)
+
+=cut
+
+sub date_ranges_overlap {
+ my ($start1, $end1, $start2, $end2) = @_;
+
+ $start1 = dt_from_string( $start1, 'iso' );
+ $end1 = dt_from_string( $end1, 'iso' );
+ $start2 = dt_from_string( $start2, 'iso' );
+ $end2 = dt_from_string( $end2, 'iso' );
+
+ if (
+ # Start of range 2 is in the range 1.
+ (
+ DateTime->compare($start2, $start1) >= 0
+ && DateTime->compare($start2, $end1) <= 0
+ )
+ ||
+ # End of range 2 is in the range 1.
+ (
+ DateTime->compare($end2, $start1) >= 0
+ && DateTime->compare($end2, $end1) <= 0
+ )
+ ||
+ # Range 2 start before and end after range 1.
+ (
+ DateTime->compare($start2, $start1) < 0
+ && DateTime->compare($end2, $end1) > 0
+ )
+ ) {
+ return 1;
+ }
+
+ return;
+}
+
1;
--- a/circ/circulation.pl
+++ a/circ/circulation.pl
@@ -419,6 +419,10 @@ if (@$barcodes) {
}
unless($confirm_required) {
my $switch_onsite_checkout = exists $messages->{ONSITE_CHECKOUT_WILL_BE_SWITCHED};
+ if ( $cancelreserve eq 'cancel' ) {
+ CancelReserve({ reserve_id => $query->param('reserveid') });
+ }
+ $cancelreserve = $cancelreserve eq 'revert' ? 'revert' : undef;
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, } );
$template_params->{issue} = $issue;
$session->clear('auto_renew');
--- a/installer/data/mysql/atomicupdate/bug_15261-add_preventchechoutonsamereserveperiod_syspref.sql
+++ a/installer/data/mysql/atomicupdate/bug_15261-add_preventchechoutonsamereserveperiod_syspref.sql
@@ -0,0 +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
+++ a/installer/data/mysql/atomicupdate/bug_15261-add_preventreservesonsameperiod_syspref.sql
@@ -0,0 +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
+++ a/installer/data/mysql/sysprefs.sql
@@ -649,5 +649,7 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `
('XSLTListsDisplay','default','','Enable XSLT stylesheet control over lists pages display on intranet','Free'),
('XSLTResultsDisplay','default','','Enable XSL stylesheet control over results page display on intranet','Free'),
('z3950AuthorAuthFields','701,702,700',NULL,'Define the MARC biblio fields for Personal Name Authorities to fill biblio.author','free'),
-('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo')
+('z3950NormalizeAuthor','0','','If ON, Personal Name Authorities will replace authors in biblio.author','YesNo'),
+('PreventCheckoutOnSameReservePeriod','0','','Prevent to checkout a document if a reserve on same period exists','YesNo'),
+('PreventReservesOnSamePeriod','0','','Prevent to hold a document if a reserve on same period exists','YesNo')
;
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref
+++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref
@@ -499,6 +499,12 @@ Circulation:
- "
ccode: [NEWFIC,NULL,DVD]"
- "
itype: [NEWBK,\"\"]"
- "
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."
+ -
+ - pref: PreventCheckoutOnSameReservePeriod
+ choices:
+ yes: Do
+ no: "Don't"
+ - If yes, checkouts periods can't overlap with a reserve period.
Checkin Policy:
-
- pref: HoldsAutoFill
@@ -783,6 +789,12 @@ Circulation:
- 'Example: "itemlost: 1" to set items.itemlost to 1 when the item is marked as lost'
- pref: UpdateItemWhenLostFromHoldList
type: textarea
+ -
+ - pref: PreventReservesOnSamePeriod
+ choices:
+ yes: Do
+ no: "Don't"
+ - If yes, Reserves periods for the same document can't overlap.
Interlibrary Loans:
-
- pref: ILLModule
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt
+++ a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt
@@ -213,6 +213,7 @@
[% IF ( RESERVED ) %]
+
[% END %]
@@ -221,6 +222,7 @@
+
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/placerequest.tt
+++ a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/placerequest.tt
@@ -0,0 +1,66 @@
+[% INCLUDE 'doc-head-open.inc' %]
+ Koha › Circulation › Holds › Confirm holds
+[% INCLUDE 'doc-head-close.inc' %]
+
+
+[% INCLUDE 'header.inc' %]
+[% INCLUDE 'circ-search.inc' %]
+
+
+
+
+
+
+
+
+
+
Confirm holds
+
+
+
+ Some of the reserves you are trying to do overlaps with existing reserves.
+ Please confirm you want to proceed.
+
+
+
+
+
+
+
+
+
+[% INCLUDE 'intranet-bottom.inc' %]
--- a/opac/opac-reserve.pl
+++ a/opac/opac-reserve.pl
@@ -287,6 +287,15 @@ if ( $query->param('place_reserve') ) {
# Inserts a null into the 'itemnumber' field of 'reserves' table.
$itemNum = undef;
}
+
+ if ($canreserve) {
+ if (C4::Context->preference("PreventReservesOnSamePeriod") &&
+ ReservesOnSamePeriod($biblioNum, $itemNum, $startdate, $expiration_date)) {
+ $canreserve = 0;
+ $failed_holds++;
+ }
+ }
+
my $notes = $query->param('notes_'.$biblioNum)||'';
if ( $maxreserves
--- a/reserve/placerequest.pl
+++ a/reserve/placerequest.pl
@@ -30,89 +30,116 @@ use C4::Output;
use C4::Reserves;
use C4::Circulation;
use C4::Members;
-use C4::Auth qw/checkauth/;
+use C4::Auth;
use Koha::Items;
use Koha::Patrons;
my $input = CGI->new();
-checkauth($input, 0, { reserveforothers => 'place_holds' }, 'intranet');
-
-my @bibitems = $input->multi_param('biblioitem');
-my @reqbib = $input->multi_param('reqbib');
-my $biblionumber = $input->param('biblionumber');
-my $borrowernumber = $input->param('borrowernumber');
-my $notes = $input->param('notes');
-my $branch = $input->param('pickup');
-my $startdate = $input->param('reserve_date') || '';
-my @rank = $input->multi_param('rank-request');
-my $type = $input->param('type');
-my $title = $input->param('title');
-my $checkitem = $input->param('checkitem');
+my ( $template, $loggedinuser, $cookie, $flags ) = get_template_and_user(
+ {
+ template_name => "reserve/placerequest.tt",
+ query => $input,
+ type => "intranet",
+ authnotrequired => 0,
+ flagsrequired => { reserveforothers => 'place_holds' },
+ }
+);
+
+my $biblionumber=$input->param('biblionumber');
+my $borrowernumber=$input->param('borrowernumber');
+my $notes=$input->param('notes');
+my $branch=$input->param('pickup');
+my $startdate=$input->param('reserve_date') || '';
+my @rank=$input->param('rank-request');
+my $title=$input->param('title');
+my $checkitem=$input->param('checkitem');
my $expirationdate = $input->param('expiration_date');
my $itemtype = $input->param('itemtype') || undef;
-
-my $borrower = Koha::Patrons->find( $borrowernumber );
-$borrower = $borrower->unblessed if $borrower;
+my $confirm = $input->param('confirm');
+my @confirm_biblionumbers = $input->param('confirm_biblionumbers');
my $multi_hold = $input->param('multi_hold');
my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ($biblionumber . '/');
my $bad_bibs = $input->param('bad_bibs');
my $holds_to_place_count = $input->param('holds_to_place_count') || 1;
+my $borrower = Koha::Patrons->find( $borrowernumber );
+$borrower = $borrower->unblessed if $borrower;
+unless ($borrower) {
+ print $input->header();
+ print "Invalid borrower number please try again";
+ exit;
+}
+
my %bibinfos = ();
my @biblionumbers = split '/', $biblionumbers;
foreach my $bibnum (@biblionumbers) {
- my %bibinfo = ();
+ my %bibinfo;
$bibinfo{title} = $input->param("title_$bibnum");
+
$bibinfo{rank} = $input->param("rank_$bibnum");
$bibinfos{$bibnum} = \%bibinfo;
}
my $found;
-if ( $type eq 'str8' && $borrower ) {
-
- foreach my $biblionumber ( keys %bibinfos ) {
- my $count = @bibitems;
- @bibitems = sort @bibitems;
- my $i2 = 1;
- my @realbi;
- $realbi[0] = $bibitems[0];
- for ( my $i = 1 ; $i < $count ; $i++ ) {
- my $i3 = $i2 - 1;
- if ( $realbi[$i3] ne $bibitems[$i] ) {
- $realbi[$i2] = $bibitems[$i];
- $i2++;
- }
- }
+my $overlap_reserves = {};
+foreach my $biblionumber (keys %bibinfos) {
+ next if ($confirm && !grep { $_ eq $biblionumber } @confirm_biblionumbers);
- if ( defined $checkitem && $checkitem ne '' ) {
- my $item = Koha::Items->find($checkitem);
- if ( $item->biblionumber ne $biblionumber ) {
- $biblionumber = $item->biblionumber;
- }
- }
+ my ($reserve_title, $reserve_rank);
+ if ($multi_hold) {
+ my $bibinfo = $bibinfos{$biblionumber};
+ $reserve_rank = $bibinfo->{rank};
+ $reserve_title = $bibinfo->{title};
+ } else {
+ $reserve_rank = $rank[0];
+ $reserve_title = $title;
+ }
- if ($multi_hold) {
- my $bibinfo = $bibinfos{$biblionumber};
- if ( CanBookBeReserved($borrower->{'borrowernumber'}, $biblionumber)->{status} eq 'OK' ) {
- AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,[$biblionumber],
- $bibinfo->{rank},$startdate,$expirationdate,$notes,$bibinfo->{title},$checkitem,$found);
- }
- } else {
- # place a request on 1st available
- for ( my $i = 0 ; $i < $holds_to_place_count ; $i++ ) {
- if ( CanBookBeReserved($borrower->{'borrowernumber'}, $biblionumber)->{status} eq 'OK' ) {
- AddReserve( $branch, $borrower->{'borrowernumber'},
- $biblionumber, \@realbi, $rank[0], $startdate, $expirationdate, $notes, $title,
- $checkitem, $found, $itemtype );
- }
- }
+ if (defined $checkitem && $checkitem ne '') {
+ my $item = Koha::Items->find($checkitem);
+ if ($item->biblionumber ne $biblionumber) {
+ $biblionumber = $item->biblionumber;
}
}
+ if (!$confirm &&
+ ReservesOnSamePeriod($biblionumber, $checkitem, $startdate, $expirationdate) &&
+ C4::Context->preference("PreventReservesOnSamePeriod")) {
+ $overlap_reserves->{$biblionumber} = {
+ title => $reserve_title ,
+ checkitem => $checkitem,
+ rank => $reserve_rank
+ };
+ next;
+ }
+
+ if ( CanBookBeReserved($borrower->{borrowernumber}, $biblionumber)->{status} eq 'OK' ) {
+ AddReserve($branch, $borrower->{'borrowernumber'}, $biblionumber, undef,
+ $reserve_rank, $startdate, $expirationdate, $notes, $reserve_title,
+ $checkitem, $found);
+ }
+}
+
+if (scalar keys %$overlap_reserves) {
+ $template->param(
+ borrowernumber => $borrowernumber,
+ biblionumbers => $biblionumbers,
+ biblionumber => $biblionumber,
+ overlap_reserves => $overlap_reserves,
+ reserve_date => $startdate,
+ expiration_date => $expirationdate,
+ notes => $notes,
+ rank_request => \@rank,
+ pickup => $branch,
+ multi_hold => $multi_hold,
+ );
+
+ output_html_with_http_headers $input, $cookie, $template->output;
+} else {
if ($multi_hold) {
if ($bad_bibs) {
$biblionumbers .= $bad_bibs;
@@ -122,12 +149,5 @@ if ( $type eq 'str8' && $borrower ) {
else {
print $input->redirect("request.pl?biblionumber=$biblionumber");
}
-}
-elsif ( $borrowernumber eq '' ) {
- print $input->header();
- print "Invalid borrower number please try again";
-
- # Not sure that Dump() does HTML escaping. Use firebug or something to trace
- # instead.
- #print $input->Dump;
+ exit;
}
--- a/t/db_dependent/Circulation/CanBookBeIssued.t
+++ a/t/db_dependent/Circulation/CanBookBeIssued.t
@@ -0,0 +1,106 @@
+#!/usr/bin/env perl
+
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see
.
+
+use Modern::Perl;
+
+use Test::More tests => 1;
+use C4::Members;
+use C4::Reserves;
+use C4::Circulation;
+use Koha::DateUtils;
+
+use t::lib::TestBuilder;
+
+my $schema = Koha::Database->new->schema;
+$schema->storage->txn_begin;
+
+my $builder = t::lib::TestBuilder->new();
+
+subtest 'Tests for CanBookBeIssued with overlap reserves' => sub {
+ plan tests => 6;
+
+ my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
+ my $branch = $builder->build({ source => 'Branch' });
+ my $branchcode = $branch->{branchcode};
+
+ my $borrower = $builder->build_object({
+ class => 'Koha::Patrons',
+ value => {
+ branchcode => $branchcode,
+ categorycode => $categorycode,
+ }
+ });
+ my $borrowernumber = $borrower->borrowernumber;
+
+ my $biblio = $builder->build({source => 'Biblio'});
+ my $biblioitem = $builder->build({
+ source => 'Biblioitem',
+ value => {
+ biblionumber => $biblio->{biblionumber},
+ },
+ });
+ my $item = $builder->build({
+ source => 'Item',
+ value => {
+ biblionumber => $biblio->{biblionumber},
+ biblioitemnumber => $biblioitem->{biblioitemnumber},
+ withdrawn => 0,
+ itemlost => 0,
+ notforloan => 0,
+ },
+ });
+
+
+ my $startdate = dt_from_string();
+ $startdate->add_duration(DateTime::Duration->new(days => 4));
+ my $expdate = $startdate->clone();
+ $expdate->add_duration(DateTime::Duration->new(days => 10));
+
+ my $reserveid = AddReserve($branchcode, $borrowernumber,
+ $item->{biblionumber}, undef, 1, $startdate->ymd(), $expdate->ymd,
+ undef, undef, undef, undef);
+
+ my $non_overlap_duedate = dt_from_string();
+ $non_overlap_duedate->add_duration(DateTime::Duration->new(days => 2));
+ my ($error, $question, $alerts ) =
+ CanBookBeIssued($borrower, $item->{barcode}, $non_overlap_duedate, 1, 0);
+
+ is_deeply($error, {}, "");
+ is_deeply($question, {}, "");
+ is_deeply($alerts, {}, "");
+
+ my $overlap_duedate = dt_from_string();
+ $overlap_duedate->add_duration(DateTime::Duration->new(days => 5));
+ ($error, $question, $alerts ) =
+ CanBookBeIssued($borrower, $item->{barcode}, $overlap_duedate, 1, 0);
+
+ is_deeply($error, {}, "");
+ my $expected = {
+ RESERVED => 1,
+ resfirstname => $borrower->firstname,
+ ressurname => $borrower->surname,
+ rescardnumber => $borrower->cardnumber,
+ resborrowernumber => $borrower->borrowernumber,
+ resbranchname => $branch->{branchname},
+ resreservedate => $startdate->ymd,
+ resreserveid => $reserveid,
+ };
+ is_deeply($question, $expected, "");
+ is_deeply($alerts, {}, "");
+};
+
+$schema->storage->txn_rollback;
--- a/t/db_dependent/Reserves/ReserveDate.t
+++ a/t/db_dependent/Reserves/ReserveDate.t
@@ -0,0 +1,108 @@
+#!/usr/bin/perl
+
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see .
+
+use Modern::Perl;
+
+use Test::More tests => 6;
+use Test::Warn;
+
+use MARC::Record;
+use DateTime::Duration;
+
+use C4::Biblio;
+use C4::Items;
+use C4::Members;
+use C4::Circulation;
+use Koha::Holds;
+use t::lib::TestBuilder;
+
+use Koha::DateUtils;
+
+
+use_ok('C4::Reserves');
+
+my $dbh = C4::Context->dbh;
+
+# Start transaction
+$dbh->{AutoCommit} = 0;
+$dbh->{RaiseError} = 1;
+
+my $builder = t::lib::TestBuilder->new();
+my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
+my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode };
+
+my $borrower = $builder->build({
+ source => 'Borrower',
+ value => {
+ branchcode => $branchcode,
+ categorycode => $categorycode,
+ }
+});
+
+my $borrower2 = $builder->build({
+ source => 'Borrower',
+ value => {
+ branchcode => $branchcode,
+ categorycode => $categorycode,
+ }
+});
+
+my $borrowernumber = $borrower->{borrowernumber};
+my $borrowernumber2 = $borrower2->{borrowernumber};
+
+# Create a helper biblio
+my $biblio = MARC::Record->new();
+my $title = 'Alone in the Dark';
+my $author = 'Karen Rose';
+if( C4::Context->preference('marcflavour') eq 'UNIMARC' ) {
+ $biblio->append_fields(
+ MARC::Field->new('600', '', '1', a => $author),
+ MARC::Field->new('200', '', '', a => $title),
+ );
+}
+else {
+ $biblio->append_fields(
+ MARC::Field->new('100', '', '', a => $author),
+ MARC::Field->new('245', '', '', a => $title),
+ );
+}
+my ($bibnum, $bibitemnum);
+($bibnum, $title, $bibitemnum) = AddBiblio($biblio, '');
+
+my ($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem({ homebranch => $branchcode, holdingbranch => $branchcode, barcode => '333' } , $bibnum);
+
+C4::Context->set_preference('AllowHoldDateInFuture', 1);
+
+AddReserve($branchcode, $borrowernumber, $bibnum,
+ $bibitemnum, 1, '2015-11-01', '2015-11-20', undef,
+ undef, undef, undef);
+
+is(ReservesOnSamePeriod($bibnum, undef, '2015-11-25', '2015-11-30'), undef, "Period doesn't overlaps");
+
+ok(ReservesOnSamePeriod($bibnum, undef, '2015-11-02', '2015-11-10'), "Period overlaps");
+
+my ($item_bibnum2, $item_bibitemnum2, $itemnumber2) = AddItem({ homebranch => $branchcode, holdingbranch => $branchcode, barcode => '444' } , $bibnum);
+is(ReservesOnSamePeriod($bibnum, undef, '2015-11-02', '2015-11-10'), undef, "Period overlaps but there is 2 items");
+
+AddReserve($branchcode, $borrowernumber2, $bibnum,
+ $bibitemnum, 1, '2016-02-01', '2016-02-10', undef,
+ undef, $itemnumber, undef);
+is(ReservesOnSamePeriod($bibnum, $itemnumber, '02/12/2015', '10/12/2015'), undef, "Period on item does not overlap (with metric date format)");
+
+my $reserve = ReservesOnSamePeriod($bibnum, $itemnumber, '2016-01-31', '2016-02-05');
+is($reserve->[0]->{itemnumber}, $itemnumber, 'Period on item overlaps');
+$dbh->rollback;
--