Added outstanding charges and outstanding credits to the customer page, and reorded fields in order of presumed importance. Removed items from Unit/Lease which really shouldn't have been there. They were added for convenience, but the redundancy was confusing. Added a couple report links to make it easy to see outstanding charges and collected rents.

git-svn-id: file:///svn-source/pmgr/branches/v0.3_work@977 97e9348a-65ac-dc4b-aefc-98561f571b83
This commit is contained in:
abijah
2010-03-31 19:49:56 +00:00
parent 4892143c0c
commit 68cbca5e28
9 changed files with 206 additions and 121 deletions

View File

@@ -263,6 +263,15 @@ class AppController extends Controller {
'SITE', $this->op_area); 'SITE', $this->op_area);
if ($this->admin()) { if ($this->admin()) {
$acct = new Account;
$this->addSideMenuLink('Collected Rent',
array('controller' => 'accounts',
'action' => 'collected',
$acct->rentAccountID()), null,
'REPORT');
$this->addSideMenuLink('Unpaid Charges',
array('controller' => 'statement_entries', 'action' => 'unpaid'), null,
'REPORT');
$this->addSideMenuLink('Lease Up', $this->addSideMenuLink('Lease Up',
array('controller' => 'leases', 'action' => 'overview'), null, array('controller' => 'leases', 'action' => 'overview'), null,
'REPORT'); 'REPORT');

View File

@@ -11,6 +11,7 @@ class StatementEntriesController extends AppController {
*/ */
function index() { $this->gridView('All Statement Entries'); } function index() { $this->gridView('All Statement Entries'); }
function unpaid() { $this->gridView('Unpaid Charges', 'unreconciled'); }
/************************************************************************** /**************************************************************************
@@ -123,18 +124,6 @@ class StatementEntriesController extends AppController {
array(array('ChargeEntry.id' => $statement_entry_id), array(array('ChargeEntry.id' => $statement_entry_id),
array('DisbursementEntry.id' => $statement_entry_id))); array('DisbursementEntry.id' => $statement_entry_id)));
if ($params['action'] === 'unreconciled') {
$query = array('conditions' => $conditions);
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, true);
$entries = array();
foreach ($set['entries'] AS $entry)
$entries[] = $entry['StatementEntry']['id'];
$conditions[] = array('StatementEntry.id' => $entries);
$params['userdata']['balance'] = $set['summary']['balance'];
}
return $conditions; return $conditions;
} }
@@ -169,25 +158,19 @@ class StatementEntriesController extends AppController {
} }
function gridDataRecordsExecute(&$params, &$model, $query) { function gridDataRecordsExecute(&$params, &$model, $query) {
/* if ($params['action'] === '???') { */
/* $tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1)); */
/* $tquery['fields'] = array("IF(StatementEntry.type = 'CHARGE'," . */
/* " SUM(COALESCE(DisbursementEntry.amount,0))," . */
/* " SUM(COALESCE(ChargeEntry.amount,0)))" . */
/* " AS 'applied'", */
/* "StatementEntry.amount - (" . */ if ($params['action'] === 'unreconciled') {
/* "IF(StatementEntry.type = 'CHARGE'," . */ $lquery = array('conditions' => $query['conditions']);
/* " SUM(COALESCE(DisbursementEntry.amount,0))," . */ $set = $this->StatementEntry->reconciledSet('CHARGE', $lquery, true);
/* " SUM(COALESCE(ChargeEntry.amount,0)))" . */
/* ") AS 'balance'", */ $entries = array();
/* ); */ foreach ($set['entries'] AS $entry)
$entries[] = $entry['StatementEntry']['id'];
$query['conditions'] = array('StatementEntry.id' => $entries);
$params['userdata']['balance'] = $set['summary']['balance'];
}
/* //pr(compact('tquery')); */
/* $total = $model->find('first', $tquery); */
/* $params['userdata']['total'] = $total[0]['applied']; */
/* $params['userdata']['balance'] = $total[0]['balance']; */
/* } */
if ($params['action'] === 'collected') { if ($params['action'] === 'collected') {
$tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1)); $tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1));
$tquery['fields'] = array("SUM(COALESCE(StatementEntry.amount,0)) AS 'total'"); $tquery['fields'] = array("SUM(COALESCE(StatementEntry.amount,0)) AS 'total'");

View File

@@ -294,7 +294,7 @@ class StatementEntry extends AppModel {
} }
return $this->prReturn(array('entries' => $resultset, return $this->prReturn(array('entries' => $resultset,
'summary' => $this->stats(null, $query))); 'summary' => $this->stats(null, $query)));
} }
@@ -338,6 +338,72 @@ class StatementEntry extends AppModel {
} }
/**************************************************************************
**************************************************************************
**************************************************************************
* function: outstandingDebits
* - Determines all debit types that have not yet been resolved.
* The name is a bit dumb, but it means any statement entry type
* that a positive customer balance could be used to offset. In
* other words, entries that are still in need of matching
* disbursements. Most notably, this means charges but could
* also mean things like refunds as well.
*/
function outstandingDebits($query = null, $customer_id = null,
$lease_id = null, $debit_types = null)
{
$this->prEnter(compact('query', 'lease_id',
'customer_id', 'charge_ids',
'debit_types'));
$this->queryInit($query);
if (empty($debit_types))
$debit_types = $this->debitTypes();
if (!empty($customer_id))
$query['conditions'][] = array('StatementEntry.customer_id' => $customer_id);
if (!empty($lease_id))
$query['conditions'][] = array('StatementEntry.lease_id' => $lease_id);
/* if (isset($charge_ids)) { */
/* // REVISIT <AP> 20100330: */
/* // Not using $query here, as this code was extracted from its */
/* // original location in assignCredits, and so I'm keeping the */
/* // logic consistent. It does seem, however, that we shouldn't */
/* // be ignoring $query if passed in. I'm sure this won't be */
/* // looked at until someone _does_ pass $query in (and it break), */
/* // so hopefully at that time, we can understand what needs to */
/* // happen in that case (requirements are not clear at present). */
/* $lquery = array('contain' => false, */
/* '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); */
/* } */
if (empty($query['order']))
$query['order'] = 'StatementEntry.effective_date ASC';
$debits = array();
foreach ($debit_types AS $dtype) {
$rset = $this->reconciledSet($dtype, $query, true);
$entries = $rset['entries'];
$debits = array_merge($debits, $entries);
$this->pr(18, compact('dtype', 'entries'), "Outstanding Debit Entries");
}
return $this->prReturn($debits);
}
function outstandingCharges($query = null, $customer_id = null, $lease_id = null) {
return $this->outstandingDebits($query, $customer_id, $lease_id, array('CHARGE'));
}
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************
@@ -359,9 +425,6 @@ class StatementEntry extends AppModel {
'customer_id', 'lease_id')); 'customer_id', 'lease_id'));
$this->queryInit($query); $this->queryInit($query);
if (!empty($customer_id))
$query['conditions'][] = array('StatementEntry.customer_id' => $customer_id);
if (empty($disbursement_type)) if (empty($disbursement_type))
$disbursement_type = 'DISBURSEMENT'; $disbursement_type = 'DISBURSEMENT';
@@ -375,6 +438,9 @@ class StatementEntry extends AppModel {
$this->INTERNAL_ERROR("Charge IDs, yet no corresponding receipt"); $this->INTERNAL_ERROR("Charge IDs, yet no corresponding receipt");
$lquery = $query; $lquery = $query;
if (!empty($customer_id))
$lquery['conditions'][] = array('StatementEntry.customer_id' => $customer_id);
$lquery['conditions'][] = array('StatementEntry.type' => 'SURPLUS'); $lquery['conditions'][] = array('StatementEntry.type' => 'SURPLUS');
// REVISIT <AP>: 20090804 // REVISIT <AP>: 20090804
// We need to ensure that we're using surplus credits ONLY from either // We need to ensure that we're using surplus credits ONLY from either
@@ -423,23 +489,16 @@ class StatementEntry extends AppModel {
"Receipt Credit Added"); "Receipt Credit Added");
} }
// Now find all unpaid charges // Now find all unpaid charges, using either the specific set
if (isset($charge_ids)) { // of charges given, or all outstanding charges based on the
$lquery = array('contain' => false, // query, customer and/or lease
'conditions' => array('StatementEntry.id' => $charge_ids)); if (!empty($charge_ids)) {
} else { $this->INTERNAL_ERROR("PERHAPS IMPLEMENTED - THOUGH NEVER TESTED");
$lquery = $query; $lquery = $query;
// If we're working with a specific lease, then limit charges to it $lquery['conditions'][] = array('StatementEntry.id' => $charge_ids);
if (!empty($lease_id)) $charges = $this->reconciledSet('CHARGE', $query, true);
$lquery['conditions'][] = array('StatementEntry.lease_id' => $lease_id); } else {
} $charges = $this->outstandingDebits($query, $customer_id, $lease_id);
$lquery['order'] = 'StatementEntry.effective_date ASC';
$charges = array();
foreach ($this->debitTypes() AS $dtype) {
$rset = $this->reconciledSet($dtype, $lquery, true);
$entries = $rset['entries'];
$charges = array_merge($charges, $entries);
$this->pr(18, compact('dtype', 'entries'), "Outstanding Debit Entries");
} }
// Work through all unpaid charges, applying disbursements as we go // Work through all unpaid charges, applying disbursements as we go

View File

@@ -130,11 +130,11 @@ function updateCharges(id) {
$("#receipt-balance").html("Calculating..."); $("#receipt-balance").html("Calculating...");
$("#receipt-charges-caption").html("Please Wait..."); $("#receipt-charges-caption").html("Please Wait...");
var custom = new Array(); var filter = new Array();
custom['customer_id'] = id; filter['StatementEntry.customer_id'] = id;
var dynamic_post = new Array(); var dynamic_post = new Array();
dynamic_post['custom'] = custom; dynamic_post['filter'] = filter;
$('#charge-entries-jqGrid').setPostDataItem('dynamic_post_replace', serialize(dynamic_post)); $('#charge-entries-jqGrid').setPostDataItem('dynamic_post_replace', serialize(dynamic_post));
$('#charge-entries-jqGrid') $('#charge-entries-jqGrid')
@@ -333,7 +333,7 @@ echo $this->element('statement_entries', array
'action' => 'unreconciled', 'action' => 'unreconciled',
'exclude' => array('Customer', 'Type', 'Debit', 'Credit'), 'exclude' => array('Customer', 'Type', 'Debit', 'Credit'),
'include' => array('Applied', 'Balance'), 'include' => array('Applied', 'Balance'),
'remap' => array('Applied' => 'Paid'), 'remap' => array('Received' => 'Paid'),
'limit' => 8, 'limit' => 8,
), ),
)); ));

View File

@@ -53,15 +53,55 @@ echo '<div CLASS="detail supporting">' . "\n";
/********************************************************************** /**********************************************************************
* Contacts * Unpaid Charges
*/ */
echo $this->element('contacts', array echo $this->element('statement_entries', array
(// Grid configuration (// Grid configuration
'config' => array 'config' => array
('caption' => 'Customer Contacts', ('caption' => 'Outstanding Charges',
'filter' => array('Customer.id' => $customer['Customer']['id']), 'limit' => 10,
'include' => array('Relationship'), 'action' => 'unreconciled',
'filter' => array('StatementEntry.customer_id' => $lease['id']),
'exclude' => array('Customer'),
)));
/**********************************************************************
* Customer Credits
*/
echo $this->element('statement_entries', array
(// Grid configuration
'config' => array
('caption' => 'Oustanding Credits',
'grid_div_id' => 'surplus',
'limit' => 10,
'filter' => array('Customer.id' => $customer['Customer']['id'],
'StatementEntry.type' => 'SURPLUS'),
'exclude' => array('Entry', 'Effective', 'Customer', 'Unit', 'Account', 'Debit', 'Credit'),
'include' => array('Transaction', 'Amount'),
'remap' => array('Transaction' => 'Receipt'),
)));
/**********************************************************************
* Receipt History
*/
echo $this->element('ledger_entries', array
(// Grid configuration
'config' => array
('caption' => 'Receipts',
'limit' => 5,
'filter' => array('Customer.id' => $customer['Customer']['id'],
'Transaction.type' => 'RECEIPT',
'Tender.id !=' => null,
//'Account.id !=' => '-AR-'
),
'include' => array('Transaction'),
'exclude' => array('Entry', 'Account', 'Cr/Dr'),
'remap' => array('Transaction' => 'Receipt'),
))); )));
@@ -73,13 +113,28 @@ echo $this->element('leases', array
(// Grid configuration (// Grid configuration
'config' => array 'config' => array
('caption' => 'Lease History', ('caption' => 'Lease History',
'filter' => array('Customer.id' => $customer['Customer']['id']), 'limit' => 5,
'filter' => array('Customer.id' => $customer['Customer']['id']),
'exclude' => array('Customer'), 'exclude' => array('Customer'),
'sort_column' => 'Move-In', 'sort_column' => 'Move-In',
'sort_order' => 'DESC', 'sort_order' => 'DESC',
))); )));
/**********************************************************************
* Contacts
*/
echo $this->element('contacts', array
(// Grid configuration
'config' => array
('caption' => 'Customer Contacts',
'limit' => 5,
'filter' => array('Customer.id' => $customer['Customer']['id']),
'include' => array('Relationship'),
)));
/********************************************************************** /**********************************************************************
* Customer Account History * Customer Account History
*/ */
@@ -90,28 +145,11 @@ echo $this->element('statement_entries', array
('caption' => 'Customer Statement', ('caption' => 'Customer Statement',
'filter' => array('Customer.id' => $customer['Customer']['id'], 'filter' => array('Customer.id' => $customer['Customer']['id'],
'type !=' => 'VOID'), 'type !=' => 'VOID'),
'include' => array('Sub-Total'),
'exclude' => array('Customer'), 'exclude' => array('Customer'),
))); )));
/**********************************************************************
* Receipt History
*/
echo $this->element('ledger_entries', array
(// Grid configuration
'config' => array
('caption' => 'Receipts',
'filter' => array('Customer.id' => $customer['Customer']['id'],
'Transaction.type' => 'RECEIPT',
'Tender.id !=' => null,
//'Account.id !=' => '-AR-'
),
'include' => array('Transaction'),
'exclude' => array('Entry', 'Account', 'Cr/Dr'),
)));
/* End "detail supporting" div */ /* End "detail supporting" div */
echo '</div>' . "\n"; echo '</div>' . "\n";

View File

@@ -21,7 +21,7 @@ $cols['Debit'] = array('index' => 'charge', 'formatter' =>
$cols['Credit'] = array('index' => 'disbursement', 'formatter' => 'currency'); $cols['Credit'] = array('index' => 'disbursement', 'formatter' => 'currency');
$cols['Amount'] = array('index' => "StatementEntry.amount", 'formatter' => 'currency'); $cols['Amount'] = array('index' => "StatementEntry.amount", 'formatter' => 'currency');
$cols['Applied'] = array('index' => "applied", 'formatter' => 'currency'); $cols['Received'] = array('index' => "applied", 'formatter' => 'currency');
// 'balance' is already in use as part of charge/disbursement/balance. // 'balance' is already in use as part of charge/disbursement/balance.
// 'unapplied' isn't quite the right term, but it's not customer visible. // 'unapplied' isn't quite the right term, but it's not customer visible.
$cols['Balance'] = array('index' => "unapplied", 'formatter' => 'currency'); $cols['Balance'] = array('index' => "unapplied", 'formatter' => 'currency');
@@ -32,6 +32,22 @@ if (isset($subtotal_column))
$cols['Sub-Total']['index'] = $cols['Sub-Total']['index'] =
'subtotal-' . $cols[$subtotal_column]['index']; 'subtotal-' . $cols[$subtotal_column]['index'];
if ((isset($action) && $action == 'unreconciled') ||
(isset($config) && isset($config['action']) && $config['action'] == 'unreconciled')) {
if (isset($config) && !isset($config['grid_div_id']))
$config['grid_div_id'] = 'unpaid';
$include_columns = array('Entry', 'Date',
'Effective', 'Customer', 'Unit',
'Account', 'Amount', 'Received', 'Balance');
}
else {
$include_columns =
array_diff(array_keys($cols),
array('Transaction', 'Through', 'Lease',
'Amount', 'Received', 'Balance', 'Sub-Total',
'Comment'));
}
// Include custom data // Include custom data
$grid->customData(compact('statement_entry_id')); $grid->customData(compact('statement_entry_id'));
@@ -41,8 +57,5 @@ $grid
->sortField('Date', 'DESC') ->sortField('Date', 'DESC')
->defaultFields(array('Entry', 'Date', 'Charge', 'Payment')) ->defaultFields(array('Entry', 'Date', 'Charge', 'Payment'))
->searchFields(array('Customer', 'Unit')) ->searchFields(array('Customer', 'Unit'))
->render($this, isset($config) ? $config : null, ->render($this, isset($config) ? $config : null, $include_columns);
array_diff(array_keys($cols), array('Transaction', 'Through', 'Lease',
'Amount', 'Applied', 'Balance', 'Sub-Total',
'Comment')));

View File

@@ -215,6 +215,12 @@ class GridHelper extends AppHelper {
} }
$this->jqGrid_options['controller'] = $controller; $this->jqGrid_options['controller'] = $controller;
// Add in any custom variables requested
if (isset($config['custom'])) {
$this->customData($config['custom']);
unset($config['custom']);
}
// Incorporate all other user options // Incorporate all other user options
if (isset($config)) if (isset($config))
$this->jqGrid_options = array_merge($this->jqGrid_options, $config); $this->jqGrid_options = array_merge($this->jqGrid_options, $config);

View File

@@ -78,6 +78,21 @@ echo '</div>' . "\n";
echo '<div CLASS="detail supporting">' . "\n"; echo '<div CLASS="detail supporting">' . "\n";
/**********************************************************************
* Unpaid Charges
*/
echo $this->element('statement_entries', array
(// Grid configuration
'config' => array
('caption' => 'Outstanding Charges',
'limit' => 10,
'action' => 'unreconciled',
'filter' => array('StatementEntry.lease_id' => $lease['id']),
'exclude' => array('Customer', 'Unit'),
)));
/********************************************************************** /**********************************************************************
* Lease Account History * Lease Account History
*/ */
@@ -86,30 +101,12 @@ echo $this->element('statement_entries', array
(// Grid configuration (// Grid configuration
'config' => array 'config' => array
('caption' => 'Lease Statement', ('caption' => 'Lease Statement',
'filter' => array('Lease.id' => $lease['id']), 'filter' => array('Lease.id' => $lease['id']),
'include' => array('Through'), 'include' => array('Through'),
'exclude' => array('Customer', 'Lease', 'Unit'), 'exclude' => array('Customer', 'Lease', 'Unit'),
))); )));
/**********************************************************************
* Receipt History
*/
echo $this->element('ledger_entries', array
(// Grid configuration
'config' => array
('caption' => 'Customer Receipts',
'filter' => array('Customer.id' => $customer['id'],
'Transaction.type' => 'RECEIPT',
'Tender.id !=' => null,
//'Account.id !=' => '-AR-'
),
'include' => array('Transaction'),
'exclude' => array('Entry', 'Account', 'Cr/Dr'),
)));
/* End "detail supporting" div */ /* End "detail supporting" div */
echo '</div>' . "\n"; echo '</div>' . "\n";

View File

@@ -76,26 +76,6 @@ echo $this->element('leases', array
))); )));
/**********************************************************************
* Current Customer Lease Account History
*/
if (isset($current_lease['id'])) {
echo $this->element('statement_entries', array
(// Grid configuration
'config' => array
(
'caption' =>
('Current Lease Statement ('
. $current_lease['Customer']['name']
. ')'),
'filter' => array('Lease.id' => $current_lease['id']),
'include' => array('Through'),
'exclude' => array('Customer', 'Lease', 'Unit'),
)));
}
/* End "detail supporting" div */ /* End "detail supporting" div */
echo '</div>' . "\n"; echo '</div>' . "\n";