Bugzilla – Attachment 165063 Details for
Bug 36271
Bookings should have circulation rules
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 36271 : Add circulation rules for booking
Bug-36271--Add-circulation-rules-for-booking.patch (text/plain), 17.18 KB, created by
Thibaud Guillot (thibaud_g)
on 2024-04-18 10:11:44 UTC
(
hide
)
Description:
Bug 36271 : Add circulation rules for booking
Filename:
MIME Type:
Creator:
Thibaud Guillot (thibaud_g)
Created:
2024-04-18 10:11:44 UTC
Size:
17.18 KB
patch
obsolete
>From 366ab0e046eed5be9cb4171d01d934e422939ea6 Mon Sep 17 00:00:00 2001 >From: Thibaud Guillot <thibaud.guillot@biblibre.com> >Date: Mon, 18 Mar 2024 14:40:21 +0100 >Subject: [PATCH] Bug 36271 : Add circulation rules for booking >MIME-Version: 1.0 >Content-Type: text/plain; charset=UTF-8 >Content-Transfer-Encoding: 8bit > >Welcome to 3 new circulation rules :) > >- Bookings allowed (total) / bookings_allowed_total >- Bookings per item (total) / bookings_per_item >- Bookings period length (day) / bookings_period_length > >Test plan: >1) Apply this patch >2) Set values for these new rules >3) Define a total to 2, 1 per item with 2 days for > example for booking period. >4) Book an item and retry on the same item, normally an error message is > showing in the pop up. >6) Book another item, and again another one, on the third an error > message is showing (always in pop up) >7) You can also try to test with a longer period than bookings period > length rule, an another error will be displayed. > >Sponsored by: Association de Gestion des Ã…uvres Sociales d'Inria (AGOS) >--- > Koha/Booking.pm | 86 +++++++++++++++++++ > Koha/CirculationRules.pm | 9 ++ > Koha/Exceptions/Booking.pm | 4 + > Koha/REST/V1/Bookings.pm | 17 ++++ > admin/smart-rules.pl | 9 ++ > .../prog/en/modules/admin/smart-rules.tt | 29 ++++++- > .../prog/js/place_booking_modal.js | 4 +- > 7 files changed, 156 insertions(+), 2 deletions(-) > >diff --git a/Koha/Booking.pm b/Koha/Booking.pm >index 65d3122b6ee..b3a106e49b1 100644 >--- a/Koha/Booking.pm >+++ b/Koha/Booking.pm >@@ -21,8 +21,15 @@ use Modern::Perl; > > use Koha::Exceptions::Booking; > use Koha::DateUtils qw( dt_from_string ); >+use Koha::Bookings; >+use Koha::CirculationRules; >+use Koha::Cache::Memory::Lite; >+ >+use C4::Circulation; >+use C4::Biblio; > > use base qw(Koha::Object); >+use List::Util qw(min); > > =head1 NAME > >@@ -32,6 +39,82 @@ Koha::Booking - Koha Booking object class > > =head2 Class methods > >+=head2 can_be_booked_in_advance >+ >+ $canBeBooked = &can_be_booked_in_advance($patron, $item, $branchcode) >+ if ($canBeBooked->{status} eq 'OK') { #We can booked this Item in advance! } >+ >+@RETURNS { status => OK }, if the Item can be booked. >+ { status => tooManyBookings, limit => $limit, rule => $rule }, if the borrower has exceeded their maximum booking amount. >+ { status => tooLongBookingPeriod, limit => $limit }, if the borrower has exceeded their maximum booking period. >+=cut >+ >+sub can_be_booked_in_advance { >+ my ( $self, $params ) = @_; >+ my $patron = $self->patron; >+ my $item = $self->item; >+ >+ my $dbh = C4::Context->dbh; >+ >+ my $borrower = $patron->unblessed; >+ >+ if ( C4::Biblio->GetMarcFromKohaField('biblioitems.agerestriction') ) { >+ my $biblio = $item->biblio; >+ >+ # Check for the age restriction >+ my ( $ageRestriction, $daysToAgeRestriction ) = >+ C4::Circulation::GetAgeRestriction( $biblio->biblioitem->agerestriction, $borrower ); >+ return { status => 'ageRestricted' } if $daysToAgeRestriction && $daysToAgeRestriction > 0; >+ } >+ >+ # By default for now, control branch is the item homebranch >+ my $bookings_control_branch = $item->homebranch; >+ >+ # we retrieve rights >+ my $rights = Koha::CirculationRules->get_effective_rules( >+ { >+ categorycode => $borrower->{'categorycode'}, >+ itemtype => $item->effective_itemtype, >+ branchcode => $bookings_control_branch, >+ rules => >+ [ 'bookings_allowed_total', 'bookings_per_item', 'bookings_period_length' ] >+ } >+ ); >+ >+ my $bookings_allowed_total = $rights->{bookings_allowed_total} // 0; >+ my $bookings_per_item = $rights->{bookings_per_item} // 1; >+ my $bookings_period_length = $rights->{bookings_period_length}; >+ >+ return { status => 'noBookingsAllowed' } if $bookings_allowed_total == 0; >+ >+ my $bookings_per_item_count = Koha::Bookings->search( { patron_id => $patron->borrowernumber, item_id => $item->itemnumber } )->count(); >+ return { status => 'tooManyBookings', limit => $bookings_per_item, rule => 'bookings_per_item' } if $bookings_per_item <= $bookings_per_item_count; >+ >+ my $querycount = q{ >+ SELECT count(*) AS count >+ FROM bookings AS b >+ LEFT JOIN biblioitems AS bi ON (b.biblio_id=bi.biblionumber) >+ WHERE b.patron_id = ? >+ AND bi.itemtype = ? >+ }; >+ >+ my $sthcount = $dbh->prepare($querycount); >+ $sthcount->execute( $patron->borrowernumber, $item->effective_itemtype ); >+ my $total_bookings_count = $sthcount->fetchrow_hashref()->{count}; >+ >+ return { status => 'tooManyBookings', limit => $bookings_allowed_total, rule => 'bookings_allowed_total' } if $bookings_allowed_total <= $total_bookings_count; >+ >+ my $start_date = dt_from_string( $self->start_date ); >+ my $end_date = dt_from_string( $self->end_date ); >+ my $duration = $end_date->delta_days($start_date); >+ >+ my $delta_days = $duration->in_units('days'); >+ >+ return { status => 'tooLongBookingPeriod', limit => $bookings_period_length } if $delta_days > $bookings_period_length; >+ >+ return { status => 'OK' }; >+} >+ > =head3 biblio > > Returns the related Koha::Biblio object for this booking >@@ -126,6 +209,9 @@ sub store { > > # FIXME: We should be able to combine the above two functions into one > >+ my $canBeBooked = can_be_booked_in_advance( $self ); >+ Koha::Exceptions::Booking::Rule->throw( $canBeBooked ) if $canBeBooked->{'status'} ne "OK"; >+ > # Assign item at booking time > if ( !$self->item_id ) { > $self->_assign_item_for_booking; >diff --git a/Koha/CirculationRules.pm b/Koha/CirculationRules.pm >index 1d074768754..642eec7c124 100644 >--- a/Koha/CirculationRules.pm >+++ b/Koha/CirculationRules.pm >@@ -216,6 +216,15 @@ our $RULE_KINDS = { > holds_pickup_period => { > scope => [ 'branchcode', 'categorycode', 'itemtype' ], > }, >+ bookings_allowed_total => { >+ scope => [ 'branchcode', 'categorycode', 'itemtype' ], >+ }, >+ bookings_per_item => { >+ scope => [ 'branchcode', 'categorycode', 'itemtype' ], >+ }, >+ bookings_period_length => { >+ scope => [ 'branchcode', 'categorycode', 'itemtype' ], >+ }, > # Not included (deprecated?): > # * accountsent > # * reservecharge >diff --git a/Koha/Exceptions/Booking.pm b/Koha/Exceptions/Booking.pm >index 79e5b6ad8cf..bad8f4037ec 100644 >--- a/Koha/Exceptions/Booking.pm >+++ b/Koha/Exceptions/Booking.pm >@@ -8,6 +8,10 @@ use Exception::Class ( > isa => 'Koha::Exceptions::Booking', > description => "Adding or updating the booking would result in a clash" > }, >+ 'Koha::Exceptions::Booking::Rule' => { >+ isa => 'Koha::Exceptions::Booking', >+ description => "Booking rejected by circulation rules" >+ } > ); > > 1; >diff --git a/Koha/REST/V1/Bookings.pm b/Koha/REST/V1/Bookings.pm >index 2d1bb25ad83..b47728a03dc 100644 >--- a/Koha/REST/V1/Bookings.pm >+++ b/Koha/REST/V1/Bookings.pm >@@ -98,6 +98,23 @@ sub add { > error => "Duplicate booking_id", > } > ); >+ } elsif ( blessed $_ and $_->isa('Koha::Exceptions::Booking::Rule') ) { >+ my $error_code = $_->{'message'}->{'status'}; >+ my $limit = $_->{'message'}->{'limit'} // ''; >+ my $rule = $_->{'message'}->{'rule'} // ''; >+ my %error_strings = ( >+ 'noBookingsAllowed' => 'Bookings are not allowed according to circulation rules', >+ 'tooManyBookings' => sprintf('Patron has reached the maximum of booking according to circulation rules : %s maximum (%s)', $limit, $rule), >+ 'tooLongBookingPeriod' => sprintf('Booking period exceed booking period limit according to circulation rules : %s day(s)', $limit), >+ 'ageRestricted' => "Age restricted", >+ ); >+ >+ return $c->render( >+ status => 403, >+ openapi => { >+ error => $error_strings{$error_code}, >+ } >+ ); > } > > return $c->unhandled_exception($_); >diff --git a/admin/smart-rules.pl b/admin/smart-rules.pl >index f5f168f44f4..f9c09b92688 100755 >--- a/admin/smart-rules.pl >+++ b/admin/smart-rules.pl >@@ -117,6 +117,9 @@ if ($op eq 'cud-delete') { > recall_shelf_time => undef, > decreaseloanholds => undef, > holds_pickup_period => undef, >+ bookings_allowed_total => undef, >+ bookings_per_item => undef, >+ bookings_period_length => undef, > } > } > ); >@@ -302,6 +305,9 @@ elsif ( $op eq 'cud-add' ) { > my $recall_overdue_fine = $input->param('recall_overdue_fine'); > my $recall_shelf_time = $input->param('recall_shelf_time'); > my $holds_pickup_period = strip_non_numeric( scalar $input->param('holds_pickup_period') ); >+ my $bookings_allowed_total = strip_non_numeric( scalar $input->param('bookings_allowed_total') ); >+ my $bookings_per_item = strip_non_numeric( scalar $input->param('bookings_per_item') ); >+ my $bookings_period_length = $input->param('bookings_period_length') || 0; > > my $rules = { > maxissueqty => $maxissueqty, >@@ -344,6 +350,9 @@ elsif ( $op eq 'cud-add' ) { > recall_overdue_fine => $recall_overdue_fine, > recall_shelf_time => $recall_shelf_time, > holds_pickup_period => $holds_pickup_period, >+ bookings_allowed_total => $bookings_allowed_total, >+ bookings_per_item => $bookings_per_item, >+ bookings_period_length => $bookings_period_length, > }; > > Koha::CirculationRules->set_rules( >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt >index f536ef97601..dedd6defe80 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt >@@ -151,6 +151,9 @@ > <th>On shelf holds allowed</th> > <th>OPAC item level holds</th> > <th>Holds pickup period (day)</th> >+ <th>Bookings allowed (total)</th> >+ <th>Bookings per item (total)</th> >+ <th>Bookings period length (day)</th> > [% IF Koha.Preference('ArticleRequests') %] > <th>Article requests</th> > [% END %] >@@ -212,8 +215,11 @@ > [% SET recall_overdue_fine = all_rules.$c.$i.recall_overdue_fine %] > [% SET recall_shelf_time = all_rules.$c.$i.recall_shelf_time %] > [% SET holds_pickup_period = all_rules.$c.$i.holds_pickup_period %] >+ [% SET bookings_allowed_total = all_rules.$c.$i.bookings_allowed_total %] >+ [% SET bookings_per_item = all_rules.$c.$i.bookings_per_item %] >+ [% SET bookings_period_length = all_rules.$c.$i.bookings_period_length %] > >- [% SET show_rule = note || maxissueqty || maxonsiteissueqty || issuelength || daysmode || lengthunit || hardduedate || hardduedatecompare || fine || chargeperiod || chargeperiod_charge_at || firstremind || overduefinescap || cap_fine_to_replacement_price || finedays || maxsuspensiondays || suspension_chargeperiod || renewalsallowed || unseenrenewalsallowed || renewalperiod || norenewalbefore || noautorenewalbefore || auto_renew || no_auto_renewal_after || no_auto_renewal_after_hard_limit || reservesallowed || holds_per_day || holds_per_record || onshelfholds || opacitemholds || article_requests || rentaldiscount || decreaseloanholds || recalls_allowed || recalls_per_record || on_shelf_recalls || recall_due_date_interval || recall_overdue_fine || recall_shelf_time || holds_pickup_period %] >+ [% SET show_rule = note || maxissueqty || maxonsiteissueqty || issuelength || daysmode || lengthunit || hardduedate || hardduedatecompare || fine || chargeperiod || chargeperiod_charge_at || firstremind || overduefinescap || cap_fine_to_replacement_price || finedays || maxsuspensiondays || suspension_chargeperiod || renewalsallowed || unseenrenewalsallowed || renewalperiod || norenewalbefore || noautorenewalbefore || auto_renew || no_auto_renewal_after || no_auto_renewal_after_hard_limit || reservesallowed || holds_per_day || holds_per_record || onshelfholds || opacitemholds || article_requests || rentaldiscount || decreaseloanholds || recalls_allowed || recalls_per_record || on_shelf_recalls || recall_due_date_interval || recall_overdue_fine || recall_shelf_time || holds_pickup_period || bookings_allowed_total || bookings_per_item || bookings_period_length %] > [% IF show_rule %] > [% SET row_count = row_count + 1 %] > <tr row_countd="row_[% row_count | html %]"> >@@ -380,6 +386,21 @@ > [% holds_pickup_period | html %] > [% END %] > </td> >+ <td> >+ [% IF bookings_allowed_total.defined && bookings_allowed_total != '' %] >+ [% bookings_allowed_total | html %] >+ [% ELSE %] >+ <span>Unlimited</span> >+ [% END %] >+ </td> >+ <td> >+ [% IF bookings_per_item.defined && bookings_per_item != '' %] >+ [% bookings_per_item | html %] >+ [% ELSE %] >+ <span>Unlimited</span> >+ [% END %] >+ </td> >+ <td>[% bookings_period_length | html %]</td> > [% IF Koha.Preference('ArticleRequests') %] > <td data-code="[% article_requests | html %]"> > [% IF article_requests == 'no' %] >@@ -530,6 +551,9 @@ > </select> > </td> > <td><input type="text" name="holds_pickup_period" id="holds_pickup_period" size="2" /></td> >+ <td><input type="text" name="bookings_allowed_total" id="bookings_allowed_total" size="2" /></td> >+ <td><input type="text" name="bookings_per_item" id="bookings_per_item" size="2" /></td> >+ <td><input type="text" name="bookings_period_length" id="bookings_period_length" size="3" /></td> > [% IF Koha.Preference('ArticleRequests') %] > <td> > <select id="article_requests" name="article_requests"> >@@ -601,6 +625,9 @@ > <th>On shelf holds allowed</th> > <th>OPAC item level holds</th> > <th>Holds pickup period (day)</th> >+ <th>Bookings allowed (total)</th> >+ <th>Bookings per item (total)</th> >+ <th>Bookings period length (day)</th> > [% IF Koha.Preference('ArticleRequests') %] > <th>Article requests</th> > [% END %] >diff --git a/koha-tmpl/intranet-tmpl/prog/js/place_booking_modal.js b/koha-tmpl/intranet-tmpl/prog/js/place_booking_modal.js >index 832ee2a6a1d..8601359f910 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/place_booking_modal.js >+++ b/koha-tmpl/intranet-tmpl/prog/js/place_booking_modal.js >@@ -494,7 +494,9 @@ $("#placeBookingForm").on('submit', function(e) { > }); > > posting.fail(function(data) { >- $('#booking_result').replaceWith('<div id="booking_result" class="alert alert-danger">'+_("Failure")+'</div>'); >+ var error = data.responseJSON.error; >+ var errorMessage = error ? error : "Failure"; >+ $('#booking_result').replaceWith('<div id="booking_result" class="alert alert-danger">'+_(errorMessage)+'</div>'); > }); > } else { > url += '/' + booking_id; >-- >2.30.2
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 36271
:
163546
|
164627
|
165061
|
165063
|
166809
|
166907
|
166930
|
166931
|
166932
|
166933
|
166952
|
166957
|
166962
|
170197
|
172866
|
172924