Jonathan Druart
424aca3d56
Here we go! Disclaimer: this patch is huge and does many things, but splitting it in several chunks would be time consuming and painful to rebase. However it adds many tests and isolate/refactor code to make it way more reusable. This patchset will make the "batch item modification" and "batch item deletion" features use the task queue (reminder: Since bug 28158, and so 21.05.00, we do no longer use the old "background job" functionality and the user does not get any info about the progress of the job). More than that, more of the code to build an item form and a list of items is now isolated in module (.pm) and include files (.inc) We are reusing the changes made by bug 27526 that simplifies the way we edit/create items (no more unecessary serialization Koha > MARC > MARCXML > XML > HTML) New module: * Koha::BackgroundJob::BatchDeleteItem Subclass for process item deletion in batch * Koha::BackgroundJob::BatchUpdateItem Subclass for process item modification in batch * Koha::Item::Attributes We needed an object to represent item's attributes that are not mapped with a koha field (aka "more subfields xml") This module will help us to create the marcxml from a hashref and the reverse. * Koha::UI::Form::Builder::Item The code that was used to build the add/edit item form is centralised in this module. In conjunction with the subfields_for_item BLOCK (from html_helpers.inc) it will be really easy to reuse this code in other places where the item form is used (acquisition and serials modules) * Koha::UI::Table::Builder::Items Same as previously for the table. We are now using this table from 3 different places (batch item mod, batch item del, backgroung job detail view) and the code is only in one place. To use with items_table_batchmod BLOCK (still from html_helpers.inc) This patch is fixing some bugs about repeatable subfields and regex. A UI change will reflect the limitation: if you want to apply a regex on a subfield you cannot add several subfields for the same subfield code. Test plan: Prepare the ground: - Make sure you are always using a bibliographic/item record using the framework you are modifying! - Add some subfields for items that are not mapped with a koha field (note that you can use 'é' for more fun, don't try more funny characters) - Make some subfields (mapped and not mapped with a kohafield) repeatable - Add default values to some of your subfields There are 4 main screens to test: 1. Add/edit item form The behaviour should be the same before and after this patch. See test plan from bug 27526. Those 2 prefs must be tested: * SubfieldsToAllowForRestrictedEditing * SubfieldsToUseWhenPrefill 2. Batch modification a. Fill some values, play with repeatable and regex. Note that the behaviour in master was buggy, only the first value was modified by the regex: * With subfield = "a | b" 1 value added with "new" => "new | b" * With subfield = "a | b" 2 new fields "new1","new2" => "new2 | b" Important note: For repeatable subfields, a regex will apply on the subfields in the "concatenated form". To apply the regex on all the different subfields of a given subfield code you must use the "g" modifier. This could be improved later, but keep in mind that it's not a regression or behaviour change. b. Play with the "Populate fields with default values from default framework" checkbox c. Use this tool to modify items and play with the different sysprefs that interfer with it: * NewItemsDefaultLocation * SubfieldsToAllowForRestrictedBatchmod * MaxItemsToDisplayForBatchMod * MaxItemsToProcessForBatchMod 3. Batch deletion a. Batch delete some items b. Check items out and try to delete them c. Use the "Delete records if no items remain" checkbox to delete bibliographic records without remaining items. d. Play with the following sysprefs and confirm that it works as expected: * MaxItemsToDisplayForBatchDel e. Stress the tool: Go to the confirmation screen with items that can be deleted, don't request the job to be processed right away, but check the item out before. 4. Background job detail view You must have seen it already if you are curious and tested the above. When a new modification or deletion batch is requested, the confirmation screen will tell you that the job has enqueued. A link to the progress of the job can be followed. On this screen you will be able to see the result of the job once it's fully processed. QA notes: * There are some FIXME's that are not blocker in my opinion. Feel free to discuss them if you have suggestions. * Do we still need MaxItemsToProcessForBatchMod? * Prior to this patchset we had a "Return to the cataloging module" link if we went from the cataloguing module and that the biblio was deleted. We cannot longer know if the biblio will be deleted but we could display a "Go to the cataloging module" link on the "job has been enqueued" screen regardless from where we were coming from. Signed-off-by: Nick Clemens <nick@bywatersolutions.com> Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io> Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
628 lines
24 KiB
Perl
Executable file
628 lines
24 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
|
|
# Copyright 2000-2002 Katipo Communications
|
|
# Copyright 2004-2010 BibLibre
|
|
# Parts Copyright Catalyst IT 2011
|
|
#
|
|
# 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 <http://www.gnu.org/licenses>.
|
|
|
|
use Modern::Perl;
|
|
|
|
use CGI qw ( -utf8 );
|
|
use C4::Auth qw( get_template_and_user haspermission );
|
|
use C4::Output qw( output_and_exit_if_error output_and_exit output_html_with_http_headers );
|
|
use C4::Biblio qw(
|
|
GetFrameworkCode
|
|
GetMarcBiblio
|
|
GetMarcFromKohaField
|
|
GetMarcStructure
|
|
IsMarcStructureInternal
|
|
ModBiblio
|
|
);
|
|
use C4::Context;
|
|
use C4::Circulation qw( barcodedecode LostItem );
|
|
use C4::Barcodes;
|
|
use C4::Barcodes::ValueBuilder;
|
|
use Koha::DateUtils qw( dt_from_string );
|
|
use Koha::Items;
|
|
use Koha::ItemTypes;
|
|
use Koha::Libraries;
|
|
use Koha::Patrons;
|
|
use Koha::SearchEngine::Indexer;
|
|
use C4::Search qw( enabled_staff_search_views );
|
|
use Storable qw( freeze thaw );
|
|
use URI::Escape qw( uri_escape_utf8 );
|
|
use C4::Members;
|
|
use Koha::UI::Form::Builder::Item;
|
|
|
|
use MARC::File::XML;
|
|
use URI::Escape qw( uri_escape_utf8 );
|
|
use Encode qw( encode_utf8 );
|
|
use MIME::Base64 qw( decode_base64url encode_base64url );
|
|
use List::Util qw( first );
|
|
use List::MoreUtils qw( any uniq );
|
|
|
|
our $dbh = C4::Context->dbh;
|
|
|
|
sub get_item_from_cookie {
|
|
my ( $input ) = @_;
|
|
|
|
my $item_from_cookie;
|
|
my $lastitemcookie = $input->cookie('LastCreatedItem');
|
|
if ($lastitemcookie) {
|
|
$lastitemcookie = decode_base64url($lastitemcookie);
|
|
eval {
|
|
if ( thaw($lastitemcookie) ) {
|
|
$item_from_cookie = thaw($lastitemcookie);
|
|
}
|
|
};
|
|
if ($@) {
|
|
$lastitemcookie ||= 'undef';
|
|
warn "Storable::thaw failed to thaw LastCreatedItem-cookie. Cookie value '".encode_base64url($lastitemcookie)."'. Caught error follows: '$@'";
|
|
}
|
|
}
|
|
return $item_from_cookie;
|
|
}
|
|
|
|
my $input = CGI->new;
|
|
my $error = $input->param('error');
|
|
|
|
my $biblionumber;
|
|
my $itemnumber;
|
|
if( $input->param('itemnumber') && !$input->param('biblionumber') ){
|
|
$itemnumber = $input->param('itemnumber');
|
|
my $item = Koha::Items->find( $itemnumber );
|
|
$biblionumber = $item->biblionumber;
|
|
} else {
|
|
$biblionumber = $input->param('biblionumber');
|
|
$itemnumber = $input->param('itemnumber');
|
|
}
|
|
|
|
my $biblio = Koha::Biblios->find($biblionumber);
|
|
|
|
my $op = $input->param('op') || q{};
|
|
my $hostitemnumber = $input->param('hostitemnumber');
|
|
my $marcflavour = C4::Context->preference("marcflavour");
|
|
my $searchid = $input->param('searchid');
|
|
# fast cataloguing datas
|
|
my $fa_circborrowernumber = $input->param('circborrowernumber');
|
|
my $fa_barcode = $input->param('barcode');
|
|
my $fa_branch = $input->param('branch');
|
|
my $fa_stickyduedate = $input->param('stickyduedate');
|
|
my $fa_duedatespec = $input->param('duedatespec');
|
|
|
|
our $frameworkcode = &GetFrameworkCode($biblionumber);
|
|
|
|
# Defining which userflag is needing according to the framework currently used
|
|
my $userflags;
|
|
if (defined $input->param('frameworkcode')) {
|
|
$userflags = ($input->param('frameworkcode') eq 'FA') ? "fast_cataloging" : "edit_items";
|
|
}
|
|
|
|
if (not defined $userflags) {
|
|
$userflags = ($frameworkcode eq 'FA') ? "fast_cataloging" : "edit_items";
|
|
}
|
|
|
|
my ($template, $loggedinuser, $cookie)
|
|
= get_template_and_user({template_name => "cataloguing/additem.tt",
|
|
query => $input,
|
|
type => "intranet",
|
|
flagsrequired => {editcatalogue => $userflags},
|
|
});
|
|
|
|
|
|
# Does the user have a restricted item editing permission?
|
|
my $uid = Koha::Patrons->find( $loggedinuser )->userid;
|
|
my $restrictededition = $uid ? haspermission($uid, {'editcatalogue' => 'edit_items_restricted'}) : undef;
|
|
# In case user is a superlibrarian, editing is not restricted
|
|
$restrictededition = 0 if ($restrictededition != 0 && C4::Context->IsSuperLibrarian());
|
|
# In case user has fast cataloging permission (and we're in fast cataloging), editing is not restricted
|
|
$restrictededition = 0 if ($restrictededition != 0 && $frameworkcode eq 'FA' && haspermission($uid, {'editcatalogue' => 'fast_cataloging'}));
|
|
|
|
our $tagslib = &GetMarcStructure(1,$frameworkcode);
|
|
my $record = GetMarcBiblio({ biblionumber => $biblionumber });
|
|
|
|
output_and_exit_if_error( $input, $cookie, $template,
|
|
{ module => 'cataloguing', record => $record } );
|
|
|
|
my $current_item;
|
|
my $nextop="additem";
|
|
my @errors; # store errors found while checking data BEFORE saving item.
|
|
|
|
# Getting last created item cookie
|
|
my $prefillitem = C4::Context->preference('PrefillItem');
|
|
|
|
#-------------------------------------------------------------------------------
|
|
if ($op eq "additem") {
|
|
|
|
my $add_submit = $input->param('add_submit');
|
|
my $add_duplicate_submit = $input->param('add_duplicate_submit');
|
|
my $add_multiple_copies_submit = $input->param('add_multiple_copies_submit');
|
|
my $number_of_copies = $input->param('number_of_copies');
|
|
|
|
my @columns = Koha::Items->columns;
|
|
my $item = Koha::Item->new;
|
|
$item->biblionumber($biblio->biblionumber);
|
|
for my $c ( @columns ) {
|
|
if ( $c eq 'more_subfields_xml' ) {
|
|
my @more_subfields_xml = $input->multi_param("items.more_subfields_xml");
|
|
my @unlinked_item_subfields;
|
|
for my $subfield ( @more_subfields_xml ) {
|
|
my $v = $input->param('items.more_subfields_xml_' . $subfield);
|
|
push @unlinked_item_subfields, $subfield, $v;
|
|
}
|
|
if ( @unlinked_item_subfields ) {
|
|
my $marc = MARC::Record->new();
|
|
# use of tag 999 is arbitrary, and doesn't need to match the item tag
|
|
# used in the framework
|
|
$marc->append_fields(MARC::Field->new('999', ' ', ' ', @unlinked_item_subfields));
|
|
$marc->encoding("UTF-8");
|
|
$item->more_subfields_xml($marc->as_xml("USMARC"));
|
|
next;
|
|
}
|
|
$item->more_subfields_xml(undef);
|
|
} else {
|
|
my @v = grep { $_ ne "" }
|
|
uniq $input->multi_param( "items." . $c );
|
|
|
|
next if !@v
|
|
&& $c ne 'permanent_location'; # See 27837
|
|
|
|
$item->$c(join ' | ', @v);
|
|
}
|
|
}
|
|
|
|
# if autoBarcode is set to 'incremental', calculate barcode...
|
|
if ( ! defined $item->barcode && C4::Context->preference('autoBarcode') eq 'incremental' ) {
|
|
my ( $barcode ) = C4::Barcodes::ValueBuilder::incremental::get_barcode;
|
|
$item->barcode($barcode);
|
|
}
|
|
|
|
$item->barcode(barcodedecode($item->barcode));
|
|
|
|
# If we have to add or add & duplicate, we add the item
|
|
if ( $add_submit || $add_duplicate_submit || $prefillitem) {
|
|
|
|
# check for item barcode # being unique
|
|
if ( defined $item->barcode
|
|
&& Koha::Items->search( { barcode => $item->barcode } )->count )
|
|
{
|
|
# if barcode exists, don't create, but report The problem.
|
|
push @errors, "barcode_not_unique";
|
|
|
|
$current_item = $item->unblessed; # Restore edit form for the same item
|
|
}
|
|
else {
|
|
$item->store->discard_changes;
|
|
|
|
# This is a bit tricky : if there is a cookie for the last created item and
|
|
# we just added an item, the cookie value is not correct yet (it will be updated
|
|
# next page). To prevent the form from being filled with outdated values, we
|
|
# force the use of "add and duplicate" feature, so the form will be filled with
|
|
# correct values.
|
|
|
|
# Pushing the last created item cookie back
|
|
if ( $prefillitem ) {
|
|
my $last_created_item_cookie = $input->cookie(
|
|
-name => 'LastCreatedItem',
|
|
# We encode_base64url the whole freezed structure so we're sure we won't have any encoding problems
|
|
-value => encode_base64url( freeze( { %{$item->unblessed}, itemnumber => undef } ) ),
|
|
-HttpOnly => 1,
|
|
-expires => ''
|
|
);
|
|
|
|
$cookie = [ $cookie, $last_created_item_cookie ];
|
|
}
|
|
|
|
}
|
|
$nextop = "additem";
|
|
|
|
}
|
|
|
|
# If we have to add & duplicate
|
|
if ($prefillitem || $add_duplicate_submit) {
|
|
|
|
$current_item = $item->unblessed;
|
|
|
|
if (C4::Context->preference('autoBarcode') eq 'incremental') {
|
|
my ( $barcode ) = C4::Barcodes::ValueBuilder::incremental::get_barcode;
|
|
$current_item->{barcode} = $barcode;
|
|
}
|
|
else {
|
|
# we have to clear the barcode field in the duplicate item record to make way for the new one generated by the javascript plugin
|
|
$current_item->{barcode} = undef; # FIXME or delete?
|
|
}
|
|
|
|
# Don't use the "prefill" feature if we want to generate the form with all the info from this item
|
|
# It will remove subfields that are not in SubfieldsToUseWhenPrefill.
|
|
$prefillitem = 0 if $add_duplicate_submit;
|
|
}
|
|
|
|
# If we have to add multiple copies
|
|
if ($add_multiple_copies_submit) {
|
|
|
|
$current_item = $item->unblessed;
|
|
|
|
my $copynumber = $current_item->{copynumber};
|
|
my $oldbarcode = $current_item->{barcode};
|
|
|
|
# If there is a barcode and we can't find their new values, we can't add multiple copies
|
|
my $testbarcode;
|
|
my $barcodeobj = C4::Barcodes->new;
|
|
$testbarcode = $barcodeobj->next_value($oldbarcode) if $barcodeobj;
|
|
if ( $oldbarcode && !$testbarcode ) {
|
|
|
|
push @errors, "no_next_barcode";
|
|
|
|
}
|
|
else {
|
|
# We add each item
|
|
|
|
# For the first iteration
|
|
my $barcodevalue = $oldbarcode;
|
|
my $exist_itemnumber;
|
|
|
|
for ( my $i = 0 ; $i < $number_of_copies ; ) {
|
|
|
|
# If there is a barcode
|
|
if ($barcodevalue) {
|
|
|
|
# Getting a new barcode (if it is not the first iteration or the barcode we tried already exists)
|
|
$barcodevalue = $barcodeobj->next_value($oldbarcode)
|
|
if ( $i > 0 || $exist_itemnumber );
|
|
|
|
# Putting it into the record
|
|
if ($barcodevalue) {
|
|
if ( C4::Context->preference("autoBarcode") eq
|
|
'hbyymmincr' && $i > 0 )
|
|
{ # The first copy already contains the homebranch prefix
|
|
# This is terribly hacky but the easiest way to fix the way hbyymmincr is working
|
|
# Contrary to what one might think, the barcode plugin does not prefix the returned string with the homebranch
|
|
# For a single item, it is handled with some JS code (see cataloguing/value_builder/barcode.pl)
|
|
# But when adding multiple copies we need to prefix it here,
|
|
# so we retrieve the homebranch from the item and prefix the barcode with it.
|
|
my $homebranch = $current_item->{homebranch};
|
|
$barcodevalue = $homebranch . $barcodevalue;
|
|
}
|
|
$current_item->{barcode} = $barcodevalue;
|
|
}
|
|
|
|
# Checking if the barcode already exists
|
|
$exist_itemnumber = Koha::Items->search({ barcode => $barcodevalue })->count;
|
|
}
|
|
|
|
# Updating record with the new copynumber
|
|
if ($copynumber) {
|
|
$current_item->{copynumber} = $copynumber;
|
|
}
|
|
|
|
# Adding the item
|
|
if ( !$exist_itemnumber ) {
|
|
delete $current_item->{itemnumber};
|
|
$current_item = Koha::Item->new($current_item)->store(
|
|
{ skip_record_index => 1 } );
|
|
$current_item->discard_changes; # Cannot chain discard_changes
|
|
$current_item = $current_item->unblessed;
|
|
|
|
# We count the item only if it was really added
|
|
# That way, all items are added, even if there was some already existing barcodes
|
|
# FIXME : Please note that there is a risk of infinite loop here if we never find a suitable barcode
|
|
$i++;
|
|
|
|
# Only increment copynumber if item was really added
|
|
$copynumber++ if ( $copynumber && $copynumber =~ m/^\d+$/ );
|
|
}
|
|
|
|
# Preparing the next iteration
|
|
$oldbarcode = $barcodevalue;
|
|
}
|
|
|
|
my $indexer = Koha::SearchEngine::Indexer->new(
|
|
{ index => $Koha::SearchEngine::BIBLIOS_INDEX } );
|
|
$indexer->index_records( $biblionumber, "specialUpdate",
|
|
"biblioserver" );
|
|
|
|
undef($current_item);
|
|
}
|
|
}
|
|
if ($frameworkcode eq 'FA' && $fa_circborrowernumber){
|
|
print $input->redirect(
|
|
'/cgi-bin/koha/circ/circulation.pl?'
|
|
.'borrowernumber='.$fa_circborrowernumber
|
|
.'&barcode='.uri_escape_utf8($fa_barcode)
|
|
.'&duedatespec='.$fa_duedatespec
|
|
.'&stickyduedate='.$fa_stickyduedate
|
|
);
|
|
exit;
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
} elsif ($op eq "edititem") {
|
|
#-------------------------------------------------------------------------------
|
|
# retrieve item if exist => then, it's a modif
|
|
$current_item = Koha::Items->find($itemnumber)->unblessed;
|
|
# FIXME Handle non existent item
|
|
$nextop = "saveitem";
|
|
#-------------------------------------------------------------------------------
|
|
} elsif ($op eq "dupeitem") {
|
|
#-------------------------------------------------------------------------------
|
|
# retrieve item if exist => then, it's a modif
|
|
$current_item = Koha::Items->find($itemnumber)->unblessed;
|
|
# FIXME Handle non existent item
|
|
if (C4::Context->preference('autoBarcode') eq 'incremental') {
|
|
my ( $barcode ) = C4::Barcodes::ValueBuilder::incremental::get_barcode;
|
|
$current_item->{barcode} = $barcode;
|
|
}
|
|
else {
|
|
$current_item->{barcode} = undef; # Don't save it!
|
|
}
|
|
|
|
$nextop = "additem";
|
|
#-------------------------------------------------------------------------------
|
|
} elsif ($op eq "delitem") {
|
|
#-------------------------------------------------------------------------------
|
|
# check that there is no issue on this item before deletion.
|
|
my $item = Koha::Items->find($itemnumber);
|
|
$error = $item->safe_delete;
|
|
if(ref($error) eq 'Koha::Item'){
|
|
print $input->redirect("additem.pl?biblionumber=$biblionumber&frameworkcode=$frameworkcode&searchid=$searchid");
|
|
}else{
|
|
push @errors,$error;
|
|
$nextop="additem";
|
|
}
|
|
#-------------------------------------------------------------------------------
|
|
} elsif ($op eq "delallitems") {
|
|
#-------------------------------------------------------------------------------
|
|
my $items = Koha::Items->search({ biblionumber => $biblionumber });
|
|
while ( my $item = $items->next ) {
|
|
$error = $item->safe_delete({ skip_record_index => 1 });
|
|
next if ref $error eq 'Koha::Item'; # Deleted item is returned if deletion successful
|
|
push @errors,$error;
|
|
}
|
|
my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
|
|
$indexer->index_records( $biblionumber, "specialUpdate", "biblioserver" );
|
|
if ( @errors ) {
|
|
$nextop="additem";
|
|
} else {
|
|
my $defaultview = C4::Context->preference('IntranetBiblioDefaultView');
|
|
my $views = { C4::Search::enabled_staff_search_views };
|
|
if ($defaultview eq 'isbd' && $views->{can_view_ISBD}) {
|
|
print $input->redirect("/cgi-bin/koha/catalogue/ISBDdetail.pl?biblionumber=$biblionumber&searchid=$searchid");
|
|
} elsif ($defaultview eq 'marc' && $views->{can_view_MARC}) {
|
|
print $input->redirect("/cgi-bin/koha/catalogue/MARCdetail.pl?biblionumber=$biblionumber&searchid=$searchid");
|
|
} elsif ($defaultview eq 'labeled_marc' && $views->{can_view_labeledMARC}) {
|
|
print $input->redirect("/cgi-bin/koha/catalogue/labeledMARCdetail.pl?biblionumber=$biblionumber&searchid=$searchid");
|
|
} else {
|
|
print $input->redirect("/cgi-bin/koha/catalogue/detail.pl?biblionumber=$biblionumber&searchid=$searchid");
|
|
}
|
|
exit;
|
|
}
|
|
#-------------------------------------------------------------------------------
|
|
} elsif ($op eq "saveitem") {
|
|
#-------------------------------------------------------------------------------
|
|
|
|
my $itemnumber = $input->param('itemnumber');
|
|
my $item = Koha::Items->find($itemnumber);
|
|
# FIXME Handle non existent item
|
|
my $olditemlost = $item->itemlost;
|
|
my @columns = Koha::Items->columns;
|
|
for my $c ( @columns ) {
|
|
if ( $c eq 'more_subfields_xml' ) {
|
|
my @more_subfields_xml = $input->multi_param("items.more_subfields_xml");
|
|
my @unlinked_item_subfields;
|
|
for my $subfield ( uniq @more_subfields_xml ) {
|
|
my @v = $input->multi_param('items.more_subfields_xml_' . encode_utf8($subfield));
|
|
push @unlinked_item_subfields, $subfield, $_ for @v;
|
|
}
|
|
if ( @unlinked_item_subfields ) {
|
|
my $marc = MARC::Record->new();
|
|
# use of tag 999 is arbitrary, and doesn't need to match the item tag
|
|
# used in the framework
|
|
$marc->append_fields(MARC::Field->new('999', ' ', ' ', @unlinked_item_subfields));
|
|
$marc->encoding("UTF-8");
|
|
$item->more_subfields_xml($marc->as_xml("USMARC"));
|
|
next;
|
|
}
|
|
$item->more_subfields_xml(undef);
|
|
} else {
|
|
my @v = $input->multi_param("items.".$c);
|
|
next unless @v;
|
|
$item->$c(join ' | ', uniq @v);
|
|
}
|
|
}
|
|
|
|
# check that the barcode don't exist already
|
|
if (
|
|
defined $item->barcode
|
|
&& Koha::Items->search(
|
|
{
|
|
barcode => $item->barcode,
|
|
itemnumber => { '!=' => $item->itemnumber }
|
|
}
|
|
)->count
|
|
)
|
|
{
|
|
# FIXME We shouldn't need that, ->store would explode as there is a unique constraint on items.barcode
|
|
push @errors,"barcode_not_unique";
|
|
$current_item = $item->unblessed; # Restore edit form for the same item
|
|
} else {
|
|
my $newitemlost = $item->itemlost;
|
|
if ( $newitemlost && $newitemlost ge '1' && !$olditemlost ) {
|
|
LostItem( $item->itemnumber, 'additem' );
|
|
}
|
|
$item->store;
|
|
}
|
|
|
|
$nextop="additem";
|
|
} elsif ($op eq "delinkitem"){
|
|
|
|
my $analyticfield = '773';
|
|
if ($marcflavour eq 'MARC21'){
|
|
$analyticfield = '773';
|
|
} elsif ($marcflavour eq 'UNIMARC') {
|
|
$analyticfield = '461';
|
|
}
|
|
foreach my $field ($record->field($analyticfield)){
|
|
if ($field->subfield('9') eq $hostitemnumber){
|
|
$record->delete_field($field);
|
|
last;
|
|
}
|
|
}
|
|
my $modbibresult = ModBiblio($record, $biblionumber,'');
|
|
}
|
|
|
|
# update OAI-PMH sets
|
|
if ($op) {
|
|
if (C4::Context->preference("OAI-PMH:AutoUpdateSets")) {
|
|
C4::OAI::Sets::UpdateOAISetsBiblio($biblionumber, $record);
|
|
}
|
|
}
|
|
|
|
#
|
|
#-------------------------------------------------------------------------------
|
|
# build screen with existing items. and "new" one
|
|
#-------------------------------------------------------------------------------
|
|
|
|
# now, build existiing item list
|
|
|
|
my @items;
|
|
for my $item ( $biblio->items->as_list, $biblio->host_items->as_list ) {
|
|
push @items, $item->columns_to_str;
|
|
}
|
|
|
|
my @witness_attributes = uniq map {
|
|
my $item = $_;
|
|
map { defined $item->{$_} && $item->{$_} ne "" ? $_ : () } keys %$item
|
|
} @items;
|
|
|
|
our ( $itemtagfield, $itemtagsubfield ) = GetMarcFromKohaField("items.itemnumber");
|
|
|
|
my $subfieldcode_attribute_mappings;
|
|
for my $subfield_code ( keys %{ $tagslib->{$itemtagfield} } ) {
|
|
|
|
my $subfield = $tagslib->{$itemtagfield}->{$subfield_code};
|
|
|
|
next if IsMarcStructureInternal( $subfield );
|
|
next unless $subfield->{tab} eq 10; # Is this really needed?
|
|
|
|
my $attribute;
|
|
if ( $subfield->{kohafield} ) {
|
|
( $attribute = $subfield->{kohafield} ) =~ s|^items\.||;
|
|
} else {
|
|
$attribute = $subfield_code; # It's in more_subfields_xml
|
|
}
|
|
next unless grep { $attribute eq $_ } @witness_attributes;
|
|
$subfieldcode_attribute_mappings->{$subfield_code} = $attribute;
|
|
}
|
|
|
|
my @header_value_loop = map {
|
|
{
|
|
header_value => $tagslib->{$itemtagfield}->{$_}->{lib},
|
|
attribute => $subfieldcode_attribute_mappings->{$_},
|
|
subfield_code => $_,
|
|
}
|
|
} sort keys %$subfieldcode_attribute_mappings;
|
|
|
|
# Using last created item if it exists
|
|
if ( $prefillitem
|
|
&& $op ne "additem"
|
|
&& $op ne "edititem"
|
|
&& $op ne "dupeitem" )
|
|
{
|
|
my $item_from_cookie = get_item_from_cookie($input);
|
|
$current_item = $item_from_cookie if $item_from_cookie;
|
|
}
|
|
|
|
if ( $current_item->{more_subfields_xml} ) {
|
|
# FIXME Use Maybe MARC::Record::new_from_xml if encoding issues on subfield (??)
|
|
$current_item->{marc_more_subfields_xml} = MARC::Record->new_from_xml($current_item->{more_subfields_xml}, 'UTF-8');
|
|
}
|
|
|
|
my $branchcode = $input->param('branch') || C4::Context->userenv->{branch};
|
|
|
|
# If we are not adding a new item
|
|
# OR
|
|
# If the subfield must be prefilled with last catalogued item
|
|
my @subfields_to_prefill;
|
|
if ( $nextop eq 'additem' && $prefillitem ) {
|
|
# Setting to 1 element if SubfieldsToUseWhenPrefill is empty to prevent all the subfields to be prefilled
|
|
@subfields_to_prefill = split(' ', C4::Context->preference('SubfieldsToUseWhenPrefill')) || ("");
|
|
}
|
|
|
|
# Getting list of subfields to keep when restricted editing is enabled
|
|
my @subfields_to_allow = $restrictededition ? split ' ', C4::Context->preference('SubfieldsToAllowForRestrictedEditing') : ();
|
|
|
|
my $subfields =
|
|
Koha::UI::Form::Builder::Item->new(
|
|
{ biblionumber => $biblionumber, item => $current_item } )->edit_form(
|
|
{
|
|
branchcode => $branchcode,
|
|
restricted_editition => $restrictededition,
|
|
(
|
|
@subfields_to_allow
|
|
? ( subfields_to_allow => \@subfields_to_allow )
|
|
: ()
|
|
),
|
|
(
|
|
@subfields_to_prefill
|
|
? ( subfields_to_prefill => \@subfields_to_prefill )
|
|
: ()
|
|
),
|
|
prefill_with_default_values => 1,
|
|
branch_limit => C4::Context->userenv->{"branch"},
|
|
}
|
|
);
|
|
|
|
if ( $frameworkcode eq 'FA' ) {
|
|
my ( $barcode_field ) = grep {$_->{kohafield} eq 'items.barcode'} @$subfields;
|
|
$barcode_field->{marc_value}->{value} ||= $input->param('barcode');
|
|
}
|
|
|
|
if( my $default_location = C4::Context->preference('NewItemsDefaultLocation') ) {
|
|
my ( $location_field ) = grep {$_->{kohafield} eq 'items.location'} @$subfields;
|
|
$location_field->{marc_value}->{value} ||= $default_location;
|
|
}
|
|
|
|
# what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
|
|
$template->param(
|
|
biblio => $biblio,
|
|
items => \@items,
|
|
item_header_loop => \@header_value_loop,
|
|
subfields => $subfields,
|
|
itemnumber => $itemnumber,
|
|
barcode => $current_item->{barcode},
|
|
op => $nextop,
|
|
popup => scalar $input->param('popup') ? 1: 0,
|
|
C4::Search::enabled_staff_search_views,
|
|
);
|
|
$template->{'VARS'}->{'searchid'} = $searchid;
|
|
|
|
if ($frameworkcode eq 'FA'){
|
|
# fast cataloguing datas
|
|
$template->param(
|
|
'circborrowernumber' => $fa_circborrowernumber,
|
|
'barcode' => $fa_barcode,
|
|
'branch' => $fa_branch,
|
|
'stickyduedate' => $fa_stickyduedate,
|
|
'duedatespec' => $fa_duedatespec,
|
|
);
|
|
}
|
|
|
|
foreach my $error (@errors) {
|
|
$template->param($error => 1);
|
|
}
|
|
output_html_with_http_headers $input, $cookie, $template->output;
|