Bug 29234: Further clean Z3950 Tests
[koha-ffzg.git] / C4 / Overdues.pm
index 0fc6232..8074411 100644 (file)
@@ -19,65 +19,42 @@ package C4::Overdues;
 # You should have received a copy of the GNU General Public License
 # along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 # 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 Modern::Perl;
+use Date::Calc qw( Today );
+use Date::Manip qw( UnixDate );
 use List::MoreUtils qw( uniq );
 use List::MoreUtils qw( uniq );
-use POSIX qw( floor ceil );
-use Locale::Currency::Format 1.28;
+use POSIX qw( ceil floor );
+use Locale::Currency::Format 1.28 qw( currency_format FMT_SYMBOL );
+use Carp qw( carp );
 
 
-use C4::Circulation;
-use C4::Context;
 use C4::Accounts;
 use C4::Accounts;
-use C4::Log; # logaction
-use C4::Debug;
-use C4::Budgets qw(GetCurrency);
-
-use vars qw($VERSION @ISA @EXPORT);
-
+use C4::Context;
+use Koha::Account::Lines;
+use Koha::Account::Offsets;
+use Koha::DateUtils qw( output_pref );
+use Koha::Libraries;
+use Koha::Recalls;
+use Koha::Logger;
+use Koha::Patrons;
+
+our (@ISA, @EXPORT_OK);
 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...)
     require Exporter;
     @ISA = qw(Exporter);
 
     # subs to rename (and maybe merge some...)
-    push @EXPORT, qw(
-      &CalcFine
-      &Getoverdues
-      &checkoverdues
-      &NumberNotifyId
-      &AmountNotify
-      &UpdateFine
-      &GetFine
-      &get_chargeable_units
-      &CheckItemNotify
-      &GetOverduesForBranch
-      &RemoveNotifyLine
-      &AddNotifyLine
-      &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
-    );
-
-    # &GetIssuingRules - delete.
-    # use C4::Circulation::GetIssuingRule instead.
-
-    # subs to move to Biblio.pm
-    push @EXPORT, qw(
-      &GetItems
+    @EXPORT_OK = qw(
+      CalcFine
+      Getoverdues
+      checkoverdues
+      UpdateFine
+      GetFine
+      GetBranchcodesWithOverdueRules
+      get_chargeable_units
+      GetOverduesForBranch
+      GetOverdueMessageTransportTypes
+      parse_overdues_letter
+      GetIssuesIteminfo
     );
 }
 
     );
 }
 
@@ -115,14 +92,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, items.itemlost
+   SELECT issues.*, items.itype as itemtype, items.homebranch, items.barcode, items.itemlost, items.replacementprice, items.biblionumber, items.holdingbranch
      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, items.itemlost
+   SELECT issues.*, biblioitems.itemtype, items.itype, items.homebranch, items.barcode, items.itemlost, replacementprice, items.biblionumber, items.holdingbranch
      FROM issues 
 LEFT JOIN items       USING (itemnumber)
 LEFT JOIN biblioitems USING (biblioitemnumber)
      FROM issues 
 LEFT JOIN items       USING (itemnumber)
 LEFT JOIN biblioitems USING (biblioitemnumber)
@@ -158,7 +135,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,
@@ -195,7 +171,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
@@ -203,7 +178,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 );
 
@@ -228,18 +203,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.
 
@@ -247,29 +219,80 @@ or "Final Notice".  But CalcFine never defined any value.
 
 sub CalcFine {
     my ( $item, $bortype, $branchcode, $due_dt, $end_date  ) = @_;
 
 sub CalcFine {
     my ( $item, $bortype, $branchcode, $due_dt, $end_date  ) = @_;
+
+    # Skip calculations if item is not overdue
+    return ( 0, 0, 0 ) unless (DateTime->compare( $due_dt, $end_date ) == -1);
+
     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::CirculationRules->get_effective_rules(
+        {
+            categorycode => $bortype,
+            itemtype     => $itemtype,
+            branchcode   => $branchcode,
+            rules => [
+                'lengthunit',
+                'firstremind',
+                'chargeperiod',
+                'chargeperiod_charge_at',
+                'fine',
+                'overduefinescap',
+                'cap_fine_to_replacement_price',
+                'recall_overdue_fine',
+            ]
+        }
+    );
+
+    $itemtype = Koha::ItemTypes->find($itemtype);
+
+    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 $chargeable_units = get_chargeable_units($fine_unit, $start_date, $end_date, $branchcode);
-    my $units_minus_grace = $chargeable_units - $data->{firstremind};
+    my $units_minus_grace = $chargeable_units - ($issuing_rule->{firstremind} || 0);
     my $amount = 0;
     my $amount = 0;
-    if ( $data->{'chargeperiod'} && ( $units_minus_grace > 0 ) ) {
+    if ( $issuing_rule->{chargeperiod} && ( $units_minus_grace > 0 ) ) {
         my $units = C4::Context->preference('FinesIncludeGracePeriod') ? $chargeable_units : $units_minus_grace;
         my $units = C4::Context->preference('FinesIncludeGracePeriod') ? $chargeable_units : $units_minus_grace;
-        my $charge_periods = $units / $data->{'chargeperiod'};
+        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
         # 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 = $data->{'chargeperiod_charge_at'} == 1 ? ceil($charge_periods) : floor($charge_periods);
-        $amount = $charge_periods * $data->{'fine'};
+        $charge_periods = defined $issuing_rule->{chargeperiod_charge_at} && $issuing_rule->{chargeperiod_charge_at} == 1 ? ceil($charge_periods) : floor($charge_periods);
+
+        # check if item has been recalled. recall should have been marked Overdue by cronjob, so only look at overdue recalls
+        # only charge using recall_overdue_fine if there is an item-level recall for this particular item, OR a biblio-level recall
+        my @recalls = Koha::Recalls->search({ biblio_id => $item->{biblionumber}, status => 'overdue' })->as_list;
+        my $bib_level_recall = 0;
+        $bib_level_recall = 1 if scalar @recalls > 0;
+        foreach my $recall ( @recalls ) {
+            if ( $recall->item_level and $recall->item_id == $item->{itemnumber} and $issuing_rule->{recall_overdue_fine} ) {
+                $bib_level_recall = 0;
+                $amount = $charge_periods * $issuing_rule->{recall_overdue_fine};
+                last;
+            }
+        }
+        if ( $bib_level_recall and $issuing_rule->{recall_overdue_fine} ) {
+            # biblio-level recall
+            $amount = $charge_periods * $issuing_rule->{recall_overdue_fine};
+        }
+        if ( scalar @recalls == 0 && $issuing_rule->{fine}) {
+            # no recall, use normal fine amount
+            $amount = $charge_periods * $issuing_rule->{fine};
+        }
     } # else { # a zero (or null) chargeperiod or negative units_minus_grace value means no charge. }
 
     } # else { # a zero (or null) chargeperiod or negative units_minus_grace value 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.
+    $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
+      && ( ! defined $item->{replacementprice} || $item->{replacementprice} == 0 )
+      && C4::Context->preference("useDefaultReplacementCost");
+
+    $amount = $item->{replacementprice} if ( $issuing_rule->{cap_fine_to_replacement_price} && $item->{replacementprice} && $amount > $item->{replacementprice} );
+
+    return ($amount, $units_minus_grace, $chargeable_units);
 }
 
 
 }
 
 
@@ -469,7 +492,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.)
 
@@ -482,10 +513,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 date
 
 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
@@ -502,141 +530,104 @@ 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} // q{};
+
+    unless ( $issue_id ) {
+        carp("No issue_id passed in!");
+        return;
+    }
+
     my $dbh = C4::Context->dbh;
     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
-    # numbers, where the description contains $due, and where the
-    # 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
-    my $sth = $dbh->prepare(
-        "SELECT * FROM accountlines
-        WHERE borrowernumber=?
-        AND   accounttype IN ('FU','O','F','M')"
+    my $overdues = Koha::Account::Lines->search(
+        {
+            borrowernumber    => $borrowernumber,
+            debit_type_code   => 'OVERDUE'
+        }
     );
     );
-    $sth->execute( $borrowernumber );
-    my $data;
+
+    my $accountline;
     my $total_amount_other = 0.00;
     my $total_amount_other = 0.00;
-    my $due_qr = qr/$due/;
     # Cycle through the fines and
     # - find line that relates to the requested $itemnum
     # - accumulate fines for other items
     # so we can update $itemnum fine taking in account fine caps
     # Cycle through the fines and
     # - find line that relates to the requested $itemnum
     # - 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 ($data) {
-                warn "Not a unique accountlines record for item $itemnum borrower $borrowernumber";
-            } else {
-                $data = $rec;
-                next;
+    while (my $overdue = $overdues->next) {
+        if ( defined $overdue->issue_id && $overdue->issue_id == $issue_id && $overdue->status eq 'UNRETURNED' ) {
+            if ($accountline) {
+                Koha::Logger->get->debug("Not a unique accountlines record for issue_id $issue_id"); # FIXME Do we really need to log that?
+                #FIXME Should we still count this one in total_amount ??
+            }
+            else {
+                $accountline = $overdue;
             }
         }
             }
         }
-        $total_amount_other += $rec->{'amountoutstanding'};
+        $total_amount_other += $overdue->amountoutstanding;
     }
 
     }
 
-    if (my $maxfine = C4::Context->preference('MaxFine')) {
-        if ($total_amount_other + $amount > $maxfine) {
-            my $new_amount = $maxfine - $total_amount_other;
-            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 ( my $maxfine = C4::Context->preference('MaxFine') ) {
+        my $maxIncrease = $maxfine - $total_amount_other;
+        return if Koha::Number::Price->new($maxIncrease)->round <= 0.00;
+        if ($accountline) {
+            if ( ( $amount - $accountline->amount ) > $maxIncrease ) {
+                my $new_amount = $accountline->amount + $maxIncrease;
+                Koha::Logger->get->debug("Reducing fine for item $itemnum borrower $borrowernumber from $amount to $new_amount - MaxFine reached");
+                $amount = $new_amount;
+            }
+        }
+        elsif ( $amount > $maxIncrease ) {
+            Koha::Logger->get->debug("Reducing fine for item $itemnum borrower $borrowernumber from $amount to $maxIncrease - MaxFine reached");
+            $amount = $maxIncrease;
         }
     }
 
         }
     }
 
-    if ( $data ) {
-
-               # 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.
-       if ( $data->{'amount'} != $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'}"
+    if ( $accountline ) {
+        if ( Koha::Number::Price->new($accountline->amount)->round != Koha::Number::Price->new($amount)->round ) {
+            $accountline->adjust(
+                {
+                    amount    => $amount,
+                    type      => 'overdue_update',
+                    interface => C4::Context->interface
+                }
+            );
         }
     } else {
         if ( $amount ) { # Don't add new fines with an amount of 0
         }
     } else {
         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=?"
+            my $patron = Koha::Patrons->find( $borrowernumber );
+            my $letter = eval { C4::Letters::GetPreparedLetter(
+                module                 => 'circulation',
+                letter_code            => 'OVERDUE_FINE_DESC',
+                message_transport_type => 'print',
+                lang                   => $patron->lang,
+                tables                 => {
+                    issues    => $itemnum,
+                    borrowers => $borrowernumber,
+                    items     => $itemnum,
+                },
+            ) };
+            my $desc = $letter ? $letter->{content} : sprintf("Item %s - due %s", $itemnum, output_pref($due) );
+
+            my $account = Koha::Account->new({ patron_id => $borrowernumber });
+            $accountline = $account->add_debit(
+                {
+                    amount      => $amount,
+                    description => $desc,
+                    note        => undef,
+                    user_id     => undef,
+                    interface   => C4::Context->interface,
+                    library_id  => undef, #FIXME: Should we grab the checkout or circ-control branch here perhaps?
+                    type        => 'OVERDUE',
+                    item_id     => $itemnum,
+                    issue_id    => $issue_id,
+                }
             );
             );
-            $sth4->execute($itemnum);
-            my $title = $sth4->fetchrow;
-
-            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 );
         }
     }
         }
     }
-    # logging action
-    &logaction(
-        "FINES",
-        $type,
-        $borrowernumber,
-        "due=".$due."  amount=".$amount." itemnumber=".$itemnum
-        ) if C4::Context->preference("FinesLog");
-}
-
-=head2 BorType
-
-    $borrower = &BorType($borrowernumber);
-
-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
-category he or she belongs to.
-
-=cut
-
-sub BorType {
-    my ($borrowernumber) = @_;
-    my $dbh              = C4::Context->dbh;
-    my $sth              = $dbh->prepare(
-        "SELECT * from borrowers
-      LEFT JOIN categories ON borrowers.categorycode=categories.categorycode 
-      WHERE borrowernumber=?"
-    );
-    $sth->execute($borrowernumber);
-    return $sth->fetchrow_hashref;
 }
 
 =head2 GetFine
 }
 
 =head2 GetFine
@@ -655,7 +646,7 @@ sub GetFine {
     my ( $itemnum, $borrowernumber ) = @_;
     my $dbh   = C4::Context->dbh();
     my $query = q|SELECT sum(amountoutstanding) as fineamount FROM accountlines
     my ( $itemnum, $borrowernumber ) = @_;
     my $dbh   = C4::Context->dbh();
     my $query = q|SELECT sum(amountoutstanding) as fineamount FROM accountlines
-    where accounttype like 'F%'
+    WHERE debit_type_code = 'OVERDUE'
   AND amountoutstanding > 0 AND borrowernumber=?|;
     my @query_param;
     push @query_param, $borrowernumber;
   AND amountoutstanding > 0 AND borrowernumber=?|;
     my @query_param;
     push @query_param, $borrowernumber;
@@ -673,87 +664,6 @@ sub GetFine {
     return 0;
 }
 
     return 0;
 }
 
-=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 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 GetBranchcodesWithOverdueRules
 
     my @branchcodes = C4::Overdues::GetBranchcodesWithOverdueRules()
 =head2 GetBranchcodesWithOverdueRules
 
     my @branchcodes = C4::Overdues::GetBranchcodesWithOverdueRules()
@@ -772,33 +682,11 @@ sub GetBranchcodesWithOverdueRules {
     |);
     if ( $branchcodes->[0] eq '' ) {
         # If a default rule exists, all branches should be returned
     |);
     if ( $branchcodes->[0] eq '' ) {
         # If a default rule exists, all branches should be returned
-        my $availbranches = C4::Branch::GetBranches();
-        return keys %$availbranches;
+        return Koha::Libraries->search({}, { order_by => 'branchname' })->get_column('branchcode');
     }
     return @$branchcodes;
 }
 
     }
     return @$branchcodes;
 }
 
-=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);
-}
-
 =head2 GetOverduesForBranch
 
 Sql request for display all information for branchoverdues.pl
 =head2 GetOverduesForBranch
 
 Sql request for display all information for branchoverdues.pl
@@ -822,6 +710,10 @@ sub GetOverduesForBranch {
             borrowers.phone,
             borrowers.email,
                biblio.title,
             borrowers.phone,
             borrowers.email,
                biblio.title,
+               biblio.subtitle,
+               biblio.medium,
+               biblio.part_number,
+               biblio.part_name,
                biblio.author,
                biblio.biblionumber,
                issues.date_due,
                biblio.author,
                biblio.biblionumber,
                issues.date_due,
@@ -834,8 +726,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
@@ -847,82 +737,18 @@ sub GetOverduesForBranch {
     LEFT JOIN itemtypes   ON itemtypes.itemtype       = $itype_link
     LEFT JOIN branches    ON  branches.branchcode     = issues.branchcode
     WHERE (accountlines.amountoutstanding  != '0.000000')
     LEFT JOIN itemtypes   ON itemtypes.itemtype       = $itype_link
     LEFT JOIN branches    ON  branches.branchcode     = issues.branchcode
     WHERE (accountlines.amountoutstanding  != '0.000000')
-      AND (accountlines.accounttype         = 'FU'      )
+      AND (accountlines.debit_type_code     = 'OVERDUE' )
+      AND (accountlines.status              = 'UNRETURNED' )
       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++;
-        }
-    }
-    return (@getoverdues);
-}
-
-
-=head2 AddNotifyLine
-
-    &AddNotifyLine($borrowernumber, $itemnumber, $overduelevel, $method, $notifyId)
-
-Create a line into notify, if the method is phone, the notification_send_date is implemented to
-
-=cut
-
-sub AddNotifyLine {
-    my ( $borrowernumber, $itemnumber, $overduelevel, $method, $notifyId ) = @_;
-    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 $q = "$select ORDER BY borrowers.surname, borrowers.firstname";
+        return @{ $dbh->selectall_arrayref($q, { Slice => {} }, $branch ) };
     }
     }
-    return 1;
-}
-
-=head2 RemoveNotifyLine
-
-    &RemoveNotifyLine( $borrowernumber, $itemnumber, $notify_date );
-
-Cancel a notification
-
-=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=?"
-    );
-    $sth->execute( $borrowernumber, $itemnumber, $notify_date );
-    return 1;
 }
 
 =head2 GetOverdueMessageTransportTypes
 }
 
 =head2 GetOverdueMessageTransportTypes
@@ -938,8 +764,11 @@ sub GetOverdueMessageTransportTypes {
     return unless $categorycode and $letternumber;
     my $dbh = C4::Context->dbh;
     my $sth = $dbh->prepare("
     return unless $categorycode and $letternumber;
     my $dbh = C4::Context->dbh;
     my $sth = $dbh->prepare("
-        SELECT message_transport_type FROM overduerules_transport_types
-        WHERE branchcode = ? AND categorycode = ? AND letternumber = ?
+        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;
     ");
     $sth->execute( $branchcode, $categorycode, $letternumber );
     my @mtts;
@@ -950,7 +779,7 @@ sub GetOverdueMessageTransportTypes {
     # 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 )
     # 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;
+        if grep { $_ eq 'print' } @mtts;
 
     return \@mtts;
 }
 
     return \@mtts;
 }
@@ -977,28 +806,24 @@ sub parse_overdues_letter {
         return unless ( exists $params->{$required} && $params->{$required} );
     }
 
         return unless ( exists $params->{$required} && $params->{$required} );
     }
 
+    my $patron = Koha::Patrons->find( $params->{borrowernumber} );
+
     my $substitute = $params->{'substitute'} || {};
     my $substitute = $params->{'substitute'} || {};
-    $substitute->{today} ||= C4::Dates->new()->output("syspref");
 
     my %tables = ( 'borrowers' => $params->{'borrowernumber'} );
     if ( my $p = $params->{'branchcode'} ) {
         $tables{'branches'} = $p;
     }
 
 
     my %tables = ( 'borrowers' => $params->{'borrowernumber'} );
     if ( my $p = $params->{'branchcode'} ) {
         $tables{'branches'} = $p;
     }
 
-    my $currencies = GetCurrency();
+    my $active_currency = Koha::Acquisition::Currencies->get_active;
+
     my $currency_format;
     my $currency_format;
-    $currency_format = $currencies->{currency} if defined($currencies);
+    $currency_format = $active_currency->currency if defined($active_currency);
 
     my @item_tables;
     if ( my $i = $params->{'items'} ) {
 
     my @item_tables;
     if ( my $i = $params->{'items'} ) {
-        my $item_format = '';
         foreach my $item (@$i) {
             my $fine = GetFine($item->{'itemnumber'}, $params->{'borrowernumber'});
         foreach my $item (@$i) {
             my $fine = GetFine($item->{'itemnumber'}, $params->{'borrowernumber'});
-            if ( !$item_format and defined $params->{'letter'}->{'content'} ) {
-                $params->{'letter'}->{'content'} =~ m/(<item>.*<\/item>)/;
-                $item_format = $1;
-            }
-
             $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'};
             $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'};
@@ -1016,7 +841,11 @@ sub parse_overdues_letter {
         module => 'circulation',
         letter_code => $params->{'letter_code'},
         branchcode => $params->{'branchcode'},
         module => 'circulation',
         letter_code => $params->{'letter_code'},
         branchcode => $params->{'branchcode'},
+        lang => $patron->lang,
         tables => \%tables,
         tables => \%tables,
+        loops => {
+            overdues => [ map { $_->{items}->{itemnumber} } @item_tables ],
+        },
         substitute => $substitute,
         repeat => { item => \@item_tables },
         message_transport_type => $params->{message_transport_type},
         substitute => $substitute,
         repeat => { item => \@item_tables },
         message_transport_type => $params->{message_transport_type},