Merge branch 'bug_8557' into 3.12-master
[koha.git] / C4 / Members / AttributeTypes.pm
1 package C4::Members::AttributeTypes;
2
3 # Copyright (C) 2008 LibLime
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 use strict;
21 #use warnings; FIXME - Bug 2505
22 use C4::Context;
23
24 use vars qw($VERSION);
25
26 BEGIN {
27     # set the version for version checking
28     $VERSION = 3.07.00.049;
29 }
30
31 =head1 NAME
32
33 C4::Members::AttributeTypes - mananage extended patron attribute types
34
35 =head1 SYNOPSIS
36
37   my @attribute_types = C4::Members::AttributeTypes::GetAttributeTypes();
38
39   my $attr_type = C4::Members::AttributeTypes->new($code, $description);
40   $attr_type->code($code);
41   $attr_type->description($description);
42   $attr_type->repeatable($repeatable);
43   $attr_type->unique_id($unique_id);
44   $attr_type->opac_display($opac_display);
45   $attr_type->password_allowed($password_allowed);
46   $attr_type->staff_searchable($staff_searchable);
47   $attr_type->authorised_value_category($authorised_value_category);
48   $attr_type->store();
49   $attr_type->delete();
50
51   my $attr_type = C4::Members::AttributeTypes->fetch($code);
52   $attr_type = C4::Members::AttributeTypes->delete($code);
53
54 =head1 FUNCTIONS
55
56 =head2 GetAttributeTypes
57
58   my @attribute_types = C4::Members::AttributeTypes::GetAttributeTypes($all_fields);
59
60 Returns an array of hashrefs of each attribute type defined
61 in the database.  The array is sorted by code.  Each hashref contains
62 at least the following fields:
63
64  - code
65  - description
66
67 If $all_fields is true, then each hashref also contains the other fields from borrower_attribute_types.
68
69 =cut
70
71 sub GetAttributeTypes {
72     my $all    = @_   ? shift : 0;
73     my $no_branch_limit = @_ ? shift : 0;
74     my $branch_limit = $no_branch_limit
75         ? 0
76         : C4::Context->userenv ? C4::Context->userenv->{"branch"} : 0;
77     my $select = $all ? '*'   : 'DISTINCT(code), description, class';
78
79     my $dbh = C4::Context->dbh;
80     my $query = "SELECT $select FROM borrower_attribute_types";
81     $query .= qq{
82         LEFT JOIN borrower_attribute_types_branches ON bat_code = code
83         WHERE b_branchcode = ? OR b_branchcode IS NULL
84     } if $branch_limit;
85     $query .= " ORDER BY code";
86     my $sth    = $dbh->prepare($query);
87     $sth->execute( $branch_limit ? $branch_limit : () );
88     my $results = $sth->fetchall_arrayref({});
89     $sth->finish;
90     return @$results;
91 }
92
93 sub GetAttributeTypes_hashref {
94     my %hash = map {$_->{code} => $_} GetAttributeTypes(@_);
95     return \%hash;
96 }
97
98 =head2 AttributeTypeExists
99
100   my $have_attr_xyz = C4::Members::AttributeTypes::AttributeTypeExists($code)
101
102 Returns true if we have attribute type C<$code>
103 in the database.
104
105 =cut
106
107 sub AttributeTypeExists {
108     my ($code) = @_;
109     my $dbh = C4::Context->dbh;
110     my $exists = $dbh->selectrow_array("SELECT code FROM borrower_attribute_types WHERE code = ?", undef, $code);
111     return $exists;
112 }
113
114 =head1 METHODS 
115
116   my $attr_type = C4::Members::AttributeTypes->new($code, $description);
117
118 Create a new attribute type.
119
120 =cut 
121
122 sub new {
123     my $class = shift;
124     my $self = {};
125
126     $self->{'code'} = shift;
127     $self->{'description'} = shift;
128     $self->{'repeatable'} = 0;
129     $self->{'unique_id'} = 0;
130     $self->{'opac_display'} = 0;
131     $self->{'password_allowed'} = 0;
132     $self->{'staff_searchable'} = 0;
133     $self->{'display_checkout'} = 0;
134     $self->{'authorised_value_category'} = '';
135     $self->{'category_code'} = '';
136     $self->{'category_description'} = '';
137     $self->{'class'} = '';
138
139     bless $self, $class;
140     return $self;
141 }
142
143 =head2 fetch
144
145   my $attr_type = C4::Members::AttributeTypes->fetch($code);
146
147 Fetches an attribute type from the database.  If no
148 type with the given C<$code> exists, returns undef.
149
150 =cut
151
152 sub fetch {
153     my $class = shift;
154     my $code = shift;
155     my $self = {};
156     my $dbh = C4::Context->dbh();
157
158     my $sth = $dbh->prepare_cached("
159         SELECT borrower_attribute_types.*, categories.description AS category_description
160         FROM borrower_attribute_types
161         LEFT JOIN categories ON borrower_attribute_types.category_code=categories.categorycode
162         WHERE code =?");
163     $sth->execute($code);
164     my $row = $sth->fetchrow_hashref;
165     $sth->finish();
166     return unless defined $row;
167
168     $self->{'code'}                      = $row->{'code'};
169     $self->{'description'}               = $row->{'description'};
170     $self->{'repeatable'}                = $row->{'repeatable'};
171     $self->{'unique_id'}                 = $row->{'unique_id'};
172     $self->{'opac_display'}              = $row->{'opac_display'};
173     $self->{'password_allowed'}          = $row->{'password_allowed'};
174     $self->{'staff_searchable'}          = $row->{'staff_searchable'};
175     $self->{'display_checkout'}          = $row->{'display_checkout'};
176     $self->{'authorised_value_category'} = $row->{'authorised_value_category'};
177     $self->{'category_code'}             = $row->{'category_code'};
178     $self->{'category_description'}      = $row->{'category_description'};
179     $self->{'class'}                     = $row->{'class'};
180
181     $sth = $dbh->prepare("SELECT branchcode, branchname FROM borrower_attribute_types_branches, branches WHERE b_branchcode = branchcode AND bat_code = ?;");
182     $sth->execute( $code );
183     while ( my $data = $sth->fetchrow_hashref ) {
184         push @{ $self->{branches} }, $data;
185     }
186     $sth->finish();
187
188     bless $self, $class;
189     return $self;
190 }
191
192 =head2 store
193
194   $attr_type->store();
195
196 Stores attribute type in the database.  If the type
197 previously retrieved from the database via the fetch()
198 method, the DB representation of the type is replaced.
199
200 =cut
201
202 sub store {
203     my $self = shift;
204
205     my $dbh = C4::Context->dbh;
206     my $sth;
207     my $existing = __PACKAGE__->fetch($self->{'code'});
208     if (defined $existing) {
209         $sth = $dbh->prepare_cached("UPDATE borrower_attribute_types
210                                      SET description = ?,
211                                          repeatable = ?,
212                                          unique_id = ?,
213                                          opac_display = ?,
214                                          password_allowed = ?,
215                                          staff_searchable = ?,
216                                          authorised_value_category = ?,
217                                          display_checkout = ?,
218                                          category_code = ?,
219                                          class = ?
220                                      WHERE code = ?");
221     } else {
222         $sth = $dbh->prepare_cached("INSERT INTO borrower_attribute_types 
223                                         (description, repeatable, unique_id, opac_display, password_allowed,
224                                          staff_searchable, authorised_value_category, display_checkout, category_code, class, code)
225                                         VALUES (?, ?, ?, ?, ?,
226                                                 ?, ?, ?, ?, ?, ?)");
227     }
228     $sth->bind_param(1, $self->{'description'});
229     $sth->bind_param(2, $self->{'repeatable'});
230     $sth->bind_param(3, $self->{'unique_id'});
231     $sth->bind_param(4, $self->{'opac_display'});
232     $sth->bind_param(5, $self->{'password_allowed'});
233     $sth->bind_param(6, $self->{'staff_searchable'});
234     $sth->bind_param(7, $self->{'authorised_value_category'});
235     $sth->bind_param(8, $self->{'display_checkout'});
236     $sth->bind_param(9, $self->{'category_code'} || undef);
237     $sth->bind_param(10, $self->{'class'});
238     $sth->bind_param(11, $self->{'code'});
239     $sth->execute;
240
241     if ( defined $$self{branches} ) {
242         $sth = $dbh->prepare("DELETE FROM borrower_attribute_types_branches WHERE bat_code = ?");
243         $sth->execute( $$self{code} );
244         $sth = $dbh->prepare(
245             "INSERT INTO borrower_attribute_types_branches
246                         ( bat_code, b_branchcode )
247                         VALUES ( ?, ? )"
248         );
249         for my $branchcode ( @{$$self{branches}} ) {
250             next if not $branchcode;
251             $sth->bind_param( 1, $$self{code} );
252             $sth->bind_param( 2, $branchcode );
253             $sth->execute;
254         }
255     }
256     $sth->finish;
257 }
258
259 =head2 code
260
261   my $code = $attr_type->code();
262   $attr_type->code($code);
263
264 Accessor.  Note that the code is immutable once
265 a type is created or fetched from the database.
266
267 =cut
268
269 sub code {
270     my $self = shift;
271     return $self->{'code'};
272 }
273
274 =head2 description
275
276   my $description = $attr_type->description();
277   $attr_type->description($description);
278
279 Accessor.
280
281 =cut
282
283 sub description {
284     my $self = shift;
285     @_ ? $self->{'description'} = shift : $self->{'description'};
286 }
287
288 =head2 branches
289
290 my $branches = $attr_type->branches();
291 $attr_type->branches($branches);
292
293 Accessor.
294
295 =cut
296
297 sub branches {
298     my $self = shift;
299     @_ ? $self->{branches} = shift : $self->{branches};
300 }
301
302 =head2 repeatable
303
304   my $repeatable = $attr_type->repeatable();
305   $attr_type->repeatable($repeatable);
306
307 Accessor.  The C<$repeatable> argument
308 is interpreted as a Perl boolean.
309
310 =cut
311
312 sub repeatable {
313     my $self = shift;
314     @_ ? $self->{'repeatable'} = ((shift) ? 1 : 0) : $self->{'repeatable'};
315 }
316
317 =head2 unique_id
318
319   my $unique_id = $attr_type->unique_id();
320   $attr_type->unique_id($unique_id);
321
322 Accessor.  The C<$unique_id> argument
323 is interpreted as a Perl boolean.
324
325 =cut
326
327 sub unique_id {
328     my $self = shift;
329     @_ ? $self->{'unique_id'} = ((shift) ? 1 : 0) : $self->{'unique_id'};
330 }
331 =head2 opac_display
332
333   my $opac_display = $attr_type->opac_display();
334   $attr_type->opac_display($opac_display);
335
336 Accessor.  The C<$opac_display> argument
337 is interpreted as a Perl boolean.
338
339 =cut
340
341 sub opac_display {
342     my $self = shift;
343     @_ ? $self->{'opac_display'} = ((shift) ? 1 : 0) : $self->{'opac_display'};
344 }
345 =head2 password_allowed
346
347   my $password_allowed = $attr_type->password_allowed();
348   $attr_type->password_allowed($password_allowed);
349
350 Accessor.  The C<$password_allowed> argument
351 is interpreted as a Perl boolean.
352
353 =cut
354
355 sub password_allowed {
356     my $self = shift;
357     @_ ? $self->{'password_allowed'} = ((shift) ? 1 : 0) : $self->{'password_allowed'};
358 }
359 =head2 staff_searchable
360
361   my $staff_searchable = $attr_type->staff_searchable();
362   $attr_type->staff_searchable($staff_searchable);
363
364 Accessor.  The C<$staff_searchable> argument
365 is interpreted as a Perl boolean.
366
367 =cut
368
369 sub staff_searchable {
370     my $self = shift;
371     @_ ? $self->{'staff_searchable'} = ((shift) ? 1 : 0) : $self->{'staff_searchable'};
372 }
373
374 =head2 display_checkout
375
376 my $display_checkout = $attr_type->display_checkout();
377 $attr_type->display_checkout($display_checkout);
378
379 Accessor.  The C<$display_checkout> argument
380 is interpreted as a Perl boolean.
381
382 =cut
383
384 sub display_checkout {
385     my $self = shift;
386     @_ ? $self->{'display_checkout'} = ((shift) ? 1 : 0) : $self->{'display_checkout'};
387 }
388
389 =head2 authorised_value_category
390
391   my $authorised_value_category = $attr_type->authorised_value_category();
392   $attr_type->authorised_value_category($authorised_value_category);
393
394 Accessor.
395
396 =cut
397
398 sub authorised_value_category {
399     my $self = shift;
400     @_ ? $self->{'authorised_value_category'} = shift : $self->{'authorised_value_category'};
401 }
402
403 =head2 category_code
404
405 my $category_code = $attr_type->category_code();
406 $attr_type->category_code($category_code);
407
408 Accessor.
409
410 =cut
411
412 sub category_code {
413     my $self = shift;
414     @_ ? $self->{'category_code'} = shift : $self->{'category_code'};
415 }
416
417 =head2 category_description
418
419 my $category_description = $attr_type->category_description();
420 $attr_type->category_description($category_description);
421
422 Accessor.
423
424 =cut
425
426 sub category_description {
427     my $self = shift;
428     @_ ? $self->{'category_description'} = shift : $self->{'category_description'};
429 }
430
431 =head2 class
432
433 my $class = $attr_type->class();
434 $attr_type->class($class);
435
436 Accessor.
437
438 =cut
439
440 sub class {
441     my $self = shift;
442     @_ ? $self->{'class'} = shift : $self->{'class'};
443 }
444
445
446 =head2 delete
447
448   $attr_type->delete();
449   C4::Members::AttributeTypes->delete($code);
450
451 Delete an attribute type from the database.  The attribute
452 type may be specified either by an object or by a code.
453
454 =cut
455
456 sub delete {
457     my $arg = shift;
458     my $code;
459     if (ref($arg) eq __PACKAGE__) {
460         $code = $arg->{'code'};
461     } else {
462         $code = shift;
463     }
464
465     my $dbh = C4::Context->dbh;
466     my $sth = $dbh->prepare_cached("DELETE FROM borrower_attribute_types WHERE code = ?");
467     $sth->execute($code);
468     $sth->finish;
469 }
470
471 =head2 num_patrons
472
473   my $count = $attr_type->num_patrons();
474
475 Returns the number of patron records that use
476 this attribute type.
477
478 =cut
479
480 sub num_patrons {
481     my $self = shift;
482
483     my $dbh = C4::Context->dbh;
484     my $sth = $dbh->prepare_cached("SELECT COUNT(DISTINCT borrowernumber)
485                                     FROM borrower_attributes
486                                     WHERE code = ?");
487     $sth->execute($self->{code});
488     my ($count) = $sth->fetchrow_array;
489     $sth->finish;
490     return $count;
491 }
492
493 =head2 get_patrons
494
495   my @borrowernumbers = $attr_type->get_patrons($attribute);
496
497 Returns the borrowernumber of the patron records that
498 have an attribute with the specifie value.
499
500 =cut
501
502 sub get_patrons {
503     my $self = shift;
504     my $value = shift;
505
506     my $dbh = C4::Context->dbh;
507     my $sth = $dbh->prepare_cached("SELECT DISTINCT borrowernumber
508                                     FROM borrower_attributes
509                                     WHERE code = ?
510                                     AND   attribute = ?");
511     $sth->execute($self->{code}, $value);
512     my @results;
513     while (my ($borrowernumber) = $sth->fetchrow_array) {
514         push @results, $borrowernumber;
515     } 
516     return @results;
517 }
518
519 =head1 AUTHOR
520
521 Koha Development Team <http://koha-community.org/>
522
523 Galen Charlton <galen.charlton@liblime.com>
524
525 =cut
526
527 1;