git-svn-id: file:///svn-source/pmgr/branches/v0.3_work@1013 97e9348a-65ac-dc4b-aefc-98561f571b83
410 lines
15 KiB
PHP
410 lines
15 KiB
PHP
<?php
|
|
|
|
class StatementEntriesController extends AppController {
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* action: index / current / past / all
|
|
* - Creates a list of statement entries
|
|
*/
|
|
|
|
function index() { $this->gridView('All Statement Entries'); }
|
|
function unpaid() { $this->gridView('Unpaid Charges', 'unreconciled'); }
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* virtuals: gridData
|
|
* - With the application controller handling the gridData action,
|
|
* these virtual functions ensure that the correct data is passed
|
|
* to jqGrid.
|
|
*/
|
|
|
|
function gridDataCountTables(&$params, &$model) {
|
|
$link =
|
|
array(// Models
|
|
'Transaction' =>
|
|
array('fields' => array('id', 'stamp'),
|
|
),
|
|
|
|
'Customer' =>
|
|
array('fields' => array('id', 'name'),
|
|
),
|
|
|
|
'Lease' =>
|
|
array('fields' => array('id', 'number'),
|
|
'Unit' =>
|
|
array('fields' => array('id', 'name'),
|
|
),
|
|
),
|
|
|
|
'Account' =>
|
|
array('fields' => array('id', 'name', 'type'),
|
|
),
|
|
);
|
|
|
|
if (!empty($params['post']['custom']['statement_entry_id'])) {
|
|
$link['ChargeEntry'] = array();
|
|
// This query actually represents a union...
|
|
// Unpaid Charge/Surplus: ChargeID - NULL; DisbursementID - NULL
|
|
// Paid Charge/Refund: ChargeID - NULL; DisbursementID - !NULL
|
|
// Disbursement/Reversal: ChargeID - !NULL; DisbursementID - NULL
|
|
// <EMPTY SET>: ChargeID - !NULL; DisbursementID - !NULL
|
|
//
|
|
// The query is really slow unless we add the `id` condition to the join.
|
|
// A cleaner query would be nice, but we must work within the Cake framework.
|
|
$link['DisbursementEntry'] = array('conditions' =>
|
|
'`DisbursementEntry`.`id` = '
|
|
. $params['post']['custom']['statement_entry_id']);
|
|
}
|
|
|
|
return array('link' => $link);
|
|
}
|
|
|
|
function gridDataTables(&$params, &$model) {
|
|
$tables = $this->gridDataCountTables($params, $model);
|
|
|
|
if (in_array('applied', $params['post']['fields'])) {
|
|
$tables['link'] +=
|
|
array('ChargeEntry' => array(),
|
|
'DisbursementEntry' => array());
|
|
}
|
|
|
|
return $tables;
|
|
}
|
|
|
|
function gridDataFields(&$params, &$model) {
|
|
$fields = parent::gridDataFields($params, $model);
|
|
|
|
if (in_array('applied', $params['post']['fields'])) {
|
|
$fields[] = ("IF(StatementEntry.type = 'CHARGE'," .
|
|
" SUM(COALESCE(DisbursementEntry.amount,0))," .
|
|
" SUM(COALESCE(ChargeEntry.amount,0)))" .
|
|
" AS 'applied'");
|
|
}
|
|
if (in_array('unapplied', $params['post']['fields'])) {
|
|
$fields[] = ("StatementEntry.amount - (" .
|
|
"IF(StatementEntry.type = 'CHARGE'," .
|
|
" SUM(COALESCE(DisbursementEntry.amount,0))," .
|
|
" SUM(COALESCE(ChargeEntry.amount,0)))" .
|
|
") AS 'unapplied'");
|
|
}
|
|
|
|
$fields = array_merge($fields,
|
|
$this->StatementEntry->chargeDisbursementFields());
|
|
|
|
return $fields;
|
|
}
|
|
|
|
function gridDataConditions(&$params, &$model) {
|
|
$conditions = parent::gridDataConditions($params, $model);
|
|
extract($params['post']['custom']);
|
|
|
|
if (!empty($from_date))
|
|
$conditions[]
|
|
= array('Transaction.stamp >=' =>
|
|
$this->StatementEntry->Transaction->dateFormatBeforeSave($from_date));
|
|
|
|
if (!empty($through_date))
|
|
$conditions[]
|
|
= array('Transaction.stamp <=' =>
|
|
$this->StatementEntry->Transaction->dateFormatBeforeSave($through_date . ' 23:59:59'));
|
|
|
|
if (isset($account_id))
|
|
$conditions[] = array('StatementEntry.account_id' => $account_id);
|
|
|
|
if (isset($customer_id))
|
|
$conditions[] = array('StatementEntry.customer_id' => $customer_id);
|
|
|
|
if (isset($statement_entry_id))
|
|
$conditions[] = array('OR' =>
|
|
array(array('ChargeEntry.id' => $statement_entry_id),
|
|
array('DisbursementEntry.id' => $statement_entry_id)));
|
|
|
|
return $conditions;
|
|
}
|
|
|
|
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
|
$links['StatementEntry'] = array('id');
|
|
$links['Transaction'] = array('id');
|
|
// REVISIT <AP>: 20090827
|
|
// Need to take 'level' into account
|
|
if ($this->Permission->allow('controller.accounts'))
|
|
$links['Account'] = array('name');
|
|
$links['Customer'] = array('name');
|
|
$links['Lease'] = array('number');
|
|
$links['Unit'] = array('name');
|
|
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
|
}
|
|
|
|
function gridDataOrder(&$params, &$model, $index, $direction) {
|
|
$order = parent::gridDataOrder($params, $model, $index, $direction);
|
|
|
|
// After sorting by whatever the user wants, add these
|
|
// defaults into the sort mechanism. If we're already
|
|
// sorting by one of them, it will only be redundant,
|
|
// and should cause no harm (possible a longer query?)
|
|
if ($index != 'Transaction.stamp' &&
|
|
$index != 'StatementEntry.effective_date') {
|
|
$order[] = 'Transaction.stamp ' . $direction;
|
|
$order[] = 'StatementEntry.effective_date ' . $direction;
|
|
}
|
|
$order[] = 'StatementEntry.id ' . $direction;
|
|
|
|
return $order;
|
|
}
|
|
|
|
function gridDataCountExecute(&$params, &$model, $query) {
|
|
if ($params['action'] === 'unreconciled') {
|
|
// REVISIT <AP> 20100413:
|
|
// This is a lame solution, as it runs the same queries twice
|
|
// (and causes code duplication). However, I'm not in the mood
|
|
// to flush out an actual "count" solution at the moment, and I
|
|
// also don't want to cache the results in $params (although
|
|
// that is probably the most sensible solution). So, I'll just
|
|
// calculate the reconciled set both times and live with the
|
|
// performance and maintenance penalty
|
|
$lquery = array('conditions' => $query['conditions']);
|
|
$set = $this->StatementEntry->reconciledSet('CHARGE', $lquery, true);
|
|
return count($set['entries']);
|
|
}
|
|
|
|
return parent::gridDataCountExecute($params, $model, $query);
|
|
}
|
|
|
|
function gridDataRecordsExecute(&$params, &$model, $query) {
|
|
|
|
if ($params['action'] === 'unreconciled') {
|
|
$lquery = array('conditions' => $query['conditions']);
|
|
$set = $this->StatementEntry->reconciledSet('CHARGE', $lquery, true);
|
|
|
|
$entries = array();
|
|
foreach ($set['entries'] AS $entry)
|
|
$entries[] = $entry['StatementEntry']['id'];
|
|
|
|
$query['conditions'] = array('StatementEntry.id' => $entries);
|
|
$params['userdata']['balance'] = $set['summary']['balance'];
|
|
}
|
|
|
|
if ($params['action'] === 'collected') {
|
|
$tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1));
|
|
$tquery['fields'] = array("SUM(COALESCE(StatementEntry.amount,0)) AS 'total'");
|
|
$total = $model->find('first', $tquery);
|
|
$params['userdata']['total'] = $total[0]['total'];
|
|
}
|
|
|
|
return parent::gridDataRecordsExecute($params, $model, $query);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* action: reverse the ledger entry
|
|
*/
|
|
|
|
function reverse($id = null) {
|
|
if ($this->data) {
|
|
//pr($this->data); die();
|
|
|
|
$this->StatementEntry->reverse
|
|
($this->data['StatementEntry']['id'],
|
|
$this->data['Transaction']['stamp'],
|
|
$this->data['Transaction']['comment']);
|
|
|
|
$this->redirect(array('action'=>'view',
|
|
$this->data['StatementEntry']['id']));
|
|
$this->INTERNAL_ERROR('SHOULD HAVE REDIRECTED');
|
|
}
|
|
|
|
$this->StatementEntry->id = $id;
|
|
$entry = $this->StatementEntry->find
|
|
('first', array
|
|
('contain' => array('Customer', 'Transaction', 'Account'),
|
|
));
|
|
|
|
if (empty($entry)) {
|
|
$this->Session->setFlash(__('Invalid Item.', true));
|
|
$this->redirect(array('controller' => 'customers',
|
|
'action'=>'index'));
|
|
}
|
|
|
|
if (!$this->StatementEntry->reversable($id)) {
|
|
$this->Session->setFlash(__('Item not reversable.', true));
|
|
$this->redirect(array('action'=>'view', $id));
|
|
}
|
|
|
|
// Prepare to render.
|
|
$title = ("Charge #{$entry['StatementEntry']['id']}" .
|
|
" : {$entry['StatementEntry']['amount']}" .
|
|
" : Reverse");
|
|
$this->set(compact('entry', 'title'));
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* action: waive the ledger entry
|
|
*/
|
|
|
|
function waive($id) {
|
|
$this->StatementEntry->waive($id);
|
|
$this->redirect(array('action'=>'view', $id));
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* action: incexpbymonth
|
|
* - Displays income and/or expenses by month
|
|
*/
|
|
|
|
function incexpbymonth($accts, $security_deposits, $months) {
|
|
$datefrom = 'DATE(NOW() - INTERVAL '.($months-1).' MONTH - INTERVAL DAY(NOW())-1 DAY)';
|
|
$dateto = 'NOW()';
|
|
/* $datefrom = '"2009-01-01"'; */
|
|
/* $dateto = '"2012-12-31"'; */
|
|
|
|
$result = $this->StatementEntry->find
|
|
('all',
|
|
array('link' => array('Account' => array('fields' => 'name')),
|
|
'fields' => array_merge(array('MONTHNAME(effective_date) AS month',
|
|
'YEAR(effective_date) AS year'),
|
|
$this->StatementEntry->chargeDisbursementFields(true)),
|
|
'conditions' => array('Account.type' => $accts,
|
|
"effective_date >= $datefrom",
|
|
"effective_date <= $dateto",
|
|
),
|
|
'group' => array('YEAR(effective_date)', 'MONTH(effective_date)', 'Account.id'),
|
|
'order' => array('YEAR(effective_date) DESC', 'MONTH(effective_date) DESC', 'Account.type',
|
|
'IF(Account.id = '.$this->StatementEntry->Account->rentAccountID().', "---", Account.name)'),
|
|
));
|
|
|
|
if ($security_deposits) {
|
|
$sdresult = $this->StatementEntry->Transaction->LedgerEntry->find
|
|
('all',
|
|
array('link' => array('Transaction' => array('StatementEntry' => array('fields' => 'effective_date'),
|
|
'fields' => array()),
|
|
'Account' => array('fields' => 'name')),
|
|
'fields' => array_merge(array('MONTHNAME(effective_date) AS month',
|
|
'YEAR(effective_date) AS year'),
|
|
$this->StatementEntry->Transaction->LedgerEntry->debitCreditFields(true)),
|
|
'conditions' => array('LedgerEntry.account_id' => $this->StatementEntry->Account->securityDepositAccountID(),
|
|
"effective_date >= $datefrom",
|
|
"effective_date <= $dateto",
|
|
'StatementEntry.id = (SELECT MIN(id) FROM pmgr_statement_entries WHERE transaction_id = `Transaction`.id)'
|
|
),
|
|
'group' => array('YEAR(effective_date)', 'MONTH(effective_date)', 'Account.id'),
|
|
'order' => array('YEAR(effective_date) DESC', 'MONTH(effective_date) DESC', 'Account.type', 'Account.name'),
|
|
));
|
|
} else {
|
|
$sdresult = array();
|
|
}
|
|
|
|
$overview = array('months' => array(), 'amount' => 0);
|
|
foreach (array_merge($result, $sdresult) AS $row) {
|
|
$mname = $row[0]['month'] .', '. $row[0]['year'];
|
|
if (empty($overview['months'][$mname]))
|
|
$overview['months'][$mname] = array('name' => $mname,
|
|
'subs' => array(),
|
|
'amount' => 0);
|
|
$month = &$overview['months'][$mname];
|
|
$month['subs'][] = array('name' => $row['Account']['name'],
|
|
'amount' => $row[0]['balance']);
|
|
|
|
$month['amount'] += $row[0]['balance'];
|
|
$overview['amount'] += $row[0]['balance'];
|
|
}
|
|
|
|
// Enable the Reports menu section
|
|
$this->sideMenuAreaActivate('REPORT');
|
|
|
|
// Prepare to render.
|
|
$this->set('months', $months);
|
|
$this->set(compact('overview'));
|
|
$this->render('chargesbymonth');
|
|
}
|
|
|
|
function incomebymonth($months = 12, $invoice = false) {
|
|
$this->set('title', 'Monthly Gross Income');
|
|
$this->set('reptype', 'Gross Income');
|
|
$this->incexpbymonth(array('INCOME'), $invoice, $months);
|
|
}
|
|
|
|
function expensebymonth($months = 12) {
|
|
$this->set('title', 'Gross Monthly Expenses');
|
|
$this->set('reptype', 'Gross Expenses');
|
|
$this->incexpbymonth(array('EXPENSE'), false, $months);
|
|
}
|
|
|
|
function netbymonth($months = 12) {
|
|
$this->set('title', 'Net Monthly Income');
|
|
$this->set('reptype', 'Net Income');
|
|
$this->incexpbymonth(array('INCOME', 'EXPENSE'), true, $months);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* action: view
|
|
* - Displays information about a specific entry
|
|
*/
|
|
|
|
function view($id = null) {
|
|
$entry = $this->StatementEntry->find
|
|
('first',
|
|
array('contain' => array
|
|
('Transaction' => array('fields' => array('id', 'type', 'stamp')),
|
|
'Account' => array('id', 'name', 'type', 'level'),
|
|
'Customer' => array('fields' => array('id', 'name')),
|
|
'Lease' => array('fields' => array('id', 'number')),
|
|
),
|
|
|
|
'conditions' => array(array('StatementEntry.id' => $id),
|
|
),
|
|
));
|
|
|
|
if (empty($entry)) {
|
|
$this->Session->setFlash(__('Invalid Item.', true));
|
|
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
|
|
}
|
|
|
|
$entry['Account']['link'] =
|
|
$entry['Account']['level'] >=
|
|
$this->Permission->level('controller.accounts');
|
|
|
|
$stats = $this->StatementEntry->stats($id);
|
|
|
|
if (in_array(strtoupper($entry['StatementEntry']['type']), $this->StatementEntry->debitTypes()))
|
|
$stats = $stats['Charge'];
|
|
else
|
|
$stats = $stats['Disbursement'];
|
|
|
|
|
|
if (strtoupper($entry['StatementEntry']['type']) === 'CHARGE') {
|
|
|
|
// Set up dynamic menu items
|
|
if ($this->StatementEntry->reversable($id))
|
|
$this->addSideMenuLink('Reverse',
|
|
array('action' => 'reverse', $id), null,
|
|
'ACTION');
|
|
|
|
if ($stats['balance'] > 0)
|
|
$this->addSideMenuLink('Waive Balance',
|
|
array('action' => 'waive', $id), null,
|
|
'ACTION');
|
|
}
|
|
|
|
// Prepare to render.
|
|
$title = "Statement Entry #{$entry['StatementEntry']['id']}";
|
|
$this->set(compact('entry', 'title', 'stats'));
|
|
}
|
|
|
|
}
|