Filename | /mnt/catalyst/koha/C4/Members.pm |
Statements | Executed 55 statements in 23.3ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 6.32ms | 7.14ms | BEGIN@35 | C4::Members::
1 | 1 | 1 | 4.58ms | 578ms | BEGIN@30 | C4::Members::
1 | 1 | 1 | 3.01ms | 11.4ms | BEGIN@26 | C4::Members::
1 | 1 | 1 | 2.77ms | 3.45ms | BEGIN@37 | C4::Members::
1 | 1 | 1 | 2.73ms | 3.43ms | BEGIN@29 | C4::Members::
1 | 1 | 1 | 2.63ms | 2.74ms | BEGIN@27 | C4::Members::
1 | 1 | 1 | 2.14ms | 23.1ms | BEGIN@43 | C4::Members::
1 | 1 | 1 | 821µs | 1.51ms | BEGIN@42 | C4::Members::
1 | 1 | 1 | 446µs | 459µs | BEGIN@23 | C4::Members::
1 | 1 | 1 | 21µs | 21µs | BEGIN@47 | C4::Members::
1 | 1 | 1 | 16µs | 46µs | BEGIN@28 | C4::Members::
1 | 1 | 1 | 11µs | 33µs | BEGIN@916 | C4::Members::
1 | 1 | 1 | 9µs | 93µs | BEGIN@34 | C4::Members::
1 | 1 | 1 | 9µs | 311µs | BEGIN@33 | C4::Members::
1 | 1 | 1 | 9µs | 31µs | BEGIN@36 | C4::Members::
1 | 1 | 1 | 9µs | 11µs | BEGIN@25 | C4::Members::
1 | 1 | 1 | 9µs | 211µs | BEGIN@31 | C4::Members::
1 | 1 | 1 | 9µs | 44µs | BEGIN@41 | C4::Members::
1 | 1 | 1 | 8µs | 247µs | BEGIN@40 | C4::Members::
1 | 1 | 1 | 8µs | 104µs | BEGIN@32 | C4::Members::
1 | 1 | 1 | 7µs | 7µs | BEGIN@39 | C4::Members::
1 | 1 | 1 | 6µs | 6µs | BEGIN@38 | C4::Members::
1 | 1 | 1 | 2µs | 2µs | END | C4::Members::
0 | 0 | 0 | 0s | 0s | AddEnrolmentFeeIfNeeded | C4::Members::
0 | 0 | 0 | 0s | 0s | AddMember | C4::Members::
0 | 0 | 0 | 0s | 0s | AddMember_Opac | C4::Members::
0 | 0 | 0 | 0s | 0s | AddMessage | C4::Members::
0 | 0 | 0 | 0s | 0s | Check_Userid | C4::Members::
0 | 0 | 0 | 0s | 0s | DelMember | C4::Members::
0 | 0 | 0 | 0s | 0s | DeleteMessage | C4::Members::
0 | 0 | 0 | 0s | 0s | ExtendMemberSubscriptionTo | C4::Members::
0 | 0 | 0 | 0s | 0s | Generate_Userid | C4::Members::
0 | 0 | 0 | 0s | 0s | GetAge | C4::Members::
0 | 0 | 0 | 0s | 0s | GetAllIssues | C4::Members::
0 | 0 | 0 | 0s | 0s | GetBorNotifyAcctRecord | C4::Members::
0 | 0 | 0 | 0s | 0s | GetBorrowerCategorycode | C4::Members::
0 | 0 | 0 | 0s | 0s | GetBorrowercategory | C4::Members::
0 | 0 | 0 | 0s | 0s | GetBorrowercategoryList | C4::Members::
0 | 0 | 0 | 0s | 0s | GetBorrowersNamesAndLatestIssue | C4::Members::
0 | 0 | 0 | 0s | 0s | GetBorrowersToExpunge | C4::Members::
0 | 0 | 0 | 0s | 0s | GetBorrowersWhoHaveNeverBorrowed | C4::Members::
0 | 0 | 0 | 0s | 0s | GetBorrowersWithEmail | C4::Members::
0 | 0 | 0 | 0s | 0s | GetBorrowersWithIssuesHistoryOlderThan | C4::Members::
0 | 0 | 0 | 0s | 0s | GetCities | C4::Members::
0 | 0 | 0 | 0s | 0s | GetExpiryDate | C4::Members::
0 | 0 | 0 | 0s | 0s | GetFirstValidEmailAddress | C4::Members::
0 | 0 | 0 | 0s | 0s | GetGuarantees | C4::Members::
0 | 0 | 0 | 0s | 0s | GetHideLostItemsPreference | C4::Members::
0 | 0 | 0 | 0s | 0s | GetMember | C4::Members::
0 | 0 | 0 | 0s | 0s | GetMemberAccountBalance | C4::Members::
0 | 0 | 0 | 0s | 0s | GetMemberAccountRecords | C4::Members::
0 | 0 | 0 | 0s | 0s | GetMemberDetails | C4::Members::
0 | 0 | 0 | 0s | 0s | GetMemberIssuesAndFines | C4::Members::
0 | 0 | 0 | 0s | 0s | GetMemberRelatives | C4::Members::
0 | 0 | 0 | 0s | 0s | GetMessages | C4::Members::
0 | 0 | 0 | 0s | 0s | GetMessagesCount | C4::Members::
0 | 0 | 0 | 0s | 0s | GetNoticeEmailAddress | C4::Members::
0 | 0 | 0 | 0s | 0s | GetPatronImage | C4::Members::
0 | 0 | 0 | 0s | 0s | GetPendingIssues | C4::Members::
0 | 0 | 0 | 0s | 0s | GetSortDetails | C4::Members::
0 | 0 | 0 | 0s | 0s | GetTitles | C4::Members::
0 | 0 | 0 | 0s | 0s | GetborCatFromCatType | C4::Members::
0 | 0 | 0 | 0s | 0s | HasOverdues | C4::Members::
0 | 0 | 0 | 0s | 0s | IsMemberBlocked | C4::Members::
0 | 0 | 0 | 0s | 0s | IssueSlip | C4::Members::
0 | 0 | 0 | 0s | 0s | ModMember | C4::Members::
0 | 0 | 0 | 0s | 0s | ModPrivacy | C4::Members::
0 | 0 | 0 | 0s | 0s | MoveMemberToDeleted | C4::Members::
0 | 0 | 0 | 0s | 0s | PutPatronImage | C4::Members::
0 | 0 | 0 | 0s | 0s | RmPatronImage | C4::Members::
0 | 0 | 0 | 0s | 0s | Search | C4::Members::
0 | 0 | 0 | 0s | 0s | UpdateGuarantees | C4::Members::
0 | 0 | 0 | 0s | 0s | _express_member_find | C4::Members::
0 | 0 | 0 | 0s | 0s | add_member_orgs | C4::Members::
0 | 0 | 0 | 0s | 0s | changepassword | C4::Members::
0 | 0 | 0 | 0s | 0s | checkcardnumber | C4::Members::
0 | 0 | 0 | 0s | 0s | checkuniquemember | C4::Members::
0 | 0 | 0 | 0s | 0s | columns | C4::Members::
0 | 0 | 0 | 0s | 0s | ethnicitycategories | C4::Members::
0 | 0 | 0 | 0s | 0s | fixEthnicity | C4::Members::
0 | 0 | 0 | 0s | 0s | fixup_cardnumber | C4::Members::
0 | 0 | 0 | 0s | 0s | get_institutions | C4::Members::
0 | 0 | 0 | 0s | 0s | getidcity | C4::Members::
0 | 0 | 0 | 0s | 0s | getzipnamecity | C4::Members::
0 | 0 | 0 | 0s | 0s | patronflags | C4::Members::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package C4::Members; | ||||
2 | |||||
3 | # Copyright 2000-2003 Katipo Communications | ||||
4 | # Copyright 2010 BibLibre | ||||
5 | # Parts Copyright 2010 Catalyst IT | ||||
6 | # | ||||
7 | # This file is part of Koha. | ||||
8 | # | ||||
9 | # Koha is free software; you can redistribute it and/or modify it under the | ||||
10 | # terms of the GNU General Public License as published by the Free Software | ||||
11 | # Foundation; either version 2 of the License, or (at your option) any later | ||||
12 | # version. | ||||
13 | # | ||||
14 | # Koha is distributed in the hope that it will be useful, but WITHOUT ANY | ||||
15 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||||
16 | # A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||||
17 | # | ||||
18 | # You should have received a copy of the GNU General Public License along | ||||
19 | # with Koha; if not, write to the Free Software Foundation, Inc., | ||||
20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
21 | |||||
22 | |||||
23 | 2 | 34µs | 2 | 472µs | # spent 459µs (446+13) within C4::Members::BEGIN@23 which was called:
# once (446µs+13µs) by main::BEGIN@73 at line 23 # spent 459µs making 1 call to C4::Members::BEGIN@23
# spent 13µs making 1 call to strict::import |
24 | #use warnings; FIXME - Bug 2505 | ||||
25 | 2 | 25µs | 2 | 13µs | # spent 11µs (9+2) within C4::Members::BEGIN@25 which was called:
# once (9µs+2µs) by main::BEGIN@73 at line 25 # spent 11µs making 1 call to C4::Members::BEGIN@25
# spent 2µs making 1 call to C4::Context::import |
26 | 2 | 2.01ms | 2 | 11.4ms | # spent 11.4ms (3.01+8.37) within C4::Members::BEGIN@26 which was called:
# once (3.01ms+8.37ms) by main::BEGIN@73 at line 26 # spent 11.4ms making 1 call to C4::Members::BEGIN@26
# spent 33µs making 1 call to Exporter::import |
27 | 2 | 957µs | 2 | 2.77ms | # spent 2.74ms (2.63+108µs) within C4::Members::BEGIN@27 which was called:
# once (2.63ms+108µs) by main::BEGIN@73 at line 27 # spent 2.74ms making 1 call to C4::Members::BEGIN@27
# spent 31µs making 1 call to Exporter::import |
28 | 2 | 22µs | 2 | 77µs | # spent 46µs (16+31) within C4::Members::BEGIN@28 which was called:
# once (16µs+31µs) by main::BEGIN@73 at line 28 # spent 46µs making 1 call to C4::Members::BEGIN@28
# spent 31µs making 1 call to Exporter::import |
29 | 2 | 2.37ms | 2 | 3.55ms | # spent 3.43ms (2.73+694µs) within C4::Members::BEGIN@29 which was called:
# once (2.73ms+694µs) by main::BEGIN@73 at line 29 # spent 3.43ms making 1 call to C4::Members::BEGIN@29
# spent 120µs making 1 call to Exporter::import |
30 | 2 | 2.12ms | 2 | 578ms | # spent 578ms (4.58+573) within C4::Members::BEGIN@30 which was called:
# once (4.58ms+573ms) by main::BEGIN@73 at line 30 # spent 578ms making 1 call to C4::Members::BEGIN@30
# spent 128µs making 1 call to Exporter::import |
31 | 2 | 26µs | 2 | 413µs | # spent 211µs (9+202) within C4::Members::BEGIN@31 which was called:
# once (9µs+202µs) by main::BEGIN@73 at line 31 # spent 211µs making 1 call to C4::Members::BEGIN@31
# spent 202µs making 1 call to Exporter::import |
32 | 2 | 24µs | 2 | 201µs | # spent 104µs (8+97) within C4::Members::BEGIN@32 which was called:
# once (8µs+97µs) by main::BEGIN@73 at line 32 # spent 104µs making 1 call to C4::Members::BEGIN@32
# spent 97µs making 1 call to Exporter::import |
33 | 2 | 26µs | 2 | 612µs | # spent 311µs (9+302) within C4::Members::BEGIN@33 which was called:
# once (9µs+302µs) by main::BEGIN@73 at line 33 # spent 311µs making 1 call to C4::Members::BEGIN@33
# spent 302µs making 1 call to Exporter::import |
34 | 2 | 26µs | 2 | 177µs | # spent 93µs (9+84) within C4::Members::BEGIN@34 which was called:
# once (9µs+84µs) by main::BEGIN@73 at line 34 # spent 93µs making 1 call to C4::Members::BEGIN@34
# spent 84µs making 1 call to Exporter::import |
35 | 2 | 2.61ms | 2 | 7.18ms | # spent 7.14ms (6.32+819µs) within C4::Members::BEGIN@35 which was called:
# once (6.32ms+819µs) by main::BEGIN@73 at line 35 # spent 7.14ms making 1 call to C4::Members::BEGIN@35
# spent 46µs making 1 call to Exporter::import |
36 | 2 | 32µs | 2 | 52µs | # spent 31µs (9+22) within C4::Members::BEGIN@36 which was called:
# once (9µs+22µs) by main::BEGIN@73 at line 36 # spent 31µs making 1 call to C4::Members::BEGIN@36
# spent 22µs making 1 call to Exporter::import |
37 | 2 | 2.55ms | 2 | 3.54ms | # spent 3.45ms (2.77+675µs) within C4::Members::BEGIN@37 which was called:
# once (2.77ms+675µs) by main::BEGIN@73 at line 37 # spent 3.45ms making 1 call to C4::Members::BEGIN@37
# spent 92µs making 1 call to Exporter::import |
38 | 2 | 21µs | 1 | 6µs | # spent 6µs within C4::Members::BEGIN@38 which was called:
# once (6µs+0s) by main::BEGIN@73 at line 38 # spent 6µs making 1 call to C4::Members::BEGIN@38 |
39 | 2 | 22µs | 1 | 7µs | # spent 7µs within C4::Members::BEGIN@39 which was called:
# once (7µs+0s) by main::BEGIN@73 at line 39 # spent 7µs making 1 call to C4::Members::BEGIN@39 |
40 | 2 | 28µs | 2 | 486µs | # spent 247µs (8+239) within C4::Members::BEGIN@40 which was called:
# once (8µs+239µs) by main::BEGIN@73 at line 40 # spent 247µs making 1 call to C4::Members::BEGIN@40
# spent 239µs making 1 call to Exporter::import |
41 | 2 | 24µs | 2 | 80µs | # spent 44µs (9+36) within C4::Members::BEGIN@41 which was called:
# once (9µs+36µs) by main::BEGIN@73 at line 41 # spent 44µs making 1 call to C4::Members::BEGIN@41
# spent 36µs making 1 call to Exporter::import |
42 | 2 | 750µs | 2 | 1.54ms | # spent 1.51ms (821µs+686µs) within C4::Members::BEGIN@42 which was called:
# once (821µs+686µs) by main::BEGIN@73 at line 42 # spent 1.51ms making 1 call to C4::Members::BEGIN@42
# spent 30µs making 1 call to Exporter::import |
43 | 2 | 2.25ms | 2 | 23.2ms | # spent 23.1ms (2.14+21.0) within C4::Members::BEGIN@43 which was called:
# once (2.14ms+21.0ms) by main::BEGIN@73 at line 43 # spent 23.1ms making 1 call to C4::Members::BEGIN@43
# spent 27µs making 1 call to Exporter::import |
44 | |||||
45 | 1 | 600ns | our ($VERSION,@ISA,@EXPORT,@EXPORT_OK,$debug); | ||
46 | |||||
47 | # spent 21µs within C4::Members::BEGIN@47 which was called:
# once (21µs+0s) by main::BEGIN@73 at line 141 | ||||
48 | 1 | 800ns | $VERSION = 3.07.00.049; | ||
49 | 1 | 500ns | $debug = $ENV{DEBUG} || 0; | ||
50 | 1 | 500ns | require Exporter; | ||
51 | 1 | 7µs | @ISA = qw(Exporter); | ||
52 | #Get data | ||||
53 | 1 | 6µs | push @EXPORT, qw( | ||
54 | &Search | ||||
55 | &GetMemberDetails | ||||
56 | &GetMemberRelatives | ||||
57 | &GetMember | ||||
58 | |||||
59 | &GetGuarantees | ||||
60 | |||||
61 | &GetMemberIssuesAndFines | ||||
62 | &GetPendingIssues | ||||
63 | &GetAllIssues | ||||
64 | |||||
65 | &get_institutions | ||||
66 | &getzipnamecity | ||||
67 | &getidcity | ||||
68 | |||||
69 | &GetFirstValidEmailAddress | ||||
70 | &GetNoticeEmailAddress | ||||
71 | |||||
72 | &GetAge | ||||
73 | &GetCities | ||||
74 | &GetSortDetails | ||||
75 | &GetTitles | ||||
76 | |||||
77 | &GetPatronImage | ||||
78 | &PutPatronImage | ||||
79 | &RmPatronImage | ||||
80 | |||||
81 | &GetHideLostItemsPreference | ||||
82 | |||||
83 | &IsMemberBlocked | ||||
84 | &GetMemberAccountRecords | ||||
85 | &GetBorNotifyAcctRecord | ||||
86 | |||||
87 | &GetborCatFromCatType | ||||
88 | &GetBorrowercategory | ||||
89 | GetBorrowerCategorycode | ||||
90 | &GetBorrowercategoryList | ||||
91 | |||||
92 | &GetBorrowersToExpunge | ||||
93 | &GetBorrowersWhoHaveNeverBorrowed | ||||
94 | &GetBorrowersWithIssuesHistoryOlderThan | ||||
95 | |||||
96 | &GetExpiryDate | ||||
97 | |||||
98 | &AddMessage | ||||
99 | &DeleteMessage | ||||
100 | &GetMessages | ||||
101 | &GetMessagesCount | ||||
102 | |||||
103 | &IssueSlip | ||||
104 | GetBorrowersWithEmail | ||||
105 | |||||
106 | HasOverdues | ||||
107 | ); | ||||
108 | |||||
109 | #Modify data | ||||
110 | 1 | 400ns | push @EXPORT, qw( | ||
111 | &ModMember | ||||
112 | &changepassword | ||||
113 | &ModPrivacy | ||||
114 | ); | ||||
115 | |||||
116 | #Delete data | ||||
117 | 1 | 200ns | push @EXPORT, qw( | ||
118 | &DelMember | ||||
119 | ); | ||||
120 | |||||
121 | #Insert data | ||||
122 | 1 | 300ns | push @EXPORT, qw( | ||
123 | &AddMember | ||||
124 | &AddMember_Opac | ||||
125 | &add_member_orgs | ||||
126 | &MoveMemberToDeleted | ||||
127 | &ExtendMemberSubscriptionTo | ||||
128 | ); | ||||
129 | |||||
130 | #Check data | ||||
131 | 1 | 5µs | push @EXPORT, qw( | ||
132 | &checkuniquemember | ||||
133 | &checkuserpassword | ||||
134 | &Check_Userid | ||||
135 | &Generate_Userid | ||||
136 | &fixEthnicity | ||||
137 | ðnicitycategories | ||||
138 | &fixup_cardnumber | ||||
139 | &checkcardnumber | ||||
140 | ); | ||||
141 | 1 | 2.16ms | 1 | 21µs | } # spent 21µs making 1 call to C4::Members::BEGIN@47 |
142 | |||||
143 | =head1 NAME | ||||
144 | |||||
145 | C4::Members - Perl Module containing convenience functions for member handling | ||||
146 | |||||
147 | =head1 SYNOPSIS | ||||
148 | |||||
149 | use C4::Members; | ||||
150 | |||||
151 | =head1 DESCRIPTION | ||||
152 | |||||
153 | This module contains routines for adding, modifying and deleting members/patrons/borrowers | ||||
154 | |||||
155 | =head1 FUNCTIONS | ||||
156 | |||||
157 | =head2 Search | ||||
158 | |||||
159 | $borrowers_result_array_ref = &Search($filter,$orderby, $limit, | ||||
160 | $columns_out, $search_on_fields,$searchtype); | ||||
161 | |||||
162 | Looks up patrons (borrowers) on filter. A wrapper for SearchInTable('borrowers'). | ||||
163 | |||||
164 | For C<$filter>, C<$orderby>, C<$limit>, C<&columns_out>, C<&search_on_fields> and C<&searchtype> | ||||
165 | refer to C4::SQLHelper:SearchInTable(). | ||||
166 | |||||
167 | Special C<$filter> key '' is effectively expanded to search on surname firstname othernamescw | ||||
168 | and cardnumber unless C<&search_on_fields> is defined | ||||
169 | |||||
170 | Examples: | ||||
171 | |||||
172 | $borrowers = Search('abcd', 'cardnumber'); | ||||
173 | |||||
174 | $borrowers = Search({''=>'abcd', category_type=>'I'}, 'surname'); | ||||
175 | |||||
176 | =cut | ||||
177 | |||||
178 | sub _express_member_find { | ||||
179 | my ($filter) = @_; | ||||
180 | |||||
181 | # this is used by circulation everytime a new borrowers cardnumber is scanned | ||||
182 | # so we can check an exact match first, if that works return, otherwise do the rest | ||||
183 | my $dbh = C4::Context->dbh; | ||||
184 | my $query = "SELECT borrowernumber FROM borrowers WHERE cardnumber = ?"; | ||||
185 | if ( my $borrowernumber = $dbh->selectrow_array($query, undef, $filter) ) { | ||||
186 | return( {"borrowernumber"=>$borrowernumber} ); | ||||
187 | } | ||||
188 | |||||
189 | my ($search_on_fields, $searchtype); | ||||
190 | if ( length($filter) == 1 ) { | ||||
191 | $search_on_fields = [ qw(surname) ]; | ||||
192 | $searchtype = 'start_with'; | ||||
193 | } else { | ||||
194 | $search_on_fields = [ qw(surname firstname othernames cardnumber) ]; | ||||
195 | $searchtype = 'contain'; | ||||
196 | } | ||||
197 | |||||
198 | return (undef, $search_on_fields, $searchtype); | ||||
199 | } | ||||
200 | |||||
201 | sub Search { | ||||
202 | my ( $filter, $orderby, $limit, $columns_out, $search_on_fields, $searchtype ) = @_; | ||||
203 | |||||
204 | my $search_string; | ||||
205 | my $found_borrower; | ||||
206 | |||||
207 | if ( my $fr = ref $filter ) { | ||||
208 | if ( $fr eq "HASH" ) { | ||||
209 | if ( my $search_string = $filter->{''} ) { | ||||
210 | my ($member_filter, $member_search_on_fields, $member_searchtype) = _express_member_find($search_string); | ||||
211 | if ($member_filter) { | ||||
212 | $filter = $member_filter; | ||||
213 | $found_borrower = 1; | ||||
214 | } else { | ||||
215 | $search_on_fields ||= $member_search_on_fields; | ||||
216 | $searchtype ||= $member_searchtype; | ||||
217 | } | ||||
218 | } | ||||
219 | } | ||||
220 | else { | ||||
221 | $search_string = $filter; | ||||
222 | } | ||||
223 | } | ||||
224 | else { | ||||
225 | $search_string = $filter; | ||||
226 | my ($member_filter, $member_search_on_fields, $member_searchtype) = _express_member_find($search_string); | ||||
227 | if ($member_filter) { | ||||
228 | $filter = $member_filter; | ||||
229 | $found_borrower = 1; | ||||
230 | } else { | ||||
231 | $search_on_fields ||= $member_search_on_fields; | ||||
232 | $searchtype ||= $member_searchtype; | ||||
233 | } | ||||
234 | } | ||||
235 | |||||
236 | if ( !$found_borrower && C4::Context->preference('ExtendedPatronAttributes') && $search_string ) { | ||||
237 | my $matching_records = C4::Members::Attributes::SearchIdMatchingAttribute($search_string); | ||||
238 | if(scalar(@$matching_records)>0) { | ||||
239 | if ( my $fr = ref $filter ) { | ||||
240 | if ( $fr eq "HASH" ) { | ||||
241 | my %f = %$filter; | ||||
242 | $filter = [ $filter ]; | ||||
243 | delete $f{''}; | ||||
244 | push @$filter, { %f, "borrowernumber"=>$$matching_records }; | ||||
245 | } | ||||
246 | else { | ||||
247 | push @$filter, {"borrowernumber"=>$matching_records}; | ||||
248 | } | ||||
249 | } | ||||
250 | else { | ||||
251 | $filter = [ $filter ]; | ||||
252 | push @$filter, {"borrowernumber"=>$matching_records}; | ||||
253 | } | ||||
254 | } | ||||
255 | } | ||||
256 | |||||
257 | # $showallbranches was not used at the time SearchMember() was mainstreamed into Search(). | ||||
258 | # Mentioning for the reference | ||||
259 | |||||
260 | if ( C4::Context->preference("IndependentBranches") ) { # && !$showallbranches){ | ||||
261 | if ( my $userenv = C4::Context->userenv ) { | ||||
262 | my $branch = $userenv->{'branch'}; | ||||
263 | if ( !C4::Context->IsSuperLibrarian() && $branch ){ | ||||
264 | if (my $fr = ref $filter) { | ||||
265 | if ( $fr eq "HASH" ) { | ||||
266 | $filter->{branchcode} = $branch; | ||||
267 | } | ||||
268 | else { | ||||
269 | foreach (@$filter) { | ||||
270 | $_ = { '' => $_ } unless ref $_; | ||||
271 | $_->{branchcode} = $branch; | ||||
272 | } | ||||
273 | } | ||||
274 | } | ||||
275 | else { | ||||
276 | $filter = { '' => $filter, branchcode => $branch }; | ||||
277 | } | ||||
278 | } | ||||
279 | } | ||||
280 | } | ||||
281 | |||||
282 | if ($found_borrower) { | ||||
283 | $searchtype = "exact"; | ||||
284 | } | ||||
285 | $searchtype ||= "start_with"; | ||||
286 | |||||
287 | return SearchInTable( "borrowers", $filter, $orderby, $limit, $columns_out, $search_on_fields, $searchtype ); | ||||
288 | } | ||||
289 | |||||
290 | =head2 GetMemberDetails | ||||
291 | |||||
292 | ($borrower) = &GetMemberDetails($borrowernumber, $cardnumber); | ||||
293 | |||||
294 | Looks up a patron and returns information about him or her. If | ||||
295 | C<$borrowernumber> is true (nonzero), C<&GetMemberDetails> looks | ||||
296 | up the borrower by number; otherwise, it looks up the borrower by card | ||||
297 | number. | ||||
298 | |||||
299 | C<$borrower> is a reference-to-hash whose keys are the fields of the | ||||
300 | borrowers table in the Koha database. In addition, | ||||
301 | C<$borrower-E<gt>{flags}> is a hash giving more detailed information | ||||
302 | about the patron. Its keys act as flags : | ||||
303 | |||||
304 | if $borrower->{flags}->{LOST} { | ||||
305 | # Patron's card was reported lost | ||||
306 | } | ||||
307 | |||||
308 | If the state of a flag means that the patron should not be | ||||
309 | allowed to borrow any more books, then it will have a C<noissues> key | ||||
310 | with a true value. | ||||
311 | |||||
312 | See patronflags for more details. | ||||
313 | |||||
314 | C<$borrower-E<gt>{authflags}> is a hash giving more detailed information | ||||
315 | about the top-level permissions flags set for the borrower. For example, | ||||
316 | if a user has the "editcatalogue" permission, | ||||
317 | C<$borrower-E<gt>{authflags}-E<gt>{editcatalogue}> will exist and have | ||||
318 | the value "1". | ||||
319 | |||||
320 | =cut | ||||
321 | |||||
322 | sub GetMemberDetails { | ||||
323 | my ( $borrowernumber, $cardnumber ) = @_; | ||||
324 | my $dbh = C4::Context->dbh; | ||||
325 | my $query; | ||||
326 | my $sth; | ||||
327 | if ($borrowernumber) { | ||||
328 | $sth = $dbh->prepare("SELECT borrowers.*,category_type,categories.description,reservefee,enrolmentperiod FROM borrowers LEFT JOIN categories ON borrowers.categorycode=categories.categorycode WHERE borrowernumber=?"); | ||||
329 | $sth->execute($borrowernumber); | ||||
330 | } | ||||
331 | elsif ($cardnumber) { | ||||
332 | $sth = $dbh->prepare("SELECT borrowers.*,category_type,categories.description,reservefee,enrolmentperiod FROM borrowers LEFT JOIN categories ON borrowers.categorycode=categories.categorycode WHERE cardnumber=?"); | ||||
333 | $sth->execute($cardnumber); | ||||
334 | } | ||||
335 | else { | ||||
336 | return; | ||||
337 | } | ||||
338 | my $borrower = $sth->fetchrow_hashref; | ||||
339 | my ($amount) = GetMemberAccountRecords( $borrowernumber); | ||||
340 | $borrower->{'amountoutstanding'} = $amount; | ||||
341 | # FIXME - patronflags calls GetMemberAccountRecords... just have patronflags return $amount | ||||
342 | my $flags = patronflags( $borrower); | ||||
343 | my $accessflagshash; | ||||
344 | |||||
345 | $sth = $dbh->prepare("select bit,flag from userflags"); | ||||
346 | $sth->execute; | ||||
347 | while ( my ( $bit, $flag ) = $sth->fetchrow ) { | ||||
348 | if ( $borrower->{'flags'} && $borrower->{'flags'} & 2**$bit ) { | ||||
349 | $accessflagshash->{$flag} = 1; | ||||
350 | } | ||||
351 | } | ||||
352 | $borrower->{'flags'} = $flags; | ||||
353 | $borrower->{'authflags'} = $accessflagshash; | ||||
354 | |||||
355 | # For the purposes of making templates easier, we'll define a | ||||
356 | # 'showname' which is the alternate form the user's first name if | ||||
357 | # 'other name' is defined. | ||||
358 | if ($borrower->{category_type} eq 'I') { | ||||
359 | $borrower->{'showname'} = $borrower->{'othernames'}; | ||||
360 | $borrower->{'showname'} .= " $borrower->{'firstname'}" if $borrower->{'firstname'}; | ||||
361 | } else { | ||||
362 | $borrower->{'showname'} = $borrower->{'firstname'}; | ||||
363 | } | ||||
364 | |||||
365 | return ($borrower); #, $flags, $accessflagshash); | ||||
366 | } | ||||
367 | |||||
368 | =head2 patronflags | ||||
369 | |||||
370 | $flags = &patronflags($patron); | ||||
371 | |||||
372 | This function is not exported. | ||||
373 | |||||
374 | The following will be set where applicable: | ||||
375 | $flags->{CHARGES}->{amount} Amount of debt | ||||
376 | $flags->{CHARGES}->{noissues} Set if debt amount >$5.00 (or syspref noissuescharge) | ||||
377 | $flags->{CHARGES}->{message} Message -- deprecated | ||||
378 | |||||
379 | $flags->{CREDITS}->{amount} Amount of credit | ||||
380 | $flags->{CREDITS}->{message} Message -- deprecated | ||||
381 | |||||
382 | $flags->{ GNA } Patron has no valid address | ||||
383 | $flags->{ GNA }->{noissues} Set for each GNA | ||||
384 | $flags->{ GNA }->{message} "Borrower has no valid address" -- deprecated | ||||
385 | |||||
386 | $flags->{ LOST } Patron's card reported lost | ||||
387 | $flags->{ LOST }->{noissues} Set for each LOST | ||||
388 | $flags->{ LOST }->{message} Message -- deprecated | ||||
389 | |||||
390 | $flags->{DBARRED} Set if patron debarred, no access | ||||
391 | $flags->{DBARRED}->{noissues} Set for each DBARRED | ||||
392 | $flags->{DBARRED}->{message} Message -- deprecated | ||||
393 | |||||
394 | $flags->{ NOTES } | ||||
395 | $flags->{ NOTES }->{message} The note itself. NOT deprecated | ||||
396 | |||||
397 | $flags->{ ODUES } Set if patron has overdue books. | ||||
398 | $flags->{ ODUES }->{message} "Yes" -- deprecated | ||||
399 | $flags->{ ODUES }->{itemlist} ref-to-array: list of overdue books | ||||
400 | $flags->{ ODUES }->{itemlisttext} Text list of overdue items -- deprecated | ||||
401 | |||||
402 | $flags->{WAITING} Set if any of patron's reserves are available | ||||
403 | $flags->{WAITING}->{message} Message -- deprecated | ||||
404 | $flags->{WAITING}->{itemlist} ref-to-array: list of available items | ||||
405 | |||||
406 | =over | ||||
407 | |||||
408 | =item C<$flags-E<gt>{ODUES}-E<gt>{itemlist}> is a reference-to-array listing the | ||||
409 | overdue items. Its elements are references-to-hash, each describing an | ||||
410 | overdue item. The keys are selected fields from the issues, biblio, | ||||
411 | biblioitems, and items tables of the Koha database. | ||||
412 | |||||
413 | =item C<$flags-E<gt>{ODUES}-E<gt>{itemlisttext}> is a string giving a text listing of | ||||
414 | the overdue items, one per line. Deprecated. | ||||
415 | |||||
416 | =item C<$flags-E<gt>{WAITING}-E<gt>{itemlist}> is a reference-to-array listing the | ||||
417 | available items. Each element is a reference-to-hash whose keys are | ||||
418 | fields from the reserves table of the Koha database. | ||||
419 | |||||
420 | =back | ||||
421 | |||||
422 | All the "message" fields that include language generated in this function are deprecated, | ||||
423 | because such strings belong properly in the display layer. | ||||
424 | |||||
425 | The "message" field that comes from the DB is OK. | ||||
426 | |||||
427 | =cut | ||||
428 | |||||
429 | # TODO: use {anonymous => hashes} instead of a dozen %flaginfo | ||||
430 | # FIXME rename this function. | ||||
431 | sub patronflags { | ||||
432 | my %flags; | ||||
433 | my ( $patroninformation) = @_; | ||||
434 | my $dbh=C4::Context->dbh; | ||||
435 | my ($balance, $owing) = GetMemberAccountBalance( $patroninformation->{'borrowernumber'}); | ||||
436 | if ( $owing > 0 ) { | ||||
437 | my %flaginfo; | ||||
438 | my $noissuescharge = C4::Context->preference("noissuescharge") || 5; | ||||
439 | $flaginfo{'message'} = sprintf 'Patron owes %.02f', $owing; | ||||
440 | $flaginfo{'amount'} = sprintf "%.02f", $owing; | ||||
441 | if ( $owing > $noissuescharge && !C4::Context->preference("AllowFineOverride") ) { | ||||
442 | $flaginfo{'noissues'} = 1; | ||||
443 | } | ||||
444 | $flags{'CHARGES'} = \%flaginfo; | ||||
445 | } | ||||
446 | elsif ( $balance < 0 ) { | ||||
447 | my %flaginfo; | ||||
448 | $flaginfo{'message'} = sprintf 'Patron has credit of %.02f', -$balance; | ||||
449 | $flaginfo{'amount'} = sprintf "%.02f", $balance; | ||||
450 | $flags{'CREDITS'} = \%flaginfo; | ||||
451 | } | ||||
452 | if ( $patroninformation->{'gonenoaddress'} | ||||
453 | && $patroninformation->{'gonenoaddress'} == 1 ) | ||||
454 | { | ||||
455 | my %flaginfo; | ||||
456 | $flaginfo{'message'} = 'Borrower has no valid address.'; | ||||
457 | $flaginfo{'noissues'} = 1; | ||||
458 | $flags{'GNA'} = \%flaginfo; | ||||
459 | } | ||||
460 | if ( $patroninformation->{'lost'} && $patroninformation->{'lost'} == 1 ) { | ||||
461 | my %flaginfo; | ||||
462 | $flaginfo{'message'} = 'Borrower\'s card reported lost.'; | ||||
463 | $flaginfo{'noissues'} = 1; | ||||
464 | $flags{'LOST'} = \%flaginfo; | ||||
465 | } | ||||
466 | if ( $patroninformation->{'debarred'} && check_date( split( /-/, $patroninformation->{'debarred'} ) ) ) { | ||||
467 | if ( Date_to_Days(Date::Calc::Today) < Date_to_Days( split( /-/, $patroninformation->{'debarred'} ) ) ) { | ||||
468 | my %flaginfo; | ||||
469 | $flaginfo{'debarredcomment'} = $patroninformation->{'debarredcomment'}; | ||||
470 | $flaginfo{'message'} = $patroninformation->{'debarredcomment'}; | ||||
471 | $flaginfo{'noissues'} = 1; | ||||
472 | $flaginfo{'dateend'} = $patroninformation->{'debarred'}; | ||||
473 | $flags{'DBARRED'} = \%flaginfo; | ||||
474 | } | ||||
475 | } | ||||
476 | if ( $patroninformation->{'borrowernotes'} | ||||
477 | && $patroninformation->{'borrowernotes'} ) | ||||
478 | { | ||||
479 | my %flaginfo; | ||||
480 | $flaginfo{'message'} = $patroninformation->{'borrowernotes'}; | ||||
481 | $flags{'NOTES'} = \%flaginfo; | ||||
482 | } | ||||
483 | my ( $odues, $itemsoverdue ) = C4::Overdues::checkoverdues($patroninformation->{'borrowernumber'}); | ||||
484 | if ( $odues && $odues > 0 ) { | ||||
485 | my %flaginfo; | ||||
486 | $flaginfo{'message'} = "Yes"; | ||||
487 | $flaginfo{'itemlist'} = $itemsoverdue; | ||||
488 | foreach ( sort { $a->{'date_due'} cmp $b->{'date_due'} } | ||||
489 | @$itemsoverdue ) | ||||
490 | { | ||||
491 | $flaginfo{'itemlisttext'} .= | ||||
492 | "$_->{'date_due'} $_->{'barcode'} $_->{'title'} \n"; # newline is display layer | ||||
493 | } | ||||
494 | $flags{'ODUES'} = \%flaginfo; | ||||
495 | } | ||||
496 | my @itemswaiting = C4::Reserves::GetReservesFromBorrowernumber( $patroninformation->{'borrowernumber'},'W' ); | ||||
497 | my $nowaiting = scalar @itemswaiting; | ||||
498 | if ( $nowaiting > 0 ) { | ||||
499 | my %flaginfo; | ||||
500 | $flaginfo{'message'} = "Reserved items available"; | ||||
501 | $flaginfo{'itemlist'} = \@itemswaiting; | ||||
502 | $flags{'WAITING'} = \%flaginfo; | ||||
503 | } | ||||
504 | return ( \%flags ); | ||||
505 | } | ||||
506 | |||||
507 | |||||
508 | =head2 GetMember | ||||
509 | |||||
510 | $borrower = &GetMember(%information); | ||||
511 | |||||
512 | Retrieve the first patron record meeting on criteria listed in the | ||||
513 | C<%information> hash, which should contain one or more | ||||
514 | pairs of borrowers column names and values, e.g., | ||||
515 | |||||
516 | $borrower = GetMember(borrowernumber => id); | ||||
517 | |||||
518 | C<&GetBorrower> returns a reference-to-hash whose keys are the fields of | ||||
519 | the C<borrowers> table in the Koha database. | ||||
520 | |||||
521 | FIXME: GetMember() is used throughout the code as a lookup | ||||
522 | on a unique key such as the borrowernumber, but this meaning is not | ||||
523 | enforced in the routine itself. | ||||
524 | |||||
525 | =cut | ||||
526 | |||||
527 | #' | ||||
528 | sub GetMember { | ||||
529 | my ( %information ) = @_; | ||||
530 | if (exists $information{borrowernumber} && !defined $information{borrowernumber}) { | ||||
531 | #passing mysql's kohaadmin?? Makes no sense as a query | ||||
532 | return; | ||||
533 | } | ||||
534 | my $dbh = C4::Context->dbh; | ||||
535 | my $select = | ||||
536 | q{SELECT borrowers.*, categories.category_type, categories.description | ||||
537 | FROM borrowers | ||||
538 | LEFT JOIN categories on borrowers.categorycode=categories.categorycode WHERE }; | ||||
539 | my $more_p = 0; | ||||
540 | my @values = (); | ||||
541 | for (keys %information ) { | ||||
542 | if ($more_p) { | ||||
543 | $select .= ' AND '; | ||||
544 | } | ||||
545 | else { | ||||
546 | $more_p++; | ||||
547 | } | ||||
548 | |||||
549 | if (defined $information{$_}) { | ||||
550 | $select .= "$_ = ?"; | ||||
551 | push @values, $information{$_}; | ||||
552 | } | ||||
553 | else { | ||||
554 | $select .= "$_ IS NULL"; | ||||
555 | } | ||||
556 | } | ||||
557 | $debug && warn $select, " ",values %information; | ||||
558 | my $sth = $dbh->prepare("$select"); | ||||
559 | $sth->execute(map{$information{$_}} keys %information); | ||||
560 | my $data = $sth->fetchall_arrayref({}); | ||||
561 | #FIXME interface to this routine now allows generation of a result set | ||||
562 | #so whole array should be returned but bowhere in the current code expects this | ||||
563 | if (@{$data} ) { | ||||
564 | return $data->[0]; | ||||
565 | } | ||||
566 | |||||
567 | return; | ||||
568 | } | ||||
569 | |||||
570 | =head2 GetMemberRelatives | ||||
571 | |||||
572 | @borrowernumbers = GetMemberRelatives($borrowernumber); | ||||
573 | |||||
574 | C<GetMemberRelatives> returns a borrowersnumber's list of guarantor/guarantees of the member given in parameter | ||||
575 | |||||
576 | =cut | ||||
577 | sub GetMemberRelatives { | ||||
578 | my $borrowernumber = shift; | ||||
579 | my $dbh = C4::Context->dbh; | ||||
580 | my @glist; | ||||
581 | |||||
582 | # Getting guarantor | ||||
583 | my $query = "SELECT guarantorid FROM borrowers WHERE borrowernumber=?"; | ||||
584 | my $sth = $dbh->prepare($query); | ||||
585 | $sth->execute($borrowernumber); | ||||
586 | my $data = $sth->fetchrow_arrayref(); | ||||
587 | push @glist, $data->[0] if $data->[0]; | ||||
588 | my $guarantor = $data->[0] ? $data->[0] : undef; | ||||
589 | |||||
590 | # Getting guarantees | ||||
591 | $query = "SELECT borrowernumber FROM borrowers WHERE guarantorid=?"; | ||||
592 | $sth = $dbh->prepare($query); | ||||
593 | $sth->execute($borrowernumber); | ||||
594 | while ($data = $sth->fetchrow_arrayref()) { | ||||
595 | push @glist, $data->[0]; | ||||
596 | } | ||||
597 | |||||
598 | # Getting sibling guarantees | ||||
599 | if ($guarantor) { | ||||
600 | $query = "SELECT borrowernumber FROM borrowers WHERE guarantorid=?"; | ||||
601 | $sth = $dbh->prepare($query); | ||||
602 | $sth->execute($guarantor); | ||||
603 | while ($data = $sth->fetchrow_arrayref()) { | ||||
604 | push @glist, $data->[0] if ($data->[0] != $borrowernumber); | ||||
605 | } | ||||
606 | } | ||||
607 | |||||
608 | return @glist; | ||||
609 | } | ||||
610 | |||||
611 | =head2 IsMemberBlocked | ||||
612 | |||||
613 | my ($block_status, $count) = IsMemberBlocked( $borrowernumber ); | ||||
614 | |||||
615 | Returns whether a patron has overdue items that may result | ||||
616 | in a block or whether the patron has active fine days | ||||
617 | that would block circulation privileges. | ||||
618 | |||||
619 | C<$block_status> can have the following values: | ||||
620 | |||||
621 | 1 if the patron has outstanding fine days, in which case C<$count> is the number of them | ||||
622 | |||||
623 | -1 if the patron has overdue items, in which case C<$count> is the number of them | ||||
624 | |||||
625 | 0 if the patron has no overdue items or outstanding fine days, in which case C<$count> is 0 | ||||
626 | |||||
627 | Outstanding fine days are checked before current overdue items | ||||
628 | are. | ||||
629 | |||||
630 | FIXME: this needs to be split into two functions; a potential block | ||||
631 | based on the number of current overdue items could be orthogonal | ||||
632 | to a block based on whether the patron has any fine days accrued. | ||||
633 | |||||
634 | =cut | ||||
635 | |||||
636 | sub IsMemberBlocked { | ||||
637 | my $borrowernumber = shift; | ||||
638 | my $dbh = C4::Context->dbh; | ||||
639 | |||||
640 | my $blockeddate = Koha::Borrower::Debarments::IsDebarred($borrowernumber); | ||||
641 | |||||
642 | return ( 1, $blockeddate ) if $blockeddate; | ||||
643 | |||||
644 | # if he have late issues | ||||
645 | my $sth = $dbh->prepare( | ||||
646 | "SELECT COUNT(*) as latedocs | ||||
647 | FROM issues | ||||
648 | WHERE borrowernumber = ? | ||||
649 | AND date_due < now()" | ||||
650 | ); | ||||
651 | $sth->execute($borrowernumber); | ||||
652 | my $latedocs = $sth->fetchrow_hashref->{'latedocs'}; | ||||
653 | |||||
654 | return ( -1, $latedocs ) if $latedocs > 0; | ||||
655 | |||||
656 | return ( 0, 0 ); | ||||
657 | } | ||||
658 | |||||
659 | =head2 GetMemberIssuesAndFines | ||||
660 | |||||
661 | ($overdue_count, $issue_count, $total_fines) = &GetMemberIssuesAndFines($borrowernumber); | ||||
662 | |||||
663 | Returns aggregate data about items borrowed by the patron with the | ||||
664 | given borrowernumber. | ||||
665 | |||||
666 | C<&GetMemberIssuesAndFines> returns a three-element array. C<$overdue_count> is the | ||||
667 | number of overdue items the patron currently has borrowed. C<$issue_count> is the | ||||
668 | number of books the patron currently has borrowed. C<$total_fines> is | ||||
669 | the total fine currently due by the borrower. | ||||
670 | |||||
671 | =cut | ||||
672 | |||||
673 | #' | ||||
674 | sub GetMemberIssuesAndFines { | ||||
675 | my ( $borrowernumber ) = @_; | ||||
676 | my $dbh = C4::Context->dbh; | ||||
677 | my $query = "SELECT COUNT(*) FROM issues WHERE borrowernumber = ?"; | ||||
678 | |||||
679 | $debug and warn $query."\n"; | ||||
680 | my $sth = $dbh->prepare($query); | ||||
681 | $sth->execute($borrowernumber); | ||||
682 | my $issue_count = $sth->fetchrow_arrayref->[0]; | ||||
683 | |||||
684 | $sth = $dbh->prepare( | ||||
685 | "SELECT COUNT(*) FROM issues | ||||
686 | WHERE borrowernumber = ? | ||||
687 | AND date_due < now()" | ||||
688 | ); | ||||
689 | $sth->execute($borrowernumber); | ||||
690 | my $overdue_count = $sth->fetchrow_arrayref->[0]; | ||||
691 | |||||
692 | $sth = $dbh->prepare("SELECT SUM(amountoutstanding) FROM accountlines WHERE borrowernumber = ?"); | ||||
693 | $sth->execute($borrowernumber); | ||||
694 | my $total_fines = $sth->fetchrow_arrayref->[0]; | ||||
695 | |||||
696 | return ($overdue_count, $issue_count, $total_fines); | ||||
697 | } | ||||
698 | |||||
699 | |||||
700 | =head2 columns | ||||
701 | |||||
702 | my @columns = C4::Member::columns(); | ||||
703 | |||||
704 | Returns an array of borrowers' table columns on success, | ||||
705 | and an empty array on failure. | ||||
706 | |||||
707 | =cut | ||||
708 | |||||
709 | sub columns { | ||||
710 | |||||
711 | # Pure ANSI SQL goodness. | ||||
712 | my $sql = 'SELECT * FROM borrowers WHERE 1=0;'; | ||||
713 | |||||
714 | # Get the database handle. | ||||
715 | my $dbh = C4::Context->dbh; | ||||
716 | |||||
717 | # Run the SQL statement to load STH's readonly properties. | ||||
718 | my $sth = $dbh->prepare($sql); | ||||
719 | my $rv = $sth->execute(); | ||||
720 | |||||
721 | # This only fails if the table doesn't exist. | ||||
722 | # This will always be called AFTER an install or upgrade, | ||||
723 | # so borrowers will exist! | ||||
724 | my @data; | ||||
725 | if ($sth->{NUM_OF_FIELDS}>0) { | ||||
726 | @data = @{$sth->{NAME}}; | ||||
727 | } | ||||
728 | else { | ||||
729 | @data = (); | ||||
730 | } | ||||
731 | return @data; | ||||
732 | } | ||||
733 | |||||
734 | |||||
735 | =head2 ModMember | ||||
736 | |||||
737 | my $success = ModMember(borrowernumber => $borrowernumber, | ||||
738 | [ field => value ]... ); | ||||
739 | |||||
740 | Modify borrower's data. All date fields should ALREADY be in ISO format. | ||||
741 | |||||
742 | return : | ||||
743 | true on success, or false on failure | ||||
744 | |||||
745 | =cut | ||||
746 | |||||
747 | sub ModMember { | ||||
748 | my (%data) = @_; | ||||
749 | # test to know if you must update or not the borrower password | ||||
750 | if (exists $data{password}) { | ||||
751 | if ($data{password} eq '****' or $data{password} eq '') { | ||||
752 | delete $data{password}; | ||||
753 | } else { | ||||
754 | $data{password} = hash_password($data{password}); | ||||
755 | } | ||||
756 | } | ||||
757 | my $old_categorycode = GetBorrowerCategorycode( $data{borrowernumber} ); | ||||
758 | my $execute_success=UpdateInTable("borrowers",\%data); | ||||
759 | if ($execute_success) { # only proceed if the update was a success | ||||
760 | # ok if its an adult (type) it may have borrowers that depend on it as a guarantor | ||||
761 | # so when we update information for an adult we should check for guarantees and update the relevant part | ||||
762 | # of their records, ie addresses and phone numbers | ||||
763 | my $borrowercategory= GetBorrowercategory( $data{'category_type'} ); | ||||
764 | if ( exists $borrowercategory->{'category_type'} && $borrowercategory->{'category_type'} eq ('A' || 'S') ) { | ||||
765 | # is adult check guarantees; | ||||
766 | UpdateGuarantees(%data); | ||||
767 | } | ||||
768 | |||||
769 | # If the patron changes to a category with enrollment fee, we add a fee | ||||
770 | if ( $data{categorycode} and $data{categorycode} ne $old_categorycode ) { | ||||
771 | AddEnrolmentFeeIfNeeded( $data{categorycode}, $data{borrowernumber} ); | ||||
772 | } | ||||
773 | |||||
774 | logaction("MEMBERS", "MODIFY", $data{'borrowernumber'}, "UPDATE (executed w/ arg: $data{'borrowernumber'})") if C4::Context->preference("BorrowersLog"); | ||||
775 | } | ||||
776 | return $execute_success; | ||||
777 | } | ||||
778 | |||||
779 | =head2 AddMember | ||||
780 | |||||
781 | $borrowernumber = &AddMember(%borrower); | ||||
782 | |||||
783 | insert new borrower into table | ||||
784 | Returns the borrowernumber upon success | ||||
785 | |||||
786 | Returns as undef upon any db error without further processing | ||||
787 | |||||
788 | =cut | ||||
789 | |||||
790 | #' | ||||
791 | sub AddMember { | ||||
792 | my (%data) = @_; | ||||
793 | my $dbh = C4::Context->dbh; | ||||
794 | |||||
795 | # generate a proper login if none provided | ||||
796 | $data{'userid'} = Generate_Userid($data{'borrowernumber'}, $data{'firstname'}, $data{'surname'}) if $data{'userid'} eq ''; | ||||
797 | |||||
798 | # add expiration date if it isn't already there | ||||
799 | unless ( $data{'dateexpiry'} ) { | ||||
800 | $data{'dateexpiry'} = GetExpiryDate( $data{'categorycode'}, C4::Dates->new()->output("iso") ); | ||||
801 | } | ||||
802 | |||||
803 | # add enrollment date if it isn't already there | ||||
804 | unless ( $data{'dateenrolled'} ) { | ||||
805 | $data{'dateenrolled'} = C4::Dates->new()->output("iso"); | ||||
806 | } | ||||
807 | |||||
808 | # create a disabled account if no password provided | ||||
809 | $data{'password'} = ($data{'password'})? hash_password($data{'password'}) : '!'; | ||||
810 | $data{'borrowernumber'}=InsertInTable("borrowers",\%data); | ||||
811 | |||||
812 | # mysql_insertid is probably bad. not necessarily accurate and mysql-specific at best. | ||||
813 | logaction("MEMBERS", "CREATE", $data{'borrowernumber'}, "") if C4::Context->preference("BorrowersLog"); | ||||
814 | |||||
815 | AddEnrolmentFeeIfNeeded( $data{categorycode}, $data{borrowernumber} ); | ||||
816 | |||||
817 | return $data{'borrowernumber'}; | ||||
818 | } | ||||
819 | |||||
820 | =head2 Check_Userid | ||||
821 | |||||
822 | my $uniqueness = Check_Userid($userid,$borrowernumber); | ||||
823 | |||||
824 | $borrowernumber is optional (i.e. it can contain a blank value). If $userid is passed with a blank $borrowernumber variable, the database will be checked for all instances of that userid (i.e. userid=? AND borrowernumber != ''). | ||||
825 | |||||
826 | If $borrowernumber is provided, the database will be checked for every instance of that userid coupled with a different borrower(number) than the one provided. | ||||
827 | |||||
828 | return : | ||||
829 | 0 for not unique (i.e. this $userid already exists) | ||||
830 | 1 for unique (i.e. this $userid does not exist, or this $userid/$borrowernumber combination already exists) | ||||
831 | |||||
832 | =cut | ||||
833 | |||||
834 | sub Check_Userid { | ||||
835 | my ($uid,$member) = @_; | ||||
836 | my $dbh = C4::Context->dbh; | ||||
837 | my $sth = | ||||
838 | $dbh->prepare( | ||||
839 | "SELECT * FROM borrowers WHERE userid=? AND borrowernumber != ?"); | ||||
840 | $sth->execute( $uid, $member ); | ||||
841 | if ( ( $uid ne '' ) && ( my $row = $sth->fetchrow_hashref ) ) { | ||||
842 | return 0; | ||||
843 | } | ||||
844 | else { | ||||
845 | return 1; | ||||
846 | } | ||||
847 | } | ||||
848 | |||||
849 | =head2 Generate_Userid | ||||
850 | |||||
851 | my $newuid = Generate_Userid($borrowernumber, $firstname, $surname); | ||||
852 | |||||
853 | Generate a userid using the $surname and the $firstname (if there is a value in $firstname). | ||||
854 | |||||
855 | $borrowernumber is optional (i.e. it can contain a blank value). A value is passed when generating a new userid for an existing borrower. When a new userid is created for a new borrower, a blank value is passed to this sub. | ||||
856 | |||||
857 | return : | ||||
858 | new userid ($firstname.$surname if there is a $firstname, or $surname if there is no value in $firstname) plus offset (0 if the $newuid is unique, or a higher numeric value if Check_Userid finds an existing match for the $newuid in the database). | ||||
859 | |||||
860 | =cut | ||||
861 | |||||
862 | sub Generate_Userid { | ||||
863 | my ($borrowernumber, $firstname, $surname) = @_; | ||||
864 | my $newuid; | ||||
865 | my $offset = 0; | ||||
866 | #The script will "do" the following code and increment the $offset until Check_Userid = 1 (i.e. until $newuid comes back as unique) | ||||
867 | do { | ||||
868 | $firstname =~ s/[[:digit:][:space:][:blank:][:punct:][:cntrl:]]//g; | ||||
869 | $surname =~ s/[[:digit:][:space:][:blank:][:punct:][:cntrl:]]//g; | ||||
870 | $newuid = lc(($firstname)? "$firstname.$surname" : $surname); | ||||
871 | $newuid = unac_string('utf-8',$newuid); | ||||
872 | $newuid .= $offset unless $offset == 0; | ||||
873 | $offset++; | ||||
874 | |||||
875 | } while (!Check_Userid($newuid,$borrowernumber)); | ||||
876 | |||||
877 | return $newuid; | ||||
878 | } | ||||
879 | |||||
880 | sub changepassword { | ||||
881 | my ( $uid, $member, $digest ) = @_; | ||||
882 | my $dbh = C4::Context->dbh; | ||||
883 | |||||
884 | #Make sure the userid chosen is unique and not theirs if non-empty. If it is not, | ||||
885 | #Then we need to tell the user and have them create a new one. | ||||
886 | my $resultcode; | ||||
887 | my $sth = | ||||
888 | $dbh->prepare( | ||||
889 | "SELECT * FROM borrowers WHERE userid=? AND borrowernumber != ?"); | ||||
890 | $sth->execute( $uid, $member ); | ||||
891 | if ( ( $uid ne '' ) && ( my $row = $sth->fetchrow_hashref ) ) { | ||||
892 | $resultcode=0; | ||||
893 | } | ||||
894 | else { | ||||
895 | #Everything is good so we can update the information. | ||||
896 | $sth = | ||||
897 | $dbh->prepare( | ||||
898 | "update borrowers set userid=?, password=? where borrowernumber=?"); | ||||
899 | $sth->execute( $uid, $digest, $member ); | ||||
900 | $resultcode=1; | ||||
901 | } | ||||
902 | |||||
903 | logaction("MEMBERS", "CHANGE PASS", $member, "") if C4::Context->preference("BorrowersLog"); | ||||
904 | return $resultcode; | ||||
905 | } | ||||
906 | |||||
- - | |||||
909 | =head2 fixup_cardnumber | ||||
910 | |||||
911 | Warning: The caller is responsible for locking the members table in write | ||||
912 | mode, to avoid database corruption. | ||||
913 | |||||
914 | =cut | ||||
915 | |||||
916 | 2 | 5.16ms | 2 | 55µs | # spent 33µs (11+22) within C4::Members::BEGIN@916 which was called:
# once (11µs+22µs) by main::BEGIN@73 at line 916 # spent 33µs making 1 call to C4::Members::BEGIN@916
# spent 22µs making 1 call to vars::import |
917 | 1 | 1µs | my @weightings = ( 8, 4, 6, 3, 5, 2, 1 ); | ||
918 | |||||
919 | sub fixup_cardnumber { | ||||
920 | my ($cardnumber) = @_; | ||||
921 | my $autonumber_members = C4::Context->boolean_preference('autoMemberNum') || 0; | ||||
922 | |||||
923 | # Find out whether member numbers should be generated | ||||
924 | # automatically. Should be either "1" or something else. | ||||
925 | # Defaults to "0", which is interpreted as "no". | ||||
926 | |||||
927 | # if ($cardnumber !~ /\S/ && $autonumber_members) { | ||||
928 | ($autonumber_members) or return $cardnumber; | ||||
929 | my $checkdigit = C4::Context->preference('checkdigit'); | ||||
930 | my $dbh = C4::Context->dbh; | ||||
931 | if ( $checkdigit and $checkdigit eq 'katipo' ) { | ||||
932 | |||||
933 | # if checkdigit is selected, calculate katipo-style cardnumber. | ||||
934 | # otherwise, just use the max() | ||||
935 | # purpose: generate checksum'd member numbers. | ||||
936 | # We'll assume we just got the max value of digits 2-8 of member #'s | ||||
937 | # from the database and our job is to increment that by one, | ||||
938 | # determine the 1st and 9th digits and return the full string. | ||||
939 | my $sth = $dbh->prepare( | ||||
940 | "select max(substring(borrowers.cardnumber,2,7)) as new_num from borrowers" | ||||
941 | ); | ||||
942 | $sth->execute; | ||||
943 | my $data = $sth->fetchrow_hashref; | ||||
944 | $cardnumber = $data->{new_num}; | ||||
945 | if ( !$cardnumber ) { # If DB has no values, | ||||
946 | $cardnumber = 1000000; # start at 1000000 | ||||
947 | } else { | ||||
948 | $cardnumber += 1; | ||||
949 | } | ||||
950 | |||||
951 | my $sum = 0; | ||||
952 | for ( my $i = 0 ; $i < 8 ; $i += 1 ) { | ||||
953 | # read weightings, left to right, 1 char at a time | ||||
954 | my $temp1 = $weightings[$i]; | ||||
955 | |||||
956 | # sequence left to right, 1 char at a time | ||||
957 | my $temp2 = substr( $cardnumber, $i, 1 ); | ||||
958 | |||||
959 | # mult each char 1-7 by its corresponding weighting | ||||
960 | $sum += $temp1 * $temp2; | ||||
961 | } | ||||
962 | |||||
963 | my $rem = ( $sum % 11 ); | ||||
964 | $rem = 'X' if $rem == 10; | ||||
965 | |||||
966 | return "V$cardnumber$rem"; | ||||
967 | } else { | ||||
968 | |||||
969 | my $sth = $dbh->prepare( | ||||
970 | 'SELECT MAX( CAST( cardnumber AS SIGNED ) ) FROM borrowers WHERE cardnumber REGEXP "^-?[0-9]+$"' | ||||
971 | ); | ||||
972 | $sth->execute; | ||||
973 | my ($result) = $sth->fetchrow; | ||||
974 | return $result + 1; | ||||
975 | } | ||||
976 | return $cardnumber; # just here as a fallback/reminder | ||||
977 | } | ||||
978 | |||||
979 | =head2 GetGuarantees | ||||
980 | |||||
981 | ($num_children, $children_arrayref) = &GetGuarantees($parent_borrno); | ||||
982 | $child0_cardno = $children_arrayref->[0]{"cardnumber"}; | ||||
983 | $child0_borrno = $children_arrayref->[0]{"borrowernumber"}; | ||||
984 | |||||
985 | C<&GetGuarantees> takes a borrower number (e.g., that of a patron | ||||
986 | with children) and looks up the borrowers who are guaranteed by that | ||||
987 | borrower (i.e., the patron's children). | ||||
988 | |||||
989 | C<&GetGuarantees> returns two values: an integer giving the number of | ||||
990 | borrowers guaranteed by C<$parent_borrno>, and a reference to an array | ||||
991 | of references to hash, which gives the actual results. | ||||
992 | |||||
993 | =cut | ||||
994 | |||||
995 | #' | ||||
996 | sub GetGuarantees { | ||||
997 | my ($borrowernumber) = @_; | ||||
998 | my $dbh = C4::Context->dbh; | ||||
999 | my $sth = | ||||
1000 | $dbh->prepare( | ||||
1001 | "select cardnumber,borrowernumber, firstname, surname from borrowers where guarantorid=?" | ||||
1002 | ); | ||||
1003 | $sth->execute($borrowernumber); | ||||
1004 | |||||
1005 | my @dat; | ||||
1006 | my $data = $sth->fetchall_arrayref({}); | ||||
1007 | return ( scalar(@$data), $data ); | ||||
1008 | } | ||||
1009 | |||||
1010 | =head2 UpdateGuarantees | ||||
1011 | |||||
1012 | &UpdateGuarantees($parent_borrno); | ||||
1013 | |||||
1014 | |||||
1015 | C<&UpdateGuarantees> borrower data for an adult and updates all the guarantees | ||||
1016 | with the modified information | ||||
1017 | |||||
1018 | =cut | ||||
1019 | |||||
1020 | #' | ||||
1021 | sub UpdateGuarantees { | ||||
1022 | my %data = shift; | ||||
1023 | my $dbh = C4::Context->dbh; | ||||
1024 | my ( $count, $guarantees ) = GetGuarantees( $data{'borrowernumber'} ); | ||||
1025 | foreach my $guarantee (@$guarantees){ | ||||
1026 | my $guaquery = qq|UPDATE borrowers | ||||
1027 | SET address=?,fax=?,B_city=?,mobile=?,city=?,phone=? | ||||
1028 | WHERE borrowernumber=? | ||||
1029 | |; | ||||
1030 | my $sth = $dbh->prepare($guaquery); | ||||
1031 | $sth->execute($data{'address'},$data{'fax'},$data{'B_city'},$data{'mobile'},$data{'city'},$data{'phone'},$guarantee->{'borrowernumber'}); | ||||
1032 | } | ||||
1033 | } | ||||
1034 | =head2 GetPendingIssues | ||||
1035 | |||||
1036 | my $issues = &GetPendingIssues(@borrowernumber); | ||||
1037 | |||||
1038 | Looks up what the patron with the given borrowernumber has borrowed. | ||||
1039 | |||||
1040 | C<&GetPendingIssues> returns a | ||||
1041 | reference-to-array where each element is a reference-to-hash; the | ||||
1042 | keys are the fields from the C<issues>, C<biblio>, and C<items> tables. | ||||
1043 | The keys include C<biblioitems> fields except marc and marcxml. | ||||
1044 | |||||
1045 | =cut | ||||
1046 | |||||
1047 | #' | ||||
1048 | sub GetPendingIssues { | ||||
1049 | my @borrowernumbers = @_; | ||||
1050 | |||||
1051 | unless (@borrowernumbers ) { # return a ref_to_array | ||||
1052 | return \@borrowernumbers; # to not cause surprise to caller | ||||
1053 | } | ||||
1054 | |||||
1055 | # Borrowers part of the query | ||||
1056 | my $bquery = ''; | ||||
1057 | for (my $i = 0; $i < @borrowernumbers; $i++) { | ||||
1058 | $bquery .= ' issues.borrowernumber = ?'; | ||||
1059 | if ($i < $#borrowernumbers ) { | ||||
1060 | $bquery .= ' OR'; | ||||
1061 | } | ||||
1062 | } | ||||
1063 | |||||
1064 | # must avoid biblioitems.* to prevent large marc and marcxml fields from killing performance | ||||
1065 | # FIXME: namespace collision: each table has "timestamp" fields. Which one is "timestamp" ? | ||||
1066 | # FIXME: circ/ciculation.pl tries to sort by timestamp! | ||||
1067 | # FIXME: namespace collision: other collisions possible. | ||||
1068 | # FIXME: most of this data isn't really being used by callers. | ||||
1069 | my $query = | ||||
1070 | "SELECT issues.*, | ||||
1071 | items.*, | ||||
1072 | biblio.*, | ||||
1073 | biblioitems.volume, | ||||
1074 | biblioitems.number, | ||||
1075 | biblioitems.itemtype, | ||||
1076 | biblioitems.isbn, | ||||
1077 | biblioitems.issn, | ||||
1078 | biblioitems.publicationyear, | ||||
1079 | biblioitems.publishercode, | ||||
1080 | biblioitems.volumedate, | ||||
1081 | biblioitems.volumedesc, | ||||
1082 | biblioitems.lccn, | ||||
1083 | biblioitems.url, | ||||
1084 | borrowers.firstname, | ||||
1085 | borrowers.surname, | ||||
1086 | borrowers.cardnumber, | ||||
1087 | issues.timestamp AS timestamp, | ||||
1088 | issues.renewals AS renewals, | ||||
1089 | issues.borrowernumber AS borrowernumber, | ||||
1090 | items.renewals AS totalrenewals | ||||
1091 | FROM issues | ||||
1092 | LEFT JOIN items ON items.itemnumber = issues.itemnumber | ||||
1093 | LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber | ||||
1094 | LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber | ||||
1095 | LEFT JOIN borrowers ON issues.borrowernumber = borrowers.borrowernumber | ||||
1096 | WHERE | ||||
1097 | $bquery | ||||
1098 | ORDER BY issues.issuedate" | ||||
1099 | ; | ||||
1100 | |||||
1101 | my $sth = C4::Context->dbh->prepare($query); | ||||
1102 | $sth->execute(@borrowernumbers); | ||||
1103 | my $data = $sth->fetchall_arrayref({}); | ||||
1104 | my $tz = C4::Context->tz(); | ||||
1105 | my $today = DateTime->now( time_zone => $tz); | ||||
1106 | foreach (@{$data}) { | ||||
1107 | if ($_->{issuedate}) { | ||||
1108 | $_->{issuedate} = dt_from_string($_->{issuedate}, 'sql'); | ||||
1109 | } | ||||
1110 | $_->{date_due} or next; | ||||
1111 | $_->{date_due} = DateTime::Format::DateParse->parse_datetime($_->{date_due}, $tz->name()); | ||||
1112 | if ( DateTime->compare($_->{date_due}, $today) == -1 ) { | ||||
1113 | $_->{overdue} = 1; | ||||
1114 | } | ||||
1115 | } | ||||
1116 | return $data; | ||||
1117 | } | ||||
1118 | |||||
1119 | =head2 GetAllIssues | ||||
1120 | |||||
1121 | $issues = &GetAllIssues($borrowernumber, $sortkey, $limit); | ||||
1122 | |||||
1123 | Looks up what the patron with the given borrowernumber has borrowed, | ||||
1124 | and sorts the results. | ||||
1125 | |||||
1126 | C<$sortkey> is the name of a field on which to sort the results. This | ||||
1127 | should be the name of a field in the C<issues>, C<biblio>, | ||||
1128 | C<biblioitems>, or C<items> table in the Koha database. | ||||
1129 | |||||
1130 | C<$limit> is the maximum number of results to return. | ||||
1131 | |||||
1132 | C<&GetAllIssues> an arrayref, C<$issues>, of hashrefs, the keys of which | ||||
1133 | are the fields from the C<issues>, C<biblio>, C<biblioitems>, and | ||||
1134 | C<items> tables of the Koha database. | ||||
1135 | |||||
1136 | =cut | ||||
1137 | |||||
1138 | #' | ||||
1139 | sub GetAllIssues { | ||||
1140 | my ( $borrowernumber, $order, $limit ) = @_; | ||||
1141 | |||||
1142 | my $dbh = C4::Context->dbh; | ||||
1143 | my $query = | ||||
1144 | 'SELECT *, issues.timestamp as issuestimestamp, issues.renewals AS renewals,items.renewals AS totalrenewals,items.timestamp AS itemstimestamp | ||||
1145 | FROM issues | ||||
1146 | LEFT JOIN items on items.itemnumber=issues.itemnumber | ||||
1147 | LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber | ||||
1148 | LEFT JOIN biblioitems ON items.biblioitemnumber=biblioitems.biblioitemnumber | ||||
1149 | WHERE borrowernumber=? | ||||
1150 | UNION ALL | ||||
1151 | SELECT *, old_issues.timestamp as issuestimestamp, old_issues.renewals AS renewals,items.renewals AS totalrenewals,items.timestamp AS itemstimestamp | ||||
1152 | FROM old_issues | ||||
1153 | LEFT JOIN items on items.itemnumber=old_issues.itemnumber | ||||
1154 | LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber | ||||
1155 | LEFT JOIN biblioitems ON items.biblioitemnumber=biblioitems.biblioitemnumber | ||||
1156 | WHERE borrowernumber=? AND old_issues.itemnumber IS NOT NULL | ||||
1157 | order by ' . $order; | ||||
1158 | if ($limit) { | ||||
1159 | $query .= " limit $limit"; | ||||
1160 | } | ||||
1161 | |||||
1162 | my $sth = $dbh->prepare($query); | ||||
1163 | $sth->execute( $borrowernumber, $borrowernumber ); | ||||
1164 | return $sth->fetchall_arrayref( {} ); | ||||
1165 | } | ||||
1166 | |||||
1167 | |||||
1168 | =head2 GetMemberAccountRecords | ||||
1169 | |||||
1170 | ($total, $acctlines, $count) = &GetMemberAccountRecords($borrowernumber); | ||||
1171 | |||||
1172 | Looks up accounting data for the patron with the given borrowernumber. | ||||
1173 | |||||
1174 | C<&GetMemberAccountRecords> returns a three-element array. C<$acctlines> is a | ||||
1175 | reference-to-array, where each element is a reference-to-hash; the | ||||
1176 | keys are the fields of the C<accountlines> table in the Koha database. | ||||
1177 | C<$count> is the number of elements in C<$acctlines>. C<$total> is the | ||||
1178 | total amount outstanding for all of the account lines. | ||||
1179 | |||||
1180 | =cut | ||||
1181 | |||||
1182 | sub GetMemberAccountRecords { | ||||
1183 | my ($borrowernumber) = @_; | ||||
1184 | my $dbh = C4::Context->dbh; | ||||
1185 | my @acctlines; | ||||
1186 | my $numlines = 0; | ||||
1187 | my $strsth = qq( | ||||
1188 | SELECT * | ||||
1189 | FROM accountlines | ||||
1190 | WHERE borrowernumber=?); | ||||
1191 | $strsth.=" ORDER BY date desc,timestamp DESC"; | ||||
1192 | my $sth= $dbh->prepare( $strsth ); | ||||
1193 | $sth->execute( $borrowernumber ); | ||||
1194 | |||||
1195 | my $total = 0; | ||||
1196 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
1197 | if ( $data->{itemnumber} ) { | ||||
1198 | my $biblio = GetBiblioFromItemNumber( $data->{itemnumber} ); | ||||
1199 | $data->{biblionumber} = $biblio->{biblionumber}; | ||||
1200 | $data->{title} = $biblio->{title}; | ||||
1201 | } | ||||
1202 | $acctlines[$numlines] = $data; | ||||
1203 | $numlines++; | ||||
1204 | $total += int(1000 * $data->{'amountoutstanding'}); # convert float to integer to avoid round-off errors | ||||
1205 | } | ||||
1206 | $total /= 1000; | ||||
1207 | return ( $total, \@acctlines,$numlines); | ||||
1208 | } | ||||
1209 | |||||
1210 | =head2 GetMemberAccountBalance | ||||
1211 | |||||
1212 | ($total_balance, $non_issue_balance, $other_charges) = &GetMemberAccountBalance($borrowernumber); | ||||
1213 | |||||
1214 | Calculates amount immediately owing by the patron - non-issue charges. | ||||
1215 | Based on GetMemberAccountRecords. | ||||
1216 | Charges exempt from non-issue are: | ||||
1217 | * Res (reserves) | ||||
1218 | * Rent (rental) if RentalsInNoissuesCharge syspref is set to false | ||||
1219 | * Manual invoices if ManInvInNoissuesCharge syspref is set to false | ||||
1220 | |||||
1221 | =cut | ||||
1222 | |||||
1223 | sub GetMemberAccountBalance { | ||||
1224 | my ($borrowernumber) = @_; | ||||
1225 | |||||
1226 | my $ACCOUNT_TYPE_LENGTH = 5; # this is plain ridiculous... | ||||
1227 | |||||
1228 | my @not_fines = ('Res'); | ||||
1229 | push @not_fines, 'Rent' unless C4::Context->preference('RentalsInNoissuesCharge'); | ||||
1230 | unless ( C4::Context->preference('ManInvInNoissuesCharge') ) { | ||||
1231 | my $dbh = C4::Context->dbh; | ||||
1232 | my $man_inv_types = $dbh->selectcol_arrayref(qq{SELECT authorised_value FROM authorised_values WHERE category = 'MANUAL_INV'}); | ||||
1233 | push @not_fines, map substr($_, 0, $ACCOUNT_TYPE_LENGTH), @$man_inv_types; | ||||
1234 | } | ||||
1235 | my %not_fine = map {$_ => 1} @not_fines; | ||||
1236 | |||||
1237 | my ($total, $acctlines) = GetMemberAccountRecords($borrowernumber); | ||||
1238 | my $other_charges = 0; | ||||
1239 | foreach (@$acctlines) { | ||||
1240 | $other_charges += $_->{amountoutstanding} if $not_fine{ substr($_->{accounttype}, 0, $ACCOUNT_TYPE_LENGTH) }; | ||||
1241 | } | ||||
1242 | |||||
1243 | return ( $total, $total - $other_charges, $other_charges); | ||||
1244 | } | ||||
1245 | |||||
1246 | =head2 GetBorNotifyAcctRecord | ||||
1247 | |||||
1248 | ($total, $acctlines, $count) = &GetBorNotifyAcctRecord($params,$notifyid); | ||||
1249 | |||||
1250 | Looks up accounting data for the patron with the given borrowernumber per file number. | ||||
1251 | |||||
1252 | C<&GetBorNotifyAcctRecord> returns a three-element array. C<$acctlines> is a | ||||
1253 | reference-to-array, where each element is a reference-to-hash; the | ||||
1254 | keys are the fields of the C<accountlines> table in the Koha database. | ||||
1255 | C<$count> is the number of elements in C<$acctlines>. C<$total> is the | ||||
1256 | total amount outstanding for all of the account lines. | ||||
1257 | |||||
1258 | =cut | ||||
1259 | |||||
1260 | sub GetBorNotifyAcctRecord { | ||||
1261 | my ( $borrowernumber, $notifyid ) = @_; | ||||
1262 | my $dbh = C4::Context->dbh; | ||||
1263 | my @acctlines; | ||||
1264 | my $numlines = 0; | ||||
1265 | my $sth = $dbh->prepare( | ||||
1266 | "SELECT * | ||||
1267 | FROM accountlines | ||||
1268 | WHERE borrowernumber=? | ||||
1269 | AND notify_id=? | ||||
1270 | AND amountoutstanding != '0' | ||||
1271 | ORDER BY notify_id,accounttype | ||||
1272 | "); | ||||
1273 | |||||
1274 | $sth->execute( $borrowernumber, $notifyid ); | ||||
1275 | my $total = 0; | ||||
1276 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
1277 | if ( $data->{itemnumber} ) { | ||||
1278 | my $biblio = GetBiblioFromItemNumber( $data->{itemnumber} ); | ||||
1279 | $data->{biblionumber} = $biblio->{biblionumber}; | ||||
1280 | $data->{title} = $biblio->{title}; | ||||
1281 | } | ||||
1282 | $acctlines[$numlines] = $data; | ||||
1283 | $numlines++; | ||||
1284 | $total += int(100 * $data->{'amountoutstanding'}); | ||||
1285 | } | ||||
1286 | $total /= 100; | ||||
1287 | return ( $total, \@acctlines, $numlines ); | ||||
1288 | } | ||||
1289 | |||||
1290 | =head2 checkuniquemember (OUEST-PROVENCE) | ||||
1291 | |||||
1292 | ($result,$categorycode) = &checkuniquemember($collectivity,$surname,$firstname,$dateofbirth); | ||||
1293 | |||||
1294 | Checks that a member exists or not in the database. | ||||
1295 | |||||
1296 | C<&result> is nonzero (=exist) or 0 (=does not exist) | ||||
1297 | C<&categorycode> is from categorycode table | ||||
1298 | C<&collectivity> is 1 (= we add a collectivity) or 0 (= we add a physical member) | ||||
1299 | C<&surname> is the surname | ||||
1300 | C<&firstname> is the firstname (only if collectivity=0) | ||||
1301 | C<&dateofbirth> is the date of birth in ISO format (only if collectivity=0) | ||||
1302 | |||||
1303 | =cut | ||||
1304 | |||||
1305 | # FIXME: This function is not legitimate. Multiple patrons might have the same first/last name and birthdate. | ||||
1306 | # This is especially true since first name is not even a required field. | ||||
1307 | |||||
1308 | sub checkuniquemember { | ||||
1309 | my ( $collectivity, $surname, $firstname, $dateofbirth ) = @_; | ||||
1310 | my $dbh = C4::Context->dbh; | ||||
1311 | my $request = ($collectivity) ? | ||||
1312 | "SELECT borrowernumber,categorycode FROM borrowers WHERE surname=? " : | ||||
1313 | ($dateofbirth) ? | ||||
1314 | "SELECT borrowernumber,categorycode FROM borrowers WHERE surname=? and firstname=? and dateofbirth=?" : | ||||
1315 | "SELECT borrowernumber,categorycode FROM borrowers WHERE surname=? and firstname=?"; | ||||
1316 | my $sth = $dbh->prepare($request); | ||||
1317 | if ($collectivity) { | ||||
1318 | $sth->execute( uc($surname) ); | ||||
1319 | } elsif($dateofbirth){ | ||||
1320 | $sth->execute( uc($surname), ucfirst($firstname), $dateofbirth ); | ||||
1321 | }else{ | ||||
1322 | $sth->execute( uc($surname), ucfirst($firstname)); | ||||
1323 | } | ||||
1324 | my @data = $sth->fetchrow; | ||||
1325 | ( $data[0] ) and return $data[0], $data[1]; | ||||
1326 | return 0; | ||||
1327 | } | ||||
1328 | |||||
1329 | sub checkcardnumber { | ||||
1330 | my ($cardnumber,$borrowernumber) = @_; | ||||
1331 | # If cardnumber is null, we assume they're allowed. | ||||
1332 | return 0 if !defined($cardnumber); | ||||
1333 | my $dbh = C4::Context->dbh; | ||||
1334 | my $query = "SELECT * FROM borrowers WHERE cardnumber=?"; | ||||
1335 | $query .= " AND borrowernumber <> ?" if ($borrowernumber); | ||||
1336 | my $sth = $dbh->prepare($query); | ||||
1337 | if ($borrowernumber) { | ||||
1338 | $sth->execute($cardnumber,$borrowernumber); | ||||
1339 | } else { | ||||
1340 | $sth->execute($cardnumber); | ||||
1341 | } | ||||
1342 | if (my $data= $sth->fetchrow_hashref()){ | ||||
1343 | return 1; | ||||
1344 | } | ||||
1345 | else { | ||||
1346 | return 0; | ||||
1347 | } | ||||
1348 | } | ||||
1349 | |||||
1350 | |||||
1351 | =head2 getzipnamecity (OUEST-PROVENCE) | ||||
1352 | |||||
1353 | take all info from table city for the fields city and zip | ||||
1354 | check for the name and the zip code of the city selected | ||||
1355 | |||||
1356 | =cut | ||||
1357 | |||||
1358 | sub getzipnamecity { | ||||
1359 | my ($cityid) = @_; | ||||
1360 | my $dbh = C4::Context->dbh; | ||||
1361 | my $sth = | ||||
1362 | $dbh->prepare( | ||||
1363 | "select city_name,city_state,city_zipcode,city_country from cities where cityid=? "); | ||||
1364 | $sth->execute($cityid); | ||||
1365 | my @data = $sth->fetchrow; | ||||
1366 | return $data[0], $data[1], $data[2], $data[3]; | ||||
1367 | } | ||||
1368 | |||||
1369 | |||||
1370 | =head2 getdcity (OUEST-PROVENCE) | ||||
1371 | |||||
1372 | recover cityid with city_name condition | ||||
1373 | |||||
1374 | =cut | ||||
1375 | |||||
1376 | sub getidcity { | ||||
1377 | my ($city_name) = @_; | ||||
1378 | my $dbh = C4::Context->dbh; | ||||
1379 | my $sth = $dbh->prepare("select cityid from cities where city_name=? "); | ||||
1380 | $sth->execute($city_name); | ||||
1381 | my $data = $sth->fetchrow; | ||||
1382 | return $data; | ||||
1383 | } | ||||
1384 | |||||
1385 | =head2 GetFirstValidEmailAddress | ||||
1386 | |||||
1387 | $email = GetFirstValidEmailAddress($borrowernumber); | ||||
1388 | |||||
1389 | Return the first valid email address for a borrower, given the borrowernumber. For now, the order | ||||
1390 | is defined as email, emailpro, B_email. Returns the empty string if the borrower has no email | ||||
1391 | addresses. | ||||
1392 | |||||
1393 | =cut | ||||
1394 | |||||
1395 | sub GetFirstValidEmailAddress { | ||||
1396 | my $borrowernumber = shift; | ||||
1397 | my $dbh = C4::Context->dbh; | ||||
1398 | my $sth = $dbh->prepare( "SELECT email, emailpro, B_email FROM borrowers where borrowernumber = ? "); | ||||
1399 | $sth->execute( $borrowernumber ); | ||||
1400 | my $data = $sth->fetchrow_hashref; | ||||
1401 | |||||
1402 | if ($data->{'email'}) { | ||||
1403 | return $data->{'email'}; | ||||
1404 | } elsif ($data->{'emailpro'}) { | ||||
1405 | return $data->{'emailpro'}; | ||||
1406 | } elsif ($data->{'B_email'}) { | ||||
1407 | return $data->{'B_email'}; | ||||
1408 | } else { | ||||
1409 | return ''; | ||||
1410 | } | ||||
1411 | } | ||||
1412 | |||||
1413 | =head2 GetNoticeEmailAddress | ||||
1414 | |||||
1415 | $email = GetNoticeEmailAddress($borrowernumber); | ||||
1416 | |||||
1417 | Return the email address of borrower used for notices, given the borrowernumber. | ||||
1418 | Returns the empty string if no email address. | ||||
1419 | |||||
1420 | =cut | ||||
1421 | |||||
1422 | sub GetNoticeEmailAddress { | ||||
1423 | my $borrowernumber = shift; | ||||
1424 | |||||
1425 | my $which_address = C4::Context->preference("AutoEmailPrimaryAddress"); | ||||
1426 | # if syspref is set to 'first valid' (value == OFF), look up email address | ||||
1427 | if ( $which_address eq 'OFF' ) { | ||||
1428 | return GetFirstValidEmailAddress($borrowernumber); | ||||
1429 | } | ||||
1430 | # specified email address field | ||||
1431 | my $dbh = C4::Context->dbh; | ||||
1432 | my $sth = $dbh->prepare( qq{ | ||||
1433 | SELECT $which_address AS primaryemail | ||||
1434 | FROM borrowers | ||||
1435 | WHERE borrowernumber=? | ||||
1436 | } ); | ||||
1437 | $sth->execute($borrowernumber); | ||||
1438 | my $data = $sth->fetchrow_hashref; | ||||
1439 | return $data->{'primaryemail'} || ''; | ||||
1440 | } | ||||
1441 | |||||
1442 | =head2 GetExpiryDate | ||||
1443 | |||||
1444 | $expirydate = GetExpiryDate($categorycode, $dateenrolled); | ||||
1445 | |||||
1446 | Calculate expiry date given a categorycode and starting date. Date argument must be in ISO format. | ||||
1447 | Return date is also in ISO format. | ||||
1448 | |||||
1449 | =cut | ||||
1450 | |||||
1451 | sub GetExpiryDate { | ||||
1452 | my ( $categorycode, $dateenrolled ) = @_; | ||||
1453 | my $enrolments; | ||||
1454 | if ($categorycode) { | ||||
1455 | my $dbh = C4::Context->dbh; | ||||
1456 | my $sth = $dbh->prepare("SELECT enrolmentperiod,enrolmentperioddate FROM categories WHERE categorycode=?"); | ||||
1457 | $sth->execute($categorycode); | ||||
1458 | $enrolments = $sth->fetchrow_hashref; | ||||
1459 | } | ||||
1460 | # die "GetExpiryDate: for enrollmentperiod $enrolmentperiod (category '$categorycode') starting $dateenrolled.\n"; | ||||
1461 | my @date = split (/-/,$dateenrolled); | ||||
1462 | if($enrolments->{enrolmentperiod}){ | ||||
1463 | return sprintf("%04d-%02d-%02d", Add_Delta_YM(@date,0,$enrolments->{enrolmentperiod})); | ||||
1464 | }else{ | ||||
1465 | return $enrolments->{enrolmentperioddate}; | ||||
1466 | } | ||||
1467 | } | ||||
1468 | |||||
1469 | =head2 GetborCatFromCatType | ||||
1470 | |||||
1471 | ($codes_arrayref, $labels_hashref) = &GetborCatFromCatType(); | ||||
1472 | |||||
1473 | Looks up the different types of borrowers in the database. Returns two | ||||
1474 | elements: a reference-to-array, which lists the borrower category | ||||
1475 | codes, and a reference-to-hash, which maps the borrower category codes | ||||
1476 | to category descriptions. | ||||
1477 | |||||
1478 | =cut | ||||
1479 | |||||
1480 | #' | ||||
1481 | sub GetborCatFromCatType { | ||||
1482 | my ( $category_type, $action, $no_branch_limit ) = @_; | ||||
1483 | |||||
1484 | my $branch_limit = $no_branch_limit | ||||
1485 | ? 0 | ||||
1486 | : C4::Context->userenv ? C4::Context->userenv->{"branch"} : ""; | ||||
1487 | |||||
1488 | # FIXME - This API seems both limited and dangerous. | ||||
1489 | my $dbh = C4::Context->dbh; | ||||
1490 | |||||
1491 | my $request = qq{ | ||||
1492 | SELECT categories.categorycode, categories.description | ||||
1493 | FROM categories | ||||
1494 | }; | ||||
1495 | $request .= qq{ | ||||
1496 | LEFT JOIN categories_branches ON categories.categorycode = categories_branches.categorycode | ||||
1497 | } if $branch_limit; | ||||
1498 | if($action) { | ||||
1499 | $request .= " $action "; | ||||
1500 | $request .= " AND (branchcode = ? OR branchcode IS NULL) GROUP BY description" if $branch_limit; | ||||
1501 | } else { | ||||
1502 | $request .= " WHERE branchcode = ? OR branchcode IS NULL GROUP BY description" if $branch_limit; | ||||
1503 | } | ||||
1504 | $request .= " ORDER BY categorycode"; | ||||
1505 | |||||
1506 | my $sth = $dbh->prepare($request); | ||||
1507 | $sth->execute( | ||||
1508 | $action ? $category_type : (), | ||||
1509 | $branch_limit ? $branch_limit : () | ||||
1510 | ); | ||||
1511 | |||||
1512 | my %labels; | ||||
1513 | my @codes; | ||||
1514 | |||||
1515 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
1516 | push @codes, $data->{'categorycode'}; | ||||
1517 | $labels{ $data->{'categorycode'} } = $data->{'description'}; | ||||
1518 | } | ||||
1519 | $sth->finish; | ||||
1520 | return ( \@codes, \%labels ); | ||||
1521 | } | ||||
1522 | |||||
1523 | =head2 GetBorrowercategory | ||||
1524 | |||||
1525 | $hashref = &GetBorrowercategory($categorycode); | ||||
1526 | |||||
1527 | Given the borrower's category code, the function returns the corresponding | ||||
1528 | data hashref for a comprehensive information display. | ||||
1529 | |||||
1530 | =cut | ||||
1531 | |||||
1532 | sub GetBorrowercategory { | ||||
1533 | my ($catcode) = @_; | ||||
1534 | my $dbh = C4::Context->dbh; | ||||
1535 | if ($catcode){ | ||||
1536 | my $sth = | ||||
1537 | $dbh->prepare( | ||||
1538 | "SELECT description,dateofbirthrequired,upperagelimit,category_type | ||||
1539 | FROM categories | ||||
1540 | WHERE categorycode = ?" | ||||
1541 | ); | ||||
1542 | $sth->execute($catcode); | ||||
1543 | my $data = | ||||
1544 | $sth->fetchrow_hashref; | ||||
1545 | return $data; | ||||
1546 | } | ||||
1547 | return; | ||||
1548 | } # sub getborrowercategory | ||||
1549 | |||||
1550 | |||||
1551 | =head2 GetBorrowerCategorycode | ||||
1552 | |||||
1553 | $categorycode = &GetBorrowerCategoryCode( $borrowernumber ); | ||||
1554 | |||||
1555 | Given the borrowernumber, the function returns the corresponding categorycode | ||||
1556 | =cut | ||||
1557 | |||||
1558 | sub GetBorrowerCategorycode { | ||||
1559 | my ( $borrowernumber ) = @_; | ||||
1560 | my $dbh = C4::Context->dbh; | ||||
1561 | my $sth = $dbh->prepare( qq{ | ||||
1562 | SELECT categorycode | ||||
1563 | FROM borrowers | ||||
1564 | WHERE borrowernumber = ? | ||||
1565 | } ); | ||||
1566 | $sth->execute( $borrowernumber ); | ||||
1567 | return $sth->fetchrow; | ||||
1568 | } | ||||
1569 | |||||
1570 | =head2 GetBorrowercategoryList | ||||
1571 | |||||
1572 | $arrayref_hashref = &GetBorrowercategoryList; | ||||
1573 | If no category code provided, the function returns all the categories. | ||||
1574 | |||||
1575 | =cut | ||||
1576 | |||||
1577 | sub GetBorrowercategoryList { | ||||
1578 | my $no_branch_limit = @_ ? shift : 0; | ||||
1579 | my $branch_limit = $no_branch_limit | ||||
1580 | ? 0 | ||||
1581 | : C4::Context->userenv ? C4::Context->userenv->{"branch"} : ""; | ||||
1582 | my $dbh = C4::Context->dbh; | ||||
1583 | my $query = "SELECT categories.* FROM categories"; | ||||
1584 | $query .= qq{ | ||||
1585 | LEFT JOIN categories_branches ON categories.categorycode = categories_branches.categorycode | ||||
1586 | WHERE branchcode = ? OR branchcode IS NULL GROUP BY description | ||||
1587 | } if $branch_limit; | ||||
1588 | $query .= " ORDER BY description"; | ||||
1589 | my $sth = $dbh->prepare( $query ); | ||||
1590 | $sth->execute( $branch_limit ? $branch_limit : () ); | ||||
1591 | my $data = $sth->fetchall_arrayref( {} ); | ||||
1592 | $sth->finish; | ||||
1593 | return $data; | ||||
1594 | } # sub getborrowercategory | ||||
1595 | |||||
1596 | =head2 ethnicitycategories | ||||
1597 | |||||
1598 | ($codes_arrayref, $labels_hashref) = ðnicitycategories(); | ||||
1599 | |||||
1600 | Looks up the different ethnic types in the database. Returns two | ||||
1601 | elements: a reference-to-array, which lists the ethnicity codes, and a | ||||
1602 | reference-to-hash, which maps the ethnicity codes to ethnicity | ||||
1603 | descriptions. | ||||
1604 | |||||
1605 | =cut | ||||
1606 | |||||
1607 | #' | ||||
1608 | |||||
1609 | sub ethnicitycategories { | ||||
1610 | my $dbh = C4::Context->dbh; | ||||
1611 | my $sth = $dbh->prepare("Select code,name from ethnicity order by name"); | ||||
1612 | $sth->execute; | ||||
1613 | my %labels; | ||||
1614 | my @codes; | ||||
1615 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
1616 | push @codes, $data->{'code'}; | ||||
1617 | $labels{ $data->{'code'} } = $data->{'name'}; | ||||
1618 | } | ||||
1619 | return ( \@codes, \%labels ); | ||||
1620 | } | ||||
1621 | |||||
1622 | =head2 fixEthnicity | ||||
1623 | |||||
1624 | $ethn_name = &fixEthnicity($ethn_code); | ||||
1625 | |||||
1626 | Takes an ethnicity code (e.g., "european" or "pi") and returns the | ||||
1627 | corresponding descriptive name from the C<ethnicity> table in the | ||||
1628 | Koha database ("European" or "Pacific Islander"). | ||||
1629 | |||||
1630 | =cut | ||||
1631 | |||||
1632 | #' | ||||
1633 | |||||
1634 | sub fixEthnicity { | ||||
1635 | my $ethnicity = shift; | ||||
1636 | return unless $ethnicity; | ||||
1637 | my $dbh = C4::Context->dbh; | ||||
1638 | my $sth = $dbh->prepare("Select name from ethnicity where code = ?"); | ||||
1639 | $sth->execute($ethnicity); | ||||
1640 | my $data = $sth->fetchrow_hashref; | ||||
1641 | return $data->{'name'}; | ||||
1642 | } # sub fixEthnicity | ||||
1643 | |||||
1644 | =head2 GetAge | ||||
1645 | |||||
1646 | $dateofbirth,$date = &GetAge($date); | ||||
1647 | |||||
1648 | this function return the borrowers age with the value of dateofbirth | ||||
1649 | |||||
1650 | =cut | ||||
1651 | |||||
1652 | #' | ||||
1653 | sub GetAge{ | ||||
1654 | my ( $date, $date_ref ) = @_; | ||||
1655 | |||||
1656 | if ( not defined $date_ref ) { | ||||
1657 | $date_ref = sprintf( '%04d-%02d-%02d', Today() ); | ||||
1658 | } | ||||
1659 | |||||
1660 | my ( $year1, $month1, $day1 ) = split /-/, $date; | ||||
1661 | my ( $year2, $month2, $day2 ) = split /-/, $date_ref; | ||||
1662 | |||||
1663 | my $age = $year2 - $year1; | ||||
1664 | if ( $month1 . $day1 > $month2 . $day2 ) { | ||||
1665 | $age--; | ||||
1666 | } | ||||
1667 | |||||
1668 | return $age; | ||||
1669 | } # sub get_age | ||||
1670 | |||||
1671 | =head2 get_institutions | ||||
1672 | |||||
1673 | $insitutions = get_institutions(); | ||||
1674 | |||||
1675 | Just returns a list of all the borrowers of type I, borrownumber and name | ||||
1676 | |||||
1677 | =cut | ||||
1678 | |||||
1679 | #' | ||||
1680 | sub get_institutions { | ||||
1681 | my $dbh = C4::Context->dbh(); | ||||
1682 | my $sth = | ||||
1683 | $dbh->prepare( | ||||
1684 | "SELECT borrowernumber,surname FROM borrowers WHERE categorycode=? ORDER BY surname" | ||||
1685 | ); | ||||
1686 | $sth->execute('I'); | ||||
1687 | my %orgs; | ||||
1688 | while ( my $data = $sth->fetchrow_hashref() ) { | ||||
1689 | $orgs{ $data->{'borrowernumber'} } = $data; | ||||
1690 | } | ||||
1691 | return ( \%orgs ); | ||||
1692 | |||||
1693 | } # sub get_institutions | ||||
1694 | |||||
1695 | =head2 add_member_orgs | ||||
1696 | |||||
1697 | add_member_orgs($borrowernumber,$borrowernumbers); | ||||
1698 | |||||
1699 | Takes a borrowernumber and a list of other borrowernumbers and inserts them into the borrowers_to_borrowers table | ||||
1700 | |||||
1701 | =cut | ||||
1702 | |||||
1703 | #' | ||||
1704 | sub add_member_orgs { | ||||
1705 | my ( $borrowernumber, $otherborrowers ) = @_; | ||||
1706 | my $dbh = C4::Context->dbh(); | ||||
1707 | my $query = | ||||
1708 | "INSERT INTO borrowers_to_borrowers (borrower1,borrower2) VALUES (?,?)"; | ||||
1709 | my $sth = $dbh->prepare($query); | ||||
1710 | foreach my $otherborrowernumber (@$otherborrowers) { | ||||
1711 | $sth->execute( $borrowernumber, $otherborrowernumber ); | ||||
1712 | } | ||||
1713 | |||||
1714 | } # sub add_member_orgs | ||||
1715 | |||||
1716 | =head2 GetCities | ||||
1717 | |||||
1718 | $cityarrayref = GetCities(); | ||||
1719 | |||||
1720 | Returns an array_ref of the entries in the cities table | ||||
1721 | If there are entries in the table an empty row is returned | ||||
1722 | This is currently only used to populate a popup in memberentry | ||||
1723 | |||||
1724 | =cut | ||||
1725 | |||||
1726 | sub GetCities { | ||||
1727 | |||||
1728 | my $dbh = C4::Context->dbh; | ||||
1729 | my $city_arr = $dbh->selectall_arrayref( | ||||
1730 | q|SELECT cityid,city_zipcode,city_name,city_state,city_country FROM cities ORDER BY city_name|, | ||||
1731 | { Slice => {} }); | ||||
1732 | if ( @{$city_arr} ) { | ||||
1733 | unshift @{$city_arr}, { | ||||
1734 | city_zipcode => q{}, | ||||
1735 | city_name => q{}, | ||||
1736 | cityid => q{}, | ||||
1737 | city_state => q{}, | ||||
1738 | city_country => q{}, | ||||
1739 | }; | ||||
1740 | } | ||||
1741 | |||||
1742 | return $city_arr; | ||||
1743 | } | ||||
1744 | |||||
1745 | =head2 GetSortDetails (OUEST-PROVENCE) | ||||
1746 | |||||
1747 | ($lib) = &GetSortDetails($category,$sortvalue); | ||||
1748 | |||||
1749 | Returns the authorized value details | ||||
1750 | C<&$lib>return value of authorized value details | ||||
1751 | C<&$sortvalue>this is the value of authorized value | ||||
1752 | C<&$category>this is the value of authorized value category | ||||
1753 | |||||
1754 | =cut | ||||
1755 | |||||
1756 | sub GetSortDetails { | ||||
1757 | my ( $category, $sortvalue ) = @_; | ||||
1758 | my $dbh = C4::Context->dbh; | ||||
1759 | my $query = qq|SELECT lib | ||||
1760 | FROM authorised_values | ||||
1761 | WHERE category=? | ||||
1762 | AND authorised_value=? |; | ||||
1763 | my $sth = $dbh->prepare($query); | ||||
1764 | $sth->execute( $category, $sortvalue ); | ||||
1765 | my $lib = $sth->fetchrow; | ||||
1766 | return ($lib) if ($lib); | ||||
1767 | return ($sortvalue) unless ($lib); | ||||
1768 | } | ||||
1769 | |||||
1770 | =head2 MoveMemberToDeleted | ||||
1771 | |||||
1772 | $result = &MoveMemberToDeleted($borrowernumber); | ||||
1773 | |||||
1774 | Copy the record from borrowers to deletedborrowers table. | ||||
1775 | |||||
1776 | =cut | ||||
1777 | |||||
1778 | # FIXME: should do it in one SQL statement w/ subquery | ||||
1779 | # Otherwise, we should return the @data on success | ||||
1780 | |||||
1781 | sub MoveMemberToDeleted { | ||||
1782 | my ($member) = shift or return; | ||||
1783 | my $dbh = C4::Context->dbh; | ||||
1784 | my $query = qq|SELECT * | ||||
1785 | FROM borrowers | ||||
1786 | WHERE borrowernumber=?|; | ||||
1787 | my $sth = $dbh->prepare($query); | ||||
1788 | $sth->execute($member); | ||||
1789 | my @data = $sth->fetchrow_array; | ||||
1790 | (@data) or return; # if we got a bad borrowernumber, there's nothing to insert | ||||
1791 | $sth = | ||||
1792 | $dbh->prepare( "INSERT INTO deletedborrowers VALUES (" | ||||
1793 | . ( "?," x ( scalar(@data) - 1 ) ) | ||||
1794 | . "?)" ); | ||||
1795 | $sth->execute(@data); | ||||
1796 | } | ||||
1797 | |||||
1798 | =head2 DelMember | ||||
1799 | |||||
1800 | DelMember($borrowernumber); | ||||
1801 | |||||
1802 | This function remove directly a borrower whitout writing it on deleteborrower. | ||||
1803 | + Deletes reserves for the borrower | ||||
1804 | |||||
1805 | =cut | ||||
1806 | |||||
1807 | sub DelMember { | ||||
1808 | my $dbh = C4::Context->dbh; | ||||
1809 | my $borrowernumber = shift; | ||||
1810 | #warn "in delmember with $borrowernumber"; | ||||
1811 | return unless $borrowernumber; # borrowernumber is mandatory. | ||||
1812 | |||||
1813 | my $query = qq|DELETE | ||||
1814 | FROM reserves | ||||
1815 | WHERE borrowernumber=?|; | ||||
1816 | my $sth = $dbh->prepare($query); | ||||
1817 | $sth->execute($borrowernumber); | ||||
1818 | $query = " | ||||
1819 | DELETE | ||||
1820 | FROM borrowers | ||||
1821 | WHERE borrowernumber = ? | ||||
1822 | "; | ||||
1823 | $sth = $dbh->prepare($query); | ||||
1824 | $sth->execute($borrowernumber); | ||||
1825 | logaction("MEMBERS", "DELETE", $borrowernumber, "") if C4::Context->preference("BorrowersLog"); | ||||
1826 | return $sth->rows; | ||||
1827 | } | ||||
1828 | |||||
1829 | =head2 ExtendMemberSubscriptionTo (OUEST-PROVENCE) | ||||
1830 | |||||
1831 | $date = ExtendMemberSubscriptionTo($borrowerid, $date); | ||||
1832 | |||||
1833 | Extending the subscription to a given date or to the expiry date calculated on ISO date. | ||||
1834 | Returns ISO date. | ||||
1835 | |||||
1836 | =cut | ||||
1837 | |||||
1838 | sub ExtendMemberSubscriptionTo { | ||||
1839 | my ( $borrowerid,$date) = @_; | ||||
1840 | my $dbh = C4::Context->dbh; | ||||
1841 | my $borrower = GetMember('borrowernumber'=>$borrowerid); | ||||
1842 | unless ($date){ | ||||
1843 | $date = (C4::Context->preference('BorrowerRenewalPeriodBase') eq 'dateexpiry') ? | ||||
1844 | C4::Dates->new($borrower->{'dateexpiry'}, 'iso')->output("iso") : | ||||
1845 | C4::Dates->new()->output("iso"); | ||||
1846 | $date = GetExpiryDate( $borrower->{'categorycode'}, $date ); | ||||
1847 | } | ||||
1848 | my $sth = $dbh->do(<<EOF); | ||||
1849 | UPDATE borrowers | ||||
1850 | SET dateexpiry='$date' | ||||
1851 | WHERE borrowernumber='$borrowerid' | ||||
1852 | EOF | ||||
1853 | |||||
1854 | AddEnrolmentFeeIfNeeded( $borrower->{categorycode}, $borrower->{borrowernumber} ); | ||||
1855 | |||||
1856 | logaction("MEMBERS", "RENEW", $borrower->{'borrowernumber'}, "Membership renewed")if C4::Context->preference("BorrowersLog"); | ||||
1857 | return $date if ($sth); | ||||
1858 | return 0; | ||||
1859 | } | ||||
1860 | |||||
1861 | =head2 GetTitles (OUEST-PROVENCE) | ||||
1862 | |||||
1863 | ($borrowertitle)= &GetTitles(); | ||||
1864 | |||||
1865 | Looks up the different title . Returns array with all borrowers title | ||||
1866 | |||||
1867 | =cut | ||||
1868 | |||||
1869 | sub GetTitles { | ||||
1870 | my @borrowerTitle = split (/,|\|/,C4::Context->preference('BorrowersTitles')); | ||||
1871 | unshift( @borrowerTitle, "" ); | ||||
1872 | my $count=@borrowerTitle; | ||||
1873 | if ($count == 1){ | ||||
1874 | return (); | ||||
1875 | } | ||||
1876 | else { | ||||
1877 | return ( \@borrowerTitle); | ||||
1878 | } | ||||
1879 | } | ||||
1880 | |||||
1881 | =head2 GetPatronImage | ||||
1882 | |||||
1883 | my ($imagedata, $dberror) = GetPatronImage($borrowernumber); | ||||
1884 | |||||
1885 | Returns the mimetype and binary image data of the image for the patron with the supplied borrowernumber. | ||||
1886 | |||||
1887 | =cut | ||||
1888 | |||||
1889 | sub GetPatronImage { | ||||
1890 | my ($borrowernumber) = @_; | ||||
1891 | warn "Borrowernumber passed to GetPatronImage is $borrowernumber" if $debug; | ||||
1892 | my $dbh = C4::Context->dbh; | ||||
1893 | my $query = 'SELECT mimetype, imagefile FROM patronimage WHERE borrowernumber = ?'; | ||||
1894 | my $sth = $dbh->prepare($query); | ||||
1895 | $sth->execute($borrowernumber); | ||||
1896 | my $imagedata = $sth->fetchrow_hashref; | ||||
1897 | warn "Database error!" if $sth->errstr; | ||||
1898 | return $imagedata, $sth->errstr; | ||||
1899 | } | ||||
1900 | |||||
1901 | =head2 PutPatronImage | ||||
1902 | |||||
1903 | PutPatronImage($cardnumber, $mimetype, $imgfile); | ||||
1904 | |||||
1905 | Stores patron binary image data and mimetype in database. | ||||
1906 | NOTE: This function is good for updating images as well as inserting new images in the database. | ||||
1907 | |||||
1908 | =cut | ||||
1909 | |||||
1910 | sub PutPatronImage { | ||||
1911 | my ($cardnumber, $mimetype, $imgfile) = @_; | ||||
1912 | warn "Parameters passed in: Cardnumber=$cardnumber, Mimetype=$mimetype, " . ($imgfile ? "Imagefile" : "No Imagefile") if $debug; | ||||
1913 | my $dbh = C4::Context->dbh; | ||||
1914 | my $query = "INSERT INTO patronimage (borrowernumber, mimetype, imagefile) VALUES ( ( SELECT borrowernumber from borrowers WHERE cardnumber = ? ),?,?) ON DUPLICATE KEY UPDATE imagefile = ?;"; | ||||
1915 | my $sth = $dbh->prepare($query); | ||||
1916 | $sth->execute($cardnumber,$mimetype,$imgfile,$imgfile); | ||||
1917 | warn "Error returned inserting $cardnumber.$mimetype." if $sth->errstr; | ||||
1918 | return $sth->errstr; | ||||
1919 | } | ||||
1920 | |||||
1921 | =head2 RmPatronImage | ||||
1922 | |||||
1923 | my ($dberror) = RmPatronImage($borrowernumber); | ||||
1924 | |||||
1925 | Removes the image for the patron with the supplied borrowernumber. | ||||
1926 | |||||
1927 | =cut | ||||
1928 | |||||
1929 | sub RmPatronImage { | ||||
1930 | my ($borrowernumber) = @_; | ||||
1931 | warn "Borrowernumber passed to GetPatronImage is $borrowernumber" if $debug; | ||||
1932 | my $dbh = C4::Context->dbh; | ||||
1933 | my $query = "DELETE FROM patronimage WHERE borrowernumber = ?;"; | ||||
1934 | my $sth = $dbh->prepare($query); | ||||
1935 | $sth->execute($borrowernumber); | ||||
1936 | my $dberror = $sth->errstr; | ||||
1937 | warn "Database error!" if $sth->errstr; | ||||
1938 | return $dberror; | ||||
1939 | } | ||||
1940 | |||||
1941 | =head2 GetHideLostItemsPreference | ||||
1942 | |||||
1943 | $hidelostitemspref = &GetHideLostItemsPreference($borrowernumber); | ||||
1944 | |||||
1945 | Returns the HideLostItems preference for the patron category of the supplied borrowernumber | ||||
1946 | C<&$hidelostitemspref>return value of function, 0 or 1 | ||||
1947 | |||||
1948 | =cut | ||||
1949 | |||||
1950 | sub GetHideLostItemsPreference { | ||||
1951 | my ($borrowernumber) = @_; | ||||
1952 | my $dbh = C4::Context->dbh; | ||||
1953 | my $query = "SELECT hidelostitems FROM borrowers,categories WHERE borrowers.categorycode = categories.categorycode AND borrowernumber = ?"; | ||||
1954 | my $sth = $dbh->prepare($query); | ||||
1955 | $sth->execute($borrowernumber); | ||||
1956 | my $hidelostitems = $sth->fetchrow; | ||||
1957 | return $hidelostitems; | ||||
1958 | } | ||||
1959 | |||||
1960 | =head2 GetBorrowersToExpunge | ||||
1961 | |||||
1962 | $borrowers = &GetBorrowersToExpunge( | ||||
1963 | not_borrowered_since => $not_borrowered_since, | ||||
1964 | expired_before => $expired_before, | ||||
1965 | category_code => $category_code, | ||||
1966 | branchcode => $branchcode | ||||
1967 | ); | ||||
1968 | |||||
1969 | This function get all borrowers based on the given criteria. | ||||
1970 | |||||
1971 | =cut | ||||
1972 | |||||
1973 | sub GetBorrowersToExpunge { | ||||
1974 | my $params = shift; | ||||
1975 | |||||
1976 | my $filterdate = $params->{'not_borrowered_since'}; | ||||
1977 | my $filterexpiry = $params->{'expired_before'}; | ||||
1978 | my $filtercategory = $params->{'category_code'}; | ||||
1979 | my $filterbranch = $params->{'branchcode'} || | ||||
1980 | ((C4::Context->preference('IndependentBranches') | ||||
1981 | && C4::Context->userenv | ||||
1982 | && !C4::Context->IsSuperLibrarian() | ||||
1983 | && C4::Context->userenv->{branch}) | ||||
1984 | ? C4::Context->userenv->{branch} | ||||
1985 | : ""); | ||||
1986 | |||||
1987 | my $dbh = C4::Context->dbh; | ||||
1988 | my $query = " | ||||
1989 | SELECT borrowers.borrowernumber, | ||||
1990 | MAX(old_issues.timestamp) AS latestissue, | ||||
1991 | MAX(issues.timestamp) AS currentissue | ||||
1992 | FROM borrowers | ||||
1993 | JOIN categories USING (categorycode) | ||||
1994 | LEFT JOIN old_issues USING (borrowernumber) | ||||
1995 | LEFT JOIN issues USING (borrowernumber) | ||||
1996 | WHERE category_type <> 'S' | ||||
1997 | AND borrowernumber NOT IN (SELECT guarantorid FROM borrowers WHERE guarantorid IS NOT NULL AND guarantorid <> 0) | ||||
1998 | "; | ||||
1999 | my @query_params; | ||||
2000 | if ( $filterbranch && $filterbranch ne "" ) { | ||||
2001 | $query.= " AND borrowers.branchcode = ? "; | ||||
2002 | push( @query_params, $filterbranch ); | ||||
2003 | } | ||||
2004 | if ( $filterexpiry ) { | ||||
2005 | $query .= " AND dateexpiry < ? "; | ||||
2006 | push( @query_params, $filterexpiry ); | ||||
2007 | } | ||||
2008 | if ( $filtercategory ) { | ||||
2009 | $query .= " AND categorycode = ? "; | ||||
2010 | push( @query_params, $filtercategory ); | ||||
2011 | } | ||||
2012 | $query.=" GROUP BY borrowers.borrowernumber HAVING currentissue IS NULL "; | ||||
2013 | if ( $filterdate ) { | ||||
2014 | $query.=" AND ( latestissue < ? OR latestissue IS NULL ) "; | ||||
2015 | push @query_params,$filterdate; | ||||
2016 | } | ||||
2017 | warn $query if $debug; | ||||
2018 | |||||
2019 | my $sth = $dbh->prepare($query); | ||||
2020 | if (scalar(@query_params)>0){ | ||||
2021 | $sth->execute(@query_params); | ||||
2022 | } | ||||
2023 | else { | ||||
2024 | $sth->execute; | ||||
2025 | } | ||||
2026 | |||||
2027 | my @results; | ||||
2028 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
2029 | push @results, $data; | ||||
2030 | } | ||||
2031 | return \@results; | ||||
2032 | } | ||||
2033 | |||||
2034 | =head2 GetBorrowersWhoHaveNeverBorrowed | ||||
2035 | |||||
2036 | $results = &GetBorrowersWhoHaveNeverBorrowed | ||||
2037 | |||||
2038 | This function get all borrowers who have never borrowed. | ||||
2039 | |||||
2040 | I<$result> is a ref to an array which all elements are a hasref. | ||||
2041 | |||||
2042 | =cut | ||||
2043 | |||||
2044 | sub GetBorrowersWhoHaveNeverBorrowed { | ||||
2045 | my $filterbranch = shift || | ||||
2046 | ((C4::Context->preference('IndependentBranches') | ||||
2047 | && C4::Context->userenv | ||||
2048 | && !C4::Context->IsSuperLibrarian() | ||||
2049 | && C4::Context->userenv->{branch}) | ||||
2050 | ? C4::Context->userenv->{branch} | ||||
2051 | : ""); | ||||
2052 | my $dbh = C4::Context->dbh; | ||||
2053 | my $query = " | ||||
2054 | SELECT borrowers.borrowernumber,max(timestamp) as latestissue | ||||
2055 | FROM borrowers | ||||
2056 | LEFT JOIN issues ON borrowers.borrowernumber = issues.borrowernumber | ||||
2057 | WHERE issues.borrowernumber IS NULL | ||||
2058 | "; | ||||
2059 | my @query_params; | ||||
2060 | if ($filterbranch && $filterbranch ne ""){ | ||||
2061 | $query.=" AND borrowers.branchcode= ?"; | ||||
2062 | push @query_params,$filterbranch; | ||||
2063 | } | ||||
2064 | warn $query if $debug; | ||||
2065 | |||||
2066 | my $sth = $dbh->prepare($query); | ||||
2067 | if (scalar(@query_params)>0){ | ||||
2068 | $sth->execute(@query_params); | ||||
2069 | } | ||||
2070 | else { | ||||
2071 | $sth->execute; | ||||
2072 | } | ||||
2073 | |||||
2074 | my @results; | ||||
2075 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
2076 | push @results, $data; | ||||
2077 | } | ||||
2078 | return \@results; | ||||
2079 | } | ||||
2080 | |||||
2081 | =head2 GetBorrowersWithIssuesHistoryOlderThan | ||||
2082 | |||||
2083 | $results = &GetBorrowersWithIssuesHistoryOlderThan($date) | ||||
2084 | |||||
2085 | this function get all borrowers who has an issue history older than I<$date> given on input arg. | ||||
2086 | |||||
2087 | I<$result> is a ref to an array which all elements are a hashref. | ||||
2088 | This hashref is containt the number of time this borrowers has borrowed before I<$date> and the borrowernumber. | ||||
2089 | |||||
2090 | =cut | ||||
2091 | |||||
2092 | sub GetBorrowersWithIssuesHistoryOlderThan { | ||||
2093 | my $dbh = C4::Context->dbh; | ||||
2094 | my $date = shift ||POSIX::strftime("%Y-%m-%d",localtime()); | ||||
2095 | my $filterbranch = shift || | ||||
2096 | ((C4::Context->preference('IndependentBranches') | ||||
2097 | && C4::Context->userenv | ||||
2098 | && !C4::Context->IsSuperLibrarian() | ||||
2099 | && C4::Context->userenv->{branch}) | ||||
2100 | ? C4::Context->userenv->{branch} | ||||
2101 | : ""); | ||||
2102 | my $query = " | ||||
2103 | SELECT count(borrowernumber) as n,borrowernumber | ||||
2104 | FROM old_issues | ||||
2105 | WHERE returndate < ? | ||||
2106 | AND borrowernumber IS NOT NULL | ||||
2107 | "; | ||||
2108 | my @query_params; | ||||
2109 | push @query_params, $date; | ||||
2110 | if ($filterbranch){ | ||||
2111 | $query.=" AND branchcode = ?"; | ||||
2112 | push @query_params, $filterbranch; | ||||
2113 | } | ||||
2114 | $query.=" GROUP BY borrowernumber "; | ||||
2115 | warn $query if $debug; | ||||
2116 | my $sth = $dbh->prepare($query); | ||||
2117 | $sth->execute(@query_params); | ||||
2118 | my @results; | ||||
2119 | |||||
2120 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
2121 | push @results, $data; | ||||
2122 | } | ||||
2123 | return \@results; | ||||
2124 | } | ||||
2125 | |||||
2126 | =head2 GetBorrowersNamesAndLatestIssue | ||||
2127 | |||||
2128 | $results = &GetBorrowersNamesAndLatestIssueList(@borrowernumbers) | ||||
2129 | |||||
2130 | this function get borrowers Names and surnames and Issue information. | ||||
2131 | |||||
2132 | I<@borrowernumbers> is an array which all elements are borrowernumbers. | ||||
2133 | This hashref is containt the number of time this borrowers has borrowed before I<$date> and the borrowernumber. | ||||
2134 | |||||
2135 | =cut | ||||
2136 | |||||
2137 | sub GetBorrowersNamesAndLatestIssue { | ||||
2138 | my $dbh = C4::Context->dbh; | ||||
2139 | my @borrowernumbers=@_; | ||||
2140 | my $query = " | ||||
2141 | SELECT surname,lastname, phone, email,max(timestamp) | ||||
2142 | FROM borrowers | ||||
2143 | LEFT JOIN issues ON borrowers.borrowernumber=issues.borrowernumber | ||||
2144 | GROUP BY borrowernumber | ||||
2145 | "; | ||||
2146 | my $sth = $dbh->prepare($query); | ||||
2147 | $sth->execute; | ||||
2148 | my $results = $sth->fetchall_arrayref({}); | ||||
2149 | return $results; | ||||
2150 | } | ||||
2151 | |||||
2152 | =head2 ModPrivacy | ||||
2153 | |||||
2154 | =over 4 | ||||
2155 | |||||
2156 | my $success = ModPrivacy( $borrowernumber, $privacy ); | ||||
2157 | |||||
2158 | Update the privacy of a patron. | ||||
2159 | |||||
2160 | return : | ||||
2161 | true on success, false on failure | ||||
2162 | |||||
2163 | =back | ||||
2164 | |||||
2165 | =cut | ||||
2166 | |||||
2167 | sub ModPrivacy { | ||||
2168 | my $borrowernumber = shift; | ||||
2169 | my $privacy = shift; | ||||
2170 | return unless defined $borrowernumber; | ||||
2171 | return unless $borrowernumber =~ /^\d+$/; | ||||
2172 | |||||
2173 | return ModMember( borrowernumber => $borrowernumber, | ||||
2174 | privacy => $privacy ); | ||||
2175 | } | ||||
2176 | |||||
2177 | =head2 AddMessage | ||||
2178 | |||||
2179 | AddMessage( $borrowernumber, $message_type, $message, $branchcode ); | ||||
2180 | |||||
2181 | Adds a message to the messages table for the given borrower. | ||||
2182 | |||||
2183 | Returns: | ||||
2184 | True on success | ||||
2185 | False on failure | ||||
2186 | |||||
2187 | =cut | ||||
2188 | |||||
2189 | sub AddMessage { | ||||
2190 | my ( $borrowernumber, $message_type, $message, $branchcode ) = @_; | ||||
2191 | |||||
2192 | my $dbh = C4::Context->dbh; | ||||
2193 | |||||
2194 | if ( ! ( $borrowernumber && $message_type && $message && $branchcode ) ) { | ||||
2195 | return; | ||||
2196 | } | ||||
2197 | |||||
2198 | my $query = "INSERT INTO messages ( borrowernumber, branchcode, message_type, message ) VALUES ( ?, ?, ?, ? )"; | ||||
2199 | my $sth = $dbh->prepare($query); | ||||
2200 | $sth->execute( $borrowernumber, $branchcode, $message_type, $message ); | ||||
2201 | logaction("MEMBERS", "ADDCIRCMESSAGE", $borrowernumber, $message) if C4::Context->preference("BorrowersLog"); | ||||
2202 | return 1; | ||||
2203 | } | ||||
2204 | |||||
2205 | =head2 GetMessages | ||||
2206 | |||||
2207 | GetMessages( $borrowernumber, $type ); | ||||
2208 | |||||
2209 | $type is message type, B for borrower, or L for Librarian. | ||||
2210 | Empty type returns all messages of any type. | ||||
2211 | |||||
2212 | Returns all messages for the given borrowernumber | ||||
2213 | |||||
2214 | =cut | ||||
2215 | |||||
2216 | sub GetMessages { | ||||
2217 | my ( $borrowernumber, $type, $branchcode ) = @_; | ||||
2218 | |||||
2219 | if ( ! $type ) { | ||||
2220 | $type = '%'; | ||||
2221 | } | ||||
2222 | |||||
2223 | my $dbh = C4::Context->dbh; | ||||
2224 | |||||
2225 | my $query = "SELECT | ||||
2226 | branches.branchname, | ||||
2227 | messages.*, | ||||
2228 | message_date, | ||||
2229 | messages.branchcode LIKE '$branchcode' AS can_delete | ||||
2230 | FROM messages, branches | ||||
2231 | WHERE borrowernumber = ? | ||||
2232 | AND message_type LIKE ? | ||||
2233 | AND messages.branchcode = branches.branchcode | ||||
2234 | ORDER BY message_date DESC"; | ||||
2235 | my $sth = $dbh->prepare($query); | ||||
2236 | $sth->execute( $borrowernumber, $type ) ; | ||||
2237 | my @results; | ||||
2238 | |||||
2239 | while ( my $data = $sth->fetchrow_hashref ) { | ||||
2240 | my $d = C4::Dates->new( $data->{message_date}, 'iso' ); | ||||
2241 | $data->{message_date_formatted} = $d->output; | ||||
2242 | push @results, $data; | ||||
2243 | } | ||||
2244 | return \@results; | ||||
2245 | |||||
2246 | } | ||||
2247 | |||||
2248 | =head2 GetMessages | ||||
2249 | |||||
2250 | GetMessagesCount( $borrowernumber, $type ); | ||||
2251 | |||||
2252 | $type is message type, B for borrower, or L for Librarian. | ||||
2253 | Empty type returns all messages of any type. | ||||
2254 | |||||
2255 | Returns the number of messages for the given borrowernumber | ||||
2256 | |||||
2257 | =cut | ||||
2258 | |||||
2259 | sub GetMessagesCount { | ||||
2260 | my ( $borrowernumber, $type, $branchcode ) = @_; | ||||
2261 | |||||
2262 | if ( ! $type ) { | ||||
2263 | $type = '%'; | ||||
2264 | } | ||||
2265 | |||||
2266 | my $dbh = C4::Context->dbh; | ||||
2267 | |||||
2268 | my $query = "SELECT COUNT(*) as MsgCount FROM messages WHERE borrowernumber = ? AND message_type LIKE ?"; | ||||
2269 | my $sth = $dbh->prepare($query); | ||||
2270 | $sth->execute( $borrowernumber, $type ) ; | ||||
2271 | my @results; | ||||
2272 | |||||
2273 | my $data = $sth->fetchrow_hashref; | ||||
2274 | my $count = $data->{'MsgCount'}; | ||||
2275 | |||||
2276 | return $count; | ||||
2277 | } | ||||
2278 | |||||
- - | |||||
2281 | =head2 DeleteMessage | ||||
2282 | |||||
2283 | DeleteMessage( $message_id ); | ||||
2284 | |||||
2285 | =cut | ||||
2286 | |||||
2287 | sub DeleteMessage { | ||||
2288 | my ( $message_id ) = @_; | ||||
2289 | |||||
2290 | my $dbh = C4::Context->dbh; | ||||
2291 | my $query = "SELECT * FROM messages WHERE message_id = ?"; | ||||
2292 | my $sth = $dbh->prepare($query); | ||||
2293 | $sth->execute( $message_id ); | ||||
2294 | my $message = $sth->fetchrow_hashref(); | ||||
2295 | |||||
2296 | $query = "DELETE FROM messages WHERE message_id = ?"; | ||||
2297 | $sth = $dbh->prepare($query); | ||||
2298 | $sth->execute( $message_id ); | ||||
2299 | logaction("MEMBERS", "DELCIRCMESSAGE", $message->{'borrowernumber'}, $message->{'message'}) if C4::Context->preference("BorrowersLog"); | ||||
2300 | } | ||||
2301 | |||||
2302 | =head2 IssueSlip | ||||
2303 | |||||
2304 | IssueSlip($branchcode, $borrowernumber, $quickslip) | ||||
2305 | |||||
2306 | Returns letter hash ( see C4::Letters::GetPreparedLetter ) | ||||
2307 | |||||
2308 | $quickslip is boolean, to indicate whether we want a quick slip | ||||
2309 | |||||
2310 | =cut | ||||
2311 | |||||
2312 | sub IssueSlip { | ||||
2313 | my ($branch, $borrowernumber, $quickslip) = @_; | ||||
2314 | |||||
2315 | # return unless ( C4::Context->boolean_preference('printcirculationslips') ); | ||||
2316 | |||||
2317 | my $now = POSIX::strftime("%Y-%m-%d", localtime); | ||||
2318 | |||||
2319 | my $issueslist = GetPendingIssues($borrowernumber); | ||||
2320 | foreach my $it (@$issueslist){ | ||||
2321 | if ((substr $it->{'issuedate'}, 0, 10) eq $now || (substr $it->{'lastreneweddate'}, 0, 10) eq $now) { | ||||
2322 | $it->{'now'} = 1; | ||||
2323 | } | ||||
2324 | elsif ((substr $it->{'date_due'}, 0, 10) le $now) { | ||||
2325 | $it->{'overdue'} = 1; | ||||
2326 | } | ||||
2327 | my $dt = dt_from_string( $it->{'date_due'} ); | ||||
2328 | $it->{'date_due'} = output_pref( $dt );; | ||||
2329 | } | ||||
2330 | my @issues = sort { $b->{'timestamp'} <=> $a->{'timestamp'} } @$issueslist; | ||||
2331 | |||||
2332 | my ($letter_code, %repeat); | ||||
2333 | if ( $quickslip ) { | ||||
2334 | $letter_code = 'ISSUEQSLIP'; | ||||
2335 | %repeat = ( | ||||
2336 | 'checkedout' => [ map { | ||||
2337 | 'biblio' => $_, | ||||
2338 | 'items' => $_, | ||||
2339 | 'issues' => $_, | ||||
2340 | }, grep { $_->{'now'} } @issues ], | ||||
2341 | ); | ||||
2342 | } | ||||
2343 | else { | ||||
2344 | $letter_code = 'ISSUESLIP'; | ||||
2345 | %repeat = ( | ||||
2346 | 'checkedout' => [ map { | ||||
2347 | 'biblio' => $_, | ||||
2348 | 'items' => $_, | ||||
2349 | 'issues' => $_, | ||||
2350 | }, grep { !$_->{'overdue'} } @issues ], | ||||
2351 | |||||
2352 | 'overdue' => [ map { | ||||
2353 | 'biblio' => $_, | ||||
2354 | 'items' => $_, | ||||
2355 | 'issues' => $_, | ||||
2356 | }, grep { $_->{'overdue'} } @issues ], | ||||
2357 | |||||
2358 | 'news' => [ map { | ||||
2359 | $_->{'timestamp'} = $_->{'newdate'}; | ||||
2360 | { opac_news => $_ } | ||||
2361 | } @{ GetNewsToDisplay("slip") } ], | ||||
2362 | ); | ||||
2363 | } | ||||
2364 | |||||
2365 | return C4::Letters::GetPreparedLetter ( | ||||
2366 | module => 'circulation', | ||||
2367 | letter_code => $letter_code, | ||||
2368 | branchcode => $branch, | ||||
2369 | tables => { | ||||
2370 | 'branches' => $branch, | ||||
2371 | 'borrowers' => $borrowernumber, | ||||
2372 | }, | ||||
2373 | repeat => \%repeat, | ||||
2374 | ); | ||||
2375 | } | ||||
2376 | |||||
2377 | =head2 GetBorrowersWithEmail | ||||
2378 | |||||
2379 | ([$borrnum,$userid], ...) = GetBorrowersWithEmail('me@example.com'); | ||||
2380 | |||||
2381 | This gets a list of users and their basic details from their email address. | ||||
2382 | As it's possible for multiple user to have the same email address, it provides | ||||
2383 | you with all of them. If there is no userid for the user, there will be an | ||||
2384 | C<undef> there. An empty list will be returned if there are no matches. | ||||
2385 | |||||
2386 | =cut | ||||
2387 | |||||
2388 | sub GetBorrowersWithEmail { | ||||
2389 | my $email = shift; | ||||
2390 | |||||
2391 | my $dbh = C4::Context->dbh; | ||||
2392 | |||||
2393 | my $query = "SELECT borrowernumber, userid FROM borrowers WHERE email=?"; | ||||
2394 | my $sth=$dbh->prepare($query); | ||||
2395 | $sth->execute($email); | ||||
2396 | my @result = (); | ||||
2397 | while (my $ref = $sth->fetch) { | ||||
2398 | push @result, $ref; | ||||
2399 | } | ||||
2400 | die "Failure searching for borrowers by email address: $sth->errstr" if $sth->err; | ||||
2401 | return @result; | ||||
2402 | } | ||||
2403 | |||||
2404 | sub AddMember_Opac { | ||||
2405 | my ( %borrower ) = @_; | ||||
2406 | |||||
2407 | $borrower{'categorycode'} = C4::Context->preference('PatronSelfRegistrationDefaultCategory'); | ||||
2408 | |||||
2409 | my $sr = new String::Random; | ||||
2410 | $sr->{'A'} = [ 'A'..'Z', 'a'..'z' ]; | ||||
2411 | my $password = $sr->randpattern("AAAAAAAAAA"); | ||||
2412 | $borrower{'password'} = $password; | ||||
2413 | |||||
2414 | $borrower{'cardnumber'} = fixup_cardnumber(); | ||||
2415 | |||||
2416 | my $borrowernumber = AddMember(%borrower); | ||||
2417 | |||||
2418 | return ( $borrowernumber, $password ); | ||||
2419 | } | ||||
2420 | |||||
2421 | =head2 AddEnrolmentFeeIfNeeded | ||||
2422 | |||||
2423 | AddEnrolmentFeeIfNeeded( $borrower->{categorycode}, $borrower->{borrowernumber} ); | ||||
2424 | |||||
2425 | Add enrolment fee for a patron if needed. | ||||
2426 | |||||
2427 | =cut | ||||
2428 | |||||
2429 | sub AddEnrolmentFeeIfNeeded { | ||||
2430 | my ( $categorycode, $borrowernumber ) = @_; | ||||
2431 | # check for enrollment fee & add it if needed | ||||
2432 | my $dbh = C4::Context->dbh; | ||||
2433 | my $sth = $dbh->prepare(q{ | ||||
2434 | SELECT enrolmentfee | ||||
2435 | FROM categories | ||||
2436 | WHERE categorycode=? | ||||
2437 | }); | ||||
2438 | $sth->execute( $categorycode ); | ||||
2439 | if ( $sth->err ) { | ||||
2440 | warn sprintf('Database returned the following error: %s', $sth->errstr); | ||||
2441 | return; | ||||
2442 | } | ||||
2443 | my ($enrolmentfee) = $sth->fetchrow; | ||||
2444 | if ($enrolmentfee && $enrolmentfee > 0) { | ||||
2445 | # insert fee in patron debts | ||||
2446 | C4::Accounts::manualinvoice( $borrowernumber, '', '', 'A', $enrolmentfee ); | ||||
2447 | } | ||||
2448 | } | ||||
2449 | |||||
2450 | sub HasOverdues { | ||||
2451 | my ( $borrowernumber ) = @_; | ||||
2452 | |||||
2453 | my $sql = "SELECT COUNT(*) FROM issues WHERE date_due < NOW() AND borrowernumber = ?"; | ||||
2454 | my $sth = C4::Context->dbh->prepare( $sql ); | ||||
2455 | $sth->execute( $borrowernumber ); | ||||
2456 | my ( $count ) = $sth->fetchrow_array(); | ||||
2457 | |||||
2458 | return $count; | ||||
2459 | } | ||||
2460 | |||||
2461 | 1 | 3µs | # spent 2µs within C4::Members::END which was called:
# once (2µs+0s) by main::RUNTIME at line 131 of C4/Service.pm | ||
2462 | |||||
2463 | 1 | 4µs | 1; | ||
2464 | |||||
2465 | __END__ |