Bug 13353 - update version used by build script
[koha.git] / C4 / Overdues.pm
blob185584fa80d0228890b79fd0919564b6c7e042d3
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 borrowernumber=?|;
659 my @query_param;
660 push @query_param, $borrowernumber;
661 if (defined $itemnum )
663 $query .= " AND itemnumber=?";
664 push @query_param, $itemnum;
666 my $sth = $dbh->prepare($query);
667 $sth->execute( @query_param );
668 my $fine = $sth->fetchrow_hashref();
669 if ($fine->{fineamount}) {
670 return $fine->{fineamount};
672 return 0;
675 =head2 NumberNotifyId
677 (@notify) = &NumberNotifyId($borrowernumber);
679 Returns amount for all file per borrowers
680 C<@notify> array contains all file per borrowers
682 C<$notify_id> contains the file number for the borrower number nad item number
684 =cut
686 sub NumberNotifyId{
687 my ($borrowernumber)=@_;
688 my $dbh = C4::Context->dbh;
689 my $query=qq| SELECT distinct(notify_id)
690 FROM accountlines
691 WHERE borrowernumber=?|;
692 my @notify;
693 my $sth = $dbh->prepare($query);
694 $sth->execute($borrowernumber);
695 while ( my ($numberofnotify) = $sth->fetchrow ) {
696 push( @notify, $numberofnotify );
698 return (@notify);
701 =head2 AmountNotify
703 ($totalnotify) = &AmountNotify($notifyid);
705 Returns amount for all file per borrowers
706 C<$notifyid> is the file number
708 C<$totalnotify> contains amount of a file
710 C<$notify_id> contains the file number for the borrower number and item number
712 =cut
714 sub AmountNotify{
715 my ($notifyid,$borrowernumber)=@_;
716 my $dbh = C4::Context->dbh;
717 my $query=qq| SELECT sum(amountoutstanding)
718 FROM accountlines
719 WHERE notify_id=? AND borrowernumber = ?|;
720 my $sth=$dbh->prepare($query);
721 $sth->execute($notifyid,$borrowernumber);
722 my $totalnotify=$sth->fetchrow;
723 $sth->finish;
724 return ($totalnotify);
727 =head2 GetItems
729 ($items) = &GetItems($itemnumber);
731 Returns the list of all delays from overduerules.
733 C<$items> is a reference-to-hash whose keys are all of the fields
734 from the items tables of the Koha database. Thus,
736 C<$itemnumber> contains the borrower categorycode
738 =cut
740 # FIXME: This is a bad function to have here.
741 # Shouldn't it be in C4::Items?
742 # Shouldn't it be called GetItem since you only get 1 row?
743 # Shouldn't it be called GetItem since you give it only 1 itemnumber?
745 sub GetItems {
746 my $itemnumber = shift or return;
747 my $query = qq|SELECT *
748 FROM items
749 WHERE itemnumber=?|;
750 my $sth = C4::Context->dbh->prepare($query);
751 $sth->execute($itemnumber);
752 my ($items) = $sth->fetchrow_hashref;
753 return ($items);
756 =head2 GetBranchcodesWithOverdueRules
758 my @branchcodes = C4::Overdues::GetBranchcodesWithOverdueRules()
760 returns a list of branch codes for branches with overdue rules defined.
762 =cut
764 sub GetBranchcodesWithOverdueRules {
765 my $dbh = C4::Context->dbh;
766 my $branchcodes = $dbh->selectcol_arrayref(q|
767 SELECT DISTINCT(branchcode)
768 FROM overduerules
769 WHERE delay1 IS NOT NULL
770 ORDER BY branchcode
772 if ( $branchcodes->[0] eq '' ) {
773 # If a default rule exists, all branches should be returned
774 my $availbranches = C4::Branch::GetBranches();
775 return keys %$availbranches;
777 return @$branchcodes;
780 =head2 CheckItemNotify
782 Sql request to check if the document has alreday been notified
783 this function is not exported, only used with GetOverduesForBranch
785 =cut
787 sub CheckItemNotify {
788 my ($notify_id,$notify_level,$itemnumber) = @_;
789 my $dbh = C4::Context->dbh;
790 my $sth = $dbh->prepare("
791 SELECT COUNT(*)
792 FROM notifys
793 WHERE notify_id = ?
794 AND notify_level = ?
795 AND itemnumber = ? ");
796 $sth->execute($notify_id,$notify_level,$itemnumber);
797 my $notified = $sth->fetchrow;
798 return ($notified);
801 =head2 GetOverduesForBranch
803 Sql request for display all information for branchoverdues.pl
804 2 possibilities : with or without location .
805 display is filtered by branch
807 FIXME: This function should be renamed.
809 =cut
811 sub GetOverduesForBranch {
812 my ( $branch, $location) = @_;
813 my $itype_link = (C4::Context->preference('item-level_itypes')) ? " items.itype " : " biblioitems.itemtype ";
814 my $dbh = C4::Context->dbh;
815 my $select = "
816 SELECT
817 borrowers.cardnumber,
818 borrowers.borrowernumber,
819 borrowers.surname,
820 borrowers.firstname,
821 borrowers.phone,
822 borrowers.email,
823 biblio.title,
824 biblio.author,
825 biblio.biblionumber,
826 issues.date_due,
827 issues.returndate,
828 issues.branchcode,
829 branches.branchname,
830 items.barcode,
831 items.homebranch,
832 items.itemcallnumber,
833 items.location,
834 items.itemnumber,
835 itemtypes.description,
836 accountlines.notify_id,
837 accountlines.notify_level,
838 accountlines.amountoutstanding
839 FROM accountlines
840 LEFT JOIN issues ON issues.itemnumber = accountlines.itemnumber
841 AND issues.borrowernumber = accountlines.borrowernumber
842 LEFT JOIN borrowers ON borrowers.borrowernumber = accountlines.borrowernumber
843 LEFT JOIN items ON items.itemnumber = issues.itemnumber
844 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
845 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
846 LEFT JOIN itemtypes ON itemtypes.itemtype = $itype_link
847 LEFT JOIN branches ON branches.branchcode = issues.branchcode
848 WHERE (accountlines.amountoutstanding != '0.000000')
849 AND (accountlines.accounttype = 'FU' )
850 AND (issues.branchcode = ? )
851 AND (issues.date_due < NOW())
853 my @getoverdues;
854 my $i = 0;
855 my $sth;
856 if ($location) {
857 $sth = $dbh->prepare("$select AND items.location = ? ORDER BY borrowers.surname, borrowers.firstname");
858 $sth->execute($branch, $location);
859 } else {
860 $sth = $dbh->prepare("$select ORDER BY borrowers.surname, borrowers.firstname");
861 $sth->execute($branch);
863 while ( my $data = $sth->fetchrow_hashref ) {
864 #check if the document has already been notified
865 my $countnotify = CheckItemNotify($data->{'notify_id'}, $data->{'notify_level'}, $data->{'itemnumber'});
866 if ($countnotify eq '0') {
867 $getoverdues[$i] = $data;
868 $i++;
871 return (@getoverdues);
875 =head2 AddNotifyLine
877 &AddNotifyLine($borrowernumber, $itemnumber, $overduelevel, $method, $notifyId)
879 Create a line into notify, if the method is phone, the notification_send_date is implemented to
881 =cut
883 sub AddNotifyLine {
884 my ( $borrowernumber, $itemnumber, $overduelevel, $method, $notifyId ) = @_;
885 my $dbh = C4::Context->dbh;
886 if ( $method eq "phone" ) {
887 my $sth = $dbh->prepare(
888 "INSERT INTO notifys (borrowernumber,itemnumber,notify_date,notify_send_date,notify_level,method,notify_id)
889 VALUES (?,?,now(),now(),?,?,?)"
891 $sth->execute( $borrowernumber, $itemnumber, $overduelevel, $method,
892 $notifyId );
894 else {
895 my $sth = $dbh->prepare(
896 "INSERT INTO notifys (borrowernumber,itemnumber,notify_date,notify_level,method,notify_id)
897 VALUES (?,?,now(),?,?,?)"
899 $sth->execute( $borrowernumber, $itemnumber, $overduelevel, $method,
900 $notifyId );
902 return 1;
905 =head2 RemoveNotifyLine
907 &RemoveNotifyLine( $borrowernumber, $itemnumber, $notify_date );
909 Cancel a notification
911 =cut
913 sub RemoveNotifyLine {
914 my ( $borrowernumber, $itemnumber, $notify_date ) = @_;
915 my $dbh = C4::Context->dbh;
916 my $sth = $dbh->prepare(
917 "DELETE FROM notifys
918 WHERE
919 borrowernumber=?
920 AND itemnumber=?
921 AND notify_date=?"
923 $sth->execute( $borrowernumber, $itemnumber, $notify_date );
924 return 1;
927 =head2 GetOverdueMessageTransportTypes
929 my $message_transport_types = GetOverdueMessageTransportTypes( $branchcode, $categorycode, $letternumber);
931 return a arrayref with all message_transport_type for given branchcode, categorycode and letternumber(1,2 or 3)
933 =cut
935 sub GetOverdueMessageTransportTypes {
936 my ( $branchcode, $categorycode, $letternumber ) = @_;
937 return unless $categorycode and $letternumber;
938 my $dbh = C4::Context->dbh;
939 my $sth = $dbh->prepare("
940 SELECT message_transport_type FROM overduerules_transport_types
941 WHERE branchcode = ? AND categorycode = ? AND letternumber = ?
943 $sth->execute( $branchcode, $categorycode, $letternumber );
944 my @mtts;
945 while ( my $mtt = $sth->fetchrow ) {
946 push @mtts, $mtt;
949 # Put 'print' in first if exists
950 # It avoid to sent a print notice with an email or sms template is no email or sms is defined
951 @mtts = uniq( 'print', @mtts )
952 if grep {/^print$/} @mtts;
954 return \@mtts;
958 __END__
960 =head1 AUTHOR
962 Koha Development Team <http://koha-community.org/>
964 =cut