@@ -, +, @@
- A child patron must have a guarantor. This is controlled by
a new syspref ChildNeedsGuarantor.
- A guarantor cannot be a guarantee.
---
Koha/Exceptions/Patron/Relationship.pm | 11 ++-
Koha/Patron.pm | 38 +++++++-
.../data/mysql/atomicupdate/bug_12133.pl | 17 ++++
installer/data/mysql/mandatory/sysprefs.sql | 1 +
.../en/modules/admin/preferences/patrons.pref | 7 ++
.../prog/en/modules/members/memberentrygen.tt | 6 ++
members/memberentry.pl | 32 ++++++-
t/db_dependent/Koha/Patron.t | 90 ++++++++++++++++++-
8 files changed, 196 insertions(+), 6 deletions(-)
create mode 100755 installer/data/mysql/atomicupdate/bug_12133.pl
--- a/Koha/Exceptions/Patron/Relationship.pm
+++ a/Koha/Exceptions/Patron/Relationship.pm
@@ -32,8 +32,12 @@ use Exception::Class (
'Koha::Exceptions::Patron::Relationship::InvalidRelationship' => {
isa => 'Koha::Exceptions::Patron::Relationship',
description => 'The specified relationship is invalid',
- fields => ['relationship','no_relationship']
- }
+ fields => [ 'relationship', 'no_relationship', 'invalid_guarantor' ]
+ },
+ 'Koha::Exceptions::Patron::Relationship::NoGuarantor' => {
+ isa => 'Koha::Exceptions::Patron::Relationship',
+ description => 'Child patron needs a guarantor',
+ },
);
sub full_message {
@@ -46,6 +50,9 @@ sub full_message {
if ( $self->no_relationship ) {
$msg = sprintf( "No relationship passed." );
}
+ elsif ( $self->invalid_guarantor ) {
+ $msg = sprintf("Child patron cannot be a guarantor.");
+ }
else {
$msg = sprintf("Invalid relationship passed, '%s' is not defined.", $self->relationship );
}
--- a/Koha/Patron.pm
+++ a/Koha/Patron.pm
@@ -185,7 +185,10 @@ to db
=cut
sub store {
- my ($self) = @_;
+ my $self = shift;
+ my $params = @_ ? shift : {};
+
+ my $guarantors = $params->{guarantors} // [];
$self->_result->result_source->schema->txn_do(
sub {
@@ -281,6 +284,21 @@ sub store {
$self->borrowernumber(undef);
+ if ( C4::Context->preference('ChildNeedsGuarantor')
+ and $self->is_child
+ and $self->contactname eq ""
+ and !@$guarantors )
+ {
+ Koha::Exceptions::Patron::Relationship::NoGuarantor->throw();
+ }
+
+ foreach my $guarantor (@$guarantors) {
+ if ( $guarantor->is_child ) {
+ Koha::Exceptions::Patron::Relationship::InvalidRelationship
+ ->throw( invalid_guarantor => 1 );
+ }
+ }
+
$self = $self->SUPER::store;
$self->add_enrolment_fee_if_needed(0);
@@ -314,6 +332,24 @@ sub store {
}
+ my @existing_guarantors = $self->guarantor_relationships()->guarantors->as_list;
+ push @$guarantors, @existing_guarantors;
+
+ if ( C4::Context->preference('ChildNeedsGuarantor')
+ and $self->is_child
+ and $self->contactname eq ""
+ and !@$guarantors )
+ {
+ Koha::Exceptions::Patron::Relationship::NoGuarantor->throw();
+ }
+
+ foreach my $guarantor (@$guarantors) {
+ if ( $guarantor->is_child ) {
+ Koha::Exceptions::Patron::Relationship::InvalidRelationship
+ ->throw( invalid_guarantor => 1 );
+ }
+ }
+
# Actionlogs
if ( C4::Context->preference("BorrowersLog") ) {
my $info;
--- a/installer/data/mysql/atomicupdate/bug_12133.pl
+++ a/installer/data/mysql/atomicupdate/bug_12133.pl
@@ -0,0 +1,17 @@
+use Modern::Perl;
+
+return {
+ bug_number => "12133",
+ description => "Add system preference ChildNeedsGuarantor",
+ up => sub {
+ my ($args) = @_;
+ my ($dbh, $out) = @$args{qw(dbh out)};
+
+ $dbh->do( q{
+ INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type)
+ VALUES('ChildNeedsGuarantor', 0, 'If ON, a child patron must have a guarantor when adding the patron.', '', 'YesNo');
+ } );
+
+ say $out "Added new system preference 'ChildNeedsGuarantor')\n";
+ },
+};
--- a/installer/data/mysql/mandatory/sysprefs.sql
+++ a/installer/data/mysql/mandatory/sysprefs.sql
@@ -138,6 +138,7 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `
('ChargeFinesOnClosedDays','0',NULL,'Charge fines on days the library is closed.','YesNo'),
('CheckPrevCheckout','hardno','hardyes|softyes|softno|hardno','By default, for every item checked out, should we warn if the patron has borrowed that item in the past?','Choice'),
('CheckPrevCheckoutDelay','0', NULL,'Maximum number of days that will trigger a warning if the patron has borrowed that item in the past when CheckPrevCheckout is enabled.','free'),
+('ChildNeedsGuarantor', 0, 'If ON, a child patron must have a guarantor when adding the patron.', '', 'YesNo'),
('CircAutoPrintQuickSlip','qslip',NULL,'Choose what should happen when an empty barcode field is submitted in circulation: Display a print quick slip window, Display a print slip window or Clear the screen.','Choice'),
('CircConfirmItemParts', '0', NULL, 'Require staff to confirm that all parts of an item are present at checkin/checkout.', 'Yes/No'),
('CircControl','ItemHomeLibrary','PickupLibrary|PatronLibrary|ItemHomeLibrary','Specify the agency that controls the circulation and fines policy','Choice'),
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/patrons.pref
+++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/patrons.pref
@@ -349,6 +349,13 @@ Patrons:
1: Allow
0: "Don't allow"
- staff to set the ability for a patron's fines to be viewed by linked patrons in the OPAC.
+ -
+ - "A child patron"
+ - pref: "ChildNeedsGuarantor"
+ choices:
+ 1: "must have"
+ 0: "doesn't need"
+ - a guarantor when adding the patron.
Privacy:
-
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/memberentrygen.tt
+++ a/koha-tmpl/intranet-tmpl/prog/en/modules/members/memberentrygen.tt
@@ -195,6 +195,12 @@ legend:hover {
[% IF ERROR_bad_email_alternative %]
The alternative email is invalid.
[% END %]
+ [% IF ( ERROR_child_no_guarantor ) %]
+ A child patron needs a guarantor.
+ [% END %]
+ [% IF ( ERROR_guarantor_is_guarantee ) %]
+ A guarantor cannot be a guarantee.
+ [% END %]
[% END %]
--- a/members/memberentry.pl
+++ a/members/memberentry.pl
@@ -116,6 +116,18 @@ foreach my $id ( @delete_guarantor ) {
$r->delete() if $r;
}
+#Search existing guarantor id(s) and new ones from params
+my @guarantors;
+my @new_guarantor_ids = grep { $_ ne '' } $input->multi_param('new_guarantor_id');
+
+foreach my $new_guarantor_id (@new_guarantor_ids) {
+ my $new_guarantor = Koha::Patrons->find( { borrowernumber => $new_guarantor_id } );
+ push @guarantors, $new_guarantor;
+}
+
+my @existing_guarantors = $patron->guarantor_relationships()->guarantors->as_list unless !$patron;
+push @guarantors, @existing_guarantors;
+
## Deal with debarments
$template->param(
restriction_types => scalar Koha::Patron::Restriction::Types->search()
@@ -265,6 +277,22 @@ if ( ( $op eq 'insert' ) and !$nodouble ) {
}
}
+#Check if guarantor requirements are met
+my $valid_guarantor = @guarantors ? @guarantors : $newdata{'contactname'};
+if ( C4::Context->preference('ChildNeedsGuarantor')
+ && $category->category_type eq 'C'
+ && !$valid_guarantor
+ && ( $op eq 'save' || $op eq 'insert' ) )
+{
+ push @errors, 'ERROR_child_no_guarantor';
+}
+
+foreach my $guarantor (@guarantors) {
+ if ( $guarantor->is_child && ( $op eq 'save' || $op eq 'insert' ) ) {
+ push @errors, 'ERROR_guarantor_is_guarantee';
+ }
+}
+
###############test to take the right zipcode, country and city name ##############
# set only if parameter was passed from the form
$newdata{'city'} = $input->param('city') if defined($input->param('city'));
@@ -428,7 +456,7 @@ if ((!$nok) and $nodouble and ($op eq 'insert' or $op eq 'save')){
if ($op eq 'insert'){
# we know it's not a duplicate borrowernumber or there would already be an error
delete $newdata{password2};
- $patron = eval { Koha::Patron->new(\%newdata)->store };
+ $patron = eval { Koha::Patron->new(\%newdata)->store({ guarantors => \@guarantors }) };
if ( $@ ) {
# FIXME Urgent error handling here, we cannot fail without relevant feedback
# Lot of code will need to be removed from this script to handle exceptions raised by Koha::Patron->store
@@ -514,7 +542,7 @@ if ((!$nok) and $nodouble and ($op eq 'insert' or $op eq 'save')){
delete $newdata{password2};
eval {
- $patron->set(\%newdata)->store if scalar(keys %newdata) > 1; # bug 4508 - avoid crash if we're not
+ $patron->set(\%newdata)->store({ guarantors => \@guarantors }) if scalar(keys %newdata) > 1; # bug 4508 - avoid crash if we're not
# updating any columns in the borrowers table,
# which can happen if we're only editing the
# patron attributes or messaging preferences sections
--- a/t/db_dependent/Koha/Patron.t
+++ a/t/db_dependent/Koha/Patron.t
@@ -19,7 +19,7 @@
use Modern::Perl;
-use Test::More tests => 20;
+use Test::More tests => 21;
use Test::Exception;
use Test::Warn;
@@ -1398,3 +1398,91 @@ subtest 'get_savings tests' => sub {
$schema->storage->txn_rollback;
};
+
+subtest 'guarantor requirements tests' => sub {
+
+ plan tests => 6;
+
+ $schema->storage->txn_begin;
+
+ my $branchcode = $builder->build( { source => 'Branch' } )->{branchcode};
+ my $child_category = $builder->build(
+ { source => 'Category', value => { category_type => 'C' } } )
+ ->{categorycode};
+ my $patron_category = $builder->build(
+ { source => 'Category', value => { category_type => 'A' } } )
+ ->{categorycode};
+
+ t::lib::Mocks::mock_preference( 'ChildNeedsGuarantor', 0 );
+
+ my $child = Koha::Patron->new(
+ {
+ branchcode => $branchcode,
+ categorycode => $child_category,
+ contactname => ''
+ }
+ );
+ $child->store();
+
+ ok(
+ Koha::Patrons->find( $child->id ),
+ 'Child patron can be stored without guarantor when ChildNeedsGuarantor is off.'
+ );
+
+ t::lib::Mocks::mock_preference( 'ChildNeedsGuarantor', 1 );
+
+ my $child2 = Koha::Patron->new(
+ {
+ branchcode => $branchcode,
+ categorycode => $child_category,
+ contactname => ''
+ }
+ );
+ my $child3 = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { categorycode => $child_category }
+ }
+ );
+ my $patron = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { categorycode => $patron_category }
+ }
+ );
+
+ throws_ok { $child2->store(); }
+ 'Koha::Exceptions::Patron::Relationship::NoGuarantor',
+ 'Exception thrown when guarantor is required but not provided.';
+
+ my @guarantors = ( $patron, $child3 );
+ throws_ok { $child2->store( { guarantors => \@guarantors } ); }
+ 'Koha::Exceptions::Patron::Relationship::InvalidRelationship',
+ 'Exception thrown when child patron is added as guarantor.';
+
+ #test ModMember
+ @guarantors = ( $patron );
+ $child2->store( { guarantors => \@guarantors } );
+
+ t::lib::Mocks::mock_preference( 'borrowerRelationship', '' );
+
+ my $relationship = Koha::Patron::Relationship->new(
+ { guarantor_id => $patron->borrowernumber,
+ guarantee_id => $child2->borrowernumber,
+ relationship => ''
+ }
+ );
+ $relationship->store();
+
+ ok( $child2->store(), 'Child patron can be modified and stored when guarantor is stored');
+
+ @guarantors = ( $child3 );
+ throws_ok { $child2->store( { guarantors => \@guarantors } ); }
+ 'Koha::Exceptions::Patron::Relationship::InvalidRelationship',
+ 'Exception thrown when child patron is modified and child patron is added as guarantor.';
+
+ $relationship->delete;
+ throws_ok { $child2->store(); }
+ 'Koha::Exceptions::Patron::Relationship::NoGuarantor',
+ 'Exception thrown when guarantor is deleted.';
+};
--