From 5383f1ad946c77c85c01f898e3f5d9cad2e710ca Mon Sep 17 00:00:00 2001 From: Julian Maurice Date: Tue, 12 Nov 2019 15:21:15 +0100 Subject: [PATCH] Bug 24023: Add ability to create bundles of items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the ability to link items to a biblio to create bundles of items that can be reserved and checked out like a regular item. A withdrawn status will be set to linked items so that they can't be checked out while they are part of a bundle. When returning a bundle, the list of items that are part of this bundle will be displayed, allowing the librarian to verify that all items are there. Bundles can also be used in the inventory tool as a filter. Test plan: 0. Apply the patch, run updatedatabase + update_dbix_class_files 1. Create a biblio record with at least a title. Create an item for this biblio record 2. In the biblio 'Edit' menu you have a new option: 'Link elements'. Click on it 3. Enter a list of valid and invalid barcodes in the textarea, select a withdrawn status and click on 'Continue' 4. Verify that new fields have been added to the MARC record (462 for UNIMARC, 774 otherwise) 5. Verify that the withdrawn status has been correctly set for the items you have linked 6. Verify that, for item created at step 1, items.materials has been updated with the number of items contained in the bundle 7. Edit the biblio record and remove one of the 462/774 fields 8. Verify that the corresponding item's withdrawn status has been reset 9. Checkout and checkin the item created at step 1 10. You should see the list of items contained in the bundle. Click on 'Confirm checkin without verifying'. Verify that the item has been returned 11. Checkout and checkin the item created at step 1 again 12. Now click on 'Verify items bundle contents'. A textarea lets you enter barcodes. Enter some (but not all) barcodes that are in the bundle. Select a 'lost' status and click on 'Confirm checkin and mark missing items as lost' 13. Verify that the items corresponding to barcodes you did not type in the textarea are now marked as lost with the lost status you chose 14. Go to the inventory tool, use the bundle filter and verify that it works Sponsored-by: Bibliothèque Départementale de l'Yonne Signed-off-by: Séverine QUEUNE --- C4/Biblio.pm | 45 +++++++ C4/Items.pm | 6 + Koha/Template/Plugin/Biblio.pm | 9 ++ admin/columns_settings.yml | 11 ++ catalogue/detail.pl | 10 ++ catalogue/items-bundle-contents.pl | 53 ++++++++ cataloguing/linkelements.pl | 144 +++++++++++++++++++++ circ/returns.pl | 31 +++++ .../atomicupdate/bug-24023-items-bundles.perl | 37 ++++++ installer/data/mysql/kohastructure.sql | 44 +++++++ .../prog/en/includes/biblio-view-menu.inc | 7 + .../intranet-tmpl/prog/en/includes/cat-toolbar.inc | 1 + .../intranet-tmpl/prog/en/includes/item-status.inc | 84 ++++++++++++ .../prog/en/modules/catalogue/detail.tt | 9 ++ .../en/modules/catalogue/items-bundle-contents.tt | 79 +++++++++++ .../prog/en/modules/cataloguing/linkelements.tt | 91 +++++++++++++ .../intranet-tmpl/prog/en/modules/circ/returns.tt | 92 +++++++++++++ .../prog/en/modules/tools/inventory.tt | 20 ++- tools/inventory.pl | 10 ++ 19 files changed, 782 insertions(+), 1 deletion(-) create mode 100644 catalogue/items-bundle-contents.pl create mode 100755 cataloguing/linkelements.pl create mode 100644 installer/data/mysql/atomicupdate/bug-24023-items-bundles.perl create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/item-status.inc create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/items-bundle-contents.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/linkelements.tt diff --git a/C4/Biblio.pm b/C4/Biblio.pm index f6c89f92e8..0251481a07 100644 --- a/C4/Biblio.pm +++ b/C4/Biblio.pm @@ -2978,6 +2978,8 @@ sub ModBiblioMarc { $m_rs->metadata( $record->as_xml_record($encoding) ); $m_rs->store; + UpdateItemsBundles($biblionumber, $record); + my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX }); $indexer->index_records( $biblionumber, "specialUpdate", "biblioserver" ); @@ -3214,6 +3216,49 @@ sub RemoveAllNsb { return $record; } +=head2 UpdateItemsBundles + +Remove from items bundle the items that were removed from the MARC record (keep +the table in sync with the MARC record) + +This is called by ModBiblio + + UpdateItemsBundles($biblionumber, $record) + +=cut + +sub UpdateItemsBundles { + my ($biblionumber, $record) = @_; + + my $schema = Koha::Database->new()->schema(); + my $items_bundle_rs = $schema->resultset('ItemsBundle'); + my $items_rs = $schema->resultset('Item'); + my $items_bundle = $items_bundle_rs->find({ biblionumber => $biblionumber }); + return unless $items_bundle; + + my $tag = C4::Context->preference('marcflavour') eq 'UNIMARC' ? '462' : '774'; + my @fields = $record->field($tag); + my %fields_by_itemnumber = map { (scalar $_->subfield('9')) // '' => $_ } @fields; + + foreach my $items_bundle_item ($items_bundle->items_bundle_items) { + my $itemnumber = $items_bundle_item->get_column('itemnumber'); + if (not exists $fields_by_itemnumber{$itemnumber}) { + $items_bundle_item->itemnumber->update({ withdrawn => 0 }); + $items_bundle_item->delete(); + } + } + + my $items_count = $items_bundle->items_bundle_items->count(); + # If bundle is empty, delete it + if ($items_count == 0) { + $items_bundle->delete(); + } + + my $materials = $items_count || undef; + $items_rs->search({ biblionumber => $biblionumber }) + ->update_all({ materials => $materials }); +} + 1; diff --git a/C4/Items.pm b/C4/Items.pm index aecf96af1f..87f9793b57 100644 --- a/C4/Items.pm +++ b/C4/Items.pm @@ -523,6 +523,7 @@ sub GetItemsForInventory { my $minlocation = $parameters->{'minlocation'} // ''; my $maxlocation = $parameters->{'maxlocation'} // ''; my $class_source = $parameters->{'class_source'} // C4::Context->preference('DefaultClassificationSource'); + my $items_bundle = $parameters->{'items_bundle'} // ''; my $location = $parameters->{'location'} // ''; my $itemtype = $parameters->{'itemtype'} // ''; my $ignoreissued = $parameters->{'ignoreissued'} // ''; @@ -569,6 +570,11 @@ sub GetItemsForInventory { push @bind_params, $max_cnsort; } + if ($items_bundle) { + push @where_strings, 'items.itemnumber IN (SELECT itemnumber FROM items_bundle_item WHERE items_bundle_id = ?)'; + push @bind_params, $items_bundle; + } + if ($datelastseen) { $datelastseen = output_pref({ str => $datelastseen, dateformat => 'iso', dateonly => 1 }); push @where_strings, '(datelastseen < ? OR datelastseen IS NULL)'; diff --git a/Koha/Template/Plugin/Biblio.pm b/Koha/Template/Plugin/Biblio.pm index e9d7a73009..df3da0b0cf 100644 --- a/Koha/Template/Plugin/Biblio.pm +++ b/Koha/Template/Plugin/Biblio.pm @@ -63,4 +63,13 @@ sub CanArticleRequest { return $biblio ? $biblio->can_article_request( $borrower ) : 0; } +sub IsItemsBundle { + my ($self, $biblionumber) = @_; + + my $items_bundle_rs = Koha::Database->new()->schema()->resultset('ItemsBundle'); + my $count = $items_bundle_rs->search({ biblionumber => $biblionumber })->count; + + return $count > 0; +} + 1; diff --git a/admin/columns_settings.yml b/admin/columns_settings.yml index df9290d727..e99295bce4 100644 --- a/admin/columns_settings.yml +++ b/admin/columns_settings.yml @@ -432,6 +432,17 @@ modules: - columnname: checkin_on + items-bundle-contents: + items-bundle-contents-table: + columns: + - columnname: title + - columnname: author + - columnname: ccode + - columnname: itemtype + - columnname: callnumber + - columnname: barcode + - columnname: status + cataloguing: additem: itemst: diff --git a/catalogue/detail.pl b/catalogue/detail.pl index 7e8d05d48d..3bb29c5440 100755 --- a/catalogue/detail.pl +++ b/catalogue/detail.pl @@ -44,6 +44,7 @@ use Koha::AuthorisedValues; use Koha::Biblios; use Koha::CoverImages; use Koha::Illrequests; +use Koha::Database; use Koha::Items; use Koha::ItemTypes; use Koha::Patrons; @@ -51,6 +52,8 @@ use Koha::Virtualshelves; use Koha::Plugins; use Koha::SearchEngine::Search; +my $schema = Koha::Database->new()->schema(); + my $query = CGI->new(); my $analyze = $query->param('analyze'); @@ -411,6 +414,12 @@ foreach my $item (@items) { $item->{cover_images} = $item_object->cover_images; } + my $items_bundle_item = $schema->resultset('ItemsBundleItem')->find({ itemnumber => $item->{itemnumber} }); + if ($items_bundle_item) { + $itemfields{items_bundle} = 1; + $item->{items_bundle} = $items_bundle_item->items_bundle; + } + if ($currentbranch and C4::Context->preference('SeparateHoldings')) { if ($itembranchcode and $itembranchcode eq $currentbranch) { push @itemloop, $item; @@ -468,6 +477,7 @@ $template->param( itemdata_stocknumber => $itemfields{stocknumber}, volinfo => $itemfields{enumchron}, itemdata_itemnotes => $itemfields{itemnotes}, + itemdata_items_bundle => $itemfields{items_bundle}, itemdata_nonpublicnotes => $itemfields{itemnotes_nonpublic}, z3950_search_params => C4::Search::z3950_search_args($dat), hostrecords => $hostrecords, diff --git a/catalogue/items-bundle-contents.pl b/catalogue/items-bundle-contents.pl new file mode 100644 index 0000000000..6969a64014 --- /dev/null +++ b/catalogue/items-bundle-contents.pl @@ -0,0 +1,53 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use CGI qw(-utf8); +use C4::Auth; +use C4::Output; + +use Koha::Database; +use Koha::Biblios; +use Koha::Items; + +my $schema = Koha::Database->new()->schema(); + +my $cgi = CGI->new(); + +my ($template, $borrowernumber, $cookie) = get_template_and_user({ + template_name => 'catalogue/items-bundle-contents.tt', + query => $cgi, + type => 'intranet', + authnotrequired => 0, + flagsrequired => { catalogue => 1 }, +}); + +my $biblionumber = $cgi->param('biblionumber'); +my $biblio = Koha::Biblios->find($biblionumber); + +my $items_bundle_rs = $schema->resultset('ItemsBundle'); +my $items_bundle = $items_bundle_rs->find({ biblionumber => $biblionumber }); +my @items = map { scalar Koha::Items->find($_->get_column('itemnumber')) } $items_bundle->items_bundle_items; + +$template->param( + biblionumber => $biblionumber, + biblio => $biblio, + items => \@items, +); + +output_html_with_http_headers $cgi, $cookie, $template->output; diff --git a/cataloguing/linkelements.pl b/cataloguing/linkelements.pl new file mode 100755 index 0000000000..320886b255 --- /dev/null +++ b/cataloguing/linkelements.pl @@ -0,0 +1,144 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use CGI qw ( -utf8 ); +use Encode; +use List::MoreUtils qw/uniq/; +use MARC::Record; + +use Koha::Biblios; +use Koha::Items; +use Koha::Database; + +use C4::Auth; +use C4::Output; +use C4::Biblio; +use C4::Context; +use C4::Items; + +my $cgi = new CGI; + +my ($template, $loggedinuser, $cookie) = get_template_and_user({ + template_name => 'cataloguing/linkelements.tt', + query => $cgi, + type => 'intranet', + authnotrequired => 0, + flagsrequired => { editcatalogue => 'edit_catalogue' }, +}); + +my $biblionumber = $cgi->param('biblionumber'); +my $sessionId = $cgi->cookie('CGISESSID'); +my $session = C4::Auth::get_session($sessionId); +my $schema = Koha::Database->new()->schema(); +my $items_bundle_rs = $schema->resultset('ItemsBundle'); +my $items_bundle_item_rs = $schema->resultset('ItemsBundleItem'); + +if ($cgi->request_method() eq 'POST') { + my $barcodelist = $cgi->param('barcodelist'); + my $withdrawn = $cgi->param('withdrawn'); + my @barcodes = uniq( split(/\s*\n\s*/, $barcodelist) ); + my @items; + my @notfound_barcodes; + my @found_barcodes; + my @alreadylinked_barcodes; + foreach my $barcode (@barcodes) { + my $item = Koha::Items->find( { barcode => $barcode } ); + if ($item) { + my $items_bundle_item = $items_bundle_item_rs->find({ itemnumber => $item->itemnumber }); + if ($items_bundle_item && $items_bundle_item->items_bundle->get_column('biblionumber') ne $biblionumber) { + # This item is already linked to another biblio + push @alreadylinked_barcodes, $barcode + } else { + push @items, $item; + push @found_barcodes, $barcode; + } + } else { + push @notfound_barcodes, $barcode; + } + } + + if (@items) { + my $biblio = Koha::Biblios->find($biblionumber); + my $record = GetMarcBiblio({ biblionumber => $biblionumber }); + my $tag = C4::Context->preference('marcflavour') eq 'UNIMARC' ? '462' : '774'; + foreach my $item (@items) { + # Remove fields that references the same itemnumber + # to avoid duplicates + my @fields = $record->field($tag); + my @duplicate_fields = grep { + $_->subfield('9') eq $item->itemnumber; + } @fields; + $record->delete_fields(@duplicate_fields); + + my $field = MARC::Field->new($tag, '', '', + 't' => $item->biblio->title, + '0' => $item->biblionumber, + '9' => $item->itemnumber); + $record->insert_fields_ordered($field); + + my $items_bundle = $items_bundle_rs->find_or_create({ biblionumber => $biblionumber }); + $items_bundle_item_rs->find_or_create({ + items_bundle_id => $items_bundle->items_bundle_id, + itemnumber => $item->itemnumber, + }); + + if (defined $withdrawn and $withdrawn ne '') { + $item->withdrawn($withdrawn)->store(); + } + } + ModBiblio($record, $biblionumber, $biblio->frameworkcode); + } + + # Store barcodes in the session to display messages after the redirect + $session->param('linkelements_notfound_barcodes', \@notfound_barcodes); + $session->param('linkelements_found_barcodes', \@found_barcodes); + $session->param('linkelements_alreadylinked_barcodes', \@alreadylinked_barcodes); + $session->flush(); + + print $cgi->redirect('/cgi-bin/koha/cataloguing/linkelements.pl?biblionumber=' . $biblionumber); + exit; +} + +my @notfound_barcodes = map { + Encode::is_utf8($_) ? $_ : Encode::decode('UTF-8', $_) +} @{ $session->param('linkelements_notfound_barcodes') // [] }; + +my @found_barcodes = map { + Encode::is_utf8($_) ? $_ : Encode::decode('UTF-8', $_) +} @{ $session->param('linkelements_found_barcodes') // [] }; + +my @alreadylinked_barcodes = map { + Encode::is_utf8($_) ? $_ : Encode::decode('UTF-8', $_) +} @{ $session->param('linkelements_alreadylinked_barcodes') // [] }; + +$session->clear([ + 'linkelements_notfound_barcodes', + 'linkelements_found_barcodes', + 'linkelements_alreadylinked_barcodes', +]); +$session->flush(); + +$template->param( + biblionumber => $biblionumber, + notfound_barcodes => \@notfound_barcodes, + found_barcodes => \@found_barcodes, + alreadylinked_barcodes => \@alreadylinked_barcodes, +); + +output_html_with_http_headers $cgi, $cookie, $template->output; diff --git a/circ/returns.pl b/circ/returns.pl index b58e340f85..5542bea3f0 100755 --- a/circ/returns.pl +++ b/circ/returns.pl @@ -280,6 +280,37 @@ if ($barcode) { additional_materials => $materials, issue => $checkout, ); + + my $items_bundle_rs = Koha::Database->new->schema->resultset('ItemsBundle'); + my $items_bundle = $items_bundle_rs->find({ biblionumber => $biblio->biblionumber }); + if ($items_bundle) { + my @items_bundle_items = map { + scalar Koha::Items->find($_->get_column('itemnumber')) + } $items_bundle->items_bundle_items; + + my $confirm = $query->param('confirm_items_bundle_return'); + unless ($confirm) { + $template->param( + items_bundle_return_confirmation => 1, + items_bundle_items => \@items_bundle_items, + ); + + output_html_with_http_headers $query, $cookie, $template->output; + exit; + } + + my $mark_missing_items_as_lost = $query->param('confirm_items_bundle_mark_missing_items_as_lost'); + if ($mark_missing_items_as_lost) { + my $barcodes = $query->param('verify-items-bundle-contents-barcodes'); + my $lost = $query->param('verify-items-bundle-contents-lost'); + my @barcodes = map { s/^\s+|\s+$//gr } (split /\n/, $barcodes); + foreach my $item (@items_bundle_items) { + unless (grep { $_ eq $item->barcode } @barcodes) { + $item->itemlost($lost)->store(); + } + } + } + } } # FIXME else we should not call AddReturn but set BadBarcode directly instead my %input = ( diff --git a/installer/data/mysql/atomicupdate/bug-24023-items-bundles.perl b/installer/data/mysql/atomicupdate/bug-24023-items-bundles.perl new file mode 100644 index 0000000000..2e6b7dc431 --- /dev/null +++ b/installer/data/mysql/atomicupdate/bug-24023-items-bundles.perl @@ -0,0 +1,37 @@ +$DBversion = 'XXX'; +if( CheckVersion( $DBversion ) ) { + $dbh->do(q{ + CREATE TABLE IF NOT EXISTS items_bundle ( + items_bundle_id SERIAL, + biblionumber INT NOT NULL, + CONSTRAINT pk_items_bundle_items_bundle_id + PRIMARY KEY (items_bundle_id), + CONSTRAINT fk_items_bundle_biblio_biblionumber + FOREIGN KEY (biblionumber) REFERENCES biblio (biblionumber) + ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT uniq_items_bundle_biblionumber + UNIQUE KEY (biblionumber) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci + }); + + $dbh->do(q{ + CREATE TABLE IF NOT EXISTS items_bundle_item ( + items_bundle_item_id SERIAL, + items_bundle_id BIGINT UNSIGNED NOT NULL, + itemnumber INT NOT NULL, + CONSTRAINT pk_items_bundle_item_items_bundle_item_id + PRIMARY KEY (items_bundle_item_id), + CONSTRAINT fk_items_bundle_item_itemnumber + FOREIGN KEY (itemnumber) REFERENCES items (itemnumber) + ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT fk_items_bundle_item_items_bundle_id + FOREIGN KEY (items_bundle_id) REFERENCES items_bundle (items_bundle_id) + ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT uniq_items_bundle_item_itemnumber + UNIQUE KEY (itemnumber) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci + }); + + SetVersion( $DBversion ); + print "Upgrade to $DBversion done (Bug 24023 - Items bundles)\n"; +} diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index d391665136..9d5e09a5ae 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -3069,6 +3069,50 @@ CREATE TABLE `items` ( /*!40101 SET character_set_client = @saved_cs_client */; -- +-- Table structure for table items_bundle +-- + +DROP TABLE IF EXISTS items_bundle; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE items_bundle ( + items_bundle_id SERIAL, + biblionumber INT NOT NULL, + CONSTRAINT pk_items_bundle_items_bundle_id + PRIMARY KEY (items_bundle_id), + CONSTRAINT fk_items_bundle_biblio_biblionumber + FOREIGN KEY (biblionumber) REFERENCES biblio (biblionumber) + ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT uniq_items_bundle_biblionumber + UNIQUE KEY (biblionumber) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table items_bundle_item +-- + +DROP TABLE IF EXISTS items_bundle_item; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE items_bundle_item ( + items_bundle_item_id SERIAL, + items_bundle_id BIGINT UNSIGNED NOT NULL, + itemnumber INT NOT NULL, + CONSTRAINT pk_items_bundle_item_items_bundle_item_id + PRIMARY KEY (items_bundle_item_id), + CONSTRAINT fk_items_bundle_item_itemnumber + FOREIGN KEY (itemnumber) REFERENCES items (itemnumber) + ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT fk_items_bundle_item_items_bundle_id + FOREIGN KEY (items_bundle_id) REFERENCES items_bundle (items_bundle_id) + ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT uniq_items_bundle_item_itemnumber + UNIQUE KEY (itemnumber) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- -- Table structure for table `items_last_borrower` -- diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/biblio-view-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/biblio-view-menu.inc index 2080b07707..62a3f5bf20 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/biblio-view-menu.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/biblio-view-menu.inc @@ -1,5 +1,6 @@ [% USE Koha %] [% USE Biblio %] +[% PROCESS 'i18n.inc' %] [% SET biblio_object_id = object || biblionumber || biblio.biblionumber %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/cat-toolbar.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/cat-toolbar.inc index a467c7a27f..ba7ea511ad 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/cat-toolbar.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/cat-toolbar.inc @@ -63,6 +63,7 @@ CAN_user_serials_create_subscription ) %] [% IF ( CAN_user_editcatalogue_edit_catalogue ) %]
  • Edit as new (duplicate)
  • +
  • Link elements
  • Replace record via Z39.50/SRU
  • [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/item-status.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/item-status.inc new file mode 100644 index 0000000000..ca6dc1d1ff --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/item-status.inc @@ -0,0 +1,84 @@ +[% USE AuthorisedValues %] +[% USE Branches %] +[% USE Koha %] +[% USE KohaDates %] +[% PROCESS 'i18n.inc' %] +[% transfer = item.get_transfer() %] +[% IF item.checkout %] + + [% IF item.checkout.onsite_checkout %] + [% t('Currently in local use by') | html %] + [% ELSE %] + [% t('Checked out to') | html %] + [% END %] + [% INCLUDE 'patron-title.inc' patron=item.checkout.patron hide_patron_infos_if_needed=1 %] + : [% tx('due {date_due}', { date_due = item.checkout.date_due }) | html %] + +[% ELSIF transfer %] + [% datesent = BLOCK %][% transfer.datesent | $KohaDates %][% END %] + [% tx('In transit from {frombranch} to {tobranch} since {datesent}', { frombranch = Branches.GetName(transfer.frombranch), tobranch = Branches.GetName(transfer.tobranch), datesent = datesent }) %] +[% END %] + +[% IF item.itemlost %] + [% itemlost_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.itemlost', authorised_value = item.itemlost }) %] + [% IF itemlost_description %] + [% itemlost_description | html %] + [% ELSE %] + [% t('Unavailable (lost or missing)') | html %] + [% END %] +[% END %] + +[% IF item.withdrawn %] + [% withdrawn_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.withdrawn', authorised_value = item.withdrawn }) %] + [% IF withdrawn_description %] + [% withdrawn_description | html %] + [% ELSE %] + [% t('Withdrawn') | html %] + [% END %] +[% END %] + +[% IF item.damaged %] + [% damaged_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.damaged', authorised_value = item.damaged }) %] + [% IF damaged_description %] + [% damaged_description | html %] + [% ELSE %] + [% t('Damaged') | html %] + [% END %] +[% END %] + +[% IF item.notforloan || item.effective_itemtype.notforloan %] + + [% t('Not for loan') | html %] + [% notforloan_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.notforloan', authorised_value = item.notforloan }) %] + [% IF notforloan_description %] + ([% notforloan_description | html %]) + [% END %] + +[% END %] + +[% hold = item.holds.next %] +[% IF hold %] + + [% IF hold.waitingdate %] + Waiting at [% Branches.GetName(hold.get_column('branchcode')) | html %] since [% hold.waitingdate | $KohaDates %]. + [% ELSE %] + Item-level hold (placed [% hold.reservedate | $KohaDates %]) for delivery at [% Branches.GetName(hold.get_column('branchcode')) | html %]. + [% END %] + [% IF Koha.Preference('canreservefromotherbranches') %] + [% t('Hold for:') | html %] + [% INCLUDE 'patron-title.inc' patron=hold.borrower hide_patron_infos_if_needed=1 %] + [% END %] + +[% END %] +[% UNLESS item.notforloan || item.effective_itemtype.notforloan || item.onloan || item.itemlost || item.withdrawn || item.damaged || transfer || hold %] + [% t('Available') | html %] +[% END %] + +[% IF ( item.restricted ) %] + [% restricted_description = AuthorisedValues.GetDescriptionByKohaField({ kohafield = 'items.restricted', authorised_value = item.restricted }) %] + [% IF restricted_description %] + ([% restricted_description | html %]) + [% ELSE %] + [% t('Restricted') | html %] + [% END %] +[% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt index 90000082c5..c11f931f3e 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt @@ -332,6 +332,7 @@ [% IF ( hostrecords ) %]Host records[% END %] [% IF ( analyze ) %]Used in[% END %] [% IF ( ShowCourseReserves ) %]Course reserves[% END %] + [% IF itemdata_items_bundle %]Items bundle[% END %] [% IF ( SpineLabelShowPrintOnBibDetails ) %]Spine label[% END %] [% IF ( CAN_user_editcatalogue_edit_items ) %] [% END %] @@ -562,6 +563,14 @@ Note that permanent location is a code, and location may be an authval. [% END %] + [% IF itemdata_items_bundle %] + + [% IF item.items_bundle.biblionumber %] + [% INCLUDE 'biblio-title.inc' biblio = item.items_bundle.biblionumber link = 1 %] + [% END %] + + [% END %] + [% IF ( SpineLabelShowPrintOnBibDetails ) %] Print label [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/items-bundle-contents.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/items-bundle-contents.tt new file mode 100644 index 0000000000..15653af7d4 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/items-bundle-contents.tt @@ -0,0 +1,79 @@ +[% USE raw %] +[% USE Asset %] +[% USE AuthorisedValues %] +[% USE ItemTypes %] +[% USE TablesSettings %] + +[% INCLUDE 'doc-head-open.inc' %] +Koha › Catalog › Items bundle contents for [% INCLUDE 'biblio-title-head.inc' %] +[% INCLUDE 'doc-head-close.inc' %] + + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + + +
    +
    +
    + [% INCLUDE 'cat-toolbar.inc' %] + +

    Items bundle contents for [% INCLUDE 'biblio-title.inc' %]

    + +
    + + + + + + + + + + + + + + [% FOREACH item IN items %] + + + + + + + + + + [% END %] + +
    TitleAuthorCollection codeItem typeCallnumberBarcodeStatus
    [% INCLUDE 'biblio-title.inc' biblio=item.biblio link = 1 %][% item.biblio.author | html %][% AuthorisedValues.GetByCode('CCODE', item.ccode) | html %][% ItemTypes.GetDescription(item.itype) | html %][% item.itemcallnumber | html %][% item.barcode | html %][% INCLUDE 'item-status.inc' item=item %]
    +
    +
    + +
    + +
    +
    + + [% Asset.js('js/table_filters.js') | $raw %] + [% Asset.js('lib/jquery/plugins/jquery.dataTables.columnFilter.js') | $raw %] + [% INCLUDE 'datatables.inc' %] + [% INCLUDE 'columns_settings.inc' %] + + +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/linkelements.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/linkelements.tt new file mode 100644 index 0000000000..cf5cb0a7d0 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/linkelements.tt @@ -0,0 +1,91 @@ +[% USE AuthorisedValues %] +[% PROCESS 'i18n.inc' %] +[% INCLUDE 'doc-head-open.inc' %] +Link elements together +[% INCLUDE 'doc-head-close.inc' %] + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + + +[% BLOCK barcodes_table %] + + + + + + + + [% FOREACH barcode IN barcodes %] + + + + [% END %] + +
    [% t('Barcode') | html %]
    [% barcode | html %]
    +[% END %] + +
    +
    +
    +
    +

    [% t('Link elements to the biblio') | html %]

    + + [% IF ( notfound_barcodes ) %] +
    +

    [% t('Warning: the following barcodes were not found:') | html %]

    + [% PROCESS barcodes_table barcodes = notfound_barcodes %] +
    + [% END %] + + [% IF ( alreadylinked_barcodes ) %] +
    +

    [% t('Warning: the following barcodes are already linked to another biblio') | html %]

    + [% PROCESS barcodes_table barcodes = alreadylinked_barcodes %] +
    + [% END %] + + [% IF found_barcodes %] +
    +

    [% t('The following barcodes were linked:') | html %]

    + + [% PROCESS barcodes_table barcodes = found_barcodes %] +
    + [% END %] + +
    +
    + [% t('Items') | html %] +
      +
    1. + + +
    2. +
    3. + + +
    4. +
    + +
    + +
    + + Cancel +
    +
    +
    +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt index 6c8eab60e0..dc4240544c 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt @@ -6,6 +6,7 @@ [% USE ItemTypes %] [% USE AuthorisedValues %] [% USE TablesSettings %] +[% PROCESS 'i18n.inc' %] [% PROCESS 'member-display-address-style.inc' %] [% SET footerjs = 1 %] [% BLOCK display_bormessagepref %] @@ -736,6 +737,72 @@ [% INCLUDE all_checkin_messages %]
    + [% IF items_bundle_return_confirmation %] +
    [% t('The returned item is an items bundle and needs confirmation') | html %]
    + + + + + + + + + + + + + + + [% FOREACH item IN items_bundle_items %] + + + + + + + + + + [% END %] + +
    [% t('Title') | html %][% t('Author') | html %][% t('Collection code') | html %][% t('Item type') | html %][% t('Callnumber') | html %][% t('Barcode') | html %][% t('Status') | html %]
    [% INCLUDE 'biblio-title.inc' biblio=item.biblio link = 1 %][% item.biblio.author | html %][% AuthorisedValues.GetByCode('CCODE', item.ccode) | html %][% ItemTypes.GetDescription(item.itype) | html %][% item.itemcallnumber | html %][% item.barcode | html %][% INCLUDE 'item-status.inc' item=item %]
    + +
    +
    + + + +
    + + +
    + +
    +
    +
    + + +
    [% t('Scan barcodes found in the items bundle and compare it to the table above') | html %]
    +
    + +
    + + +
    [% t('If some items are missing, they will be marked as lost with this status') | html %]
    +
    + + + + + +
    +
    + [% END %] +
    @@ -1163,6 +1230,31 @@ }); + + [% END %] [% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/inventory.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/inventory.tt index 096279bbf4..df907aecbd 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/inventory.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/inventory.tt @@ -133,6 +133,17 @@ [% END %] + [% IF items_bundles.size > 0 %] +
  • + + +
  • + [% END %]
    @@ -348,7 +359,8 @@ $('#locationloop').val() || $('#minlocation').val() || $('#maxlocation').val() || - $('#statuses input:checked').length + $('#statuses input:checked').length || + $('#items_bundle').val() ) ) { return confirm( _("You have not selected any catalog filters and are about to compare a file of barcodes to your entire catalog.") + "\n\n" + @@ -488,6 +500,12 @@ }); }); + [% INCLUDE 'select2.inc' %] + [% END %] [% INCLUDE 'intranet-bottom.inc' %] diff --git a/tools/inventory.pl b/tools/inventory.pl index e82e77fe99..6daf4c5aa5 100755 --- a/tools/inventory.pl +++ b/tools/inventory.pl @@ -48,6 +48,7 @@ my $minlocation=$input->param('minlocation') || ''; my $maxlocation=$input->param('maxlocation'); my $class_source=$input->param('class_source'); $maxlocation=$minlocation.'Z' unless ( $maxlocation || ! $minlocation ); +my $items_bundle = $input->param('items_bundle'); my $location=$input->param('location') || ''; my $ignoreissued=$input->param('ignoreissued'); my $ignore_waiting_holds = $input->param('ignore_waiting_holds'); @@ -68,6 +69,12 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user( } ); +my $schema = Koha::Database->new()->schema(); +my $items_bundle_rs = $schema->resultset('ItemsBundle'); +my @items_bundles = $items_bundle_rs->search(undef, { + order_by => { -asc => ['biblionumber.title'] }, + join => ['biblionumber'], +}); my @authorised_value_list; my $authorisedvalue_categories = ''; @@ -136,6 +143,7 @@ foreach my $itemtype ( @itemtypes ) { $template->param( authorised_values => \@authorised_value_list, + items_bundles => \@items_bundles, today => dt_from_string, minlocation => $minlocation, maxlocation => $maxlocation, @@ -251,6 +259,7 @@ if ( $op && ( !$uploadbarcodes || $compareinv2barcd )) { minlocation => $minlocation, maxlocation => $maxlocation, class_source => $class_source, + items_bundle => $items_bundle, location => $location, ignoreissued => $ignoreissued, datelastseen => $datelastseen, @@ -269,6 +278,7 @@ if( @scanned_items ) { maxlocation => $maxlocation, class_source => $class_source, location => $location, + items_bundle => $items_bundle, ignoreissued => undef, datelastseen => undef, branchcode => $branchcode, -- 2.11.0