From 3e54f241d1944a83e6774af504fbd1880383c37c Mon Sep 17 00:00:00 2001 From: Dan Wells Date: Fri, 8 Nov 2013 11:43:38 -0500 Subject: [PATCH] LP#1479107 adjust to zero API API call for manually adjusting a transaction to zero. It works by rebilling previously voided billings (if necessary) until a positive balance is reached, then applying adjustment payments to zero out the balance. open-ils.circ.money.billable_xact.adjust_to_zero(auth, [xact_ids]) Signed-off-by: Dan Wells Signed-off-by: Kathy Lussier Signed-off-by: Jason Stephenson --- Open-ILS/examples/fm_IDL.xml | 2 + .../lib/OpenILS/Application/AppUtils.pm | 13 ++ .../lib/OpenILS/Application/Circ/Money.pm | 150 ++++++++++++++++++ 3 files changed, 165 insertions(+) diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index cd547db0e7..b5a44b6e89 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -7278,11 +7278,13 @@ SELECT usr, + + diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm index 8c92ee5d83..44449f757c 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm @@ -2231,6 +2231,9 @@ sub check_open_xact { # values are all multiplied by 100 before being used, and the result # is divided by 100 in order to avoid decimal rounding errors inherent # in floating point math. +# +# XXX shifting using multiplication/division *may* still introduce +# rounding errors -- better to implement using string manipulation? sub fpdiff { my ($class, @args) = @_; my $result = shift(@args) * 100; @@ -2240,5 +2243,15 @@ sub fpdiff { return $result / 100; } +sub fpsum { + my ($class, @args) = @_; + my $result = shift(@args) * 100; + while (my $arg = shift(@args)) { + $result += $arg * 100; + } + return $result / 100; +} + + 1; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm index 038761d923..2cd07f8c93 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Money.pm @@ -921,6 +921,156 @@ sub void_bill { } +__PACKAGE__->register_method( + method => 'adjust_bills_to_zero_manual', + api_name => 'open-ils.circ.money.billable_xact.adjust_to_zero', + signature => { + desc => q/ + Given a list of billable transactions, manipulate the + transaction using account adjustments to result in a + balance of $0. + /, + params => [ + {desc => 'Authtoken', type => 'string'}, + {desc => 'Array of transaction IDs', type => 'array'} + ], + return => { + desc => q/Array of IDs for each transaction updated, + Event on error./ + } + } +); + +sub _rebill_xact { + my ($e, $xact) = @_; + + my $xact_id = $xact->id; + # the plan: rebill voided billings until we get a positive balance + # + # step 1: get the voided/adjusted billings + my $billings = $e->search_money_billing([ + { + xact => $xact_id, + }, + { + order_by => {mb => 'amount desc'}, + flesh => 1, + flesh_fields => {mb => ['adjustments']}, + } + ]); + my @billings = grep { $U->is_true($_->voided) or @{$_->adjustments} } @$billings; + + my $xact_balance = $xact->balance_owed; + $logger->debug("rebilling for xact $xact_id with balance $xact_balance"); + + my $rebill_amount = 0; + my @rebill_ids; + # step 2: generate new bills just like the old ones + for my $billing (@billings) { + my $amount = 0; + if ($U->is_true($billing->voided)) { + $amount = $billing->amount; + } else { # adjusted billing + map { $amount = $U->fpadd($amount, $_->amount) } @{$billing->adjustments}; + } + my $evt = $CC->create_bill( + $e, + $amount, + $billing->btype, + $billing->billing_type, + $xact_id, + "System: MANUAL ADJUSTMENT, BILLING #".$billing->id." REINSTATED\n(PREV: ".$billing->note.")", + $billing->billing_ts() + ); + return $evt if $evt; + $rebill_amount += $billing->amount; + + # if we have a postive (or zero) balance now, stop + last if $rebill_amount >= $xact_balance; + } +} + +sub _is_fully_adjusted { + my ($billing) = @_; + + my $amount_adj = 0; + map { $amount_adj = $U->fpadd($amount_adj, $_->amount) } @{$billing->adjustments}; + + return $billing->amount == $amount_adj; +} + +sub adjust_bills_to_zero_manual { + my ($self, $client, $auth, $xact_ids) = @_; + + my $e = new_editor(xact => 1, authtoken => $auth); + return $e->die_event unless $e->checkauth; + + # in case a bare ID is passed + $xact_ids = [$xact_ids] unless ref $xact_ids; + + my @modified; + for my $xact_id (@$xact_ids) { + + my $xact = + $e->retrieve_money_billable_transaction_summary([ + $xact_id, + {flesh => 1, flesh_fields => {mbts => ['usr']}} + ]) or return $e->die_event; + + return $e->die_event unless + $e->allowed('ADJUST_BILLS', $xact->usr->home_ou); + + if ($xact->balance_owed < 0) { + my $evt = _rebill_xact($e, $xact); + return $evt if $evt; + # refetch xact to get new balance + $xact = + $e->retrieve_money_billable_transaction_summary([ + $xact_id, + {flesh => 1, flesh_fields => {mbts => ['usr']}} + ]) or return $e->die_event; + } + + my $billings = $e->search_money_billing([ + { + xact => $xact_id, + }, + { + order_by => {mb => 'amount desc'}, + flesh => 1, + flesh_fields => {mb => ['adjustments']}, + } + ]); + + if ($xact->balance_owed == 0) { + # if was zero, or we rebilled it to zero + next; + } else { + # it's positive and needs to be adjusted + my @billings_to_zero = grep { !$U->is_true($_->voided) or !_is_fully_adjusted($_) } @$billings; + $CC->adjust_bills_to_zero($e, \@billings_to_zero, "System: MANUAL ADJUSTMENT"); + } + + push(@modified, $xact->id); + + # now we see if we can close the transaction + # same logic as make_payments(); + my $circ = $e->retrieve_action_circulation($xact_id); + if ($circ and !$CC->can_close_circ($e, $circ)) { + # we don't check to see if the xact is already closed. since the + # xact had a negative balance, it should not have been closed, so + # assume 'now' is the correct close time regardless. + my $trans = $e->retrieve_money_billable_transaction($xact_id); + $trans->xact_finish("now"); + $e->update_money_billable_transaction($trans) or return $e->die_event; + } + } + + $e->commit; + return \@modified; +} + + __PACKAGE__->register_method( method => 'edit_bill_note', api_name => 'open-ils.circ.money.billing.note.edit', -- 2.43.2