Bug 21738: make call of CanBookBeReserved more safe
[koha.git] / C4 / Overdues.pm
index d17f445..98e7bbf 100644 (file)
@@ -6,85 +6,69 @@ package C4::Overdues;
 #
 # This file is part of Koha.
 #
 #
 # This file is part of Koha.
 #
-# Koha is free software; you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
 #
 #
-# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
 #
 #
-# You should have received a copy of the GNU General Public License along
-# with Koha; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 use strict;
 #use warnings; FIXME - Bug 2505
 use Date::Calc qw/Today Date_to_Days/;
 use Date::Manip qw/UnixDate/;
 
 use strict;
 #use warnings; FIXME - Bug 2505
 use Date::Calc qw/Today Date_to_Days/;
 use Date::Manip qw/UnixDate/;
+use List::MoreUtils qw( uniq );
+use POSIX qw( floor ceil );
+use Locale::Currency::Format 1.28;
+use Carp;
+
 use C4::Circulation;
 use C4::Context;
 use C4::Accounts;
 use C4::Log; # logaction
 use C4::Debug;
 use C4::Circulation;
 use C4::Context;
 use C4::Accounts;
 use C4::Log; # logaction
 use C4::Debug;
+use Koha::DateUtils;
+use Koha::Account::Lines;
+use Koha::Account::Offsets;
+use Koha::IssuingRules;
+use Koha::Libraries;
 
 
-use vars qw($VERSION @ISA @EXPORT);
+use vars qw(@ISA @EXPORT);
 
 BEGIN {
 
 BEGIN {
-       # set the version for version checking
-    $VERSION = 3.07.00.049;
-       require Exporter;
-       @ISA    = qw(Exporter);
-       # subs to rename (and maybe merge some...)
-       push @EXPORT, qw(
-        &CalcFine
-        &Getoverdues
-        &checkoverdues
-        &CheckAccountLineLevelInfo
-        &CheckAccountLineItemInfo
-        &CheckExistantNotifyid
-        &GetNextIdNotify
-        &GetNotifyId
-        &NumberNotifyId
-        &AmountNotify
-        &UpdateAccountLines
-        &UpdateFine
-        &GetOverdueDelays
-        &GetOverduerules
-        &GetFine
-        &CreateItemAccountLine
-        &ReplacementCost2
-        
-        &CheckItemNotify
-        &GetOverduesForBranch
-        &RemoveNotifyLine
-        &AddNotifyLine
-       );
-       # subs to remove
-       push @EXPORT, qw(
-        &BorType
-       );
-
-       # check that an equivalent don't exist already before moving
-
-       # subs to move to Circulation.pm
-       push @EXPORT, qw(
-        &GetIssuesIteminfo
-       );
-    #
-       # &GetIssuingRules - delete.
-       # use C4::Circulation::GetIssuingRule instead.
-       
-       # subs to move to Members.pm
-       push @EXPORT, qw(
-        &CheckBorrowerDebarred
-       );
-       # subs to move to Biblio.pm
-       push @EXPORT, qw(
-        &GetItems
-        &ReplacementCost
-       );
+    require Exporter;
+    @ISA = qw(Exporter);
+
+    # subs to rename (and maybe merge some...)
+    push @EXPORT, qw(
+      &CalcFine
+      &Getoverdues
+      &checkoverdues
+      &UpdateFine
+      &GetFine
+      &get_chargeable_units
+      &GetOverduesForBranch
+      &GetOverdueMessageTransportTypes
+      &parse_overdues_letter
+    );
+
+    # subs to remove
+    push @EXPORT, qw(
+      &BorType
+    );
+
+    # check that an equivalent don't exist already before moving
+
+    # subs to move to Circulation.pm
+    push @EXPORT, qw(
+      &GetIssuesIteminfo
+    );
 }
 
 =head1 NAME
 }
 
 =head1 NAME
@@ -121,14 +105,14 @@ sub Getoverdues {
     my $statement;
     if ( C4::Context->preference('item-level_itypes') ) {
         $statement = "
     my $statement;
     if ( C4::Context->preference('item-level_itypes') ) {
         $statement = "
-   SELECT issues.*, items.itype as itemtype, items.homebranch, items.barcode
+   SELECT issues.*, items.itype as itemtype, items.homebranch, items.barcode, items.itemlost, items.replacementprice
      FROM issues 
 LEFT JOIN items       USING (itemnumber)
     WHERE date_due < NOW()
 ";
     } else {
         $statement = "
      FROM issues 
 LEFT JOIN items       USING (itemnumber)
     WHERE date_due < NOW()
 ";
     } else {
         $statement = "
-   SELECT issues.*, biblioitems.itemtype, items.itype, items.homebranch, items.barcode
+   SELECT issues.*, biblioitems.itemtype, items.itype, items.homebranch, items.barcode, items.itemlost, replacementprice
      FROM issues 
 LEFT JOIN items       USING (itemnumber)
 LEFT JOIN biblioitems USING (biblioitemnumber)
      FROM issues 
 LEFT JOIN items       USING (itemnumber)
 LEFT JOIN biblioitems USING (biblioitemnumber)
@@ -164,7 +148,6 @@ Returns a count and a list of overdueitems for a given borrowernumber
 
 sub checkoverdues {
     my $borrowernumber = shift or return;
 
 sub checkoverdues {
     my $borrowernumber = shift or return;
-    # don't select biblioitems.marc or biblioitems.marcxml... too slow on large systems
     my $sth = C4::Context->dbh->prepare(
         "SELECT biblio.*, items.*, issues.*,
                 biblioitems.volume,
     my $sth = C4::Context->dbh->prepare(
         "SELECT biblio.*, items.*, issues.*,
                 biblioitems.volume,
@@ -201,7 +184,6 @@ sub checkoverdues {
             WHERE issues.borrowernumber  = ?
             AND   issues.date_due < NOW()"
     );
             WHERE issues.borrowernumber  = ?
             AND   issues.date_due < NOW()"
     );
-    # FIXME: SELECT * across 4 tables?  do we really need the marc AND marcxml blobs??
     $sth->execute($borrowernumber);
     my $results = $sth->fetchall_arrayref({});
     return ( scalar(@$results), $results);  # returning the count and the results is silly
     $sth->execute($borrowernumber);
     my $results = $sth->fetchall_arrayref({});
     return ( scalar(@$results), $results);  # returning the count and the results is silly
@@ -209,7 +191,7 @@ sub checkoverdues {
 
 =head2 CalcFine
 
 
 =head2 CalcFine
 
-    ($amount, $chargename,  $daycounttotal) = &CalcFine($item,
+    ($amount, $units_minus_grace, $chargeable_units) = &CalcFine($item,
                                   $categorycode, $branch,
                                   $start_dt, $end_dt );
 
                                   $categorycode, $branch,
                                   $start_dt, $end_dt );
 
@@ -234,18 +216,15 @@ defining the date range over which to determine the fine.
 
 Fines scripts should just supply the date range over which to calculate the fine.
 
 
 Fines scripts should just supply the date range over which to calculate the fine.
 
-C<&CalcFine> returns four values:
+C<&CalcFine> returns three values:
 
 C<$amount> is the fine owed by the patron (see above).
 
 
 C<$amount> is the fine owed by the patron (see above).
 
-C<$chargename> is the chargename field from the applicable record in
-the categoryitem table, whatever that is.
+C<$units_minus_grace> is the number of chargeable units minus the grace period
 
 
-C<$unitcount> is the number of chargeable units (days between start and end dates, Calendar adjusted where needed,
+C<$chargeable_units> is the number of chargeable units (days between start and end dates, Calendar adjusted where needed,
 minus any applicable grace period, or hours)
 
 minus any applicable grace period, or hours)
 
-FIXME - What is chargename supposed to be ?
-
 FIXME: previously attempted to return C<$message> as a text message, either "First Notice", "Second Notice",
 or "Final Notice".  But CalcFine never defined any value.
 
 FIXME: previously attempted to return C<$message> as a text message, either "First Notice", "Second Notice",
 or "Final Notice".  But CalcFine never defined any value.
 
@@ -256,28 +235,44 @@ sub CalcFine {
     my $start_date = $due_dt->clone();
     # get issuingrules (fines part will be used)
     my $itemtype = $item->{itemtype} || $item->{itype};
     my $start_date = $due_dt->clone();
     # get issuingrules (fines part will be used)
     my $itemtype = $item->{itemtype} || $item->{itype};
-    my $data = C4::Circulation::GetIssuingRule($bortype, $itemtype, $branchcode);
-    my $fine_unit = $data->{lengthunit};
-    $fine_unit ||= 'days';
+    my $issuing_rule = Koha::IssuingRules->get_effective_issuing_rule({ categorycode => $bortype, itemtype => $itemtype, branchcode => $branchcode });
+
+    $itemtype = Koha::ItemTypes->find($itemtype);
 
 
-    my $chargeable_units = _get_chargeable_units($fine_unit, $start_date, $end_date, $branchcode);
-    my $units_minus_grace = $chargeable_units - $data->{firstremind};
+    return unless $issuing_rule; # If not rule exist, there is no fine
+
+    my $fine_unit = $issuing_rule->lengthunit || 'days';
+
+    my $chargeable_units = get_chargeable_units($fine_unit, $start_date, $end_date, $branchcode);
+    my $units_minus_grace = $chargeable_units - $issuing_rule->firstremind;
     my $amount = 0;
     my $amount = 0;
-    if ($data->{'chargeperiod'}  && $units_minus_grace  ) {
-        $amount = int($chargeable_units / $data->{'chargeperiod'}) * $data->{'fine'};# TODO fine calc should be in cents
-    } else {
-        # a zero (or null)  chargeperiod means no charge.
-    }
-    $amount = $data->{overduefinescap} if $data->{overduefinescap} && $amount > $data->{overduefinescap};
-    $debug and warn sprintf("CalcFine returning (%s, %s, %s, %s)", $amount, $data->{'chargename'}, $units_minus_grace, $chargeable_units);
-    return ($amount, $data->{'chargename'}, $units_minus_grace, $chargeable_units);
-    # FIXME: chargename is NEVER populated anywhere.
+    if ( $issuing_rule->chargeperiod && ( $units_minus_grace > 0 ) ) {
+        my $units = C4::Context->preference('FinesIncludeGracePeriod') ? $chargeable_units : $units_minus_grace;
+        my $charge_periods = $units / $issuing_rule->chargeperiod;
+        # If chargeperiod_charge_at = 1, we charge a fine at the start of each charge period
+        # if chargeperiod_charge_at = 0, we charge at the end of each charge period
+        $charge_periods = $issuing_rule->chargeperiod_charge_at == 1 ? ceil($charge_periods) : floor($charge_periods);
+        $amount = $charge_periods * $issuing_rule->fine;
+    } # else { # a zero (or null) chargeperiod or negative units_minus_grace value means no charge. }
+
+    $amount = $issuing_rule->overduefinescap if $issuing_rule->overduefinescap && $amount > $issuing_rule->overduefinescap;
+
+    # This must be moved to Koha::Item (see also similar code in C4::Accounts::chargelostitem
+    $item->{replacementprice} ||= $itemtype->defaultreplacecost
+      if $itemtype
+      && $item->{replacementprice} == 0
+      && C4::Context->preference("useDefaultReplacementCost");
+
+    $amount = $item->{replacementprice} if ( $issuing_rule->cap_fine_to_replacement_price && $item->{replacementprice} && $amount > $item->{replacementprice} );
+
+    $debug and warn sprintf("CalcFine returning (%s, %s, %s)", $amount, $units_minus_grace, $chargeable_units);
+    return ($amount, $units_minus_grace, $chargeable_units);
 }
 
 
 }
 
 
-=head2 _get_chargeable_units
+=head2 get_chargeable_units
 
 
-    _get_chargeable_units($unit, $start_date_ $end_date, $branchcode);
+    get_chargeable_units($unit, $start_date_ $end_date, $branchcode);
 
 return integer value of units between C<$start_date> and C<$end_date>, factoring in holidays for C<$branchcode>.
 
 
 return integer value of units between C<$start_date> and C<$end_date>, factoring in holidays for C<$branchcode>.
 
@@ -289,16 +284,20 @@ C<$branchcode> is the branch whose calendar to use for finding holidays.
 
 =cut
 
 
 =cut
 
-sub _get_chargeable_units {
-    my ($unit, $dt1, $dt2, $branchcode) = @_;
+sub get_chargeable_units {
+    my ($unit, $date_due, $date_returned, $branchcode) = @_;
+
+    # If the due date is later than the return date
+    return 0 unless ( $date_returned > $date_due );
+
     my $charge_units = 0;
     my $charge_duration;
     if ($unit eq 'hours') {
         if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
             my $calendar = Koha::Calendar->new( branchcode => $branchcode );
     my $charge_units = 0;
     my $charge_duration;
     if ($unit eq 'hours') {
         if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
             my $calendar = Koha::Calendar->new( branchcode => $branchcode );
-            $charge_duration = $calendar->hours_between( $dt1, $dt2 );
+            $charge_duration = $calendar->hours_between( $date_due, $date_returned );
         } else {
         } else {
-            $charge_duration = $dt2->delta_ms( $dt1 );
+            $charge_duration = $date_returned->delta_ms( $date_due );
         }
         if($charge_duration->in_units('hours') == 0 && $charge_duration->in_units('seconds') > 0){
             return 1;
         }
         if($charge_duration->in_units('hours') == 0 && $charge_duration->in_units('seconds') > 0){
             return 1;
@@ -308,9 +307,9 @@ sub _get_chargeable_units {
     else { # days
         if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
             my $calendar = Koha::Calendar->new( branchcode => $branchcode );
     else { # days
         if(C4::Context->preference('finesCalendar') eq 'noFinesWhenClosed') {
             my $calendar = Koha::Calendar->new( branchcode => $branchcode );
-            $charge_duration = $calendar->days_between( $dt1, $dt2 );
+            $charge_duration = $calendar->days_between( $date_due, $date_returned );
         } else {
         } else {
-            $charge_duration = $dt2->delta_days( $dt1 );
+            $charge_duration = $date_returned->delta_days( $date_due );
         }
         return $charge_duration->in_units('days');
     }
         }
         return $charge_duration->in_units('days');
     }
@@ -467,7 +466,15 @@ sub GetIssuesIteminfo {
 
 =head2 UpdateFine
 
 
 =head2 UpdateFine
 
-    &UpdateFine($itemnumber, $borrowernumber, $amount, $type, $description);
+    &UpdateFine(
+        {
+            issue_id       => $issue_id,
+            itemnumber     => $itemnumber,
+            borrowernumber => $borrowernumber,
+            amount         => $amount,
+            due            => $date_due
+        }
+    );
 
 (Note: the following is mostly conjecture and guesswork.)
 
 
 (Note: the following is mostly conjecture and guesswork.)
 
@@ -480,10 +487,7 @@ has the book on loan.
 
 C<$amount> is the current amount owed by the patron.
 
 
 C<$amount> is the current amount owed by the patron.
 
-C<$type> will be used in the description of the fine.
-
-C<$description> is a string that must be present in the description of
-the fine. I think this is expected to be a date in DD/MM/YYYY format.
+C<$due> is the due date formatted to the currently specified date format
 
 C<&UpdateFine> looks up the amount currently owed on the given item
 and sets it to C<$amount>, creating, if necessary, a new entry in the
 
 C<&UpdateFine> looks up the amount currently owed on the given item
 and sets it to C<$amount>, creating, if necessary, a new entry in the
@@ -500,8 +504,21 @@ accountlines table of the Koha database.
 # Possible Answer: You might update a fine for a damaged item, *after* it is returned.
 #
 sub UpdateFine {
 # Possible Answer: You might update a fine for a damaged item, *after* it is returned.
 #
 sub UpdateFine {
-    my ( $itemnum, $borrowernumber, $amount, $type, $due ) = @_;
-       $debug and warn "UpdateFine($itemnum, $borrowernumber, $amount, " . ($type||'""') . ", $due) called";
+    my ($params) = @_;
+
+    my $issue_id       = $params->{issue_id};
+    my $itemnum        = $params->{itemnumber};
+    my $borrowernumber = $params->{borrowernumber};
+    my $amount         = $params->{amount};
+    my $due            = $params->{due};
+
+    $debug and warn "UpdateFine({ itemnumber => $itemnum, borrowernumber => $borrowernumber, due => $due, issue_id => $issue_id})";
+
+    unless ( $issue_id ) {
+        carp("No issue_id passed in!");
+        return;
+    }
+
     my $dbh = C4::Context->dbh;
     # FIXME - What exactly is this query supposed to do? It looks up an
     # entry in accountlines that matches the given item and borrower
     my $dbh = C4::Context->dbh;
     # FIXME - What exactly is this query supposed to do? It looks up an
     # entry in accountlines that matches the given item and borrower
@@ -509,19 +526,20 @@ sub UpdateFine {
     # account type has one of several values, but what does this _mean_?
     # Does it look up existing fines for this item?
     # FIXME - What are these various account types? ("FU", "O", "F", "M")
     # account type has one of several values, but what does this _mean_?
     # Does it look up existing fines for this item?
     # FIXME - What are these various account types? ("FU", "O", "F", "M")
-       #       "L"   is LOST item
-       #   "A"   is Account Management Fee
-       #   "N"   is New Card
-       #   "M"   is Sundry
-       #   "O"   is Overdue ??
-       #   "F"   is Fine ??
-       #   "FU"  is Fine UPDATE??
-       #       "Pay" is Payment
-       #   "REF" is Cash Refund
+    #   "L"   is LOST item
+    #   "A"   is Account Management Fee
+    #   "N"   is New Card
+    #   "M"   is Sundry
+    #   "O"   is Overdue ??
+    #   "F"   is Fine ??
+    #   "FU"  is Fine UPDATE??
+    #   "Pay" is Payment
+    #   "REF" is Cash Refund
     my $sth = $dbh->prepare(
         "SELECT * FROM accountlines
     my $sth = $dbh->prepare(
         "SELECT * FROM accountlines
-        WHERE borrowernumber=?
-        AND   accounttype IN ('FU','O','F','M')"
+        WHERE borrowernumber=? AND
+        (( accounttype IN ('O','F','M') AND amountoutstanding<>0 ) OR
+           accounttype = 'FU' )"
     );
     $sth->execute( $borrowernumber );
     my $data;
     );
     $sth->execute( $borrowernumber );
     my $data;
@@ -532,86 +550,97 @@ sub UpdateFine {
     # - accumulate fines for other items
     # so we can update $itemnum fine taking in account fine caps
     while (my $rec = $sth->fetchrow_hashref) {
     # - accumulate fines for other items
     # so we can update $itemnum fine taking in account fine caps
     while (my $rec = $sth->fetchrow_hashref) {
-        if ($rec->{itemnumber} == $itemnum && $rec->{description} =~ /$due_qr/) {
+        if ( $rec->{issue_id} == $issue_id && $rec->{accounttype} eq 'FU' ) {
             if ($data) {
             if ($data) {
-                warn "Not a unique accountlines record for item $itemnum borrower $borrowernumber";
-            } else {
+                warn "Not a unique accountlines record for issue_id $issue_id";
+                #FIXME Should we still count this one in total_amount ??
+            }
+            else {
                 $data = $rec;
                 next;
             }
         }
                 $data = $rec;
                 next;
             }
         }
-        $total_amount_other += $rec->{'amount'};
+        $total_amount_other += $rec->{'amountoutstanding'};
     }
     }
+
     if (my $maxfine = C4::Context->preference('MaxFine')) {
         if ($total_amount_other + $amount > $maxfine) {
             my $new_amount = $maxfine - $total_amount_other;
     if (my $maxfine = C4::Context->preference('MaxFine')) {
         if ($total_amount_other + $amount > $maxfine) {
             my $new_amount = $maxfine - $total_amount_other;
-            warn "Reducing fine for item $itemnum borrower $borrowernumber from $amount to $new_amount - MaxFine reached";
             return if $new_amount <= 0.00;
             return if $new_amount <= 0.00;
-
+            warn "Reducing fine for item $itemnum borrower $borrowernumber from $amount to $new_amount - MaxFine reached";
             $amount = $new_amount;
         }
     }
 
     if ( $data ) {
             $amount = $new_amount;
         }
     }
 
     if ( $data ) {
-
-               # we're updating an existing fine.  Only modify if amount changed
+        # we're updating an existing fine.  Only modify if amount changed
         # Note that in the current implementation, you cannot pay against an accruing fine
         # (i.e. , of accounttype 'FU').  Doing so will break accrual.
         # Note that in the current implementation, you cannot pay against an accruing fine
         # (i.e. , of accounttype 'FU').  Doing so will break accrual.
-       if ( $data->{'amount'} != $amount ) {
+        if ( $data->{'amount'} != $amount ) {
+            my $accountline = Koha::Account::Lines->find( $data->{accountlines_id} );
             my $diff = $amount - $data->{'amount'};
             my $diff = $amount - $data->{'amount'};
-           #3341: diff could be positive or negative!
-            my $out  = $data->{'amountoutstanding'} + $diff;
-            my $query = "
-                UPDATE accountlines
-                               SET date=now(), amount=?, amountoutstanding=?,
-                                       lastincrement=?, accounttype='FU'
-                               WHERE borrowernumber=?
-                               AND   itemnumber=?
-                               AND   accounttype IN ('FU','O')
-                               AND   description LIKE ?
-                               LIMIT 1 ";
-            my $sth2 = $dbh->prepare($query);
-                       # FIXME: BOGUS query cannot ensure uniqueness w/ LIKE %x% !!!
-                       #               LIMIT 1 added to prevent multiple affected lines
-                       # FIXME: accountlines table needs unique key!! Possibly a combo of borrowernumber and accountline.  
-                       #               But actually, we should just have a regular autoincrementing PK and forget accountline,
-                       #               including the bogus getnextaccountno function (doesn't prevent conflict on simultaneous ops).
-                       # FIXME: Why only 2 account types here?
-                       $debug and print STDERR "UpdateFine query: $query\n" .
-                               "w/ args: $amount, $out, $diff, $data->{'borrowernumber'}, $data->{'itemnumber'}, \"\%$due\%\"\n";
-            $sth2->execute($amount, $out, $diff, $data->{'borrowernumber'}, $data->{'itemnumber'}, "%$due%");
-        } else {
-            #      print "no update needed $data->{'amount'}"
+
+            #3341: diff could be positive or negative!
+            my $out   = $data->{'amountoutstanding'} + $diff;
+
+            $accountline->set(
+                {
+                    date          => dt_from_string(),
+                    amount        => $amount,
+                    amountoutstanding   => $out,
+                    lastincrement => $diff,
+                    accounttype   => 'FU',
+                }
+            )->store();
+
+            Koha::Account::Offset->new(
+                {
+                    debit_id => $accountline->id,
+                    type     => 'Fine Update',
+                    amount   => $diff,
+                }
+            )->store();
         }
     } else {
         }
     } else {
-        my $sth4 = $dbh->prepare(
-            "SELECT title FROM biblio LEFT JOIN items ON biblio.biblionumber=items.biblionumber WHERE items.itemnumber=?"
-        );
-        $sth4->execute($itemnum);
-        my $title = $sth4->fetchrow;
-
-#         #   print "not in account";
-#         my $sth3 = $dbh->prepare("Select max(accountno) from accountlines");
-#         $sth3->execute;
-# 
-#         # FIXME - Make $accountno a scalar.
-#         my @accountno = $sth3->fetchrow_array;
-#         $sth3->finish;
-#         $accountno[0]++;
-# begin transaction
-               my $nextaccntno = C4::Accounts::getnextacctno($borrowernumber);
-               my $desc = ($type ? "$type " : '') . "$title $due";     # FIXEDME, avoid whitespace prefix on empty $type
-               my $query = "INSERT INTO accountlines
-                   (borrowernumber,itemnumber,date,amount,description,accounttype,amountoutstanding,lastincrement,accountno)
-                           VALUES (?,?,now(),?,?,'FU',?,?,?)";
-               my $sth2 = $dbh->prepare($query);
-               $debug and print STDERR "UpdateFine query: $query\nw/ args: $borrowernumber, $itemnum, $amount, $desc, $amount, $amount, $nextaccntno\n";
-        $sth2->execute($borrowernumber, $itemnum, $amount, $desc, $amount, $amount, $nextaccntno);
+        if ( $amount ) { # Don't add new fines with an amount of 0
+            my $sth4 = $dbh->prepare(
+                "SELECT title FROM biblio LEFT JOIN items ON biblio.biblionumber=items.biblionumber WHERE items.itemnumber=?"
+            );
+            $sth4->execute($itemnum);
+            my $title = $sth4->fetchrow;
+
+            my $nextaccntno = C4::Accounts::getnextacctno($borrowernumber);
+
+            my $desc = "$title $due";
+
+            my $accountline = Koha::Account::Line->new(
+                {
+                    borrowernumber    => $borrowernumber,
+                    itemnumber        => $itemnum,
+                    date              => dt_from_string(),
+                    amount            => $amount,
+                    description       => $desc,
+                    accounttype       => 'FU',
+                    amountoutstanding => $amount,
+                    lastincrement     => $amount,
+                    accountno         => $nextaccntno,
+                    issue_id          => $issue_id,
+                }
+            )->store();
+
+            Koha::Account::Offset->new(
+                {
+                    debit_id => $accountline->id,
+                    type     => 'Fine',
+                    amount   => $amount,
+                }
+            )->store();
+        }
     }
     # logging action
     &logaction(
         "FINES",
     }
     # logging action
     &logaction(
         "FINES",
-        $type,
+        undef,
         $borrowernumber,
         "due=".$due."  amount=".$amount." itemnumber=".$itemnum
         ) if C4::Context->preference("FinesLog");
         $borrowernumber,
         "due=".$due."  amount=".$amount." itemnumber=".$itemnum
         ) if C4::Context->preference("FinesLog");
@@ -626,11 +655,10 @@ Looks up a patron by borrower number.
 C<$borrower> is a reference-to-hash whose keys are all of the fields
 from the borrowers and categories tables of the Koha database. Thus,
 C<$borrower> contains all information about both the borrower and
 C<$borrower> is a reference-to-hash whose keys are all of the fields
 from the borrowers and categories tables of the Koha database. Thus,
 C<$borrower> contains all information about both the borrower and
-category he or she belongs to.
+category they belong to.
 
 =cut
 
 
 =cut
 
-#'
 sub BorType {
     my ($borrowernumber) = @_;
     my $dbh              = C4::Context->dbh;
 sub BorType {
     my ($borrowernumber) = @_;
     my $dbh              = C4::Context->dbh;
@@ -643,27 +671,6 @@ sub BorType {
     return $sth->fetchrow_hashref;
 }
 
     return $sth->fetchrow_hashref;
 }
 
-=head2 ReplacementCost
-
-    $cost = &ReplacementCost($itemnumber);
-
-Returns the replacement cost of the item with the given item number.
-
-=cut
-
-#'
-sub ReplacementCost {
-    my ($itemnum) = @_;
-    my $dbh       = C4::Context->dbh;
-    my $sth       =
-      $dbh->prepare("Select replacementprice from items where itemnumber=?");
-    $sth->execute($itemnum);
-
-    # FIXME - Use fetchrow_array or a slice.
-    my $data = $sth->fetchrow_hashref;
-    return ( $data->{'replacementprice'} );
-}
-
 =head2 GetFine
 
     $data->{'sum(amountoutstanding)'} = &GetFine($itemnum,$borrowernumber);
 =head2 GetFine
 
     $data->{'sum(amountoutstanding)'} = &GetFine($itemnum,$borrowernumber);
@@ -676,15 +683,21 @@ C<$borrowernumber> is the borrowernumber
 
 =cut 
 
 
 =cut 
 
-
 sub GetFine {
     my ( $itemnum, $borrowernumber ) = @_;
     my $dbh   = C4::Context->dbh();
     my $query = q|SELECT sum(amountoutstanding) as fineamount FROM accountlines
     where accounttype like 'F%'
 sub GetFine {
     my ( $itemnum, $borrowernumber ) = @_;
     my $dbh   = C4::Context->dbh();
     my $query = q|SELECT sum(amountoutstanding) as fineamount FROM accountlines
     where accounttype like 'F%'
-  AND amountoutstanding > 0 AND itemnumber = ? AND borrowernumber=?|;
+  AND amountoutstanding > 0 AND borrowernumber=?|;
+    my @query_param;
+    push @query_param, $borrowernumber;
+    if (defined $itemnum )
+    {
+        $query .= " AND itemnumber=?";
+        push @query_param, $itemnum;
+    }
     my $sth = $dbh->prepare($query);
     my $sth = $dbh->prepare($query);
-    $sth->execute( $itemnum, $borrowernumber );
+    $sth->execute( @query_param );
     my $fine = $sth->fetchrow_hashref();
     if ($fine->{fineamount}) {
         return $fine->{fineamount};
     my $fine = $sth->fetchrow_hashref();
     if ($fine->{fineamount}) {
         return $fine->{fineamount};
@@ -692,329 +705,6 @@ sub GetFine {
     return 0;
 }
 
     return 0;
 }
 
-
-=head2 GetIssuingRules
-
-FIXME - This sub should be deprecated and removed.
-It ignores branch and defaults.
-
-    $data = &GetIssuingRules($itemtype,$categorycode);
-
-Looks up for all issuingrules an item info 
-
-C<$itemnumber> is a reference-to-hash whose keys are all of the fields
-from the borrowers and categories tables of the Koha database. Thus,
-
-C<$categorycode> contains  information about borrowers category 
-
-C<$data> contains all information about both the borrower and
-category he or she belongs to.
-=cut 
-
-sub GetIssuingRules {
-       warn "GetIssuingRules is deprecated: use GetIssuingRule from C4::Circulation instead.";
-   my ($itemtype,$categorycode)=@_;
-   my $dbh   = C4::Context->dbh();    
-   my $query=qq|SELECT *
-        FROM issuingrules
-        WHERE issuingrules.itemtype=?
-            AND issuingrules.categorycode=?
-        |;
-    my $sth = $dbh->prepare($query);
-    #  print $query;
-    $sth->execute($itemtype,$categorycode);
-    return $sth->fetchrow_hashref;
-}
-
-
-sub ReplacementCost2 {
-    my ( $itemnum, $borrowernumber ) = @_;
-    my $dbh   = C4::Context->dbh();
-    my $query = "SELECT amountoutstanding
-         FROM accountlines
-             WHERE accounttype like 'L'
-         AND amountoutstanding > 0
-         AND itemnumber = ?
-         AND borrowernumber= ?";
-    my $sth = $dbh->prepare($query);
-    $sth->execute( $itemnum, $borrowernumber );
-    my $data = $sth->fetchrow_hashref();
-    return ( $data->{'amountoutstanding'} );
-}
-
-
-=head2 GetNextIdNotify
-
-    ($result) = &GetNextIdNotify($reference);
-
-Returns the new file number
-
-C<$result> contains the next file number
-
-C<$reference> contains the beggining of file number
-
-=cut
-
-sub GetNextIdNotify {
-    my ($reference) = @_;
-    my $query = qq|SELECT max(notify_id)
-         FROM accountlines
-         WHERE notify_id  like \"$reference%\"
-         |;
-
-    # AND borrowernumber=?|;
-    my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare($query);
-    $sth->execute();
-    my $result = $sth->fetchrow;
-    my $count;
-    if ( $result eq '' ) {
-        ( $result = $reference . "01" );
-    }
-    else {
-        $count = substr( $result, 6 ) + 1;
-
-        if ( $count < 10 ) {
-            ( $count = "0" . $count );
-        }
-        $result = $reference . $count;
-    }
-    return $result;
-}
-
-=head2 NumberNotifyId
-
-    (@notify) = &NumberNotifyId($borrowernumber);
-
-Returns amount for all file per borrowers
-C<@notify> array contains all file per borrowers
-
-C<$notify_id> contains the file number for the borrower number nad item number
-
-=cut
-
-sub NumberNotifyId{
-    my ($borrowernumber)=@_;
-    my $dbh = C4::Context->dbh;
-    my $query=qq|    SELECT distinct(notify_id)
-            FROM accountlines
-            WHERE borrowernumber=?|;
-    my @notify;
-    my $sth = $dbh->prepare($query);
-    $sth->execute($borrowernumber);
-    while ( my ($numberofnotify) = $sth->fetchrow ) {
-        push( @notify, $numberofnotify );
-    }
-    return (@notify);
-}
-
-=head2 AmountNotify
-
-    ($totalnotify) = &AmountNotify($notifyid);
-
-Returns amount for all file per borrowers
-C<$notifyid> is the file number
-
-C<$totalnotify> contains amount of a file
-
-C<$notify_id> contains the file number for the borrower number and item number
-
-=cut
-
-sub AmountNotify{
-    my ($notifyid,$borrowernumber)=@_;
-    my $dbh = C4::Context->dbh;
-    my $query=qq|    SELECT sum(amountoutstanding)
-            FROM accountlines
-            WHERE notify_id=? AND borrowernumber = ?|;
-    my $sth=$dbh->prepare($query);
-       $sth->execute($notifyid,$borrowernumber);
-       my $totalnotify=$sth->fetchrow;
-    $sth->finish;
-    return ($totalnotify);
-}
-
-
-=head2 GetNotifyId
-
-    ($notify_id) = &GetNotifyId($borrowernumber,$itemnumber);
-
-Returns the file number per borrower and itemnumber
-
-C<$borrowernumber> is a reference-to-hash whose keys are all of the fields
-from the items tables of the Koha database. Thus,
-
-C<$itemnumber> contains the borrower categorycode
-
-C<$notify_id> contains the file number for the borrower number nad item number
-
-=cut
-
-sub GetNotifyId {
-    my ( $borrowernumber, $itemnumber ) = @_;
-    my $query = qq|SELECT notify_id
-           FROM accountlines
-           WHERE borrowernumber=?
-          AND itemnumber=?
-           AND (accounttype='FU' or accounttype='O')|;
-    my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare($query);
-    $sth->execute( $borrowernumber, $itemnumber );
-    my ($notify_id) = $sth->fetchrow;
-    $sth->finish;
-    return ($notify_id);
-}
-
-=head2 CreateItemAccountLine
-
-    () = &CreateItemAccountLine($borrowernumber, $itemnumber, $date, $amount,
-                               $description, $accounttype, $amountoutstanding, 
-                               $timestamp, $notify_id, $level);
-
-update the account lines with file number or with file level
-
-C<$items> is a reference-to-hash whose keys are all of the fields
-from the items tables of the Koha database. Thus,
-
-C<$itemnumber> contains the item number
-
-C<$borrowernumber> contains the borrower number
-
-C<$date> contains the date of the day
-
-C<$amount> contains item price
-
-C<$description> contains the descritpion of accounttype 
-
-C<$accounttype> contains the account type
-
-C<$amountoutstanding> contains the $amountoutstanding 
-
-C<$timestamp> contains the timestamp with time and the date of the day
-
-C<$notify_id> contains the file number
-
-C<$level> contains the file level
-
-=cut
-
-sub CreateItemAccountLine {
-    my (
-        $borrowernumber, $itemnumber,  $date,              $amount,
-        $description,    $accounttype, $amountoutstanding, $timestamp,
-        $notify_id,      $level
-    ) = @_;
-    my $dbh         = C4::Context->dbh;
-    my $nextaccntno = C4::Accounts::getnextacctno($borrowernumber);
-    my $query       = "INSERT into accountlines
-         (borrowernumber,accountno,itemnumber,date,amount,description,accounttype,amountoutstanding,timestamp,notify_id,notify_level)
-          VALUES
-             (?,?,?,?,?,?,?,?,?,?,?)";
-
-    my $sth = $dbh->prepare($query);
-    $sth->execute(
-        $borrowernumber, $nextaccntno,       $itemnumber,
-        $date,           $amount,            $description,
-        $accounttype,    $amountoutstanding, $timestamp,
-        $notify_id,      $level
-    );
-}
-
-=head2 UpdateAccountLines
-
-    () = &UpdateAccountLines($notify_id,$notify_level,$borrowernumber,$itemnumber);
-
-update the account lines with file number or with file level
-
-C<$items> is a reference-to-hash whose keys are all of the fields
-from the items tables of the Koha database. Thus,
-
-C<$itemnumber> contains the item number
-
-C<$notify_id> contains the file number
-
-C<$notify_level> contains the file level
-
-C<$borrowernumber> contains the borrowernumber
-
-=cut
-
-sub UpdateAccountLines {
-    my ( $notify_id, $notify_level, $borrowernumber, $itemnumber ) = @_;
-    my $query;
-    if ( $notify_id eq '' ) {
-        $query = qq|UPDATE accountlines
-    SET  notify_level=?
-    WHERE borrowernumber=? AND itemnumber=?
-    AND (accounttype='FU' or accounttype='O')|;
-    } else {
-        $query = qq|UPDATE accountlines
-     SET notify_id=?, notify_level=?
-   WHERE borrowernumber=?
-    AND itemnumber=?
-    AND (accounttype='FU' or accounttype='O')|;
-    }
-
-    my $sth = C4::Context->dbh->prepare($query);
-    if ( $notify_id eq '' ) {
-        $sth->execute( $notify_level, $borrowernumber, $itemnumber );
-    } else {
-        $sth->execute( $notify_id, $notify_level, $borrowernumber, $itemnumber );
-    }
-}
-
-=head2 GetItems
-
-    ($items) = &GetItems($itemnumber);
-
-Returns the list of all delays from overduerules.
-
-C<$items> is a reference-to-hash whose keys are all of the fields
-from the items tables of the Koha database. Thus,
-
-C<$itemnumber> contains the borrower categorycode
-
-=cut
-
-# FIXME: This is a bad function to have here.
-# Shouldn't it be in C4::Items?
-# Shouldn't it be called GetItem since you only get 1 row?
-# Shouldn't it be called GetItem since you give it only 1 itemnumber?
-
-sub GetItems {
-    my $itemnumber = shift or return;
-    my $query = qq|SELECT *
-             FROM items
-              WHERE itemnumber=?|;
-    my $sth = C4::Context->dbh->prepare($query);
-    $sth->execute($itemnumber);
-    my ($items) = $sth->fetchrow_hashref;
-    return ($items);
-}
-
-=head2 GetOverdueDelays
-
-    (@delays) = &GetOverdueDelays($categorycode);
-
-Returns the list of all delays from overduerules.
-
-C<@delays> it's an array contains the three delays from overduerules table
-
-C<$categorycode> contains the borrower categorycode
-
-=cut
-
-sub GetOverdueDelays {
-    my ($category) = @_;
-    my $query      = qq|SELECT delay1,delay2,delay3
-                FROM overduerules
-                WHERE categorycode=?|;
-    my $sth = C4::Context->dbh->prepare($query);
-    $sth->execute($category);
-    my (@delays) = $sth->fetchrow_array;
-    return (@delays);
-}
-
 =head2 GetBranchcodesWithOverdueRules
 
     my @branchcodes = C4::Overdues::GetBranchcodesWithOverdueRules()
 =head2 GetBranchcodesWithOverdueRules
 
     my @branchcodes = C4::Overdues::GetBranchcodesWithOverdueRules()
@@ -1025,185 +715,17 @@ returns a list of branch codes for branches with overdue rules defined.
 
 sub GetBranchcodesWithOverdueRules {
     my $dbh               = C4::Context->dbh;
 
 sub GetBranchcodesWithOverdueRules {
     my $dbh               = C4::Context->dbh;
-    my $rqoverduebranches = $dbh->prepare("SELECT DISTINCT branchcode FROM overduerules WHERE delay1 IS NOT NULL AND branchcode <> '' ORDER BY branchcode");
-    $rqoverduebranches->execute;
-    my @branches = map { shift @$_ } @{ $rqoverduebranches->fetchall_arrayref };
-    if (!$branches[0]) {
-       my $availbranches = C4::Branch::GetBranches();
-       @branches = keys %$availbranches;
+    my $branchcodes = $dbh->selectcol_arrayref(q|
+        SELECT DISTINCT(branchcode)
+        FROM overduerules
+        WHERE delay1 IS NOT NULL
+        ORDER BY branchcode
+    |);
+    if ( $branchcodes->[0] eq '' ) {
+        # If a default rule exists, all branches should be returned
+        return map { $_->branchcode } Koha::Libraries->search({}, { order_by => 'branchname' });
     }
     }
-    return @branches;
-}
-
-=head2 CheckAccountLineLevelInfo
-
-    ($exist) = &CheckAccountLineLevelInfo($borrowernumber,$itemnumber,$accounttype,notify_level);
-
-Check and Returns the list of all overdue books.
-
-C<$exist> contains number of line in accounlines
-with the same .biblionumber,itemnumber,accounttype,and notify_level
-
-C<$borrowernumber> contains the borrower number
-
-C<$itemnumber> contains item number
-
-C<$accounttype> contains account type
-
-C<$notify_level> contains the accountline level 
-
-
-=cut
-
-sub CheckAccountLineLevelInfo {
-    my ( $borrowernumber, $itemnumber, $level ) = @_;
-    my $dbh   = C4::Context->dbh;
-    my $query = qq|SELECT count(*)
-            FROM accountlines
-            WHERE borrowernumber =?
-            AND itemnumber = ?
-            AND notify_level=?|;
-    my $sth = $dbh->prepare($query);
-    $sth->execute( $borrowernumber, $itemnumber, $level );
-    my ($exist) = $sth->fetchrow;
-    return ($exist);
-}
-
-=head2 GetOverduerules
-
-    ($overduerules) = &GetOverduerules($categorycode);
-
-Returns the value of borrowers (debarred or not) with notify level
-
-C<$overduerules> return value of debbraed field in overduerules table
-
-C<$category> contains the borrower categorycode
-
-C<$notify_level> contains the notify level
-
-=cut
-
-sub GetOverduerules {
-    my ( $category, $notify_level ) = @_;
-    my $dbh   = C4::Context->dbh;
-    my $query = qq|SELECT debarred$notify_level
-                     FROM overduerules
-                    WHERE categorycode=?|;
-    my $sth = $dbh->prepare($query);
-    $sth->execute($category);
-    my ($overduerules) = $sth->fetchrow;
-    return ($overduerules);
-}
-
-
-=head2 CheckBorrowerDebarred
-
-    ($debarredstatus) = &CheckBorrowerDebarred($borrowernumber);
-
-Check if the borrowers is already debarred
-
-C<$debarredstatus> return 0 for not debarred and return 1 for debarred
-
-C<$borrowernumber> contains the borrower number
-
-=cut
-
-# FIXME: Shouldn't this be in C4::Members?
-sub CheckBorrowerDebarred {
-    my ($borrowernumber) = @_;
-    my $dbh   = C4::Context->dbh;
-    my $query = qq|
-        SELECT debarred
-        FROM borrowers
-        WHERE borrowernumber=?
-        AND debarred > NOW()
-    |;
-    my $sth = $dbh->prepare($query);
-    $sth->execute($borrowernumber);
-    my $debarredstatus = $sth->fetchrow;
-    return $debarredstatus;
-}
-
-
-=head2 CheckExistantNotifyid
-
-    ($exist) = &CheckExistantNotifyid($borrowernumber,$itemnumber,$accounttype,$notify_id);
-
-Check and Returns the notify id if exist else return 0.
-
-C<$exist> contains a notify_id 
-
-C<$borrowernumber> contains the borrower number
-
-C<$date_due> contains the date of item return 
-
-
-=cut
-
-sub CheckExistantNotifyid {
-    my ( $borrowernumber, $date_due ) = @_;
-    my $dbh   = C4::Context->dbh;
-    my $query = qq|SELECT notify_id FROM accountlines
-             LEFT JOIN issues ON issues.itemnumber= accountlines.itemnumber
-             WHERE accountlines.borrowernumber =?
-              AND date_due = ?|;
-    my $sth = $dbh->prepare($query);
-    $sth->execute( $borrowernumber, $date_due );
-    return $sth->fetchrow || 0;
-}
-
-=head2 CheckAccountLineItemInfo
-
-    ($exist) = &CheckAccountLineItemInfo($borrowernumber,$itemnumber,$accounttype,$notify_id);
-
-Check and Returns the list of all overdue items from the same file number(notify_id).
-
-C<$exist> contains number of line in accounlines
-with the same .biblionumber,itemnumber,accounttype,notify_id
-
-C<$borrowernumber> contains the borrower number
-
-C<$itemnumber> contains item number
-
-C<$accounttype> contains account type
-
-C<$notify_id> contains the file number 
-
-=cut
-
-sub CheckAccountLineItemInfo {
-    my ( $borrowernumber, $itemnumber, $accounttype, $notify_id ) = @_;
-    my $dbh   = C4::Context->dbh;
-    my $query = qq|SELECT count(*) FROM accountlines
-             WHERE borrowernumber =?
-             AND itemnumber = ?
-              AND accounttype= ?
-            AND notify_id = ?|;
-    my $sth = $dbh->prepare($query);
-    $sth->execute( $borrowernumber, $itemnumber, $accounttype, $notify_id );
-    my ($exist) = $sth->fetchrow;
-    return ($exist);
-}
-
-=head2 CheckItemNotify
-
-Sql request to check if the document has alreday been notified
-this function is not exported, only used with GetOverduesForBranch
-
-=cut
-
-sub CheckItemNotify {
-    my ($notify_id,$notify_level,$itemnumber) = @_;
-    my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare("
-    SELECT COUNT(*)
-     FROM notifys
-    WHERE notify_id    = ?
-     AND  notify_level = ? 
-     AND  itemnumber   = ? ");
-    $sth->execute($notify_id,$notify_level,$itemnumber);
-    my $notified = $sth->fetchrow;
-    return ($notified);
+    return @$branchcodes;
 }
 
 =head2 GetOverduesForBranch
 }
 
 =head2 GetOverduesForBranch
@@ -1222,6 +744,7 @@ sub GetOverduesForBranch {
     my $dbh = C4::Context->dbh;
     my $select = "
     SELECT
     my $dbh = C4::Context->dbh;
     my $select = "
     SELECT
+            borrowers.cardnumber,
             borrowers.borrowernumber,
             borrowers.surname,
             borrowers.firstname,
             borrowers.borrowernumber,
             borrowers.surname,
             borrowers.firstname,
@@ -1240,8 +763,6 @@ sub GetOverduesForBranch {
                 items.location,
                 items.itemnumber,
             itemtypes.description,
                 items.location,
                 items.itemnumber,
             itemtypes.description,
-         accountlines.notify_id,
-         accountlines.notify_level,
          accountlines.amountoutstanding
     FROM  accountlines
     LEFT JOIN issues      ON    issues.itemnumber     = accountlines.itemnumber
          accountlines.amountoutstanding
     FROM  accountlines
     LEFT JOIN issues      ON    issues.itemnumber     = accountlines.itemnumber
@@ -1257,78 +778,114 @@ sub GetOverduesForBranch {
       AND (issues.branchcode =  ?   )
       AND (issues.date_due  < NOW())
     ";
       AND (issues.branchcode =  ?   )
       AND (issues.date_due  < NOW())
     ";
-    my @getoverdues;
-    my $i = 0;
-    my $sth;
     if ($location) {
     if ($location) {
-        $sth = $dbh->prepare("$select AND items.location = ? ORDER BY borrowers.surname, borrowers.firstname");
-        $sth->execute($branch, $location);
+        my $q = "$select AND items.location = ? ORDER BY borrowers.surname, borrowers.firstname";
+        return @{ $dbh->selectall_arrayref($q, { Slice => {} }, $branch, $location ) };
     } else {
     } else {
-        $sth = $dbh->prepare("$select ORDER BY borrowers.surname, borrowers.firstname");
-        $sth->execute($branch);
-    }
-    while ( my $data = $sth->fetchrow_hashref ) {
-    #check if the document has already been notified
-        my $countnotify = CheckItemNotify($data->{'notify_id'}, $data->{'notify_level'}, $data->{'itemnumber'});
-        if ($countnotify eq '0') {
-            $getoverdues[$i] = $data;
-            $i++;
-        }
+        my $q = "$select ORDER BY borrowers.surname, borrowers.firstname";
+        return @{ $dbh->selectall_arrayref($q, { Slice => {} }, $branch ) };
     }
     }
-    return (@getoverdues);
 }
 
 }
 
+=head2 GetOverdueMessageTransportTypes
 
 
-=head2 AddNotifyLine
-
-    &AddNotifyLine($borrowernumber, $itemnumber, $overduelevel, $method, $notifyId)
+    my $message_transport_types = GetOverdueMessageTransportTypes( $branchcode, $categorycode, $letternumber);
 
 
-Create a line into notify, if the method is phone, the notification_send_date is implemented to
+    return a arrayref with all message_transport_type for given branchcode, categorycode and letternumber(1,2 or 3)
 
 =cut
 
 
 =cut
 
-sub AddNotifyLine {
-    my ( $borrowernumber, $itemnumber, $overduelevel, $method, $notifyId ) = @_;
+sub GetOverdueMessageTransportTypes {
+    my ( $branchcode, $categorycode, $letternumber ) = @_;
+    return unless $categorycode and $letternumber;
     my $dbh = C4::Context->dbh;
     my $dbh = C4::Context->dbh;
-    if ( $method eq "phone" ) {
-        my $sth = $dbh->prepare(
-            "INSERT INTO notifys (borrowernumber,itemnumber,notify_date,notify_send_date,notify_level,method,notify_id)
-        VALUES (?,?,now(),now(),?,?,?)"
-        );
-        $sth->execute( $borrowernumber, $itemnumber, $overduelevel, $method,
-            $notifyId );
-    }
-    else {
-        my $sth = $dbh->prepare(
-            "INSERT INTO notifys (borrowernumber,itemnumber,notify_date,notify_level,method,notify_id)
-        VALUES (?,?,now(),?,?,?)"
-        );
-        $sth->execute( $borrowernumber, $itemnumber, $overduelevel, $method,
-            $notifyId );
+    my $sth = $dbh->prepare("
+        SELECT message_transport_type
+        FROM overduerules odr LEFT JOIN overduerules_transport_types ott USING (overduerules_id)
+        WHERE branchcode = ?
+          AND categorycode = ?
+          AND letternumber = ?
+    ");
+    $sth->execute( $branchcode, $categorycode, $letternumber );
+    my @mtts;
+    while ( my $mtt = $sth->fetchrow ) {
+        push @mtts, $mtt;
     }
     }
-    return 1;
+
+    # Put 'print' in first if exists
+    # It avoid to sent a print notice with an email or sms template is no email or sms is defined
+    @mtts = uniq( 'print', @mtts )
+        if grep {/^print$/} @mtts;
+
+    return \@mtts;
 }
 
 }
 
-=head2 RemoveNotifyLine
+=head2 parse_overdues_letter
+
+parses the letter template, replacing the placeholders with data
+specific to this patron, biblio, or item for overdues
 
 
-    &RemoveNotifyLine( $borrowernumber, $itemnumber, $notify_date );
+named parameters:
+  letter - required hashref
+  borrowernumber - required integer
+  substitute - optional hashref of other key/value pairs that should
+    be substituted in the letter content
 
 
-Cancel a notification
+returns the C<letter> hashref, with the content updated to reflect the
+substituted keys and values.
 
 =cut
 
 
 =cut
 
-sub RemoveNotifyLine {
-    my ( $borrowernumber, $itemnumber, $notify_date ) = @_;
-    my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare(
-        "DELETE FROM notifys 
-            WHERE
-            borrowernumber=?
-            AND itemnumber=?
-            AND notify_date=?"
+sub parse_overdues_letter {
+    my $params = shift;
+    foreach my $required (qw( letter_code borrowernumber )) {
+        return unless ( exists $params->{$required} && $params->{$required} );
+    }
+
+    my $patron = Koha::Patrons->find( $params->{borrowernumber} );
+
+    my $substitute = $params->{'substitute'} || {};
+
+    my %tables = ( 'borrowers' => $params->{'borrowernumber'} );
+    if ( my $p = $params->{'branchcode'} ) {
+        $tables{'branches'} = $p;
+    }
+
+    my $active_currency = Koha::Acquisition::Currencies->get_active;
+
+    my $currency_format;
+    $currency_format = $active_currency->currency if defined($active_currency);
+
+    my @item_tables;
+    if ( my $i = $params->{'items'} ) {
+        foreach my $item (@$i) {
+            my $fine = GetFine($item->{'itemnumber'}, $params->{'borrowernumber'});
+            $item->{'fine'} = currency_format($currency_format, "$fine", FMT_SYMBOL);
+            # if active currency isn't correct ISO code fallback to sprintf
+            $item->{'fine'} = sprintf('%.2f', $fine) unless $item->{'fine'};
+
+            push @item_tables, {
+                'biblio' => $item->{'biblionumber'},
+                'biblioitems' => $item->{'biblionumber'},
+                'items' => $item,
+                'issues' => $item->{'itemnumber'},
+            };
+        }
+    }
+
+    return C4::Letters::GetPreparedLetter (
+        module => 'circulation',
+        letter_code => $params->{'letter_code'},
+        branchcode => $params->{'branchcode'},
+        lang => $patron->lang,
+        tables => \%tables,
+        loops => {
+            overdues => [ map { $_->{items}->{itemnumber} } @item_tables ],
+        },
+        substitute => $substitute,
+        repeat => { item => \@item_tables },
+        message_transport_type => $params->{message_transport_type},
     );
     );
-    $sth->execute( $borrowernumber, $itemnumber, $notify_date );
-    return 1;
 }
 
 1;
 }
 
 1;