From 389ca58978e5d47ef43af59c598a810a18cfc99e Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Fri, 10 Jan 2020 11:59:56 -0500 Subject: [PATCH] Bug 19382: Add ability to block guarantees based on fees owed by guarantor and other guarantees Some libraries would like to not only block the circulation of a guarantor based on fines owed by guarantees, but would also like to block circulation for all guarantees as well. Basically, if a family as a whole reaches a certain threshold of fines, the entire family will be blocked from checking out items. Test Plan: 1) Apply this patch 2) Set NoIssuesChargeGuarantorsWithGuarantees to $14 3) Create a family of four ( 2 guarantors, 2 guarantees ) where the parents guarantee both children 4) Give 3 of the 4 a $5 fine 5) None of them should be able to check out items --- C4/Circulation.pm | 15 ++ C4/SIP/ILS/Patron.pm | 10 +- Koha/Patron.pm | 45 ++++ circ/circulation.pl | 6 + .../data/mysql/atomicupdate/bug_19283.perl | 10 + installer/data/mysql/sysprefs.sql | 1 + .../admin/preferences/circulation.pref | 5 + .../prog/en/modules/circ/circulation.tt | 8 + opac/sco/sco-main.pl | 2 +- t/db_dependent/Koha/Patron.t | 206 +++++++++++++++++- 10 files changed, 303 insertions(+), 5 deletions(-) create mode 100644 installer/data/mysql/atomicupdate/bug_19283.perl diff --git a/C4/Circulation.pm b/C4/Circulation.pm index d2506275e3..ea836b9a39 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -796,6 +796,21 @@ sub CanBookBeIssued { } } + # Check the debt of this patrons guarantors *and* the guarantees of those guarantors + my $no_issues_charge_guarantors = C4::Context->preference("NoIssuesChargeGuarantorsWithGuarantees"); + $no_issues_charge_guarantors = undef unless looks_like_number( $no_issues_charge_guarantors ); + if ( defined $no_issues_charge_guarantors ) { + my $guarantors_non_issues_charges += $patron->relationships_debt({ include_guarantors => 1, only_this_guaranor => 0, include_this_patron => 1 }); + + if ( $guarantors_non_issues_charges > $no_issues_charge_guarantors && !$inprocess && !$allowfineoverride) { + $issuingimpossible{DEBT_GUARANTORS} = $guarantors_non_issues_charges; + } elsif ( $guarantors_non_issues_charges > $no_issues_charge_guarantors && !$inprocess && $allowfineoverride) { + $needsconfirmation{DEBT_GUARANTORS} = $guarantors_non_issues_charges; + } elsif ( $allfinesneedoverride && $guarantors_non_issues_charges > 0 && $guarantors_non_issues_charges <= $no_issues_charge_guarantors && !$inprocess ) { + $needsconfirmation{DEBT_GUARANTORS} = $guarantors_non_issues_charges; + } + } + if ( C4::Context->preference("IssuingInProcess") ) { if ( $non_issues_charges > $amountlimit && !$inprocess && !$allowfineoverride) { $issuingimpossible{DEBT} = $non_issues_charges; diff --git a/C4/SIP/ILS/Patron.pm b/C4/SIP/ILS/Patron.pm index 497ec98771..4ceca70a6f 100644 --- a/C4/SIP/ILS/Patron.pm +++ b/C4/SIP/ILS/Patron.pm @@ -67,10 +67,14 @@ sub new { $dexpiry and $dexpiry =~ s/-//g; # YYYYMMDD # Get fines and add fines for guarantees (depends on preference NoIssuesChargeGuarantees) - my $fines_amount = $flags->{CHARGES}->{amount}; + my $fines_amount = $flags->{CHARGES}->{amount}; #TODO Replace with $patron->account->non_issues_charges $fines_amount = ($fines_amount and $fines_amount > 0) ? $fines_amount : 0; - my $guarantees_fines_amount = $flags->{CHARGES_GUARANTEES} ? $flags->{CHARGES_GUARANTEES}->{amount} : 0; - $fines_amount += $guarantees_fines_amount; + if ( C4::Context->preference('NoIssuesChargeGuarantorsWithGuarantees') ) { + $fines_amount += $patron->relationships_debt({ include_guarantors => 1, only_this_guaranor => 0, include_this_patron => 1 }); + } else { + my $guarantees_fines_amount = $flags->{CHARGES_GUARANTEES} ? $flags->{CHARGES_GUARANTEES}->{amount} : 0; #TODO: Replace with $patron->relationships_debt + $fines_amount += $guarantees_fines_amount; + } my $fee_limit = _fee_limit(); my $fine_blocked = $fines_amount > $fee_limit; diff --git a/Koha/Patron.pm b/Koha/Patron.pm index f48e67a696..08017c9b0d 100644 --- a/Koha/Patron.pm +++ b/Koha/Patron.pm @@ -470,6 +470,51 @@ sub guarantee_relationships { ); } +=head3 relationships_debt + +Returns the amount owed by the patron's guarantors *and* the other guarantees of those guarantors + +=cut + +sub relationships_debt { + my ($self, $params) = @_; + + my $include_guarantors = $params->{include_guarantors}; + my $only_this_guarantor = $params->{only_this_guaranor}; + my $include_this_patron = $params->{include_this_patron}; + + my @guarantors; + if ( $only_this_guarantor ) { + @guarantors = $self->guarantor_relationships ? ( $self ) : (); + } elsif ( $self->guarantor_relationships ) { + # I am a guarantee, just get all my guarantors + @guarantors = $self->guarantor_relationships->guarantors; + } else { + # I am a guarantor, I need to get all the guarantors of all my guarantees + @guarantors = map { $_->guarantor_relationships->guarantors } $self->guarantee_relationships->guarantees; + } + + my $non_issues_charges = 0; + my $seen = $include_this_patron ? {} : { $self->id => 1 }; # For tracking members already added to the total + foreach my $g (@guarantors) { + $non_issues_charges += $g->account->non_issues_charges if $include_guarantors; + + # We've added what the guarantor owes, not added in that guarantor's guarantees as well + my @guarantees = map { $_->guarantee } $g->guarantee_relationships(); + my $guarantees_non_issues_charges; + foreach my $g (@guarantees) { + next if $seen->{ $g->id }; + $guarantees_non_issues_charges += $g->account->non_issues_charges; + # Mark this guarantee as seen so we don't double count a guarantee linked to multiple guarantors + $seen->{ $g->id } = 1; + } + + $non_issues_charges += $guarantees_non_issues_charges; + } + + return $non_issues_charges; +} + =head3 housebound_profile Returns the HouseboundProfile associated with this patron. diff --git a/circ/circulation.pl b/circ/circulation.pl index 5bc227d032..368e114dbd 100755 --- a/circ/circulation.pl +++ b/circ/circulation.pl @@ -402,6 +402,12 @@ if (@$barcodes) { } } + if ( $error->{DEBT_GUARANTORS} ) { + $template_params->{DEBT_GUARANTORS} = $error->{DEBT_GUARANTORS}; + $template_params->{IMPOSSIBLE} = 1; + $blocker = 1; + } + if ( $error->{UNKNOWN_BARCODE} or not $onsite_checkout or not C4::Context->preference("OnSiteCheckoutsForce") ) { delete $question->{'DEBT'} if ($debt_confirmed); foreach my $impossible ( keys %$error ) { diff --git a/installer/data/mysql/atomicupdate/bug_19283.perl b/installer/data/mysql/atomicupdate/bug_19283.perl new file mode 100644 index 0000000000..46d94ad641 --- /dev/null +++ b/installer/data/mysql/atomicupdate/bug_19283.perl @@ -0,0 +1,10 @@ +$DBversion = 'XXX'; # will be replaced by the RM +if ( CheckVersion($DBversion) ) { + $dbh->do(q{ + INSERT IGNORE INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type` ) VALUES + ('NoIssuesChargeGuarantorsWithGuarantees','','','Define maximum amount withstanding before check outs are blocked including guarantors and their other guarantees','Integer'); + }); + + print "Upgrade to $DBversion done (Bug 19382 - Add ability to block guarantees based on fees owed by guarantor and other guarantees)\n"; + SetVersion($DBversion); +} diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index 5a4fa4c3a9..37d977a93a 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -325,6 +325,7 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('NewsAuthorDisplay','none','none|opac|staff|both','Display the author name for news items.','Choice'), ('noissuescharge','5','','Define maximum amount withstanding before check outs are blocked','Integer'), ('NoIssuesChargeGuarantees','','','Define maximum amount withstanding before check outs are blocked','Integer'), +('NoIssuesChargeGuarantorsWithGuarantees','','','Define maximum amount withstanding before check outs are blocked including guarantors and their other guarantees','Integer'), ('noItemTypeImages','0',NULL,'If ON, disables item-type images','YesNo'), ('NoRenewalBeforePrecision','exact_time','date|exact_time','Calculate "No renewal before" based on date only or exact time of due date','Choice'), ('NotesBlacklist','',NULL,'List of notes fields that should not appear in the title notes/description separator of details','free'), diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref index deab1560aa..4153c00841 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref @@ -344,6 +344,11 @@ Circulation: - pref: NoIssuesChargeGuarantees class: integer - '[% local_currency %] in fines.' + - + - Prevent a patron from checking out if the patron has guarantors and those guarantor's guarantees owing in total more than + - pref: NoIssuesChargeGuarantorsWithGuarantees + class: integer + - '[% local_currency %] in fines.' - - pref: RentalsInNoissuesCharge choices: 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 71809af850..66fa93ea3b 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt @@ -105,6 +105,10 @@
  • The patron's guarantees collectively have a debt of [% DEBT_GUARANTEES | $Price %].
  • [% END %] + [% IF ( DEBT_GUARANTORS ) %] +
  • The patron's guarantors and their other guarantees collectively have a debt of [% DEBT_GUARANTORS | $Price %].
  • + [% END %] + [% IF ( RENTALCHARGE && RENTALCHARGE > 0 ) %]
  • Rental charge for this item: [% RENTALCHARGE | $Price %]
  • [% END %] @@ -309,6 +313,10 @@