Bug 9302: (QA follow-up) Merge should be a transaction
[koha.git] / Koha / Patrons.pm
blob0ef5f4f0b940f4b9bc18c4507be1f60ca9f3752b
1 package Koha::Patrons;
3 # Copyright 2014 ByWater Solutions
4 # Copyright 2016 Koha Development Team
6 # This file is part of Koha.
8 # Koha is free software; you can redistribute it and/or modify it under the
9 # terms of the GNU General Public License as published by the Free Software
10 # Foundation; either version 3 of the License, or (at your option) any later
11 # version.
13 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License along
18 # with Koha; if not, write to the Free Software Foundation, Inc.,
19 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 use Modern::Perl;
23 use Carp;
25 use Koha::Database;
26 use Koha::DateUtils;
28 use Koha::ArticleRequests;
29 use Koha::ArticleRequest::Status;
30 use Koha::Patron;
32 use base qw(Koha::Objects);
34 our $RESULTSET_PATRON_ID_MAPPING = {
35 Accountline => 'borrowernumber',
36 ArticleRequest => 'borrowernumber',
37 BorrowerAttribute => 'borrowernumber',
38 BorrowerDebarment => 'borrowernumber',
39 BorrowerFile => 'borrowernumber',
40 BorrowerModification => 'borrowernumber',
41 ClubEnrollment => 'borrowernumber',
42 Issue => 'borrowernumber',
43 ItemsLastBorrower => 'borrowernumber',
44 Linktracker => 'borrowernumber',
45 Message => 'borrowernumber',
46 MessageQueue => 'borrowernumber',
47 OldIssue => 'borrowernumber',
48 OldReserve => 'borrowernumber',
49 Rating => 'borrowernumber',
50 Reserve => 'borrowernumber',
51 Review => 'borrowernumber',
52 Statistic => 'borrowernumber',
53 SearchHistory => 'userid',
54 Suggestion => 'suggestedby',
55 TagAll => 'borrowernumber',
56 Virtualshelfcontent => 'borrowernumber',
57 Virtualshelfshare => 'borrowernumber',
58 Virtualshelve => 'owner',
61 =head1 NAME
63 Koha::Patron - Koha Patron Object class
65 =head1 API
67 =head2 Class Methods
69 =cut
71 =head3 search_limited
73 my $patrons = Koha::Patrons->search_limit( $params, $attributes );
75 Returns all the patrons the logged in user is allowed to see
77 =cut
79 sub search_limited {
80 my ( $self, $params, $attributes ) = @_;
82 my $userenv = C4::Context->userenv;
83 my @restricted_branchcodes;
84 if ( $userenv and $userenv->{number} ) {
85 my $logged_in_user = Koha::Patrons->find( $userenv->{number} );
86 @restricted_branchcodes = $logged_in_user->libraries_where_can_see_patrons;
88 $params->{'me.branchcode'} = { -in => \@restricted_branchcodes } if @restricted_branchcodes;
89 return $self->search( $params, $attributes );
92 =head3 search_housebound_choosers
94 Returns all Patrons which are Housebound choosers.
96 =cut
98 sub search_housebound_choosers {
99 my ( $self ) = @_;
100 my $cho = $self->_resultset
101 ->search_related('housebound_role', {
102 housebound_chooser => 1,
103 })->search_related('borrowernumber');
104 return Koha::Patrons->_new_from_dbic($cho);
107 =head3 search_housebound_deliverers
109 Returns all Patrons which are Housebound deliverers.
111 =cut
113 sub search_housebound_deliverers {
114 my ( $self ) = @_;
115 my $del = $self->_resultset
116 ->search_related('housebound_role', {
117 housebound_deliverer => 1,
118 })->search_related('borrowernumber');
119 return Koha::Patrons->_new_from_dbic($del);
122 =head3 search_upcoming_membership_expires
124 my $patrons = Koha::Patrons->search_upcoming_membership_expires();
126 The 'before' and 'after' represent the number of days before/after the date
127 that is set by the preference MembershipExpiryDaysNotice.
128 If the pref is 14, before 2 and after 3 then you will get all expires
129 from 12 to 17 days.
131 =cut
133 sub search_upcoming_membership_expires {
134 my ( $self, $params ) = @_;
135 my $before = $params->{before} || 0;
136 my $after = $params->{after} || 0;
137 delete $params->{before};
138 delete $params->{after};
140 my $days = C4::Context->preference("MembershipExpiryDaysNotice") || 0;
141 my $date_before = dt_from_string->add( days => $days - $before );
142 my $date_after = dt_from_string->add( days => $days + $after );
143 my $dtf = Koha::Database->new->schema->storage->datetime_parser;
145 $params->{dateexpiry} = {
146 ">=" => $dtf->format_date( $date_before ),
147 "<=" => $dtf->format_date( $date_after ),
149 return $self->SUPER::search(
150 $params, { join => ['branchcode', 'categorycode'] }
154 =head3 guarantor
156 Returns a Koha::Patron object for this borrower's guarantor
158 =cut
160 sub guarantor {
161 my ( $self ) = @_;
163 return Koha::Patrons->find( $self->guarantorid() );
166 =head3 search_patrons_to_anonymise
168 my $patrons = Koha::Patrons->search_patrons_to_anonymise( { before => $older_than_date, [ library => $library ] } );
170 This method returns all patrons who has an issue history older than a given date.
172 =cut
174 sub search_patrons_to_anonymise {
175 my ( $class, $params ) = @_;
176 my $older_than_date = $params->{before};
177 my $library = $params->{library};
178 $older_than_date = $older_than_date ? dt_from_string($older_than_date) : dt_from_string;
179 $library ||=
180 ( C4::Context->preference('IndependentBranches') && C4::Context->userenv && !C4::Context->IsSuperLibrarian() && C4::Context->userenv->{branch} )
181 ? C4::Context->userenv->{branch}
182 : undef;
184 my $dtf = Koha::Database->new->schema->storage->datetime_parser;
185 my $rs = $class->_resultset->search(
186 { returndate => { '<' => $dtf->format_datetime($older_than_date), },
187 'old_issues.borrowernumber' => { 'not' => undef },
188 privacy => { '<>' => 0 }, # Keep forever
189 ( $library ? ( 'old_issues.branchcode' => $library ) : () ),
191 { join => ["old_issues"],
192 distinct => 1,
195 return Koha::Patrons->_new_from_dbic($rs);
198 =head3 anonymise_issue_history
200 Koha::Patrons->search->anonymise_issue_history( { [ before => $older_than_date ] } );
202 Anonymise issue history (old_issues) for all patrons older than the given date (optional).
203 To make sure all the conditions are met, the caller has the responsibility to
204 call search_patrons_to_anonymise to filter the Koha::Patrons set
206 =cut
208 sub anonymise_issue_history {
209 my ( $self, $params ) = @_;
211 my $older_than_date = $params->{before};
213 $older_than_date = dt_from_string $older_than_date if $older_than_date;
215 # The default of 0 does not work due to foreign key constraints
216 # The anonymisation should not fail quietly if AnonymousPatron is not a valid entry
217 # Set it to undef (NULL)
218 my $dtf = Koha::Database->new->schema->storage->datetime_parser;
219 my $nb_rows = 0;
220 while ( my $patron = $self->next ) {
221 my $old_issues_to_anonymise = $patron->old_checkouts->search(
224 $older_than_date
225 ? ( returndate =>
226 { '<' => $dtf->format_datetime($older_than_date) } )
227 : ()
231 my $anonymous_patron = C4::Context->preference('AnonymousPatron') || undef;
232 $nb_rows += $old_issues_to_anonymise->update( { 'old_issues.borrowernumber' => $anonymous_patron } );
234 return $nb_rows;
237 =head3 merge
239 Koha::Patrons->search->merge( { keeper => $borrowernumber, patrons => \@borrowernumbers } );
241 This subroutine merges a list of patrons into another patron record. This is accomplished by finding
242 all related patron ids for the patrons to be merged in other tables and changing the ids to be that
243 of the keeper patron.
245 =cut
247 sub merge {
248 my ( $self, $params ) = @_;
250 my $keeper = $params->{keeper};
251 my @borrowernumbers = @{ $params->{patrons} };
253 my $patron_to_keep = Koha::Patrons->find( $keeper );
254 return unless $patron_to_keep;
256 # Ensure the keeper isn't in the list of patrons to merge
257 @borrowernumbers = grep { $_ ne $keeper } @borrowernumbers;
259 my $schema = Koha::Database->new()->schema();
261 my $results;
263 $self->_resultset->result_source->schema->txn_do( sub {
264 foreach my $borrowernumber (@borrowernumbers) {
265 my $patron = Koha::Patrons->find( $borrowernumber );
267 next unless $patron;
269 # Unbless for safety, the patron will end up being deleted
270 $results->{merged}->{$borrowernumber}->{patron} = $patron->unblessed;
272 while (my ($r, $field) = each(%$RESULTSET_PATRON_ID_MAPPING)) {
273 my $rs = $schema->resultset($r)->search({ $field => $borrowernumber} );
274 $results->{merged}->{ $borrowernumber }->{updated}->{$r} = $rs->count();
275 $rs->update( { $field => $keeper });
278 $patron->move_to_deleted();
279 $patron->delete();
283 $results->{keeper} = $patron_to_keep;
285 return $results;
288 =head3 type
290 =cut
292 sub _type {
293 return 'Borrower';
296 sub object_class {
297 return 'Koha::Patron';
300 =head1 AUTHOR
302 Kyle M Hall <kyle@bywatersolutions.com>
304 =cut