From e5e19f426114da2d6d2578fbcd259fdec95f58b8 Mon Sep 17 00:00:00 2001 From: Jacob O'Mara Date: Thu, 6 Feb 2025 12:22:06 +0000 Subject: [PATCH] Bug 38924: Add quota picker to checkout confirmation This patch adds the quota selector to the checkout confirmation for if multiple eligable quotas are available Bug 38924: Return all eligable quotas so that the staff user can pick This patch adds the logic to return multiple eligable quotas if there are more than 1, allowing the staff user to pick which to assign an item to. Bug 38924: Add searcher method for currently active quota for patron Bug 38924: amend circ logic to handle multiple eligable quotas This amends the circulations logic to handle the fact that we can return multiple eligable quotas now. This ensures that the same quotas as was used for the checkout will be used for the renewal (both check and assignment) and that we use the selected quota from our confirmation dropdown for checkout of new item Bug 38924: add better name display for quota select at checkout --- C4/Circulation.pm | 51 ++++++++---- Koha/Patron/Quotas.pm | 80 +++++++++++++------ circ/circulation.pl | 4 +- .../prog/en/modules/circ/circulation.tt | 23 +++++- 4 files changed, 116 insertions(+), 42 deletions(-) diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 9f3e47da1ff..1ac764de288 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -69,6 +69,7 @@ use Koha::Library::Hours; use Koha::Patron::Quotas; use Koha::Patron::Quota; use Koha::Patron::Quota::Usage; +use Koha::Patron::Quota::Usages; use Carp qw( carp ); use List::MoreUtils qw( any ); @@ -1377,11 +1378,15 @@ sub CanBookBeIssued { # CHECK FOR QUOTAS if ( my $quota = Koha::Patron::Quotas->get_patron_quota($patron->borrowernumber) ) { - unless ( $quota->has_available_quota ) { + if (ref($quota) eq 'ARRAY') { + # Multiple available quotas found - need confirmation from user + $needsconfirmation{QUOTA_SELECT} = $quota; + } + elsif (!$quota->has_available_quota) { if ( C4::Context->preference("AllowQuotaOverride") ) { $needsconfirmation{QUOTA_EXCEEDED} = { available => $quota->available_quota, - total => $quota->allocation, + total => $quota->allocation, used => $quota->used, }; } @@ -1628,6 +1633,7 @@ sub AddIssue { my $recall_id = $params && $params->{recall_id}; my $confirmations = $params->{confirmations}; my $forced = $params->{forced}; + my $selected_quota_id = $params && $params->{selected_quota_id}; my $dbh = C4::Context->dbh; my $barcodecheck = CheckValidBarcode($barcode); @@ -2013,9 +2019,16 @@ sub AddIssue { if C4::Context->preference('RealTimeHoldsQueue'); # Check quotas and record usage if needed - if ( my $quota = Koha::Patron::Quotas->get_patron_quota($patron->borrowernumber) ) { - # Update patron's used quota value - $quota->add_usage({ + my $quota; + if ($selected_quota_id) { + $quota = Koha::Patron::Quotas->find($selected_quota_id); + } else { + $quota = Koha::Patron::Quotas->get_patron_quota($patron->borrowernumber); + } + + if ($quota) { + # Update patron's used quota value + $quota->add_usage({ patron_id => $patron->patron_id, issue_id => $issue->issue_id, }); @@ -3374,10 +3387,15 @@ sub CanBookBeRenewed { } } - # CHECK FOR QUOTAS - if ( my $quota = Koha::Patron::Quotas->get_patron_quota($patron->borrowernumber) ) { - unless ( $quota->has_available_quota ) { - return ( 0, "QUOTA_EXCEEDED" ); + # # CHECK FOR QUOTAS + if (my $quota_usage = Koha::Patron::Quota::Usages->find({ issue_id => $issue->issue_id })) { + + # Get current active quota for the patron + if (my $active_quota = Koha::Patron::Quotas->get_active_quota($quota_usage->patron_id)) { + + if (!$active_quota->has_available_quota) { + return (0, "QUOTA_EXCEEDED"); + } } } @@ -3496,12 +3514,15 @@ sub AddRenewal { my $circ_library = Koha::Libraries->find( _GetCircControlBranch( $item_object, $patron ) ); # Check quotas and record usage if needed - if ( my $quota = Koha::Patron::Quotas->get_patron_quota($patron->borrowernumber) ) { - # Update patron's used quota value - $quota->add_usage({ - patron_id => $patron->patron_id, - issue_id => $issue->issue_id, - }); + if (my $quota_usage = Koha::Patron::Quota::Usages->find({ issue_id => $issue->issue_id })) { + # Get current active quota for the patron + if (my $active_quota = Koha::Patron::Quotas->get_active_quota($quota_usage->patron_id)) { + # Update patron's used quota value + $active_quota->add_usage({ + patron_id => $patron->patron_id, + issue_id => $issue->issue_id, + }); + } } my $schema = Koha::Database->schema; diff --git a/Koha/Patron/Quotas.pm b/Koha/Patron/Quotas.pm index 8f31767999e..c8e2715a86b 100644 --- a/Koha/Patron/Quotas.pm +++ b/Koha/Patron/Quotas.pm @@ -1,9 +1,9 @@ package Koha::Patron::Quotas; +use base qw(Koha::Objects); use Modern::Perl; use Koha::Patron::Quota; use Koha::Patrons; -use base qw(Koha::Objects); =head1 NAME @@ -25,47 +25,57 @@ Searches for any applicable quota for the given patron. Returns: =cut sub get_patron_quota { - my ($self, $patron_id) = @_; - - my $patron_quota = undef; + my ( $self, $patron_id ) = @_; + + my @available_quotas; my $first_found_quota = undef; - # First check all guarantor quotas if enabled - if (C4::Context->preference('UseGuarantorQuota')) { - my $patron = Koha::Patrons->find($patron_id); + # First check patron's own quota + my $patron_quota = $self->search( + { + patron_id => $patron_id, + start_date => { '<=' => \'CURRENT_DATE' }, + end_date => { '>=' => \'CURRENT_DATE' } + } + )->single; + + if ( $patron_quota && $patron_quota->has_available_quota ) { + push @available_quotas, $patron_quota; + } + $first_found_quota ||= $patron_quota if $patron_quota; + + # Then check all guarantor quotas if enabled + if ( C4::Context->preference('UseGuarantorQuota') ) { + my $patron = Koha::Patrons->find($patron_id); my @guarantors = $patron->guarantor_relationships->guarantors->as_list; foreach my $guarantor (@guarantors) { my $guarantor_quota = $self->search( { - patron_id => $guarantor->borrowernumber, + patron_id => $guarantor->borrowernumber, start_date => { '<=' => \'CURRENT_DATE' }, end_date => { '>=' => \'CURRENT_DATE' } } )->single; if ($guarantor_quota) { - # Return immediately if guarantor quota has availability - if ($guarantor_quota->has_available_quota) { - return $guarantor_quota; - } + # Store first found quota in case we need it later $first_found_quota ||= $guarantor_quota; + + # Collect available guarantor quotas + if ( $guarantor_quota->has_available_quota ) { + push @available_quotas, $guarantor_quota; + } } } } - # Check patron's own quota if no available guarantor quota was found - $patron_quota = $self->search( - { - patron_id => $patron_id, - start_date => { '<=' => \'CURRENT_DATE' }, - end_date => { '>=' => \'CURRENT_DATE' } - } - )->single; - - # Return patron quota if exists, otherwise first found guarantor quota - return $patron_quota || $first_found_quota; + # Return single quota if only one available, array of quotas if multiple available, + # first found quota if none available + return $available_quotas[0] if @available_quotas == 1; + return \@available_quotas if @available_quotas > 1; + return $first_found_quota; } =head3 create_quota @@ -107,6 +117,28 @@ sub get_active_quotas { ); } +=head2 get_active_quota + + my $active_quota = Koha::Patron::Quotas->get_active_quota($patron_id); + +Returns the currently active quota for a patron if one exists. +Active means start_date <= NOW() and end_date >= NOW(). +Returns undef if no active quota found. + +=cut + +sub get_active_quota { + my ( $self, $patron_id ) = @_; + + return Koha::Patron::Quotas->search( + { + patron_id => $patron_id, + start_date => { '<=' => \'CURRENT_DATE' }, + end_date => { '>=' => \'CURRENT_DATE' } + } + )->single; +} + =head2 Internal methods =head3 _type @@ -119,7 +151,7 @@ sub _type { =head3 object_class -Returns the package name for patron quota objects +Returns the package name for koha patron quota objects =cut diff --git a/circ/circulation.pl b/circ/circulation.pl index dddd2a1f5f9..fc03734485f 100755 --- a/circ/circulation.pl +++ b/circ/circulation.pl @@ -187,6 +187,7 @@ if ( $restoreduedatespec && $restoreduedatespec eq "highholds_empty" ) { my $issueconfirmed = $query->param('issueconfirmed'); my $cancelreserve = $query->param('cancelreserve'); my $cancel_recall = $query->param('cancel_recall'); +my $selected_quota_id = $query->param('selected_quota_id'); my $recall_id = $query->param('recall_id'); my $debt_confirmed = $query->param('debt_confirmed') || 0; # Don't show the debt error dialog twice my $charges = $query->param('charges') || q{}; @@ -559,7 +560,8 @@ if ( @$barcodes && $op eq 'cud-checkout' ) { cancel_recall => $cancel_recall, recall_id => $recall_id, confirmations => [ grep { /^[A-Z_]+$/ } keys %{$needsconfirmation} ], - forced => [ keys %{$issuingimpossible} ] + forced => [ keys %{$issuingimpossible} ], + selected_quota_id => $selected_quota_id } ); $template_params->{issue} = $issue; diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt index 27fbfbde2f4..f170f5d042e 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt @@ -157,7 +157,7 @@ [%IF ( QUOTA_EXCEEDED ) %]
  • - Quota Exceeded [% QUOTA_EXCEEDED | html %]. + Quota Exceeded. Available: [% QUOTA_EXCEEDED.available | html %] / [% QUOTA_EXCEEDED.total | html %]. [% IF CAN_user_circulate_force_checkout %] Check out anyway? [% END %] @@ -401,6 +401,25 @@ [% END %] [% END %] + [% IF ( QUOTA_SELECT ) %] +

    + + +

    + [% END %] + @@ -662,7 +681,7 @@ [% END %] [%IF ( QUOTA_EXCEEDED ) %] -
  • Quota Exceeded [% QUOTA_EXCEEDED | html %].
  • +
  • Quota Exceeded. Available: [% QUOTA_EXCEEDED.available | html %] / [% QUOTA_EXCEEDED.total | html %].
  • [% END %] [% IF ( EXPIRED ) %] -- 2.39.5