Bugzilla – Attachment 172924 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), 18.36 KB, created by
PTFS Europe Sandboxes
on 2024-10-17 17:59:16 UTC
(
hide
)
Description:
Bug 36271: Add circulation rules for booking
Filename:
MIME Type:
Creator:
PTFS Europe Sandboxes
Created:
2024-10-17 17:59:16 UTC
Size:
18.36 KB
patch
obsolete
>From a8ea5cdbe007fa983c34d7c0b615d83469ce6585 Mon Sep 17 00:00:00 2001 >From: Thibaud Guillot <thibaud.guillot@biblibre.com> >Date: Thu, 17 Oct 2024 11:53:03 +0000 >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) > >Signed-off-by: esther <esther@bywatersolutions.com> >--- > Koha/Booking.pm | 99 +++++++++++++++++++ > Koha/CirculationRules.pm | 9 ++ > Koha/Exceptions/Booking.pm | 4 + > Koha/REST/V1/Bookings.pm | 18 ++++ > admin/smart-rules.pl | 9 ++ > .../prog/en/modules/admin/smart-rules.tt | 35 ++++++- > .../prog/js/modals/place_booking.js | 4 +- > 7 files changed, 176 insertions(+), 2 deletions(-) > >diff --git a/Koha/Booking.pm b/Koha/Booking.pm >index e5297d8d17..11e29344ff 100644 >--- a/Koha/Booking.pm >+++ b/Koha/Booking.pm >@@ -24,10 +24,16 @@ use Koha::DateUtils qw( dt_from_string ); > use Koha::Items; > use Koha::Patrons; > use Koha::Libraries; >+use Koha::Bookings; >+use Koha::CirculationRules; >+use Koha::Cache::Memory::Lite; > > use C4::Letters; >+use C4::Circulation; >+use C4::Biblio; > > use base qw(Koha::Object); >+use List::Util qw(min); > > =head1 NAME > >@@ -37,6 +43,96 @@ 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. >+ { status => bookingPeriodNotValid }, if booking period is not valid (undef or equal to 0). >+=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} || undef; >+ my $bookings_per_item = $rights->{bookings_per_item} || undef; >+ my $bookings_period_length = $rights->{bookings_period_length} || 0; >+ >+ return { status => 'noBookingsAllowed' } if defined($bookings_allowed_total) && $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 defined($bookings_per_item) && $bookings_per_item <= $bookings_per_item_count; >+ >+ >+ my $querycount; >+ if (C4::Context->preference('item-level_itypes')) { >+ $querycount = q{ >+ SELECT count(*) AS count >+ FROM bookings AS b >+ LEFT JOIN items AS i ON (b.item_id=i.itemnumber) >+ WHERE b.patron_id = ? >+ AND i.itype = ? >+ }; >+ } else { >+ $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 defined($bookings_allowed_total) && $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 => 'bookingPeriodNotValid'} if $bookings_period_length == 0; >+ 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 >@@ -144,6 +240,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 d558c1e781..1a27b21260 100644 >--- a/Koha/CirculationRules.pm >+++ b/Koha/CirculationRules.pm >@@ -222,6 +222,15 @@ our $RULE_KINDS = { > bookings_trail_period => { > scope => [ 'branchcode', '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 79e5b6ad8c..bad8f4037e 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 945614cd90..8682e36ac3 100644 >--- a/Koha/REST/V1/Bookings.pm >+++ b/Koha/REST/V1/Bookings.pm >@@ -96,6 +96,24 @@ 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), >+ 'bookingPeriodNotValid' => sprintf('Booking period must be valid'), >+ '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 6a0895448f..964fccf9fb 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, > } > } > ); >@@ -314,6 +317,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, >@@ -356,6 +362,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 0b143b0cea..ec961564ff 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 >@@ -143,6 +143,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 %] >@@ -204,8 +207,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 %]"> >@@ -378,6 +384,27 @@ > [% 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> >+ [% IF bookings_period_length.defined && bookings_period_length != '' %] >+ [% bookings_period_length | html %] >+ [% ELSE %] >+ <span>Not defined</span> >+ [% END %] >+ </td> > [% IF Koha.Preference('ArticleRequests') %] > <td data-code="[% article_requests | html %]"> > [% IF article_requests == 'no' %] >@@ -528,6 +555,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"> >@@ -599,6 +629,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/modals/place_booking.js b/koha-tmpl/intranet-tmpl/prog/js/modals/place_booking.js >index 4a168d107b..86a3ff2ab9 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/modals/place_booking.js >+++ b/koha-tmpl/intranet-tmpl/prog/js/modals/place_booking.js >@@ -1084,9 +1084,11 @@ $("#placeBookingForm").on("submit", function (e) { > }); > > posting.fail(function (data) { >+ var error = data.responseJSON.error; >+ var errorMessage = error ? error : "Failure"; > $("#booking_result").replaceWith( > '<div id="booking_result" class="alert alert-danger">' + >- __("Failure") + >+ _(errorMessage) + > "</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 36271
:
163546
|
164627
|
165061
|
165063
|
166809
|
166907
|
166930
|
166931
|
166932
|
166933
|
166952
|
166957
|
166962
|
170197
|
172866
| 172924