From 507c7f422485aaf7ae601b0a84bacdbaedac061c Mon Sep 17 00:00:00 2001 From: Colin Campbell Date: Tue, 2 Aug 2011 13:59:28 +0100 Subject: [PATCH] Bug 3498 [ENH] Allow Partial Payment of Fines Allow partial payment of outstanding fines against either individual fine entries or as a lump payment Sponsered by East Brunswick Public Library, East Brunswick, NJ, USA NB: Adds a version of the include circ-menu.inc as circ-menu.tt this has the same functionality as the old include but does not require all the borrowers attributes to be passed as global scoped variables Signed-off-by: Liz Rea Bug 3498 - Documentation update to explain what every button does. Help file updated to the following: Pay and Writeoff Fines Each line item can be paid in full, partially paid, or written off. Pay a fine in full Click "Pay" next to the fine you want to pay in full The full amount of the fine will be populated for you in the "Collect From Patron" box Click "Confirm" The fine will be removed from outstanding fines, and displayed as fully paid. Pay a partial fine Click "Pay" next to the fine you want to partially pay Enter the amount you are collecting from the patron in the "Collect From Patron" box Click "Confirm" The fine will be updated to show the original Amount, and the current Amount Outstanding Writeoff a single fine Click "Writeoff" next to the fine you wish to writeoff. The fine will be removed from outstanding fines, and displayed as fully paid. Pay an amount towards all fines Click the "Pay Amount" button Enter the amount you are collecting from the patron in "Collect from Patron." The sum of all fines is shown in "Total Amount Outstanding" Click "Confirm" The fine totals will be updated with the payment applied to oldest fines first. Writeoff All fines Click the "Writeoff All" button All fines will be removed from outstanding fines, and displayed as written off. Pay Selected fines Check the selection boxes next to the fines you wish to pay, click "Pay Selected" Enter an amount to pay towards the fines. Click "Confirm" The fine totals will be updated with the payment applied to the oldest selected fines first. Bug 3498 - adding parens to TT IF Statements for style pay.tt Bug 3498 - Add parens to TT IF statements for style paycollect.tt Signed-off-by: Ian Walls Signed-off-by: Chris Cormack --- C4/Accounts.pm | 113 +++++- .../prog/en/includes/circ-menu.tt | 73 ++++ .../prog/en/modules/help/members/pay.tt | 51 ++- .../prog/en/modules/members/pay.tt | 102 ++--- .../prog/en/modules/members/paycollect.tt | 227 +++++++++++ members/pay.pl | 361 ++++++++++-------- members/paycollect.pl | 171 +++++++++ 7 files changed, 878 insertions(+), 220 deletions(-) create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.tt create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/paycollect.tt create mode 100755 members/paycollect.pl diff --git a/C4/Accounts.pm b/C4/Accounts.pm index eea142c1df..2352e9e5b7 100644 --- a/C4/Accounts.pm +++ b/C4/Accounts.pm @@ -38,7 +38,9 @@ BEGIN { &getnextacctno &reconcileaccount &getcharges &ModNote &getcredits &getrefunds &chargelostitem &ReversePayment - ); # removed &fixaccounts + makepartialpayment + recordpayment_selectaccts + ); } =head1 NAME @@ -369,7 +371,6 @@ sub manualinvoice { my $dbh = C4::Context->dbh; my $notifyid = 0; my $insert; - $itemnum =~ s/ //g; my $accountno = getnextacctno($borrowernumber); my $amountleft = $amount; @@ -413,12 +414,12 @@ sub manualinvoice { $notifyid = 1; } - if ( $itemnum ne '' ) { - $desc .= " " . $itemnum; + if ( $itemnum ) { + $desc .= ' ' . $itemnum; my $sth = $dbh->prepare( - "INSERT INTO accountlines + 'INSERT INTO accountlines (borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding, itemnumber,notify_id, note, manager_id) - VALUES (?, ?, now(), ?,?, ?,?,?,?,?,?)"); + VALUES (?, ?, now(), ?,?, ?,?,?,?,?,?)'); $sth->execute($borrowernumber, $accountno, $amount, $desc, $type, $amountleft, $itemnum,$notifyid, $note, $manager_id) || return $sth->errstr; } else { my $sth=$dbh->prepare("INSERT INTO accountlines @@ -686,6 +687,106 @@ sub ReversePayment { } } +=head2 recordpayment_selectaccts + + recordpayment_selectaccts($borrowernumber, $payment,$accts); + +Record payment by a patron. C<$borrowernumber> is the patron's +borrower number. C<$payment> is a floating-point number, giving the +amount that was paid. C<$accts> is an array ref to a list of +accountnos which the payment can be recorded against + +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 payment +of $1.50, then the oldest fine will be paid off in full, and $0.50 +will be credited to the next one. + +=cut + +sub recordpayment_selectaccts { + my ( $borrowernumber, $amount, $accts ) = @_; + + my $dbh = C4::Context->dbh; + my $newamtos = 0; + my $accdata = q{}; + my $branch = C4::Context->userenv->{branch}; + my $amountleft = $amount; + my $sql = 'SELECT * FROM accountlines WHERE (borrowernumber = ?) ' . + 'AND (amountoutstanding<>0) '; + if (@{$accts} ) { + $sql .= ' AND accountno IN ( ' . join ',', @{$accts}; + $sql .= ' ) '; + } + $sql .= ' ORDER BY date'; + # begin transaction + my $nextaccntno = getnextacctno($borrowernumber); + + # get lines with outstanding amounts to offset + my $rows = $dbh->selectall_arrayref($sql, { Slice => {} }, $borrowernumber); + + # offset transactions + my $sth = $dbh->prepare('UPDATE accountlines SET amountoutstanding= ? ' . + 'WHERE (borrowernumber = ?) AND (accountno=?)'); + for my $accdata ( @{$rows} ) { + if ($amountleft == 0) { + last; + } + if ( $accdata->{amountoutstanding} < $amountleft ) { + $newamtos = 0; + $amountleft -= $accdata->{amountoutstanding}; + } + else { + $newamtos = $accdata->{amountoutstanding} - $amountleft; + $amountleft = 0; + } + my $thisacct = $accdata->{accountno}; + $sth->execute( $newamtos, $borrowernumber, $thisacct ); + } + + # create new line + $sql = 'INSERT INTO accountlines ' . + '(borrowernumber, accountno,date,amount,description,accounttype,amountoutstanding) ' . + q|VALUES (?,?,now(),?,'Payment,thanks','Pay',?)|; + $dbh->do($sql,{},$borrowernumber, $nextaccntno, 0 - $amount, 0 - $amountleft ); + UpdateStats( $branch, 'payment', $amount, '', '', '', $borrowernumber, $nextaccntno ); + return; +} + +# makepayment needs to be fixed to handle partials till then this separate subroutine +# fills in +sub makepartialpayment { + my ( $borrowernumber, $accountno, $amount, $user, $branch ) = @_; + if (!$amount || $amount < 0) { + return; + } + my $dbh = C4::Context->dbh; + + my $nextaccntno = getnextacctno($borrowernumber); + my $newamtos = 0; + + my $data = $dbh->selectrow_hashref( + 'SELECT * FROM accountlines WHERE borrowernumber=? AND accountno=?',undef,$borrowernumber,$accountno); + my $new_outstanding = $data->{amountoutstanding} - $amount; + + my $update = 'UPDATE accountlines SET amountoutstanding = ? WHERE borrowernumber = ? ' + . ' AND accountno = ?'; + $dbh->do( $update, undef, $new_outstanding, $borrowernumber, $accountno); + + # create new line + my $insert = 'INSERT INTO accountlines (borrowernumber, accountno, date, amount, ' + . 'description, accounttype, amountoutstanding) ' + . ' VALUES (?, ?, now(), ?, ?, ?, 0)'; + + $dbh->do( $insert, undef, $borrowernumber, $nextaccntno, $amount, + "Payment, thanks - $user", 'Pay'); + + UpdateStats( $user, 'payment', $amount, '', '', '', $borrowernumber, $accountno ); + + return; +} + + + END { } # module clean-up code here (global destructor) 1; diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.tt b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.tt new file mode 100644 index 0000000000..39be1bebc0 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.tt @@ -0,0 +1,73 @@ +[%# duplicates circ-menu.inc but assumes all borrower attributes are in a borrower variable rather than +in the global namespace %] +[% IF borrower %] +
[% borrower.firstname %] [% borrower.surname %] ([% borrower.cardnumber %])
+ +
    +[% IF ( patronimages ) %] +[% IF borrower.has_picture %] +
  • [% borrower.firstname %] [% borrower.surname %] ([% borrower.cardnumber %])
  • +[% ELSE %] +
  • [% borrower.firstname %] [% borrower.surname %] ([% borrower.cardnumber %])
  • +[% END %] +[% END %] +
  • [% IF borrower.address %] + [% borrower.address %] + [% ELSE %] + No address stored. + [% END %]
  • + [% IF borrower.address2 %] +
  • [% borrower.address2 %]
  • + [% END %]
  • + [% IF borrower.city %] + [% borrower.city %][% IF borrower.state %], [% borrower.state %][% END %] + [% borrower.zipcode %][% IF ( borrower.country ) %], [% borrower.country %][% END %] + [% ELSE %] + No city stored. + [% END %]
  • +
  • [% IF borrower.phone %] + [% borrower.phone %] + [% ELSE %] + [% IF borrower.mobile %] + [% borrower.mobile %] + [% ELSE %] + [% IF borrower.phonepro %] + [% borrower.phonepro %] + [% ELSE %] + No phone stored. + [% END %] + [% END %] + [% END %]
  • + [% IF borrower.email %] + + [% ELSE %] + [% IF borrower.emailpro %] + + [% ELSE %] +
  • No email stored.
  • + [% END %] + [% END %] +
  • Category: [% borrower.description %] ([% borrower.categorycode %])
  • +
  • Home Library: [% IF ( borrower.branchname ) %][% borrower.branchname %][% ELSE %][% borrower.branch %][% END %]
  • +
+ +[% END %] + diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/help/members/pay.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/help/members/pay.tt index f8eabfad22..e0dbfd0e04 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/help/members/pay.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/help/members/pay.tt @@ -1,18 +1,51 @@ [% INCLUDE 'help-top.inc' %] -

Pay/Reverse Fines

+

Pay and Writeoff Fines

-

Each line item can be paid in full (or written off) using the 'Pay Fines' tab.

+

Each line item can be paid in full, partially paid, or written off.

+

Pay a fine in full

    -
  • Choose the payment type (Unpaid, Paid, Writeoff) from the pull down menu
  • -
  • Click 'Make Payment'
  • -
  • A line item will be added to the account information showing the fee paid in full (or written off)
  • -
  • If you accidentally mark and item as paid, you can reverse that line item by clicking 'Reverse' to the right of the line +
  • Click "Pay" next to the fine you want to pay in full
  • +
  • The full amount of the fine will be populated for you in the "Collect From Patron" box
  • +
  • Click "Confirm"
  • +
  • The fine will be removed from outstanding fines, and displayed as fully paid.
  • +
+ +

Pay a partial fine

+
    +
  • Click "Pay" next to the fine you want to partially pay
  • +
  • Enter the amount you are collecting from the patron in the "Collect From Patron" box
  • +
  • Click "Confirm"
  • +
  • The fine will be updated to show the original Amount, and the current Amount Outstanding
  • +
+ +

Writeoff a single fine

+
    +
  • Click "Writeoff" next to the fine you wish to writeoff.
  • +
  • The fine will be removed from outstanding fines, and displayed as fully paid.
  • +
+ +

Pay an amount towards all fines

    -
  • Once clicked a new line item will be added to the account, showing the payment as reversed
  • +
  • Click the "Pay Amount" button
  • +
  • Enter the amount you are collecting from the patron in "Collect from Patron." The sum of all fines is shown in "Total Amount Outstanding"
  • +
  • Click "Confirm"
  • +
  • The fine totals will be updated with the payment applied to oldest fines first.
- + +

Writeoff All fines

+
    +
  • Click the "Writeoff All" button
  • +
  • All fines will be removed from outstanding fines, and displayed as written off.
  • +
+ +

Pay Selected fines

+
    +
  • Check the selection boxes next to the fines you wish to pay, click "Pay Selected"
  • +
  • Enter an amount to pay towards the fines.
  • +
  • Click "Confirm"
  • +
  • The fine totals will be updated with the payment applied to the oldest selected fines first.
-[% INCLUDE 'help-bottom.inc' %] \ No newline at end of file +[% INCLUDE 'help-bottom.inc' %] 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 ddc3d41f09..6dfd8f9d7c 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/pay.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/pay.tt @@ -1,12 +1,12 @@ [% INCLUDE 'doc-head-open.inc' %] -Koha › Patrons › Pay Fines for [% firstname %] [% surname %] +Koha › Patrons › Pay Fines for [% borrower.firstname %] [% borrower.surname %] [% INCLUDE 'doc-head-close.inc' %] [% INCLUDE 'header.inc' %] [% INCLUDE 'patron-search.inc' %] - +
@@ -18,85 +18,91 @@
-[% IF ( allfile ) %]
- +[% IF ( accounts ) %] + + + - - -[% FOREACH allfil IN allfile %] - [% FOREACH loop_pa IN allfil.loop_pay %] + +[% FOREACH account_grp IN accounts %] + [% FOREACH line IN account_grp.accountlines %] - - + - - - - - + + + + + + [% END %] -[% IF ( allfil.total ) %] +[% IF ( account_grp.total ) %] - - + + [% END %] [% END %] - - + +
Fines & ChargesSel DescriptionNote Account Type Notify id Level Amount Amount Outstanding
- [% IF ( loop_pa.net_balance ) %] - - [% END %] - - - - - - - - - - [% loop_pa.description %] [% loop_pa.title |html %] - [% IF ( loop_pa.net_balance ) %] - - [% ELSE %] - [% loop_pa.note %] - [% END %] + [% IF ( line.amountoutstanding > 0 ) %] + + + [% END %] + + + + + + + + + + + + [% IF ( line.amountoutstanding > 0 ) %] + + [% END %] [% loop_pa.accounttype %][% loop_pa.notify_id %][% loop_pa.notify_level %][% loop_pa.amount %][% loop_pa.amountoutstanding %][% line.description %] [% line.title |html_entity %][% line.accounttype %][% line.notify_id %][% line.notify_level %][% line.amount | format('%.2f') %][% line.amountoutstanding | format('%.2f') %]
Sub Total[% allfil.total %]Sub Total:[% account_grp.total | format('%.2f') %]
Total Due[% total %]Total Due:[% total | format('%.2f') %]
-
Cancel
[% ELSE %]

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

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

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

+[% END %]
-[% INCLUDE 'circ-menu.inc' %] +[% INCLUDE 'circ-menu.tt' %]
[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/paycollect.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/paycollect.tt new file mode 100644 index 0000000000..08ee90949e --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/paycollect.tt @@ -0,0 +1,227 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Patrons › Collect Fine Payment for [% borrower.firstname %] [% borrower.surname %] +[% INCLUDE 'doc-head-close.inc' %] + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'patron-search.inc' %] + + +
+ +
+
+
+[% INCLUDE 'members-toolbar.inc' %] + + + +
+ +
+[% IF ( error ) %] +
+ [% error %] +
+[% END %] + +[% IF ( pay_individual ) %] +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DescriptionAccount TypeNotify idLevelAmountAmount Outstanding
+ [% description %] [% title %] + [% accounttype %][% notify_id %][% notify_level %][% amount | format('%.2f') %][% amountoutstanding | format('%.2f') %]
Total Amount Payable : [% amountoutstanding | format('%.2f') %]
Collect From Patron: + + +
+ + Cancel +
+
+[% ELSIF ( writeoff_individual ) %] +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DescriptionAccount TypeNotify idLevelAmountAmount Outstanding
[% description %] [% title %][% accounttype %][% notify_id %][% notify_level %][% amount | format('%.2f') %][% amountoutstanding | format('%.2f') %]
Writeoff This Charge?
+ + Cancel +
+
+[% ELSE %] + +
+ + + + + + + + + + + + + + + + + + +
Total Amount Outstanding : [% total | format('%.2f') %]
Collect From Patron: + + +
+ + Cancel +
+
+[% END %] +
+
+
+ +
+[% INCLUDE 'circ-menu.tt' %] +
+
+[% INCLUDE 'intranet-bottom.inc' %] + diff --git a/members/pay.pl b/members/pay.pl index 4ecdcc925d..555cfd9c78 100755 --- a/members/pay.pl +++ b/members/pay.pl @@ -2,6 +2,7 @@ # Copyright 2000-2002 Katipo Communications # Copyright 2010 BibLibre +# Copyright 2010,2011 PTFS-Europe Ltd # # This file is part of Koha. # @@ -18,7 +19,6 @@ # with Koha; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - =head1 pay.pl written 11/1/2000 by chris@katipo.oc.nz @@ -38,196 +38,243 @@ use C4::Accounts; use C4::Stats; use C4::Koha; use C4::Overdues; -use C4::Branch; # GetBranches +use C4::Branch; -my $input = new CGI; +my $input = CGI->new; my ( $template, $loggedinuser, $cookie ) = get_template_and_user( - { - template_name => "members/pay.tmpl", + { template_name => 'members/pay.tmpl', query => $input, - type => "intranet", + type => 'intranet', authnotrequired => 0, flagsrequired => { borrowers => 1, updatecharges => 1 }, debug => 1, } ); +my $writeoff_sth; +my $add_writeoff_sth; + +my @names = $input->param; + my $borrowernumber = $input->param('borrowernumber'); -if ( $borrowernumber eq '' ) { +if ( !$borrowernumber ) { $borrowernumber = $input->param('borrowernumber0'); } # get borrower details -my $data = GetMember( borrowernumber => $borrowernumber ); +my $borrower = GetMember( borrowernumber => $borrowernumber ); my $user = $input->remote_user; +$user ||= q{}; -# get account details my $branches = GetBranches(); -my $branch = GetBranch( $input, $branches ); +my $branch = GetBranch( $input, $branches ); -my @names = $input->param; -my %inp; -my $check = 0; -for ( my $i = 0 ; $i < @names ; $i++ ) { - my $temp = $input->param( $names[$i] ); - if ( $temp eq 'wo' ) { - $inp{ $names[$i] } = $temp; - $check = 1; - } - if ( $temp eq 'yes' ) { - -# FIXME : using array +4, +5, +6 is dirty. Should use arrays for each accountline - my $amount = $input->param( $names[ $i + 4 ] ); - my $borrowernumber = $input->param( $names[ $i + 5 ] ); - my $accountno = $input->param( $names[ $i + 6 ] ); - makepayment( $borrowernumber, $accountno, $amount, $user, $branch ); - $check = 2; - } - if ( $temp eq 'no'||$temp eq 'yes'||$temp eq 'wo') { - my $borrowernumber = $input->param( $names[ $i + 5 ] ); - my $accountno = $input->param( $names[ $i + 6 ] ); - my $note = $input->param( $names[ $i + 10 ] ); - ModNote( $borrowernumber, $accountno, $note ); - } +my $writeoff_item = $input->param('confirm_writeoff'); +my $paycollect = $input->param('paycollect'); +if ($paycollect) { + print $input->redirect( + "/cgi-bin/koha/members/paycollect.pl?borrowernumber=$borrowernumber"); +} +my $payselected = $input->param('payselected'); +if ($payselected) { + payselected(@names); } -my $total = $input->param('total') || ''; -if ( $check == 0 ) { - if ( $total ne '' ) { - recordpayment( $borrowernumber, $total ); +my $writeoff_all = $input->param('woall'); # writeoff all fines +if ($writeoff_all) { + writeoff_all(@names); +} elsif ($writeoff_item) { + my $accountno = $input->param('accountno'); + my $itemno = $input->param('itemnumber'); + my $account_type = $input->param('accounttype'); + my $amount = $input->param('amount'); + writeoff( $accountno, $itemno, $account_type, $amount ); +} + +for (@names) { + if (/^pay_indiv_(\d+)$/) { + my $line_no = $1; + redirect_to_paycollect( 'pay_individual', $line_no ); + } elsif (/^wo_indiv_(\d+)$/) { + my $line_no = $1; + redirect_to_paycollect( 'writeoff_individual', $line_no ); } +} - my ( $total, $accts, $numaccts) = GetMemberAccountRecords( $borrowernumber ); - - my @allfile; - my @notify = NumberNotifyId($borrowernumber); - - my $numberofnotify = scalar(@notify); - for ( my $j = 0 ; $j < scalar(@notify) ; $j++ ) { - my @loop_pay; - my ( $total , $accts, $numaccts) = - GetBorNotifyAcctRecord( $borrowernumber, $notify[$j] ); - for ( my $i = 0 ; $i < $numaccts ; $i++ ) { - my %line; - if ( $accts->[$i]{'amountoutstanding'} != 0 ) { - $accts->[$i]{'amount'} += 0.00; - $accts->[$i]{'amountoutstanding'} += 0.00; - $line{i} = $j . "" . $i; - $line{itemnumber} = $accts->[$i]{'itemnumber'}; - $line{accounttype} = $accts->[$i]{'accounttype'}; - $line{amount} = sprintf( "%.2f", $accts->[$i]{'amount'} ); - $line{amountoutstanding} = - sprintf( "%.2f", $accts->[$i]{'amountoutstanding'} ); - $line{borrowernumber} = $borrowernumber; - $line{accountno} = $accts->[$i]{'accountno'}; - $line{description} = $accts->[$i]{'description'}; - $line{note} = $accts->[$i]{'note'}; - $line{title} = $accts->[$i]{'title'}; - $line{notify_id} = $accts->[$i]{'notify_id'}; - $line{notify_level} = $accts->[$i]{'notify_level'}; - $line{net_balance} = 1 if($accts->[$i]{'amountoutstanding'} > 0); # you can't pay a credit. - push( @loop_pay, \%line ); - } - } +add_accounts_to_template(); - my $totalnotify = AmountNotify( $notify[$j], $borrowernumber ); - ( $totalnotify = '0' ) if ( $totalnotify =~ /^0.00/ ); - push @allfile, - { - 'loop_pay' => \@loop_pay, - 'notify' => $notify[$j], - 'total' => sprintf( "%.2f",$totalnotify), - - }; - } - -if ( $data->{'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; +output_html_with_http_headers $input, $cookie, $template->output; + +sub writeoff { + my ( $accountnum, $itemnum, $accounttype, $amount ) = @_; + + # if no item is attached to fine, make sure to store it as a NULL + $itemnum ||= undef; + get_writeoff_sth(); + $writeoff_sth->execute( $accountnum, $borrowernumber ); + + my $acct = getnextacctno($borrowernumber); + $add_writeoff_sth->execute( $borrowernumber, $acct, $itemnum, $amount ); + + UpdateStats( $branch, 'writeoff', $amount, q{}, q{}, q{}, $borrowernumber ); + + return; } - -$template->param( adultborrower => 1 ) if ( $data->{'category_type'} eq 'A' ); -my ($picture, $dberror) = GetPatronImage($data->{'cardnumber'}); -$template->param( picture => 1 ) if $picture; - + +sub add_accounts_to_template { + + my ( $total, undef, undef ) = GetMemberAccountRecords($borrowernumber); + my $accounts = []; + my @notify = NumberNotifyId($borrowernumber); + + my $notify_groups = []; + for my $notify_id (@notify) { + my ( $acct_total, $accountlines, undef ) = + GetBorNotifyAcctRecord( $borrowernumber, $notify_id ); + if ( @{$accountlines} ) { + my $totalnotify = AmountNotify( $notify_id, $borrowernumber ); + push @{$accounts}, + { accountlines => $accountlines, + notify => $notify_id, + total => $totalnotify, + }; + } + } + borrower_add_additional_fields($borrower); $template->param( - allfile => \@allfile, - firstname => $data->{'firstname'}, - surname => $data->{'surname'}, - borrowernumber => $borrowernumber, - cardnumber => $data->{'cardnumber'}, - categorycode => $data->{'categorycode'}, - category_type => $data->{'category_type'}, - categoryname => $data->{'description'}, - address => $data->{'address'}, - address2 => $data->{'address2'}, - city => $data->{'city'}, - state => $data->{'state'}, - zipcode => $data->{'zipcode'}, - country => $data->{'country'}, - phone => $data->{'phone'}, - email => $data->{'email'}, - branchcode => $data->{'branchcode'}, - branchname => GetBranchName($data->{'branchcode'}), - is_child => ($data->{'category_type'} eq 'C'), - total => sprintf( "%.2f", $total ) + accounts => $accounts, + borrower => $borrower, + total => $total, ); - output_html_with_http_headers $input, $cookie, $template->output; + return; } -else { - - my %inp; - my @name = $input->param; - for ( my $i = 0 ; $i < @name ; $i++ ) { - my $test = $input->param( $name[$i] ); - if ( $test eq 'wo' ) { - my $temp = $name[$i]; - $temp =~ s/payfine//; - $inp{ $name[$i] } = $temp; - } + +sub get_for_redirect { + my ( $name, $name_in, $money ) = @_; + my $s = q{&} . $name . q{=}; + my $value = $input->param($name_in); + if ( !defined $value ) { + $value = ( $money == 1 ) ? 0 : q{}; + } + if ($money) { + $s .= sprintf '%.2f', $value; + } else { + $s .= $value; } - my $borrowernumber; - while ( my ( $key, $value ) = each %inp ) { - - my $accounttype = $input->param("accounttype$value"); - $borrowernumber = $input->param("borrowernumber$value"); - my $itemno = $input->param("itemnumber$value"); - my $amount = $input->param("amount$value"); - my $accountno = $input->param("accountno$value"); - writeoff( $borrowernumber, $accountno, $itemno, $accounttype, $amount ); + return $s; +} + +sub redirect_to_paycollect { + my ( $action, $line_no ) = @_; + my $redirect = + "/cgi-bin/koha/members/paycollect.pl?borrowernumber=$borrowernumber"; + $redirect .= q{&}; + $redirect .= "$action=1"; + $redirect .= get_for_redirect( 'accounttype', "accounttype$line_no", 0 ); + $redirect .= get_for_redirect( 'amount', "amount$line_no", 1 ); + $redirect .= + get_for_redirect( 'amountoutstanding', "amountoutstanding$line_no", 1 ); + $redirect .= get_for_redirect( 'accountno', "accountno$line_no", 0 ); + $redirect .= get_for_redirect( 'description', "description$line_no", 0 ); + $redirect .= get_for_redirect( 'title', "title$line_no", 0 ); + $redirect .= get_for_redirect( 'itemnumber', "itemnumber$line_no", 0 ); + $redirect .= get_for_redirect( 'notify_id', "notify_id$line_no", 0 ); + $redirect .= get_for_redirect( 'notify_level', "notify_level$line_no", 0 ); + $redirect .= '&remote_user='; + $redirect .= $user; + return print $input->redirect($redirect); +} + +sub writeoff_all { + my @params = @_; + my @wo_lines = grep { /^accountno\d+$/ } @params; + for (@wo_lines) { + if (/(\d+)/) { + my $value = $1; + my $accounttype = $input->param("accounttype$value"); + + # my $borrowernum = $input->param("borrowernumber$value"); + my $itemno = $input->param("itemnumber$value"); + my $amount = $input->param("amount$value"); + my $accountno = $input->param("accountno$value"); + writeoff( $accountno, $itemno, $accounttype, $amount ); + } } + $borrowernumber = $input->param('borrowernumber'); print $input->redirect( "/cgi-bin/koha/members/boraccount.pl?borrowernumber=$borrowernumber"); + return; } -sub writeoff { - my ( $borrowernumber, $accountnum, $itemnum, $accounttype, $amount ) = @_; - my $user = $input->remote_user; - my $dbh = C4::Context->dbh; - undef $itemnum unless $itemnum; # if no item is attached to fine, make sure to store it as a NULL - my $sth = - $dbh->prepare( -"Update accountlines set amountoutstanding=0 where accountno=? and borrowernumber=?" - ); - $sth->execute( $accountnum, $borrowernumber ); - $sth->finish; - $sth = $dbh->prepare("select max(accountno) from accountlines"); - $sth->execute; - my $account = $sth->fetchrow_hashref; - $sth->finish; - $account->{'max(accountno)'}++; - $sth = $dbh->prepare( -"insert into accountlines (borrowernumber,accountno,itemnumber,date,amount,description,accounttype) - values (?,?,?,now(),?,'Writeoff','W')" - ); - $sth->execute( $borrowernumber, $account->{'max(accountno)'}, - $itemnum, $amount ); - $sth->finish; - UpdateStats( $branch, 'writeoff', $amount, '', '', '', - $borrowernumber ); +sub borrower_add_additional_fields { + my $b_ref = shift; + +# some borrower info is not returned in the standard call despite being assumed +# in a number of templates. It should not be the business of this script but in lieu of +# a revised api here it is ... + if ( $b_ref->{category_type} eq 'C' ) { + my ( $catcodes, $labels ) = + GetborCatFromCatType( 'A', 'WHERE category_type = ?' ); + if ( @{$catcodes} ) { + if ( @{$catcodes} > 1 ) { + $b_ref->{CATCODE_MULTI} = 1; + } elsif ( @{$catcodes} == 1 ) { + $b_ref->{catcode} = $catcodes->[0]; + } + } + } elsif ( $b_ref->{category_type} eq 'A' ) { + $b_ref->{adultborrower} = 1; + } + my ( $picture, $dberror ) = GetPatronImage( $b_ref->{cardnumber} ); + if ($picture) { + $b_ref->{has_picture} = 1; + } + + $b_ref->{branchname} = GetBranchName( $b_ref->{branchcode} ); + return; +} + +sub payselected { + my @params = @_; + my $amt = 0; + my @lines_to_pay; + foreach (@params) { + if (/^incl_par_(\d+)$/) { + my $index = $1; + push @lines_to_pay, $input->param("accountno$index"); + $amt += $input->param("amountoutstanding$index"); + } + } + $amt = '&amt=' . $amt; + my $sel = '&selected=' . join ',', @lines_to_pay; + my $redirect = + "/cgi-bin/koha/members/paycollect.pl?borrowernumber=$borrowernumber" + . $amt + . $sel; + + print $input->redirect($redirect); + return; +} + +sub get_writeoff_sth { + + # lets prepare these statement handles only once + if ($writeoff_sth) { + return; + } else { + my $dbh = C4::Context->dbh; + + # Do we need to validate accounttype + my $sql = 'Update accountlines set amountoutstanding=0 ' + . 'WHERE accountno=? and borrowernumber=?'; + $writeoff_sth = $dbh->prepare($sql); + my $insert = +q{insert into accountlines (borrowernumber,accountno,itemnumber,date,amount,description,accounttype)} + . q{values (?,?,?,now(),?,'Writeoff','W')}; + $add_writeoff_sth = $dbh->prepare($insert); + } + return; } diff --git a/members/paycollect.pl b/members/paycollect.pl new file mode 100755 index 0000000000..cbddc05be6 --- /dev/null +++ b/members/paycollect.pl @@ -0,0 +1,171 @@ +#!/usr/bin/perl +# Copyright 2009,2010 PTFS Inc. +# Copyright 2011 PTFS-Europe Ltd +# +# 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::Context; +use C4::Auth; +use C4::Output; +use CGI; +use C4::Members; +use C4::Accounts; +use C4::Koha; +use C4::Branch; + +my $input = CGI->new(); + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { template_name => 'members/paycollect.tmpl', + query => $input, + type => 'intranet', + authnotrequired => 0, + flagsrequired => { borrowers => 1, updatecharges => 1 }, + debug => 1, + } +); + +# get borrower details +my $borrowernumber = $input->param('borrowernumber'); +my $borrower = GetMember( borrowernumber => $borrowernumber ); +my $user = $input->remote_user; + +# get account details +my $branch = GetBranch( $input, GetBranches() ); + +my ( $total_due, $accts, $numaccts ) = GetMemberAccountRecords($borrowernumber); +my $total_paid = $input->param('paid'); + +my $individual = $input->param('pay_individual'); +my $writeoff = $input->param('writeoff_individual'); +my $select_lines = $input->param('selected'); +my $select = $input->param('selected_accts'); +my $accountno; + +if ( $individual || $writeoff ) { + if ($individual) { + $template->param( pay_individual => 1 ); + } elsif ($writeoff) { + $template->param( writeoff_individual => 1 ); + } + my $accounttype = $input->param('accounttype'); + my $amount = $input->param('amount'); + my $amountoutstanding = $input->param('amountoutstanding'); + $accountno = $input->param('accountno'); + my $description = $input->param('description'); + my $title = $input->param('title'); + my $notify_id = $input->param('notify_id'); + my $notify_level = $input->param('notify_level'); + $total_due = $amountoutstanding; + $template->param( + accounttype => $accounttype, + accountno => $accountno, + amount => $amount, + amountoutstanding => $amountoutstanding, + title => $title, + description => $description, + notify_id => $notify_id, + notify_level => $notify_level, + ); +} elsif ($select_lines) { + $total_due = $input->param('amt'); + $template->param( + selected_accts => $select_lines, + amt => $total_due + ); +} + +if ( $total_paid and $total_paid ne '0.00' ) { + if ( $total_paid < 0 or $total_paid > $total_due ) { + $template->param( + error => sprintf( 'You must pay a value less than or equal to %f.2', + $total_due ) + ); + } else { + if ($individual) { + if ( $total_paid == $total_due ) { + makepayment( $borrowernumber, $accountno, $total_paid, $user, + $branch ); + } else { + makepartialpayment( $borrowernumber, $accountno, $total_paid, + $user, $branch ); + } + print $input->redirect( + "/cgi-bin/koha/members/pay.pl?borrowernumber=$borrowernumber"); + } else { + if ($select) { + if ( $select =~ /^([\d,]*).*/ ) { + $select = $1; # ensure passing no junk + } + my @acc = split /,/, $select; + recordpayment_selectaccts( $borrowernumber, $total_paid, + \@acc ); + } else { + recordpayment( $borrowernumber, $total_paid ); + } + +# recordpayment does not return success or failure so lets redisplay the boraccount + + print $input->redirect( +"/cgi-bin/koha/members/boraccount.pl?borrowernumber=$borrowernumber" + ); + } + } +} else { + $total_paid = '0.00'; #TODO not right with pay_individual +} + +borrower_add_additional_fields($borrower); + +$template->param( + + #borrowenumber => $borrower->{borrowernumber}, # some templates require global + borrowenumber => $borrowernumber, # some templates require global + borrower => $borrower, + total => $total_due +); + +output_html_with_http_headers $input, $cookie, $template->output; + +sub borrower_add_additional_fields { + my $b_ref = shift; + +# some borrower info is not returned in the standard call despite being assumed +# in a number of templates. It should not be the business of this script but in lieu of +# a revised api here it is ... + if ( $b_ref->{category_type} eq 'C' ) { + my ( $catcodes, $labels ) = + GetborCatFromCatType( 'A', 'WHERE category_type = ?' ); + if ( @{$catcodes} ) { + if ( @{$catcodes} > 1 ) { + $b_ref->{CATCODE_MULTI} = 1; + } elsif ( @{$catcodes} == 1 ) { + $b_ref->{catcode} = $catcodes->[0]; + } + } + } elsif ( $b_ref->{category_type} eq 'A' ) { + $b_ref->{adultborrower} = 1; + } + my ( $picture, $dberror ) = GetPatronImage( $b_ref->{cardnumber} ); + if ($picture) { + $b_ref->{has_picture} = 1; + } + + $b_ref->{branchname} = GetBranchName( $b_ref->{branchcode} ); + return; +} -- 2.39.5