Compare commits
300 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 115c82cd21 | |||
| 331e235e77 | |||
| d0dfd8657c | |||
| 4d4bebc3d3 | |||
| ae04aafaca | |||
| fcb23d12a5 | |||
| dd7da1489d | |||
| 85cc6d7c6a | |||
| e457f87c42 | |||
| e8d12882cc | |||
| 8f2b43239d | |||
| 51d786ab8b | |||
| 6f4519fe56 | |||
| 69adfd900c | |||
| 33ba912a7e | |||
| c4942b922b | |||
| 2588fd6a91 | |||
| 3baa199fda | |||
| 68a10e783b | |||
| 34e3251240 | |||
| 01aa2d1697 | |||
| 69b3cf3ed3 | |||
| 3551ad0603 | |||
| f960349e0f | |||
| 4ad4033e4f | |||
| 00cff7bb3a | |||
| 3ffa3c81bb | |||
| 886a63773e | |||
| dc79667dca | |||
| 8039495e18 | |||
| 46417de2b8 | |||
| 14ab0975ac | |||
| 06bf383707 | |||
| e29c2ba927 | |||
| cec4fb4fdd | |||
| d5c80a65b3 | |||
| 0c0033d1f5 | |||
| d683895413 | |||
| f7b81ec615 | |||
| aa36213698 | |||
| ad3d1c92bf | |||
| fa17e319e0 | |||
| b96a8f01da | |||
| 62b27ec255 | |||
| 30292bb4f5 | |||
| 0d3403032c | |||
| 42dda55643 | |||
| b9a67fa8e1 | |||
| 6c89536073 | |||
| 43382f4ab1 | |||
| b79df98d36 | |||
| 99904f22bb | |||
| bcbe3767e3 | |||
| ba82eabcbc | |||
| e15f053c5c | |||
| 0c8035b402 | |||
| 55413590a2 | |||
| 99a787bba6 | |||
| fb29380ac5 | |||
| 37beb8b436 | |||
| 0e1b0c111e | |||
| 7483f6ed2c | |||
| a45fd6879d | |||
| 09090d1b3c | |||
| dbd914ff78 | |||
| 222758b20f | |||
| 233b08df59 | |||
| 849077bb9a | |||
| c01c67161e | |||
| 75c44d1b3a | |||
| 2aeb069297 | |||
| 21534c28d8 | |||
| 5f0c2463e3 | |||
| 7e0d8a9da2 | |||
| 4d123b63f0 | |||
| a88f5829ce | |||
| 5c29200428 | |||
| 33fa8e732c | |||
| 6c62d66068 | |||
| 4e2b073110 | |||
| abd166cb98 | |||
| cb627b8ec7 | |||
| c455b383f5 | |||
| ba36729485 | |||
| 011c05d098 | |||
| aa2e647199 | |||
| 472d2d7166 | |||
| 9ea397ff8f | |||
| 9c65d408fa | |||
| ae85ed7907 | |||
| 019b97fe53 | |||
| ae442db01f | |||
| 65e3c99ae9 | |||
| d4ceca3aeb | |||
| 4ea8f1ae42 | |||
| c1b402bd72 | |||
| ca0ddcbc29 | |||
| 5f8ae73049 | |||
| 66e2647c05 | |||
| 81e415e5bd | |||
| 85756975ae | |||
| 58d3cbf66b | |||
| 850d15eb50 | |||
| 280c5dae55 | |||
| 50e7f58d35 | |||
| 47104919f2 | |||
| 4dac7a7f0c | |||
| d51c29bc91 | |||
| 8237ca1f9d | |||
| 87f0d3217a | |||
| fbd43fbbb2 | |||
| 826874b2a5 | |||
| eab49091ae | |||
| e150237fd8 | |||
| 207808d6d5 | |||
| dc787bd9e7 | |||
| 61af7693cd | |||
| 24245e24f1 | |||
| ae276d310a | |||
| 0f9bafab31 | |||
| 41aa3f81a8 | |||
| bb78a85d1b | |||
| 90604bf672 | |||
| bb5c44f19f | |||
| 6c656b07c3 | |||
| 5f82404a4f | |||
| 9bb957aff4 | |||
| b335378edb | |||
| 3a1fb00527 | |||
| 6adf7e2358 | |||
| 99504c6de5 | |||
| e060c16252 | |||
| ed673de22f | |||
| dfe61cbc2a | |||
| b5d7ecc269 | |||
| 5fa166e881 | |||
| ea1ad4764f | |||
| dfe20e7ef9 | |||
| 3550bf775c | |||
| 5558079dcc | |||
| 18e1f4986a | |||
| 4b6da6cffa | |||
| 9fea9c79f3 | |||
| 10f71a1fe3 | |||
| 73eacdbcf2 | |||
| 009ea6b44d | |||
| 7ea002850a | |||
| eee3d45c5d | |||
| de93c7545b | |||
| e00e10bbb5 | |||
| 3953e1dbf3 | |||
| 788cbde710 | |||
| 4a064df594 | |||
| c048d44972 | |||
| f6d6659d2a | |||
| 19cee7290f | |||
| 3c067f6586 | |||
| f7bcfef665 | |||
| d06f34de9a | |||
| d5388e7767 | |||
| 5a88f29600 | |||
| 8dd64f60bc | |||
| de2319e93d | |||
| 1fbc452581 | |||
| 65acd0e181 | |||
| f5bb9bac83 | |||
| 45ae013e99 | |||
| 16126b7b6e | |||
| fdd3fe2641 | |||
| c340c25eee | |||
| b8de98917a | |||
| d6c7fbb735 | |||
| 5dd3efa12f | |||
| cb02b9e1e0 | |||
| 212c982851 | |||
| 0aa94b8fe5 | |||
| 76e9b7590f | |||
| af405a44d5 | |||
| 1f9772e926 | |||
| b1a0437117 | |||
| 8db9b90157 | |||
| 33b7d3d67d | |||
| 57c73eafdb | |||
| 30b3185473 | |||
| e9dd1366bc | |||
| 7ee461d993 | |||
| 365b2295af | |||
| dfb0d90246 | |||
| af32d88605 | |||
| c9c06a9fb4 | |||
| c3139220ee | |||
| 68273302cb | |||
| abd45244e1 | |||
| e3b838d62a | |||
| 5fd7483ac1 | |||
| 274ed13644 | |||
| 4537174873 | |||
| 3c5a31aaff | |||
| fd155aaaeb | |||
| 284765e06a | |||
| 352e3487cc | |||
| b02695631e | |||
| b6317d3ee9 | |||
| 2cbd5da8ee | |||
| 960ac108b8 | |||
| 0133c85c01 | |||
| 92d8387e8c | |||
| 67d640b1a0 | |||
| ddf4d5cb29 | |||
| 2143960ce9 | |||
| 06a4a25be8 | |||
| 25677ffa5e | |||
| f06d831329 | |||
| eefc0d7bce | |||
| 046ffba340 | |||
| cf0da7d3c2 | |||
| 95181f4718 | |||
| 447c6a8376 | |||
| 54291b4467 | |||
| 94c78ccb2a | |||
| 323c88a669 | |||
| 3fb680e20f | |||
| cbdd198be5 | |||
| 8cdf061fca | |||
| 5e321888d5 | |||
| d45481b770 | |||
| ff5c7260f8 | |||
| e2f623b7ee | |||
| b47bb9d46e | |||
| 9cf245d030 | |||
| 8bce19d8c4 | |||
| ab766c2dc5 | |||
| 4a53ebd70b | |||
| ce735cad3f | |||
| ef3b5f3022 | |||
| 73ea4fa86c | |||
| 806f732de6 | |||
| b78834f7d6 | |||
| a0274b4d89 | |||
| 970b2ad202 | |||
| e0d9edc4a8 | |||
| ffd1b64580 | |||
| 3dcd83229b | |||
| ebc4a42ae9 | |||
| f08884f326 | |||
| e740cc859b | |||
| f12a95f3b9 | |||
| 677458e942 | |||
| ce24abc812 | |||
| 1ead830ad6 | |||
| 0848ab5055 | |||
| 52c72b08b2 | |||
| fc71c058fb | |||
| dd5402d2f1 | |||
| f473a91870 | |||
| cdd274adf7 | |||
| 8ea0822ed1 | |||
| 492e70b2f6 | |||
| abff728a37 | |||
| b8c4257f95 | |||
| 5f715cc076 | |||
| f40dc205f9 | |||
| feaabd29d9 | |||
| f48e7d8907 | |||
| 991b5a3317 | |||
| 68585a0b30 | |||
| a9a570d666 | |||
| 15c4b96a2a | |||
| ce252f2e70 | |||
| 4b857ff11f | |||
| 6661d26c76 | |||
| b409bf09d1 | |||
| b488054106 | |||
| 59398cb3f0 | |||
| 1145d293b2 | |||
| 2feb7f60a4 | |||
| bd6cc37d4a | |||
| 8dae1ccf84 | |||
| 50449205b4 | |||
| 77c038e880 | |||
| 44bdb384f5 | |||
| 2b135b0e66 | |||
| 23ec1b6b20 | |||
| 8f4f3c054e | |||
| 1249854514 | |||
| 7805e4d229 | |||
| 54c7287ee4 | |||
| bcec2a9891 | |||
| e772ff8755 | |||
| 3288969b0d | |||
| d268b6fe7e | |||
| e9b31d964c | |||
| 47d362750e | |||
| 47af64aefe | |||
| 1ca704157b | |||
| 8a3be97d41 | |||
| 05a4ee38f0 | |||
| 0e68fea04b | |||
| a9f47d73ba | |||
| 5b5e478bd0 |
@@ -0,0 +1,461 @@
|
||||
<?php
|
||||
/* SVN FILE: $Id: app_controller.php 7945 2008-12-19 02:16:01Z gwoo $ */
|
||||
/**
|
||||
* Short description for file.
|
||||
*
|
||||
* This file is application-wide controller file. You can put all
|
||||
* application-wide controller-related methods here.
|
||||
*
|
||||
* PHP versions 4 and 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
|
||||
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @filesource
|
||||
* @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
|
||||
* @package cake
|
||||
* @subpackage cake.app
|
||||
* @since CakePHP(tm) v 0.2.9
|
||||
* @version $Revision: 7945 $
|
||||
* @modifiedby $LastChangedBy: gwoo $
|
||||
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
/**
|
||||
* Short description for class.
|
||||
*
|
||||
* Add your application-wide methods in the class below, your controllers
|
||||
* will inherit them.
|
||||
*
|
||||
* @package cake
|
||||
* @subpackage cake.app
|
||||
*/
|
||||
class AppController extends Controller {
|
||||
var $helpers = array('Html', 'Form', 'Javascript', 'Format', 'Time', 'Grid');
|
||||
var $components = array('DebugKit.Toolbar');
|
||||
|
||||
function sideMenuLinks() {
|
||||
return array(
|
||||
array('name' => 'Common', 'header' => true),
|
||||
array('name' => 'Site Map', 'url' => array('controller' => 'maps', 'action' => 'view', 1)),
|
||||
array('name' => 'Units', 'url' => array('controller' => 'units', 'action' => 'index')),
|
||||
array('name' => 'Leases', 'url' => array('controller' => 'leases', 'action' => 'index')),
|
||||
array('name' => 'Customers', 'url' => array('controller' => 'customers', 'action' => 'index')),
|
||||
array('name' => 'Accounts', 'url' => array('controller' => 'accounts', 'action' => 'index')),
|
||||
array('name' => 'Debug', 'header' => true),
|
||||
array('name' => 'Contacts', 'url' => array('controller' => 'contacts', 'action' => 'index')),
|
||||
array('name' => 'Ledgers', 'url' => array('controller' => 'ledgers', 'action' => 'index')),
|
||||
array('name' => 'New Ledgers', 'url' => array('controller' => 'accounts', 'action' => 'newledger')),
|
||||
array('name' => 'RESET DATA', 'url' => array('controller' => 'accounts', 'action' => 'reset_data')),
|
||||
);
|
||||
}
|
||||
|
||||
function beforeRender() {
|
||||
$this->set('sidemenu', $this->sideMenuLinks());
|
||||
}
|
||||
|
||||
function reset_data() {
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
Configure::write('debug', '0');
|
||||
$script = $_SERVER['DOCUMENT_ROOT'] . '/pmgr/build.cmd';
|
||||
echo "<P>" . date('r') . "\n";
|
||||
//echo "<P>Script: $script" . "\n";
|
||||
$db = & $this->Account->getDataSource();
|
||||
$script .= ' "' . $db->config['database'] . '"';
|
||||
$script .= ' "' . $db->config['login'] . '"';
|
||||
$script .= ' "' . $db->config['password'] . '"';
|
||||
$handle = popen($script . ' 2>&1', 'r');
|
||||
//echo "<P>Handle: $handle; " . gettype($handle) . "\n";
|
||||
echo "<P><PRE>\n";
|
||||
while (($read = fread($handle, 2096))) {
|
||||
echo $read;
|
||||
}
|
||||
echo "</PRE>\n";
|
||||
pclose($handle);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* helper: jqGridView
|
||||
* - called by function to create an index listing
|
||||
*/
|
||||
|
||||
function jqGridView($title, $action = null, $element = null) {
|
||||
$this->set('title', $title);
|
||||
// The resulting page will contain a jqGrid, which will
|
||||
// use ajax to obtain the actual data for this action
|
||||
$this->set('action', $action ? $action : $this->params['action']);
|
||||
$this->render('/elements/' . ($element ? $element : $this->params['controller']));
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: jqGridData
|
||||
* - Fetches the actual data requested by jqGrid as XML
|
||||
*/
|
||||
|
||||
function jqGridData() {
|
||||
// Grab a copy of the parameters that control this request
|
||||
$params = array();
|
||||
if (isset($this->params['url']) && is_array($this->params['url']))
|
||||
$params = $this->params['url'];
|
||||
|
||||
// Do any preliminary setup necessary
|
||||
$this->jqGridDataSetup($params);
|
||||
|
||||
// Get the top level model for this grid
|
||||
$model = $this->jqGridDataModel($params);
|
||||
|
||||
// Establish the basic query and conditions
|
||||
$query = array_intersect_key($this->jqGridDataCountTables($params, $model),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
$query['conditions'] = $this->jqGridDataCountConditions($params, $model);
|
||||
$query['group'] = $this->jqGridDataCountGroup($params, $model);
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
$params['count_query'] = $query;
|
||||
|
||||
// Get the number of records prior to pagination
|
||||
$count = $this->jqGridDataRecordCount($params, $model, $query);
|
||||
|
||||
// Start the query over, this time getting the actual set
|
||||
// of tables needed, not just those needed for counting.
|
||||
$query = array_intersect_key($this->jqGridDataTables($params, $model),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
$query['conditions'] = $this->jqGridDataConditions($params, $model);
|
||||
|
||||
// Verify a few parameters and determine our starting row
|
||||
$limit = $params['rows'];
|
||||
$total = ($count < 0) ? 0 : ceil($count/$limit);
|
||||
$page = ($params['page'] <= 1) ? 1 : (($params['page'] > $total) ? $total : $params['page']);
|
||||
$start = $limit*$page - $limit;
|
||||
|
||||
// Grab the actual records taking pagination into account
|
||||
$query['group'] = $this->jqGridDataGroup($params, $model);
|
||||
$query['order'] = $this->jqGridDataOrder($params, $model,
|
||||
isset($params['sidx']) ? $params['sidx'] : null,
|
||||
isset($params['sord']) ? $params['sord'] : null);
|
||||
$query['limit'] = $this->jqGridDataLimit($params, $model, $start, $limit);
|
||||
$query['fields'] = $this->jqGridDataFields($params, $model);
|
||||
$results = $this->jqGridDataRecords($params, $model, $query);
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
$params['query'] = $query;
|
||||
|
||||
// Post process the records
|
||||
$this->jqGridRecordsPostProcess($params, $model, $results);
|
||||
|
||||
// Add in any needed hyperlinks
|
||||
$this->jqGridRecordLinks($params, $model, $results, array());
|
||||
|
||||
// Finally, dump out the data
|
||||
$this->jqGridDataOutputHeader($params, $model);
|
||||
echo "<?xml version='1.0' encoding='utf-8'?>\n";
|
||||
echo "<rows>\n";
|
||||
$this->jqGridDataOutputSummary($params, $model, $page, $total, $count);
|
||||
$this->jqGridDataOutputRecords($params, $model, $results);
|
||||
echo "</rows>\n";
|
||||
|
||||
// Call out to finalize everything
|
||||
$this->jqGridDataFinalize($params);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtual: jqGridData* functions
|
||||
* - These set up the context for the jqGrid data, and will
|
||||
* need to be overridden in the derived class for anything
|
||||
* other than the most basic of grids.
|
||||
*/
|
||||
|
||||
function jqGridDataSetup(&$params) {
|
||||
// Assume we're debugging.
|
||||
// The actual jqGrid request will set this to false
|
||||
$debug = true;
|
||||
|
||||
if (isset($this->passedArgs['debug']))
|
||||
$debug = $this->passedArgs['debug'];
|
||||
|
||||
$params['debug'] = $debug;
|
||||
|
||||
if (!$debug) {
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
Configure::write('debug', '0');
|
||||
}
|
||||
|
||||
// Establish some defaults (except for serialized items)
|
||||
$params = array_merge(array('page' => 1,
|
||||
'rows' => 20),
|
||||
$params);
|
||||
|
||||
// Unserialize our complex structure of fields
|
||||
// This SHOULD always be set, except when debugging
|
||||
if (isset($params['fields']))
|
||||
$params['fields'] = unserialize($params['fields']);
|
||||
else
|
||||
$params['fields'] = array($this->{$this->modelClass}->alias
|
||||
.'.'.
|
||||
$this->{$this->modelClass}->primarKey);
|
||||
|
||||
// Unserialize the list of ids, if present.
|
||||
if (isset($params['custom']))
|
||||
$params['custom'] = unserialize($params['custom']);
|
||||
else
|
||||
$params['custom'] = null;
|
||||
|
||||
// Unserialize the list of ids, if present.
|
||||
if (isset($params['idlist']))
|
||||
$params['idlist'] = unserialize($params['idlist']);
|
||||
|
||||
}
|
||||
|
||||
function jqGridDataModel(&$params) {
|
||||
return $this->{$this->modelClass};
|
||||
}
|
||||
|
||||
function jqGridDataCountTables(&$params, &$model) {
|
||||
// If not overridden, we use the same tables to
|
||||
// perform our count as we do to for the actual query
|
||||
return $this->jqGridDataTables($params, $model);
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
return array('contain' => false);
|
||||
}
|
||||
|
||||
function jqGridDataCountConditions(&$params, &$model) {
|
||||
return $this->jqGridDataConditions($params, $model);
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$searches = array();
|
||||
|
||||
if (isset($params['_search']) && $params['_search'] === 'true') {
|
||||
if (isset($params['searchOper'])) {
|
||||
$searches[] = array('op' => $params['searchOper'],
|
||||
'field' => $params['searchField'],
|
||||
'value' => $params['searchString']);
|
||||
}
|
||||
else {
|
||||
// DOH! Crappy mechanism puts toolbar search terms
|
||||
// directly into params as name/value pairs. No
|
||||
// way to know which elements of params are search
|
||||
// terms, so skipping this at the moment.
|
||||
}
|
||||
}
|
||||
elseif (isset($params['filt']) && $params['filt']) {
|
||||
$searches[] = array('op' => 'bw',
|
||||
'field' => $params['filtField'],
|
||||
'value' => $params['filtValue']);
|
||||
}
|
||||
|
||||
$ops = array('eq' => array('op' => null, 'pre' => '', 'post' => ''),
|
||||
'ne' => array('op' => '<>', 'pre' => '', 'post' => ''),
|
||||
'lt' => array('op' => '<', 'pre' => '', 'post' => ''),
|
||||
'le' => array('op' => '<=', 'pre' => '', 'post' => ''),
|
||||
'gt' => array('op' => '>', 'pre' => '', 'post' => ''),
|
||||
'ge' => array('op' => '>=', 'pre' => '', 'post' => ''),
|
||||
'bw' => array('op' => 'LIKE', 'pre' => '', 'post' => '%'),
|
||||
'ew' => array('op' => 'LIKE', 'pre' => '%', 'post' => ''),
|
||||
'cn' => array('op' => 'LIKE', 'pre' => '%', 'post' => '%'),
|
||||
);
|
||||
|
||||
$conditions = array();
|
||||
foreach ($searches AS $search) {
|
||||
$op = $ops[$search['op']];
|
||||
$field = $search['field'] . ($op['op'] ? ' '.$op['op'] : '');
|
||||
$value = $op['pre'] . $search['value']. $op['post'];
|
||||
$conditions[] = array($field => $value);
|
||||
}
|
||||
|
||||
if (isset($params['action']) && $params['action'] === 'idlist') {
|
||||
if (count($params['idlist']))
|
||||
$conditions[] = array($model->alias.'.'.$model->primaryKey => $params['idlist']);
|
||||
else
|
||||
$conditions[] = '0=1';
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function jqGridDataCountGroup(&$params, &$model) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function jqGridDataGroup(&$params, &$model) {
|
||||
return $model->alias.'.'.$model->primaryKey;
|
||||
}
|
||||
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
return $index ? array($index .' '. $direction) : null;
|
||||
}
|
||||
|
||||
function jqGridDataLimit(&$params, &$model, $start, $limit) {
|
||||
return $start . ', ' . $limit;
|
||||
}
|
||||
|
||||
function jqGridDataRecordCount(&$params, &$model, $query) {
|
||||
return $model->find('count', $query);
|
||||
}
|
||||
|
||||
function jqGridDataRecords(&$params, &$model, $query) {
|
||||
return $model->find('all', $query);
|
||||
}
|
||||
|
||||
function jqGridRecordsPostProcess(&$params, &$model, &$records) {
|
||||
$model_alias = $model->alias;
|
||||
$id = $model->primaryKey;
|
||||
|
||||
$subtotals = array();
|
||||
foreach ($params['fields'] AS $field) {
|
||||
if (preg_match('/subtotal-(.*)$/', $field, $matches))
|
||||
$subtotals[] = array('field' => $matches[1],
|
||||
'name' => $field,
|
||||
'amount' => 0);
|
||||
}
|
||||
|
||||
foreach ($records AS &$record) {
|
||||
$record['jqGrid_id'] = $record[$model_alias][$id];
|
||||
// Add the calculated fields (if any), to the model fields
|
||||
if (isset($record[0])) {
|
||||
$record[$model_alias] += $record[0];
|
||||
unset($record[0]);
|
||||
}
|
||||
|
||||
foreach ($subtotals AS &$subtotal) {
|
||||
$field = $subtotal['field'];
|
||||
if (preg_match("/\./", $field)) {
|
||||
list($tbl, $col) = explode(".", $field);
|
||||
$record['subtotal-'.$tbl][$col] =
|
||||
($subtotal['amount'] += $record[$tbl][$col]);
|
||||
}
|
||||
else {
|
||||
$record[$model->alias]['subtotal-'.$field] =
|
||||
($subtotal['amount'] += $record[$model->alias][$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
//$params['records'] = $records;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
// Don't create any links if ordered not to.
|
||||
if (isset($params['nolinks']))
|
||||
return;
|
||||
|
||||
foreach ($links AS $table => $fields) {
|
||||
$special = array('controller', 'id');
|
||||
$controller = Inflector::pluralize(Inflector::underscore($table));
|
||||
$id = 'id';
|
||||
extract(array_intersect_key($fields, array_flip($special)));
|
||||
foreach ($records AS &$record) {
|
||||
if (!isset($record[$table]))
|
||||
continue;
|
||||
|
||||
foreach (array_diff_key($fields, array_flip($special)) AS $field) {
|
||||
if (!isset($record[$table][$id]) || !isset($record[$table][$field]))
|
||||
continue;
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
//$params['linkrecord'][] = compact('table', 'field', 'id', 'controller', 'record');
|
||||
$record[$table][$field] =
|
||||
'<A HREF="' .
|
||||
Router::url(array('controller' => $controller,
|
||||
'action' => 'view',
|
||||
$record[$table][$id])) .
|
||||
'">' .
|
||||
$record[$table][$field] .
|
||||
'</A>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function jqGridDataOutputHeader(&$params, &$model) {
|
||||
if ($params['debug']) {
|
||||
ob_start();
|
||||
}
|
||||
else {
|
||||
header("Content-type: text/xml;charset=utf-8");
|
||||
}
|
||||
}
|
||||
|
||||
function jqGridDataOutputSummary(&$params, &$model, $page, $total, $records) {
|
||||
echo " <params><![CDATA[\n" . print_r($params, true) . "\n]]></params>\n";
|
||||
echo " <page>$page</page>\n";
|
||||
echo " <total>$total</total>\n";
|
||||
echo " <records>$records</records>\n";
|
||||
|
||||
if (isset($params['userdata'])) {
|
||||
foreach ($params['userdata'] AS $field => $value)
|
||||
echo ' <userdata name="'.$field.'">' . "{$value}</userdata>\n";
|
||||
}
|
||||
}
|
||||
|
||||
function jqGridDataOutputRecords(&$params, &$model, &$records) {
|
||||
$id_field = 'jqGrid_id';
|
||||
foreach ($records AS $record) {
|
||||
$this->jqGridDataOutputRecord($params, $model, $record,
|
||||
$record[$id_field], $params['fields']);
|
||||
}
|
||||
}
|
||||
|
||||
function jqGridDataOutputRecord(&$params, &$model, &$record, $id, $fields) {
|
||||
echo " <row id='$id'>\n";
|
||||
foreach ($fields AS $field) {
|
||||
$this->jqGridDataOutputRecordField($params, $model, $record, $field);
|
||||
}
|
||||
echo " </row>\n";
|
||||
}
|
||||
|
||||
function jqGridDataOutputRecordField(&$params, &$model, &$record, $field) {
|
||||
if (preg_match("/\./", $field)) {
|
||||
list($tbl, $col) = explode(".", $field);
|
||||
$data = $record[$tbl][$col];
|
||||
}
|
||||
else {
|
||||
$data = $record[$model->alias][$field];
|
||||
}
|
||||
$this->jqGridDataOutputRecordCell($params, $model, $record, $field, $data);
|
||||
}
|
||||
|
||||
function jqGridDataOutputRecordCell(&$params, &$model, &$record, $field, $data) {
|
||||
// be sure to put text data in CDATA
|
||||
if (preg_match("/^\d*$/", $data))
|
||||
echo " <cell>$data</cell>\n";
|
||||
else
|
||||
echo " <cell><![CDATA[$data]]></cell>\n";
|
||||
}
|
||||
|
||||
function jqGridDataFinalize(&$params) {
|
||||
if ($params['debug']) {
|
||||
$xml = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$xml = preg_replace("/&/", "&", $xml);
|
||||
$xml = preg_replace("/</", "<", $xml);
|
||||
$xml = preg_replace("/>/", ">", $xml);
|
||||
|
||||
echo ("\n<PRE>\n$xml\n</PRE>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
/* SVN FILE: $Id: app_model.php 7945 2008-12-19 02:16:01Z gwoo $ */
|
||||
|
||||
/**
|
||||
* Application model for Cake.
|
||||
*
|
||||
* This file is application-wide model file. You can put all
|
||||
* application-wide model-related methods here.
|
||||
*
|
||||
* PHP versions 4 and 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
|
||||
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @filesource
|
||||
* @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
|
||||
* @package cake
|
||||
* @subpackage cake.app
|
||||
* @since CakePHP(tm) v 0.2.9
|
||||
* @version $Revision: 7945 $
|
||||
* @modifiedby $LastChangedBy: gwoo $
|
||||
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Application model for Cake.
|
||||
*
|
||||
* Add your application-wide methods in the class below, your models
|
||||
* will inherit them.
|
||||
*
|
||||
* @package cake
|
||||
* @subpackage cake.app
|
||||
*/
|
||||
class AppModel extends Model {
|
||||
|
||||
var $actsAs = array('Containable', 'Linkable');
|
||||
var $useNullForEmpty = true;
|
||||
var $formatDateFields = true;
|
||||
|
||||
/**
|
||||
* Get Enum Values
|
||||
* Snippet v0.1.3
|
||||
* http://cakeforge.org/snippet/detail.php?type=snippet&id=112
|
||||
*
|
||||
* Gets the enum values for MySQL 4 and 5 to use in selectTag()
|
||||
*/
|
||||
function getEnumValues($columnName=null, $tableName=null)
|
||||
{
|
||||
if ($columnName==null) { return array(); } //no field specified
|
||||
|
||||
if (!isset($tableName)) {
|
||||
//Get the name of the table
|
||||
$db =& ConnectionManager::getDataSource($this->useDbConfig);
|
||||
$tableName = $db->fullTableName($this, false);
|
||||
}
|
||||
|
||||
//Get the values for the specified column (database and version specific, needs testing)
|
||||
$result = $this->query("SHOW COLUMNS FROM {$tableName} LIKE '{$columnName}'");
|
||||
|
||||
//figure out where in the result our Types are (this varies between mysql versions)
|
||||
$types = null;
|
||||
if ( isset( $result[0]['COLUMNS']['Type'] ) ) { //MySQL 5
|
||||
$types = $result[0]['COLUMNS']['Type']; $default = $result[0]['COLUMNS']['Default'];
|
||||
}
|
||||
elseif ( isset( $result[0][0]['Type'] ) ) { //MySQL 4
|
||||
$types = $result[0][0]['Type']; $default = $result[0][0]['Default'];
|
||||
}
|
||||
else { //types return not accounted for
|
||||
return array();
|
||||
}
|
||||
|
||||
//Get the values
|
||||
return array_flip(array_merge(array(''), // MySQL sets 0 to be the empty string
|
||||
explode("','", strtoupper(preg_replace("/(enum)\('(.+?)'\)/","\\2", $types)))
|
||||
));
|
||||
} //end getEnumValues
|
||||
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: nameToID
|
||||
* - Returns the ID of the named item
|
||||
*/
|
||||
function nameToID($name) {
|
||||
$this->cacheQueries = true;
|
||||
$item = $this->find('first', array
|
||||
('recursive' => -1,
|
||||
'conditions' => compact('name'),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
if ($item) {
|
||||
$item = current($item);
|
||||
return $item['id'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: statMerge
|
||||
* - Merges summary data from $b into $a
|
||||
*/
|
||||
|
||||
function statsMerge (&$a, $b) {
|
||||
if (!isset($b))
|
||||
return;
|
||||
|
||||
if (!isset($a)) {
|
||||
$a = $b;
|
||||
}
|
||||
elseif (!is_array($a) && !is_array($b)) {
|
||||
$a += $b;
|
||||
}
|
||||
elseif (is_array($a) && is_array($b)) {
|
||||
foreach (array_intersect_key($a, $b) AS $k => $v)
|
||||
{
|
||||
if (preg_match("/^sp\./", $k))
|
||||
$a[$k] .= '; ' . $b[$k];
|
||||
else
|
||||
$this->statsMerge($a[$k], $b[$k]);
|
||||
}
|
||||
$a = array_merge($a, array_diff_key($b, $a));
|
||||
}
|
||||
else {
|
||||
die ("Can't yet merge array and non-array stats");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function recursive_array_replace($find, $replace, &$data) {
|
||||
if (!isset($data))
|
||||
return;
|
||||
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $key => &$value) {
|
||||
$this->recursive_array_replace($find, $replace, $value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($replace))
|
||||
$data = preg_replace($find, $replace, $data);
|
||||
elseif (preg_match($find, $data))
|
||||
$data = null;
|
||||
}
|
||||
|
||||
function beforeSave() {
|
||||
/* pr(array('class' => $this->name, */
|
||||
/* 'alias' => $this->alias, */
|
||||
/* 'function' => 'AppModel::beforeSave')); */
|
||||
|
||||
// Replace all empty strings with NULL.
|
||||
// If a particular model doesn't like this, they'll have to
|
||||
// override the behavior, or set useNullForEmpty to false.
|
||||
if ($this->useNullForEmpty)
|
||||
$this->recursive_array_replace("/^\s*$/", null, $this->data);
|
||||
|
||||
if ($this->formatDateFields) {
|
||||
$alias = $this->alias;
|
||||
|
||||
foreach ($this->_schema AS $field => $info) {
|
||||
if ($info['type'] == 'date' || $info['type'] == 'timestamp') {
|
||||
if (isset($this->data[$alias][$field])) {
|
||||
/* pr("Fix Date for '$alias'.'$field'; current value = " . */
|
||||
/* "'{$this->data[$alias][$field]}'"); */
|
||||
if ($this->data[$alias][$field] === 'CURRENT_TIMESTAMP')
|
||||
// Seems CakePHP is broken for the default timestamp.
|
||||
// It tries to automagically set the value to CURRENT_TIMESTAMP
|
||||
// which is wholly rejected by MySQL. Just put it back to NULL
|
||||
// and let the SQL engine deal with the defaults... it's not
|
||||
// Cake's place to do this anyway :-/
|
||||
$this->data[$alias][$field] = null;
|
||||
else
|
||||
$this->data[$alias][$field] =
|
||||
$this->dateFormatBeforeSave($this->data[$alias][$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: dateFormatBeforeSave
|
||||
* - convert dates to database format
|
||||
*/
|
||||
|
||||
function dateFormatBeforeSave($dateString) {
|
||||
/* $time = ''; */
|
||||
/* if (preg_match('/(\d+(:\d+))/', $dateString, $match)) */
|
||||
/* $time = ' '.$match[1]; */
|
||||
/* $dateString = preg_replace('/(\d+(:\d+))/', '', $dateString); */
|
||||
/* return date('Y-m-d', strtotime($dateString)) . $time; */
|
||||
|
||||
if (preg_match('/:/', $dateString))
|
||||
return date('Y-m-d H:i:s', strtotime($dateString));
|
||||
else
|
||||
return date('Y-m-d', strtotime($dateString));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
%~dp0\scripts\sitelink2pmgr.pl %~dp0\db\schema.sql %~dp0db\vss.mdb %*
|
||||
echo Done!
|
||||
@@ -31,39 +31,6 @@
|
||||
* You can also use this to include or require any files in your application.
|
||||
*
|
||||
*/
|
||||
|
||||
function INTERNAL_ERROR($message) {
|
||||
echo '<DIV class="internal-error" style="color:#000; background:#c22; padding:0.5em 1.5em 0.5em 1.5em;">';
|
||||
echo '<H1 style="margin-bottom:0.2em">INTERNAL ERROR:</H1>';
|
||||
echo '<H2 style="margin-top:0; margin-left:1.5em">' . $message . '</H2>';
|
||||
echo '<H4>This error was not caused by anything that you did wrong.';
|
||||
echo '<BR>It is a problem within the application itself and should be reported to the administrator.</H4>';
|
||||
|
||||
// Print out the entire stack trace
|
||||
echo "<HR>Stack Trace:<OL>";
|
||||
$trace = debug_backtrace(false);
|
||||
for ($i = 0; $i < count($trace); ++$i) {
|
||||
$bline = $trace[$i]['line'];
|
||||
$bfile = $trace[$i]['file'];
|
||||
$bfile = str_replace(ROOT.DS, '', $bfile);
|
||||
$bfile = str_replace(CAKE_CORE_INCLUDE_PATH.DS, '', $bfile);
|
||||
|
||||
if ($i < count($trace)-1) {
|
||||
$bfunc = $trace[$i+1]['function'];
|
||||
$bclas = $trace[$i+1]['class'];
|
||||
} else {
|
||||
$bfunc = null;
|
||||
$bclas = null;
|
||||
}
|
||||
|
||||
echo("<LI>$bfile:$bline (" . ($bclas ? "$bclas::$bfunc" : "entry point") . ")</LI>\n");
|
||||
}
|
||||
echo '</OL>';
|
||||
|
||||
echo '</DIV>';
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* The settings below can be used to set additional paths to models, views and controllers.
|
||||
* This is related to Ticket #470 (https://trac.cakephp.org/ticket/470)
|
||||
@@ -38,7 +38,7 @@
|
||||
*
|
||||
* $uninflectedPlural = array('.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox');
|
||||
*/
|
||||
$uninflectedPlural = array('.*cash');
|
||||
$uninflectedPlural = array();
|
||||
/**
|
||||
* This is a key => value array of plural irregular words.
|
||||
* If key matches then the value is returned.
|
||||
@@ -12,9 +12,7 @@ class AccountsController extends AppController {
|
||||
array('name' => 'Equity', 'url' => array('controller' => 'accounts', 'action' => 'equity')),
|
||||
array('name' => 'Income', 'url' => array('controller' => 'accounts', 'action' => 'income')),
|
||||
array('name' => 'Expense', 'url' => array('controller' => 'accounts', 'action' => 'expense')),
|
||||
array('name' => 'Deposits', 'header' => true),
|
||||
array('name' => 'Prior Deposits', 'url' => array('controller' => 'transactions', 'action' => 'deposit')),
|
||||
array('name' => 'New Deposit', 'url' => array('controller' => 'tenders', 'action' => 'deposit')),
|
||||
array('name' => 'Bank Deposit', 'url' => array('controller' => 'accounts', 'action' => 'deposit')),
|
||||
);
|
||||
|
||||
|
||||
@@ -37,57 +35,77 @@ class AccountsController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function asset() { $this->gridView('Asset Accounts'); }
|
||||
function liability() { $this->gridView('Liability Accounts'); }
|
||||
function equity() { $this->gridView('Equity Accounts'); }
|
||||
function income() { $this->gridView('Income Accounts'); }
|
||||
function expense() { $this->gridView('Expense Accounts'); }
|
||||
function all() { $this->gridView('All Accounts', 'all'); }
|
||||
function asset() { $this->jqGridView('Asset Accounts'); }
|
||||
function liability() { $this->jqGridView('Liability Accounts'); }
|
||||
function equity() { $this->jqGridView('Equity Accounts'); }
|
||||
function income() { $this->jqGridView('Income Accounts'); }
|
||||
function expense() { $this->jqGridView('Expense Accounts'); }
|
||||
function all() { $this->jqGridView('All Accounts', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function gridDataSetup(&$params) {
|
||||
parent::gridDataSetup($params);
|
||||
function jqGridDataSetup(&$params) {
|
||||
parent::jqGridDataSetup($params);
|
||||
if (!isset($params['action']))
|
||||
$params['action'] = 'all';
|
||||
}
|
||||
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
// Our count should NOT include anything extra,
|
||||
// so we need the virtual function to prevent
|
||||
// the base class from just calling our
|
||||
// gridDataTables function
|
||||
return parent::gridDataTables($params, $model);
|
||||
function jqGridDataCountTables(&$params, &$model) {
|
||||
return parent::jqGridDataTables($params, $model);
|
||||
}
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
return array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'CurrentLedger' => array
|
||||
(// Models
|
||||
'LedgerEntry'
|
||||
/* REVISIT <AP> 20090615:
|
||||
* I'll remove this 'conditions' section on a future checkin,
|
||||
* after I've proven out the %{MODEL_ALIAS} feature will be
|
||||
* sticking around.
|
||||
|
||||
=> array
|
||||
('conditions' =>
|
||||
array('OR' =>
|
||||
array('LedgerEntry.debit_ledger_id = CurrentLedger.id',
|
||||
'LedgerEntry.credit_ledger_id = CurrentLedger.id'),
|
||||
),
|
||||
),
|
||||
|
||||
* END REVISIT
|
||||
*/
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
return array_merge($fields,
|
||||
$this->Account->Ledger->LedgerEntry->debitCreditFields(true));
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
return array
|
||||
('Account.*',
|
||||
'SUM(IF(LedgerEntry.debit_ledger_id = CurrentLedger.id,
|
||||
LedgerEntry.amount, NULL)) AS debits',
|
||||
'SUM(IF(LedgerEntry.credit_ledger_id = CurrentLedger.id,
|
||||
LedgerEntry.amount, NULL)) AS credits',
|
||||
"SUM(IF(Account.type IN ('ASSET', 'EXPENSE'),
|
||||
IF(LedgerEntry.debit_ledger_id = CurrentLedger.id, 1, -1),
|
||||
IF(LedgerEntry.credit_ledger_id = CurrentLedger.id, 1, -1)
|
||||
) * IF(LedgerEntry.amount, LedgerEntry.amount, 0)
|
||||
) AS balance",
|
||||
'COUNT(LedgerEntry.id) AS entries');
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
|
||||
if (in_array($params['action'], array('asset', 'liability', 'equity', 'income', 'expense'))) {
|
||||
$conditions[] = array('Account.type' => strtoupper($params['action']));
|
||||
@@ -96,9 +114,9 @@ class AccountsController extends AppController {
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Account'] = array('name');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -111,11 +129,7 @@ class AccountsController extends AppController {
|
||||
*/
|
||||
|
||||
function newledger($id = null) {
|
||||
$result = $this->Account->closeCurrentLedgers($id);
|
||||
|
||||
if ($result['error']) {
|
||||
pr(compact('result'));
|
||||
die("Unable to create new ledger.");
|
||||
if (!$this->Account->closeCurrentLedger($id)) {
|
||||
$this->Session->setFlash(__('Unable to create new Ledger.', true));
|
||||
}
|
||||
if ($id)
|
||||
@@ -138,23 +152,94 @@ class AccountsController extends AppController {
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
$payment_accounts = $this->Account->collectableAccounts();
|
||||
//$payment_accounts[$this->Account->nameToID('Bank')] = 'Bank';
|
||||
$default_accounts = array_diff_key($payment_accounts,
|
||||
array($this->Account->concessionAccountID() => 1));
|
||||
$this->set(compact('payment_accounts', 'default_accounts'));
|
||||
|
||||
$this->Account->recursive = -1;
|
||||
$account = $this->Account->read(null, $id);
|
||||
$account = $account['Account'];
|
||||
|
||||
$payment_accounts = $this->Account->collectableAccounts();
|
||||
//$payment_accounts[$this->Account->nameToID('Closing')] = 'Closing';
|
||||
//$payment_accounts[$this->Account->nameToID('Equity')] = 'Equity';
|
||||
//$payment_accounts[$id] = 'Reversals';
|
||||
$default_accounts = array_diff_key($this->Account->receiptAccounts(),
|
||||
array($this->Account->concessionAccountID() => 1));
|
||||
$this->set(compact('payment_accounts', 'default_accounts'));
|
||||
|
||||
$title = ($account['name'] . ': Collected Report');
|
||||
$this->set(compact('account', 'title'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: deposit
|
||||
* - Prepares the books for a bank deposit
|
||||
*/
|
||||
function deposit() {
|
||||
if ($this->data) {
|
||||
// Action the close based on provided data
|
||||
//pr($this->data);
|
||||
|
||||
// Get data about each closed ledger.
|
||||
$deposit = array('total' => 0, 'ledgers' => array());
|
||||
foreach ($this->data['Tillable']['Ledger'] AS $ledger_id => $ledger) {
|
||||
if (!$ledger['checked'])
|
||||
continue;
|
||||
|
||||
$ledger_entries =
|
||||
$this->Account->Ledger->find
|
||||
('all',
|
||||
array('link' => array
|
||||
('Account' =>
|
||||
array('fields' => array('name')),
|
||||
|
||||
'LedgerEntry' =>
|
||||
array('fields' => array('id', 'amount'),
|
||||
|
||||
'MonetarySource' =>
|
||||
array('fields' => array('name')),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('name')),
|
||||
|
||||
//'Transaction' =>
|
||||
//array('fields' => array('stamp')),
|
||||
),
|
||||
),
|
||||
'fields' => false,
|
||||
'conditions' => array(array('Ledger.id' => $ledger_id),
|
||||
array('LedgerEntry.id IS NOT NULL'),
|
||||
),
|
||||
));
|
||||
|
||||
$deposit['total'] += $ledger['amount'];
|
||||
$deposit['ledgers'][] = array('id' => $ledger_id,
|
||||
'name' => $ledger['account_name'],
|
||||
'total' => $ledger['amount'],
|
||||
'entries' => $ledger_entries);
|
||||
}
|
||||
|
||||
// Perform the accounting work necessary to close the
|
||||
// monetary ledgers and deposit into the bank account.
|
||||
$this->Account->closeAndDeposit($deposit['ledgers'], $this->data['Deposit']['Account']['id']);
|
||||
|
||||
$title = 'Account: Deposit Slip';
|
||||
$this->set(compact('title', 'deposit'));
|
||||
$this->render('deposit_slip');
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare a close page...
|
||||
$tillable_account = $this->Account->relatedAccounts('tillable');
|
||||
$depositable_account = $this->Account->relatedAccounts('depositable');
|
||||
|
||||
foreach ($tillable_account AS &$acct) {
|
||||
$acct['Account']['stats'] = $this->Account->stats($acct['Account']['id']);
|
||||
}
|
||||
|
||||
$title = 'Account: Prepare Deposit';
|
||||
$this->set(compact('title', 'tillable_account', 'depositable_account'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -174,20 +259,24 @@ class AccountsController extends AppController {
|
||||
array('contain' =>
|
||||
array(// Models
|
||||
'CurrentLedger' =>
|
||||
array('fields' => array('id', 'sequence', 'name')),
|
||||
array('fields' => array('id', 'sequence')),
|
||||
|
||||
'Ledger' =>
|
||||
array('CloseTransaction' => array
|
||||
('order' => array('CloseTransaction.stamp' => 'DESC'))),
|
||||
array('Close' => array
|
||||
('order' => array('Close.stamp' => 'DESC'))),
|
||||
),
|
||||
'conditions' => array(array('Account.id' => $id)),
|
||||
)
|
||||
);
|
||||
|
||||
// Get all ledger entries of the CURRENT ledger
|
||||
$entries = $this->Account->ledgerEntries($id);
|
||||
//pr(compact('entries'));
|
||||
$account['CurrentLedger']['LedgerEntry'] = $entries;
|
||||
$entries = $this->Account->findLedgerEntries($id);
|
||||
$account['CurrentLedger']['LedgerEntry'] = $entries['Entries'];
|
||||
|
||||
// Summarize each ledger
|
||||
foreach($account['Ledger'] AS &$ledger)
|
||||
$ledger = array_merge($ledger,
|
||||
$this->Account->Ledger->stats($ledger['id']));
|
||||
|
||||
// Obtain stats across ALL ledgers for the summary infobox
|
||||
$stats = $this->Account->stats($id, true);
|
||||
@@ -204,9 +293,5 @@ class AccountsController extends AppController {
|
||||
$title = 'Account: ' . $account['Account']['name'];
|
||||
$this->set(compact('account', 'title', 'stats'));
|
||||
}
|
||||
|
||||
function tst($id) {
|
||||
//$entries = $this->Account->($id);
|
||||
pr($entries);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,20 +23,20 @@ class ContactsController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function all() { $this->gridView('All Contacts', 'all'); }
|
||||
function all() { $this->jqGridView('All Contacts', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
$order = parent::gridDataOrder($params, $model, $index, $direction);
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
$order = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
if ($index === 'Contact.last_name') {
|
||||
$order[] = 'Contact.first_name ' . $direction;
|
||||
}
|
||||
@@ -46,9 +46,9 @@ class ContactsController extends AppController {
|
||||
return $order;
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Contact'] = array('id');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,76 +31,145 @@ class CustomersController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->current(); }
|
||||
function current() { $this->gridView('Current Tenants', 'current'); }
|
||||
function past() { $this->gridView('Past Tenants'); }
|
||||
function all() { $this->gridView('All Customers'); }
|
||||
function current() { $this->jqGridView('Current Tenants', 'current'); }
|
||||
function past() { $this->jqGridView('Past Tenants'); }
|
||||
function all() { $this->jqGridView('All Customers'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
function jqGridDataSetup(&$params) {
|
||||
parent::jqGridDataSetup($params);
|
||||
if (!isset($params['action']))
|
||||
$params['action'] = 'all';
|
||||
}
|
||||
|
||||
function jqGridDataCountTables(&$params, &$model) {
|
||||
return array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'PrimaryContact',
|
||||
'CurrentLease' => array('fields' => array()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$link = $this->gridDataCountTables($params, $model);
|
||||
// StatementEntry is needed to determine customer balance
|
||||
$link['link']['StatementEntry'] = array('fields' => array());
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
$link = $this->jqGridDataCountTables($params, $model);
|
||||
$link['link']['LedgerEntry'] = array('fields' => array());
|
||||
$link['link']['LedgerEntry']['Ledger'] = array('fields' => array());
|
||||
$link['link']['LedgerEntry']['Ledger']['Account'] = array('fields' => array());
|
||||
// INNER JOIN would be great, as it would ensure we're only looking
|
||||
// at the ledger entries that we truly want. However, this also
|
||||
// removes from the query any units that do not yet have a ledger
|
||||
// entry in A/R. A solution would be to INNER JOIN these tables,
|
||||
// and LEFT JOIN it to the rest. Grouping of JOINs, however, is
|
||||
// implemented with the 'joins' tag, and is not available through
|
||||
// the Linkable behavior interface.
|
||||
//$link['link']['LedgerEntry']['Ledger']['Account']['type'] = 'INNER';
|
||||
$link['link']['LedgerEntry']['Ledger']['Account']['conditions']
|
||||
= array('Account.id' =>
|
||||
$this->Customer->LedgerEntry->Ledger->Account->accountReceivableAccountID());
|
||||
return $link;
|
||||
}
|
||||
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
return array_merge($fields,
|
||||
$this->Customer->StatementEntry->chargeDisbursementFields(true));
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
$db = &$model->getDataSource();
|
||||
$fields = $db->fields($model, $model->alias);
|
||||
$fields[] = ('COUNT(DISTINCT CurrentLease.id) AS lease_count');
|
||||
$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;
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'current') {
|
||||
$conditions[] = array('Customer.current_lease_count >' => 0);
|
||||
$conditions[] = 'CurrentLease.id IS NOT NULL';
|
||||
}
|
||||
elseif ($params['action'] === 'past') {
|
||||
$conditions[] = array('Customer.current_lease_count' => 0);
|
||||
$conditions[] = array('Customer.past_lease_count >' => 0);
|
||||
$conditions[] = 'CurrentLease.id IS NULL';
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
$order = array();
|
||||
$order[] = parent::gridDataOrder($params, $model, $index, $direction);
|
||||
$order[] = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
if ($index !== 'PrimaryContact.last_name')
|
||||
$order[] = parent::gridDataOrder($params, $model,
|
||||
$order[] = parent::jqGridDataOrder($params, $model,
|
||||
'PrimaryContact.last_name', $direction);
|
||||
if ($index !== 'PrimaryContact.first_name')
|
||||
$order[] = parent::gridDataOrder($params, $model,
|
||||
$order[] = parent::jqGridDataOrder($params, $model,
|
||||
'PrimaryContact.first_name', $direction);
|
||||
if ($index !== 'Customer.id')
|
||||
$order[] = parent::gridDataOrder($params, $model,
|
||||
$order[] = parent::jqGridDataOrder($params, $model,
|
||||
'Customer.id', $direction);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
function jqGridDataRecordCount(&$params, &$model, $query) {
|
||||
|
||||
// We don't have a good way to use the query to obtain
|
||||
// our count. The problem is that we're relying on the
|
||||
// group by for the query, which will destroy the count,
|
||||
// whether we omit the group by or leave it in.
|
||||
// So, build a fresh query for counting.
|
||||
|
||||
$query['conditions'] = parent::jqGridDataConditions($params, $model);
|
||||
|
||||
$count = $model->find('count',
|
||||
array_merge(array('link' => array_diff_key($query['link'],
|
||||
array('CurrentLease'=>1))),
|
||||
array_diff_key($query, array('link'=>1))));
|
||||
|
||||
if ($params['action'] === 'all')
|
||||
return $count;
|
||||
|
||||
$query['conditions'][] = 'CurrentLease.id IS NULL';
|
||||
$count_past = $model->find('count', $query);
|
||||
|
||||
// Since we can't easily count 'current' directly, we
|
||||
// can quickly derive it since 'current' customers
|
||||
// are mutually exclusive to 'past' customers.
|
||||
if ($params['action'] == 'current')
|
||||
$count = $count - $count_past;
|
||||
elseif ($params['action'] == 'past') {
|
||||
$count = $count_past;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
function jqGridDataRecords(&$params, &$model, $query) {
|
||||
$customers = parent::jqGridDataRecords($params, $model, $query);
|
||||
|
||||
// Get the balance on each customer.
|
||||
foreach ($customers AS &$customer) {
|
||||
$stats = $this->Customer->stats($customer['Customer']['id']);
|
||||
$customer['Customer']['balance'] = $stats['balance'];
|
||||
}
|
||||
|
||||
return $customers;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Customer'] = array('name');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -178,33 +247,10 @@ class CustomersController extends AppController {
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// Figure out the outstanding balances for this customer
|
||||
$outstanding_balance = $this->Customer->balance($id);
|
||||
$outstanding_deposit = $this->Customer->securityDepositBalance($id);
|
||||
$outstanding_balance = $customer['stats']['balance'];
|
||||
$outstanding_deposit = $customer['deposits']['summary']['balance'];
|
||||
|
||||
// Figure out if this customer has any non-closed leases
|
||||
$show_moveout = false;
|
||||
@@ -244,11 +290,6 @@ 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',
|
||||
@@ -362,29 +403,21 @@ class CustomersController extends AppController {
|
||||
$this->Customer->recursive = -1;
|
||||
$customer = $this->Customer->read(null, $id);
|
||||
$customer = $customer['Customer'];
|
||||
$unreconciled = $this->Customer->unreconciledCharges($id);
|
||||
//pr($unreconciled);
|
||||
$charges = $unreconciled['entries'];
|
||||
$stats = $unreconciled['summary']['Charge'];
|
||||
// Kludge until we update receipt to have the unpaid
|
||||
// charges grid generated from a dynamic query instead
|
||||
// of simply pre-providing the list of charge IDs
|
||||
foreach ($charges AS &$charge)
|
||||
$charge['id'] = $charge['StatementEntry']['id'];
|
||||
$unreconciled = $this->Customer->findUnreconciledLedgerEntries($id);
|
||||
$charges = $unreconciled['debit'];
|
||||
}
|
||||
else {
|
||||
$customer = null;
|
||||
$charges = array();
|
||||
$stats = array('balance' => 0);
|
||||
$charges = array('balance' => 0, 'entry' => array());
|
||||
}
|
||||
|
||||
$TT = new TenderType();
|
||||
$payment_types = $TT->paymentTypes();
|
||||
$default_type = $TT->defaultPaymentType();
|
||||
$this->set(compact('payment_types', 'default_type'));
|
||||
$A = new Account();
|
||||
$payment_accounts = $A->paymentAccounts();
|
||||
$default_account = $A->cashAccountID();
|
||||
$this->set(compact('payment_accounts', 'default_account'));
|
||||
|
||||
$title = ($customer['name'] . ': Receipt Entry');
|
||||
$this->set(compact('customer', 'charges', 'stats', 'title'));
|
||||
$title = ($customer['name'] . ': Payment Entry');
|
||||
$this->set(compact('customer', 'charges', 'title'));
|
||||
}
|
||||
|
||||
|
||||
@@ -396,24 +429,24 @@ class CustomersController extends AppController {
|
||||
*/
|
||||
|
||||
function refund() {
|
||||
$entries = $this->Customer->StatementEntry->find
|
||||
$entries = $this->Customer->LedgerEntry->find
|
||||
('all', array
|
||||
('contain' => false,
|
||||
'conditions' => array('StatementEntry.id' =>
|
||||
'conditions' => array('LedgerEntry.id' =>
|
||||
//array(199,200,201)
|
||||
61
|
||||
),
|
||||
));
|
||||
pr(compact('entries'));
|
||||
|
||||
$this->Customer->refund($entries);
|
||||
$this->Customer->LedgerEntry->reverse($entries);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: unreconciled
|
||||
* action: unreconciledEntries
|
||||
* - returns the list of unreconciled entries
|
||||
*/
|
||||
|
||||
@@ -431,23 +464,17 @@ class CustomersController extends AppController {
|
||||
|
||||
// Find the unreconciled entries, then manipulate the structure
|
||||
// slightly to accomodate the format necessary for XML Helper.
|
||||
$unreconciled = $this->Customer->unreconciledCharges($id);
|
||||
|
||||
foreach ($unreconciled['entries'] AS &$entry)
|
||||
$entry = array_intersect_key($entry['StatementEntry'],
|
||||
array('id'=>1));
|
||||
|
||||
$unreconciled = $this->Customer->findUnreconciledLedgerEntries($id);
|
||||
$unreconciled = array('entries' =>
|
||||
array('entry' => $unreconciled['entries'],
|
||||
'balance' => $unreconciled['summary']['balance']));
|
||||
array_intersect_key($unreconciled['debit'],
|
||||
array('entry'=>1, 'balance'=>1)));
|
||||
|
||||
// XML Helper will dump an empty tag if the array is empty
|
||||
if (empty($unreconciled['entries']['entry']))
|
||||
if (!count($unreconciled['entries']['entry']))
|
||||
unset($unreconciled['entries']['entry']);
|
||||
|
||||
/* pr(compact('unreconciled')); */
|
||||
/* echo htmlspecialchars($xml->serialize($unreconciled)); */
|
||||
/* $this->render('/fake'); */
|
||||
pr($unreconciled);
|
||||
//$reconciled = $cust->reconcileNewLedgerEntry($cust_id, 'credit', $amount);
|
||||
|
||||
$opts = array();
|
||||
//$opts['format'] = 'tags';
|
||||
@@ -29,46 +29,64 @@ class LeasesController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function active() { $this->gridView('Active Leases'); }
|
||||
function closed() { $this->gridView('Closed Leases'); }
|
||||
function all() { $this->gridView('All Leases', 'all'); }
|
||||
function active() { $this->jqGridView('Active Leases'); }
|
||||
function closed() { $this->jqGridView('Closed Leases'); }
|
||||
function all() { $this->jqGridView('All Leases', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function gridDataSetup(&$params) {
|
||||
parent::gridDataSetup($params);
|
||||
function jqGridDataSetup(&$params) {
|
||||
parent::jqGridDataSetup($params);
|
||||
if (!isset($params['action']))
|
||||
$params['action'] = 'all';
|
||||
}
|
||||
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
function jqGridDataCountTables(&$params, &$model) {
|
||||
return array
|
||||
('link' => array('Unit' => array('fields' => array('id', 'name')),
|
||||
'Customer' => array('fields' => array('id', 'name'))));
|
||||
('link' => array('Unit' => array('fields' => array('Unit.id', 'Unit.name')),
|
||||
'Customer' => array('fields' => array('Customer.id', 'Customer.name'))));
|
||||
}
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$link = $this->gridDataCountTables($params, $model);
|
||||
$link['link']['StatementEntry'] = array('fields' => array());
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
$link = $this->jqGridDataCountTables($params, $model);
|
||||
$link['link']['LedgerEntry'] = array('fields' => array());
|
||||
$link['link']['LedgerEntry']['Ledger'] = array('fields' => array());
|
||||
$link['link']['LedgerEntry']['Ledger']['Account'] = array('fields' => array());
|
||||
// INNER JOIN would be great, as it would ensure we're only looking
|
||||
// at the ledger entries that we truly want. However, this also
|
||||
// removes from the query any leases that do not yet have a ledger
|
||||
// entry in A/R. A solution would be to INNER JOIN these tables,
|
||||
// and LEFT JOIN it to the rest. Grouping of JOINs, however, is
|
||||
// implemented with the 'joins' tag, and is not available through
|
||||
// the Linkable behavior interface.
|
||||
//$link['link']['LedgerEntry']['Ledger']['Account']['type'] = 'INNER';
|
||||
$link['link']['LedgerEntry']['Ledger']['Account']['conditions']
|
||||
= array('Account.id' =>
|
||||
$this->Lease->LedgerEntry->Ledger->Account->accountReceivableAccountID());
|
||||
return $link;
|
||||
}
|
||||
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
return array_merge($fields,
|
||||
$this->Lease->StatementEntry->chargeDisbursementFields(true));
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
$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;
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'active') {
|
||||
$conditions[] = 'Lease.close_date IS NULL';
|
||||
@@ -77,13 +95,10 @@ class LeasesController extends AppController {
|
||||
$conditions[] = 'Lease.close_date IS NOT NULL';
|
||||
}
|
||||
|
||||
if (isset($customer_id))
|
||||
$conditions[] = array('Lease.customer_id' => $customer_id);
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
// Do not sort by number, which is type varchar and
|
||||
// sorts on an ascii basis. Sort by ID instead.
|
||||
if ($index === 'Lease.number')
|
||||
@@ -94,31 +109,31 @@ class LeasesController extends AppController {
|
||||
$index = 'Unit.sort_order';
|
||||
|
||||
$order = array();
|
||||
$order[] = parent::gridDataOrder($params, $model, $index, $direction);
|
||||
$order[] = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
// If sorting by anything other than id/number
|
||||
// add sorting by id as a secondary condition.
|
||||
if ($index !== 'Lease.id' && $index !== 'Lease.number')
|
||||
$order[] = parent::gridDataOrder($params, $model,
|
||||
$order[] = parent::jqGridDataOrder($params, $model,
|
||||
'Lease.id', $direction);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
/* function gridDataPostProcess(&$params, &$model, &$records) { */
|
||||
/* function jqGridRecordsPostProcess(&$params, &$model, &$records) { */
|
||||
/* foreach ($records AS &$record) { */
|
||||
/* $record['Lease']['through_date'] */
|
||||
/* = $this->Lease->rentChargeThrough($record['Lease']['id']); */
|
||||
/* } */
|
||||
|
||||
/* parent::gridDataPostProcess($params, $model, $records); */
|
||||
/* parent::jqGridRecordsPostProcess($params, $model, $records); */
|
||||
/* } */
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Lease'] = array('number');
|
||||
$links['Unit'] = array('name');
|
||||
$links['Customer'] = array('name');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -172,6 +187,8 @@ class LeasesController extends AppController {
|
||||
);
|
||||
|
||||
$this->redirect($this->data['redirect']);
|
||||
$this->autoRender = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($id))
|
||||
@@ -211,68 +228,58 @@ 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: refund
|
||||
* - Provides lease customer with a refund
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function refund($id) {
|
||||
function apply_deposit($id) {
|
||||
$A = new Account();
|
||||
|
||||
$lease = $this->Lease->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Unit' => array('fields' => array('id', 'name')),
|
||||
'Customer' => array('fields' => array('id', 'name')),
|
||||
'Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
// Make sure lease is not closed...
|
||||
array('Lease.close_date' => null),
|
||||
),
|
||||
));
|
||||
if (empty($lease)) {
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
// Determine the lease balance, bailing if the customer owes money
|
||||
$balance = $this->Lease->balance($id);
|
||||
if ($balance >= 0) {
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
// The refund will be for a positive amount
|
||||
$balance *= -1;
|
||||
// Get the lease balance, part of lease stats
|
||||
$this->Lease->statsMerge($lease['Lease'],
|
||||
array('stats' => $this->Lease->stats($id)));
|
||||
|
||||
// Get the accounts capable of paying the refund
|
||||
$refundAccounts = $this->Lease->StatementEntry->Account->refundAccounts();
|
||||
$defaultAccount = current($refundAccounts);
|
||||
$this->set(compact('refundAccounts', 'defaultAccount'));
|
||||
// Determine the lease security deposit
|
||||
$deposit = $this->Lease->findSecurityDeposits($lease['Lease']['id']);
|
||||
$this->set(compact('deposit'));
|
||||
$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); */
|
||||
|
||||
// Prepare to render
|
||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||
$lease['Unit']['name'] . ': ' .
|
||||
$lease['Customer']['name'] . ': Refund');
|
||||
$this->set(compact('title', 'lease', 'balance'));
|
||||
$lease['Customer']['name'] . ': Utilize Security Deposit');
|
||||
$this->set(compact('title', 'redirect'));
|
||||
}
|
||||
|
||||
|
||||
@@ -280,39 +287,70 @@ class LeasesController extends AppController {
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: bad_debt
|
||||
* - Sets up the write-off entry page, so that the
|
||||
* user can write off remaining charges on a lease.
|
||||
* - Writes off remaining charges on a lease.
|
||||
* REVISIT <AP>: 20090710
|
||||
* Should this be a customer function? What customer
|
||||
* would have only one lease that results in bad debt.
|
||||
*/
|
||||
|
||||
function bad_debt($id) {
|
||||
$A = new Account();
|
||||
|
||||
$lease = $this->Lease->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Unit' => array('fields' => array('id', 'name')),
|
||||
'Customer' => array('fields' => array('id', 'name')),
|
||||
'Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
// Make sure lease is not closed...
|
||||
array('Lease.close_date' => null),
|
||||
),
|
||||
));
|
||||
|
||||
// Make sure we have a valid lease to write off
|
||||
if (empty($lease))
|
||||
$this->redirect(array('action' => 'view', $id));
|
||||
|
||||
// Get the lease balance, part of lease stats
|
||||
$stats = $this->Lease->stats($id);
|
||||
$balance = $stats['balance'];
|
||||
$this->Lease->statsMerge($lease['Lease'],
|
||||
array('stats' => $this->Lease->stats($id)));
|
||||
|
||||
// Determine the lease security deposit
|
||||
$deposit = $this->Lease->findSecurityDeposits($lease['Lease']['id']);
|
||||
if ($deposit['summary']['balance'] > 0)
|
||||
die("Still have un-utilized security deposit");
|
||||
|
||||
$this->set('customer', $lease['Customer']);
|
||||
$this->set('unit', $lease['Unit']);
|
||||
$this->set('lease', $lease['Lease']);
|
||||
$this->set('account', array('id' => $A->badDebtAccountID()));
|
||||
|
||||
/* $redirect = array('controller' => 'leases', */
|
||||
/* 'action' => 'view', */
|
||||
/* $id); */
|
||||
|
||||
// Prepare to render
|
||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||
$lease['Unit']['name'] . ': ' .
|
||||
$lease['Customer']['name'] . ': Write Off Bad Debt');
|
||||
$this->set(compact('title', 'lease', 'balance'));
|
||||
$this->render('/transactions/bad_debt');
|
||||
$this->set(compact('title', 'redirect'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: refund
|
||||
* - Provides user with a refund
|
||||
* REVISIT <AP>: 20090710
|
||||
* Should this be a customer function?
|
||||
*/
|
||||
|
||||
function refund($id) {
|
||||
}
|
||||
|
||||
|
||||
@@ -360,7 +398,7 @@ class LeasesController extends AppController {
|
||||
));
|
||||
|
||||
$A = new Account();
|
||||
$charge_accounts = $A->invoiceAccounts();
|
||||
$charge_accounts = $A->chargeAccounts();
|
||||
$default_account = $A->rentAccountID();
|
||||
$this->set(compact('charge_accounts', 'default_account'));
|
||||
|
||||
@@ -409,9 +447,14 @@ class LeasesController extends AppController {
|
||||
$this->set('charge_gaps', $this->Lease->rentChargeGaps($id));
|
||||
$this->set('charge_through', $this->Lease->rentChargeThrough($id));
|
||||
|
||||
// Figure out the outstanding balances for this lease
|
||||
$outstanding_balance = $this->Lease->balance($id);
|
||||
$outstanding_deposit = $this->Lease->securityDepositBalance($id);
|
||||
// Obtain the overall lease balance
|
||||
$this->Lease->statsMerge($lease['Lease'],
|
||||
array('stats' => $this->Lease->stats($id)));
|
||||
$outstanding_balance = $lease['Lease']['stats']['balance'];
|
||||
|
||||
// Determine the lease security deposit
|
||||
$deposits = $this->Lease->findSecurityDeposits($lease['Lease']['id']);
|
||||
$outstanding_deposit = $deposits['summary']['balance'];
|
||||
|
||||
// Set up dynamic menu items
|
||||
if (!isset($lease['Lease']['close_date'])) {
|
||||
@@ -432,17 +475,19 @@ class LeasesController extends AppController {
|
||||
'action' => 'receipt',
|
||||
$lease['Customer']['id']));
|
||||
|
||||
/* if ($outstanding_balance < 0) */
|
||||
/* $this->sidemenu_links[] = */
|
||||
/* array('name' => 'Transfer Credit to Customer', */
|
||||
/* 'url' => array('action' => 'promote_surplus', $id)); */
|
||||
|
||||
if ($outstanding_balance < 0)
|
||||
if (isset($lease['Lease']['moveout_date']) && $outstanding_deposit > 0 && $outstanding_balance > 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Issue Refund',
|
||||
'url' => array('action' => 'refund', $id));
|
||||
array('name' => 'Apply Deposit', 'url' => array('action' => 'apply_deposit',
|
||||
$id));
|
||||
|
||||
if (isset($lease['Lease']['moveout_date']) && $outstanding_balance > 0)
|
||||
if (isset($lease['Lease']['moveout_date']) &&
|
||||
$outstanding_balance <= 0 &&
|
||||
($outstanding_deposit - $outstanding_balance) > 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Issue Refund', 'url' => array('action' => 'refund',
|
||||
$id));
|
||||
|
||||
if (isset($lease['Lease']['moveout_date']) && $outstanding_deposit == 0 && $outstanding_balance > 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Write-Off', 'url' => array('action' => 'bad_debt',
|
||||
$id));
|
||||
@@ -0,0 +1,467 @@
|
||||
<?php
|
||||
|
||||
class LedgerEntriesController extends AppController {
|
||||
|
||||
var $sidemenu_links = array();
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function jqGridDataSetup(&$params) {
|
||||
parent::jqGridDataSetup($params);
|
||||
if (isset($params['custom']['ar_account'])) {
|
||||
$params['custom']['account_id'] =
|
||||
$this->LedgerEntry->DebitLedger->Account->accountReceivableAccountID();
|
||||
}
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
$link =
|
||||
array(// Models
|
||||
'Transaction' =>
|
||||
array('fields' => array('id', 'stamp'),
|
||||
),
|
||||
|
||||
'MonetarySource' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Lease' =>
|
||||
array('fields' => array('id', 'number'),
|
||||
'Unit' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (isset($params['custom']['account_ftype'])) {
|
||||
$ftype = $params['custom']['account_ftype'];
|
||||
$ftype = ucfirst($ftype);
|
||||
//$ftype = $this->LedgerEntry->DebitLedger->Account->fundamentalOpposite($ftype);
|
||||
$link[$ftype . 'Ledger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'Account' => array('class' => 'Account',
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
}
|
||||
elseif (isset($params['custom']['ledger_id'])) {
|
||||
$ledger_id = $params['custom']['ledger_id'];
|
||||
$link['Ledger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'conditions' => ("Ledger.id = IF(LedgerEntry.debit_ledger_id = $ledger_id," .
|
||||
" LedgerEntry.credit_ledger_id," .
|
||||
" LedgerEntry.debit_ledger_id)"),
|
||||
'Account' => array(
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
}
|
||||
elseif ($params['action'] === 'collected') {
|
||||
// Income / Receipt / Money
|
||||
// debit: A/R credit: Income <-- this entry
|
||||
// debit: Receipt credit: A/R <-- ReceiptLedgerEntry, below
|
||||
// debit: Money credit: Receipt <-- MoneyLedgerEntry, below
|
||||
|
||||
$link['CreditLedger'] =
|
||||
array('fields' => 'sequence',
|
||||
'Account' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
|
||||
// We're searching for the Receipt<->A/R entries,
|
||||
// which are debits on the A/R account. Find the
|
||||
// reconciling entries to that A/R debit.
|
||||
$link['DebitReconciliationLedgerEntry'] =
|
||||
array('alias' => 'ReceiptLedgerEntry',
|
||||
|
||||
'Transaction' =>
|
||||
array('alias' => 'ReceiptTransaction'),
|
||||
|
||||
// Credit Ledger should be A/R;
|
||||
// Debit Ledger should be Receipt
|
||||
'DebitLedger' =>
|
||||
array('alias' => 'ReceiptLedger',
|
||||
'Account' => array('alias' => 'ReceiptAccount'),
|
||||
),
|
||||
|
||||
// Finally, the Money (Cash/Check/etc) Entry is the one
|
||||
// which reconciles our ReceiptLedgerEntry debit
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
array('alias' => 'MoneyLedgerEntry',
|
||||
'linkalias' => 'MoneyLedgerEntryR',
|
||||
|
||||
// Credit Ledger should be Receipt;
|
||||
// Debit Ledger should be our Money Account
|
||||
'DebitLedger' =>
|
||||
array('alias' => 'MoneyLedger',
|
||||
'Account' =>
|
||||
array('alias' => 'MoneyAccount'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$link['DebitLedger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'DebitAccount' => array('class' => 'Account',
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
|
||||
$link['CreditLedger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'CreditAccount' => array('class' => 'Account',
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['account_id'])) {
|
||||
$account_id = $params['custom']['account_id'];
|
||||
$link['Ledger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'conditions' => ("Ledger.id = IF(DebitLedger.account_id = $account_id," .
|
||||
" LedgerEntry.credit_ledger_id," .
|
||||
" LedgerEntry.debit_ledger_id)"),
|
||||
'Account' => array(
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['reconcile_id'])) {
|
||||
$ftype = $params['custom']['account_ftype'];
|
||||
$ftype = $this->LedgerEntry->DebitLedger->Account->fundamentalOpposite($ftype);
|
||||
$ftype = ucfirst($ftype);
|
||||
$link[$ftype.'ReconciliationLedgerEntry'] =
|
||||
array('fields' => array('Reconciliation.amount'));
|
||||
}
|
||||
|
||||
return array('link' => $link);
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
$ledger_id = (isset($params['custom']['ledger_id'])
|
||||
? $params['custom']['ledger_id']
|
||||
: null);
|
||||
$account_id = (isset($params['custom']['account_id'])
|
||||
? $params['custom']['account_id']
|
||||
: null);
|
||||
$account_type = (isset($params['custom']['account_type'])
|
||||
? $params['custom']['account_type']
|
||||
: null);
|
||||
|
||||
$fields = $model->ledgerContextFields2($ledger_id, $account_id, $account_type);
|
||||
|
||||
if (count(array_intersect($params['fields'], array('applied'))) == 1)
|
||||
$fields[] = 'SUM(Reconciliation.amount) AS applied';
|
||||
|
||||
if ($params['action'] === 'collected')
|
||||
$fields[] = 'MAX(ReceiptTransaction.stamp) AS last_paid';
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$ledger_id = (isset($params['custom']['ledger_id'])
|
||||
? $params['custom']['ledger_id']
|
||||
: null);
|
||||
$account_type = (isset($params['custom']['account_type'])
|
||||
? $params['custom']['account_type']
|
||||
: null);
|
||||
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'collected') {
|
||||
extract($params['custom']);
|
||||
|
||||
if (isset($collected_account_id))
|
||||
$conditions[] = array('Account.id' => $params['custom']['collected_account_id']);
|
||||
else
|
||||
die("INTERNAL ERROR: COLLECTED ACCOUNT ID NOT SET");
|
||||
|
||||
if (!empty($collected_from_date))
|
||||
$conditions[]
|
||||
= array('ReceiptTransaction.stamp >=' =>
|
||||
$this->LedgerEntry->Transaction->dateFormatBeforeSave($collected_from_date));
|
||||
|
||||
if (!empty($collected_through_date))
|
||||
$conditions[]
|
||||
= array('ReceiptTransaction.stamp <=' =>
|
||||
$this->LedgerEntry->Transaction->dateFormatBeforeSave($collected_through_date . ' 23:59:59'));
|
||||
|
||||
if (isset($collected_payment_accounts))
|
||||
$conditions[] = array('MoneyAccount.id' => $collected_payment_accounts);
|
||||
else
|
||||
$conditions[] = array('NOT' => array(array('MoneyAccount.id' => null)));
|
||||
}
|
||||
|
||||
if ($params['action'] === 'ledger') {
|
||||
$conditions[] = $model->ledgerContextConditions($ledger_id, $account_type);
|
||||
}
|
||||
if (isset($params['custom']['reconcile_id'])) {
|
||||
$ftype = $params['custom']['account_ftype'];
|
||||
//$ftype = $this->LedgerEntry->DebitLedger->Account->fundamentalOpposite($ftype);
|
||||
$conditions[] = array('Reconciliation.'.$ftype.'_ledger_entry_id' => $params['custom']['reconcile_id']);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['account_id'])) {
|
||||
$conditions[] =
|
||||
array('OR' =>
|
||||
array(array('CreditAccount.id' => $params['custom']['account_id']),
|
||||
array('DebitAccount.id' => $params['custom']['account_id'])));
|
||||
}
|
||||
|
||||
if (isset($params['custom']['customer_id'])) {
|
||||
$conditions[] =
|
||||
array('Customer.id' => $params['custom']['customer_id']);
|
||||
|
||||
/* $Account = new Account(); */
|
||||
/* if (isset($params['custom']['account_ftype']) || */
|
||||
/* isset($params['custom']['ledger_id'])) { */
|
||||
/* $conditions[] = */
|
||||
/* array('OR' => array('Account.id' => $Account->invoiceAccountID(), */
|
||||
/* 'Account.id' => $Account->receiptAccountID())); */
|
||||
/* } else { */
|
||||
/* $conditions[] = */
|
||||
/* array('OR' => array('DebitAccount.id' => $Account->invoiceAccountID(), */
|
||||
/* //'CreditAccount.id' => $Account->invoiceAccountID(), */
|
||||
/* //'DebitAccount.id' => $Account->receiptAccountID(), */
|
||||
/* 'CreditAccount.id' => $Account->receiptAccountID(), */
|
||||
/* )); */
|
||||
/* } */
|
||||
}
|
||||
|
||||
if (isset($params['custom']['lease_id'])) {
|
||||
$conditions[] =
|
||||
array('Lease.id' => $params['custom']['lease_id']);
|
||||
|
||||
/* $Account = new Account(); */
|
||||
/* if (isset($params['custom']['account_ftype']) || */
|
||||
/* isset($params['custom']['ledger_id'])) { */
|
||||
/* $conditions[] = */
|
||||
/* array('OR' => array('Account.id' => $Account->invoiceAccountID(), */
|
||||
/* 'Account.id' => $Account->receiptAccountID())); */
|
||||
/* } else { */
|
||||
/* $conditions[] = */
|
||||
/* array('OR' => array('DebitAccount.id' => $Account->invoiceAccountID(), */
|
||||
/* //'CreditAccount.id' => $Account->invoiceAccountID(), */
|
||||
/* //'DebitAccount.id' => $Account->receiptAccountID(), */
|
||||
/* 'CreditAccount.id' => $Account->receiptAccountID(), */
|
||||
/* )); */
|
||||
/* } */
|
||||
}
|
||||
|
||||
if (isset($params['custom']['transaction_id'])) {
|
||||
$conditions[] =
|
||||
array('Transaction.id' => $params['custom']['transaction_id']);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['monetary_source_id'])) {
|
||||
$conditions[] =
|
||||
array('MonetarySource.id' => $params['custom']['monetary_source_id']);
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Transaction'] = array('id');
|
||||
$links['LedgerEntry'] = array('id');
|
||||
$links['Account'] = array('controller' => 'accounts', 'name');
|
||||
$links['DebitAccount'] = array('controller' => 'accounts', 'name');
|
||||
$links['CreditAccount'] = array('controller' => 'accounts', 'name');
|
||||
$links['MonetarySource'] = array('name');
|
||||
$links['Customer'] = array('name');
|
||||
$links['Lease'] = array('number');
|
||||
$links['Unit'] = array('name');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
function jqGridDataGroup(&$params, &$model) {
|
||||
if (isset($params['custom']['group_by_tx']) && $params['custom']['group_by_tx'])
|
||||
return $model->alias.'.transaction_id';
|
||||
|
||||
return parent::jqGridDataGroup($params, $model);
|
||||
}
|
||||
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
/* if ($index === 'balance') */
|
||||
/* return ($index .' '. $direction); */
|
||||
$order = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
if ($index === 'Transaction.stamp') {
|
||||
$order[] = 'LedgerEntry.id ' . $direction;
|
||||
}
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
function jqGridDataRecords(&$params, &$model, $query) {
|
||||
if ($params['action'] === 'collected') {
|
||||
$tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1));
|
||||
$tquery['fields'] = array('SUM(Reconciliation.amount) AS applied');
|
||||
$total = $model->find('first', $tquery);
|
||||
|
||||
$params['userdata']['total'] = $total[0]['applied'];
|
||||
}
|
||||
|
||||
return parent::jqGridDataRecords($params, $model, $query);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: reverse the ledger entry
|
||||
*/
|
||||
|
||||
function reverse($id) {
|
||||
$this->LedgerEntry->reverse($id);
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: view
|
||||
* - Displays information about a specific entry
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
|
||||
}
|
||||
|
||||
// Get the LedgerEntry and related fields
|
||||
$entry = $this->LedgerEntry->find
|
||||
('first',
|
||||
array('contain' => array('MonetarySource.id',
|
||||
'MonetarySource.name',
|
||||
'Transaction.id',
|
||||
'Transaction.stamp',
|
||||
'DebitLedger.id',
|
||||
'DebitLedger.sequence',
|
||||
'DebitLedger.account_id',
|
||||
'CreditLedger.id',
|
||||
'CreditLedger.sequence',
|
||||
'CreditLedger.account_id',
|
||||
'Customer.id',
|
||||
'Customer.name',
|
||||
'Lease.id',
|
||||
),
|
||||
|
||||
'fields' => array('LedgerEntry.*'),
|
||||
|
||||
'conditions' => array('LedgerEntry.id' => $id),
|
||||
));
|
||||
//pr($entry);
|
||||
|
||||
// Because 'DebitLedger' and 'CreditLedger' both relate to 'Account',
|
||||
// CakePHP will not include them in the LedgerEntry->find (or so it
|
||||
// seems). We'll have to break out each Account separately.
|
||||
|
||||
// Get the Account from DebitLedger
|
||||
$entry['DebitLedger'] += $this->LedgerEntry->DebitLedger->Account->find
|
||||
('first',
|
||||
array('contain' => true,
|
||||
'fields' => array('Account.id', 'Account.name', 'Account.type', 'Account.trackable'),
|
||||
'conditions' => array('Account.id' => $entry['DebitLedger']['account_id']),
|
||||
));
|
||||
$entry['DebitLedger']['Account']['ftype'] =
|
||||
$this->LedgerEntry->DebitLedger->Account
|
||||
->fundamentalType($entry['DebitLedger']['Account']['type']);
|
||||
|
||||
// Get the Account from CreditLedger
|
||||
$entry['CreditLedger'] += $this->LedgerEntry->CreditLedger->Account->find
|
||||
('first',
|
||||
array('contain' => true,
|
||||
'fields' => array('Account.id', 'Account.name', 'Account.type', 'Account.trackable'),
|
||||
'conditions' => array('Account.id' => $entry['CreditLedger']['account_id']),
|
||||
));
|
||||
$entry['CreditLedger']['Account']['ftype'] =
|
||||
$this->LedgerEntry->CreditLedger->Account
|
||||
->fundamentalType($entry['CreditLedger']['Account']['type']);
|
||||
|
||||
// Get the reconciliation balances for this ledger entry
|
||||
$stats = $this->LedgerEntry->stats($id);
|
||||
$stats['debit']['amount_reconciled'] = $stats['debit_amount_reconciled'];
|
||||
$stats['credit']['amount_reconciled'] = $stats['credit_amount_reconciled'];
|
||||
if ($entry['DebitLedger']['Account']['trackable'])
|
||||
$stats['debit']['amount_remaining'] = $entry['LedgerEntry']['amount'] - $stats['debit']['amount_reconciled'];
|
||||
if ($entry['CreditLedger']['Account']['trackable'])
|
||||
$stats['credit']['amount_remaining'] = $entry['LedgerEntry']['amount'] - $stats['credit']['amount_reconciled'];
|
||||
//pr($stats);
|
||||
|
||||
$reconciled = $this->LedgerEntry->findReconciledLedgerEntries($id);
|
||||
//pr($reconciled);
|
||||
|
||||
|
||||
// REVISIT <AP>: 20090711
|
||||
// It's not clear whether we should be able to reverse charges that have
|
||||
// already been paid/cleared/reconciled. Certainly, that will be the
|
||||
// case when someone has pre-paid and then moves out early. However, this
|
||||
// will work well for items accidentally charged but not yet paid for.
|
||||
if ((!$entry['DebitLedger']['Account']['trackable'] ||
|
||||
$stats['debit']['amount_reconciled'] == 0) &&
|
||||
(!$entry['CreditLedger']['Account']['trackable'] ||
|
||||
$stats['credit']['amount_reconciled'] == 0)
|
||||
|
||||
&& 0
|
||||
|
||||
)
|
||||
{
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Undo',
|
||||
'url' => array('action' => 'reverse',
|
||||
$id));
|
||||
}
|
||||
|
||||
if ($this->LedgerEntry->Ledger->Account->type
|
||||
($entry['CreditLedger']['Account']['id']) == 'INCOME')
|
||||
{
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Reverse',
|
||||
'url' => array('action' => 'reverse',
|
||||
$id));
|
||||
}
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Double Ledger Entry #{$entry['LedgerEntry']['id']}";
|
||||
$this->set(compact('entry', 'title', 'reconciled', 'stats'));
|
||||
}
|
||||
}
|
||||
@@ -29,73 +29,78 @@ class LedgersController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function current() { $this->gridView('Current Ledgers'); }
|
||||
function closed() { $this->gridView('Closed Ledgers'); }
|
||||
function all() { $this->gridView('All Ledgers', 'all'); }
|
||||
function current() { $this->jqGridView('Current Ledgers'); }
|
||||
function closed() { $this->jqGridView('Closed Ledgers'); }
|
||||
function all() { $this->jqGridView('All Ledgers', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function gridDataSetup(&$params) {
|
||||
parent::gridDataSetup($params);
|
||||
function jqGridDataSetup(&$params) {
|
||||
parent::jqGridDataSetup($params);
|
||||
if (!isset($params['action']))
|
||||
$params['action'] = 'all';
|
||||
}
|
||||
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
// Our count should NOT include anything extra,
|
||||
// so we need the virtual function to prevent
|
||||
// the base class from just calling our
|
||||
// gridDataTables function.
|
||||
return parent::gridDataTables($params, $model);
|
||||
function jqGridDataCountTables(&$params, &$model) {
|
||||
return array('contain' => false);
|
||||
}
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
return array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'Account',
|
||||
'LedgerEntry',
|
||||
'CloseTransaction',
|
||||
'Close',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
$fields[] = 'CONCAT(Account.id, "-", Ledger.sequence) AS id_sequence';
|
||||
return array_merge($fields,
|
||||
$this->Ledger->LedgerEntry->debitCreditFields(true));
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
return array
|
||||
('Ledger.*',
|
||||
'CONCAT(Account.id, "-", Ledger.sequence) AS id_sequence',
|
||||
'SUM(IF(LedgerEntry.debit_ledger_id = Ledger.id,
|
||||
LedgerEntry.amount, NULL)) AS debits',
|
||||
'SUM(IF(LedgerEntry.credit_ledger_id = Ledger.id,
|
||||
LedgerEntry.amount, NULL)) AS credits',
|
||||
"SUM(IF(Account.type IN ('ASSET', 'EXPENSE'),
|
||||
IF(LedgerEntry.debit_ledger_id = Ledger.id, 1, -1),
|
||||
IF(LedgerEntry.credit_ledger_id = Ledger.id, 1, -1)
|
||||
) * IF(LedgerEntry.amount, LedgerEntry.amount, 0)
|
||||
) AS balance",
|
||||
'COUNT(LedgerEntry.id) AS entries');
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'current') {
|
||||
$conditions[] = array('Ledger.close_transaction_id' => null);
|
||||
$conditions[] = array('NOT' => array('Ledger.closed'));
|
||||
}
|
||||
elseif ($params['action'] === 'closed') {
|
||||
$conditions[] = array('Ledger.close_transaction_id !=' => null);
|
||||
$conditions[] = 'Ledger.closed';
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
$id_sequence = false;
|
||||
if ($index === 'id_sequence') {
|
||||
$id_sequence = true;
|
||||
$index = 'Ledger.account_id';
|
||||
}
|
||||
|
||||
$order = parent::gridDataOrder($params, $model, $index, $direction);
|
||||
$order = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
if ($id_sequence) {
|
||||
$order[] = 'Ledger.sequence ' . $direction;
|
||||
@@ -104,10 +109,10 @@ class LedgersController extends AppController {
|
||||
return $order;
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Ledger'] = array('id_sequence');
|
||||
$links['Account'] = array('name');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,28 +15,28 @@ class MapsController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function all() { $this->gridView('All Maps', 'all'); }
|
||||
function all() { $this->jqGridView('All Maps', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
return array
|
||||
('link' => array('SiteArea' => array('fields' => array('SiteArea.id', 'SiteArea.name')),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Map'] = array('id');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
class MonetarySourcesController extends AppController {
|
||||
|
||||
var $sidemenu_links = array();
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: index / all
|
||||
* - Generate a listing of MonetarySources
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function all() { $this->jqGridView('All MonetarySources', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
return array
|
||||
('contain' => false,
|
||||
);
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['MonetarySource'] = array('id');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: nsf
|
||||
* - Marks a monetary source as having insufficient funds.
|
||||
*/
|
||||
|
||||
function nsf($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
// REVISIT <AP>: 20090713
|
||||
// For testing purposes, must be deleted
|
||||
$stamp = '2009-07-09';
|
||||
|
||||
$this->MonetarySource->nsf($id, $stamp);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: view
|
||||
* - Displays information about a specific entry
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
|
||||
}
|
||||
|
||||
// Get the MonetarySource and related fields
|
||||
$monetary_source = $this->MonetarySource->find
|
||||
('first', array
|
||||
('contain' => false,
|
||||
));
|
||||
|
||||
// REVISIT <AP>: 20090713
|
||||
// Consider allowing the NSF operation only if the source is used on
|
||||
// a ledger entry that is debited on a "payable" account (perhaps
|
||||
// even restricted to "payable" ASSET accounts), credited on Receipt
|
||||
// (or A/R), and reconciles the credit to an entry that debits on a
|
||||
// "depositable" account.
|
||||
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'NSF',
|
||||
'url' => array('action' => 'nsf',
|
||||
$id));
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Monetary Source #{$monetary_source['MonetarySource']['id']}";
|
||||
$this->set(compact('monetary_source', 'title'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
class TransactionsController extends AppController {
|
||||
|
||||
var $components = array('RequestHandler');
|
||||
|
||||
var $sidemenu_links =
|
||||
array(array('name' => 'Transactions', 'header' => true),
|
||||
array('name' => 'All', 'url' => array('controller' => 'transactions', 'action' => 'all')),
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: index / all
|
||||
* - Generate a listing of transactions
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function all() { $this->jqGridView('All Transactions', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Transaction'] = array('id');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: view
|
||||
* - Displays information about a specific transaction
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
$transaction = $this->Transaction->find
|
||||
('first',
|
||||
array('contain' =>
|
||||
array(// Models
|
||||
'LedgerEntry' => array('fields' => array('LedgerEntry.id',
|
||||
'LedgerEntry.amount',
|
||||
'LedgerEntry.comment'),
|
||||
//Models
|
||||
|
||||
'DebitLedger' => array
|
||||
('fields' => array('DebitLedger.id', 'DebitLedger.sequence'),
|
||||
'Account' => array
|
||||
('fields' => array('Account.id', 'Account.name')),
|
||||
),
|
||||
|
||||
'CreditLedger' => array
|
||||
('fields' => array('CreditLedger.id', 'CreditLedger.sequence'),
|
||||
'Account' => array
|
||||
('fields' => array('Account.id', 'Account.name')),
|
||||
),
|
||||
),
|
||||
),
|
||||
'conditions' => array('Transaction.id' => $id),
|
||||
));
|
||||
|
||||
// Figure out the transaction total
|
||||
$total = 0;
|
||||
foreach($transaction['LedgerEntry'] AS $entry)
|
||||
$total += $entry['amount'];
|
||||
|
||||
// OK, prepare to render.
|
||||
$title = 'Transaction #' . $transaction['Transaction']['id'];
|
||||
$this->set(compact('transaction', 'title', 'total'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: postInvoice
|
||||
* - handles the creation of a charge invoice
|
||||
*/
|
||||
|
||||
function postInvoice() {
|
||||
if (!$this->RequestHandler->isPost()) {
|
||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->Transaction->addInvoice($this->data, null,
|
||||
$this->data['Lease']['id'])) {
|
||||
$this->Session->setFlash("INVOICE FAILED", true);
|
||||
// REVISIT <AP> 20090706:
|
||||
// Until we can work out the session problems,
|
||||
// just die.
|
||||
die("<H1>INVOICE FAILED</H1>");
|
||||
}
|
||||
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: postReceipt
|
||||
* - handles the creation of a payment receipt
|
||||
*/
|
||||
|
||||
function postReceipt() {
|
||||
if (!$this->RequestHandler->isPost()) {
|
||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($this->data['LedgerEntry'] AS &$entry) {
|
||||
if (!isset($entry['acct'][$entry['account_id']]))
|
||||
continue;
|
||||
|
||||
$entry['MonetarySource'] = $entry['acct'][$entry['account_id']];
|
||||
}
|
||||
|
||||
pr($this->data);
|
||||
if (!$this->Transaction->addReceipt($this->data,
|
||||
$this->data['Customer']['id'],
|
||||
(isset($this->data['Lease']['id'])
|
||||
? $this->data['Lease']['id']
|
||||
: null ))) {
|
||||
$this->Session->setFlash("RECEIPT FAILED", true);
|
||||
// REVISIT <AP> 20090706:
|
||||
// Until we can work out the session problems,
|
||||
// just die.
|
||||
die("<H1>RECEIPT FAILED</H1>");
|
||||
}
|
||||
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -30,63 +30,77 @@ class UnitsController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function unavailable() { $this->gridView('Unavailable Units'); }
|
||||
function vacant() { $this->gridView('Vacant Units'); }
|
||||
function occupied() { $this->gridView('Occupied Units'); }
|
||||
function all() { $this->gridView('All Units', 'all'); }
|
||||
function unavailable() { $this->jqGridView('Unavailable Units'); }
|
||||
function vacant() { $this->jqGridView('Vacant Units'); }
|
||||
function occupied() { $this->jqGridView('Occupied Units'); }
|
||||
function all() { $this->jqGridView('All Units', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function gridDataSetup(&$params) {
|
||||
parent::gridDataSetup($params);
|
||||
function jqGridDataSetup(&$params) {
|
||||
parent::jqGridDataSetup($params);
|
||||
if (!isset($params['action']))
|
||||
$params['action'] = 'all';
|
||||
}
|
||||
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
return array
|
||||
('link' => array('UnitSize' => array('fields' => array('id', 'name')),
|
||||
'CurrentLease' => array('fields' => array('id'))));
|
||||
function jqGridDataCountTables(&$params, &$model) {
|
||||
$link = array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'UnitSize' => array('fields' => array('id', 'name')),
|
||||
),
|
||||
);
|
||||
|
||||
/* if ($params['action'] === 'occupied') */
|
||||
/* $link['Lease'] = array('fields' => array(), */
|
||||
/* // Models */
|
||||
/* 'Contact' => array('fields' => array('display_name'), */
|
||||
/* //'type' => 'LEFT', */
|
||||
/* ), */
|
||||
/* ); */
|
||||
if ($params['action'] === 'occupied')
|
||||
$link['Lease'] = array('fields' => array(),
|
||||
// Models
|
||||
'Contact' => array('fields' => array('display_name'),
|
||||
//'type' => 'LEFT',
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$link = $this->gridDataCountTables($params, $model);
|
||||
$link['link']['CurrentLease']['StatementEntry'] = array('fields' => array());
|
||||
return $link;
|
||||
}
|
||||
|
||||
/* function gridDataTables(&$params, &$model) { */
|
||||
/* return array */
|
||||
/* ('link' => array('Unit' => array('fields' => array('Unit.id', 'Unit.name')), */
|
||||
/* 'Customer' => array('fields' => array('Customer.id', 'Customer.name')))); */
|
||||
/* } */
|
||||
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
|
||||
return array_merge($fields,
|
||||
$this->Unit->Lease->StatementEntry->chargeDisbursementFields(true));
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
$link = $this->jqGridDataCountTables($params, $model);
|
||||
$link['link']['CurrentLease']['LedgerEntry'] = array('fields' => array());
|
||||
$link['link']['CurrentLease']['LedgerEntry']['Ledger'] = array('fields' => array());
|
||||
$link['link']['CurrentLease']['LedgerEntry']['Ledger']['Account'] = array('fields' => array());
|
||||
// INNER JOIN would be great, as it would ensure we're only looking
|
||||
// at the ledger entries that we truly want. However, this also
|
||||
// removes from the query any leases that do not yet have a ledger
|
||||
// entry in A/R. A solution would be to INNER JOIN these tables,
|
||||
// and LEFT JOIN it to the rest. Grouping of JOINs, however, is
|
||||
// implemented with the 'joins' tag, and is not available through
|
||||
// the Linkable behavior interface.
|
||||
//$link['link']['CurrentLease']['LedgerEntry']['Ledger']['Account']['type'] = 'INNER';
|
||||
$link['link']['CurrentLease']['LedgerEntry']['Ledger']['Account']['conditions']
|
||||
= array('Account.id' =>
|
||||
$this->Unit->CurrentLease->LedgerEntry->Ledger->Account->accountReceivableAccountID());
|
||||
return $link;
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
$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))" .
|
||||
" * LedgerEntry.amount) AS 'balance'");
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'unavailable') {
|
||||
$conditions[] = $this->Unit->conditionUnavailable();
|
||||
@@ -104,27 +118,27 @@ class UnitsController extends AppController {
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
// Instead of sorting by name, sort by defined order
|
||||
if ($index === 'Unit.name')
|
||||
$index = 'Unit.sort_order';
|
||||
|
||||
$order = array();
|
||||
$order[] = parent::gridDataOrder($params, $model, $index, $direction);
|
||||
$order[] = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
// If sorting by anything other than name (defined order)
|
||||
// add the sort-order as a secondary condition
|
||||
if ($index !== 'Unit.name')
|
||||
$order[] = parent::gridDataOrder($params, $model,
|
||||
$order[] = parent::jqGridDataOrder($params, $model,
|
||||
'Unit.sort_order', $direction);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Unit'] = array('name');
|
||||
$links['UnitSize'] = array('name');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -228,18 +242,14 @@ class UnitsController extends AppController {
|
||||
$stats['CurrentLease']['balance'];
|
||||
|
||||
// Figure out the total security deposit for the current lease.
|
||||
$deposits = $this->Unit->Lease->securityDeposits($unit['CurrentLease']['id']);
|
||||
$outstanding_deposit = $this->Unit->Lease->securityDepositBalance($unit['CurrentLease']['id']);
|
||||
$deposits = $this->Unit->Lease->findSecurityDeposits($unit['CurrentLease']['id']);
|
||||
$outstanding_deposit = $deposits['summary']['balance'];
|
||||
}
|
||||
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Edit', 'url' => array('action' => 'edit',
|
||||
$id));
|
||||
|
||||
if (isset($unit['CurrentLease']['id']) &&
|
||||
!isset($unit['CurrentLease']['moveout_date'])) {
|
||||
$this->sidemenu_links[] =
|
||||
@@ -253,10 +263,6 @@ class UnitsController extends AppController {
|
||||
|
||||
if (isset($unit['CurrentLease']['id']) &&
|
||||
!isset($unit['CurrentLease']['close_date'])) {
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Charge', 'url' => array('controller' => 'leases',
|
||||
'action' => 'invoice',
|
||||
$unit['CurrentLease']['id']));
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Payment', 'url' => array('controller' => 'customers',
|
||||
'action' => 'receipt',
|
||||
@@ -269,71 +275,4 @@ class UnitsController extends AppController {
|
||||
'outstanding_balance',
|
||||
'outstanding_deposit'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: edit
|
||||
* - Edit unit information
|
||||
*/
|
||||
|
||||
function edit($id = null) {
|
||||
if (isset($this->data)) {
|
||||
// Check to see if the operation was cancelled.
|
||||
if (isset($this->params['form']['cancel'])) {
|
||||
if (empty($this->data['Unit']['id']))
|
||||
$this->redirect(array('action'=>'index'));
|
||||
|
||||
$this->redirect(array('action'=>'view', $this->data['Unit']['id']));
|
||||
}
|
||||
|
||||
// Make sure we have unit data
|
||||
if (empty($this->data['Unit']))
|
||||
$this->redirect(array('action'=>'index'));
|
||||
|
||||
// Make sure we have a rental rate
|
||||
if (empty($this->data['Unit']['rent']))
|
||||
$this->redirect(array('action'=>'view', $this->data['Unit']['id']));
|
||||
|
||||
// Save the unit and all associated data
|
||||
$this->Unit->create();
|
||||
$this->Unit->id = $this->data['Unit']['id'];
|
||||
if (!$this->Unit->save($this->data, false)) {
|
||||
$this->Session->setFlash("UNIT SAVE FAILED", true);
|
||||
pr("UNIT SAVE FAILED");
|
||||
}
|
||||
|
||||
$this->redirect(array('action'=>'view', $this->Unit->id));
|
||||
|
||||
// For debugging, only if the redirects above have been
|
||||
// commented out, otherwise this section isn't reached.
|
||||
$this->render('/fake');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
$this->data = $this->Unit->findById($id);
|
||||
$title = 'Unit ' . $this->data['Unit']['name'] . " : Edit";
|
||||
}
|
||||
else {
|
||||
$title = "Enter New Unit";
|
||||
$this->data = array();
|
||||
}
|
||||
|
||||
$statusEnums = $this->Unit->allowedStatusSet($id);
|
||||
$statusEnums = array_combine(array_keys($statusEnums),
|
||||
array_keys($statusEnums));
|
||||
$this->set(compact('statusEnums'));
|
||||
|
||||
$unit_sizes = $this->Unit->UnitSize->find
|
||||
('list', array('order' => array('unit_type_id', 'width', 'depth', 'id')));
|
||||
$this->set(compact('unit_sizes'));
|
||||
|
||||
// Prepare to render.
|
||||
pr($this->data);
|
||||
$this->set(compact('title'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-1359
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,850 @@
|
||||
<?php
|
||||
class Account extends AppModel {
|
||||
|
||||
var $name = 'Account';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'name' => array('notempty'),
|
||||
'external_name' => array('notempty')
|
||||
);
|
||||
|
||||
var $hasOne = array(
|
||||
'CurrentLedger' => array(
|
||||
'className' => 'Ledger',
|
||||
// REVISIT <AP> 20090702:
|
||||
// I would prefer this statement, which has no
|
||||
// engine specific code. However, it doesn't
|
||||
// work with the Linkable behavior. I need to
|
||||
// look into that, just not right now.
|
||||
//'conditions' => array('CurrentLedger.close_id' => null),
|
||||
'conditions' => array('CurrentLedger.close_id IS NULL'),
|
||||
),
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'Ledger',
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: type
|
||||
* - Returns the type of this account
|
||||
*/
|
||||
function type($id) {
|
||||
$this->cacheQueries = true;
|
||||
$account = $this->find('first', array
|
||||
('recursive' => -1,
|
||||
'fields' => array('type'),
|
||||
'conditions' => array(array('Account.id' => $id)),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
|
||||
return $account['Account']['type'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: fundamentalType
|
||||
* - Returns the fundmental type of the account, credit or debit
|
||||
*/
|
||||
function fundamentalType($id_or_type) {
|
||||
if (is_numeric($id_or_type))
|
||||
$type = $this->type($id_or_type);
|
||||
else
|
||||
$type = $id_or_type;
|
||||
|
||||
// Asset and Expense accounts are debit accounts
|
||||
if (in_array(strtoupper($type), array('ASSET', 'EXPENSE')))
|
||||
return 'debit';
|
||||
|
||||
// Otherwise, it's a credit account
|
||||
return 'credit';
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: fundamentalOpposite
|
||||
* - Returns the opposite fundmental type of the account, credit or debit
|
||||
*/
|
||||
function fundamentalOpposite($id_or_type) {
|
||||
if (in_array(strtolower($id_or_type), array('credit', 'debit')))
|
||||
$fund = $id_or_type;
|
||||
else
|
||||
$fund = $this->fundamentalType($id_or_type);
|
||||
|
||||
if ($fund == 'debit')
|
||||
return 'credit';
|
||||
|
||||
return 'debit';
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: name
|
||||
* - Returns the name of this account
|
||||
*/
|
||||
function name($id) {
|
||||
$this->cacheQueries = true;
|
||||
$account = $this->find('first', array
|
||||
('recursive' => -1,
|
||||
'fields' => array('name'),
|
||||
'conditions' => array(array('Account.id' => $id)),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
|
||||
return $account['Account']['name'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: Account IDs
|
||||
* - Returns the ID of the desired account
|
||||
*/
|
||||
|
||||
function securityDepositAccountID() { return $this->nameToID('Security Deposit'); }
|
||||
function rentAccountID() { return $this->nameToID('Rent'); }
|
||||
function lateChargeAccountID() { return $this->nameToID('Late Charge'); }
|
||||
function nsfAccountID() { return $this->nameToID('NSF'); }
|
||||
function nsfChargeAccountID() { return $this->nameToID('NSF Charge'); }
|
||||
function taxAccountID() { return $this->nameToID('Tax'); }
|
||||
function accountReceivableAccountID() { return $this->nameToID('A/R'); }
|
||||
function cashAccountID() { return $this->nameToID('Cash'); }
|
||||
function checkAccountID() { return $this->nameToID('Check'); }
|
||||
function moneyOrderAccountID() { return $this->nameToID('Money Order'); }
|
||||
function concessionAccountID() { return $this->nameToID('Concession'); }
|
||||
function pettyCashAccountID() { return $this->nameToID('Petty Cash'); }
|
||||
function invoiceAccountID() { return $this->nameToID('Invoice'); }
|
||||
function receiptAccountID() { return $this->nameToID('Receipt'); }
|
||||
function badDebtAccountID() { return $this->nameToID('Bad Debt'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: fundamentalAccounts
|
||||
* - Returns an array of accounts by their fundamental type
|
||||
*/
|
||||
|
||||
function fundamentalAccounts($ftype) {
|
||||
$this->cacheQueries = true;
|
||||
$account = $this->find('all', array
|
||||
('contain' => array('CurrentLedger'),
|
||||
'fields' => array('Account.id', 'Account.type', 'Account.name', 'CurrentLedger.id'),
|
||||
'conditions' => array('Account.type' => strtoupper($ftype))
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: relatedAccounts
|
||||
* - Returns an array of accounts related by similar attributes
|
||||
*/
|
||||
|
||||
function relatedAccounts($attribute, $extra = null) {
|
||||
$this->cacheQueries = true;
|
||||
$account = $this->find('all', array
|
||||
('contain' => array('CurrentLedger'),
|
||||
'fields' => array('Account.id', 'Account.type', 'Account.name', 'CurrentLedger.id'),
|
||||
'conditions' => array('Account.'.$attribute => true),
|
||||
'order' => array('Account.name'),
|
||||
) + (isset($extra) ? $extra : array())
|
||||
);
|
||||
$this->cacheQueries = false;
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: chargeAccounts
|
||||
* - Returns an array of accounts suitable for charges
|
||||
*/
|
||||
|
||||
function chargeAccounts() {
|
||||
// Get all accounts that support charges
|
||||
$accounts = $this->relatedAccounts('chargeable', array('order' => 'name'));
|
||||
|
||||
// Rearrange to be of the form (id => name)
|
||||
$charge_accounts = array();
|
||||
foreach ($accounts AS $acct) {
|
||||
$charge_accounts[$acct['Account']['id']] = $acct['Account']['name'];
|
||||
}
|
||||
|
||||
return $charge_accounts;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: paymentAccounts
|
||||
* - Returns an array of accounts suitable for payments
|
||||
*/
|
||||
|
||||
function paymentAccounts() {
|
||||
// Get all accounts that support payments
|
||||
$accounts = $this->relatedAccounts('payable', array('order' => 'name'));
|
||||
|
||||
// Rearrange to be of the form (id => name)
|
||||
$payment_accounts = array();
|
||||
foreach ($accounts AS $acct) {
|
||||
$payment_accounts[$acct['Account']['id']] = $acct['Account']['name'];
|
||||
}
|
||||
|
||||
return $payment_accounts;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: collectableAccounts
|
||||
* - Returns an array of accounts suitable to show income collection
|
||||
*/
|
||||
|
||||
function collectableAccounts() {
|
||||
$accounts = $this->paymentAccounts();
|
||||
|
||||
foreach(array($this->nsfAccountID(),
|
||||
$this->securityDepositAccountID())
|
||||
AS $account_id) {
|
||||
$accounts[$account_id] = $this->name($account_id);
|
||||
}
|
||||
|
||||
return $accounts;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: currentLedgerID
|
||||
* - Returns the current ledger ID of the account
|
||||
*/
|
||||
function currentLedgerID($id) {
|
||||
$this->cacheQueries = true;
|
||||
$item = $this->find('first', array
|
||||
('contain' => 'CurrentLedger',
|
||||
'conditions' => array('Account.id' => $id),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
return $item['CurrentLedger']['id'];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: ledgers
|
||||
* - Returns an array of ledger ids from the given account
|
||||
*/
|
||||
function ledgers($id, $all = false) {
|
||||
if ($all) {
|
||||
$contain = array('Ledger' => array('fields' => array('Ledger.id')));
|
||||
} else {
|
||||
$contain = array('CurrentLedger' => array('fields' => array('CurrentLedger.id')));
|
||||
}
|
||||
|
||||
$this->cacheQueries = true;
|
||||
$account = $this->find('first', array
|
||||
('contain' => $contain,
|
||||
'fields' => array(),
|
||||
'conditions' => array(array('Account.id' => $id)),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
|
||||
if ($all) {
|
||||
$ledger_ids = array();
|
||||
foreach ($account['Ledger'] AS $ledger)
|
||||
array_push($ledger_ids, $ledger['id']);
|
||||
}
|
||||
else {
|
||||
$ledger_ids = array($account['CurrentLedger']['id']);
|
||||
}
|
||||
|
||||
/* pr(array('function' => 'Account::ledgers', */
|
||||
/* 'args' => compact('id', 'all'), */
|
||||
/* 'return' => $ledger_ids)); */
|
||||
|
||||
return $ledger_ids;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: closeCurrentLedger
|
||||
* - Closes the current account ledger, and opens a new one
|
||||
* with the old balance carried forward.
|
||||
*/
|
||||
function closeCurrentLedger($id = null, $close_id = null) {
|
||||
$contain = array('CurrentLedger' => array('fields' => array('CurrentLedger.id')));
|
||||
|
||||
if (!$close_id) {
|
||||
$close = new Close();
|
||||
$close->create();
|
||||
if (!$close->save(array('stamp' => null), false)) {
|
||||
return false;
|
||||
}
|
||||
$close_id = $close->id;
|
||||
}
|
||||
|
||||
$this->cacheQueries = true;
|
||||
$account = $this->find('all', array
|
||||
('contain' => $contain,
|
||||
'fields' => array(),
|
||||
'conditions' =>
|
||||
$id ? array(array('Account.id' => $id)) : array()
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
//pr(compact('id', 'account'));
|
||||
|
||||
foreach ($account AS $acct) {
|
||||
if (!$this->Ledger->closeLedger($acct['CurrentLedger']['id'], $close_id))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findLedgerEntries
|
||||
* - Returns an array of ledger entries that belong to the given
|
||||
* account, either just from the current ledger, or from all ledgers.
|
||||
*/
|
||||
function findLedgerEntries($id, $all = false, $cond = null, $link = null) {
|
||||
/* pr(array('function' => 'Account::findLedgerEntries', */
|
||||
/* 'args' => compact('id', 'all', 'cond', 'link'), */
|
||||
/* )); */
|
||||
|
||||
$entries = array();
|
||||
foreach ($this->ledgers($id, $all) AS $ledger_id) {
|
||||
$ledger_entries = $this->Ledger->findLedgerEntries
|
||||
($ledger_id, $this->type($id), $cond, $link);
|
||||
$entries = array_merge($entries, $ledger_entries);
|
||||
}
|
||||
|
||||
$stats = $this->stats($id, $all, $cond);
|
||||
$entries = array('Entries' => $entries,
|
||||
'summary' => $stats['Ledger']);
|
||||
|
||||
/* pr(array('function' => 'Account::findLedgerEntries', */
|
||||
/* 'args' => compact('id', 'all', 'cond', 'link'), */
|
||||
/* 'vars' => compact('stats'), */
|
||||
/* 'return' => compact('entries'), */
|
||||
/* )); */
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findLedgerEntriesRelatedToAccount
|
||||
* - Returns an array of ledger entries that belong to the given
|
||||
* account, and are related to a specific account, either just from
|
||||
* the current ledger, or from all ledgers.
|
||||
*/
|
||||
function findLedgerEntriesRelatedToAccount($id, $rel_ids, $all = false, $cond = null, $link = null) {
|
||||
/* pr(array('function' => 'Account::findLedgerEntriesRelatedToAccount', */
|
||||
/* 'args' => compact('id', 'rel_ids', 'all', 'cond', 'link'), */
|
||||
/* )); */
|
||||
|
||||
if (!isset($cond))
|
||||
$cond = array();
|
||||
if (!is_array($rel_ids))
|
||||
$rel_ids = array($rel_ids);
|
||||
|
||||
$ledger_ids = array();
|
||||
foreach ($rel_ids AS $rel_id)
|
||||
$ledger_ids = array_merge($ledger_ids, $this->ledgers($rel_id));
|
||||
|
||||
array_push($cond, $this->Ledger->LedgerEntry->conditionEntryAsCreditOrDebit($ledger_ids));
|
||||
$entries = $this->findLedgerEntries($id, $all, $cond, $link);
|
||||
|
||||
/* pr(array('function' => 'Account::findLedgerEntriesRelatedToAccount', */
|
||||
/* 'args' => compact('id', 'relid', 'all', 'cond', 'link'), */
|
||||
/* 'vars' => compact('ledger_ids'), */
|
||||
/* 'return' => compact('entries'), */
|
||||
/* )); */
|
||||
return $entries;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findUnreconciledLedgerEntries
|
||||
* - Returns ledger entries that are not yet reconciled
|
||||
* (such as charges not paid).
|
||||
*/
|
||||
|
||||
function findUnreconciledLedgerEntries($id = null, $fundamental_type = null, $cond = null) {
|
||||
if (!isset($cond))
|
||||
$cond = array();
|
||||
$cond[] = array('Account.id' => $id);
|
||||
|
||||
foreach (($fundamental_type
|
||||
? array($fundamental_type)
|
||||
: array('debit', 'credit')) AS $fund) {
|
||||
$ucfund = ucfirst($fund);
|
||||
$unreconciled[$fund]['entry'] = $this->find
|
||||
('all', array
|
||||
('link' => array
|
||||
('Ledger' => array
|
||||
('fields' => array(),
|
||||
"LedgerEntry" => array
|
||||
('class' => "{$ucfund}LedgerEntry",
|
||||
'fields' => array('id', 'customer_id', 'lease_id', 'amount'),
|
||||
"ReconciliationLedgerEntry" => array
|
||||
('class' => "{$ucfund}ReconciliationLedgerEntry",
|
||||
'fields' => array
|
||||
("COALESCE(SUM(Reconciliation.amount),0) AS 'reconciled'",
|
||||
"LedgerEntry.amount - COALESCE(SUM(Reconciliation.amount),0) AS 'balance'",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'group' => ("LedgerEntry.id" .
|
||||
" HAVING LedgerEntry.amount" .
|
||||
" <> COALESCE(SUM(Reconciliation.amount),0)"),
|
||||
'conditions' => $cond,
|
||||
'fields' => array(),
|
||||
));
|
||||
$balance = 0;
|
||||
foreach ($unreconciled[$fund]['entry'] AS &$entry) {
|
||||
$entry = array_merge(array_diff_key($entry["LedgerEntry"], array(0=>true)),
|
||||
$entry[0]);
|
||||
$balance += $entry['balance'];
|
||||
}
|
||||
$unreconciled[$fund]['balance'] = $balance;
|
||||
}
|
||||
|
||||
return $unreconciled;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reconcileNewLedgerEntry
|
||||
* - Returns which ledger entries a new credit/debit would
|
||||
* reconcile, and how much.
|
||||
*
|
||||
* - REVISIT <AP> 20090617
|
||||
* This should be subject to different algorithms, such
|
||||
* as apply to oldest charges first, newest first, to fees
|
||||
* before rent, etc. Until we get there, I'll hardcode
|
||||
* whatever algorithm is simplest.
|
||||
*/
|
||||
|
||||
function reconcileNewLedgerEntry($id, $fundamental_type, $amount, $cond = null) {
|
||||
$ofund = $this->fundamentalOpposite($fundamental_type);
|
||||
$unreconciled = array($ofund => array('entry'=>array(), 'balance'=>0));
|
||||
$applied = 0;
|
||||
|
||||
// if there is no money in the entry, it can reconcile nothing
|
||||
// don't bother wasting time sifting ledger entries.
|
||||
if ($amount > 0) {
|
||||
$unreconciled = $this->findUnreconciledLedgerEntries($id, $ofund, $cond);
|
||||
|
||||
foreach ($unreconciled[$ofund]['entry'] AS $i => &$entry) {
|
||||
// Determine if amount is sufficient to cover the entry
|
||||
if ($amount > $entry['balance'])
|
||||
$apply = $entry['balance'];
|
||||
elseif ($amount > 0)
|
||||
$apply = $amount;
|
||||
else {
|
||||
unset($unreconciled[$ofund]['entry'][$i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$entry['applied'] = $apply;
|
||||
$entry['reconciled'] += $apply;
|
||||
$entry['balance'] -= $apply;
|
||||
$applied += $apply;
|
||||
$amount -= $apply;
|
||||
}
|
||||
}
|
||||
|
||||
$unreconciled[$ofund]['unapplied'] = $amount;
|
||||
$unreconciled[$ofund]['applied'] = $applied;
|
||||
$unreconciled[$ofund]['balance'] -= $applied;
|
||||
return $unreconciled;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: postLedgerEntry
|
||||
* -
|
||||
* transaction_data
|
||||
* - transaction_id (optional... if set all else is ignored)
|
||||
* - Transaction
|
||||
* - stamp (optional... otherwise NOW is used)
|
||||
* - comment
|
||||
*
|
||||
* monetary_source_data
|
||||
* - monetary_source_id (optional... if set all else is ignored)
|
||||
* - account_name
|
||||
* - MonetarySource
|
||||
* - name
|
||||
*/
|
||||
|
||||
function postLedgerEntry($transaction_data,
|
||||
$monetary_data,
|
||||
$entry_data,
|
||||
$reconcile = null) {
|
||||
//pr(compact('transaction_data', 'monetary_data', 'entry_data', 'reconcile'));
|
||||
|
||||
// Automatically figure out the customer if we have the lease
|
||||
if (isset($entry_data['lease_id']) && !isset($entry_data['customer_id'])) {
|
||||
$L = new Lease();
|
||||
$L->recursive = -1;
|
||||
$lease = $L->read(null, $entry_data['lease_id']);
|
||||
$entry_data['customer_id'] = $lease['Lease']['customer_id'];
|
||||
}
|
||||
|
||||
if (!isset($entry_data['lease_id']))
|
||||
$entry_data['lease_id'] = null;
|
||||
|
||||
if (!isset($entry_data['customer_id']))
|
||||
$entry_data['customer_id'] = null;
|
||||
|
||||
// Get the Transaction squared away
|
||||
if (isset($transaction_data['transaction_id'])) {
|
||||
$transaction_data
|
||||
= array_intersect_key($transaction_data,
|
||||
array('transaction_id'=>1,
|
||||
'split_transaction_id'=>1));
|
||||
}
|
||||
elseif (isset($transaction_data['Transaction'])) {
|
||||
$transaction_data
|
||||
= array_intersect_key($transaction_data,
|
||||
array('Transaction'=>1,
|
||||
'split_transaction_id'=>1));
|
||||
}
|
||||
else {
|
||||
$transaction_data = array('Transaction'=>array('stamp' => null));
|
||||
}
|
||||
|
||||
|
||||
// Get the Monetary Source squared away
|
||||
if (isset($monetary_data)) {
|
||||
if (!isset($monetary_data['monetary_source_id'])) {
|
||||
|
||||
// Convert Account ID to name or vice versa
|
||||
if (isset($monetary_data['account_id'])) {
|
||||
$monetary_data['account_name'] = $this->name($monetary_data['account_id']);
|
||||
} elseif (isset($monetary_data['account_name'])) {
|
||||
$monetary_data['account_id'] = $this->nameToID($monetary_data['account_name']);
|
||||
}
|
||||
|
||||
if ($monetary_data['account_id'] == $this->cashAccountID()) {
|
||||
// No distinguishing features of Cash, just
|
||||
// use the shared monetary source
|
||||
$monetary_data['monetary_source_id'] =
|
||||
$this->Ledger->LedgerEntry->MonetarySource->nameToID('Cash');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($monetary_data['monetary_source_id'])) {
|
||||
$monetary_data
|
||||
= array_intersect_key($monetary_data,
|
||||
array('monetary_source_id'=>1));
|
||||
}
|
||||
else {
|
||||
// The monetary source needs to be unique
|
||||
// Create a new one dedicated to this entry
|
||||
// Give it a fancy name based on the check number
|
||||
$monetary_data['MonetarySource']['name'] = $monetary_data['account_name'];
|
||||
if ($monetary_data['account_name'] === $this->name($this->checkAccountID()) ||
|
||||
$monetary_data['account_name'] === $this->name($this->moneyOrderAccountID())) {
|
||||
$monetary_data['MonetarySource']['name'] .=
|
||||
' #' . $monetary_data['MonetarySource']['data1'];
|
||||
}
|
||||
|
||||
$monetary_data
|
||||
= array_intersect_key($monetary_data,
|
||||
array('MonetarySource'=>1));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$monetary_data = array();
|
||||
}
|
||||
|
||||
// Make sure to clean out any unwanted data from the entry
|
||||
$entry_data
|
||||
= array_diff_key($entry_data,
|
||||
array('transaction_id'=>1, 'Transaction'=>1,
|
||||
'monetary_source_id'=>1, 'MonetarySource'=>1));
|
||||
|
||||
// Then add in the transaction and monetary source data
|
||||
//pr(compact('transaction_data', 'monetary_data', 'entry_data'));
|
||||
if (isset($transaction_data))
|
||||
$entry_data += $transaction_data;
|
||||
if (isset($monetary_data))
|
||||
$entry_data += $monetary_data;
|
||||
|
||||
// Set up the debit ledger id
|
||||
if (!isset($entry_data['debit_ledger_id'])) {
|
||||
$entry_data['debit_ledger_id'] =
|
||||
(isset($entry_data['debit_account_id'])
|
||||
? $this->currentLedgerID($entry_data['debit_account_id'])
|
||||
: (isset($entry_data['debit_account_name'])
|
||||
? $this->currentLedgerID($this->nameToID($entry_data['debit_account_name']))
|
||||
: null
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Set up the credit ledger id
|
||||
if (!isset($entry_data['credit_ledger_id'])) {
|
||||
$entry_data['credit_ledger_id'] =
|
||||
(isset($entry_data['credit_account_id'])
|
||||
? $this->currentLedgerID($entry_data['credit_account_id'])
|
||||
: (isset($entry_data['credit_account_name'])
|
||||
? $this->currentLedgerID($this->nameToID($entry_data['credit_account_name']))
|
||||
: null
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
//pr(array('pre-save', compact('entry_data')));
|
||||
// Create it!
|
||||
$new_entry = new LedgerEntry();
|
||||
$new_entry->create();
|
||||
if (!$new_entry->saveAll($entry_data, array('validate'=>false))) {
|
||||
return array('error' => true);
|
||||
}
|
||||
|
||||
// See if the user has entered some sort of non-array
|
||||
// for the reconcile parameter.
|
||||
if (isset($reconcile) && is_bool($reconcile) && $reconcile) {
|
||||
$reconcile = array('debit' => true, 'credit' => true);
|
||||
}
|
||||
elseif (isset($reconcile) && $reconcile == 'invoice') {
|
||||
$reconcile = array('credit' => 'invoice');
|
||||
}
|
||||
elseif (isset($reconcile) && $reconcile == 'receipt') {
|
||||
$reconcile = array('debit' => 'receipt');
|
||||
}
|
||||
elseif (!isset($reconcile) || !is_array($reconcile)) {
|
||||
$reconcile = array();
|
||||
}
|
||||
|
||||
// Reconcile the new entry... assume we'll have success
|
||||
$err = false;
|
||||
foreach (array_intersect_key($reconcile, array('credit'=>1,'debit'=>1))
|
||||
AS $dc_type => $reconcile_set) {
|
||||
if (!isset($reconcile_set) || (is_bool($reconcile_set) && !$reconcile_set))
|
||||
continue;
|
||||
|
||||
if ($reconcile_set === 'receipt') {
|
||||
$C = new Customer();
|
||||
$reconciled = $C->reconcileNewLedgerEntry($entry_data['customer_id'],
|
||||
$this->fundamentalOpposite($dc_type),
|
||||
$entry_data['amount']);
|
||||
|
||||
/* pr(array("reconcile receipt", */
|
||||
/* compact('reconciled', 'split_transaction', 'transaction_data'))); */
|
||||
$split_transaction = array_intersect_key($transaction_data,
|
||||
array('Transaction'=>1,
|
||||
'split_transaction_id'=>1));
|
||||
|
||||
if (isset($split_transaction['split_transaction_id']))
|
||||
$split_transaction['transaction_id'] = $split_transaction['split_transaction_id'];
|
||||
|
||||
if (is_array($reconciled) && count($reconciled[$dc_type]['entry'])) {
|
||||
foreach ($reconciled[$dc_type]['entry'] AS $rec) {
|
||||
//pr(compact('rec', 'split_transaction'));
|
||||
if (!$rec['applied'])
|
||||
continue;
|
||||
|
||||
// Create an entry to handle the splitting of the funds ("Payment")
|
||||
// and reconcile against the new cash/check/etc entry created above,
|
||||
// as well as the A/R account.
|
||||
|
||||
// Payment must debit the Receipt ledger, and credit the A/R ledger
|
||||
// debit: Receipt credit: A/R
|
||||
$ids = $this->postLedgerEntry
|
||||
($split_transaction,
|
||||
null,
|
||||
array('debit_ledger_id' => $this->currentLedgerID($this->receiptAccountID()),
|
||||
'credit_ledger_id' => $this->currentLedgerID($this->accountReceivableAccountID()),
|
||||
'amount' => $rec['applied'],
|
||||
'lease_id' => $rec['lease_id'],
|
||||
'customer_id' => $rec['customer_id'],
|
||||
),
|
||||
array('debit' => array(array('LedgerEntry' => array('id' => $new_entry->id,
|
||||
'amount' => $rec['applied']))),
|
||||
'credit' => array(array('LedgerEntry' => array('id' => $rec['id'],
|
||||
'amount' => $rec['applied']))))
|
||||
);
|
||||
// Keep using the same split transaction for all reconciled entries
|
||||
$split_transaction = array_intersect_key($ids, array('transaction_id'=>1));
|
||||
//pr(compact('ids', 'split_transaction'));
|
||||
}
|
||||
|
||||
//pr("end reconciled is array");
|
||||
}
|
||||
|
||||
//pr("end reconcile receipt");
|
||||
}
|
||||
|
||||
if (is_array($reconcile_set)) {
|
||||
//pr("reconcile_set is array");
|
||||
foreach ($reconcile_set AS $reconcile_entry) {
|
||||
if (!isset($reconcile_entry['LedgerEntry']['id']))
|
||||
continue;
|
||||
|
||||
$amount = $reconcile_entry['LedgerEntry']['amount'];
|
||||
if (!$amount)
|
||||
continue;
|
||||
|
||||
if ($dc_type == 'debit') {
|
||||
$debit_ledger_entry_id = $new_entry->id;
|
||||
$credit_ledger_entry_id = $reconcile_entry['LedgerEntry']['id'];
|
||||
}
|
||||
else {
|
||||
$debit_ledger_entry_id = $reconcile_entry['LedgerEntry']['id'];
|
||||
$credit_ledger_entry_id = $new_entry->id;
|
||||
}
|
||||
|
||||
$R = new Reconciliation();
|
||||
$R->create();
|
||||
if (!$R->save(compact('amount',
|
||||
'debit_ledger_entry_id',
|
||||
'credit_ledger_entry_id'), false))
|
||||
$err = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$new_entry->recursive = -1;
|
||||
$new_entry->read();
|
||||
//pr(array('post-save', $entry->data));
|
||||
|
||||
$ret = array
|
||||
('error' => $err,
|
||||
'id' => $new_entry->data['LedgerEntry']['id'],
|
||||
'transaction_id' => $new_entry->data['LedgerEntry']['transaction_id'],
|
||||
'monetary_source_id' => $new_entry->data['LedgerEntry']['monetary_source_id']);
|
||||
|
||||
if (isset($split_transaction['transaction_id']))
|
||||
$ret['split_transaction_id'] = $split_transaction['transaction_id'];
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: closeAndDeposit
|
||||
* - Closes the current set of ledgers, transferring
|
||||
* their balances to specified ledger.
|
||||
*/
|
||||
function closeAndDeposit($set, $deposit_account_id) {
|
||||
|
||||
$close = new Close();
|
||||
$close->create();
|
||||
if (!$close->save(array('stamp' => null, 'comment' => 'Deposit'), false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$transaction = array();
|
||||
foreach ($set AS $ledger) {
|
||||
// REVISIT <AP>: 20090710
|
||||
// If the user said to include a ledger in the
|
||||
// set, should we really be excluding it?
|
||||
if ($ledger['total'] == 0)
|
||||
continue;
|
||||
|
||||
$ids = $this->postLedgerEntry
|
||||
($transaction,
|
||||
null,
|
||||
array('debit_account_id' => $deposit_account_id,
|
||||
'credit_ledger_id' => $ledger['id'],
|
||||
'amount' => $ledger['total']),
|
||||
// Reconcile the account for cash/check/etc,
|
||||
// which is the credit side of this entry.
|
||||
array('credit' => $ledger['entries']));
|
||||
//pr(compact('ids'));
|
||||
|
||||
if ($ids['error'])
|
||||
die("closeAndDeposit : postLedgerEntry returned error!");
|
||||
|
||||
$transaction = array_intersect_key($ids, array('transaction_id'=>1));
|
||||
|
||||
$this->Ledger->closeLedger($ledger['id'], $close->id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: stats
|
||||
* - Returns summary data from the requested account.
|
||||
*/
|
||||
|
||||
function stats($id = null, $all = false, $cond = null) {
|
||||
if (!$id)
|
||||
return null;
|
||||
|
||||
// All old, closed ledgers MUST balance to 0.
|
||||
// However, the user may want the ENTIRE running totals,
|
||||
// (not just the balance), so we may have to query all
|
||||
// ledgers, as dictated by the $all parameter.
|
||||
|
||||
$account = $this->find('first',
|
||||
array('contain' =>
|
||||
($all
|
||||
? array('Ledger' => array
|
||||
('fields' => array('id')))
|
||||
: array('CurrentLedger' => array
|
||||
('fields' => array('id')))
|
||||
),
|
||||
'conditions' => array
|
||||
(array('Account.id' => $id))
|
||||
));
|
||||
|
||||
$stats = array();
|
||||
if ($all) {
|
||||
foreach ($account['Ledger'] AS $ledger)
|
||||
$this->statsMerge($stats['Ledger'],
|
||||
$this->Ledger->stats($ledger['id'], $cond));
|
||||
}
|
||||
else {
|
||||
$stats['Ledger'] =
|
||||
$this->Ledger->stats($account['CurrentLedger']['id'], $cond);
|
||||
}
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,406 @@
|
||||
<?php
|
||||
/*
|
||||
* LinkableBehavior
|
||||
* Light-weight approach for data mining on deep relations between models.
|
||||
* Join tables based on model relations to easily enable right to left find operations.
|
||||
*
|
||||
* Can be used as a alternative to the ContainableBehavior:
|
||||
* - On data fetching only in right to left operations,
|
||||
* wich means that in "one to many" relations (hasMany, hasAndBelongsToMany)
|
||||
* should only be used from the "many to one" tables. i.e:
|
||||
* To fetch all Users assigneds to a Project with ProjectAssignment,
|
||||
* $Project->find('all', array('link' => 'User', 'conditions' => 'project_id = 1'))
|
||||
* - Won't produce the desired result as data came from users table will be lost.
|
||||
* $User->find('all', array('link' => 'Project', 'conditions' => 'project_id = 1'))
|
||||
* - Will fetch all users related to the specified project in one query
|
||||
*
|
||||
* - On data mining as a much lighter approach - can reduce 300+ query find operations
|
||||
* in one single query with joins; "or your money back!" ;-)
|
||||
*
|
||||
* - Has the 'fields' param enabled to make it easy to replace Containable usage,
|
||||
* only change the 'contain' param to 'link'.
|
||||
*
|
||||
* Linkable Behavior. Taking it easy in your DB.
|
||||
* RafaelBandeira <rafaelbandeira3(at)gmail(dot)com>
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @version 1.0;
|
||||
*/
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* NOTE TO <AP> 20090615:
|
||||
* This structure should be linkable (it was my test case).
|
||||
|
||||
$entry = $this->LedgerEntry->find
|
||||
('first',
|
||||
array('link' => array('DebitLedger' =>
|
||||
array(
|
||||
'fields' => array('id'),
|
||||
'alias' => 'MyDebitLedger',
|
||||
'Account' =>
|
||||
array(
|
||||
'fields' => array('id'),
|
||||
'alias' => 'MyDebitAccount'),
|
||||
),
|
||||
|
||||
'MyCreditLedger' =>
|
||||
array(
|
||||
'fields' => array('id'),
|
||||
'class' => 'CreditLedger',
|
||||
'MyCreditAccount' =>
|
||||
array(
|
||||
'fields' => array('id'),
|
||||
'class' => 'Account'),
|
||||
),
|
||||
|
||||
'MyOtherLedger' =>
|
||||
array(
|
||||
'fields' => array('id'),
|
||||
'class' => 'Ledger',
|
||||
'alias' => 'AliasToMyOtherLedger',
|
||||
'Account' =>
|
||||
array(
|
||||
'fields' => array('id'),
|
||||
'alias' => 'AliasToMyOtherAccount'),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array('LedgerEntry.id' => $id),
|
||||
));
|
||||
**********************************************************************/
|
||||
|
||||
|
||||
class LinkableBehavior extends ModelBehavior {
|
||||
|
||||
protected $_key = 'link';
|
||||
|
||||
protected $_options = array(
|
||||
'type' => true, 'table' => true, 'alias' => true, 'joins' => true,
|
||||
'conditions' => true, 'fields' => true, 'reference' => true,
|
||||
'class' => true, 'defaults' => true, 'linkalias' => true,
|
||||
);
|
||||
|
||||
protected $_defaults = array('type' => 'LEFT');
|
||||
|
||||
function pr($lev, $mixed) {
|
||||
if ($lev >= 5)
|
||||
return;
|
||||
|
||||
pr($mixed);
|
||||
return;
|
||||
|
||||
$trace = debug_backtrace(false);
|
||||
//array_shift($trace);
|
||||
$calls = array();
|
||||
foreach ($trace AS $call) {
|
||||
$call = array_intersect_key($call,
|
||||
array('file'=>1,
|
||||
'line'=>1,
|
||||
//'class'=>1,
|
||||
'function'=>1,
|
||||
));
|
||||
$calls[] = implode("; ", $call);
|
||||
}
|
||||
pr(array('debug' => $mixed, 'stack' => $calls));
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a function for made recursive str_replaces in an array
|
||||
* NOTE: The palacement of this function is terrible, but I don't
|
||||
* know if I really want to go down this path or not.
|
||||
*/
|
||||
function recursive_array_replace($find, $replace, &$data) {
|
||||
if (!isset($data))
|
||||
return;
|
||||
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$this->recursive_array_replace($find, $replace, $data[$key]);
|
||||
} else {
|
||||
$data[$key] = str_replace($find, $replace, $value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$data = str_replace($find, $replace, $data);
|
||||
}
|
||||
}
|
||||
|
||||
public function beforeFind(&$Model, $query) {
|
||||
$this->pr(10,
|
||||
array('function' => 'Linkable::beforeFind',
|
||||
'args' => array('Model->alias' => '$Model->alias') + compact('query'),
|
||||
));
|
||||
if (isset($query[$this->_key])) {
|
||||
$optionsDefaults = $this->_defaults + array('reference' =>
|
||||
array('class' => $Model->alias,
|
||||
'alias' => $Model->alias),
|
||||
$this->_key => array());
|
||||
$optionsKeys = $this->_options + array($this->_key => true);
|
||||
if (!isset($query['fields']) || $query['fields'] === true) {
|
||||
//$query['fields'] = array_keys($Model->_schema);
|
||||
$query['fields'] = $Model->getDataSource()->fields($Model);
|
||||
} elseif (!is_array($query['fields'])) {
|
||||
$query['fields'] = array($query['fields']);
|
||||
}
|
||||
$query = am(array('joins' => array()), $query, array('recursive' => -1));
|
||||
$iterators[] = $query[$this->_key];
|
||||
$cont = 0;
|
||||
do {
|
||||
$iterator = $iterators[$cont];
|
||||
$defaults = $optionsDefaults;
|
||||
if (isset($iterator['defaults'])) {
|
||||
$defaults = array_merge($defaults, $iterator['defaults']);
|
||||
unset($iterator['defaults']);
|
||||
}
|
||||
$iterations = Set::normalize($iterator);
|
||||
$this->pr(25,
|
||||
array('checkpoint' => 'Iterations',
|
||||
compact('iterations'),
|
||||
));
|
||||
foreach ($iterations as $alias => $options) {
|
||||
if (is_null($options)) {
|
||||
$options = array();
|
||||
}
|
||||
$options = am($defaults, compact('alias'), $options);
|
||||
if (empty($options['alias'])) {
|
||||
throw new InvalidArgumentException(sprintf('%s::%s must receive aliased links', get_class($this), __FUNCTION__));
|
||||
}
|
||||
|
||||
if (empty($options['class']))
|
||||
$options['class'] = $alias;
|
||||
|
||||
if (!isset($options['conditions']))
|
||||
$options['conditions'] = array();
|
||||
elseif (!is_array($options['conditions']))
|
||||
$options['conditions'] = array($options['conditions']);
|
||||
|
||||
$this->pr(20,
|
||||
array('checkpoint' => 'Begin Model Work',
|
||||
compact('alias', 'options'),
|
||||
));
|
||||
|
||||
$modelClass = $options['class'];
|
||||
$modelAlias = $options['alias'];
|
||||
$referenceClass = $options['reference']['class'];
|
||||
$referenceAlias = $options['reference']['alias'];
|
||||
|
||||
$_Model =& ClassRegistry::init($modelClass); // the incoming model to be linked in query
|
||||
$Reference =& ClassRegistry::init($referenceClass); // the already in query model that links to $_Model
|
||||
$this->pr(12,
|
||||
array('checkpoint' => 'Aliases Established',
|
||||
'Model' => ($modelAlias .' : '. $modelClass .
|
||||
' ('. $_Model->alias .' : '. $_Model->name .')'),
|
||||
'Reference' => ($referenceAlias .' : '. $referenceClass .
|
||||
' ('. $Reference->alias .' : '. $Reference->name .')'),
|
||||
));
|
||||
|
||||
$db =& $_Model->getDataSource();
|
||||
|
||||
$associatedThroughReference = 0;
|
||||
$association = null;
|
||||
|
||||
// Figure out how these two models are related, creating
|
||||
// a relationship if one doesn't otherwise already exists.
|
||||
if (($associations = $Reference->getAssociated()) &&
|
||||
isset($associations[$_Model->alias])) {
|
||||
$this->pr(12, array('checkpoint' => "Reference defines association to _Model"));
|
||||
$associatedThroughReference = 1;
|
||||
$type = $associations[$_Model->alias];
|
||||
$association = $Reference->{$type}[$_Model->alias];
|
||||
}
|
||||
elseif (($associations = $_Model->getAssociated()) &&
|
||||
isset($associations[$Reference->alias])) {
|
||||
$this->pr(12, array('checkpoint' => "_Model defines association to Reference"));
|
||||
$type = $associations[$Reference->alias];
|
||||
$association = $_Model->{$type}[$Reference->alias];
|
||||
}
|
||||
else {
|
||||
// No relationship... make our best effort to create one.
|
||||
$this->pr(12, array('checkpoint' => "No assocation between _Model and Reference"));
|
||||
$type = 'belongsTo';
|
||||
$_Model->bind($Reference->alias);
|
||||
// Grab the association now, since we'll unbind in a moment.
|
||||
$association = $_Model->{$type}[$Reference->alias];
|
||||
$_Model->unbindModel(array('belongsTo' => array($Reference->alias)));
|
||||
}
|
||||
|
||||
// Determine which model holds the foreign key
|
||||
if (($type === 'hasMany' || $type === 'hasOne') ^ $associatedThroughReference) {
|
||||
$primaryAlias = $referenceAlias;
|
||||
$foreignAlias = $modelAlias;
|
||||
$primaryModel = $Reference;
|
||||
$foreignModel = $_Model;
|
||||
} else {
|
||||
$primaryAlias = $modelAlias;
|
||||
$foreignAlias = $referenceAlias;
|
||||
$primaryModel = $_Model;
|
||||
$foreignModel = $Reference;
|
||||
}
|
||||
|
||||
if ($associatedThroughReference)
|
||||
$associationAlias = $referenceAlias;
|
||||
else
|
||||
$associationAlias = $modelAlias;
|
||||
|
||||
$this->recursive_array_replace("%{MODEL_ALIAS}",
|
||||
$associationAlias,
|
||||
$association['conditions']);
|
||||
|
||||
$this->pr(15,
|
||||
array('checkpoint' => 'Models Established - Check Associations',
|
||||
'primaryModel' => $primaryAlias .' : '. $primaryModel->name,
|
||||
'foreignModel' => $foreignAlias .' : '. $foreignModel->name,
|
||||
compact('type', 'association'),
|
||||
));
|
||||
|
||||
if ($type === 'hasAndBelongsToMany') {
|
||||
if (isset($association['with']))
|
||||
$linkClass = $association['with'];
|
||||
else
|
||||
$linkClass = Inflector::classify($association['joinTable']);
|
||||
|
||||
$Link =& $_Model->{$linkClass};
|
||||
|
||||
if (isset($options['linkalias']))
|
||||
$linkAlias = $options['linkalias'];
|
||||
else
|
||||
$linkAlias = $Link->alias;
|
||||
|
||||
$this->pr(17,
|
||||
array('checkpoint' => 'Linking HABTM',
|
||||
compact('linkClass', 'linkAlias'),
|
||||
));
|
||||
|
||||
// Get the foreign key fields (for the link table) directly from
|
||||
// the defined model associations, if they exists. This is the
|
||||
// users direct specification, and therefore definitive if present.
|
||||
$modelLink = $Link->escapeField($association['foreignKey'], $linkAlias);
|
||||
$referenceLink = $Link->escapeField($association['associationForeignKey'], $linkAlias);
|
||||
|
||||
// If we haven't figured out the foreign keys, see if there is a
|
||||
// model for the link table, and if it has the appropriate
|
||||
// associations with the two tables we're trying to join.
|
||||
if (empty($modelLink) && isset($Link->belongsTo[$_Model->alias]))
|
||||
$modelLink = $Link->escapeField($Link->belongsTo[$_Model->alias]['foreignKey'], $linkAlias);
|
||||
if (empty($referenceLink) && isset($Link->belongsTo[$Reference->alias]))
|
||||
$referenceLink = $Link->escapeField($Link->belongsTo[$Reference->alias]['foreignKey'], $linkAlias);
|
||||
|
||||
// We're running quite thin here. None of the models spell
|
||||
// out the appropriate linkages. We'll have to SWAG it.
|
||||
if (empty($modelLink))
|
||||
$modelLink = $Link->escapeField(Inflector::underscore($_Model->alias) . '_id', $linkAlias);
|
||||
if (empty($referenceLink))
|
||||
$referenceLink = $Link->escapeField(Inflector::underscore($Reference->alias) . '_id', $linkAlias);
|
||||
|
||||
// Get the primary key from the tables we're joining.
|
||||
$referenceKey = $Reference->escapeField(null, $referenceAlias);
|
||||
$modelKey = $_Model->escapeField(null, $modelAlias);
|
||||
|
||||
// Join the linkage table to our model. We'll use an inner join,
|
||||
// as the whole purpose of the linkage table is to make this
|
||||
// connection. As we are embedding this join, the INNER will not
|
||||
// cause any problem with the overall query, should the user not
|
||||
// be concerned with whether or not the join has any results.
|
||||
// They control that with the 'type' parameter which will be at
|
||||
// the top level join.
|
||||
$options['joins'][] = array('type' => 'INNER',
|
||||
'alias' => $modelAlias,
|
||||
'conditions' => "{$modelKey} = {$modelLink}",
|
||||
'table' => $db->fullTableName($_Model, true));
|
||||
|
||||
// Now for the top level join. This will be added into the list
|
||||
// of joins down below, outside of the HABTM specific code.
|
||||
$options['class'] = $linkClass;
|
||||
$options['alias'] = $linkAlias;
|
||||
$options['table'] = $Link->getDataSource()->fullTableName($Link);
|
||||
$options['conditions'][] = "{$referenceLink} = {$referenceKey}";
|
||||
}
|
||||
elseif (isset($association['foreignKey']) && $association['foreignKey']) {
|
||||
$foreignKey = $primaryModel->escapeField($association['foreignKey'], $primaryAlias);
|
||||
$primaryKey = $foreignModel->escapeField($foreignModel->primaryKey, $foreignAlias);
|
||||
|
||||
$this->pr(17,
|
||||
array('checkpoint' => 'Linking due to foreignKey',
|
||||
compact('foreignKey', 'primaryKey'),
|
||||
));
|
||||
|
||||
// Only differentiating to help show the logical flow.
|
||||
// Either way works and this test can be tossed out
|
||||
if (($type === 'hasMany' || $type === 'hasOne') ^ $associatedThroughReference)
|
||||
$options['conditions'][] = "{$primaryKey} = {$foreignKey}";
|
||||
else
|
||||
$options['conditions'][] = "{$foreignKey} = {$primaryKey}";
|
||||
}
|
||||
else {
|
||||
$this->pr(17,
|
||||
array('checkpoint' => 'Linking with no logic (expecting user defined)',
|
||||
));
|
||||
|
||||
// No Foreign Key... nothing we can do.
|
||||
}
|
||||
|
||||
$this->pr(19,
|
||||
array('checkpoint' => 'Conditions',
|
||||
array('options[conditions]' => $options['conditions'],
|
||||
'association[conditions]' => $association['conditions'],
|
||||
),
|
||||
));
|
||||
|
||||
// The user may have specified conditions directly in the model
|
||||
// for this join. Make sure to adhere to those conditions.
|
||||
if (isset($association['conditions']) && is_array($association['conditions']))
|
||||
$options['conditions'] = array_merge($options['conditions'], $association['conditions']);
|
||||
elseif (!empty($association['conditions']))
|
||||
$options['conditions'][] = $association['conditions'];
|
||||
|
||||
$this->pr(19,
|
||||
array('checkpoint' => 'Conditions2',
|
||||
array('options[conditions]' => $options['conditions'],
|
||||
),
|
||||
));
|
||||
|
||||
if (empty($options['table'])) {
|
||||
$options['table'] = $db->fullTableName($_Model, true);
|
||||
}
|
||||
|
||||
if (!isset($options['fields']) || !is_array($options['fields']))
|
||||
$options['fields'] = $db->fields($_Model, $modelAlias);
|
||||
elseif (!empty($options['fields']))
|
||||
$options['fields'] = $db->fields($_Model, $modelAlias, $options['fields']);
|
||||
|
||||
$query['fields'] = array_merge($query['fields'], $options['fields'],
|
||||
(empty($association['fields'])
|
||||
? array() : $db->fields($_Model, $modelAlias, $association['fields'])));
|
||||
|
||||
$options[$this->_key] = am($options[$this->_key], array_diff_key($options, $optionsKeys));
|
||||
$options = array_intersect_key($options, $optionsKeys);
|
||||
if (!empty($options[$this->_key])) {
|
||||
$iterators[] = $options[$this->_key] +
|
||||
array('defaults' =>
|
||||
array_merge($defaults,
|
||||
array('reference' =>
|
||||
array('class' => $modelClass,
|
||||
'alias' => $modelAlias))));
|
||||
}
|
||||
$query['joins'][] = array_intersect_key($options, array('type' => true, 'alias' => true, 'table' => true, 'joins' => true, 'conditions' => true));
|
||||
|
||||
$this->pr(19,
|
||||
array('checkpoint' => 'Model Join Complete',
|
||||
compact('options', 'modelClass', 'modelAlias', 'query'),
|
||||
));
|
||||
}
|
||||
++$cont;
|
||||
$notDone = isset($iterators[$cont]);
|
||||
} while ($notDone);
|
||||
}
|
||||
$this->pr(20,
|
||||
array('function' => 'Linkable::beforeFind',
|
||||
'return' => compact('query'),
|
||||
));
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
class Close extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'Ledger',
|
||||
);
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -3,6 +3,13 @@ class Contact extends AppModel {
|
||||
|
||||
var $displayField = 'display_name';
|
||||
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'display_name' => array('notempty'),
|
||||
'id_federal' => array('ssn'),
|
||||
'id_exp' => array('date')
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'ContactsMethod',
|
||||
'ContactsCustomer',
|
||||
@@ -19,14 +19,17 @@ class Customer extends AppModel {
|
||||
'conditions' => 'CurrentLease.close_date IS NULL',
|
||||
),
|
||||
'Lease',
|
||||
'StatementEntry',
|
||||
'LedgerEntry',
|
||||
'ContactsCustomer',
|
||||
|
||||
'Transaction',
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
'Contact',
|
||||
'Transaction' => array(
|
||||
'joinTable' => 'ledger_entries',
|
||||
'foreignKey' => 'customer_id',
|
||||
'associationForeignKey' => 'transaction_id',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -70,60 +73,115 @@ class Customer extends AppModel {
|
||||
return $ids;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: securityDeposits
|
||||
* function: findSecurityDeposits
|
||||
* - Returns an array of security deposit entries
|
||||
*/
|
||||
function securityDeposits($id, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
function findSecurityDeposits($id, $link = null) {
|
||||
/* pr(array('function' => 'Customer::findSecurityDeposits', */
|
||||
/* 'args' => compact('id', 'link'), */
|
||||
/* )); */
|
||||
|
||||
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$query['conditions'][] = array('StatementEntry.account_id' =>
|
||||
$this->StatementEntry->Account->securityDepositAccountID());
|
||||
$A = new Account();
|
||||
$entries = $A->findLedgerEntries
|
||||
($A->securityDepositAccountID(),
|
||||
true, array('LedgerEntry.customer_id' => $id), $link);
|
||||
|
||||
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, false, true);
|
||||
return $this->prReturn($set);
|
||||
/* pr(array('function' => 'Customer::findSecurityDeposits', */
|
||||
/* 'args' => compact('id', 'link'), */
|
||||
/* 'vars' => compact('customer'), */
|
||||
/* 'return' => compact('entries'), */
|
||||
/* )); */
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: securityDepositBalance
|
||||
* - Returns the balance of the customer security deposit(s)
|
||||
* function: findUnreconciledLedgerEntries
|
||||
* - Returns ledger entries that are not yet reconciled
|
||||
* (such as charges not paid).
|
||||
*/
|
||||
|
||||
function securityDepositBalance($id, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
function findUnreconciledLedgerEntries($id = null, $fundamental_type = null) {
|
||||
$A = new Account();
|
||||
$unreconciled = $A->findUnreconciledLedgerEntries
|
||||
($A->accountReceivableAccountID(),
|
||||
$fundamental_type,
|
||||
array('LedgerEntry.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);
|
||||
return $this->prReturn($stats['account_balance']);
|
||||
return $unreconciled;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: unreconciledCharges
|
||||
* - Returns charges have not yet been fully paid
|
||||
* function: reconcileNewLedgerEntry
|
||||
* - Returns which ledger entries a new credit/debit would
|
||||
* reconcile, and how much.
|
||||
*
|
||||
* - REVISIT <AP> 20090617
|
||||
* This should be subject to different algorithms, such
|
||||
* as apply to oldest charges first, newest first, to fees
|
||||
* before rent, etc. Until we get there, I'll hardcode
|
||||
* whatever algorithm is simplest.
|
||||
*/
|
||||
|
||||
function unreconciledCharges($id, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
function reconcileNewLedgerEntry($id, $fundamental_type, $amount) {
|
||||
$A = new Account();
|
||||
$reconciled = $A->reconcileNewLedgerEntry
|
||||
($A->accountReceivableAccountID(),
|
||||
$fundamental_type,
|
||||
$amount,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
|
||||
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, true);
|
||||
return $this->prReturn($set);
|
||||
return $reconciled;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: details
|
||||
* - Returns detail information for the customer
|
||||
*/
|
||||
|
||||
function details($id = null) {
|
||||
// Query the DB for need information.
|
||||
$customer = $this->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'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array('Customer.id' => $id),
|
||||
));
|
||||
|
||||
// Figure out the outstanding balance for this customer
|
||||
$customer['stats'] = $this->stats($id);
|
||||
|
||||
// Figure out the total security deposit for the current lease.
|
||||
$customer['deposits'] = $this->findSecurityDeposits($id);
|
||||
|
||||
return $customer;
|
||||
}
|
||||
|
||||
|
||||
@@ -203,44 +261,6 @@ class Customer extends AppModel {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: updateLeaseCount
|
||||
* - Updates the internal lease count
|
||||
*/
|
||||
|
||||
function updateLeaseCount($id) {
|
||||
$this->id = $id;
|
||||
|
||||
$lease_count =
|
||||
$this->find('count',
|
||||
array('link' => array('Lease' => array('type' => 'INNER')),
|
||||
'conditions' => array('Customer.id' => $id)));
|
||||
$current_count =
|
||||
$this->find('count',
|
||||
array('link' => array('CurrentLease' => array('type' => 'INNER')),
|
||||
'conditions' => array('Customer.id' => $id)));
|
||||
|
||||
$this->saveField('lease_count', $lease_count);
|
||||
$this->saveField('current_lease_count', $current_count);
|
||||
$this->saveField('past_lease_count', $lease_count - $current_count);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: balance
|
||||
* - Returns the balance of money owed on the lease
|
||||
*/
|
||||
|
||||
function balance($id) {
|
||||
$stats = $this->stats($id);
|
||||
return $stats['balance'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -248,62 +268,17 @@ class Customer extends AppModel {
|
||||
* - Returns summary data from the requested customer.
|
||||
*/
|
||||
|
||||
function stats($id = null, $query = null) {
|
||||
//$this->prFunctionLevel(20);
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
function stats($id = null) {
|
||||
if (!$id)
|
||||
return $this->prExit(null);
|
||||
return null;
|
||||
|
||||
$this->queryInit($query);
|
||||
$A = new Account();
|
||||
$stats = $A->stats($A->accountReceivableAccountID(), true,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
|
||||
// REVISIT <AP>: 20090725
|
||||
// We'll need to go directly to the statement entries if
|
||||
// transactions are not always associated with the customer.
|
||||
// This could happen if either we remove the customer_id
|
||||
// field from Transaction, or we allow multiple customers
|
||||
// to be part of the same transaction (essentially making
|
||||
// the Transaction.customer_id meaningless).
|
||||
|
||||
/* $stats = $this->StatementEntry->find */
|
||||
/* ('first', array */
|
||||
/* ('contain' => false, */
|
||||
/* 'fields' => $this->StatementEntry->chargeDisbursementFields(true), */
|
||||
/* 'conditions' => array('StatementEntry.customer_id' => $id), */
|
||||
/* )); */
|
||||
|
||||
$find_stats = $this->StatementEntry->find
|
||||
('first', array
|
||||
('contain' => false,
|
||||
'fields' => $this->StatementEntry->chargeDisbursementFields(true),
|
||||
'conditions' => array('StatementEntry.customer_id' => $id),
|
||||
));
|
||||
$find_stats = $find_stats[0];
|
||||
$this->pr(17, compact('find_stats'));
|
||||
|
||||
$tquery = $query;
|
||||
$tquery['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$statement_stats = $this->StatementEntry->stats(null, $tquery);
|
||||
$statement_stats['balance'] = $statement_stats['Charge']['balance'];
|
||||
$this->pr(17, compact('statement_stats'));
|
||||
|
||||
$tquery = $query;
|
||||
//$tquery['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$tquery['conditions'][] = array('Transaction.customer_id' => $id);
|
||||
$transaction_stats = $this->Transaction->stats(null, $tquery);
|
||||
$transaction_stats += $transaction_stats['StatementEntry'];
|
||||
$this->pr(17, compact('transaction_stats'));
|
||||
|
||||
$tquery = $query;
|
||||
//$tquery['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$tquery['conditions'][] = array('Transaction.customer_id' => $id);
|
||||
$ar_transaction_stats = $this->Transaction->stats(null, $tquery,
|
||||
$this->Transaction->Account->accountReceivableAccountID());
|
||||
$ar_transaction_stats += $ar_transaction_stats['LedgerEntry'];
|
||||
$this->pr(17, compact('ar_transaction_stats'));
|
||||
|
||||
//$stats = $ar_transaction_stats;
|
||||
$stats = $find_stats;
|
||||
return $this->prReturn($stats);
|
||||
// Pull to the top level and return
|
||||
$stats = $stats['Ledger'];
|
||||
return $stats;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,506 @@
|
||||
<?php
|
||||
class Lease extends AppModel {
|
||||
|
||||
var $name = 'Lease';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'number' => array('alphanumeric'),
|
||||
'lease_type_id' => array('numeric'),
|
||||
'unit_id' => array('numeric'),
|
||||
'late_schedule_id' => array('numeric'),
|
||||
'lease_date' => array('date'),
|
||||
'movein_planned_date' => array('date'),
|
||||
'movein_date' => array('date'),
|
||||
'moveout_date' => array('date'),
|
||||
'moveout_planned_date' => array('date'),
|
||||
'notice_given_date' => array('date'),
|
||||
'notice_received_date' => array('date'),
|
||||
'close_date' => array('date'),
|
||||
'deposit' => array('money'),
|
||||
'rent' => array('money'),
|
||||
'next_rent' => array('money'),
|
||||
'next_rent_date' => array('date')
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
'LeaseType',
|
||||
'Unit',
|
||||
'Customer',
|
||||
'LateSchedule',
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry',
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: accountId
|
||||
* - Returns the accountId of the given lease
|
||||
*/
|
||||
function accountId($id) {
|
||||
$A = new Account();
|
||||
return $A->invoiceAccountID();
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findAccountEntries
|
||||
* - Returns an array of ledger entries from the account of the given
|
||||
* lease.
|
||||
*/
|
||||
function findAccountEntries($id, $all = false, $cond = null, $link = null) {
|
||||
/* pr(array('function' => 'Lease::findAccountEntries', */
|
||||
/* 'args' => compact('id', 'all', 'cond', 'link'), */
|
||||
/* )); */
|
||||
|
||||
if (!isset($cond))
|
||||
$cond = array();
|
||||
$cond[] = array('LedgerEntry.lease_id' => $id);
|
||||
|
||||
$A = new Account();
|
||||
$entries = $A->findLedgerEntries($this->accountId($id),
|
||||
$all, $cond, $link);
|
||||
|
||||
/* pr(array('function' => 'Lease::findAccountEntries', */
|
||||
/* 'args' => compact('id', 'all', 'cond', 'link'), */
|
||||
/* 'vars' => compact('lease'), */
|
||||
/* 'return' => compact('entries'), */
|
||||
/* )); */
|
||||
return $entries;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findSecurityDeposits
|
||||
* - Returns an array of security deposit entries
|
||||
*/
|
||||
function findSecurityDeposits($id, $link = null) {
|
||||
/* pr(array('function' => 'Lease::findSecurityDeposits', */
|
||||
/* 'args' => compact('id', 'link'), */
|
||||
/* )); */
|
||||
|
||||
$A = new Account();
|
||||
$entries = $A->findLedgerEntries
|
||||
($A->securityDepositAccountID(),
|
||||
true, array('LedgerEntry.lease_id' => $id), $link);
|
||||
|
||||
/* pr(array('function' => 'Lease::findSecurityDeposits', */
|
||||
/* 'args' => compact('id', 'link'), */
|
||||
/* 'vars' => compact('lease'), */
|
||||
/* 'return' => compact('entries'), */
|
||||
/* )); */
|
||||
return $entries;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findUnreconciledLedgerEntries
|
||||
* - Returns ledger entries that are not yet reconciled
|
||||
* (such as charges not paid).
|
||||
*/
|
||||
|
||||
function findUnreconciledLedgerEntries($id = null, $fundamental_type = null) {
|
||||
$A = new Account();
|
||||
return $A->findUnreconciledLedgerEntries
|
||||
($this->accountId($id), $fundamental_type, array('LedgerEntry.lease_id' => $id));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reconcileNewLedgerEntry
|
||||
* - Returns which ledger entries a new credit/debit would
|
||||
* reconcile, and how much.
|
||||
*
|
||||
* - REVISIT <AP> 20090617
|
||||
* This should be subject to different algorithms, such
|
||||
* as apply to oldest charges first, newest first, to fees
|
||||
* before rent, etc. Until we get there, I'll hardcode
|
||||
* whatever algorithm is simplest.
|
||||
*/
|
||||
|
||||
function reconcileNewLedgerEntry($id, $fundamental_type, $amount) {
|
||||
$A = new Account();
|
||||
return $A->reconcileNewLedgerEntry
|
||||
($this->accountId($id), $fundamental_type, $amount, array('LedgerEntry.lease_id' => $id));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: rentLastCharges
|
||||
* - Returns a list of rent charges from this lease that
|
||||
* do not have sequential followup charges. Under normal
|
||||
* circumstances, there would only be one entry, which is
|
||||
* the most recent rent charge. However, it's possible
|
||||
* that there are several, indicating a problem with lease.
|
||||
*/
|
||||
|
||||
function rentLastCharges($id) {
|
||||
$A = new Account();
|
||||
$entries = $this->find
|
||||
('all',
|
||||
array('link' =>
|
||||
array(// Models
|
||||
'LedgerEntry' => array
|
||||
('Ledger' => array
|
||||
('fields' => array(),
|
||||
'Account' => array
|
||||
('fields' => array(),
|
||||
'Ledger' => array
|
||||
('alias' => 'Lx',
|
||||
'fields' => array(),
|
||||
'LedgerEntry' => array
|
||||
('alias' => 'LEx',
|
||||
'fields' => array(),
|
||||
'conditions' => array
|
||||
('LEx.effective_date = DATE_ADD(LedgerEntry.through_date, INTERVAL 1 day)',
|
||||
'LEx.lease_id = LedgerEntry.lease_id',
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
//'fields' => array('id', 'amount', 'effective_date', 'through_date'),
|
||||
'fields' => array(),
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
array('Account.id' => $A->rentAccountID()),
|
||||
array('LEx.id' => null),
|
||||
),
|
||||
)
|
||||
);
|
||||
return $entries;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: rentChargeGaps
|
||||
* - Checks for gaps in rent charges
|
||||
*/
|
||||
|
||||
function rentChargeGaps($id) {
|
||||
$entries = $this->rentLastCharges($id);
|
||||
if ($entries && count($entries) > 1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: rentChargeThrough
|
||||
* - Determines the date that rent has been charged through
|
||||
* Returns one of:
|
||||
* null: There are gaps in the charges
|
||||
* false: There are not yet any charges
|
||||
* date: The date rent has been charged through
|
||||
*/
|
||||
|
||||
function rentChargeThrough($id) {
|
||||
$entries = $this->rentLastCharges($id);
|
||||
if (!$entries)
|
||||
return false;
|
||||
if (count($entries) != 1)
|
||||
return null;
|
||||
return $entries[0]['LedgerEntry']['through_date'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: rentPaidThrough
|
||||
* - Determines the date of the first unpaid rent
|
||||
*/
|
||||
|
||||
function rentPaidThrough($id) {
|
||||
|
||||
// Income / Receipt / Money
|
||||
// debit: A/R credit: Income <-- this entry
|
||||
// debit: Receipt credit: A/R <-- ReceiptLedgerEntry, below
|
||||
// debit: Money credit: Receipt <-- MoneyLedgerEntry, below
|
||||
|
||||
$query = array
|
||||
('link' => array
|
||||
(
|
||||
'CreditLedger' =>
|
||||
array('fields' => array(),
|
||||
'Account' =>
|
||||
array('fields' => array(),
|
||||
),
|
||||
),
|
||||
|
||||
// We're searching for the Receipt<->A/R entries,
|
||||
// which are debits on the A/R account. Find the
|
||||
// reconciling entries to that A/R debit.
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
array('alias' => 'ReceiptLedgerEntry',
|
||||
'fields' => array(),
|
||||
|
||||
// Finally, the Money (Cash/Check/etc) Entry is the one
|
||||
// which reconciles our ReceiptLedgerEntry debit
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
array('alias' => 'MoneyLedgerEntry',
|
||||
'linkalias' => 'MoneyLedgerEntryR',
|
||||
'fields' => array('SUM(COALESCE(MoneyLedgerEntryR.amount,0)) AS paid'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
'fields' => array('LedgerEntry.amount',
|
||||
'DATE_SUB(LedgerEntry.effective_date, INTERVAL 1 DAY) AS paid_through',
|
||||
),
|
||||
|
||||
'group' => 'LedgerEntry.id HAVING paid <> LedgerEntry.amount',
|
||||
|
||||
'conditions' => array(array('LedgerEntry.lease_id' => $id),
|
||||
array('Account.id' => $this->LedgerEntry->Ledger->Account->rentAccountID()),
|
||||
),
|
||||
'order' => array('LedgerEntry.effective_date',
|
||||
),
|
||||
);
|
||||
|
||||
$rent = $this->LedgerEntry->find('first', $query);
|
||||
if ($rent)
|
||||
return $rent[0]['paid_through'];
|
||||
|
||||
$query['fields'] = 'LedgerEntry.through_date';
|
||||
$query['order'] = 'LedgerEntry.through_date DESC';
|
||||
$query['group'] = 'LedgerEntry.id';
|
||||
$rent = $this->LedgerEntry->find('first', $query);
|
||||
if ($rent)
|
||||
return $rent['LedgerEntry']['through_date'];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: moveIn
|
||||
* - Moves the specified customer into the specified lease
|
||||
*/
|
||||
|
||||
function moveIn($customer_id, $unit_id,
|
||||
$deposit = null, $rent = null,
|
||||
$stamp = null, $comment = null) {
|
||||
|
||||
$lt = $this->LeaseType->find('first',
|
||||
array('conditions' =>
|
||||
array('code' => 'SL')));
|
||||
|
||||
// Use NOW if not given a movein date
|
||||
if (!isset($stamp))
|
||||
$stamp = date('Y-m-d G:i:s');
|
||||
|
||||
if (!$comment)
|
||||
$comment = null;
|
||||
|
||||
if (!isset($deposit) || !isset($rent)) {
|
||||
$rates = $this->Unit->find
|
||||
('first',
|
||||
array('contain' =>
|
||||
array('UnitSize' =>
|
||||
array('deposit', 'rent'),
|
||||
),
|
||||
'fields' => array('deposit', 'rent'),
|
||||
'conditions' => array('Unit.id' => $unit_id),
|
||||
));
|
||||
|
||||
$deposit =
|
||||
(isset($deposit)
|
||||
? $deposit
|
||||
: (isset($rates['Unit']['deposit'])
|
||||
? $rates['Unit']['deposit']
|
||||
: (isset($rates['UnitSize']['deposit'])
|
||||
? $rates['UnitSize']['deposit']
|
||||
: 0)));
|
||||
|
||||
$rent =
|
||||
(isset($rent)
|
||||
? $rent
|
||||
: (isset($rates['Unit']['rent'])
|
||||
? $rates['Unit']['rent']
|
||||
: (isset($rates['UnitSize']['rent'])
|
||||
? $rates['UnitSize']['rent']
|
||||
: 0)));
|
||||
}
|
||||
|
||||
|
||||
// Save this new lease.
|
||||
$this->create();
|
||||
if (!$this->save(array('lease_type_id' => $lt['LeaseType']['id'],
|
||||
'unit_id' => $unit_id,
|
||||
'customer_id' => $customer_id,
|
||||
'lease_date' => $stamp,
|
||||
'movein_date' => $stamp,
|
||||
'deposit' => $deposit,
|
||||
'rent' => $rent,
|
||||
'comment' => $comment), false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set the lease number to be the same as the lease ID
|
||||
$this->id;
|
||||
$this->saveField('number', $this->id);
|
||||
|
||||
// Update the unit status
|
||||
$this->Unit->updateStatus($unit_id, 'OCCUPIED');
|
||||
|
||||
// REVISIT <AP>: 20090702
|
||||
// We need to assess the security deposit charge,
|
||||
// and probably rent as well. Rent, however, will
|
||||
// require user parameters to indicate whether it
|
||||
// was waived, pro-rated, etc.
|
||||
|
||||
// Return the new lease ID
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: moveOut
|
||||
* - Moves the customer out of the specified lease
|
||||
*/
|
||||
|
||||
function moveOut($id, $status = 'VACANT',
|
||||
$stamp = null, $close = false) {
|
||||
// Use NOW if not given a moveout date
|
||||
if (!isset($stamp))
|
||||
$stamp = date('Y-m-d G:i:s');
|
||||
|
||||
// Reset the data
|
||||
$this->create();
|
||||
$this->id = $id;
|
||||
|
||||
// Set the customer move-out date
|
||||
$this->data['Lease']['moveout_date'] = $stamp;
|
||||
|
||||
// Save it!
|
||||
$this->save($this->data, false);
|
||||
|
||||
// Close the lease, if so requested
|
||||
if ($close)
|
||||
$this->close($id, $stamp);
|
||||
|
||||
// Finally, update the unit status
|
||||
$this->recursive = -1;
|
||||
$this->read();
|
||||
$this->Unit->updateStatus($this->data['Lease']['unit_id'], $status);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: close
|
||||
* - Closes the lease to further action
|
||||
*/
|
||||
|
||||
function close($id, $stamp = null) {
|
||||
if (!$this->closeable($id))
|
||||
return false;
|
||||
|
||||
// Reset the data
|
||||
$this->create();
|
||||
$this->id = $id;
|
||||
|
||||
// Use NOW if not given a moveout date
|
||||
if (!isset($stamp))
|
||||
$stamp = date('Y-m-d G:i:s');
|
||||
|
||||
// Set the close date
|
||||
$this->data['Lease']['close_date'] = $stamp;
|
||||
|
||||
// Save it!
|
||||
$this->save($this->data, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: closeable
|
||||
* - Indicates whether or not the lease can be closed
|
||||
*/
|
||||
|
||||
function closeable($id) {
|
||||
$this->recursive = -1;
|
||||
$this->read(null, $id);
|
||||
|
||||
// We can't close a lease that's still in use
|
||||
if (!isset($this->data['Lease']['moveout_date']))
|
||||
return false;
|
||||
|
||||
// We can't close a lease that's already closed
|
||||
if (isset($this->data['Lease']['close_date']))
|
||||
return false;
|
||||
|
||||
$deposits = $this->findSecurityDeposits($id);
|
||||
$stats = $this->stats($id);
|
||||
|
||||
// A lease can only be closed if there are no outstanding
|
||||
// security deposits, and if the account balance is zero.
|
||||
if ($deposits['summary']['balance'] != 0 || $stats['balance'] != 0)
|
||||
return false;
|
||||
|
||||
// Apparently this lease meets all the criteria!
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addCharge
|
||||
* - Adds an additional charge to the lease
|
||||
*/
|
||||
|
||||
function addCharge($id, $charge) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: stats
|
||||
* - Returns summary data from the requested lease.
|
||||
*/
|
||||
|
||||
function stats($id = null) {
|
||||
if (!$id)
|
||||
return null;
|
||||
|
||||
$A = new Account();
|
||||
$stats = $A->stats($A->accountReceivableAccountID(), true,
|
||||
array('LedgerEntry.lease_id' => $id));
|
||||
|
||||
// Pull to the top level and return
|
||||
$stats = $stats['Ledger'];
|
||||
return $stats;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
class Ledger extends AppModel {
|
||||
|
||||
var $name = 'Ledger';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'name' => array('notempty'),
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
'Account',
|
||||
'PriorLedger' => array('className' => 'Ledger'),
|
||||
'Close',
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry' => array(
|
||||
'foreignKey' => false,
|
||||
|
||||
// conditions will be used when JOINing tables
|
||||
// (such as find with LinkableBehavior)
|
||||
'conditions' => array('OR' =>
|
||||
array('LedgerEntry.debit_ledger_id = %{MODEL_ALIAS}.id',
|
||||
'LedgerEntry.credit_ledger_id = %{MODEL_ALIAS}.id')),
|
||||
|
||||
// finderQuery will be used when tables are put
|
||||
// together across several querys, not with JOIN.
|
||||
// (such as find with ContainableBehavior)
|
||||
'finderQuery' => 'SELECT `LedgerEntry`.*
|
||||
FROM pmgr_ledger_entries AS `LedgerEntry`
|
||||
WHERE LedgerEntry.debit_ledger_id = ({$__cakeID__$})
|
||||
OR LedgerEntry.credit_ledger_id = ({$__cakeID__$})',
|
||||
|
||||
'counterQuery' => ''
|
||||
),
|
||||
'DebitLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'foreignKey' => 'debit_ledger_id',
|
||||
'dependent' => false,
|
||||
),
|
||||
'CreditLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'foreignKey' => 'credit_ledger_id',
|
||||
'dependent' => false,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: accountID
|
||||
* - Returns the account ID for the given ledger
|
||||
*/
|
||||
function accountID($id) {
|
||||
$this->cacheQueries = true;
|
||||
$item = $this->find('first', array
|
||||
('contain' => 'Account.id',
|
||||
'conditions' => array('Ledger.id' => $id),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
return $item['Account']['id'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: currentLedgerID
|
||||
* - Returns the current ledger ID of the account for the given ledger.
|
||||
*/
|
||||
function currentLedgerID($id) {
|
||||
return $this->Account->currentLedgerID($this->accountID($id));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: closeLedger
|
||||
* - Closes the current ledger, and returns a fresh one
|
||||
*/
|
||||
function closeLedger($id, $close_id) {
|
||||
$this->recursive = -1;
|
||||
|
||||
$stamp = date('Y-m-d G:i:s');
|
||||
$this->id = $id;
|
||||
$this->read();
|
||||
$this->data['Ledger']['close_id'] = $close_id;
|
||||
$this->save($this->data, false);
|
||||
|
||||
$stats = $this->stats($id);
|
||||
|
||||
$this->read();
|
||||
$this->data['Ledger']['id'] = null;
|
||||
$this->data['Ledger']['close_id'] = null;
|
||||
$this->data['Ledger']['prior_ledger_id'] = $id;
|
||||
$this->data['Ledger']['comment'] = null;
|
||||
++$this->data['Ledger']['sequence'];
|
||||
$this->id = null;
|
||||
$this->save($this->data, false);
|
||||
//pr($this->data);
|
||||
|
||||
if ($stats['balance'] == 0)
|
||||
return $this->id;
|
||||
|
||||
$this->read();
|
||||
$ftype = $this->Account->fundamentalType($this->data['Ledger']['account_id']);
|
||||
$otype = $this->Account->fundamentalOpposite($ftype);
|
||||
|
||||
// Create a transaction for balance transfer
|
||||
$transaction = new Transaction();
|
||||
$transaction->create();
|
||||
if (!$transaction->save(array(), false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create an entry to carry the balance forward
|
||||
$carry_entry_data = array
|
||||
($ftype.'_ledger_id' => $this->id,
|
||||
$otype.'_ledger_id' => $id,
|
||||
'transaction_id' => $transaction->id,
|
||||
'amount' => $stats['balance'],
|
||||
'comment' => "Ledger Balance Forward",
|
||||
);
|
||||
|
||||
$carry_entry = new LedgerEntry();
|
||||
$carry_entry->create();
|
||||
if (!$carry_entry->save($carry_entry_data, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findLedgerEntries
|
||||
* - Returns an array of ledger entries that belong to a given
|
||||
* ledger. There is extra work done... see the LedgerEntry model.
|
||||
*/
|
||||
function findLedgerEntries($id, $account_type = null, $cond = null, $link = null) {
|
||||
/* pr(array('function' => 'Ledger::findLedgerEntries', */
|
||||
/* 'args' => compact('id', 'account_type', 'cond', 'link'), */
|
||||
/* )); */
|
||||
|
||||
if (!isset($account_type)) {
|
||||
$ledger = $this->find('first', array
|
||||
('contain' => array
|
||||
('Account' => array
|
||||
('fields' => array('type'),
|
||||
),
|
||||
),
|
||||
'fields' => array(),
|
||||
'conditions' => array(array('Ledger.id' => $id)),
|
||||
));
|
||||
$account_type = $ledger['Account']['type'];
|
||||
}
|
||||
|
||||
// If the requested entries are limited by date, we must calculate
|
||||
// a balance forward, or the resulting balance will be thrown off.
|
||||
//
|
||||
// REVISIT <AP>: This obviously is more general than date.
|
||||
// As such, it will not work (or, only work if the
|
||||
// condition only manages to exclude the first parts
|
||||
// of the ledger, nothing in the middle or at the
|
||||
// end. For now, I'll just create an 'other' entry,
|
||||
// not necessarily a balance forward.
|
||||
|
||||
$bf = array();
|
||||
if (0 && isset($cond)) {
|
||||
//$date = '<NOT IMPLEMENTED>';
|
||||
$stats = $this->stats($id, array('NOT' => array($cond)));
|
||||
$bf = array(array(array('debit' => $stats['debits'],
|
||||
'credit' => $stats['credits'],
|
||||
'balance' => $stats['balance']),
|
||||
|
||||
'LedgerEntry' => array('id' => null,
|
||||
//'comment' => "Balance Forward from $date"),
|
||||
'comment' => "-- SUMMARY OF EXCLUDED ENTRIES --"),
|
||||
|
||||
'Transaction' => array('id' => null,
|
||||
//'stamp' => $date,
|
||||
'stamp' => null,
|
||||
'comment' => null),
|
||||
));
|
||||
}
|
||||
|
||||
$entries = $this->LedgerEntry->findInLedgerContext($id, $account_type, $cond, $link);
|
||||
/* pr(array('function' => 'Ledger::findLedgerEntries', */
|
||||
/* 'args' => compact('id', 'account_type', 'cond', 'link'), */
|
||||
/* 'vars' => compact('ledger'), */
|
||||
/* 'return' => compact('entries'), */
|
||||
/* )); */
|
||||
return $entries;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: stats
|
||||
* - Returns summary data from the requested ledger.
|
||||
*/
|
||||
function stats($id, $cond = null) {
|
||||
if (!isset($cond))
|
||||
$cond = array();
|
||||
$cond[] = array('Ledger.id' => $id);
|
||||
|
||||
$stats = $this->find
|
||||
('first', array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'Account' => array('fields' => array()),
|
||||
//'LedgerEntry' => array('fields' => array()),
|
||||
'LedgerEntry' =>
|
||||
array('fields' => array(),
|
||||
'Transaction' => array('fields' => array('stamp')),
|
||||
),
|
||||
),
|
||||
'fields' =>
|
||||
array("SUM(IF(LedgerEntry.debit_ledger_id = Ledger.id,
|
||||
LedgerEntry.amount, NULL)) AS debits",
|
||||
"SUM(IF(LedgerEntry.credit_ledger_id = Ledger.id,
|
||||
LedgerEntry.amount, NULL)) AS credits",
|
||||
"SUM(IF(Account.type IN ('ASSET', 'EXPENSE'),
|
||||
IF(LedgerEntry.debit_ledger_id = Ledger.id, 1, -1),
|
||||
IF(LedgerEntry.credit_ledger_id = Ledger.id, 1, -1)
|
||||
) * IF(LedgerEntry.amount, LedgerEntry.amount, 0)
|
||||
) AS balance",
|
||||
"COUNT(LedgerEntry.id) AS entries"),
|
||||
'conditions' => $cond,
|
||||
'group' => 'Ledger.id',
|
||||
));
|
||||
|
||||
// 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;
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,525 @@
|
||||
<?php
|
||||
class LedgerEntry extends AppModel {
|
||||
|
||||
var $name = 'LedgerEntry';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'transaction_id' => array('numeric'),
|
||||
'amount' => array('money')
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'DebitReconciliation' => array(
|
||||
'className' => 'Reconciliation',
|
||||
'foreignKey' => 'debit_ledger_entry_id',
|
||||
),
|
||||
'CreditReconciliation' => array(
|
||||
'className' => 'Reconciliation',
|
||||
'foreignKey' => 'credit_ledger_entry_id',
|
||||
),
|
||||
);
|
||||
var $belongsTo = array(
|
||||
'MonetarySource',
|
||||
'Transaction',
|
||||
'Customer',
|
||||
'Lease',
|
||||
|
||||
'DebitLedger' => array(
|
||||
'className' => 'Ledger',
|
||||
'foreignKey' => 'debit_ledger_id',
|
||||
),
|
||||
'CreditLedger' => array(
|
||||
'className' => 'Ledger',
|
||||
'foreignKey' => 'credit_ledger_id',
|
||||
),
|
||||
|
||||
'Ledger' => array(
|
||||
'foreignKey' => false,
|
||||
// conditions will be used when JOINing tables
|
||||
// (such as find with LinkableBehavior)
|
||||
'conditions' => array('OR' =>
|
||||
array('%{MODEL_ALIAS}.debit_ledger_id = Ledger.id',
|
||||
'%{MODEL_ALIAS}.credit_ledger_id = Ledger.id')),
|
||||
|
||||
// finderQuery will be used when tables are put
|
||||
// together across several querys, not with JOIN.
|
||||
// (such as find with ContainableBehavior)
|
||||
'finderQuery' => 'NOT-IMPLEMENTED',
|
||||
|
||||
'counterQuery' => ''
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
'DebitReconciliationLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'joinTable' => 'reconciliations',
|
||||
'foreignKey' => 'credit_ledger_entry_id',
|
||||
'associationForeignKey' => 'debit_ledger_entry_id',
|
||||
),
|
||||
// STUPID CakePHP bug screws up when using Containable
|
||||
// and CLASS contains CLASS. This extra HABTM give the
|
||||
// option of multiple depths on one CLASS, since there
|
||||
// isn't an alias specification for Containable.
|
||||
'DebitReconciliationLedgerEntry2' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'joinTable' => 'reconciliations',
|
||||
'foreignKey' => 'credit_ledger_entry_id',
|
||||
'associationForeignKey' => 'debit_ledger_entry_id',
|
||||
),
|
||||
'CreditReconciliationLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'joinTable' => 'reconciliations',
|
||||
'foreignKey' => 'debit_ledger_entry_id',
|
||||
'associationForeignKey' => 'credit_ledger_entry_id',
|
||||
),
|
||||
);
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: conditionEntryAsCreditOrDebit
|
||||
* - returns the condition necessary to match a set of
|
||||
* Ledgers to all related LedgerEntries
|
||||
*/
|
||||
function conditionEntryAsCreditOrDebit($ledger_ids) {
|
||||
return array('OR' =>
|
||||
array(array('debit_ledger_id' => $ledger_ids),
|
||||
array('credit_ledger_id' => $ledger_ids)));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: ledgerContext query helpers
|
||||
* - Returns parameters necessary to generate a query which
|
||||
* puts ledger entries into the context of a ledger. Since
|
||||
* debit/credit depends on the account type, it is required
|
||||
* as an argument for each function to avoid having to
|
||||
* query the ledger/account to find it out.
|
||||
*/
|
||||
function ledgerContextFields($ledger_id = null, $account_type = null) {
|
||||
$fields = array('id', 'effective_date', 'through_date',
|
||||
'lease_id', 'customer_id', 'comment', 'amount');
|
||||
|
||||
if (isset($ledger_id)) {
|
||||
$fields[] = ("IF(LedgerEntry.debit_ledger_id = $ledger_id," .
|
||||
" LedgerEntry.amount, NULL) AS debit");
|
||||
$fields[] = ("IF(LedgerEntry.credit_ledger_id = $ledger_id," .
|
||||
" LedgerEntry.amount, NULL) AS credit");
|
||||
|
||||
if (isset($account_type)) {
|
||||
if (in_array($account_type, array('ASSET', 'EXPENSE')))
|
||||
$ledger_type = 'debit';
|
||||
else
|
||||
$ledger_type = 'credit';
|
||||
|
||||
$fields[] = ("(IF(LedgerEntry.{$ledger_type}_ledger_id = $ledger_id," .
|
||||
" 1, -1) * LedgerEntry.amount) AS balance");
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function ledgerContextFields2($ledger_id = null, $account_id = null, $account_type = null) {
|
||||
$fields = array('id', 'effective_date', 'through_date', 'comment', 'amount');
|
||||
|
||||
if (isset($ledger_id)) {
|
||||
$fields[] = ("IF(LedgerEntry.debit_ledger_id = $ledger_id," .
|
||||
" SUM(LedgerEntry.amount), NULL) AS debit");
|
||||
$fields[] = ("IF(LedgerEntry.credit_ledger_id = $ledger_id," .
|
||||
" SUM(LedgerEntry.amount), NULL) AS credit");
|
||||
|
||||
if (isset($account_id) || isset($account_type)) {
|
||||
$Account = new Account();
|
||||
$account_ftype = $Account->fundamentalType($account_id ? $account_id : $account_type);
|
||||
$fields[] = ("(IF(LedgerEntry.{$account_ftype}_ledger_id = $ledger_id," .
|
||||
" 1, -1) * SUM(LedgerEntry.amount)) AS balance");
|
||||
}
|
||||
}
|
||||
elseif (isset($account_id)) {
|
||||
$fields[] = ("IF(DebitLedger.account_id = $account_id," .
|
||||
" SUM(LedgerEntry.amount), NULL) AS debit");
|
||||
$fields[] = ("IF(CreditLedger.account_id = $account_id," .
|
||||
" SUM(LedgerEntry.amount), NULL) AS credit");
|
||||
|
||||
$Account = new Account();
|
||||
$account_ftype = ucfirst($Account->fundamentalType($account_id));
|
||||
$fields[] = ("(IF({$account_ftype}Ledger.account_id = $account_id," .
|
||||
" 1, -1) * SUM(LedgerEntry.amount)) AS balance");
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
|
||||
function ledgerContextConditions($ledger_id, $account_type) {
|
||||
if (isset($ledger_id)) {
|
||||
return array
|
||||
('OR' =>
|
||||
array(array('LedgerEntry.debit_ledger_id' => $ledger_id),
|
||||
array('LedgerEntry.credit_ledger_id' => $ledger_id)),
|
||||
);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findInLedgerContext
|
||||
* - Returns an array of ledger entries that belong to a given ledger.
|
||||
* There is extra logic to also figure out whether the ledger_entry
|
||||
* amount is either a credit, or a debit, depending on how it was
|
||||
* written into the ledger, as well as whether the amount increases or
|
||||
* decreases the balance depending on the particular account type of
|
||||
* the ledger.
|
||||
*/
|
||||
function findInLedgerContext($ledger_id, $account_type, $cond = null, $link = null) {
|
||||
if (!isset($link))
|
||||
$link = array('Transaction');
|
||||
|
||||
if (!isset($cond))
|
||||
$cond = array();
|
||||
|
||||
$fields = $this->ledgerContextFields($ledger_id, $account_type);
|
||||
$cond[] = $this->ledgerContextConditions($ledger_id, $account_type);
|
||||
$order = array('Transaction.stamp');
|
||||
|
||||
$entries = $this->find
|
||||
('all',
|
||||
array('link' => $link,
|
||||
'fields' => $fields,
|
||||
'conditions' => $cond,
|
||||
'order' => $order,
|
||||
));
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findReconciledLedgerEntries
|
||||
* - Returns ledger entries that are reconciled to the given entry.
|
||||
* (such as payments towards a charge).
|
||||
*/
|
||||
|
||||
function findReconciledLedgerEntries($id = null, $fundamental_type = null) {
|
||||
foreach (($fundamental_type
|
||||
? array($fundamental_type)
|
||||
: array('debit', 'credit')) AS $fund) {
|
||||
$ucfund = ucfirst($fund);
|
||||
$reconciled[$fund]['entry'] = $this->find
|
||||
('all', array
|
||||
('link' => array
|
||||
("ReconciliationLedgerEntry" => array
|
||||
('class' => "{$ucfund}ReconciliationLedgerEntry",
|
||||
'fields' => array
|
||||
('id',
|
||||
"COALESCE(SUM(Reconciliation.amount),0) AS 'reconciled'",
|
||||
"LedgerEntry.amount - COALESCE(SUM(Reconciliation.amount),0) AS 'balance'",
|
||||
),
|
||||
),
|
||||
),
|
||||
'group' => ("ReconciliationLedgerEntry.id"),
|
||||
'conditions' => array('LedgerEntry.id' => $id),
|
||||
'fields' => array(),
|
||||
));
|
||||
//pr($reconciled);
|
||||
$balance = 0;
|
||||
foreach ($reconciled[$fund]['entry'] AS &$entry) {
|
||||
$entry = array_merge($entry["ReconciliationLedgerEntry"], $entry[0]);
|
||||
$balance += $entry['balance'];
|
||||
}
|
||||
$reconciled[$fund]['balance'] = $balance;
|
||||
}
|
||||
|
||||
return $reconciled;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reverse
|
||||
* - Reverses the ledger entry
|
||||
*/
|
||||
|
||||
function reverse1($id, $amount = null, $transaction_id = null, $rec_id = null) {
|
||||
/* pr(array('LedgerEntry::reverse', */
|
||||
/* compact('id', 'amount', 'transaction_id', 'rec_id'))); */
|
||||
|
||||
// Get the LedgerEntry and related fields
|
||||
$entry = $this->find
|
||||
('first',
|
||||
array('contain' => array('MonetarySource.id',
|
||||
'Transaction.id',
|
||||
'DebitLedger.id',
|
||||
'DebitLedger.account_id',
|
||||
'CreditLedger.id',
|
||||
'CreditLedger.account_id',
|
||||
'DebitReconciliationLedgerEntry'
|
||||
/* => */
|
||||
/* array('DebitLedger.id', */
|
||||
/* 'DebitLedger.account_id', */
|
||||
/* 'CreditLedger.id', */
|
||||
/* 'CreditLedger.account_id', */
|
||||
/* ) */
|
||||
,
|
||||
'CreditReconciliationLedgerEntry'
|
||||
/* => */
|
||||
/* array('DebitLedger.id', */
|
||||
/* 'DebitLedger.account_id', */
|
||||
/* 'CreditLedger.id', */
|
||||
/* 'CreditLedger.account_id', */
|
||||
/* ) */
|
||||
,
|
||||
'Customer.id',
|
||||
'Lease.id',
|
||||
),
|
||||
|
||||
'fields' => array('LedgerEntry.*'),
|
||||
|
||||
'conditions' => array(array('LedgerEntry.id' => $id),
|
||||
/* array('NOT' => */
|
||||
/* array('OR' => */
|
||||
/* array(array('DebitReconciliationLedgerEntry.id' => $rec_id), */
|
||||
/* array('CreditReconciliationLedgerEntry.id' => $rec_id), */
|
||||
/* ), */
|
||||
/* ), */
|
||||
/* ), */
|
||||
),
|
||||
));
|
||||
//pr($entry);
|
||||
|
||||
if (!isset($amount))
|
||||
$amount = $entry['LedgerEntry']['amount'];
|
||||
|
||||
$A = new Account();
|
||||
|
||||
$ids = $this->Ledger->Account->postLedgerEntry
|
||||
(array('transaction_id' => $transaction_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($entry['CreditLedger']['account_id']),
|
||||
'credit_ledger_id' => $A->currentLedgerID($entry['DebitLedger']['account_id']),
|
||||
'effective_date' => $entry['LedgerEntry']['effective_date'],
|
||||
//'effective_date' => $entry['LedgerEntry']['effective_date'],
|
||||
'amount' => $amount,
|
||||
'lease_id' => $entry['Lease']['id'],
|
||||
'customer_id' => $entry['Customer']['id'],
|
||||
'comment' => "Reversal of Ledger Entry #{$id}",
|
||||
),
|
||||
array('debit' => array(array('LedgerEntry' => array('id' => $entry['LedgerEntry']['id'],
|
||||
'amount' => $amount,
|
||||
))),
|
||||
'credit' => array(array('LedgerEntry' => array('id' => $entry['LedgerEntry']['id'],
|
||||
'amount' => $amount,
|
||||
))),
|
||||
));
|
||||
|
||||
if ($ids['error'])
|
||||
return null;
|
||||
|
||||
$tid = $ids['transaction_id'];
|
||||
|
||||
pr(compact('entry'));
|
||||
|
||||
foreach (array('Debit', 'Credit') AS $dc_type) {
|
||||
foreach ($entry[$dc_type . 'ReconciliationLedgerEntry'] AS $RLE) {
|
||||
pr(array('checkpoint' => "Reverse $dc_type LE",
|
||||
compact('id', 'rec_id', 'RLE')));
|
||||
if ($RLE['id'] == $rec_id) {
|
||||
pr(array('checkpoint' => "Skipping Reverse $dc_type LE, due to rec_id",
|
||||
compact('id', 'RLE')));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->reverse($RLE['id'], $RLE['Reconciliation']['amount'], $tid, $id))
|
||||
$ids['error'] = true;
|
||||
|
||||
/* $rids = $this->Ledger->Account->postLedgerEntry */
|
||||
/* (array('transaction_id' => $tid), */
|
||||
/* null, */
|
||||
/* array('debit_ledger_id' => $A->currentLedgerID($RLE['CreditLedger']['account_id']), */
|
||||
/* 'credit_ledger_id' => $A->currentLedgerID($RLE['DebitLedger']['account_id']), */
|
||||
/* 'effective_date' => $RLE['effective_date'], */
|
||||
/* //'effective_date' => $RLE['effective_date'], */
|
||||
/* 'amount' => $RLE['Reconciliation']['amount'], */
|
||||
/* 'lease_id' => $entry['Lease']['id'], */
|
||||
/* 'customer_id' => $entry['Customer']['id'], */
|
||||
/* 'comment' => "Reversal of Ledger Entry #{$RLE['id']}", */
|
||||
/* ), */
|
||||
/* array('debit' => array(array('LedgerEntry' => array('id' => $RLE['id'], */
|
||||
/* 'amount' => $RLE['Reconciliation']['amount'], */
|
||||
/* ))), */
|
||||
/* 'credit' => array(array('LedgerEntry' => array('id' => $RLE['id'], */
|
||||
/* 'amount' => $RLE['Reconciliation']['amount'], */
|
||||
/* ))), */
|
||||
/* )); */
|
||||
|
||||
/* if ($rids['error']) */
|
||||
/* $ids['error'] = true; */
|
||||
}
|
||||
}
|
||||
|
||||
if ($ids['error'])
|
||||
return null;
|
||||
|
||||
return $ids['id'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reverse
|
||||
* - Reverses the charges
|
||||
*
|
||||
* SAMPLE MOVE IN w/ PRE PAYMENT
|
||||
* DEPOSIT RENT A/R RECEIPT CHECK PETTY BANK
|
||||
* ------- ------- ------- ------- ------- ------- -------
|
||||
* |25 | 25| | | | |
|
||||
* | |20 20| | | | |
|
||||
* | |20 20| | | | |
|
||||
* | |20 20| | | | |
|
||||
* | | |25 25| | | |
|
||||
* | | |20 20| | | |
|
||||
* | | |20 20| | | |
|
||||
* | | |20 20| | | |
|
||||
* | | | |85 85| | |
|
||||
* | | | | |85 | 85|
|
||||
|
||||
* MOVE OUT and REFUND FINAL MONTH
|
||||
* DEPOSIT RENT C/P RECEIPT CHECK PETTY BANK
|
||||
* ------- ------- ------- ------- ------- ------- -------
|
||||
* 25| | |25 | | | | t20 e20a
|
||||
* | 20| |20 | | | | t20 e20b
|
||||
|
||||
* -ONE REFUND CHECK-
|
||||
* | | 25| |25 | | | t30 e30a
|
||||
* | | 20| |20 | | | t30 e30b
|
||||
* | | | 45| | | |45 t40 e40
|
||||
* -OR MULTIPLE-
|
||||
* | | 15| |15 | | | t50a e50a
|
||||
* | | | 15| | |15 | t60a e60a
|
||||
* | | 30| |30 | | | t50b e50b
|
||||
* | | | 30| | | |30 t60b e60b
|
||||
* | | | | | | |
|
||||
|
||||
|
||||
OPTION 1
|
||||
* |-25 | -25| | | | |
|
||||
* | |-20 -20| | | | |
|
||||
* | | |-25 -25| | | |
|
||||
* | | |-20 -20| | | |
|
||||
|
||||
OPTION 2
|
||||
* |-25 | | -25| | | |
|
||||
* | |-20 | -20| | | |
|
||||
* | | | |-15 | -15| |
|
||||
* | | | |-30 | | -30|
|
||||
* | | | | | | |
|
||||
*
|
||||
*/
|
||||
function reverse($ledger_entries, $stamp = null) {
|
||||
pr(array('LedgerEntry::reverse',
|
||||
compact('ledger_entries', 'stamp')));
|
||||
|
||||
// If the user only wants to reverse one ID, we'll allow it
|
||||
if (!is_array($ledger_entries))
|
||||
$ledger_entries = $this->find
|
||||
('all', array
|
||||
('contain' => false,
|
||||
'conditions' => array('LedgerEntry.id' => $ledger_entries)));
|
||||
|
||||
$A = new Account();
|
||||
|
||||
$ar_account_id = $A->accountReceivableAccountID();
|
||||
$receipt_account_id = $A->receiptAccountID();
|
||||
|
||||
$transaction_id = null;
|
||||
foreach ($ledger_entries AS $entry) {
|
||||
$entry = $entry['LedgerEntry'];
|
||||
$amount = -1*$entry['amount'];
|
||||
|
||||
if (isset($entry['credit_account_id']))
|
||||
$refund_account_id = $entry['credit_account_id'];
|
||||
elseif (isset($entry['CreditLedger']['Account']['id']))
|
||||
$refund_account_id = $entry['CreditLedger']['Account']['id'];
|
||||
elseif (isset($entry['credit_ledger_id']))
|
||||
$refund_account_id = $this->Ledger->accountID($entry['credit_ledger_id']);
|
||||
else
|
||||
return null;
|
||||
|
||||
// post new refund in the income account
|
||||
$ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $transaction_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($ar_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($refund_account_id),
|
||||
'effective_date' => $entry['effective_date'],
|
||||
'through_date' => $entry['through_date'],
|
||||
'amount' => $amount,
|
||||
'lease_id' => $entry['lease_id'],
|
||||
'customer_id' => $entry['customer_id'],
|
||||
'comment' => "Refund; Entry #{$entry['id']}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $entry['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
);
|
||||
|
||||
if ($ids['error'])
|
||||
return null;
|
||||
$transaction_id = $ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Refund Ledger Entry',
|
||||
compact('ids', 'amount', 'refund_account_id', 'ar_account_id')));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: stats
|
||||
* - Returns summary data from the requested ledger entry
|
||||
*/
|
||||
function stats($id) {
|
||||
|
||||
$query = array
|
||||
(
|
||||
'fields' => array("SUM(Reconciliation.amount) AS 'reconciled'"),
|
||||
|
||||
'conditions' => array(isset($cond) ? $cond : array(),
|
||||
array('LedgerEntry.id' => $id)),
|
||||
|
||||
'group' => 'LedgerEntry.id',
|
||||
);
|
||||
|
||||
// Get the applied amounts on the debit side
|
||||
$query['link'] =
|
||||
array('DebitReconciliationLedgerEntry' => array('alias' => 'DRLE', 'DRLETransaction' => array('class' => 'Transaction')));
|
||||
$tmpstats = $this->find('first', $query);
|
||||
$stats['debit_amount_reconciled'] = $tmpstats[0]['reconciled'];
|
||||
|
||||
// Get the applied amounts on the credit side
|
||||
$query['link'] =
|
||||
array('CreditReconciliationLedgerEntry' => array('alias' => 'CRLE', 'CRLETransaction' => array('class' => 'Transaction')));
|
||||
$tmpstats = $this->find('first', $query);
|
||||
$stats['credit_amount_reconciled'] = $tmpstats[0]['reconciled'];
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
class MonetarySource extends AppModel {
|
||||
|
||||
var $name = 'MonetarySource';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'name' => array('notempty'),
|
||||
'tillable' => array('boolean')
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry',
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: nsf
|
||||
* - Flags the ledger entry as having insufficient funds
|
||||
* - NOTE: nsf only works if given the monetary source id
|
||||
* to transaction e3, below
|
||||
* - NOTE: In order to show that the rent formerly considered
|
||||
* "collected" is now recognized in reverse, we must
|
||||
* credit A/R with a negative amount in order to
|
||||
* reconcile it against the Rent<->A/R ledger entry.
|
||||
*
|
||||
* FEE RENT A/R RECEIPT CHECK NSF BANK
|
||||
* ------- ------- ------- ------- ------- ------- -------
|
||||
* | |30 30| | | | | t1 e1a : R e2/e7a :
|
||||
* | |20 20| | | | | t1 e1b : R e2/e7b :
|
||||
* | | | | | | |
|
||||
* | | |30 30| | | | t2 e2a : R e3 : R e1a
|
||||
* | | |20 20| | | | t2 e2b : R e3 : R e1b
|
||||
* | | | |50 50| | | t2 e3 : R e4 : R e2
|
||||
* | | | | | | |
|
||||
* | | | | |50 | 50| t3 e4 : : R e3
|
||||
* | | | | | | |
|
||||
* | | | | | |-50 -50| t4 e5 : : R e6
|
||||
* | | | |-50 | -50| | t5 e6 : R e5 : R e7a/e7b
|
||||
* | | |-30 -30| | | | t6 e7a : R e6 : R e1a
|
||||
* | | |-20 -20| | | | t6 e7b : R e6 : R e1b
|
||||
* |35 | 35| | | | | t6 e8
|
||||
*
|
||||
*/
|
||||
|
||||
function nsf($id, $stamp = null) {
|
||||
pr(array('MonetarySource::nsf',
|
||||
compact('id')));
|
||||
|
||||
$A = new Account();
|
||||
|
||||
// Get the LedgerEntries that use this monetary source
|
||||
$source = $this->find
|
||||
('first',
|
||||
array('contain' =>
|
||||
array(/* e3 */
|
||||
'LedgerEntry' =>
|
||||
array('Transaction.id',
|
||||
'MonetarySource.id',
|
||||
'Customer.id',
|
||||
'Lease.id',
|
||||
|
||||
/* e3 debit */
|
||||
'DebitLedger' => /* e.g. CHECK Ledger */
|
||||
array('fields' => array(),
|
||||
|
||||
'Account' => /* e.g. CHECK Account */
|
||||
array('fields' => array('id', 'name'),
|
||||
'conditions' =>
|
||||
array('Account.payable' => 1,
|
||||
'Account.type' => 'ASSET'),
|
||||
),
|
||||
),
|
||||
|
||||
/* e3 credit */
|
||||
'CreditLedger' => /* i.e. RECEIPT Ledger */
|
||||
array('fields' => array('id'),
|
||||
'Account' => /* i.e. RECEIPT Account */
|
||||
array('fields' => array('id', 'name'),
|
||||
'conditions' =>
|
||||
array('Account.id' => $A->receiptAccountID()),
|
||||
),
|
||||
),
|
||||
|
||||
/* e2 */
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
array(/* e2 credit */
|
||||
'CreditLedger' => /* i.e. A/R Ledger */
|
||||
array('fields' => array(),
|
||||
|
||||
'Account' => /* i.e. A/R Account */
|
||||
array('fields' => array(),
|
||||
'conditions' =>
|
||||
array('Account.id' => $A->accountReceivableAccountID()),
|
||||
),
|
||||
),
|
||||
|
||||
/* e1 */
|
||||
// STUPID CakePHP bug screws up CLASS contains CLASS.
|
||||
// Use the same class, but with different name.
|
||||
'DebitReconciliationLedgerEntry2',
|
||||
),
|
||||
|
||||
/* e4 */
|
||||
'CreditReconciliationLedgerEntry' =>
|
||||
array(/* e4 debit */
|
||||
'DebitLedger' => /* e.g. BANK Ledger */
|
||||
array('fields' => array('id'),
|
||||
'Account' => /* e.g. BANK Account */
|
||||
array('fields' => array('id', 'name'),
|
||||
'conditions' =>
|
||||
array('Account.depositable' => 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array(array('MonetarySource.id' => $id)),
|
||||
));
|
||||
pr($source);
|
||||
|
||||
$nsf_account_id = $A->nsfAccountID();
|
||||
$nsf_fee_account_id = $A->nsfChargeAccountID();
|
||||
$ar_account_id = $A->accountReceivableAccountID();
|
||||
$receipt_account_id = $A->receiptAccountID();
|
||||
|
||||
$t4_id = null;
|
||||
$t5_id = null;
|
||||
foreach ($source['LedgerEntry'] AS $e3) {
|
||||
// We expect only a single e4 entry
|
||||
$e4 = $e3['CreditReconciliationLedgerEntry'];
|
||||
if (count($e4) < 1)
|
||||
continue;
|
||||
if (count($e4) > 1)
|
||||
die('Too many e4 entries');
|
||||
|
||||
// Pullup e4 from the single member array
|
||||
$e4 = $e4[0];
|
||||
|
||||
// e3 amount
|
||||
$amount = -$e3['amount'];
|
||||
|
||||
// e4 account
|
||||
$bank_account_id = $e4['DebitLedger']['account_id'];
|
||||
|
||||
// post new e5
|
||||
$e5_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t4_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($bank_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($nsf_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => $amount,
|
||||
'lease_id' => $e3['lease_id'],
|
||||
'customer_id' => $e3['customer_id'],
|
||||
'comment' => "NSF Bank Reversal; Monetary Source #{$id}",
|
||||
)
|
||||
);
|
||||
|
||||
if ($e5_ids['error'])
|
||||
return null;
|
||||
$t4_id = $e5_ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e5',
|
||||
compact('e5_ids', 'amount')));
|
||||
|
||||
// post new e6... this will be our crossover point
|
||||
// from typical positive entries to negative entries.
|
||||
// Therefore, no reconciliation on this account.
|
||||
$e6_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t5_id),
|
||||
array('monetary_source_id' => $e3['monetary_source_id']),
|
||||
array('debit_ledger_id' => $A->currentLedgerID($nsf_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($receipt_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => $amount,
|
||||
'lease_id' => $e3['lease_id'],
|
||||
'customer_id' => $e3['customer_id'],
|
||||
'comment' => "NSF tracker; Monetary Source #{$id}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $e5_ids['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
);
|
||||
|
||||
if ($e6_ids['error'])
|
||||
return null;
|
||||
$t5_id = $e6_ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e6',
|
||||
compact('e6_ids', 'amount')));
|
||||
|
||||
$t6_id = null;
|
||||
foreach ($e3['DebitReconciliationLedgerEntry'] AS $e2) {
|
||||
foreach ($e2['DebitReconciliationLedgerEntry2'] AS $e1) {
|
||||
$amount = -1*$e1['Reconciliation']['amount'];
|
||||
|
||||
// post new e7
|
||||
$e7_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t6_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($receipt_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($ar_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => $amount,
|
||||
'lease_id' => $e1['lease_id'],
|
||||
'customer_id' => $e1['customer_id'],
|
||||
'comment' => "NSF Receipt; Monetary Source #{$id}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $e6_ids['id'],
|
||||
'amount' => $amount))),
|
||||
|
||||
'credit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $e1['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
);
|
||||
|
||||
if ($e7_ids['error'])
|
||||
return null;
|
||||
$t6_id = $e7_ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e7',
|
||||
compact('e7_ids', 'amount')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cheat for now
|
||||
$customer_id = $source['LedgerEntry'][0]['customer_id'];
|
||||
$lease_id = null;
|
||||
|
||||
// post new e8
|
||||
$e8_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t6_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($ar_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($nsf_fee_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => 35,
|
||||
'lease_id' => $lease_id,
|
||||
'customer_id' => $customer_id,
|
||||
'comment' => "NSF Fee; Monetary Source #{$id}",
|
||||
)
|
||||
);
|
||||
|
||||
if ($e8_ids['error'])
|
||||
return null;
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e8',
|
||||
compact('e8_ids')));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
class Reconciliation extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
'DebitLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
//'foreignKey' => 'credit_ledger_entry_id',
|
||||
),
|
||||
'CreditLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
//'foreignKey' => 'credit_ledger_entry_id',
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
class Transaction extends AppModel {
|
||||
|
||||
var $name = 'Transaction';
|
||||
|
||||
var $validate = array(
|
||||
'stamp' => array('date')
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry',
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addInvoice
|
||||
* - Adds a new invoice transaction
|
||||
*/
|
||||
|
||||
function addInvoice($data, $customer_id, $lease_id = null) {
|
||||
// Create some models for convenience
|
||||
$A = new Account();
|
||||
|
||||
//pr(compact('data', 'customer_id', 'lease_id'));
|
||||
|
||||
// Assume this will succeed
|
||||
$ret = true;
|
||||
|
||||
// Determine the total charges on the invoice
|
||||
$grand_total = 0;
|
||||
foreach ($data['LedgerEntry'] AS $entry)
|
||||
$grand_total += $entry['amount'];
|
||||
|
||||
// Go through the entered charges
|
||||
$invoice_transaction = array_intersect_key($data, array('Transaction'=>1, 'transaction_id'=>1));
|
||||
foreach ($data['LedgerEntry'] AS $entry) {
|
||||
//pr(compact('entry'));
|
||||
// Create the receipt entry, and reconcile the credit side
|
||||
// of the double-entry (which should be A/R) as a payment.
|
||||
$ids = $this->LedgerEntry->Ledger->Account->postLedgerEntry
|
||||
($invoice_transaction,
|
||||
array_intersect_key($entry, array('MonetarySource'=>1))
|
||||
+ array_intersect_key($entry, array('account_id'=>1)),
|
||||
array('debit_ledger_id' => $A->currentLedgerID($A->accountReceivableAccountID()),
|
||||
'credit_ledger_id' => $A->currentLedgerID($entry['account_id']),
|
||||
'customer_id' => $customer_id,
|
||||
'lease_id' => $lease_id)
|
||||
+ $entry
|
||||
);
|
||||
|
||||
if ($ids['error'])
|
||||
$ret = false;
|
||||
|
||||
$invoice_transaction = array_intersect_key($ids, array('transaction_id'=>1));
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addReceipt
|
||||
* - Adds a new receipt transaction
|
||||
*/
|
||||
|
||||
function addReceipt($data, $customer_id, $lease_id = null) {
|
||||
// Create some models for convenience
|
||||
$A = new Account();
|
||||
|
||||
// Assume this will succeed
|
||||
$ret = true;
|
||||
|
||||
// Go through the entered payments
|
||||
$receipt_transaction = array_intersect_key($data, array('Transaction'=>1, 'transaction_id'=>1));
|
||||
foreach ($data['LedgerEntry'] 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->LedgerEntry->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;
|
||||
|
||||
$receipt_transaction = array_intersect_key($ids,
|
||||
array('transaction_id'=>1,
|
||||
'split_transaction_id'=>1));
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -27,8 +27,6 @@ class Unit extends AppModel {
|
||||
'Lease',
|
||||
);
|
||||
|
||||
//var $default_log_level = array('log' => 30, 'show' => 15);
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -52,7 +50,7 @@ class Unit extends AppModel {
|
||||
}
|
||||
|
||||
function occupiedEnumValue() {
|
||||
return $this->statusValue('OCCUPIED');
|
||||
return statusValue('OCCUPIED');
|
||||
}
|
||||
|
||||
function conditionOccupied() {
|
||||
@@ -70,71 +68,18 @@ class Unit extends AppModel {
|
||||
return ('Unit.status <= ' . $this->statusValue('UNAVAILABLE'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: allowedStatusSet
|
||||
* - Returns the status set allowed for the given unit
|
||||
*/
|
||||
function allowedStatusSet($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
$this->id = $id;
|
||||
$old_status = $this->field('status');
|
||||
$old_val = $this->statusValue($old_status);
|
||||
$this->pr(17, compact('old_status', 'old_val'));
|
||||
|
||||
$enums = $this->activeStatusEnums();
|
||||
$this->pr(21, compact('enums'));
|
||||
|
||||
foreach ($enums AS $enum => $val) {
|
||||
if (($old_val < $this->occupiedEnumValue()) !=
|
||||
($val < $this->occupiedEnumValue())) {
|
||||
unset($enums[$enum]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->prReturn($enums);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: updateStatus
|
||||
* - Update the given unit to the given status
|
||||
*/
|
||||
function updateStatus($id, $status, $check = false) {
|
||||
$this->prEnter(compact('id', 'status', 'check'));
|
||||
|
||||
/* if ($check) { */
|
||||
/* $old_status = $this->field('status'); */
|
||||
/* $this->pr(17, compact('old_status')); */
|
||||
/* if ($this->statusValue($old_status) < $this->occupiedEnumValue() && */
|
||||
/* $this->statusValue($status) >= $this->occupiedEnumValue()) */
|
||||
/* { */
|
||||
/* die("Can't transition a unit from vacant to occupied"); */
|
||||
/* return $this->prReturn(false); */
|
||||
/* } */
|
||||
/* if ($this->statusValue($old_status) >= $this->occupiedEnumValue() && */
|
||||
/* $this->statusValue($status) < $this->occupiedEnumValue()) */
|
||||
/* { */
|
||||
/* die("Can't transition a unit from occupied to vacant"); */
|
||||
/* return $this->prReturn(false); */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
if ($check) {
|
||||
if (!array_key_exists($status, $this->allowedStatusSet($id)))
|
||||
return $this->prReturn(false);
|
||||
}
|
||||
|
||||
function updateStatus($id, $status) {
|
||||
$this->id = $id;
|
||||
//pr(compact('id', 'status'));
|
||||
$this->saveField('status', $status);
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
Vendored
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,68 +0,0 @@
|
||||
N - GATE
|
||||
N - ACH / CREDIT CARD PROCESSING
|
||||
Y - CREDIT CARD ENTRY
|
||||
Y - ACH ENTRY
|
||||
P - INVENTORY TRACKING / POS
|
||||
Y - UNIT TYPES
|
||||
Y - UNIT SIZES
|
||||
Y - UNITS
|
||||
Y - MOVE IN / OUT
|
||||
Y - UNIT TRANSFERS
|
||||
Y - LEASE TRACKING (PDF Generation)
|
||||
Y - LETTERS (PDF Generation)
|
||||
Y - REMINDERS
|
||||
Y - MULTIPLE LATE RENT SCHEDULES (Tenant A vs Tenant B)
|
||||
Y - ACCOUNTING (assign charges to accounts)
|
||||
Y - DETAILED REPORTING (HTML & PDF)
|
||||
Y - SITE MAP; HOT CLICKABLE
|
||||
P - PROSPECTIVE TENANTS
|
||||
Y - MARKETING
|
||||
P - RESERVATIONS
|
||||
P - MOVE OUT NOTICES
|
||||
P - MULTI-SITE (One database, multiple sites)
|
||||
Y - GENERATE GEOGRAPHIC MAP OF CUSTOMERS USING GOOGLE!
|
||||
- Major advantage here... MapPoint only choice with competitors
|
||||
Y - WEB BASED
|
||||
Y - CUSTOMER VIEW / MANAGER VIEW
|
||||
Y - CUSTOMERS CAN CREATE ACCOUNTS, VIEW HISTORY
|
||||
Y - CUSTOMERS CAN SIGN UP FOR AUTO PAY
|
||||
|
||||
|
||||
|
||||
----------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
Operations to be functional
|
||||
'X' marks functionality sufficiently completed
|
||||
|
||||
X - Create Customer ID/Account
|
||||
X - Add Contact information to Customer
|
||||
X - Move Customer into Unit
|
||||
? - Enter Rent Concessions given
|
||||
X - Asses Rent Charges
|
||||
X - Asses Late Charges
|
||||
X - Asses Security Deposits
|
||||
X - Receive and record Checks
|
||||
X - Receive and record Money Orders
|
||||
X - Receive and record Cash
|
||||
X - Receive and record ACH Deposits
|
||||
? - Reverse rent charges (early moveout on prepaid occupancy)
|
||||
X - Handle NSF checks
|
||||
X - Assess NSF Fees
|
||||
X - Determine Lease Paid-Through status
|
||||
- Report: List of customers overdue
|
||||
X - Flag unit as overlocked
|
||||
X - Flag unit as evicting
|
||||
X - Flag unit as normal status
|
||||
X - Flag unit as dirty
|
||||
- Enter notes when communicating with Customer
|
||||
X - Accept pre-payments
|
||||
X - Record Customer Move-Out from Unit
|
||||
X - Record utilization of Security Deposit
|
||||
X - Record issuing of a refund
|
||||
- Record Deposit into Petty Cash
|
||||
- Record Payment from Petty Cash to expenses
|
||||
X - Record Petty Cash to refund.
|
||||
X - Write Off Bad Debt
|
||||
X - Perform a Deposit
|
||||
X - Close the Books (nightly / weekly, etc)
|
||||
X - Determine Rents Collected for a given period.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,807 +0,0 @@
|
||||
<?php
|
||||
/* SVN FILE: $Id: app_controller.php 7945 2008-12-19 02:16:01Z gwoo $ */
|
||||
/**
|
||||
* Short description for file.
|
||||
*
|
||||
* This file is application-wide controller file. You can put all
|
||||
* application-wide controller-related methods here.
|
||||
*
|
||||
* PHP versions 4 and 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
|
||||
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @filesource
|
||||
* @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
|
||||
* @package cake
|
||||
* @subpackage cake.app
|
||||
* @since CakePHP(tm) v 0.2.9
|
||||
* @version $Revision: 7945 $
|
||||
* @modifiedby $LastChangedBy: gwoo $
|
||||
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
/**
|
||||
* Short description for class.
|
||||
*
|
||||
* Add your application-wide methods in the class below, your controllers
|
||||
* will inherit them.
|
||||
*
|
||||
* @package cake
|
||||
* @subpackage cake.app
|
||||
*/
|
||||
class AppController extends Controller {
|
||||
var $helpers = array('Html', 'Form', 'Javascript', 'Format', 'Time', 'Grid');
|
||||
var $components = array('DebugKit.Toolbar');
|
||||
|
||||
function sideMenuLinks() {
|
||||
return array(
|
||||
array('name' => 'Common', 'header' => true),
|
||||
array('name' => 'Site Map', 'url' => array('controller' => 'maps', 'action' => 'view', 1)),
|
||||
array('name' => 'Units', 'url' => array('controller' => 'units', 'action' => 'index')),
|
||||
array('name' => 'Leases', 'url' => array('controller' => 'leases', 'action' => 'index')),
|
||||
array('name' => 'Customers', 'url' => array('controller' => 'customers', 'action' => 'index')),
|
||||
array('name' => 'Accounts', 'url' => array('controller' => 'accounts', 'action' => 'index')),
|
||||
array('name' => 'Debug', 'header' => true),
|
||||
array('name' => 'Un-Nuke', 'url' => '#', 'htmlAttributes' =>
|
||||
array('onclick' => '$(".pr-section").show(); return false;')),
|
||||
array('name' => 'Contacts', 'url' => array('controller' => 'contacts', 'action' => 'index')),
|
||||
array('name' => 'Ledgers', 'url' => array('controller' => 'ledgers', 'action' => 'index')),
|
||||
array('name' => 'New Ledgers', 'url' => array('controller' => 'accounts', 'action' => 'newledger')),
|
||||
array('name' => 'RESET DATA', 'url' => array('controller' => 'accounts', 'action' => 'reset_data')),
|
||||
);
|
||||
}
|
||||
|
||||
function beforeRender() {
|
||||
$this->set('sidemenu', $this->sideMenuLinks());
|
||||
}
|
||||
|
||||
function reset_data() {
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
Configure::write('debug', '0');
|
||||
$script = $_SERVER['DOCUMENT_ROOT'] . '/pmgr/build.cmd';
|
||||
echo "<P>" . date('r') . "\n";
|
||||
//echo "<P>Script: $script" . "\n";
|
||||
$db = & $this->Account->getDataSource();
|
||||
$script .= ' "' . $db->config['database'] . '"';
|
||||
$script .= ' "' . $db->config['login'] . '"';
|
||||
$script .= ' "' . $db->config['password'] . '"';
|
||||
$handle = popen($script . ' 2>&1', 'r');
|
||||
//echo "<P>Handle: $handle; " . gettype($handle) . "\n";
|
||||
echo "<P><PRE>\n";
|
||||
while (($read = fread($handle, 2096))) {
|
||||
echo $read;
|
||||
}
|
||||
echo "</PRE>\n";
|
||||
pclose($handle);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* helper: gridView
|
||||
* - called by derived controllers to create an index listing
|
||||
*/
|
||||
|
||||
function gridView($title, $action = null, $element = null) {
|
||||
$this->set('title', $title);
|
||||
// The resulting page will contain a grid, which will
|
||||
// use ajax to obtain the actual data for this action
|
||||
$this->set('action', $action ? $action : $this->params['action']);
|
||||
$this->render('/elements/' . ($element ? $element : $this->params['controller']));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: gridData
|
||||
* - Fetches the actual data requested by grid as XML
|
||||
*/
|
||||
|
||||
function gridData() {
|
||||
// Grab a copy of the parameters that control this request
|
||||
$params = array();
|
||||
if (isset($this->params['url']) && is_array($this->params['url']))
|
||||
$params = $this->params['url'];
|
||||
|
||||
// Do any preliminary setup necessary
|
||||
$this->gridDataSetup($params);
|
||||
|
||||
// Get the top level model for this grid
|
||||
$model = $this->gridDataModel($params);
|
||||
|
||||
// Get the number of records prior to pagination
|
||||
$count = $this->gridDataCount($params, $model);
|
||||
|
||||
// Determine pagination configuration (and save to $params)
|
||||
$pagination = $this->gridDataPagination($params, $model, $count);
|
||||
|
||||
// Retreive the appropriate subset of data
|
||||
$records = $this->gridDataRecords($params, $model, $pagination);
|
||||
|
||||
// Post process the records
|
||||
$this->gridDataPostProcess($params, $model, $records);
|
||||
|
||||
// Output the resulting record set
|
||||
$this->gridDataOutput($params, $model, $records, $pagination);
|
||||
|
||||
// Call out to finalize everything
|
||||
$this->gridDataFinalize($params);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtual: gridData* functions
|
||||
* - These set up the context for the grid data, and will
|
||||
* need to be overridden in the derived class for anything
|
||||
* other than the most basic of grids.
|
||||
*/
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData SETUP / CLEANUP
|
||||
*/
|
||||
|
||||
function gridDataModel(&$params) {
|
||||
return $this->{$this->modelClass};
|
||||
}
|
||||
|
||||
function gridDataSetup(&$params) {
|
||||
// Assume we're debugging.
|
||||
// The actual grid request will set this to false
|
||||
$debug = true;
|
||||
|
||||
if (isset($this->passedArgs['debug']))
|
||||
$debug = $this->passedArgs['debug'];
|
||||
|
||||
$params['debug'] = $debug;
|
||||
|
||||
if ($debug) {
|
||||
ob_start();
|
||||
}
|
||||
else {
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
Configure::write('debug', '0');
|
||||
}
|
||||
|
||||
// Establish some defaults (except for serialized items)
|
||||
$params = array_merge(array('page' => 1,
|
||||
'rows' => 20),
|
||||
$params);
|
||||
|
||||
// Unserialize our complex structure of post data.
|
||||
// This SHOULD always be set, except when debugging
|
||||
if (isset($params['post']))
|
||||
$params['post'] = unserialize($params['post']);
|
||||
else
|
||||
$params['post'] = array();
|
||||
|
||||
// Unserialize our complex structure of dynamic post data
|
||||
if (isset($params['dynamic_post']))
|
||||
$params['dynamic_post'] = unserialize($params['dynamic_post']);
|
||||
else
|
||||
$params['dynamic_post'] = null;
|
||||
|
||||
// Unserialize our complex structure of dynamic post data
|
||||
if (isset($params['dynamic_post_replace']))
|
||||
$params['dynamic_post_replace'] = unserialize($params['dynamic_post_replace']);
|
||||
else
|
||||
$params['dynamic_post_replace'] = null;
|
||||
|
||||
// Merge the static and dynamic post data
|
||||
if (!empty($params['dynamic_post']))
|
||||
$params['post'] = array_merge_recursive($params['post'], $params['dynamic_post']);
|
||||
if (!empty($params['dynamic_post_replace']))
|
||||
$params['post'] = array_merge($params['post'], $params['dynamic_post_replace']);
|
||||
|
||||
// This SHOULD always be set, except when debugging
|
||||
if (!isset($params['post']['fields']))
|
||||
$params['post']['fields'] = array($this->{$this->modelClass}->alias
|
||||
.'.'.
|
||||
$this->{$this->modelClass}->primaryKey);
|
||||
|
||||
// Make sure the action parameter at least exists, and
|
||||
// promote it to the top level (since it drives the operation).
|
||||
if (isset($params['post']['action']))
|
||||
$params['action'] = $params['post']['action'];
|
||||
else
|
||||
$params['action'] = null;
|
||||
}
|
||||
|
||||
function gridDataFinalize(&$params) {
|
||||
if ($params['debug']) {
|
||||
$xml = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$xml = preg_replace("/&/", "&", $xml);
|
||||
$xml = preg_replace("/</", "<", $xml);
|
||||
$xml = preg_replace("/>/", ">", $xml);
|
||||
echo ("\n<PRE>\n$xml\n</PRE>\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData COUNTING
|
||||
*/
|
||||
|
||||
function gridDataCount(&$params, &$model) {
|
||||
// Establish the tables and conditions for counting
|
||||
$query = array_intersect_key($this->gridDataCountTableSet($params, $model),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
|
||||
// Conditions for the count
|
||||
$query['conditions'] = $this->gridDataCountConditionSet($params, $model);
|
||||
|
||||
// Grouping (which would not be typical)
|
||||
$query['group'] = $this->gridDataCountGroup($params, $model);
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
$params['count_query'] = $query;
|
||||
|
||||
// Get the number of records prior to pagination
|
||||
return $this->gridDataCountExecute($params, $model, $query);
|
||||
}
|
||||
|
||||
function gridDataCountExecute(&$params, &$model, $query) {
|
||||
return $model->find('count', $query);
|
||||
}
|
||||
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
// Same tables for counting as for retreiving
|
||||
return $this->gridDataTables($params, $model);
|
||||
}
|
||||
|
||||
function gridDataCountTableSet(&$params, &$model) {
|
||||
// Preliminary set of tables
|
||||
$query = array_intersect_key($this->gridDataCountTables($params, $model),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
|
||||
// Perform filtering based on user request: $params['post']['filter']
|
||||
return array_intersect_key($this->gridDataFilterTables($params, $model, $query),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
}
|
||||
|
||||
function gridDataCountConditions(&$params, &$model) {
|
||||
// Same conditions for counting as for retreiving
|
||||
return $this->gridDataConditions($params, $model);
|
||||
}
|
||||
|
||||
function gridDataCountConditionSet(&$params, &$model) {
|
||||
// Conditions for the count
|
||||
$conditions = $this->gridDataCountConditions($params, $model);
|
||||
|
||||
// Perform filtering based on user request: $params['post']['filter']
|
||||
return $this->gridDataFilterConditions($params, $model, $conditions);
|
||||
}
|
||||
|
||||
function gridDataCountGroup(&$params, &$model) {
|
||||
// Grouping will screw up the count, since it
|
||||
// causes the results to be split into chunks
|
||||
// based on the GROUP BY clause. We can't have
|
||||
// more than one row of data in the count query,
|
||||
// just a _single_ row with the actual count.
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData FILTERING
|
||||
*/
|
||||
|
||||
function gridDataFilterTables(&$params, &$model, $query) {
|
||||
if (isset($query['contain']))
|
||||
$link = 'contain';
|
||||
else
|
||||
$link = 'link';
|
||||
|
||||
if (empty($params['post']['filter']))
|
||||
return $query;
|
||||
|
||||
foreach ($params['post']['filter'] AS $filter => $filter_value) {
|
||||
$split = $this->gridDataFilterSplit($params, $model, $filter, $filter_value);
|
||||
|
||||
/* pr(array('AppController::gridDataFilterTable' => */
|
||||
/* array('checkpoint' => "Filter split") */
|
||||
/* + compact('split'))); */
|
||||
|
||||
$table = $this->gridDataFilterTablesTable($params, $model, $split['table']);
|
||||
if (!$table)
|
||||
continue;
|
||||
|
||||
$config = $this->gridDataFilterTablesConfig($params, $model, $split['table']);
|
||||
|
||||
/* pr(array('AppController::gridDataFilterTable' => */
|
||||
/* array('checkpoint' => "Add filter config to query") */
|
||||
/* + array('query[link]' => $query[$link]) */
|
||||
/* + compact('config'))); */
|
||||
|
||||
// If the table is already part of the query, append to it
|
||||
if ($table == $model->alias) {
|
||||
$query[$link] =
|
||||
array_merge_recursive(array_diff_key($config, array('fields'=>1)),
|
||||
$query[$link]);
|
||||
}
|
||||
elseif (isset($query[$link][$table])) {
|
||||
$query[$link][$table] =
|
||||
array_merge_recursive($config, $query[$link][$table]);
|
||||
}
|
||||
elseif (in_array($table, $query[$link])) {
|
||||
// REVISIT <AP>: 20090727
|
||||
// This presents a dilema. $config may specify fields
|
||||
// as array(), whereas the user has already indicated
|
||||
// they desire ALL fields (by the fact that table is
|
||||
// a member of the query['link'] array without anything
|
||||
// specified). However, $config _could_ specify a
|
||||
// non-standard field in the array, such as using SUM()
|
||||
// or other equation. In that case, we'd have to add
|
||||
// in all the fields of table to the array. That
|
||||
// wouldn't be very hard, but I'll ignore it at the
|
||||
// moment and wait until it's needed.
|
||||
$key = array_search($table, $query[$link]);
|
||||
$query[$link][$table] = array('fields' => null) + $config;
|
||||
unset($query[$link][$key]);
|
||||
}
|
||||
else {
|
||||
// Table is NOT already part of the query... set it now
|
||||
$query[$link][$table] = $config;
|
||||
}
|
||||
|
||||
/* pr(array('post-filter-table-config' => */
|
||||
/* array('query[link]' => $query[$link]))); */
|
||||
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
function gridDataFilterTablesTable(&$params, &$model, $table) {
|
||||
// By default, don't add anything if the filter
|
||||
// condition occurs on the actual model table
|
||||
if ($table == $model->alias)
|
||||
return null;
|
||||
return $this->gridDataFilterTableName($params, $model, $table);
|
||||
}
|
||||
|
||||
function gridDataFilterTablesConfig(&$params, &$model, $table) {
|
||||
return array('fields' => array());
|
||||
}
|
||||
|
||||
function gridDataFilterConditions(&$params, &$model, $conditions) {
|
||||
if (empty($params['post']['filter']))
|
||||
return $conditions;
|
||||
|
||||
foreach ($params['post']['filter'] AS $filter => $filter_value) {
|
||||
$split = $this->gridDataFilterSplit($params, $model, $filter, $filter_value);
|
||||
|
||||
$table = $this->gridDataFilterConditionsTable($params, $model, $split['table']);
|
||||
$key = $this->gridDataFilterConditionsKey($params, $model, $split['table'], $split['field']);
|
||||
if (!$key)
|
||||
continue;
|
||||
|
||||
$conditions[]
|
||||
= $this->gridDataFilterConditionsStatement($params, $model, $table, $key,
|
||||
array_intersect_key
|
||||
($split,
|
||||
array('value'=>1,
|
||||
'value_present'=>1)));
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function gridDataFilterConditionsTable(&$params, &$model, $table) {
|
||||
return $this->gridDataFilterTableName($params, $model, $table);
|
||||
}
|
||||
|
||||
function gridDataFilterConditionsKey(&$params, &$model, $table, $id) {
|
||||
// REVISIT <AP>: 20090722
|
||||
// When $id is null, we could instantiate the table,
|
||||
// and use the _actual_ primary key. However, I don't
|
||||
// expect that functionality to be used, and will just
|
||||
// stick with 'id' for now.
|
||||
return $id ? $id : 'id';
|
||||
}
|
||||
|
||||
function gridDataFilterConditionsStatement(&$params, &$model, $table, $key, $value) {
|
||||
$key = (empty($table)?"":"{$table}.") . $key;
|
||||
if (isset($value['value_present']) && $value['value_present'])
|
||||
return array($key => $value['value']);
|
||||
else
|
||||
return array($key);
|
||||
}
|
||||
|
||||
function gridDataFilterSplit(&$params, &$model, $filter, $value) {
|
||||
$value_present = true;
|
||||
if (!is_string($filter)) {
|
||||
// only a filter condition was set, not filter=>value
|
||||
$filter = $value;
|
||||
$value = null;
|
||||
$value_present = false;
|
||||
}
|
||||
|
||||
$table = $field = null;
|
||||
if (preg_match("/^\w+\.\w+(\s*[!<>=]+)?$/", $filter)) {
|
||||
list($table, $field) = explode(".", $filter);
|
||||
}
|
||||
elseif (preg_match('/^[A-Z][A-Za-z]*$/', $filter)) {
|
||||
$table = $filter;
|
||||
$field = null;
|
||||
}
|
||||
elseif (preg_match('/^\w+(\s*[!<>=]+)?$/', $filter)) {
|
||||
$table = $model->alias;
|
||||
$field = $filter;
|
||||
}
|
||||
else {
|
||||
// $filter must be a function or other complicated condition
|
||||
$table = null;
|
||||
$field = $filter;
|
||||
}
|
||||
|
||||
return compact('table', 'field', 'value', 'value_present');
|
||||
}
|
||||
|
||||
function gridDataFilterTableName(&$params, &$model, $table) {
|
||||
return Inflector::camelize($table);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData PAGINATION
|
||||
*/
|
||||
|
||||
function gridDataPagination(&$params, &$model, $record_count) {
|
||||
// Retrieve, or calculate, all parameters necesssary for
|
||||
// pagination. Verify the passed in parameters are valid.
|
||||
|
||||
$limit = $params['rows'] > 0 ? $params['rows'] : 10;
|
||||
$total = ($record_count < 0) ? 0 : ceil($record_count/$limit);
|
||||
$page = ($params['page'] <= 1) ? 1 : (($params['page'] > $total) ? $total : $params['page']);
|
||||
$start = $limit * ($page - 1);
|
||||
|
||||
return compact('record_count', 'limit', 'page', 'start', 'total');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData RETREIVAL
|
||||
*/
|
||||
|
||||
function gridDataRecords(&$params, &$model, $pagination) {
|
||||
// Establish the tables for this query
|
||||
$query = array_intersect_key($this->gridDataTableSet($params, $model),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
|
||||
// Specify the fields for the query
|
||||
$query['fields'] = $this->gridDataFields($params, $model);
|
||||
|
||||
// Conditions of the query
|
||||
$query['conditions'] = $this->gridDataConditionSet($params, $model);
|
||||
|
||||
// Data record grouping
|
||||
$query['group'] = $this->gridDataGroup($params, $model);
|
||||
|
||||
// The subset of data based on pagination
|
||||
$query['limit'] = $this->gridDataLimit($params, $model, $pagination['start'], $pagination['limit']);
|
||||
|
||||
// Ordering based on user request
|
||||
$query['order'] = $this->gridDataOrder($params, $model,
|
||||
isset($params['sidx']) ? $params['sidx'] : null,
|
||||
isset($params['sord']) ? $params['sord'] : null);
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
$params['query'] = $query;
|
||||
|
||||
return $this->gridDataRecordsExecute($params, $model, $query);
|
||||
}
|
||||
|
||||
function gridDataRecordsExecute(&$params, &$model, $query) {
|
||||
return $model->find('all', $query);
|
||||
}
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
return array('link' => array());
|
||||
}
|
||||
|
||||
function gridDataTableSet(&$params, &$model) {
|
||||
// Preliminary set of tables
|
||||
$query = array_intersect_key($this->gridDataTables($params, $model),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
|
||||
// Perform filtering based on user request: $params['post']['filter']
|
||||
$query = array_intersect_key($this->gridDataFilterTables($params, $model, $query),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$searches = array();
|
||||
|
||||
if (isset($params['_search']) && $params['_search'] === 'true') {
|
||||
if (isset($params['searchOper'])) {
|
||||
$searches[] = array('op' => $params['searchOper'],
|
||||
'field' => $params['searchField'],
|
||||
'value' => $params['searchString']);
|
||||
}
|
||||
else {
|
||||
// DOH! Crappy mechanism puts toolbar search terms
|
||||
// directly into params as name/value pairs. No
|
||||
// way to know which elements of params are search
|
||||
// terms, so skipping this at the moment.
|
||||
}
|
||||
}
|
||||
elseif (isset($params['filt']) && $params['filt']) {
|
||||
$searches[] = array('op' => 'bw',
|
||||
'field' => $params['filtField'],
|
||||
'value' => $params['filtValue']);
|
||||
}
|
||||
|
||||
$ops = array('eq' => array('op' => null, 'pre' => '', 'post' => ''),
|
||||
'ne' => array('op' => '<>', 'pre' => '', 'post' => ''),
|
||||
'lt' => array('op' => '<', 'pre' => '', 'post' => ''),
|
||||
'le' => array('op' => '<=', 'pre' => '', 'post' => ''),
|
||||
'gt' => array('op' => '>', 'pre' => '', 'post' => ''),
|
||||
'ge' => array('op' => '>=', 'pre' => '', 'post' => ''),
|
||||
'bw' => array('op' => 'LIKE', 'pre' => '', 'post' => '%'),
|
||||
'ew' => array('op' => 'LIKE', 'pre' => '%', 'post' => ''),
|
||||
'cn' => array('op' => 'LIKE', 'pre' => '%', 'post' => '%'),
|
||||
);
|
||||
|
||||
$conditions = array();
|
||||
foreach ($searches AS $search) {
|
||||
$op = $ops[$search['op']];
|
||||
$field = $search['field'] . ($op['op'] ? ' '.$op['op'] : '');
|
||||
$value = $op['pre'] . $search['value']. $op['post'];
|
||||
$conditions[] = array($field => $value);
|
||||
}
|
||||
|
||||
if (isset($params['action']) && $params['action'] === 'idlist') {
|
||||
if (count($params['post']['idlist']))
|
||||
$conditions[] = array($model->alias.'.'.$model->primaryKey => $params['post']['idlist']);
|
||||
else
|
||||
$conditions[] = '0=1';
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function gridDataConditionSet(&$params, &$model) {
|
||||
// Conditions for record retrieval
|
||||
$conditions = $this->gridDataConditions($params, $model);
|
||||
|
||||
// Perform filtering based on user request: $params['post']['filter']
|
||||
return $this->gridDataFilterConditions($params, $model, $conditions);
|
||||
}
|
||||
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$db = &$model->getDataSource();
|
||||
$fields = $db->fields($model, $model->alias);
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function gridDataGroup(&$params, &$model) {
|
||||
return $model->alias.'.'.$model->primaryKey;
|
||||
}
|
||||
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
return $index ? array($index .' '. $direction) : null;
|
||||
}
|
||||
|
||||
function gridDataLimit(&$params, &$model, $start, $limit) {
|
||||
return $start . ', ' . $limit;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData POST PROCESSING
|
||||
*/
|
||||
|
||||
function gridDataPostProcess(&$params, &$model, &$records) {
|
||||
// Add grid IDs to each record
|
||||
$this->gridDataPostProcessGridIDs($params, $model, $records);
|
||||
|
||||
// Add the calculated fields (if any), to the model fields
|
||||
$this->gridDataPostProcessCalculatedFields($params, $model, $records);
|
||||
|
||||
// Perform any requested subtotaling of fields
|
||||
$this->gridDataPostProcessSubtotal($params, $model, $records);
|
||||
|
||||
// Add in any needed hyperlinks
|
||||
$this->gridDataPostProcessLinks($params, $model, $records, array());
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
//$params['records'] = $records;
|
||||
}
|
||||
|
||||
function gridDataPostProcessGridIDs(&$params, &$model, &$records) {
|
||||
$model_alias = $model->alias;
|
||||
$id = $model->primaryKey;
|
||||
|
||||
foreach ($records AS &$record) {
|
||||
$record['grid_id'] = $record[$model_alias][$id];
|
||||
}
|
||||
}
|
||||
|
||||
function gridDataPostProcessCalculatedFields(&$params, &$model, &$records) {
|
||||
$model_alias = $model->alias;
|
||||
|
||||
foreach ($records AS &$record) {
|
||||
// Add the calculated fields (if any), to the model fields
|
||||
if (isset($record[0])) {
|
||||
$record[$model_alias] += $record[0];
|
||||
unset($record[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function gridDataPostProcessSubtotal(&$params, &$model, &$records) {
|
||||
$model_alias = $model->alias;
|
||||
|
||||
// REVISIT <AP>: 20090722
|
||||
// Horrible solution to something that should be done
|
||||
// in SQL. But, it works for now, so what the heck...
|
||||
|
||||
$subtotals = array();
|
||||
foreach ($params['post']['fields'] AS $field) {
|
||||
if (preg_match('/subtotal-(.*)$/', $field, $matches))
|
||||
$subtotals[] = array('field' => $matches[1],
|
||||
'name' => $field,
|
||||
'amount' => 0);
|
||||
}
|
||||
|
||||
foreach ($records AS &$record) {
|
||||
foreach ($subtotals AS &$subtotal) {
|
||||
$field = $subtotal['field'];
|
||||
if (preg_match("/\./", $field)) {
|
||||
list($tbl, $col) = explode(".", $field);
|
||||
$record['subtotal-'.$tbl][$col] =
|
||||
($subtotal['amount'] += $record[$tbl][$col]);
|
||||
}
|
||||
else {
|
||||
$record[$model->alias]['subtotal-'.$field] =
|
||||
($subtotal['amount'] += $record[$model->alias][$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
// Don't create any links if ordered not to.
|
||||
if (isset($params['post']['nolinks']))
|
||||
return;
|
||||
|
||||
foreach ($links AS $table => $fields) {
|
||||
$special = array('controller', 'id');
|
||||
$controller = Inflector::pluralize(Inflector::underscore($table));
|
||||
$id = 'id';
|
||||
extract(array_intersect_key($fields, array_flip($special)));
|
||||
foreach ($records AS &$record) {
|
||||
if (!isset($record[$table]))
|
||||
continue;
|
||||
|
||||
foreach (array_diff_key($fields, array_flip($special)) AS $field) {
|
||||
if (!isset($record[$table][$id]) || !isset($record[$table][$field]))
|
||||
continue;
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
//$params['linkrecord'][] = compact('table', 'field', 'id', 'controller', 'record');
|
||||
$record[$table][$field] =
|
||||
'<A HREF="' .
|
||||
Router::url(array('controller' => $controller,
|
||||
'action' => 'view',
|
||||
$record[$table][$id])) .
|
||||
'">' .
|
||||
$record[$table][$field] .
|
||||
'</A>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData OUTPUT
|
||||
*/
|
||||
|
||||
function gridDataOutput(&$params, &$model, &$records, $pagination) {
|
||||
$this->gridDataOutputHeader($params, $model);
|
||||
$this->gridDataOutputXMLHeader($params, $model);
|
||||
$this->gridDataOutputRootNodeBegin($params, $model);
|
||||
$this->gridDataOutputSummary($params, $model, $pagination);
|
||||
$this->gridDataOutputRecords($params, $model, $records);
|
||||
$this->gridDataOutputRootNodeEnd($params, $model);
|
||||
}
|
||||
|
||||
function gridDataOutputHeader(&$params, &$model) {
|
||||
if (!$params['debug']) {
|
||||
header("Content-type: text/xml;charset=utf-8");
|
||||
}
|
||||
}
|
||||
|
||||
function gridDataOutputXMLHeader(&$params, &$model) {
|
||||
echo "<?xml version='1.0' encoding='utf-8'?>\n";
|
||||
}
|
||||
|
||||
function gridDataOutputRootNodeBegin(&$params, &$model) {
|
||||
echo "<rows>\n";
|
||||
}
|
||||
|
||||
function gridDataOutputRootNodeEnd(&$params, &$model) {
|
||||
echo "</rows>\n";
|
||||
}
|
||||
|
||||
function gridDataOutputSummary(&$params, &$model, $pagination) {
|
||||
echo " <params><![CDATA[\n" . print_r($params, true) . "\n]]></params>\n";
|
||||
echo " <page>{$pagination['page']}</page>\n";
|
||||
echo " <total>{$pagination['total']}</total>\n";
|
||||
echo " <records>{$pagination['record_count']}</records>\n";
|
||||
|
||||
if (isset($params['userdata'])) {
|
||||
foreach ($params['userdata'] AS $field => $value)
|
||||
echo ' <userdata name="'.$field.'">' . "{$value}</userdata>\n";
|
||||
}
|
||||
}
|
||||
|
||||
function gridDataOutputRecords(&$params, &$model, &$records) {
|
||||
$id_field = 'grid_id';
|
||||
foreach ($records AS $record) {
|
||||
$this->gridDataOutputRecord($params, $model, $record,
|
||||
$record[$id_field], $params['post']['fields']);
|
||||
}
|
||||
}
|
||||
|
||||
function gridDataOutputRecord(&$params, &$model, &$record, $id, $fields) {
|
||||
echo " <row id='$id'>\n";
|
||||
foreach ($fields AS $field) {
|
||||
$this->gridDataOutputRecordField($params, $model, $record, $field);
|
||||
}
|
||||
echo " </row>\n";
|
||||
}
|
||||
|
||||
function gridDataOutputRecordField(&$params, &$model, &$record, $field) {
|
||||
if (preg_match("/\./", $field)) {
|
||||
list($tbl, $col) = explode(".", $field);
|
||||
//pr(compact('record', 'field', 'tbl', 'col'));
|
||||
$data = $record[$tbl][$col];
|
||||
}
|
||||
else {
|
||||
$data = $record[$model->alias][$field];
|
||||
}
|
||||
$this->gridDataOutputRecordCell($params, $model, $record, $field, $data);
|
||||
}
|
||||
|
||||
function gridDataOutputRecordCell(&$params, &$model, &$record, $field, $data) {
|
||||
// be sure to put text data in CDATA
|
||||
if (preg_match("/^\d*$/", $data))
|
||||
echo " <cell>$data</cell>\n";
|
||||
else
|
||||
echo " <cell><![CDATA[$data]]></cell>\n";
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -1,479 +0,0 @@
|
||||
<?php
|
||||
/* SVN FILE: $Id: app_model.php 7945 2008-12-19 02:16:01Z gwoo $ */
|
||||
|
||||
/**
|
||||
* Application model for Cake.
|
||||
*
|
||||
* This file is application-wide model file. You can put all
|
||||
* application-wide model-related methods here.
|
||||
*
|
||||
* PHP versions 4 and 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
|
||||
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @filesource
|
||||
* @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
|
||||
* @package cake
|
||||
* @subpackage cake.app
|
||||
* @since CakePHP(tm) v 0.2.9
|
||||
* @version $Revision: 7945 $
|
||||
* @modifiedby $LastChangedBy: gwoo $
|
||||
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Application model for Cake.
|
||||
*
|
||||
* Add your application-wide methods in the class below, your models
|
||||
* will inherit them.
|
||||
*
|
||||
* @package cake
|
||||
* @subpackage cake.app
|
||||
*/
|
||||
class AppModel extends Model {
|
||||
|
||||
var $actsAs = array('Containable', 'Linkable');
|
||||
var $useNullForEmpty = true;
|
||||
var $formatDateFields = true;
|
||||
|
||||
// Default Log Level, if not specified at the function level
|
||||
var $default_log_level = 5;
|
||||
|
||||
// Class specific log levels
|
||||
var $class_log_level = array('Model' => 5);
|
||||
|
||||
// Function specific log levels
|
||||
var $function_log_level = array();
|
||||
|
||||
// Force the module to log at LEAST at this level
|
||||
var $min_log_level;
|
||||
|
||||
// Force logging of nothing higher than this level
|
||||
var $max_log_level;
|
||||
|
||||
|
||||
// REVISIT <AP>: 20090730
|
||||
// Why is this constructor crashing?
|
||||
// Clearly it's in some sort of infinite
|
||||
// loop, but it seems the correct way
|
||||
// to have a constructor call the parent...
|
||||
|
||||
/* function __construct() { */
|
||||
/* parent::__construct(); */
|
||||
/* $this->prClassLevel(5, 'Model'); */
|
||||
/* } */
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: pr
|
||||
* - Prints out debug information, if the log level allows
|
||||
*/
|
||||
|
||||
function prClassLevel($level, $class = null) {
|
||||
$trace = debug_backtrace(false);
|
||||
$caller = array_shift($trace);
|
||||
$caller = array_shift($trace);
|
||||
if (empty($class))
|
||||
$class = $caller['class'];
|
||||
$this->class_log_level[$class] = $level;
|
||||
}
|
||||
|
||||
function prFunctionLevel($level, $function = null, $class = null) {
|
||||
$trace = debug_backtrace(false);
|
||||
$caller = array_shift($trace);
|
||||
$caller = array_shift($trace);
|
||||
if (empty($class))
|
||||
$class = $caller['class'];
|
||||
if (empty($function))
|
||||
$function = $caller['function'];
|
||||
$this->function_log_level["{$class}-{$function}"] = $level;
|
||||
}
|
||||
|
||||
function _pr($level, $mixed, $checkpoint = null) {
|
||||
if (Configure::read() <= 0)
|
||||
return;
|
||||
|
||||
$log_level = $this->default_log_level;
|
||||
|
||||
$trace = debug_backtrace(false);
|
||||
|
||||
// Get rid of pr/prEnter/prReturn
|
||||
$caller = array_shift($trace);
|
||||
|
||||
// The next entry shows where pr was called from, but it
|
||||
// shows _what_ was called, which is pr/prEntry/prReturn.
|
||||
$caller = array_shift($trace);
|
||||
$file = $caller['file'];
|
||||
$line = $caller['line'];
|
||||
|
||||
// So, this caller holds the calling function name
|
||||
$caller = $trace[0];
|
||||
$function = $caller['function'];
|
||||
$class = $caller['class'];
|
||||
//$class = $this->name;
|
||||
|
||||
// Use class or function specific log level if available
|
||||
if (isset($this->class_log_level[$class]))
|
||||
$log_level = $this->class_log_level[$class];
|
||||
if (isset($this->function_log_level["{$class}-{$function}"]))
|
||||
$log_level = $this->function_log_level["{$class}-{$function}"];
|
||||
|
||||
// Establish log level minimums
|
||||
$min_log_level = $this->min_log_level;
|
||||
if (is_array($this->min_log_level)) {
|
||||
$min_show_level = $min_log_level['show'];
|
||||
$min_log_level = $min_log_level['log'];
|
||||
}
|
||||
|
||||
// Establish log level maximums
|
||||
$max_log_level = $this->max_log_level;
|
||||
if (is_array($this->max_log_level)) {
|
||||
$max_show_level = $max_log_level['show'];
|
||||
$max_log_level = $max_log_level['log'];
|
||||
}
|
||||
|
||||
// Determine the applicable log and show levels
|
||||
if (is_array($log_level)) {
|
||||
$show_level = $log_level['show'];
|
||||
$log_level = $log_level['log'];
|
||||
}
|
||||
|
||||
// Adjust log level up/down to min/max
|
||||
if (isset($min_log_level))
|
||||
$log_level = max($log_level, $min_log_level);
|
||||
if (isset($max_log_level))
|
||||
$log_level = min($log_level, $max_log_level);
|
||||
|
||||
// Adjust show level up/down to min/max
|
||||
if (isset($min_show_level))
|
||||
$show_level = max($show_level, $min_show_level);
|
||||
if (isset($max_show_level))
|
||||
$show_level = min($show_level, $max_show_level);
|
||||
|
||||
// If the level is insufficient, bail out
|
||||
if ($level > $log_level)
|
||||
return;
|
||||
|
||||
if (!empty($checkpoint)) {
|
||||
$chk = array("checkpoint" => $checkpoint);
|
||||
if (is_array($mixed))
|
||||
$mixed = $chk + $mixed;
|
||||
else
|
||||
$mixed = $chk + array($mixed);
|
||||
}
|
||||
|
||||
static $pr_unique_number = 0;
|
||||
$pr_id = 'pr-section-class-' . $class . '-print-' . (++$pr_unique_number);
|
||||
$pr_trace_id = $pr_id . '-trace';
|
||||
$pr_output_id = $pr_id . '-output';
|
||||
|
||||
$pr_entire_base_class = "pr-section";
|
||||
$pr_entire_class_class = $pr_entire_base_class . '-class-' . $class;
|
||||
$pr_entire_function_class = $pr_entire_class_class . '-function-' . $function;
|
||||
$pr_entire_class = "$pr_entire_base_class $pr_entire_class_class $pr_entire_function_class";
|
||||
$pr_header_class = "pr-caller";
|
||||
$pr_trace_class = "pr-trace";
|
||||
$pr_output_base_class = 'pr-output';
|
||||
$pr_output_class_class = $pr_output_base_class . '-class-' . $class;
|
||||
$pr_output_function_class = $pr_output_class_class . '-function-' . $function;
|
||||
$pr_output_class = "$pr_output_base_class $pr_output_class_class $pr_output_function_class";
|
||||
|
||||
echo '<DIV class="'.$pr_entire_class.'" id="'.$pr_id.'">'."\n";
|
||||
echo '<DIV class="'.$pr_header_class.'">'."\n";
|
||||
echo '<DIV class="'.$pr_trace_class.'" id="'.$pr_trace_id.'" style="display:none;">'."\n";
|
||||
echo '<HR />' . "\n";
|
||||
|
||||
// Flip trace around so the sequence flows from top to bottom
|
||||
// Then print out the entire stack trace (in hidden div)
|
||||
$trace = array_reverse($trace);
|
||||
for ($i = 0; $i < count($trace); ++$i) {
|
||||
$bline = $trace[$i]['line'];
|
||||
$bfile = $trace[$i]['file'];
|
||||
$bfile = str_replace(ROOT.DS, '', $bfile);
|
||||
$bfile = str_replace(CAKE_CORE_INCLUDE_PATH.DS, '', $bfile);
|
||||
|
||||
if ($i > 0) {
|
||||
$bfunc = $trace[$i-1]['function'];
|
||||
$bclas = $trace[$i-1]['class'];
|
||||
} else {
|
||||
$bfunc = null;
|
||||
$bclas = null;
|
||||
}
|
||||
|
||||
echo("$bfile:$bline (" . ($bclas ? "$bclas::$bfunc" : "entry point") . ")<BR>\n");
|
||||
//echo(($bclas ? "$bclas::$bfunc" : "entry point") . "; $bfile : $bline<BR>\n");
|
||||
}
|
||||
echo '</DIV>' . "\n"; // End pr_trace_class
|
||||
$file = str_replace(ROOT.DS, '', $file);
|
||||
$file = str_replace(CAKE_CORE_INCLUDE_PATH.DS, '', $file);
|
||||
|
||||
echo "<strong>$file:$line ($class::$function)</strong>" . ";\n";
|
||||
/* $log_show_level = isset($show_level) ? $show_level : '?'; */
|
||||
/* echo ' L' . $level . "({$log_level}/{$log_show_level})" . ";\n"; */
|
||||
echo ' L' . $level . ";\n";
|
||||
echo ' <A HREF="#" onclick="$' . "('#{$pr_trace_id}').slideToggle(); return false;" . '">stack</A>'.";\n";
|
||||
|
||||
echo " this ";
|
||||
echo '<A HREF="#" onclick="$' . "('#{$pr_output_id}').slideToggle(); return false;" . '">t</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('#{$pr_id}').hide(); return false;" . '">n</A>'.";\n";
|
||||
|
||||
echo " $class ";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_class_class}').slideDown(); return false;" . '">s</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_class_class}').slideUp(); return false;" . '">h</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_entire_class_class}').hide(); return false;" . '">n</A>'.";\n";
|
||||
|
||||
echo " $function ";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_function_class}').slideDown(); return false;" . '">s</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_function_class}').slideUp(); return false;" . '">h</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_entire_function_class}').hide(); return false;" . '">n</A>'.";\n";
|
||||
|
||||
echo " all ";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_base_class}').show(); return false;" . '">s</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_base_class}').hide(); return false;" . '">h</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_entire_base_class}').hide(); return false;" . '">n</A>'."\n";
|
||||
|
||||
echo '</DIV>' . "\n"; // End pr_header_class
|
||||
|
||||
if (isset($show_level) && $level > $show_level)
|
||||
$display = 'none';
|
||||
else
|
||||
$display = 'block';
|
||||
|
||||
echo '<DIV class="'.$pr_output_class.'" id="'.$pr_output_id.'" style="display:'.$display.';">'."\n";
|
||||
pr($mixed, false, false);
|
||||
echo '</DIV>' . "\n"; // End pr_output_class
|
||||
echo '</DIV>' . "\n"; // End pr_entire_class
|
||||
}
|
||||
|
||||
function pr($level, $mixed, $checkpoint = null) {
|
||||
$this->_pr($level, $mixed, $checkpoint);
|
||||
}
|
||||
|
||||
function prEnter($args, $level = 15) {
|
||||
$this->_pr($level, $args, 'Function Entry');
|
||||
}
|
||||
|
||||
function prReturn($retval, $level = 16) {
|
||||
$this->_pr($level, $retval, 'Function Return');
|
||||
return $retval;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: queryInit
|
||||
* - Initializes the query fields
|
||||
*/
|
||||
function prDump($all = false) {
|
||||
$vars = get_object_vars($this);
|
||||
foreach (array_keys($vars) AS $name) {
|
||||
if (preg_match("/^[A-Z]/", $name))
|
||||
unset($vars[$name]);
|
||||
if (preg_match("/^_/", $name) && !$all)
|
||||
unset($vars[$name]);
|
||||
}
|
||||
pr($vars);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Enum Values
|
||||
* Snippet v0.1.3
|
||||
* http://cakeforge.org/snippet/detail.php?type=snippet&id=112
|
||||
*
|
||||
* Gets the enum values for MySQL 4 and 5 to use in selectTag()
|
||||
*/
|
||||
function getEnumValues($columnName=null, $tableName=null)
|
||||
{
|
||||
if ($columnName==null) { return array(); } //no field specified
|
||||
|
||||
if (!isset($tableName)) {
|
||||
//Get the name of the table
|
||||
$db =& ConnectionManager::getDataSource($this->useDbConfig);
|
||||
$tableName = $db->fullTableName($this, false);
|
||||
}
|
||||
|
||||
//Get the values for the specified column (database and version specific, needs testing)
|
||||
$result = $this->query("SHOW COLUMNS FROM {$tableName} LIKE '{$columnName}'");
|
||||
|
||||
//figure out where in the result our Types are (this varies between mysql versions)
|
||||
$types = null;
|
||||
if ( isset( $result[0]['COLUMNS']['Type'] ) ) { //MySQL 5
|
||||
$types = $result[0]['COLUMNS']['Type']; $default = $result[0]['COLUMNS']['Default'];
|
||||
}
|
||||
elseif ( isset( $result[0][0]['Type'] ) ) { //MySQL 4
|
||||
$types = $result[0][0]['Type']; $default = $result[0][0]['Default'];
|
||||
}
|
||||
else { //types return not accounted for
|
||||
return array();
|
||||
}
|
||||
|
||||
//Get the values
|
||||
return array_flip(array_merge(array(''), // MySQL sets 0 to be the empty string
|
||||
explode("','", strtoupper(preg_replace("/(enum)\('(.+?)'\)/","\\2", $types)))
|
||||
));
|
||||
} //end getEnumValues
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: queryInit
|
||||
* - Initializes the query fields
|
||||
*/
|
||||
function queryInit(&$query, $link = true) {
|
||||
if (!isset($query))
|
||||
$query = array();
|
||||
if (!isset($query['conditions']))
|
||||
$query['conditions'] = array();
|
||||
if (!isset($query['group']))
|
||||
$query['group'] = null;
|
||||
if (!isset($query['fields']))
|
||||
$query['fields'] = null;
|
||||
if ($link && !isset($query['link']))
|
||||
$query['link'] = array();
|
||||
if (!$link && !isset($query['contain']))
|
||||
$query['contain'] = array();
|
||||
|
||||
// In case caller expects query to come back
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: nameToID
|
||||
* - Returns the ID of the named item
|
||||
*/
|
||||
function nameToID($name) {
|
||||
$this->cacheQueries = true;
|
||||
$item = $this->find('first', array
|
||||
('recursive' => -1,
|
||||
'conditions' => compact('name'),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
if ($item) {
|
||||
$item = current($item);
|
||||
return $item['id'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: statMerge
|
||||
* - Merges summary data from $b into $a
|
||||
*/
|
||||
|
||||
function statsMerge (&$a, $b) {
|
||||
if (!isset($b))
|
||||
return;
|
||||
|
||||
if (!isset($a)) {
|
||||
$a = $b;
|
||||
}
|
||||
elseif (!is_array($a) && !is_array($b)) {
|
||||
$a += $b;
|
||||
}
|
||||
elseif (is_array($a) && is_array($b)) {
|
||||
foreach (array_intersect_key($a, $b) AS $k => $v)
|
||||
{
|
||||
if (preg_match("/^sp\./", $k))
|
||||
$a[$k] .= '; ' . $b[$k];
|
||||
else
|
||||
$this->statsMerge($a[$k], $b[$k]);
|
||||
}
|
||||
$a = array_merge($a, array_diff_key($b, $a));
|
||||
}
|
||||
else {
|
||||
die ("Can't yet merge array and non-array stats");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function recursive_array_replace($find, $replace, &$data) {
|
||||
if (!isset($data))
|
||||
return;
|
||||
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $key => &$value) {
|
||||
$this->recursive_array_replace($find, $replace, $value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($replace))
|
||||
$data = preg_replace($find, $replace, $data);
|
||||
elseif (preg_match($find, $data))
|
||||
$data = null;
|
||||
}
|
||||
|
||||
function beforeSave() {
|
||||
/* pr(array('class' => $this->name, */
|
||||
/* 'alias' => $this->alias, */
|
||||
/* 'function' => 'AppModel::beforeSave')); */
|
||||
|
||||
// Replace all empty strings with NULL.
|
||||
// If a particular model doesn't like this, they'll have to
|
||||
// override the behavior, or set useNullForEmpty to false.
|
||||
if ($this->useNullForEmpty)
|
||||
$this->recursive_array_replace("/^\s*$/", null, $this->data);
|
||||
|
||||
if ($this->formatDateFields) {
|
||||
$alias = $this->alias;
|
||||
|
||||
foreach ($this->_schema AS $field => $info) {
|
||||
if ($info['type'] == 'date' || $info['type'] == 'timestamp') {
|
||||
if (isset($this->data[$alias][$field])) {
|
||||
/* pr("Fix Date for '$alias'.'$field'; current value = " . */
|
||||
/* "'{$this->data[$alias][$field]}'"); */
|
||||
if ($this->data[$alias][$field] === 'CURRENT_TIMESTAMP')
|
||||
// Seems CakePHP is broken for the default timestamp.
|
||||
// It tries to automagically set the value to CURRENT_TIMESTAMP
|
||||
// which is wholly rejected by MySQL. Just put it back to NULL
|
||||
// and let the SQL engine deal with the defaults... it's not
|
||||
// Cake's place to do this anyway :-/
|
||||
$this->data[$alias][$field] = null;
|
||||
else
|
||||
$this->data[$alias][$field] =
|
||||
$this->dateFormatBeforeSave($this->data[$alias][$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: dateFormatBeforeSave
|
||||
* - convert dates to database format
|
||||
*/
|
||||
|
||||
function dateFormatBeforeSave($dateString) {
|
||||
/* $time = ''; */
|
||||
/* if (preg_match('/(\d+(:\d+))/', $dateString, $match)) */
|
||||
/* $time = ' '.$match[1]; */
|
||||
/* $dateString = preg_replace('/(\d+(:\d+))/', '', $dateString); */
|
||||
/* return date('Y-m-d', strtotime($dateString)) . $time; */
|
||||
|
||||
if (preg_match('/:/', $dateString))
|
||||
return date('Y-m-d H:i:s', strtotime($dateString));
|
||||
else
|
||||
return date('Y-m-d', strtotime($dateString));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
class DoubleEntriesController extends AppController {
|
||||
|
||||
var $sidemenu_links = array();
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: view
|
||||
* - Displays information about a specific entry
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
|
||||
}
|
||||
|
||||
// Get the Entry and related fields
|
||||
$entry = $this->DoubleEntry->find
|
||||
('first',
|
||||
array('contain' => array('DebitEntry', 'CreditEntry'),
|
||||
'conditions' => array('DoubleEntry.id' => $id),
|
||||
));
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Double Ledger Entry #{$entry['DoubleEntry']['id']}";
|
||||
$this->set(compact('entry', 'title'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
<?php
|
||||
|
||||
class LedgerEntriesController extends AppController {
|
||||
|
||||
var $sidemenu_links = array();
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: index / current / past / all
|
||||
* - Creates a list of ledger entries
|
||||
*/
|
||||
|
||||
function index() { $this->gridView('All Ledger Entries'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$link =
|
||||
array(// Models
|
||||
'Transaction' =>
|
||||
array('fields' => array('id', 'stamp'),
|
||||
),
|
||||
|
||||
'Ledger' =>
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'Account' =>
|
||||
array('fields' => array('id', 'name', 'type'),
|
||||
),
|
||||
),
|
||||
|
||||
'Tender' =>
|
||||
array('fields' => array('id', 'name', 'nsf_transaction_id'),
|
||||
),
|
||||
|
||||
/* 'DebitEntry', */
|
||||
/* 'CreditEntry', */
|
||||
);
|
||||
|
||||
return array('link' => $link);
|
||||
}
|
||||
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
return array_merge($fields,
|
||||
$this->LedgerEntry->debitCreditFields());
|
||||
}
|
||||
|
||||
function gridDataFilterTablesTable(&$params, &$model, $table) {
|
||||
$table = $this->gridDataFilterTableName($params, $model, $table);
|
||||
// Account is already part of our standard table set.
|
||||
// Ensure we don't add it in again as part of filtering.
|
||||
if ($table == 'Account')
|
||||
return null;
|
||||
|
||||
// Customer needs to be added beneath Transaction
|
||||
if ($table == 'Customer')
|
||||
return 'Transaction';
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
function gridDataFilterTablesConfig(&$params, &$model, $table) {
|
||||
$config = parent::gridDataFilterTablesConfig($params, $model, $table);
|
||||
|
||||
// Customer is special in that its linked in by Transaction
|
||||
// Therefore, the actual table used for the join is 'Transaction',
|
||||
// not 'Customer', and so we need to specify Customer here.
|
||||
if ($table == 'Customer')
|
||||
$config = array('Customer' => $config);
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
function gridDataFilterConditionsStatement(&$params, &$model, $table, $key, $value) {
|
||||
//pr(compact('table', 'key', 'value'));
|
||||
if ($table == 'Account' && $value['value_present'] && $value['value'] === '-AR-')
|
||||
$value = $this->LedgerEntry->Ledger->Account->accountReceivableAccountID();
|
||||
return parent::gridDataFilterConditionsStatement($params, $model, $table, $key, $value);
|
||||
}
|
||||
|
||||
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
/* if ($index === 'balance') */
|
||||
/* return ($index .' '. $direction); */
|
||||
$order = parent::gridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
if ($index === 'Transaction.stamp') {
|
||||
$order[] = 'LedgerEntry.id ' . $direction;
|
||||
}
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
function gridDataPostProcessCalculatedFields(&$params, &$model, &$records) {
|
||||
parent::gridDataPostProcessCalculatedFields($params, $model, $records);
|
||||
foreach ($records AS &$record) {
|
||||
// REVISIT <AP>: 20090730
|
||||
// We really need the grid to handle this. We probably need to
|
||||
// either create a hidden column with the nsf id, or pass back
|
||||
// a list of nsf items as user data. We can then add an onload
|
||||
// function to sweep through the nsf items and format them.
|
||||
// For now... this works.
|
||||
if (!empty($record['Tender']['nsf_transaction_id']))
|
||||
$record['Tender']['name'] =
|
||||
'<SPAN class="nsf-tender">' . $record['Tender']['name'] . '</SPAN>';
|
||||
}
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['LedgerEntry'] = array('id');
|
||||
$links['Transaction'] = array('id');
|
||||
$links['Ledger'] = array('id');
|
||||
$links['Account'] = array('controller' => 'accounts', 'name');
|
||||
$links['Tender'] = array('name');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: view
|
||||
* - Displays information about a specific entry
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
|
||||
}
|
||||
|
||||
// Get the Entry and related fields
|
||||
$entry = $this->LedgerEntry->find
|
||||
('first',
|
||||
array('contain' => array
|
||||
(
|
||||
'Transaction' =>
|
||||
array('fields' => array('id', 'stamp'),
|
||||
),
|
||||
|
||||
'Ledger' =>
|
||||
array('fields' => array('id', 'sequence', 'name'),
|
||||
'Account' =>
|
||||
array('fields' => array('id', 'name', 'type'),
|
||||
),
|
||||
),
|
||||
|
||||
'Tender' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'DebitEntry' => array('fields' => array('id', 'crdr')),
|
||||
'CreditEntry' => array('fields' => array('id', 'crdr')),
|
||||
),
|
||||
|
||||
'conditions' => array('LedgerEntry.id' => $id),
|
||||
));
|
||||
|
||||
if (!empty($entry['DebitEntry']) && !empty($entry['CreditEntry']))
|
||||
die("LedgerEntry has both a matching DebitEntry and CreditEntry");
|
||||
if (empty($entry['DebitEntry']) && empty($entry['CreditEntry']))
|
||||
die("LedgerEntry has neither a matching DebitEntry nor a CreditEntry");
|
||||
if (empty($entry['DebitEntry']) && count($entry['CreditEntry']) != 1)
|
||||
die("LedgerEntry has more than one CreditEntry");
|
||||
if (empty($entry['CreditEntry']) && count($entry['DebitEntry']) != 1)
|
||||
die("LedgerEntry has more than one DebitEntry");
|
||||
|
||||
if (empty($entry['DebitEntry']))
|
||||
$entry['MatchingEntry'] = $entry['CreditEntry'][0];
|
||||
else
|
||||
$entry['MatchingEntry'] = $entry['DebitEntry'][0];
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Ledger Entry #{$entry['LedgerEntry']['id']}";
|
||||
$this->set(compact('entry', 'title'));
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user