LP1901930 Payments support
authorBill Erickson <berickxx@gmail.com>
Mon, 2 Nov 2020 21:38:12 +0000 (16:38 -0500)
committerBill Erickson <berickxx@gmail.com>
Mon, 30 Nov 2020 16:38:26 +0000 (08:38 -0800)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/perlmods/lib/OpenILS/Application/SIP2.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/SIP2/Payment.pm [new file with mode: 0644]

index 0bdda66..348f4d8 100644 (file)
@@ -16,6 +16,7 @@ use OpenILS::Application::SIP2::Item;
 use OpenILS::Application::SIP2::Patron;
 use OpenILS::Application::SIP2::Checkout;
 use OpenILS::Application::SIP2::Checkin;
+use OpenILS::Application::SIP2::Payment;
 
 my $U = 'OpenILS::Application::AppUtils';
 my $SC = 'OpenILS::Application::SIP2::Common';
@@ -70,6 +71,7 @@ sub dispatch_sip2_request {
         '11' => \&handle_checkout,
         '17' => \&handle_item_info,
         '23' => \&handle_patron_status,
+        '37' => \&handle_payment,
         '63' => \&handle_patron_info,
         'XS' => \&handle_end_session
     };
@@ -528,6 +530,49 @@ sub handle_checkin {
     };
 }
 
+sub handle_payment {
+    my ($session, $message) = @_;
+    my $config = $session->config;
+
+    my $fee_type = $message->fixed_fields[1];
+    my $pay_type = $message->fixed_fields[2];
+    my $pay_amount = $SC->get_field_value($message, 'BV');
+    my $patron_barcode = $SC->get_field_value($message, 'AA');
+    my $fee_id = $SC->get_field_value($message, 'CG');
+    my $terminal_xact = $SC->get_field_value($message, 'BK');
+
+    # Envisionware extensions for relaying information about 
+    # payments made via credit card kiosk or cash register.
+    my $register_login = $SC->get_field_value($message, 'OR');
+    my $check_number = $SC->get_field_value($message, 'RN');
+
+    my $details = OpenILS::Application::SIP2::Payment->apply_payment(
+        $session, 
+        fee_id => $fee_id,
+        fee_type => $fee_type,
+        pay_type => $pay_type,
+        pay_amount => $pay_amount,
+        check_number => $check_number,
+        patron_barcode => $patron_barcode,
+        terminal_xact => $terminal_xact,
+        register_login => $register_login
+    );
+
+    my $screen_msg = $details->{screen_msg};
+
+    return {
+        code => '38',
+        fixed_fields => [
+            $SC->sipbool($details->{ok}),
+            $SC->sipdate,
+        ],
+        fields => [
+            {AA => $patron_barcode},
+            {AO => $config->{institution}},
+            $screen_msg ? {AF => $screen_msg} : (),
+        ]
+    }
+}
 
 1;
 
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/SIP2/Payment.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/SIP2/Payment.pm
new file mode 100644 (file)
index 0000000..062be09
--- /dev/null
@@ -0,0 +1,181 @@
+package OpenILS::Application::SIP2::Payment;
+use strict; use warnings;
+use DateTime;
+use DateTime::Format::ISO8601;
+use OpenSRF::System;
+use OpenILS::Utils::CStoreEditor q/:funcs/;
+use OpenSRF::Utils::Logger q/$logger/;
+use OpenILS::Application::AppUtils;
+use OpenILS::Utils::DateTime qw/:datetime/;
+use OpenILS::Const qw/:const/;
+use OpenILS::Application::SIP2::Common;
+use OpenILS::Application::SIP2::Session;
+my $U = 'OpenILS::Application::AppUtils';
+my $SC = 'OpenILS::Application::SIP2::Common';
+
+
+sub apply_payment {
+    my ($class, $session, %params) = @_;
+
+    my $details = {ok => 0};
+
+    my $card = $session->editor->search_actor_card([
+        {barcode => $params{patron_barcode}}, 
+        {flesh => 1, flesh_fields => {ac => [qw/usr/]}}
+    ])->[0];
+
+    return $details unless $card;
+    my $user = $card->usr;
+
+    if ($params{fee_id}) {
+        pay_one_transaction($session, $details, $user, %params);
+
+    } else {
+        # No transaction was specified, pay whatever we can.
+        pay_multi_transactions($session, $details, $user, %params);
+    }
+
+    return $details;
+}
+
+sub pay_one_transaction {
+    my ($session, $details, $user, %params) = @_;
+
+    my $fee_id = $params{fee_id};  # action.billable_xact.id
+
+    my $xact = $session->editor->retrieve_money_billable_xact_summary($fee_id);
+
+    return unless $xact && $xact->usr == $user->id;
+
+    my $pay_amount = $params{pay_amount};
+
+    return unless $pay_amount > 0;
+
+    if ($pay_amount > $xact->balance_owed) {
+        # TODO strings
+        $details->{screen_msg} = 'Overpayment not allowed';
+        return;
+    }
+
+    my $payments = [[$xact->id, $pay_amount]];
+
+    send_payments($session, $details, $user, $payments, %params);
+}
+
+sub pay_multi_transactions {
+    my ($session, $details, $user, %params) = @_;
+    my $payments = [];
+
+    # See if we can find some find some transactions to pay.
+    my $xacts = $U->simplereq('open-ils.actor', 
+        'open-ils.actor.user.transactions.history.have_balance', 
+        $session->editor->authtoken, $user->id);
+
+    if (!$xacts || !@$xacts) { # nothing to pay
+        # TODO: i18n
+        $details->{screen_msg} = 'Bill not found';
+        return;
+    }
+
+    my $pay_amount = $params{pay_amount};
+    my $amount_paid = $pay_amount;
+
+    for my $xact (@$xacts) {
+        next if $xact->balance_owed <= 0;
+
+        my $payment;
+        my $xact_id = $xact->id;
+        my $balance_owed = $xact->balance_owed;
+
+        if ($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 = $balance_owed;
+            $amount_paid = $U->fpdiff($amount_paid, $balance_owed);
+        }
+
+        push(@$payments, [$xact->id, $payment]);
+
+        $amount_paid = sprintf("%.2f", $amount_paid);
+        $balance_owed = sprintf("%.2f", $balance_owed);
+
+        $logger->info("SIP paid $payment on $xact_id with a ".
+            "balance of $balance_owed and $amount_paid remaining");
+
+        # Leave if we ran out of money.
+        last if $amount_paid == 0;
+    }
+
+    if ($amount_paid == 0) {
+        # TODO strings
+        $details->{screen_msg} = 'Overpayment not allowed';
+        return;
+    }
+
+    send_payments($session, $details, $user, $payments, %params);
+}
+
+# Takes array ref of array ref of [xact_id, payment_amount] to pay in batch.
+sub send_payments {
+    my ($session, $details, $user, $payments, %params);
+
+    my $pay_type = $params{pay_type};
+    my $register_login = $params{register_login};
+
+    if ($register_login) {
+        $logger->debug("SIP register login sent as '$register_login'");
+
+        if ($register_login =~ /\\.+/) { # Windows domain login DOMAIN\user
+            my @parts = split(/\\/, $register_login);
+            $register_login = $parts[1];
+        }
+    }
+
+    my $args = {
+        userid => $user->id,
+        note => $register_login ? 
+            "Via SIP2: Register login '$register_login'" : "Via SIP2",
+        payments => $payments,
+        payment_type => 'cash_payment'
+    };
+
+    if ($pay_type eq '01' || $pay_type eq '02') {
+        # '01' is "VISA"
+        # '02' is "credit card"
+
+        $args->{payment_type} = 'credit_card_payment';
+        $args->{cc_args} = {
+            approval_code => 
+                $params{terminal_xact} || 'Not provided by SIP client'
+        };
+
+    } elsif ($pay_type eq '05') {
+
+        $args->{payment_type} = 'check_payment';
+        $args->{check_number} = 
+            $params{check_number} || 'Not Provided by SIP Client';
+    }
+
+    my $resp = $U->simplereq(
+        'open-ils.circ', 'open-ils.circ.money.payment', 
+        $session->editor->authtoken, $args, $user->last_xact_id);
+
+    if ($U->event_code($resp)) {
+        $details->{screen_msg} = $resp->{descr} || $resp->{textcode};
+    } else {
+        $details->{ok} = 1;
+    }
+}
+
+
+
+
+