@@ -, +, @@
- Display a unique item block at once
- PrepareItemrecordDisplay is now used for cloning block
- New system preference UniqueItemFields
- 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)
---
acqui/check_uniqueness.pl | 68 ++++
acqui/neworderempty.pl | 16 +-
acqui/orderreceive.pl | 15 +-
installer/data/mysql/updatedatabase.pl | 11 +
.../intranet-tmpl/prog/en/includes/additem.js.inc | 10 +
koha-tmpl/intranet-tmpl/prog/en/js/additem.js | 337 ++++++++++++++------
.../prog/en/modules/acqui/neworderempty.tt | 157 +++++-----
.../prog/en/modules/acqui/orderreceive.tt | 133 +++++---
.../en/modules/admin/preferences/acquisitions.pref | 3 +
.../prog/en/modules/services/itemrecorddisplay.tt | 25 ++
services/itemrecorddisplay.pl | 58 ++++
11 files changed, 590 insertions(+), 243 deletions(-)
create mode 100755 acqui/check_uniqueness.pl
create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/additem.js.inc
create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/services/itemrecorddisplay.tt
create mode 100755 services/itemrecorddisplay.pl
--- a/acqui/check_uniqueness.pl
+++ a/acqui/check_uniqueness.pl
@@ -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';
--- a/acqui/neworderempty.pl
+++ a/acqui/neworderempty.pl
@@ -317,17 +317,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;
--- a/acqui/orderreceive.pl
+++ a/acqui/orderreceive.pl
@@ -116,16 +116,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 ) {
--- a/installer/data/mysql/updatedatabase.pl
+++ a/installer/data/mysql/updatedatabase.pl
@@ -4663,6 +4663,17 @@ ENDOFRENEWAL
print "Upgrade to $DBversion done (Added a system preference to allow renewal of Patron account either from todays date or from existing expiry date in the patrons account.)\n";
SetVersion($DBversion);
}
+
+$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);
+}
+
=head1 FUNCTIONS
=head2 DropAllForeignKeys($table)
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/additem.js.inc
+++ a/koha-tmpl/intranet-tmpl/prog/en/includes/additem.js.inc
@@ -0,0 +1,10 @@
+
--- a/koha-tmpl/intranet-tmpl/prog/en/js/additem.js
+++ a/koha-tmpl/intranet-tmpl/prog/en/js/additem.js
@@ -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 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.");
+ }
+ } 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 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 = "
- var clone = clone_with_selected(original)
+
+function cloneItemBlock(index, unique_item_fields) {
+ var original;
+ if(index) {
+ original = $("#" + index); //original
+ }
+ 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.
- // 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;
+ var clone = $("")
+ $.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 = 'Add';
+ var buttonClear = '' + (window.MSG_ADDITEM_JS_CLEAR || 'Clear') + '';
+ $(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);
}
- }
- // }
- //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 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;
+
+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")
+ + " ");
+ 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")
+ + " "
+ );
+ }
+ }
+ }
+ });
+
+ if ( success == false ) {
+ $(".error").show();
+ }
+ return success;
}
-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
+[% INCLUDE 'additem.js.inc' %]
+
[% INCLUDE 'header.inc' %]
@@ -18,10 +55,11 @@
Receive items from : [% name %] [% IF ( invoice ) %][[% invoice %]] [% END %] (order #[% ordernumber %])
[% IF ( count ) %]
-
[% ELSE %]
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/acquisitions.pref
+++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/acquisitions.pref
@@ -9,6 +9,9 @@ Acquisitions:
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
default: 1
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/services/itemrecorddisplay.tt
+++ a/koha-tmpl/intranet-tmpl/prog/en/modules/services/itemrecorddisplay.tt
@@ -0,0 +1,25 @@
+
+ [% FOREACH iteminfo IN iteminformation %]
+
+
+
+ [% iteminfo.marc_value %]
+
+
+
+
+
+
+
+ [% END %]
+
+
--- a/services/itemrecorddisplay.pl
+++ a/services/itemrecorddisplay.pl
@@ -0,0 +1,58 @@
+#!/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::Biblio;
+
+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;
+
--