From bcda3de678af1461b862275c5658de9c28f88f93 Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Fri, 28 Feb 2020 11:32:01 -0500 Subject: [PATCH] Bug 24860: Add ability to select a volume when placing a hold Signed-off-by: Andrew Fuerste-Henry Signed-off-by: Your Full Name Signed-off-by: Rebecca Coert --- Koha/Hold.pm | 16 +++++ Koha/Holds.pm | 3 + .../intranet-tmpl/prog/en/includes/holds_table.inc | 2 + .../prog/en/modules/reserve/request.tt | 78 ++++++++++++++++++++-- reserve/placerequest.pl | 2 + reserve/request.pl | 7 ++ t/db_dependent/Koha/Holds.t | 28 +++++++- 7 files changed, 128 insertions(+), 8 deletions(-) diff --git a/Koha/Hold.pm b/Koha/Hold.pm index 8969e32839..a285c83a60 100644 --- a/Koha/Hold.pm +++ b/Koha/Hold.pm @@ -291,6 +291,22 @@ sub biblio { return $self->{_biblio}; } +=head3 volume + +Returns the related Koha::Biblio::Volume object for this hold + +=cut + +sub volume { + my ($self) = @_; + + my $volume_rs = $self->_result->volume; + + return unless $volume_rs; + + return Koha::Biblio::Volume->_new_from_dbic($volume_rs); +} + =head3 item Returns the related Koha::Item object for this Hold diff --git a/Koha/Holds.pm b/Koha/Holds.pm index 604953d4c9..e34921bcb4 100644 --- a/Koha/Holds.pm +++ b/Koha/Holds.pm @@ -86,6 +86,9 @@ sub forced_hold_level { my $item_level_count = $self->search( { itemnumber => { '!=' => undef } } )->count(); return 'item' if $item_level_count > 0; + my $volume_level_count = $self->search( { volume_id => { '!=' => undef } } )->count(); + return 'volume' if $volume_level_count > 0; + my $record_level_count = $self->search( { itemnumber => undef } )->count(); return 'record' if $record_level_count > 0; diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/holds_table.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/holds_table.inc index 600977a054..42843652e4 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/holds_table.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/holds_table.inc @@ -163,6 +163,8 @@ [% ELSE %] [% IF hold.itemtype %] Next available [% ItemTypes.GetDescription( hold.itemtype ) | html %] item + [% ELSIF hold.object.volume_id %] + Next available item from volume [% hold.object.volume.description | html %] [% ELSE %] Next available [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt index 44c46d2395..726436d376 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt @@ -451,7 +451,7 @@ [% UNLESS ( multi_hold ) %]
  • - [% IF force_hold_level == 'item' %] + [% IF force_hold_level == 'item' || force_hold_level == 'volume' %] [% ELSIF force_hold_level == 'record' %] @@ -471,6 +471,41 @@ [% ELSE %] [% END %] + + + [% FOREACH bibitemloo IN bibitemloop %] + [% IF Koha.Preference('EnableVolumeHolds') && bibitemloo.volumes.count %] + + + [% IF bibitemloo.force_hold_level == 'record' # Patron has placed a record level hold previously for this record %] + + + Hold must be record level + + [% ELSIF bibitemloo.force_hold_level == 'item' # Patron has placed an item level hold previously for this record %] + + + Hold must be item level + + [% ELSE %] + [% FOREACH v IN bibitemloo.volumes %] + [% IF v.items %] +
  • + + +
  • + [% END %] + [% END %] + [% END %] + [% END %] + [% END %] + + [% END # /UNLESS multi_hold %] @@ -519,6 +554,9 @@ Item type [% END %] Barcode + [% IF Koha.Preference('EnableVolumeHolds') && bibitemloo.volumes.count %] + Volume + [% END %] Home library Last location [% IF itemdata_ccode %] @@ -544,6 +582,11 @@ Hold must be record level + [% ELSIF itemloo.force_hold_level == 'volume' # Patron has placed a volume level hold previously for this record %] + + + Hold must be volume level + [% ELSIF ( itemloo.available ) %] [% ELSIF ( itemloo.override ) %] @@ -591,6 +634,11 @@ [% itemloo.barcode | html %] + [% IF Koha.Preference('EnableVolumeHolds') && bibitemloo.volumes.count %] + + [% itemloo.object.volume.description | html %] + + [% END %] [% Branches.GetName( itemloo.homebranch ) | html %] @@ -1028,6 +1076,8 @@ }; if($('input[name="checkitem"]:checked').length) data.item_id = $('input[name="checkitem"]:checked').val(); + if($('input[name="volume_id"]:checked').length) + data.volume_id = $('input[name="volume_id"]:checked').val(); if($('input[name="borrowernumber"]').length) data.patron_id = $('input[name="borrowernumber"]').val(); if($('textarea[name="notes"]').length) @@ -1047,12 +1097,14 @@ const count = $('input[name="holds_to_place_count"]').length?$('input[name="holds_to_place_count"]').val():1; biblionumbers.forEach(function(biblionumber) { data.biblio_id = biblionumber; + console.log(data); let options = { url: $t.attr('action'), method: $t.attr('method').toUpperCase(), contentType: 'application/json', data: JSON.stringify(data) }; + console.log(options); for(let i = 0; i < count; i++) { $.ajax(options) .then(function(result) { @@ -1077,8 +1129,8 @@ }); [% UNLESS ( multi_hold ) %] - $("#hold-request-form").on("submit", function(){ - return check(); + $("#hold-request-form [type='submit']").on("click", function(e){ + return check(e); }); [% ELSE %] $("#hold-request-form").on("submit", function(){ @@ -1088,7 +1140,7 @@ }); - function check() { + function check(e) { var msg = ""; var count_reserv = 0; @@ -1107,6 +1159,8 @@ } } + count_reserv += $("input[name='volume_id']:checked").length; + if (document.form.requestany.checked == true){ count_reserv++ ; } @@ -1119,6 +1173,7 @@ $('#hold-request-form').preventDoubleFormSubmit(); return(true); } else { + e.preventDefault(); alert(msg); return(false); } @@ -1171,14 +1226,14 @@ }); $("#requestany").click(function() { if(this.checked){ - $("input[name=checkitem]").each(function() { + $("input[name=checkitem], input[name='volume_id']").each(function() { $(this).prop("checked", false); }); } }); - $("input[name=checkitem]").click(function() { + $("input[name=checkitem], input[name='volume_id']").click(function() { onechecked = 0; - $("input[name=checkitem]").each(function() { + $("input[name=checkitem], input[name='volume_id']").each(function() { if(this.checked){ onechecked = 1; } @@ -1250,6 +1305,15 @@ [% END %] [% END %] + [% IF Koha.Preference('EnableVolumeHolds') %] + $(':radio[name="volume_id"]').change(function(){ + $(':radio[name="checkitem"]').attr('checked', false); + }); + $(':radio[name="checkitem"]').change(function(){ + $(':radio[name="volume_id"]').attr('checked', false); + }); + [% END %] + }); [% END %] diff --git a/reserve/placerequest.pl b/reserve/placerequest.pl index 0b4a12c206..2381bd7854 100755 --- a/reserve/placerequest.pl +++ b/reserve/placerequest.pl @@ -50,6 +50,7 @@ my @rank = $input->multi_param('rank-request'); my $type = $input->param('type'); my $title = $input->param('title'); my $checkitem = $input->param('checkitem'); +my $volume_id = $input->param('volume_id'); my $expirationdate = $input->param('expiration_date'); my $itemtype = $input->param('itemtype') || undef; @@ -155,6 +156,7 @@ if ( $type eq 'str8' && $borrower ) { itemnumber => $checkitem, found => $found, itemtype => $itemtype, + volume_id => $volume_id, } ); } diff --git a/reserve/request.pl b/reserve/request.pl index 26e40dd685..9f09106099 100755 --- a/reserve/request.pl +++ b/reserve/request.pl @@ -498,6 +498,7 @@ foreach my $biblionumber (@biblionumbers) { # checking reserve my $item_object = Koha::Items->find( $itemnumber ); + $item->{object} = $item_object; my $holds = $item_object->current_holds; if ( my $first_hold = $holds->next ) { my $p = Koha::Patrons->find( $first_hold->borrowernumber ); @@ -626,6 +627,12 @@ foreach my $biblionumber (@biblionumbers) { } $template->param( hiddencount => $hiddencount); + if ( C4::Context->preference('EnableVolumeHolds') ) { + my $volumes = Koha::Biblio::Volumes->search( + { biblionumber => $biblionumber } ); + $biblioitem->{volumes} = $volumes; + } + push @bibitemloop, $biblioitem; } diff --git a/t/db_dependent/Koha/Holds.t b/t/db_dependent/Koha/Holds.t index 055dd18d11..a9b4e341dc 100644 --- a/t/db_dependent/Koha/Holds.t +++ b/t/db_dependent/Koha/Holds.t @@ -25,6 +25,7 @@ use Test::Warn; use C4::Reserves; use Koha::Holds; use Koha::Database; +use Koha::Biblio::Volumes; use t::lib::Mocks; use t::lib::TestBuilder; @@ -59,7 +60,7 @@ subtest 'DB constraints' => sub { }; subtest 'cancel' => sub { - plan tests => 12; + plan tests => 13; my $biblioitem = $builder->build_object( { class => 'Koha::Biblioitems' } ); my $library = $builder->build_object( { class => 'Koha::Libraries' } ); my $itemtype = $builder->build_object( { class => 'Koha::ItemTypes', value => { rentalcharge => 0 } } ); @@ -182,6 +183,31 @@ subtest 'cancel' => sub { is( $hold_old->found, 'W', 'The found column should have been kept and a hold is cancelled' ); }; + subtest 'Test Koha::Hold::volume' => sub { + plan tests => 1; + my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); + my $volume = $builder->build_object( + { + class => 'Koha::Biblio::Volumes', + value => { biblionumber => $item->biblionumber } + } + ); + my $reserve_id = C4::Reserves::AddReserve( + $library->branchcode, $patron->borrowernumber, + $item->biblionumber, '', + 1, undef, + undef, '', + "title for fee", $item->itemnumber, + 'W', undef, + $volume->id + ); + + my $hold = Koha::Holds->find($reserve_id); + is( $hold->volume_id, $volume->id, + 'Koha::Hold::volume returns the correct volume' ); + }; + + subtest 'HoldsLog' => sub { plan tests => 2; my $patron = $builder->build_object({ class => 'Koha::Patrons' }); -- 2.11.0