Bug 7178: Acquisition item creation improvement

- Display a unique item block at once

On orderreceive.pl when AcqCreateItem is 'receiving', and on
neworderempty.pl when AcqCreateItem is 'ordering' it displays an
item block with item infos to fill, and a '+' button.
When user clicks on '+', the block is hidden and a list shows up with
the items that will be received. User can then edit or delete items in
the list and click 'Save' to receive items.

- PrepareItemrecordDisplay is now used for cloning block

Previous cloning function was duplicating ids, the side effect is that
plugins didn't work when several items were displayed.
PrepareItemrecordDisplay regenerate the form with new ids

- New system preference UniqueItemFields

Contains a space-separated list of sql column names (of items table).
This syspref is used in two ways:
 - Values corresponding to fields in syspref are not duplicated when
   adding a new item (button 'Add')
 - When saving the form, a check is made on fields in syspref for
   detecting duplicate (in DB and in the form)

Signed-off-by: Katrin Fischer <Katrin.Fischer.83@web.de>
All tests done are noted on the bug report.

2012-03-23: Fixed conflict in updatedatabase.
Signed-off-by: Paul Poulain <paul.poulain@biblibre.com>
This commit is contained in:
Julian Maurice 2012-01-19 16:59:58 +01:00 committed by Paul Poulain
parent d029ec835d
commit eefc774e27
11 changed files with 589 additions and 246 deletions

68
acqui/check_uniqueness.pl Executable file
View file

@ -0,0 +1,68 @@
#!/usr/bin/perl
# Copyright 2011 BibLibre SARL
#
# 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 2 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, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# This script search in items table if a value for a given field exists.
# It is used in check_additem (additem.js)
# Parameters are a list of 'field', which must be field names in items table
# and a list of 'value', which are the corresponding value to check
# Eg. @field = ('barcode', 'barcode', 'stocknumber')
# @value = ('1234', '1235', 'ABC')
# The script will check if there is already an item with barcode '1234',
# then an item with barcode '1235', and finally check if there is an item
# with stocknumber 'ABC'
# It returns a JSON string which contains what have been found
# Eg. { barcode: ['1234', '1235'], stocknumber: ['ABC'] }
use Modern::Perl;
use CGI;
use JSON;
use C4::Context;
use C4::Output;
use C4::Auth;
my $input = new CGI;
my @field = $input->param('field');
my @value = $input->param('value');
my $dbh = C4::Context->dbh;
my $query = "SHOW COLUMNS FROM items";
my $sth = $dbh->prepare($query);
$sth->execute;
my $results = $sth->fetchall_hashref('Field');
my @columns = keys %$results;
my $r = {};
my $index = 0;
for my $f ( @field ) {
if(0 < grep /^$f$/, @columns) {
$query = "SELECT $f FROM items WHERE $f = ?";
$sth = $dbh->prepare( $query );
$sth->execute( $value[$index] );
my @values = $sth->fetchrow_array;
if ( @values ) {
push @{ $r->{$f} }, $values[0];
}
}
$index++;
}
output_with_http_headers $input, undef, to_json($r), 'json';

View file

@ -308,17 +308,15 @@ if ($CGIsort2) {
}
if (C4::Context->preference('AcqCreateItem') eq 'ordering' && !$ordernumber) {
# prepare empty item form
my $cell = PrepareItemrecordDisplay('','','','ACQ');
# warn "==> ".Data::Dumper::Dumper($cell);
unless ($cell) {
$cell = PrepareItemrecordDisplay('','','','');
# Check if ACQ framework exists
my $marc = GetMarcStructure(1, 'ACQ');
unless($marc) {
$template->param('NoACQframework' => 1);
}
my @itemloop;
push @itemloop,$cell;
$template->param(items => \@itemloop);
$template->param(
AcqCreateItemOrdering => 1,
UniqueItemFields => C4::Context->preference('UniqueItemFields'),
);
}
# Get the item types list, but only if item_level_itype is YES. Otherwise, it will be in the item, no need to display it in the biblio
my @itemtypes;

View file

@ -117,16 +117,15 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
# prepare the form for receiving
if ( $count == 1 ) {
if (C4::Context->preference('AcqCreateItem') eq 'receiving') {
# prepare empty item form
my $cell = PrepareItemrecordDisplay('','','','ACQ');
unless ($cell) {
$cell = PrepareItemrecordDisplay('','','','');
# Check if ACQ framework exists
my $marc = GetMarcStructure(1, 'ACQ');
unless($marc) {
$template->param('NoACQframework' => 1);
}
my @itemloop;
push @itemloop,$cell;
$template->param(items => \@itemloop);
$template->param(
AcqCreateItemReceiving => 1,
UniqueItemFields => C4::Context->preference('UniqueItemFields'),
);
}
if ( @$results[0]->{'quantityreceived'} == 0 ) {

View file

@ -5033,6 +5033,15 @@ if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) {
ALTER TABLE z3950servers ADD timeout INT( 11 ) NOT NULL DEFAULT '0' AFTER syntax;
});
print "Upgrade to $DBversion done (New timeout field in z3950servers)\n";
}
$DBversion = "XXX";
if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) {
$dbh->do(qq{
INSERT INTO systempreferences(variable,value,explanation,options,type)
VALUES('UniqueItemFields', 'barcode', 'Space-separated list of fields that should be unique (used in acquisition module for item creation). Fields must be valid SQL column names of items table', '', 'Free')
});
print "Upgrade to $DBversion done (Added system preference 'UniqueItemFields')\n";
SetVersion($DBversion);
}

View file

@ -0,0 +1,10 @@
<script type="text/javascript">
//<![CDATA[
var MSG_ADDITEM_JS_EDIT = _("Edit");
var MSG_ADDITEM_JS_DELETE = _("Delete");
var MSG_ADDITEM_JS_CLEAR = _("Clear");
var MSG_ADDITEM_JS_CANT_RECEIVE_MORE_ITEMS = _("You can't receive any more items");
var MSG_ADDITEM_JS_IS_DUPLICATE = _("is duplicated");
var MSG_ADDITEM_JS_ALREADY_EXISTS_IN_DB = _("already exists in database");
//]]>
</script>

View file

@ -1,110 +1,239 @@
function deleteItemBlock(index) {
var aDiv = document.getElementById(index);
aDiv.parentNode.removeChild(aDiv);
var quantity = document.getElementById('quantity');
quantity.setAttribute('value',parseFloat(quantity.getAttribute('value'))-1);
}
function cloneItemBlock(index) {
var original = document.getElementById(index); //original <div>
var clone = clone_with_selected(original)
var random = Math.floor(Math.random()*100000); // get a random itemid.
// set the attribute for the new 'div' subfields
clone.setAttribute('id',index + random);//set another id.
var NumTabIndex;
NumTabIndex = parseInt(original.getAttribute('tabindex'));
if(isNaN(NumTabIndex)) NumTabIndex = 0;
clone.setAttribute('tabindex',NumTabIndex+1);
var CloneButtonPlus;
var CloneButtonMinus;
// try{
var jclone = $(clone);
CloneButtonPlus = $("a.addItem", jclone).get(0);
CloneButtonPlus.setAttribute('onclick',"cloneItemBlock('" + index + random + "')");
CloneButtonMinus = $("a.delItem", jclone).get(0);
CloneButtonMinus.setAttribute('onclick',"deleteItemBlock('" + index + random + "')");
CloneButtonMinus.setAttribute('style',"display:inline");
// change itemids of the clone
var elems = clone.getElementsByTagName('input');
for( i = 0 ; elems[i] ; i++ )
{
if(elems[i].name.match(/^itemid/)) {
elems[i].value = random;
function addItem( node, unique_item_fields ) {
var index = $(node).parent().attr('id');
var current_qty = parseInt($("#quantity").val());
var max_qty;
if($("#quantity_to_receive").length != 0){
max_qty = parseInt($("#quantity_to_receive").val());
} else {
max_qty = 99999;
}
if ( $("#items_list table").find('tr[idblock="' + index + '"]').length == 0 ) {
if ( current_qty < max_qty ) {
if ( current_qty < max_qty - 1 )
cloneItemBlock(index, unique_item_fields);
addItemInList(index, unique_item_fields);
$("#" + index).find("a[name='buttonPlus']").text("Update");
$("#quantity").val(current_qty + 1);
} else if ( current_qty >= max_qty ) {
alert(window.MSG_ADDITEM_JS_CANT_RECEIVE_MORE_ITEMS
|| "You can't receive any more items.");
}
}
// }
//catch(e){ // do nothig if ButtonPlus & CloneButtonPlus don't exist.
//}
// insert this line on the page
original.parentNode.insertBefore(clone,original.nextSibling);
var quantity = document.getElementById('quantity');
quantity.setAttribute('value',parseFloat(quantity.getAttribute('value'))+1);
}
function check_additem() {
var barcodes = document.getElementsByName('barcode');
var success = true;
for(i=0;i<barcodes.length;i++){
for(j=0;j<barcodes.length;j++){
if( (i > j) && (barcodes[i].value == barcodes[j].value) && barcodes[i].value !='') {
barcodes[i].className='error';
barcodes[j].className='error';
success = false;
}
}
}
// TODO : Add AJAX function to test against barcodes already in the database, not just
// duplicates within the form.
return success;
} else {
if ( current_qty < max_qty )
cloneItemBlock(index, unique_item_fields);
var tr = constructTrNode(index);
$("#items_list table").find('tr[idblock="' + index + '"]:first').replaceWith(tr);
}
$("#" + index).hide();
}
function clone_with_selected (node) {
var origin = node.getElementsByTagName("select");
var tmp = node.cloneNode(true)
var selectelem = tmp.getElementsByTagName("select");
for (var i=0; i<origin.length; i++) {
selectelem[i].selectedIndex = origin[i].selectedIndex;
}
origin = null;
selectelem = null;
return tmp;
}
function showItem(index) {
$("#outeritemblock").children("div").each(function(){
if ( $(this).attr('id') == index ) {
$(this).show();
} else {
if ( $("#items_list table").find('tr[idblock="' + $(this).attr('id') + '"]').length == 0 ) {
$(this).remove();
} else {
$(this).hide();
}
}
});
}
function constructTrNode(index, unique_item_fields) {
var fields = ['barcode', 'homebranch', 'holdingbranch', 'notforloan',
'restricted', 'location', 'itemcallnumber', 'copynumber',
'stocknumber', 'ccode', 'itype', 'materials', 'itemnotes'];
var result = "<tr idblock='" + index + "'>";
var edit_link = "<a href='#itemfieldset' style='text-decoration:none' onclick='showItem(\"" + index + "\");'>"
+ (window.MSG_ADDITEM_JS_EDIT || "Edit") + "</a>";
var del_link = "<a style='cursor:pointer' "
+ "onclick='deleteItemBlock(this, \"" + index + "\", \"" + unique_item_fields + "\");'>"
+ (window.MSG_ADDITEM_JS_DELETE || "Delete") + "</a>";
result += "<td>" + edit_link + "</td>";
result += "<td>" + del_link + "</td>";
for(i in fields) {
var field = fields[i];
var field_elt = $("#" + index)
.find("[name='kohafield'][value='items."+field+"']")
.prevAll("[name='field_value']")[0];
var field_value;
if($(field_elt).is('select')) {
field_value = $(field_elt).find("option:selected").text();
} else {
field_value = $(field_elt).val();
}
result += "<td>" + field_value + "</td>";
}
result += "</tr>";
return result;
}
function addItemInList(index, unique_item_fields) {
$("#items_list").show();
var tr = constructTrNode(index, unique_item_fields);
$("#items_list table tbody").append(tr);
}
function deleteItemBlock(node_a, index, unique_item_fields) {
$("#" + index).remove();
var current_qty = parseInt($("#quantity").val());
var max_qty;
if($("#quantity_to_receive").length != 0) {
max_qty = parseInt($("#quantity_to_receive").val());
} else {
max_qty = 99999;
}
$("#quantity").val(current_qty - 1);
$(node_a).parents('tr').remove();
if(current_qty - 1 == 0)
$("#items_list").hide();
if ( $("#quantity").val() <= max_qty - 1) {
if ( $("#outeritemblock").children("div :visible").length == 0 ) {
$("#outeritemblock").children("div:last").show();
}
}
if ( $("#quantity").val() == 0 && $("#outeritemblock > div").length == 0) {
cloneItemBlock(0, unique_item_fields);
}
}
function cloneItemBlock(index, unique_item_fields) {
var original;
if(index) {
original = $("#" + index); //original <div>
}
var dont_copy_fields = new Array();
if(unique_item_fields) {
var dont_copy_fields = unique_item_fields.split(' ');
for(i in dont_copy_fields) {
dont_copy_fields[i] = "items." + dont_copy_fields[i];
}
}
var random = Math.floor(Math.random()*100000); // get a random itemid.
var clone = $("<div id='itemblock"+random+"'></div>")
$.ajax({
url: "/cgi-bin/koha/services/itemrecorddisplay.pl",
dataType: 'html',
data: {
frameworkcode: 'ACQ'
},
success: function(data, textStatus, jqXHR) {
/* Create the item block */
$(clone).append(data);
/* Change all itemid fields value */
$(clone).find("input[name='itemid']").each(function(){
$(this).val(random);
});
/* Add buttons + and Clear */
var buttonPlus = '<a name="buttonPlus" style="cursor:pointer; margin:0 1em;" onclick="addItem(this,\'' + unique_item_fields + '\')">Add</a>';
var buttonClear = '<a name="buttonClear" style="cursor:pointer;" onclick="clearItemBlock(this)">' + (window.MSG_ADDITEM_JS_CLEAR || 'Clear') + '</a>';
$(clone).append(buttonPlus).append(buttonClear);
/* Copy values from the original block (input) */
$(original).find("input[name='field_value']").each(function(){
var kohafield = $(this).siblings("input[name='kohafield']").val();
if($(this).val() && dont_copy_fields.indexOf(kohafield) == -1) {
$(this).parent("div").attr("id").match(/^(subfield.)/);
var id = RegExp.$1;
var value = $(this).val();
$(clone).find("div[id^='"+id+"'] input[name='field_value']").val(value);
}
});
/* Copy values from the original block (select) */
$(original).find("select[name='field_value']").each(function(){
var kohafield = $(this).siblings("input[name='kohafield']").val();
if($(this).val() && dont_copy_fields.indexOf(kohafield) == -1) {
$(this).parent("div").attr("id").match(/^(subfield.)/);
var id = RegExp.$1;
var value = $(this).val();
$(clone).find("div[id^='"+id+"'] select[name='field_value']").val(value);
}
});
$("#outeritemblock").append(clone);
}
});
}
function clearItemBlock(node) {
var index = $(node).parent().attr('id');
var block = $("#"+index);
$(block).find("input[type='text']").each(function(){
$(this).val("");
});
$(block).find("select").each(function(){
$(this).find("option:first").attr("selected", true);
});
}
function check_additem(unique_item_fields) {
var success = true;
var data = new Object();
data['field'] = new Array();
data['value'] = new Array();
var array_fields = unique_item_fields.split(' ');
$(".error").empty(); // Clear error div
// Check if a value is duplicated in form
for ( field in array_fields ) {
var fieldname = array_fields[field];
var values = new Array();
$("[name='kohafield'][value=items."+array_fields[field]+"]").each(function(){
var input = $(this).prevAll("input[name='field_value']")[0];
if($(input).val()) {
values.push($(input).val());
data['field'].push(fieldname);
data['value'].push($(input).val());
}
});
var sorted_arr = values.sort();
for (var i = 0; i < sorted_arr.length - 1; i += 1) {
if (sorted_arr[i + 1] == sorted_arr[i]) {
$(".error").append(
fieldname + " '" + sorted_arr[i] + "' "
+ (window.MSG_ADDITEM_JS_IS_DUPLICATE || "is duplicated")
+ "<br/>");
success = false;
}
}
}
// If there is a duplication, we raise an error
if ( success == false ) {
$(".error").show();
return false;
}
$.ajax({
url: '/cgi-bin/koha/acqui/check_uniqueness.pl',
async: false,
dataType: 'json',
data: data,
success: function(data) {
for (field in data) {
success = false;
for (var i=0; i < data[field].length; i++) {
var value = data[field][i];
$(".error").append(
field + " '" + value + "' "
+ (window.MSG_ADDITEM_JS_ALREADY_EXISTS_IN_DB
|| "already exists in database")
+ "<br />"
);
}
}
}
});
if ( success == false ) {
$(".error").show();
}
return success;
}
$(document).ready(function(){
$(".cloneItemBlock").click(function(){
var clonedRow = $(this).parent().parent().clone(true);
clonedRow.insertAfter($(this).parent().parent()).find("a.deleteItemBlock").show();
// find ID of cloned row so we can increment it for the clone
var count = $("input[id^=volinf]",clonedRow).attr("id");
var current = Number(count.replace("volinf",""));
var increment = current + 1;
// loop over inputs
var inputs = ["volinf","barcode"];
jQuery.each(inputs,function() {
// increment IDs of labels and inputs in the clone
$("label[for="+this+current+"]",clonedRow).attr("for",this+increment);
$("input[name="+this+"]",clonedRow).attr("id",this+increment);
});
// loop over selects
var selects = ["homebranch","location","itemtype","ccode"];
jQuery.each(selects,function() {
// increment IDs of labels and selects in the clone
$("label[for="+this+current+"]",clonedRow).attr("for",this+increment);
$("input[name="+this+"]",clonedRow).attr("id",this+increment);
$("select[name="+this+"]",clonedRow).attr("id",this+increment);
// find the selected option and select it in the clone
var selectedVal = $("select#"+this+current).find("option:selected").attr("value");
$("select[name="+this+"] option[value="+selectedVal+"]",clonedRow).attr("selected","selected");
});
var quantityrec = parseFloat($("#quantityrec").attr("value"));
quantityrec++;
$("#quantityrec").attr("value",quantityrec);
return false;
});
$(".deleteItemBlock").click(function(){
$(this).parent().parent().remove();
var quantityrec = parseFloat($("#quantityrec").attr("value"));
quantityrec--;
$("#quantityrec").attr("value",quantityrec);
return false;
});
});

View file

@ -4,12 +4,28 @@
[% INCLUDE 'doc-head-close.inc' %]
<script type="text/javascript" src="[% themelang %]/js/acq.js"></script>
[% INCLUDE 'additem.js.inc' %]
<script type="text/javascript" src="[% themelang %]/js/additem.js"></script>
<script type="text/javascript">
//<![CDATA[
actTotal = "";
function Check(ff) {
[% IF (AcqCreateItemOrdering) %]
// Remove last itemblock if it is not in items_list
var lastitemblock = $("#outeritemblock > div:last");
var tobedeleted = true;
var listitems = $("#items_list tr");
$(listitems).each(function(){
if($(this).attr('idblock') == $(lastitemblock).attr('id')){
tobedeleted = false;
}
});
if(tobedeleted){
$(lastitemblock).remove();
}
[% END %]
var ok=0;
var _alertString= _("Form not submitted because of the following problem(s)")+"\n";
@ -25,7 +41,7 @@ function Check(ff) {
_alertString += "\n- "+ _("You must select a budget");
}
if (!(isNum(ff.quantity,0))){
if (!(isNum(ff.quantity,0)) || ff.quantity.value == 0){
ok=1;
_alertString += "\n- " + _("Quantity must be greater than '0'");
}
@ -47,16 +63,12 @@ function Check(ff) {
}
if ( ff.field_value ) {
var barcodes = [];
var empty_item_mandatory = 0;
for (i = 0; i < ff.field_value.length; i++) {
//alert("i = " + i + " => " + ff.kohafield[i] );
if (ff.field_value[i].value.length == 0 && ff.mandatory[i].value == 1) {
empty_item_mandatory++;
}
if(ff.subfield[i].value === '[% barcode_subfield %]' && ff.field_value[i].value.length != 0) {
barcodes.push(ff.field_value[i].value);
}
}
if (empty_item_mandatory > 0) {
ok = 1;
@ -64,56 +76,34 @@ function Check(ff) {
"\n- " + empty_item_mandatory + _(" item mandatory fields empty");
}
if(barcodes.length > 0) {
// Check for duplicate barcodes in the form
barcodes = barcodes.sort();
for(var i=0; i<barcodes.length-1; i++) {
if(barcodes[i] == barcodes[i+1]) {
ok = 1;
_alertString += "\n- " + _("The barcode ") + barcodes[i] + _(" is used more than once in the form. Every barcode must be unique");
}
}
// Check for duplicate barcodes in the database via an ajax call
$.ajax({
url: "/cgi-bin/koha/acqui/check_duplicate_barcode_ajax.pl",
async:false,
method: "post",
data: {barcodes : barcodes},
dataType: 'json',
error: function(xhr) {
alert("Error: \n" + xhr.responseText);
},
success: function(json) {
switch(json.status) {
case 'UNAUTHORIZED':
ok = 1;
_alertString += "\n- " + _("Error: Duplicate barcode verification failed. Insufficient user permissions.");
break;
case 'DUPLICATES':
ok = 1;
$.each(json.barcodes, function(index, barcode) {
_alertString += "\n- " + _("The barcode ") + barcode + _(" already exists in the database");
});
break;
}
},
});
}
}
if (ok) {
alert(_alertString);
[% IF (AcqCreateItemOrdering) %]
if(tobedeleted) {
$(lastitemblock).appendTo('#outeritemblock');
}
[% END %]
return false;
}
ff.submit();
[% IF (AcqCreateItemOrdering) %]
if(check_additem('[% UniqueItemFields %]') == false) {
alert(_('Duplicate values detected. Please correct the errors and resubmit.') );
if(tobedeleted) {
$(lastitemblock).appendTo('#outeritemblock');
}
return false;
}
[% END %]
}
$(document).ready(function()
{
[% IF (AcqCreateItemOrdering) %]
cloneItemBlock(0, '[% UniqueItemFields %]');
[% END %]
//We apply the fonction only for modify option
[% IF ( quantityrec ) %]
$('#quantity').blur(function()
@ -169,6 +159,8 @@ $(document).ready(function()
[% END %]
</h2>
<div class="error" style="display:none"></div>
[% IF ( basketno ) %]
<div id="acqui_basket_summary" class="yui-g">
<fieldset class="rows">
@ -208,7 +200,7 @@ $(document).ready(function()
</div>
[% END %]
<form action="/cgi-bin/koha/acqui/addorder.pl" method="post" id="Aform">
<form action="/cgi-bin/koha/acqui/addorder.pl" method="post" id="Aform" onsubmit="return Check(this);">
<fieldset class="rows">
<legend>
@ -333,41 +325,47 @@ $(document).ready(function()
</fieldset>
[% END %]
[% IF ( items ) %]
<fieldset class="rows">
[% IF (AcqCreateItemOrdering) %]
<div id="items_list" style="display:none">
<p><b>Items list</b></p>
<div style="width:100%;overflow:auto;">
<table>
<thead>
<tr>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>Barcode</th>
<th>Home branch</th>
<th>Holding branch</th>
<th>Not for loan</th>
<th>Restricted</th>
<th>Location</th>
<th>Call number</th>
<th>Copy number</th>
<th>Stock number</th>
<th>Collection code</th>
<th>Item type</th>
<th>Materials</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<fieldset class="rows" id="itemfieldset">
<legend>Item</legend>
[% IF ( NoACQframework ) %]
<div class="dialog message">No ACQ framework, using default. You should create a framework with code ACQ, the items framework would be used</div>
[% END %]
[% FOREACH item IN items %]
<div id="outeritemblock">
<div id="itemblock">
<ol>[% FOREACH iteminformatio IN item.iteminformation %]<li style="[% iteminformatio.hidden %];">
<div class="subfield_line" id="subfield[% iteminformatio.serialid %][% iteminformatio.countitems %][% iteminformatio.subfield %][% iteminformatio.random %]">
<div id="outeritemblock"></div>
<label>[% iteminformatio.subfield %] - [% IF ( iteminformatio.mandatory ) %]<b>[% END %][% iteminformatio.marc_lib %][% IF ( iteminformatio.mandatory ) %] *</b>[% END %]</label>
[% iteminformatio.marc_value %]
<input type="hidden" name="itemid" value="1" />
<input type="hidden" name="kohafield" value="[% iteminformatio.kohafield %]" />
<input type="hidden" name="tag" value="[% iteminformatio.tag %]" />
<input type="hidden" name="subfield" value="[% iteminformatio.subfield %]" />
<input type="hidden" name="mandatory" value="[% iteminformatio.mandatory %]" />
[% IF ( iteminformatio.ITEM_SUBFIELDS_ARE_NOT_REPEATABLE ) %]
<span class="buttonPlus" onclick="CloneSubfield('subfield[% iteminformatio.serialid %][% iteminformatio.countitems %][% iteminformatio.subfield %][% iteminformatio.random %]')">+</span>
[% END %]
</div></li>
[% END %]
</ol>
<a class="addItem" onclick="cloneItemBlock('itemblock[% item.itemBlockIndex %]')">Add</a>
<a class="delItem" style="display:none;" onclick="deleteItemBlock('itemblock[% item.itemBlockIndex %]')">Delete</a>
</div><!-- /iteminformation -->
</div>
[% END %] <!-- /items -->
</fieldset>
[% END %] <!-- items -->
[% END %][%# IF (AcqCreateItemOrdering) %]
<fieldset class="rows">
<legend>Accounting Details</legend>
<ol>
@ -376,9 +374,9 @@ $(document).ready(function()
<span class="label required">Quantity: </span>
<input type="hidden" size="20" name="quantity" value="[% quantity %]" />[% quantity %]
[% ELSE %]
<label class="required" for="quantity">Quantity: </label>
[% IF ( items ) %]
<input type="text" readonly="readonly" size="20" id="quantity" name="quantity" value="1" onchange="calcNeworderTotal();" />
<label class="required" for="quantity">Quantity: </label>
[% IF (AcqCreateItemOrdering) %]
<input type="text" readonly="readonly" size="20" id="quantity" name="quantity" value="0" onchange="updateCosts();" />
[% ELSE %]
<input type="text" size="20" id="quantity" name="quantity" value="[% quantityrec %]" onchange="calcNeworderTotal();" />
[% END %]
@ -530,7 +528,12 @@ $(document).ready(function()
</ol>
</fieldset>
<fieldset class="action">
<input type="button" value="Save" onclick="Check(this.form)" /> [% IF ( suggestionid ) %]<a class="cancel" href="/cgi-bin/koha/acqui/newordersuggestion.pl?booksellerid=[% booksellerid %]&amp;basketno=[% basketno %]">Cancel</a>[% ELSE %]<a class="cancel" href="/cgi-bin/koha/acqui/basket.pl?basketno=[% basketno %]">Cancel</a>[% END %]
<input type="submit" value="Save" />
[% IF (suggestionid) %]
<a class="cancel" href="/cgi-bin/koha/acqui/newordersuggestion.pl?booksellerid=[% booksellerid %]&amp;basketno=[% basketno %]">Cancel</a>
[% ELSE %]
<a class="cancel" href="/cgi-bin/koha/acqui/basket.pl?basketno=[% basketno %]">Cancel</a>
[% END %]
</fieldset>
</form>
</div>

View file

@ -1,7 +1,44 @@
[% INCLUDE 'doc-head-open.inc' %]
<title>Koha &rsaquo; Acquisitions &rsaquo; Receipt summary for : [% name %] [% IF ( invoice ) %]invoice, [% invoice %][% END %]</title>
[% INCLUDE 'doc-head-close.inc' %]
[% INCLUDE 'additem.js.inc' %]
<script type="text/javascript" src="[% themelang %]/js/additem.js"> </script>
<script type="text/javascript">
//<![CDATA[
function Check(form) {
[% IF (AcqCreateItemReceiving) %]
// Remove last itemblock if it is not in items_list
var lastitemblock = $("#outeritemblock > div:last");
var tobedeleted = true;
var listitems = $("#items_list tr");
$(listitems).each(function(){
if($(this).attr('idblock') == $(lastitemblock).attr('id')){
tobedeleted = false;
}
});
if(tobedeleted){
$(lastitemblock).remove();
}
if(check_additem('[% UniqueItemFields %]') == false){
alert(_('Duplicate values detected. Please correct the errors and resubmit.') );
if(tobedeleted) {
$(lastitemblock).appendTo("#outeritemblock");
}
return false;
};
[% END %]
return true;
}
$(document).ready(function() {
[% IF (AcqCreateItemReceiving) %]
cloneItemBlock(0, '[% UniqueItemFields %]');
[% END %]
});
//]]>
</script>
</head>
<body id="acq_orderreceive" class="acq">
[% INCLUDE 'header.inc' %]
@ -18,10 +55,11 @@
<h1>Receive items from : [% name %] [% IF ( invoice ) %][[% invoice %]] [% END %] (order #[% ordernumber %])</h1>
[% IF ( count ) %]
<form action="/cgi-bin/koha/acqui/finishreceive.pl" method="post">
<form action="/cgi-bin/koha/acqui/finishreceive.pl" method="post" onsubmit="return Check(this);">
<div class="yui-g">
<div class="yui-u first">
<div class="error" style="display:none"></div>
<fieldset class="rows">
<legend>Catalog Details</legend>
<ol><li><span class="label">Title: </span><span class="title">[% title |html %]</span></li>
@ -48,48 +86,48 @@
</fieldset>
[% END %]
[% IF ( items ) %]
<fieldset class="rows">
<legend>Item</legend>
[% IF ( NoACQframework ) %]
<p class="required">No ACQ framework, using default. You should create a framework with code ACQ, the items framework would be used</p>
[% END %]
[% FOREACH item IN items %]
<div id="outeritemblock">
<div id="itemblock">
<ol>[% FOREACH iteminformatio IN item.iteminformation %]<li style="[% iteminformatio.hidden %];">
<div class="subfield_line" id="subfield[% iteminformatio.serialid %][% iteminformatio.countitems %][% iteminformatio.subfield %][% iteminformatio.random %]">
<label>[% iteminformatio.subfield %] - [% IF ( iteminformatio.mandatory ) %]<b>[% END %][% iteminformatio.marc_lib %][% IF ( iteminformatio.mandatory ) %] *</b>[% END %]</label>
[% iteminformatio.marc_value %]
<input type="hidden" name="itemid" value="1" />
<input type="hidden" name="kohafield" value="[% iteminformatio.kohafield %]" />
<input type="hidden" name="tag" value="[% iteminformatio.tag %]" />
<input type="hidden" name="subfield" value="[% iteminformatio.subfield %]" />
<input type="hidden" name="mandatory" value="[% iteminformatio.mandatory %]" />
[% IF ( iteminformatio.ITEM_SUBFIELDS_ARE_NOT_REPEATABLE ) %]
<span class="buttonPlus" onclick="CloneSubfield('subfield[% iteminformatio.serialid %][% iteminformatio.countitems %][% iteminformatio.subfield %][% iteminformatio.random %]')">+</span>
[% END %]
</div></li>
[% END %]
</ol>
<a class="addItem" onclick="cloneItemBlock('itemblock[% item.itemBlockIndex %]')">Add</a>
<a class="delItem" style="display:none;" onclick="deleteItemBlock('itemblock[% item.itemBlockIndex %]')">Delete</a>
</div><!-- /iteminformation -->
[% IF (AcqCreateItemReceiving) %]
<div id="items_list" style="display:none">
<p><b>Items list</b></p>
<div style="width:100%;overflow:auto;">
<table>
<thead>
<tr>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>Barcode</th>
<th>Home branch</th>
<th>Holding branch</th>
<th>Not for loan</th>
<th>Restricted</th>
<th>Location</th>
<th>Call number</th>
<th>Copy number</th>
<th>Stock number</th>
<th>Collection code</th>
<th>Item type</th>
<th>Materials</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<input type="hidden" name="moditem" value="" />
<input type="hidden" name="tag" value="[% item.itemtagfield %]" />
<input type="hidden" name="subfield" value="[% item.itemtagsubfield %]" />
<input type="hidden" name="serial" value="[% item.serialid %]" />
<input type="hidden" name="bibnum" value="[% item.biblionumber %]" />
<input type="hidden" name="itemid" value="1" />
<input type="hidden" name="field_value" value="[% item.itemnumber %]" />
[% END %] <!-- /items -->
</fieldset>
[% END %] <!-- items -->
<fieldset class="rows" id="itemfieldset">
<legend>Item</legend>
[% IF ( NoACQframework ) %]
<p class="required">
No ACQ framework, using default. You should create a
framework with code ACQ, the items framework would be
used
</p>
[% END %]
<div id="outeritemblock"></div>
</fieldset>
[% END %][%# IF (AcqCreateItemReceiving) %]
<input type="hidden" name="biblionumber" value="[% biblionumber %]" />
<input type="hidden" name="ordernumber" value="[% ordernumber %]" />
<input type="hidden" name="biblioitemnumber" value="[% biblioitemnumber %]" />
@ -107,12 +145,15 @@
<li><label for="creator">Created by: </label><span> [% IF ( memberfirstname and membersurname ) %][% IF ( memberfirstname ) %][% memberfirstname %][% END %] [% membersurname %][% ELSE %]No name[% END %]</span></li>
<li><label for="quantityto">Quantity to receive: </label><span class="label">
[% IF ( edit ) %]
<input type="text" name="quantity" value="[% quantity %]" />
<input type="text" id="quantity_to_receive" name="quantity" value="[% quantity %]" />
[% ELSE %]
<input type="text" READONLY name="quantity" value="[% quantity %]" />
<input type="text" readonly="readonly" id="quantity_to_receive" name="quantity" value="[% quantity %]" />
[% END %]
</span></li>
<li><label for="quantity">Quantity received: </label>
[% IF (AcqCreateItemReceiving) %]
<input readonly="readonly" type="text" size="20" name="quantityrec" id="quantity" value="0" />
[% ELSE %]
[% IF ( quantityreceived ) %]
[% IF ( edit ) %]
<input type="text" size="20" name="quantityrec" id="quantity" value="[% quantityreceived %]" />
@ -133,6 +174,7 @@
[% END %]
<input id="origquantityrec" READONLY type="hidden" name="origquantityrec" value="0" />
[% END %]
[% END %][%# IF (AcqCreateItemReceiving) %]
</li>
<li><label for="rrp">Replacement cost: </label><input type="text" size="20" name="rrp" id="rrp" value="[% rrp %]" /></li>
<li><label for="ecost">Budgeted cost: </label><input type="text" size="20" name="ecost" id="ecost" value="[% ecost %]" /></li>
@ -148,7 +190,8 @@
</div>
</div><div class="yui-g"><fieldset class="action">
<input type="button" value="Save" onclick="javascript:if(check_additem()) { this.form.submit(); } else { alert( _('Duplicate barcodes detected. Please correct the errors and resubmit.') ); return false };" /> <a class="cancel" href="/cgi-bin/koha/acqui/parcel.pl?booksellerid=[% booksellerid %]&amp;invoice=[% invoice %]&amp;gst=[% gst %]&amp;freight=[% freight %]">Cancel</a>
<input type="submit" value="Save" />
<a class="cancel" href="/cgi-bin/koha/acqui/parcel.pl?booksellerid=[% supplierid %]&amp;invoice=[% invoice %]&amp;gst=[% gst %]&amp;freight=[% freight %]">Cancel</a>
</fieldset></div> </form>
[% ELSE %]
<div id="acqui_acquire_orderlist">

View file

@ -8,6 +8,9 @@ Acquisitions:
ordering: placing an order.
receiving: receiving an order.
cataloguing: cataloging the record.
-
- pref: UniqueItemFields
- (space-separated list of fields that should be unique for items, must be valid SQL fields of items table)
-
- When closing or reopening a basket,
- pref: BasketConfirmations

View file

@ -0,0 +1,24 @@
<ol>
[% FOREACH iteminfo IN iteminformation %]
<li>
<div class="subfield_line" style="[% iteminfo.hidden %];" id="subfield[% iteminfo.serialid %][% iteminfo.countitems %][% iteminfo.subfield %][% iteminfo.random %]">
<label>
[% iteminfo.subfield %] -
[% IF ( iteminfo.mandatory ) %]
<b>
[% END %]
[% iteminfo.marc_lib %]
[% IF ( iteminfo.mandatory ) %]
*</b>
[% END %]
</label>
[% iteminfo.marc_value %]
<input type="hidden" name="itemid" value="1" />
<input type="hidden" name="kohafield" value="[% iteminfo.kohafield %]" />
<input type="hidden" name="tag" value="[% iteminfo.tag %]" />
<input type="hidden" name="subfield" value="[% iteminfo.subfield %]" />
<input type="hidden" name="mandatory" value="[% iteminfo.mandatory %]" />
</div>
</li>
[% END %]
</ol>

57
services/itemrecorddisplay.pl Executable file
View file

@ -0,0 +1,57 @@
#!/usr/bin/perl
# Copyright 2011 BibLibre SARL
# 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 2 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, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
=head1 NAME
itemrecorddisplay.pl
=head1 DESCRIPTION
Return a HTML form for Item record modification or creation.
It uses PrepareItemrecordDisplay
=cut
use strict;
use warnings;
use CGI;
use C4::Auth;
use C4::Output;
use C4::Items;
my $input = new CGI;
my ($template, $loggedinuser, $cookie, $flags) = get_template_and_user( {
template_name => 'services/itemrecorddisplay.tmpl',
query => $input,
type => 'intranet',
authnotrequired => 1,
} );
my $biblionumber = $input->param('biblionumber') || '';
my $itemnumber = $input->param('itemnumber') || '';
my $frameworkcode = $input->param('frameworkcode') || '';
my $result = PrepareItemrecordDisplay($biblionumber, $itemnumber, undef, $frameworkcode);
unless($result) {
$result = PrepareItemrecordDisplay($biblionumber, $itemnumber, undef, '');
}
$template->param(%$result);
output_html_with_http_headers $input, $cookie, $template->output;