Bug 3186 - invalid or uninstalled SMSSendDriver (or bad number format) causes process...
[koha.git] / C4 / Overdues.pm
blob31314d6849b572cbdd0ca2f71907f8119d382049
1 package C4::Overdues;
4 # Copyright 2000-2002 Katipo Communications
5 # copyright 2010 BibLibre
7 # This file is part of Koha.
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.
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.
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.
22 use strict;
23 #use warnings; FIXME - Bug 2505
24 use Date::Calc qw/Today Date_to_Days/;
25 use Date::Manip qw/UnixDate/;
26 use List::MoreUtils qw( uniq );
28 use C4::Circulation;
29 use C4::Context;
30 use C4::Accounts;
31 use C4::Log; # logaction
32 use C4::Debug;
34 use vars qw($VERSION @ISA @EXPORT);
36 BEGIN {
37 # set the version for version checking
38 $VERSION = 3.07.00.049;
39 require Exporter;
40 @ISA = qw(Exporter);
41 # subs to rename (and maybe merge some...)
42 push @EXPORT, qw(
43 &CalcFine
44 &Getoverdues
45 &checkoverdues
46 &NumberNotifyId
47 &AmountNotify
48 &UpdateFine
49 &GetFine
51 &CheckItemNotify
52 &GetOverduesForBranch
53 &RemoveNotifyLine
54 &AddNotifyLine
55 &GetOverdueMessageTransportTypes
57 # subs to remove
58 push @EXPORT, qw(
59 &BorType
62 # check that an equivalent don't exist already before moving
64 # subs to move to Circulation.pm
65 push @EXPORT, qw(
66 &GetIssuesIteminfo
69 # &GetIssuingRules - delete.
70 # use C4::Circulation::GetIssuingRule instead.
72 # subs to move to Biblio.pm
73 push @EXPORT, qw(
74 &GetItems
78 =head1 NAME
80 C4::Circulation::Fines - Koha module dealing with fines
82 =head1 SYNOPSIS
84 use C4::Overdues;
86 =head1 DESCRIPTION
88 This module contains several functions for dealing with fines for
89 overdue items. It is primarily used by the 'misc/fines2.pl' script.
91 =head1 FUNCTIONS
93 =head2 Getoverdues
95 $overdues = Getoverdues( { minimumdays => 1, maximumdays => 30 } );
97 Returns the list of all overdue books, with their itemtype.
99 C<$overdues> is a reference-to-array. Each element is a
100 reference-to-hash whose keys are the fields of the issues table in the
101 Koha database.
103 =cut
106 sub Getoverdues {
107 my $params = shift;
108 my $dbh = C4::Context->dbh;
109 my $statement;
110 if ( C4::Context->preference('item-level_itypes') ) {
111 $statement = "
112 SELECT issues.*, items.itype as itemtype, items.homebranch, items.barcode
113 FROM issues
114 LEFT JOIN items USING (itemnumber)
115 WHERE date_due < NOW()
117 } else {
118 $statement = "
119 SELECT issues.*, biblioitems.itemtype, items.itype, items.homebranch, items.barcode
120 FROM issues
121 LEFT JOIN items USING (itemnumber)
122 LEFT JOIN biblioitems USING (biblioitemnumber)
123 WHERE date_due < NOW()
127 my @bind_parameters;
128 if ( exists $params->{'minimumdays'} and exists $params->{'maximumdays'} ) {
129 $statement .= ' AND TO_DAYS( NOW() )-TO_DAYS( date_due ) BETWEEN ? and ? ';
130 push @bind_parameters, $params->{'minimumdays'}, $params->{'maximumdays'};
131 } elsif ( exists $params->{'minimumdays'} ) {
132 $statement .= ' AND ( TO_DAYS( NOW() )-TO_DAYS( date_due ) ) > ? ';
133 push @bind_parameters, $params->{'minimumdays'};
134 } elsif ( exists $params->{'maximumdays'} ) {
135 $statement .= ' AND ( TO_DAYS( NOW() )-TO_DAYS( date_due ) ) < ? ';
136 push @bind_parameters, $params->{'maximumdays'};
138 $statement .= 'ORDER BY borrowernumber';
139 my $sth = $dbh->prepare( $statement );
140 $sth->execute( @bind_parameters );
141 return $sth->fetchall_arrayref({});
145 =head2 checkoverdues
147 ($count, $overdueitems) = checkoverdues($borrowernumber);
149 Returns a count and a list of overdueitems for a given borrowernumber
151 =cut
153 sub checkoverdues {
154 my $borrowernumber = shift or return;
155 # don't select biblioitems.marc or biblioitems.marcxml... too slow on large systems
156 my $sth = C4::Context->dbh->prepare(
157 "SELECT biblio.*, items.*, issues.*,
158 biblioitems.volume,
159 biblioitems.number,
160 biblioitems.itemtype,
161 biblioitems.isbn,
162 biblioitems.issn,
163 biblioitems.publicationyear,
164 biblioitems.publishercode,
165 biblioitems.volumedate,
166 biblioitems.volumedesc,
167 biblioitems.collectiontitle,
168 biblioitems.collectionissn,
169 biblioitems.collectionvolume,
170 biblioitems.editionstatement,
171 biblioitems.editionresponsibility,
172 biblioitems.illus,
173 biblioitems.pages,
174 biblioitems.notes,
175 biblioitems.size,
176 biblioitems.place,
177 biblioitems.lccn,
178 biblioitems.url,
179 biblioitems.cn_source,
180 biblioitems.cn_class,
181 biblioitems.cn_item,
182 biblioitems.cn_suffix,
183 biblioitems.cn_sort,
184 biblioitems.totalissues
185 FROM issues
186 LEFT JOIN items ON issues.itemnumber = items.itemnumber
187 LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
188 LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber
189 WHERE issues.borrowernumber = ?
190 AND issues.date_due < NOW()"
192 # FIXME: SELECT * across 4 tables? do we really need the marc AND marcxml blobs??
193 $sth->execute($borrowernumber);
194 my $results = $sth->fetchall_arrayref({});
195 return ( scalar(@$results), $results); # returning the count and the results is silly
198 =head2 CalcFine
200 ($amount, $chargename, $daycounttotal) = &CalcFine($item,
201 $categorycode, $branch,
202 $start_dt, $end_dt );
204 Calculates the fine for a book.
206 The issuingrules table in the Koha database is a fine matrix, listing
207 the penalties for each type of patron for each type of item and each branch (e.g., the
208 standard fine for books might be $0.50, but $1.50 for DVDs, or staff
209 members might get a longer grace period between the first and second
210 reminders that a book is overdue).
213 C<$item> is an item object (hashref).
215 C<$categorycode> is the category code (string) of the patron who currently has
216 the book.
218 C<$branchcode> is the library (string) whose issuingrules govern this transaction.
220 C<$start_date> & C<$end_date> are DateTime objects
221 defining the date range over which to determine the fine.
223 Fines scripts should just supply the date range over which to calculate the fine.
225 C<&CalcFine> returns four values:
227 C<$amount> is the fine owed by the patron (see above).
229 C<$chargename> is the chargename field from the applicable record in
230 the categoryitem table, whatever that is.
232 C<$unitcount> is the number of chargeable units (days between start and end dates, Calendar adjusted where needed,
233 minus any applicable grace period, or hours)
235 FIXME - What is chargename supposed to be ?
237 FIXME: previously attempted to return C<$message> as a text message, either "First Notice", "Second Notice",
238 or "Final Notice". But CalcFine never defined any value.
240 =cut
242 sub CalcFine {
243 my ( $item, $bortype, $branchcode, $due_dt, $end_date ) = @_;
244 my $start_date = $due_dt->clone();
245 # get issuingrules (fines part will be used)
246 my $itemtype = $item->{itemtype} || $item->{itype};
247 my $data = C4::Circulation::GetIssuingRule($bortype, $itemtype, $branchcode);
248 my $fine_unit = $data->{lengthunit};
249 $fine_unit ||= 'days';
251 my $chargeable_units = _get_chargeable_units($fine_unit, $start_date, $end_date, $branchcode);
252 my $units_minus_grace = $chargeable_units - $data->{firstremind};
253 my $amount = 0;
254 if ($data->{'chargeperiod'} && ($units_minus_grace > 0) ) {
255 if ( C4::Context->preference('FinesIncludeGracePeriod') ) {
256 $amount = int($chargeable_units / $data->{'chargeperiod'}) * $data->{'fine'};# TODO fine calc should be in cents
257 } else {
258 $amount = int($units_minus_grace / $data->{'chargeperiod'}) * $data->{'fine'};
260 } else {
261 # a zero (or null) chargeperiod or negative units_minus_grace value means no charge.
263 $amount = $data->{overduefinescap} if $data->{overduefinescap} && $amount > $data->{overduefinescap};
264 $debug and warn sprintf("CalcFine returning (%s, %s, %s, %s)", $amount, $data->{'chargename'}, $units_minus_grace, $chargeable_units);
265 return ($amount, $data->{'chargename'}, $units_minus_grace, $chargeable_units);
266 # FIXME: chargename is NEVER populated anywhere.
270 =head2 _get_chargeable_units
272 _get_chargeable_units($unit, $start_date_ $end_date, $branchcode);
274 return integer value of units between C<$start_date> and C<$end_date>, factoring in holidays for C<$branchcode>.
276 C<$unit> is 'days' or 'hours' (default is 'days').
278 C<$start_date> and C<$end_date> are the two DateTimes to get the number of units between.
280 C<$branchcode> is the branch whose calendar to use for finding holidays.
282 =cut
284 sub _get_chargeable_units {
285 my ($unit, $date_due, $date_returned, $branchcode) = @_;
287 # If the due date is later than the return date
288 return 0 unless ( $date_returned > $date_due );
290 my $charge_units = 0;
291 my $charge_duration;
292 if ($unit eq 'hours') {
293 if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
294 my $calendar = Koha::Calendar->new( branchcode => $branchcode );
295 $charge_duration = $calendar->hours_between( $date_due, $date_returned );
296 } else {
297 $charge_duration = $date_returned->delta_ms( $date_due );
299 if($charge_duration->in_units('hours') == 0 && $charge_duration->in_units('seconds') > 0){
300 return 1;
302 return $charge_duration->in_units('hours');
304 else { # days
305 if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
306 my $calendar = Koha::Calendar->new( branchcode => $branchcode );
307 $charge_duration = $calendar->days_between( $date_due, $date_returned );
308 } else {
309 $charge_duration = $date_returned->delta_days( $date_due );
311 return $charge_duration->in_units('days');
316 =head2 GetSpecialHolidays
318 &GetSpecialHolidays($date_dues,$itemnumber);
320 return number of special days between date of the day and date due
322 C<$date_dues> is the envisaged date of book return.
324 C<$itemnumber> is the book's item number.
326 =cut
328 sub GetSpecialHolidays {
329 my ( $date_dues, $itemnumber ) = @_;
331 # calcul the today date
332 my $today = join "-", &Today();
334 # return the holdingbranch
335 my $iteminfo = GetIssuesIteminfo($itemnumber);
337 # use sql request to find all date between date_due and today
338 my $dbh = C4::Context->dbh;
339 my $query =
340 qq|SELECT DATE_FORMAT(concat(year,'-',month,'-',day),'%Y-%m-%d') as date
341 FROM `special_holidays`
342 WHERE DATE_FORMAT(concat(year,'-',month,'-',day),'%Y-%m-%d') >= ?
343 AND DATE_FORMAT(concat(year,'-',month,'-',day),'%Y-%m-%d') <= ?
344 AND branchcode=?
346 my @result = GetWdayFromItemnumber($itemnumber);
347 my @result_date;
348 my $wday;
349 my $dateinsec;
350 my $sth = $dbh->prepare($query);
351 $sth->execute( $date_dues, $today, $iteminfo->{'branchcode'} )
352 ; # FIXME: just use NOW() in SQL instead of passing in $today
354 while ( my $special_date = $sth->fetchrow_hashref ) {
355 push( @result_date, $special_date );
358 my $specialdaycount = scalar(@result_date);
360 for ( my $i = 0 ; $i < scalar(@result_date) ; $i++ ) {
361 $dateinsec = UnixDate( $result_date[$i]->{'date'}, "%o" );
362 ( undef, undef, undef, undef, undef, undef, $wday, undef, undef ) =
363 localtime($dateinsec);
364 for ( my $j = 0 ; $j < scalar(@result) ; $j++ ) {
365 if ( $wday == ( $result[$j]->{'weekday'} ) ) {
366 $specialdaycount--;
371 return $specialdaycount;
374 =head2 GetRepeatableHolidays
376 &GetRepeatableHolidays($date_dues, $itemnumber, $difference,);
378 return number of day closed between date of the day and date due
380 C<$date_dues> is the envisaged date of book return.
382 C<$itemnumber> is item number.
384 C<$difference> numbers of between day date of the day and date due
386 =cut
388 sub GetRepeatableHolidays {
389 my ( $date_dues, $itemnumber, $difference ) = @_;
390 my $dateinsec = UnixDate( $date_dues, "%o" );
391 my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
392 localtime($dateinsec);
393 my @result = GetWdayFromItemnumber($itemnumber);
394 my @dayclosedcount;
395 my $j;
397 for ( my $i = 0 ; $i < scalar(@result) ; $i++ ) {
398 my $k = $wday;
400 for ( $j = 0 ; $j < $difference ; $j++ ) {
401 if ( $result[$i]->{'weekday'} == $k ) {
402 push( @dayclosedcount, $k );
404 $k++;
405 ( $k = 0 ) if ( $k eq 7 );
408 return scalar(@dayclosedcount);
412 =head2 GetWayFromItemnumber
414 &Getwdayfromitemnumber($itemnumber);
416 return the different week day from repeatable_holidays table
418 C<$itemnumber> is item number.
420 =cut
422 sub GetWdayFromItemnumber {
423 my ($itemnumber) = @_;
424 my $iteminfo = GetIssuesIteminfo($itemnumber);
425 my @result;
426 my $query = qq|SELECT weekday
427 FROM repeatable_holidays
428 WHERE branchcode=?
430 my $sth = C4::Context->dbh->prepare($query);
432 $sth->execute( $iteminfo->{'branchcode'} );
433 while ( my $weekday = $sth->fetchrow_hashref ) {
434 push( @result, $weekday );
436 return @result;
440 =head2 GetIssuesIteminfo
442 &GetIssuesIteminfo($itemnumber);
444 return all data from issues about item
446 C<$itemnumber> is item number.
448 =cut
450 sub GetIssuesIteminfo {
451 my ($itemnumber) = @_;
452 my $dbh = C4::Context->dbh;
453 my $query = qq|SELECT *
454 FROM issues
455 WHERE itemnumber=?
457 my $sth = $dbh->prepare($query);
458 $sth->execute($itemnumber);
459 my ($issuesinfo) = $sth->fetchrow_hashref;
460 return $issuesinfo;
464 =head2 UpdateFine
466 &UpdateFine($itemnumber, $borrowernumber, $amount, $type, $description);
468 (Note: the following is mostly conjecture and guesswork.)
470 Updates the fine owed on an overdue book.
472 C<$itemnumber> is the book's item number.
474 C<$borrowernumber> is the borrower number of the patron who currently
475 has the book on loan.
477 C<$amount> is the current amount owed by the patron.
479 C<$type> will be used in the description of the fine.
481 C<$description> is a string that must be present in the description of
482 the fine. I think this is expected to be a date in DD/MM/YYYY format.
484 C<&UpdateFine> looks up the amount currently owed on the given item
485 and sets it to C<$amount>, creating, if necessary, a new entry in the
486 accountlines table of the Koha database.
488 =cut
491 # Question: Why should the caller have to
492 # specify both the item number and the borrower number? A book can't
493 # be on loan to two different people, so the item number should be
494 # sufficient.
496 # Possible Answer: You might update a fine for a damaged item, *after* it is returned.
498 sub UpdateFine {
499 my ( $itemnum, $borrowernumber, $amount, $type, $due ) = @_;
500 $debug and warn "UpdateFine($itemnum, $borrowernumber, $amount, " . ($type||'""') . ", $due) called";
501 my $dbh = C4::Context->dbh;
502 # FIXME - What exactly is this query supposed to do? It looks up an
503 # entry in accountlines that matches the given item and borrower
504 # numbers, where the description contains $due, and where the
505 # account type has one of several values, but what does this _mean_?
506 # Does it look up existing fines for this item?
507 # FIXME - What are these various account types? ("FU", "O", "F", "M")
508 # "L" is LOST item
509 # "A" is Account Management Fee
510 # "N" is New Card
511 # "M" is Sundry
512 # "O" is Overdue ??
513 # "F" is Fine ??
514 # "FU" is Fine UPDATE??
515 # "Pay" is Payment
516 # "REF" is Cash Refund
517 my $sth = $dbh->prepare(
518 "SELECT * FROM accountlines
519 WHERE borrowernumber=?
520 AND accounttype IN ('FU','O','F','M')"
522 $sth->execute( $borrowernumber );
523 my $data;
524 my $total_amount_other = 0.00;
525 my $due_qr = qr/$due/;
526 # Cycle through the fines and
527 # - find line that relates to the requested $itemnum
528 # - accumulate fines for other items
529 # so we can update $itemnum fine taking in account fine caps
530 while (my $rec = $sth->fetchrow_hashref) {
531 if ($rec->{itemnumber} == $itemnum && $rec->{description} =~ /$due_qr/) {
532 if ($data) {
533 warn "Not a unique accountlines record for item $itemnum borrower $borrowernumber";
534 } else {
535 $data = $rec;
536 next;
539 $total_amount_other += $rec->{'amountoutstanding'};
542 if (my $maxfine = C4::Context->preference('MaxFine')) {
543 if ($total_amount_other + $amount > $maxfine) {
544 my $new_amount = $maxfine - $total_amount_other;
545 return if $new_amount <= 0.00;
546 warn "Reducing fine for item $itemnum borrower $borrowernumber from $amount to $new_amount - MaxFine reached";
547 $amount = $new_amount;
551 if ( $data ) {
553 # we're updating an existing fine. Only modify if amount changed
554 # Note that in the current implementation, you cannot pay against an accruing fine
555 # (i.e. , of accounttype 'FU'). Doing so will break accrual.
556 if ( $data->{'amount'} != $amount ) {
557 my $diff = $amount - $data->{'amount'};
558 #3341: diff could be positive or negative!
559 my $out = $data->{'amountoutstanding'} + $diff;
560 my $query = "
561 UPDATE accountlines
562 SET date=now(), amount=?, amountoutstanding=?,
563 lastincrement=?, accounttype='FU'
564 WHERE borrowernumber=?
565 AND itemnumber=?
566 AND accounttype IN ('FU','O')
567 AND description LIKE ?
568 LIMIT 1 ";
569 my $sth2 = $dbh->prepare($query);
570 # FIXME: BOGUS query cannot ensure uniqueness w/ LIKE %x% !!!
571 # LIMIT 1 added to prevent multiple affected lines
572 # FIXME: accountlines table needs unique key!! Possibly a combo of borrowernumber and accountline.
573 # But actually, we should just have a regular autoincrementing PK and forget accountline,
574 # including the bogus getnextaccountno function (doesn't prevent conflict on simultaneous ops).
575 # FIXME: Why only 2 account types here?
576 $debug and print STDERR "UpdateFine query: $query\n" .
577 "w/ args: $amount, $out, $diff, $data->{'borrowernumber'}, $data->{'itemnumber'}, \"\%$due\%\"\n";
578 $sth2->execute($amount, $out, $diff, $data->{'borrowernumber'}, $data->{'itemnumber'}, "%$due%");
579 } else {
580 # print "no update needed $data->{'amount'}"
582 } else {
583 my $sth4 = $dbh->prepare(
584 "SELECT title FROM biblio LEFT JOIN items ON biblio.biblionumber=items.biblionumber WHERE items.itemnumber=?"
586 $sth4->execute($itemnum);
587 my $title = $sth4->fetchrow;
589 # # print "not in account";
590 # my $sth3 = $dbh->prepare("Select max(accountno) from accountlines");
591 # $sth3->execute;
593 # # FIXME - Make $accountno a scalar.
594 # my @accountno = $sth3->fetchrow_array;
595 # $sth3->finish;
596 # $accountno[0]++;
597 # begin transaction
598 my $nextaccntno = C4::Accounts::getnextacctno($borrowernumber);
599 my $desc = ($type ? "$type " : '') . "$title $due"; # FIXEDME, avoid whitespace prefix on empty $type
600 my $query = "INSERT INTO accountlines
601 (borrowernumber,itemnumber,date,amount,description,accounttype,amountoutstanding,lastincrement,accountno)
602 VALUES (?,?,now(),?,?,'FU',?,?,?)";
603 my $sth2 = $dbh->prepare($query);
604 $debug and print STDERR "UpdateFine query: $query\nw/ args: $borrowernumber, $itemnum, $amount, $desc, $amount, $amount, $nextaccntno\n";
605 $sth2->execute($borrowernumber, $itemnum, $amount, $desc, $amount, $amount, $nextaccntno);
607 # logging action
608 &logaction(
609 "FINES",
610 $type,
611 $borrowernumber,
612 "due=".$due." amount=".$amount." itemnumber=".$itemnum
613 ) if C4::Context->preference("FinesLog");
616 =head2 BorType
618 $borrower = &BorType($borrowernumber);
620 Looks up a patron by borrower number.
622 C<$borrower> is a reference-to-hash whose keys are all of the fields
623 from the borrowers and categories tables of the Koha database. Thus,
624 C<$borrower> contains all information about both the borrower and
625 category he or she belongs to.
627 =cut
629 sub BorType {
630 my ($borrowernumber) = @_;
631 my $dbh = C4::Context->dbh;
632 my $sth = $dbh->prepare(
633 "SELECT * from borrowers
634 LEFT JOIN categories ON borrowers.categorycode=categories.categorycode
635 WHERE borrowernumber=?"
637 $sth->execute($borrowernumber);
638 return $sth->fetchrow_hashref;
641 =head2 GetFine
643 $data->{'sum(amountoutstanding)'} = &GetFine($itemnum,$borrowernumber);
645 return the total of fine
647 C<$itemnum> is item number
649 C<$borrowernumber> is the borrowernumber
651 =cut
653 sub GetFine {
654 my ( $itemnum, $borrowernumber ) = @_;
655 my $dbh = C4::Context->dbh();
656 my $query = q|SELECT sum(amountoutstanding) as fineamount FROM accountlines
657 where accounttype like 'F%'
658 AND amountoutstanding > 0 AND itemnumber = ? AND borrowernumber=?|;
659 my $sth = $dbh->prepare($query);
660 $sth->execute( $itemnum, $borrowernumber );
661 my $fine = $sth->fetchrow_hashref();
662 if ($fine->{fineamount}) {
663 return $fine->{fineamount};
665 return 0;
668 =head2 NumberNotifyId
670 (@notify) = &NumberNotifyId($borrowernumber);
672 Returns amount for all file per borrowers
673 C<@notify> array contains all file per borrowers
675 C<$notify_id> contains the file number for the borrower number nad item number
677 =cut
679 sub NumberNotifyId{
680 my ($borrowernumber)=@_;
681 my $dbh = C4::Context->dbh;
682 my $query=qq| SELECT distinct(notify_id)
683 FROM accountlines
684 WHERE borrowernumber=?|;
685 my @notify;
686 my $sth = $dbh->prepare($query);
687 $sth->execute($borrowernumber);
688 while ( my ($numberofnotify) = $sth->fetchrow ) {
689 push( @notify, $numberofnotify );
691 return (@notify);
694 =head2 AmountNotify
696 ($totalnotify) = &AmountNotify($notifyid);
698 Returns amount for all file per borrowers
699 C<$notifyid> is the file number
701 C<$totalnotify> contains amount of a file
703 C<$notify_id> contains the file number for the borrower number and item number
705 =cut
707 sub AmountNotify{
708 my ($notifyid,$borrowernumber)=@_;
709 my $dbh = C4::Context->dbh;
710 my $query=qq| SELECT sum(amountoutstanding)
711 FROM accountlines
712 WHERE notify_id=? AND borrowernumber = ?|;
713 my $sth=$dbh->prepare($query);
714 $sth->execute($notifyid,$borrowernumber);
715 my $totalnotify=$sth->fetchrow;
716 $sth->finish;
717 return ($totalnotify);
720 =head2 GetItems
722 ($items) = &GetItems($itemnumber);
724 Returns the list of all delays from overduerules.
726 C<$items> is a reference-to-hash whose keys are all of the fields
727 from the items tables of the Koha database. Thus,
729 C<$itemnumber> contains the borrower categorycode
731 =cut
733 # FIXME: This is a bad function to have here.
734 # Shouldn't it be in C4::Items?
735 # Shouldn't it be called GetItem since you only get 1 row?
736 # Shouldn't it be called GetItem since you give it only 1 itemnumber?
738 sub GetItems {
739 my $itemnumber = shift or return;
740 my $query = qq|SELECT *
741 FROM items
742 WHERE itemnumber=?|;
743 my $sth = C4::Context->dbh->prepare($query);
744 $sth->execute($itemnumber);
745 my ($items) = $sth->fetchrow_hashref;
746 return ($items);
749 =head2 GetBranchcodesWithOverdueRules
751 my @branchcodes = C4::Overdues::GetBranchcodesWithOverdueRules()
753 returns a list of branch codes for branches with overdue rules defined.
755 =cut
757 sub GetBranchcodesWithOverdueRules {
758 my $dbh = C4::Context->dbh;
759 my $branchcodes = $dbh->selectcol_arrayref(q|
760 SELECT DISTINCT(branchcode)
761 FROM overduerules
762 WHERE delay1 IS NOT NULL
763 ORDER BY branchcode
765 if ( $branchcodes->[0] eq '' ) {
766 # If a default rule exists, all branches should be returned
767 my $availbranches = C4::Branch::GetBranches();
768 return keys %$availbranches;
770 return @$branchcodes;
773 =head2 CheckItemNotify
775 Sql request to check if the document has alreday been notified
776 this function is not exported, only used with GetOverduesForBranch
778 =cut
780 sub CheckItemNotify {
781 my ($notify_id,$notify_level,$itemnumber) = @_;
782 my $dbh = C4::Context->dbh;
783 my $sth = $dbh->prepare("
784 SELECT COUNT(*)
785 FROM notifys
786 WHERE notify_id = ?
787 AND notify_level = ?
788 AND itemnumber = ? ");
789 $sth->execute($notify_id,$notify_level,$itemnumber);
790 my $notified = $sth->fetchrow;
791 return ($notified);
794 =head2 GetOverduesForBranch
796 Sql request for display all information for branchoverdues.pl
797 2 possibilities : with or without location .
798 display is filtered by branch
800 FIXME: This function should be renamed.
802 =cut
804 sub GetOverduesForBranch {
805 my ( $branch, $location) = @_;
806 my $itype_link = (C4::Context->preference('item-level_itypes')) ? " items.itype " : " biblioitems.itemtype ";
807 my $dbh = C4::Context->dbh;
808 my $select = "
809 SELECT
810 borrowers.cardnumber,
811 borrowers.borrowernumber,
812 borrowers.surname,
813 borrowers.firstname,
814 borrowers.phone,
815 borrowers.email,
816 biblio.title,
817 biblio.author,
818 biblio.biblionumber,
819 issues.date_due,
820 issues.returndate,
821 issues.branchcode,
822 branches.branchname,
823 items.barcode,
824 items.homebranch,
825 items.itemcallnumber,
826 items.location,
827 items.itemnumber,
828 itemtypes.description,
829 accountlines.notify_id,
830 accountlines.notify_level,
831 accountlines.amountoutstanding
832 FROM accountlines
833 LEFT JOIN issues ON issues.itemnumber = accountlines.itemnumber
834 AND issues.borrowernumber = accountlines.borrowernumber
835 LEFT JOIN borrowers ON borrowers.borrowernumber = accountlines.borrowernumber
836 LEFT JOIN items ON items.itemnumber = issues.itemnumber
837 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
838 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
839 LEFT JOIN itemtypes ON itemtypes.itemtype = $itype_link
840 LEFT JOIN branches ON branches.branchcode = issues.branchcode
841 WHERE (accountlines.amountoutstanding != '0.000000')
842 AND (accountlines.accounttype = 'FU' )
843 AND (issues.branchcode = ? )
844 AND (issues.date_due < NOW())
846 my @getoverdues;
847 my $i = 0;
848 my $sth;
849 if ($location) {
850 $sth = $dbh->prepare("$select AND items.location = ? ORDER BY borrowers.surname, borrowers.firstname");
851 $sth->execute($branch, $location);
852 } else {
853 $sth = $dbh->prepare("$select ORDER BY borrowers.surname, borrowers.firstname");
854 $sth->execute($branch);
856 while ( my $data = $sth->fetchrow_hashref ) {
857 #check if the document has already been notified
858 my $countnotify = CheckItemNotify($data->{'notify_id'}, $data->{'notify_level'}, $data->{'itemnumber'});
859 if ($countnotify eq '0') {
860 $getoverdues[$i] = $data;
861 $i++;
864 return (@getoverdues);
868 =head2 AddNotifyLine
870 &AddNotifyLine($borrowernumber, $itemnumber, $overduelevel, $method, $notifyId)
872 Create a line into notify, if the method is phone, the notification_send_date is implemented to
874 =cut
876 sub AddNotifyLine {
877 my ( $borrowernumber, $itemnumber, $overduelevel, $method, $notifyId ) = @_;
878 my $dbh = C4::Context->dbh;
879 if ( $method eq "phone" ) {
880 my $sth = $dbh->prepare(
881 "INSERT INTO notifys (borrowernumber,itemnumber,notify_date,notify_send_date,notify_level,method,notify_id)
882 VALUES (?,?,now(),now(),?,?,?)"
884 $sth->execute( $borrowernumber, $itemnumber, $overduelevel, $method,
885 $notifyId );
887 else {
888 my $sth = $dbh->prepare(
889 "INSERT INTO notifys (borrowernumber,itemnumber,notify_date,notify_level,method,notify_id)
890 VALUES (?,?,now(),?,?,?)"
892 $sth->execute( $borrowernumber, $itemnumber, $overduelevel, $method,
893 $notifyId );
895 return 1;
898 =head2 RemoveNotifyLine
900 &RemoveNotifyLine( $borrowernumber, $itemnumber, $notify_date );
902 Cancel a notification
904 =cut
906 sub RemoveNotifyLine {
907 my ( $borrowernumber, $itemnumber, $notify_date ) = @_;
908 my $dbh = C4::Context->dbh;
909 my $sth = $dbh->prepare(
910 "DELETE FROM notifys
911 WHERE
912 borrowernumber=?
913 AND itemnumber=?
914 AND notify_date=?"
916 $sth->execute( $borrowernumber, $itemnumber, $notify_date );
917 return 1;
920 =head2 GetOverdueMessageTransportTypes
922 my $message_transport_types = GetOverdueMessageTransportTypes( $branchcode, $categorycode, $letternumber);
924 return a arrayref with all message_transport_type for given branchcode, categorycode and letternumber(1,2 or 3)
926 =cut
928 sub GetOverdueMessageTransportTypes {
929 my ( $branchcode, $categorycode, $letternumber ) = @_;
930 return unless $categorycode and $letternumber;
931 my $dbh = C4::Context->dbh;
932 my $sth = $dbh->prepare("
933 SELECT message_transport_type FROM overduerules_transport_types
934 WHERE branchcode = ? AND categorycode = ? AND letternumber = ?
936 $sth->execute( $branchcode, $categorycode, $letternumber );
937 my @mtts;
938 while ( my $mtt = $sth->fetchrow ) {
939 push @mtts, $mtt;
942 # Put 'print' in first if exists
943 # It avoid to sent a print notice with an email or sms template is no email or sms is defined
944 @mtts = uniq( 'print', @mtts )
945 if grep {/^print$/} @mtts;
947 return \@mtts;
951 __END__
953 =head1 AUTHOR
955 Koha Development Team <http://koha-community.org/>
957 =cut