Add SIP2 fee payment, the 37/38 message/response pairs.
authorJason Stephenson <jstephenson@mvlc.org>
Thu, 30 Jun 2011 15:48:28 +0000 (11:48 -0400)
committerBill Erickson <berick@esilibrary.com>
Fri, 12 Aug 2011 20:54:26 +0000 (16:54 -0400)
* 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
Open-ILS/src/perlmods/lib/OpenILS/SIP.pm
Open-ILS/src/perlmods/lib/OpenILS/SIP/Msg.pm
Open-ILS/src/perlmods/lib/OpenILS/SIP/Transaction.pm
Open-ILS/src/perlmods/lib/OpenILS/SIP/Transaction/FeePayment.pm [new file with mode: 0644]

index fa1e19d..14446aa 100644 (file)
@@ -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'/>
index fe312ff..729fc5c 100644 (file)
@@ -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) = @_;
index 620a9ba..72b0c03 100644 (file)
@@ -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 ] );
 
index eeb9faf..a4813cb 100644 (file)
@@ -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 (file)
index 0000000..ceab0db
--- /dev/null
@@ -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;