LP 1198465: Initial tests for conditional negative balances
[working/Evergreen.git] / Open-ILS / src / perlmods / live_t / 09-lp1198465_neg_balances.t
1 #!perl
2
3 use Test::More tests => 64;
4
5 diag("Test features of Conditional Negative Balances code.");
6
7 use constant WORKSTATION_NAME => 'BR1-test-09-lp1198465_neg_balances.t';
8 use constant WORKSTATION_LIB => 4;
9
10 use strict; use warnings;
11
12 use DateTime;
13 use DateTime::Format::ISO8601;
14 use OpenSRF::Utils qw/cleanse_ISO8601/;
15 use OpenILS::Utils::TestUtils;
16 my $script = OpenILS::Utils::TestUtils->new();
17 use Data::Dumper;
18
19 our $apputils   = "OpenILS::Application::AppUtils";
20
21 my ($patron_id, $patron_usrname, $xact_id, $item_id, $item_barcode);
22 my ($summary, $payment_blob, $pay_resp, $item_req, $checkin_resp);
23 my $user_obj;
24 my $storage_ses = $script->session('open-ils.storage');
25
26
27 sub retrieve_patron {
28     my $patron_id = shift;
29
30     my $user_req = $storage_ses->request('open-ils.storage.direct.actor.user.retrieve', $patron_id);
31     if (my $user_resp = $user_req->recv) {
32         if (my $patron_obj = $user_resp->content) {
33             return $patron_obj;
34         }
35     }
36     return 0;
37 }
38
39 sub fetch_billable_xact_summary {
40     my $xact_id = shift;
41     my $ses = $script->session('open-ils.cstore');
42     my $req = $ses->request(
43         'open-ils.cstore.direct.money.billable_transaction_summary.retrieve',
44         $xact_id);
45
46     if (my $resp = $req->recv) {
47         return $resp->content;
48     } else {
49         return 0;
50     }
51 }
52
53 sub pay_bills {
54     my $payment_blob = shift;
55     my $resp = $apputils->simplereq(
56         'open-ils.circ',
57         'open-ils.circ.money.payment',
58         $script->authtoken,
59         $payment_blob,
60         $user_obj->last_xact_id
61     );
62
63     #refetch user_obj to get latest last_xact_id
64     $user_obj = retrieve_patron($patron_id)
65         or die 'Could not refetch patron';
66
67     return $resp;
68 }
69
70 sub void_bills {
71     my $billing_ids = shift; #array ref
72     my $resp = $apputils->simplereq(
73         'open-ils.circ',
74         'open-ils.circ.money.billing.void',
75         $script->authtoken,
76         @$billing_ids
77     );
78
79     return $resp;
80 }
81
82 #----------------------------------------------------------------
83 # The tests...  assumes stock sample data, full-auto install by
84 # eg_wheezy_installer.sh, etc.
85 #----------------------------------------------------------------
86
87 # Connect to Evergreen
88 $script->authenticate({
89     username => 'admin',
90     password => 'demo123',
91     type => 'staff'});
92 ok( $script->authtoken, 'Have an authtoken');
93
94 my $ws = $script->register_workstation(WORKSTATION_NAME,WORKSTATION_LIB);
95 ok( ! ref $ws, 'Registered a new workstation');
96
97 $script->logout();
98 $script->authenticate({
99     username => 'admin',
100     password => 'demo123',
101     type => 'staff',
102     workstation => WORKSTATION_NAME});
103 ok( $script->authtoken, 'Have an authtoken associated with the workstation');
104
105
106 ### TODO: verify that stock data is ready for testing
107
108 ### Setup Org Unit Settings that apply to all test cases
109
110 my $org_id = 1; #CONS
111 my $settings = {
112     'circ.max_item_price' => 50,
113     'circ.min_item_price' => 50,
114     'circ.void_lost_on_checkin' => 1
115 };
116
117 $apputils->simplereq(
118     'open-ils.actor',
119     'open-ils.actor.org_unit.settings.update',
120     $script->authtoken,
121     $org_id,
122     $settings
123 );
124
125 # Setup first patron
126 $patron_id = 4;
127 $patron_usrname = '99999355250';
128
129 # Look up the patron
130 if ($user_obj = retrieve_patron($patron_id)) {
131     is(
132         ref $user_obj,
133         'Fieldmapper::actor::user',
134         'open-ils.storage.direct.actor.user.retrieve returned aou object'
135     );
136     is(
137         $user_obj->usrname,
138         $patron_usrname,
139         'Patron with id = ' . $patron_id . ' has username ' . $patron_usrname
140     );
141 }
142
143
144 ##############################
145 # 1. No Prohibit Negative Balance Settings Are Enabled, Payment Made
146 ##############################
147
148 ### Setup use case variables
149 $xact_id = 1;
150 $item_id = 2;
151 $item_barcode = 'CONC4000037';
152 $org_id = 1; #CONS
153
154 $summary = fetch_billable_xact_summary($xact_id);
155 ok( $summary, 'CASE 1: Found the transaction summary');
156 is(
157     $summary->balance_owed,
158     '50.00',
159     'Starting balance owed is 50.00 for lost item'
160 );
161
162 ### pay the whole bill
163 $payment_blob = {
164     userid => $patron_id,
165     note => '09-lp1198465_neg_balances.t',
166     payment_type => 'cash_payment',
167     patron_credit => '0.00',
168     payments => [ [ $xact_id, '50.00' ] ]
169 };
170 $pay_resp = pay_bills($payment_blob);
171
172 is(
173     scalar( @{ $pay_resp->{payments} } ),
174     1,
175     'Payment response included one payment id'
176 );
177
178 $summary = fetch_billable_xact_summary($xact_id);
179 is(
180     $summary->balance_owed,
181     '0.00',
182     'Remaining balance of 0.00 after payment'
183 );
184
185 ### check-in the lost copy
186
187 $item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
188 if (my $item_resp = $item_req->recv) {
189     if (my $item = $item_resp->content) {
190         is(
191             ref $item,
192             'Fieldmapper::asset::copy',
193             'open-ils.storage.direct.asset.copy.retrieve returned acp object'
194         );
195         is(
196             $item->status,
197             3,
198             'Item with id = ' . $item_id . ' has status of LOST'
199         );
200     }
201 }
202
203 $checkin_resp = $script->do_checkin_override({
204     barcode => $item_barcode});
205 is(
206     $checkin_resp->{ilsevent},
207     0,
208     'Checkin returned a SUCCESS event'
209 );
210
211 $item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
212 if (my $item_resp = $item_req->recv) {
213     if (my $item = $item_resp->content) {
214         ok(
215             $item->status == 7 || $item->status == 0,
216             'Item with id = ' . $item_id . ' has status of Reshelving or Available after fresh Storage request'
217         );
218     }
219 }
220
221 ### verify ending state
222
223 $summary = fetch_billable_xact_summary($xact_id);
224 is(
225     $summary->balance_owed,
226     '-50.00',
227     'Patron has a negative balance (credit) of 50.00 due to overpayment'
228 );
229
230
231 ##############################
232 # 2. Negative Balance Settings Are Unset, No Payment Made
233 ##############################
234
235 ### Setup use case variables
236 $xact_id = 2;
237 $item_id = 3;
238 $item_barcode = 'CONC4000038';
239 $org_id = 1; #CONS
240
241 $summary = fetch_billable_xact_summary($xact_id);
242 ok( $summary, 'CASE 2: Found the transaction summary');
243 is(
244     $summary->balance_owed,
245     '50.00',
246     'Starting balance owed is 50.00 for lost item'
247 );
248
249 ### check-in the lost copy
250
251 $item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
252 if (my $item_resp = $item_req->recv) {
253     if (my $item = $item_resp->content) {
254         is(
255             $item->status,
256             3,
257             'Item with id = ' . $item_id . ' has status of LOST'
258         );
259     }
260 }
261
262 $checkin_resp = $script->do_checkin_override({
263     barcode => $item_barcode});
264 is(
265     $checkin_resp->{ilsevent},
266     0,
267     'Checkin returned a SUCCESS event'
268 );
269
270 $item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
271 if (my $item_resp = $item_req->recv) {
272     if (my $item = $item_resp->content) {
273         ok(
274             $item->status == 7 || $item->status == 0,
275             'Item with id = ' . $item_id . ' has status of Reshelving or Available after fresh Storage request'
276         );
277     }
278 }
279
280 ### verify ending state
281
282 $summary = fetch_billable_xact_summary($xact_id);
283 is(
284     $summary->balance_owed,
285     '0.00',
286     'Patron has a balance of 0.00'
287 );
288
289
290 ##############################
291 # 3. Basic No Negative Balance Test
292 ##############################
293
294 ### Setup use case variables
295 $xact_id = 3;
296 $item_id = 4;
297 $item_barcode = 'CONC4000039';
298 $org_id = 1; #CONS
299
300 # Setup Org Unit Settings
301 $settings = {
302     'bill.prohibit_negative_balance_default' => 1
303 };
304 $apputils->simplereq(
305     'open-ils.actor',
306     'open-ils.actor.org_unit.settings.update',
307     $script->authtoken,
308     $org_id,
309     $settings
310 );
311
312 $summary = fetch_billable_xact_summary($xact_id);
313 ok( $summary, 'CASE 3: Found the transaction summary');
314 is(
315     $summary->balance_owed,
316     '50.00',
317     'Starting balance owed is 50.00 for lost item'
318 );
319
320 ### check-in the lost copy
321
322 $item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
323 if (my $item_resp = $item_req->recv) {
324     if (my $item = $item_resp->content) {
325         is(
326             $item->status,
327             3,
328             'Item with id = ' . $item_id . ' has status of LOST'
329         );
330     }
331 }
332
333 $checkin_resp = $script->do_checkin_override({
334     barcode => $item_barcode});
335 is(
336     $checkin_resp->{ilsevent},
337     0,
338     'Checkin returned a SUCCESS event'
339 );
340
341 $item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
342 if (my $item_resp = $item_req->recv) {
343     if (my $item = $item_resp->content) {
344         ok(
345             $item->status == 7 || $item->status == 0,
346             'Item with id = ' . $item_id . ' has status of Reshelving or Available after fresh Storage request'
347         );
348     }
349 }
350
351 ### verify ending state
352
353 $summary = fetch_billable_xact_summary($xact_id);
354 is(
355     $summary->balance_owed,
356     '0.00',
357     'Patron has a balance of 0.00 (negative balance prohibited)'
358 );
359
360 ##############################
361 # 4. Prohibit Negative Balances with Partial Payment
362 ##############################
363
364 ### Setup use case variables
365 $xact_id = 4;
366 $item_id = 5;
367 $item_barcode = 'CONC4000040';
368 $org_id = 1; #CONS
369
370 # Setup Org Unit Settings
371 # already set: 'bill.prohibit_negative_balance_default' => 1
372
373 $summary = fetch_billable_xact_summary($xact_id);
374 ok( $summary, 'CASE 4: Found the transaction summary');
375 is(
376     $summary->balance_owed,
377     '50.00',
378     'Starting balance owed is 50.00 for lost item'
379 );
380
381 ### confirm the copy is lost
382 $item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
383 if (my $item_resp = $item_req->recv) {
384     if (my $item = $item_resp->content) {
385         is(
386             $item->status,
387             3,
388             'Item with id = ' . $item_id . ' has status of LOST'
389         );
390     }
391 }
392
393 ### partially pay the bill
394 $payment_blob = {
395     userid => $patron_id,
396     note => '09-lp1198465_neg_balances.t',
397     payment_type => 'cash_payment',
398     patron_credit => '0.00',
399     payments => [ [ $xact_id, '10.00' ] ]
400 };
401 $pay_resp = pay_bills($payment_blob);
402
403 is(
404     scalar( @{ $pay_resp->{payments} } ),
405     1,
406     'Payment response included one payment id'
407 );
408
409 $summary = fetch_billable_xact_summary($xact_id);
410 is(
411     $summary->balance_owed,
412     '40.00',
413     'Remaining balance of 40.00 after payment'
414 );
415
416 ### check-in the lost copy
417 $checkin_resp = $script->do_checkin_override({
418     barcode => $item_barcode});
419 is(
420     $checkin_resp->{ilsevent},
421     0,
422     'Checkin returned a SUCCESS event'
423 );
424
425 $item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
426 if (my $item_resp = $item_req->recv) {
427     if (my $item = $item_resp->content) {
428         ok(
429             $item->status == 7 || $item->status == 0,
430             'Item with id = ' . $item_id . ' has status of Reshelving or Available after fresh Storage request'
431         );
432     }
433 }
434
435 ### verify ending state
436
437 $summary = fetch_billable_xact_summary($xact_id);
438 is(
439     $summary->balance_owed,
440     '0.00',
441     'Patron has a balance of 0.00 (negative balance prohibited)'
442 );
443
444
445 ###############################
446 ## 11. Manually voiding lost book fee does not result in negative balances
447 ###############################
448 #
449 #### Setup use case variables
450 #$xact_id = 5;
451 #$item_id = 6;
452 #$item_barcode = 'CONC4000040';
453 #$org_id = 1; #CONS
454 #
455 ## Setup Org Unit Settings
456 ## already set: 'bill.prohibit_negative_balance_default' => 1
457 #
458 #$summary = fetch_billable_xact_summary($xact_id);
459 #ok( $summary, 'CASE 11: Found the transaction summary');
460 #is(
461 #    $summary->balance_owed,
462 #    '50.00',
463 #    'Starting balance owed is 50.00 for lost item'
464 #);
465 #
466 #### confirm the copy is lost
467 #$item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
468 #if (my $item_resp = $item_req->recv) {
469 #    if (my $item = $item_resp->content) {
470 #        is(
471 #            $item->status,
472 #            3,
473 #            'Item with id = ' . $item_id . ' has status of LOST'
474 #        );
475 #    }
476 #}
477 #
478 #### partially pay the bill
479 #$payment_blob = {
480 #    userid => $patron_id,
481 #    note => '09-lp1198465_neg_balances.t',
482 #    payment_type => 'cash_payment',
483 #    patron_credit => '0.00',
484 #    payments => [ [ $xact_id, '10.00' ] ]
485 #};
486 #$pay_resp = pay_bills($payment_blob);
487 #
488 #is(
489 #    scalar( @{ $pay_resp->{payments} } ),
490 #    1,
491 #    'Payment response included one payment id'
492 #);
493 #
494 #$summary = fetch_billable_xact_summary($xact_id);
495 #is(
496 #    $summary->balance_owed,
497 #    '40.00',
498 #    'Remaining balance of 40.00 after payment'
499 #);
500 #
501 #### TODO: manually void "the rest" of the bill (i.e. prevent neg bal)
502 #### XXX: HARDCODING billing id for now; should look up the LOST bill for this xact?
503 #my @billing_ids = (6);
504 #my $void_resp = void_bills(\@billing_ids);
505 #
506 #is(
507 #    $void_resp,
508 #    '1',
509 #    'Voiding was successful'
510 #);
511 #
512 #### verify ending state
513 #
514 #$summary = fetch_billable_xact_summary($xact_id);
515 #is(
516 #    $summary->balance_owed,
517 #    '0.00',
518 #    'Patron has a balance of 0.00 (negative balance prohibited)'
519 #);
520
521
522 ##############################
523 # 12. Test negative balance settings on fines
524 ##############################
525
526 # Setup next patron
527 $patron_id = 5;
528 $patron_usrname = '99999387993';
529
530 # Look up the patron
531 if ($user_obj = retrieve_patron($patron_id)) {
532     is(
533         ref $user_obj,
534         'Fieldmapper::actor::user',
535         'open-ils.storage.direct.actor.user.retrieve returned aou object'
536     );
537     is(
538         $user_obj->usrname,
539         $patron_usrname,
540         'Patron with id = ' . $patron_id . ' has username ' . $patron_usrname
541     );
542 }
543
544 ### Setup use case variables
545 $xact_id = 7;
546 $item_id = 8;
547 $item_barcode = 'CONC4000043';
548 $org_id = 1; #CONS
549
550 # Setup Org Unit Settings
551 # already set: 'bill.prohibit_negative_balance_default' => 1
552
553 $summary = fetch_billable_xact_summary($xact_id);
554 ok( $summary, 'CASE 12: Found the transaction summary');
555 is(
556     $summary->balance_owed,
557     '0.70',
558     'Starting balance owed is 0.70 for overdue fines'
559 );
560
561 ### partially pay the bill
562 $payment_blob = {
563     userid => $patron_id,
564     note => '09-lp1198465_neg_balances.t',
565     payment_type => 'cash_payment',
566     patron_credit => '0.00',
567     payments => [ [ $xact_id, '0.20' ] ]
568 };
569 $pay_resp = pay_bills($payment_blob);
570
571 is(
572     scalar( @{ $pay_resp->{payments} } ),
573     1,
574     'Payment response included one payment id'
575 );
576
577 $summary = fetch_billable_xact_summary($xact_id);
578 is(
579     $summary->balance_owed,
580     '0.50',
581     'Remaining balance of 0.50 after payment'
582 );
583
584 ### Check in using Amnesty Mode
585 $checkin_resp = $script->do_checkin_override({
586     barcode => $item_barcode,
587     void_overdues => 1
588 });
589 is(
590     $checkin_resp->{ilsevent},
591     0,
592     'Checkin returned a SUCCESS event'
593 );
594
595 $item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
596 if (my $item_resp = $item_req->recv) {
597     if (my $item = $item_resp->content) {
598         ok(
599             $item->status == 7 || $item->status == 0,
600             'Item with id = ' . $item_id . ' has status of Reshelving or Available after fresh Storage request'
601         );
602     }
603 }
604
605 ### verify ending state
606 $summary = fetch_billable_xact_summary($xact_id);
607 is(
608     $summary->balance_owed,
609     '0.00',
610     'Patron has a balance of 0.00 (remaining fines forgiven)'
611 );
612
613
614 ##############################
615 # 10. Interval Testing
616 ##############################
617
618 # Setup Org Unit Settings
619 # already set: 'bill.prohibit_negative_balance_default' => 1
620
621 # Setup Org Unit Settings
622 $org_id = 1; #CONS
623 $settings = {
624     'bill.negative_balance_interval_default' => '1 hour'
625 };
626
627 $apputils->simplereq(
628     'open-ils.actor',
629     'open-ils.actor.org_unit.settings.update',
630     $script->authtoken,
631     $org_id,
632     $settings
633 );
634
635 ### Setup use case variables
636 $xact_id = 8;
637 $item_id = 9;
638 $item_barcode = 'CONC4000044';
639
640 $summary = fetch_billable_xact_summary($xact_id);
641 ok( $summary, 'CASE 10.1: Found the transaction summary');
642 is(
643     $summary->balance_owed,
644     '0.00',
645     'Starting balance owed is 0.00 (LOST fee paid)'
646 );
647
648 ### Check in first item (right after its payment)
649 $checkin_resp = $script->do_checkin_override({
650     barcode => $item_barcode,
651 });
652 is(
653     $checkin_resp->{ilsevent},
654     0,
655     'Checkin returned a SUCCESS event'
656 );
657
658 $item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
659 if (my $item_resp = $item_req->recv) {
660     if (my $item = $item_resp->content) {
661         ok(
662             $item->status == 7 || $item->status == 0,
663             'Item with id = ' . $item_id . ' has status of Reshelving or Available after fresh Storage request'
664         );
665     }
666 }
667
668 ### verify ending state for 10.1
669 $summary = fetch_billable_xact_summary($xact_id);
670 is(
671     $summary->balance_owed,
672     '-50.00',
673     'Patron has a balance of -50.00 (lost item returned during interval)'
674 );
675
676 ### Setup use case variables
677 $xact_id = 9;
678 $item_id = 10;
679 $item_barcode = 'CONC4000045';
680
681 $summary = fetch_billable_xact_summary($xact_id);
682 ok( $summary, 'CASE 10.2: Found the transaction summary');
683 is(
684     $summary->balance_owed,
685     '0.00',
686     'Starting balance owed is 0.00 (LOST fee paid)'
687 );
688
689 ### Check in second item (2 hours after its payment)
690 $checkin_resp = $script->do_checkin_override({
691     barcode => $item_barcode,
692 });
693 is(
694     $checkin_resp->{ilsevent},
695     0,
696     'Checkin returned a SUCCESS event'
697 );
698
699 $item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
700 if (my $item_resp = $item_req->recv) {
701     if (my $item = $item_resp->content) {
702         ok(
703             $item->status == 7 || $item->status == 0,
704             'Item with id = ' . $item_id . ' has status of Reshelving or Available after fresh Storage request'
705         );
706     }
707 }
708
709 ### verify ending state
710 $summary = fetch_billable_xact_summary($xact_id);
711 is(
712     $summary->balance_owed,
713     '0.00',
714     'Patron has a balance of 0.00 (lost item returned after interval)'
715 );
716
717
718 #############################
719 # 8. Restore Overdue Fines Appropriately, Previous Voids, Negative Balance Allowed
720 #############################
721
722 ## TODO: consider using a later xact_id/item_id, instead of reverting back to user 4
723
724 # Setup first patron (again)
725 $patron_id = 4;
726 $patron_usrname = '99999355250';
727
728 # Look up the patron
729 if ($user_obj = retrieve_patron($patron_id)) {
730     is(
731         ref $user_obj,
732         'Fieldmapper::actor::user',
733         'open-ils.storage.direct.actor.user.retrieve returned aou object'
734     );
735     is(
736         $user_obj->usrname,
737         $patron_usrname,
738         'Patron with id = ' . $patron_id . ' has username ' . $patron_usrname
739     );
740 }
741
742 ### Setup use case variables
743 $xact_id = 6;
744 $item_id = 7;
745 $item_barcode = 'CONC4000042';
746 $org_id = 1; #CONS
747
748 # Setup Org Unit Settings
749 $settings = {
750     'bill.prohibit_negative_balance_default' => 0,
751     'circ.restore_overdue_on_lost_return' => 1,
752     'circ.lost.generate_overdue_on_checkin' => 1
753 };
754
755 $apputils->simplereq(
756     'open-ils.actor',
757     'open-ils.actor.org_unit.settings.update',
758     $script->authtoken,
759     $org_id,
760     $settings
761 );
762
763 $summary = fetch_billable_xact_summary($xact_id);
764 ok( $summary, 'CASE 8: Found the transaction summary');
765 is(
766     $summary->balance_owed,
767     '50.00',
768     'Starting balance owed is 50.00 for lost item'
769 );
770
771 ### partially pay the bill
772 $payment_blob = {
773     userid => $patron_id,
774     note => '09-lp1198465_neg_balances.t',
775     payment_type => 'cash_payment',
776     patron_credit => '0.00',
777     payments => [ [ $xact_id, '10.00' ] ]
778 };
779 $pay_resp = pay_bills($payment_blob);
780
781 is(
782     scalar( @{ $pay_resp->{payments} } ),
783     1,
784     'Payment response included one payment id'
785 );
786
787 $summary = fetch_billable_xact_summary($xact_id);
788 is(
789     $summary->balance_owed,
790     '40.00',
791     'Remaining balance of 40.00 after payment'
792 );
793
794 ### check-in the lost copy
795
796 $item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
797 if (my $item_resp = $item_req->recv) {
798     if (my $item = $item_resp->content) {
799         is(
800             ref $item,
801             'Fieldmapper::asset::copy',
802             'open-ils.storage.direct.asset.copy.retrieve returned acp object'
803         );
804         is(
805             $item->status,
806             3,
807             'Item with id = ' . $item_id . ' has status of LOST'
808         );
809     }
810 }
811
812 $checkin_resp = $script->do_checkin_override({
813     barcode => $item_barcode});
814 is(
815     $checkin_resp->{ilsevent},
816     0,
817     'Checkin returned a SUCCESS event'
818 );
819
820 $item_req = $storage_ses->request('open-ils.storage.direct.asset.copy.retrieve', $item_id);
821 if (my $item_resp = $item_req->recv) {
822     if (my $item = $item_resp->content) {
823         ok(
824             $item->status == 7 || $item->status == 0,
825             'Item with id = ' . $item_id . ' has status of Reshelving or Available after fresh Storage request'
826         );
827     }
828 }
829
830 ### verify ending state
831
832 $summary = fetch_billable_xact_summary($xact_id);
833 is(
834     $summary->balance_owed,
835     '-7.00',
836     'Patron has a negative balance of 7.00 due to overpayment'
837 );
838
839
840
841 $script->logout();
842
843