1 # ---------------------------------------------------------------
2 # Copyright (C) 2005 Georgia Public Library Service
3 # Bill Erickson <billserickson@gmail.com>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 # ---------------------------------------------------------------
16 package OpenILS::Application::Circ::Money;
17 use base qw/OpenILS::Application/;
18 use strict; use warnings;
19 use OpenILS::Application::AppUtils;
20 my $apputils = "OpenILS::Application::AppUtils";
21 my $U = "OpenILS::Application::AppUtils";
23 use OpenSRF::EX qw(:try);
27 use OpenSRF::Utils::Logger qw/:logger/;
28 use OpenILS::Utils::CStoreEditor qw/:funcs/;
29 use OpenILS::Utils::Penalty;
31 __PACKAGE__->register_method(
32 method => "make_payments",
33 api_name => "open-ils.circ.money.payment",
35 desc => q/Create payments for a given user and set of transactions,
36 login must have CREATE_PAYMENT privileges.
37 If any payments fail, all are reverted back./,
39 {desc => 'Authtoken', type => 'string'},
40 {desc => q/Arguments Hash, supporting the following params:
47 where_process 1 to use processor, !1 for out-of-band
48 approval_code (for out-of-band payment)
49 type (for out-of-band payment)
50 number (for call to payment processor)
51 expire_month (for call to payment processor)
52 expire_year (for call to payment processor)
53 billing_first (for call to payment processor)
54 billing_last (for call to payment processor)
55 billing_address (for call to payment processor)
56 billing_city (for call to payment processor)
57 billing_state (for call to payment processor)
58 billing_zip (for call to payment processor)
59 note (if payments->{note} is blank, use this)
71 q{1 on success, event on failure. Event possibilities include:
73 Bad parameters were given to this API method itself.
75 REFUND_EXCEEDS_BALANCE
76 REFUND_EXCEEDS_DESK_PAYMENTS
77 CREDIT_PROCESSOR_NOT_SPECIFIED
78 Evergreen has not been set up to process CC payments.
79 CREDIT_PROCESSOR_NOT_ALLOWED
80 Evergreen has been incorrectly setup for CC payments.
81 CREDIT_PROCESSOR_NOT_ENABLED
82 Evergreen has been set up for CC payments, but an admin
83 has not explicitly enabled them.
84 CREDIT_PROCESSOR_BAD_PARAMS
85 Evergreen has been incorrectly setup for CC payments;
86 specifically, the login and/or password for the CC
87 processor weren't provided.
88 CREDIT_PROCESSOR_INVALID_CC_NUMBER
89 You have supplied a credit card number that Evergreen
90 has judged to be invalid even before attempting to contact
91 the payment processor.
92 CREDIT_PROCESSOR_DECLINED_TRANSACTION
93 We contacted the CC processor to attempt the charge, but
95 The error_message field of the event payload will
96 contain the payment processor's response. This
97 typically includes a message in plain English intended
98 for human consumption. In PayPal's case, the message
99 is preceded by an integer, a colon, and a space, so
100 a caller might take the 2nd match from /^(\d+: )?(.+)$/
101 to present to the user.
102 The payload also contains other fields from the payment
103 processor, but these are generally not user-friendly
105 CREDIT_PROCESSOR_SUCCESS_WO_RECORD
106 A payment was processed successfully, but couldn't be
107 recorded in Evergreen. This is _bad bad bad_, as it means
108 somebody made a payment but isn't getting credit for it.
109 See note field for more info.
116 my($self, $client, $auth, $payments) = @_;
118 my $e = new_editor(authtoken => $auth, xact => 1);
119 return $e->die_event unless $e->checkauth;
121 my $type = $payments->{payment_type};
122 my $user_id = $payments->{userid};
123 my $credit = $payments->{patron_credit} || 0;
124 my $drawer = $e->requestor->wsid;
125 my $note = $payments->{note};
126 my $cc_args = $payments->{cc_args};
127 my $check_number = $payments->{check_number};
129 my $this_ou = $e->requestor->ws_ou;
132 # unless/until determined by payment processor API
133 my ($approval_code, $cc_processor, $cc_type) = (undef,undef,undef);
135 my $patron = $e->retrieve_actor_user($user_id) or return $e->die_event;
137 # A user is allowed to make credit card payments on his/her own behalf
138 # All other scenarious require permission
139 unless($type eq 'credit_card_payment' and $user_id == $e->requestor->id) {
140 return $e->die_event unless $e->allowed('CREATE_PAYMENT', $patron->home_ou);
143 # first collect the transactions and make sure the transaction
144 # user matches the requested user
146 for my $pay (@{$payments->{payments}}) {
147 my $xact_id = $pay->[0];
148 my $xact = $e->retrieve_money_billable_transaction_summary($xact_id)
149 or return $e->die_event;
151 if($xact->usr != $user_id) {
153 return OpenILS::Event->new('BAD_PARAMS', note => q/user does not match transaction/);
156 $xacts{$xact_id} = $xact;
161 for my $pay (@{$payments->{payments}}) {
162 my $transid = $pay->[0];
163 my $amount = $pay->[1];
164 $amount =~ s/\$//og; # just to be safe
165 my $trans = $xacts{$transid};
167 $total_paid += $amount;
169 $orgs{$U->xact_org($transid, $e)} = 1;
171 # A negative payment is a refund.
174 # Negative credit card payments are not allowed
175 if($type eq 'credit_card_payment') {
177 return OpenILS::Event->new(
179 note => q/Negative credit card payments not allowed/
183 # If the refund causes the transaction balance to exceed 0 dollars,
184 # we are in effect loaning the patron money. This is not allowed.
185 if( ($trans->balance_owed - $amount) > 0 ) {
187 return OpenILS::Event->new('REFUND_EXCEEDS_BALANCE');
190 # Otherwise, make sure the refund does not exceed desk payments
191 # This is also not allowed
193 my $desk_payments = $e->search_money_desk_payment({xact => $transid, voided => 'f'});
194 $desk_total += $_->amount for @$desk_payments;
196 if( (-$amount) > $desk_total ) {
198 return OpenILS::Event->new(
199 'REFUND_EXCEEDS_DESK_PAYMENTS',
200 payload => { allowed_refund => $desk_total, submitted_refund => -$amount } );
204 my $payobj = "Fieldmapper::money::$type";
205 $payobj = $payobj->new;
207 $payobj->amount($amount);
208 $payobj->amount_collected($amount);
209 $payobj->xact($transid);
210 $payobj->note($note);
211 if ((not $payobj->note) and ($type eq 'credit_card_payment')) {
212 $payobj->note($cc_args->{note});
215 if ($payobj->has_field('accepting_usr')) { $payobj->accepting_usr($e->requestor->id); }
216 if ($payobj->has_field('cash_drawer')) { $payobj->cash_drawer($drawer); }
217 if ($payobj->has_field('cc_type')) { $payobj->cc_type($cc_args->{type}); }
218 if ($payobj->has_field('check_number')) { $payobj->check_number($check_number); }
220 # Store the last 4 digits of the CC number
221 if ($payobj->has_field('cc_number')) {
222 $payobj->cc_number(substr($cc_args->{number}, -4));
224 if ($payobj->has_field('expire_month')) { $payobj->expire_month($cc_args->{expire_month}); }
225 if ($payobj->has_field('expire_year')) { $payobj->expire_year($cc_args->{expire_year}); }
227 # Note: It is important not to set approval_code
228 # on the fieldmapper object yet.
230 push(@payment_objs, $payobj);
232 } # all payment objects have been created and inserted.
234 #### NO WRITES TO THE DB ABOVE THIS LINE -- THEY'LL ONLY BE DISCARDED ###
237 # After we try to externally process a credit card (if desired), we'll
238 # open a new transaction. We cannot leave one open while credit card
239 # processing might be happening, as it can easily time out the database
241 if($type eq 'credit_card_payment') {
242 $approval_code = $cc_args->{approval_code};
243 # If an approval code was not given, we'll need
244 # to call to the payment processor ourselves.
245 if ($cc_args->{where_process} == 1) {
246 return OpenILS::Event->new('BAD_PARAMS', note => 'Need CC number')
247 if not $cc_args->{number};
249 OpenILS::Application::Circ::CreditCard::process_payment({
250 "desc" => $cc_args->{note},
251 "amount" => $total_paid,
252 "patron_id" => $user_id,
253 "cc" => $cc_args->{number},
254 "expiration" => sprintf(
256 $cc_args->{expire_month},
257 $cc_args->{expire_year}
260 "first_name" => $cc_args->{billing_first},
261 "last_name" => $cc_args->{billing_last},
262 "address" => $cc_args->{billing_address},
263 "city" => $cc_args->{billing_city},
264 "state" => $cc_args->{billing_state},
265 "zip" => $cc_args->{billing_zip},
268 if ($U->event_code($response)) { # non-success
270 "Credit card payment for user $user_id failed: " .
271 $response->{"textcode"} . " " .
272 $response->{"payload"}->{"error_message"}
277 $approval_code = $response->{"payload"}->{"authorization"};
278 $cc_type = $response->{"payload"}->{"card_type"};
279 $cc_processor = $response->{"payload"}->{"processor"};
281 "Credit card payment for user $user_id succeeded"
285 return OpenILS::Event->new(
286 'BAD_PARAMS', note => 'Need approval code'
287 ) if not $cc_args->{approval_code};
291 ### RE-OPEN TRANSACTION HERE ###
294 # create payment records
295 my $create_money_method = "create_money_" . $type;
296 for my $payment (@payment_objs) {
297 # update the transaction if it's done
298 my $amount = $payment->amount;
299 my $transid = $payment->xact;
300 my $trans = $xacts{$transid};
301 if( (my $cred = ($trans->balance_owed - $amount)) <= 0 ) {
302 # Any overpay on this transaction goes directly into patron
303 # credit making payment with existing patron credit.
304 $credit -= $amount if $type eq 'credit_payment';
308 my $circ = $e->retrieve_action_circulation($transid);
310 if(!$circ || $circ->stop_fines) {
311 # If this is a circulation, we can't close the transaction
312 # unless stop_fines is set.
313 $trans = $e->retrieve_money_billable_transaction($transid);
314 $trans->xact_finish("now");
315 if (!$e->update_money_billable_transaction($trans)) {
316 $logger->warn("update_money_billable_transaction() " .
319 return OpenILS::Event->new(
320 'CREDIT_PROCESSOR_SUCCESS_WO_RECORD',
321 note => 'update_money_billable_transaction() failed'
327 $payment->approval_code($approval_code) if $approval_code;
328 $payment->cc_type($cc_type) if $cc_type;
329 $payment->cc_processor($cc_processor) if $cc_processor;
330 if (!$e->$create_money_method($payment)) {
331 $logger->warn("$create_money_method failed: " .
332 Dumper($payment)); # won't contain CC number.
334 return OpenILS::Event->new(
335 'CREDIT_PROCESSOR_SUCCESS_WO_RECORD',
336 note => "$create_money_method failed"
341 my $evt = _update_patron_credit($e, $patron, $credit);
343 $logger->warn("_update_patron_credit() failed");
345 return OpenILS::Event->new(
346 'CREDIT_PROCESSOR_SUCCESS_WO_RECORD',
347 note => "_update_patron_credit() failed"
351 for my $org_id (keys %orgs) {
352 # calculate penalties for each of the affected orgs
353 $evt = OpenILS::Utils::Penalty->calculate_penalties(
354 $e, $user_id, $org_id
358 "OpenILS::Utils::Penalty::calculate_penalties() failed"
361 return OpenILS::Event->new(
362 'CREDIT_PROCESSOR_SUCCESS_WO_RECORD',
363 note => "OpenILS::Utils::Penalty::calculate_penalties() failed"
372 sub _update_patron_credit {
373 my($e, $patron, $credit) = @_;
374 return undef if $credit == 0;
375 $patron->credit_forward_balance($patron->credit_forward_balance + $credit);
376 return OpenILS::Event->new('NEGATIVE_PATRON_BALANCE') if $patron->credit_forward_balance < 0;
377 $e->update_actor_user($patron) or return $e->die_event;
382 __PACKAGE__->register_method(
383 method => "retrieve_payments",
384 api_name => "open-ils.circ.money.payment.retrieve.all_",
385 notes => "Returns a list of payments attached to a given transaction"
387 sub retrieve_payments {
388 my( $self, $client, $login, $transid ) = @_;
391 $apputils->checksesperm($login, 'VIEW_TRANSACTION');
394 # XXX the logic here is wrong.. we need to check the owner of the transaction
395 # to make sure the requestor has access
397 # XXX grab the view, for each object in the view, grab the real object
399 return $apputils->simplereq(
401 'open-ils.cstore.direct.money.payment.search.atomic', { xact => $transid } );
405 __PACKAGE__->register_method(
406 method => "retrieve_payments2",
408 api_name => "open-ils.circ.money.payment.retrieve.all",
409 notes => "Returns a list of payments attached to a given transaction"
412 sub retrieve_payments2 {
413 my( $self, $client, $login, $transid ) = @_;
415 my $e = new_editor(authtoken=>$login);
416 return $e->event unless $e->checkauth;
417 return $e->event unless $e->allowed('VIEW_TRANSACTION');
420 my $pmnts = $e->search_money_payment({ xact => $transid });
422 my $type = $_->payment_type;
423 my $meth = "retrieve_money_$type";
424 my $p = $e->$meth($_->id) or return $e->event;
425 $p->payment_type($type);
426 $p->cash_drawer($e->retrieve_actor_workstation($p->cash_drawer))
427 if $p->has_field('cash_drawer');
428 push( @payments, $p );
434 __PACKAGE__->register_method(
435 method => "format_payment_receipt",
436 api_name => "open-ils.circ.money.payment_receipt.print",
438 desc => 'Returns a printable receipt for the specified payments',
440 { desc => 'Authentication token', type => 'string'},
441 { desc => 'Payment ID or array of payment IDs', type => 'number' },
444 desc => q/An action_trigger.event object or error event./,
449 __PACKAGE__->register_method(
450 method => "format_payment_receipt",
451 api_name => "open-ils.circ.money.payment_receipt.email",
453 desc => 'Emails a receipt for the specified payments to the user associated with the first payment',
455 { desc => 'Authentication token', type => 'string'},
456 { desc => 'Payment ID or array of payment IDs', type => 'number' },
459 desc => q/Undefined on success, otherwise an error event./,
465 sub format_payment_receipt {
466 my($self, $conn, $auth, $mp_id) = @_;
469 if (ref $mp_id ne 'ARRAY') {
470 $mp_ids = [ $mp_id ];
475 my $for_print = ($self->api_name =~ /print/);
476 my $for_email = ($self->api_name =~ /email/);
477 my $e = new_editor(authtoken => $auth);
478 return $e->event unless $e->checkauth;
481 for my $id (@$mp_ids) {
483 my $payment = $e->retrieve_money_payment([
491 ]) or return OpenILS::Event->new('MP_NOT_FOUND');
493 return $e->event unless $e->allowed('VIEW_TRANSACTION', $payment->xact->usr->home_ou);
495 push @$payments, $payment;
500 return $U->fire_object_event(undef, 'money.format.payment_receipt.print', $payments, $$payments[0]->xact->usr->home_ou);
502 } elsif ($for_email) {
504 for my $p (@$payments) {
505 $U->create_events_for_hook('money.format.payment_receipt.email', $p, $p->xact->usr->home_ou, undef, undef, 1);
512 __PACKAGE__->register_method(
513 method => "create_grocery_bill",
514 api_name => "open-ils.circ.money.grocery.create",
516 Creates a new grocery transaction using the transaction object provided
517 PARAMS: (login_session, money.grocery (mg) object)
520 sub create_grocery_bill {
521 my( $self, $client, $login, $transaction ) = @_;
523 my( $staff, $evt ) = $apputils->checkses($login);
525 $evt = $apputils->check_perms($staff->id,
526 $transaction->billing_location, 'CREATE_TRANSACTION' );
530 $logger->activity("Creating grocery bill " . Dumper($transaction) );
532 $transaction->clear_id;
533 my $session = $apputils->start_db_session;
534 my $transid = $session->request(
535 'open-ils.storage.direct.money.grocery.create', $transaction)->gather(1);
537 throw OpenSRF::EX ("Error creating new money.grocery") unless defined $transid;
539 $logger->debug("Created new grocery transaction $transid");
541 $apputils->commit_db_session($session);
543 my $e = new_editor(xact=>1);
544 $evt = _check_open_xact($e, $transid);
552 __PACKAGE__->register_method(
553 method => 'fetch_reservation',
554 api_name => 'open-ils.circ.booking.reservation.retrieve'
556 sub fetch_reservation {
557 my( $self, $conn, $auth, $id ) = @_;
558 my $e = new_editor(authtoken=>$auth);
559 return $e->event unless $e->checkauth;
560 return $e->event unless $e->allowed('VIEW_TRANSACTION'); # eh.. basically the same permission
561 my $g = $e->retrieve_booking_reservation($id)
566 __PACKAGE__->register_method(
567 method => 'fetch_grocery',
568 api_name => 'open-ils.circ.money.grocery.retrieve'
571 my( $self, $conn, $auth, $id ) = @_;
572 my $e = new_editor(authtoken=>$auth);
573 return $e->event unless $e->checkauth;
574 return $e->event unless $e->allowed('VIEW_TRANSACTION'); # eh.. basically the same permission
575 my $g = $e->retrieve_money_grocery($id)
581 __PACKAGE__->register_method(
582 method => "billing_items",
583 api_name => "open-ils.circ.money.billing.retrieve.all",
586 desc => 'Returns a list of billing items for the given transaction ID. ' .
587 'If the operator is not the owner of the transaction, the VIEW_TRANSACTION permission is required.',
589 { desc => 'Authentication token', type => 'string'},
590 { desc => 'Transaction ID', type => 'number'}
593 desc => 'Transaction object, event on error'
599 my( $self, $client, $login, $transid ) = @_;
601 my( $trans, $evt ) = $U->fetch_billable_xact($transid);
605 ($staff, $evt ) = $apputils->checkses($login);
608 if($staff->id ne $trans->usr) {
609 $evt = $U->check_perms($staff->id, $staff->home_ou, 'VIEW_TRANSACTION');
613 return $apputils->simplereq( 'open-ils.cstore',
614 'open-ils.cstore.direct.money.billing.search.atomic', { xact => $transid } )
618 __PACKAGE__->register_method(
619 method => "billing_items_create",
620 api_name => "open-ils.circ.money.billing.create",
622 Creates a new billing line item
623 PARAMS( login, bill_object (mb) )
626 sub billing_items_create {
627 my( $self, $client, $login, $billing ) = @_;
629 my $e = new_editor(authtoken => $login, xact => 1);
630 return $e->die_event unless $e->checkauth;
631 return $e->die_event unless $e->allowed('CREATE_BILL');
633 my $xact = $e->retrieve_money_billable_transaction($billing->xact)
634 or return $e->die_event;
636 # if the transaction was closed, re-open it
637 if($xact->xact_finish) {
638 $xact->clear_xact_finish;
639 $e->update_money_billable_transaction($xact)
640 or return $e->die_event;
643 my $amt = $billing->amount;
645 $billing->amount($amt);
647 $e->create_money_billing($billing) or return $e->die_event;
648 my $evt = OpenILS::Utils::Penalty->calculate_penalties($e, $xact->usr, $U->xact_org($xact->id));
656 __PACKAGE__->register_method(
657 method => 'void_bill',
658 api_name => 'open-ils.circ.money.billing.void',
661 @param authtoken Login session key
662 @param billid Id for the bill to void. This parameter may be repeated to reference other bills.
663 @return 1 on success, Event on error
667 my( $s, $c, $authtoken, @billids ) = @_;
669 my $e = new_editor( authtoken => $authtoken, xact => 1 );
670 return $e->die_event unless $e->checkauth;
671 return $e->die_event unless $e->allowed('VOID_BILLING');
674 for my $billid (@billids) {
676 my $bill = $e->retrieve_money_billing($billid)
677 or return $e->die_event;
679 my $xact = $e->retrieve_money_billable_transaction($bill->xact)
680 or return $e->die_event;
682 if($U->is_true($bill->voided)) {
684 return OpenILS::Event->new('BILL_ALREADY_VOIDED', payload => $bill);
687 my $org = $U->xact_org($bill->xact, $e);
688 $users{$xact->usr} = {} unless $users{$xact->usr};
689 $users{$xact->usr}->{$org} = 1;
692 $bill->voider($e->requestor->id);
693 $bill->void_time('now');
695 $e->update_money_billing($bill) or return $e->die_event;
696 my $evt = _check_open_xact($e, $bill->xact, $xact);
700 # calculate penalties for all user/org combinations
701 for my $user_id (keys %users) {
702 for my $org_id (keys %{$users{$user_id}}) {
703 OpenILS::Utils::Penalty->calculate_penalties($e, $user_id, $org_id);
711 __PACKAGE__->register_method(
712 method => 'edit_bill_note',
713 api_name => 'open-ils.circ.money.billing.note.edit',
715 Edits the note for a bill
716 @param authtoken Login session key
717 @param note The replacement note for the bills we're editing
718 @param billid Id for the bill to edit the note of. This parameter may be repeated to reference other bills.
719 @return 1 on success, Event on error
723 my( $s, $c, $authtoken, $note, @billids ) = @_;
725 my $e = new_editor( authtoken => $authtoken, xact => 1 );
726 return $e->die_event unless $e->checkauth;
727 return $e->die_event unless $e->allowed('UPDATE_BILL_NOTE');
729 for my $billid (@billids) {
731 my $bill = $e->retrieve_money_billing($billid)
732 or return $e->die_event;
735 # FIXME: Does this get audited? Need some way so that the original creator of the bill does not get credit/blame for the new note.
737 $e->update_money_billing($bill) or return $e->die_event;
744 __PACKAGE__->register_method(
745 method => 'edit_payment_note',
746 api_name => 'open-ils.circ.money.payment.note.edit',
748 Edits the note for a payment
749 @param authtoken Login session key
750 @param note The replacement note for the payments we're editing
751 @param paymentid Id for the payment to edit the note of. This parameter may be repeated to reference other payments.
752 @return 1 on success, Event on error
755 sub edit_payment_note {
756 my( $s, $c, $authtoken, $note, @paymentids ) = @_;
758 my $e = new_editor( authtoken => $authtoken, xact => 1 );
759 return $e->die_event unless $e->checkauth;
760 return $e->die_event unless $e->allowed('UPDATE_PAYMENT_NOTE');
762 for my $paymentid (@paymentids) {
764 my $payment = $e->retrieve_money_payment($paymentid)
765 or return $e->die_event;
767 $payment->note($note);
768 # FIXME: Does this get audited? Need some way so that the original taker of the payment does not get credit/blame for the new note.
770 $e->update_money_payment($payment) or return $e->die_event;
777 sub _check_open_xact {
778 my( $editor, $xactid, $xact ) = @_;
780 # Grab the transaction
781 $xact ||= $editor->retrieve_money_billable_transaction($xactid);
782 return $editor->event unless $xact;
783 $xactid ||= $xact->id;
785 # grab the summary and see how much is owed on this transaction
786 my ($summary) = $U->fetch_mbts($xactid, $editor);
788 # grab the circulation if it is a circ;
789 my $circ = $editor->retrieve_action_circulation($xactid);
791 # If nothing is owed on the transaction but it is still open
792 # and this transaction is not an open circulation, close it
794 ( $summary->balance_owed == 0 and ! $xact->xact_finish ) and
795 ( !$circ or $circ->stop_fines )) {
797 $logger->info("closing transaction ".$xact->id. ' becauase balance_owed == 0');
798 $xact->xact_finish('now');
799 $editor->update_money_billable_transaction($xact)
800 or return $editor->event;
804 # If money is owed or a refund is due on the xact and xact_finish
805 # is set, clear it (to reopen the xact) and update
806 if( $summary->balance_owed != 0 and $xact->xact_finish ) {
807 $logger->info("re-opening transaction ".$xact->id. ' becauase balance_owed != 0');
808 $xact->clear_xact_finish;
809 $editor->update_money_billable_transaction($xact)
810 or return $editor->event;
817 __PACKAGE__->register_method (
818 method => 'fetch_mbts',
820 api_name => 'open-ils.circ.money.billable_xact_summary.retrieve'
823 my( $self, $conn, $auth, $id) = @_;
825 my $e = new_editor(xact => 1, authtoken=>$auth);
826 return $e->event unless $e->checkauth;
827 my ($mbts) = $U->fetch_mbts($id, $e);
829 my $user = $e->retrieve_actor_user($mbts->usr)
830 or return $e->die_event;
832 return $e->die_event unless $e->allowed('VIEW_TRANSACTION', $user->home_ou);
838 __PACKAGE__->register_method(
839 method => 'desk_payments',
840 api_name => 'open-ils.circ.money.org_unit.desk_payments'
843 my( $self, $conn, $auth, $org, $start_date, $end_date ) = @_;
844 my $e = new_editor(authtoken=>$auth);
845 return $e->event unless $e->checkauth;
846 return $e->event unless $e->allowed('VIEW_TRANSACTION', $org);
847 my $data = $U->storagereq(
848 'open-ils.storage.money.org_unit.desk_payments.atomic',
849 $org, $start_date, $end_date );
851 $_->workstation( $_->workstation->name ) for(@$data);
856 __PACKAGE__->register_method(
857 method => 'user_payments',
858 api_name => 'open-ils.circ.money.org_unit.user_payments'
862 my( $self, $conn, $auth, $org, $start_date, $end_date ) = @_;
863 my $e = new_editor(authtoken=>$auth);
864 return $e->event unless $e->checkauth;
865 return $e->event unless $e->allowed('VIEW_TRANSACTION', $org);
866 my $data = $U->storagereq(
867 'open-ils.storage.money.org_unit.user_payments.atomic',
868 $org, $start_date, $end_date );
871 $e->retrieve_actor_card($_->usr->card)->barcode);
873 $e->retrieve_actor_org_unit($_->usr->home_ou)->shortname);
879 __PACKAGE__->register_method(
880 method => 'retrieve_credit_payable_balance',
881 api_name => 'open-ils.circ.credit.payable_balance.retrieve',
884 desc => q/Returns the total amount the patron can pay via credit card/,
886 { desc => 'Authentication token', type => 'string' },
887 { desc => 'User id', type => 'number' }
889 return => { desc => 'The ID of the new provider' }
893 sub retrieve_credit_payable_balance {
894 my ( $self, $conn, $auth, $user_id ) = @_;
895 my $e = new_editor(authtoken => $auth);
896 return $e->event unless $e->checkauth;
898 my $user = $e->retrieve_actor_user($user_id)
901 if($e->requestor->id != $user_id) {
902 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou)
905 my $circ_orgs = $e->json_query({
906 "select" => {circ => ["circ_lib"]},
908 "where" => {usr => $user_id, xact_finish => undef},
912 my $groc_orgs = $e->json_query({
913 "select" => {mg => ["billing_location"]},
915 "where" => {usr => $user_id, xact_finish => undef},
920 for my $org ( @$circ_orgs, @$groc_orgs ) {
921 my $o = $org->{billing_location};
922 $o = $org->{circ_lib} unless $o;
923 next if $hash{$o}; # was $hash{$org}, but that doesn't make sense. $org is a hashref and $o gets added in the next line.
924 $hash{$o} = $U->ou_ancestor_setting_value($o, 'credit.payments.allow', $e);
927 my @credit_orgs = map { $hash{$_} ? ($_) : () } keys %hash;
928 $logger->debug("credit: relevant orgs that allow credit payments => @credit_orgs");
931 OpenILS::Application::AppUtils->simplereq('open-ils.actor',
932 'open-ils.actor.user.transactions.have_charge', $auth, $user_id);
936 for my $xact (@$xact_summaries) {
938 # make two lists and grab them in batch XXX
939 if ( $xact->xact_type eq 'circulation' ) {
940 my $circ = $e->retrieve_action_circulation($xact->id) or return $e->event;
941 next unless grep { $_ == $circ->circ_lib } @credit_orgs;
943 } elsif ($xact->xact_type eq 'grocery') {
944 my $bill = $e->retrieve_money_grocery($xact->id) or return $e->event;
945 next unless grep { $_ == $bill->billing_location } @credit_orgs;
946 } elsif ($xact->xact_type eq 'reservation') {
947 my $bill = $e->retrieve_booking_reservation($xact->id) or return $e->event;
948 next unless grep { $_ == $bill->pickup_lib } @credit_orgs;
950 $sum += $xact->balance_owed();