Update fines cronjob: remove redundant scripts, remove some superfluous code in remai...
[koha.git] / C4 / Overdues.pm
1 package C4::Overdues;
2
3
4 # Copyright 2000-2002 Katipo Communications
5 #
6 # This file is part of Koha.
7 #
8 # Koha is free software; you can redistribute it and/or modify it under the
9 # terms of the GNU General Public License as published by the Free Software
10 # Foundation; either version 2 of the License, or (at your option) any later
11 # version.
12 #
13 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License along with
18 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
19 # Suite 330, Boston, MA  02111-1307 USA
20
21 use strict;
22 use Date::Calc qw/Today/;
23 use Date::Manip qw/UnixDate/;
24 use C4::Circulation;
25 use C4::Context;
26 use C4::Accounts;
27 use C4::Log; # logaction
28 use C4::Debug;
29
30 use vars qw($VERSION @ISA @EXPORT);
31
32 BEGIN {
33         # set the version for version checking
34         $VERSION = 3.01;
35         require Exporter;
36         @ISA    = qw(Exporter);
37         # subs to rename (and maybe merge some...)
38         push @EXPORT, qw(
39         &CalcFine
40         &Getoverdues
41         &checkoverdues
42         &CheckAccountLineLevelInfo
43         &CheckAccountLineItemInfo
44         &CheckExistantNotifyid
45         &GetNextIdNotify
46         &GetNotifyId
47         &NumberNotifyId
48         &AmountNotify
49         &UpdateAccountLines
50         &UpdateFine
51         &GetOverdueDelays
52         &GetOverduerules
53         &GetFine
54         &CreateItemAccountLine
55         &ReplacementCost2
56         
57         &CheckItemNotify
58         &GetOverduesForBranch
59         &RemoveNotifyLine
60         &AddNotifyLine
61         );
62         # subs to remove
63         push @EXPORT, qw(
64         &BorType
65         );
66
67         # check that an equivalent don't exist already before moving
68
69         # subs to move to Circulation.pm
70         push @EXPORT, qw(
71         &GetIssuesIteminfo
72         );
73     #
74         # &GetIssuingRules - delete.
75         # use C4::Circulation::GetIssuingRule instead.
76         
77         # subs to move to Members.pm
78         push @EXPORT, qw(
79         &CheckBorrowerDebarred
80         &UpdateBorrowerDebarred
81         );
82         # subs to move to Biblio.pm
83         push @EXPORT, qw(
84         &GetItems
85         &ReplacementCost
86         );
87 }
88
89 =head1 NAME
90
91 C4::Circulation::Fines - Koha module dealing with fines
92
93 =head1 SYNOPSIS
94
95   use C4::Overdues;
96
97 =head1 DESCRIPTION
98
99 This module contains several functions for dealing with fines for
100 overdue items. It is primarily used by the 'misc/fines2.pl' script.
101
102 =head1 FUNCTIONS
103
104 =over 2
105
106 =item Getoverdues
107
108   $overdues = Getoverdues( { minimumdays => 1, maximumdays => 30 } );
109
110 Returns the list of all overdue books, with their itemtype.
111
112 C<$overdues> is a reference-to-array. Each element is a
113 reference-to-hash whose keys are the fields of the issues table in the
114 Koha database.
115
116 =cut
117
118 #'
119 sub Getoverdues {
120     my $params = shift;
121
122     my $dbh = C4::Context->dbh;
123     my $statement;
124     if ( C4::Context->preference('item-level_itypes') ) {
125         $statement = "
126 SELECT issues.*,items.itype as itemtype, items.homebranch FROM issues 
127 LEFT JOIN items USING (itemnumber)
128 WHERE date_due < now() 
129 ";
130     } else {
131         $statement = "
132 SELECT issues.*,biblioitems.itemtype,items.itype, items.homebranch  FROM issues 
133   LEFT JOIN items USING (itemnumber)
134   LEFT JOIN biblioitems USING (biblioitemnumber)
135   WHERE date_due < now() 
136 ";
137     }
138
139     my @bind_parameters;
140     if ( exists $params->{'minimumdays'} and exists $params->{'maximumdays'} ) {
141         $statement .= ' AND TO_DAYS( NOW() )-TO_DAYS( date_due ) BETWEEN ? and ? ';
142         push @bind_parameters, $params->{'minimumdays'}, $params->{'maximumdays'};
143     } elsif ( exists $params->{'minimumdays'} ) {
144         $statement .= ' AND ( TO_DAYS( NOW() )-TO_DAYS( date_due ) ) > ? ';
145         push @bind_parameters, $params->{'minimumdays'};
146     } elsif ( exists $params->{'maximumdays'} ) {
147         $statement .= ' AND ( TO_DAYS( NOW() )-TO_DAYS( date_due ) ) < ? ';
148         push @bind_parameters, $params->{'maximumdays'};
149     }
150     $statement .= 'ORDER BY borrowernumber';
151     my $sth = $dbh->prepare( $statement );
152     $sth->execute( @bind_parameters );
153     return $sth->fetchall_arrayref({});
154 }
155
156
157 =head2 checkoverdues
158
159 ( $count, $overdueitems )=checkoverdues( $borrowernumber, $dbh );
160
161 Not exported
162
163 =cut
164
165 sub checkoverdues {
166
167 # From Main.pm, modified to return a list of overdueitems, in addition to a count
168 #checks whether a borrower has overdue items
169     my ( $borrowernumber, $dbh ) = @_;
170     my @datearr = localtime;
171     my $today   =
172       ( $datearr[5] + 1900 ) . "-" . ( $datearr[4] + 1 ) . "-" . $datearr[3];
173     my @overdueitems;
174     my $count = 0;
175     my $sth   = $dbh->prepare(
176         "SELECT * FROM issues
177          LEFT JOIN items ON issues.itemnumber      = items.itemnumber
178          LEFT JOIN biblio ON items.biblionumber=biblio.biblionumber
179          LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber
180             WHERE issues.borrowernumber  = ?
181                 AND issues.date_due < ?"
182     );
183     $sth->execute( $borrowernumber, $today );
184     while ( my $data = $sth->fetchrow_hashref ) {
185         push( @overdueitems, $data );
186         $count++;
187     }
188     $sth->finish;
189     return ( $count, \@overdueitems );
190 }
191
192 =item CalcFine
193
194   ($amount, $chargename, $daycount, $daycounttotal) =
195     &CalcFine($item, $categorycode, $branch, $days_overdue, $description, $start_date, $end_date );
196
197 Calculates the fine for a book.
198
199 The issuingrules table in the Koha database is a fine matrix, listing
200 the penalties for each type of patron for each type of item and each branch (e.g., the
201 standard fine for books might be $0.50, but $1.50 for DVDs, or staff
202 members might get a longer grace period between the first and second
203 reminders that a book is overdue).
204
205
206 C<$item> is an item object (hashref).
207
208 C<$categorycode> is the category code (string) of the patron who currently has
209 the book.
210
211 C<$branchcode> is the library (string) whose issuingrules govern this transaction.
212
213 C<$days_overdue> is the number of days elapsed since the book's due date.
214   NOTE: supplying days_overdue is deprecated.
215
216 C<$start_date> & C<$end_date> are C4::Dates objects 
217 defining the date range over which to determine the fine.
218 Note that if these are defined, we ignore C<$difference> and C<$dues> , 
219 but retain these for backwards-comptibility with extant fines scripts.
220
221 Fines scripts should just supply the date range over which to calculate the fine.
222
223 C<&CalcFine> returns four values:
224
225 C<$amount> is the fine owed by the patron (see above).
226
227 C<$chargename> is the chargename field from the applicable record in
228 the categoryitem table, whatever that is.
229
230 C<$daycount> is the number of days between start and end dates, Calendar adjusted (where needed), 
231 minus any applicable grace period.
232
233 C<$daycounttotal> is C<$daycount> without consideration of grace period.
234
235 FIXME - What is chargename supposed to be ?
236
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.
239
240 =cut
241
242 #'
243 sub CalcFine {
244     my ( $item, $bortype, $branchcode, $difference ,$dues , $start_date, $end_date  ) = @_;
245         $debug and warn sprintf("CalcFine(%s, %s, %s, %s, %s, %s, %s)",
246                         ($item    ? '{item}' : 'UNDEF'), 
247                         ($bortype    || 'UNDEF'), 
248                         ($branchcode || 'UNDEF'), 
249                         ($difference || 'UNDEF'), 
250                         ($dues       || 'UNDEF'), 
251                         ($start_date ? ($start_date->output('iso') || 'Not a C4::Dates object') : 'UNDEF'), 
252                         (  $end_date ? (  $end_date->output('iso') || 'Not a C4::Dates object') : 'UNDEF')
253         );
254     my $dbh = C4::Context->dbh;
255     my $amount = 0;
256         my $daystocharge;
257         # get issuingrules (fines part will be used)
258     my $data = C4::Circulation::GetIssuingRule($bortype, $item->{'itemtype'},$branchcode);
259         if($difference) {
260                 # if $difference is supplied, the difference has already been calculated, but we still need to adjust for the calendar.
261         # use copy-pasted functions from calendar module.  (deprecated -- these functions will be removed from C4::Overdues ).
262             my $countspecialday    =    &GetSpecialHolidays($dues,$item->{itemnumber});
263             my $countrepeatableday = &GetRepeatableHolidays($dues,$item->{itemnumber},$difference);    
264             my $countalldayclosed  = $countspecialday + $countrepeatableday;
265             $daystocharge = $difference - $countalldayclosed;
266         } else {
267                 # if $difference is not supplied, we have C4::Dates objects giving us the date range, and we use the calendar module.
268                 if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
269                         my $calendar = C4::Calendar->new(  branchcode => $branchcode );
270                         $daystocharge = $calendar->daysBetween( $start_date, $end_date );
271                 } else {
272                         $daystocharge = Date_to_Days(split('-',$end_date->output('iso'))) - Date_to_Days(split('-',$start_date->output('iso')));
273                 }
274         }
275         # correct for grace period.
276         my $days_minus_grace = $daystocharge - $data->{'firstremind'};
277     if ($data->{'chargeperiod'} > 0 && $days_minus_grace > 0 ) { 
278         $amount = int($days_minus_grace / $data->{'chargeperiod'}) * $data->{'fine'};
279     } else {
280         # a zero (or null)  chargeperiod means no charge.
281     }
282         $amount = C4::Context->preference('maxFine') if(C4::Context->preference('maxFine') && ( $amount > C4::Context->preference('maxFine')));
283     return ( $amount, $data->{'chargename'}, $days_minus_grace, $daystocharge);
284 }
285
286
287 =item GetSpecialHolidays
288
289 &GetSpecialHolidays($date_dues,$itemnumber);
290
291 return number of special days  between date of the day and date due
292
293 C<$date_dues> is the envisaged date of book return.
294
295 C<$itemnumber> is the book's item number.
296
297 =cut
298
299 sub GetSpecialHolidays {
300 my ($date_dues,$itemnumber) = @_;
301 # calcul the today date
302 my $today = join "-", &Today();
303
304 # return the holdingbranch
305 my $iteminfo=GetIssuesIteminfo($itemnumber);
306 # use sql request to find all date between date_due and today
307 my $dbh = C4::Context->dbh;
308 my $query=qq|SELECT DATE_FORMAT(concat(year,'-',month,'-',day),'%Y-%m-%d')as date 
309 FROM `special_holidays`
310 WHERE DATE_FORMAT(concat(year,'-',month,'-',day),'%Y-%m-%d') >= ?
311 AND   DATE_FORMAT(concat(year,'-',month,'-',day),'%Y-%m-%d') <= ?
312 AND branchcode=?
313 |;
314 my @result=GetWdayFromItemnumber($itemnumber);
315 my @result_date;
316 my $wday;
317 my $dateinsec;
318 my $sth = $dbh->prepare($query);
319 $sth->execute($date_dues,$today,$iteminfo->{'branchcode'});
320
321 while ( my $special_date=$sth->fetchrow_hashref){
322     push (@result_date,$special_date);
323 }
324
325 my $specialdaycount=scalar(@result_date);
326
327     for (my $i=0;$i<scalar(@result_date);$i++){
328         $dateinsec=UnixDate($result_date[$i]->{'date'},"%o");
329         (undef,undef,undef,undef,undef,undef,$wday,undef,undef) =localtime($dateinsec);
330         for (my $j=0;$j<scalar(@result);$j++){
331             if ($wday == ($result[$j]->{'weekday'})){
332             $specialdaycount --;
333             }
334         }
335     }
336
337 return $specialdaycount;
338 }
339
340 =item GetRepeatableHolidays
341
342 &GetRepeatableHolidays($date_dues, $itemnumber, $difference,);
343
344 return number of day closed between date of the day and date due
345
346 C<$date_dues> is the envisaged date of book return.
347
348 C<$itemnumber> is item number.
349
350 C<$difference> numbers of between day date of the day and date due
351
352 =cut
353
354 sub GetRepeatableHolidays{
355 my ($date_dues,$itemnumber,$difference) = @_;
356 my $dateinsec=UnixDate($date_dues,"%o");
357 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =localtime($dateinsec);
358 my @result=GetWdayFromItemnumber($itemnumber);
359 my @dayclosedcount;
360 my $j;
361
362 for (my $i=0;$i<scalar(@result);$i++){
363     my $k=$wday;
364
365         for ( $j=0;$j<$difference;$j++){
366             if ($result[$i]->{'weekday'} == $k)
367                     {
368                     push ( @dayclosedcount ,$k);
369             }
370         $k++;
371         ($k=0) if($k eq 7);
372         }
373     }
374 return scalar(@dayclosedcount);
375 }
376
377
378 =item GetWayFromItemnumber
379
380 &Getwdayfromitemnumber($itemnumber);
381
382 return the different week day from repeatable_holidays table
383
384 C<$itemnumber> is  item number.
385
386 =cut
387
388 sub GetWdayFromItemnumber{
389 my($itemnumber)=@_;
390 my $iteminfo=GetIssuesIteminfo($itemnumber);
391 my @result;
392 my $dbh = C4::Context->dbh;
393 my $query = qq|SELECT weekday  
394     FROM repeatable_holidays
395     WHERE branchcode=?
396 |;
397 my $sth = $dbh->prepare($query);
398     #  print $query;
399
400 $sth->execute($iteminfo->{'branchcode'});
401 while ( my $weekday=$sth->fetchrow_hashref){
402     push (@result,$weekday);
403     }
404 return @result;
405 }
406
407
408 =item GetIssuesIteminfo
409
410 &GetIssuesIteminfo($itemnumber);
411
412 return all data from issues about item
413
414 C<$itemnumber> is  item number.
415
416 =cut
417
418 sub GetIssuesIteminfo{
419 my($itemnumber)=@_;
420 my $dbh = C4::Context->dbh;
421 my $query = qq|SELECT *  
422     FROM issues
423     WHERE itemnumber=?
424 |;
425 my $sth = $dbh->prepare($query);
426 $sth->execute($itemnumber);
427 my ($issuesinfo)=$sth->fetchrow_hashref;
428 return $issuesinfo;
429 }
430
431
432 =item UpdateFine
433
434   &UpdateFine($itemnumber, $borrowernumber, $amount, $type, $description);
435
436 (Note: the following is mostly conjecture and guesswork.)
437
438 Updates the fine owed on an overdue book.
439
440 C<$itemnumber> is the book's item number.
441
442 C<$borrowernumber> is the borrower number of the patron who currently
443 has the book on loan.
444
445 C<$amount> is the current amount owed by the patron.
446
447 C<$type> will be used in the description of the fine.
448
449 C<$description> is a string that must be present in the description of
450 the fine. I think this is expected to be a date in DD/MM/YYYY format.
451
452 C<&UpdateFine> looks up the amount currently owed on the given item
453 and sets it to C<$amount>, creating, if necessary, a new entry in the
454 accountlines table of the Koha database.
455
456 =cut
457
458 #
459 # Question: Why should the caller have to
460 # specify both the item number and the borrower number? A book can't
461 # be on loan to two different people, so the item number should be
462 # sufficient.
463 #
464 # Possible Answer: You might update a fine for a damaged item, *after* it is returned.
465 #
466 sub UpdateFine {
467     my ( $itemnum, $borrowernumber, $amount, $type, $due ) = @_;
468         $debug and warn "UpdateFine($itemnum, $borrowernumber, $amount, " . ($type||'""') . ", $due) called";
469     my $dbh = C4::Context->dbh;
470     # FIXME - What exactly is this query supposed to do? It looks up an
471     # entry in accountlines that matches the given item and borrower
472     # numbers, where the description contains $due, and where the
473     # account type has one of several values, but what does this _mean_?
474     # Does it look up existing fines for this item?
475     # FIXME - What are these various account types? ("FU", "O", "F", "M")
476         #       "L"   is LOST item
477         #   "A"   is Account Management Fee
478         #   "N"   is New Card
479         #   "M"   is Sundry
480         #   "O"   is Overdue ??
481         #   "F"   is Fine ??
482         #   "FU"  is Fine UPDATE??
483         #       "Pay" is Payment
484         #   "REF" is Cash Refund
485     my $sth = $dbh->prepare(
486         "SELECT * FROM accountlines 
487                 WHERE itemnumber=?
488                 AND   borrowernumber=?
489                 AND   accounttype IN ('FU','O','F','M')
490                 AND   description like ? "
491     );
492     $sth->execute( $itemnum, $borrowernumber, "%$due%" );
493
494     if ( my $data = $sth->fetchrow_hashref ) {
495
496                 # we're updating an existing fine.  Only modify if we're adding to the charge.
497         # Note that in the current implementation, you cannot pay against an accruing fine
498         # (i.e. , of accounttype 'FU').  Doing so will break accrual.
499         if ( $data->{'amount'} != $amount ) {
500             my $diff = $amount - $data->{'amount'};
501             $diff = 0 if ( $data->{amount} > $amount);
502             my $out  = $data->{'amountoutstanding'} + $diff;
503             my $query = "
504                 UPDATE accountlines
505                                 SET date=now(), amount=?, amountoutstanding=?,
506                                         lastincrement=?, accounttype='FU'
507                                 WHERE borrowernumber=?
508                                 AND   itemnumber=?
509                                 AND   accounttype IN ('FU','O')
510                                 AND   description LIKE ?
511                                 LIMIT 1 ";
512             my $sth2 = $dbh->prepare($query);
513                         # FIXME: BOGUS query cannot ensure uniqueness w/ LIKE %x% !!!
514                         #               LIMIT 1 added to prevent multiple affected lines
515                         # FIXME: accountlines table needs unique key!! Possibly a combo of borrowernumber and accountline.  
516                         #               But actually, we should just have a regular autoincrementing PK and forget accountline,
517                         #               including the bogus getnextaccountno function (doesn't prevent conflict on simultaneous ops).
518                         # FIXME: Why only 2 account types here?
519                         $debug and print STDERR "UpdateFine query: $query\n" .
520                                 "w/ args: $amount, $out, $diff, $data->{'borrowernumber'}, $data->{'itemnumber'}, \"\%$due\%\"\n";
521             $sth2->execute($amount, $out, $diff, $data->{'borrowernumber'}, $data->{'itemnumber'}, "%$due%");
522         } else {
523             #      print "no update needed $data->{'amount'}"
524         }
525     } else {
526         my $sth4 = $dbh->prepare(
527             "SELECT title FROM biblio LEFT JOIN items ON biblio.biblionumber=items.biblionumber WHERE items.itemnumber=?"
528         );
529         $sth4->execute($itemnum);
530         my $title = $sth4->fetchrow;
531
532 #         #   print "not in account";
533 #         my $sth3 = $dbh->prepare("Select max(accountno) from accountlines");
534 #         $sth3->execute;
535
536 #         # FIXME - Make $accountno a scalar.
537 #         my @accountno = $sth3->fetchrow_array;
538 #         $sth3->finish;
539 #         $accountno[0]++;
540 # begin transaction
541                 my $nextaccntno = C4::Accounts::getnextacctno($borrowernumber);
542                 my $desc = ($type ? "$type " : '') . "$title $due";     # FIXEDME, avoid whitespace prefix on empty $type
543                 my $query = "INSERT INTO accountlines
544                     (borrowernumber,itemnumber,date,amount,description,accounttype,amountoutstanding,lastincrement,accountno)
545                             VALUES (?,?,now(),?,?,'FU',?,?,?)";
546                 my $sth2 = $dbh->prepare($query);
547                 $debug and print STDERR "UpdateFine query: $query\nw/ args: $borrowernumber, $itemnum, $amount, $desc, $amount, $amount, $nextaccntno\n";
548         $sth2->execute($borrowernumber, $itemnum, $amount, $desc, $amount, $amount, $nextaccntno);
549     }
550     # logging action
551     &logaction(
552         "FINES",
553         $type,
554         $borrowernumber,
555         "due=".$due."  amount=".$amount." itemnumber=".$itemnum
556         ) if C4::Context->preference("FinesLog");
557 }
558
559 =item BorType
560
561   $borrower = &BorType($borrowernumber);
562
563 Looks up a patron by borrower number.
564
565 C<$borrower> is a reference-to-hash whose keys are all of the fields
566 from the borrowers and categories tables of the Koha database. Thus,
567 C<$borrower> contains all information about both the borrower and
568 category he or she belongs to.
569
570 =cut
571
572 #'
573 sub BorType {
574     my ($borrowernumber) = @_;
575     my $dbh              = C4::Context->dbh;
576     my $sth              = $dbh->prepare(
577         "SELECT * from borrowers 
578       LEFT JOIN categories ON borrowers.categorycode=categories.categorycode 
579       WHERE borrowernumber=?"
580     );
581     $sth->execute($borrowernumber);
582     my $data = $sth->fetchrow_hashref;
583     $sth->finish;
584     return ($data);
585 }
586
587 =item ReplacementCost
588
589   $cost = &ReplacementCost($itemnumber);
590
591 Returns the replacement cost of the item with the given item number.
592
593 =cut
594
595 #'
596 sub ReplacementCost {
597     my ($itemnum) = @_;
598     my $dbh       = C4::Context->dbh;
599     my $sth       =
600       $dbh->prepare("Select replacementprice from items where itemnumber=?");
601     $sth->execute($itemnum);
602
603     # FIXME - Use fetchrow_array or something.
604     my $data = $sth->fetchrow_hashref;
605     $sth->finish;
606     return ( $data->{'replacementprice'} );
607 }
608
609 =item GetFine
610
611 $data->{'sum(amountoutstanding)'} = &GetFine($itemnum,$borrowernumber);
612
613 return the total of fine
614
615 C<$itemnum> is item number
616
617 C<$borrowernumber> is the borrowernumber
618
619 =cut 
620
621
622 sub GetFine {
623     my ( $itemnum, $borrowernumber ) = @_;
624     my $dbh   = C4::Context->dbh();
625     my $query = "SELECT sum(amountoutstanding) FROM accountlines 
626     where accounttype like 'F%'  
627   AND amountoutstanding > 0 AND itemnumber = ? AND borrowernumber=?";
628     my $sth = $dbh->prepare($query);
629     $sth->execute( $itemnum, $borrowernumber );
630     my $data = $sth->fetchrow_hashref();
631     return ( $data->{'sum(amountoutstanding)'} );
632 }
633
634
635 =item GetIssuingRules
636
637 FIXME - This sub should be deprecated and removed.
638 It ignores branch and defaults.
639
640 $data = &GetIssuingRules($itemtype,$categorycode);
641
642 Looks up for all issuingrules an item info 
643
644 C<$itemnumber> is a reference-to-hash whose keys are all of the fields
645 from the borrowers and categories tables of the Koha database. Thus,
646
647 C<$categorycode> contains  information about borrowers category 
648
649 C<$data> contains all information about both the borrower and
650 category he or she belongs to.
651 =cut 
652
653 sub GetIssuingRules {
654         warn "GetIssuingRules is deprecated: use GetIssuingRule from C4::Circulation instead.";
655    my ($itemtype,$categorycode)=@_;
656    my $dbh   = C4::Context->dbh();    
657    my $query=qq|SELECT * 
658         FROM issuingrules
659         WHERE issuingrules.itemtype=?
660             AND issuingrules.categorycode=?
661         |;
662     my $sth = $dbh->prepare($query);
663     #  print $query;
664     $sth->execute($itemtype,$categorycode);
665     return $sth->fetchrow_hashref;
666 }
667
668
669 sub ReplacementCost2 {
670     my ( $itemnum, $borrowernumber ) = @_;
671     my $dbh   = C4::Context->dbh();
672     my $query = "SELECT amountoutstanding 
673          FROM accountlines
674              WHERE accounttype like 'L'
675          AND amountoutstanding > 0
676          AND itemnumber = ?
677          AND borrowernumber= ?";
678     my $sth = $dbh->prepare($query);
679     $sth->execute( $itemnum, $borrowernumber );
680     my $data = $sth->fetchrow_hashref();
681     return ( $data->{'amountoutstanding'} );
682 }
683
684
685 =item GetNextIdNotify
686
687 ($result) = &GetNextIdNotify($reference);
688
689 Returns the new file number
690
691 C<$result> contains the next file number
692
693 C<$reference> contains the beggining of file number
694
695 =cut
696
697
698
699 sub GetNextIdNotify {
700 my ($reference)=@_;
701 my $query=qq|SELECT max(notify_id) 
702          FROM accountlines
703          WHERE notify_id  like \"$reference%\"
704          |;
705 # AND borrowernumber=?|;   
706 my $dbh = C4::Context->dbh;
707 my $sth=$dbh->prepare($query);
708 $sth->execute();
709 my $result=$sth->fetchrow;
710 $sth->finish;
711 my $count;
712     if ($result eq '')
713     {
714     ($result=$reference."01")  ;
715     }else
716     {
717     $count=substr($result,6)+1;
718      
719     if($count<10){
720      ($count = "0".$count);
721      }
722      $result=$reference.$count;
723      }
724 return $result;
725 }
726
727
728 =item NumberNotifyId
729
730 (@notify) = &NumberNotifyId($borrowernumber);
731
732 Returns amount for all file per borrowers
733 C<@notify> array contains all file per borrowers
734
735 C<$notify_id> contains the file number for the borrower number nad item number
736
737 =cut
738
739 sub NumberNotifyId{
740     my ($borrowernumber)=@_;
741     my $dbh = C4::Context->dbh;
742     my $query=qq|    SELECT distinct(notify_id)
743             FROM accountlines
744             WHERE borrowernumber=?|;
745     my @notify;
746     my $sth=$dbh->prepare($query);
747         $sth->execute($borrowernumber);
748           while ( my ($numberofnotify)=$sth->fetchrow){
749     push (@notify,$numberofnotify);
750     }
751     $sth->finish;
752
753     return (@notify);
754
755 }
756
757 =item AmountNotify
758
759 ($totalnotify) = &AmountNotify($notifyid);
760
761 Returns amount for all file per borrowers
762 C<$notifyid> is the file number
763
764 C<$totalnotify> contains amount of a file
765
766 C<$notify_id> contains the file number for the borrower number and item number
767
768 =cut
769
770 sub AmountNotify{
771     my ($notifyid,$borrowernumber)=@_;
772     my $dbh = C4::Context->dbh;
773     my $query=qq|    SELECT sum(amountoutstanding)
774             FROM accountlines
775             WHERE notify_id=? AND borrowernumber = ?|;
776     my $sth=$dbh->prepare($query);
777         $sth->execute($notifyid,$borrowernumber);
778         my $totalnotify=$sth->fetchrow;
779     $sth->finish;
780     return ($totalnotify);
781 }
782
783
784 =item GetNotifyId
785
786 ($notify_id) = &GetNotifyId($borrowernumber,$itemnumber);
787
788 Returns the file number per borrower and itemnumber
789
790 C<$borrowernumber> is a reference-to-hash whose keys are all of the fields
791 from the items tables of the Koha database. Thus,
792
793 C<$itemnumber> contains the borrower categorycode
794
795 C<$notify_id> contains the file number for the borrower number nad item number
796
797 =cut
798
799  sub GetNotifyId {
800  my ($borrowernumber,$itemnumber)=@_;
801  my $query=qq|SELECT notify_id 
802            FROM accountlines
803            WHERE borrowernumber=?
804           AND itemnumber=?
805            AND (accounttype='FU' or accounttype='O')|;
806  my $dbh = C4::Context->dbh;
807  my $sth=$dbh->prepare($query);
808  $sth->execute($borrowernumber,$itemnumber);
809  my ($notify_id)=$sth->fetchrow;
810  $sth->finish;
811  return ($notify_id);
812
813  }
814
815 =item CreateItemAccountLine
816
817 () = &CreateItemAccountLine($borrowernumber,$itemnumber,$date,$amount,$description,$accounttype,$amountoutstanding,$timestamp,$notify_id,$level);
818
819 update the account lines with file number or with file level
820
821 C<$items> is a reference-to-hash whose keys are all of the fields
822 from the items tables of the Koha database. Thus,
823
824 C<$itemnumber> contains the item number
825
826 C<$borrowernumber> contains the borrower number
827
828 C<$date> contains the date of the day
829
830 C<$amount> contains item price
831
832 C<$description> contains the descritpion of accounttype 
833
834 C<$accounttype> contains the account type
835
836 C<$amountoutstanding> contains the $amountoutstanding 
837
838 C<$timestamp> contains the timestamp with time and the date of the day
839
840 C<$notify_id> contains the file number
841
842 C<$level> contains the file level
843
844
845 =cut
846
847  sub CreateItemAccountLine {
848   my ($borrowernumber,$itemnumber,$date,$amount,$description,$accounttype,$amountoutstanding,$timestamp,$notify_id,$level)=@_;
849   my $dbh = C4::Context->dbh;
850   my $nextaccntno = C4::Accounts::getnextacctno($borrowernumber);
851    my $query= "INSERT into accountlines  
852          (borrowernumber,accountno,itemnumber,date,amount,description,accounttype,amountoutstanding,timestamp,notify_id,notify_level)
853           VALUES
854              (?,?,?,?,?,?,?,?,?,?,?)";
855   
856   
857   my $sth=$dbh->prepare($query);
858   $sth->execute($borrowernumber,$nextaccntno,$itemnumber,$date,$amount,$description,$accounttype,$amountoutstanding,$timestamp,$notify_id,$level);
859   $sth->finish;
860  }
861
862 =item UpdateAccountLines
863
864 () = &UpdateAccountLines($notify_id,$notify_level,$borrowernumber,$itemnumber);
865
866 update the account lines with file number or with file level
867
868 C<$items> is a reference-to-hash whose keys are all of the fields
869 from the items tables of the Koha database. Thus,
870
871 C<$itemnumber> contains the item number
872
873 C<$notify_id> contains the file number
874
875 C<$notify_level> contains the file level
876
877 C<$borrowernumber> contains the borrowernumber
878
879 =cut
880
881 sub UpdateAccountLines {
882 my ($notify_id,$notify_level,$borrowernumber,$itemnumber)=@_;
883 my $query;
884 if ($notify_id eq '')
885 {
886
887     $query=qq|UPDATE accountlines
888     SET  notify_level=?
889     WHERE borrowernumber=? AND itemnumber=?
890     AND (accounttype='FU' or accounttype='O')|;
891 }else
892 {
893     $query=qq|UPDATE accountlines
894      SET notify_id=?, notify_level=?
895            WHERE borrowernumber=?
896     AND itemnumber=?
897         AND (accounttype='FU' or accounttype='O')|;
898 }
899  my $dbh = C4::Context->dbh;
900  my $sth=$dbh->prepare($query);
901
902 if ($notify_id eq '')
903 {
904     $sth->execute($notify_level,$borrowernumber,$itemnumber);
905 }else
906 {
907     $sth->execute($notify_id,$notify_level,$borrowernumber,$itemnumber);
908 }
909  $sth->finish;
910
911 }
912
913
914 =item GetItems
915
916 ($items) = &GetItems($itemnumber);
917
918 Returns the list of all delays from overduerules.
919
920 C<$items> is a reference-to-hash whose keys are all of the fields
921 from the items tables of the Koha database. Thus,
922
923 C<$itemnumber> contains the borrower categorycode
924
925 =cut
926
927 sub GetItems {
928     my($itemnumber) = @_;
929     my $query=qq|SELECT *
930              FROM items
931               WHERE itemnumber=?|;
932         my $dbh = C4::Context->dbh;
933         my $sth=$dbh->prepare($query);
934         $sth->execute($itemnumber);
935         my ($items)=$sth->fetchrow_hashref;
936         $sth->finish;
937     return($items);
938 }
939
940 =item GetOverdueDelays
941
942 (@delays) = &GetOverdueDelays($categorycode);
943
944 Returns the list of all delays from overduerules.
945
946 C<@delays> it's an array contains the three delays from overduerules table
947
948 C<$categorycode> contains the borrower categorycode
949
950 =cut
951
952 sub GetOverdueDelays {
953     my($category) = @_;
954     my $dbh = C4::Context->dbh;
955         my $query=qq|SELECT delay1,delay2,delay3
956                 FROM overduerules
957                 WHERE categorycode=?|;
958     my $sth=$dbh->prepare($query);
959         $sth->execute($category);
960         my (@delays)=$sth->fetchrow_array;
961         $sth->finish;
962         return(@delays);
963 }
964
965 =head2 GetBranchcodesWithOverdueRules
966
967 =over 4
968
969 my @branchcodes = C4::Overdues::GetBranchcodesWithOverdueRules()
970
971 returns a list of branch codes for branches with overdue rules defined.
972
973 =back
974
975 =cut
976
977 sub GetBranchcodesWithOverdueRules {
978     my $dbh               = C4::Context->dbh;
979     my $rqoverduebranches = $dbh->prepare("SELECT DISTINCT branchcode FROM overduerules WHERE delay1 IS NOT NULL AND branchcode <> ''");
980     $rqoverduebranches->execute;
981     my @branches = map { shift @$_ } @{ $rqoverduebranches->fetchall_arrayref };
982     $rqoverduebranches->finish;
983     return @branches;
984 }
985
986 =item CheckAccountLineLevelInfo
987
988 ($exist) = &CheckAccountLineLevelInfo($borrowernumber,$itemnumber,$accounttype,notify_level);
989
990 Check and Returns the list of all overdue books.
991
992 C<$exist> contains number of line in accounlines
993 with the same .biblionumber,itemnumber,accounttype,and notify_level
994
995 C<$borrowernumber> contains the borrower number
996
997 C<$itemnumber> contains item number
998
999 C<$accounttype> contains account type
1000
1001 C<$notify_level> contains the accountline level 
1002
1003
1004 =cut
1005
1006 sub CheckAccountLineLevelInfo {
1007     my($borrowernumber,$itemnumber,$level) = @_;
1008     my $dbh = C4::Context->dbh;
1009         my $query=    qq|SELECT count(*)
1010             FROM accountlines
1011             WHERE borrowernumber =?
1012             AND itemnumber = ?
1013             AND notify_level=?|;
1014     my $sth=$dbh->prepare($query);
1015         $sth->execute($borrowernumber,$itemnumber,$level);
1016         my ($exist)=$sth->fetchrow;
1017         $sth->finish;
1018         return($exist);
1019 }
1020
1021 =item GetOverduerules
1022
1023 ($overduerules) = &GetOverduerules($categorycode);
1024
1025 Returns the value of borrowers (debarred or not) with notify level
1026
1027 C<$overduerules> return value of debbraed field in overduerules table
1028
1029 C<$category> contains the borrower categorycode
1030
1031 C<$notify_level> contains the notify level
1032 =cut
1033
1034
1035 sub GetOverduerules{
1036     my($category,$notify_level) = @_;
1037     my $dbh = C4::Context->dbh;
1038         my $query=qq|SELECT debarred$notify_level
1039              FROM overduerules
1040              WHERE categorycode=?|;
1041     my $sth=$dbh->prepare($query);
1042         $sth->execute($category);
1043         my ($overduerules)=$sth->fetchrow;
1044         $sth->finish;
1045         return($overduerules);
1046 }
1047
1048
1049 =item CheckBorrowerDebarred
1050
1051 ($debarredstatus) = &CheckBorrowerDebarred($borrowernumber);
1052
1053 Check if the borrowers is already debarred
1054
1055 C<$debarredstatus> return 0 for not debarred and return 1 for debarred
1056
1057 C<$borrowernumber> contains the borrower number
1058
1059 =cut
1060
1061
1062 sub CheckBorrowerDebarred{
1063     my($borrowernumber) = @_;
1064     my $dbh = C4::Context->dbh;
1065         my $query=qq|SELECT debarred
1066               FROM borrowers
1067              WHERE borrowernumber=?
1068             |;
1069     my $sth=$dbh->prepare($query);
1070         $sth->execute($borrowernumber);
1071         my ($debarredstatus)=$sth->fetchrow;
1072         $sth->finish;
1073         if ($debarredstatus eq '1'){
1074     return(1);}
1075     else{
1076     return(0);
1077     }
1078 }
1079
1080 =item UpdateBorrowerDebarred
1081
1082 ($borrowerstatut) = &UpdateBorrowerDebarred($borrowernumber);
1083
1084 update status of borrowers in borrowers table (field debarred)
1085
1086 C<$borrowernumber> borrower number
1087
1088 =cut
1089
1090 sub UpdateBorrowerDebarred{
1091     my($borrowernumber) = @_;
1092     my $dbh = C4::Context->dbh;
1093         my $query=qq|UPDATE borrowers
1094              SET debarred='1'
1095                      WHERE borrowernumber=?
1096             |;
1097     my $sth=$dbh->prepare($query);
1098         $sth->execute($borrowernumber);
1099         $sth->finish;
1100         return 1;
1101 }
1102
1103 =item CheckExistantNotifyid
1104
1105   ($exist) = &CheckExistantNotifyid($borrowernumber,$itemnumber,$accounttype,$notify_id);
1106
1107 Check and Returns the notify id if exist else return 0.
1108
1109 C<$exist> contains a notify_id 
1110
1111 C<$borrowernumber> contains the borrower number
1112
1113 C<$date_due> contains the date of item return 
1114
1115
1116 =cut
1117
1118 sub CheckExistantNotifyid {
1119      my($borrowernumber,$date_due) = @_;
1120      my $dbh = C4::Context->dbh;
1121          my $query =  qq|SELECT notify_id FROM accountlines 
1122              LEFT JOIN issues ON issues.itemnumber= accountlines.itemnumber
1123              WHERE accountlines.borrowernumber =?
1124               AND date_due = ?|;
1125     my $sth=$dbh->prepare($query);
1126          $sth->execute($borrowernumber,$date_due);
1127          my ($exist)=$sth->fetchrow;
1128          $sth->finish;
1129          if ($exist eq '')
1130     {
1131     return(0);
1132     }else
1133         {
1134     return($exist);
1135     }
1136 }
1137
1138 =item CheckAccountLineItemInfo
1139
1140   ($exist) = &CheckAccountLineItemInfo($borrowernumber,$itemnumber,$accounttype,$notify_id);
1141
1142 Check and Returns the list of all overdue items from the same file number(notify_id).
1143
1144 C<$exist> contains number of line in accounlines
1145 with the same .biblionumber,itemnumber,accounttype,notify_id
1146
1147 C<$borrowernumber> contains the borrower number
1148
1149 C<$itemnumber> contains item number
1150
1151 C<$accounttype> contains account type
1152
1153 C<$notify_id> contains the file number 
1154
1155 =cut
1156
1157 sub CheckAccountLineItemInfo {
1158      my($borrowernumber,$itemnumber,$accounttype,$notify_id) = @_;
1159      my $dbh = C4::Context->dbh;
1160          my $query =  qq|SELECT count(*) FROM accountlines
1161              WHERE borrowernumber =?
1162              AND itemnumber = ?
1163               AND accounttype= ?
1164             AND notify_id = ?|;
1165     my $sth=$dbh->prepare($query);
1166          $sth->execute($borrowernumber,$itemnumber,$accounttype,$notify_id);
1167          my ($exist)=$sth->fetchrow;
1168          $sth->finish;
1169          return($exist);
1170  }
1171
1172 =head2 CheckItemNotify
1173
1174 Sql request to check if the document has alreday been notified
1175 this function is not exported, only used with GetOverduesForBranch
1176
1177 =cut
1178
1179 sub CheckItemNotify {
1180         my ($notify_id,$notify_level,$itemnumber) = @_;
1181         my $dbh = C4::Context->dbh;
1182         my $sth = $dbh->prepare("
1183           SELECT COUNT(*) FROM notifys
1184  WHERE notify_id  = ?
1185  AND notify_level  = ? 
1186   AND  itemnumber  =  ? ");
1187  $sth->execute($notify_id,$notify_level,$itemnumber);
1188         my $notified = $sth->fetchrow;
1189 $sth->finish;
1190 return ($notified);
1191 }
1192
1193 =head2 GetOverduesForBranch
1194
1195 Sql request for display all information for branchoverdues.pl
1196 2 possibilities : with or without location .
1197 display is filtered by branch
1198
1199 =cut
1200
1201 sub GetOverduesForBranch {
1202     my ( $branch, $location) = @_;
1203         my $itype_link =  (C4::Context->preference('item-level_itypes')) ?  " items.itype " :  " biblioitems.itemtype ";
1204     if ( not $location ) {
1205         my $dbh = C4::Context->dbh;
1206         my $sth = $dbh->prepare("
1207             SELECT 
1208                 borrowers.surname,
1209                 borrowers.firstname,
1210                 biblio.title,
1211                 itemtypes.description,
1212                 issues.date_due,
1213                 issues.returndate,
1214                 branches.branchname,
1215                 items.barcode,
1216                 borrowers.phone,
1217                 borrowers.email,
1218                 items.itemcallnumber,
1219                 borrowers.borrowernumber,
1220                 items.itemnumber,
1221                 biblio.biblionumber,
1222                 issues.branchcode,
1223                 accountlines.notify_id,
1224                 accountlines.notify_level,
1225                 items.location,
1226                 accountlines.amountoutstanding
1227             FROM  accountlines
1228             LEFT JOIN issues ON issues.itemnumber = accountlines.itemnumber AND issues.borrowernumber = accountlines.borrowernumber
1229             LEFT JOIN borrowers ON borrowers.borrowernumber = accountlines.borrowernumber
1230             LEFT JOIN items ON items.itemnumber = issues.itemnumber
1231             LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1232             LEFT JOIN biblioitems ON biblioitems.biblioitemnumber=items.biblioitemnumber
1233             LEFT JOIN itemtypes ON itemtypes.itemtype = $itype_link
1234             LEFT JOIN branches ON branches.branchcode = issues.branchcode
1235             WHERE ( accountlines.amountoutstanding  != '0.000000')
1236               AND ( accountlines.accounttype  = 'FU')
1237               AND (issues.branchcode = ?)
1238               AND (issues.date_due <= NOW())
1239             ORDER BY  borrowers.surname
1240         ");
1241         $sth->execute($branch);
1242         my @getoverdues;
1243         my $i = 0;
1244         while ( my $data = $sth->fetchrow_hashref ) {
1245         #check if the document has already been notified
1246         my $countnotify = CheckItemNotify($data->{'notify_id'},$data->{'notify_level'},$data->{'itemnumber'});
1247         if ($countnotify eq '0'){
1248             $getoverdues[$i] = $data;
1249             $i++;
1250          }
1251         }
1252         return (@getoverdues);
1253         $sth->finish;
1254     }
1255     else {
1256         my $dbh = C4::Context->dbh;
1257         my $sth = $dbh->prepare( "
1258             SELECT  borrowers.surname,
1259                     borrowers.firstname,
1260                     biblio.title,
1261                     itemtypes.description,
1262                     issues.date_due,
1263                     issues.returndate,
1264                     branches.branchname,
1265                     items.barcode,
1266                     borrowers.phone,
1267                     borrowers.email,
1268                     items.itemcallnumber,
1269                     borrowers.borrowernumber,
1270                     items.itemnumber,
1271                     biblio.biblionumber,
1272                     issues.branchcode,
1273                     accountlines.notify_id,
1274                     accountlines.notify_level,
1275                     items.location,
1276                     accountlines.amountoutstanding
1277             FROM  accountlines
1278             LEFT JOIN issues ON issues.itemnumber = accountlines.itemnumber AND issues.borrowernumber = accountlines.borrowernumber
1279             LEFT JOIN borrowers ON borrowers.borrowernumber = accountlines.borrowernumber
1280             LEFT JOIN items ON items.itemnumber = issues.itemnumber
1281             LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1282             LEFT JOIN biblioitems ON biblioitems.biblioitemnumber=items.biblioitemnumber
1283             LEFT JOIN itemtypes ON itemtypes.itemtype = $itype_link
1284             LEFT JOIN branches ON branches.branchcode = issues.branchcode
1285            WHERE ( accountlines.amountoutstanding  != '0.000000')
1286              AND ( accountlines.accounttype  = 'FU')
1287              AND (issues.branchcode = ? AND items.location = ?)
1288              AND (issues.date_due <= NOW())
1289            ORDER BY  borrowers.surname
1290         " );
1291         $sth->execute( $branch, $location);
1292         my @getoverdues;
1293         my $i = 0;
1294         while ( my $data = $sth->fetchrow_hashref ) {
1295         #check if the document has already been notified
1296           my $countnotify = CheckItemNotify($data->{'notify_id'},$data->{'notify_level'},$data->{'itemnumber'});
1297           if ($countnotify eq '0'){                     
1298                 $getoverdues[$i] = $data;
1299                  $i++;
1300          }
1301         }
1302         $sth->finish;
1303         return (@getoverdues); 
1304     }
1305 }
1306
1307
1308 =head2 AddNotifyLine
1309
1310 &AddNotifyLine($borrowernumber, $itemnumber, $overduelevel, $method, $notifyId)
1311
1312 Creat a line into notify, if the method is phone, the notification_send_date is implemented to
1313
1314 =cut
1315
1316 sub AddNotifyLine {
1317     my ( $borrowernumber, $itemnumber, $overduelevel, $method, $notifyId ) = @_;
1318     if ( $method eq "phone" ) {
1319         my $dbh = C4::Context->dbh;
1320         my $sth = $dbh->prepare(
1321             "INSERT INTO notifys (borrowernumber,itemnumber,notify_date,notify_send_date,notify_level,method,notify_id)
1322         VALUES (?,?,now(),now(),?,?,?)"
1323         );
1324         $sth->execute( $borrowernumber, $itemnumber, $overduelevel, $method,
1325             $notifyId );
1326         $sth->finish;
1327     }
1328     else {
1329         my $dbh = C4::Context->dbh;
1330         my $sth = $dbh->prepare(
1331             "INSERT INTO notifys (borrowernumber,itemnumber,notify_date,notify_level,method,notify_id)
1332         VALUES (?,?,now(),?,?,?)"
1333         );
1334         $sth->execute( $borrowernumber, $itemnumber, $overduelevel, $method,
1335             $notifyId );
1336         $sth->finish;
1337     }
1338     return 1;
1339 }
1340
1341 =head2 RemoveNotifyLine
1342
1343 &RemoveNotifyLine( $borrowernumber, $itemnumber, $notify_date );
1344
1345 Cancel a notification
1346
1347 =cut
1348
1349 sub RemoveNotifyLine {
1350     my ( $borrowernumber, $itemnumber, $notify_date ) = @_;
1351     my $dbh = C4::Context->dbh;
1352     my $sth = $dbh->prepare(
1353         "DELETE FROM notifys 
1354             WHERE
1355             borrowernumber=?
1356             AND itemnumber=?
1357             AND notify_date=?"
1358     );
1359     $sth->execute( $borrowernumber, $itemnumber, $notify_date );
1360     $sth->finish;
1361     return 1;
1362 }
1363
1364 1;
1365 __END__
1366
1367 =back
1368
1369 =head1 AUTHOR
1370
1371 Koha Developement team <info@koha.org>
1372
1373 =cut