View | Details | Raw Unified | Return to bug 32121
Collapse All | Expand All

(-)a/Koha/Exceptions/Checkin.pm (+32 lines)
Line 0 Link Here
1
package Koha::Exceptions::Checkin;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Koha::Exception;
21
22
use Exception::Class (
23
    'Koha::Exceptions::Checkin' => {
24
        isa => 'Koha::Exception',
25
    },
26
    'Koha::Exceptions::Checkin::FailedCheckin' => {
27
        isa         => 'Koha::Exceptions::Checkin',
28
        description => "Checkin failed"
29
    },
30
);
31
32
1;
(-)a/Koha/Exceptions/Item/Bundle.pm (-2 / +9 lines)
Lines 20-33 use Modern::Perl; Link Here
20
use Koha::Exception;
20
use Koha::Exception;
21
21
22
use Exception::Class (
22
use Exception::Class (
23
24
    'Koha::Exceptions::Item::Bundle' => {
23
    'Koha::Exceptions::Item::Bundle' => {
25
        isa => 'Koha::Exception',
24
        isa => 'Koha::Exception',
26
    },
25
    },
27
    'Koha::Exceptions::Item::Bundle::IsBundle' => {
26
    'Koha::Exceptions::Item::Bundle::IsBundle' => {
28
        isa         => 'Koha::Exceptions::Item::Bundle',
27
        isa         => 'Koha::Exceptions::Item::Bundle',
29
        description => "A bundle cannot be added to a bundle",
28
        description => "A bundle cannot be added to a bundle",
30
    }
29
    },
30
    'Koha::Exceptions::Item::Bundle::ItemIsCheckedOut' => {
31
        isa         => 'Koha::Exceptions::Item::Bundle',
32
        description => 'Someone tried to add a checked out item to a bundle',
33
    },
31
);
34
);
32
35
33
=head1 NAME
36
=head1 NAME
Lines 44-49 Generic Item::Bundle exception Link Here
44
47
45
Exception to be used when attempting to add one bundle into another.
48
Exception to be used when attempting to add one bundle into another.
46
49
50
=head2 Koha::Exceptions::Item::Bundle::ItemIsCheckedOut
51
52
Exception to be used when attempting to add a checked out item to a bundle.
53
47
=cut
54
=cut
48
55
49
1;
56
1;
(-)a/Koha/Item.pm (-2 / +19 lines)
Lines 36-41 use Koha::Biblio::ItemGroups; Link Here
36
use Koha::Checkouts;
36
use Koha::Checkouts;
37
use Koha::CirculationRules;
37
use Koha::CirculationRules;
38
use Koha::CoverImages;
38
use Koha::CoverImages;
39
use Koha::Exceptions::Checkin;
40
use Koha::Exceptions::Item::Bundle;
39
use Koha::Exceptions::Item::Transfer;
41
use Koha::Exceptions::Item::Transfer;
40
use Koha::Item::Attributes;
42
use Koha::Item::Attributes;
41
use Koha::Exceptions::Item::Bundle;
43
use Koha::Exceptions::Item::Bundle;
Lines 1703-1709 Adds the bundle_item passed to this item Link Here
1703
=cut
1705
=cut
1704
1706
1705
sub add_to_bundle {
1707
sub add_to_bundle {
1706
    my ( $self, $bundle_item ) = @_;
1708
    my ( $self, $bundle_item, $options ) = @_;
1709
1710
    $options //= {};
1707
1711
1708
    Koha::Exceptions::Item::Bundle::IsBundle->throw()
1712
    Koha::Exceptions::Item::Bundle::IsBundle->throw()
1709
      if ( $self->itemnumber eq $bundle_item->itemnumber
1713
      if ( $self->itemnumber eq $bundle_item->itemnumber
Lines 1717-1722 sub add_to_bundle { Link Here
1717
    try {
1721
    try {
1718
        $schema->txn_do(
1722
        $schema->txn_do(
1719
            sub {
1723
            sub {
1724
                my $checkout = $bundle_item->checkout;
1725
                if ($checkout) {
1726
                    unless ($options->{force_checkin}) {
1727
                        Koha::Exceptions::Item::Bundle::ItemIsCheckedOut->throw();
1728
                    }
1729
1730
                    my $branchcode = C4::Context->userenv->{'branch'};
1731
                    my ($success) = C4::Circulation::AddReturn($bundle_item->barcode, $branchcode);
1732
                    unless ($success) {
1733
                        Koha::Exceptions::Checkin::FailedCheckin->throw();
1734
                    }
1735
                }
1736
1720
                $self->_result->add_to_item_bundles_hosts(
1737
                $self->_result->add_to_item_bundles_hosts(
1721
                    { item => $bundle_item->itemnumber } );
1738
                    { item => $bundle_item->itemnumber } );
1722
1739
Lines 1768-1774 sub add_to_bundle { Link Here
1768
            $_->rethrow();
1785
            $_->rethrow();
1769
        }
1786
        }
1770
        else {
1787
        else {
1771
            $_;
1788
            $_->rethrow();
1772
        }
1789
        }
1773
    };
1790
    };
1774
}
1791
}
(-)a/Koha/REST/V1/Items.pm (-3 / +19 lines)
Lines 278-284 sub add_to_bundle { Link Here
278
    }
278
    }
279
279
280
    return try {
280
    return try {
281
        my $link = $item->add_to_bundle($bundle_item);
281
        my $force_checkin = $c->validation->param('body')->{'force_checkin'};
282
        my $link = $item->add_to_bundle($bundle_item, { force_checkin => $force_checkin });
282
        return $c->render(
283
        return $c->render(
283
            status  => 201,
284
            status  => 201,
284
            openapi => $bundle_item
285
            openapi => $bundle_item
Lines 301-308 sub add_to_bundle { Link Here
301
                    error => 'Bundles cannot be nested'
302
                    error => 'Bundles cannot be nested'
302
                }
303
                }
303
            );
304
            );
304
        }
305
        } elsif (ref($_) eq 'Koha::Exceptions::Item::Bundle::ItemIsCheckedOut') {
305
        else {
306
            return $c->render(
307
                status  => 409,
308
                openapi => {
309
                    error => 'Item is checked out',
310
                    key   => 'checked_out'
311
                }
312
            );
313
        } elsif (ref($_) eq 'Koha::Exceptions::Checkin::FailedCheckin') {
314
            return $c->render(
315
                status  => 409,
316
                openapi => {
317
                    error => 'Item cannot be checked in',
318
                    key   => 'failed_checkin'
319
                }
320
            );
321
        } else {
306
            $c->unhandled_exception($_);
322
            $c->unhandled_exception($_);
307
        }
323
        }
308
    };
324
    };
(-)a/api/v1/swagger/definitions/bundle_link.yaml (+4 lines)
Lines 11-14 properties: Link Here
11
      - string
11
      - string
12
      - "null"
12
      - "null"
13
    description: Item barcode
13
    description: Item barcode
14
  force_checkin:
15
    type:
16
      - boolean
17
      - "null"
14
additionalProperties: false
18
additionalProperties: false
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/detail.tt (-12 / +31 lines)
Lines 1937-1972 Note that permanent location is a code, and location may be an authval. Link Here
1937
                bundle_form_active = item_id;
1937
                bundle_form_active = item_id;
1938
            });
1938
            });
1939
1939
1940
            $("#addToBundleForm").submit(function(event) {
1940
            function addToBundle (url, data) {
1941
1942
                  /* stop form from submitting normally */
1943
                  event.preventDefault();
1944
1945
                  /* get the action attribute from the <form action=""> element */
1946
                  var $form = $(this),
1947
                  url = $form.attr('action');
1948
1949
                  /* Send the data using post with external_id */
1941
                  /* Send the data using post with external_id */
1950
                  var posting = $.post({
1942
                  var posting = $.post({
1951
                      url: url,
1943
                      url: url,
1952
                      data: JSON.stringify({ external_id: $('#external_id').val()}),
1944
                      data: JSON.stringify(data),
1953
                      contentType: "application/json; charset=utf-8",
1945
                      contentType: "application/json; charset=utf-8",
1954
                      dataType: "json"
1946
                      dataType: "json"
1955
                  });
1947
                  });
1956
1948
1949
                  const barcode = data.external_id;
1950
1957
                  /* Report the results */
1951
                  /* Report the results */
1958
                  posting.done(function(data) {
1952
                  posting.done(function(data) {
1959
                      var barcode = $('#external_id').val();
1960
                      $('#addResult').replaceWith('<div id="addResult" class="alert alert-success">'+_("Success: Added '%s'").format(barcode)+'</div>');
1953
                      $('#addResult').replaceWith('<div id="addResult" class="alert alert-success">'+_("Success: Added '%s'").format(barcode)+'</div>');
1961
                      $('#external_id').val('').focus();
1954
                      $('#external_id').val('').focus();
1962
                      bundle_changed = 1;
1955
                      bundle_changed = 1;
1963
                  });
1956
                  });
1964
                  posting.fail(function(data) {
1957
                  posting.fail(function(data) {
1965
                      var barcode = $('#external_id').val();
1966
                      if ( data.status === 409 ) {
1958
                      if ( data.status === 409 ) {
1967
                          var response = data.responseJSON;
1959
                          var response = data.responseJSON;
1968
                          if ( response.key === "PRIMARY" ) {
1960
                          if ( response.key === "PRIMARY" ) {
1969
                              $('#addResult').replaceWith('<div id="addResult" class="alert alert-warning">'+_("Warning: Item '%s' already attached").format(barcode)+'</div>');
1961
                              $('#addResult').replaceWith('<div id="addResult" class="alert alert-warning">'+_("Warning: Item '%s' already attached").format(barcode)+'</div>');
1962
                          } else if (response.key === 'checked_out') {
1963
                              const button = $('<button type="button">')
1964
                                .addClass('btn btn-xs')
1965
                                .text(__('Check in and add to bundle'))
1966
                                .on('click', function () {
1967
                                    addToBundle(url, { external_id: barcode, force_checkin: true });
1968
                                });
1969
                              $('#addResult')
1970
                                .empty()
1971
                                .attr('class', 'alert alert-warning')
1972
                                .append(__x('Warning: Item {barcode} is checked out', { barcode }))
1973
                                .append(' ', button);
1974
                          } else if (response.key === 'failed_checkin') {
1975
                              $('#addResult')
1976
                                .empty()
1977
                                .attr('class', 'alert alert-danger')
1978
                                .append(__x('Failure: Item {barcode} cannot be checked in', { barcode }))
1970
                          } else {
1979
                          } else {
1971
                              $('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Item '%s' belongs to another bundle").format(barcode)+'</div>');
1980
                              $('#addResult').replaceWith('<div id="addResult" class="alert alert-danger">'+_("Failure: Item '%s' belongs to another bundle").format(barcode)+'</div>');
1972
                          }
1981
                          }
Lines 1984-1989 Note that permanent location is a code, and location may be an authval. Link Here
1984
                      }
1993
                      }
1985
                      $('#external_id').val('').focus();
1994
                      $('#external_id').val('').focus();
1986
                  });
1995
                  });
1996
            }
1997
1998
            $("#addToBundleForm").submit(function(event) {
1999
                  /* stop form from submitting normally */
2000
                  event.preventDefault();
2001
2002
                  const url = this.action;
2003
                  const data = { external_id: this.elements.external_id.value };
2004
2005
                  addToBundle(url, data);
1987
            });
2006
            });
1988
2007
1989
            $("#addToBundleModal").on("hidden.bs.modal", function(e){
2008
            $("#addToBundleModal").on("hidden.bs.modal", function(e){
(-)a/t/db_dependent/Koha/Item.t (-2 / +26 lines)
Lines 213-224 subtest 'bundle_host tests' => sub { Link Here
213
};
213
};
214
214
215
subtest 'add_to_bundle tests' => sub {
215
subtest 'add_to_bundle tests' => sub {
216
    plan tests => 6;
216
    plan tests => 7;
217
217
218
    $schema->storage->txn_begin;
218
    $schema->storage->txn_begin;
219
219
220
    t::lib::Mocks::mock_preference( 'BundleNotLoanValue', 1 );
220
    t::lib::Mocks::mock_preference( 'BundleNotLoanValue', 1 );
221
221
222
    my $library = $builder->build_object({ class => 'Koha::Libraries' });
223
    t::lib::Mocks::mock_userenv({
224
        branchcode => $library->branchcode
225
    });
226
222
    my $host_item = $builder->build_sample_item();
227
    my $host_item = $builder->build_sample_item();
223
    my $bundle_item1 = $builder->build_sample_item();
228
    my $bundle_item1 = $builder->build_sample_item();
224
    my $bundle_item2 = $builder->build_sample_item();
229
    my $bundle_item2 = $builder->build_sample_item();
Lines 242-247 subtest 'add_to_bundle tests' => sub { Link Here
242
    'Koha::Exceptions::Item::Bundle::IsBundle',
247
    'Koha::Exceptions::Item::Bundle::IsBundle',
243
      'Exception thrown if you try to add a bundle host to a bundle item';
248
      'Exception thrown if you try to add a bundle host to a bundle item';
244
249
250
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
251
    C4::Circulation::AddIssue( $patron->unblessed, $bundle_item2->barcode );
252
    throws_ok { $host_item->add_to_bundle($bundle_item2) }
253
    'Koha::Exceptions::Item::Bundle::ItemIsCheckedOut',
254
      'Exception thrown if you try to add a checked out item';
255
256
    $bundle_item2->withdrawn(1)->store;
257
    t::lib::Mocks::mock_preference( 'BlockReturnOfWithdrawnItems', 1 );
258
    throws_ok { $host_item->add_to_bundle( $bundle_item2, { force_checkin => 1 } ) }
259
    'Koha::Exceptions::Checkin::FailedCheckin',
260
      'Exception thrown if you try to add a checked out item using
261
      "force_checkin" and the return is not possible';
262
263
    $bundle_item2->withdrawn(0)->store;
264
    lives_ok { $host_item->add_to_bundle( $bundle_item2, { force_checkin => 1 } ) }
265
    'No exception if you try to add a checked out item using "force_checkin" and the return is possible';
266
267
    $bundle_item2->discard_changes;
268
    ok( !$bundle_item2->checkout, 'Item is not checked out after being added to a bundle' );
269
245
    $schema->storage->txn_rollback;
270
    $schema->storage->txn_rollback;
246
};
271
};
247
272
248
- 

Return to bug 32121