Bug 17825: (followup) Remove unused function AttributeTypeExists
[koha.git] / C4 / Overdues.pm
index 047c000..c26b5f0 100644 (file)
@@ -26,20 +26,22 @@ 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::Budgets qw(GetCurrency);
 use Koha::DateUtils;
+use Koha::Account::Line;
+use Koha::Account::Lines;
+use Koha::IssuingRules;
+use Koha::Libraries;
 
-use vars qw($VERSION @ISA @EXPORT);
+use vars qw(@ISA @EXPORT);
 
 BEGIN {
-    # set the version for version checking
-    $VERSION = 3.07.00.049;
     require Exporter;
     @ISA = qw(Exporter);
 
@@ -73,9 +75,6 @@ BEGIN {
       &GetIssuesIteminfo
     );
 
-    # &GetIssuingRules - delete.
-    # use C4::Circulation::GetIssuingRule instead.
-
     # subs to move to Biblio.pm
     push @EXPORT, qw(
       &GetItems
@@ -116,14 +115,14 @@ sub Getoverdues {
     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
      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
      FROM issues 
 LEFT JOIN items       USING (itemnumber)
 LEFT JOIN biblioitems USING (biblioitemnumber)
@@ -159,7 +158,6 @@ Returns a count and a list of overdueitems for a given borrowernumber
 
 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,
@@ -196,7 +194,6 @@ sub checkoverdues {
             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
@@ -204,7 +201,7 @@ sub checkoverdues {
 
 =head2 CalcFine
 
-    ($amount, $chargename,  $daycounttotal) = &CalcFine($item,
+    ($amount, $chargename,  $units_minus_grace, $chargeable_units) = &CalcFine($item,
                                   $categorycode, $branch,
                                   $start_dt, $end_dt );
 
@@ -236,10 +233,10 @@ 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<$unitcount> is the number of chargeable units (days between start and end dates, Calendar adjusted where needed,
-minus any applicable grace period, or hours)
+C<$units_minus_grace> is the number of chargeable units minus the grace period
 
-FIXME - What is chargename supposed to be ?
+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)
 
 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.
@@ -251,25 +248,28 @@ sub CalcFine {
     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 });
+
+    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 - $data->{firstremind};
+    my $units_minus_grace = $chargeable_units - $issuing_rule->firstremind;
     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 $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
-        $charge_periods = $data->{'chargeperiod_charge_at'} == 1 ? ceil($charge_periods) : floor($charge_periods);
-        $amount = $charge_periods * $data->{'fine'};
+        $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 = $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);
+    $amount = $issuing_rule->overduefinescap if $issuing_rule->overduefinescap && $amount > $issuing_rule->overduefinescap;
+    $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, %s)", $amount, $issuing_rule->chargename, $units_minus_grace, $chargeable_units);
+    return ($amount, $issuing_rule->chargename, $units_minus_grace, $chargeable_units);
     # FIXME: chargename is NEVER populated anywhere.
 }
 
@@ -470,7 +470,7 @@ sub GetIssuesIteminfo {
 
 =head2 UpdateFine
 
-    &UpdateFine($itemnumber, $borrowernumber, $amount, $type, $description);
+    &UpdateFine({ issue_id => $issue_id, itemnumber => $itemnumber, borrwernumber => $borrowernumber, amount => $amount, type => $type, $due => $date_due });
 
 (Note: the following is mostly conjecture and guesswork.)
 
@@ -485,8 +485,7 @@ 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
@@ -503,8 +502,22 @@ accountlines table of the Koha database.
 # 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 $type           = $params->{type};
+    my $due            = $params->{due};
+
+    $debug and warn "UpdateFine({ itemnumber => $itemnum, borrowernumber => $borrowernumber, type => $type, 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
@@ -512,19 +525,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")
-       #       "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
-        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;
@@ -535,10 +549,12 @@ sub UpdateFine {
     # - 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) {
-                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;
             }
@@ -556,35 +572,25 @@ sub UpdateFine {
     }
 
     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.
-       if ( $data->{'amount'} != $amount ) {
+        if ( $data->{'amount'} != $amount ) {
+            my $accountline = Koha::Account::Lines->find( $data->{accountlines_id} );
             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();
         }
     } else {
         if ( $amount ) { # Don't add new fines with an amount of 0
@@ -598,12 +604,20 @@ sub UpdateFine {
 
             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 );
+            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();
         }
     }
     # logging action
@@ -773,8 +787,7 @@ sub GetBranchcodesWithOverdueRules {
     |);
     if ( $branchcodes->[0] eq '' ) {
         # If a default rule exists, all branches should be returned
-        my $availbranches = C4::Branch::GetBranches();
-        return keys %$availbranches;
+        return map { $_->branchcode } Koha::Libraries->search({}, { order_by => 'branchname' });
     }
     return @$branchcodes;
 }
@@ -989,20 +1002,15 @@ sub parse_overdues_letter {
         $tables{'branches'} = $p;
     }
 
-    my $currencies = GetCurrency();
+    my $active_currency = Koha::Acquisition::Currencies->get_active;
+
     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_format = '';
         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'};