From 34b4fd0133d1901d0f3d66904f336452b0effa5f Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Wed, 2 Oct 2024 11:02:13 +0200 Subject: [PATCH] Bug 37334: Allow embed _status for GET /biblio/{biblio_id}/items --- Koha/Item.pm | 50 ++++++ api/v1/swagger/definitions/item.yaml | 5 + api/v1/swagger/paths/biblios.yaml | 1 + .../tables/items/catalogue_detail.inc | 142 +++++++++--------- 4 files changed, 129 insertions(+), 69 deletions(-) diff --git a/Koha/Item.pm b/Koha/Item.pm index b325570910a..a5fb2508a54 100644 --- a/Koha/Item.pm +++ b/Koha/Item.pm @@ -1420,6 +1420,56 @@ sub columns_to_str { return $values; } +sub _status { + my ($self) = @_; + + my @statuses; + if ( my $checkout = $self->checkout ) { + unless ( $checkout->onsite_checkout ) { + push @statuses, "checked_out"; + } else { + push @statuses, "local_use"; + } + } elsif ( my $transfer = $self->transfer ) { + if ( $transfer->datesent ) { + push @statuses, "in_transit"; + } else { + push @statuses, "transit_pending"; + } + } + if ( $self->itemlost ) { + push @statuses, 'lost'; + } + if ( $self->withdrawn ) { + push @statuses, 'withdrawn'; + } + if ( $self->damaged ) { + push @statuses, 'damaged'; + } + if ( $self->notforloan || $self->item_type->notforloan ) { + # TODO on a big Koha::Items loop we are going to join with item_type too often, use a cache + push @statuses, 'not_for_loan'; + } + if ( $self->first_hold ) { + push @statuses, 'on_hold'; + } + if ( C4::Context->preference('UseRecalls') && $self->recall ) { + push @statuses, 'recalled'; + } + + unless (@statuses ){ + push @statuses, 'available'; + } + + if ( $self->restricted ) { + push @statuses, 'restricted'; + } + + # TODO in_bundle? + + return join ',', @statuses; +} + =head3 additional_attributes my $attributes = $item->additional_attributes; diff --git a/api/v1/swagger/definitions/item.yaml b/api/v1/swagger/definitions/item.yaml index ba1a8a890e1..9f0a1af52e2 100644 --- a/api/v1/swagger/definitions/item.yaml +++ b/api/v1/swagger/definitions/item.yaml @@ -309,4 +309,9 @@ properties: - object - "null" description: A return claims object if one exists that's unresolved + _status: + type: + - string + - "null" + description: The status of the item additionalProperties: false diff --git a/api/v1/swagger/paths/biblios.yaml b/api/v1/swagger/paths/biblios.yaml index 005d75ec231..ba9bcdbb5f4 100644 --- a/api/v1/swagger/paths/biblios.yaml +++ b/api/v1/swagger/paths/biblios.yaml @@ -455,6 +455,7 @@ type: string enum: - +strings + - _status - home_library - holding_library - biblio.title diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/html_helpers/tables/items/catalogue_detail.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/html_helpers/tables/items/catalogue_detail.inc index 099cfdd6fd1..b214e5af78d 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/html_helpers/tables/items/catalogue_detail.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/html_helpers/tables/items/catalogue_detail.inc @@ -207,7 +207,7 @@ [%# In case or SeparateHoldings we may need to display the number of biblios in each tab %] [%# Do we need separate/new endpoints or do we hack the somewhere client-side? %] let item_table_url = "/api/v1/biblios/[% biblio.biblionumber | uri %]/items?"; - let embed = ["+strings,checkout,checkout.patron,transfer,transfer+strings,first_hold,first_hold+strings,first_hold.patron,first_hold.desk"]; + let embed = ["+strings,_status,checkout,checkout.patron,transfer,transfer+strings,first_hold,first_hold+strings,first_hold.patron,first_hold.desk"]; [% IF Koha.Preference('LocalCoverImages') %] embed.push('cover_image_ids'); [% END %] @@ -440,98 +440,102 @@ } }, { - data: "me.lost_status", + data: "me._status", className: "status", searchable: false, // FIXME We are losing the ability to search on the status orderable: false, render: function (data, type, row, meta) { let nodes = ""; - if ( row.checkout ) { - [%# Hacky for patron_to_html in case we simply want to display the patron's library name %] - row.checkout.patron.library = { name: libraries_names.get(row.checkout.patron.library_id) }; + row._status.split(",").forEach( status => { + if ( status == 'checked_out' || status == 'local_use') { + nodes += ''; - nodes += ''; - if ( row.checkout.onsite_checkout ) { + [%# Hacky for patron_to_html in case we simply want to display the patron's library name %] + row.checkout.patron.library = { name: libraries_names.get(row.checkout.patron.library_id) }; let patron_to_html = $patron_to_html(row.checkout.patron, { url: true, display_cardnumber: true, hide_patron_name }); - nodes += _("Currently in local use by %s").format(patron_to_html); - } else { - nodes += ''; - let patron_to_html = $patron_to_html(row.checkout.patron, { url: true, display_cardnumber: true, hide_patron_name }); - nodes += _("Checked out to %s").format(patron_to_html); + + if ( status == 'local_use' ) { + nodes += _("Currently in local use by %s").format(patron_to_html); + } else { + nodes += ''; + nodes += _("Checked out to %s").format(patron_to_html); + } + nodes += ': '; + nodes += _("due %s").format($date(row.checkout.due_date, { as_due_date: true })); + nodes += "" + } - nodes += ': '; - nodes += _("due %s").format($date(row.checkout.due_date, { as_due_date: true })); - nodes += "" - } else if ( row.transfer ) { - if ( row.transfer.datesent ) { + if ( status == 'in_transit' ) { nodes += '%s'.format(_("In transit from %s to %s since %s").format(escape_str(row.transfer._strings.from_library.str), escape_str(row.transfer._strings.to_library.str), $date(row.transfer.datesent))); - } else { + } + + if ( status == 'transit_pending' ) { nodes += '%s'.format(_("Transit pending from %s to %s since %s").format(escape_str(row.transfer._strings.from_library.str), escape_str(row.transfer._strings.to_library.str), $date(row.transfer.daterequested))); } - } - if ( row.lost_status ) { - let lost_lib = av_lost.get(row.lost_status.toString()) || _("Unavailable (lost or missing"); - nodes += '%s'.format(escape_str(lost_lib)); - } + if ( status == 'lost' ) { + let lost_lib = av_lost.get(row.lost_status.toString()) || _("Unavailable (lost or missing"); + nodes += '%s'.format(escape_str(lost_lib)); + } - if ( row.withdrawn ) { - let withdrawn_lib = av_withdrawn.get(row.withdrawn.toString()) || _("Withdrawn"); - nodes += '%s'.format(escape_str(withdrawn_lib)); - } + if ( status == 'withdrawn' ) { + let withdrawn_lib = av_withdrawn.get(row.withdrawn.toString()) || _("Withdrawn"); + nodes += '%s'.format(escape_str(withdrawn_lib)); + } - if ( row.damaged_status ) { - let damaged_lib = av_damaged.get(row.damaged_status.toString()) || _("Damaged"); - nodes += '%s'.format(escape_str(damaged_lib)); - } + if ( status == 'damaged' ) { + let damaged_lib = av_damaged.get(row.damaged_status.toString()) || _("Damaged"); + nodes += '%s'.format(escape_str(damaged_lib)); + } - if ( row.not_for_loan_status || item_types_notforloan.get(row.item_type_id) ) { - let not_for_loan_lib = av_not_for_loan.get(row.not_for_loan_status.toString()); - nodes += '%s'.format(_("Not for loan")) + ( not_for_loan_lib ? ' (%s)'.format(escape_str(not_for_loan_lib)) : '' ) + ''; - } + if ( status == 'not_for_loan' ) { + let not_for_loan_lib = av_not_for_loan.get(row.not_for_loan_status.toString()); + nodes += '%s'.format(_("Not for loan")) + ( not_for_loan_lib ? ' (%s)'.format(escape_str(not_for_loan_lib)) : '' ) + ''; + } - if ( row.first_hold ) { - if ( row.first_hold.waiting_date ) { - if ( row.first_hold.desk ) { - nodes += '%s'.format(_("Waiting at %s, %s since %s.".format(row.first_hold._strings.pickup_library_id.str, row.first_hold.desk.desk_name, $date(row.first_hold.waiting_date)))); + if ( status == 'on_hold') { + if ( row.first_hold.waiting_date ) { + if ( row.first_hold.desk ) { + nodes += '%s'.format(_("Waiting at %s, %s since %s.".format(row.first_hold._strings.pickup_library_id.str, row.first_hold.desk.desk_name, $date(row.first_hold.waiting_date)))); + } else { + nodes += '%s'.format(_("Waiting at %s since %s.".format(row.first_hold._strings.pickup_library_id.str, $date(row.first_hold.waiting_date)))); + } + [% IF Koha.Preference('canreservefromotherbranches') %] + if ( row.first_hold.waiting_date || row.first_hold.priority == 1 ) { + [%# Hacky for patron_to_html in case we simply want to display the patron's library name %] + row.first_hold.patron.library = { name: libraries_names.get(row.first_hold.patron.library_id) }; + + let patron_to_html = $patron_to_html(row.first_hold.patron, { url: true, display_cardnumber: true, hide_patron_name }); + nodes += ' %s'.format(_("Hold for: %s").format(patron_to_html)); + } + [% END %] } else { - nodes += '%s'.format(_("Waiting at %s since %s.".format(row.first_hold._strings.pickup_library_id.str, $date(row.first_hold.waiting_date)))); + nodes += ' %s'.format(_("There is an item level hold on this item (priority = %s).").format(row.first_hold.priority)); } - [% IF Koha.Preference('canreservefromotherbranches') %] - if ( row.first_hold.waiting_date || row.first_hold.priority == 1 ) { - [%# Hacky for patron_to_html in case we simply want to display the patron's library name %] - row.first_hold.patron.library = { name: libraries_names.get(row.first_hold.patron.library_id) }; - - let patron_to_html = $patron_to_html(row.first_hold.patron, { url: true, display_cardnumber: true, hide_patron_name }); - nodes += ' %s'.format(_("Hold for: %s").format(patron_to_html)); - } - [% END %] - } else { - nodes += ' %s'.format(_("There is an item level hold on this item (priority = %s).").format(row.first_hold.priority)); } - } - [% IF Koha.Preference('UseRecalls') %] - if ( row.recall ) { - if ( row.recall.waiting_date ) { - nodes += '%s'.format(_("Waiting at %s since %s").format(escape_str(row.recall.pickup_library_id.str), $date(row.recall.waiting_date))); - } else { - [%# Hacky for patron_to_html in case we simply want to display the patron's library name %] - row.recall.patron.library = { name: libraries_names.get(row.recall.patron.library_id) }; + [% IF Koha.Preference('UseRecalls') %] + if ( status == 'recalled' ) { + if ( row.recall.waiting_date ) { + nodes += '%s'.format(_("Waiting at %s since %s").format(escape_str(row.recall.pickup_library_id.str), $date(row.recall.waiting_date))); + } else { + [%# Hacky for patron_to_html in case we simply want to display the patron's library name %] + row.recall.patron.library = { name: libraries_names.get(row.recall.patron.library_id) }; - let patron_to_html = $patron_to_html(row.recall.patron, {url: true, display_cardnumber: true, hide_patron_name }); - nodes += '%s'.format(_("recalled by %s on %s").format(patron_to_html, $date(row.recall.created_date))) + let patron_to_html = $patron_to_html(row.recall.patron, {url: true, display_cardnumber: true, hide_patron_name }); + nodes += '%s'.format(_("recalled by %s on %s").format(patron_to_html, $date(row.recall.created_date))) + } } - } - [% END %] + [% END %] - if ( ! ( row.not_for_loan_status || item_types_notforloan.get(row.item_type_id) || row.checked_out_date || row.lost_status || row.withdrawn || row.damaged_status || row.transfer || row.first_hold || row.recall ) ) { - nodes += ' %s'.format(_("Available")) - } + if ( status == 'available' ) { + nodes += ' %s'.format(_("Available")) + } - if ( row.restricted_status ) { - nodes += '(%s)'.format(escape_str(av_restricted.get(row.restricted_status.toString()))); - } + if ( status == 'restricted') { + nodes += '(%s)'.format(escape_str(av_restricted.get(row.restricted_status.toString()))); + } + }); [% IF bundlesEnabled %] if ( row.in_bundle ) { -- 2.34.1