Bug 12177 - Remove HTML from authorities.pl
[koha.git] / C4 / Overdues.pm
blobb3ec0660ba8de670094b2c9ad3eb2ade5e2487cb
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 $rqoverduebranches = $dbh->prepare("SELECT DISTINCT branchcode FROM overduerules WHERE delay1 IS NOT NULL AND branchcode <> '' ORDER BY branchcode");
760 $rqoverduebranches->execute;
761 my @branches = map { shift @$_ } @{ $rqoverduebranches->fetchall_arrayref };
762 if (!$branches[0]) {
763 my $availbranches = C4::Branch::GetBranches();
764 @branches = keys %$availbranches;
766 return @branches;
769 =head2 CheckItemNotify
771 Sql request to check if the document has alreday been notified
772 this function is not exported, only used with GetOverduesForBranch
774 =cut
776 sub CheckItemNotify {
777 my ($notify_id,$notify_level,$itemnumber) = @_;
778 my $dbh = C4::Context->dbh;
779 my $sth = $dbh->prepare("
780 SELECT COUNT(*)
781 FROM notifys
782 WHERE notify_id = ?
783 AND notify_level = ?
784 AND itemnumber = ? ");
785 $sth->execute($notify_id,$notify_level,$itemnumber);
786 my $notified = $sth->fetchrow;
787 return ($notified);
790 =head2 GetOverduesForBranch
792 Sql request for display all information for branchoverdues.pl
793 2 possibilities : with or without location .
794 display is filtered by branch
796 FIXME: This function should be renamed.
798 =cut
800 sub GetOverduesForBranch {
801 my ( $branch, $location) = @_;
802 my $itype_link = (C4::Context->preference('item-level_itypes')) ? " items.itype " : " biblioitems.itemtype ";
803 my $dbh = C4::Context->dbh;
804 my $select = "
805 SELECT
806 borrowers.cardnumber,
807 borrowers.borrowernumber,
808 borrowers.surname,
809 borrowers.firstname,
810 borrowers.phone,
811 borrowers.email,
812 biblio.title,
813 biblio.author,
814 biblio.biblionumber,
815 issues.date_due,
816 issues.returndate,
817 issues.branchcode,
818 branches.branchname,
819 items.barcode,
820 items.homebranch,
821 items.itemcallnumber,
822 items.location,
823 items.itemnumber,
824 itemtypes.description,
825 accountlines.notify_id,
826 accountlines.notify_level,
827 accountlines.amountoutstanding
828 FROM accountlines
829 LEFT JOIN issues ON issues.itemnumber = accountlines.itemnumber
830 AND issues.borrowernumber = accountlines.borrowernumber
831 LEFT JOIN borrowers ON borrowers.borrowernumber = accountlines.borrowernumber
832 LEFT JOIN items ON items.itemnumber = issues.itemnumber
833 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
834 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
835 LEFT JOIN itemtypes ON itemtypes.itemtype = $itype_link
836 LEFT JOIN branches ON branches.branchcode = issues.branchcode
837 WHERE (accountlines.amountoutstanding != '0.000000')
838 AND (accountlines.accounttype = 'FU' )
839 AND (issues.branchcode = ? )
840 AND (issues.date_due < NOW())
842 my @getoverdues;
843 my $i = 0;
844 my $sth;
845 if ($location) {
846 $sth = $dbh->prepare("$select AND items.location = ? ORDER BY borrowers.surname, borrowers.firstname");
847 $sth->execute($branch, $location);
848 } else {
849 $sth = $dbh->prepare("$select ORDER BY borrowers.surname, borrowers.firstname");
850 $sth->execute($branch);
852 while ( my $data = $sth->fetchrow_hashref ) {
853 #check if the document has already been notified
854 my $countnotify = CheckItemNotify($data->{'notify_id'}, $data->{'notify_level'}, $data->{'itemnumber'});
855 if ($countnotify eq '0') {
856 $getoverdues[$i] = $data;
857 $i++;
860 return (@getoverdues);
864 =head2 AddNotifyLine
866 &AddNotifyLine($borrowernumber, $itemnumber, $overduelevel, $method, $notifyId)
868 Create a line into notify, if the method is phone, the notification_send_date is implemented to
870 =cut
872 sub AddNotifyLine {
873 my ( $borrowernumber, $itemnumber, $overduelevel, $method, $notifyId ) = @_;
874 my $dbh = C4::Context->dbh;
875 if ( $method eq "phone" ) {
876 my $sth = $dbh->prepare(
877 "INSERT INTO notifys (borrowernumber,itemnumber,notify_date,notify_send_date,notify_level,method,notify_id)
878 VALUES (?,?,now(),now(),?,?,?)"
880 $sth->execute( $borrowernumber, $itemnumber, $overduelevel, $method,
881 $notifyId );
883 else {
884 my $sth = $dbh->prepare(
885 "INSERT INTO notifys (borrowernumber,itemnumber,notify_date,notify_level,method,notify_id)
886 VALUES (?,?,now(),?,?,?)"
888 $sth->execute( $borrowernumber, $itemnumber, $overduelevel, $method,
889 $notifyId );
891 return 1;
894 =head2 RemoveNotifyLine
896 &RemoveNotifyLine( $borrowernumber, $itemnumber, $notify_date );
898 Cancel a notification
900 =cut
902 sub RemoveNotifyLine {
903 my ( $borrowernumber, $itemnumber, $notify_date ) = @_;
904 my $dbh = C4::Context->dbh;
905 my $sth = $dbh->prepare(
906 "DELETE FROM notifys
907 WHERE
908 borrowernumber=?
909 AND itemnumber=?
910 AND notify_date=?"
912 $sth->execute( $borrowernumber, $itemnumber, $notify_date );
913 return 1;
916 =head2 GetOverdueMessageTransportTypes
918 my $message_transport_types = GetOverdueMessageTransportTypes( $branchcode, $categorycode, $letternumber);
920 return a arrayref with all message_transport_type for given branchcode, categorycode and letternumber(1,2 or 3)
922 =cut
924 sub GetOverdueMessageTransportTypes {
925 my ( $branchcode, $categorycode, $letternumber ) = @_;
926 return unless $categorycode and $letternumber;
927 my $dbh = C4::Context->dbh;
928 my $sth = $dbh->prepare("
929 SELECT message_transport_type FROM overduerules_transport_types
930 WHERE branchcode = ? AND categorycode = ? AND letternumber = ?
932 $sth->execute( $branchcode, $categorycode, $letternumber );
933 my @mtts;
934 while ( my $mtt = $sth->fetchrow ) {
935 push @mtts, $mtt;
938 # Put 'print' in first if exists
939 # It avoid to sent a print notice with an email or sms template is no email or sms is defined
940 @mtts = uniq( 'print', @mtts )
941 if grep {/^print$/} @mtts;
943 return \@mtts;
947 __END__
949 =head1 AUTHOR
951 Koha Development Team <http://koha-community.org/>
953 =cut