Bugzilla – Attachment 194543 Details for
Bug 41993
Add a page to show items available for bookings
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 41993: Add a page to show items available for bookings
Bug-41993-Add-a-page-to-show-items-available-for-b.patch (text/plain), 27.51 KB, created by
Owen Leonard
on 2026-03-05 17:17:47 UTC
(
hide
)
Description:
Bug 41993: Add a page to show items available for bookings
Filename:
MIME Type:
Creator:
Owen Leonard
Created:
2026-03-05 17:17:47 UTC
Size:
27.51 KB
patch
obsolete
>From e7b8b1c50f7c278262deb0be07a067fdaf50c11e Mon Sep 17 00:00:00 2001 >From: Thibaud Guillot <thibaud.guillot@biblibre.com> >Date: Fri, 27 Feb 2026 16:23:30 +0100 >Subject: [PATCH] Bug 41993: Add a page to show items available for bookings >MIME-Version: 1.0 >Content-Type: text/plain; charset=UTF-8 >Content-Transfer-Encoding: 8bit > >Test plan: > >1) Apply this patch, rebuild sources and restart_all >2) On Circulation module you will see a new links into "Holds and > bookings" section, click on "Items available for bookings" >3) First of all the datatable list your bookable items but not filtered >4) On the left you have many filters for the datatable, please note > that dates are modified on server side to include lead and trail > periods if they exist. > >Sponsored by: Loire Forez Agglomération > >Signed-off-by: Owen Leonard <oleonard@myacpl.org> >--- > Koha/Items.pm | 138 ++++++++- > Koha/REST/V1/Items.pm | 32 ++ > admin/columns_settings.yml | 21 ++ > .../paths/items_available_for_booking.yaml | 81 +++++ > api/v1/swagger/swagger.yaml | 2 + > circ/available-bookings.pl | 52 ++++ > .../prog/en/includes/circ-nav.inc | 16 +- > .../en/modules/circ/available-bookings.tt | 276 ++++++++++++++++++ > .../prog/en/modules/circ/circulation-home.tt | 3 + > 9 files changed, 609 insertions(+), 12 deletions(-) > create mode 100644 api/v1/swagger/paths/items_available_for_booking.yaml > create mode 100644 circ/available-bookings.pl > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/circ/available-bookings.tt > >diff --git a/Koha/Items.pm b/Koha/Items.pm >index 862631fbfb3..4eec60514e5 100644 >--- a/Koha/Items.pm >+++ b/Koha/Items.pm >@@ -36,6 +36,7 @@ use Koha::CirculationRules; > use base qw(Koha::Objects); > > use Koha::SearchEngine::Indexer; >+use Koha::DateUtils qw( dt_from_string); > > =head1 NAME > >@@ -192,16 +193,23 @@ sub filter_out_lost { > =head3 filter_by_bookable > > my $filterd_items = $items->filter_by_bookable; >+ my $filterd_items = $items->filter_by_bookable({ >+ start_date => '2024-01-01', >+ end_date => '2024-01-07' >+ }); > > Returns a new resultset, containing only those items that are allowed to be booked. >+If start_date and end_date are provided, also filters out items that are not available >+during the specified period (not booked and not checked out). > > =cut > > sub filter_by_bookable { >- my ($self) = @_; >+ my ($self, $params) = @_; > >+ my $bookable_items; > if ( !C4::Context->preference('item-level_itypes') ) { >- return $self->search( >+ $bookable_items = $self->search( > [ > { bookable => 1 }, > { >@@ -212,17 +220,125 @@ sub filter_by_bookable { > ], > { join => 'biblioitem' } > ); >+ } else { >+ $bookable_items = $self->search( >+ [ >+ { bookable => 1 }, >+ { >+ bookable => undef, >+ itype => { -in => [ Koha::ItemTypes->search( { bookable => 1 } )->get_column('itemtype') ] } >+ }, >+ ] >+ ); > } > >- return $self->search( >- [ >- { bookable => 1 }, >+ return $self->_filter_by_availability($bookable_items, $params); >+} >+ >+=head3 _filter_by_availability >+ >+Internal helper method to filter items by availability during a date range. >+ >+=cut >+ >+sub _filter_by_availability { >+ my ($self, $bookable_items, $params) = @_; >+ >+ return $bookable_items unless $params && $params->{start_date} && $params->{end_date}; >+ >+ my $start_date = dt_from_string( $params->{start_date} ); >+ my $end_date = dt_from_string( $params->{end_date} ); >+ >+ $start_date->set_hour(0)->set_minute(0)->set_second(0); >+ $end_date->set_hour(23)->set_minute(59)->set_second(59); >+ >+ my $dtf = Koha::Database->new->schema->storage->datetime_parser; >+ my @excluded_items; >+ >+ while ( my $item = $bookable_items->next ) { >+ >+ my $processing_rules = Koha::CirculationRules->get_effective_rules( > { >- bookable => undef, >- itype => { -in => [ Koha::ItemTypes->search( { bookable => 1 } )->get_column('itemtype') ] } >- }, >- ] >- ); >+ categorycode => '*', >+ itemtype => $item->effective_itemtype, >+ branchcode => $item->homebranch, >+ rules => [ 'bookings_lead_period', 'bookings_trail_period' ] >+ } >+ ); >+ >+ my $item_start_date = $start_date->clone; >+ my $item_end_date = $end_date->clone; >+ >+ if (defined $processing_rules->{'bookings_lead_period'} && $processing_rules->{'bookings_lead_period'} ne '') { >+ my $lead_days = $processing_rules->{'bookings_lead_period'}; >+ $item_start_date->subtract(days => $lead_days); >+ $item_start_date->set_hour(0)->set_minute(0)->set_second(0); >+ } >+ >+ if (defined $processing_rules->{'bookings_trail_period'} && $processing_rules->{'bookings_trail_period'} ne '') { >+ my $trail_days = $processing_rules->{'bookings_trail_period'}; >+ $item_end_date->add(days => $trail_days); >+ $item_end_date->set_hour(23)->set_minute(59)->set_second(59); >+ } >+ >+ my $existing_bookings = $item->bookings( >+ { >+ '-and' => [ >+ { >+ '-or' => [ >+ { >+ start_date => { >+ '-between' => [ >+ $dtf->format_datetime($item_start_date), >+ $dtf->format_datetime($item_end_date) >+ ] >+ } >+ }, >+ { >+ end_date => { >+ '-between' => [ >+ $dtf->format_datetime($item_start_date), >+ $dtf->format_datetime($item_end_date) >+ ] >+ } >+ }, >+ { >+ start_date => { '<' => $dtf->format_datetime($item_start_date) }, >+ end_date => { '>' => $dtf->format_datetime($item_end_date) } >+ } >+ ] >+ }, >+ { status => { '-not_in' => [ 'cancelled', 'completed' ] } } >+ ] >+ } >+ ); >+ >+ my $checkout = $item->checkout; >+ my $checkout_conflicts = 0; >+ if ($checkout) { >+ my $due_date = dt_from_string($checkout->date_due); >+ $checkout_conflicts = 1 if $due_date >= $item_start_date; >+ } >+ >+ if ($existing_bookings->count > 0 || $checkout_conflicts) { >+ push @excluded_items, $item->itemnumber; >+ # Debug - uncomment to see why items are excluded: >+ # warn "EXCLUDED Item #" . $item->itemnumber . " - Bookings: " . $existing_bookings->count . ", Checkout conflicts: $checkout_conflicts, Lead: " . ($processing_rules->{'bookings_lead_period'} // 'none') . ", Trail: " . ($processing_rules->{'bookings_trail_period'} // 'none'); >+ } else { >+ # Debug - uncomment to see items that pass: >+ # warn "PASSED Item #" . $item->itemnumber . " - Lead: " . ($processing_rules->{'bookings_lead_period'} // 'none') . ", Trail: " . ($processing_rules->{'bookings_trail_period'} // 'none'); >+ } >+ } >+ >+ $bookable_items->reset; >+ >+ if (@excluded_items) { >+ return $bookable_items->search({ >+ 'me.itemnumber' => { '-not_in' => \@excluded_items } >+ }); >+ } >+ >+ return $bookable_items; > } > > =head3 filter_by_checked_out >@@ -773,4 +889,4 @@ Martin Renvoize <martin.renvoize@ptfs-europe.com> > > =cut > >-1; >+1; >\ No newline at end of file >diff --git a/Koha/REST/V1/Items.pm b/Koha/REST/V1/Items.pm >index 07a8a4f03e5..9ddcb46d787 100644 >--- a/Koha/REST/V1/Items.pm >+++ b/Koha/REST/V1/Items.pm >@@ -22,6 +22,8 @@ use Mojo::Base 'Mojolicious::Controller'; > use C4::Circulation qw( barcodedecode ); > > use Koha::Items; >+use Koha::DateUtils; >+use Koha::Database; > > use List::MoreUtils qw( any ); > use Try::Tiny qw( catch try ); >@@ -424,4 +426,34 @@ sub remove_from_bundle { > }; > } > >+=head3 available_for_booking >+ >+Controller function that handles retrieving items available for booking >+ >+=cut >+ >+sub available_for_booking { >+ my $c = shift->openapi->valid_input or return; >+ >+ return try { >+ my $start_date = $c->param('start_date'); >+ my $end_date = $c->param('end_date'); >+ >+ $c->req->params->remove('start_date'); >+ $c->req->params->remove('end_date'); >+ my $items_set = Koha::Items->new->filter_by_bookable( >+ ($start_date && $end_date) ? { >+ start_date => $start_date, >+ end_date => $end_date, >+ } : undef >+ ); >+ >+ my $items = $c->objects->search( $items_set ); >+ return $c->render( status => 200, openapi => $items ); >+ >+ } catch { >+ $c->unhandled_exception($_); >+ }; >+} >+ > 1; >diff --git a/admin/columns_settings.yml b/admin/columns_settings.yml >index 75f73437fee..836677dacf8 100644 >--- a/admin/columns_settings.yml >+++ b/admin/columns_settings.yml >@@ -2400,6 +2400,27 @@ modules: > - > columnname: booking_dates > >+ available-bookings: >+ available-items: >+ default_sort_order: 2 >+ columns: >+ - >+ columnname: holding_library >+ - >+ columnname: home_library >+ - >+ columnname: title >+ - >+ columnname: item_type >+ - >+ columnname: barcode >+ - >+ columnname: call_number >+ - >+ columnname: location >+ - >+ columnname: localuse >+ > opac: > biblio-detail: > holdingst: >diff --git a/api/v1/swagger/paths/items_available_for_booking.yaml b/api/v1/swagger/paths/items_available_for_booking.yaml >new file mode 100644 >index 00000000000..8ac5142c928 >--- /dev/null >+++ b/api/v1/swagger/paths/items_available_for_booking.yaml >@@ -0,0 +1,81 @@ >+--- >+/items/available_for_booking: >+ get: >+ x-mojo-to: Items#available_for_booking >+ operationId: listItemsAvailableForBooking >+ tags: >+ - items >+ summary: List items available for booking >+ parameters: >+ - description: Start date for availability period (YYYY-MM-DD format) >+ in: query >+ name: start_date >+ required: false >+ type: string >+ - description: End date for availability period (YYYY-MM-DD format) >+ in: query >+ name: end_date >+ required: false >+ type: string >+ - name: x-koha-embed >+ in: header >+ required: false >+ description: Embed list sent as a request header >+ type: array >+ items: >+ type: string >+ enum: >+ - +strings >+ - biblio >+ - effective_bookable >+ - home_library >+ - holding_library >+ - item_type >+ collectionFormat: csv >+ - $ref: "../swagger.yaml#/parameters/match" >+ - $ref: "../swagger.yaml#/parameters/order_by" >+ - $ref: "../swagger.yaml#/parameters/page" >+ - $ref: "../swagger.yaml#/parameters/per_page" >+ - $ref: "../swagger.yaml#/parameters/q_param" >+ - $ref: "../swagger.yaml#/parameters/q_body" >+ - $ref: "../swagger.yaml#/parameters/request_id_header" >+ consumes: >+ - application/json >+ produces: >+ - application/json >+ responses: >+ "200": >+ description: A list of items available for booking >+ schema: >+ type: array >+ items: >+ $ref: "../swagger.yaml#/definitions/item" >+ "400": >+ description: | >+ Bad request. Possible `error_code` attribute values: >+ >+ * `invalid_query` >+ schema: >+ $ref: "../swagger.yaml#/definitions/error" >+ "401": >+ description: Authentication required >+ schema: >+ $ref: "../swagger.yaml#/definitions/error" >+ "403": >+ description: Access forbidden >+ schema: >+ $ref: "../swagger.yaml#/definitions/error" >+ "500": >+ description: | >+ Internal server error. Possible `error_code` attribute values: >+ >+ * `internal_server_error` >+ schema: >+ $ref: "../swagger.yaml#/definitions/error" >+ "503": >+ description: Under maintenance >+ schema: >+ $ref: "../swagger.yaml#/definitions/error" >+ x-koha-authorization: >+ permissions: >+ circulate: manage_bookings >\ No newline at end of file >diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml >index 9a9b7af7565..2ea7113234a 100644 >--- a/api/v1/swagger/swagger.yaml >+++ b/api/v1/swagger/swagger.yaml >@@ -463,6 +463,8 @@ paths: > $ref: ./paths/item_types.yaml#/~1item_types > /items: > $ref: ./paths/items.yaml#/~1items >+ /items/available_for_booking: >+ $ref: ./paths/items_available_for_booking.yaml#/~1items~1available_for_booking > "/items/{item_id}": > $ref: "./paths/items.yaml#/~1items~1{item_id}" > "/items/{item_id}/bookings": >diff --git a/circ/available-bookings.pl b/circ/available-bookings.pl >new file mode 100644 >index 00000000000..d22014175c6 >--- /dev/null >+++ b/circ/available-bookings.pl >@@ -0,0 +1,52 @@ >+#!/usr/bin/perl >+ >+# Copyright 2024 >+# >+# 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 <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use CGI qw ( -utf8 ); >+ >+use C4::Context; >+use C4::Output qw( output_html_with_http_headers ); >+use C4::Auth qw( get_template_and_user ); >+ >+use Koha::DateUtils qw(dt_from_string); >+ >+my $input = CGI->new; >+my ( $template, $borrowernumber, $cookie, $flags ) = get_template_and_user( >+ { >+ template_name => "circ/available-bookings.tt", >+ query => $input, >+ type => "intranet", >+ flagsrequired => { circulate => 'manage_bookings' }, >+ } >+); >+ >+my $branchcode = defined( $input->param('library') ) ? $input->param('library') : C4::Context->userenv->{'branch'}; >+ >+my $today = dt_from_string(); >+my $startdate = $today->clone->truncate( to => 'day' ); >+my $enddate = $startdate->clone->add( days => 7 ); >+ >+$template->param( >+ branchcode => $branchcode, >+ start_date_default => $startdate, >+ end_date_default => $enddate, >+); >+ >+output_html_with_http_headers $input, $cookie, $template->output; >\ No newline at end of file >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-nav.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-nav.inc >index 07cb781c347..9d8832e5638 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-nav.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-nav.inc >@@ -64,7 +64,21 @@ > </li> > </ul> > >- [% IF Koha.Preference('UseRecalls') and CAN_user_recalls %] >+ >+ <div class="col-sm-6 col-md-12"> >+ [% IF ( CAN_user_circulate_manage_bookings ) %] >+ <h5>Bookings</h5> >+ <ul> >+ <li> >+ <a href="/cgi-bin/koha/circ/pendingbookings.pl">Bookings to collect</a> >+ </li> >+ <li> >+ <a href="/cgi-bin/koha/circ/available-bookings.pl">Items available for booking</a> >+ </li> >+ </ul> >+ [% END %] >+ >+ [% IF Koha.Preference('UseRecalls') and CAN_user_recalls %] > <h5>Recalls</h5> > <ul> > <li> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/available-bookings.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/available-bookings.tt >new file mode 100644 >index 00000000000..d41ad3d2d04 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/available-bookings.tt >@@ -0,0 +1,276 @@ >+[% USE raw %] >+[% USE Asset %] >+[% USE Branches %] >+[% USE Koha %] >+[% USE KohaDates %] >+[% USE TablesSettings %] >+[% USE To %] >+[% USE ItemTypes %] >+[% PROCESS 'i18n.inc' %] >+[% SET footerjs = 1 %] >+[% INCLUDE 'doc-head-open.inc' %] >+<title>[% FILTER collapse %] >+ [% t("Items available for booking") | html %] › >+ [% t("Circulation") | html %] › >+ [% t("Koha") | html %] >+[% END %]</title> >+[% INCLUDE 'doc-head-close.inc' %] >+</head> >+ >+<body id="circ_available_for_booking" class="circ"> >+[% WRAPPER 'header.inc' %] >+ [% INCLUDE 'circ-search.inc' %] >+[% END %] >+ >+[% WRAPPER 'sub-header.inc' %] >+ [% WRAPPER breadcrumbs %] >+ [% WRAPPER breadcrumb_item %] >+ <a href="/cgi-bin/koha/circ/circulation-home.pl">Circulation</a> >+ [% END %] >+ [% WRAPPER breadcrumb_item bc_active= 1 %] >+ <span>Items available for booking</span> >+ [% END %] >+ [% END #/ WRAPPER breadcrumbs %] >+[% END #/ WRAPPER sub-header.inc %] >+ >+<div class="main container-fluid"> >+ <div class="row"> >+ >+ <!-- Results --> >+ <div class="col-md-10 order-md-2 order-sm1"> >+ <main> >+ [% INCLUDE 'messages.inc' %] >+ <h1>Items available for booking</h1> >+ <p>Items with bookable status that are not booked and not checked out for the requested period.</p> >+ <div id="searchresults"> >+ <table id="available_items_table"></table> >+ </div> >+ </main> >+ </div> >+ >+ <!-- Filters & Navigation --> >+ <div class="col-md-2 order-sm-2 order-md-1"> >+ <aside> >+ <form id="available_items_filter"> >+ <fieldset class="rows"> >+ <h4>Filter by</h4> >+ <ol> >+ <li> >+ <label for="start_date">Start date: </label> >+ <input type="text" size="10" id="start_date" name="start_date" value="[% start_date_default | html %]" class="flatpickr" data-date_to="end_date" required/> >+ </li> >+ <li> >+ <label for="end_date">End date: </label> >+ <input type="text" size="10" id="end_date" name="end_date" value="[% end_date_default | html %]" class="flatpickr" required/> >+ </li> >+ <li> >+ <label for="pickup_libraries">Pickup libraries:</label> >+ <select name="pickup_libraries" id="pickup_libraries" multiple size="10"> >+ [% SET libraries = Branches.all( only_from_group => 1 ) %] >+ <option value="">All libraries</option> >+ [% FOREACH l IN libraries %] >+ <option value="[% l.branchcode | html %]">[% l.branchname | html %]</option> >+ [% END %] >+ </select> >+ </li> >+ <li> >+ <label for="item_type">Item type:</label> >+ <select name="item_type" id="item_type" style="width: auto; min-width: 200px;"> >+ <option value="">Any</option> >+ [% FOREACH itemtype IN ItemTypes.Get() %] >+ <option value="[% itemtype.itemtype | html %]">[% itemtype.description | html %]</option> >+ [% END %] >+ </select> >+ </li> >+ </ol> >+ </fieldset> >+ <fieldset class="action"> >+ <input type="submit" name="run_report" value="Search" class="btn btn-primary" /> >+ <input type="reset" name="clear_form" value="Clear" class="btn btn-default" /> >+ </fieldset> >+ </form> >+ >+ [% INCLUDE 'circ-nav.inc' %] >+ </aside> >+ </div> >+ </div> >+ <!-- /.row --> >+</div> >+ >+[% MACRO jsinclude BLOCK %] >+[% INCLUDE 'calendar.inc' %] >+[% INCLUDE 'datatables.inc' %] >+[% INCLUDE 'js-biblio-format.inc' %] >+[% INCLUDE 'js-date-format.inc' %] >+ >+<script> >+let table_settings = [% TablesSettings.GetTableSettings( 'circ', 'available-bookings', 'available-items', 'json' ) | $raw %]; >+ >+$(document).ready(function() { >+ >+ let additional_filters = { >+ 'me.holding_library_id': function() { >+ let selectedLibraries = $("#pickup_libraries").val(); >+ if (selectedLibraries && selectedLibraries.length > 0) { >+ selectedLibraries = selectedLibraries.filter(lib => lib !== ''); >+ if (selectedLibraries.length > 0) { >+ return selectedLibraries; >+ } >+ } >+ return; >+ }, >+ 'me.item_type_id': function() { >+ let itemType = $("#item_type").val(); >+ if (itemType && itemType !== '') { >+ return itemType; >+ } >+ return; >+ } >+ }; >+ >+ [% SET libraries = Branches.all %] >+ let all_libraries = [% To.json(libraries) | $raw %].map(e => { >+ e['_id'] = e.branchcode; >+ e['_str'] = e.branchname; >+ return e; >+ }); >+ let filters_options = {}; >+ >+ var available_items_table_url = '/api/v1/items/available_for_booking'; >+ >+ var available_items_table = $("#available_items_table").kohaTable({ >+ "ajax": { >+ "url": available_items_table_url >+ }, >+ "embed": [ >+ "biblio", >+ "+strings", >+ "home_library", >+ "holding_library", >+ "item_type" >+ ], >+ "order": [[ 2, "asc" ]], >+ "columns": [{ >+ "data": "home_library.name", >+ "title": _("Homebranch"), >+ "searchable": true, >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return escape_str(row.home_library_id ? row.home_library.name : row.home_library_id); >+ } >+ }, >+ { >+ "data": "holding_library.name", >+ "title": _("Pickup library"), >+ "searchable": true, >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return escape_str(row.holding_library_id ? row.holding_library.name : row.holding_library_id); >+ } >+ }, >+ { >+ "data": "biblio.title", >+ "title": _("Title"), >+ "searchable": true, >+ "orderable": true, >+ "render": function(data,type,row,meta) { >+ if ( row.biblio ) { >+ return $biblio_to_html(row.biblio, { >+ link: 'detail' >+ }); >+ } else { >+ return 'No title'; >+ } >+ } >+ }, >+ { >+ "data": "item_type_id", >+ "title": _("Item type"), >+ "searchable": true, >+ "orderable": true, >+ "render": function(data,type,row,meta) { >+ if ( row.item_type && row.item_type.description ) { >+ return escape_str(row.item_type.description); >+ } else if ( row._strings && row._strings.item_type_id ) { >+ return escape_str(row._strings.item_type_id.str); >+ } else { >+ return escape_str(row.item_type_id || ''); >+ } >+ } >+ }, >+ { >+ "data": "external_id", >+ "title": _("Barcode"), >+ "searchable": true, >+ "orderable": true >+ }, >+ { >+ "data": "callnumber", >+ "title": _("Callnumber"), >+ "searchable": true, >+ "orderable": true >+ }, >+ { >+ "data": "location", >+ "title": _("Location"), >+ "searchable": true, >+ "orderable": true, >+ "render": function(data,type,row,meta) { >+ if ( row._strings && row._strings.location ) { >+ return row._strings.location.str; >+ } else { >+ return row.location || ''; >+ } >+ } >+ }, >+ { >+ "data": "localuse", >+ "title": _("Local use"), >+ "searchable": true, >+ "orderable": true >+ }] >+ }, table_settings, 1, additional_filters, filters_options); >+ >+ $("#available_items_filter").on("submit", function(e){ >+ e.preventDefault(); >+ >+ if (!$("#start_date").val() || !$("#end_date").val()) { >+ alert("Please select both start and end dates"); >+ return false; >+ } >+ let newUrl = '/api/v1/items/available_for_booking'; >+ let params = []; >+ >+ let fromdate = $("#start_date"); >+ let todate = $("#end_date"); >+ if ( fromdate.val() !== '' && todate.val() !== '' ) { >+ let fromDateStr = fromdate.val(); >+ let toDateStr = todate.val(); >+ params.push('start_date=' + encodeURIComponent(fromDateStr)); >+ params.push('end_date=' + encodeURIComponent(toDateStr)); >+ >+ } >+ if (params.length > 0) { >+ newUrl += '?' + params.join('&'); >+ } >+ available_items_table.DataTable().ajax.url(newUrl).load(); >+ }); >+ >+ $("#available_items_filter input[type=reset]").on("click", function(e){ >+ $("#pickup_libraries").val([]); >+ $("#item_type").val(''); >+ $("#start_date").val('[% start_date_default | html %]'); >+ $("#end_date").val('[% end_date_default | html %]'); >+ >+ let resetUrl = '/api/v1/items/available_for_booking'; >+ if ('[% start_date_default %]' && '[% end_date_default %]') { >+ resetUrl += '?start_date=[% start_date_default | uri %]&end_date=[% end_date_default | uri %]'; >+ } >+ available_items_table.DataTable().ajax.url(resetUrl).load(); >+ }); >+ >+}); >+</script> >+[% END %] >+ >+[% INCLUDE 'intranet-bottom.inc' %] >\ No newline at end of file >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt >index 5def67f6558..7488a5ee764 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt >@@ -100,6 +100,9 @@ > <li> > <a class="circ-button bookings-to-collect" href="/cgi-bin/koha/circ/pendingbookings.pl"><i class="fa-solid fa-calendar-days"></i> Bookings to collect</a> > </li> >+ <li> >+ <a class="circ-button" href="/cgi-bin/koha/circ/available-bookings.pl"><i class="fa-solid fa-calendar-days"></i> Items available for bookings</a> >+ </li> > [% END %] > </ul> > </div> >-- >2.39.5
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 41993
:
194490
| 194543 |
194544
|
194545