Bug 21337: (QA follow-up) Rollback for partial delete
[koha.git] / Koha / Patrons.pm
1 package Koha::Patrons;
2
3 # Copyright 2014 ByWater Solutions
4 # Copyright 2016 Koha Development Team
5 #
6 # This file is part of Koha.
7 #
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.
12 #
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.
16 #
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.
20
21 use Modern::Perl;
22
23 use Carp;
24
25 use Koha::Database;
26 use Koha::DateUtils;
27
28 use Koha::ArticleRequests;
29 use Koha::ArticleRequest::Status;
30 use Koha::Patron;
31 use Koha::Exceptions::Patron;
32
33 use base qw(Koha::Objects);
34
35 =head1 NAME
36
37 Koha::Patron - Koha Patron Object class
38
39 =head1 API
40
41 =head2 Class Methods
42
43 =cut
44
45 =head3 search_limited
46
47 my $patrons = Koha::Patrons->search_limit( $params, $attributes );
48
49 Returns all the patrons the logged in user is allowed to see
50
51 =cut
52
53 sub search_limited {
54     my ( $self, $params, $attributes ) = @_;
55
56     my $userenv = C4::Context->userenv;
57     my @restricted_branchcodes;
58     if ( $userenv and $userenv->{number} ) {
59         my $logged_in_user = Koha::Patrons->find( $userenv->{number} );
60         @restricted_branchcodes = $logged_in_user->libraries_where_can_see_patrons;
61     }
62     $params->{'me.branchcode'} = { -in => \@restricted_branchcodes } if @restricted_branchcodes;
63     return $self->search( $params, $attributes );
64 }
65
66 =head3 search_housebound_choosers
67
68 Returns all Patrons which are Housebound choosers.
69
70 =cut
71
72 sub search_housebound_choosers {
73     my ( $self ) = @_;
74     my $cho = $self->_resultset
75         ->search_related('housebound_role', {
76             housebound_chooser => 1,
77         })->search_related('borrowernumber');
78     return Koha::Patrons->_new_from_dbic($cho);
79 }
80
81 =head3 search_housebound_deliverers
82
83 Returns all Patrons which are Housebound deliverers.
84
85 =cut
86
87 sub search_housebound_deliverers {
88     my ( $self ) = @_;
89     my $del = $self->_resultset
90         ->search_related('housebound_role', {
91             housebound_deliverer => 1,
92         })->search_related('borrowernumber');
93     return Koha::Patrons->_new_from_dbic($del);
94 }
95
96 =head3 search_upcoming_membership_expires
97
98 my $patrons = Koha::Patrons->search_upcoming_membership_expires();
99
100 The 'before' and 'after' represent the number of days before/after the date
101 that is set by the preference MembershipExpiryDaysNotice.
102 If the pref is 14, before 2 and after 3 then you will get all expires
103 from 12 to 17 days.
104
105 =cut
106
107 sub search_upcoming_membership_expires {
108     my ( $self, $params ) = @_;
109     my $before = $params->{before} || 0;
110     my $after  = $params->{after} || 0;
111     delete $params->{before};
112     delete $params->{after};
113
114     my $days = C4::Context->preference("MembershipExpiryDaysNotice") || 0;
115     my $date_before = dt_from_string->add( days => $days - $before );
116     my $date_after = dt_from_string->add( days => $days + $after );
117     my $dtf = Koha::Database->new->schema->storage->datetime_parser;
118
119     $params->{dateexpiry} = {
120         ">=" => $dtf->format_date( $date_before ),
121         "<=" => $dtf->format_date( $date_after ),
122     };
123     return $self->SUPER::search(
124         $params, { join => ['branchcode', 'categorycode'] }
125     );
126 }
127
128 =head3 guarantor
129
130 Returns a Koha::Patron object for this borrower's guarantor
131
132 =cut
133
134 sub guarantor {
135     my ( $self ) = @_;
136
137     return Koha::Patrons->find( $self->guarantorid() );
138 }
139
140 =head3 search_patrons_to_anonymise
141
142     my $patrons = Koha::Patrons->search_patrons_to_anonymise( { before => $older_than_date, [ library => $library ] } );
143
144 This method returns all patrons who has an issue history older than a given date.
145
146 =cut
147
148 sub search_patrons_to_anonymise {
149     my ( $class, $params ) = @_;
150     my $older_than_date = $params->{before};
151     my $library         = $params->{library};
152     $older_than_date = $older_than_date ? dt_from_string($older_than_date) : dt_from_string;
153     $library ||=
154       ( C4::Context->preference('IndependentBranches') && C4::Context->userenv && !C4::Context->IsSuperLibrarian() && C4::Context->userenv->{branch} )
155       ? C4::Context->userenv->{branch}
156       : undef;
157
158     my $dtf = Koha::Database->new->schema->storage->datetime_parser;
159     my $rs = $class->_resultset->search(
160         {   returndate                  => { '<'   =>  $dtf->format_datetime($older_than_date), },
161             'old_issues.borrowernumber' => { 'not' => undef },
162             privacy                     => { '<>'  => 0 },                  # Keep forever
163             ( $library ? ( 'old_issues.branchcode' => $library ) : () ),
164         },
165         {   join     => ["old_issues"],
166             distinct => 1,
167         }
168     );
169     return Koha::Patrons->_new_from_dbic($rs);
170 }
171
172 =head3 anonymise_issue_history
173
174     Koha::Patrons->search->anonymise_issue_history( { [ before => $older_than_date ] } );
175
176 Anonymise issue history (old_issues) for all patrons older than the given date (optional).
177 To make sure all the conditions are met, the caller has the responsibility to
178 call search_patrons_to_anonymise to filter the Koha::Patrons set
179
180 =cut
181
182 sub anonymise_issue_history {
183     my ( $self, $params ) = @_;
184
185     my $older_than_date = $params->{before};
186
187     $older_than_date = dt_from_string $older_than_date if $older_than_date;
188
189     # The default of 0 does not work due to foreign key constraints
190     # The anonymisation should not fail quietly if AnonymousPatron is not a valid entry
191     # Set it to undef (NULL)
192     my $dtf = Koha::Database->new->schema->storage->datetime_parser;
193     my $nb_rows = 0;
194     while ( my $patron = $self->next ) {
195         my $old_issues_to_anonymise = $patron->old_checkouts->search(
196         {
197             (
198                 $older_than_date
199                 ? ( returndate =>
200                       { '<' => $dtf->format_datetime($older_than_date) } )
201                 : ()
202             )
203         }
204         );
205         my $anonymous_patron = C4::Context->preference('AnonymousPatron') || undef;
206         $nb_rows += $old_issues_to_anonymise->update( { 'old_issues.borrowernumber' => $anonymous_patron } );
207     }
208     return $nb_rows;
209 }
210
211 =head3 delete
212
213     Koha::Patrons->search({ some filters here })->delete({ move => 1 });
214
215     Delete passed set of patron objects.
216     Wrapper for Koha::Patron->delete. (We do not want to bypass Koha::Patron
217     and let DBIx do the job without further housekeeping.)
218     Includes a move to deletedborrowers if move flag set.
219
220     Just like DBIx, the delete will only succeed when all entries could be
221     deleted. Returns true or throws an exception.
222
223 =cut
224
225 sub delete {
226     my ( $self, $params ) = @_;
227     $self->_resultset->result_source->schema->txn_do( sub {
228         my ( $set, $params ) = @_;
229         while( my $patron = $set->next ) {
230             $patron->move_to_deleted if $params->{move};
231             $patron->delete == 1 || Koha::Exceptions::Patron::Delete->throw;
232         }
233     }, $self, $params );
234     return 1;
235 }
236
237 =head3 _type
238
239 =cut
240
241 sub _type {
242     return 'Borrower';
243 }
244
245 sub object_class {
246     return 'Koha::Patron';
247 }
248
249 =head1 AUTHOR
250
251 Kyle M Hall <kyle@bywatersolutions.com>
252
253 =cut
254
255 1;