From c394f0bac6111dd9c84ba4384da7be1ef7613728 Mon Sep 17 00:00:00 2001 From: Jason Stephenson <jstephenson@mvlc.org> Date: Thu, 30 Jun 2011 11:48:28 -0400 Subject: [PATCH] Add SIP2 fee payment, the 37/38 message/response pairs. * Pay individual bill if a fee id is given by the SC. * Pay multiple bills in batch so if one fails the whole transaction is aborted and a failure response is sent to the SC. * Reject payments outright if the SC sends an overpayment. * Add constants for bill not found and overpayment messages. * Whole lotta logging goin' on (maybe too much). Signed-off-by: Jason Stephenson <jstephenson@mvlc.org> Signed-off-by: Bill Erickson <berick@esilibrary.com> --- Open-ILS/examples/oils_sip.xml.example | 2 +- Open-ILS/src/perlmods/lib/OpenILS/SIP.pm | 46 ++++-- Open-ILS/src/perlmods/lib/OpenILS/SIP/Msg.pm | 2 + .../src/perlmods/lib/OpenILS/SIP/Transaction.pm | 2 +- .../lib/OpenILS/SIP/Transaction/FeePayment.pm | 182 +++++++++++++++++++++ 5 files changed, 215 insertions(+), 19 deletions(-) create mode 100644 Open-ILS/src/perlmods/lib/OpenILS/SIP/Transaction/FeePayment.pm diff --git a/Open-ILS/examples/oils_sip.xml.example b/Open-ILS/examples/oils_sip.xml.example index fa1e19d960..14446aac20 100644 --- a/Open-ILS/examples/oils_sip.xml.example +++ b/Open-ILS/examples/oils_sip.xml.example @@ -81,7 +81,7 @@ <item name='login' value='true'/> <item name='patron information' value='true'/> <item name='end patron session' value='true'/> - <item name='fee paid' value='false'/> + <item name='fee paid' value='true'/> <item name='item information' value='true'/> <item name='item status update' value='false'/> <item name='patron enable' value='false'/> diff --git a/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm b/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm index fe312ffd2f..729fc5c607 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm @@ -14,6 +14,7 @@ use OpenILS::SIP::Transaction; use OpenILS::SIP::Transaction::Checkout; use OpenILS::SIP::Transaction::Checkin; use OpenILS::SIP::Transaction::Renew; +use OpenILS::SIP::Transaction::FeePayment; use OpenSRF::System; use OpenILS::Utils::Fieldmapper; @@ -408,23 +409,34 @@ sub end_patron_session { } -#sub pay_fee { -# my ($self, $patron_id, $patron_pwd, $fee_amt, $fee_type, -# $pay_type, $fee_id, $trans_id, $currency) = @_; -# my $trans; -# my $patron; -# -# $trans = new ILS::Transaction::FeePayment; -# -# $patron = new ILS::Patron $patron_id; -# -# $trans->transaction_id($trans_id); -# $trans->patron($patron); -# $trans->ok(1); -# -# return $trans; -#} -# +sub pay_fee { + my ($self, $patron_id, $patron_pwd, $fee_amt, $fee_type, + $pay_type, $fee_id, $trans_id, $currency) = @_; + + my $xact = OpenILS::SIP::Transaction::FeePayment->new(authtoken => $self->{authtoken}); + my $patron = $self->find_patron($patron_id); + + if (!$patron) { + $xact->screen_msg("Invalid Patron Barcode '$patron_id'"); + $xact->ok(0); + return $xact; + } + + $xact->patron($patron); + $xact->sip_currency($currency); + $xact->fee_amount($fee_amt); + $xact->sip_fee_type($fee_type); + $xact->transaction_id($trans_id); + $xact->fee_id($fee_id); + # We don't presently use these, but we might in the future. + $xact->patron_password($patron_pwd); + $xact->sip_payment_type($pay_type); + + $xact->do_fee_payment(); + + return $xact; +} + #sub add_hold { # my ($self, $patron_id, $patron_pwd, $item_id, $title_id, # $expiry_date, $pickup_location, $hold_type, $fee_ack) = @_; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/SIP/Msg.pm b/Open-ILS/src/perlmods/lib/OpenILS/SIP/Msg.pm index 620a9ba9df..72b0c032ac 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/SIP/Msg.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/SIP/Msg.pm @@ -25,6 +25,8 @@ sub econst { econst OILS_SIP_MSG_CIRC_EXISTS => 'This item is already checked out'; econst OILS_SIP_MSG_CIRC_PERMIT_FAILED => 'Patron is not allowed to check out the selected item'; +econst OILS_SIP_MSG_NO_BILL => 'Bill not found'; +econst OILS_SIP_MSG_OVERPAYMENT => 'Overpayment not allowed'; %EXPORT_TAGS = ( const => [ @EXPORT_OK ] ); diff --git a/Open-ILS/src/perlmods/lib/OpenILS/SIP/Transaction.pm b/Open-ILS/src/perlmods/lib/OpenILS/SIP/Transaction.pm index eeb9fafc80..a4813cb147 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/SIP/Transaction.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/SIP/Transaction.pm @@ -18,7 +18,7 @@ my %fields = ( item => undef, desensitize => 0, alert => '', - transation_id => undef, + transaction_id => undef, sip_fee_type => '01', # Other/Unknown fee_amount => undef, sip_currency => 'CAD', diff --git a/Open-ILS/src/perlmods/lib/OpenILS/SIP/Transaction/FeePayment.pm b/Open-ILS/src/perlmods/lib/OpenILS/SIP/Transaction/FeePayment.pm new file mode 100644 index 0000000000..ceab0db801 --- /dev/null +++ b/Open-ILS/src/perlmods/lib/OpenILS/SIP/Transaction/FeePayment.pm @@ -0,0 +1,182 @@ +# --------------------------------------------------------------- +# Copyright (C) 2011 Merrimack Valley Library Consortium +# Jason Stephenson <jstephenson@mvlc.org> + +# This program 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. + +# This program 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. +# --------------------------------------------------------------- +# +# An object to handle fee payment +# + +package OpenILS::SIP::Transaction::FeePayment; + +use warnings; +use strict; + +use POSIX qw(strftime); + +use OpenILS::SIP; +use OpenILS::SIP::Transaction; +use OpenILS::SIP::Msg qw/:const/; +use Sys::Syslog qw(syslog); + +use OpenILS::Application::AppUtils; +my $U = 'OpenILS::Application::AppUtils'; + + +our @ISA = qw(OpenILS::SIP::Transaction); + +# Most fields are handled by the Transaction superclass +my %fields = ( + sip_payment_type => undef, + fee_id => 0, + patron_password => undef, + ); + +sub new { + my $class = shift; + + my $self = $class->SUPER::new(@_); + + foreach my $element (keys %fields) { + $self->{_permitted}->{$element} = $fields{$element}; + } + + @{$self}{keys %fields} = values %fields; + return bless $self, $class; +} + +sub do_fee_payment { + my $self = shift; + + # Just in case something completely unexpected happens, we'll + # reject the payment to be 'safe.' + $self->ok(0); + + # If the SC sends over a fee id, we try to pay that + # fee/transaction on the patron's record. + if ($self->fee_id) { + my $bill; + $bill = $U->simplereq('open-ils.actor', 'open-ils.actor.user.transaction.retrieve', $self->{authtoken}, $self->fee_id); + syslog('LOG_DEBUG', 'OILS: open-ils.actor.user.transaction.retrieve returned ' . OpenSRF::Utils::JSON->perl2JSON($bill)); + # If we got an event or the bill belongs to another patron, set bill to undef. + $bill = undef if ($U->event_code($bill) || $bill->usr != $self->patron->internal_id); + + # Attempt the payment here. + if ($bill && $bill->balance_owed >= $self->fee_amount) { + # We only attempt payment if the balance_owed on the bill + # is greater than or equal to the amount paid by the + # client. + my $payref = [ [$bill->id, $self->fee_amount] ]; + my $resp = $self->pay_bills($payref); + syslog('LOG_INFO', 'OILS: pay_bills returned ' . OpenSRF::Utils::JSON->perl2JSON($resp)); + if ($U->event_code($resp)) { + $self->ok(0); + $self->screen_msg($resp->{descr}); + } else { + $self->ok(1); + } + } else { + $self->ok(0); + if ($bill) { + # The payment had to be greater than the bill balance + # to end up here. We don't allow credits or + # overpayment. + $self->sreen_msg(OILS_SIP_MSG_OVERPAYMENT); + } + else { + # In this case, the bill was not found or did not + # belong to the patron. + $self->screen_msg(OILS_SIP_MSG_NO_BILL); + } + } + } else { + # We attempt to pay as many of the patron's bills as possible with the payment provided. + + my $results = $U->simplereq('open-ils.actor', 'open-ils.actor.user.transactions.history.have_balance', $self->{authtoken}, $self->patron->internal_id); + if ($results && ref($results) eq 'ARRAY') { + syslog('LOG_INFO', 'OILS: ' . scalar @$results . " bills found for " . $self->patron->internal_id); + + # We fill an array with the payment information as + # open-ils.circ.money.payment expects it, i.e. an arrayref + # with the bill_id and payment amount of its members. To + # actually pay the bils, we pass the reference to this + # array to our pay_bils method. + my @payments = (); + + # Pay each bill from the fee_amount provided until we + # either run out of bills or the input payment balance + # hits zero. + my $amount_paid = $self->fee_amount; # If this hits zero, we're done. + foreach my $bill (@{$results}) { + my $payment; + syslog('LOG_INFO', 'OILS: bill '. $bill->id . ' amount ' . $bill->balance_owed); + if ($bill->balance_owed >= $amount_paid) { + # We owe as much as or more than we have money + # left, so pay what we have left. + $payment = $amount_paid; + $amount_paid = 0; + } else { + # This bill is for less than the amount we have + # left, so pay the full bill amount. + $payment = $bill->balance_owed; + $amount_paid -= $bill->balance_owed; + } + # Add the payment to our array. + push(@payments, [$bill->id, $payment]); + # Leave if we ran out of money. + last if ($amount_paid == 0); + } + if (@payments && $amount_paid == 0) { + # pay the bills with a reference to our payments + # array. + my $resp = $self->pay_bills(\@payments); + syslog('LOG_INFO', 'OILS: pay_bills returned ' . OpenSRF::Utils::JSON->perl2JSON($resp)); + if ($U->event_code($resp)) { + $self->ok(0); + $self->screen_msg($resp->{descr}); + } else { + $self->ok(1); + } + } else { + $self->ok(0); + if (scalar(@payments) == 0) { + # We didn't find any bills for the patron. + $self->screen_msg(OILS_SIP_MSG_NO_BILL); + } else { + # We have an overpayment + $self->screen_msg(OILS_SIP_MSG_OVERPAYMENT); + } + } + } else { + $self->ok(0); + if ($results && $U->event_code($results)) { + $self->screen_msg($results->{descr}); + } else { + $self->screen_msg(OILS_SIP_MSG_NO_BILL); + } + } + } + return $self->ok; +} + +# Takes array ref of array ref of [billid, payment_amount] to pay in +# batch. +sub pay_bills { + my ($self, $paymentref) = @_; + my $user = $self->patron->{user}; + return $U->simplereq('open-ils.circ', 'open-ils.circ.money.payment', $self->{authtoken}, + { payment_type => "cash_payment", userid => $user->id, note => "via SIP2", + payments => $paymentref}, $user->last_xact_id); +} + + +1; -- 2.11.0