From 2446809a9f3dffe0631e1c7ff9630833eb52045f Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Wed, 9 Oct 2013 10:36:44 -0400 Subject: [PATCH] Bug 6427 - Rewrite of the accounts system - WIP --- C4/Circulation.pm | 311 ++++++------- C4/Members.pm | 67 ++-- C4/Overdues.pm | 219 ++++----- C4/Reserves.pm | 22 +- Koha/Accounts.pm | 515 ++++++++++++++++++++ Koha/Accounts/CreditTypes.pm | 117 +++++ Koha/Accounts/DebitTypes.pm | 160 ++++++ Koha/Accounts/OffsetTypes.pm | 72 +++ Koha/DateUtils.pm | 7 +- Koha/Schema/Result/AccountCredit.pm | 140 ++++++ Koha/Schema/Result/AccountDebit.pm | 207 ++++++++ Koha/Schema/Result/AccountOffset.pm | 118 +++++ Koha/Schema/Result/Borrower.pm | 137 +++--- Koha/Schema/Result/Deleteditem.pm | 11 + Koha/Schema/Result/Issue.pm | 82 ++-- Koha/Schema/Result/OldIssue.pm | 28 +- Koha/Template/Plugin/Currency.pm | 90 ++++ installer/data/mysql/kohastructure.sql | 84 ++++ installer/data/mysql/updatedatabase.pl | 80 +++ .../intranet-tmpl/prog/en/includes/circ-menu.inc | 2 +- .../intranet-tmpl/prog/en/includes/circ-menu.tt | 2 +- .../prog/en/includes/members-menu.inc | 2 +- .../prog/en/modules/circ/circulation.tt | 7 +- .../intranet-tmpl/prog/en/modules/circ/returns.tt | 2 +- .../prog/en/modules/members/account.tt | 418 ++++++++++++++++ .../prog/en/modules/members/account_credit.tt | 91 ++++ .../prog/en/modules/members/account_debit.tt | 108 ++++ .../prog/en/modules/members/account_payment.tt | 200 ++++++++ .../prog/en/modules/members/account_print.tt | 136 +++++ .../prog/en/modules/members/boraccount.tt | 12 +- .../prog/en/modules/members/mancredit.tt | 12 +- .../prog/en/modules/members/maninvoice.tt | 12 +- .../intranet-tmpl/prog/en/modules/members/pay.tt | 12 +- .../prog/en/modules/members/paycollect.tt | 18 +- koha-tmpl/intranet-tmpl/prog/img/details_close.png | Bin 0 -> 841 bytes koha-tmpl/intranet-tmpl/prog/img/details_open.png | Bin 0 -> 881 bytes members/account.pl | 113 +++++ members/account_credit.pl | 104 ++++ members/account_credit_do.pl | 68 +++ members/account_debit.pl | 104 ++++ members/account_debit_do.pl | 69 +++ members/account_payment.pl | 123 +++++ members/account_payment_do.pl | 62 +++ members/account_print.pl | 58 +++ members/mancredit.pl | 2 +- members/maninvoice.pl | 2 +- members/pay.pl | 4 +- members/paycollect.pl | 4 +- members/printfeercpt.pl | 2 +- members/printinvoice.pl | 2 +- misc/cronjobs/create_koc_db.pl | 41 +-- misc/cronjobs/fines.pl | 10 +- misc/release_notes/release_notes_3_10_0.txt | 2 +- misc/release_notes/release_notes_3_12_0.txt | 2 +- t/db_dependent/Accounts.t | 189 +++++++- t/db_dependent/Circulation.t | 22 +- 56 files changed, 3920 insertions(+), 564 deletions(-) create mode 100644 Koha/Accounts.pm create mode 100644 Koha/Accounts/CreditTypes.pm create mode 100644 Koha/Accounts/DebitTypes.pm create mode 100644 Koha/Accounts/OffsetTypes.pm create mode 100644 Koha/Schema/Result/AccountCredit.pm create mode 100644 Koha/Schema/Result/AccountDebit.pm create mode 100644 Koha/Schema/Result/AccountOffset.pm create mode 100644 Koha/Template/Plugin/Currency.pm create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/account.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/account_credit.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/account_debit.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/account_payment.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/account_print.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/img/details_close.png create mode 100644 koha-tmpl/intranet-tmpl/prog/img/details_open.png create mode 100755 members/account.pl create mode 100755 members/account_credit.pl create mode 100755 members/account_credit_do.pl create mode 100755 members/account_debit.pl create mode 100755 members/account_debit_do.pl create mode 100755 members/account_payment.pl create mode 100755 members/account_payment_do.pl create mode 100755 members/account_print.pl diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 0a22b76..7321aad 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,30 @@ 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 = $issue->borrower(); + my $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 b04cc64..387ee89 100644 --- a/C4/Members.pm +++ b/C4/Members.pm @@ -41,6 +41,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); @@ -338,9 +339,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 +430,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 +686,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]; @@ -1216,8 +1211,8 @@ sub GetMemberAccountRecords { 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,24 +1220,38 @@ 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; + + push( @not_fines, Koha::Accounts::DebitTypes::Hold() ); + + push( @not_fines, Koha::Accounts::DebitTypes::Rental() ) + unless C4::Context->preference('RentalsInNoissuesCharge'); - 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 $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 ); } - my %not_fine = map {$_ => 1} @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) }; - } + my $other_charges = + Koha::Database->new()->schema->resultset('AccountDebit')->search( + { + borrowernumber => $borrowernumber, + type => { -not_in => \@not_fines } + } + )->get_column('amount_outstanding')->sum(); - return ( $total, $total - $other_charges, $other_charges); + return ( + $borrower->account_balance(), + $borrower->account_balance() - $other_charges, + $other_charges + ); } =head2 GetBorNotifyAcctRecord diff --git a/C4/Overdues.pm b/C4/Overdues.pm index 33dbd98..58aca1a 100644 --- a/C4/Overdues.pm +++ b/C4/Overdues.pm @@ -28,6 +28,8 @@ use C4::Context; use C4::Accounts; use C4::Log; # logaction use C4::Debug; +use Koha::Database; +use Koha::DateUtils; use vars qw($VERSION @ISA @EXPORT); @@ -456,154 +458,112 @@ 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 } ); + + 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; + + 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 @@ -804,6 +764,7 @@ sub GetOverduesForBranch { biblio.title, biblio.author, biblio.biblionumber, + issues.issue_id, issues.date_due, issues.returndate, issues.branchcode, diff --git a/C4/Reserves.pm b/C4/Reserves.pm index d1bca25..3b8e77b 100644 --- a/C4/Reserves.pm +++ b/C4/Reserves.pm @@ -172,19 +172,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/Koha/Accounts.pm b/Koha/Accounts.pm new file mode 100644 index 0000000..7cb0a80 --- /dev/null +++ b/Koha/Accounts.pm @@ -0,0 +1,515 @@ +package Koha::Accounts; + +# Copyright 2013 ByWater Solutions +# +# 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 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. + +use Modern::Perl; + +use Carp; +use Data::Dumper qw(Dumper); + +use C4::Context; +use C4::Log qw(logaction); +use Koha::DateUtils qw(get_timestamp); + +use Koha::Accounts::CreditTypes; +use Koha::Accounts::DebitTypes; + +use vars qw($VERSION @ISA @EXPORT); + +BEGIN { + require Exporter; + @ISA = qw(Exporter); + @EXPORT = qw( + AddDebit + AddCredit + + NormalizeBalances + + RecalculateAccountBalance + + DebitLostItem + CreditLostItem + ); +} + +=head1 NAME + +Koha::Accounts - Functions for dealing with Koha accounts + +=head1 SYNOPSIS + +use Koha::Accounts; + +=head1 DESCRIPTION + +The functions in this module deal with the monetary aspect of Koha, +including looking up and modifying the amount of money owed by a +patron. + +=head1 FUNCTIONS + +=head2 AddDebit + +my $debit = AddDebit({ + borrower => $borrower, + amount => $amount, + [ type => $type, ] + [ itemnumber => $itemnumber, ] + [ issue_id => $issue_id, ] + [ description => $description, ] + [ notes => $notes, ] + [ branchcode => $branchcode, ] + [ manager_id => $manager_id, ] + [ accruing => $accruing, ] # Default 0 if not accruing, 1 if accruing +}); + +Create a new debit for a given borrower. To standardize nomenclature, any charge +against a borrower ( e.g. a fine, a new card charge, the cost of losing an item ) +will be referred to as a 'debit'. + +=cut + +sub AddDebit { + my ($params) = @_; + + my $borrower = $params->{borrower}; + my $amount = $params->{amount}; + + my $type = $params->{type}; + my $itemnumber = $params->{itemnumber}; + my $issue_id = $params->{issue_id}; + my $description = $params->{description}; + my $notes = $params->{notes}; + + my $branchcode = $params->{branchcode}; + $branchcode ||= + defined( C4::Context->userenv ) + ? C4::Context->userenv->{branch} + : undef; + + my $manager_id = $params->{manager_id}; + $manager_id ||= + defined( C4::Context->userenv ) + ? C4::Context->userenv->{manager_id} + : undef; + + my $accruing = $params->{accruing} || 0; + + croak("Required parameter 'borrower' not passed in.") + unless ($borrower); + croak("Required parameter 'amount' not passed in.") + unless ($amount); + croak("Invalid debit type: '$type'!") + unless ( Koha::Accounts::DebitTypes::IsValid($type) ); + croak("No issue id passed in for accruing debit!") + if ( $accruing && !$issue_id ); + + my $debit = Koha::Database->new()->schema->resultset('AccountDebit')->create( + { + borrowernumber => $borrower->borrowernumber(), + itemnumber => $itemnumber, + issue_id => $issue_id, + type => $type, + accruing => $accruing, + amount_original => $amount, + amount_outstanding => $amount, + amount_last_increment => $amount, + description => $description, + notes => $notes, + manager_id => $manager_id, + created_on => get_timestamp(), + } + ); + + if ($debit) { + $borrower->account_balance( $borrower->account_balance() + $amount ); + $borrower->update(); + + NormalizeBalances( { borrower => $borrower } ); + + if ( C4::Context->preference("FinesLog") ) { + logaction( "FINES", "CREATE_FEE", $debit->id, + Dumper( { $debit->get_columns(), accruing => $accruing } ) ); + } + } + else { + carp("Something went wrong! Debit not created!"); + } + + return $debit; +} + +=head2 DebitLostItem + +my $debit = DebitLostItem({ + borrower => $borrower, + issue => $issue, +}); + +DebitLostItem adds a replacement fee charge for the item +of the given issue. + +=cut + +sub DebitLostItem { + my ($params) = @_; + + my $borrower = $params->{borrower}; + my $issue = $params->{issue}; + + croak("Required param 'borrower' not passed in!") unless ($borrower); + croak("Required param 'issue' not passed in!") unless ($issue); + + # Don't add lost debit if borrower has already been charged for this lost item before, + # for this issue. It seems reasonable that a borrower could lose an item, find and return it, + # check it out again, and lose it again, so we should do this based on issue_id, not itemnumber. + unless ( + Koha::Database->new()->schema->resultset('AccountDebit')->search( + { + borrowernumber => $borrower->borrowernumber(), + issue_id => $issue->issue_id(), + type => Koha::Accounts::DebitTypes::Lost + } + )->count() + ) + { + my $item = $issue->item(); + + $params->{accruing} = 0; + $params->{type} = Koha::Accounts::DebitTypes::Lost; + $params->{amount} = $item->replacementprice(); + $params->{itemnumber} = $item->itemnumber(); + $params->{issue_id} = $issue->issue_id(); + + #TODO: Shouldn't we have a default replacement price as a syspref? + if ( $params->{amount} ) { + return AddDebit($params); + } + else { + carp("Cannot add lost debit! Item has no replacement price!"); + } + } +} + +=head2 CreditLostItem + +my $debit = CreditLostItem( + { + borrower => $borrower, + debit => $debit, + } +); + +CreditLostItem creates a payment in the amount equal +to the replacement price charge created by DebitLostItem. + +=cut + +sub CreditLostItem { + my ($params) = @_; + + my $borrower = $params->{borrower}; + my $debit = $params->{debit}; + + croak("Required param 'borrower' not passed in!") unless ($borrower); + croak("Required param 'debit' not passed in!") + unless ($debit); + + my $item = + Koha::Database->new()->schema->resultset('Item') + ->find( $debit->itemnumber() ); + carp("No item found!") unless $item; + + $params->{type} = Koha::Accounts::CreditTypes::Found; + $params->{amount} = $debit->amount_original(); + $params->{debit_id} = $debit->debit_id(); + $params->{notes} = "Lost item found: " . $item->barcode(); + + return AddCredit($params); +} + +=head2 AddCredit + +AddCredit({ + borrower => $borrower, + amount => $amount, + [ branchcode => $branchcode, ] + [ manager_id => $manager_id, ] + [ debit_id => $debit_id, ] # The primary debit to be paid + [ notes => $notes, ] +}); + +Record credit by a patron. C<$borrowernumber> is the patron's +borrower number. C<$credit> is a floating-point number, giving the +amount that was paid. + +Amounts owed are paid off oldest first. That is, if the patron has a +$1 fine from Feb. 1, another $1 fine from Mar. 1, and makes a credit +of $1.50, then the oldest fine will be paid off in full, and $0.50 +will be credited to the next one. + +debit_id can be passed as a scalar or an array ref to make the passed +in debit or debits the first to be credited. + +=cut + +sub AddCredit { + my ($params) = @_; + + my $type = $params->{type}; + my $borrower = $params->{borrower}; + my $amount = $params->{amount}; + my $debit_id = $params->{debit_id}; + my $notes = $params->{notes}; + my $branchcode = $params->{branchcode}; + my $manager_id = $params->{manager_id}; + + my $userenv = C4::Context->userenv; + + unless ( $manager_id || $userenv ) { + $manager_id = $userenv->{number}; + } + + unless ( $branchcode || $userenv ) { + $branchcode = $userenv->{branch}; + } + + unless ($borrower) { + croak("Required parameter 'borrower' not passed in"); + } + unless ($amount) { + croak("Required parameter amount not passed in"); + } + + unless ( Koha::Accounts::CreditTypes::IsValid($type) ) { + carp("Invalid credit type! Returning without creating credit."); + return; + } + + unless ($type) { + carp("No type passed in, assuming Payment"); + $type = Koha::Accounts::CreditTypes::Payment; + } + + my $debit = Koha::Database->new()->schema->resultset('AccountDebit')->find($debit_id); + + # First, we make the credit. We'll worry about what we paid later on + my $credit = Koha::Database->new()->schema->resultset('AccountCredit')->create( + { + borrowernumber => $borrower->borrowernumber(), + type => $type, + amount_paid => $amount, + amount_remaining => $amount, + notes => $notes, + manager_id => $manager_id, + created_on => get_timestamp(), + } + ); + + $borrower->account_balance( $borrower->account_balance() - $amount ); + $borrower->update(); + + # If we are given specific debits, pay those ones first. + if ( $debit_id ) { + my @debit_ids = ref( $debit_id ) eq "ARRAY" ? @$debit_id : $debit_id; + foreach my $debit_id (@debit_ids) { + my $debit = + Koha::Database->new()->schema->resultset('AccountDebit')->find($debit_id); + + if ($debit) { + CreditDebit( { credit => $credit, debit => $debit } ); + } + else { + carp("Invalid debit_id passed in!"); + } + } + } + + # We still have leftover money, or we weren't given a specific debit to pay + if ( $credit->amount_remaining() > 0 ) { + my @debits = Koha::Database->new()->schema->resultset('AccountDebit')->search( + { + borrowernumber => $borrower->borrowernumber(), + amount_outstanding => { '>' => '0' } + } + ); + + foreach my $debit (@debits) { + if ( $credit->amount_remaining() > 0 ) { + CreditDebit( + { + credit => $credit, + debit => $debit, + borrower => $borrower, + type => $type, + } + ); + } + } + } + + return $credit; +} + +=head2 CreditDebit + +$account_offset = CreditDebit({ + credit => $credit, + debit => $debit, +}); + +Given a credit and a debit, this subroutine +will pay the appropriate amount of the debit, +update the debit's amount outstanding, the credit's +amout remaining, and create the appropriate account +offset. + +=cut + +sub CreditDebit { + my ($params) = @_; + + my $credit = $params->{credit}; + my $debit = $params->{debit}; + + croak("Required parameter 'credit' not passed in!") + unless $credit; + croak("Required parameter 'debit' not passed in!") unless $debit; + + my $amount_to_pay = + ( $debit->amount_outstanding() > $credit->amount_remaining() ) + ? $credit->amount_remaining() + : $debit->amount_outstanding(); + + if ( $amount_to_pay > 0 ) { + $debit->amount_outstanding( + $debit->amount_outstanding() - $amount_to_pay ); + $debit->update(); + + $credit->amount_remaining( + $credit->amount_remaining() - $amount_to_pay ); + $credit->update(); + + my $offset = Koha::Database->new()->schema->resultset('AccountOffset')->create( + { + amount => $amount_to_pay * -1, + debit_id => $debit->id(), + credit_id => $credit->id(), + created_on => get_timestamp(), + } + ); + + return $offset; + } +} + +=head2 RecalculateAccountBalance + +$account_balance = RecalculateAccountBalance({ + borrower => $borrower +}); + +Recalculates a borrower's balance based on the +sum of the amount outstanding for the borrower's +debits minus the sum of the amount remaining for +the borrowers credits. + +TODO: Would it be better to use af.amount_original - ap.amount_paid for any reason? + Or, perhaps calculate both and compare the two, for error checking purposes. +=cut + +sub RecalculateAccountBalance { + my ($params) = @_; + + my $borrower = $params->{borrower}; + croak("Requred paramter 'borrower' not passed in!") + unless ($borrower); + + my $debits = + Koha::Database->new()->schema->resultset('AccountDebit') + ->search( { borrowernumber => $borrower->borrowernumber() } ); + my $amount_outstanding = $debits->get_column('amount_outstanding')->sum(); + + my $credits = + Koha::Database->new()->schema->resultset('AccountCredit') + ->search( { borrowernumber => $borrower->borrowernumber() } ); + my $amount_remaining = $credits->get_column('amount_remaining')->sum(); + + my $account_balance = $amount_outstanding - $amount_remaining; + $borrower->account_balance($account_balance); + $borrower->update(); + + return $account_balance; +} + +=head2 NormalizeBalances + + $account_balance = NormalizeBalances({ borrower => $borrower }); + + For a given borrower, this subroutine will find all debits + with an outstanding balance and all credits with an unused + amount remaining and will pay those debits with those credits. + +=cut + +sub NormalizeBalances { + my ($params) = @_; + + my $borrower = $params->{borrower}; + + croak("Required param 'borrower' not passed in!") unless $borrower; + + my @credits = Koha::Database->new()->schema->resultset('AccountCredit')->search( + { + borrowernumber => $borrower->borrowernumber(), + amount_remaining => { '>' => '0' } + } + ); + + return unless @credits; + + my @debits = Koha::Database->new()->schema->resultset('AccountDebit')->search( + { + borrowernumber => $borrower->borrowernumber(), + amount_outstanding => { '>' => '0' } + } + ); + + return unless @debits; + + foreach my $credit (@credits) { + foreach my $debit (@debits) { + if ( $credit->amount_remaining() + && $debit->amount_outstanding() ) + { + CreditDebit( { credit => $credit, debit => $debit } ); + } + } + } + + return RecalculateAccountBalance( { borrower => $borrower } ); +} + +1; +__END__ + +=head1 AUTHOR + +Kyle M Hall + +=cut diff --git a/Koha/Accounts/CreditTypes.pm b/Koha/Accounts/CreditTypes.pm new file mode 100644 index 0000000..d51ec93 --- /dev/null +++ b/Koha/Accounts/CreditTypes.pm @@ -0,0 +1,117 @@ +package Koha::Accounts::CreditTypes; + +# Copyright 2013 ByWater Solutions +# +# 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 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. + +use Modern::Perl; + +=head1 NAME + +Koha::AccountsCreditTypes - Module representing the enumerated data types for account fees + +=head1 SYNOPSIS + +use Koha::Accounts::CreditTypes; + +my $type = Koha::Accounts::CreditTypes::Payment; + +=head1 DESCRIPTION + +The subroutines in this modules act as enumerated data types for the +different credit types in Koha ( i.e. payments, writeoffs, etc. ) + +=head1 FUNCTIONS + +=head2 IsValid + +This subroutine takes a given string and returns 1 if +the string matches one of the data types, and 0 if not. + +FIXME: Perhaps we should use Class::Inspector instead of hard +coding the subs? It seems like it would be a major trade off +of speed just so we don't update something in two separate places +in the same file. + +=cut + +sub IsValid { + my ($string) = @_; + + my $is_valid = + ( $string eq Koha::Accounts::CreditTypes::Payment() + || $string eq Koha::Accounts::CreditTypes::WriteOff() + || $string eq Koha::Accounts::CreditTypes::Found() + || $string eq Koha::Accounts::CreditTypes::Credit() + || $string eq Koha::Accounts::CreditTypes::Forgiven() ); + + unless ($is_valid) { + $is_valid = + Koha::Database->new()->schema->resultset('AuthorisedValue') + ->count( + { category => 'ACCOUNT_CREDIT', authorised_value => $string } ); + } + + return $is_valid; +} + +=head2 Credit + +=cut + +sub Credit { + return 'CREDIT'; +} + +=head2 Payment + +=cut + +sub Payment { + return 'PAYMENT'; +} + +=head2 Writeoff + +=cut + +sub WriteOff { + return 'WRITEOFF'; +} + +=head2 Writeoff + +=cut + +sub Found { + return 'FOUND'; +} + +=head2 Forgiven + +=cut + +sub Forgiven { + return 'FORGIVEN'; +} + +1; + +=head1 AUTHOR + +Kyle M Hall + +=cut diff --git a/Koha/Accounts/DebitTypes.pm b/Koha/Accounts/DebitTypes.pm new file mode 100644 index 0000000..3f30392 --- /dev/null +++ b/Koha/Accounts/DebitTypes.pm @@ -0,0 +1,160 @@ +package Koha::Accounts::DebitTypes; + +# Copyright 2013 ByWater Solutions +# +# 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 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. + +use Modern::Perl; + +=head1 NAME + +Koha::Accounts::DebitTypes - Module representing an enumerated data type for account fees + +=head1 SYNOPSIS + +use Koha::Accounts::DebitTypes; + +my $type = Koha::Accounts::DebitTypes::Fine; + +=head1 DESCRIPTION + +The subroutines in this modules act as an enumerated data type +for debit types ( stored in account_debits.type ) in Koha. + +=head1 FUNCTIONS + +=head2 IsValid + +This subroutine takes a given string and returns 1 if +the string matches one of the data types, and 0 if not. + +=cut + +sub IsValid { + my ($string) = @_; + + my $is_valid = + ( $string eq Koha::Accounts::DebitTypes::Fine() + || $string eq Koha::Accounts::DebitTypes::AccountManagementFee() + || $string eq Koha::Accounts::DebitTypes::Sundry() + || $string eq Koha::Accounts::DebitTypes::Lost() + || $string eq Koha::Accounts::DebitTypes::Hold() + || $string eq Koha::Accounts::DebitTypes::Rental() + || $string eq Koha::Accounts::DebitTypes::NewCard() ); + + unless ($is_valid) { + $is_valid = + Koha::Database->new()->schema->resultset('AuthorisedValue') + ->count( { category => 'MANUAL_INV', authorised_value => $string } ); + } + + return $is_valid; +} + +=head2 Fine + +This data type represents a standard fine within Koha. + +A fine still accruing no longer needs to be differiated by type +from a fine done accuring. Instead, that differentication is made +by which table the fine exists in, account_fees_accruing vs account_fees_accrued. + +In addition, fines can be checked for correctness based on the issue_id +they have. A fine in account_fees_accruing should always have a matching +issue_id in the issues table. A fine done accruing will almost always have +a matching issue_id in the old_issues table. However, in the case of an overdue +item with fines that has been renewed, and becomes overdue again, you may have +a case where a given issue may have a matching fine in account_fees_accruing and +one or more matching fines in account_fees_accrued ( one for each for the first +checkout and one each for any subsequent renewals ) + +=cut + +sub Fine { + return 'FINE'; +} + +=head2 AccountManagementFee + +This fee type is usually reserved for payments for library cards, +in cases where a library must charge a patron for the ability to +check out items. + +=cut + +sub AccountManagementFee { + return 'ACCOUNT_MANAGEMENT_FEE'; +} + +=head2 Sundry + +This fee type is basically a 'misc' type, and should be used +when no other fee type is more appropriate. + +=cut + +sub Sundry { + return 'SUNDRY'; +} + +=head2 Lost + +This fee type is used when a library charges for lost items. + +=cut + +sub Lost { + return 'LOST'; +} + +=head2 Hold + +This fee type is used when a library charges for holds. + +=cut + +sub Hold { + return 'HOLD'; +} + +=head2 Rental + +This fee type is used when a library charges a rental fee for the item type. + +=cut + +sub Rental { + return 'RENTAL'; +} + +=head2 NewCard + +This fee type is used when a library charges for replacement +library cards. + +=cut + +sub NewCard { + return 'NEW_CARD'; +} + +1; + +=head1 AUTHOR + +Kyle M Hall + +=cut diff --git a/Koha/Accounts/OffsetTypes.pm b/Koha/Accounts/OffsetTypes.pm new file mode 100644 index 0000000..1a5aabb --- /dev/null +++ b/Koha/Accounts/OffsetTypes.pm @@ -0,0 +1,72 @@ +package Koha::Accounts::OffsetTypes; + +# Copyright 2013 ByWater Solutions +# +# 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 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. + +use Modern::Perl; + +=head1 NAME + +Koha::AccountsOffsetTypes - Module representing the enumerated data types for account fees + +=head1 SYNOPSIS + +use Koha::Accounts::OffsetTypes; + +my $type = Koha::Accounts::OffsetTypes::Dropbox; + +=head1 DESCRIPTION + +The subroutines in this modules act as enumerated data types for the +different automatic offset types in Koha ( i.e. forgiveness, dropbox mode, etc ) + +These types are used for account offsets that have no corrosponding account credit, +e.g. automatic fine increments, dropbox mode, etc. + +=head1 FUNCTIONS + +=cut + +=head2 Dropbox + +Offset type for automatic fine reductions +via dropbox mode. + +=cut + +sub Dropbox { + return 'DROPBOX'; +} + +=head2 Fine + +Indicates this offset was an automatically +generated fine increment/decrement. + +=cut + +sub Fine { + return 'FINE'; +} + +1; + +=head1 AUTHOR + +Kyle M Hall + +=cut diff --git a/Koha/DateUtils.pm b/Koha/DateUtils.pm index 4ae0d0c..6dbc979 100644 --- a/Koha/DateUtils.pm +++ b/Koha/DateUtils.pm @@ -21,13 +21,14 @@ use warnings; use 5.010; use DateTime; use DateTime::Format::DateParse; +use DateTime::Format::MySQL; use C4::Context; use base 'Exporter'; use version; our $VERSION = qv('1.0.0'); our @EXPORT = ( - qw( dt_from_string output_pref format_sqldatetime output_pref_due format_sqlduedatetime) + qw( dt_from_string output_pref format_sqldatetime output_pref_due format_sqlduedatetime get_timestamp ) ); =head1 DateUtils @@ -239,4 +240,8 @@ sub format_sqlduedatetime { return q{}; } +sub get_timestamp { + return DateTime::Format::MySQL->format_datetime( dt_from_string() ); +} + 1; diff --git a/Koha/Schema/Result/AccountCredit.pm b/Koha/Schema/Result/AccountCredit.pm new file mode 100644 index 0000000..df612ff --- /dev/null +++ b/Koha/Schema/Result/AccountCredit.pm @@ -0,0 +1,140 @@ +package Koha::Schema::Result::AccountCredit; + +# Created by DBIx::Class::Schema::Loader +# DO NOT MODIFY THE FIRST PART OF THIS FILE + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + + +=head1 NAME + +Koha::Schema::Result::AccountCredit + +=cut + +__PACKAGE__->table("account_credits"); + +=head1 ACCESSORS + +=head2 credit_id + + data_type: 'integer' + is_auto_increment: 1 + is_nullable: 0 + +=head2 borrowernumber + + data_type: 'integer' + is_foreign_key: 1 + is_nullable: 0 + +=head2 type + + data_type: 'varchar' + is_nullable: 0 + size: 255 + +=head2 amount_paid + + data_type: 'decimal' + is_nullable: 0 + size: [28,6] + +=head2 amount_remaining + + data_type: 'decimal' + is_nullable: 0 + size: [28,6] + +=head2 notes + + data_type: 'text' + is_nullable: 1 + +=head2 manager_id + + data_type: 'integer' + is_nullable: 1 + +=head2 created_on + + data_type: 'timestamp' + is_nullable: 1 + +=head2 updated_on + + data_type: 'timestamp' + is_nullable: 1 + +=cut + +__PACKAGE__->add_columns( + "credit_id", + { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, + "borrowernumber", + { data_type => "integer", is_foreign_key => 1, is_nullable => 0 }, + "type", + { data_type => "varchar", is_nullable => 0, size => 255 }, + "amount_paid", + { data_type => "decimal", is_nullable => 0, size => [28, 6] }, + "amount_remaining", + { data_type => "decimal", is_nullable => 0, size => [28, 6] }, + "notes", + { data_type => "text", is_nullable => 1 }, + "manager_id", + { data_type => "integer", is_nullable => 1 }, + "created_on", + { data_type => "timestamp", is_nullable => 1 }, + "updated_on", + { data_type => "timestamp", is_nullable => 1 }, +); +__PACKAGE__->set_primary_key("credit_id"); + +=head1 RELATIONS + +=head2 borrowernumber + +Type: belongs_to + +Related object: L + +=cut + +__PACKAGE__->belongs_to( + "borrowernumber", + "Koha::Schema::Result::Borrower", + { borrowernumber => "borrowernumber" }, + { on_delete => "CASCADE", on_update => "CASCADE" }, +); + +=head2 account_offsets + +Type: has_many + +Related object: L + +=cut + +__PACKAGE__->has_many( + "account_offsets", + "Koha::Schema::Result::AccountOffset", + { "foreign.credit_id" => "self.credit_id" }, + { cascade_copy => 0, cascade_delete => 0 }, +); + + +# Created by DBIx::Class::Schema::Loader v0.07000 @ 2013-10-09 10:37:23 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:uvt4OuaJxv4jk08zJsKouw + +__PACKAGE__->belongs_to( + "borrower", + "Koha::Schema::Result::Borrower", + { borrowernumber => "borrowernumber" }, +); + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/Koha/Schema/Result/AccountDebit.pm b/Koha/Schema/Result/AccountDebit.pm new file mode 100644 index 0000000..e57347d --- /dev/null +++ b/Koha/Schema/Result/AccountDebit.pm @@ -0,0 +1,207 @@ +package Koha::Schema::Result::AccountDebit; + +# Created by DBIx::Class::Schema::Loader +# DO NOT MODIFY THE FIRST PART OF THIS FILE + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + + +=head1 NAME + +Koha::Schema::Result::AccountDebit + +=cut + +__PACKAGE__->table("account_debits"); + +=head1 ACCESSORS + +=head2 debit_id + + data_type: 'integer' + is_auto_increment: 1 + is_nullable: 0 + +=head2 borrowernumber + + data_type: 'integer' + default_value: 0 + is_foreign_key: 1 + is_nullable: 0 + +=head2 itemnumber + + data_type: 'integer' + is_nullable: 1 + +=head2 issue_id + + data_type: 'integer' + is_nullable: 1 + +=head2 type + + data_type: 'varchar' + is_nullable: 0 + size: 255 + +=head2 accruing + + data_type: 'tinyint' + default_value: 0 + is_nullable: 0 + +=head2 amount_original + + data_type: 'decimal' + is_nullable: 1 + size: [28,6] + +=head2 amount_outstanding + + data_type: 'decimal' + is_nullable: 1 + size: [28,6] + +=head2 amount_last_increment + + data_type: 'decimal' + is_nullable: 1 + size: [28,6] + +=head2 description + + data_type: 'mediumtext' + is_nullable: 1 + +=head2 notes + + data_type: 'text' + is_nullable: 1 + +=head2 manager_id + + data_type: 'integer' + is_nullable: 1 + +=head2 created_on + + data_type: 'timestamp' + is_nullable: 1 + +=head2 updated_on + + data_type: 'timestamp' + is_nullable: 1 + +=cut + +__PACKAGE__->add_columns( + "debit_id", + { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, + "borrowernumber", + { + data_type => "integer", + default_value => 0, + is_foreign_key => 1, + is_nullable => 0, + }, + "itemnumber", + { data_type => "integer", is_nullable => 1 }, + "issue_id", + { data_type => "integer", is_nullable => 1 }, + "type", + { data_type => "varchar", is_nullable => 0, size => 255 }, + "accruing", + { data_type => "tinyint", default_value => 0, is_nullable => 0 }, + "amount_original", + { data_type => "decimal", is_nullable => 1, size => [28, 6] }, + "amount_outstanding", + { data_type => "decimal", is_nullable => 1, size => [28, 6] }, + "amount_last_increment", + { data_type => "decimal", is_nullable => 1, size => [28, 6] }, + "description", + { data_type => "mediumtext", is_nullable => 1 }, + "notes", + { data_type => "text", is_nullable => 1 }, + "manager_id", + { data_type => "integer", is_nullable => 1 }, + "created_on", + { data_type => "timestamp", is_nullable => 1 }, + "updated_on", + { data_type => "timestamp", is_nullable => 1 }, +); +__PACKAGE__->set_primary_key("debit_id"); + +=head1 RELATIONS + +=head2 borrowernumber + +Type: belongs_to + +Related object: L + +=cut + +__PACKAGE__->belongs_to( + "borrowernumber", + "Koha::Schema::Result::Borrower", + { borrowernumber => "borrowernumber" }, + { on_delete => "CASCADE", on_update => "CASCADE" }, +); + +=head2 account_offsets + +Type: has_many + +Related object: L + +=cut + +__PACKAGE__->has_many( + "account_offsets", + "Koha::Schema::Result::AccountOffset", + { "foreign.debit_id" => "self.debit_id" }, + { cascade_copy => 0, cascade_delete => 0 }, +); + + +# Created by DBIx::Class::Schema::Loader v0.07000 @ 2013-11-05 08:09:09 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ulQStZJSzcD4hvrPbtew4g + +__PACKAGE__->belongs_to( + "item", + "Koha::Schema::Result::Item", + { itemnumber => "itemnumber" } +); + +__PACKAGE__->belongs_to( + "deleted_item", + "Koha::Schema::Result::Deleteditem", + { itemnumber => "itemnumber" } +); + +__PACKAGE__->belongs_to( + "issue", + "Koha::Schema::Result::Issue", + { issue_id => "issue_id" } +); + +__PACKAGE__->belongs_to( + "old_issue", + "Koha::Schema::Result::OldIssue", + { issue_id => "issue_id" } +); + +__PACKAGE__->belongs_to( + "borrower", + "Koha::Schema::Result::Borrower", + { borrowernumber => "borrowernumber" }, + { on_delete => "CASCADE", on_update => "CASCADE" }, +); + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/Koha/Schema/Result/AccountOffset.pm b/Koha/Schema/Result/AccountOffset.pm new file mode 100644 index 0000000..179392f --- /dev/null +++ b/Koha/Schema/Result/AccountOffset.pm @@ -0,0 +1,118 @@ +package Koha::Schema::Result::AccountOffset; + +# Created by DBIx::Class::Schema::Loader +# DO NOT MODIFY THE FIRST PART OF THIS FILE + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + + +=head1 NAME + +Koha::Schema::Result::AccountOffset + +=cut + +__PACKAGE__->table("account_offsets"); + +=head1 ACCESSORS + +=head2 offset_id + + data_type: 'integer' + is_auto_increment: 1 + is_nullable: 0 + +=head2 debit_id + + data_type: 'integer' + is_foreign_key: 1 + is_nullable: 1 + +=head2 credit_id + + data_type: 'integer' + is_foreign_key: 1 + is_nullable: 1 + +=head2 type + + data_type: 'varchar' + is_nullable: 1 + size: 255 + +=head2 amount + + data_type: 'decimal' + is_nullable: 0 + size: [28,6] + +=head2 created_on + + data_type: 'timestamp' + default_value: current_timestamp + is_nullable: 0 + +=cut + +__PACKAGE__->add_columns( + "offset_id", + { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, + "debit_id", + { data_type => "integer", is_foreign_key => 1, is_nullable => 1 }, + "credit_id", + { data_type => "integer", is_foreign_key => 1, is_nullable => 1 }, + "type", + { data_type => "varchar", is_nullable => 1, size => 255 }, + "amount", + { data_type => "decimal", is_nullable => 0, size => [28, 6] }, + "created_on", + { + data_type => "timestamp", + default_value => \"current_timestamp", + is_nullable => 0, + }, +); +__PACKAGE__->set_primary_key("offset_id"); + +=head1 RELATIONS + +=head2 debit + +Type: belongs_to + +Related object: L + +=cut + +__PACKAGE__->belongs_to( + "debit", + "Koha::Schema::Result::AccountDebit", + { debit_id => "debit_id" }, + { join_type => "LEFT", on_delete => "CASCADE", on_update => "CASCADE" }, +); + +=head2 credit + +Type: belongs_to + +Related object: L + +=cut + +__PACKAGE__->belongs_to( + "credit", + "Koha::Schema::Result::AccountCredit", + { credit_id => "credit_id" }, + { join_type => "LEFT", on_delete => "CASCADE", on_update => "CASCADE" }, +); + + +# Created by DBIx::Class::Schema::Loader v0.07000 @ 2013-11-05 08:47:10 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:BLjpL8skXmzxOQ/J0jzdvw + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/Koha/Schema/Result/Borrower.pm b/Koha/Schema/Result/Borrower.pm index 3d9f9ae..580bbec 100644 --- a/Koha/Schema/Result/Borrower.pm +++ b/Koha/Schema/Result/Borrower.pm @@ -1,21 +1,17 @@ -use utf8; package Koha::Schema::Result::Borrower; # Created by DBIx::Class::Schema::Loader # DO NOT MODIFY THE FIRST PART OF THIS FILE -=head1 NAME - -Koha::Schema::Result::Borrower - -=cut - use strict; use warnings; use base 'DBIx::Class::Core'; -=head1 TABLE: C + +=head1 NAME + +Koha::Schema::Result::Borrower =cut @@ -191,7 +187,6 @@ __PACKAGE__->table("borrowers"); =head2 dateofbirth data_type: 'date' - datetime_undef_if_invalid: 1 is_nullable: 1 =head2 branchcode @@ -213,13 +208,11 @@ __PACKAGE__->table("borrowers"); =head2 dateenrolled data_type: 'date' - datetime_undef_if_invalid: 1 is_nullable: 1 =head2 dateexpiry data_type: 'date' - datetime_undef_if_invalid: 1 is_nullable: 1 =head2 gonenoaddress @@ -235,7 +228,6 @@ __PACKAGE__->table("borrowers"); =head2 debarred data_type: 'date' - datetime_undef_if_invalid: 1 is_nullable: 1 =head2 debarredcomment @@ -397,6 +389,13 @@ __PACKAGE__->table("borrowers"); default_value: 1 is_nullable: 0 +=head2 account_balance + + data_type: 'decimal' + default_value: 0.000000 + is_nullable: 0 + size: [28,6] + =cut __PACKAGE__->add_columns( @@ -463,7 +462,7 @@ __PACKAGE__->add_columns( "b_phone", { data_type => "mediumtext", is_nullable => 1 }, "dateofbirth", - { data_type => "date", datetime_undef_if_invalid => 1, is_nullable => 1 }, + { data_type => "date", is_nullable => 1 }, "branchcode", { data_type => "varchar", @@ -481,15 +480,15 @@ __PACKAGE__->add_columns( size => 10, }, "dateenrolled", - { data_type => "date", datetime_undef_if_invalid => 1, is_nullable => 1 }, + { data_type => "date", is_nullable => 1 }, "dateexpiry", - { data_type => "date", datetime_undef_if_invalid => 1, is_nullable => 1 }, + { data_type => "date", is_nullable => 1 }, "gonenoaddress", { data_type => "tinyint", is_nullable => 1 }, "lost", { data_type => "tinyint", is_nullable => 1 }, "debarred", - { data_type => "date", datetime_undef_if_invalid => 1, is_nullable => 1 }, + { data_type => "date", is_nullable => 1 }, "debarredcomment", { data_type => "varchar", is_nullable => 1, size => 255 }, "contactname", @@ -546,35 +545,48 @@ __PACKAGE__->add_columns( { data_type => "varchar", is_nullable => 1, size => 50 }, "privacy", { data_type => "integer", default_value => 1, is_nullable => 0 }, + "account_balance", + { + data_type => "decimal", + default_value => "0.000000", + is_nullable => 0, + size => [28, 6], + }, ); +__PACKAGE__->set_primary_key("borrowernumber"); +__PACKAGE__->add_unique_constraint("cardnumber", ["cardnumber"]); -=head1 PRIMARY KEY +=head1 RELATIONS -=over 4 +=head2 account_credits -=item * L +Type: has_many -=back +Related object: L =cut -__PACKAGE__->set_primary_key("borrowernumber"); - -=head1 UNIQUE CONSTRAINTS - -=head2 C +__PACKAGE__->has_many( + "account_credits", + "Koha::Schema::Result::AccountCredit", + { "foreign.borrowernumber" => "self.borrowernumber" }, + { cascade_copy => 0, cascade_delete => 0 }, +); -=over 4 +=head2 account_debits -=item * L +Type: has_many -=back +Related object: L =cut -__PACKAGE__->add_unique_constraint("cardnumber", ["cardnumber"]); - -=head1 RELATIONS +__PACKAGE__->has_many( + "account_debits", + "Koha::Schema::Result::AccountDebit", + { "foreign.borrowernumber" => "self.borrowernumber" }, + { cascade_copy => 0, cascade_delete => 0 }, +); =head2 accountlines @@ -696,34 +708,34 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); -=head2 branchcode +=head2 categorycode Type: belongs_to -Related object: L +Related object: L =cut __PACKAGE__->belongs_to( - "branchcode", - "Koha::Schema::Result::Branch", - { branchcode => "branchcode" }, - { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, + "categorycode", + "Koha::Schema::Result::Category", + { categorycode => "categorycode" }, + { on_delete => "CASCADE", on_update => "CASCADE" }, ); -=head2 categorycode +=head2 branchcode Type: belongs_to -Related object: L +Related object: L =cut __PACKAGE__->belongs_to( - "categorycode", - "Koha::Schema::Result::Category", - { categorycode => "categorycode" }, - { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, + "branchcode", + "Koha::Schema::Result::Branch", + { branchcode => "branchcode" }, + { on_delete => "CASCADE", on_update => "CASCADE" }, ); =head2 course_instructors @@ -1041,40 +1053,15 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); -=head2 basketnoes - -Type: many_to_many - -Composing rels: L -> basketno - -=cut - -__PACKAGE__->many_to_many("basketnoes", "aqbasketusers", "basketno"); - -=head2 budgets - -Type: many_to_many - -Composing rels: L -> budget - -=cut - -__PACKAGE__->many_to_many("budgets", "aqbudgetborrowers", "budget"); - -=head2 courses - -Type: many_to_many -Composing rels: L -> course - -=cut - -__PACKAGE__->many_to_many("courses", "course_instructors", "course"); - - -# Created by DBIx::Class::Schema::Loader v0.07025 @ 2013-10-31 16:31:19 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:z4kW3xYX1CyrwvGdZu32nA +# Created by DBIx::Class::Schema::Loader v0.07000 @ 2013-11-12 08:27:25 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CcXsGi7pVHQtO+YH3pY/1A +__PACKAGE__->belongs_to( + "branch", + "Koha::Schema::Result::Branch", + { branchcode => "branchcode" }, +); # You can replace this text with custom content, and it will be preserved on regeneration 1; diff --git a/Koha/Schema/Result/Deleteditem.pm b/Koha/Schema/Result/Deleteditem.pm index be28986..9112ff2 100644 --- a/Koha/Schema/Result/Deleteditem.pm +++ b/Koha/Schema/Result/Deleteditem.pm @@ -367,6 +367,17 @@ __PACKAGE__->set_primary_key("itemnumber"); # Created by DBIx::Class::Schema::Loader v0.07025 @ 2013-10-14 20:56:21 # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dfUPy7ijJ/uh9+0AqKjSBw +__PACKAGE__->belongs_to( + "biblio", + "Koha::Schema::Result::Biblio", + { biblionumber => "biblionumber" } +); + +__PACKAGE__->belongs_to( + "deleted_biblio", + "Koha::Schema::Result::Deletedbiblio", + { biblionumber => "biblionumber" } +); # You can replace this text with custom content, and it will be preserved on regeneration 1; diff --git a/Koha/Schema/Result/Issue.pm b/Koha/Schema/Result/Issue.pm index 282c802..d5ee1b1 100644 --- a/Koha/Schema/Result/Issue.pm +++ b/Koha/Schema/Result/Issue.pm @@ -1,21 +1,17 @@ -use utf8; package Koha::Schema::Result::Issue; # Created by DBIx::Class::Schema::Loader # DO NOT MODIFY THE FIRST PART OF THIS FILE -=head1 NAME - -Koha::Schema::Result::Issue - -=cut - use strict; use warnings; use base 'DBIx::Class::Core'; -=head1 TABLE: C + +=head1 NAME + +Koha::Schema::Result::Issue =cut @@ -23,6 +19,12 @@ __PACKAGE__->table("issues"); =head1 ACCESSORS +=head2 issue_id + + data_type: 'integer' + is_auto_increment: 1 + is_nullable: 0 + =head2 borrowernumber data_type: 'integer' @@ -38,7 +40,6 @@ __PACKAGE__->table("issues"); =head2 date_due data_type: 'datetime' - datetime_undef_if_invalid: 1 is_nullable: 1 =head2 branchcode @@ -56,13 +57,11 @@ __PACKAGE__->table("issues"); =head2 returndate data_type: 'datetime' - datetime_undef_if_invalid: 1 is_nullable: 1 =head2 lastreneweddate data_type: 'datetime' - datetime_undef_if_invalid: 1 is_nullable: 1 =head2 return @@ -79,63 +78,47 @@ __PACKAGE__->table("issues"); =head2 timestamp data_type: 'timestamp' - datetime_undef_if_invalid: 1 default_value: current_timestamp is_nullable: 0 =head2 issuedate data_type: 'datetime' - datetime_undef_if_invalid: 1 is_nullable: 1 =cut __PACKAGE__->add_columns( + "issue_id", + { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, "borrowernumber", { data_type => "integer", is_foreign_key => 1, is_nullable => 1 }, "itemnumber", { data_type => "integer", is_foreign_key => 1, is_nullable => 1 }, "date_due", - { - data_type => "datetime", - datetime_undef_if_invalid => 1, - is_nullable => 1, - }, + { data_type => "datetime", is_nullable => 1 }, "branchcode", { data_type => "varchar", is_nullable => 1, size => 10 }, "issuingbranch", { data_type => "varchar", is_nullable => 1, size => 18 }, "returndate", - { - data_type => "datetime", - datetime_undef_if_invalid => 1, - is_nullable => 1, - }, + { data_type => "datetime", is_nullable => 1 }, "lastreneweddate", - { - data_type => "datetime", - datetime_undef_if_invalid => 1, - is_nullable => 1, - }, + { data_type => "datetime", is_nullable => 1 }, "return", { data_type => "varchar", is_nullable => 1, size => 4 }, "renewals", { data_type => "tinyint", is_nullable => 1 }, "timestamp", { - data_type => "timestamp", - datetime_undef_if_invalid => 1, + data_type => "timestamp", default_value => \"current_timestamp", - is_nullable => 0, + is_nullable => 0, }, "issuedate", - { - data_type => "datetime", - datetime_undef_if_invalid => 1, - is_nullable => 1, - }, + { data_type => "datetime", is_nullable => 1 }, ); +__PACKAGE__->set_primary_key("issue_id"); =head1 RELATIONS @@ -151,12 +134,7 @@ __PACKAGE__->belongs_to( "borrowernumber", "Koha::Schema::Result::Borrower", { borrowernumber => "borrowernumber" }, - { - is_deferrable => 1, - join_type => "LEFT", - on_delete => "CASCADE", - on_update => "CASCADE", - }, + { join_type => "LEFT", on_delete => "CASCADE", on_update => "CASCADE" }, ); =head2 itemnumber @@ -171,23 +149,25 @@ __PACKAGE__->belongs_to( "itemnumber", "Koha::Schema::Result::Item", { itemnumber => "itemnumber" }, - { - is_deferrable => 1, - join_type => "LEFT", - on_delete => "CASCADE", - on_update => "CASCADE", - }, + { join_type => "LEFT", on_delete => "CASCADE", on_update => "CASCADE" }, ); -# Created by DBIx::Class::Schema::Loader v0.07025 @ 2013-10-14 20:56:21 -# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZEh31EKBmURMKxDxI+H3EA +# Created by DBIx::Class::Schema::Loader v0.07000 @ 2013-11-12 09:32:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:zBewWFig+yZYtSkcIxCZpg __PACKAGE__->belongs_to( "borrower", "Koha::Schema::Result::Borrower", { borrowernumber => "borrowernumber" }, - { join_type => "LEFT", on_delete => "CASCADE", on_update => "CASCADE" }, + { join_type => "LEFT" }, +); + +__PACKAGE__->belongs_to( + "item", + "Koha::Schema::Result::Item", + { itemnumber => "itemnumber" }, + { join_type => "LEFT" }, ); 1; diff --git a/Koha/Schema/Result/OldIssue.pm b/Koha/Schema/Result/OldIssue.pm index 4050f06..e1e7e69 100644 --- a/Koha/Schema/Result/OldIssue.pm +++ b/Koha/Schema/Result/OldIssue.pm @@ -183,6 +183,32 @@ __PACKAGE__->belongs_to( # Created by DBIx::Class::Schema::Loader v0.07025 @ 2013-10-14 20:56:21 # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:uPOxNROoMMRZ0qZsXsxEjA +__PACKAGE__->belongs_to( + "borrower", + "Koha::Schema::Result::Borrower", + { borrowernumber => "borrowernumber" }, + { join_type => "LEFT" }, +); + +__PACKAGE__->belongs_to( + "item", + "Koha::Schema::Result::Item", + { itemnumber => "itemnumber" }, + { join_type => "LEFT" }, +); + +__PACKAGE__->belongs_to( + "deletedborrower", + "Koha::Schema::Result::Deletedborrower", + { borrowernumber => "borrowernumber" }, + { join_type => "LEFT" }, +); + +__PACKAGE__->belongs_to( + "deleteditem", + "Koha::Schema::Result::Deleteditem", + { itemnumber => "itemnumber" }, + { join_type => "LEFT" }, +); -# You can replace this text with custom content, and it will be preserved on regeneration 1; diff --git a/Koha/Template/Plugin/Currency.pm b/Koha/Template/Plugin/Currency.pm new file mode 100644 index 0000000..7c0b19c --- /dev/null +++ b/Koha/Template/Plugin/Currency.pm @@ -0,0 +1,90 @@ +package Koha::Template::Plugin::Currency; + +# Copyright ByWater Solutions 2013 + +# 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 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. + +use Modern::Perl; + +use base qw( Template::Plugin::Filter ); + +use Locale::Currency::Format; + +use C4::Context; +use Koha::DateUtils; + +sub init { + my $self = shift; + $self->{ _DYNAMIC } = 1; + + my $active_currency = C4::Context->dbh->selectrow_hashref( + 'SELECT * FROM currency WHERE active = 1', {} ); + $self->{active_currency} = $active_currency; + + return $self; +} + +sub filter { + my ( $self, $amount, $args, $conf ) = @_; + + return $self->format( $amount, undef, $conf->{highlight} ); +} + +sub format { + my ( $self, $amount, $format, $highlight ) = @_; + + my $is_negative = $amount < 0; + $amount = abs( $amount ) if $highlight; + + # A negative debit is a credit and visa versa + if ($highlight) { + if ( $highlight eq 'debit' ) { + if ($is_negative) { + $highlight = 'credit'; + } + } + elsif ( $highlight eq 'credit' ) { + if ($is_negative) { + $highlight = 'debit'; + } + + } + elsif ( $highlight eq 'offset' ) { + $highlight = $is_negative ? 'credit' : 'debit'; + } + } + + my $formatted = currency_format( $self->{active_currency}->{currency}, + $amount, $format || FMT_HTML ); + + $formatted = "$formatted" if ( $highlight && $amount ); + + return $formatted; +} + +sub format_without_symbol { + my ( $self, $amount ) = @_; + + return substr( $self->format( $amount, FMT_SYMBOL ), 1, 0 ); +} + +sub symbol { + my ($self) = @_; + + return currency_symbol( $self->{active_currency}->{'currency'}, SYM_HTML ); +} + +1; diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index fefc985..4d5ec7b 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -265,6 +265,7 @@ CREATE TABLE `borrowers` ( -- this table includes information about your patrons `altcontactphone` varchar(50) default NULL, -- the phone number for the alternate contact for the patron/borrower `smsalertnumber` varchar(50) default NULL, -- the mobile phone number where the patron/borrower would like to receive notices (if SNS turned on) `privacy` integer(11) DEFAULT '1' NOT NULL, -- patron/borrower's privacy settings related to their reading history + `account_balance` decimal(28,6) NOT NULL, UNIQUE KEY `cardnumber` (`cardnumber`), PRIMARY KEY `borrowernumber` (`borrowernumber`), KEY `categorycode` (`categorycode`), @@ -3387,6 +3388,89 @@ CREATE TABLE IF NOT EXISTS marc_modification_template_actions ( CONSTRAINT `mmta_ibfk_1` FOREIGN KEY (`template_id`) REFERENCES `marc_modification_templates` (`template_id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +-- +-- Table structure for table 'account_credits' +-- +DROP TABLE IF EXISTS account_credits; +CREATE TABLE IF account_credits ( + credit_id int(11) NOT NULL AUTO_INCREMENT, + borrowernumber int(11) NOT NULL, + `type` varchar(255) NOT NULL, + amount_paid decimal(28,6) NOT NULL, + amount_remaining decimal(28,6) NOT NULL, + notes text, + manager_id int(11) DEFAULT NULL, + created_on timestamp NULL DEFAULT NULL, + updated_on timestamp NULL DEFAULT NULL, + PRIMARY KEY (credit_id), + KEY borrowernumber (borrowernumber) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +-- Constraints for table `account_credits` +-- +ALTER TABLE `account_credits` + ADD CONSTRAINT account_credits_ibfk_1 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Table structure for table 'account_debits' +-- + +DROP TABLE IF EXISTS account_debits; +CREATE TABLE account_debits ( + debit_id int(11) NOT NULL AUTO_INCREMENT, + borrowernumber int(11) NOT NULL DEFAULT '0', + itemnumber int(11) DEFAULT NULL, + issue_id int(11) DEFAULT NULL, + `type` varchar(255) NOT NULL, + accruing tinyint(1) NOT NULL DEFAULT '0', + amount_original decimal(28,6) DEFAULT NULL, + amount_outstanding decimal(28,6) DEFAULT NULL, + amount_last_increment decimal(28,6) DEFAULT NULL, + description mediumtext, + notes text, + manager_id int(11) DEFAULT NULL, + created_on timestamp NULL DEFAULT NULL, + updated_on timestamp NULL DEFAULT NULL, + PRIMARY KEY (debit_id), + KEY acctsborridx (borrowernumber), + KEY itemnumber (itemnumber), + KEY borrowernumber (borrowernumber), + KEY issue_id (issue_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +-- Constraints for table `account_debits` +-- +ALTER TABLE `account_debits` + ADD CONSTRAINT account_debits_ibfk_1 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE; + +-- +-- Table structure for table 'account_offsets' +-- + +DROP TABLE IF EXISTS account_offsets; +CREATE TABLE account_offsets ( + offset_id int(11) NOT NULL AUTO_INCREMENT, + debit_id int(11) DEFAULT NULL, + credit_id int(11) DEFAULT NULL, + `type` varchar(255) DEFAULT NULL, + amount decimal(28,6) NOT NULL, + created_on timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (offset_id), + KEY fee_id (debit_id), + KEY payment_id (credit_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +-- Constraints for table `account_offsets` +-- +ALTER TABLE `account_offsets` + ADD CONSTRAINT account_offsets_ibfk_1 FOREIGN KEY (debit_id) REFERENCES account_debits (debit_id) ON DELETE CASCADE ON UPDATE CASCADE, + ADD CONSTRAINT account_offsets_ibfk_2 FOREIGN KEY (credit_id) REFERENCES account_credits (credit_id) ON DELETE CASCADE ON UPDATE CASCADE; + +>>>>>>> 0c386fa... Bug 6427 - Rewrite of the accounts system - WIP + /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl index 6162bc5..5c16574 100755 --- a/installer/data/mysql/updatedatabase.pl +++ b/installer/data/mysql/updatedatabase.pl @@ -7259,6 +7259,7 @@ if ( CheckVersion($DBversion) ) { $dbh->{AutoCommit} = 1; $dbh->{RaiseError} = 0; + SetVersion ($DBversion); } $DBversion = "3.13.00.031"; @@ -7743,6 +7744,85 @@ if(CheckVersion($DBversion)) { SetVersion($DBversion); } +$DBversion = "3.15.00.XXX"; +if ( CheckVersion($DBversion) ) { + $dbh->do("ALTER TABLE issues ADD issue_id INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST"); + $dbh->do("ALTER TABLE old_issues ADD issue_id INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST"); + $dbh->do(" + CREATE TABLE IF NOT EXISTS account_credits ( + credit_id int(11) NOT NULL AUTO_INCREMENT, + borrowernumber int(11) NOT NULL, + `type` varchar(255) NOT NULL, + amount_paid decimal(28,6) NOT NULL, + amount_remaining decimal(28,6) NOT NULL, + notes text, + manager_id int(11) DEFAULT NULL, + created_on timestamp NULL DEFAULT NULL, + updated_on timestamp NULL DEFAULT NULL, + PRIMARY KEY (credit_id), + KEY borrowernumber (borrowernumber) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + "); + $dbh->do(" + CREATE TABLE IF NOT EXISTS account_debits ( + debit_id int(11) NOT NULL AUTO_INCREMENT, + borrowernumber int(11) NOT NULL DEFAULT '0', + itemnumber int(11) DEFAULT NULL, + issue_id int(11) DEFAULT NULL, + `type` varchar(255) NOT NULL, + accruing tinyint(1) NOT NULL DEFAULT '0', + amount_original decimal(28,6) DEFAULT NULL, + amount_outstanding decimal(28,6) DEFAULT NULL, + amount_last_increment decimal(28,6) DEFAULT NULL, + description mediumtext, + notes text, + manager_id int(11) DEFAULT NULL, + created_on timestamp NULL DEFAULT NULL, + updated_on timestamp NULL DEFAULT NULL, + PRIMARY KEY (debit_id), + KEY acctsborridx (borrowernumber), + KEY itemnumber (itemnumber), + KEY borrowernumber (borrowernumber), + KEY issue_id (issue_id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + "); + + $dbh->do(" + CREATE TABLE account_offsets ( + offset_id int(11) NOT NULL AUTO_INCREMENT, + debit_id int(11) DEFAULT NULL, + credit_id int(11) DEFAULT NULL, + `type` varchar(255) DEFAULT NULL, + amount decimal(28,6) NOT NULL COMMENT 'A positive number here represents a payment, a negative is a increase in a fine.', + created_on timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (offset_id), + KEY fee_id (debit_id), + KEY payment_id (credit_id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + "); + + $dbh->do(" + ALTER TABLE `account_credits` + ADD CONSTRAINT account_credits_ibfk_1 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE; + "); + $dbh->do(" + ALTER TABLE `account_debits` + ADD CONSTRAINT account_debits_ibfk_1 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE; + "); + $dbh->do(" + ALTER TABLE `account_offsets` + ADD CONSTRAINT account_offsets_ibfk_1 FOREIGN KEY (debit_id) REFERENCES account_debits (debit_id) ON DELETE CASCADE ON UPDATE CASCADE, + ADD CONSTRAINT account_offsets_ibfk_2 FOREIGN KEY (credit_id) REFERENCES account_credits (credit_id) ON DELETE CASCADE ON UPDATE CASCADE; + "); + + $dbh->do(" + ALTER TABLE borrowers ADD account_balance DECIMAL( 28, 6 ) NOT NULL; + "); + + print "Upgrade to $DBversion done ( Bug 6427 - Rewrite of the accounts system )\n"; + SetVersion ($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc index 62db1c5..f5b2706 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc @@ -67,7 +67,7 @@ [% IF ( detailview ) %]
  • [% ELSE %]
  • [% END %]Details
  • [% END %] [% IF ( CAN_user_updatecharges ) %] - [% IF ( finesview ) %]
  • [% ELSE %]
  • [% END %]Fines
  • + [% IF ( finesview ) %]
  • [% ELSE %]
  • [% END %]Fines
  • [% END %] [% IF ( RoutingSerials ) %][% IF ( routinglistview ) %]
  • [% ELSE %]
  • [% END %]Routing lists
  • [% END %] [% IF ( intranetreadinghistory ) %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.tt b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.tt index 7f8cb48..bffe929 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.tt @@ -70,7 +70,7 @@ in the global namespace %] [% IF ( detailview ) %]
  • [% ELSE %]
  • [% END %]Details
  • [% END %] [% IF ( CAN_user_updatecharges ) %] - [% IF ( finesview ) %]
  • [% ELSE %]
  • [% END %]Fines
  • + [% IF ( finesview ) %]
  • [% ELSE %]
  • [% END %]Fines
  • [% END %] [% IF ( RoutingSerials ) %][% IF ( routinglistview ) %]
  • [% ELSE %]
  • [% END %]Routing lists
  • [% END %] [% IF ( intranetreadinghistory ) %][% IF ( readingrecordview ) %]
  • [% ELSE %]
  • [% END %]Circulation history
  • [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/members-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/members-menu.inc index 9bd5285..9208e17 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/members-menu.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/members-menu.inc @@ -4,7 +4,7 @@ [% IF ( circview ) %]
  • [% ELSE %]
  • [% END %]Check out
  • [% IF ( detailview ) %]
  • [% ELSE %]
  • [% END %]Details
  • [% IF ( CAN_user_updatecharges ) %] - [% IF ( finesview ) %]
  • [% ELSE %]
  • [% END %]Fines
  • + [% IF ( finesview ) %]
  • [% ELSE %]
  • [% END %]Fines
  • [% END %] [% IF ( intranetreadinghistory ) %] [% IF ( readingrecordview ) %]
  • [% ELSE %]
  • [% END %]Circulation history
  • diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt index 46b82a8..5bfbd58 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt @@ -1,5 +1,6 @@ [% USE KohaBranchName %] [% USE KohaDates %] +[% USE Currency %] [% IF ( export_remove_fields OR export_with_csv_profile ) %] [% SET exports_enabled = 1 %] [% END %] @@ -577,16 +578,16 @@ No patron matched [% message %] [% IF ( charges ) %]
  • - Fees & Charges: Patron has Outstanding fees & charges[% IF ( chargesamount ) %] of [% chargesamount %][% END %]. + Fees & Charges: Patron has Outstanding fees & charges[% IF ( chargesamount ) %] of [% chargesamount | $Currency %][% END %]. [% IF ( charges_is_blocker ) %] Checkouts are BLOCKED because fine balance is OVER THE LIMIT. [% END %] - Make payment
  • + Make payment [% END %] [% IF ( credits ) %]
  • - Credits: Patron has a credit[% IF ( creditsamount ) %] of [% creditsamount %][% END %] + Credits: Patron has a credit[% IF ( creditsamount ) %] of [% creditsamount | $Currency %][% END %]
  • [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt index fb30311..ffe4e0d 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt @@ -74,7 +74,7 @@ $(document).ready(function () { [% IF ( fines ) %]

    Patron has outstanding fines of [% fines %].

    -

    Make payment.

    +

    Make payment.

    [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/account.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/account.tt new file mode 100644 index 0000000..1e73c03 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/account.tt @@ -0,0 +1,418 @@ +[% USE KohaDates %] +[% USE Currency %] +[% INCLUDE 'doc-head-open.inc' %] +Koha › Patrons › Account for [% INCLUDE 'patron-title.inc' %] +[% INCLUDE 'doc-head-close.inc' %] + + + +[% INCLUDE 'datatables-strings.inc' %] + + + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'patron-search.inc' %] + + + +
    +
    +
    +
    + [% INCLUDE 'members-toolbar.inc' %] + +
    + +
    + +
    + +

    +

    Account balance: [% borrower.account_balance | $Currency %]

    +

    + +
    +
    + View payments + + + + + + + + + + + + + + + + + +
    Fees
    IDDescriptionTypeAmountOutsandingCreated onUpdated on
    +
    + +
    + View fees + + + + + + + + + + + + + + + + + +
    Payments
    IDNotesTypeAmountRemainingCreated onUpdated on
    +
    +
    +
    +
    +
    + +
    + [% INCLUDE 'circ-menu.inc' %] +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] + +[% BLOCK format_data %] + [% FOREACH key IN data.result_source.columns %] + [% IF key.match('^amount') %] + "[% key %]": "[% data.$key FILTER $Currency highlight => highlight %]", + [% ELSIF key.match('_on$') %] + "[% key %]": "[% data.$key | $KohaDates %]", + [% ELSE %] + "[% key %]": "[% data.$key %]", + [% END %] + [% END %] +[% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/account_credit.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/account_credit.tt new file mode 100644 index 0000000..3b5f941 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/account_credit.tt @@ -0,0 +1,91 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Borrowers › Create manual credit +[% INCLUDE 'doc-head-close.inc' %] + + + + [% INCLUDE 'header.inc' %] + [% INCLUDE 'patron-search.inc' %] + + + +
    +
    +
    +
    + [% INCLUDE 'members-toolbar.inc' %] + +
    + + +
    + +
    + + +
    + Manual credit + +
      +
    1. + + +
    2. + +
    3. + + +
    4. + +
    5. + + +
    6. + +
    7. + + +
    8. + +
    9. + + + Example: 5.00 +
    10. +
    + +
    + +
    + + Cancel +
    +
    + +
    +
    +
    +
    +
    + [% INCLUDE 'circ-menu.inc' %] +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/account_debit.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/account_debit.tt new file mode 100644 index 0000000..f9aaa16 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/account_debit.tt @@ -0,0 +1,108 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Borrowers › Create manual invoice +[% INCLUDE 'doc-head-close.inc' %] + + + + + [% INCLUDE 'header.inc' %] + [% INCLUDE 'patron-search.inc' %] + + + +
    +
    +
    +
    + [% INCLUDE 'members-toolbar.inc' %] + +
    + +
    + +
    + + +
    + Manual Invoice + +
      +
    1. + + +
    2. + + +
    3. + + +
    4. + +
    5. + + +
    6. + +
    7. + + +
    8. + +
    9. + + Example: 5.00 +
    10. + +
    +
    + +
    + + Cancel +
    + +
    + +
    +
    +
    +
    + +
    + [% INCLUDE 'circ-menu.inc' %] +
    + +
    +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/account_payment.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/account_payment.tt new file mode 100644 index 0000000..bcaeb64 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/account_payment.tt @@ -0,0 +1,200 @@ +[% USE Currency %] +[% INCLUDE 'doc-head-open.inc' %] +Koha › Patrons › Pay Fines for [% borrower.firstname %] [% borrower.surname %] +[% INCLUDE 'doc-head-close.inc' %] + + + + + [% INCLUDE 'header.inc' %] + [% INCLUDE 'patron-search.inc' %] + + + +
    +
    +
    +
    + [% INCLUDE 'members-toolbar.inc' borrowernumber=borrower.borrowernumber %] + +
    + + +
    + + [% IF ( debits ) %] +
    + + + +

    + + Select all + + + | + + + Clear all + +

    + + + + + + + + + + + + + + [% SET total_due = 0 %] + [% FOREACH d IN debits %] + [% SET total_due = total_due + d.amount_outstanding %] + + + + + + + + + + + + [% END %] + + + + + + + + + +
     DescriptionAccount typeOriginal amountAmount outstanding
    + + + [% d.description %] + + [% IF d.notes %] + ( [% d.notes %] ) + [% END %] + + [% d.type %] + + [% d.amount_original | $Currency %] + + + [% d.amount_outstanding | $Currency %] + +
    Total Due:[% total_due | $Currency %]
    + +
    +

    + + + + + +

    + +

    + + +

    + +

    + + +

    + +

    + + +

    +

    + +
    + + Cancel +
    + +
    + + [% ELSE %] +

    + [% borrower.firstname %] [% borrower.surname %] has no outstanding fines. +

    + [% END %] + +
    +
    +
    +
    + +
    + [% INCLUDE 'circ-menu.tt' %] +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/account_print.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/account_print.tt new file mode 100644 index 0000000..9845f64 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/account_print.tt @@ -0,0 +1,136 @@ +[% USE KohaDates %] +[% USE Currency %] +[% USE EncodeUTF8 %] +[% INCLUDE 'doc-head-open.inc' %] +Print Receipt for [% cardnumber %] +[% INCLUDE 'doc-head-close.inc' %] + + + + + +[% SET account = debit || credit %] +[% SET borrower = account.borrower %] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [% IF account.description %] + + + + + [% END %] + + [% IF credit %] + + + + + + + + + [% IF credit.account_offsets %] + + + + + + + + + + + + [% END %] + [% ELSIF debit %] + + + + + + + + + [% IF debit.account_offsets %] + + + + + + + + + + + + [% END %] + [% END %] + + + + [% IF credit.account_offsets %] + [% FOREACH ao IN credit.account_offsets %] + + + + + + + + + [% END %] + [% ELSIF debit.account_offsets %] + [% FOREACH ao IN debit.account_offsets %] + + + + + + + + + [% END %] + [% END %] + +
    + [% IF debit %] + Invoice + [% ELSIF credit %] + Payment receipt + [% END %] +
    + [% borrower.branch.branchname | $EncodeUTF8 %] +
    Name:[% borrower.firstname | $EncodeUTF8 %] [% borrower.surname | $EncodeUTF8 %]
    Card number:[% borrower.cardnumber %]
    Date:[% account.created_on | $KohaDates %]
    Description:[% account.description | $EncodeUTF8 %]
    Amount:[% credit.amount_paid | $Currency highlight => type %]
    Balance:[% credit.amount_remaining | $Currency highlight => type %]
    Fees paid
    DescriptionTypeAmountPaidOutstandingDate
    Amount:[% debit.amount_original | $Currency highlight => type %]
    Outstanding:[% debit.amount_outstanding | $Currency highlight => type %]
    Payments applied
    DateTypePaymentAppliedBalanceNotes
    [% ao.debit.description %][% ao.debit.type %][% ao.debit.amount_original | $Currency highlight => 'debit' %][% ao.amount | $Currency highlight => 'offset' %][% ao.debit.amount_outstanding | $Currency highlight => 'debit' %][% ao.debit.created_on | $KohaDates %]
    [% ao.credit.type %][% ao.credit.created_on | $KohaDates %][% ao.credit.amount_paid | $Currency highlight => 'credit' %][% ao.amount | $Currency highlight => 'offset' %][% ao.credit.amount_remaining | $Currency highlight => 'credit' %][% ao.credit.notes %]
    + +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/boraccount.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/boraccount.tt index d64c63b..e4d3500 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/boraccount.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/boraccount.tt @@ -14,15 +14,15 @@
    [% INCLUDE 'members-toolbar.inc' %] -
    +
    @@ -51,7 +51,7 @@ [% IF ( reverse_col ) %] [% IF ( account.payment ) %] - Reverse + Reverse [% ELSE %]   [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/mancredit.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/mancredit.tt index ff2b6b6..48d10ac 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/mancredit.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/mancredit.tt @@ -26,14 +26,14 @@ $(document).ready(function(){
    -
    +
    @@ -48,7 +48,7 @@ $(document).ready(function(){
  • Example: 5.00
  • -
    Cancel
    +
    Cancel
    diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/maninvoice.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/maninvoice.tt index 30a4e20..cb6f1cf 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/maninvoice.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/maninvoice.tt @@ -26,10 +26,10 @@ $(document).ready(function(){
    @@ -38,7 +38,7 @@ $(document).ready(function(){ ERROR an invalid itemnumber was entered, please hit back and try again [% END %] [% ELSE %] -
    +
    Manual Invoice
      @@ -71,7 +71,7 @@ type_fees['[% invoice_types_loo.authorised_value %]'] = "[% invoice_types_loo.li
    1. Example: 5.00
    -
    Cancel
    +
    Cancel
    [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/pay.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/pay.tt index 91f1609..da4599e 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/pay.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/pay.tt @@ -55,15 +55,15 @@ function enableCheckboxActions(){
    [% IF ( accounts ) %] -
    +

    Select all | Clear all

    @@ -135,7 +135,7 @@ function enableCheckboxActions(){ -Cancel +Cancel [% ELSE %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/paycollect.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/paycollect.tt index 9cec275..369b9ec 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/paycollect.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/paycollect.tt @@ -64,7 +64,7 @@ function moneyFormat(textObj) { [% INCLUDE 'header.inc' %] [% INCLUDE 'patron-search.inc' %] - +
    @@ -78,16 +78,16 @@ function moneyFormat(textObj) {
    @@ -150,10 +150,10 @@ function moneyFormat(textObj) { + Cancel
    [% ELSIF ( writeoff_individual ) %] -
    +
    Write off an individual fine @@ -190,7 +190,7 @@ function moneyFormat(textObj) {
    + Cancel
    [% ELSE %] @@ -218,7 +218,7 @@ function moneyFormat(textObj) { + Cancel
    [% END %]
    diff --git a/koha-tmpl/intranet-tmpl/prog/img/details_close.png b/koha-tmpl/intranet-tmpl/prog/img/details_close.png new file mode 100644 index 0000000000000000000000000000000000000000..fcc23c63e22f9883bef72c9e3adae3d5ddfb35cc GIT binary patch literal 841 zcmV-P1GfB$P)DA_on|3pY;aiIaw_AzX~% z0wEzG!5U~A62!y<6#_3!Y0@BA($@0c+jVBUFF+~kfo;CbzTKH`cV=g%b|^n=hzi67 zM5UV3FcF6cUCnu^p}pjrsWm6Ug}98UR-~ZFvWN`g0Fg%+2vP1rl3*$mmwr5rOIH=?}WDABr;u?U4@YDw`9eOu1C&^?L7Z zY;1fZBc9KT-2A-Q9Uc}tJv}1T-7R)sycF4)8NstzNldzAgSOWc2V?31C|17Z<>j$J zAke}0_aQSe0l9^R;<1c^#DEC_0!d$>Do3Jo15TlYl9m;-_3M7y&8+fGbP>p=udnZ^^3rY*Ry8&@-XZS}=H{ef zDUJ>}0_oCdYHA|eDvG0BC2ghK?XIQGd^|3d(Ko2a8%4c^+ z2RQuxqA|Bf6p=-Yhg81x*8ObQBJv5%iB0 zU9{XrXRDi=!8bAj&V~kvL?Uy+VDJ@gXo|Fa=^duiBKhvUSnKK%G5>85_4~!w&Q6gS z9~Xt4ozl{)vr6y0nuGJKj&rJgrBbPcMMU#|wJ-Lc4fdkiFZ`>)vh)56_#?mow%h=n ThIkt{00000NkvXXu0mjfW_^Rc literal 0 HcmV?d00001 diff --git a/koha-tmpl/intranet-tmpl/prog/img/details_open.png b/koha-tmpl/intranet-tmpl/prog/img/details_open.png new file mode 100644 index 0000000000000000000000000000000000000000..6f034d0f2d5c7902dce190355b12defdc07d6c9a GIT binary patch literal 881 zcmV-%1CIQOP)P5xVIgh(4gM3Zjdy!n~2N zjD^FnHQ1(tR(9cJl^-l~RyvtO-;dMt9M4;+6T9$?ALpFs)Rcr6nirw3t>UzAPx#r5M&8N9I=W>B2)xRbt)puL>xs_c64-H@%#Or zXf!&j>$*muHOWXvHo`VD#lg6MPE~+#^!N8aRTO1|?J`J5He@S&wvbBVO8K%ZpNm8y zAIT}HC7Cxi$gY3tW-XzMtU1`kx+go?tM3CWp(Z#n*^&?X9upjrBoaWeDh>vNcO4E# z)3UM*kH0>GH%mi2IAp}6%}CKyd$~2W6jvjYwyv(OhW7UM zP7O5aPWUzq$A-}#e>#Hw16_xihIJroxfm&IbSiP6!~SxS;g5FTo6ZM`H( zlIh)&4}38nXc|&$DyX^!-nt?1)(-JKW(QQ%jr|ydxA>>z)7I8@S=`cW5N4N`m!G77 zdZMpERgp|rK~d3JRsUp;HJ6lA@hNmr@UjvF zT!Ag6;H&rZ+37vY=g+HO_-wIQtVRQvx7QEFj83+*vi=TLQ+cY^m5t3Z!D|~jv(ZlR zc>I^mX3LW^WSCo>=P5lkaGKBKE#utS*o(YFK~4deqwi1AMVd+US~dit;|^yb25sIpdZA`Wys(O1dAvo2(I ziyc~u+*C?>Z*T8&(cem%afeVS6nDGb_L8EKvNL%#5MBx6)#tXj`d$yLwBMhA-vSH(T>k(8(cOU?00000NkvXX Hu0mjf8E% "members/account.tt", + query => $cgi, + type => "intranet", + authnotrequired => 0, + flagsrequired => { borrowers => 1, updatecharges => 1 }, + debug => 1, + } +); + +my $borrowernumber = $cgi->param('borrowernumber'); + +my $borrower = GetMember( 'borrowernumber' => $borrowernumber ); + +my @debits = Koha::Database->new()->schema->resultset('AccountDebit')->search( + { 'me.borrowernumber' => $borrowernumber }, + { prefetch => { account_offsets => 'credit' } } +); + +my @credits = Koha::Database->new()->schema->resultset('AccountCredit')->search( + { 'me.borrowernumber' => $borrowernumber }, + { prefetch => { account_offsets => 'debit' } } +); + +$template->param( + debits => \@debits, + credits => \@credits, + borrower => $borrower, +); + +# Standard /members/ borrower details data +## FIXME: This code is in every /members/ script and should be unified + +if ( $borrower->{'category_type'} eq 'C' ) { + my ( $catcodes, $labels ) = + GetborCatFromCatType( 'A', 'WHERE category_type = ?' ); + my $cnt = scalar(@$catcodes); + $template->param( 'CATCODE_MULTI' => 1 ) if $cnt > 1; + $template->param( 'catcode' => $catcodes->[0] ) if $cnt == 1; +} + +my ( $picture, $dberror ) = GetPatronImage( $borrower->{'borrowernumber'} ); +$template->param( picture => 1 ) if $picture; + +if ( C4::Context->preference('ExtendedPatronAttributes') ) { + my $attributes = GetBorrowerAttributes($borrowernumber); + $template->param( + ExtendedPatronAttributes => 1, + extendedattributes => $attributes + ); +} + +$template->param( + borrowernumber => $borrowernumber, + firstname => $borrower->{'firstname'}, + surname => $borrower->{'surname'}, + cardnumber => $borrower->{'cardnumber'}, + categorycode => $borrower->{'categorycode'}, + category_type => $borrower->{'category_type'}, + categoryname => $borrower->{'description'}, + address => $borrower->{'address'}, + address2 => $borrower->{'address2'}, + city => $borrower->{'city'}, + state => $borrower->{'state'}, + zipcode => $borrower->{'zipcode'}, + country => $borrower->{'country'}, + phone => $borrower->{'phone'}, + email => $borrower->{'email'}, + branchcode => $borrower->{'branchcode'}, + branchname => GetBranchName( $borrower->{'branchcode'} ), + is_child => ( $borrower->{'category_type'} eq 'C' ), + activeBorrowerRelationship => + ( C4::Context->preference('borrowerRelationship') ne '' ), + RoutingSerials => C4::Context->preference('RoutingSerials'), +); + +output_html_with_http_headers $cgi, $cookie, $template->output; diff --git a/members/account_credit.pl b/members/account_credit.pl new file mode 100755 index 0000000..976232a --- /dev/null +++ b/members/account_credit.pl @@ -0,0 +1,104 @@ +#!/usr/bin/perl + +#written 11/1/2000 by chris@katipo.oc.nz +#script to display borrowers account details + +# Copyright 2000-2002 Katipo Communications +# Copyright 2010 BibLibre +# +# 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 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. + +use strict; +use warnings; + +use C4::Auth; +use C4::Output; +use CGI; + +use C4::Koha; +use C4::Members; +use C4::Branch; +use C4::Accounts; +use C4::Items; +use C4::Members::Attributes qw(GetBorrowerAttributes); +use Koha::Database; + +my $cgi = new CGI; + +my $borrowernumber = $cgi->param('borrowernumber'); + +my $borrower = GetMember( 'borrowernumber' => $borrowernumber ); + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "members/account_credit.tt", + query => $cgi, + type => "intranet", + authnotrequired => 0, + flagsrequired => { borrowers => 1, updatecharges => 1 }, + debug => 1, + } +); + +$template->param( credit_types_loop => GetAuthorisedValues('ACCOUNT_CREDIT') ); + +# Standard /members/ borrower details data +## FIXME: This code is in every /members/ script and should be unified + +if ( $borrower->{'category_type'} eq 'C' ) { + my ( $catcodes, $labels ) = + GetborCatFromCatType( 'A', 'WHERE category_type = ?' ); + my $cnt = scalar(@$catcodes); + $template->param( 'CATCODE_MULTI' => 1 ) if $cnt > 1; + $template->param( 'catcode' => $catcodes->[0] ) if $cnt == 1; +} + +my ( $picture, $dberror ) = GetPatronImage( $borrower->{'borrowernumber'} ); +$template->param( picture => 1 ) if $picture; + +if ( C4::Context->preference('ExtendedPatronAttributes') ) { + my $attributes = GetBorrowerAttributes($borrowernumber); + $template->param( + ExtendedPatronAttributes => 1, + extendedattributes => $attributes + ); +} + +$template->param( + borrowernumber => $borrowernumber, + firstname => $borrower->{'firstname'}, + surname => $borrower->{'surname'}, + cardnumber => $borrower->{'cardnumber'}, + categorycode => $borrower->{'categorycode'}, + category_type => $borrower->{'category_type'}, + categoryname => $borrower->{'description'}, + address => $borrower->{'address'}, + address2 => $borrower->{'address2'}, + city => $borrower->{'city'}, + state => $borrower->{'state'}, + zipcode => $borrower->{'zipcode'}, + country => $borrower->{'country'}, + phone => $borrower->{'phone'}, + email => $borrower->{'email'}, + branchcode => $borrower->{'branchcode'}, + branchname => GetBranchName( $borrower->{'branchcode'} ), + is_child => ( $borrower->{'category_type'} eq 'C' ), + activeBorrowerRelationship => + ( C4::Context->preference('borrowerRelationship') ne '' ), + RoutingSerials => C4::Context->preference('RoutingSerials'), +); + +output_html_with_http_headers $cgi, $cookie, $template->output; diff --git a/members/account_credit_do.pl b/members/account_credit_do.pl new file mode 100755 index 0000000..c1979e8 --- /dev/null +++ b/members/account_credit_do.pl @@ -0,0 +1,68 @@ +#!/usr/bin/perl + +# Copyright 2000-2002 Katipo Communications +# Copyright 2010 BibLibre +# Copyright 2013 ByWater Solutions +# +# 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 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. + +use Modern::Perl; + +use CGI; + +use C4::Auth; +use C4::Output; +use C4::Members; +use C4::Items; +use C4::Branch; +use C4::Members::Attributes qw(GetBorrowerAttributes); +use Koha::Accounts; +use Koha::Database; + +my $cgi = new CGI; + +my $borrowernumber = $cgi->param('borrowernumber'); +my $borrower = + Koha::Database->new()->schema->resultset('Borrower')->find($borrowernumber); + +if ( checkauth( $cgi, 0, { borrowers => 1 }, 'intranet' ) ) { + + my $barcode = $cgi->param('barcode'); + my $itemnumber = $cgi->param('itemnumber'); + my $description = $cgi->param('description'); + my $amount = $cgi->param('amount'); + my $type = $cgi->param('type'); + my $notes = $cgi->param('notes'); + + if ( !$itemnumber && $barcode ) { + $itemnumber = GetItemnumberFromBarcode($barcode); + } + + my $debit = AddCredit( + { + borrower => $borrower, + amount => $amount, + type => $type, + itemnumber => $itemnumber, + description => $description, + notes => $notes, + + } + ); + + print $cgi->redirect( + "/cgi-bin/koha/members/account.pl?borrowernumber=$borrowernumber"); +} diff --git a/members/account_debit.pl b/members/account_debit.pl new file mode 100755 index 0000000..41d78c6 --- /dev/null +++ b/members/account_debit.pl @@ -0,0 +1,104 @@ +#!/usr/bin/perl + +#written 11/1/2000 by chris@katipo.oc.nz +#script to display borrowers account details + +# Copyright 2000-2002 Katipo Communications +# Copyright 2010 BibLibre +# +# 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 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. + +use strict; +use warnings; + +use CGI; + +use C4::Auth; +use C4::Output; +use C4::Members; +use C4::Items; +use C4::Branch; +use C4::Members::Attributes qw(GetBorrowerAttributes); +use C4::Koha; +use Koha::Accounts; +use Koha::Database; + +my $input = new CGI; + +my $borrowernumber = $input->param('borrowernumber'); + +my $borrower = GetMember( 'borrowernumber' => $borrowernumber ); + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "members/account_debit.tt", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { borrowers => 1, updatecharges => 1 }, + debug => 1, + } +); + +$template->param( invoice_types_loop => GetAuthorisedValues('MANUAL_INV') ); + +# Standard /members/ borrower details data +## FIXME: This code is in every /members/ script and should be unified + +if ( $borrower->{'category_type'} eq 'C' ) { + my ( $catcodes, $labels ) = + GetborCatFromCatType( 'A', 'WHERE category_type = ?' ); + my $cnt = scalar(@$catcodes); + $template->param( 'CATCODE_MULTI' => 1 ) if $cnt > 1; + $template->param( 'catcode' => $catcodes->[0] ) if $cnt == 1; +} + +my ( $picture, $dberror ) = GetPatronImage( $borrower->{'borrowernumber'} ); +$template->param( picture => 1 ) if $picture; + +if ( C4::Context->preference('ExtendedPatronAttributes') ) { + my $attributes = GetBorrowerAttributes($borrowernumber); + $template->param( + ExtendedPatronAttributes => 1, + extendedattributes => $attributes + ); +} + +$template->param( + borrowernumber => $borrowernumber, + firstname => $borrower->{'firstname'}, + surname => $borrower->{'surname'}, + cardnumber => $borrower->{'cardnumber'}, + categorycode => $borrower->{'categorycode'}, + category_type => $borrower->{'category_type'}, + categoryname => $borrower->{'description'}, + address => $borrower->{'address'}, + address2 => $borrower->{'address2'}, + city => $borrower->{'city'}, + state => $borrower->{'state'}, + zipcode => $borrower->{'zipcode'}, + country => $borrower->{'country'}, + phone => $borrower->{'phone'}, + email => $borrower->{'email'}, + branchcode => $borrower->{'branchcode'}, + branchname => GetBranchName( $borrower->{'branchcode'} ), + is_child => ( $borrower->{'category_type'} eq 'C' ), + activeBorrowerRelationship => + ( C4::Context->preference('borrowerRelationship') ne '' ), + RoutingSerials => C4::Context->preference('RoutingSerials'), +); + +output_html_with_http_headers $input, $cookie, $template->output; diff --git a/members/account_debit_do.pl b/members/account_debit_do.pl new file mode 100755 index 0000000..c9878fa --- /dev/null +++ b/members/account_debit_do.pl @@ -0,0 +1,69 @@ +#!/usr/bin/perl + +# Copyright 2000-2002 Katipo Communications +# Copyright 2010 BibLibre +# Copyright 2013 ByWater Solutions +# +# 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 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. + +use Modern::Perl; + +use CGI; + +use C4::Auth; +use C4::Output; +use C4::Members; +use C4::Items; +use C4::Branch; +use C4::Members::Attributes qw(GetBorrowerAttributes); +use Koha::Accounts; +use Koha::Database; + +my $cgi = new CGI; + +my $borrowernumber = $cgi->param('borrowernumber'); +my $borrower = + Koha::Database->new()->schema->resultset('Borrower')->find($borrowernumber); + +if ( checkauth( $cgi, 0, { borrowers => 1 }, 'intranet' ) ) { + + # print $cgi->header; + my $barcode = $cgi->param('barcode'); + my $itemnumber = $cgi->param('itemnumber'); + my $description = $cgi->param('description'); + my $amount = $cgi->param('amount'); + my $type = $cgi->param('type'); + my $notes = $cgi->param('notes'); + + if ( !$itemnumber && $barcode ) { + $itemnumber = GetItemnumberFromBarcode($barcode); + } + + my $debit = AddDebit( + { + borrower => $borrower, + amount => $amount, + type => $type, + itemnumber => $itemnumber, + description => $description, + notes => $notes, + + } + ); + + print $cgi->redirect( + "/cgi-bin/koha/members/account.pl?borrowernumber=$borrowernumber"); +} diff --git a/members/account_payment.pl b/members/account_payment.pl new file mode 100755 index 0000000..32fec34 --- /dev/null +++ b/members/account_payment.pl @@ -0,0 +1,123 @@ +#!/usr/bin/perl + +# Copyright 2000-2002 Katipo Communications +# Copyright 2010 BibLibre +# Copyright 2010,2011 PTFS-Europe Ltd +# Copyright 2013 ByWater Solutions +# +# 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 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. + +=head1 account_payment.pl + + written 11/1/2000 by chris@katipo.oc.nz + part of the koha library system, script to facilitate paying off fines + +=cut + +use Modern::Perl; + +use CGI; + +use URI::Escape; + +use C4::Context; +use C4::Auth; +use C4::Output; +use C4::Members; +use C4::Accounts; +use C4::Stats; +use C4::Koha; +use C4::Overdues; +use C4::Branch; +use C4::Members::Attributes qw(GetBorrowerAttributes); +use Koha::Database; + +our $cgi = CGI->new; + +our ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => 'members/account_payment.tt', + query => $cgi, + type => 'intranet', + authnotrequired => 0, + flagsrequired => { borrowers => 1, updatecharges => 1 }, + debug => 1, + } +); + +my $borrowernumber = $cgi->param('borrowernumber'); + +my $borrower = GetMember( borrowernumber => $borrowernumber ); + +my @debits = Koha::Database->new()->schema->resultset('AccountDebit')->search( + { + 'me.borrowernumber' => $borrowernumber, + amount_outstanding => { '>' => 0 } + } +); + +$template->param( + debits => \@debits, + borrower => $borrower, +); + +# Standard /members/ borrower details data +## FIXME: This code is in every /members/ script and should be unified + +if ( $borrower->{'category_type'} eq 'C' ) { + my ( $catcodes, $labels ) = + GetborCatFromCatType( 'A', 'WHERE category_type = ?' ); + my $cnt = scalar(@$catcodes); + $template->param( 'CATCODE_MULTI' => 1 ) if $cnt > 1; + $template->param( 'catcode' => $catcodes->[0] ) if $cnt == 1; +} + +my ( $picture, $dberror ) = GetPatronImage( $borrower->{'borrowernumber'} ); +$template->param( picture => 1 ) if $picture; + +if ( C4::Context->preference('ExtendedPatronAttributes') ) { + my $attributes = GetBorrowerAttributes($borrowernumber); + $template->param( + ExtendedPatronAttributes => 1, + extendedattributes => $attributes + ); +} + +$template->param( + borrowernumber => $borrowernumber, + firstname => $borrower->{'firstname'}, + surname => $borrower->{'surname'}, + cardnumber => $borrower->{'cardnumber'}, + categorycode => $borrower->{'categorycode'}, + category_type => $borrower->{'category_type'}, + categoryname => $borrower->{'description'}, + address => $borrower->{'address'}, + address2 => $borrower->{'address2'}, + city => $borrower->{'city'}, + state => $borrower->{'state'}, + zipcode => $borrower->{'zipcode'}, + country => $borrower->{'country'}, + phone => $borrower->{'phone'}, + email => $borrower->{'email'}, + branchcode => $borrower->{'branchcode'}, + branchname => GetBranchName( $borrower->{'branchcode'} ), + is_child => ( $borrower->{'category_type'} eq 'C' ), + activeBorrowerRelationship => + ( C4::Context->preference('borrowerRelationship') ne '' ), + RoutingSerials => C4::Context->preference('RoutingSerials'), +); + +output_html_with_http_headers $cgi, $cookie, $template->output; diff --git a/members/account_payment_do.pl b/members/account_payment_do.pl new file mode 100755 index 0000000..cc60099 --- /dev/null +++ b/members/account_payment_do.pl @@ -0,0 +1,62 @@ +#!/usr/bin/perl + +# Copyright 2000-2002 Katipo Communications +# Copyright 2010 BibLibre +# Copyright 2013 ByWater Solutions +# +# 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 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. + +use Modern::Perl; + +use CGI; + +use C4::Auth; +use C4::Members; +use C4::Items; +use C4::Branch; +use C4::Members::Attributes qw(GetBorrowerAttributes); +use Koha::Accounts; +use Koha::Database; + +my $cgi = new CGI; + +if ( checkauth( $cgi, 0, { borrowers => 1 }, 'intranet' ) ) { + my $borrowernumber = $cgi->param('borrowernumber'); + + my $borrower = + Koha::Database->new()->schema->resultset('Borrower') + ->find($borrowernumber); + + my $amount_to_pay = $cgi->param('amount_to_pay'); + my $amount_received = $cgi->param('amount_received'); + my $type = $cgi->param('type'); + my $notes = $cgi->param('notes'); + my @debit_id = $cgi->param('debit_id'); + + my $debit = AddCredit( + { + borrower => $borrower, + amount => $amount_to_pay, + type => $type, + notes => $notes, + debit_id => \@debit_id, + + } + ); + + print $cgi->redirect( + "/cgi-bin/koha/members/account.pl?borrowernumber=$borrowernumber"); +} diff --git a/members/account_print.pl b/members/account_print.pl new file mode 100755 index 0000000..0f02ca9 --- /dev/null +++ b/members/account_print.pl @@ -0,0 +1,58 @@ +#!/usr/bin/perl + +# 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 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. + +use Modern::Perl; + +use CGI; + +use C4::Auth; +use C4::Output; +use Koha::Database; + +my $cgi = new CGI; + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "members/account_print.tt", + query => $cgi, + type => "intranet", + authnotrequired => 0, + flagsrequired => { borrowers => 1, updatecharges => 1 }, + debug => 1, + } +); + +my $type = $cgi->param('type'); +my $id = $cgi->param('id'); + +warn "No type passed in!" unless $type; +warn "No id passed in!" unless $id; + +if ( $type eq 'debit' ) { + my $debit = + Koha::Database->new()->schema->resultset('AccountDebit')->find($id); + $template->param( debit => $debit ); +} +elsif ( $type eq 'credit' ) { + my $credit = + Koha::Database->new()->schema->resultset('AccountCredit')->find($id); + $template->param( credit => $credit ); +} + +$template->param( type => $type ); + +output_html_with_http_headers $cgi, $cookie, $template->output; diff --git a/members/mancredit.pl b/members/mancredit.pl index a2c2258..03079ae 100755 --- a/members/mancredit.pl +++ b/members/mancredit.pl @@ -57,7 +57,7 @@ if ($add){ $amount = -$amount; my $type = $input->param('type'); manualinvoice( $borrowernumber, $itemnum, $desc, $type, $amount, $note ); - print $input->redirect("/cgi-bin/koha/members/boraccount.pl?borrowernumber=$borrowernumber"); + print $input->redirect("/cgi-bin/koha/members/account.pl?borrowernumber=$borrowernumber"); } } else { my ($template, $loggedinuser, $cookie) diff --git a/members/maninvoice.pl b/members/maninvoice.pl index c4a9294..4a7cce5 100755 --- a/members/maninvoice.pl +++ b/members/maninvoice.pl @@ -72,7 +72,7 @@ if ($add){ $template->param( 'ERROR' => $error ); output_html_with_http_headers $input, $cookie, $template->output; } else { - print $input->redirect("/cgi-bin/koha/members/boraccount.pl?borrowernumber=$borrowernumber"); + print $input->redirect("/cgi-bin/koha/members/account.pl?borrowernumber=$borrowernumber"); exit; } } diff --git a/members/pay.pl b/members/pay.pl index 8a8e902..5c95c5e 100755 --- a/members/pay.pl +++ b/members/pay.pl @@ -19,7 +19,7 @@ # with Koha; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -=head1 pay.pl +=head1 account_payment.pl written 11/1/2000 by chris@katipo.oc.nz part of the koha library system, script to facilitate paying off fines @@ -197,7 +197,7 @@ sub writeoff_all { $borrowernumber = $input->param('borrowernumber'); print $input->redirect( - "/cgi-bin/koha/members/boraccount.pl?borrowernumber=$borrowernumber"); + "/cgi-bin/koha/members/account.pl?borrowernumber=$borrowernumber"); return; } diff --git a/members/paycollect.pl b/members/paycollect.pl index 96ca0fb..48f1d0a 100755 --- a/members/paycollect.pl +++ b/members/paycollect.pl @@ -114,7 +114,7 @@ if ( $total_paid and $total_paid ne '0.00' ) { $user, $branch, $payment_note ); } print $input->redirect( - "/cgi-bin/koha/members/pay.pl?borrowernumber=$borrowernumber"); + "/cgi-bin/koha/members/account_payment.pl?borrowernumber=$borrowernumber"); } else { if ($select) { if ( $select =~ /^([\d,]*).*/ ) { @@ -130,7 +130,7 @@ if ( $total_paid and $total_paid ne '0.00' ) { # recordpayment does not return success or failure so lets redisplay the boraccount print $input->redirect( -"/cgi-bin/koha/members/boraccount.pl?borrowernumber=$borrowernumber" +"/cgi-bin/koha/members/account.pl?borrowernumber=$borrowernumber" ); } } diff --git a/members/printfeercpt.pl b/members/printfeercpt.pl index 6a86bc3..8410944 100755 --- a/members/printfeercpt.pl +++ b/members/printfeercpt.pl @@ -1,7 +1,7 @@ #!/usr/bin/perl -#writen 3rd May 2010 by kmkale@anantcorp.com adapted from boraccount.pl by chris@katipo.oc.nz +#writen 3rd May 2010 by kmkale@anantcorp.com adapted from account.pl by chris@katipo.oc.nz #script to print fee receipts diff --git a/members/printinvoice.pl b/members/printinvoice.pl index 10ae114..cdbaa14 100755 --- a/members/printinvoice.pl +++ b/members/printinvoice.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl -#writen 3rd May 2010 by kmkale@anantcorp.com adapted from boraccount.pl by chris@katipo.oc.nz +#writen 3rd May 2010 by kmkale@anantcorp.com adapted from account.pl by chris@katipo.oc.nz #script to print fee receipts # Copyright Koustubha Kale diff --git a/misc/cronjobs/create_koc_db.pl b/misc/cronjobs/create_koc_db.pl index ccc4a5b..f49519e 100755 --- a/misc/cronjobs/create_koc_db.pl +++ b/misc/cronjobs/create_koc_db.pl @@ -256,10 +256,8 @@ SELECT borrowernumber, city, phone, dateofbirth, - sum( accountlines.amountoutstanding ) as total_fines + account_balance as total_fines FROM borrowers -LEFT JOIN accountlines USING (borrowernumber) -GROUP BY borrowernumber; END_SQL my $fields_count = $sth_mysql->execute(); @@ -278,43 +276,6 @@ END_SQL } $dbh_sqlite->commit(); print "inserted $count borrowers\n" if $verbose; - # add_fines_to_borrowers_table(); -} - -=head2 add_fines_to_borrowers_table - -Import the fines from koha.accountlines into the sqlite db - -=cut - -sub add_fines_to_borrowers_table { - - print "preparing to update borrowers\n" if $verbose; - my $sth_mysql = $dbh_mysql->prepare( - "SELECT DISTINCT borrowernumber, SUM( amountoutstanding ) AS total_fines - FROM accountlines - GROUP BY borrowernumber" - ); - $sth_mysql->execute(); - my $count; - while ( my $result = $sth_mysql->fetchrow_hashref() ) { - $count++; - if ( $verbose ) { - print '.' unless ( $count % 10 ); - print "$count\n" unless ( $count % 1000 ); - } - - my $borrowernumber = $result->{'borrowernumber'}; - my $total_fines = $result->{'total_fines'}; - - # warn "Fines for Borrower # $borrowernumber are \$ $total_fines \n" if $verbose; - my $sql = "UPDATE borrowers SET total_fines = ? WHERE borrowernumber = ?"; - - my $sth_sqlite = $dbh_sqlite->prepare($sql); - $sth_sqlite->execute( $total_fines, $borrowernumber ); - $sth_sqlite->finish(); - } - print "updated $count borrowers\n" if ( $verbose && $count ); } =head2 create_issue_table diff --git a/misc/cronjobs/fines.pl b/misc/cronjobs/fines.pl index 02bde14..f85b898 100755 --- a/misc/cronjobs/fines.pl +++ b/misc/cronjobs/fines.pl @@ -127,9 +127,13 @@ for my $overdue ( @{$overdues} ) { if ( $mode eq 'production' && !$is_holiday{$branchcode} ) { if ( $amount > 0 ) { UpdateFine( - $overdue->{itemnumber}, - $overdue->{borrowernumber}, - $amount, $type, output_pref($datedue) + { + itemnumber => $overdue->{itemnumber}, + borrowernumber => $overdue->{borrowernumber}, + amount => $amount, + due => output_pref($datedue), + issue_id => $overdue->{issue_id} + } ); } } diff --git a/misc/release_notes/release_notes_3_10_0.txt b/misc/release_notes/release_notes_3_10_0.txt index a1ebdef..5a44aaf 100644 --- a/misc/release_notes/release_notes_3_10_0.txt +++ b/misc/release_notes/release_notes_3_10_0.txt @@ -1762,7 +1762,7 @@ Staff Client 8996 normal In result page items with negative notforloan are available 9017 normal Quote of the day: Table footer not translated 5312 minor XHTML correction in authority summary - 8009 minor Item descriptive data not populated on pay.pl + 8009 minor Item descriptive data not populated on account_payment.pl 8593 minor Add unique IDs to pending approval markup on staff client home page 8646 minor Certain search terms cause browser "script taking too long" error 8793 minor Fix materialTypeCode/typeOf008 icons for NORMARC XSLT diff --git a/misc/release_notes/release_notes_3_12_0.txt b/misc/release_notes/release_notes_3_12_0.txt index 68d19e5..7a6db13 100644 --- a/misc/release_notes/release_notes_3_12_0.txt +++ b/misc/release_notes/release_notes_3_12_0.txt @@ -579,7 +579,7 @@ Architecture, internals, and plumbing 8429 minor Unnecessary use of Exporter in SIP/ILS objects 9292 minor Remove dead code related to 'publictype' 9401 minor Javascript used for tags handling wants access to CGISESSID cookie - 9582 minor Unused code in members/pay.pl + 9582 minor Unused code in members/account_payment.pl 10054 minor When SingleBranchMode is enabled, allow superlibrarians to set logged in library 10143 minor Fix FSF address in license headers 9609 trivial Rebuild zebra reports double numbers for exported records with -z option diff --git a/t/db_dependent/Accounts.t b/t/db_dependent/Accounts.t index 1c2dd5a..7957cbe 100644 --- a/t/db_dependent/Accounts.t +++ b/t/db_dependent/Accounts.t @@ -1,16 +1,199 @@ #!/usr/bin/perl # -# This Koha test module is a stub! +# This Koha test module is a stub! # Add more tests here!!! use strict; use warnings; -use Test::More tests => 1; +use Test::More tests => 19; + +use C4::Context; BEGIN { - use_ok('C4::Accounts'); + use_ok('Koha::Database'); + use_ok('Koha::Accounts'); + use_ok('Koha::Accounts::DebitTypes'); + use_ok('Koha::Accounts::CreditTypes'); } +## Intial Setup ## +my $borrower = Koha::Database->new()->schema->resultset('Borrower')->create( + { + surname => 'Test', + categorycode => 'S', + branchcode => 'MPL', + account_balance => 0, + } +); + +my $biblio = + Koha::Database->new()->schema->resultset('Biblio') + ->create( { title => "Test Record" } ); +my $biblioitem = + Koha::Database->new()->schema->resultset('Biblioitem') + ->create( { biblionumber => $biblio->biblionumber() } ); +my $item = Koha::Database->new()->schema->resultset('Item')->create( + { + biblionumber => $biblio->biblionumber(), + biblioitemnumber => $biblioitem->biblioitemnumber(), + replacementprice => 25.00, + barcode => q{TEST_ITEM_BARCODE} + } +); + +my $issue = Koha::Database->new()->schema->resultset('Issue')->create( + { + borrowernumber => $borrower->borrowernumber(), + itemnumber => $item->itemnumber(), + } +); +## END initial setup + +ok( Koha::Accounts::DebitTypes::Fine eq 'FINE', 'Test DebitTypes::Fine' ); +ok( Koha::Accounts::DebitTypes::Lost eq 'LOST', 'Test DebitTypes::Lost' ); +ok( + Koha::Accounts::DebitTypes::IsValid('FINE'), + 'Test DebitTypes::IsValid with valid debit type' +); +ok( + !Koha::Accounts::DebitTypes::IsValid('Not A Valid Fee Type'), + 'Test DebitTypes::IsValid with an invalid debit type' +); +my $authorised_value = + Koha::Database->new()->schema->resultset('AuthorisedValue')->create( + { + category => 'MANUAL_INV', + authorised_value => 'TEST', + lib => 'Test', + } + ); +ok( Koha::Accounts::DebitTypes::IsValid('TEST'), + 'Test DebitTypes::IsValid with valid authorised value debit type' ); +$authorised_value->delete(); + +my $debit = AddDebit( + { + borrower => $borrower, + amount => 5.00, + type => Koha::Accounts::DebitTypes::Fine, + branchcode => 'MPL', + } +); +ok( $debit, "AddDebit returned a valid debit id " . $debit->id() ); + +ok( + $borrower->account_balance() == 5.00, + "Borrower's account balance updated correctly" +); + +my $debit2 = AddDebit( + { + borrower => $borrower, + amount => 7.00, + type => Koha::Accounts::DebitTypes::Fine, + branchcode => 'MPL', + } +); + +my $credit = AddCredit( + { + borrower => $borrower, + type => Koha::Accounts::CreditTypes::Payment, + amount => 9.00, + branchcode => 'MPL', + } +); + +RecalculateAccountBalance( { borrower => $borrower } ); +ok( + sprintf( "%.2f", $borrower->account_balance() ) eq "3.00", + "RecalculateAccountBalance updated balance correctly." +); + +Koha::Database->new()->schema->resultset('AccountCredit')->create( + { + borrowernumber => $borrower->borrowernumber(), + type => Koha::Accounts::CreditTypes::Payment, + amount_paid => 3.00, + amount_remaining => 3.00, + } +); +NormalizeBalances( { borrower => $borrower } ); +ok( + $borrower->account_balance() == 0.00, + "NormalizeBalances updated balance correctly." +); + +# Adding advance credit with no balance due +$credit = AddCredit( + { + borrower => $borrower, + type => Koha::Accounts::CreditTypes::Payment, + amount => 9.00, + branchcode => 'MPL', + } +); +ok( + $borrower->account_balance() == -9, +'Adding a $9 credit for borrower with 0 balance results in a -9 dollar account balance' +); + +my $debit3 = AddDebit( + { + borrower => $borrower, + amount => 5.00, + type => Koha::Accounts::DebitTypes::Fine, + branchcode => 'MPL', + } +); +ok( + $borrower->account_balance() == -4, +'Adding a $5 debit when the balance is negative results in the debit being automatically paid, resulting in a balance of -4' +); + +my $debit4 = AddDebit( + { + borrower => $borrower, + amount => 6.00, + type => Koha::Accounts::DebitTypes::Fine, + branchcode => 'MPL', + } +); +ok( + $borrower->account_balance() == 2, +'Adding another debit ( 6.00 ) more than the negative account balance results in a partial credit and a balance due of 2.00' +); +$credit = AddCredit( + { + borrower => $borrower, + type => Koha::Accounts::CreditTypes::WriteOff, + amount => 2.00, + branchcode => 'MPL', + debit_id => $debit4->debit_id(), + } +); +ok( $borrower->account_balance() == 0, + 'WriteOff of remaining 2.00 balance succeeds' ); + +my $debit5 = DebitLostItem( + { + borrower => $borrower, + issue => $issue, + } +); +ok( $borrower->account_balance() == 25, + 'DebitLostItem adds debit for replacement price of item' ); +my $lost_credit = + CreditLostItem( { borrower => $borrower, debit => $debit5 } ); +ok( + $borrower->account_balance() == 0, + 'CreditLostItem adds credit for same about as the debit for the lost tiem' +); +## Post test cleanup ## +$issue->delete(); +$item->delete(); +$biblio->delete(); +$borrower->delete(); diff --git a/t/db_dependent/Circulation.t b/t/db_dependent/Circulation.t index aa2a969..f60145b 100755 --- a/t/db_dependent/Circulation.t +++ b/t/db_dependent/Circulation.t @@ -302,8 +302,15 @@ C4::Context->dbh->do("DELETE FROM accountlines"); C4::Context->set_preference('WhenLostForgiveFine','1'); C4::Context->set_preference('WhenLostChargeReplacementFee','1'); - C4::Overdues::UpdateFine( $itemnumber, $renewing_borrower->{borrowernumber}, - 15.00, q{}, Koha::DateUtils::output_pref($datedue) ); + C4::Overdues::UpdateFine( + { + itemnumber => $itemnumber, + borrowernumber => $renewing_borrower->{borrowernumber}, + amount => 15.00, + due => Koha::DateUtils::output_pref($datedue), + issue_id => GetItemIssue($itemnumber)->{issue_id} + } + ); LostItem( $itemnumber, 1 ); @@ -319,8 +326,15 @@ C4::Context->dbh->do("DELETE FROM accountlines"); C4::Context->set_preference('WhenLostForgiveFine','0'); C4::Context->set_preference('WhenLostChargeReplacementFee','0'); - C4::Overdues::UpdateFine( $itemnumber2, $renewing_borrower->{borrowernumber}, - 15.00, q{}, Koha::DateUtils::output_pref($datedue) ); + C4::Overdues::UpdateFine( + { + itemnumber => $itemnumber2, + borrowernumber => $renewing_borrower->{borrowernumber}, + amount => 15.00, + due => Koha::DateUtils::output_pref($datedue), + issue_id => GetItemIssue($itemnumber2)->{issue_id}, + } + ); LostItem( $itemnumber2, 1 ); -- 1.7.2.5