From 67280c89e7773fb311ffd47d6330db3cd3cc77dd Mon Sep 17 00:00:00 2001 From: abijah Date: Tue, 4 Aug 2009 21:19:20 +0000 Subject: [PATCH] Fixed bug when marking NSF of a tender that has only been used as a surplus and never applied to any charges. Modified the applyCredits algorithm to try and distinguish between surplus credits of a lease and general customer surplus. I don't know if it works completely, but I do know it creates an issue since a lease surplus can now never be utilized without additional lease charges, a refund (no yet implemented), or moving the surplus to the customer level (not intended to be implemented). git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@483 97e9348a-65ac-dc4b-aefc-98561f571b83 --- site/controllers/customers_controller.php | 39 ++++++++--- site/controllers/leases_controller.php | 81 ++++++++++------------- site/models/lease.php | 26 ++++---- site/models/statement_entry.php | 27 +++++++- site/models/transaction.php | 56 +++++++++++++--- 5 files changed, 152 insertions(+), 77 deletions(-) diff --git a/site/controllers/customers_controller.php b/site/controllers/customers_controller.php index b2dbada..56ad22f 100644 --- a/site/controllers/customers_controller.php +++ b/site/controllers/customers_controller.php @@ -178,17 +178,33 @@ class CustomersController extends AppController { $this->redirect(array('action'=>'index')); } -/* //$result = $this->Customer->securityDeposits($id); */ -/* $result = $this->Customer->excessPayments($id); */ -/* //$result = $this->Customer->unreconciledCharges($id); */ -/* echo('
'); */ -/* pr($result); */ -/* $this->autoRender = false; */ -/* return; */ + // Get details on this customer, its contacts and leases + $customer = $this->Customer->find + ('first', array + ('contain' => array + (// Models + 'Contact' => + array('order' => array('Contact.display_name'), + // Models + 'ContactPhone', + 'ContactEmail', + 'ContactAddress', + ), + 'Lease' => + array('Unit' => + array('order' => array('sort_order'), + 'fields' => array('id', 'name'), + ), + ), + ), - $customer = $this->Customer->details($id); + 'conditions' => array('Customer.id' => $id), + )); //pr($customer); - $outstanding_balance = $customer['stats']['balance']; + + // Figure out the outstanding balances for this customer + $stats = $this->Customer->stats($id); + $outstanding_balance = $stats['balance']; $outstanding_deposit = $this->Customer->securityDepositBalance($id); // Figure out if this customer has any non-closed leases @@ -229,6 +245,11 @@ class CustomersController extends AppController { $id)); } + if ($outstanding_balance < 0) + $this->sidemenu_links[] = + array('name' => 'Issue Refund', + 'url' => array('action' => 'refund', $id)); + // Prepare to render. $title = 'Customer: ' . $customer['Customer']['name']; $this->set(compact('customer', 'title', diff --git a/site/controllers/leases_controller.php b/site/controllers/leases_controller.php index 24e4012..9189556 100644 --- a/site/controllers/leases_controller.php +++ b/site/controllers/leases_controller.php @@ -213,22 +213,38 @@ class LeasesController extends AppController { } +/* /\************************************************************************** */ +/* ************************************************************************** */ +/* ************************************************************************** */ +/* * action: promote_credit */ +/* * - Moves any lease credit up to the customer level, so that */ +/* * it may be used for charges other than those on this lease. */ +/* *\/ */ + +/* function promote_surplus($id) { */ +/* $this->Lease->promoteSurplus($id); */ +/* pr("PREVENTING REDIRECT"); */ +/* $this->render('fake'); */ +/* $this->redirect(array('controller' => 'leases', */ +/* 'action' => 'view', */ +/* $id)); */ +/* } */ + + /************************************************************************** ************************************************************************** ************************************************************************** - * action: apply_deposit - * - Applies the security deposit to charges. This is much - * like a receipt, but it's separated to keep it simple and - * to prevent feature overload on the receipt page. + * action: refund + * - Provides lease customer with a refund */ - function apply_deposit($id) { - $this->Lease->releaseSecurityDeposits($id); - pr("PREVENTING REDIRECT"); - $this->render('fake'); - $this->redirect(array('controller' => 'leases', - 'action' => 'view', - $id)); + function refund($id) { + // Obtain the overall lease balance + $stats = $this->Lease->stats($id); + $outstanding_balance = $stats['balance']; + + $this->set(compact('lease', 'title', + 'outstanding_balance')); } @@ -290,31 +306,6 @@ class LeasesController extends AppController { } - /************************************************************************** - ************************************************************************** - ************************************************************************** - * action: refund - * - Provides user with a refund - * REVISIT : 20090710 - * Should this be a customer function? - */ - - function refund($id) { -/* // Obtain the overall lease balance */ -/* $stats = $this->Lease->stats($id); */ -/* $outstanding_balance = $stats['balance']; */ - -/* // Determine the lease security deposit */ -/* $deposits = $this->Lease->securityDeposits($id); */ -/* $outstanding_deposit = $deposits['summary']['balance']; */ - - -/* $this->set(compact('lease', 'title', */ -/* 'outstanding_deposit', */ -/* 'outstanding_balance')); */ - } - - /************************************************************************** ************************************************************************** ************************************************************************** @@ -435,19 +426,17 @@ class LeasesController extends AppController { 'action' => 'receipt', $lease['Customer']['id'])); - if (isset($lease['Lease']['moveout_date']) && $outstanding_deposit > 0 && $outstanding_balance > 0) - $this->sidemenu_links[] = - array('name' => 'Apply Deposit', 'url' => array('action' => 'apply_deposit', - $id)); +/* if ($outstanding_balance < 0) */ +/* $this->sidemenu_links[] = */ +/* array('name' => 'Transfer Credit to Customer', */ +/* 'url' => array('action' => 'promote_surplus', $id)); */ - if (isset($lease['Lease']['moveout_date']) && - $outstanding_balance <= 0 && - ($outstanding_deposit - $outstanding_balance) > 0) + if ($outstanding_balance < 0) $this->sidemenu_links[] = - array('name' => 'Issue Refund', 'url' => array('action' => 'refund', - $id)); + array('name' => 'Issue Refund', + 'url' => array('action' => 'refund', $id)); - if (isset($lease['Lease']['moveout_date']) && $outstanding_deposit == 0 && $outstanding_balance > 0) + if (isset($lease['Lease']['moveout_date']) && $outstanding_balance > 0) $this->sidemenu_links[] = array('name' => 'Write-Off', 'url' => array('action' => 'bad_debt', $id)); diff --git a/site/models/lease.php b/site/models/lease.php index 4ab7624..30965ca 100644 --- a/site/models/lease.php +++ b/site/models/lease.php @@ -48,6 +48,20 @@ class Lease extends AppModel { $query['conditions'][] = array('StatementEntry.account_id' => $this->StatementEntry->Account->securityDepositAccountID()); + // REVISIT : 20090804 + // Dilemma... how to handle security deposits used to pay + // charges on the lease, yet are not part of the lease + // security deposit(s)? For example, Lease A & Lease B, + // each with $25 Sec. Dep. Move out of Lease A, and + // promote the lease surplus to the customer. A new + // charge on Lease B for $15, which is assigned $15/$25 + // from the promoted Lease A surplus. They way this + // function works at present, it will presume the $15 is + // part of the security deposit balance, and will end up + // calculating it as only $10, which is wrong. Perhaps + // the fix is to release security deposits into some sort + // of income account. + $stats = $this->StatementEntry->stats(null, $query); return $this->prReturn($stats['account_balance']); } @@ -450,18 +464,6 @@ class Lease extends AppModel { } - /************************************************************************** - ************************************************************************** - ************************************************************************** - * function: addCharge - * - Adds an additional charge to the lease - */ - - function addCharge($id, $charge) { - - } - - /************************************************************************** ************************************************************************** ************************************************************************** diff --git a/site/models/statement_entry.php b/site/models/statement_entry.php index f55f03a..885e243 100644 --- a/site/models/statement_entry.php +++ b/site/models/statement_entry.php @@ -22,7 +22,7 @@ class StatementEntry extends AppModel { ); - //var $default_log_level = array('log' => 30, 'show' => 15); + var $default_log_level = array('log' => 30, 'show' => 15); /************************************************************************** ************************************************************************** @@ -380,12 +380,15 @@ OPTION 2 $charge_ids = null, $payment_type = null, $customer_id = null, $lease_id = null) { - //$this->prFunctionLevel(25); + $this->prFunctionLevel(25); $this->prEnter(compact('query', 'receipt_id', 'charge_ids', 'payment_type', 'customer_id', 'lease_id')); $this->queryInit($query); + if (!empty($customer_id)) + $query['conditions'][] = array('StatementEntry.customer_id' => $customer_id); + if (empty($payment_type)) $payment_type = 'PAYMENT'; @@ -394,6 +397,22 @@ OPTION 2 // First, find all known credits $lquery = $query; $lquery['conditions'][] = array('StatementEntry.type' => 'SURPLUS'); + // REVISIT : 20090804 + // We need to ensure that we're using surplus credits ONLY from either + // the given lease, or those that do not apply to any specific lease. + // However, by doing this, it forces any lease surplus amounts to + // remain frozen with that lease until either there is a lease charge, + // we refund the money, or we "promote" that surplus to the customer + // level and out of the leases direct control. + // That seems like a pain. Perhaps we should allow any customer + // surplus to be used on any customer charge. + $lquery['conditions'][] = + array('OR' => + array(array('StatementEntry.lease_id' => null), + (!empty($lease_id) + ? array('StatementEntry.lease_id' => $lease_id) + : array()), + )); $lquery['order'][] = 'StatementEntry.effective_date ASC'; $credits = $this->find('all', $lquery); $this->pr(18, compact('credits'), @@ -403,6 +422,7 @@ OPTION 2 $receipt_credit = null; if (!empty($receipt_id)) { $lquery = $query; + $lquery['link'] = array('StatementEntry' => $lquery['link']); $lquery['link'] += array('LedgerEntry' => array('conditions' => //array(LedgerEntry.'crdr'=>'DEBIT'), @@ -429,6 +449,9 @@ OPTION 2 'conditions' => array('StatementEntry.id' => $charge_ids)); } else { $lquery = $query; + // If we're working with a specific lease, then limit charges to it + if (!empty($lease_id)) + $lquery['conditions'][] = array('StatementEntry.lease_id' => $lease_id); } $lquery['order'] = 'StatementEntry.effective_date ASC'; $charges = $this->reconciledSet('CHARGE', $lquery, true); diff --git a/site/models/transaction.php b/site/models/transaction.php index f257080..7f4a8a1 100644 --- a/site/models/transaction.php +++ b/site/models/transaction.php @@ -246,6 +246,44 @@ class Transaction extends AppModel { } + /************************************************************************** + ************************************************************************** + ************************************************************************** + * function: addRefund + * - Adds a new refund transaction + */ + + function addRefund($data, $customer_id, $lease_id = null) { + $this->prEnter(compact('data', 'customer_id', 'lease_id')); + + // REVISIT : 20090804 + // NOT IMPLEMENTED AT ALL. Just cut and paste so far + return array('error' => true); + + // Establish the transaction as an refund + $refund =& $data['Transaction']; + $refund += + array('type' => 'VOUCHER', + 'crdr' => 'DEBIT', + 'account_id' => $this->Account->accountReceivableAccountID(), + 'customer_id' => $customer_id, + 'lease_id' => $lease_id, + ); + + // Go through the statement entries and flag as charges + foreach ($data['Entry'] AS &$entry) + $entry += array('type' => 'CHARGE', + 'crdr' => 'CREDIT', + ); + + $ids = $this->addTransaction($data['Transaction'], $data['Entry']); + if (isset($ids['transaction_id'])) + $ids['refund_id'] = $ids['transaction_id']; + + return $this->prReturn($ids); + } + + /************************************************************************** ************************************************************************** ************************************************************************** @@ -511,8 +549,7 @@ class Transaction extends AppModel { $transaction['type'] == 'RECEIPT') && $subtype !== 'NSF' && !$ret['error']) { $result = $this->StatementEntry->assignCredits - (array('link' => array('Customer'), - 'conditions' => array('Customer.id' => $transaction['customer_id'])), + (null, ($transaction['type'] == 'RECEIPT' ? $ret['transaction_id'] : null), @@ -621,11 +658,13 @@ class Transaction extends AppModel { // Record the transaction, which will un-pay previously paid // charges, void any credits, and other similar work. - $rollback_result = $this->addTransaction($rollback['Transaction'], $rollback['Entry'], 'NSF'); - $this->pr(20, compact('rollback', 'rollback_result')); - $ret['rollback'] = $rollback_result; - if ($rollback_result['error']) - return $this->prReturn(array('error' => true) + $ret); + if (count($rollback['Entry'])) { + $rollback_result = $this->addTransaction($rollback['Transaction'], $rollback['Entry'], 'NSF'); + $this->pr(20, compact('rollback', 'rollback_result')); + $ret['rollback'] = $rollback_result; + if ($rollback_result['error']) + return $this->prReturn(array('error' => true) + $ret); + } // Add NSF Charge $charge_result = $this->addInvoice @@ -651,7 +690,8 @@ class Transaction extends AppModel { return $this->prReturn(array('error' => true) + $ret); $ret['nsf_transaction_id'] = $ret['bounce']['transaction_id']; - $ret['nsf_ledger_entry_id'] = $ret['rollback']['entries'][0]['DoubleEntry']['Entry1']['ledger_entry_id']; + if (!empty($ret['rollback'])) + $ret['nsf_ledger_entry_id'] = $ret['rollback']['entries'][0]['DoubleEntry']['Entry1']['ledger_entry_id']; return $this->prReturn($ret + array('error' => false)); }