#
# 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 List::MoreUtils qw( uniq );
+use POSIX qw( floor ceil );
+use Locale::Currency::Format 1.28;
use C4::Circulation;
use C4::Context;
use C4::Accounts;
use C4::Log; # logaction
use C4::Debug;
+use C4::Budgets qw(GetCurrency);
use vars qw($VERSION @ISA @EXPORT);
BEGIN {
- # set the version for version checking
+ # 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
- &NumberNotifyId
- &AmountNotify
- &UpdateFine
- &GetFine
-
- &CheckItemNotify
- &GetOverduesForBranch
- &RemoveNotifyLine
- &AddNotifyLine
- &GetOverdueMessageTransportTypes
- );
- # 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
- );
+ 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
+ );
}
=head1 NAME
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
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
FROM issues
LEFT JOIN items USING (itemnumber)
LEFT JOIN biblioitems USING (biblioitemnumber)
my $fine_unit = $data->{lengthunit};
$fine_unit ||= '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 $amount = 0;
- if ($data->{'chargeperiod'} && ($units_minus_grace > 0) ) {
- if ( C4::Context->preference('FinesIncludeGracePeriod') ) {
- $amount = int($chargeable_units / $data->{'chargeperiod'}) * $data->{'fine'};# TODO fine calc should be in cents
- } else {
- $amount = int($units_minus_grace / $data->{'chargeperiod'}) * $data->{'fine'};
- }
- } else {
- # a zero (or null) chargeperiod or negative units_minus_grace value means no charge.
- }
+ if ( $data->{'chargeperiod'} && ( $units_minus_grace > 0 ) ) {
+ my $units = C4::Context->preference('FinesIncludeGracePeriod') ? $chargeable_units : $units_minus_grace;
+ my $charge_periods = $units / $data->{'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'};
+ } # 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);
}
-=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>.
=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 );
- $charge_duration = $calendar->hours_between( $dt1, $dt2 );
+ $charge_duration = $calendar->hours_between( $date_due, $date_returned );
} 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;
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 {
- $charge_duration = $dt2->delta_days( $dt1 );
+ $charge_duration = $date_returned->delta_days( $date_due );
}
return $charge_duration->in_units('days');
}
# print "no update needed $data->{'amount'}"
}
} 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 = ( $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(
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);
- $sth->execute( $itemnum, $borrowernumber );
+ $sth->execute( @query_param );
my $fine = $sth->fetchrow_hashref();
if ($fine->{fineamount}) {
return $fine->{fineamount};
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
+ my $availbranches = C4::Branch::GetBranches();
+ return keys %$availbranches;
}
- return @branches;
+ return @$branchcodes;
}
=head2 CheckItemNotify
return \@mtts;
}
+=head2 parse_overdues_letter
+
+parses the letter template, replacing the placeholders with data
+specific to this patron, biblio, or item for overdues
+
+named parameters:
+ letter - required hashref
+ borrowernumber - required integer
+ substitute - optional hashref of other key/value pairs that should
+ be substituted in the letter content
+
+returns the C<letter> hashref, with the content updated to reflect the
+substituted keys and values.
+
+=cut
+
+sub parse_overdues_letter {
+ my $params = shift;
+ foreach my $required (qw( letter_code borrowernumber )) {
+ return unless ( exists $params->{$required} && $params->{$required} );
+ }
+
+ 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 $currencies = GetCurrency();
+ my $currency_format;
+ $currency_format = $currencies->{currency} if defined($currencies);
+
+ 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'};
+
+ 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'},
+ tables => \%tables,
+ substitute => $substitute,
+ repeat => { item => \@item_tables },
+ message_transport_type => $params->{message_transport_type},
+ );
+}
+
1;
__END__