]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Circ/Money.pm
Patch from Lebbeous Fogle-Weekley to fine-tune credit card payment support in the...
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Circ / Money.pm
1 # ---------------------------------------------------------------
2 # Copyright (C) 2005  Georgia Public Library Service 
3 # Bill Erickson <billserickson@gmail.com>
4
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.
9
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 # ---------------------------------------------------------------
15
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";
22
23 use OpenSRF::EX qw(:try);
24 use OpenILS::Perm;
25 use Data::Dumper;
26 use OpenILS::Event;
27 use OpenSRF::Utils::Logger qw/:logger/;
28 use OpenILS::Utils::CStoreEditor qw/:funcs/;
29 use OpenILS::Utils::Penalty;
30
31 __PACKAGE__->register_method(
32     method => "make_payments",
33     api_name => "open-ils.circ.money.payment",
34     signature => {
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./,
38         params => [
39             {desc => 'Authtoken', type => 'string'},
40             {desc => q/Arguments Hash, supporting the following params:
41                 { 
42                     payment_type
43                     userid
44                     patron_credit
45                     note
46                     cc_args: {
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                         note            (if payments->{note} is blank, use this)
54                     },
55                     check_number
56                     payments: [ 
57                         [trans_id, amt], 
58                         [...]
59                     ], 
60                 }/, type => 'hash'
61             },
62         ]
63     }
64 );
65 sub make_payments {
66     my($self, $client, $auth, $payments) = @_;
67
68     my $e = new_editor(authtoken => $auth, xact => 1);
69     return $e->die_event unless $e->checkauth;
70
71     my $type = $payments->{payment_type};
72     my $user_id = $payments->{userid};
73     my $credit = $payments->{patron_credit} || 0;
74     my $drawer = $e->requestor->wsid;
75     my $note = $payments->{note};
76     my $cc_args = $payments->{cc_args};
77     my $check_number = $payments->{check_number};
78     my $total_paid = 0;
79     my $this_ou = $e->requestor->ws_ou;
80     my %orgs;
81
82     # unless/until determined by payment processor API
83     my ($approval_code, $cc_processor, $cc_type) = (undef,undef,undef);
84
85     my $patron = $e->retrieve_actor_user($user_id) or return $e->die_event;
86
87     # A user is allowed to make credit card payments on his/her own behalf
88     # All other scenarious require permission
89     unless($type eq 'credit_card_payment' and $user_id == $e->requestor->id) {
90         return $e->die_event unless $e->allowed('CREATE_PAYMENT', $patron->home_ou);
91     }
92
93     # first collect the transactions and make sure the transaction
94     # user matches the requested user
95     my %xacts;
96     for my $pay (@{$payments->{payments}}) {
97         my $xact_id = $pay->[0];
98         my $xact = $e->retrieve_money_billable_transaction_summary($xact_id)
99             or return $e->die_event;
100         
101         if($xact->usr != $user_id) {
102             $e->rollback;
103             return OpenILS::Event->new('BAD_PARAMS', note => q/user does not match transaction/);
104         }
105
106         $xacts{$xact_id} = $xact;
107     }
108
109     my @payment_objs;
110
111     for my $pay (@{$payments->{payments}}) {
112         my $transid = $pay->[0];
113         my $amount = $pay->[1];
114         $amount =~ s/\$//og; # just to be safe
115         my $trans = $xacts{$transid};
116
117         $total_paid += $amount;
118
119         $orgs{$U->xact_org($transid, $e)} = 1;
120
121         # A negative payment is a refund.  
122         if( $amount < 0 ) {
123
124             # Negative credit card payments are not allowed
125             if($type eq 'credit_card_payment') {
126                 $e->rollback;
127                 return OpenILS::Event->new(
128                     'BAD_PARAMS', 
129                     note => q/Negative credit card payments not allowed/
130                 );
131             }
132
133             # If the refund causes the transaction balance to exceed 0 dollars, 
134             # we are in effect loaning the patron money.  This is not allowed.
135             if( ($trans->balance_owed - $amount) > 0 ) {
136                 $e->rollback;
137                 return OpenILS::Event->new('REFUND_EXCEEDS_BALANCE');
138             }
139
140             # Otherwise, make sure the refund does not exceed desk payments
141             # This is also not allowed
142             my $desk_total = 0;
143             my $desk_payments = $e->search_money_desk_payment({xact => $transid, voided => 'f'});
144             $desk_total += $_->amount for @$desk_payments;
145
146             if( (-$amount) > $desk_total ) {
147                 $e->rollback;
148                 return OpenILS::Event->new(
149                     'REFUND_EXCEEDS_DESK_PAYMENTS', 
150                     payload => { allowed_refund => $desk_total, submitted_refund => -$amount } );
151             }
152         }
153
154         my $payobj = "Fieldmapper::money::$type";
155         $payobj = $payobj->new;
156
157         $payobj->amount($amount);
158         $payobj->amount_collected($amount);
159         $payobj->xact($transid);
160         $payobj->note($note);
161         if ((not $payobj->note) and ($type eq 'credit_card_payment')) {
162             $payobj->note($cc_args->{note});
163         }
164
165         if ($payobj->has_field('accepting_usr')) { $payobj->accepting_usr($e->requestor->id); }
166         if ($payobj->has_field('cash_drawer')) { $payobj->cash_drawer($drawer); }
167         if ($payobj->has_field('cc_type')) { $payobj->cc_type($cc_args->{type}); }
168         if ($payobj->has_field('check_number')) { $payobj->check_number($check_number); }
169
170         # Store the last 4 digits of the CC number
171         if ($payobj->has_field('cc_number')) {
172             $payobj->cc_number(substr($cc_args->{number}, -4));
173         }
174         if ($payobj->has_field('expire_month')) { $payobj->expire_month($cc_args->{expire_month}); }
175         if ($payobj->has_field('expire_year')) { $payobj->expire_year($cc_args->{expire_year}); }
176         
177         # Note: It is important not to set approval_code
178         # on the fieldmapper object yet.
179
180         push(@payment_objs, $payobj);
181
182     } # all payment objects have been created and inserted. 
183
184     #### NO WRITES TO THE DB ABOVE THIS LINE -- THEY'LL ONLY BE DISCARDED  ###
185     $e->rollback;
186
187     # After we try to externally process a credit card (if desired), we'll
188     # open a new transaction.  We cannot leave one open while credit card
189     # processing might be happening, as it can easily time out the database
190     # transaction.
191     if($type eq 'credit_card_payment') {
192         $approval_code = $cc_args->{approval_code};
193         # If an approval code was not given, we'll need
194         # to call to the payment processor ourselves.
195         if ($cc_args->{where_process} == 1) {
196             return OpenILS::Event->new('BAD_PARAMS', note => 'Need CC number')
197                 if not $cc_args->{number};
198             my $response = $apputils->simplereq(
199                 'open-ils.credit',
200                 'open-ils.credit.process',
201                 {
202                     "desc" => $cc_args->{note},
203                     "amount" => $total_paid,
204                     "patron_id" => $user_id,
205                     "cc" => $cc_args->{number},
206                     "expiration" => sprintf(
207                         "%02d-%04d",
208                         $cc_args->{expire_month},
209                         $cc_args->{expire_year}
210                     ),
211                     "ou" => $this_ou
212                 }
213             );
214
215             if (exists $response->{ilsevent}) {
216                 return $response;
217             }
218             if ($response->{statusCode} != 200) {
219                 $logger->info("Credit card payment for user $user_id " .
220                     "failed with message: " . $response->{statusText});
221                 return OpenILS::Event->new(
222                     'CREDIT_PROCESSOR_DECLINED_TRANSACTION',
223                     note => $response->{statusText}
224                 );
225             }
226             $approval_code = $response->{approvalCode};
227             $cc_type = $response->{cardType};
228             $cc_processor = $response->{processor};
229             $logger->info("Credit card payment processing for " .
230                 "user $user_id succeeded");
231         }
232         else {
233             return OpenILS::Event->new(
234                 'BAD_PARAMS', note => 'Need approval code'
235             ) if not $cc_args->{approval_code};
236         }
237     }
238
239     ### RE-OPEN TRANSACTION HERE ###
240     $e->xact_begin;
241
242     # create payment records
243     my $create_money_method = "create_money_" . $type;
244     for my $payment (@payment_objs) {
245         # update the transaction if it's done
246         my $amount = $payment->amount;
247         my $transid = $payment->xact;
248         my $trans = $xacts{$transid};
249         if( (my $cred = ($trans->balance_owed - $amount)) <= 0 ) {
250             # Any overpay on this transaction goes directly into patron
251             # credit making payment with existing patron credit.
252             $credit -= $amount if $type eq 'credit_payment';
253
254             $cred = -$cred;
255             $credit += $cred;
256             my $circ = $e->retrieve_action_circulation($transid);
257
258             if(!$circ || $circ->stop_fines) {
259                 # If this is a circulation, we can't close the transaction
260                 # unless stop_fines is set.
261                 $trans = $e->retrieve_money_billable_transaction($transid);
262                 $trans->xact_finish("now");
263                 if (!$e->update_money_billable_transaction($trans)) {
264                     $logger->warn("update_money_billable_transaction() " .
265                         "failed");
266                     $e->rollback;
267                     return OpenILS::Event->new(
268                         'CREDIT_PROCESSOR_SUCCESS_WO_RECORD',
269                         note => 'update_money_billable_transaction() failed'
270                     );
271                 }
272             }
273         }
274
275         $payment->approval_code($approval_code) if $approval_code;
276         $payment->cc_type($cc_type) if $cc_type;
277         $payment->cc_processor($cc_processor) if $cc_processor;
278         if (!$e->$create_money_method($payment)) {
279             $logger->warn("$create_money_method failed: " .
280                 Dumper($payment)); # won't contain CC number.
281             $e->rollback;
282             return OpenILS::Event->new(
283                 'CREDIT_PROCESSOR_SUCCESS_WO_RECORD',
284                 note => "$create_money_method failed"
285             );
286         }
287     }
288
289     my $evt = _update_patron_credit($e, $patron, $credit);
290     if ($evt) {
291         $logger->warn("_update_patron_credit() failed");
292         $e->rollback;
293         return OpenILS::Event->new(
294             'CREDIT_PROCESSOR_SUCCESS_WO_RECORD',
295             note => "_update_patron_credit() failed"
296         );
297     }
298
299     for my $org_id (keys %orgs) {
300         # calculate penalties for each of the affected orgs
301         $evt = OpenILS::Utils::Penalty->calculate_penalties(
302             $e, $user_id, $org_id
303         );
304         if ($evt) {
305             $logger->warn(
306                 "OpenILS::Utils::Penalty::calculate_penalties() failed"
307             );
308             $e->rollback;
309             return OpenILS::Event->new(
310                 'CREDIT_PROCESSOR_SUCCESS_WO_RECORD',
311                 note => "OpenILS::Utils::Penalty::calculate_penalties() failed"
312             );
313         }
314     }
315
316     $e->commit;
317     return 1;
318 }
319
320 sub _update_patron_credit {
321     my($e, $patron, $credit) = @_;
322     return undef if $credit == 0;
323     $patron->credit_forward_balance($patron->credit_forward_balance + $credit);
324     return OpenILS::Event->new('NEGATIVE_PATRON_BALANCE') if $patron->credit_forward_balance < 0;
325     $e->update_actor_user($patron) or return $e->die_event;
326     return undef;
327 }
328
329
330 __PACKAGE__->register_method(
331     method    => "retrieve_payments",
332     api_name    => "open-ils.circ.money.payment.retrieve.all_",
333     notes        => "Returns a list of payments attached to a given transaction"
334     );
335 sub retrieve_payments {
336     my( $self, $client, $login, $transid ) = @_;
337
338     my( $staff, $evt ) =  
339         $apputils->checksesperm($login, 'VIEW_TRANSACTION');
340     return $evt if $evt;
341
342     # XXX the logic here is wrong.. we need to check the owner of the transaction
343     # to make sure the requestor has access
344
345     # XXX grab the view, for each object in the view, grab the real object
346
347     return $apputils->simplereq(
348         'open-ils.cstore',
349         'open-ils.cstore.direct.money.payment.search.atomic', { xact => $transid } );
350 }
351
352
353 __PACKAGE__->register_method(
354     method    => "retrieve_payments2",
355     authoritative => 1,
356     api_name    => "open-ils.circ.money.payment.retrieve.all",
357     notes        => "Returns a list of payments attached to a given transaction"
358     );
359     
360 sub retrieve_payments2 {
361     my( $self, $client, $login, $transid ) = @_;
362
363     my $e = new_editor(authtoken=>$login);
364     return $e->event unless $e->checkauth;
365     return $e->event unless $e->allowed('VIEW_TRANSACTION');
366
367     my @payments;
368     my $pmnts = $e->search_money_payment({ xact => $transid });
369     for( @$pmnts ) {
370         my $type = $_->payment_type;
371         my $meth = "retrieve_money_$type";
372         my $p = $e->$meth($_->id) or return $e->event;
373         $p->payment_type($type);
374         $p->cash_drawer($e->retrieve_actor_workstation($p->cash_drawer))
375             if $p->has_field('cash_drawer');
376         push( @payments, $p );
377     }
378
379     return \@payments;
380 }
381
382
383 __PACKAGE__->register_method(
384     method    => "create_grocery_bill",
385     api_name    => "open-ils.circ.money.grocery.create",
386     notes        => <<"    NOTE");
387     Creates a new grocery transaction using the transaction object provided
388     PARAMS: (login_session, money.grocery (mg) object)
389     NOTE
390
391 sub create_grocery_bill {
392     my( $self, $client, $login, $transaction ) = @_;
393
394     my( $staff, $evt ) = $apputils->checkses($login);
395     return $evt if $evt;
396     $evt = $apputils->check_perms($staff->id, 
397         $transaction->billing_location, 'CREATE_TRANSACTION' );
398     return $evt if $evt;
399
400
401     $logger->activity("Creating grocery bill " . Dumper($transaction) );
402
403     $transaction->clear_id;
404     my $session = $apputils->start_db_session;
405     my $transid = $session->request(
406         'open-ils.storage.direct.money.grocery.create', $transaction)->gather(1);
407
408     throw OpenSRF::EX ("Error creating new money.grocery") unless defined $transid;
409
410     $logger->debug("Created new grocery transaction $transid");
411     
412     $apputils->commit_db_session($session);
413
414     my $e = new_editor(xact=>1);
415     $evt = _check_open_xact($e, $transid);
416     return $evt if $evt;
417     $e->commit;
418
419     return $transid;
420 }
421
422
423 __PACKAGE__->register_method(
424     method => 'fetch_grocery',
425     api_name => 'open-ils.circ.money.grocery.retrieve'
426 );
427 sub fetch_grocery {
428     my( $self, $conn, $auth, $id ) = @_;
429     my $e = new_editor(authtoken=>$auth);
430     return $e->event unless $e->checkauth;
431     return $e->event unless $e->allowed('VIEW_TRANSACTION'); # eh.. basically the same permission
432     my $g = $e->retrieve_money_grocery($id)
433         or return $e->event;
434     return $g;
435 }
436
437
438 __PACKAGE__->register_method(
439     method    => "billing_items",
440     authoritative => 1,
441     api_name    => "open-ils.circ.money.billing.retrieve.all",
442     notes        =><<"    NOTE");
443     Returns a list of billing items for the given transaction.
444     PARAMS( login, transaction_id )
445     NOTE
446
447 sub billing_items {
448     my( $self, $client, $login, $transid ) = @_;
449
450     my( $trans, $evt ) = $U->fetch_billable_xact($transid);
451     return $evt if $evt;
452
453     my $staff;
454     ($staff, $evt ) = $apputils->checkses($login);
455     return $evt if $evt;
456
457     if($staff->id ne $trans->usr) {
458         $evt = $U->check_perms($staff->id, $staff->home_ou, 'VIEW_TRANSACTION');
459         return $evt if $evt;
460     }
461     
462     return $apputils->simplereq( 'open-ils.cstore',
463         'open-ils.cstore.direct.money.billing.search.atomic', { xact => $transid } )
464 }
465
466
467 __PACKAGE__->register_method(
468     method    => "billing_items_create",
469     api_name    => "open-ils.circ.money.billing.create",
470     notes        =><<"    NOTE");
471     Creates a new billing line item
472     PARAMS( login, bill_object (mb) )
473     NOTE
474
475 sub billing_items_create {
476     my( $self, $client, $login, $billing ) = @_;
477
478     my $e = new_editor(authtoken => $login, xact => 1);
479     return $e->die_event unless $e->checkauth;
480     return $e->die_event unless $e->allowed('CREATE_BILL');
481
482     my $xact = $e->retrieve_money_billable_transaction($billing->xact)
483         or return $e->die_event;
484
485     # if the transaction was closed, re-open it
486     if($xact->xact_finish) {
487         $xact->clear_xact_finish;
488         $e->update_money_billable_transaction($xact)
489             or return $e->die_event;
490     }
491
492     my $amt = $billing->amount;
493     $amt =~ s/\$//og;
494     $billing->amount($amt);
495
496     $e->create_money_billing($billing) or return $e->die_event;
497     my $evt = OpenILS::Utils::Penalty->calculate_penalties($e, $xact->usr, $U->xact_org($xact->id));
498     return $evt if $evt;
499     $e->commit;
500
501     return $billing->id;
502 }
503
504
505 __PACKAGE__->register_method(
506     method        =>    'void_bill',
507     api_name        => 'open-ils.circ.money.billing.void',
508     signature    => q/
509         Voids a bill
510         @param authtoken Login session key
511         @param billid Id for the bill to void.  This parameter may be repeated to reference other bills.
512         @return 1 on success, Event on error
513     /
514 );
515 sub void_bill {
516     my( $s, $c, $authtoken, @billids ) = @_;
517
518     my $e = new_editor( authtoken => $authtoken, xact => 1 );
519     return $e->die_event unless $e->checkauth;
520     return $e->die_event unless $e->allowed('VOID_BILLING');
521
522     my %users;
523     for my $billid (@billids) {
524
525         my $bill = $e->retrieve_money_billing($billid)
526             or return $e->die_event;
527
528         my $xact = $e->retrieve_money_billable_transaction($bill->xact)
529             or return $e->die_event;
530
531         if($U->is_true($bill->voided)) {
532             $e->rollback;
533             return OpenILS::Event->new('BILL_ALREADY_VOIDED', payload => $bill);
534         }
535
536         my $org = $U->xact_org($bill->xact, $e);
537         $users{$xact->usr} = {} unless $users{$xact->usr};
538         $users{$xact->usr}->{$org} = 1;
539
540         $bill->voided('t');
541         $bill->voider($e->requestor->id);
542         $bill->void_time('now');
543     
544         $e->update_money_billing($bill) or return $e->die_event;
545         my $evt = _check_open_xact($e, $bill->xact, $xact);
546         return $evt if $evt;
547     }
548
549     # calculate penalties for all user/org combinations
550     for my $user_id (keys %users) {
551         for my $org_id (keys %{$users{$user_id}}) {
552             OpenILS::Utils::Penalty->calculate_penalties($e, $user_id, $org_id);
553         }
554     }
555     $e->commit;
556     return 1;
557 }
558
559
560 __PACKAGE__->register_method(
561     method        =>    'edit_bill_note',
562     api_name        => 'open-ils.circ.money.billing.note.edit',
563     signature    => q/
564         Edits the note for a bill
565         @param authtoken Login session key
566         @param note The replacement note for the bills we're editing
567         @param billid Id for the bill to edit the note of.  This parameter may be repeated to reference other bills.
568         @return 1 on success, Event on error
569     /
570 );
571 sub edit_bill_note {
572     my( $s, $c, $authtoken, $note, @billids ) = @_;
573
574     my $e = new_editor( authtoken => $authtoken, xact => 1 );
575     return $e->die_event unless $e->checkauth;
576     return $e->die_event unless $e->allowed('UPDATE_BILL_NOTE');
577
578     for my $billid (@billids) {
579
580         my $bill = $e->retrieve_money_billing($billid)
581             or return $e->die_event;
582
583         $bill->note($note);
584         # 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.
585     
586         $e->update_money_billing($bill) or return $e->die_event;
587     }
588     $e->commit;
589     return 1;
590 }
591
592
593 __PACKAGE__->register_method(
594     method        =>    'edit_payment_note',
595     api_name        => 'open-ils.circ.money.payment.note.edit',
596     signature    => q/
597         Edits the note for a payment
598         @param authtoken Login session key
599         @param note The replacement note for the payments we're editing
600         @param paymentid Id for the payment to edit the note of.  This parameter may be repeated to reference other payments.
601         @return 1 on success, Event on error
602     /
603 );
604 sub edit_payment_note {
605     my( $s, $c, $authtoken, $note, @paymentids ) = @_;
606
607     my $e = new_editor( authtoken => $authtoken, xact => 1 );
608     return $e->die_event unless $e->checkauth;
609     return $e->die_event unless $e->allowed('UPDATE_PAYMENT_NOTE');
610
611     for my $paymentid (@paymentids) {
612
613         my $payment = $e->retrieve_money_payment($paymentid)
614             or return $e->die_event;
615
616         $payment->note($note);
617         # 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.
618     
619         $e->update_money_payment($payment) or return $e->die_event;
620     }
621
622     $e->commit;
623     return 1;
624 }
625
626 sub _check_open_xact {
627     my( $editor, $xactid, $xact ) = @_;
628
629     # Grab the transaction
630     $xact ||= $editor->retrieve_money_billable_transaction($xactid);
631     return $editor->event unless $xact;
632     $xactid ||= $xact->id;
633
634     # grab the summary and see how much is owed on this transaction
635     my ($summary) = $U->fetch_mbts($xactid, $editor);
636
637     # grab the circulation if it is a circ;
638     my $circ = $editor->retrieve_action_circulation($xactid);
639
640     # If nothing is owed on the transaction but it is still open
641     # and this transaction is not an open circulation, close it
642     if( 
643         ( $summary->balance_owed == 0 and ! $xact->xact_finish ) and
644         ( !$circ or $circ->stop_fines )) {
645
646         $logger->info("closing transaction ".$xact->id. ' becauase balance_owed == 0');
647         $xact->xact_finish('now');
648         $editor->update_money_billable_transaction($xact)
649             or return $editor->event;
650         return undef;
651     }
652
653     # If money is owed or a refund is due on the xact and xact_finish
654     # is set, clear it (to reopen the xact) and update
655     if( $summary->balance_owed != 0 and $xact->xact_finish ) {
656         $logger->info("re-opening transaction ".$xact->id. ' becauase balance_owed != 0');
657         $xact->clear_xact_finish;
658         $editor->update_money_billable_transaction($xact)
659             or return $editor->event;
660         return undef;
661     }
662     return undef;
663 }
664
665
666 __PACKAGE__->register_method (
667     method => 'fetch_mbts',
668     authoritative => 1,
669     api_name => 'open-ils.circ.money.billable_xact_summary.retrieve'
670 );
671 sub fetch_mbts {
672     my( $self, $conn, $auth, $id) = @_;
673
674     my $e = new_editor(xact => 1, authtoken=>$auth);
675     return $e->event unless $e->checkauth;
676     my ($mbts) = $U->fetch_mbts($id, $e);
677
678     my $user = $e->retrieve_actor_user($mbts->usr)
679         or return $e->die_event;
680
681     return $e->die_event unless $e->allowed('VIEW_TRANSACTION', $user->home_ou);
682     $e->rollback;
683     return $mbts
684 }
685
686
687 __PACKAGE__->register_method(
688     method => 'desk_payments',
689     api_name => 'open-ils.circ.money.org_unit.desk_payments'
690 );
691 sub desk_payments {
692     my( $self, $conn, $auth, $org, $start_date, $end_date ) = @_;
693     my $e = new_editor(authtoken=>$auth);
694     return $e->event unless $e->checkauth;
695     return $e->event unless $e->allowed('VIEW_TRANSACTION', $org);
696     my $data = $U->storagereq(
697         'open-ils.storage.money.org_unit.desk_payments.atomic',
698         $org, $start_date, $end_date );
699
700     $_->workstation( $_->workstation->name ) for(@$data);
701     return $data;
702 }
703
704
705 __PACKAGE__->register_method(
706     method => 'user_payments',
707     api_name => 'open-ils.circ.money.org_unit.user_payments'
708 );
709
710 sub user_payments {
711     my( $self, $conn, $auth, $org, $start_date, $end_date ) = @_;
712     my $e = new_editor(authtoken=>$auth);
713     return $e->event unless $e->checkauth;
714     return $e->event unless $e->allowed('VIEW_TRANSACTION', $org);
715     my $data = $U->storagereq(
716         'open-ils.storage.money.org_unit.user_payments.atomic',
717         $org, $start_date, $end_date );
718     for(@$data) {
719         $_->usr->card(
720             $e->retrieve_actor_card($_->usr->card)->barcode);
721         $_->usr->home_ou(
722             $e->retrieve_actor_org_unit($_->usr->home_ou)->shortname);
723     }
724     return $data;
725 }
726
727 1;