diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 2f91511dbd..093b12eb1a 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -853,6 +853,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 6969c115ed..07e1a9b931 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 25ceac1470..c2b6395ce9 100644 --- a/Koha/Patron.pm +++ b/Koha/Patron.pm @@ -486,6 +486,52 @@ 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_guarantor}; + my $include_this_patron = $params->{include_this_patron}; + + my @guarantors; + if ( $only_this_guarantor ) { + @guarantors = $self->guarantee_relationships->count ? ( $self ) : (); + } elsif ( $self->guarantor_relationships->count ) { + # 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 $guarantor (@guarantors) { + $non_issues_charges += $guarantor->account->non_issues_charges if $include_guarantors && !$seen->{ $guarantor->id }; + + # We've added what the guarantor owes, not added in that guarantor's guarantees as well + my @guarantees = map { $_->guarantee } $guarantor->guarantee_relationships(); + my $guarantees_non_issues_charges = 0; + foreach my $guarantee (@guarantees) { + next if $seen->{ $guarantee->id }; + $guarantees_non_issues_charges += $guarantee->account->non_issues_charges; + # Mark this guarantee as seen so we don't double count a guarantee linked to multiple guarantors + $seen->{ $guarantee->id } = 1; + } + + $non_issues_charges += $guarantees_non_issues_charges; + $seen->{ $guarantor->id } = 1; + } + + 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 dcb206119c..33d3614b22 100755 --- a/circ/circulation.pl +++ b/circ/circulation.pl @@ -379,6 +379,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..f36ce497cd --- /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 checkouts 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/mandatory/sysprefs.sql b/installer/data/mysql/mandatory/sysprefs.sql index 313296cef3..0b1028cadf 100644 --- a/installer/data/mysql/mandatory/sysprefs.sql +++ b/installer/data/mysql/mandatory/sysprefs.sql @@ -335,8 +335,9 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('NewItemsDefaultLocation','','','If set, all new items will have a location of the given Location Code ( Authorized Value type LOC )',''), ('NewsAuthorDisplay','none','none|opac|staff|both','Display the author name for news items.','Choice'), ('NewsToolEditor','tinymce','tinymce|codemirror','Choose tool for editing News.', 'Choice'), -('noissuescharge','5','','Define maximum amount withstanding before check outs are blocked','Integer'), -('NoIssuesChargeGuarantees','','','Define maximum amount withstanding before check outs are blocked','Integer'), +('noissuescharge','5','','Define maximum amount withstanding before checkouts are blocked','Integer'), +('NoIssuesChargeGuarantees','','','Define maximum amount withstanding before checkouts are blocked','Integer'), +('NoIssuesChargeGuarantorsWithGuarantees','','','Define maximum amount withstanding before checkouts are blocked including guarantors and their other guarantees','Integer'), ('noItemTypeImages','0',NULL,'If ON, disables itemtype images in the staff interface','YesNo'), ('NoRefundOnLostReturnedItemsAge','','','Do not refund lost item fees if item is lost for more than this number of days','Integer'), ('NoRenewalBeforePrecision','exact_time','date|exact_time','Calculate "No renewal before" based on date only or exact time of due date','Choice'), 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 dd803e71b2..9fd1b0ff6c 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 @@ -355,6 +355,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 778cdb3d9f..c38fb930e6 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt @@ -106,6 +106,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 %] @@ -316,6 +320,10 @@