From 9fa814f421fce5fe5ab1b8395c3a09ed3e523959 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 | 49 ++++++++---- Koha/Patron/Quotas.pm | 78 +++++++++++++------ circ/circulation.pl | 3 +- .../prog/en/modules/circ/circulation.tt | 23 +++++- 4 files changed, 113 insertions(+), 40 deletions(-) diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 0fde83f555..db5b0741a8 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -68,6 +68,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 ); @@ -1371,7 +1372,11 @@ 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, @@ -1620,6 +1625,7 @@ sub AddIssue { my $auto_renew = $params && $params->{auto_renew}; my $cancel_recall = $params && $params->{cancel_recall}; my $recall_id = $params && $params->{recall_id}; + my $selected_quota_id = $params && $params->{selected_quota_id}; my $dbh = C4::Context->dbh; my $barcodecheck = CheckValidBarcode($barcode); @@ -1979,9 +1985,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, }); @@ -3337,10 +3350,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"); + } } } @@ -3457,12 +3475,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 6b5adba7b8..bc64fc27c2 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 ( $self, $patron_id ) = @_; - my $patron_quota = undef; + 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 0a9906028e..7e75c2b401 100755 --- a/circ/circulation.pl +++ b/circ/circulation.pl @@ -186,6 +186,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{}; @@ -547,7 +548,7 @@ if ( @$barcodes && $op eq 'cud-checkout' ) { { onsite_checkout => $onsite_checkout, auto_renew => $session->param('auto_renew'), switch_onsite_checkout => $switch_onsite_checkout, cancel_recall => $cancel_recall, - recall_id => $recall_id, + recall_id => $recall_id, 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 a1a2b491a6..4f65855b0c 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt @@ -158,7 +158,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 %] @@ -402,6 +402,25 @@ [% END %] [% END %] + [% IF ( QUOTA_SELECT ) %] +

    + + +

    + [% END %] + @@ -663,7 +682,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.50.1