diff --git a/db/schema.sql b/db/schema.sql index 663cc86..e99da63 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -1120,6 +1120,9 @@ CREATE TABLE `pmgr_statement_entries` ( -- Allow the disbursement to reconcile against the charge `charge_entry_id` INT(10) UNSIGNED DEFAULT NULL, + -- The transaction that reversed this charge, if any + `reverse_transaction_id` INT(10) UNSIGNED DEFAULT NULL, + `comment` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`) diff --git a/site/controllers/statement_entries_controller.php b/site/controllers/statement_entries_controller.php index d69d34f..6cbae10 100644 --- a/site/controllers/statement_entries_controller.php +++ b/site/controllers/statement_entries_controller.php @@ -245,19 +245,14 @@ class StatementEntriesController extends AppController { if (strtoupper($entry['StatementEntry']['type']) === 'CHARGE') { - $reversal = $this->StatementEntry->find - ('first', - array('link' => array('DisbursementEntry'), - 'conditions' => array(array('StatementEntry.id' => $id), - array('DisbursementEntry.type' => 'REVERSAL')), - )); + $reversable = $this->StatementEntry->reversable($id); // Set up dynamic menu items - if (empty($reversal) || $stats['balance'] > 0) + if ($reversable || $stats['balance'] > 0) $this->sidemenu_links[] = array('name' => 'Operations', 'header' => true); - if (empty($reversal)) + if ($reversable) $this->sidemenu_links[] = array('name' => 'Reverse', 'url' => array('action' => 'reverse', diff --git a/site/models/statement_entry.php b/site/models/statement_entry.php index a2d4067..79ccbc5 100644 --- a/site/models/statement_entry.php +++ b/site/models/statement_entry.php @@ -181,8 +181,8 @@ class StatementEntry extends AppModel { /************************************************************************** ************************************************************************** ************************************************************************** - * function: reverse - * - Reverses the charges + * function: reversable + * - Returns true if the charge can be reversed; false otherwise */ function reversable($id) { $this->prEnter(compact('id')); @@ -194,15 +194,9 @@ class StatementEntry extends AppModel { return $this->prReturn(false); // Determine anything reconciled against the charge - $reconciled = $this->reconciledEntries($id); - $this->pr(21, compact('reconciled')); - - if (!empty($reconciled)) { - // Double check that this charge has not already been reversed - foreach ($reconciled['entries'] AS $entry) - if ($entry['DisbursementEntry']['type'] === 'REVERSAL') - return $this->prReturn(false); - } + $reverse_transaction_id = $this->field('reverse_transaction_id'); + if (!empty($reverse_transaction_id)) + return $this->prReturn(false); return $this->prReturn(true); } @@ -222,28 +216,19 @@ class StatementEntry extends AppModel { // Get the basic information about this charge $charge = $this->find('first', array('contain' => true)); - $charge = $charge['StatementEntry']; + //$charge = $charge['StatementEntry']; // Query the stats to get the remaining balance $stats = $this->stats($id); - - // Build a transaction - $reversal = array('Transaction' => array(), 'Entry' => array()); - $reversal['Transaction']['stamp'] = $stamp; - $reversal['Transaction']['comment'] = "Credit Note: Charge Reversal"; - - // Add the charge reversal - $reversal['Entry'][] = - array('amount' => $stats['Charge']['total'], - 'account_id' => $charge['account_id'], - 'comment' => 'Charge Reversal', - ); + $charge['paid'] = $stats['Charge']['disbursement']; // Record the reversal transaction $result = $this->Transaction->addReversal - ($reversal, - $id, $stats['Charge']['disbursement'], - $charge['customer_id'], $charge['lease_id']); + ($charge, $stamp, 'Charge Reversal'); + + // Mark the charge as reversed + $this->id = $id; + $this->saveField('reverse_transaction_id', $result['transaction_id']); return $this->prReturn($result); } @@ -416,8 +401,8 @@ class StatementEntry extends AppModel { array('StatementEntry', 'LedgerEntry' => array('conditions' => - array('LedgerEntry.account_id !=' => - $this->Account->accountReceivableAccountID()), + array('LedgerEntry.account_id <> Transaction.account_id') + //$this->Account->accountReceivableAccountID()), ), ), 'conditions' => array('Transaction.id' => $receipt_id), diff --git a/site/models/transaction.php b/site/models/transaction.php index 802c0c8..5db6020 100644 --- a/site/models/transaction.php +++ b/site/models/transaction.php @@ -178,14 +178,17 @@ class Transaction extends AppModel { * - Adds a new charge reversal */ - function addReversal($data, $charge_id, $credit_amount, $customer_id, $lease_id = null) { - $this->prEnter(compact('data', 'charge_id', 'credit_amount', 'customer_id', 'lease_id')); + function addReversal1($data, $charge) { + $this->prEnter(compact('data', 'charge')); if (count($data['Entry']) != 1) $this->INTERNAL_ERROR("Should be one Entry for addReversal"); $data += array('control' => array()); +/* 'amount' => $stats['Charge']['total'], */ +/* 'account_id' => $charge['account_id'], */ + // First, reverse the charge $reversal = $data; $reversal['control'] += @@ -194,20 +197,22 @@ class Transaction extends AppModel { 'include_statement_entry' => true, ); - $reversal['Transaction']['type'] = 'CREDIT_NOTE'; - //$reversal['Transaction']['crdr'] = 'CREDIT'; + //$reversal['Transaction']['type'] = 'CREDIT_NOTE'; $reversal['Entry'][0]['charge_entry_id'] = $charge_id; - $reversal['Entry'][0]['type'] = 'REVERSAL'; + //$reversal['Entry'][0]['type'] = 'REVERSAL'; + $reversal['Entry'][0]['amount'] *= -1; + + //$reversal['Transaction']['crdr'] = 'CREDIT'; //$reversal['Entry'][0]['crdr'] = 'DEBIT'; - $ids['reversal'] = $this->addReceipt($reversal, $customer_id, $lease_id); + + $ids['reversal'] = $this->addInvoice($reversal, $customer_id, $lease_id); + // Then issue a credit for the amount already paid, if any if ($credit_amount > 0) { $ids['credit'] = $this->addReceipt (array('control' => - array('assign' => true, - 'assign_receipt' => true, - 'include_ledger_entry' => false, + array('include_ledger_entry' => false, 'include_statement_entry' => true, ), @@ -218,6 +223,8 @@ class Transaction extends AppModel { 'Entry' => array(array('amount' => $credit_amount, 'charge_entry_id' => $charge_id, + // REVISIT : 20090814 + // TEMPORARY. JUST NEED AN ACCOUNT AT THE MOMENT 'account_id' => $this->StatementEntry->Account->accountPayableAccountID(), ))), $customer_id, $lease_id); @@ -590,7 +597,7 @@ class Transaction extends AppModel { // Break each entry out of the combined statement/ledger entry // and into individual entries appropriate for saving. While // we're at it, calculate the transaction total as well. - $transaction['amount'] = 0; + $transaction_amount = 0; foreach ($entries AS &$entry) { // Ensure these items are null'ed out so we don't // accidentally pick up stale data. @@ -614,7 +621,11 @@ class Transaction extends AppModel { $entry['statement_entry_comment'] = null; } - if (!empty($control['include_ledger_entry']) || !empty($entry['include_ledger_entry'])) { + // Priority goes to settings defined in $entry, but + // use the control information as defaults. + $entry += $control; + + if (!empty($entry['include_ledger_entry'])) { // Create one half of the Double Ledger Entry (and the Tender) $le1 = array_intersect_key($entry, @@ -643,10 +654,10 @@ class Transaction extends AppModel { $le1 = $le1_tender = $le2 = null; // Now that the ledger entries are in place, respect the 'negative' flag - if (!empty($control['negative'])) + if (!empty($entry['negative'])) $entry['amount'] *= -1; - if (!empty($control['include_statement_entry']) || !empty($entry['include_statement_entry'])) { + if (!empty($entry['include_statement_entry'])) { // Create the statement entry $se = array_intersect_key($entry, @@ -659,7 +670,9 @@ class Transaction extends AppModel { $se['comment'] = $entry['statement_entry_comment']; } else { - if (!empty($control['assign'])) { + if (!empty($entry['assign']) && + !empty($entry['type']) && + empty($entry['charge_entry_id'])) { if (empty($assign_disbursement_type)) $assign_disbursement_type = $entry['type']; elseif ($entry['type'] != $assign_disbursement_type) @@ -670,12 +683,16 @@ class Transaction extends AppModel { } // Add entry amount into the transaction total - $transaction['amount'] += $entry['amount']; + $transaction_amount += $entry['amount']; // Replace combined entry with our new individual entries $entry = compact('le1', 'le1_tender', 'le2', 'se'); } + // If transaction amount is not already set, use the + // sum of the entry amounts. + $transaction += array('amount' => $transaction_amount); + /* // Add a summary ledger entry if requested */ /* if (!empty($control['summary_double_entry'])) { */ /* $entry = $control['summary_double_entry']; */ @@ -854,7 +871,7 @@ class Transaction extends AppModel { 'Entry' => array()); - $summary_amount = 0; + $rollback['Transaction']['amount'] = 0; foreach ($nsf_ledger_entry['Transaction']['StatementEntry'] AS $disbursement) { if ($disbursement['type'] === 'SURPLUS') { $disbursement['type'] = 'VOID'; @@ -870,7 +887,7 @@ class Transaction extends AppModel { 'lease_id' => $disbursement['lease_id'], 'charge_entry_id' => $disbursement['charge_entry_id'], ); - $summary_amount += $disbursement['amount']; + $rollback['Transaction']['amount'] += $disbursement['amount']; } } @@ -878,11 +895,14 @@ class Transaction extends AppModel { $rollback['Entry'][] = array('include_ledger_entry' => true, 'include_statement_entry' => false, - 'amount' => $summary_amount, + 'amount' => $rollback['Transaction']['amount'], 'account_id' => $this->Account->accountReceivableAccountID(), 'crdr' => 'DEBIT', ); + // Set the transaction amount to be negative + $rollback['Transaction']['amount'] *= -1; + // Record the transaction, which will un-pay previously paid // charges, void any credits, and other similar work. if (count($rollback['Entry'])) { @@ -925,6 +945,98 @@ class Transaction extends AppModel { } + /************************************************************************** + ************************************************************************** + ************************************************************************** + * function: addReversal + * - Adds a new charge reversal + */ + + function addReversal($charge, $stamp = null, $comment = null) { + $this->prEnter(compact('charge', 'stamp', 'comment')); + + $ret = array(); + + // Finding all statement entries affected by reversing this charge. + $disb_entries = $this->StatementEntry->find + ('first', array + ('contain' => array('DisbursementEntry' => + array(//'fields' => array(), + ), + ), + 'conditions' => array('StatementEntry.id' => $charge['StatementEntry']['id']), + )); + + $this->pr(20, compact('disb_entries')); + if (!$disb_entries) + return $this->prReturn(array('error' => true) + $ret); + + // Build a transaction to adjust all of the statement entries + // These are all disbursements against the charge we're reversing + $rollback = + array('control' => + array('assign' => true, + 'assign_receipt' => true, + 'include_ledger_entry' => false, + 'include_statement_entry' => true, + ), + + 'Transaction' => + array('stamp' => $stamp, + //'type' => 'RECEIPT', + 'type' => 'CREDIT_NOTE', + // The transaction amount will equal that of the charge + 'amount' => $charge['StatementEntry']['amount'], + 'account_id' => $charge['StatementEntry']['account_id'], + 'crdr' => 'DEBIT', + 'customer_id' => $charge['StatementEntry']['customer_id'], + 'lease_id' => null, + 'comment' => $comment, + ), + + 'Entry' => array()); + + foreach ($disb_entries['DisbursementEntry'] AS $disbursement) { + $rollback['Entry'][] = + array('type' => $disbursement['type'], + 'amount' => -1 * $disbursement['amount'], + 'account_id' => $disbursement['account_id'], + 'customer_id' => $disbursement['customer_id'], + 'lease_id' => $disbursement['lease_id'], + 'charge_entry_id' => $disbursement['charge_entry_id'], + ); + } + + // Add the sole ledger entry for this transaction + $rollback['Entry'][] = + array('include_ledger_entry' => true, + 'include_statement_entry' => false, + 'type' => 'REVERSAL', + 'amount' => $rollback['Transaction']['amount'], + //'account_id' => $this->Account->accountPayableAccountID(), + 'account_id' => $this->Account->accountReceivableAccountID(), + 'crdr' => 'CREDIT', + ); + + // Record the transaction, which will un-disburse previously + // disbursed payments, and other similar work. + if (count($rollback['Entry'])) { + $rollback_result = $this->addTransaction($rollback['control'], + $rollback['Transaction'], + $rollback['Entry']); + $this->pr(20, compact('rollback', 'rollback_result')); + $ret['rollback'] = $rollback_result; + if ($rollback_result['error']) + return $this->prReturn(array('error' => true) + $ret); + } + +/* $ret['nsf_transaction_id'] = $ret['bounce']['transaction_id']; */ +/* if (!empty($ret['rollback'])) */ +/* $ret['nsf_ledger_entry_id'] = $ret['rollback']['entries'][0]['DoubleEntry']['Entry1']['ledger_entry_id']; */ + $ret = $ret['rollback']; + return $this->prReturn($ret + array('error' => false)); + } + /************************************************************************** ************************************************************************** **************************************************************************