Bug 5670: Does not return 0 in Koha::Patron->housebound_* methods
[koha.git] / Koha / Patron.pm
1 package Koha::Patron;
2
3 # Copyright ByWater Solutions 2014
4 # Copyright PTFS Europe 2016
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 C4::Context;
26 use C4::Log;
27 use Koha::Database;
28 use Koha::DateUtils;
29 use Koha::Holds;
30 use Koha::Issues;
31 use Koha::OldIssues;
32 use Koha::Patron::Categories;
33 use Koha::Patron::HouseboundProfile;
34 use Koha::Patron::HouseboundRole;
35 use Koha::Patron::Images;
36 use Koha::Patrons;
37 use Koha::Virtualshelves;
38
39 use base qw(Koha::Object);
40
41 =head1 NAME
42
43 Koha::Patron - Koha Patron Object class
44
45 =head1 API
46
47 =head2 Class Methods
48
49 =cut
50
51 =head3 delete
52
53 $patron->delete
54
55 Delete patron's holds, lists and finally the patron.
56
57 Lists owned by the borrower are deleted, but entries from the borrower to
58 other lists are kept.
59
60 =cut
61
62 sub delete {
63     my ($self) = @_;
64
65     my $deleted;
66     $self->_result->result_source->schema->txn_do(
67         sub {
68             # Delete Patron's holds
69             # FIXME Should be $patron->get_holds
70             $_->delete for Koha::Holds->search( { borrowernumber => $self->borrowernumber } );
71
72             # Delete all lists and all shares of this borrower
73             # Consistent with the approach Koha uses on deleting individual lists
74             # Note that entries in virtualshelfcontents added by this borrower to
75             # lists of others will be handled by a table constraint: the borrower
76             # is set to NULL in those entries.
77             # NOTE:
78             # We could handle the above deletes via a constraint too.
79             # But a new BZ report 11889 has been opened to discuss another approach.
80             # Instead of deleting we could also disown lists (based on a pref).
81             # In that way we could save shared and public lists.
82             # The current table constraints support that idea now.
83             # This pref should then govern the results of other routines/methods such as
84             # Koha::Virtualshelf->new->delete too.
85             # FIXME Could be $patron->get_lists
86             $_->delete for Koha::Virtualshelves->search( { owner => $self->borrowernumber } );
87
88             $deleted = $self->SUPER::delete;
89
90             logaction( "MEMBERS", "DELETE", $self->borrowernumber, "" ) if C4::Context->preference("BorrowersLog");
91         }
92     );
93     return $deleted;
94 }
95
96 =head3 guarantor
97
98 Returns a Koha::Patron object for this patron's guarantor
99
100 =cut
101
102 sub guarantor {
103     my ( $self ) = @_;
104
105     return unless $self->guarantorid();
106
107     return Koha::Patrons->find( $self->guarantorid() );
108 }
109
110 sub image {
111     my ( $self ) = @_;
112
113     return Koha::Patron::Images->find( $self->borrowernumber )
114 }
115
116 =head3 guarantees
117
118 Returns the guarantees (list of Koha::Patron) of this patron
119
120 =cut
121
122 sub guarantees {
123     my ( $self ) = @_;
124
125     return Koha::Patrons->search( { guarantorid => $self->borrowernumber } );
126 }
127
128 =head3 housebound_profile
129
130 Returns the HouseboundProfile associated with this patron.
131
132 =cut
133
134 sub housebound_profile {
135     my ( $self ) = @_;
136     my $profile = $self->_result->housebound_profile;
137     return Koha::Patron::HouseboundProfile->_new_from_dbic($profile)
138         if ( $profile );
139     return;
140 }
141
142 =head3 housebound_role
143
144 Returns the HouseboundRole associated with this patron.
145
146 =cut
147
148 sub housebound_role {
149     my ( $self ) = @_;
150
151     my $role = $self->_result->housebound_role;
152     return Koha::Patron::HouseboundRole->_new_from_dbic($role) if ( $role );
153     return;
154 }
155
156 =head3 siblings
157
158 Returns the siblings of this patron.
159
160 =cut
161
162 sub siblings {
163     my ( $self ) = @_;
164
165     my $guarantor = $self->guarantor;
166
167     return unless $guarantor;
168
169     return Koha::Patrons->search(
170         {
171             guarantorid => {
172                 '!=' => undef,
173                 '=' => $guarantor->id,
174             },
175             borrowernumber => {
176                 '!=' => $self->borrowernumber,
177             }
178         }
179     );
180 }
181
182 =head3 wants_check_for_previous_checkout
183
184     $wants_check = $patron->wants_check_for_previous_checkout;
185
186 Return 1 if Koha needs to perform PrevIssue checking, else 0.
187
188 =cut
189
190 sub wants_check_for_previous_checkout {
191     my ( $self ) = @_;
192     my $syspref = C4::Context->preference("checkPrevCheckout");
193
194     # Simple cases
195     ## Hard syspref trumps all
196     return 1 if ($syspref eq 'hardyes');
197     return 0 if ($syspref eq 'hardno');
198     ## Now, patron pref trumps all
199     return 1 if ($self->checkprevcheckout eq 'yes');
200     return 0 if ($self->checkprevcheckout eq 'no');
201
202     # More complex: patron inherits -> determine category preference
203     my $checkPrevCheckoutByCat = Koha::Patron::Categories
204         ->find($self->categorycode)->checkprevcheckout;
205     return 1 if ($checkPrevCheckoutByCat eq 'yes');
206     return 0 if ($checkPrevCheckoutByCat eq 'no');
207
208     # Finally: category preference is inherit, default to 0
209     if ($syspref eq 'softyes') {
210         return 1;
211     } else {
212         return 0;
213     }
214 }
215
216 =head3 do_check_for_previous_checkout
217
218     $do_check = $patron->do_check_for_previous_checkout($item);
219
220 Return 1 if the bib associated with $ITEM has previously been checked out to
221 $PATRON, 0 otherwise.
222
223 =cut
224
225 sub do_check_for_previous_checkout {
226     my ( $self, $item ) = @_;
227
228     # Find all items for bib and extract item numbers.
229     my @items = Koha::Items->search({biblionumber => $item->{biblionumber}});
230     my @item_nos;
231     foreach my $item (@items) {
232         push @item_nos, $item->itemnumber;
233     }
234
235     # Create (old)issues search criteria
236     my $criteria = {
237         borrowernumber => $self->borrowernumber,
238         itemnumber => \@item_nos,
239     };
240
241     # Check current issues table
242     my $issues = Koha::Issues->search($criteria);
243     return 1 if $issues->count; # 0 || N
244
245     # Check old issues table
246     my $old_issues = Koha::OldIssues->search($criteria);
247     return $old_issues->count;  # 0 || N
248 }
249
250 =head2 is_debarred
251
252 my $debarment_expiration = $patron->is_debarred;
253
254 Returns the date a patron debarment will expire, or undef if the patron is not
255 debarred
256
257 =cut
258
259 sub is_debarred {
260     my ($self) = @_;
261
262     return unless $self->debarred;
263     return $self->debarred
264       if $self->debarred =~ '^9999'
265       or dt_from_string( $self->debarred ) > dt_from_string;
266     return;
267 }
268
269 =head2 update_password
270
271 my $updated = $patron->update_password( $userid, $password );
272
273 Update the userid and the password of a patron.
274 If the userid already exists, returns and let DBIx::Class warns
275 This will add an entry to action_logs if BorrowersLog is set.
276
277 =cut
278
279 sub update_password {
280     my ( $self, $userid, $password ) = @_;
281     eval { $self->userid($userid)->store; };
282     return if $@; # Make sure the userid is not already in used by another patron
283     $self->password($password)->store;
284     logaction( "MEMBERS", "CHANGE PASS", $self->borrowernumber, "" ) if C4::Context->preference("BorrowersLog");
285     return 1;
286 }
287
288 =head3 renew_account
289
290 my $new_expiry_date = $patron->renew_account
291
292 Extending the subscription to the expiry date.
293
294 =cut
295
296 sub renew_account {
297     my ($self) = @_;
298
299     my $date =
300       C4::Context->preference('BorrowerRenewalPeriodBase') eq 'dateexpiry'
301       ? dt_from_string( $self->dateexpiry )
302       : dt_from_string;
303     my $patron_category = Koha::Patron::Categories->find( $self->categorycode );    # FIXME Should be $self->category
304     my $expiry_date     = $patron_category->get_expiry_date($date);
305
306     $self->dateexpiry($expiry_date)->store;
307
308     C4::Members::AddEnrolmentFeeIfNeeded( $self->categorycode, $self->borrowernumber );
309
310     logaction( "MEMBERS", "RENEW", $self->borrowernumber, "Membership renewed" ) if C4::Context->preference("BorrowersLog");
311     return dt_from_string( $expiry_date )->truncate( to => 'day' );
312 }
313
314 =head2 has_overdues
315
316 my $has_overdues = $patron->has_overdues;
317
318 Returns the number of patron's overdues
319
320 =cut
321
322 sub has_overdues {
323     my ($self) = @_;
324     my $dtf = Koha::Database->new->schema->storage->datetime_parser;
325     return $self->_result->issues->search({ date_due => { '<' => $dtf->format_datetime( dt_from_string() ) } })->count;
326 }
327
328 =head2 track_login
329
330     $patron->track_login;
331     $patron->track_login({ force => 1 });
332
333     Tracks a (successful) login attempt.
334     The preference TrackLastPatronActivity must be enabled. Or you
335     should pass the force parameter.
336
337 =cut
338
339 sub track_login {
340     my ( $self, $params ) = @_;
341     return if
342         !$params->{force} &&
343         !C4::Context->preference('TrackLastPatronActivity');
344     $self->lastseen( dt_from_string() )->store;
345 }
346
347 =head2 move_to_deleted
348
349 my $is_moved = $patron->move_to_deleted;
350
351 Move a patron to the deletedborrowers table.
352 This can be done before deleting a patron, to make sure the data are not completely deleted.
353
354 =cut
355
356 sub move_to_deleted {
357     my ($self) = @_;
358     my $patron_infos = $self->unblessed;
359     return Koha::Database->new->schema->resultset('Deletedborrower')->create($patron_infos);
360 }
361
362 =head3 type
363
364 =cut
365
366 sub _type {
367     return 'Borrower';
368 }
369
370 =head1 AUTHOR
371
372 Kyle M Hall <kyle@bywatersolutions.com>
373 Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
374
375 =cut
376
377 1;