Bug 7309 - Add NORMARCslim2intranetDetail.xsl for detail view in intranet
[koha.git] / C4 / Overdues.pm
blob660e10b830b055a4202207b54e3141db20f29e59
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 C4::Circulation;
27 use C4::Context;
28 use C4::Accounts;
29 use C4::Log; # logaction
30 use C4::Debug;
32 use vars qw($VERSION @ISA @EXPORT);
34 BEGIN {
35 # set the version for version checking
36 $VERSION = 3.01;
37 require Exporter;
38 @ISA = qw(Exporter);
39 # subs to rename (and maybe merge some...)
40 push @EXPORT, qw(
41 &CalcFine
42 &Getoverdues
43 &checkoverdues
44 &CheckAccountLineLevelInfo
45 &CheckAccountLineItemInfo
46 &CheckExistantNotifyid
47 &GetNextIdNotify
48 &GetNotifyId
49 &NumberNotifyId
50 &AmountNotify
51 &UpdateAccountLines
52 &UpdateFine
53 &GetOverdueDelays
54 &GetOverduerules
55 &GetFine
56 &CreateItemAccountLine
57 &ReplacementCost2
59 &CheckItemNotify
60 &GetOverduesForBranch
61 &RemoveNotifyLine
62 &AddNotifyLine
64 # subs to remove
65 push @EXPORT, qw(
66 &BorType
69 # check that an equivalent don't exist already before moving
71 # subs to move to Circulation.pm
72 push @EXPORT, qw(
73 &GetIssuesIteminfo
76 # &GetIssuingRules - delete.
77 # use C4::Circulation::GetIssuingRule instead.
79 # subs to move to Members.pm
80 push @EXPORT, qw(
81 &CheckBorrowerDebarred
83 # subs to move to Biblio.pm
84 push @EXPORT, qw(
85 &GetItems
86 &ReplacementCost
90 =head1 NAME
92 C4::Circulation::Fines - Koha module dealing with fines
94 =head1 SYNOPSIS
96 use C4::Overdues;
98 =head1 DESCRIPTION
100 This module contains several functions for dealing with fines for
101 overdue items. It is primarily used by the 'misc/fines2.pl' script.
103 =head1 FUNCTIONS
105 =head2 Getoverdues
107 $overdues = Getoverdues( { minimumdays => 1, maximumdays => 30 } );
109 Returns the list of all overdue books, with their itemtype.
111 C<$overdues> is a reference-to-array. Each element is a
112 reference-to-hash whose keys are the fields of the issues table in the
113 Koha database.
115 =cut
118 sub Getoverdues {
119 my $params = shift;
120 my $dbh = C4::Context->dbh;
121 my $statement;
122 if ( C4::Context->preference('item-level_itypes') ) {
123 $statement = "
124 SELECT issues.*, items.itype as itemtype, items.homebranch, items.barcode
125 FROM issues
126 LEFT JOIN items USING (itemnumber)
127 WHERE date_due < CURDATE()
129 } else {
130 $statement = "
131 SELECT issues.*, biblioitems.itemtype, items.itype, items.homebranch, items.barcode
132 FROM issues
133 LEFT JOIN items USING (itemnumber)
134 LEFT JOIN biblioitems USING (biblioitemnumber)
135 WHERE date_due < CURDATE()
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'};
150 $statement .= 'ORDER BY borrowernumber';
151 my $sth = $dbh->prepare( $statement );
152 $sth->execute( @bind_parameters );
153 return $sth->fetchall_arrayref({});
157 =head2 checkoverdues
159 ($count, $overdueitems) = checkoverdues($borrowernumber);
161 Returns a count and a list of overdueitems for a given borrowernumber
163 =cut
165 sub checkoverdues {
166 my $borrowernumber = shift or return;
167 # don't select biblioitems.marc or biblioitems.marcxml... too slow on large systems
168 my $sth = C4::Context->dbh->prepare(
169 "SELECT biblio.*, items.*, issues.*,
170 biblioitems.volume,
171 biblioitems.number,
172 biblioitems.itemtype,
173 biblioitems.isbn,
174 biblioitems.issn,
175 biblioitems.publicationyear,
176 biblioitems.publishercode,
177 biblioitems.volumedate,
178 biblioitems.volumedesc,
179 biblioitems.collectiontitle,
180 biblioitems.collectionissn,
181 biblioitems.collectionvolume,
182 biblioitems.editionstatement,
183 biblioitems.editionresponsibility,
184 biblioitems.illus,
185 biblioitems.pages,
186 biblioitems.notes,
187 biblioitems.size,
188 biblioitems.place,
189 biblioitems.lccn,
190 biblioitems.url,
191 biblioitems.cn_source,
192 biblioitems.cn_class,
193 biblioitems.cn_item,
194 biblioitems.cn_suffix,
195 biblioitems.cn_sort,
196 biblioitems.totalissues
197 FROM issues
198 LEFT JOIN items ON issues.itemnumber = items.itemnumber
199 LEFT JOIN biblio ON items.biblionumber = biblio.biblionumber
200 LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber
201 WHERE issues.borrowernumber = ?
202 AND issues.date_due < CURDATE()"
204 # FIXME: SELECT * across 4 tables? do we really need the marc AND marcxml blobs??
205 $sth->execute($borrowernumber);
206 my $results = $sth->fetchall_arrayref({});
207 return ( scalar(@$results), $results); # returning the count and the results is silly
210 =head2 CalcFine
212 ($amount, $chargename, $daycount, $daycounttotal) = &CalcFine($item,
213 $categorycode, $branch, $days_overdue,
214 $description, $start_date, $end_date );
216 Calculates the fine for a book.
218 The issuingrules table in the Koha database is a fine matrix, listing
219 the penalties for each type of patron for each type of item and each branch (e.g., the
220 standard fine for books might be $0.50, but $1.50 for DVDs, or staff
221 members might get a longer grace period between the first and second
222 reminders that a book is overdue).
225 C<$item> is an item object (hashref).
227 C<$categorycode> is the category code (string) of the patron who currently has
228 the book.
230 C<$branchcode> is the library (string) whose issuingrules govern this transaction.
232 C<$days_overdue> is the number of days elapsed since the book's due date.
233 NOTE: supplying days_overdue is deprecated.
235 C<$start_date> & C<$end_date> are C4::Dates objects
236 defining the date range over which to determine the fine.
237 Note that if these are defined, we ignore C<$difference> and C<$dues> ,
238 but retain these for backwards-comptibility with extant fines scripts.
240 Fines scripts should just supply the date range over which to calculate the fine.
242 C<&CalcFine> returns four values:
244 C<$amount> is the fine owed by the patron (see above).
246 C<$chargename> is the chargename field from the applicable record in
247 the categoryitem table, whatever that is.
249 C<$daycount> is the number of days between start and end dates, Calendar adjusted (where needed),
250 minus any applicable grace period.
252 C<$daycounttotal> is C<$daycount> without consideration of grace period.
254 FIXME - What is chargename supposed to be ?
256 FIXME: previously attempted to return C<$message> as a text message, either "First Notice", "Second Notice",
257 or "Final Notice". But CalcFine never defined any value.
259 =cut
261 sub CalcFine {
262 my ( $item, $bortype, $branchcode, $difference ,$dues , $start_date, $end_date ) = @_;
263 $debug and warn sprintf("CalcFine(%s, %s, %s, %s, %s, %s, %s)",
264 ($item ? '{item}' : 'UNDEF'),
265 ($bortype || 'UNDEF'),
266 ($branchcode || 'UNDEF'),
267 ($difference || 'UNDEF'),
268 ($dues || 'UNDEF'),
269 ($start_date ? ($start_date->output('iso') || 'Not a C4::Dates object') : 'UNDEF'),
270 ( $end_date ? ( $end_date->output('iso') || 'Not a C4::Dates object') : 'UNDEF')
272 my $dbh = C4::Context->dbh;
273 my $amount = 0;
274 my $daystocharge;
275 # get issuingrules (fines part will be used)
276 $debug and warn sprintf("CalcFine calling GetIssuingRule(%s, %s, %s)", $bortype, $item->{'itemtype'}, $branchcode);
277 my $data = C4::Circulation::GetIssuingRule($bortype, $item->{'itemtype'}, $branchcode);
278 if($difference) {
279 # if $difference is supplied, the difference has already been calculated, but we still need to adjust for the calendar.
280 # use copy-pasted functions from calendar module. (deprecated -- these functions will be removed from C4::Overdues ).
281 my $countspecialday = &GetSpecialHolidays($dues,$item->{itemnumber});
282 my $countrepeatableday = &GetRepeatableHolidays($dues,$item->{itemnumber},$difference);
283 my $countalldayclosed = $countspecialday + $countrepeatableday;
284 $daystocharge = $difference - $countalldayclosed;
285 } else {
286 # if $difference is not supplied, we have C4::Dates objects giving us the date range, and we use the calendar module.
287 if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
288 my $calendar = C4::Calendar->new( branchcode => $branchcode );
289 $daystocharge = $calendar->daysBetween( $start_date, $end_date );
290 } else {
291 $daystocharge = Date_to_Days(split('-',$end_date->output('iso'))) - Date_to_Days(split('-',$start_date->output('iso')));
294 # correct for grace period.
295 my $days_minus_grace = $daystocharge - $data->{'firstremind'};
296 if ($data->{'chargeperiod'} > 0 && $days_minus_grace > 0 ) {
297 $amount = int($daystocharge / $data->{'chargeperiod'}) * $data->{'fine'};
298 } else {
299 # a zero (or null) chargeperiod means no charge.
301 $amount = C4::Context->preference('maxFine') if(C4::Context->preference('maxFine') && ( $amount > C4::Context->preference('maxFine')));
302 $debug and warn sprintf("CalcFine returning (%s, %s, %s, %s)", $amount, $data->{'chargename'}, $days_minus_grace, $daystocharge);
303 return ($amount, $data->{'chargename'}, $days_minus_grace, $daystocharge);
304 # FIXME: chargename is NEVER populated anywhere.
308 =head2 GetSpecialHolidays
310 &GetSpecialHolidays($date_dues,$itemnumber);
312 return number of special days between date of the day and date due
314 C<$date_dues> is the envisaged date of book return.
316 C<$itemnumber> is the book's item number.
318 =cut
320 sub GetSpecialHolidays {
321 my ( $date_dues, $itemnumber ) = @_;
323 # calcul the today date
324 my $today = join "-", &Today();
326 # return the holdingbranch
327 my $iteminfo = GetIssuesIteminfo($itemnumber);
329 # use sql request to find all date between date_due and today
330 my $dbh = C4::Context->dbh;
331 my $query =
332 qq|SELECT DATE_FORMAT(concat(year,'-',month,'-',day),'%Y-%m-%d') as date
333 FROM `special_holidays`
334 WHERE DATE_FORMAT(concat(year,'-',month,'-',day),'%Y-%m-%d') >= ?
335 AND DATE_FORMAT(concat(year,'-',month,'-',day),'%Y-%m-%d') <= ?
336 AND branchcode=?
338 my @result = GetWdayFromItemnumber($itemnumber);
339 my @result_date;
340 my $wday;
341 my $dateinsec;
342 my $sth = $dbh->prepare($query);
343 $sth->execute( $date_dues, $today, $iteminfo->{'branchcode'} )
344 ; # FIXME: just use NOW() in SQL instead of passing in $today
346 while ( my $special_date = $sth->fetchrow_hashref ) {
347 push( @result_date, $special_date );
350 my $specialdaycount = scalar(@result_date);
352 for ( my $i = 0 ; $i < scalar(@result_date) ; $i++ ) {
353 $dateinsec = UnixDate( $result_date[$i]->{'date'}, "%o" );
354 ( undef, undef, undef, undef, undef, undef, $wday, undef, undef ) =
355 localtime($dateinsec);
356 for ( my $j = 0 ; $j < scalar(@result) ; $j++ ) {
357 if ( $wday == ( $result[$j]->{'weekday'} ) ) {
358 $specialdaycount--;
363 return $specialdaycount;
366 =head2 GetRepeatableHolidays
368 &GetRepeatableHolidays($date_dues, $itemnumber, $difference,);
370 return number of day closed between date of the day and date due
372 C<$date_dues> is the envisaged date of book return.
374 C<$itemnumber> is item number.
376 C<$difference> numbers of between day date of the day and date due
378 =cut
380 sub GetRepeatableHolidays {
381 my ( $date_dues, $itemnumber, $difference ) = @_;
382 my $dateinsec = UnixDate( $date_dues, "%o" );
383 my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
384 localtime($dateinsec);
385 my @result = GetWdayFromItemnumber($itemnumber);
386 my @dayclosedcount;
387 my $j;
389 for ( my $i = 0 ; $i < scalar(@result) ; $i++ ) {
390 my $k = $wday;
392 for ( $j = 0 ; $j < $difference ; $j++ ) {
393 if ( $result[$i]->{'weekday'} == $k ) {
394 push( @dayclosedcount, $k );
396 $k++;
397 ( $k = 0 ) if ( $k eq 7 );
400 return scalar(@dayclosedcount);
404 =head2 GetWayFromItemnumber
406 &Getwdayfromitemnumber($itemnumber);
408 return the different week day from repeatable_holidays table
410 C<$itemnumber> is item number.
412 =cut
414 sub GetWdayFromItemnumber {
415 my ($itemnumber) = @_;
416 my $iteminfo = GetIssuesIteminfo($itemnumber);
417 my @result;
418 my $query = qq|SELECT weekday
419 FROM repeatable_holidays
420 WHERE branchcode=?
422 my $sth = C4::Context->dbh->prepare($query);
424 $sth->execute( $iteminfo->{'branchcode'} );
425 while ( my $weekday = $sth->fetchrow_hashref ) {
426 push( @result, $weekday );
428 return @result;
432 =head2 GetIssuesIteminfo
434 &GetIssuesIteminfo($itemnumber);
436 return all data from issues about item
438 C<$itemnumber> is item number.
440 =cut
442 sub GetIssuesIteminfo {
443 my ($itemnumber) = @_;
444 my $dbh = C4::Context->dbh;
445 my $query = qq|SELECT *
446 FROM issues
447 WHERE itemnumber=?
449 my $sth = $dbh->prepare($query);
450 $sth->execute($itemnumber);
451 my ($issuesinfo) = $sth->fetchrow_hashref;
452 return $issuesinfo;
456 =head2 UpdateFine
458 &UpdateFine($itemnumber, $borrowernumber, $amount, $type, $description);
460 (Note: the following is mostly conjecture and guesswork.)
462 Updates the fine owed on an overdue book.
464 C<$itemnumber> is the book's item number.
466 C<$borrowernumber> is the borrower number of the patron who currently
467 has the book on loan.
469 C<$amount> is the current amount owed by the patron.
471 C<$type> will be used in the description of the fine.
473 C<$description> is a string that must be present in the description of
474 the fine. I think this is expected to be a date in DD/MM/YYYY format.
476 C<&UpdateFine> looks up the amount currently owed on the given item
477 and sets it to C<$amount>, creating, if necessary, a new entry in the
478 accountlines table of the Koha database.
480 =cut
483 # Question: Why should the caller have to
484 # specify both the item number and the borrower number? A book can't
485 # be on loan to two different people, so the item number should be
486 # sufficient.
488 # Possible Answer: You might update a fine for a damaged item, *after* it is returned.
490 sub UpdateFine {
491 my ( $itemnum, $borrowernumber, $amount, $type, $due ) = @_;
492 $debug and warn "UpdateFine($itemnum, $borrowernumber, $amount, " . ($type||'""') . ", $due) called";
493 my $dbh = C4::Context->dbh;
494 # FIXME - What exactly is this query supposed to do? It looks up an
495 # entry in accountlines that matches the given item and borrower
496 # numbers, where the description contains $due, and where the
497 # account type has one of several values, but what does this _mean_?
498 # Does it look up existing fines for this item?
499 # FIXME - What are these various account types? ("FU", "O", "F", "M")
500 # "L" is LOST item
501 # "A" is Account Management Fee
502 # "N" is New Card
503 # "M" is Sundry
504 # "O" is Overdue ??
505 # "F" is Fine ??
506 # "FU" is Fine UPDATE??
507 # "Pay" is Payment
508 # "REF" is Cash Refund
509 my $sth = $dbh->prepare(
510 "SELECT * FROM accountlines
511 WHERE itemnumber=?
512 AND borrowernumber=?
513 AND accounttype IN ('FU','O','F','M')
514 AND description like ? "
516 $sth->execute( $itemnum, $borrowernumber, "%$due%" );
518 if ( my $data = $sth->fetchrow_hashref ) {
520 # we're updating an existing fine. Only modify if amount changed
521 # Note that in the current implementation, you cannot pay against an accruing fine
522 # (i.e. , of accounttype 'FU'). Doing so will break accrual.
523 if ( $data->{'amount'} != $amount ) {
524 my $diff = $amount - $data->{'amount'};
525 #3341: diff could be positive or negative!
526 my $out = $data->{'amountoutstanding'} + $diff;
527 my $query = "
528 UPDATE accountlines
529 SET date=now(), amount=?, amountoutstanding=?,
530 lastincrement=?, accounttype='FU'
531 WHERE borrowernumber=?
532 AND itemnumber=?
533 AND accounttype IN ('FU','O')
534 AND description LIKE ?
535 LIMIT 1 ";
536 my $sth2 = $dbh->prepare($query);
537 # FIXME: BOGUS query cannot ensure uniqueness w/ LIKE %x% !!!
538 # LIMIT 1 added to prevent multiple affected lines
539 # FIXME: accountlines table needs unique key!! Possibly a combo of borrowernumber and accountline.
540 # But actually, we should just have a regular autoincrementing PK and forget accountline,
541 # including the bogus getnextaccountno function (doesn't prevent conflict on simultaneous ops).
542 # FIXME: Why only 2 account types here?
543 $debug and print STDERR "UpdateFine query: $query\n" .
544 "w/ args: $amount, $out, $diff, $data->{'borrowernumber'}, $data->{'itemnumber'}, \"\%$due\%\"\n";
545 $sth2->execute($amount, $out, $diff, $data->{'borrowernumber'}, $data->{'itemnumber'}, "%$due%");
546 } else {
547 # print "no update needed $data->{'amount'}"
549 } else {
550 my $sth4 = $dbh->prepare(
551 "SELECT title FROM biblio LEFT JOIN items ON biblio.biblionumber=items.biblionumber WHERE items.itemnumber=?"
553 $sth4->execute($itemnum);
554 my $title = $sth4->fetchrow;
556 # # print "not in account";
557 # my $sth3 = $dbh->prepare("Select max(accountno) from accountlines");
558 # $sth3->execute;
560 # # FIXME - Make $accountno a scalar.
561 # my @accountno = $sth3->fetchrow_array;
562 # $sth3->finish;
563 # $accountno[0]++;
564 # begin transaction
565 my $nextaccntno = C4::Accounts::getnextacctno($borrowernumber);
566 my $desc = ($type ? "$type " : '') . "$title $due"; # FIXEDME, avoid whitespace prefix on empty $type
567 my $query = "INSERT INTO accountlines
568 (borrowernumber,itemnumber,date,amount,description,accounttype,amountoutstanding,lastincrement,accountno)
569 VALUES (?,?,now(),?,?,'FU',?,?,?)";
570 my $sth2 = $dbh->prepare($query);
571 $debug and print STDERR "UpdateFine query: $query\nw/ args: $borrowernumber, $itemnum, $amount, $desc, $amount, $amount, $nextaccntno\n";
572 $sth2->execute($borrowernumber, $itemnum, $amount, $desc, $amount, $amount, $nextaccntno);
574 # logging action
575 &logaction(
576 "FINES",
577 $type,
578 $borrowernumber,
579 "due=".$due." amount=".$amount." itemnumber=".$itemnum
580 ) if C4::Context->preference("FinesLog");
583 =head2 BorType
585 $borrower = &BorType($borrowernumber);
587 Looks up a patron by borrower number.
589 C<$borrower> is a reference-to-hash whose keys are all of the fields
590 from the borrowers and categories tables of the Koha database. Thus,
591 C<$borrower> contains all information about both the borrower and
592 category he or she belongs to.
594 =cut
597 sub BorType {
598 my ($borrowernumber) = @_;
599 my $dbh = C4::Context->dbh;
600 my $sth = $dbh->prepare(
601 "SELECT * from borrowers
602 LEFT JOIN categories ON borrowers.categorycode=categories.categorycode
603 WHERE borrowernumber=?"
605 $sth->execute($borrowernumber);
606 return $sth->fetchrow_hashref;
609 =head2 ReplacementCost
611 $cost = &ReplacementCost($itemnumber);
613 Returns the replacement cost of the item with the given item number.
615 =cut
618 sub ReplacementCost {
619 my ($itemnum) = @_;
620 my $dbh = C4::Context->dbh;
621 my $sth =
622 $dbh->prepare("Select replacementprice from items where itemnumber=?");
623 $sth->execute($itemnum);
625 # FIXME - Use fetchrow_array or a slice.
626 my $data = $sth->fetchrow_hashref;
627 return ( $data->{'replacementprice'} );
630 =head2 GetFine
632 $data->{'sum(amountoutstanding)'} = &GetFine($itemnum,$borrowernumber);
634 return the total of fine
636 C<$itemnum> is item number
638 C<$borrowernumber> is the borrowernumber
640 =cut
643 sub GetFine {
644 my ( $itemnum, $borrowernumber ) = @_;
645 my $dbh = C4::Context->dbh();
646 my $query = q|SELECT sum(amountoutstanding) as fineamount FROM accountlines
647 where accounttype like 'F%'
648 AND amountoutstanding > 0 AND itemnumber = ? AND borrowernumber=?|;
649 my $sth = $dbh->prepare($query);
650 $sth->execute( $itemnum, $borrowernumber );
651 my $fine = $sth->fetchrow_hashref();
652 if ($fine->{fineamount}) {
653 return $fine->{fineamount};
655 return 0;
659 =head2 GetIssuingRules
661 FIXME - This sub should be deprecated and removed.
662 It ignores branch and defaults.
664 $data = &GetIssuingRules($itemtype,$categorycode);
666 Looks up for all issuingrules an item info
668 C<$itemnumber> is a reference-to-hash whose keys are all of the fields
669 from the borrowers and categories tables of the Koha database. Thus,
671 C<$categorycode> contains information about borrowers category
673 C<$data> contains all information about both the borrower and
674 category he or she belongs to.
675 =cut
677 sub GetIssuingRules {
678 warn "GetIssuingRules is deprecated: use GetIssuingRule from C4::Circulation instead.";
679 my ($itemtype,$categorycode)=@_;
680 my $dbh = C4::Context->dbh();
681 my $query=qq|SELECT *
682 FROM issuingrules
683 WHERE issuingrules.itemtype=?
684 AND issuingrules.categorycode=?
686 my $sth = $dbh->prepare($query);
687 # print $query;
688 $sth->execute($itemtype,$categorycode);
689 return $sth->fetchrow_hashref;
693 sub ReplacementCost2 {
694 my ( $itemnum, $borrowernumber ) = @_;
695 my $dbh = C4::Context->dbh();
696 my $query = "SELECT amountoutstanding
697 FROM accountlines
698 WHERE accounttype like 'L'
699 AND amountoutstanding > 0
700 AND itemnumber = ?
701 AND borrowernumber= ?";
702 my $sth = $dbh->prepare($query);
703 $sth->execute( $itemnum, $borrowernumber );
704 my $data = $sth->fetchrow_hashref();
705 return ( $data->{'amountoutstanding'} );
709 =head2 GetNextIdNotify
711 ($result) = &GetNextIdNotify($reference);
713 Returns the new file number
715 C<$result> contains the next file number
717 C<$reference> contains the beggining of file number
719 =cut
721 sub GetNextIdNotify {
722 my ($reference) = @_;
723 my $query = qq|SELECT max(notify_id)
724 FROM accountlines
725 WHERE notify_id like \"$reference%\"
728 # AND borrowernumber=?|;
729 my $dbh = C4::Context->dbh;
730 my $sth = $dbh->prepare($query);
731 $sth->execute();
732 my $result = $sth->fetchrow;
733 my $count;
734 if ( $result eq '' ) {
735 ( $result = $reference . "01" );
737 else {
738 $count = substr( $result, 6 ) + 1;
740 if ( $count < 10 ) {
741 ( $count = "0" . $count );
743 $result = $reference . $count;
745 return $result;
748 =head2 NumberNotifyId
750 (@notify) = &NumberNotifyId($borrowernumber);
752 Returns amount for all file per borrowers
753 C<@notify> array contains all file per borrowers
755 C<$notify_id> contains the file number for the borrower number nad item number
757 =cut
759 sub NumberNotifyId{
760 my ($borrowernumber)=@_;
761 my $dbh = C4::Context->dbh;
762 my $query=qq| SELECT distinct(notify_id)
763 FROM accountlines
764 WHERE borrowernumber=?|;
765 my @notify;
766 my $sth = $dbh->prepare($query);
767 $sth->execute($borrowernumber);
768 while ( my ($numberofnotify) = $sth->fetchrow ) {
769 push( @notify, $numberofnotify );
771 return (@notify);
774 =head2 AmountNotify
776 ($totalnotify) = &AmountNotify($notifyid);
778 Returns amount for all file per borrowers
779 C<$notifyid> is the file number
781 C<$totalnotify> contains amount of a file
783 C<$notify_id> contains the file number for the borrower number and item number
785 =cut
787 sub AmountNotify{
788 my ($notifyid,$borrowernumber)=@_;
789 my $dbh = C4::Context->dbh;
790 my $query=qq| SELECT sum(amountoutstanding)
791 FROM accountlines
792 WHERE notify_id=? AND borrowernumber = ?|;
793 my $sth=$dbh->prepare($query);
794 $sth->execute($notifyid,$borrowernumber);
795 my $totalnotify=$sth->fetchrow;
796 $sth->finish;
797 return ($totalnotify);
801 =head2 GetNotifyId
803 ($notify_id) = &GetNotifyId($borrowernumber,$itemnumber);
805 Returns the file number per borrower and itemnumber
807 C<$borrowernumber> is a reference-to-hash whose keys are all of the fields
808 from the items tables of the Koha database. Thus,
810 C<$itemnumber> contains the borrower categorycode
812 C<$notify_id> contains the file number for the borrower number nad item number
814 =cut
816 sub GetNotifyId {
817 my ( $borrowernumber, $itemnumber ) = @_;
818 my $query = qq|SELECT notify_id
819 FROM accountlines
820 WHERE borrowernumber=?
821 AND itemnumber=?
822 AND (accounttype='FU' or accounttype='O')|;
823 my $dbh = C4::Context->dbh;
824 my $sth = $dbh->prepare($query);
825 $sth->execute( $borrowernumber, $itemnumber );
826 my ($notify_id) = $sth->fetchrow;
827 $sth->finish;
828 return ($notify_id);
831 =head2 CreateItemAccountLine
833 () = &CreateItemAccountLine($borrowernumber, $itemnumber, $date, $amount,
834 $description, $accounttype, $amountoutstanding,
835 $timestamp, $notify_id, $level);
837 update the account lines with file number or with file level
839 C<$items> is a reference-to-hash whose keys are all of the fields
840 from the items tables of the Koha database. Thus,
842 C<$itemnumber> contains the item number
844 C<$borrowernumber> contains the borrower number
846 C<$date> contains the date of the day
848 C<$amount> contains item price
850 C<$description> contains the descritpion of accounttype
852 C<$accounttype> contains the account type
854 C<$amountoutstanding> contains the $amountoutstanding
856 C<$timestamp> contains the timestamp with time and the date of the day
858 C<$notify_id> contains the file number
860 C<$level> contains the file level
862 =cut
864 sub CreateItemAccountLine {
865 my (
866 $borrowernumber, $itemnumber, $date, $amount,
867 $description, $accounttype, $amountoutstanding, $timestamp,
868 $notify_id, $level
869 ) = @_;
870 my $dbh = C4::Context->dbh;
871 my $nextaccntno = C4::Accounts::getnextacctno($borrowernumber);
872 my $query = "INSERT into accountlines
873 (borrowernumber,accountno,itemnumber,date,amount,description,accounttype,amountoutstanding,timestamp,notify_id,notify_level)
874 VALUES
875 (?,?,?,?,?,?,?,?,?,?,?)";
877 my $sth = $dbh->prepare($query);
878 $sth->execute(
879 $borrowernumber, $nextaccntno, $itemnumber,
880 $date, $amount, $description,
881 $accounttype, $amountoutstanding, $timestamp,
882 $notify_id, $level
886 =head2 UpdateAccountLines
888 () = &UpdateAccountLines($notify_id,$notify_level,$borrowernumber,$itemnumber);
890 update the account lines with file number or with file level
892 C<$items> is a reference-to-hash whose keys are all of the fields
893 from the items tables of the Koha database. Thus,
895 C<$itemnumber> contains the item number
897 C<$notify_id> contains the file number
899 C<$notify_level> contains the file level
901 C<$borrowernumber> contains the borrowernumber
903 =cut
905 sub UpdateAccountLines {
906 my ( $notify_id, $notify_level, $borrowernumber, $itemnumber ) = @_;
907 my $query;
908 if ( $notify_id eq '' ) {
909 $query = qq|UPDATE accountlines
910 SET notify_level=?
911 WHERE borrowernumber=? AND itemnumber=?
912 AND (accounttype='FU' or accounttype='O')|;
913 } else {
914 $query = qq|UPDATE accountlines
915 SET notify_id=?, notify_level=?
916 WHERE borrowernumber=?
917 AND itemnumber=?
918 AND (accounttype='FU' or accounttype='O')|;
921 my $sth = C4::Context->dbh->prepare($query);
922 if ( $notify_id eq '' ) {
923 $sth->execute( $notify_level, $borrowernumber, $itemnumber );
924 } else {
925 $sth->execute( $notify_id, $notify_level, $borrowernumber, $itemnumber );
929 =head2 GetItems
931 ($items) = &GetItems($itemnumber);
933 Returns the list of all delays from overduerules.
935 C<$items> is a reference-to-hash whose keys are all of the fields
936 from the items tables of the Koha database. Thus,
938 C<$itemnumber> contains the borrower categorycode
940 =cut
942 # FIXME: This is a bad function to have here.
943 # Shouldn't it be in C4::Items?
944 # Shouldn't it be called GetItem since you only get 1 row?
945 # Shouldn't it be called GetItem since you give it only 1 itemnumber?
947 sub GetItems {
948 my $itemnumber = shift or return;
949 my $query = qq|SELECT *
950 FROM items
951 WHERE itemnumber=?|;
952 my $sth = C4::Context->dbh->prepare($query);
953 $sth->execute($itemnumber);
954 my ($items) = $sth->fetchrow_hashref;
955 return ($items);
958 =head2 GetOverdueDelays
960 (@delays) = &GetOverdueDelays($categorycode);
962 Returns the list of all delays from overduerules.
964 C<@delays> it's an array contains the three delays from overduerules table
966 C<$categorycode> contains the borrower categorycode
968 =cut
970 sub GetOverdueDelays {
971 my ($category) = @_;
972 my $query = qq|SELECT delay1,delay2,delay3
973 FROM overduerules
974 WHERE categorycode=?|;
975 my $sth = C4::Context->dbh->prepare($query);
976 $sth->execute($category);
977 my (@delays) = $sth->fetchrow_array;
978 return (@delays);
981 =head2 GetBranchcodesWithOverdueRules
983 my @branchcodes = C4::Overdues::GetBranchcodesWithOverdueRules()
985 returns a list of branch codes for branches with overdue rules defined.
987 =cut
989 sub GetBranchcodesWithOverdueRules {
990 my $dbh = C4::Context->dbh;
991 my $rqoverduebranches = $dbh->prepare("SELECT DISTINCT branchcode FROM overduerules WHERE delay1 IS NOT NULL AND branchcode <> '' ORDER BY branchcode");
992 $rqoverduebranches->execute;
993 my @branches = map { shift @$_ } @{ $rqoverduebranches->fetchall_arrayref };
994 if (!$branches[0]) {
995 my $availbranches = C4::Branch::GetBranches();
996 @branches = keys %$availbranches;
998 return @branches;
1001 =head2 CheckAccountLineLevelInfo
1003 ($exist) = &CheckAccountLineLevelInfo($borrowernumber,$itemnumber,$accounttype,notify_level);
1005 Check and Returns the list of all overdue books.
1007 C<$exist> contains number of line in accounlines
1008 with the same .biblionumber,itemnumber,accounttype,and notify_level
1010 C<$borrowernumber> contains the borrower number
1012 C<$itemnumber> contains item number
1014 C<$accounttype> contains account type
1016 C<$notify_level> contains the accountline level
1019 =cut
1021 sub CheckAccountLineLevelInfo {
1022 my ( $borrowernumber, $itemnumber, $level ) = @_;
1023 my $dbh = C4::Context->dbh;
1024 my $query = qq|SELECT count(*)
1025 FROM accountlines
1026 WHERE borrowernumber =?
1027 AND itemnumber = ?
1028 AND notify_level=?|;
1029 my $sth = $dbh->prepare($query);
1030 $sth->execute( $borrowernumber, $itemnumber, $level );
1031 my ($exist) = $sth->fetchrow;
1032 return ($exist);
1035 =head2 GetOverduerules
1037 ($overduerules) = &GetOverduerules($categorycode);
1039 Returns the value of borrowers (debarred or not) with notify level
1041 C<$overduerules> return value of debbraed field in overduerules table
1043 C<$category> contains the borrower categorycode
1045 C<$notify_level> contains the notify level
1047 =cut
1049 sub GetOverduerules {
1050 my ( $category, $notify_level ) = @_;
1051 my $dbh = C4::Context->dbh;
1052 my $query = qq|SELECT debarred$notify_level
1053 FROM overduerules
1054 WHERE categorycode=?|;
1055 my $sth = $dbh->prepare($query);
1056 $sth->execute($category);
1057 my ($overduerules) = $sth->fetchrow;
1058 return ($overduerules);
1062 =head2 CheckBorrowerDebarred
1064 ($debarredstatus) = &CheckBorrowerDebarred($borrowernumber);
1066 Check if the borrowers is already debarred
1068 C<$debarredstatus> return 0 for not debarred and return 1 for debarred
1070 C<$borrowernumber> contains the borrower number
1072 =cut
1074 # FIXME: Shouldn't this be in C4::Members?
1075 sub CheckBorrowerDebarred {
1076 my ($borrowernumber) = @_;
1077 my $dbh = C4::Context->dbh;
1078 my $query = qq|
1079 SELECT debarred
1080 FROM borrowers
1081 WHERE borrowernumber=?
1082 AND debarred > NOW()
1084 my $sth = $dbh->prepare($query);
1085 $sth->execute($borrowernumber);
1086 my $debarredstatus = $sth->fetchrow;
1087 return $debarredstatus;
1091 =head2 CheckExistantNotifyid
1093 ($exist) = &CheckExistantNotifyid($borrowernumber,$itemnumber,$accounttype,$notify_id);
1095 Check and Returns the notify id if exist else return 0.
1097 C<$exist> contains a notify_id
1099 C<$borrowernumber> contains the borrower number
1101 C<$date_due> contains the date of item return
1104 =cut
1106 sub CheckExistantNotifyid {
1107 my ( $borrowernumber, $date_due ) = @_;
1108 my $dbh = C4::Context->dbh;
1109 my $query = qq|SELECT notify_id FROM accountlines
1110 LEFT JOIN issues ON issues.itemnumber= accountlines.itemnumber
1111 WHERE accountlines.borrowernumber =?
1112 AND date_due = ?|;
1113 my $sth = $dbh->prepare($query);
1114 $sth->execute( $borrowernumber, $date_due );
1115 return $sth->fetchrow || 0;
1118 =head2 CheckAccountLineItemInfo
1120 ($exist) = &CheckAccountLineItemInfo($borrowernumber,$itemnumber,$accounttype,$notify_id);
1122 Check and Returns the list of all overdue items from the same file number(notify_id).
1124 C<$exist> contains number of line in accounlines
1125 with the same .biblionumber,itemnumber,accounttype,notify_id
1127 C<$borrowernumber> contains the borrower number
1129 C<$itemnumber> contains item number
1131 C<$accounttype> contains account type
1133 C<$notify_id> contains the file number
1135 =cut
1137 sub CheckAccountLineItemInfo {
1138 my ( $borrowernumber, $itemnumber, $accounttype, $notify_id ) = @_;
1139 my $dbh = C4::Context->dbh;
1140 my $query = qq|SELECT count(*) FROM accountlines
1141 WHERE borrowernumber =?
1142 AND itemnumber = ?
1143 AND accounttype= ?
1144 AND notify_id = ?|;
1145 my $sth = $dbh->prepare($query);
1146 $sth->execute( $borrowernumber, $itemnumber, $accounttype, $notify_id );
1147 my ($exist) = $sth->fetchrow;
1148 return ($exist);
1151 =head2 CheckItemNotify
1153 Sql request to check if the document has alreday been notified
1154 this function is not exported, only used with GetOverduesForBranch
1156 =cut
1158 sub CheckItemNotify {
1159 my ($notify_id,$notify_level,$itemnumber) = @_;
1160 my $dbh = C4::Context->dbh;
1161 my $sth = $dbh->prepare("
1162 SELECT COUNT(*)
1163 FROM notifys
1164 WHERE notify_id = ?
1165 AND notify_level = ?
1166 AND itemnumber = ? ");
1167 $sth->execute($notify_id,$notify_level,$itemnumber);
1168 my $notified = $sth->fetchrow;
1169 return ($notified);
1172 =head2 GetOverduesForBranch
1174 Sql request for display all information for branchoverdues.pl
1175 2 possibilities : with or without location .
1176 display is filtered by branch
1178 FIXME: This function should be renamed.
1180 =cut
1182 sub GetOverduesForBranch {
1183 my ( $branch, $location) = @_;
1184 my $itype_link = (C4::Context->preference('item-level_itypes')) ? " items.itype " : " biblioitems.itemtype ";
1185 my $dbh = C4::Context->dbh;
1186 my $select = "
1187 SELECT
1188 borrowers.borrowernumber,
1189 borrowers.surname,
1190 borrowers.firstname,
1191 borrowers.phone,
1192 borrowers.email,
1193 biblio.title,
1194 biblio.author,
1195 biblio.biblionumber,
1196 issues.date_due,
1197 issues.returndate,
1198 issues.branchcode,
1199 branches.branchname,
1200 items.barcode,
1201 items.homebranch,
1202 items.itemcallnumber,
1203 items.location,
1204 items.itemnumber,
1205 itemtypes.description,
1206 accountlines.notify_id,
1207 accountlines.notify_level,
1208 accountlines.amountoutstanding
1209 FROM accountlines
1210 LEFT JOIN issues ON issues.itemnumber = accountlines.itemnumber
1211 AND issues.borrowernumber = accountlines.borrowernumber
1212 LEFT JOIN borrowers ON borrowers.borrowernumber = accountlines.borrowernumber
1213 LEFT JOIN items ON items.itemnumber = issues.itemnumber
1214 LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
1215 LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
1216 LEFT JOIN itemtypes ON itemtypes.itemtype = $itype_link
1217 LEFT JOIN branches ON branches.branchcode = issues.branchcode
1218 WHERE (accountlines.amountoutstanding != '0.000000')
1219 AND (accountlines.accounttype = 'FU' )
1220 AND (issues.branchcode = ? )
1221 AND (issues.date_due < CURDATE())
1223 my @getoverdues;
1224 my $i = 0;
1225 my $sth;
1226 if ($location) {
1227 $sth = $dbh->prepare("$select AND items.location = ? ORDER BY borrowers.surname, borrowers.firstname");
1228 $sth->execute($branch, $location);
1229 } else {
1230 $sth = $dbh->prepare("$select ORDER BY borrowers.surname, borrowers.firstname");
1231 $sth->execute($branch);
1233 while ( my $data = $sth->fetchrow_hashref ) {
1234 #check if the document has already been notified
1235 my $countnotify = CheckItemNotify($data->{'notify_id'}, $data->{'notify_level'}, $data->{'itemnumber'});
1236 if ($countnotify eq '0') {
1237 $getoverdues[$i] = $data;
1238 $i++;
1241 return (@getoverdues);
1245 =head2 AddNotifyLine
1247 &AddNotifyLine($borrowernumber, $itemnumber, $overduelevel, $method, $notifyId)
1249 Create a line into notify, if the method is phone, the notification_send_date is implemented to
1251 =cut
1253 sub AddNotifyLine {
1254 my ( $borrowernumber, $itemnumber, $overduelevel, $method, $notifyId ) = @_;
1255 my $dbh = C4::Context->dbh;
1256 if ( $method eq "phone" ) {
1257 my $sth = $dbh->prepare(
1258 "INSERT INTO notifys (borrowernumber,itemnumber,notify_date,notify_send_date,notify_level,method,notify_id)
1259 VALUES (?,?,now(),now(),?,?,?)"
1261 $sth->execute( $borrowernumber, $itemnumber, $overduelevel, $method,
1262 $notifyId );
1264 else {
1265 my $sth = $dbh->prepare(
1266 "INSERT INTO notifys (borrowernumber,itemnumber,notify_date,notify_level,method,notify_id)
1267 VALUES (?,?,now(),?,?,?)"
1269 $sth->execute( $borrowernumber, $itemnumber, $overduelevel, $method,
1270 $notifyId );
1272 return 1;
1275 =head2 RemoveNotifyLine
1277 &RemoveNotifyLine( $borrowernumber, $itemnumber, $notify_date );
1279 Cancel a notification
1281 =cut
1283 sub RemoveNotifyLine {
1284 my ( $borrowernumber, $itemnumber, $notify_date ) = @_;
1285 my $dbh = C4::Context->dbh;
1286 my $sth = $dbh->prepare(
1287 "DELETE FROM notifys
1288 WHERE
1289 borrowernumber=?
1290 AND itemnumber=?
1291 AND notify_date=?"
1293 $sth->execute( $borrowernumber, $itemnumber, $notify_date );
1294 return 1;
1298 __END__
1300 =head1 AUTHOR
1302 Koha Development Team <http://koha-community.org/>
1304 =cut