From 18a0ae9d7ffebf541f040c724afa0c502b5b626c Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Wed, 11 Dec 2013 11:08:16 -0500 Subject: [PATCH] Bug 6427 [Part 9] - Update existing perl modules to support new accounting system --- C4/Circulation.pm | 315 +++++++++++++++---------------- C4/Members.pm | 156 ++++------------ C4/Overdues.pm | 345 +++++++++++----------------------- C4/Reserves.pm | 23 +-- C4/SIP/ILS/Patron.pm | 2 +- C4/SIP/ILS/Transaction/FeePayment.pm | 19 ++- 6 files changed, 327 insertions(+), 533 deletions(-) diff --git a/C4/Circulation.pm b/C4/Circulation.pm index f8b204a..02e1db2 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -30,7 +30,7 @@ use C4::Items; use C4::Members; use C4::Dates; use C4::Dates qw(format_date); -use C4::Accounts; +use Koha::Accounts; use C4::ItemCirculationAlertPreference; use C4::Message; use C4::Debug; @@ -48,6 +48,7 @@ use Data::Dumper; use Koha::DateUtils; use Koha::Calendar; use Koha::Borrower::Debarments; +use Koha::Database; use Carp; use Date::Calc qw( Today @@ -1275,7 +1276,7 @@ sub AddIssue { ## If item was lost, it has now been found, reverse any list item charges if neccessary. if ( $item->{'itemlost'} ) { if ( C4::Context->preference('RefundLostItemFeeOnReturn' ) ) { - _FixAccountForLostAndReturned( $item->{'itemnumber'}, undef, $item->{'barcode'} ); + _FixAccountForLostAndReturned( $item->{'itemnumber'} ); } } @@ -1812,9 +1813,15 @@ sub AddReturn { if ( $amount > 0 && C4::Context->preference('finesMode') eq 'production' ) { - C4::Overdues::UpdateFine( $issue->{itemnumber}, - $issue->{borrowernumber}, - $amount, $type, output_pref($datedue) ); + C4::Overdues::UpdateFine( + { + itemnumber => $issue->{itemnumber}, + borrowernumber => $issue->{borrowernumber}, + amount => $amount, + due => output_pref($datedue), + issue_id => $issue->{issue_id} + } + ); } } @@ -1864,18 +1871,23 @@ sub AddReturn { $messages->{'WasLost'} = 1; if ( C4::Context->preference('RefundLostItemFeeOnReturn' ) ) { - _FixAccountForLostAndReturned($item->{'itemnumber'}, $borrowernumber, $barcode); # can tolerate undef $borrowernumber + _FixAccountForLostAndReturned( $item->{'itemnumber'} ); $messages->{'LostItemFeeRefunded'} = 1; } } # fix up the overdues in accounts... if ($borrowernumber) { - my $fix = _FixOverduesOnReturn($borrowernumber, $item->{itemnumber}, $exemptfine, $dropbox); - defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, $item->{itemnumber}...) failed!"; # zero is OK, check defined + _FixOverduesOnReturn( + { + exempt_fine => $exemptfine, + dropbox => $dropbox, + issue => $issue, + } + ); if ( $issue->{overdue} && $issue->{date_due} ) { -# fix fine days + # fix fine days my $debardate = _debar_user_on_return( $borrower, $item, $issue->{date_due}, $today ); $messages->{Debarred} = $debardate if ($debardate); @@ -2093,139 +2105,107 @@ Internal function, called only by AddReturn =cut sub _FixOverduesOnReturn { - my ($borrowernumber, $item); - unless ($borrowernumber = shift) { - warn "_FixOverduesOnReturn() not supplied valid borrowernumber"; - return; - } - unless ($item = shift) { - warn "_FixOverduesOnReturn() not supplied valid itemnumber"; - return; - } - my ($exemptfine, $dropbox) = @_; + my ( $params ) = @_; + + my $exemptfine = $params->{exempt_fine}; + my $dropbox = $params->{dropbox}; + my $issue = $params->{issue}; + my $dbh = C4::Context->dbh; - # check for overdue fine - my $sth = $dbh->prepare( -"SELECT * FROM accountlines WHERE (borrowernumber = ?) AND (itemnumber = ?) AND (accounttype='FU' OR accounttype='O')" - ); - $sth->execute( $borrowernumber, $item ); + my $schema = Koha::Database->new()->schema; + my $fine = + $schema->resultset('AccountDebit') + ->single( { issue_id => $issue->{issue_id}, type => Koha::Accounts::DebitTypes::Fine() } ); - # alter fine to show that the book has been returned - my $data = $sth->fetchrow_hashref; - return 0 unless $data; # no warning, there's just nothing to fix + return unless ( $fine ); + + $fine->accruing(0); - my $uquery; - my @bind = ($data->{'accountlines_id'}); if ($exemptfine) { - $uquery = "update accountlines set accounttype='FFOR', amountoutstanding=0"; - if (C4::Context->preference("FinesLog")) { - &logaction("FINES", 'MODIFY',$borrowernumber,"Overdue forgiven: item $item"); - } - } elsif ($dropbox && $data->{lastincrement}) { - my $outstanding = $data->{amountoutstanding} - $data->{lastincrement} ; - my $amt = $data->{amount} - $data->{lastincrement} ; + AddCredit( + { + borrower => $fine->borrowernumber(), + amount => $fine->amount_original(), + debit_id => $fine->debit_id(), + type => Koha::Accounts::CreditTypes::Forgiven(), + } + ); if (C4::Context->preference("FinesLog")) { - &logaction("FINES", 'MODIFY',$borrowernumber,"Dropbox adjustment $amt, item $item"); + &logaction( + "FINES", 'MODIFY', + $issue->{borrowernumber}, + "Overdue forgiven: item " . $issue->{itemnumber} + ); } - $uquery = "update accountlines set accounttype='F' "; - if($outstanding >= 0 && $amt >=0) { - $uquery .= ", amount = ? , amountoutstanding=? "; - unshift @bind, ($amt, $outstanding) ; + } elsif ($dropbox && $fine->amount_last_increment() != $fine->amount_original() ) { + if ( C4::Context->preference("FinesLog") ) { + &logaction( "FINES", 'MODIFY', $issue->{borrowernumber}, + "Dropbox adjustment " + . $fine->amount_last_increment() + . ", item " . $issue->{itemnumber} ); } - } else { - $uquery = "update accountlines set accounttype='F' "; + $fine->amount_original( + $fine->amount_original() - $fine->amount_last_increment() ); + $fine->amount_outstanding( + $fine->amount_outstanding - $fine->amount_last_increment() ); + $schema->resultset('AccountOffset')->create( + { + debit_id => $fine->debit_id(), + type => Koha::Accounts::OffsetTypes::Dropbox(), + amount => $fine->amount_last_increment() * -1, + } + ); } - $uquery .= " where (accountlines_id = ?)"; - my $usth = $dbh->prepare($uquery); - return $usth->execute(@bind); + + return $fine->update(); } =head2 _FixAccountForLostAndReturned - &_FixAccountForLostAndReturned($itemnumber, [$borrowernumber, $barcode]); - -Calculates the charge for a book lost and returned. + &_FixAccountForLostAndReturned($itemnumber); -Internal function, not exported, called only by AddReturn. - -FIXME: This function reflects how inscrutable fines logic is. Fix both. -FIXME: Give a positive return value on success. It might be the $borrowernumber who received credit, or the amount forgiven. + Refunds a lost item fee in necessary =cut sub _FixAccountForLostAndReturned { - my $itemnumber = shift or return; - my $borrowernumber = @_ ? shift : undef; - my $item_id = @_ ? shift : $itemnumber; # Send the barcode if you want that logged in the description - my $dbh = C4::Context->dbh; - # check for charge made for lost book - my $sth = $dbh->prepare("SELECT * FROM accountlines WHERE itemnumber = ? AND accounttype IN ('L', 'Rep', 'W') ORDER BY date DESC, accountno DESC"); - $sth->execute($itemnumber); - my $data = $sth->fetchrow_hashref; - $data or return; # bail if there is nothing to do - $data->{accounttype} eq 'W' and return; # Written off - - # writeoff this amount - my $offset; - my $amount = $data->{'amount'}; - my $acctno = $data->{'accountno'}; - my $amountleft; # Starts off undef/zero. - if ($data->{'amountoutstanding'} == $amount) { - $offset = $data->{'amount'}; - $amountleft = 0; # Hey, it's zero here, too. - } else { - $offset = $amount - $data->{'amountoutstanding'}; # Um, isn't this the same as ZERO? We just tested those two things are == - $amountleft = $data->{'amountoutstanding'} - $amount; # Um, isn't this the same as ZERO? We just tested those two things are == - } - my $usth = $dbh->prepare("UPDATE accountlines SET accounttype = 'LR',amountoutstanding='0' - WHERE (accountlines_id = ?)"); - $usth->execute($data->{'accountlines_id'}); # We might be adjusting an account for some OTHER borrowernumber now. Not the one we passed in. - #check if any credit is left if so writeoff other accounts - my $nextaccntno = getnextacctno($data->{'borrowernumber'}); - $amountleft *= -1 if ($amountleft < 0); - if ($amountleft > 0) { - my $msth = $dbh->prepare("SELECT * FROM accountlines WHERE (borrowernumber = ?) - AND (amountoutstanding >0) ORDER BY date"); # might want to order by amountoustanding ASC (pay smallest first) - $msth->execute($data->{'borrowernumber'}); - # offset transactions - my $newamtos; - my $accdata; - while (($accdata=$msth->fetchrow_hashref) and ($amountleft>0)){ - if ($accdata->{'amountoutstanding'} < $amountleft) { - $newamtos = 0; - $amountleft -= $accdata->{'amountoutstanding'}; - } else { - $newamtos = $accdata->{'amountoutstanding'} - $amountleft; - $amountleft = 0; - } - my $thisacct = $accdata->{'accountlines_id'}; - # FIXME: move prepares outside while loop! - my $usth = $dbh->prepare("UPDATE accountlines SET amountoutstanding= ? - WHERE (accountlines_id = ?)"); - $usth->execute($newamtos,$thisacct); - $usth = $dbh->prepare("INSERT INTO accountoffsets - (borrowernumber, accountno, offsetaccount, offsetamount) - VALUES - (?,?,?,?)"); - $usth->execute($data->{'borrowernumber'},$accdata->{'accountno'},$nextaccntno,$newamtos); + my ( $itemnumber ) = @_; + + my $schema = Koha::Database->new()->schema; + + # Find the last issue for this item + my $issue = + $schema->resultset('Issue')->single( { itemnumber => $itemnumber } ); + $issue ||= + $schema->resultset('OldIssue')->single( { itemnumber => $itemnumber } ); + + return unless $issue; + + # Find a lost fee for this issue + my $debit = $schema->resultset('AccountDebit')->single( + { + issue_id => $issue->issue_id(), + type => Koha::Accounts::DebitTypes::Lost() } - } - $amountleft *= -1 if ($amountleft > 0); - my $desc = "Item Returned " . $item_id; - $usth = $dbh->prepare("INSERT INTO accountlines - (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding) - VALUES (?,?,now(),?,?,'CR',?)"); - $usth->execute($data->{'borrowernumber'},$nextaccntno,0-$amount,$desc,$amountleft); - if ($borrowernumber) { - # FIXME: same as query above. use 1 sth for both - $usth = $dbh->prepare("INSERT INTO accountoffsets - (borrowernumber, accountno, offsetaccount, offsetamount) - VALUES (?,?,?,?)"); - $usth->execute($borrowernumber, $data->{'accountno'}, $nextaccntno, $offset); - } + ); + + return unless $debit; + + # Check for an existing found credit for this debit, if there is one, the fee has already been refunded and we do nothing + my @credits = $debit->account_offsets->search_related('credit', { 'credit.type' => Koha::Accounts::CreditTypes::Found() }); + + return if @credits; + + # Ok, so we know we have an unrefunded lost item fee, let's refund it + CreditLostItem( + { + borrower => $issue->borrower(), + debit => $debit + } + ); + ModItem({ paidfor => '' }, undef, $itemnumber); - return; } =head2 _GetCircControlBranch @@ -2584,19 +2564,22 @@ sub AddRenewal { # Charge a new rental fee, if applicable? my ( $charge, $type ) = GetIssuingCharges( $itemnumber, $borrowernumber ); if ( $charge > 0 ) { - my $accountno = getnextacctno( $borrowernumber ); my $item = GetBiblioFromItemNumber($itemnumber); - my $manager_id = 0; - $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv; - $sth = $dbh->prepare( - "INSERT INTO accountlines - (date, borrowernumber, accountno, amount, manager_id, - description,accounttype, amountoutstanding, itemnumber) - VALUES (now(),?,?,?,?,?,?,?,?)" + + my $borrower = + Koha::Database->new()->schema->resultset('Borrower') + ->find($borrowernumber); + + AddDebit( + { + borrower => $borrower, + itemnumber => $itemnumber, + amount => $charge, + type => Koha::Accounts::DebitTypes::Rental(), + description => + "Renewal of Rental Item $item->{'title'} $item->{'barcode'}" + } ); - $sth->execute( $borrowernumber, $accountno, $charge, $manager_id, - "Renewal of Rental Item $item->{'title'} $item->{'barcode'}", - 'Rent', $charge, $itemnumber ); } # Send a renewal slip according to checkout alert preferencei @@ -2767,25 +2750,21 @@ sub _get_discount_from_rule { =head2 AddIssuingCharge - &AddIssuingCharge( $itemno, $borrowernumber, $charge ) + &AddIssuingCharge( $itemnumber, $borrowernumber, $amount ) =cut sub AddIssuingCharge { - my ( $itemnumber, $borrowernumber, $charge ) = @_; - my $dbh = C4::Context->dbh; - my $nextaccntno = getnextacctno( $borrowernumber ); - my $manager_id = 0; - $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv; - my $query =" - INSERT INTO accountlines - (borrowernumber, itemnumber, accountno, - date, amount, description, accounttype, - amountoutstanding, manager_id) - VALUES (?, ?, ?,now(), ?, 'Rental', 'Rent',?,?) - "; - my $sth = $dbh->prepare($query); - $sth->execute( $borrowernumber, $itemnumber, $nextaccntno, $charge, $charge, $manager_id ); + my ( $itemnumber, $borrowernumber, $amount ) = @_; + + return AddDebit( + { + borrower => Koha::Database->new()->schema->resultset('Borrower')->find($borrowernumber), + itemnumber => $itemnumber, + amount => $amount, + type => Koha::Accounts::DebitTypes::Rental(), + } + ); } =head2 GetTransfers @@ -3304,30 +3283,34 @@ sub ReturnLostItem{ sub LostItem{ my ($itemnumber, $mark_returned) = @_; - my $dbh = C4::Context->dbh(); - my $sth=$dbh->prepare("SELECT issues.*,items.*,biblio.title - FROM issues - JOIN items USING (itemnumber) - JOIN biblio USING (biblionumber) - WHERE issues.itemnumber=?"); - $sth->execute($itemnumber); - my $issues=$sth->fetchrow_hashref(); + my $schema = Koha::Database->new()->schema; - # If a borrower lost the item, add a replacement cost to the their record - if ( my $borrowernumber = $issues->{borrowernumber} ){ - my $borrower = C4::Members::GetMemberDetails( $borrowernumber ); + my $issue = + $schema->resultset('Issue')->single( { itemnumber => $itemnumber } ); + my ( $borrower, $item ); + + if ( $issue ) { + $borrower = $issue->borrower(); + $item = $issue->item(); + } + + # If a borrower lost the item, add a replacement cost to the their record + if ( $borrower ){ if (C4::Context->preference('WhenLostForgiveFine')){ - my $fix = _FixOverduesOnReturn($borrowernumber, $itemnumber, 1, 0); # 1, 0 = exemptfine, no-dropbox - defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, $itemnumber...) failed!"; # zero is OK, check defined + _FixOverduesOnReturn( + { + exempt_fine => 1, + dropbox => 0, + issue => $issue, + } + ); } - if (C4::Context->preference('WhenLostChargeReplacementFee')){ - C4::Accounts::chargelostitem($borrowernumber, $itemnumber, $issues->{'replacementprice'}, "Lost Item $issues->{'title'} $issues->{'barcode'}"); - #FIXME : Should probably have a way to distinguish this from an item that really was returned. - #warn " $issues->{'borrowernumber'} / $itemnumber "; + if ( C4::Context->preference('WhenLostChargeReplacementFee') ) { + DebitLostItem( { borrower => $borrower, issue => $issue } ); } - MarkIssueReturned($borrowernumber,$itemnumber,undef,undef,$borrower->{'privacy'}) if $mark_returned; + MarkIssueReturned( $borrower->borrowernumber(), $item->itemnumber(), undef, undef, $borrower->privacy() ) if $mark_returned; } } diff --git a/C4/Members.pm b/C4/Members.pm index 29eda68..9da085b 100644 --- a/C4/Members.pm +++ b/C4/Members.pm @@ -29,7 +29,6 @@ use Date::Calc qw/Today Add_Delta_YM check_date Date_to_Days/; use C4::Log; # logaction use C4::Overdues; use C4::Reserves; -use C4::Accounts; use C4::Biblio; use C4::Letters; use C4::SQLHelper qw(InsertInTable UpdateInTable SearchInTable); @@ -41,6 +40,7 @@ use Koha::DateUtils; use Koha::Borrower::Debarments qw(IsDebarred); use Text::Unaccent qw( unac_string ); use Koha::AuthUtils qw(hash_password); +use Koha::Accounts::DebitTypes; our ($VERSION,@ISA,@EXPORT,@EXPORT_OK,$debug); @@ -83,8 +83,6 @@ BEGIN { &GetHideLostItemsPreference &IsMemberBlocked - &GetMemberAccountRecords - &GetBorNotifyAcctRecord &GetborCatFromCatType &GetBorrowercategory @@ -338,9 +336,6 @@ sub GetMemberDetails { return; } my $borrower = $sth->fetchrow_hashref; - my ($amount) = GetMemberAccountRecords( $borrowernumber); - $borrower->{'amountoutstanding'} = $amount; - # FIXME - patronflags calls GetMemberAccountRecords... just have patronflags return $amount my $flags = patronflags( $borrower); my $accessflagshash; @@ -432,23 +427,20 @@ The "message" field that comes from the DB is OK. # FIXME rename this function. sub patronflags { my %flags; - my ( $patroninformation) = @_; - my $dbh=C4::Context->dbh; - my ($balance, $owing) = GetMemberAccountBalance( $patroninformation->{'borrowernumber'}); - if ( $owing > 0 ) { + my ($patroninformation) = @_; + my $dbh = C4::Context->dbh; + if ( $patroninformation->{account_balance} > 0 ) { my %flaginfo; my $noissuescharge = C4::Context->preference("noissuescharge") || 5; - $flaginfo{'message'} = sprintf 'Patron owes %.02f', $owing; - $flaginfo{'amount'} = sprintf "%.02f", $owing; - if ( $owing > $noissuescharge && !C4::Context->preference("AllowFineOverride") ) { + $flaginfo{'amount'} = $patroninformation->{account_balance}; + if ( $patroninformation->{account_balance} > $noissuescharge && !C4::Context->preference("AllowFineOverride") ) { $flaginfo{'noissues'} = 1; } $flags{'CHARGES'} = \%flaginfo; } - elsif ( $balance < 0 ) { + elsif ( $patroninformation->{account_balance} < 0 ) { my %flaginfo; - $flaginfo{'message'} = sprintf 'Patron has credit of %.02f', -$balance; - $flaginfo{'amount'} = sprintf "%.02f", $balance; + $flaginfo{'amount'} = $patroninformation->{account_balance}; $flags{'CREDITS'} = \%flaginfo; } if ( $patroninformation->{'gonenoaddress'} @@ -691,7 +683,7 @@ sub GetMemberIssuesAndFines { $sth->execute($borrowernumber); my $overdue_count = $sth->fetchrow_arrayref->[0]; - $sth = $dbh->prepare("SELECT SUM(amountoutstanding) FROM accountlines WHERE borrowernumber = ?"); + $sth = $dbh->prepare("SELECT account_balance FROM borrowers WHERE borrowernumber = ?"); $sth->execute($borrowernumber); my $total_fines = $sth->fetchrow_arrayref->[0]; @@ -1167,57 +1159,14 @@ sub GetAllIssues { } -=head2 GetMemberAccountRecords - - ($total, $acctlines, $count) = &GetMemberAccountRecords($borrowernumber); - -Looks up accounting data for the patron with the given borrowernumber. - -C<&GetMemberAccountRecords> returns a three-element array. C<$acctlines> is a -reference-to-array, where each element is a reference-to-hash; the -keys are the fields of the C table in the Koha database. -C<$count> is the number of elements in C<$acctlines>. C<$total> is the -total amount outstanding for all of the account lines. - -=cut - -sub GetMemberAccountRecords { - my ($borrowernumber) = @_; - my $dbh = C4::Context->dbh; - my @acctlines; - my $numlines = 0; - my $strsth = qq( - SELECT * - FROM accountlines - WHERE borrowernumber=?); - $strsth.=" ORDER BY date desc,timestamp DESC"; - my $sth= $dbh->prepare( $strsth ); - $sth->execute( $borrowernumber ); - - my $total = 0; - while ( my $data = $sth->fetchrow_hashref ) { - if ( $data->{itemnumber} ) { - my $biblio = GetBiblioFromItemNumber( $data->{itemnumber} ); - $data->{biblionumber} = $biblio->{biblionumber}; - $data->{title} = $biblio->{title}; - } - $acctlines[$numlines] = $data; - $numlines++; - $total += int(1000 * $data->{'amountoutstanding'}); # convert float to integer to avoid round-off errors - } - $total /= 1000; - return ( $total, \@acctlines,$numlines); -} - =head2 GetMemberAccountBalance ($total_balance, $non_issue_balance, $other_charges) = &GetMemberAccountBalance($borrowernumber); Calculates amount immediately owing by the patron - non-issue charges. -Based on GetMemberAccountRecords. Charges exempt from non-issue are: -* Res (reserves) -* Rent (rental) if RentalsInNoissuesCharge syspref is set to false +* HOLD fees (reserves) +* RENTAL if RentalsInNoissuesCharge syspref is set to false * Manual invoices if ManInvInNoissuesCharge syspref is set to false =cut @@ -1225,70 +1174,41 @@ Charges exempt from non-issue are: sub GetMemberAccountBalance { my ($borrowernumber) = @_; - my $ACCOUNT_TYPE_LENGTH = 5; # this is plain ridiculous... + my $borrower = + Koha::Database->new()->schema->resultset('Borrower') + ->find($borrowernumber); - my @not_fines = ('Res'); - push @not_fines, 'Rent' unless C4::Context->preference('RentalsInNoissuesCharge'); - unless ( C4::Context->preference('ManInvInNoissuesCharge') ) { - my $dbh = C4::Context->dbh; - my $man_inv_types = $dbh->selectcol_arrayref(qq{SELECT authorised_value FROM authorised_values WHERE category = 'MANUAL_INV'}); - push @not_fines, map substr($_, 0, $ACCOUNT_TYPE_LENGTH), @$man_inv_types; - } - my %not_fine = map {$_ => 1} @not_fines; + my @not_fines; - my ($total, $acctlines) = GetMemberAccountRecords($borrowernumber); - my $other_charges = 0; - foreach (@$acctlines) { - $other_charges += $_->{amountoutstanding} if $not_fine{ substr($_->{accounttype}, 0, $ACCOUNT_TYPE_LENGTH) }; - } - - return ( $total, $total - $other_charges, $other_charges); -} - -=head2 GetBorNotifyAcctRecord - - ($total, $acctlines, $count) = &GetBorNotifyAcctRecord($params,$notifyid); + push( @not_fines, Koha::Accounts::DebitTypes::Hold() ); -Looks up accounting data for the patron with the given borrowernumber per file number. + push( @not_fines, Koha::Accounts::DebitTypes::Rental() ) + unless C4::Context->preference('RentalsInNoissuesCharge'); -C<&GetBorNotifyAcctRecord> returns a three-element array. C<$acctlines> is a -reference-to-array, where each element is a reference-to-hash; the -keys are the fields of the C table in the Koha database. -C<$count> is the number of elements in C<$acctlines>. C<$total> is the -total amount outstanding for all of the account lines. - -=cut + unless ( C4::Context->preference('ManInvInNoissuesCharge') ) { + my $dbh = C4::Context->dbh; + my $man_inv_types = $dbh->selectcol_arrayref( + qq{SELECT authorised_value FROM authorised_values WHERE category = 'MANUAL_INV'} + ); + push( @not_fines, @$man_inv_types ); + } -sub GetBorNotifyAcctRecord { - my ( $borrowernumber, $notifyid ) = @_; - my $dbh = C4::Context->dbh; - my @acctlines; - my $numlines = 0; - my $sth = $dbh->prepare( - "SELECT * - FROM accountlines - WHERE borrowernumber=? - AND notify_id=? - AND amountoutstanding != '0' - ORDER BY notify_id,accounttype - "); - - $sth->execute( $borrowernumber, $notifyid ); - my $total = 0; - while ( my $data = $sth->fetchrow_hashref ) { - if ( $data->{itemnumber} ) { - my $biblio = GetBiblioFromItemNumber( $data->{itemnumber} ); - $data->{biblionumber} = $biblio->{biblionumber}; - $data->{title} = $biblio->{title}; + my $other_charges = + Koha::Database->new()->schema->resultset('AccountDebit')->search( + { + borrowernumber => $borrowernumber, + type => { -not_in => \@not_fines } } - $acctlines[$numlines] = $data; - $numlines++; - $total += int(100 * $data->{'amountoutstanding'}); - } - $total /= 100; - return ( $total, \@acctlines, $numlines ); + )->get_column('amount_outstanding')->sum(); + + return ( + $borrower->account_balance(), + $borrower->account_balance() - $other_charges, + $other_charges + ); } + =head2 checkuniquemember (OUEST-PROVENCE) ($result,$categorycode) = &checkuniquemember($collectivity,$surname,$firstname,$dateofbirth); diff --git a/C4/Overdues.pm b/C4/Overdues.pm index 33dbd98..419a996 100644 --- a/C4/Overdues.pm +++ b/C4/Overdues.pm @@ -25,9 +25,12 @@ use Date::Calc qw/Today Date_to_Days/; use Date::Manip qw/UnixDate/; use C4::Circulation; use C4::Context; -use C4::Accounts; use C4::Log; # logaction use C4::Debug; +use Koha::Database; +use Koha::DateUtils; +use Koha::Accounts::OffsetTypes; +use Koha::Accounts::DebitTypes; use vars qw($VERSION @ISA @EXPORT); @@ -41,12 +44,9 @@ BEGIN { &CalcFine &Getoverdues &checkoverdues - &NumberNotifyId - &AmountNotify &UpdateFine &GetFine - &CheckItemNotify &GetOverduesForBranch &RemoveNotifyLine &AddNotifyLine @@ -456,154 +456,115 @@ sub GetIssuesIteminfo { =head2 UpdateFine - &UpdateFine($itemnumber, $borrowernumber, $amount, $type, $description); + UpdateFine( + { + itemnumber => $itemnumber, + borrowernumber => $borrowernumber, + amount => $amount, + due => $due, + issue_id => $issue_id + } + ); -(Note: the following is mostly conjecture and guesswork.) +Updates the fine owed on an overdue item. -Updates the fine owed on an overdue book. +C<$itemnumber> is the items's id. -C<$itemnumber> is the book's item number. +C<$borrowernumber> is the id of the patron who currently +has the item on loan. -C<$borrowernumber> is the borrower number of the patron who currently -has the book on loan. +C<$amount> is the total amount of the fine owed by the patron. -C<$amount> is the current amount owed by the patron. +C<&UpdateFine> updates the amount owed for a given fine if an issue_id +is passed to it. Otherwise, a new fine will be created. -C<$type> will be used in the description of the fine. +=cut -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. +sub UpdateFine { + my ($params) = @_; -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 -accountlines table of the Koha database. + my $itemnumber = $params->{itemnumber}; + my $borrowernumber = $params->{borrowernumber}; + my $amount = $params->{amount}; + my $due = $params->{due}; + my $issue_id = $params->{issue_id}; -=cut + my $schema = Koha::Database->new()->schema; -# -# Question: Why should the caller have to -# specify both the item number and the borrower number? A book can't -# be on loan to two different people, so the item number should be -# sufficient. -# -# 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 $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')" - ); - $sth->execute( $borrowernumber ); - my $data; - 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 - 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; - } - } - $total_amount_other += $rec->{'amountoutstanding'}; - } + my $borrower = $schema->resultset('Borrower')->find($borrowernumber); - 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"; + if ( my $maxfine = C4::Context->preference('MaxFine') ) { + if ( $borrower->account_balance() + $amount > $maxfine ) { + my $new_amount = $maxfine - $borrower->account_balance(); + warn "Reducing fine for item $itemnumber borrower $borrowernumber from $amount to $new_amount - MaxFine reached"; + if ( $new_amount <= 0 ) { + warn "Fine reduced to a non-positive ammount. Fine not created."; + return; + } $amount = $new_amount; } } - 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'}" + my $timestamp = get_timestamp(); + + my $fine = + $schema->resultset('AccountDebit')->single( { issue_id => $issue_id, type => Koha::Accounts::DebitTypes::Fine } ); + + my $offset = 0; + if ($fine) { + if ( + sprintf( "%.6f", $fine->amount_original() ) + ne + sprintf( "%.6f", $amount ) ) + { + my $difference = $amount - $fine->amount_original(); + + $fine->amount_original( $fine->amount_original() + $difference ); + $fine->amount_outstanding( $fine->amount_outstanding() + $difference ); + $fine->amount_last_increment($difference); + $fine->updated_on($timestamp); + $fine->update(); + + $offset = 1; } - } else { - my $sth4 = $dbh->prepare( - "SELECT title FROM biblio LEFT JOIN items ON biblio.biblionumber=items.biblionumber WHERE items.itemnumber=?" + } + else { + my $item = $schema->resultset('Item')->find($itemnumber); + + $fine = $schema->resultset('AccountDebit')->create( + { + borrowernumber => $borrowernumber, + itemnumber => $itemnumber, + issue_id => $issue_id, + type => Koha::Accounts::DebitTypes::Fine(), + accruing => 1, + amount_original => $amount, + amount_outstanding => $amount, + amount_last_increment => $amount, + description => $item->biblio()->title() . " / Due:$due", + created_on => $timestamp, + } ); - $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); + + $offset = 1; } - # logging action - &logaction( - "FINES", - $type, + + $schema->resultset('AccountOffset')->create( + { + debit_id => $fine->debit_id(), + amount => $fine->amount_last_increment(), + created_on => $timestamp, + type => Koha::Accounts::OffsetTypes::Fine(), + } + ) if $offset; + + $borrower->account_balance( $borrower->account_balance + $fine->amount_last_increment() ); + $borrower->update(); + + logaction( "FINES", Koha::Accounts::DebitTypes::Fine(), $borrowernumber, - "due=".$due." amount=".$amount." itemnumber=".$itemnum - ) if C4::Context->preference("FinesLog"); + "due=" . $due . " amount=" . $amount . " itemnumber=" . $itemnumber ) + if C4::Context->preference("FinesLog"); } =head2 BorType @@ -644,70 +605,19 @@ C<$borrowernumber> is the borrowernumber =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%' - AND amountoutstanding > 0 AND itemnumber = ? AND borrowernumber=?|; - my $sth = $dbh->prepare($query); - $sth->execute( $itemnum, $borrowernumber ); - my $fine = $sth->fetchrow_hashref(); - if ($fine->{fineamount}) { - return $fine->{fineamount}; - } - 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 + my ( $itemnumber, $borrowernumber ) = @_; -C<$totalnotify> contains amount of a file + my $schema = Koha::Database->new()->schema; -C<$notify_id> contains the file number for the borrower number and item number + my $amount_outstanding = $schema->resultset('AccountDebit')->search( + { + itemnumber => $itemnumber, + borrowernumber => $borrowernumber, + type => Koha::Accounts::DebitTypes::Fine(), + }, + )->get_column('amount_outstanding')->sum(); -=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); + return $amount_outstanding; } =head2 GetItems @@ -759,27 +669,6 @@ sub GetBranchcodesWithOverdueRules { return @branches; } -=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 @@ -804,6 +693,7 @@ sub GetOverduesForBranch { biblio.title, biblio.author, biblio.biblionumber, + issues.issue_id, issues.date_due, issues.returndate, issues.branchcode, @@ -814,25 +704,23 @@ sub GetOverduesForBranch { items.location, items.itemnumber, itemtypes.description, - accountlines.notify_id, - accountlines.notify_level, - accountlines.amountoutstanding - FROM accountlines - LEFT JOIN issues ON issues.itemnumber = accountlines.itemnumber - AND issues.borrowernumber = accountlines.borrowernumber - LEFT JOIN borrowers ON borrowers.borrowernumber = accountlines.borrowernumber + account_debits.amount_outstanding + FROM account_debits + LEFT JOIN issues ON issues.itemnumber = account_debits.itemnumber + AND issues.borrowernumber = account_debits.borrowernumber + LEFT JOIN borrowers ON borrowers.borrowernumber = account_debits.borrowernumber LEFT JOIN items ON items.itemnumber = issues.itemnumber LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber 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' ) + WHERE (account_debits.amount_outstanding != '0.000000') + AND (account_debits.type = 'FINE') + AND (account_debits.accruing = 1 ) AND (issues.branchcode = ? ) AND (issues.date_due < NOW()) "; my @getoverdues; - my $i = 0; my $sth; if ($location) { $sth = $dbh->prepare("$select AND items.location = ? ORDER BY borrowers.surname, borrowers.firstname"); @@ -842,12 +730,7 @@ sub GetOverduesForBranch { $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++; - } + push( @getoverdues, $data ); } return (@getoverdues); } diff --git a/C4/Reserves.pm b/C4/Reserves.pm index f066404..7223990 100644 --- a/C4/Reserves.pm +++ b/C4/Reserves.pm @@ -28,7 +28,6 @@ use C4::Biblio; use C4::Members; use C4::Items; use C4::Circulation; -use C4::Accounts; # for _koha_notify_reserve use C4::Members::Messaging; @@ -172,19 +171,17 @@ sub AddReserve { $waitingdate = $resdate; } - #eval { - # updates take place here if ( $fee > 0 ) { - my $nextacctno = &getnextacctno( $borrowernumber ); - my $query = qq/ - INSERT INTO accountlines - (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding) - VALUES - (?,?,now(),?,?,'Res',?) - /; - my $usth = $dbh->prepare($query); - $usth->execute( $borrowernumber, $nextacctno, $fee, - "Reserve Charge - $title", $fee ); + AddDebit( + { + borrowernumber => $borrowernumber, + itemnumber => $checkitem, + amount => $fee, + type => Koha::Accounts::DebitTypes::Hold(), + description => "Hold fee - $title", + notes => "Record ID: $biblionumber", + } + ); } #if ($const eq 'a'){ diff --git a/C4/SIP/ILS/Patron.pm b/C4/SIP/ILS/Patron.pm index 5b835ca..83621d2 100644 --- a/C4/SIP/ILS/Patron.pm +++ b/C4/SIP/ILS/Patron.pm @@ -85,7 +85,7 @@ sub new { hold_ok => ( !$debarred && !$expired ), card_lost => ( $kp->{lost} || $kp->{gonenoaddress} || $flags->{LOST} ), claims_returned => 0, - fines => $fines_amount, # GetMemberAccountRecords($kp->{borrowernumber}) + fines => $fines_amount, fees => 0, # currently not distinct from fines recall_overdue => 0, items_billed => 0, diff --git a/C4/SIP/ILS/Transaction/FeePayment.pm b/C4/SIP/ILS/Transaction/FeePayment.pm index 52619f4..c69cd73 100644 --- a/C4/SIP/ILS/Transaction/FeePayment.pm +++ b/C4/SIP/ILS/Transaction/FeePayment.pm @@ -20,7 +20,8 @@ use strict; # with Koha; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use C4::Accounts qw(recordpayment); +use Koha::Accounts qw(AddCredit); +use Koha::Database; use ILS; use parent qw(ILS::Transaction); @@ -45,9 +46,19 @@ sub new { sub pay { my $self = shift; my $borrowernumber = shift; - my $amt = shift; - warn("RECORD:$borrowernumber::$amt"); - recordpayment( $borrowernumber, $amt ); + my $amount = shift; + + warn("RECORD:$borrowernumber::$amount"); + + my $borrower = Koha::Database->new()->schema->resultset('Borrower') + ->find( $borrowernumber ); + AddCredit( + { + borrower => $borrower, + amount => $amount, + notes => 'via SIP2', + } + ); } #sub DESTROY { -- 1.7.2.5