More work cleaning up and testing the use of charge waivers, as well as security deposit utilizations. Much of this was just around the concept of determining balances, which wasn't / isn't very straightforward. It's not hard to calculate a balance for a particular sitation, but it is difficult to generalize. I think it's reasonable as it now stands, but the StatementEntry::stats algorithm is certainly subject to change. Also, I didn't double check every case which called the stats() function, so I highly suspect we'll find cases where the code is not expecting the new return values, either symantically or logically.

git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716/site@480 97e9348a-65ac-dc4b-aefc-98561f571b83
This commit is contained in:
abijah
2009-08-04 03:12:09 +00:00
parent aa846de284
commit e0d15b7396
5 changed files with 116 additions and 278 deletions

View File

@@ -222,109 +222,13 @@ class LeasesController extends AppController {
* to prevent feature overload on the receipt page.
*/
function apply_deposit($id = null) {
// Create some models for convenience
$A = new Account();
if ($this->data) {
// Handle the move out based on the data given
pr($this->data);
$this->Lease->releaseSecurityDeposits($this->data['Lease']['id']);
die();
// Assume this will succeed
$ret = true;
// Go through the entered payments
$receipt_transaction = array_intersect_key($this->data,
array('Transaction'=>1,
'transaction_id'=>1));
foreach ($data['StatementEntry'] AS $entry) {
// Create the receipt entry, and reconcile the credit side
// of the double-entry (which should be A/R) as a payment.
$ids = $this->StatementEntry->Ledger->Account->postLedgerEntry
($receipt_transaction,
array_intersect_key($entry, array('MonetarySource'=>1))
+ array_intersect_key($entry, array('account_id'=>1)),
array('debit_ledger_id' => $A->currentLedgerID($entry['account_id']),
'credit_ledger_id' => $A->currentLedgerID($A->receiptAccountID()),
'customer_id' => $customer_id,
'lease_id' => $lease_id)
+ $entry,
'receipt');
if ($ids['error'])
$ret = false;
$db = &$model->getDataSource();
$fields = $db->fields($model, $model->alias);
$fields[] = ("SUM(IF(Account.id IS NULL, 0," .
" IF(LedgerEntry.debit_ledger_id = Account.id," .
" 1, -1))" .
" * IF(LedgerEntry.amount IS NULL, 0, LedgerEntry.amount))" .
" AS 'balance'");
return $fields;
$receipt_transaction = array_intersect_key($ids,
array('transaction_id'=>1,
'split_transaction_id'=>1));
}
$this->Lease->moveOut($this->data['Lease']['id'],
'VACANT',
$this->data['Lease']['moveout_date'],
//true // Close this lease, if able
false
);
$this->redirect(array('controller' => 'leases',
'action' => 'view',
$this->data['Lease']['id']));
$this->autoRender = false;
return;
}
$A = new Account();
$lease = $this->Lease->find
('first', array
('contain' => array
(// Models
'Unit' =>
array('order' => array('sort_order'),
'fields' => array('id', 'name'),
),
'Customer' =>
array('fields' => array('id', 'name'),
),
),
'conditions' => array(array('Lease.id' => $id),
array('Lease.close_date' => null),
),
));
// Get the lease balance, part of lease stats
$this->Lease->statsMerge($lease['Lease'],
array('stats' => $this->Lease->stats($id)));
// Determine the lease security deposit
$deposit_balance = $this->Lease->securityDeposits($lease['Lease']['id']);
$this->set(compact('deposit_balance'));
$this->set('customer', $lease['Customer']);
$this->set('unit', $lease['Unit']);
$this->set('lease', $lease['Lease']);
$this->set('account', array('id' => $A->securityDepositAccountID()));
/* $redirect = array('controller' => 'leases', */
/* 'action' => 'view', */
/* $id); */
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
$lease['Unit']['name'] . ': ' .
$lease['Customer']['name'] . ': Utilize Security Deposit');
$this->set(compact('title', 'redirect'));
function apply_deposit($id) {
$this->Lease->releaseSecurityDeposits($id);
pr("PREVENTING REDIRECT");
$this->render('fake');
$this->redirect(array('controller' => 'leases',
'action' => 'view',
$id));
}

View File

@@ -81,12 +81,7 @@ class Customer extends AppModel {
$this->prEnter(compact('id', 'query'));
$this->queryInit($query);
if (!isset($query['link']['Customer']))
$query['link']['Customer'] = array();
if (!isset($query['link']['Customer']['fields']))
$query['link']['Customer']['fields'] = array();
$query['conditions'][] = array('Customer.id' => $id);
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
$query['conditions'][] = array('StatementEntry.account_id' =>
$this->StatementEntry->Account->securityDepositAccountID());
@@ -106,18 +101,12 @@ class Customer extends AppModel {
$this->prEnter(compact('id', 'query'));
$this->queryInit($query);
if (!isset($query['link']['Customer']))
$query['link']['Customer'] = array();
if (!isset($query['link']['Customer']['fields']))
$query['link']['Customer']['fields'] = array();
$query['conditions'][] = array('Customer.id' => $id);
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
$query['conditions'][] = array('StatementEntry.account_id' =>
$this->StatementEntry->Account->securityDepositAccountID());
$stats = $this->StatementEntry->stats(null, $query);
$balance = $stats['Charge']['reconciled'] - $stats['Payment']['reconciled'];
return $this->prReturn($balance);
return $this->prReturn($stats['account_balance']);
}
@@ -132,19 +121,7 @@ class Customer extends AppModel {
$this->prEnter(compact('id', 'query'));
$this->queryInit($query);
if (!isset($query['link']['Customer']))
$query['link']['Customer'] = array();
if (!isset($query['link']['Customer']['fields']))
$query['link']['Customer']['fields'] = array();
/* if (!isset($query['link']['StatementEntry'])) */
/* $query['link']['StatementEntry'] = array(); */
/* if (!isset($query['link']['StatementEntry']['Customer'])) */
/* $query['link']['StatementEntry']['Customer'] = array(); */
/* if (!isset($query['link']['StatementEntry']['Customer']['fields'])) */
/* $query['link']['StatementEntry']['Customer']['fields'] = array(); */
$query['conditions'][] = array('Customer.id' => $id);
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, true);
return $this->prReturn($set);
}
@@ -161,15 +138,7 @@ class Customer extends AppModel {
$this->prEnter(compact('id', 'query'));
$this->queryInit($query);
if (!isset($query['link']['StatementEntry']))
$query['link']['StatementEntry'] = array();
/* if (!isset($query['link']['StatementEntry']['Customer'])) */
/* $query['link']['StatementEntry']['Customer'] = array(); */
/* if (!isset($query['link']['StatementEntry']['Customer']['fields'])) */
/* $query['link']['StatementEntry']['Customer']['fields'] = array(); */
$query['conditions'][] = array('Customer.id' => $id);
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
$set = $this->StatementEntry->reconciledSet('PAYMENT', $query, true);
return $this->prReturn($set);
}

View File

@@ -12,7 +12,7 @@ class Lease extends AppModel {
'StatementEntry',
);
var $default_log_level = array('log' => 30, 'show' => 15);
//var $default_log_level = array('log' => 30, 'show' => 15);
/**************************************************************************
**************************************************************************
@@ -24,21 +24,11 @@ class Lease extends AppModel {
$this->prEnter(compact('id', 'query'));
$this->queryInit($query);
if (!isset($query['link']['Lease']))
$query['link']['Lease'] = array();
if (!isset($query['link']['Lease']['fields']))
$query['link']['Lease']['fields'] = array();
$query['conditions'][] = array('Lease.id' => $id);
$query['conditions'][] = array('StatementEntry.lease_id' => $id);
$query['conditions'][] = array('StatementEntry.account_id' =>
$this->StatementEntry->Account->securityDepositAccountID());
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, false, true);
/* $set['summary'] = array('total' => $set['summary']['Charge']['total'], */
/* 'balance' => $set['summary']['Charge']['reconciled'], */
/* ); */
return $this->prReturn($set);
}
@@ -54,18 +44,12 @@ class Lease extends AppModel {
$this->prEnter(compact('id', 'query'));
$this->queryInit($query);
if (!isset($query['link']['Lease']))
$query['link']['Lease'] = array();
if (!isset($query['link']['Lease']['fields']))
$query['link']['Lease']['fields'] = array();
$query['conditions'][] = array('Lease.id' => $id);
$query['conditions'][] = array('StatementEntry.lease_id' => $id);
$query['conditions'][] = array('StatementEntry.account_id' =>
$this->StatementEntry->Account->securityDepositAccountID());
$stats = $this->StatementEntry->stats(null, $query);
$balance = $stats['Charge']['reconciled'] - $stats['Payment']['reconciled'];
return $this->prReturn($balance);
return $this->prReturn($stats['account_balance']);
}
@@ -78,9 +62,9 @@ class Lease extends AppModel {
* to pay outstanding customer charges, or simply to become
* a customer surplus (customer credit).
*/
function releaseSecurityDeposits($id, $query = null) {
$this->prFunctionLevel(30);
$this->prEnter(compact('id', 'query'));
function releaseSecurityDeposits($id, $stamp = null, $query = null) {
//$this->prFunctionLevel(30);
$this->prEnter(compact('id', 'stamp', 'query'));
$secdeps = $this->securityDeposits($id, $query);
$secdeps = $secdeps['entries'];
@@ -93,6 +77,7 @@ class Lease extends AppModel {
// Build a transaction
$release = array('Transaction' => array(), 'Entry' => array());
$release['Transaction']['stamp'] = $stamp;
$release['Transaction']['comment'] = "Security Deposit Release";
foreach ($secdeps AS $charge) {
if ($charge['StatementEntry']['type'] !== 'CHARGE')
@@ -102,7 +87,7 @@ class Lease extends AppModel {
// any unpaid (or only partially paid) security deposit should
// have the remaining balance simply waived.
if ($charge['StatementEntry']['balance'] > 0)
$this->StatementEntry->waive($charge['StatementEntry']['id']);
$this->StatementEntry->waive($charge['StatementEntry']['id'], $stamp);
$release['Entry'][] =
array('amount' => $charge['StatementEntry']['reconciled'],
@@ -112,7 +97,7 @@ class Lease extends AppModel {
}
$customer_id = $secdeps[0]['StatementEntry']['customer_id'];
$lease_id = $secdeps[0]['StatementEntry']['lease_id'];
$lease_id = $secdeps[0]['StatementEntry']['lease_id'];
$result = $this->StatementEntry->Transaction->addReceipt
($release, $customer_id, $lease_id);
@@ -377,6 +362,9 @@ class Lease extends AppModel {
// Save it!
$this->save($this->data, false);
// Release the security deposit(s)
$this->releaseSecurityDeposits($id, $stamp);
// Close the lease, if so requested
if ($close)
$this->close($id, $stamp);
@@ -477,40 +465,8 @@ class Lease extends AppModel {
return $this->prReturn(null);
$this->queryInit($query);
//$query['link'] = array('Lease' => $query['link']);
/* if (!isset($query['link']['StatementEntry'])) */
/* $query['link']['StatementEntry'] = array(); */
/* if (!isset($query['link']['StatementEntry']['ChargeEntry'])) */
/* $query['link']['StatementEntry']['ChargeEntry'] = array(); */
/* $query['link']['StatementEntry']['fields'] = array(); */
/* $query['link']['ChargeEntry']['fields'] = array(); */
/* $query['link']['ChargeEntry']['Account']['fields'] = array(); */
/* $query['link']['ChargeEntry']['StatementEntry']['fields'] = array(); */
/* $query['link']['ChargeEntry']['StatementEntry']['Invoice']['fields'] = array(); */
if (!isset($query['fields']))
$query['fields'] = array();
$query['fields'] = array_merge($query['fields'],
$this->StatementEntry->chargePaymentFields(true));
$query['conditions'][] = array('StatementEntry.lease_id' => $id);
$query['group'] = null;
$stats = $this->StatementEntry->find('first', $query);
//$this->pr(20, compact('query', 'stats'));
// The fields are all tucked into the [0] index,
// and the rest of the array is useless (empty).
$stats = $stats[0];
// Make sure we have a non-null balance
if (!isset($stats['balance']))
$stats['balance'] = 0;
$stats = $this->StatementEntry->stats(null, $query);
return $this->prReturn($stats);
}

View File

@@ -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);
/**************************************************************************
**************************************************************************
@@ -40,14 +40,14 @@ class StatementEntry extends AppModel {
($sum ? ')' : '') . ' AS charge' . ($sum ? 's' : ''),
($sum ? 'SUM(' : '') .
//"IF({$entry_name}.type IN('PAYMENT', 'SURPLUS', 'WAIVE')," .
//"IF({$entry_name}.type IN('PAYMENT', 'SURPLUS', 'WAIVER')," .
"IF({$entry_name}.type NOT IN('CHARGE', 'VOID')," .
" {$entry_name}.amount, NULL)" .
($sum ? ')' : '') . ' AS payment' . ($sum ? 's' : ''),
($sum ? 'SUM(' : '') .
//"IF({$entry_name}.type = 'CHARGE', 1," .
//" IF({$entry_name}.type IN('PAYMENT', 'SURPLUS', 'WAIVE'), -1, 0))" .
//" IF({$entry_name}.type IN('PAYMENT', 'SURPLUS', 'WAIVER'), -1, 0))" .
"IF({$entry_name}.type = 'VOID', 0," .
" IF({$entry_name}.type = 'CHARGE', 1, -1))" .
" * IF({$entry_name}.amount, {$entry_name}.amount, 0)" .
@@ -377,10 +377,13 @@ OPTION 2
*
*/
function assignCredits($query = null, $receipt_id = null,
$charge_ids = null, $payment_type = null)
$charge_ids = null, $payment_type = null,
$customer_id = null, $lease_id = null)
{
//$this->prFunctionLevel(25);
$this->prEnter( compact('query', 'receipt_id', 'charge_ids', 'payment_type'));
$this->prEnter(compact('query', 'receipt_id',
'charge_ids', 'payment_type',
'customer_id', 'lease_id'));
$this->queryInit($query);
if (empty($payment_type))
@@ -570,7 +573,8 @@ OPTION 2
'amount' => $credit['balance'],
'effective_date' => $credit['Transaction']['stamp'],
'transaction_id' => $credit['Transaction']['id'],
'customer_id' => $credit['Customer']['id'],
'customer_id' => $customer_id,
'lease_id' => $lease_id,
));
$ret['Credit'] = $result;
if ($result['error'])
@@ -597,92 +601,95 @@ OPTION 2
if (isset($id))
$query['conditions'][] = array('StatementEntry.id' => $id);
$rquery = $query;
unset($rquery['link']['ChargeEntry']);
$rquery['link']['PaymentEntry'] = array('fields' => array());
// Determine the total in charges
$charge_query = $query;
unset($charge_query['link']['ChargeEntry']);
unset($charge_query['link']['PaymentEntry']);
$rquery['fields'] = array();
$rquery['fields'][] = "StatementEntry.amount";
$rquery['fields'][] = "SUM(PaymentEntry.amount) AS reconciled";
$charge_query['fields'] = array();
$charge_query['fields'][] = "SUM(StatementEntry.amount) AS total";
$charge_query['conditions'][] = array('StatementEntry.type' => 'CHARGE');
$result = $this->find('first', $charge_query);
$stats['Charge'] = $result[0];
$rquery['conditions'][] = array('StatementEntry.type' => 'CHARGE');
$rquery['group'] = 'StatementEntry.id';
$result = $this->find('all', $rquery);
$stats['Charge'] = array('total' => 0, 'reconciled' => 0);
foreach($result AS $charge) {
$stats['Charge']['total'] += $charge['StatementEntry']['amount'];
$stats['Charge']['reconciled'] += $charge[0]['reconciled'];
}
$stats['Charge']['balance'] =
$stats['Charge']['total'] - $stats['Charge']['reconciled'];
$this->pr(17, compact('rquery', 'result'),
$this->pr(17, compact('charge_query', 'result'),
'Charges');
$rquery = $query;
unset($rquery['link']['ChargeEntry']);
$rquery['link']['PaymentEntry'] = array('fields' => array(),
/* 'conditions' => */
/* array('PaymentEntry.type' => 'WAIVE'), */
);
$rquery['fields'] = array();
$rquery['fields'][] = "SUM(PaymentEntry.amount) AS reconciled";
$rquery['conditions'][] = array('StatementEntry.type' => 'CHARGE');
$rquery['conditions'][] = array('PaymentEntry.type' => 'WAIVE');
$rquery['group'] = 'StatementEntry.id';
$result = $this->find('first', $rquery);
$stats['Charge']['waived'] = $result[0]['reconciled'];
/* $stats['Waiver'] = array('total' => 0, 'reconciled' => 0); */
/* foreach($result AS $charge) { */
/* $stats['Waiver']['total'] += $charge['StatementEntry']['amount']; */
/* $stats['Waiver']['reconciled'] += $charge[0]['reconciled']; */
/* } */
/* $stats['Waiver']['balance'] = */
/* $stats['Waiver']['total'] - $stats['Waiver']['reconciled']; */
// Tally the amount actually _paid_ to those charges
$charge_payment_query = $charge_query;
$charge_payment_query['link']['PaymentEntry'] = array('fields' => array());
$charge_payment_query['fields'] = array();
$charge_payment_query['fields'][] = "COALESCE(SUM(PaymentEntry.amount),0) AS paid";
$charge_payment_query['conditions'][] = array('PaymentEntry.type' => 'PAYMENT');
$result = $this->find('first', $charge_payment_query);
$stats['Charge'] += $result[0];
$this->pr(17, compact('rquery', 'result'),
'Waived');
$this->pr(17, compact('charge_payment_query', 'result'),
'Charge Payments');
$rquery = $query;
unset($rquery['link']['PaymentEntry']);
$rquery['link']['ChargeEntry'] = array('fields' => array());
// Tally the amount of charges that have been waived
$charge_waiver_query = $charge_query;
$charge_waiver_query['link']['PaymentEntry'] = array('fields' => array());
$charge_waiver_query['fields'] = array();
$charge_waiver_query['fields'][] = "COALESCE(SUM(PaymentEntry.amount),0) AS waived";
$charge_waiver_query['conditions'][] = array('PaymentEntry.type' => 'WAIVER');
$result = $this->find('first', $charge_waiver_query);
$stats['Charge'] += $result[0];
$this->pr(17, compact('charge_waiver_query', 'result'),
'Charge Waivers');
$rquery['fields'] = array();
$rquery['fields'][] = "SUM(StatementEntry.amount) AS total";
$rquery['fields'][] = "SUM(IF(ChargeEntry.id IS NULL, 0, StatementEntry.amount)) AS reconciled";
$rquery['fields'][] = "SUM(IF(ChargeEntry.id IS NULL, StatementEntry.amount, 0)) AS balance";
// Compute some summary information for charges
$stats['Charge']['reconciled'] =
$stats['Charge']['paid'] + $stats['Charge']['waived'];
$stats['Charge']['balance'] =
$stats['Charge']['total'] - $stats['Charge']['reconciled'];
if (!isset($stats['Charge']['balance']))
$stats['Charge']['balance'] = 0;
$rquery['conditions'][] = array('StatementEntry.type' => 'PAYMENT');
$result = $this->find('first', $rquery);
if (!isset($result[0]['balance']))
$result[0]['balance'] = 0;
// Determine the total in payments, including those which
// are charge waivers and those that do not even reconcile
// to charges (i.e. they are surplus payments).
$payment_query = $query;
unset($payment_query['link']['PaymentEntry']);
$payment_query['link']['ChargeEntry'] = array('fields' => array());
$payment_query['fields'] = array();
$payment_query['fields'][] = "SUM(StatementEntry.amount) AS total";
$payment_query['fields'][] = "COALESCE(SUM(IF(ChargeEntry.id IS NULL, 0, StatementEntry.amount)), 0) AS charged";
$payment_query['fields'][] = "COALESCE(SUM(IF(ChargeEntry.id IS NULL, StatementEntry.amount, 0)), 0) AS surplus";
$payment_query['conditions'][] = array('StatementEntry.type' => array('PAYMENT', 'WAIVER', 'SURPLUS'));
$result = $this->find('first', $payment_query);
$stats['Payment'] = $result[0];
$this->pr(17, compact('rquery', 'result'),
$this->pr(17, compact('payment_query', 'result'),
'Payments');
$rquery = $query;
unset($rquery['link']['PaymentEntry']);
unset($rquery['link']['ChargeEntry']);
// Compute some summary information for payments.
// Add a reconciled field just for consistency with Charge.
$stats['Payment']['reconciled'] =
$stats['Payment']['charged'];
$stats['Payment']['balance'] =
$stats['Payment']['total'] - $stats['Payment']['reconciled'];
if (!isset($stats['Payment']['balance']))
$stats['Payment']['balance'] = 0;
$rquery['fields'] = array();
$rquery['fields'][] = "SUM(StatementEntry.amount) AS total";
$rquery['fields'][] = "SUM(0) AS reconciled";
$rquery['conditions'][] = array('StatementEntry.type' => 'SURPLUS');
$result = $this->find('first', $rquery);
$result[0]['balance'] = $result[0]['total'] - $result[0]['reconciled'];
if (!isset($result[0]['balance']))
$result[0]['balance'] = 0;
$stats['Surplus'] = $result[0];
$this->pr(17, compact('rquery', 'result'),
'Surplus');
// 'balance' is simply the difference between
// the balances of charges and payments
$stats['balance'] = $stats['Charge']['balance'] - $stats['Payment']['balance'];
if (!isset($stats['balance']))
$stats['balance'] = 0;
// 'account_balance' is really only relevant to
// callers that have requested charge and payment
// stats with respect to a particular account.
// It represents the difference between inflow
// and outflow from that account.
$stats['account_balance'] = $stats['Charge']['paid'] - $stats['Payment']['total'];
if (!isset($stats['account_balance']))
$stats['account_balance'] = 0;
return $this->prReturn($stats);
}

View File

@@ -37,7 +37,7 @@ class Transaction extends AppModel {
);
var $default_log_level = array('log' => 30, 'show' => 15);
//var $default_log_level = array('log' => 30, 'show' => 15);
/**************************************************************************
**************************************************************************
@@ -127,7 +127,7 @@ class Transaction extends AppModel {
// Just make sure the payment(s) are marked as waivers
// and that they go to cover the specific charge.
$data['Transaction']['payment_type'] = 'WAIVE';
$data['Transaction']['payment_type'] = 'WAIVER';
$data['Transaction']['charge_entry_id'] = $charge_id;
// In all other respects this is just a receipt.
@@ -521,7 +521,9 @@ class Transaction extends AppModel {
: null),
(!empty($transaction['payment_type'])
? $transaction['payment_type']
: null)
: null),
$transaction['customer_id'],
$transaction['lease_id']
);
$ret['assigned'] = $result;