From 0f0bacd74ca5e623d41e5f99a2c520a7f920533e Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Mon, 2 May 2016 15:00:39 +0000 Subject: [PATCH] Bug 14570 - Make it possible to add multiple guarantors to a record This patch adds the ability to set an unlimited number of guarantors for a given patron. As before, each guarantor may be linked to another Koha patron, and all the behavior that applies to a given guarantor remains the same. Test Plan: 1) Apply this patch 2) Run updatedatabase.pl 3) Find some patrons with guarantors, verify the still have their guarantor 4) Test adding and removing guarantors on a patron record, both Koha users and not 5) Verify the "Add child" button works 6) Verify NoIssuesChargeGuarantees still works 7) Verify tools/cleanborrowers.pl will not delete a guarantor 8) Verify the guarantors are displayed on moremember.pl 9) Verify the guarantor is removed by members/update-child.pl 10) Verify the guarantor is removed by misc/cronjobs/j2a.pl 11) Verify import patrons converts guarantor_id, relationship, contactfirstname, and contactsurname into a guarantor 12) prove t/Patron.t 13) prove t/db_dependent/Circulation.t 14) prove t/db_dependent/Circulation/NoIssuesChargeGuarantees.t 15) prove t/db_dependent/Items.t 16) prove t/db_dependent/Koha/Patrons.t 17) prove t/db_dependent/Members.t 18) prove t/db_dependent/Patron/Relationships.t --- C4/Circulation.pm | 2 +- C4/Members.pm | 12 +- Koha/Item.pm | 11 + Koha/Patron.pm | 85 ++++-- Koha/Patron/Checkout.pm | 57 ++++ Koha/Patron/Checkouts.pm | 50 ++++ Koha/Patron/Relationship.pm | 73 +++++ Koha/Patron/Relationships.pm | 94 ++++++ Koha/Schema/Result/Borrower.pm | 34 ++- Koha/Schema/Result/Relationship.pm | 132 +++++++++ circ/circulation.pl | 8 +- installer/data/mysql/atomicupdate/bug_14560.sql | 18 ++ installer/data/mysql/kohastructure.sql | 33 ++- koha-tmpl/intranet-tmpl/prog/en/columns.def | 1 - .../prog/en/includes/members-toolbar.inc | 9 +- .../prog/en/modules/circ/circulation.tt | 2 +- .../prog/en/modules/members/memberentrygen.tt | 323 +++++++++++++-------- .../prog/en/modules/members/moremember-brief.tt | 27 +- .../prog/en/modules/members/moremember.tt | 30 +- koha-tmpl/intranet-tmpl/prog/js/members.js | 90 +++--- .../bootstrap/en/modules/opac-memberentry.tt | 9 +- .../opac-tmpl/bootstrap/en/modules/opac-privacy.tt | 9 +- .../opac-tmpl/bootstrap/en/modules/opac-user.tt | 10 +- members/deletemem.pl | 22 +- members/memberentry.pl | 172 +++++++---- members/moremember.pl | 30 +- members/update-child.pl | 47 +-- misc/cronjobs/j2a.pl | 55 +++- opac/opac-memberentry.pl | 5 +- opac/opac-user.pl | 17 +- t/Patron.t | 24 +- t/db_dependent/Circulation.t | 9 +- .../Circulation/NoIssuesChargeGuarantees.t | 12 +- t/db_dependent/Items.t | 6 +- t/db_dependent/Koha/Patrons.t | 36 ++- t/db_dependent/Members.t | 6 +- t/db_dependent/Patron/Relationships.t | 181 ++++++++++++ tools/import_borrowers.pl | 26 ++ 38 files changed, 1326 insertions(+), 441 deletions(-) create mode 100644 Koha/Patron/Checkout.pm create mode 100644 Koha/Patron/Checkouts.pm create mode 100644 Koha/Patron/Relationship.pm create mode 100644 Koha/Patron/Relationships.pm create mode 100644 Koha/Schema/Result/Relationship.pm create mode 100644 installer/data/mysql/atomicupdate/bug_14560.sql create mode 100755 t/db_dependent/Patron/Relationships.t diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 50e6c10..fc3d1f0 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -862,7 +862,7 @@ sub CanBookBeIssued { $no_issues_charge_guarantees = undef unless looks_like_number( $no_issues_charge_guarantees ); if ( defined $no_issues_charge_guarantees ) { my $p = Koha::Patrons->find( $borrower->{borrowernumber} ); - my @guarantees = $p->guarantees(); + my @guarantees = map { $_->guarantee } $p->guarantee_relationships(); my $guarantees_non_issues_charges; foreach my $g ( @guarantees ) { my ( $b, $n, $o ) = C4::Members::GetMemberAccountBalance( $g->id ); diff --git a/C4/Members.pm b/C4/Members.pm index 383799d..710df88 100644 --- a/C4/Members.pm +++ b/C4/Members.pm @@ -1820,18 +1820,18 @@ sub GetBorrowersToExpunge { FROM borrowers JOIN categories USING (categorycode) LEFT JOIN ( - SELECT guarantorid - FROM borrowers - WHERE guarantorid IS NOT NULL - AND guarantorid <> 0 - ) as tmp ON borrowers.borrowernumber=tmp.guarantorid + SELECT guarantor_id + FROM relationships + WHERE guarantor_id IS NOT NULL + AND guarantor_id <> 0 + ) as tmp ON borrowers.borrowernumber=tmp.guarantor_id LEFT JOIN old_issues USING (borrowernumber) LEFT JOIN issues USING (borrowernumber)|; if ( $filterpatronlist ){ $query .= q| LEFT JOIN patron_list_patrons USING (borrowernumber)|; } $query .= q| WHERE category_type <> 'S' - AND tmp.guarantorid IS NULL + AND tmp.guarantor_id IS NULL |; my @query_params; if ( $filterbranch && $filterbranch ne "" ) { diff --git a/Koha/Item.pm b/Koha/Item.pm index d721537..59a225f 100644 --- a/Koha/Item.pm +++ b/Koha/Item.pm @@ -23,6 +23,7 @@ use Carp; use Koha::Database; +use Koha::Biblios; use Koha::Patrons; use Koha::Libraries; @@ -107,6 +108,16 @@ sub last_returned_by { } } +=head3 biblio + +=cut + +sub biblio { + my ( $self ) = @_; + + return Koha::Biblios->find( $self->biblionumber ); +} + =head3 type =cut diff --git a/Koha/Patron.pm b/Koha/Patron.pm index ac62594..8855074 100644 --- a/Koha/Patron.pm +++ b/Koha/Patron.pm @@ -24,6 +24,8 @@ use Carp; use Koha::Database; use Koha::Patrons; use Koha::Patron::Images; +use Koha::Patron::Checkouts; +use Koha::Patron::Relationships; use base qw(Koha::Object); @@ -37,36 +39,60 @@ Koha::Patron - Koha Patron Object class =cut -=head3 guarantor - -Returns a Koha::Patron object for this patron's guarantor +=head3 image =cut -sub guarantor { +sub image { my ( $self ) = @_; - return unless $self->guarantorid(); - - return Koha::Patrons->find( $self->guarantorid() ); + return Koha::Patron::Images->find( $self->borrowernumber ) } -sub image { +=head3 checkouts + +=cut + +sub checkouts { my ( $self ) = @_; - return Koha::Patron::Images->find( $self->borrowernumber ) + return Koha::Patron::Checkouts->search( { borrowernumber => $self->id } ); } -=head3 guarantees +=head3 guarantor_relationships + +Returns Koha::Patron::Relationships object for this patron's guarantor + +Returns the set of relationships for the patrons that are guarantors for this patron. -Returns the guarantees (list of Koha::Patron) of this patron +This is returned instead of a Koha::Patron object because the guarantor +may not exist as a patron in Koha. If this is true, the guarantors name +exists in the Koha::Patron::Relationship object and will have no guarantor_id. =cut -sub guarantees { - my ( $self ) = @_; +sub guarantor_relationships { + my ($self) = @_; - return Koha::Patrons->search( { guarantorid => $self->borrowernumber } ); + return Koha::Patron::Relationships->search( { guarantee_id => $self->id } ); +} + +=head3 guarantee_relationships + +Returns Koha::Patron::Relationships object for this patron's guarantors + +Returns the set of relationships for the patrons that are guarantees for this patron. + +The method returns Koha::Patron::Relationship objects for the sake +of consistency with the guantors method. +A guarantee by definition must exist as a patron in Koha. + +=cut + +sub guarantee_relationships { + my ($self) = @_; + + return Koha::Patron::Relationships->search( { guarantor_id => $self->id } ); } =head3 siblings @@ -76,23 +102,22 @@ Returns the siblings of this patron. =cut sub siblings { - my ( $self ) = @_; + my ($self) = @_; + + my @guarantors = $self->guarantor_relationships()->guarantors(); + + return unless @guarantors; + + my @siblings = + map { $_->guarantee_relationships()->guarantees() } @guarantors; + + return unless @siblings; + + my %seen; + @siblings = + grep { !$seen{ $_->id }++ && ( $_->id != $self->id ) } @siblings; - my $guarantor = $self->guarantor; - - return unless $guarantor; - - return Koha::Patrons->search( - { - guarantorid => { - '!=' => undef, - '=' => $guarantor->id, - }, - borrowernumber => { - '!=' => $self->borrowernumber, - } - } - ); + return wantarray ? @siblings : Koha::Patrons->search( { borrowernumber => { -in => [ map { $_->id } @siblings ] } } ); } =head3 type diff --git a/Koha/Patron/Checkout.pm b/Koha/Patron/Checkout.pm new file mode 100644 index 0000000..3b9723e --- /dev/null +++ b/Koha/Patron/Checkout.pm @@ -0,0 +1,57 @@ +package Koha::Patron::Checkout; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Carp; + +use C4::Context; + +use Koha::Database; +use Koha::Items; + +use base qw(Koha::Object); + +=head1 NAME + +Koha::Patron::Checkout - Koha Checkout Object class + +=head1 API + +=head2 Class Methods + +=cut + +=head3 item + +=cut + +sub item { + my ( $self ) = @_; + + return Koha::Items->find( $self->itemnumber ); +} + +=head3 _type + +=cut + +sub _type { + return 'Issue'; +} + +1; diff --git a/Koha/Patron/Checkouts.pm b/Koha/Patron/Checkouts.pm new file mode 100644 index 0000000..37f20b9 --- /dev/null +++ b/Koha/Patron/Checkouts.pm @@ -0,0 +1,50 @@ +package Koha::Patron::Checkouts; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Carp; + +use Koha::Database; + +use Koha::Patron::Checkout; + +use base qw(Koha::Objects); + +=head1 NAME + +Koha::Patron::Checkouts - Koha Checkout Object set class + +=head1 API + +=head2 Class Methods + +=cut + +=head3 _type + +=cut + +sub _type { + return 'Issue'; +} + +sub object_class { + return 'Koha::Patron::Checkout'; +} + +1; diff --git a/Koha/Patron/Relationship.pm b/Koha/Patron/Relationship.pm new file mode 100644 index 0000000..424acbf --- /dev/null +++ b/Koha/Patron/Relationship.pm @@ -0,0 +1,73 @@ +package Koha::Patron::Relationship; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Carp; + +use Koha::Database; + +use base qw(Koha::Object); + +=head1 NAME + +Koha::Patron::Relationship - A class to represent relationships between patrons + +Patrons in Koha may be guarantors or guarantees. This class models that relationship +and provides a way to access those relationships. + +=head1 API + +=head2 Class Methods + +=cut + +=head3 guarantor + +Returns the Koha::Patron object for the guarantor, if there is one + +=cut + +sub guarantor { + my ( $self ) = @_; + + return unless $self->guarantor_id; + + return Koha::Patrons->find( $self->guarantor_id ); +} + +=head3 guarantee + +Returns the Koha::Patron object for the guarantee + +=cut + +sub guarantee { + my ( $self ) = @_; + + return Koha::Patrons->find( $self->guarantee_id ); +} + +=head3 type + +=cut + +sub _type { + return 'Relationship'; +} + +1; diff --git a/Koha/Patron/Relationships.pm b/Koha/Patron/Relationships.pm new file mode 100644 index 0000000..e9da572 --- /dev/null +++ b/Koha/Patron/Relationships.pm @@ -0,0 +1,94 @@ +package Koha::Patron::Relationships; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Carp; +use List::MoreUtils qw( uniq ); + +use Koha::Database; +use Koha::Patrons; +use Koha::Patron::Relationship; + +use base qw(Koha::Objects); + +=head1 NAME + +Koha::Patron::Relationships - Koha Patron Relationship Object set class + +=head1 API + +=head2 Class Methods + +=cut + +=head3 guarantors + +Returns all the guarantors in this set of relationships as a list of Koha::Patron objects +or as a Koha::Patrons object depending on the calling context + +=cut + +sub guarantors { + my ($self) = @_; + + my $rs = $self->_resultset(); + + my @guarantor_ids = $rs->get_column('guarantor_id')->all(); + # Guarantors may not have a guarantor_id, strip out undefs + @guarantor_ids = grep { defined $_ } @guarantor_ids; + @guarantor_ids = uniq( @guarantor_ids ); + + my $guarantors = Koha::Patrons->search( { borrowernumber => \@guarantor_ids } ); + + return wantarray ? $guarantors->as_list : $guarantors; +} + +=head3 guarantees + +Returns all the guarantees in this set of relationships as a list of Koha::Patron objects +or as a Koha::Patrons object depending on the calling context + +=cut + +sub guarantees { + my ($self) = @_; + + my $rs = $self->_resultset(); + + my @guarantee_ids = uniq( $rs->get_column('guarantee_id')->all() ); + + my $guarantees = Koha::Patrons->search( { borrowernumber => \@guarantee_ids } ); + + return wantarray ? $guarantees->as_list : $guarantees; +} + +=cut + +=head3 type + +=cut + +sub _type { + return 'Relationship'; +} + +sub object_class { + return 'Koha::Patron::Relationship'; +} + +1; diff --git a/Koha/Schema/Result/Borrower.pm b/Koha/Schema/Result/Borrower.pm index 39880aa..f1573c5 100644 --- a/Koha/Schema/Result/Borrower.pm +++ b/Koha/Schema/Result/Borrower.pm @@ -1008,6 +1008,36 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); +=head2 relationships_guarantees + +Type: has_many + +Related object: L + +=cut + +__PACKAGE__->has_many( + "relationships_guarantees", + "Koha::Schema::Result::Relationship", + { "foreign.guarantee_id" => "self.borrowernumber" }, + { cascade_copy => 0, cascade_delete => 0 }, +); + +=head2 relationships_guarantors + +Type: has_many + +Related object: L + +=cut + +__PACKAGE__->has_many( + "relationships_guarantors", + "Koha::Schema::Result::Relationship", + { "foreign.guarantor_id" => "self.borrowernumber" }, + { cascade_copy => 0, cascade_delete => 0 }, +); + =head2 reserves Type: has_many @@ -1204,8 +1234,8 @@ Composing rels: L -> ordernumber __PACKAGE__->many_to_many("ordernumbers", "aqorder_users", "ordernumber"); -# Created by DBIx::Class::Schema::Loader v0.07042 @ 2016-02-14 12:46:14 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:bI6qJYw+ulTUwA7XMCkkRw +# Created by DBIx::Class::Schema::Loader v0.07042 @ 2016-05-03 13:19:34 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1u8SP/oLjNEtEIOYd14t9w __PACKAGE__->belongs_to( "guarantor", diff --git a/Koha/Schema/Result/Relationship.pm b/Koha/Schema/Result/Relationship.pm new file mode 100644 index 0000000..c815df4 --- /dev/null +++ b/Koha/Schema/Result/Relationship.pm @@ -0,0 +1,132 @@ +use utf8; +package Koha::Schema::Result::Relationship; + +# Created by DBIx::Class::Schema::Loader +# DO NOT MODIFY THE FIRST PART OF THIS FILE + +=head1 NAME + +Koha::Schema::Result::Relationship + +=cut + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + +=head1 TABLE: C + +=cut + +__PACKAGE__->table("relationships"); + +=head1 ACCESSORS + +=head2 id + + data_type: 'integer' + is_auto_increment: 1 + is_nullable: 0 + +=head2 guarantor_id + + data_type: 'integer' + is_foreign_key: 1 + is_nullable: 1 + +=head2 guarantee_id + + data_type: 'integer' + is_foreign_key: 1 + is_nullable: 0 + +=head2 relationship + + data_type: 'varchar' + is_nullable: 0 + size: 100 + +=head2 surname + + data_type: 'mediumtext' + is_nullable: 1 + +=head2 firstname + + data_type: 'mediumtext' + is_nullable: 1 + +=cut + +__PACKAGE__->add_columns( + "id", + { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, + "guarantor_id", + { data_type => "integer", is_foreign_key => 1, is_nullable => 1 }, + "guarantee_id", + { data_type => "integer", is_foreign_key => 1, is_nullable => 0 }, + "relationship", + { data_type => "varchar", is_nullable => 0, size => 100 }, + "surname", + { data_type => "mediumtext", is_nullable => 1 }, + "firstname", + { data_type => "mediumtext", is_nullable => 1 }, +); + +=head1 PRIMARY KEY + +=over 4 + +=item * L + +=back + +=cut + +__PACKAGE__->set_primary_key("id"); + +=head1 RELATIONS + +=head2 guarantee + +Type: belongs_to + +Related object: L + +=cut + +__PACKAGE__->belongs_to( + "guarantee", + "Koha::Schema::Result::Borrower", + { borrowernumber => "guarantee_id" }, + { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, +); + +=head2 guarantor + +Type: belongs_to + +Related object: L + +=cut + +__PACKAGE__->belongs_to( + "guarantor", + "Koha::Schema::Result::Borrower", + { borrowernumber => "guarantor_id" }, + { + is_deferrable => 1, + join_type => "LEFT", + on_delete => "CASCADE", + on_update => "CASCADE", + }, +); + + +# Created by DBIx::Class::Schema::Loader v0.07042 @ 2016-05-03 13:19:34 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:2Nv437KMrKHvpQleXLOjYw + + +# You can replace this text with custom code or comments, and it will be preserved on regeneration +1; diff --git a/circ/circulation.pl b/circ/circulation.pl index b2bcdd1..294eff2 100755 --- a/circ/circulation.pl +++ b/circ/circulation.pl @@ -596,11 +596,11 @@ my $view = $batch my @relatives; if ( $borrowernumber ) { if ( my $patron = Koha::Patrons->find( $borrower->{borrowernumber} ) ) { - if ( my $guarantor = $patron->guarantor ) { - push @relatives, $guarantor->borrowernumber; - push @relatives, $_->borrowernumber for $patron->siblings; + if ( my @guarantors = $patron->guarantor_relationships()->guarantors() ) { + push( @relatives, $_->id ) for @guarantors; + push( @relatives, $_->id ) for $patron->siblings(); } else { - push @relatives, $_->borrowernumber for $patron->guarantees; + push( @relatives, $_->id ) for $patron->guarantee_relationships()->guarantees(); } } } diff --git a/installer/data/mysql/atomicupdate/bug_14560.sql b/installer/data/mysql/atomicupdate/bug_14560.sql new file mode 100644 index 0000000..9ba0743 --- /dev/null +++ b/installer/data/mysql/atomicupdate/bug_14560.sql @@ -0,0 +1,18 @@ +DROP TABLE IF EXISTS relationships; +CREATE TABLE `relationships` ( + id INT(11) NOT NULL AUTO_INCREMENT, + guarantor_id INT(11) NULL DEFAULT NULL, + guarantee_id INT(11) NOT NULL, + relationship VARCHAR(100) COLLATE utf8_unicode_ci NOT NULL, + surname MEDIUMTEXT COLLATE utf8_unicode_ci NULL DEFAULT NULL, + firstname MEDIUMTEXT COLLATE utf8_unicode_ci NULL DEFAULT NULL, + PRIMARY KEY (id), + CONSTRAINT r_guarantor FOREIGN KEY ( guarantor_id ) REFERENCES borrowers ( borrowernumber ) ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT r_guarantee FOREIGN KEY ( guarantee_id ) REFERENCES borrowers ( borrowernumber ) ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +UPDATE borrowers LEFT JOIN borrowers guarantor ON ( borrowers.guarantorid = guarantor.borrowernumber ) SET borrowers.guarantorid = NULL WHERE guarantor.borrowernumber IS NULL; + +INSERT INTO relationships ( guarantor_id, guarantee_id, relationship, surname, firstname ) SELECT guarantorid, borrowernumber, relationship, contactname, contactfirstname FROM borrowers WHERE guarantorid IS NOT NULL OR contactname != ""; + +ALTER TABLE borrowers DROP guarantorid, DROP relationship, DROP contactname, DROP contactfirstname, DROP contacttitle; diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index c5a3f4a..90a6429 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -591,12 +591,7 @@ CREATE TABLE `deletedborrowers` ( -- stores data related to the patrons/borrower `lost` tinyint(1) default NULL, -- set to 1 for yes and 0 for no, flag to note that library marked this patron/borrower as having lost their card `debarred` date default NULL, -- until this date the patron can only check-in (no loans, no holds, etc.), is a fine based on days instead of money (YYY-MM-DD) `debarredcomment` VARCHAR(255) DEFAULT NULL, -- comment on the stop of patron - `contactname` mediumtext, -- used for children and profesionals to include surname or last name of guarentor or organization name - `contactfirstname` text, -- used for children to include first name of guarentor - `contacttitle` text, -- used for children to include title (Mr., Mrs., etc) of guarentor - `guarantorid` int(11) default NULL, -- borrowernumber used for children or professionals to link them to guarentors or organizations `borrowernotes` mediumtext, -- a note on the patron/borrower's account that is only visible in the staff client - `relationship` varchar(100) default NULL, -- used for children to include the relationship to their guarentor `sex` varchar(1) default NULL, -- patron/borrower's gender `password` varchar(60) default NULL, -- patron/borrower's encrypted password `flags` int(11) default NULL, -- will include a number associated with the staff member's permissions @@ -1596,12 +1591,7 @@ CREATE TABLE `borrowers` ( -- this table includes information about your patrons `lost` tinyint(1) default NULL, -- set to 1 for yes and 0 for no, flag to note that library marked this patron/borrower as having lost their card `debarred` date default NULL, -- until this date the patron can only check-in (no loans, no holds, etc.), is a fine based on days instead of money (YYY-MM-DD) `debarredcomment` VARCHAR(255) DEFAULT NULL, -- comment on the stop of the patron - `contactname` mediumtext, -- used for children and profesionals to include surname or last name of guarentor or organization name - `contactfirstname` text, -- used for children to include first name of guarentor - `contacttitle` text, -- used for children to include title (Mr., Mrs., etc) of guarentor - `guarantorid` int(11) default NULL, -- borrowernumber used for children or professionals to link them to guarentors or organizations `borrowernotes` mediumtext, -- a note on the patron/borrower's account that is only visible in the staff client - `relationship` varchar(100) default NULL, -- used for children to include the relationship to their guarentor `sex` varchar(1) default NULL, -- patron/borrower's gender `password` varchar(60) default NULL, -- patron/borrower's encrypted password `flags` int(11) default NULL, -- will include a number associated with the staff member's permissions @@ -1628,7 +1618,6 @@ CREATE TABLE `borrowers` ( -- this table includes information about your patrons KEY `categorycode` (`categorycode`), KEY `branchcode` (`branchcode`), UNIQUE KEY `userid` (`userid`), - KEY `guarantorid` (`guarantorid`), KEY `surname_idx` (`surname`(255)), KEY `firstname_idx` (`firstname`(255)), KEY `othernames_idx` (`othernames`(255)), @@ -3349,12 +3338,7 @@ CREATE TABLE IF NOT EXISTS `borrower_modifications` ( `lost` tinyint(1) DEFAULT NULL, `debarred` date DEFAULT NULL, `debarredcomment` varchar(255) DEFAULT NULL, - `contactname` mediumtext, - `contactfirstname` text, - `contacttitle` text, - `guarantorid` int(11) DEFAULT NULL, `borrowernotes` mediumtext, - `relationship` varchar(100) DEFAULT NULL, `sex` varchar(1) DEFAULT NULL, `password` varchar(30) DEFAULT NULL, `flags` int(11) DEFAULT NULL, @@ -3774,6 +3758,23 @@ CREATE TABLE `hold_fill_targets` ( REFERENCES `branches` (`branchcode`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +-- +-- Table structure for table 'guarantors_guarantees' +-- + +DROP TABLE IF EXISTS relationships; +CREATE TABLE `relationships` ( + id INT(11) NOT NULL AUTO_INCREMENT, + guarantor_id INT(11) NULL DEFAULT NULL, + guarantee_id INT(11) NOT NULL, + relationship VARCHAR(100) COLLATE utf8_unicode_ci NOT NULL, + surname MEDIUMTEXT COLLATE utf8_unicode_ci NULL DEFAULT NULL, + firstname MEDIUMTEXT COLLATE utf8_unicode_ci NULL DEFAULT NULL, + PRIMARY KEY (id), + CONSTRAINT r_guarantor FOREIGN KEY ( guarantor_id ) REFERENCES borrowers ( borrowernumber ) ON UPDATE CASCADE ON DELETE CASCADE, + CONSTRAINT r_guarantee FOREIGN KEY ( guarantee_id ) REFERENCES borrowers ( borrowernumber ) ON UPDATE CASCADE ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; diff --git a/koha-tmpl/intranet-tmpl/prog/en/columns.def b/koha-tmpl/intranet-tmpl/prog/en/columns.def index c80975a..c82e31e 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/columns.def +++ b/koha-tmpl/intranet-tmpl/prog/en/columns.def @@ -7,7 +7,6 @@ Other name Gender Relationship -Guarantor borrower number Street number Street type Address diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc index 9ecce10..05d96ec 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc @@ -136,17 +136,14 @@ function searchToHold(){
[% IF ( CAN_user_borrowers ) %] - [% IF ( guarantor ) %] - - [% ELSE %] - [% END %] - Edit + Edit + [% END %] [% IF ( CAN_user_borrowers ) %] [% IF ( adultborrower AND activeBorrowerRelationship ) %] - Add child + Add child [% END %] [% IF ( CAN_user_borrowers ) %] Change password 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 2cd368f..3859473 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt @@ -892,7 +892,7 @@ No patron matched [% message %] [% IF relatives_issues_count %] -
  • Relatives' checkouts
  • +
  • [% relatives_issues_count %] Relatives' checkouts
  • [% END %]
  • diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/memberentrygen.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/memberentrygen.tt index 5abb6e4..cd1f9c5 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/memberentrygen.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/memberentrygen.tt @@ -1,3 +1,4 @@ +[% USE To %] [% USE Koha %] [% USE KohaDates %] [% INCLUDE 'doc-head-open.inc' %] @@ -6,34 +7,41 @@ [% INCLUDE 'calendar.inc' %] @@ -289,106 +297,171 @@ $(document).ready(function() { [% END # hide fieldset %] -[% IF ( showguarantor ) %] - - [% UNLESS step_6 %] - - [% END %] -
    - Guarantor information -
      -[% IF ( P ) %] - [% IF ( guarantorid ) %] -
    1. - [% ELSE %] -
    2. -
    3. - - [% IF ( guarantorid ) %] - [% contactname %] - - [% ELSE %] - - [% END %] -
    4. -[% ELSE %] - [% IF ( C ) %] - [% IF ( guarantorid ) %] -
    5. - [% ELSE %] -
    6. - [% UNLESS nocontactname %] -
    7. - - [% IF ( guarantorid ) %] - [% contactname %] - - [% ELSE %] - - [% END %] -
    8. - [% END %] - [% UNLESS nocontactfirstname %] -
    9. - - [% IF ( guarantorid ) %] - [% contactfirstname %] - - [% ELSE %] - - [% END %] -
    10. - [% END %] - [% IF ( relshiploop ) %] -
    11. - - -
    12. - [% END %] - [% END %] -[% END %] -
    13. -   - [% IF ( guarantorid ) %] - - [% ELSE %] - - [% END %] - -
    14. - [% IF guarantorid && Koha.Preference('AllowStaffToSetCheckoutsVisibilityForGuarantor') %] -
    15. - - +
    16. + [% END %] + [% END %] +
    +
    + [% END # END relationships foreach %] + + +
    +
      +
    1. + Patron #: + + +
    2. + +
    3. + + + +
    4. + +
    5. + + + +
    6. + +
    7. + + +
    8. + +
    9. + + Cancel +
    10. +
    +
    + +
    + Add new guarantor +
      + + + [% IF catetory_type == 'P' %] +
    1. + + +
    2. [% ELSE %] - - +
    3. + + +
    4. + +
    5. + + +
    6. + + [% IF ( possible_relationships ) %] +
    7. + + +
    8. + [% END %] [% END %] - -
      Allow guarantor of this patron to view this patron's checkouts from the OPAC
      - - [% END %] -
    -
    +
  • +   + Add guarantor + Set to patron + Clear +
  • + + [% IF relationships && Koha.Preference('AllowStaffToSetCheckoutsVisibilityForGuarantor') %] +
  • + + +
    Allow guarantors of this patron to view this patron's checkouts from the OPAC
    +
  • + [% END %] + + + [% END %] + [% UNLESS noaddress && noaddress2 && nocity && nostate && nozipcode && nocountry %] [% IF Koha.Preference( 'AddressFormat' ) %] [% INCLUDE "member-main-address-style-${ Koha.Preference( 'AddressFormat' ) }.inc" %] @@ -491,13 +564,13 @@ $(document).ready(function() { [% END # UNLESS noB_address && noB_city && noB_state && noB_phone && noB_email %] [% END %] [% IF ( step_2 ) %] - [% UNLESS noaltcontactsurname && noaltcontactfirstname && noaltcontactaddress1 && noaltcontactaddress2 && noaltcontactaddress3 && noaltcontactstate && noaltcontactzipcode && noaltcontactcountry && noaltcontactphone %] + [% UNLESS noaltcontactsurname && noaltguarantor_firstname && noaltcontactaddress1 && noaltcontactaddress2 && noaltcontactaddress3 && noaltcontactstate && noaltcontactzipcode && noaltcontactcountry && noaltcontactphone %] [% IF Koha.Preference( 'AddressFormat' ) %] [% INCLUDE "member-alt-contact-style-${ Koha.Preference( 'AddressFormat' ) }.inc" %] [% ELSE %] [% INCLUDE 'member-alt-contact-style-us.inc' %] [% END %] - [% END # UNLESS noaltcontactsurname && noaltcontactfirstname etc %] + [% END # UNLESS noaltcontactsurname && noaltguarantor_firstname etc %] [% END %] [% IF ( step_3 ) %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember-brief.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember-brief.tt index 34367d8..a416722 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember-brief.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember-brief.tt @@ -50,13 +50,26 @@
  • Initials: [% initials %]
  • Date of birth:[% dateofbirth | $KohaDates %]
  • Gender:[% IF ( sex == 'F' ) %]Female[% ELSIF ( sex == 'M' ) %]Male[% ELSE %][% sex %][% END %]
  • [% END %] - [% IF ( isguarantee ) %] - [% IF ( guaranteeloop ) %] -
  • Guarantees:
  • - [% END %] - [% ELSE %] - [% IF ( guarantorborrowernumber ) %] -
  • Guarantor:[% guarantorsurname %], [% guarantorfirstname %]
  • + [% IF guarantees %] +
  • + Guarantees: + +
  • + [% ELSIF guarantor_relationships %] + [% FOREACH gr IN guarantor_relationships %] +
  • + Guarantor: + [% IF gr.guarantor_id %] + [% SET guarantor = gr.guarantor %] + [% guarantor.firstname %] [% guarantor.surname %] + [% ELSE %] + [% gr.firstname %] [% gr.surname %] + [% END %] +
  • [% END %] [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt index 80a80a9..78a8699 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt @@ -247,25 +247,21 @@ function validate1(date) { [% END %] - [% ELSIF guarantor %] -
  • - Guarantor: - [% IF guarantor.borrowernumber %] - [% guarantor.firstname %] [% guarantor.surname %] - [% ELSE %] - [% guarantor.firstname %] [% guarantor.surname %] - [% END %] -
  • + [% ELSIF guarantor_relationships %] + [% FOREACH gr IN guarantor_relationships %] +
  • + Guarantor: + [% IF gr.guarantor_id %] + [% SET guarantor = gr.guarantor %] + [% guarantor.firstname %] [% guarantor.surname %] + [% ELSE %] + [% gr.firstname %] [% gr.surname %] + [% END %] +
  • + [% END %] [% END %]
    -
    - [% IF ( guarantorborrowernumber ) %] - Edit - [% ELSE %] - Edit - [% END %]
    - @@ -449,7 +445,7 @@ function validate1(date) {
    • [% issuecount %] Checkout(s)
    • [% IF relatives_issues_count %] -
    • Relatives' checkouts
    • +
    • [% relatives_issues_count %] Relatives' checkouts
    • [% END %]
    • Fines & Charges
    • diff --git a/koha-tmpl/intranet-tmpl/prog/js/members.js b/koha-tmpl/intranet-tmpl/prog/js/members.js index 969f39f..366d7eb 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/members.js +++ b/koha-tmpl/intranet-tmpl/prog/js/members.js @@ -142,10 +142,6 @@ function Dopop(link) { var newin=window.open(link,'popup','width=600,height=400,resizable=no,toolbar=false,scrollbars=no,top'); } -function Dopopguarantor(link) { - var newin=window.open(link,'popup','width=800,height=500,resizable=no,toolbar=false,scrollbars=yes,top'); -} - function clear_entry(node) { var original = $(node).parent(); $("textarea", original).attr('value', ''); @@ -183,36 +179,11 @@ function update_category_code(category_code) { } function select_user(borrowernumber, borrower) { - var form = $('#entryform').get(0); - if (form.guarantorid.value) { - $("#contact-details").find('a').remove(); - $("#contactname, #contactfirstname").parent().find('span').remove(); - } + $('#guarantor_id').val(borrower.borrowernumber); + $('#guarantor_surname').val(borrower.surname); + $('#guarantor_firstname').val(borrower.firstname); - var id = borrower.borrowernumber; - form.guarantorid.value = id; - $('#contact-details') - .show() - .find('span') - .after('' + id + ''); - - $(form.contactname) - .val(borrower.surname) - .before('' + borrower.surname + '').get(0).type = 'hidden'; - $(form.contactfirstname) - .val(borrower.firstname) - .before('' + borrower.firstname + '').get(0).type = 'hidden'; - - form.streetnumber.value = borrower.streetnumber; - form.address.value = borrower.address; - form.address2.value = borrower.address2; - form.city.value = borrower.city; - form.state.value = borrower.state; - form.zipcode.value = borrower.zipcode; - form.country.value = borrower.country; - form.branchcode.value = borrower.branchcode; - - form.guarantorsearch.value = LABEL_CHANGE; + $('#guarantor_add').click(); return 0; } @@ -286,13 +257,54 @@ $(document).ready(function(){ $("fieldset.rows input, fieldset.rows select").addClass("noEnterSubmit"); - $("#guarantordelete").click(function() { + $('#guarantor_template').hide(); + + $('#guarantor_search').on('click', function(e) { + e.preventDefault(); + var newin = window.open('guarantor_search.pl','popup','width=600,height=400,resizable=no,toolbar=false,scrollbars=yes,top'); + }); + + $('#guarantor_relationships').on('click', '.guarantor_cancel', function(e) { + e.preventDefault(); + $(this).parents('fieldset').first().remove(); + }); + + $('#guarantor_add').on('click', function(e) { + e.preventDefault(); + var fieldset = $('#guarantor_template').clone(); + fieldset.removeAttr('id'); + + var guarantor_id = $('#guarantor_id').val(); + if ( guarantor_id ) { + fieldset.find('.new_guarantor_id').first().val( guarantor_id ); + fieldset.find('.new_guarantor_id_text').first().text( guarantor_id ); + } else { + fieldset.find('.guarantor_id').first().hide(); + } + $('#guarantor_id').val(""); + + var guarantor_surname = $('#guarantor_surname').val(); + fieldset.find('.new_guarantor_surname').first().val( guarantor_surname ); + fieldset.find('.new_guarantor_surname_text').first().text( guarantor_surname ); + $('#guarantor_surname').val(""); + + var guarantor_firstname = $('#guarantor_firstname').val(); + fieldset.find('.new_guarantor_firstname').first().val( guarantor_firstname ); + fieldset.find('.new_guarantor_firstname_text').first().text( guarantor_firstname ); + $('#guarantor_firstname').val(""); + + var guarantor_relationship = $('#relationship').val(); + fieldset.find('.new_guarantor_relationship').first().val( guarantor_relationship ); + $('#relationship').find('option:eq(0)').prop('selected', true);; + + $('#guarantor_relationships').append( fieldset ); + fieldset.show(); + }); + + $("#guarantor_clear").click(function(e) { + e.preventDefault(); $("#contact-details").hide().find('a').remove(); - $("#guarantorid, #contactname, #contactfirstname").each(function () { this.value = ""; }); - $("#contactname, #contactfirstname") - .each(function () { this.type = 'text'; }) - .parent().find('span').remove(); - $("#guarantorsearch").val(LABEL_SET_TO_PATRON); + $("#guarantor_id, #guarantor_surname, #guarantor_firstname").val(""); }); $("#select_city").change(function(){ diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt index 2dc63f5..cabba5c 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt @@ -85,7 +85,7 @@
      You typed in the wrong characters in the box before submitting. Please try again.
      [% END %] - [% IF borrower.guarantorid && !Koha.Preference('OPACPrivacy') && Koha.Preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') %] + [% IF patron.guarantor_relationships && !Koha.Preference('OPACPrivacy') && Koha.Preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') %]
      Privacy
        @@ -104,7 +104,12 @@ - Your guarantor is [% guarantor.firstname %] [% guarantor.surname %] + Guaranteed by + [% FOREACH gr IN patron.guarantor_relationships %] + [% SET g = gr.guarantor %] + [% g.firstname || gr.firstname %] [% g.surname || gr.surname %] + [%- IF ! loop.last %], [% END %] + [% END %]
      diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-privacy.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-privacy.tt index 38ca4fa..c86cbc0 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-privacy.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-privacy.tt @@ -69,7 +69,7 @@ - [% IF borrower.guarantorid && Koha.Preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') %] + [% IF borrower.guarantor_relationships && Koha.Preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') %]
      - Your guarantor is [% borrower.guarantor.firstname %] [% borrower.guarantor.surname %] + Guaranteed by + [% FOREACH gr IN borrower.guarantor_relationships %] + [% SET g = gr.guarantor %] + [% g.firstname || gr.firstname %] [% g.surname || gr.surname %] + [%- IF ! loop.last %], [% END %] + [% END %]
      [% END %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt index f31f47e..806f494 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt @@ -350,24 +350,24 @@ [% FOREACH r IN relatives %] - [% FOREACH i IN r.issues %] + [% FOREACH c IN r.checkouts %] - [% i.item.biblio.title %] + [% c.item.biblio.title %] - [% i.date_due | $KohaDates %] + [% c.date_due | $KohaDates %] - [% i.item.barcode %] + [% c.item.barcode %] - [% i.item.itemcallnumber %] + [% c.item.itemcallnumber %] diff --git a/members/deletemem.pl b/members/deletemem.pl index fd53ecc..64e028d 100755 --- a/members/deletemem.pl +++ b/members/deletemem.pl @@ -74,11 +74,10 @@ my $issues = GetPendingIssues($member); # FIXME: wasteful call when really, my $countissues = scalar(@$issues); my ($bor)=GetMemberDetails($member,''); +my $patron = Koha::Patrons->find( $member ); my $flags=$bor->{flags}; my $userenv = C4::Context->userenv; - - if ($bor->{category_type} eq "S") { unless(C4::Auth::haspermission($userenv->{'id'},{'staffaccess'=>1})) { print $input->redirect("/cgi-bin/koha/members/moremember.pl?borrowernumber=$member&error=CANT_DELETE_STAFF"); @@ -101,13 +100,12 @@ if (C4::Context->preference("IndependentBranches")) { } } -my $dbh = C4::Context->dbh; -my $sth=$dbh->prepare("Select * from borrowers where guarantorid=?"); -$sth->execute($member); -my $data=$sth->fetchrow_hashref; -if ($countissues > 0 or $flags->{'CHARGES'} or $data->{'borrowernumber'} or $deletelocal == 0){ - # print $input->header; - +if ( + $countissues > 0 + || $flags->{'CHARGES'} + || $patron->guarantee_relationships()->count() + || $deletelocal == 0 ) +{ my $patron_image = Koha::Patron::Images->find($bor->{borrowernumber}); $template->param( picture => 1 ) if $patron_image; @@ -128,7 +126,7 @@ if ($countissues > 0 or $flags->{'CHARGES'} or $data->{'borrowernumber'} or $de email => $bor->{'email'}, branchcode => $bor->{'branchcode'}, branchname => GetBranchName($bor->{'branchcode'}), - activeBorrowerRelationship => (C4::Context->preference('borrowerRelationship') ne ''), + activeBorrowerRelationship => (C4::Context->preference('borrowerRelationship') ne ''), RoutingSerials => C4::Context->preference('RoutingSerials'), ); if ($countissues >0) { @@ -137,8 +135,8 @@ if ($countissues > 0 or $flags->{'CHARGES'} or $data->{'borrowernumber'} or $de if ($flags->{'CHARGES'} ne '') { $template->param(charges => $flags->{'CHARGES'}->{'amount'}); } - if ($data->{'borrowernumber'}) { - $template->param(guarantees => 1); + if ( $patron->guarantor_relationships() ) { + $template->param( guarantees => 1 ); } if ($deletelocal == 0) { $template->param(keeplocal => 1); diff --git a/members/memberentry.pl b/members/memberentry.pl index 597c68d..c57b924 100755 --- a/members/memberentry.pl +++ b/members/memberentry.pl @@ -75,7 +75,6 @@ if ( C4::Context->preference('SMSSendDriver') eq 'Email' ) { $template->param( sms_providers => \@providers ); } -my $guarantorid = $input->param('guarantorid'); my $borrowernumber = $input->param('borrowernumber'); my $actionType = $input->param('actionType') || ''; my $modify = $input->param('modify'); @@ -91,13 +90,25 @@ $nodouble = 1 if ($op eq 'modify' or $op eq 'duplicate'); # FIXME hack to rep # isn't a duplicate. Marking FIXME because this # script needs to be refactored. my $nok = $input->param('nok'); -my $guarantorinfo = $input->param('guarantorinfo'); my $step = $input->param('step') || 0; my @errors; my $borrower_data; my $NoUpdateLogin; my $userenv = C4::Context->userenv; +## Deal with guarantor stuff +my $patron = Koha::Patrons->find($borrowernumber); +$template->param( relationships => scalar $patron->guarantor_relationships ) if $patron; + +my $guarantor_id = $input->param('guarantor_id'); +my $guarantor = Koha::Patrons->find( $guarantor_id ) if $guarantor_id; +$template->param( guarantor => $guarantor ); + +my @delete_guarantor = $input->param('delete_guarantor'); +foreach my $id ( @delete_guarantor ) { + my $r = Koha::Patron::Relationships->find( $id ); + $r->delete() if $r; +} ## Deal with debarments $template->param( @@ -134,14 +145,14 @@ $template->param("minPasswordLength" => $minpw); my $check_BorrowerMandatoryField=C4::Context->preference("BorrowerMandatoryField"); my @field_check=split(/\|/,$check_BorrowerMandatoryField); foreach (@field_check) { - $template->param( "mandatory$_" => 1); + $template->param( "mandatory$_" => 1 ); } # function to designate unwanted fields my $check_BorrowerUnwantedField=C4::Context->preference("BorrowerUnwantedField"); @field_check=split(/\|/,$check_BorrowerUnwantedField); foreach (@field_check) { next unless m/\w/o; - $template->param( "no$_" => 1); + $template->param( "no$_" => 1 ); } $template->param( "add" => 1 ) if ( $op eq 'add' ); $template->param( "duplicate" => 1 ) if ( $op eq 'duplicate' ); @@ -237,25 +248,6 @@ if ( ( $op eq 'insert' ) and !$nodouble ) { } } - #recover all data from guarantor address phone ,fax... -if ( $guarantorid ) { - if (my $guarantordata=GetMember(borrowernumber => $guarantorid)) { - $category_type = $guarantordata->{categorycode} eq 'I' ? 'P' : 'C'; - $guarantorinfo=$guarantordata->{'surname'}." , ".$guarantordata->{'firstname'}; - $newdata{'contactfirstname'}= $guarantordata->{'firstname'}; - $newdata{'contactname'} = $guarantordata->{'surname'}; - $newdata{'contacttitle'} = $guarantordata->{'title'}; - if ( $op eq 'add' ) { - foreach (qw(streetnumber address streettype address2 - zipcode country city state phone phonepro mobile fax email emailpro branchcode - B_streetnumber B_streettype B_address B_address2 - B_city B_state B_zipcode B_country B_email B_phone)) { - $newdata{$_} = $guarantordata->{$_}; - } - } - } -} - ###############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')); @@ -380,6 +372,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 $borrowernumber = &AddMember(%newdata); + add_guarantors( $borrowernumber ); $newdata{'borrowernumber'} = $borrowernumber; # If 'AutoEmailOpacUser' syspref is on, email user their account details from the 'notice' that matches the user's branchcode. @@ -435,6 +428,7 @@ if ((!$nok) and $nodouble and ($op eq 'insert' or $op eq 'save')){ # updating any columns in the borrowers table, # which can happen if we're only editing the # patron attributes or messaging preferences sections + add_guarantors( $borrowernumber ); if (C4::Context->preference('ExtendedPatronAttributes') and $input->param('setting_extended_patron_attributes')) { C4::Members::Attributes::SetBorrowerAttributes($borrowernumber, $extended_patron_attributes); } @@ -513,29 +507,46 @@ my @typeloop; my $no_categories = 1; my $no_add; foreach (qw(C A S P I X)) { - my $action="WHERE category_type=?"; - my ($categories,$labels)=GetborCatFromCatType($_,$action); - if(scalar(@$categories) > 0){ $no_categories = 0; } - my @categoryloop; - foreach my $cat (@$categories){ - push @categoryloop,{'categorycode' => $cat, - 'categoryname' => $labels->{$cat}, - 'categorycodeselected' => ((defined($borrower_data->{'categorycode'}) && - $cat eq $borrower_data->{'categorycode'}) - || (defined($categorycode) && $cat eq $categorycode)), - }; - } - my %typehash; - $typehash{'typename'}=$_; - my $typedescription = "typename_".$typehash{'typename'}; - $typehash{'categoryloop'}=\@categoryloop; - push @typeloop,{'typename' => $_, + my $action = "WHERE category_type=?"; + + my ( $categories, $labels ) = GetborCatFromCatType( $_, $action ); + + if ( scalar(@$categories) > 0 ) { $no_categories = 0; } + + my @categoryloop; + foreach my $cat (@$categories) { + push @categoryloop, + { + 'categorycode' => $cat, + 'categoryname' => $labels->{$cat}, + 'categorycodeselected' => ( + ( + defined( $borrower_data->{'categorycode'} ) + && $cat eq $borrower_data->{'categorycode'} + ) + || ( defined($categorycode) && $cat eq $categorycode ) + ), + }; + } + + my %typehash; + $typehash{'typename'} = $_; + + my $typedescription = "typename_" . $typehash{'typename'}; + $typehash{'categoryloop'} = \@categoryloop; + + push @typeloop, + { + 'typename' => $_, $typedescription => 1, - 'categoryloop' => \@categoryloop}; + 'categoryloop' => \@categoryloop + }; } -$template->param('typeloop' => \@typeloop, - no_categories => $no_categories); -if($no_categories){ $no_add = 1; } +$template->param( + 'typeloop' => \@typeloop, + no_categories => $no_categories +); +if ($no_categories) { $no_add = 1; } my $cities = Koha::Cities->search( {}, { order_by => 'city_name' } ); @@ -569,24 +580,27 @@ while (@relationships) { push(@relshipdata, \%row); } -my %flags = ( 'gonenoaddress' => ['gonenoaddress' ], - 'lost' => ['lost']); +my %flags = ( + 'gonenoaddress' => ['gonenoaddress'], + 'lost' => ['lost'] +); - my @flagdata; -foreach (keys(%flags)) { - my $key = $_; - my %row = ('key' => $key, - 'name' => $flags{$key}[0]); - if ($data{$key}) { - $row{'yes'}=' checked'; - $row{'no'}=''; +foreach ( keys(%flags) ) { + my $key = $_; + my %row = ( + 'key' => $key, + 'name' => $flags{$key}[0] + ); + if ( $data{$key} ) { + $row{'yes'} = ' checked'; + $row{'no'} = ''; } - else { - $row{'yes'}=''; - $row{'no'}=' checked'; - } - push @flagdata,\%row; + else { + $row{'yes'} = ''; + $row{'no'} = ' checked'; + } + push @flagdata, \%row; } # get Branch Loop @@ -662,7 +676,7 @@ if (C4::Context->preference('EnhancedMessagingPreferences')) { $template->param(TalkingTechItivaPhone => C4::Context->preference("TalkingTechItivaPhoneNotification")); } -$template->param( "showguarantor" => ($category_type=~/A|I|S|X/) ? 0 : 1); # associate with step to know where you are +$template->param( "show_guarantor" => ( $category_type =~ /A|I|S|X/ ) ? 0 : 1 ); # associate with step to know where you are $debug and warn "memberentry step: $step"; $template->param(%data); $template->param( "step_$step" => 1) if $step; # associate with step to know where u are @@ -680,10 +694,8 @@ $template->param( branchloop => $branchloop ) if ( $branchloop ); $template->param( nodouble => $nodouble, borrowernumber => $borrowernumber, #register number - guarantorid => ($borrower_data->{'guarantorid'} || $guarantorid), relshiploop => \@relshipdata, borrotitlepopup => $borrotitlepopup, - guarantorinfo => $guarantorinfo, flagloop => \@flagdata, category_type =>$category_type, modify => $modify, @@ -795,6 +807,42 @@ sub patron_attributes_form { } +sub add_guarantors { + my ($borrowernumber) = @_; + + my @new_guarantor_id = $input->param('new_guarantor_id'); + my @new_guarantor_surname = $input->param('new_guarantor_surname'); + my @new_guarantor_firstname = $input->param('new_guarantor_firstname'); + my @new_guarantor_relationship = $input->param('new_guarantor_relationship'); + + for ( my $i = 0 ; $i < scalar @new_guarantor_id; $i++ ) { + my $guarantor_id = $new_guarantor_id[$i]; + my $surname = $new_guarantor_surname[$i]; + my $firstname = $new_guarantor_firstname[$i]; + my $relationship = $new_guarantor_relationship[$i]; + + if ($guarantor_id) { + Koha::Patron::Relationship->new( + { + guarantee_id => $borrowernumber, + guarantor_id => $guarantor_id, + relationship => $relationship + } + )->store(); + } + elsif ($surname) { + Koha::Patron::Relationship->new( + { + guarantee_id => $borrowernumber, + surname => $surname, + firstname => $firstname, + relationship => $relationship + } + )->store(); + } + } +} + # Local Variables: # tab-width: 8 # End: diff --git a/members/moremember.pl b/members/moremember.pl index e7c54f4..6a294e9 100755 --- a/members/moremember.pl +++ b/members/moremember.pl @@ -163,24 +163,22 @@ if ( $category_type eq 'C') { $template->param( 'catcode' => $catcodes->[0]) if $cnt == 1; } -my $patron = Koha::Patrons->find($data->{borrowernumber}); +my $patron = Koha::Patrons->find( $data->{borrowernumber} ); my @relatives; -if ( my $guarantor = $patron->guarantor ) { - $template->param( guarantor => $guarantor ); - push @relatives, $guarantor->borrowernumber; - push @relatives, $_->borrowernumber for $patron->siblings; -} elsif ( $patron->contactname || $patron->contactfirstname ) { - $template->param( - guarantor => { - firstname => $patron->contactfirstname, - surname => $patron->contactname, - } - ); -} else { - my @guarantees = $patron->guarantees; - $template->param( guarantees => \@guarantees ); - push @relatives, $_->borrowernumber for @guarantees; +my $guarantor_relationships = $patron->guarantor_relationships(); +my @guarantees = $patron->guarantee_relationships()->guarantees(); +my @guarantors = $guarantor_relationships->guarantors(); +if (@guarantors) { + push( @relatives, $_->id ) for @guarantors; + push( @relatives, $_->id ) for $patron->siblings(); } +else { + push( @relatives, $_->id ) for @guarantees; +} +$template->param( + guarantor_relationships => $guarantor_relationships, + guarantees => \@guarantees, +); my $relatives_issues_count = Koha::Database->new()->schema()->resultset('Issue') diff --git a/members/update-child.pl b/members/update-child.pl index e5e2302..e1a83c6 100755 --- a/members/update-child.pl +++ b/members/update-child.pl @@ -33,6 +33,7 @@ use C4::Context; use C4::Auth; use C4::Output; use C4::Members; +use Koha::Patrons; # use Smart::Comments; @@ -53,50 +54,54 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user( my $borrowernumber = $input->param('borrowernumber'); my $catcode = $input->param('catcode'); my $cattype = $input->param('cattype'); -my $catcode_multi = $input->param('catcode_multi'); +my $catcode_multi = $input->param('catcode_multi'); my $op = $input->param('op'); if ( $op eq 'multi' ) { + # FIXME - what are the possible upgrade paths? C -> A , C -> S ... + # currently just allowing C -> A because of limitation of API. my ( $catcodes, $labels ) = - # FIXME - what are the possible upgrade paths? C -> A , C -> S ... - # currently just allowing C -> A because of limitation of API. GetborCatFromCatType( 'A', 'WHERE category_type = ?' ); my @rows; foreach my $k ( keys %$labels ) { my $row; - $row->{catcode} = $k; - $row->{catdesc} = $labels->{$k}; my $borcat = GetBorrowercategory( $row->{catcode} ); $row->{cattype} = $borcat->{'category_type'}; + $row->{catcode} = $k; + $row->{catdesc} = $labels->{$k}; push @rows, $row; } $template->param( MULTI => 1, - CATCODE_MULTI => 1, + CATCODE_MULTI => 1, borrowernumber => $borrowernumber, CAT_LOOP => \@rows, ); output_html_with_http_headers $input, $cookie, $template->output; } - elsif ( $op eq 'update' ) { - my $member = GetMember('borrowernumber'=>$borrowernumber); - $member->{'guarantorid'} = 0; - $member->{'categorycode'} = $catcode; - my $borcat = GetBorrowercategory($catcode); - $member->{'category_type'} = $borcat->{'category_type'}; - $member->{'description'} = $borcat->{'description'}; - delete $member->{password}; - ModMember(%$member); + my $patron = Koha::Patrons->find($borrowernumber); + $_->delete() for $patrons->guarantor_relationships(); + + my $patron_hashref = patron->unblessed(); + my $borcat = GetBorrowercategory($catcode); + $patron_hashref->{'category_type'} = $borcat->{'category_type'}; + $patron_hashref->{'categorycode'} = $catcode; + $patron_hashref->{'description'} = $borcat->{'description'}; + delete $patron_hashref->{password}; + ModMember(%$patron_hashref); - if ( $catcode_multi ) { + if ($catcode_multi) { $template->param( - SUCCESS => 1, - borrowernumber => $borrowernumber, - ); + SUCCESS => 1, + borrowernumber => $borrowernumber, + ); output_html_with_http_headers $input, $cookie, $template->output; - } else { - print $input->redirect("/cgi-bin/koha/members/moremember.pl?borrowernumber=$borrowernumber"); + } + else { + print $input->redirect( + "/cgi-bin/koha/members/moremember.pl?borrowernumber=$borrowernumber" + ); } } diff --git a/misc/cronjobs/j2a.pl b/misc/cronjobs/j2a.pl index ba90ad0..246bb25 100755 --- a/misc/cronjobs/j2a.pl +++ b/misc/cronjobs/j2a.pl @@ -163,12 +163,12 @@ $verbose and print "The age limit for category $fromcat is $agelimit\n"; my $itsyourbirthday = "$year-$mon-$mday"; if ( not $noaction ) { + # Start a transaction since we need to delete from relationships and update borrowers atomically + $dbh->{AutoCommit} = 0; + if ($mybranch) { #yep, we received a specific branch to work on. $verbose and print "Looking for patrons of $mybranch to update from $fromcat to $tocat that were born before $itsyourbirthday\n"; - my $query = qq| - UPDATE borrowers - SET guarantorid ='0', - categorycode = ? + my $where = qq| WHERE dateofbirth <= ? AND dateofbirth != '0000-00-00' AND branchcode = ? @@ -177,10 +177,28 @@ if ( not $noaction ) { FROM categories WHERE category_type = 'C' AND categorycode = ? - )|; + ) + |; + + my $query = qq| + DELETE relationships FROM relationships + LEFT JOIN borrowers ON ( borrowers.borrowernumber = relationships.guarantee_id ) + $where + |; my $sth = $dbh->prepare($query); + $sth->execute( $itsyourbirthday, $mybranch, $fromcat ) + or ( $dbh->rollback && die "can't execute" ); + + $query = qq| + UPDATE borrowers + SET categorycode = ? + $where + |; + $sth = $dbh->prepare($query); my $res = $sth->execute( $tocat, $itsyourbirthday, $mybranch, $fromcat ) - or die "can't execute"; + or ( $dbh->rollback && die "can't execute" ); + + $dbh->commit; if ( $res eq '0E0' ) { print "No patrons updated\n"; @@ -191,10 +209,7 @@ if ( not $noaction ) { } else { # branch was not supplied, processing all branches $verbose and print "Looking in all branches for patrons to update from $fromcat to $tocat that were born before $itsyourbirthday\n"; - my $query = qq| - UPDATE borrowers - SET guarantorid = '0', - categorycode = ? + my $where = qq| WHERE dateofbirth <= ? AND dateofbirth!='0000-00-00' AND categorycode IN ( @@ -202,10 +217,26 @@ if ( not $noaction ) { FROM categories WHERE category_type = 'C' AND categorycode = ? - )|; + ) + |; + + my $query = qq| + DELETE relationships FROM relationships + LEFT JOIN borrowers ON ( borrowers.borrowernumber = relationships.guarantee_id ) + $where + |; my $sth = $dbh->prepare($query); + $sth->execute( $itsyourbirthday, $fromcat ) + or ( $dbh->rollback && die "can't execute" ); + + $query = qq| + UPDATE borrowers + SET categorycode = ? + $where + |; + $sth = $dbh->prepare($query); my $res = $sth->execute( $tocat, $itsyourbirthday, $fromcat ) - or die "can't execute"; + or ( $dbh->rollback && die "can't execute" ); if ( $res eq '0E0' ) { print "No patrons updated\n"; diff --git a/opac/opac-memberentry.pl b/opac/opac-memberentry.pl index 75ecdc3..79fdf2f 100755 --- a/opac/opac-memberentry.pl +++ b/opac/opac-memberentry.pl @@ -31,6 +31,7 @@ use C4::Branch qw(GetBranchesLoop); use C4::Scrubber; use Email::Valid; use Koha::DateUtils; +use Koha::Patrons; use Koha::Patron::Images; my $cgi = new CGI; @@ -240,7 +241,6 @@ elsif ( $action eq 'edit' ) { #Display logged in borrower's data $template->param( borrower => $borrower, - guarantor => scalar Koha::Patrons->find($borrowernumber)->guarantor(), hidden => GetHiddenFields( $mandatory, 'modification' ), ); @@ -255,7 +255,8 @@ my $captcha = random_string("CCCCC"); $template->param( captcha => $captcha, - captcha_digest => md5_base64($captcha) + captcha_digest => md5_base64($captcha), + patron => Koha::Patrons->find( $borrowernumber ), ); output_html_with_http_headers $cgi, $cookie, $template->output, undef, { force_no_caching => 1 }; diff --git a/opac/opac-user.pl b/opac/opac-user.pl index 413c26d..de3ad82 100755 --- a/opac/opac-user.pl +++ b/opac/opac-user.pl @@ -38,6 +38,7 @@ use Koha::DateUtils; use Koha::Patron::Debarments qw(IsDebarred); use Koha::Holds; use Koha::Database; +use Koha::Patrons; use Koha::Patron::Messages; use constant ATTRIBUTE_SHOW_BARCODE => 'SHOW_BCODE'; @@ -68,6 +69,8 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user( } ); +my $patron = Koha::Patrons->find( $borrowernumber ); + my %renewed = map { $_ => 1 } split( ':', $query->param('renewed') ); my $show_priority; @@ -333,14 +336,12 @@ if ( $borr->{'opacnote'} ) { if ( C4::Context->preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') || C4::Context->preference('AllowStaffToSetCheckoutsVisibilityForGuarantor') ) { - my @relatives = - Koha::Database->new()->schema()->resultset("Borrower")->search( - { - privacy_guarantor_checkouts => 1, - 'me.guarantorid' => $borrowernumber - }, - { prefetch => [ { 'issues' => { 'item' => 'biblio' } } ] } - ); + my @relatives; + # Filter out guarantees that don't want guarantor to see checkouts + foreach my $gr ( $patron->guarantee_relationships() ) { + my $g = $gr->guarantee; + push( @relatives, $g ) if $g->privacy_guarantor_checkouts; + } $template->param( relatives => \@relatives ); } diff --git a/t/Patron.t b/t/Patron.t index b62accd..ab18b6b 100755 --- a/t/Patron.t +++ b/t/Patron.t @@ -101,12 +101,7 @@ my $patron = Koha::Patron->new( lost => '0', debarred => '2015-04-19', debarredcomment => 'You are debarred', - contactname => 'myContactname', - contactfirstname => 'myContactfirstname', - contacttitle => 'myContacttitle', - guarantorid => '123454321', borrowernotes => 'borrowernotes', - relationship => 'myRelationship', sex => 'M', password => 'hfkurhfe976634èj!', flags => '55555', @@ -131,7 +126,7 @@ my $patron = Koha::Patron->new( # patron Accessor tests subtest 'Accessor tests' => sub { - plan tests => 65; + plan tests => 60; is( $patron->borrowernumber, '12345', 'borrowernumber accessor returns correct value' ); is( $patron->cardnumber, '1234567890', 'cardnumber accessor returns correct value' ); is( $patron->surname, 'mySurname', 'surname accessor returns correct value' ); @@ -172,12 +167,7 @@ subtest 'Accessor tests' => sub { is( $patron->lost, '0', 'lost accessor returns correct value' ); is( $patron->debarred, '2015-04-19', 'debarred accessor returns correct value' ); is( $patron->debarredcomment, 'You are debarred', 'debarredcomment accessor returns correct value' ); - is( $patron->contactname, 'myContactname', 'contactname accessor returns correct value' ); - is( $patron->contactfirstname, 'myContactfirstname', 'contactfirstname accessor returns correct value' ); - is( $patron->contacttitle, 'myContacttitle', 'contacttitle accessor returns correct value' ); - is( $patron->guarantorid, '123454321', 'guarantorid accessor returns correct value' ); is( $patron->borrowernotes, 'borrowernotes', 'borrowernotes accessor returns correct value' ); - is( $patron->relationship, 'myRelationship', 'relationship accessor returns correct value' ); is( $patron->sex, 'M', 'sex accessor returns correct value' ); is( $patron->password, 'hfkurhfe976634èj!', 'password accessor returns correct value' ); is( $patron->flags, '55555', 'flags accessor returns correct value' ); @@ -201,7 +191,7 @@ subtest 'Accessor tests' => sub { # patron Set tests subtest 'Set tests' => sub { - plan tests => 65; + plan tests => 60; $patron->set( { @@ -245,12 +235,7 @@ subtest 'Set tests' => sub { lost => '1', debarred => '2016-04-19', debarredcomment => 'You are still debarred', - contactname => 'SmyContactname', - contactfirstname => 'SmyContactfirstname', - contacttitle => 'SmyContacttitle', - guarantorid => '223454321', borrowernotes => 'Sborrowernotes', - relationship => 'SmyRelationship', sex => 'F', password => 'zerzerzer#', flags => '666666', @@ -313,12 +298,7 @@ subtest 'Set tests' => sub { is( $patron->lost, '1', 'lost field set ok' ); is( $patron->debarred, '2016-04-19', 'debarred field set ok' ); is( $patron->debarredcomment, 'You are still debarred', 'debarredcomment field set ok' ); - is( $patron->contactname, 'SmyContactname', 'contactname field set ok' ); - is( $patron->contactfirstname, 'SmyContactfirstname', 'contactfirstname field set ok' ); - is( $patron->contacttitle, 'SmyContacttitle', 'contacttitle field set ok' ); - is( $patron->guarantorid, '223454321', 'guarantorid field set ok' ); is( $patron->borrowernotes, 'Sborrowernotes', 'borrowernotes field set ok' ); - is( $patron->relationship, 'SmyRelationship', 'relationship field set ok' ); is( $patron->sex, 'F', 'sex field set ok' ); is( $patron->password, 'zerzerzer#', 'password field set ok' ); is( $patron->flags, '666666', 'flags field set ok' ); diff --git a/t/db_dependent/Circulation.t b/t/db_dependent/Circulation.t index cfee3e2..682091d 100755 --- a/t/db_dependent/Circulation.t +++ b/t/db_dependent/Circulation.t @@ -27,10 +27,11 @@ use C4::Reserves; use C4::Overdues qw(UpdateFine); use Koha::DateUtils; use Koha::Database; +use Koha::Patron; use t::lib::TestBuilder; -use Test::More tests => 84; +use Test::More tests => 87; BEGIN { use_ok('C4::Circulation'); @@ -302,6 +303,12 @@ C4::Context->dbh->do("DELETE FROM accountlines"); $datedue = dt_from_string( $issue->date_due() ); is (defined $issue2, 1, "Item 2 checked out, due date: " . $issue2->date_due()); + my $patron = Koha::Patrons->find( $renewing_borrower->{borrowernumber} ); + my @checkouts = $patron->checkouts(); + is( @checkouts, 2, "Patron has 2 checkouts" ); + is( $checkouts[0]->borrowernumber, $patron->id, 'Checkout 1 patron matches' ); + is( $checkouts[1]->borrowernumber, $patron->id, 'Checkout 2 patron matches' ); + my $borrowing_borrowernumber = GetItemIssue($itemnumber)->{borrowernumber}; is ($borrowing_borrowernumber, $renewing_borrowernumber, "Item checked out to $renewing_borrower->{firstname} $renewing_borrower->{surname}"); diff --git a/t/db_dependent/Circulation/NoIssuesChargeGuarantees.t b/t/db_dependent/Circulation/NoIssuesChargeGuarantees.t index a5cbb62..22baeaa 100644 --- a/t/db_dependent/Circulation/NoIssuesChargeGuarantees.t +++ b/t/db_dependent/Circulation/NoIssuesChargeGuarantees.t @@ -23,6 +23,7 @@ use t::lib::TestBuilder; use C4::Accounts qw( manualinvoice ); use C4::Circulation qw( CanBookBeIssued ); +use Koha::Patron::Relationship; my $schema = Koha::Database->new->schema; $schema->storage->txn_begin; @@ -47,12 +48,17 @@ my $patron = $builder->build( my $guarantee = $builder->build( { source => 'Borrower', - value => { - guarantorid => $patron->{borrowernumber}, - } } ); +my $r = Koha::Patron::Relationship->new( + { + guarantor_id => $patron->{borrowernumber}, + guarantee_id => $guarantee->{borrowernumber}, + relationship => 'parent', + } +)->store(); + C4::Context->set_preference( 'NoIssuesChargeGuarantees', '5.00' ); my ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron, $item->{barcode} ); diff --git a/t/db_dependent/Items.t b/t/db_dependent/Items.t index 2b990c1..e683963 100755 --- a/t/db_dependent/Items.t +++ b/t/db_dependent/Items.t @@ -426,7 +426,7 @@ subtest 'SearchItems test' => sub { subtest 'Koha::Item(s) tests' => sub { - plan tests => 5; + plan tests => 7; $schema->storage->txn_begin(); @@ -455,6 +455,10 @@ subtest 'Koha::Item(s) tests' => sub { is( ref($holdingbranch), 'Koha::Library', "Got Koha::Library from holding_branch method" ); is( $holdingbranch->branchcode(), $library2->{branchcode}, "Home branch code matches holdingbranch" ); + my $biblio = $item->biblio(); + is( ref($biblio), 'Koha::Biblio', "Got Koha::Biblio from biblio method" ); + is( $biblio->title(), 'Silence in the library', 'Title matches biblio title' ); + $schema->storage->txn_rollback; }; diff --git a/t/db_dependent/Koha/Patrons.t b/t/db_dependent/Koha/Patrons.t index fafd829..42d7932 100644 --- a/t/db_dependent/Koha/Patrons.t +++ b/t/db_dependent/Koha/Patrons.t @@ -23,6 +23,7 @@ use Test::More tests => 5; use Koha::Patron; use Koha::Patrons; +use Koha::Patron::Relationship; use Koha::Database; use t::lib::TestBuilder; @@ -58,38 +59,43 @@ is( $retrieved_patron_1->cardnumber, $new_patron_1->cardnumber, 'Find a patron b subtest 'guarantees' => sub { plan tests => 8; - my $guarantees = $new_patron_1->guarantees; - is( ref($guarantees), 'Koha::Patrons', 'Koha::Patron->guarantees should return a Koha::Patrons result set in a scalar context' ); - is( $guarantees->count, 0, 'new_patron_1 should have 0 guarantee' ); - my @guarantees = $new_patron_1->guarantees; - is( ref(\@guarantees), 'ARRAY', 'Koha::Patron->guarantees should return an array in a list context' ); + my $guarantees = $new_patron_1->guarantee_relationships; + is( ref($guarantees), 'Koha::Patron::Relationships', 'Koha::Patron->guarantees should return a Koha::Patrons result set in a scalar context' ); + is( $guarantees->count, 0, 'new_patron_1 should have 0 guarantee relationships' ); + my @guarantees = $new_patron_1->guarantee_relationships; + is( ref(\@guarantees), 'ARRAY', 'Koha::Patron->guarantee_relationships should return an array in a list context' ); is( scalar(@guarantees), 0, 'new_patron_1 should have 0 guarantee' ); - my $guarantee_1 = $builder->build({ source => 'Borrower', value => { guarantorid => $new_patron_1->borrowernumber }}); - my $guarantee_2 = $builder->build({ source => 'Borrower', value => { guarantorid => $new_patron_1->borrowernumber }}); + my $guarantee_1 = $builder->build({ source => 'Borrower' }); + my $relationship_1 = Koha::Patron::Relationship->new( { guarantor_id => $new_patron_1->id, guarantee_id => $guarantee_1->{borrowernumber} } )->store(); + my $guarantee_2 = $builder->build({ source => 'Borrower' }); + my $relationship_2 = Koha::Patron::Relationship->new( { guarantor_id => $new_patron_1->id, guarantee_id => $guarantee_2->{borrowernumber} } )->store(); - $guarantees = $new_patron_1->guarantees; - is( ref($guarantees), 'Koha::Patrons', 'Koha::Patron->guarantees should return a Koha::Patrons result set in a scalar context' ); + $guarantees = $new_patron_1->guarantee_relationships; + is( ref($guarantees), 'Koha::Patron::Relationships', 'Koha::Patron->guarantee_relationships should return a Koha::Patrons result set in a scalar context' ); is( $guarantees->count, 2, 'new_patron_1 should have 2 guarantees' ); - @guarantees = $new_patron_1->guarantees; - is( ref(\@guarantees), 'ARRAY', 'Koha::Patron->guarantees should return an array in a list context' ); + @guarantees = $new_patron_1->guarantee_relationships; + is( ref(\@guarantees), 'ARRAY', 'Koha::Patron->guarantee_relationships should return an array in a list context' ); is( scalar(@guarantees), 2, 'new_patron_1 should have 2 guarantees' ); - $_->delete for @guarantees; + map { $_->guarantee->delete } @guarantees; }; subtest 'siblings' => sub { plan tests => 7; my $siblings = $new_patron_1->siblings; is( $siblings, undef, 'Koha::Patron->siblings should not crashed if the patron has no guarantor' ); - my $guarantee_1 = $builder->build( { source => 'Borrower', value => { guarantorid => $new_patron_1->borrowernumber } } ); + my $guarantee_1 = $builder->build( { source => 'Borrower' } ); + my $relationship_1 = Koha::Patron::Relationship->new( { guarantor_id => $new_patron_1->borrowernumber, guarantee_id => $guarantee_1->{borrowernumber} } )->store(); my $retrieved_guarantee_1 = Koha::Patrons->find($guarantee_1); $siblings = $retrieved_guarantee_1->siblings; is( ref($siblings), 'Koha::Patrons', 'Koha::Patron->siblings should return a Koha::Patrons result set in a scalar context' ); my @siblings = $retrieved_guarantee_1->siblings; is( ref( \@siblings ), 'ARRAY', 'Koha::Patron->siblings should return an array in a list context' ); is( $siblings->count, 0, 'guarantee_1 should not have siblings yet' ); - my $guarantee_2 = $builder->build( { source => 'Borrower', value => { guarantorid => $new_patron_1->borrowernumber } } ); - my $guarantee_3 = $builder->build( { source => 'Borrower', value => { guarantorid => $new_patron_1->borrowernumber } } ); + my $guarantee_2 = $builder->build( { source => 'Borrower' } ); + my $relationship_2 = Koha::Patron::Relationship->new( { guarantor_id => $new_patron_1->borrowernumber, guarantee_id => $guarantee_2->{borrowernumber} } )->store(); + my $guarantee_3 = $builder->build( { source => 'Borrower' } ); + my $relationship_3 = Koha::Patron::Relationship->new( { guarantor_id => $new_patron_1->borrowernumber, guarantee_id => $guarantee_3->{borrowernumber} } )->store(); $siblings = $retrieved_guarantee_1->siblings; is( $siblings->count, 2, 'guarantee_1 should have 2 siblings' ); is( $guarantee_2->{borrowernumber}, $siblings->next->borrowernumber, 'guarantee_2 should exist in the guarantees' ); diff --git a/t/db_dependent/Members.t b/t/db_dependent/Members.t index 797e9bc..2ef26d2 100755 --- a/t/db_dependent/Members.t +++ b/t/db_dependent/Members.t @@ -23,6 +23,7 @@ use Data::Dumper; use C4::Context; use Koha::Database; use Koha::List::Patron; +use Koha::Patron::Relationship; use t::lib::Mocks; @@ -329,7 +330,7 @@ $patstodel = GetBorrowersToExpunge( {not_borrowed_since => '2016-01-02', patron_ ok( scalar(@$patstodel) == 1 && $patstodel->[0]->{'borrowernumber'} eq $bor2inlist,'Staff patron not deleted by last issue date'); ModMember( borrowernumber => $bor1inlist, categorycode => 'CIVILIAN' ); -ModMember( borrowernumber => $guarantee->{borrowernumber} ,guarantorid=>$bor1inlist ); +my $relationship = Koha::Patron::Relationship->new( { guarantor_id => $bor1inlist, guarantee_id => $guarantee->{borrowernumber} } )->store(); $patstodel = GetBorrowersToExpunge( {patron_list_id => $list1->patron_list_id()} ); ok( scalar(@$patstodel)== 1 && $patstodel->[0]->{'borrowernumber'} eq $bor2inlist,'Guarantor patron not deleted from list'); @@ -339,7 +340,8 @@ $patstodel = GetBorrowersToExpunge( {expired_before => '2015-01-02', patron_list ok( scalar(@$patstodel) == 1 && $patstodel->[0]->{'borrowernumber'} eq $bor2inlist,'Guarantor patron not deleted by expirationdate and list'); $patstodel = GetBorrowersToExpunge( {not_borrowed_since => '2016-01-02', patron_list_id => $list1->patron_list_id() } ); ok( scalar(@$patstodel) == 1 && $patstodel->[0]->{'borrowernumber'} eq $bor2inlist,'Guarantor patron not deleted by last issue date'); -ModMember( borrowernumber => $guarantee->{borrowernumber}, guarantorid=>'' ); + +$relationship->delete(); $builder->build({ source => 'Issue', diff --git a/t/db_dependent/Patron/Relationships.t b/t/db_dependent/Patron/Relationships.t new file mode 100755 index 0000000..4aec07a --- /dev/null +++ b/t/db_dependent/Patron/Relationships.t @@ -0,0 +1,181 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use Test::More tests => 67; + +use C4::Context; + +use t::lib::TestBuilder; + +BEGIN { + use_ok('Koha::Objects'); + use_ok('Koha::Patrons'); + use_ok('Koha::Patron::Relationship'); + use_ok('Koha::Patron::Relationships'); +} + +# Start transaction +my $dbh = C4::Context->dbh; +$dbh->{AutoCommit} = 0; +$dbh->{RaiseError} = 1; + +my $builder = t::lib::TestBuilder->new(); + +# Father +my $kyle = Koha::Patrons->find( + $builder->build( + { + source => 'Borrower', + value => { + firstname => 'Kyle', + surname => 'Hall', + } + } + )->{borrowernumber} +); + +# Mother +my $chelsea = Koha::Patrons->find( + $builder->build( + { + source => 'Borrower', + value => { + firstname => 'Chelsea', + surname => 'Hall', + } + } + )->{borrowernumber} +); + +# Children +my $daria = Koha::Patrons->find( + $builder->build( + { + source => 'Borrower', + value => { + firstname => 'Daria', + surname => 'Hall', + } + } + )->{borrowernumber} +); + +my $kylie = Koha::Patrons->find( + $builder->build( + { + source => 'Borrower', + value => { + firstname => 'Kylie', + surname => 'Hall', + } + } + )->{borrowernumber} +); + +Koha::Patron::Relationship->new({ guarantor_id => $kyle->id, guarantee_id => $daria->id, relationship => 'father' })->store(); +Koha::Patron::Relationship->new({ guarantor_id => $kyle->id, guarantee_id => $kylie->id, relationship => 'father' })->store(); +Koha::Patron::Relationship->new({ guarantor_id => $chelsea->id, guarantee_id => $daria->id, relationship => 'mother' })->store(); +Koha::Patron::Relationship->new({ guarantor_id => $chelsea->id, guarantee_id => $kylie->id, relationship => 'mother' })->store(); +Koha::Patron::Relationship->new({ firstname => 'John', surname => 'Hall', guarantee_id => $daria->id, relationship => 'grandfather' })->store(); +Koha::Patron::Relationship->new({ firstname => 'Debra', surname => 'Hall', guarantee_id => $daria->id, relationship => 'grandmother' })->store(); +Koha::Patron::Relationship->new({ firstname => 'John', surname => 'Hall', guarantee_id => $kylie->id, relationship => 'grandfather' })->store(); +Koha::Patron::Relationship->new({ firstname => 'Debra', surname => 'Hall', guarantee_id => $kylie->id, relationship => 'grandmother' })->store(); + +my @gr; + +@gr = $kyle->guarantee_relationships(); +is( @gr, 2, 'Found 2 guarantee relationships for father' ); +is( $gr[0]->guarantor_id, $kyle->id, 'Guarantor matches for first relationship' ); +is( $gr[0]->guarantee_id, $daria->id, 'Guarantee matches for first relationship' ); +is( $gr[0]->relationship, 'father', 'Relationship is father' ); +is( ref($gr[0]->guarantee), 'Koha::Patron', 'Method guarantee returns a Koha::Patron' ); +is( $gr[0]->guarantee->id, $daria->id, 'Koha::Patron returned is the correct guarantee' ); +is( ref($gr[0]->guarantor), 'Koha::Patron', 'Method guarantor returns a Koha::Patron' ); +is( $gr[0]->guarantor->id, $kyle->id, 'Koha::Patron returned is the correct guarantor' ); + +is( $gr[1]->guarantor_id, $kyle->id, 'Guarantor matches for first relationship' ); +is( $gr[1]->guarantee_id, $kylie->id, 'Guarantee matches for first relationship' ); +is( $gr[1]->relationship, 'father', 'Relationship is father' ); +is( ref($gr[1]->guarantee), 'Koha::Patron', 'Method guarantee returns a Koha::Patron' ); +is( $gr[1]->guarantee->id, $kylie->id, 'Koha::Patron returned is the correct guarantee' ); +is( ref($gr[1]->guarantor), 'Koha::Patron', 'Method guarantor returns a Koha::Patron' ); +is( $gr[1]->guarantor->id, $kyle->id, 'Koha::Patron returned is the correct guarantor' ); + +@gr = $chelsea->guarantee_relationships(); +is( @gr, 2, 'Found 2 guarantee relationships for mother' ); +is( $gr[0]->guarantor_id, $chelsea->id, 'Guarantor matches for first relationship' ); +is( $gr[0]->guarantee_id, $daria->id, 'Guarantee matches for first relationship' ); +is( $gr[0]->relationship, 'mother', 'Relationship is mother' ); +is( ref($gr[0]->guarantee), 'Koha::Patron', 'Method guarantee returns a Koha::Patron' ); +is( $gr[0]->guarantee->id, $daria->id, 'Koha::Patron returned is the correct guarantee' ); +is( ref($gr[0]->guarantor), 'Koha::Patron', 'Method guarantor returns a Koha::Patron' ); +is( $gr[0]->guarantor->id, $chelsea->id, 'Koha::Patron returned is the correct guarantor' ); + +is( $gr[1]->guarantor_id, $chelsea->id, 'Guarantor matches for first relationship' ); +is( $gr[1]->guarantee_id, $kylie->id, 'Guarantee matches for first relationship' ); +is( $gr[1]->relationship, 'mother', 'Relationship is mother' ); +is( ref($gr[1]->guarantee), 'Koha::Patron', 'Method guarantee returns a Koha::Patron' ); +is( $gr[1]->guarantee->id, $kylie->id, 'Koha::Patron returned is the correct guarantee' ); +is( ref($gr[1]->guarantor), 'Koha::Patron', 'Method guarantor returns a Koha::Patron' ); +is( $gr[1]->guarantor->id, $chelsea->id, 'Koha::Patron returned is the correct guarantor' ); + +@gr = $daria->guarantor_relationships(); +is( @gr, 4, 'Found 4 guarantor relationships for child' ); +is( $gr[0]->guarantor_id, $kyle->id, 'Guarantor matches for first relationship' ); +is( $gr[0]->guarantee_id, $daria->id, 'Guarantee matches for first relationship' ); +is( $gr[0]->relationship, 'father', 'Relationship is father' ); +is( ref($gr[0]->guarantee), 'Koha::Patron', 'Method guarantee returns a Koha::Patron' ); +is( $gr[0]->guarantee->id, $daria->id, 'Koha::Patron returned is the correct guarantee' ); +is( ref($gr[0]->guarantor), 'Koha::Patron', 'Method guarantor returns a Koha::Patron' ); +is( $gr[0]->guarantor->id, $kyle->id, 'Koha::Patron returned is the correct guarantor' ); + +is( $gr[1]->guarantor_id, $chelsea->id, 'Guarantor matches for first relationship' ); +is( $gr[1]->guarantee_id, $daria->id, 'Guarantee matches for first relationship' ); +is( $gr[1]->relationship, 'mother', 'Relationship is mother' ); +is( ref($gr[1]->guarantee), 'Koha::Patron', 'Method guarantee returns a Koha::Patron' ); +is( $gr[1]->guarantee->id, $daria->id, 'Koha::Patron returned is the correct guarantee' ); +is( ref($gr[1]->guarantor), 'Koha::Patron', 'Method guarantor returns a Koha::Patron' ); +is( $gr[1]->guarantor->id, $chelsea->id, 'Koha::Patron returned is the correct guarantor' ); + +is( $gr[2]->guarantor_id, undef, 'Grandfather has no id, not a Koha patron' ); +is( $gr[2]->firstname, 'John', 'Grandfather has first name of John' ); +is( $gr[2]->surname, 'Hall', 'Grandfather has surname of Hall' ); +is( $gr[2]->guarantor, undef, 'Calling guarantor method for Grandfather returns undef' ); + +is( $gr[3]->guarantor_id, undef, 'Grandmother has no id, not a Koha patron' ); +is( $gr[3]->firstname, 'Debra', 'Grandmother has first name of John' ); +is( $gr[3]->surname, 'Hall', 'Grandmother has surname of Hall' ); +is( $gr[3]->guarantor, undef, 'Calling guarantor method for Grandmother returns undef' ); + +my @siblings = $daria->siblings; +is( @siblings, 1, 'Method siblings called in list context returns list' ); +is( ref($siblings[0]), 'Koha::Patron', 'List contains a Koha::Patron' ); +is( $siblings[0]->firstname, 'Kylie', 'Sibling from list first name matches correctly' ); +is( $siblings[0]->surname, 'Hall', 'Sibling from list surname matches correctly' ); +is( $siblings[0]->id, $kylie->id, 'Sibling from list patron id matches correctly' ); + +my $siblings = $daria->siblings; +my $sibling = $siblings->next(); +is( ref($siblings), 'Koha::Patrons', 'Calling siblings in scalar context results in a Koha::Patrons object' ); +is( ref($sibling), 'Koha::Patron', 'Method next returns a Koha::Patron' ); +is( $sibling->firstname, 'Kylie', 'Sibling from scalar first name matches correctly' ); +is( $sibling->surname, 'Hall', 'Sibling from scalar surname matches correctly' ); +is( $sibling->id, $kylie->id, 'Sibling from scalar patron id matches correctly' ); + +1; diff --git a/tools/import_borrowers.pl b/tools/import_borrowers.pl index 8c03274..2e14229 100755 --- a/tools/import_borrowers.pl +++ b/tools/import_borrowers.pl @@ -312,6 +312,19 @@ if ( $uploadborrowers && length($uploadborrowers) > 0 ) { } } + # Add a guarantor if we are given a relationship + if ( $borrower{relationship} ) { + Koha::Patron::Relationship->new( + { + guarantee_id => $borrowernumber, + relationship => $borrower{relationship}, + guarantor_id => $borrower{guarantorid} || undef, + firstname => $borrower{contactfirstname} || undef, + surname => $borrower{contactsurname} || undef, + } + )->store(); + } + if ($extended) { if ($ext_preserve) { my $old_attributes = GetBorrowerAttributes($borrowernumber); @@ -339,6 +352,19 @@ if ( $uploadborrowers && length($uploadborrowers) > 0 ) { ); } + # Add a guarantor if we are given a relationship + if ( $borrower{relationship} ) { + Koha::Patron::Relationship->new( + { + guarantee_id => $borrowernumber, + relationship => $borrower{relationship}, + guarantor_id => $borrower{guarantorid} || undef, + firstname => $borrower{contactfirstname} || undef, + surname => $borrower{contactsurname} || undef, + } + )->store(); + } + if ($extended) { SetBorrowerAttributes($borrowernumber, $patron_attributes); } -- 2.1.4