Browse Source

Allow the reserve date to be set on holds

This adds to the interface and code the ability to set the reserve date when
requesting a hold.

Resubmit.  Sorry, I formatted it from the wrong branch.

Signed-off-by: Galen Charlton <gmcharlt@gmail.com>
3.2.x
Michael Hafen 14 years ago
committed by Galen Charlton
parent
commit
74a4f29236
  1. 86
      C4/Reserves.pm
  2. 4
      C4/SIP/ILS/Transaction/Hold.pm
  3. 31
      koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tmpl
  4. 46
      koha-tmpl/opac-tmpl/prog/en/includes/calendar.inc
  5. BIN
      koha-tmpl/opac-tmpl/prog/en/lib/calendar/cal.gif
  6. 127
      koha-tmpl/opac-tmpl/prog/en/lib/calendar/calendar-en.js
  7. 179
      koha-tmpl/opac-tmpl/prog/en/lib/calendar/calendar-setup.js
  8. 254
      koha-tmpl/opac-tmpl/prog/en/lib/calendar/calendar-system.css
  9. 1714
      koha-tmpl/opac-tmpl/prog/en/lib/calendar/calendar.js
  10. BIN
      koha-tmpl/opac-tmpl/prog/en/lib/calendar/menuarrow.gif
  11. 41
      koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tmpl
  12. 25
      opac/opac-reserve.pl
  13. 9
      reserve/placerequest.pl
  14. 11
      reserve/request.pl
  15. 2
      serials/routing-preview.pl

86
C4/Reserves.pm

@ -34,6 +34,7 @@ use C4::Members::Messaging;
use C4::Members qw( GetMember );
use C4::Letters;
use C4::Branch qw( GetBranchDetail );
use C4::Dates qw( format_date_in_iso );
use List::MoreUtils qw( firstidx );
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
@ -124,7 +125,7 @@ BEGIN {
sub AddReserve {
my (
$branch, $borrowernumber, $biblionumber,
$constraint, $bibitems, $priority, $notes,
$constraint, $bibitems, $priority, $resdate, $notes,
$title, $checkitem, $found
) = @_;
my $fee =
@ -132,9 +133,12 @@ sub AddReserve {
$bibitems );
my $dbh = C4::Context->dbh;
my $const = lc substr( $constraint, 0, 1 );
my @datearr = localtime(time);
my $resdate =
( 1900 + $datearr[5] ) . "-" . ( $datearr[4] + 1 ) . "-" . $datearr[3];
$resdate = format_date_in_iso( $resdate ) if ( $resdate );
$resdate = C4::Dates->today( 'iso' ) unless ( $resdate );
if ( C4::Context->preference( 'AllowHoldDateInFuture' ) ) {
# Make room in reserves for this before those of a later reserve date
$priority = _ShiftPriorityByDateAndPriority( $biblionumber, $resdate, $priority );
}
my $waitingdate;
# If the reserv had the waiting status, we had the value of the resdate
@ -199,6 +203,7 @@ of the reserves and an arrayref pointing to the reserves for C<$biblionumber>.
sub GetReservesFromBiblionumber {
my ($biblionumber) = shift or return (0, []);
my ($all_dates) = shift;
my $dbh = C4::Context->dbh;
# Find the desired items in the reserves
@ -214,8 +219,11 @@ sub GetReservesFromBiblionumber {
itemnumber,
reservenotes
FROM reserves
WHERE biblionumber = ?
ORDER BY priority";
WHERE biblionumber = ? ";
unless ( $all_dates ) {
$query .= "AND reservedate <= CURRENT_DATE()";
}
$query .= "ORDER BY priority";
my $sth = $dbh->prepare($query);
$sth->execute($biblionumber);
my @results;
@ -271,13 +279,16 @@ sub GetReservesFromBiblionumber {
=cut
sub GetReservesFromItemnumber {
my ( $itemnumber ) = @_;
my ( $itemnumber, $all_dates ) = @_;
my $dbh = C4::Context->dbh;
my $query = "
SELECT reservedate,borrowernumber,branchcode
FROM reserves
WHERE itemnumber=?
";
unless ( $all_dates ) {
$query .= " AND reservedate <= CURRENT_DATE()";
}
my $sth_res = $dbh->prepare($query);
$sth_res->execute($itemnumber);
my ( $reservedate, $borrowernumber,$branchcode ) = $sth_res->fetchrow_array;
@ -599,11 +610,11 @@ sub CheckReserves {
";
if ($item) {
$sth = $dbh->prepare("$select WHERE itemnumber = ?");
$sth = $dbh->prepare("$select WHERE reservedate <= CURRENT_DATE() AND itemnumber = ?");
$sth->execute($item);
}
else {
$sth = $dbh->prepare("$select WHERE barcode = ?");
$sth = $dbh->prepare("$select WHERE reservedate <= CURRENT_DATE() AND barcode = ?");
$sth->execute($barcode);
}
# note: we get the itemnumber because we might have started w/ just the barcode. Now we know for sure we have it.
@ -1280,6 +1291,7 @@ sub _Findgroupreserve {
AND priority > 0
AND item_level_request = 1
AND itemnumber = ?
AND reservedate <= CURRENT_DATE()
/;
my $sth = $dbh->prepare($item_level_target_query);
$sth->execute($itemnumber);
@ -1309,6 +1321,7 @@ sub _Findgroupreserve {
AND priority > 0
AND item_level_request = 0
AND hold_fill_targets.itemnumber = ?
AND reservedate <= CURRENT_DATE()
/;
$sth = $dbh->prepare($title_level_target_query);
$sth->execute($itemnumber);
@ -1338,6 +1351,7 @@ sub _Findgroupreserve {
AND reserves.reservedate = reserveconstraints.reservedate )
OR reserves.constrainttype='a' )
AND (reserves.itemnumber IS NULL OR reserves.itemnumber = ?)
AND reservedate <= CURRENT_DATE()
/;
$sth = $dbh->prepare($query);
$sth->execute( $biblio, $bibitem, $itemnumber );
@ -1414,6 +1428,60 @@ sub _koha_notify_reserve {
}
}
=item _ShiftPriorityByDateAndPriority
=over 4
$new_priority = _ShiftPriorityByDateAndPriority( $biblionumber, $reservedate, $priority );
=back
This increments the priority of all reserves after the one
with either the lowest date after C<$reservedate>
or the lowest priority after C<$priority>.
It effectively makes room for a new reserve to be inserted with a certain
priority, which is returned.
This is most useful when the reservedate can be set by the user. It allows
the new reserve to be placed before other reserves that have a later
reservedate. Since priority also is set by the form in reserves/request.pl
the sub accounts for that too.
=cut
sub _ShiftPriorityByDateAndPriority {
my ( $biblio, $resdate, $new_priority ) = @_;
my $dbh = C4::Context->dbh;
my $query = "SELECT priority FROM reserves WHERE biblionumber = ? AND ( reservedate > ? OR priority > ? ) ORDER BY priority ASC";
my $sth = $dbh->prepare( $query );
$sth->execute( $biblio, $resdate, $new_priority );
my ( $min_priority ) = $sth->fetchrow;
$sth->finish; # $sth might have more data.
$new_priority = $min_priority if ( $min_priority );
my $updated_priority = $new_priority + 1;
$query = "
UPDATE reserves
SET priority = ?
WHERE biblionumber = ?
AND borrowernumber = ?
AND reservedate = ?
AND found IS NULL";
my $sth_update = $dbh->prepare( $query );
$query = "SELECT * FROM reserves WHERE priority >= ?";
$sth = $dbh->prepare( $query );
$sth->execute( $new_priority );
while ( my $row = $sth->fetchrow_hashref ) {
$sth_update->execute( $updated_priority, $biblio, $row->{borrowernumber}, $row->{reservedate} );
$updated_priority++;
}
return $new_priority; # so the caller knows what priority they end up at
}
=back
=head1 AUTHOR

4
C4/SIP/ILS/Transaction/Hold.pm

@ -132,8 +132,8 @@ sub change_hold {
1;
__END__
# 10 friggin arguments
AddReserve($branch,$borrowernumber,$biblionumber,$constraint,$bibitems,$priority,$notes,$title,$checkitem,$found)
# 11 friggin arguments
AddReserve($branch,$borrowernumber,$biblionumber,$constraint,$bibitems,$priority,$startdate,$notes,$title,$checkitem,$found)
ModReserve($rank, $biblio, $borrower, $branch , $itemnumber)

31
koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tmpl

@ -5,6 +5,7 @@
<title>Koha &rsaquo; Circulation &rsaquo; Holds &rsaquo; Confirm Holds</title>
<!-- /TMPL_UNLESS -->
<!-- TMPL_INCLUDE NAME="doc-head-close.inc" -->
<!-- TMPL_INCLUDE NAME="calendar.inc" -->
<script type="text/javascript">
// <![CDATA[
var patron_homebranch = "<!-- TMPL_VAR NAME="borrower_branchname" ESCAPE="JS" -->";
@ -281,6 +282,36 @@ function checkMultiHold() {
<label for="pickup">Pickup at:</label>
<!-- TMPL_VAR NAME="CGIbranch" -->
</li>
<!-- TMPL_IF NAME="reserve_in_future" -->
<li>
<label for="reserve_date">Reserve starts on date:</label>
<input name="reserve_date" id="reserve_date" size="10" readonly="readonly">
<img src="<!-- TMPL_VAR NAME="themelang" -->/lib/calendar/cal.gif" alt="Show Calendar" border="0" id="CalendarReserveDate" style="cursor: pointer;" />
<script language="JavaScript" type="text/javascript">
//<![CDATA[
function validate1(date) {
var today = new Date();
if ( date < today ) {
return false;
} else if ( date.getDate() == today.getDate() && date.getMonth() == today.getMonth() && date.getFullYear() == today.getFullYear() ) {
return false;
} else {
return true;
}
};
Calendar.setup(
{
inputField : "reserve_date",
ifFormat : "<!-- TMPL_VAR NAME="DHTMLcalendar_dateformat" -->",
button : "CalendarReserveDate",
disableFunc : validate1,
dateStatusFunc : validate1,
}
);
//]]>
</script>
</li>
<!-- /TMPL_IF -->
<!-- TMPL_UNLESS NAME="multi_hold" -->
<li> <label for="requestany">Place a hold on the next available copy </label>

46
koha-tmpl/opac-tmpl/prog/en/includes/calendar.inc

@ -0,0 +1,46 @@
<link rel="stylesheet" type="text/css" href="<!-- TMPL_VAR name="themelang" -->/lib/calendar/calendar-system.css"/>
<script type="text/javascript" src="<!-- TMPL_VAR name="themelang" -->/lib/calendar/calendar.js"></script>
<script type="text/javascript" src="<!-- TMPL_VAR name="themelang" -->/lib/calendar/calendar-en.js"></script>
<script type="text/javascript" src="<!-- TMPL_VAR name="themelang" -->/lib/calendar/calendar-setup.js"></script>
<script type="text/javascript">
//<![CDATA[
var debug = "<!-- TMPL_VAR Name="debug" -->";
var dformat = "<!-- TMPL_VAR Name="dateformat" -->";
var sentmsg = 0;
if (debug > 1) {alert("dateformat: " + dformat + "\ndebug is on (level " + debug + ")");}
function Date_from_syspref(dstring) {
var dateX = dstring.split(/[-/]/);
if (debug > 1 && sentmsg < 1) {sentmsg++; alert("Date_from_syspref(" + dstring + ") splits to:\n" + dateX.join("\n"));}
if (dformat === "iso") {
return new Date(dateX[0], (dateX[1] - 1), dateX[2]); // YYYY-MM-DD to (YYYY,m(0-11),d)
} else if (dformat === "us") {
return new Date(dateX[2], (dateX[0] - 1), dateX[1]); // MM/DD/YYYY to (YYYY,m(0-11),d)
} else if (dformat === "metric") {
return new Date(dateX[2], (dateX[1] - 1), dateX[0]); // DD/MM/YYYY to (YYYY,m(0-11),d)
} else {
if (debug > 0) {alert("KOHA ERROR - Unrecognized date format: " +dformat);}
return 0;
}
}
function get_Calendar_limit (date,did) {
// this function could be moved to a static calendar-extras.js file
var dvalue = document.getElementById(did).value;
if (dvalue == "") { return false; }
var limitDate = Date_from_syspref(dvalue);
if (debug && debug > 5) {
var month = date.getMonth() + 1;
var dateString = date.getFullYear() + '-' + month + '-' + date.getDate();
alert("Comparing incoming (" + dateString + ") vs "
+ "'" + did + "' limit\n"
+ "\n thisdate: " + date
+ "\nlimitdate: " + limitDate
+ "\nlimit > thisdate : " + (limitDate > date)
+ "\nlimit < thisdate : " + (limitDate < date)
);
}
return limitDate;
}
//]]>
</script>

BIN
koha-tmpl/opac-tmpl/prog/en/lib/calendar/cal.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

127
koha-tmpl/opac-tmpl/prog/en/lib/calendar/calendar-en.js

@ -0,0 +1,127 @@
// ** I18N
// Calendar EN language
// Author: Mihai Bazon, <mishoo@infoiasi.ro>
// Encoding: any
// Distributed under the same terms as the calendar itself.
// For translators: please use UTF-8 if possible. We strongly believe that
// Unicode is the answer to a real internationalized world. Also please
// include your contact information in the header, as can be seen above.
// full day names
Calendar._DN = new Array
("Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday");
// Please note that the following array of short day names (and the same goes
// for short month names, _SMN) isn't absolutely necessary. We give it here
// for exemplification on how one can customize the short day names, but if
// they are simply the first N letters of the full name you can simply say:
//
// Calendar._SDN_len = N; // short day name length
// Calendar._SMN_len = N; // short month name length
//
// If N = 3 then this is not needed either since we assume a value of 3 if not
// present, to be compatible with translation files that were written before
// this feature.
// short day names
Calendar._SDN = new Array
("Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
"Sun");
// First day of the week. "0" means display Sunday first, "1" means display
// Monday first, etc.
Calendar._FD = 0;
// full month names
Calendar._MN = new Array
("January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December");
// short month names
Calendar._SMN = new Array
("Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec");
// tooltips
Calendar._TT = {};
Calendar._TT["INFO"] = "About the calendar";
Calendar._TT["ABOUT"] =
"DHTML Date/Time Selector\n" +
"(c) dynarch.com 2002-2003\n" + // don't translate this this ;-)
"For latest version visit: http://dynarch.com/mishoo/calendar.epl\n" +
"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." +
"\n\n" +
"Date selection:\n" +
"- Use the \xab, \xbb buttons to select year\n" +
"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" +
"- Hold mouse button on any of the above buttons for faster selection.";
Calendar._TT["ABOUT_TIME"] = "\n\n" +
"Time selection:\n" +
"- Click on any of the time parts to increase it\n" +
"- or Shift-click to decrease it\n" +
"- or click and drag for faster selection.";
Calendar._TT["PREV_YEAR"] = "Prev. year (hold for menu)";
Calendar._TT["PREV_MONTH"] = "Prev. month (hold for menu)";
Calendar._TT["GO_TODAY"] = "Go Today";
Calendar._TT["NEXT_MONTH"] = "Next month (hold for menu)";
Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)";
Calendar._TT["SEL_DATE"] = "Select date";
Calendar._TT["DRAG_TO_MOVE"] = "Drag to move";
Calendar._TT["PART_TODAY"] = " (today)";
// the following is to inform that "%s" is to be the first day of week
// %s will be replaced with the day name.
Calendar._TT["DAY_FIRST"] = "Display %s first";
// This may be locale-dependent. It specifies the week-end days, as an array
// of comma-separated numbers. The numbers are from 0 to 6: 0 means Sunday, 1
// means Monday, etc.
Calendar._TT["WEEKEND"] = "0,6";
Calendar._TT["CLOSE"] = "Close";
Calendar._TT["TODAY"] = "Today";
Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value";
// date formats
Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d";
Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e";
Calendar._TT["WK"] = "wk";
Calendar._TT["TIME"] = "Time:";

179
koha-tmpl/opac-tmpl/prog/en/lib/calendar/calendar-setup.js

@ -0,0 +1,179 @@
/* Copyright Mihai Bazon, 2002, 2003 | http://dynarch.com/mishoo/
* ---------------------------------------------------------------------------
*
* The DHTML Calendar
*
* Details and latest version at:
* http://dynarch.com/mishoo/calendar.epl
*
* This script is distributed under the GNU Lesser General Public License.
* Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
*
* This file defines helper functions for setting up the calendar. They are
* intended to help non-programmers get a working calendar on their site
* quickly. This script should not be seen as part of the calendar. It just
* shows you what one can do with the calendar, while in the same time
* providing a quick and simple method for setting it up. If you need
* exhaustive customization of the calendar creation process feel free to
* modify this code to suit your needs (this is recommended and much better
* than modifying calendar.js itself).
*/
/**
* This function "patches" an input field (or other element) to use a calendar
* widget for date selection.
*
* The "params" is a single object that can have the following properties:
*
* prop. name | description
* -------------------------------------------------------------------------------------------------
* inputField | the ID of an input field to store the date
* displayArea | the ID of a DIV or other element to show the date
* button | ID of a button or other element that will trigger the calendar
* eventName | event that will trigger the calendar, without the "on" prefix (default: "click")
* ifFormat | date format that will be stored in the input field
* daFormat | the date format that will be used to display the date in displayArea
* singleClick | (true/false) whether the calendar is in single click mode or not (default: true)
* firstDay | numeric: 0 to 6. "0" means display Sunday first, "1" means display Monday first, etc.
* align | alignment (default: "Br"); if you don't know what's this see the calendar documentation
* range | array with 2 elements. Default: [1900, 2999] -- the range of years available
* weekNumbers | (true/false) if it's true (default) the calendar will display week numbers
* flat | null or element ID; if not null the calendar will be a flat calendar having the parent with the given ID
* flatCallback | function that receives a JS Date object and returns an URL to point the browser to (for flat calendar)
* disableFunc | function that receives a JS Date object and should return true if that date has to be disabled in the calendar
* onSelect | function that gets called when a date is selected. You don't _have_ to supply this (the default is generally okay)
* onClose | function that gets called when the calendar is closed. [default]
* onUpdate | function that gets called after the date is updated in the input field. Receives a reference to the calendar.
* date | the date that the calendar will be initially displayed to
* showsTime | default: false; if true the calendar will include a time selector
* timeFormat | the time format; can be "12" or "24", default is "12"
* electric | if true (default) then given fields/date areas are updated for each move; otherwise they're updated only on close
* step | configures the step of the years in drop-down boxes; default: 2
* position | configures the calendar absolute position; default: null
* cache | if "true" (but default: "false") it will reuse the same calendar object, where possible
* showOthers | if "true" (but default: "false") it will show days from other months too
*
* None of them is required, they all have default values. However, if you
* pass none of "inputField", "displayArea" or "button" you'll get a warning
* saying "nothing to setup".
*/
Calendar.setup = function (params) {
function param_default(pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };
param_default("inputField", null);
param_default("displayArea", null);
param_default("button", null);
param_default("eventName", "click");
param_default("ifFormat", "%Y/%m/%d");
param_default("daFormat", "%Y/%m/%d");
param_default("singleClick", true);
param_default("disableFunc", 'dateStatusHandler');
param_default("dateStatusFunc", params["disableFunc"]); // takes precedence if both are defined
param_default("firstDay", 0); // defaults to "Sunday" first
param_default("align", "Br");
param_default("range", [1900, 2999]);
param_default("weekNumbers", true);
param_default("flat", null);
param_default("flatCallback", null);
param_default("onSelect", null);
param_default("onClose", null);
param_default("onUpdate", null);
param_default("date", null);
param_default("showsTime", false);
param_default("timeFormat", "24");
param_default("electric", true);
param_default("step", 2);
param_default("position", null);
param_default("cache", false);
param_default("showOthers", false);
var tmp = ["inputField", "displayArea", "button"];
for (var i in tmp) {
if (typeof params[tmp[i]] == "string") {
params[tmp[i]] = document.getElementById(params[tmp[i]]);
}
}
if (!(params.flat || params.inputField || params.displayArea || params.button)) {
alert("Calendar.setup:\n Nothing to setup (no fields found). Please check your code");
return false;
}
function onSelect(cal) {
var p = cal.params;
var update = (cal.dateClicked || p.electric);
if (update && p.flat) {
if (typeof p.flatCallback == "function")
p.flatCallback(cal);
else
alert("No flatCallback given -- doing nothing.");
return false;
}
if (update && p.inputField) {
p.inputField.value = cal.date.print(p.ifFormat);
if (typeof p.inputField.onchange == "function")
p.inputField.onchange();
}
if (update && p.displayArea)
p.displayArea.innerHTML = cal.date.print(p.daFormat);
if (update && p.singleClick && cal.dateClicked)
cal.callCloseHandler();
if (update && typeof p.onUpdate == "function")
p.onUpdate(cal);
};
if (params.flat != null) {
if (typeof params.flat == "string")
params.flat = document.getElementById(params.flat);
if (!params.flat) {
alert("Calendar.setup:\n Flat specified but can't find parent.");
return false;
}
var cal = new Calendar(params.firstDay, params.date, params.onSelect || onSelect);
cal.showsTime = params.showsTime;
cal.time24 = (params.timeFormat == "24");
cal.params = params;
cal.weekNumbers = params.weekNumbers;
cal.setRange(params.range[0], params.range[1]);
cal.setDateStatusHandler(params.dateStatusFunc);
cal.create(params.flat);
cal.show();
return false;
}
var triggerEl = params.button || params.displayArea || params.inputField;
triggerEl["on" + params.eventName] = function() {
var dateEl = params.inputField || params.displayArea;
var dateFmt = params.inputField ? params.ifFormat : params.daFormat;
var mustCreate = false;
var cal = window.calendar;
if (!(cal && params.cache)) {
window.calendar = cal = new Calendar(params.firstDay,
params.date,
params.onSelect || onSelect,
params.onClose || function(cal) { cal.hide(); });
cal.showsTime = params.showsTime;
cal.time24 = (params.timeFormat == "24");
cal.weekNumbers = params.weekNumbers;
mustCreate = true;
} else {
if (params.date)
cal.setDate(params.date);
cal.hide();
}
cal.showsOtherMonths = params.showOthers;
cal.yearStep = params.step;
cal.setRange(params.range[0], params.range[1]);
cal.params = params;
cal.setDateStatusHandler(params.dateStatusFunc);
cal.setDateFormat(dateFmt);
if (mustCreate)
cal.create();
cal.parseDate(dateEl.value || dateEl.innerHTML);
cal.refresh();
if (!params.position)
cal.showAtElement(params.button || params.displayArea || params.inputField, params.align);
else
cal.showAt(params.position[0], params.position[1]);
return false;
};
};

254
koha-tmpl/opac-tmpl/prog/en/lib/calendar/calendar-system.css

@ -0,0 +1,254 @@
/* The main calendar widget. DIV containing a table. */
.calendar {
position: relative;
display: none;
border: 1px solid;
border-color: #fff #000 #000 #fff;
font-size: 11px;
cursor: default;
background: Window;
color: WindowText;
font-family: tahoma,verdana,sans-serif;
width:250px;
}
.calendar table {
border: 1px solid;
border-color: #fff #000 #000 #fff;
font-size: 11px;
cursor: default;
background: Window;
color: WindowText;
font-family: tahoma,verdana,sans-serif;
border-collapse:separate;
}
/* Header part -- contains navigation buttons and day names. */
.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */
text-align: center;
padding: 1px;
border: 1px solid;
display: table-cell;
-moz-border-radius: 0px;
border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
background: ButtonFace;
}
.calendar .nav {
background: ButtonFace url('menuarrow.gif') no-repeat 100% 100%;
}
.calendar thead .title { /* This holds the current "month, year" */
font-weight: bold;
padding: 1px;
border: 1px solid #000;
background: ActiveCaption;
color: CaptionText;
text-align: center;
}
.calendar thead .headrow { /* Row <TR> containing navigation buttons */
}
.calendar thead .daynames { /* Row <TR> containing the day names */
}
.calendar thead .name { /* Cells <TD> containing the day names */
border-bottom: 1px solid ButtonShadow;
padding: 2px;
text-align: center;
background: ButtonFace;
color: ButtonText;
}
.calendar thead .weekend { /* How a weekend day name shows in header */
// color: #f00;
}
.calendar thead .hilite { /* How do the buttons in header appear when hover */
border: 2px solid;
padding: 0px;
border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
}
.calendar thead .active { /* Active (pressed) buttons in header */
border-width: 1px;
padding: 2px 0px 0px 2px;
border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
}
/* The body part -- contains all the days in month. */
.calendar tbody .day { /* Cells <TD> containing month days dates */
width: 2em;
text-align: right;
padding: 2px 4px 2px 2px;
}
.calendar tbody .day.othermonth {
font-size: 80%;
color: #aaa;
}
.calendar tbody .day.othermonth.oweekend {
color: #faa;
}
.calendar table .wn {
padding: 2px 3px 2px 2px;
border-right: 1px solid ButtonShadow;
background: ButtonFace;
color: ButtonText;
}
.calendar tbody .rowhilite td {
// background: Highlight;
// color: HighlightText;
}
.calendar tbody td.hilite { /* Hovered cells <TD> */
padding: 1px 3px 1px 1px;
border-top: 1px solid #fff;
border-right: 1px solid #000;
border-bottom: 1px solid #000;
border-left: 1px solid #fff;
}
.calendar tbody td.active { /* Active (pressed) cells <TD> */
// padding: 2px 2px 0px 2px;
// border: 1px solid;
// border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
}
.calendar tbody td.selected { /* Cell showing selected date */
font-weight: bold;
border: 1px solid;
border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
padding: 2px 2px 0px 2px;
// background: ButtonFace;
color: ButtonText;
}
.calendar tbody td.weekend { /* Cells showing weekend days */
// color: #f00;
}
.calendar tbody td.today { /* Cell showing today date */
font-weight: bold;
color: #00f;
}
.calendar tbody td.disabled { color: GrayText; }
.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */
visibility: hidden;
}
.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */
display: none;
}
/* The footer part -- status bar and "Close" button */
.calendar tfoot .footrow { /* The <TR> in footer (only one right now) */
}
.calendar tfoot .ttip { /* Tooltip (status bar) cell <TD> */
background: ButtonFace;
padding: 1px;
border: 1px solid;
border-color: ButtonShadow ButtonHighlight ButtonHighlight ButtonShadow;
color: ButtonText;
text-align: center;
}
.calendar tfoot .hilite { /* Hover style for buttons in footer */
border-top: 1px solid #fff;
border-right: 1px solid #000;
border-bottom: 1px solid #000;
border-left: 1px solid #fff;
padding: 1px;
background: #e4e0d8;
}
.calendar tfoot .active { /* Active (pressed) style for buttons in footer */
padding: 2px 0px 0px 2px;
border-top: 1px solid #000;
border-right: 1px solid #fff;
border-bottom: 1px solid #fff;
border-left: 1px solid #000;
}
/* Combo boxes (menus that display months/years for direct selection) */
.calendar .combo {
position: absolute;
display: none;
width: 4em;
top: 0px;
left: 0px;
cursor: default;
border: 1px solid;
border-color: ButtonHighlight ButtonShadow ButtonShadow ButtonHighlight;
background: Menu;
color: MenuText;
font-size: 90%;
padding: 1px;
}
.calendar .combo .label,
.calendar .combo .label-IEfix {
text-align: center;
padding: 1px;
}
.calendar .combo .label-IEfix {
width: 4em;
}
.calendar .combo .active {
padding: 0px;
border: 1px solid #000;
}
.calendar .combo .hilite {
background: Highlight;
color: HighlightText;
}
.calendar td.time {
border-top: 1px solid ButtonShadow;
padding: 1px 0px;
text-align: center;
background-color: ButtonFace;
}
.calendar td.time .hour,
.calendar td.time .minute,
.calendar td.time .ampm {
padding: 0px 3px 0px 4px;
border: 1px solid #889;
font-weight: bold;
background-color: Menu;
}
.calendar td.time .ampm {
text-align: center;
}
.calendar td.time .colon {
padding: 0px 2px 0px 3px;
font-weight: bold;
}
.calendar td.time span.hilite {
border-color: #000;
background-color: Highlight;
color: HighlightText;
}
.calendar td.time span.active {
border-color: #f00;
background-color: #000;
color: #0f0;
}

1714
koha-tmpl/opac-tmpl/prog/en/lib/calendar/calendar.js

File diff suppressed because it is too large

BIN
koha-tmpl/opac-tmpl/prog/en/lib/calendar/menuarrow.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

41
koha-tmpl/opac-tmpl/prog/en/modules/opac-reserve.tmpl

@ -1,5 +1,6 @@
<!-- TMPL_INCLUDE NAME="doc-head-open.inc" --><!-- TMPL_VAR NAME="LibraryNameTitle" DEFAULT="Koha Online" --> Catalog &rsaquo; Reserving <!-- TMPL_VAR NAME="title" escape="html" --> for <!-- TMPL_LOOP NAME="USER_INFO" --><!-- TMPL_VAR NAME="firstname" --> <!-- TMPL_VAR NAME="surname" --><!-- /TMPL_LOOP -->
<!-- TMPL_INCLUDE NAME="doc-head-close.inc" -->
<!-- TMPL_INCLUDE NAME="calendar.inc" -->
<script type="text/javascript">
// <![CDATA[
var MSG_NO_COPY_SELECTED = _("Expecting a specific copy selection.");
@ -238,6 +239,9 @@
<th>Item Type</th>
<!-- /TMPL_UNLESS -->
<th>Priority</th>
<!-- TMPL_IF NAME="reserve_in_future" -->
<th>Reserve Starts on Date</th>
<!-- /TMPL_IF -->
<!-- TMPL_IF NAME="OPACItemHolds" -->
<th id="place_on_hdr" style="display:none">Place On:</th>
<!-- /TMPL_IF -->
@ -295,6 +299,43 @@
</td>
<!-- /TMPL_UNLESS -->
<td><!-- TMPL_VAR name="rank" --> out of <!-- TMPL_VAR NAME="reservecount" --></td>
<!-- TMPL_IF NAME="reserve_in_future" -->
<td>
<input name="reserve_date_<!-- TMPL_VAR NAME="biblionumber" -->" id="reserve_date_<!-- TMPL_VAR NAME="biblionumber" -->" size="10">
<!-- <img src="<!-- TMPL_VAR NAME="themelang" -->/lib/calendar/cal.gif" alt="Show Calendar" border="0" id="CalendarReserveDate<!-- TMPL_VAR NAME="biblionumber" -->" style="cursor: pointer;" /> -->
<script language="JavaScript" type="text/javascript">
//<![CDATA[
$("#reserve_date_<!-- TMPL_VAR NAME="biblionumber" -->").attr( 'readonly', 'readonly' );
var cal_img = document.createElement('img');
cal_img.src = "<!-- TMPL_VAR NAME="themelang" -->/lib/calendar/cal.gif";
cal_img.alt = "Show Calendar";
cal_img.border = "0";
cal_img.id = "CalendarReserveDate<!-- TMPL_VAR NAME="biblionumber" -->";
cal_img.style.cursor = "pointer";
document.getElementById("reserve_date_<!-- TMPL_VAR NAME="biblionumber" -->").parentNode.appendChild( cal_img );
function validate<!-- TMPL_VAR NAME="biblionumber" -->(date) {
var today = new Date();
if ( date < today ) {
return true;
} else {
return false;
}
};
Calendar.setup(
{
inputField : "reserve_date_<!-- TMPL_VAR NAME="biblionumber" -->",
ifFormat : "<!-- TMPL_VAR NAME="DHTMLcalendar_dateformat" -->",
button : "CalendarReserveDate<!-- TMPL_VAR NAME="biblionumber" -->",
disableFunc : validate<!-- TMPL_VAR NAME="biblionumber" -->,
dateStatusFunc : validate<!-- TMPL_VAR NAME="biblionumber" -->,
}
);
//]]>
</script>
</td>
<!-- /TMPL_IF -->
<!-- TMPL_IF NAME="OPACItemHolds" -->
<td class="place_on_type" style="display:none">
<table>

25
opac/opac-reserve.pl

@ -130,7 +130,7 @@ foreach my $biblioNumber (@biblionumbers) {
}
# Compute the priority rank.
my ( $rank, $reserves ) = GetReservesFromBiblionumber($biblioNumber);
my ( $rank, $reserves ) = GetReservesFromBiblionumber($biblioNumber,1);
$biblioData->{reservecount} = $rank;
foreach my $res (@$reserves) {
my $found = $res->{'found'};
@ -191,6 +191,15 @@ if ( $query->param('place_reserve') ) {
my $biblioData = $biblioDataHash{$biblioNum};
my $found;
# Check for user supplied reserve date
my $startdate;
if (
C4::Context->preference( 'AllowHoldDateInFuture' ) &&
C4::Context->preference( 'OPACAllowHoldDateInFuture' )
) {
$startdate = $query->param("reserve_date_$biblioNum");
}
# If a specific item was selected and the pickup branch is the same as the
# holdingbranch, force the value $rank and $found.
my $rank = $biblioData->{rank};
@ -207,7 +216,7 @@ if ( $query->param('place_reserve') ) {
}
# Here we actually do the reserveration. Stage 3.
AddReserve($branch, $borrowernumber, $biblioNum, 'a', [$biblioNum], $rank, $notes,
AddReserve($branch, $borrowernumber, $biblioNum, 'a', [$biblioNum], $rank, $startdate, $notes,
$biblioData->{'title'}, $itemNum, $found);
}
@ -465,5 +474,17 @@ $template->param(itemtable_colspan => $itemTableColspan);
# display infos
$template->param(bibitemloop => $biblioLoop);
# can set reserve date in future
if (
C4::Context->preference( 'AllowHoldDateInFuture' ) &&
C4::Context->preference( 'OPACAllowHoldDateInFuture' )
) {
$template->param(
reserve_in_future => 1,
DHTMLcalendar_dateformat => C4::Dates->DHTMLcalendar(),
);
}
output_html_with_http_headers $query, $cookie, $template->output;

9
reserve/placerequest.pl

@ -45,6 +45,7 @@ my $biblionumber=$input->param('biblionumber');
my $borrower=$input->param('member');
my $notes=$input->param('notes');
my $branch=$input->param('pickup');
my $startdate=$input->param('reserve_date') || '';
my @rank=$input->param('rank-request');
my $type=$input->param('type');
my $title=$input->param('title');
@ -97,17 +98,17 @@ if ($type eq 'str8' && $borrowernumber ne ''){
if ($multi_hold) {
my $bibinfo = $bibinfos{$biblionumber};
AddReserve($branch,$borrowernumber->{'borrowernumber'},$biblionumber,'a',[$biblionumber],
$bibinfo->{rank},$notes,$bibinfo->{title},$checkitem,$found);
$bibinfo->{rank},$startdate,$notes,$bibinfo->{title},$checkitem,$found);
} else {
if ($input->param('request') eq 'any'){
# place a request on 1st available
AddReserve($branch,$borrowernumber->{'borrowernumber'},$biblionumber,'a',\@realbi,$rank[0],$notes,$title,$checkitem,$found);
AddReserve($branch,$borrowernumber->{'borrowernumber'},$biblionumber,'a',\@realbi,$rank[0],$startdate,$notes,$title,$checkitem,$found);
} elsif ($reqbib[0] ne ''){
# FIXME : elsif probably never reached, (see top of the script)
# place a request on a given item
AddReserve($branch,$borrowernumber->{'borrowernumber'},$biblionumber,'o',\@reqbib,$rank[0],$notes,$title,$checkitem, $found);
AddReserve($branch,$borrowernumber->{'borrowernumber'},$biblionumber,'o',\@reqbib,$rank[0],$startdate,$notes,$title,$checkitem, $found);
} else {
AddReserve($branch,$borrowernumber->{'borrowernumber'},$biblionumber,'a',\@realbi,$rank[0],$notes,$title,$checkitem, $found);
AddReserve($branch,$borrowernumber->{'borrowernumber'},$biblionumber,'a',\@realbi,$rank[0],$startdate,$notes,$title,$checkitem, $found);
}
}
}

11
reserve/request.pl

@ -219,7 +219,7 @@ foreach my $biblionumber (@biblionumbers) {
my $dat = GetBiblioData($biblionumber);
# get existing reserves .....
my ( $count, $reserves ) = GetReservesFromBiblionumber($biblionumber);
my ( $count, $reserves ) = GetReservesFromBiblionumber($biblionumber,1);
my $totalcount = $count;
my $alreadyreserved;
@ -434,7 +434,7 @@ foreach my $biblionumber (@biblionumbers) {
# existingreserves building
my @reserveloop;
( $count, $reserves ) = GetReservesFromBiblionumber($biblionumber);
( $count, $reserves ) = GetReservesFromBiblionumber($biblionumber,1);
foreach my $res ( sort {
my $a_found = $a->{found} || '';
my $b_found = $a->{found} || '';
@ -550,6 +550,13 @@ $template->param( biblionumbers => $biblionumbers );
if ($multihold) {
$template->param( multi_hold => 1 );
}
if ( C4::Context->preference( 'AllowHoldDateInFuture' ) ) {
$template->param(
reserve_in_future => 1,
DHTMLcalendar_dateformat => C4::Dates->DHTMLcalendar(),
);
}
# printout the page
output_html_with_http_headers $input, $cookie, $template->output;

2
serials/routing-preview.pl

@ -71,7 +71,7 @@ if($ok){
if($routinglist[$i]->{'borrowernumber'} == $data->{'borrowernumber'}){
ModReserve($routinglist[$i]->{'ranking'},$biblio,$routinglist[$i]->{'borrowernumber'},$branch);
} else {
AddReserve($branch,$routinglist[$i]->{'borrowernumber'},$biblio,$const,\@bibitems,$routinglist[$i]->{'ranking'},$notes,$title);
AddReserve($branch,$routinglist[$i]->{'borrowernumber'},$biblio,$const,\@bibitems,$routinglist[$i]->{'ranking'},'',$notes,$title);
}
}

Loading…
Cancel
Save