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);
&GetIssuesIteminfo
);
- # &GetIssuingRules - delete.
- # use C4::Circulation::GetIssuingRule instead.
-
# subs to move to Biblio.pm
push @EXPORT, qw(
&GetItems
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)
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,
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
=head2 CalcFine
- ($amount, $chargename, $daycounttotal) = &CalcFine($item,
+ ($amount, $chargename, $units_minus_grace, $chargeable_units) = &CalcFine($item,
$categorycode, $branch,
$start_dt, $end_dt );
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.
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.
}
=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.)
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
# 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
# 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;
# - 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;
}
}
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
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
|);
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;
}
$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'};