@@ -, +, @@ biblio record Click on it withdrawn status and click on 'Continue' UNIMARC, 774 otherwise) you have linked updated with the number of items contained in the bundle 'Confirm checkin without verifying'. Verify that the item has been returned 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' the textarea are now marked as lost with the lost status you chose works --- 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 + .../prog/en/includes/cat-toolbar.inc | 1 + .../prog/en/includes/item-status.inc | 84 ++++++++++ .../prog/en/modules/catalogue/detail.tt | 9 ++ .../catalogue/items-bundle-contents.tt | 79 ++++++++++ .../en/modules/cataloguing/linkelements.tt | 91 +++++++++++ .../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 --- a/C4/Biblio.pm +++ a/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; --- a/C4/Items.pm +++ a/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)'; --- a/Koha/Template/Plugin/Biblio.pm +++ a/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; --- a/admin/columns_settings.yml +++ a/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: --- a/catalogue/detail.pl +++ a/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; @@ -469,6 +478,7 @@ $template->param( itemdata_publisheddate => $itemfields{publisheddate}, 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, --- a/catalogue/items-bundle-contents.pl +++ a/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; --- a/cataloguing/linkelements.pl +++ a/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; --- a/circ/returns.pl +++ a/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 = ( --- a/installer/data/mysql/atomicupdate/bug-24023-items-bundles.perl +++ a/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"; +} --- a/installer/data/mysql/kohastructure.sql +++ a/installer/data/mysql/kohastructure.sql @@ -3068,6 +3068,50 @@ CREATE TABLE `items` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!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` -- --- a/koha-tmpl/intranet-tmpl/prog/en/includes/biblio-view-menu.inc +++ a/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 %] --- a/koha-tmpl/intranet-tmpl/prog/en/includes/cat-toolbar.inc +++ a/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 %] --- a/koha-tmpl/intranet-tmpl/prog/en/includes/item-status.inc +++ a/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 %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt @@ -336,6 +336,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 %] @@ -567,6 +568,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 %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/items-bundle-contents.tt +++ a/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' %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/linkelements.tt +++ a/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' %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt +++ a/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' %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/inventory.tt +++ a/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" + @@ -489,6 +501,12 @@ }); }); + [% INCLUDE 'select2.inc' %] + [% END %] [% INCLUDE 'intranet-bottom.inc' %] --- a/tools/inventory.pl +++ a/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, --