From 8ba277970f81d5f2feb662d5de51e13573bd1dd2 Mon Sep 17 00:00:00 2001
From: Julian Maurice <julian.maurice@biblibre.com>
Date: Tue, 21 Feb 2023 11:14:06 +0100
Subject: [PATCH] Bug 33021: Show an alert when adding a reserved item to an
 item bundle

Test plan:
1. Create an item bundle (see bug 28854 comment 458)
2. Create a biblio with one item and place a hold for this item.
3. Try to add the reserved item to the bundle
   You should see a message saying that the item is reserved. Next to
   this message should be a button "Ignore holds and add to bundle".
4. Click on the button. There should be a message saying that the item
   was added to the bundle.
5. Close the modal window and verify that the item was correctly
   added to the bundle
---
 Koha/Exceptions/Item/Bundle.pm                 |  4 ++++
 Koha/Item.pm                                   |  7 +++++++
 Koha/REST/V1/Items.pm                          | 18 ++++++++++++++++--
 api/v1/swagger/definitions/bundle_link.yaml    |  4 ++++
 .../prog/en/modules/catalogue/detail.tt        | 12 ++++++++++++
 5 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/Koha/Exceptions/Item/Bundle.pm b/Koha/Exceptions/Item/Bundle.pm
index 8c42fd2cb2..fb8c46d22f 100644
--- a/Koha/Exceptions/Item/Bundle.pm
+++ b/Koha/Exceptions/Item/Bundle.pm
@@ -31,6 +31,10 @@ use Exception::Class (
         isa         => 'Koha::Exceptions::Item::Bundle',
         description => 'Someone tried to add a checked out item to a bundle',
     },
+    'Koha::Exceptions::Item::Bundle::ItemHasHolds' => {
+        isa         => 'Koha::Exceptions::Item::Bundle',
+        description => 'Someone tried to add a reserved item to a bundle',
+    },
 );
 
 =head1 NAME
diff --git a/Koha/Item.pm b/Koha/Item.pm
index d27b9a2360..0136c9493c 100644
--- a/Koha/Item.pm
+++ b/Koha/Item.pm
@@ -1734,6 +1734,13 @@ sub add_to_bundle {
                     }
                 }
 
+                my $holds = $bundle_item->current_holds;
+                if ($holds->count) {
+                    unless ($options->{ignore_holds}) {
+                        Koha::Exceptions::Item::Bundle::ItemHasHolds->throw();
+                    }
+                }
+
                 $self->_result->add_to_item_bundles_hosts(
                     { item => $bundle_item->itemnumber } );
 
diff --git a/Koha/REST/V1/Items.pm b/Koha/REST/V1/Items.pm
index 365d5e2992..561d327ec0 100644
--- a/Koha/REST/V1/Items.pm
+++ b/Koha/REST/V1/Items.pm
@@ -277,8 +277,13 @@ sub add_to_bundle {
     }
 
     return try {
-        my $force_checkin = $c->validation->param('body')->{'force_checkin'};
-        my $link = $item->add_to_bundle($bundle_item, { force_checkin => $force_checkin });
+        my $body = $c->validation->param('body');
+        my $options = {
+            force_checkin => $body->{force_checkin},
+            ignore_holds => $body->{ignore_holds},
+        };
+
+        my $link = $item->add_to_bundle($bundle_item, $options);
         return $c->render(
             status  => 201,
             openapi => $bundle_item
@@ -314,6 +319,15 @@ sub add_to_bundle {
                 }
             );
         }
+        elsif ( ref($_) eq 'Koha::Exceptions::Item::Bundle::ItemHasHolds' ) {
+            return $c->render(
+                status  => 409,
+                openapi => {
+                    error      => 'Item is reserved',
+                    error_code => 'reserved'
+                }
+            );
+        }
         elsif ( ref($_) eq 'Koha::Exceptions::Item::Bundle::IsBundle' ) {
             return $c->render(
                 status  => 400,
diff --git a/api/v1/swagger/definitions/bundle_link.yaml b/api/v1/swagger/definitions/bundle_link.yaml
index 382ad67516..b564d039ee 100644
--- a/api/v1/swagger/definitions/bundle_link.yaml
+++ b/api/v1/swagger/definitions/bundle_link.yaml
@@ -15,4 +15,8 @@ properties:
     type:
       - boolean
       - "null"
+  ignore_holds:
+    type:
+      - boolean
+      - "null"
 additionalProperties: false
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 59f2351c35..dfa8495ae6 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt
@@ -1827,6 +1827,18 @@ Note that permanent location is a code, and location may be an authval.
                                 .empty()
                                 .attr('class', 'alert alert-danger')
                                 .append(__x('Failure: Item {barcode} cannot be checked in', { barcode }))
+                          } else if (response.error_code === 'reserved') {
+                              const button = $('<button type="button">')
+                                .addClass('btn btn-xs')
+                                .text(__('Ignore holds and add to bundle'))
+                                .on('click', function () {
+                                    addToBundle(url, { external_id: barcode, ignore_holds: true });
+                                });
+                              $('#addResult')
+                                .empty()
+                                .attr('class', 'alert alert-warning')
+                                .append(__x('Warning: Item {barcode} is reserved', { barcode }))
+                                .append(' ', button);
                           } else {
                               $('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Item '%s' belongs to another bundle").format(barcode)+'</div>');
                           }
-- 
2.30.2