Bug 14570: Make it possible to add multiple guarantors to a record
authorKyle M Hall <kyle@bywatersolutions.com>
Mon, 2 May 2016 15:00:39 +0000 (15:00 +0000)
committerMartin Renvoize <martin.renvoize@ptfs-europe.com>
Tue, 20 Aug 2019 15:04:48 +0000 (16:04 +0100)
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

Signed-off-by: Kim Peine <kmpeine@gmail.com>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Agustin Moyano <agustinmoyano@theke.io>
Signed-off-by: Liz Rea <wizzyrea@gmail.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>

41 files changed:
C4/Auth_with_ldap.pm
C4/Circulation.pm
C4/Members.pm
C4/SIP/ILS/Patron.pm
Koha/Object.pm
Koha/Patron.pm
Koha/Patron/Relationship.pm [new file with mode: 0644]
Koha/Patron/Relationships.pm [new file with mode: 0644]
Koha/Patrons.pm
Koha/Patrons/Import.pm
Koha/REST/V1/Auth.pm
Koha/Schema/Result/Borrower.pm
Koha/Schema/Result/BorrowerRelationship.pm [new file with mode: 0644]
api/v1/swagger/definitions/patron.json
circ/circulation.pl
installer/data/mysql/atomicupdate/bug_14570.perl [new file with mode: 0644]
installer/data/mysql/kohastructure.sql
koha-tmpl/intranet-tmpl/prog/en/columns.def
koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc
koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt
koha-tmpl/intranet-tmpl/prog/en/modules/members/memberentrygen.tt
koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember-brief.tt
koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt
koha-tmpl/intranet-tmpl/prog/js/members.js
koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt
koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-privacy.tt
koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt
members/deletemem.pl
members/memberentry.pl
members/moremember.pl
members/update-child.pl
misc/cronjobs/j2a.pl
opac/opac-memberentry.pl
opac/opac-user.pl
t/Patron.t
t/db_dependent/Circulation/NoIssuesChargeGuarantees.t
t/db_dependent/Items.t
t/db_dependent/Koha/Patrons.t
t/db_dependent/Members.t
t/db_dependent/Patron/Relationships.t [new file with mode: 0755]
tools/import_borrowers.pl

index 6f3b9a3..09db190 100644 (file)
@@ -468,7 +468,6 @@ C4::Auth - Authenticates Koha users
                | contactname         | mediumtext   | YES  |     | NULL    |                |
                | contactfirstname    | text         | YES  |     | NULL    |                |
                | contacttitle        | text         | YES  |     | NULL    |                |
-               | guarantorid         | int(11)      | YES  | MUL | NULL    |                |
                | borrowernotes       | mediumtext   | YES  |     | NULL    |                |
                | relationship        | varchar(100) | YES  |     | NULL    |                |
                | ethnicity           | varchar(50)  | YES  |     | NULL    |                |
index 9961d38..fb798c8 100644 (file)
@@ -785,7 +785,7 @@ sub CanBookBeIssued {
     my $no_issues_charge_guarantees = C4::Context->preference("NoIssuesChargeGuarantees");
     $no_issues_charge_guarantees = undef unless looks_like_number( $no_issues_charge_guarantees );
     if ( defined $no_issues_charge_guarantees ) {
-        my @guarantees = $patron->guarantees();
+        my @guarantees = map { $_->guarantee } $patron->guarantee_relationships();
         my $guarantees_non_issues_charges;
         foreach my $g ( @guarantees ) {
             $guarantees_non_issues_charges += $g->account->non_issues_charges;
index 205f45b..6f2cb37 100644 (file)
@@ -176,7 +176,7 @@ sub patronflags {
     $no_issues_charge_guarantees = undef unless looks_like_number( $no_issues_charge_guarantees );
     if ( defined $no_issues_charge_guarantees ) {
         my $p = Koha::Patrons->find( $patroninformation->{borrowernumber} );
-        my @guarantees = $p->guarantees();
+        my @guarantees = map { $_->guarantee } $p->guarantee_relationships;
         my $guarantees_non_issues_charges;
         foreach my $g ( @guarantees ) {
             $guarantees_non_issues_charges += $g->account->non_issues_charges;
@@ -397,18 +397,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 borrower_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 "" ) {
index 30c0e48..eb8e30b 100644 (file)
@@ -627,7 +627,6 @@ __END__
 | contactname         | mediumtext   | YES  |     | NULL    |                |
 | contactfirstname    | text         | YES  |     | NULL    |                |
 | contacttitle        | text         | YES  |     | NULL    |                |
-| guarantorid         | int(11)      | YES  | MUL | NULL    |                |
 | borrowernotes       | mediumtext   | YES  |     | NULL    |                |
 | relationship        | varchar(100) | YES  |     | NULL    |                |
 | ethnicity           | varchar(50)  | YES  |     | NULL    |                |
index 1c382f2..04d34c4 100644 (file)
@@ -72,8 +72,9 @@ sub new {
             next if not exists $attributes->{$column_name} or defined $attributes->{$column_name};
             delete $attributes->{$column_name};
         }
-        $self->{_result} = $schema->resultset( $class->_type() )
-          ->new($attributes);
+
+        $self->{_result} =
+          $schema->resultset( $class->_type() )->new($attributes);
     }
 
     croak("No _type found! Koha::Object must be subclassed!")
index d13d27d..ab49821 100644 (file)
@@ -27,8 +27,10 @@ use Text::Unaccent qw( unac_string );
 
 use C4::Context;
 use C4::Log;
+use Koha::Account;
 use Koha::AuthUtils;
 use Koha::Checkouts;
+use Koha::Club::Enrollments;
 use Koha::Database;
 use Koha::DateUtils;
 use Koha::Exceptions::Password;
@@ -39,12 +41,11 @@ use Koha::Patron::Categories;
 use Koha::Patron::HouseboundProfile;
 use Koha::Patron::HouseboundRole;
 use Koha::Patron::Images;
+use Koha::Patron::Relationships;
 use Koha::Patrons;
-use Koha::Virtualshelves;
-use Koha::Club::Enrollments;
-use Koha::Account;
 use Koha::Subscription::Routinglists;
 use Koha::Token;
+use Koha::Virtualshelves;
 
 use base qw(Koha::Object);
 
@@ -372,41 +373,67 @@ sub category {
     return Koha::Patron::Category->_new_from_dbic( $self->_result->categorycode );
 }
 
-=head3 guarantor
-
-Returns a Koha::Patron object for this patron's guarantor
+=head3 image
 
 =cut
 
-sub guarantor {
-    my ( $self ) = @_;
-
-    return unless $self->guarantorid();
-
-    return Koha::Patrons->find( $self->guarantorid() );
-}
-
 sub image {
     my ( $self ) = @_;
 
     return scalar Koha::Patron::Images->find( $self->borrowernumber );
 }
 
+=head3 library
+
+Returns a Koha::Library object representing the patron's home library.
+
+=cut
+
 sub library {
     my ( $self ) = @_;
     return Koha::Library->_new_from_dbic($self->_result->branchcode);
 }
 
-=head3 guarantees
+=head3 guarantor_relationships
+
+Returns Koha::Patron::Relationships object for this patron's guarantors
 
-Returns the guarantees (list of Koha::Patron) of this patron
+Returns the set of relationships for the patrons that are guarantors for 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 }, { order_by => { -asc => ['surname','firstname'] } } );
+    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 },
+        {
+            prefetch => 'guarantee',
+            order_by => { -asc => [ 'guarantee.surname', 'guarantee.firstname' ] },
+        }
+    );
 }
 
 =head3 housebound_profile
@@ -444,23 +471,22 @@ Returns the siblings of this patron.
 =cut
 
 sub siblings {
-    my ( $self ) = @_;
+    my ($self) = @_;
 
-    my $guarantor = $self->guarantor;
+    my @guarantors = $self->guarantor_relationships()->guarantors();
 
-    return unless $guarantor;
+    return unless @guarantors;
 
-    return Koha::Patrons->search(
-        {
-            guarantorid => {
-                '!=' => undef,
-                '=' => $guarantor->id,
-            },
-            borrowernumber => {
-                '!=' => $self->borrowernumber,
-            }
-        }
-    );
+    my @siblings =
+      map { $_->guarantee_relationships()->guarantees() } @guarantors;
+
+    return unless @siblings;
+
+    my %seen;
+    @siblings =
+      grep { !$seen{ $_->id }++ && ( $_->id != $self->id ) } @siblings;
+
+    return wantarray ? @siblings : Koha::Patrons->search( { borrowernumber => { -in => [ map { $_->id } @siblings ] } } );
 }
 
 =head3 merge_with
@@ -1419,6 +1445,34 @@ sub _anonymize_column {
     $self->$col($val);
 }
 
+=head3 add_guarantor
+
+    my @relationships = $patron->add_guarantor(
+        {
+            borrowernumber => $borrowernumber,
+            relationships  => $relationship,
+        }
+    );
+
+    Adds a new guarantor to a patron.
+
+=cut
+
+sub add_guarantor {
+    my ( $self, $params ) = @_;
+
+    my $guarantor_id = $params->{guarantor_id};
+    my $relationship = $params->{relationship};
+
+    return Koha::Patron::Relationship->new(
+        {
+            guarantee_id => $self->id,
+            guarantor_id => $guarantor_id,
+            relationship => $relationship
+        }
+    )->store();
+}
+
 =head2 Internal methods
 
 =head3 _type
diff --git a/Koha/Patron/Relationship.pm b/Koha/Patron/Relationship.pm
new file mode 100644 (file)
index 0000000..24994aa
--- /dev/null
@@ -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 scalar Koha::Patrons->find( $self->guarantor_id );
+}
+
+=head3 guarantee
+
+Returns the Koha::Patron object for the guarantee
+
+=cut
+
+sub guarantee {
+    my ( $self ) = @_;
+
+    return scalar Koha::Patrons->find( $self->guarantee_id );
+}
+
+=head3 type
+
+=cut
+
+sub _type {
+    return 'BorrowerRelationship';
+}
+
+1;
diff --git a/Koha/Patron/Relationships.pm b/Koha/Patron/Relationships.pm
new file mode 100644 (file)
index 0000000..0cdfa9f
--- /dev/null
@@ -0,0 +1,99 @@
+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 },
+        {
+            order_by => { -asc => [ 'surname', 'firstname' ] }
+        },
+    );
+
+    return wantarray ? $guarantees->as_list : $guarantees;
+}
+
+=cut
+
+=head3 type
+
+=cut
+
+sub _type {
+    return 'BorrowerRelationship';
+}
+
+sub object_class {
+    return 'Koha::Patron::Relationship';
+}
+
+1;
index c113c2c..8053719 100644 (file)
@@ -125,18 +125,6 @@ sub search_upcoming_membership_expires {
     );
 }
 
-=head3 guarantor
-
-Returns a Koha::Patron object for this borrower's guarantor
-
-=cut
-
-sub guarantor {
-    my ( $self ) = @_;
-
-    return Koha::Patrons->find( $self->guarantorid() );
-}
-
 =head3 search_patrons_to_anonymise
 
     my $patrons = Koha::Patrons->search_patrons_to_anonymise( { before => $older_than_date, [ library => $library ] } );
index b18db0d..3575b8a 100644 (file)
@@ -216,6 +216,11 @@ sub import_patrons {
             next LINE;
         }
 
+        my $relationship        = $borrower{relationship};
+        my $guarantor_id        = $borrower{guarantor_id};
+        delete $borrower{relationship};
+        delete $borrower{guarantor_id};
+
         if ($borrowernumber) {
 
             # borrower exists
@@ -352,6 +357,17 @@ sub import_patrons {
                 );
             }
         }
+
+        # Add a guarantor if we are given a relationship
+        if ( $guarantor_id ) {
+            Koha::Patron::Relationship->new(
+                {
+                    guarantee_id => $borrowernumber,
+                    relationship => $relationship,
+                    guarantor_id => $guarantor_id,
+                }
+            )->store();
+        }
     }
 
     return {
index 88b78dc..8bffc97 100644 (file)
@@ -320,7 +320,7 @@ sub allow_guarantor {
         return;
     }
 
-    my $guarantees = $user->guarantees->as_list;
+    my $guarantees = $user->guarantee_relationships->guarantees->as_list;
     foreach my $guarantee (@{$guarantees}) {
         return 1 if check_object_ownership($c, $guarantee);
     }
index 7000711..024a6da 100644 (file)
@@ -1532,12 +1532,6 @@ __PACKAGE__->many_to_many("ordernumbers", "aqorder_users", "ordernumber");
 # Created by DBIx::Class::Schema::Loader v0.07046 @ 2019-04-25 10:08:38
 # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:3qd/l8OkObSn8gTKTsHrkA
 
-__PACKAGE__->belongs_to(
-    "guarantor",
-    "Koha::Schema::Result::Borrower",
-    { borrowernumber => "guarantorid" },
-);
-
 __PACKAGE__->add_columns(
     '+anonymized' => { is_boolean => 1 },
     '+lost'          => { is_boolean => 1 },
diff --git a/Koha/Schema/Result/BorrowerRelationship.pm b/Koha/Schema/Result/BorrowerRelationship.pm
new file mode 100644 (file)
index 0000000..88cef4f
--- /dev/null
@@ -0,0 +1,118 @@
+use utf8;
+package Koha::Schema::Result::BorrowerRelationship;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+=head1 NAME
+
+Koha::Schema::Result::BorrowerRelationship
+
+=cut
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+=head1 TABLE: C<borrower_relationships>
+
+=cut
+
+__PACKAGE__->table("borrower_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
+
+=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 },
+);
+
+=head1 PRIMARY KEY
+
+=over 4
+
+=item * L</id>
+
+=back
+
+=cut
+
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 guarantee
+
+Type: belongs_to
+
+Related object: L<Koha::Schema::Result::Borrower>
+
+=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<Koha::Schema::Result::Borrower>
+
+=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.07046 @ 2018-07-18 10:52:59
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:R8RThgcrct40Zq0UMW3TWQ
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+1;
index 7fca15b..10675ac 100644 (file)
       "readOnly": true,
       "description": "If any restriction applies to the patron"
     },
-    "guarantor_id": {
-      "type": ["integer", "null"],
-      "description": "patron_id used for children or professionals to link them to guarantor or organizations"
-    },
     "staff_notes": {
       "type": ["string", "null"],
       "description": "a note on the patron's account"
index e7b4459..74c42d8 100755 (executable)
@@ -512,7 +512,7 @@ if ( $patron ) {
     $no_issues_charge_guarantees = undef unless looks_like_number( $no_issues_charge_guarantees );
     if ( defined $no_issues_charge_guarantees ) {
         my $guarantees_non_issues_charges = 0;
-        my $guarantees = $patron->guarantees;
+        my $guarantees = $patron->guarantee_relationships->guarantees;
         while ( my $g = $guarantees->next ) {
             $guarantees_non_issues_charges += $g->account->non_issues_charges;
         }
@@ -567,14 +567,12 @@ my $view = $batch
     : 'circview';
 
 my @relatives;
-if ( $borrowernumber ) {
-    if ( $patron ) {
-        if ( my $guarantor = $patron->guarantor ) {
-            push @relatives, $guarantor->borrowernumber;
-            push @relatives, $_->borrowernumber for $patron->siblings;
-        } else {
-            push @relatives, $_->borrowernumber for $patron->guarantees;
-        }
+if ( $patron ) {
+    if ( my @guarantors = $patron->guarantor_relationships()->guarantors() ) {
+        push( @relatives, $_->id ) for @guarantors;
+        push( @relatives, $_->id ) for $patron->siblings();
+    } else {
+        push( @relatives, $_->id ) for $patron->guarantee_relationships()->guarantees();
     }
 }
 my $relatives_issues_count =
diff --git a/installer/data/mysql/atomicupdate/bug_14570.perl b/installer/data/mysql/atomicupdate/bug_14570.perl
new file mode 100644 (file)
index 0000000..967fd34
--- /dev/null
@@ -0,0 +1,52 @@
+$DBversion = 'XXX';  # will be replaced by the RM
+if( CheckVersion( $DBversion ) ) {
+    # you can use $dbh here like:
+    # $dbh->do( "ALTER TABLE biblio ADD COLUMN badtaste int" );
+    unless (TableExists('borrower_relationships')){
+        $dbh->do(q{
+            CREATE TABLE `borrower_relationships` (
+                  id INT(11) NOT NULL AUTO_INCREMENT,
+                  guarantor_id INT(11) NOT NULL,
+                  guarantee_id INT(11) NOT NULL,
+                  relationship VARCHAR(100) NOT 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=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+        });
+
+        $dbh->do(q{
+            UPDATE borrowers
+            LEFT JOIN borrowers guarantor ON ( borrowers.guarantorid = guarantor.borrowernumber )
+            SET borrowers.guarantorid = NULL WHERE guarantor.borrowernumber IS NULL;
+        });
+
+        $dbh->do(q{
+            INSERT INTO borrower_relationships ( guarantor_id, guarantee_id, relationship )
+            SELECT guarantorid, borrowernumber, relationship FROM borrowers WHERE guarantorid IS NOT NULL;
+        });
+
+    }
+
+     if( column_exists( 'borrowers', 'guarantorid' ) ) {
+        $dbh->do(q{
+            ALTER TABLE borrowers DROP guarantorid;
+        });
+     }
+
+     if( column_exists( 'deletedborrowers', 'guarantorid' ) ) {
+        $dbh->do(q{
+            ALTER TABLE deletedborrowers DROP guarantorid;
+        });
+     }
+
+     if( column_exists( 'borrower_modifications', 'guarantorid' ) ) {
+        $dbh->do(q{
+            ALTER TABLE borrower_modifications DROP guarantorid;
+        });
+     }
+
+    # Always end with this (adjust the bug info)
+    SetVersion( $DBversion );
+    print "Upgrade to $DBversion done (Bug 14570 - Make it possible to add multiple guarantors to a record)\n";
+}
index f637d27..d28822c 100644 (file)
@@ -559,7 +559,6 @@ CREATE TABLE `deletedborrowers` ( -- stores data related to the patrons/borrower
   `contactname` LONGTEXT, -- used for children and profesionals to include surname or last name of guarantor or organization name
   `contactfirstname` MEDIUMTEXT, -- used for children to include first name of guarantor
   `contacttitle` MEDIUMTEXT, -- used for children to include title (Mr., Mrs., etc) of guarantor
-  `guarantorid` int(11) default NULL, -- borrowernumber used for children or professionals to link them to guarantors or organizations
   `borrowernotes` LONGTEXT, -- 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 guarantor
   `sex` varchar(1) default NULL, -- patron/borrower's gender
@@ -1548,7 +1547,6 @@ CREATE TABLE `borrowers` ( -- this table includes information about your patrons
   `contactname` LONGTEXT, -- used for children and profesionals to include surname or last name of guarantor or organization name
   `contactfirstname` MEDIUMTEXT, -- used for children to include first name of guarantor
   `contacttitle` MEDIUMTEXT, -- used for children to include title (Mr., Mrs., etc) of guarantor
-  `guarantorid` int(11) default NULL, -- borrowernumber used for children or professionals to link them to guarantors or organizations
   `borrowernotes` LONGTEXT, -- 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 guarantor
   `sex` varchar(1) default NULL, -- patron/borrower's gender
@@ -1584,7 +1582,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` (191)),
   KEY `firstname_idx` (`firstname` (191)),
   KEY `othernames_idx` (`othernames` (191)),
@@ -3364,7 +3361,6 @@ CREATE TABLE IF NOT EXISTS `borrower_modifications` (
   `contactname` LONGTEXT,
   `contactfirstname` MEDIUMTEXT,
   `contacttitle` MEDIUMTEXT,
-  `guarantorid` int(11) DEFAULT NULL,
   `borrowernotes` LONGTEXT,
   `relationship` varchar(100) DEFAULT NULL,
   `sex` varchar(1) DEFAULT NULL,
@@ -4074,6 +4070,21 @@ CREATE TABLE IF NOT EXISTS club_fields (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
 
 --
+-- Table structure for table 'guarantors_guarantees'
+--
+
+DROP TABLE IF EXISTS borrower_relationships;
+CREATE TABLE `borrower_relationships` (
+      id INT(11) NOT NULL AUTO_INCREMENT,
+      guarantor_id INT(11) NULL DEFAULT NULL,
+      guarantee_id INT(11) NOT NULL,
+      relationship VARCHAR(100) NOT 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=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
 -- Table structure for table `illrequests`
 --
 
index ffbfae0..a8d1591 100644 (file)
@@ -7,7 +7,6 @@
 <field name="borrowers.othernames">Other name</field>
 <field name="borrowers.sex">Gender</field>
 <field name="borrowers.relationship">Relationship</field>
-<field name="borrowers.guarantorid">Guarantor borrower number</field>
 <field name="borrowers.streetnumber">Street number</field>
 <field name="borrowers.streettype">Street type</field>
 <field name="borrowers.address">Address</field>
index 6e51ff5..d9d9b27 100644 (file)
@@ -7,21 +7,14 @@
 [% USE scalar %]
 <div id="toolbar" class="btn-toolbar">
     [% IF CAN_user_borrowers_edit_borrowers %]
-        [% IF ( guarantor ) %]
-            <a id="editpatron" class="btn btn-default" href="/cgi-bin/koha/members/memberentry.pl?op=modify&amp;destination=circ&amp;borrowernumber=[% patron.borrowernumber | html %]&amp;guarantorid=[% guarantor.borrowernumber | html %]&amp;categorycode=[% patron.categorycode | html %]">
-        [% ELSE %]
-            <a id="editpatron" class="btn btn-default" href="/cgi-bin/koha/members/memberentry.pl?op=modify&amp;destination=circ&amp;borrowernumber=[% patron.borrowernumber | html %]&amp;categorycode=[% patron.categorycode | html %]">
-        [% END %]
-        <i class="fa fa-pencil"></i> Edit</a>
+        <a id="editpatron" class="btn btn-default" href="/cgi-bin/koha/members/memberentry.pl?op=modify&amp;destination=circ&amp;borrowernumber=[% patron.borrowernumber | html %]&amp;categorycode=[% patron.categorycode | html %]"><i class="fa fa-pencil"></i> Edit</a>
     [% END %]
 
     [% IF CAN_user_borrowers_edit_borrowers %]
         [% IF patron.is_adult AND Koha.Preference("borrowerRelationship") %]
-            <a id="addchild" class="btn btn-default" href="/cgi-bin/koha/members/memberentry.pl?op=add&amp;guarantorid=[% patron.borrowernumber | html %]"><i class="fa fa-plus"></i> Add child</a>
-        [% END %]
-        [% IF CAN_user_borrowers_edit_borrowers %]
-            <a id="changepassword" class="btn btn-default" href="/cgi-bin/koha/members/member-password.pl?member=[% patron.borrowernumber | html %]"><i class="fa fa-lock"></i> Change password</a>
+            <a id="addchild" class="btn btn-default" href="/cgi-bin/koha/members/memberentry.pl?op=add&amp;guarantor_id=[% patron.borrowernumber | html %]"><i class="fa fa-plus"></i> Add child</a>
         [% END %]
+        <a id="changepassword" class="btn btn-default" href="/cgi-bin/koha/members/member-password.pl?member=[% patron.borrowernumber | html %]"><i class="fa fa-lock"></i> Change password</a>
         <a id="duplicate" class="btn btn-default" href="/cgi-bin/koha/members/memberentry.pl?op=duplicate&amp;borrowernumber=[% patron.borrowernumber | html %]&amp;categorycode=[% patron.categorycode | html %]"><i class="fa fa-copy"></i> Duplicate</a>
     [% END %]
 
index eba096c..3c41a25 100644 (file)
                             </li>
 
                             [% IF relatives_issues_count %]
-                                <li><a id="relatives-issues-tab" href="#relatives-issues">Relatives' checkouts</a></li>
+                                <li><a id="relatives-issues-tab" href="#relatives-issues">[% relatives_issues_count %] Relatives' checkouts</a></li>
                             [% END %]
 
                             <li>
index 62fc2db..ec20030 100644 (file)
@@ -1,5 +1,6 @@
 [% USE raw %]
 [% USE Asset %]
+[% USE To %]
 [% USE Koha %]
 [% USE KohaDates %]
 [% USE Branches %]
        </fieldset>
 [% END # hide fieldset %]
 
-[% IF ( showguarantor ) %]
-    <input type="hidden" id="guarantorid" name="guarantorid"   value="[% guarantorid | html %]" />
-    [% UNLESS step_6 %]
-        <input type="hidden" name="branchcode" value="[% branchcode | html %]" />
-    [% END %]
-    <fieldset id="memberentry_guarantor" class="rows">
-        <legend id="guarantor_lgd">Guarantor information</legend>
-        <ol>
-[% IF ( P ) %]
-               [% IF ( guarantorid ) %]
-               <li id="contact-details">
-               [% ELSE %]
-               <li id="contact-details" style="display: none">
-               [% END %]
-                <span class="label">Organization #:</span> [% IF ( guarantorid ) %] <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% guarantorid | uri %]" target="blank">[% guarantorid | html %]</a>[% END %]
-               </li>
-               <li>
-                   <label for="contactname">Organization name: </label>
-                   [% IF ( guarantorid ) %]
-                   <span>[% contactname | html %]</span>
-                   <input name="contactname" id="contactname" type="hidden" size="20" value="[% contactname | html %]" />
-                   [% ELSE %]
-                    <input name="contactname" id="contactname" type="text" size="20" value="[% contactname | html %]" />
-                   [% END %]
-               </li>
-[% ELSE %]
- [% IF ( C ) %]
- [% IF ( guarantorid ) %]
- <li id="contact-details">
- [% ELSE %]
- <li id="contact-details" style="display: none">
- [% END %]
-     <span class="label">Patron #:</span>
-     [% IF guarantorid %]
-        [% IF logged_in_user.can_see_patron_infos( guarantor ) %]
-            <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% guarantorid | uri %]" target="blank">[% guarantorid | html %]</a>
-        [% ELSE %]
-            [% guarantorid | html %]
-        [% END %]
-    [% END %]
- </li>
-        [% UNLESS nocontactname %]
- <li>
-     <label for="contactname">Surname: </label>
-     [% IF ( guarantorid ) %]
-     <span>[% contactname | html %]</span>
-     <input name="contactname" id="contactname" type="hidden" size="20" value="[% contactname | html %]" />
-     [% ELSE %]
-        <input name="contactname" id="contactname" type="text" size="20" value="[% contactname | html %]" />
-     [% END %]
- </li>
-        [% END %]
-        [% UNLESS nocontactfirstname %]
- <li>
-     <label for="contactfirstname">First name: </label>
-     [% IF ( guarantorid ) %]
-     <span>[% contactfirstname | html %]</span>
-     <input name="contactfirstname" id="contactfirstname" type="hidden" size="20" value="[% contactfirstname | html %]" />
-     [% ELSE %]
-        <input name="contactfirstname" id="contactfirstname" type="text" size="20" value="[% contactfirstname | html %]" />
-     [% END %]
- </li>
-        [% END %]
- [% IF ( relshiploop ) %]
- <li>
-     <label for="relationship">Relationship: </label>
-     <select name="relationship" id="relationship" >
-         [% FOREACH relshiploo IN relshiploop %]
-         [% IF ( relshiploo.selected ) %]
-         <option value="[% relshiploo.relationship | html %]" selected="selected" >[% relshiploo.relationship | html %]</option>
-         [% ELSE %]
-         <option value="[% relshiploo.relationship | html %]">[% relshiploo.relationship | html %]</option>
-         [% END %]
-         [% END %]
-     </select>
- </li>
- [% END %]
- [% END %]
-[% END %]
-        <li>
-            <span class="label">&nbsp;</span>
-            [% IF ( guarantorid ) %]
-            <input id="guarantorsearch" type="button" value="Change" onclick="Dopopguarantor('guarantor_search.pl');" />
-            [% ELSE %]
-            <input id="guarantorsearch" type="button" value="Set to patron" onclick="Dopopguarantor('guarantor_search.pl');" />
-            [% END %]
-            <input id="guarantordelete" type="button" value="Delete" />
-        </li>
-    [% IF guarantorid && Koha.Preference('AllowStaffToSetCheckoutsVisibilityForGuarantor') %]
-        <li>
-            <label for="privacy_guarantor_checkouts">Show checkouts to guarantor</label>
-            <select name="privacy_guarantor_checkouts" id="privacy_guarantor_checkouts">
-                [% IF privacy_guarantor_checkouts %]
-                    <option value="0">No</option>
-                    <option value="1" selected>Yes</option>
-                [% ELSE %]
-                    <option value="0" selected>No</option>
-                    <option value="1">Yes</option>
+[% IF show_guarantor || guarantor %]
+    [% SET possible_relationships = Koha.Preference('borrowerRelationship') %]
+    <fieldset class="rows">
+        <legend>Guarantor information</legend>
+
+        <span id="guarantor_relationships">
+            [% FOREACH r IN relationships %]
+                <fieldset class="rows">
+                    <ol>
+                        [% IF category_type == 'P' %]
+                            <li id="contact-details">
+                                <span class="label">Organization #:</span> [% IF ( r.guarantor_id ) %] <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% r.guarantor_id | uri %]" target="blank">[% r.guarantor_id | html %]</a>[% END %]
+                            </li>
+
+                            <li>
+                                <label for="guarantor_surname">Organization name: </label>
+                                <span>[% r.guarantor.surname | html %]</span>
+                            </li>
+                        [% ELSE %]
+                            <li id="contact-details">
+                                <span class="label">Patron #:</span>
+                                <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% r.guarantor_id | uri %]" target="blank">[% r.guarantor_id | html %]</a>
+                            </li>
+
+                            [% IF r.guarantor.surname %]
+                                <li>
+                                    <label for="guarantor_surname">Surname: </label>
+                                    <span>[% r.guarantor.surname | html %]</span>
+                                </li>
+                            [% END %]
+
+                            [% IF r.guarantor.firstname %]
+                                <li>
+                                    <label for="guarantor_firstname">First name: </label>
+                                    <span>[% r.guarantor.firstname | html %]</span>
+                                </li>
+                            [% END %]
+
+                            <li>
+                                <label for="relationship">Relationship: </label>
+                                <span>[% r.relationship | html %]</span>
+                            </li>
+
+                            <li>
+                                <label for="delete_guarantor-[% r.id | uri %]">Delete: </label>
+                                <input type="checkbox" id="delete_guarantor-[% r.id | uri %]" name="delete_guarantor" value="[% r.id | html %]" />
+                            </li>
+                        [% END %]
+                    </ol>
+                </fieldset>
+            [% END # END relationships foreach %]
+        </span>
+
+        <fieldset class="rows guarantor" id="guarantor_template">
+            <ol>
+                <li class="guarantor_id">
+                    <span class="label">Patron #:</span>
+                    <span class="new_guarantor_id_text"></span>
+                    <input type="hidden" class="new_guarantor_id" name="new_guarantor_id" value=""/>
+                </li>
+
+                <li>
+                    <label for="guarantor_surname">Surname: </label>
+                    <span class="new_guarantor_surname_text"></span>
+                </li>
+
+                <li>
+                    <label for="guarantor_firstname">First name: </label>
+                    <span class="new_guarantor_firstname_text"></span>
+                </li>
+
+                <li>
+                    <label for="guarantor_relationship">Relationship: </label>
+                    <select class="new_guarantor_relationship" name="new_guarantor_relationship">
+                        [% FOREACH pr IN possible_relationships.split('\|') %]
+                            <option value="[% pr | html %]">[% pr | html %]</option>
+                        [% END %]
+                    </select>
+                </li>
+
+                <li>
+                    <label for="guarantor_cancel">&nbsp;</label>
+                    <span><a href="#" class="guarantor_cancel">Cancel</a></span>
+                </li>
+            </ol>
+        </fieldset>
+
+            <ol>
+                <input type="hidden" id="guarantor_id" value="" />
+                <input name="guarantor_surname" id="guarantor_surname" type="hidden" />
+                <input name="guarantor_firstname" id="guarantor_firstname" type="hidden" />
+
+                <li>
+                    <a href="#" id="guarantor_search" class="btn btn-sm"><i class="fa fa-search"></i> Search</a>
+                </li>
+
+                [% IF relationships && Koha.Preference('AllowStaffToSetCheckoutsVisibilityForGuarantor') %]
+                    <li>
+                        <label for="privacy_guarantor_checkouts">Show checkouts to guarantors</label>
+                        <select name="privacy_guarantor_checkouts" id="privacy_guarantor_checkouts">
+                            [% IF privacy_guarantor_checkouts %]
+                                <option value="0">No</option>
+                                <option value="1" selected>Yes</option>
+                            [% ELSE %]
+                                <option value="0" selected>No</option>
+                                <option value="1">Yes</option>
+                            [% END %]
+                        </select>
+                        <div class="hint">Allow guarantors of this patron to view this patron's checkouts from the OPAC</div>
+                    </li>
                 [% END %]
-            </select>
-            <div class="hint">Allow guarantor of this patron to view this patron's checkouts from the OPAC</div>
-        </li>
-    [% END %]
-        </ol>
+            </ol>
     </fieldset>
-
 [% END %]
+
+
 [% UNLESS noaddress && noaddress2 && nocity && nostate && nozipcode && nocountry %]
     [% IF Koha.Preference( 'AddressFormat' ) %]
         [% INCLUDE "member-main-address-style-${ Koha.Preference( 'AddressFormat' ) }.inc" %]
 [% END # nostreet && nocity etc group%]
 
 [% UNLESS nophone && nophonepro && nomobile && noemail && noemailpro && nofax %]
-  <fieldset class="rows" id="memberentry_contact">
-    <legend id="contact_lgd">Contact</legend><ol>
-        [% UNLESS nophone %]
-      <li>
-      [% IF ( mandatoryphone ) %]
-      <label for="phone" class="required">
-      [% ELSE %]
-      <label for="phone">
-      [% END %]
-      Primary phone: </label>
-        <input type="text" id="phone" name="phone" value="[% phone | html %]" />
-         [% IF ( mandatoryphone ) %]<span class="required">Required</span>[% END %]<div class="hint">Shows on transit slips</div>
+    <fieldset class="rows" id="memberentry_contact">
+        <legend id="contact_lgd">Contact</legend><ol>
 
-    </li>
+        [% UNLESS nocontactname %]
+            <li>
+                [% IF ( mandatorycontactname ) %]
+                    <label for="contactname" class="required">
+                [% ELSE %]
+                    <label for="contactname">
+                [% END %]
+                Guarantor surname: </label>
+                <input type="text" id="contactname" name="contactname" value="[% contactname | html %]" />
+                [% IF ( mandatorycontactname ) %]<span class="required">Required</span>[% END %]<div class="hint">Non-patron guarantor surname</div>
+            </li>
+        [% END %]
+
+        [% UNLESS nocontactfirstname %]
+            <li>
+                [% IF ( mandatorycontactfirstname ) %]
+                    <label for="contactfirstname" class="required">
+                [% ELSE %]
+                    <label for="contactfirstname">
+                [% END %]
+                Guarantor first name: </label>
+                <input type="text" id="contactfirstname" name="contactfirstname" value="[% contactfirstname | html %]" />
+                [% IF ( mandatorycontactfirstname ) %]<span class="required">Required</span>[% END %]<div class="hint">Non-patron guarantor first name</div>
+            </li>
         [% END %]
+
+               [% UNLESS norelationship %]
+                       [% IF possible_relationships %]
+                <li>
+                    <label for="relationship">Relationship: </label>
+                    <select class="relationship" name="relationship">
+                        <option value=""></option>
+                        [% FOREACH pr IN possible_relationships.split('\|') %]
+                            [% IF pr == relationship %]
+                                <option value="[% pr %]" selected="selected">[% pr %]</option>
+                            [% ELSE %]
+                                <option value="[% pr %]">[% pr %]</option>
+                            [% END %]
+                        [% END %]
+                    </select>
+                </li>
+                       [% END %]
+               [% END %]
+
+        [% UNLESS nophone %]
+            <li>
+                [% IF ( mandatoryphone ) %]
+                    <label for="phone" class="required">
+                [% ELSE %]
+                    <label for="phone">
+                [% END %]
+                Primary phone: </label>
+                <input type="text" id="phone" name="phone" value="[% phone | html %]" />
+                [% IF ( mandatoryphone ) %]<span class="required">Required</span>[% END %]<div class="hint">Shows on transit slips</div>
+            </li>
+        [% END %]
+
         [% UNLESS nophonepro %]
-    <li>
-      [% IF ( mandatoryphonepro ) %]
-      <label for="phonepro" class="required">
-      [% ELSE %]
-      <label for="phonepro">
-      [% END %]
-      Secondary phone: </label>
-    <input type="text" id="phonepro" name="phonepro" value="[% phonepro | html %]" />
-         [% IF ( mandatoryphonepro ) %]<span class="required">Required</span>[% END %]
-    </li>
+            <li>
+                [% IF ( mandatoryphonepro ) %]
+                    <label for="phonepro" class="required">
+                [% ELSE %]
+                    <label for="phonepro">
+                [% END %]
+                Secondary phone: </label>
+                <input type="text" id="phonepro" name="phonepro" value="[% phonepro | html %]" />
+                [% IF ( mandatoryphonepro ) %]<span class="required">Required</span>[% END %]
+            </li>
         [% END %]
+
         [% UNLESS nomobile %]
-    <li>
-      [% IF ( mandatorymobile ) %]
-      <label for="mobile" class="required">
-      [% ELSE %]
-      <label for="mobile">
-      [% END %]
-      Other phone: </label>
-        <input type="text" id="mobile" name="mobile" value="[% mobile | html %]" />
-         [% IF ( mandatorymobile ) %]<span class="required">Required</span>[% END %]
-    </li>
+            <li>
+                [% IF ( mandatorymobile ) %]
+                    <label for="mobile" class="required">
+                [% ELSE %]
+                    <label for="mobile">
+                [% END %]
+                Other phone: </label>
+                <input type="text" id="mobile" name="mobile" value="[% mobile | html %]" />
+                [% IF ( mandatorymobile ) %]<span class="required">Required</span>[% END %]
+            </li>
         [% END %]
-        [% UNLESS noemail %]
-    <li>
-      [% IF ( mandatoryemail ) %]
-      <label for="email" class="required">
-      [% ELSE %]
-      <label for="email">
-      [% END %]
-      Primary email: </label>
-        <input type="text" id="email" name="email" size="45" value="[% email | html %]" />
-         [% IF ( mandatoryemail ) %]<span class="required">Required</span>[% END %]<div class="hint">Shows on transit slips</div>
 
-    </li>
+        [% UNLESS noemail %]
+            <li>
+                [% IF ( mandatoryemail ) %]
+                    <label for="email" class="required">
+                [% ELSE %]
+                    <label for="email">
+                [% END %]
+                Primary email: </label>
+                <input type="text" id="email" name="email" size="45" value="[% email | html %]" />
+                [% IF ( mandatoryemail ) %]<span class="required">Required</span>[% END %]<div class="hint">Shows on transit slips</div>
+            </li>
         [% END %]
+
         [% UNLESS noemailpro %]
-    <li>
-      [% IF ( mandatoryemailpro ) %]
-      <label for="emailpro" class="required">
-      [% ELSE %]
-      <label for="emailpro">
-      [% END %]
-      Secondary email: </label>
-        <input type="text" id="emailpro" name="emailpro" size="45" value="[% emailpro | html %]" />
-         [% IF ( mandatoryemailpro ) %]<span class="required">Required</span>[% END %]
-    </li>
+            <li>
+                [% IF ( mandatoryemailpro ) %]
+                    <label for="emailpro" class="required">
+                [% ELSE %]
+                    <label for="emailpro">
+                [% END %]
+                Secondary email: </label>
+                <input type="text" id="emailpro" name="emailpro" size="45" value="[% emailpro | html %]" />
+                [% IF ( mandatoryemailpro ) %]<span class="required">Required</span>[% END %]
+            </li>
         [% END %]
+
         [% UNLESS nofax %]
-    <li>
-      [% IF ( mandatoryfax ) %]
-      <label for="fax" class="required">
-      [% ELSE %]
-      <label for="fax">
-      [% END %]
-      Fax: </label>
-        <input type="text" id="fax" name="fax" value="[% fax | html %]" />
-         [% IF ( mandatoryfax ) %]<span class="required">Required</span>[% END %]
-    </li>
+            <li>
+                [% IF ( mandatoryfax ) %]
+                    <label for="fax" class="required">
+                [% ELSE %]
+                    <label for="fax">
+                [% END %]
+                Fax: </label>
+                <input type="text" id="fax" name="fax" value="[% fax | html %]" />
+                [% IF ( mandatoryfax ) %]<span class="required">Required</span>[% END %]
+            </li>
         [% END %]
-       </ol>
-  </fieldset>
+    </ol>
+</fieldset>
 [%END # hide fieldset %]
 
 <!-- ************************ STEP_1 *********************** -->
                 }
             [% END %]
 
+            [% IF guarantor %]
+                select_user( '[% guarantor.borrowernumber %]', [% To.json( guarantor.unblessed ) | $raw %] );
+            [% END %]
+
             $("#cn_max").hide();
             var content;
             $("#cardnumber").on("keydown", function(e){
index c68fd9d..5170914 100644 (file)
@@ -1,3 +1,4 @@
+[% USE raw %]
 [% USE Koha %]
 [% USE KohaDates %]
 [% SET footerjs = 1 %]
                 [% IF ( patron.dateofbirth ) %]<li><span class="label">Date of birth:</span>[% patron.dateofbirth | $KohaDates %]</li>[% END %]
                 [% IF ( patron.sex ) %]<li><span class="label">Gender:</span>[% IF ( patron.sex == 'F' ) %]Female[% ELSIF ( patron.sex == 'M' ) %]Male[% ELSE %][% patron.sex | html %][% END %]</li>[% END %]
             [% END %]
-            [% IF ( guarantees ) %]
-                <li><span class="label">Guarantees:</span><ul>[% FOREACH guarantee IN guarantees %]<li><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% guarantee.borrowernumber | uri %]">[% INCLUDE 'patron-title.inc' patron=guarantee %]</a></li>[% END %]</ul></li>
-            [% END %]
-            [% IF ( guarantor ) %]
-                <li><span class="label">Guarantor:</span><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% guarantor.borrowernumber | uri %]">[% guarantor.surname | html %], [% guarantor.firstname | html %]</a></li>
+
+            [% IF guarantees %]
+                <li>
+                    <span class="label">Guarantees:</span>
+                    <ul>
+                        [% FOREACH guarantee IN guarantees %]
+                            <li><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% guarantee.borrowernumber | uri %]">[% guarantee.firstname | html %] [% guarantee.surname | html %]</a></li>
+                        [% END %]
+                    </ul>
+                </li>
+            [% ELSIF guarantor_relationships %]
+                [% FOREACH gr IN guarantor_relationships %]
+                    <li>
+                        <span class="label">Guarantor:</span>
+                        [% SET guarantor = gr.guarantor %]
+                        <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% guarantor.id | uri %]">[% guarantor.firstname | html %] [% guarantor.surname | html %]</a>
+                    </li>
+                [% END %]
             [% END %]
-        </ol>
+           </ol>
     </div>
 </div>
 </div>
index 86d26a5..8076459 100644 (file)
                                                     [% END %]
                                                 </ul>
                                             </li>
-                                        [% ELSIF guarantor %]
-                                            <li>
-                                                <span class="label">Guarantor:</span>
-                                                [% IF guarantor.borrowernumber AND logged_in_user.can_see_patron_infos( guarantor ) %]
-                                                    <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% guarantor.borrowernumber | uri %]">[% guarantor.firstname | html %] [% guarantor.surname | html %]</a>
-                                                [% ELSE %]
-                                                    [% guarantor.firstname | html %] [% guarantor.surname | html %]
-                                                [% END %]
-                                            </li>
+                                        [% ELSIF guarantor_relationships %]
+                                            [% FOREACH gr IN guarantor_relationships %]
+                                                <li>
+                                                    <span class="label">Guarantor:</span>
+                                                    [% SET guarantor = gr.guarantor %]
+                                                    [% IF logged_in_user.can_see_patron_infos( guarantor ) %]
+                                                        <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% guarantor.id | uri %]">[% guarantor.firstname | html %] [% guarantor.surname | html %]</a>
+                                                    [% END %]
+                                                </li>
+                                            [% END %]
                                         [% END %]
                                     </ol>
                                 </div> [% # /div.rows %]
index ea1f7d7..5e12bb7 100644 (file)
@@ -129,10 +129,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', '');
@@ -170,48 +166,38 @@ function update_category_code(category_code) {
 }
 
 function select_user(borrowernumber, borrower) {
-    var form = $('#entryform').get(0);
-    if (form.guarantorid.value) {
-        $("#contact-details, #quick_add_form #contact-details").find('a').remove();
-        $("#contactname, #contactfirstname, #quick_add_form #contactname, #quick_add_form #contactfirstname").parent().find('span').remove();
+    $('#guarantor_id').val(borrower.borrowernumber);
+    $('#guarantor_surname').val(borrower.surname);
+    $('#guarantor_firstname').val(borrower.firstname);
+
+    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 id = borrower.borrowernumber;
-    form.guarantorid.value = id;
-    $('#contact-details, #quick_add_form #contact-details')
-        .show()
-        .find('span')
-        .after('<a target="blank" href="/cgi-bin/koha/members/moremember.pl?borrowernumber=' + id + '">' + id + '</a>');
-
-    $(form.contactname)
-        .val(borrower.surname)
-        .before('<span>' + borrower.surname + '</span>').get(0).type = 'hidden';
-    $("#quick_add_form #contactname").val(borrower.surname).before('<span>'+borrower.surname+'</span.').attr({type:"hidden"});
-    $(form.contactfirstname,"#quick_add_form #contactfirstname")
-        .val(borrower.firstname)
-        .before('<span>' + borrower.firstname + '</span>').get(0).type = 'hidden';
-    $("#quick_add_form #contactfirstname").val(borrower.firstname).before('<span>'+borrower.firstname+'</span.').attr({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;
-
-    $("#quick_add_form #streetnumber").val(borrower.streetnumber);
-    $("#quick_add_form #address").val(borrower.address);
-    $("#quick_add_form #address2").val(borrower.address2);
-    $("#quick_add_form #city").val(borrower.city);
-    $("#quick_add_form #state").val(borrower.state);
-    $("#quick_add_form #zipcode").val(borrower.zipcode);
-    $("#quick_add_form #country").val(borrower.country);
-    $("#quick_add_form select[name='branchcode']").val(borrower.branchcode);
-
-    form.guarantorsearch.value = LABEL_CHANGE;
-    $("#quick_add_form #guarantorsearch").val(LABEL_CHANGE);
+    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();
 
     return 0;
 }
@@ -284,13 +270,17 @@ $(document).ready(function(){
     });
 
     $("fieldset.rows input, fieldset.rows select").addClass("noEnterSubmit");
-    $("#guarantordelete").click(function() {
-        $("#quick_add_form #contact-details, #contact-details").hide().find('a').remove();
-        $("#quick_add_form #guarantorid, #quick_add_form  #contactname, #quick_add_form #contactfirstname, #guarantorid, #contactname, #contactfirstname").each(function () { this.value = ""; });
-        $("#quick_add_form #contactname, #quick_add_form #contactfirstname, #contactname, #contactfirstname")
-            .each(function () { this.type = 'text'; })
-            .parent().find('span').remove();
-        $("#quick_add_form #guarantorsearch, #guarantorsearch").val(LABEL_SET_TO_PATRON);
+
+    $('#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();
     });
 
     $(document.body).on('change','.select_city',function(){
index 0b6e26c..90a1d3f 100644 (file)
                     <div class="alert">You typed in the wrong characters in the box before submitting. Please try again.</div>
                 [% END %]
 
-                [% IF borrower.guarantorid && !Koha.Preference('OPACPrivacy') && Koha.Preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') %]
+                [% IF patron.guarantor_relationships && !Koha.Preference('OPACPrivacy') && Koha.Preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') %]
                     <fieldset class="rows" id="memberentry_privacy">
                         <legend id="privacy_legend">Privacy</legend>
                         <ol>
                                     <span id="update_privacy_guarantor_checkouts_message" class="alert" style="display:none"></span>
                                 </span>
                                 <span class="hint">
-                                    Your guarantor is <i>[% guarantor.firstname | html %] [% guarantor.surname | html %]</i>
+                                    Guaranteed by
+                                    [% FOREACH gr IN patron.guarantor_relationships %]
+                                        [% SET g = gr.guarantor %]
+                                        [% g.firstname | html %] [% g.surname | html %]
+                                        [%- IF ! loop.last %], [% END %]
+                                    [% END %]
                                 </span>
                             </li>
                         </ol>
                 }
             });
 
-            [% IF borrower.guarantorid && !Koha.Preference('OPACPrivacy') && Koha.Preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') %]
+            [% IF patron.guarantor_relationships && !Koha.Preference('OPACPrivacy') && Koha.Preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') %]
                 $('#update_privacy_guarantor_checkouts').click( function() {
                     $.post( "/cgi-bin/koha/svc/patron/show_checkouts_to_relatives", { privacy_guarantor_checkouts: $('#privacy_guarantor_checkouts').val() }, null, 'json')
                      .done(function( data ) {
index 84a2519..6583b7c 100644 (file)
@@ -71,7 +71,7 @@
                                         </select>
                                     </div>
 
-                                    [% IF borrower.guarantorid && Koha.Preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') %]
+                                    [% IF borrower.guarantor_relationships && Koha.Preference('AllowPatronToSetCheckoutsVisibilityForGuarantor') %]
                                         <div>
                                             <label for="privacy_guarantor_checkouts">Allow your guarantor to view your current checkouts?</label>
                                             <select name="privacy_guarantor_checkouts">
                                                 [% END %]
                                             </select>
                                             <span class="hint">
-                                                Your guarantor is <i>[% borrower.guarantor.firstname | html %] [% borrower.guarantor.surname | html %]</i>
+                                                Guaranteed by
+                                                [% FOREACH gr IN borrower.guarantor_relationships %]
+                                                    [% SET g = gr.guarantor %]
+                                                    [% g.firstname | html %] [% g.surname | html %]
+                                                    [%- IF ! loop.last %], [% END %]
+                                                [% END %]
                                             </span>
                                         </div>
                                     [% END %]
index d94a05b..40f5eed 100644 (file)
 
                                     <tbody>
                                         [% FOREACH r IN relatives %]
-                                            [% FOREACH i IN r.issues %]
+                                            [% FOREACH c IN r.checkouts %]
                                                 <tr>
                                                     <td>
                                                         <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% i.item.biblio.biblionumber | uri %]">
                                                     </td>
 
                                                     <td>
-                                                        [% i.date_due | $KohaDates %]
+                                                        [% c.date_due | $KohaDates %]
                                                     </td>
 
                                                     <td>
-                                                        [% i.item.barcode | html %]
+                                                        [% c.item.barcode | html %]
                                                     </td>
 
                                                     <td>
-                                                        [% i.item.itemcallnumber | html %]
+                                                        [% c.item.itemcallnumber | html %]
                                                     </td>
 
                                                     <td>
index 440f9d0..b30d28d 100755 (executable)
@@ -84,7 +84,7 @@ if (C4::Context->preference("IndependentBranches")) {
 
 my $op = $input->param('op') || 'delete_confirm';
 my $dbh = C4::Context->dbh;
-my $is_guarantor = $dbh->selectrow_array("SELECT COUNT(*) FROM borrowers WHERE guarantorid=?", undef, $member);
+my $is_guarantor = $patron->guarantee_relationships->count;
 my $countholds = $dbh->selectrow_array("SELECT COUNT(*) FROM reserves WHERE borrowernumber=?", undef, $member);
 if ( $op eq 'delete_confirm' or $countissues > 0 or $charges or $is_guarantor ) {
 
@@ -97,8 +97,8 @@ if ( $op eq 'delete_confirm' or $countissues > 0 or $charges or $is_guarantor )
     if ( $charges > 0 ) {
         $template->param(charges => $charges);
     }
-    if ($is_guarantor) {
-        $template->param(guarantees => 1);
+    if ( $is_guarantor ) {
+        $template->param( guarantees => 1 );
     }
     if($countholds > 0){
         $template->param(ItemsOnHold => $countholds);
index 18f3a96..9aa1462 100755 (executable)
@@ -83,7 +83,6 @@ if ( C4::Context->preference('SMSSendDriver') eq 'Email' ) {
     $template->param( sms_providers => \@providers );
 }
 
-my $guarantorid    = $input->param('guarantorid');
 my $actionType     = $input->param('actionType') || '';
 my $modify         = $input->param('modify');
 my $delete         = $input->param('delete');
@@ -99,13 +98,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
+$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->multi_param('delete_guarantor');
+foreach my $id ( @delete_guarantor ) {
+    my $r = Koha::Patron::Relationships->find( $id );
+    $r->delete() if $r;
+}
+
 ## Deal with debarments
 $template->param(
     debarments => scalar GetDebarments( { borrowernumber => $borrowernumber } ) );
@@ -137,14 +148,14 @@ $template->param("uppercasesurnames" => C4::Context->preference('uppercasesurnam
 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( "quickadd" => 1 ) if ( $quickadd );
@@ -224,6 +235,10 @@ if ( $op eq 'insert' || $op eq 'modify' || $op eq 'save' || $op eq 'duplicate' )
         qr/^add_debarment$/, qr/^debarred_expiration$/, qr/^remove_debarment$/, # We already dealt with debarments previously
         qr/^housebound_chooser$/, qr/^housebound_deliverer$/,
         qr/^select_city$/,
+        qr/^new_guarantor_/,
+        qr/^guarantor_firstname$/,
+        qr/^guarantor_surname$/,
+        qr/^delete_guarantor$/,
     );
     for my $regexp (@keys_to_delete) {
         for (keys %newdata) {
@@ -248,26 +263,6 @@ if ( ( $op eq 'insert' ) and !$nodouble ) {
     }
 }
 
-  #recover all data from guarantor address phone ,fax... 
-if ( $guarantorid ) {
-    if (my $guarantor = Koha::Patrons->find( $guarantorid )) {
-        my $guarantordata = $guarantor->unblessed;
-        $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'));
@@ -431,6 +426,7 @@ if ((!$nok) and $nodouble and ($op eq 'insert' or $op eq 'save')){
             # Lot of code will need to be removed from this script to handle exceptions raised by Koha::Patron->store
             warn "Patron creation failed! - $@"; # Maybe we must die instead of just warn
         } else {
+            add_guarantors( $patron, $input );
             $borrowernumber = $patron->borrowernumber;
             $newdata{'borrowernumber'} = $borrowernumber;
         }
@@ -538,6 +534,7 @@ if ((!$nok) and $nodouble and ($op eq 'insert' or $op eq 'save')){
             $patron->set_password({ password => $password });
         }
 
+        add_guarantors( $patron, $input );
         if (C4::Context->preference('ExtendedPatronAttributes') and $input->param('setting_extended_patron_attributes')) {
             C4::Members::Attributes::SetBorrowerAttributes($borrowernumber, $extended_patron_attributes);
         }
@@ -636,11 +633,10 @@ foreach my $category_type (qw(C A S P I X)) {
         'categoryloop'   => \@categoryloop
       };
 }
-
-$template->param('typeloop' => \@typeloop,
-        no_categories => $no_categories);
-if($no_categories){ $no_add = 1; }
-
+$template->param(
+    typeloop      => \@typeloop,
+    no_categories => $no_categories,
+);
 
 my $cities = Koha::Cities->search( {}, { order_by => 'city_name' } );
 my $roadtypes = C4::Koha::GetAuthorisedValues( 'ROADTYPE' );
@@ -665,24 +661,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
@@ -757,7 +756,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
@@ -771,17 +770,12 @@ $template->param(
   check_member    => $check_member,#to know if the borrower already exist(=>1) or not (=>0) 
   "op$op"   => 1);
 
-$guarantorid = $borrower_data->{'guarantorid'} || $guarantorid;
-my $guarantor = $guarantorid ? Koha::Patrons->find( $guarantorid ) : undef;
 $template->param(
   patron => $patron ? $patron : \%newdata, # Used by address include templates now
   nodouble  => $nodouble,
   borrowernumber  => $borrowernumber, #register number
-  guarantor   => $guarantor,
-  guarantorid => $guarantorid,
   relshiploop => \@relshipdata,
   btitle=> $default_borrowertitle,
-  guarantorinfo   => $guarantorinfo,
   flagloop  => \@flagdata,
   category_type =>$category_type,
   modify          => $modify,
@@ -911,6 +905,28 @@ sub patron_attributes_form {
 
 }
 
+sub add_guarantors {
+    my ( $patron, $input ) = @_;
+
+    my @new_guarantor_id           = $input->multi_param('new_guarantor_id');
+    my @new_guarantor_relationship = $input->multi_param('new_guarantor_relationship');
+
+    for ( my $i = 0 ; $i < scalar @new_guarantor_id; $i++ ) {
+        my $guarantor_id = $new_guarantor_id[$i];
+        my $relationship = $new_guarantor_relationship[$i];
+
+        next unless $guarantor_id;
+
+        $patron->add_guarantor(
+            {
+                guarantee_id => $patron->id,
+                guarantor_id => $guarantor_id,
+                relationship => $relationship,
+            }
+        );
+    }
+}
+
 # Local Variables:
 # tab-width: 8
 # End:
index 33f833e..593b0d1 100755 (executable)
@@ -99,22 +99,20 @@ if ( $patron->is_debarred ) {
 $template->param( flagged => 1 ) if $patron->account_locked;
 
 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::Checkouts->count({ borrowernumber => \@relatives });
index b0aff0d..60d239a 100755 (executable)
@@ -33,6 +33,7 @@ use C4::Auth;
 use C4::Output;
 use Koha::Patrons;
 use Koha::Patron::Categories;
+use Koha::Patrons;
 
 # use Smart::Comments;
 
@@ -68,7 +69,6 @@ if ( $op eq 'multi' ) {
     );
     output_html_with_http_headers $input, $cookie, $template->output;
 }
-
 elsif ( $op eq 'update' ) {
     my $patron         = Koha::Patrons->find( $borrowernumber );
     output_and_exit_if_error( $input, $cookie, $template, { module => 'members', logged_in_user => $logged_in_user, current_patron => $patron } );
@@ -84,7 +84,8 @@ elsif ( $op eq 'update' ) {
     # But we should not hit that with a normal use of the interface
     die "You are doing something wrong updating this child" unless $adult_category;
 
-    $patron->guarantorid(undef);
+    $_->delete() for $patron->guarantor_relationships();
+
     $patron->categorycode($adult_category->categorycode);
     $patron->store;
 
@@ -92,12 +93,15 @@ elsif ( $op eq 'update' ) {
     # We could redirect with a friendly message
     if ( $patron_categories->count > 1 ) {
         $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"
+        );
     }
 }
 
index cdbb8f8..a63666b 100755 (executable)
@@ -96,6 +96,7 @@ C<juv2adult.pl> - Suggests that you read this help. :)
 C<juv2adult.pl> -b=<branchcode> -f=<categorycode> -t=<categorycode>  - Processes a single branch, and updates the patron categories from fromcat to tocat.
 
 C<juv2adult.pl> -f=<categorycode> -t=<categorycode> -v -n - Processes all branches, shows all messages, and reports the patrons who would be affected. Takes no action on the database.
+
 =cut
 
 # These variables are set by command line options.
@@ -128,7 +129,9 @@ if ( not $fromcat && $tocat ) {    #make sure we've specified the info we need.
 
 cronlogaction();
 
-my $dbh = C4::Context->dbh;
+my $dbh      = C4::Context->dbh;
+my $database = Koha::Database->new();
+my $schema   = $database->schema;
 
 #get today's date, format it and subtract upperagelimit
 my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
@@ -164,12 +167,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
+
+    my $success = 1;
     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 = ?
@@ -178,10 +181,35 @@ if ( not $noaction ) {
                 FROM categories
                 WHERE category_type = 'C'
                   AND categorycode = ?
-              )|;
+              )
+        |;
+
+        $schema->storage->txn_begin;
+
+        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 $success = 0;
+
+        $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 $success = 0;
+
+        if ( $success ) {
+            $schema->storage->txn_commit;
+        } else {
+            $schema->storage->txn_rollback;
+            die "can't execute";
+        }
 
         if ( $res eq '0E0' ) {
             print "No patrons updated\n";
@@ -192,10 +220,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 (
@@ -203,10 +228,34 @@ 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 $success = 0;
+
+        $query = qq|
+            UPDATE borrowers
+            SET categorycode = ?
+            $where
+        |;
+        $sth = $dbh->prepare($query);
         my $res = $sth->execute( $tocat, $itsyourbirthday, $fromcat )
-          or die "can't execute";
+          or $success = 0;
+        $dbh->commit;
+
+        if ( $success ) {
+            $dbh->commit;
+        } else {
+            $dbh->rollback;
+            die "can't execute";
+        }
 
         if ( $res eq '0E0' ) {
             print "No patrons updated\n";
@@ -268,6 +317,7 @@ else {
         my $sth = $dbh->prepare($query);
         $sth->execute( $itsyourbirthday, $fromcat )
           or die "Couldn't execute statement: " . $sth->errstr;
+        $dbh->commit;
 
         while ( my @res = $sth->fetchrow_array() ) {
             my $firstname = $res[0];
index 11a7ae2..d1e484a 100755 (executable)
@@ -312,7 +312,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, 'edit' ),
         csrf_token => Koha::Token->new->generate_csrf({
             session_id => scalar $cgi->cookie('CGISESSID'),
@@ -333,7 +332,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 };
index 613d5cb..360ed6e 100755 (executable)
@@ -39,6 +39,7 @@ use Koha::Holds;
 use Koha::Database;
 use Koha::ItemTypes;
 use Koha::Patron::Attribute::Types;
+use Koha::Patrons;
 use Koha::Patron::Messages;
 use Koha::Patron::Discharge;
 use Koha::Patrons;
@@ -329,14 +330,12 @@ my $patron_messages = Koha::Patron::Messages->search(
 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 );
 }
 
index 81b3f80..4330e16 100755 (executable)
@@ -95,12 +95,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',
@@ -125,7 +120,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' );
@@ -166,12 +161,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' );
@@ -195,7 +185,7 @@ subtest 'Accessor tests' => sub {
 
 # patron Set tests
 subtest 'Set tests' => sub {
-    plan tests => 65;
+    plan tests => 60;
 
     $patron->set(
         {
@@ -239,12 +229,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',
@@ -307,12 +292,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' );
index df2a407..45f412d 100644 (file)
@@ -26,6 +26,7 @@ use C4::Circulation qw( CanBookBeIssued );
 use Koha::Account;
 use Koha::Account::Lines;
 use Koha::Account::Offsets;
+use Koha::Patron::Relationship;
 
 my $schema = Koha::Database->new->schema;
 $schema->storage->txn_begin;
@@ -52,12 +53,23 @@ my $patron = $builder->build_object(
         }
     }
 );
-my $guarantee = $builder->build(
+
+my $guarantee = $builder->build_object(
     {
-        source => 'Borrower',
-        value  => {
-            guarantorid => $patron->borrowernumber,
-            categorycode => $patron_category->{categorycode},
+        class => 'Koha::Patrons',
+        value => {
+            patron_category => $patron_category->{categorycode},
+        }
+    }
+);
+
+my $r = $builder->build_object(
+    {
+        class => 'Koha::Patron::Relationships',
+        value => {
+            guarantor_id => $patron->id,
+            guarantee_id => $guarantee->id,
+            relationship => 'parent',
         }
     }
 );
@@ -73,7 +85,7 @@ $account->add_debit({ amount => 10.00, type => 'lost_item', interface => 'test'
 ( $issuingimpossible, $needsconfirmation ) = CanBookBeIssued( $patron, $item->{barcode} );
 is( $issuingimpossible->{DEBT_GUARANTEES} + 0, '10.00' + 0, "Patron cannot check out item due to debt for guarantee" );
 
-my $accountline = Koha::Account::Lines->search({ borrowernumber => $guarantee->{borrowernumber} })->next();
+my $accountline = Koha::Account::Lines->search({ borrowernumber => $guarantee->id })->next();
 is( $accountline->amountoutstanding, "10.000000", "Found 10.00 amount outstanding" );
 is( $accountline->accounttype, "LOST", "Account type is LOST" );
 
index af7f796..0130824 100755 (executable)
@@ -550,7 +550,7 @@ subtest 'SearchItems test' => sub {
 
 subtest 'Koha::Item(s) tests' => sub {
 
-    plan tests => 5;
+    plan tests => 7;
 
     $schema->storage->txn_begin();
 
@@ -589,6 +589,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;
 };
 
index 8f2c406..e511e19 100644 (file)
@@ -37,6 +37,7 @@ use Koha::Holds;
 use Koha::Old::Holds;
 use Koha::Patrons;
 use Koha::Patron::Categories;
+use Koha::Patron::Relationship;
 use Koha::Database;
 use Koha::DateUtils;
 use Koha::Virtualshelves;
@@ -86,21 +87,23 @@ subtest 'library' => sub {
 
 subtest 'guarantees' => sub {
     plan tests => 13;
-    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}, relationship => 'test' } )->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}, relationship => 'test' } )->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;
 
@@ -110,40 +113,107 @@ subtest 'guarantees' => sub {
 
     my $guarantor = $builder->build_object( { class => 'Koha::Patrons' } );
 
-    my $order_guarantee1 = $builder->build_object( { class => 'Koha::Patrons' ,  value => {
-            surname => 'Zebra',
-            guarantorid => $guarantor->borrowernumber
+    my $order_guarantee1 = $builder->build_object(
+        {
+            class => 'Koha::Patrons',
+            value => {
+                surname     => 'Zebra',
+            }
         }
-    })->borrowernumber;
+    )->borrowernumber;
+    $builder->build_object(
+        {
+            class => 'Koha::Patron::Relationships',
+            value => {
+                guarantor_id  => $guarantor->id,
+                guarantee_id => $order_guarantee1,
+                relationship => 'test',
+            }
+        }
+    );
 
-    my $order_guarantee2 = $builder->build_object( { class => 'Koha::Patrons' ,  value => {
-            surname => 'Yak',
-            guarantorid => $guarantor->borrowernumber
+    my $order_guarantee2 = $builder->build_object(
+        {
+            class => 'Koha::Patrons',
+            value => {
+                surname     => 'Yak',
+            }
         }
-    })->borrowernumber;
+    )->borrowernumber;
+    $builder->build_object(
+        {
+            class => 'Koha::Patron::Relationships',
+            value => {
+                guarantor_id  => $guarantor->id,
+                guarantee_id => $order_guarantee2,
+                relationship => 'test',
+            }
+        }
+    );
 
-    my $order_guarantee3 = $builder->build_object( { class => 'Koha::Patrons' ,  value => {
-            surname => 'Xerus',
-            firstname => 'Walrus',
-            guarantorid => $guarantor->borrowernumber
+    my $order_guarantee3 = $builder->build_object(
+        {
+            class => 'Koha::Patrons',
+            value => {
+                surname     => 'Xerus',
+                firstname   => 'Walrus',
+            }
         }
-    })->borrowernumber;
+    )->borrowernumber;
+    $builder->build_object(
+        {
+            class => 'Koha::Patron::Relationships',
+            value => {
+                guarantor_id  => $guarantor->id,
+                guarantee_id => $order_guarantee3,
+                relationship => 'test',
+            }
+        }
+    );
 
-    my $order_guarantee4 = $builder->build_object( { class => 'Koha::Patrons' ,  value => {
-            surname => 'Xerus',
-            firstname => 'Vulture',
-            guarantorid => $guarantor->borrowernumber
+    my $order_guarantee4 = $builder->build_object(
+        {
+            class => 'Koha::Patrons',
+            value => {
+                surname     => 'Xerus',
+                firstname   => 'Vulture',
+                guarantorid => $guarantor->borrowernumber
+            }
+        }
+    )->borrowernumber;
+    $builder->build_object(
+        {
+            class => 'Koha::Patron::Relationships',
+            value => {
+                guarantor_id  => $guarantor->id,
+                guarantee_id => $order_guarantee4,
+                relationship => 'test',
+            }
         }
-    })->borrowernumber;
+    );
 
-    my $order_guarantee5 = $builder->build_object( { class => 'Koha::Patrons' ,  value => {
-            surname => 'Xerus',
-            firstname => 'Unicorn',
-            guarantorid => $guarantor->borrowernumber
+    my $order_guarantee5 = $builder->build_object(
+        {
+            class => 'Koha::Patrons',
+            value => {
+                surname     => 'Xerus',
+                firstname   => 'Unicorn',
+                guarantorid => $guarantor->borrowernumber
+            }
         }
-    })->borrowernumber;
+    )->borrowernumber;
+    my $r = $builder->build_object(
+        {
+            class => 'Koha::Patron::Relationships',
+            value => {
+                guarantor_id  => $guarantor->id,
+                guarantee_id => $order_guarantee5,
+                relationship => 'test',
+            }
+        }
+    );
 
-    $guarantees = $guarantor->guarantees();
+    $guarantees = $guarantor->guarantee_relationships->guarantees;
 
     is( $guarantees->next()->borrowernumber, $order_guarantee5, "Return first guarantor alphabetically" );
     is( $guarantees->next()->borrowernumber, $order_guarantee4, "Return second guarantor alphabetically" );
@@ -163,15 +233,18 @@ 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}, relationship => 'test' } )->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}, relationship => 'test' } )->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}, relationship => 'test' } )->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' );
index f9595be..91f4b3d 100755 (executable)
@@ -27,6 +27,7 @@ use Koha::Database;
 use Koha::Holds;
 use Koha::List::Patron;
 use Koha::Patrons;
+use Koha::Patron::Relationship;
 
 use t::lib::Mocks;
 use t::lib::TestBuilder;
@@ -40,9 +41,6 @@ $schema->storage->txn_begin;
 my $builder = t::lib::TestBuilder->new;
 my $dbh = C4::Context->dbh;
 
-# Remove invalid guarantorid's as long as we have no FK
-$dbh->do("UPDATE borrowers b1 LEFT JOIN borrowers b2 ON b2.borrowernumber=b1.guarantorid SET b1.guarantorid=NULL where b1.guarantorid IS NOT NULL AND b2.borrowernumber IS NULL");
-
 my $library1 = $builder->build({
     source => 'Branch',
 });
@@ -215,7 +213,6 @@ my $borrower1 = $builder->build({
             categorycode=>'STAFFER',
             branchcode => $library3->{branchcode},
             dateexpiry => '2015-01-01',
-            guarantorid=> undef,
         },
 });
 my $bor1inlist = $borrower1->{borrowernumber};
@@ -225,7 +222,6 @@ my $borrower2 = $builder->build({
             categorycode=>'STAFFER',
             branchcode => $library3->{branchcode},
             dateexpiry => '2015-01-01',
-            guarantorid=> undef,
         },
 });
 
@@ -235,7 +231,6 @@ my $guarantee = $builder->build({
             categorycode=>'KIDclamp',
             branchcode => $library3->{branchcode},
             dateexpiry => '2015-01-01',
-            guarantorid=> undef, # will be filled later
         },
 });
 
@@ -270,7 +265,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');
 
 Koha::Patrons->find($bor1inlist)->set({ categorycode => 'CIVILIAN' })->store;
-Koha::Patrons->find($guarantee->{borrowernumber})->set({ guarantorid => $bor1inlist })->store;
+my $relationship = Koha::Patron::Relationship->new( { guarantor_id => $bor1inlist, guarantee_id => $guarantee->{borrowernumber}, relationship => 'test' } )->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');
@@ -280,7 +275,7 @@ $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');
-Koha::Patrons->find($guarantee->{borrowernumber})->set({ guarantorid => undef })->store;
+$relationship->delete();
 
 $builder->build({
         source => 'Issue',
@@ -308,9 +303,9 @@ is( scalar(@$patstodel),2,'Borrowers without issues deleted by last issue date')
 
 # Test GetBorrowersToExpunge and TrackLastPatronActivity
 $dbh->do(q|UPDATE borrowers SET lastseen=NULL|);
-$builder->build({ source => 'Borrower', value => { lastseen => '2016-01-01 01:01:01', categorycode => 'CIVILIAN', guarantorid => undef } } );
-$builder->build({ source => 'Borrower', value => { lastseen => '2016-02-02 02:02:02', categorycode => 'CIVILIAN', guarantorid => undef } } );
-$builder->build({ source => 'Borrower', value => { lastseen => '2016-03-03 03:03:03', categorycode => 'CIVILIAN', guarantorid => undef } } );
+$builder->build({ source => 'Borrower', value => { lastseen => '2016-01-01 01:01:01', categorycode => 'CIVILIAN' } } );
+$builder->build({ source => 'Borrower', value => { lastseen => '2016-02-02 02:02:02', categorycode => 'CIVILIAN' } } );
+$builder->build({ source => 'Borrower', value => { lastseen => '2016-03-03 03:03:03', categorycode => 'CIVILIAN' } } );
 $patstodel = GetBorrowersToExpunge( { last_seen => '1999-12-12' });
 is( scalar @$patstodel, 0, 'TrackLastPatronActivity - 0 patrons must be deleted' );
 $patstodel = GetBorrowersToExpunge( { last_seen => '2016-02-15' });
diff --git a/t/db_dependent/Patron/Relationships.t b/t/db_dependent/Patron/Relationships.t
new file mode 100755 (executable)
index 0000000..5bc5b23
--- /dev/null
@@ -0,0 +1,167 @@
+#!/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 <http://www.gnu.org/licenses>.
+
+use Modern::Perl;
+
+use Test::More tests => 59;
+
+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();
+
+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, 2, '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' );
+
+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;
index ec784be..4da2cd0 100755 (executable)
@@ -62,6 +62,7 @@ my $extended = C4::Context->preference('ExtendedPatronAttributes');
 
 my @columnkeys = map { $_ ne 'borrowernumber' ? $_ : () } Koha::Patrons->columns();
 push( @columnkeys, 'patron_attributes' ) if $extended;
+push( @columnkeys, qw( relationship guarantor_id  guarantor_firstname guarantor_surname ) );
 
 my $input = CGI->new();