This rework is nowhere near complete, but there are certain things that are falling in place, and worth capturing. I started a branch for just this purpose of being able to check in intermediate work.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716/site@352 97e9348a-65ac-dc4b-aefc-98561f571b83
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
class AccountsController extends AppController {
|
||||
|
||||
var $uses = array('Account', 'LedgerEntry');
|
||||
var $uses = array('Account', 'DoubleEntry');
|
||||
|
||||
var $sidemenu_links =
|
||||
array(array('name' => 'Accounts', 'header' => true),
|
||||
@@ -68,40 +68,15 @@ class AccountsController extends AppController {
|
||||
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
|
||||
*/
|
||||
'DoubleEntry'
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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');
|
||||
return array_merge(array('Account.*'),
|
||||
$this->Account->Ledger->DoubleEntry->debitCreditFields('DoubleEntry', 'CurrentLedger'));
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
@@ -191,7 +166,7 @@ class AccountsController extends AppController {
|
||||
('Account' =>
|
||||
array('fields' => array('name')),
|
||||
|
||||
'LedgerEntry' =>
|
||||
'DoubleEntry' =>
|
||||
array('fields' => array('id', 'amount'),
|
||||
|
||||
'MonetarySource' =>
|
||||
@@ -206,7 +181,7 @@ class AccountsController extends AppController {
|
||||
),
|
||||
'fields' => false,
|
||||
'conditions' => array(array('Ledger.id' => $ledger_id),
|
||||
array('LedgerEntry.id IS NOT NULL'),
|
||||
array('DoubleEntry.id IS NOT NULL'),
|
||||
),
|
||||
));
|
||||
|
||||
@@ -271,7 +246,7 @@ class AccountsController extends AppController {
|
||||
|
||||
// Get all ledger entries of the CURRENT ledger
|
||||
$entries = $this->Account->findLedgerEntries($id);
|
||||
$account['CurrentLedger']['LedgerEntry'] = $entries['Entries'];
|
||||
$account['CurrentLedger']['DoubleEntry'] = $entries['Entries'];
|
||||
|
||||
// Summarize each ledger
|
||||
foreach($account['Ledger'] AS &$ledger)
|
||||
@@ -294,4 +269,8 @@ class AccountsController extends AppController {
|
||||
$this->set(compact('account', 'title', 'stats'));
|
||||
}
|
||||
|
||||
function tst($id) {
|
||||
$entries = $this->Account->ledgerEntries($id, true);
|
||||
pr($entries);
|
||||
}
|
||||
}
|
||||
|
||||
335
controllers/double_entries_controller.php
Normal file
335
controllers/double_entries_controller.php
Normal file
@@ -0,0 +1,335 @@
|
||||
<?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);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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']['collected_account_id']))
|
||||
$params['custom']['account_id'] = $params['custom']['collected_account_id'];
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
$link =
|
||||
array(// Models
|
||||
'Transaction' =>
|
||||
array('fields' => array('id', 'stamp'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Lease' =>
|
||||
array('fields' => array('id', 'number'),
|
||||
'Unit' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if ($params['action'] == 'collected' || $params['action'] == 'reconcile') {
|
||||
$link['Payment'] = array('Account' => array('alias' => 'PaymentAccount'),
|
||||
'Receipt');
|
||||
$link['Charge'] = array('Account' => array('alias' => 'ChargeAccount'),
|
||||
'Invoice');
|
||||
}
|
||||
|
||||
if (isset($params['custom']['ledger_id'])) {
|
||||
$link['Ledger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'Account' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return array('link' => $link);
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
if ($params['action'] == 'collected') {
|
||||
$fields[] = ('SUM(COALESCE(AppliedPayment.amount,0)' .
|
||||
' + COALESCE(AppliedCharge.amount,0)) AS applied');
|
||||
$fields[] = 'MAX(Receipt.stamp) AS last_paid';
|
||||
}
|
||||
elseif ($params['action'] == 'reconcile') {
|
||||
$fields[] = array("IF(Entry.type = 'CHARGE',",
|
||||
" COALESCE(AppliedCharge.amount,0),",
|
||||
" COALESCE(AppliedPayment.amount,0))",
|
||||
" AS 'applied'");
|
||||
$fields[] = array("Entry.amount - (",
|
||||
"IF(Entry.type = 'CHARGE',",
|
||||
" COALESCE(AppliedCharge.amount,0),",
|
||||
" COALESCE(AppliedPayment.amount,0))",
|
||||
") AS 'balance'");
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
if ($params['action'] === 'collected') {
|
||||
if (!isset($params['custom']['account_id']))
|
||||
die("INTERNAL ERROR: ACCOUNT ID NOT SET");
|
||||
|
||||
extract($params['custom']);
|
||||
|
||||
if (!empty($collected_from_date))
|
||||
$conditions[]
|
||||
= array('Receipt.stamp >=' =>
|
||||
$this->DoubleEntry->Transaction->dateFormatBeforeSave($collected_from_date));
|
||||
|
||||
if (!empty($collected_through_date))
|
||||
$conditions[]
|
||||
= array('Receipt.stamp <=' =>
|
||||
$this->DoubleEntry->Transaction->dateFormatBeforeSave($collected_through_date . ' 23:59:59'));
|
||||
|
||||
if (isset($collected_payment_accounts))
|
||||
$conditions[] = array('PaymentAccount.id' => $collected_payment_accounts);
|
||||
else
|
||||
$conditions[] = array('NOT' => array(array('PaymentAccount.id' => null)));
|
||||
}
|
||||
|
||||
if ($params['action'] === 'ledger') {
|
||||
$ledger_id = $params['custom']['ledger_id'];
|
||||
$conditions[] = array('Ledger.id' => $ledger_id);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['reconcile_id'])) {
|
||||
$conditions[] = array('OR' =>
|
||||
array('AppliedCharge.id' => $reconcile_id),
|
||||
array('AppliedPayment.id' => $reconcile_id));
|
||||
}
|
||||
|
||||
if (isset($params['custom']['account_id'])) {
|
||||
$account_id = $params['custom']['account_id'];
|
||||
$conditions[] = array('Account.id' => $account_id);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['customer_id'])) {
|
||||
$conditions[] =
|
||||
array('Customer.id' => $params['custom']['customer_id']);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['lease_id'])) {
|
||||
$conditions[] =
|
||||
array('Lease.id' => $params['custom']['lease_id']);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['transaction_id'])) {
|
||||
$conditions[] =
|
||||
array('Transaction.id' => $params['custom']['transaction_id']);
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Transaction'] = array('id');
|
||||
$links['Entry'] = 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) {
|
||||
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[] = 'Entry.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['group'] = array('AppliedPayment.id');
|
||||
$tquery['fields'] = array("IF(Entry.type = 'CHARGE',",
|
||||
" SUM(COALESCE(AppliedCharge.amount,0)),",
|
||||
" SUM(COALESCE(AppliedPayment.amount,0)))",
|
||||
" AS 'applied'",
|
||||
|
||||
"Charge.amount - (",
|
||||
"IF(Entry.type = 'CHARGE',",
|
||||
" SUM(COALESCE(AppliedCharge.amount,0)),",
|
||||
" SUM(COALESCE(AppliedPayment.amount,0)))",
|
||||
") AS 'balance'",
|
||||
);
|
||||
|
||||
$total = $model->find('first', $tquery);
|
||||
$params['userdata']['total'] = $total[0]['applied'];
|
||||
$params['userdata']['balance'] = $total[0]['balance'];
|
||||
}
|
||||
|
||||
return parent::jqGridDataRecords($params, $model, $query);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: reverse the ledger entry
|
||||
*/
|
||||
|
||||
function reverse($id) {
|
||||
$this->DoubleEntry->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 Entry and related fields
|
||||
$entry = $this->DoubleEntry->find
|
||||
('first',
|
||||
array('contain' => array
|
||||
('Transaction' => array('fields' => array('id', 'stamp')),
|
||||
'Customer' => array('fields' => array('id', 'name')),
|
||||
'Lease' => array('fields' => array('id')),
|
||||
'DebitLedger' => array('fields' => array('id', 'sequence'),
|
||||
'Account' => array('id', 'name', 'type', 'trackable')),
|
||||
'CreditLedger' => array('fields' => array('id', 'sequence'),
|
||||
'Account' => array('id', 'name', 'type', 'trackable')),
|
||||
'DebitEntry'/* => array('fields' => array('id'))*/,
|
||||
'CreditEntry'/* => array('fields' => array('id'))*/,
|
||||
'Entry' => array('fields' => array('id')),
|
||||
),
|
||||
|
||||
//'fields' => array('DoubleEntry.*'),
|
||||
|
||||
'conditions' => array('DoubleEntry.id' => $id),
|
||||
));
|
||||
pr($entry);
|
||||
|
||||
$stats = $this->DoubleEntry->stats($id);
|
||||
|
||||
foreach (array('debit', 'credit') AS $crdr) {
|
||||
$CrDr = ucfirst($crdr);
|
||||
$entry[$CrDr.'Ledger']['Account']['ftype'] =
|
||||
$this->DoubleEntry->Ledger->Account->fundamentalType
|
||||
($entry[$CrDr.'Ledger']['Account']['type']);
|
||||
|
||||
$stats[$crdr]['amount_reconciled'] = null;
|
||||
$stats[$crdr]['amount_remaining'] = null;
|
||||
}
|
||||
|
||||
/* // Get the reconciliation balances for this ledger entry */
|
||||
/* $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['Entry']['amount'] - $stats['debit']['amount_reconciled']; */
|
||||
/* if ($entry['CreditLedger']['Account']['trackable']) */
|
||||
/* $stats['credit']['amount_remaining'] = $entry['Entry']['amount'] - $stats['credit']['amount_reconciled']; */
|
||||
/* //pr($stats); */
|
||||
|
||||
/* $reconciled = $this->DoubleEntry->findReconciledDoubleEntries($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->DoubleEntry->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['DoubleEntry']['id']}";
|
||||
$this->set(compact('entry', 'title', 'reconciled', 'stats'));
|
||||
}
|
||||
|
||||
function tst($id = null) {
|
||||
$entry = $this->DoubleEntry->find
|
||||
('first',
|
||||
array('contain' => array('Account',
|
||||
|
||||
'DoubleEntry',
|
||||
|
||||
'Payment' => array('fields' => array('Payment.*'/*, 'AppliedPayment.*'*/),
|
||||
'DoubleEntry'/* => array('alias' => 'PaymentDoubleEntry')*/),
|
||||
'Charge' => array('fields' => array('Charge.*'/*, 'AppliedCharge.*'*/),
|
||||
'DoubleEntry'/* => array('alias' => 'ChargeDoubleEntry')*/),
|
||||
),
|
||||
'conditions' => array('Entry.id' => $id),
|
||||
));
|
||||
pr($entry);
|
||||
}
|
||||
|
||||
}
|
||||
337
controllers/entries_controller.php
Normal file
337
controllers/entries_controller.php
Normal file
@@ -0,0 +1,337 @@
|
||||
<?php
|
||||
|
||||
class EntriesController 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']['collected_account_id']))
|
||||
$params['custom']['account_id'] = $params['custom']['collected_account_id'];
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
$link =
|
||||
array(// Models
|
||||
'Account' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'DoubleEntry' => array
|
||||
('Transaction' =>
|
||||
array('fields' => array('id', 'stamp'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Lease' =>
|
||||
array('fields' => array('id', 'number'),
|
||||
'Unit' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if ($params['action'] === 'collected' ||
|
||||
isset($params['custom']['reconcile_id'])) {
|
||||
$link['Payment'] = array(//'linkalias' => 'MatchingPayment',
|
||||
'Account' => array('alias' => 'PaymentAccount'),
|
||||
'Receipt');
|
||||
$link['Charge'] = array(//'linkalias' => 'MatchingCharge',
|
||||
'Account' => array('alias' => 'ChargeAccount'),
|
||||
'Invoice');
|
||||
}
|
||||
|
||||
if (isset($params['custom']['ledger_id'])) {
|
||||
$link['Ledger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'Account' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return array('link' => $link);
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
$fields = parent::jqGridDataFields($params, $model);
|
||||
|
||||
if (count(array_intersect($params['fields'], array('applied'))) == 1)
|
||||
$fields[] = ('SUM(COALESCE(AppliedPayment.amount,0)' .
|
||||
' + COALESCE(AppliedCharge.amount,0)) AS applied');
|
||||
|
||||
if (isset($params['custom']['reconcile_id'])) {
|
||||
$fields[] = array("IF(Entry.type = 'CHARGE',",
|
||||
" COALESCE(AppliedCharge.amount,0),",
|
||||
" COALESCE(AppliedPayment.amount,0))",
|
||||
" AS 'applied'");
|
||||
$fields[] = array("Entry.amount - (",
|
||||
"IF(Entry.type = 'CHARGE',",
|
||||
" COALESCE(AppliedCharge.amount,0),",
|
||||
" COALESCE(AppliedPayment.amount,0))",
|
||||
") AS 'balance'");
|
||||
}
|
||||
|
||||
if ($params['action'] === 'collected')
|
||||
$fields[] = 'MAX(Receipt.stamp) AS last_paid';
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'collected') {
|
||||
extract($params['custom']);
|
||||
|
||||
if (!isset($params['custom']['account_id']))
|
||||
die("INTERNAL ERROR: ACCOUNT ID NOT SET");
|
||||
|
||||
if (!empty($collected_from_date))
|
||||
$conditions[]
|
||||
= array('Receipt.stamp >=' =>
|
||||
$this->Entry->Transaction->dateFormatBeforeSave($collected_from_date));
|
||||
|
||||
if (!empty($collected_through_date))
|
||||
$conditions[]
|
||||
= array('Receipt.stamp <=' =>
|
||||
$this->Entry->Transaction->dateFormatBeforeSave($collected_through_date . ' 23:59:59'));
|
||||
|
||||
if (isset($collected_payment_accounts))
|
||||
$conditions[] = array('PaymentAccount.id' => $collected_payment_accounts);
|
||||
else
|
||||
$conditions[] = array('NOT' => array(array('PaymentAccount.id' => null)));
|
||||
}
|
||||
|
||||
if (isset($params['custom']['ledger_id'])) {
|
||||
$ledger_id = $params['custom']['ledger_id'];
|
||||
$conditions[] = array('Ledger.id' => $ledger_id);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['reconcile_id'])) {
|
||||
$conditions[] = array('OR' =>
|
||||
array('AppliedCharge.id' => $reconcile_id),
|
||||
array('AppliedPayment.id' => $reconcile_id));
|
||||
}
|
||||
|
||||
if (isset($params['custom']['account_id'])) {
|
||||
$account_id = $params['custom']['account_id'];
|
||||
$conditions[] = array('Account.id' => $account_id);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['customer_id'])) {
|
||||
$conditions[] =
|
||||
array('Customer.id' => $params['custom']['customer_id']);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['lease_id'])) {
|
||||
$conditions[] =
|
||||
array('Lease.id' => $params['custom']['lease_id']);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['transaction_id'])) {
|
||||
$conditions[] =
|
||||
array('Transaction.id' => $params['custom']['transaction_id']);
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Transaction'] = array('id');
|
||||
$links['Entry'] = 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) {
|
||||
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[] = 'Entry.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['group'] = array('AppliedPayment.id');
|
||||
$tquery['fields'] = array("IF(Entry.type = 'CHARGE',",
|
||||
" SUM(COALESCE(AppliedCharge.amount,0)),",
|
||||
" SUM(COALESCE(AppliedPayment.amount,0)))",
|
||||
" AS 'applied'",
|
||||
|
||||
"Charge.amount - (",
|
||||
"IF(Entry.type = 'CHARGE',",
|
||||
" SUM(COALESCE(AppliedCharge.amount,0)),",
|
||||
" SUM(COALESCE(AppliedPayment.amount,0)))",
|
||||
") AS 'balance'",
|
||||
);
|
||||
|
||||
$total = $model->find('first', $tquery);
|
||||
$params['userdata']['total'] = $total[0]['applied'];
|
||||
$params['userdata']['balance'] = $total[0]['balance'];
|
||||
}
|
||||
|
||||
return parent::jqGridDataRecords($params, $model, $query);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: reverse the ledger entry
|
||||
*/
|
||||
|
||||
function reverse($id) {
|
||||
$this->Entry->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 Entry and related fields
|
||||
$entry = $this->Entry->find
|
||||
('first',
|
||||
array('contain' => array
|
||||
('Account' => array('id', 'name', 'type', 'trackable'),
|
||||
'DoubleEntry' => array
|
||||
(//'fields' => array('id'),
|
||||
'DebitEntry' => array('fields' => array('id', 'crdr')),
|
||||
'CreditEntry' => array('fields' => array('id', 'crdr')),
|
||||
'Transaction' => array('fields' => array('id', 'stamp')),
|
||||
'Customer' => array('fields' => array('id', 'name')),
|
||||
'Lease' => array('fields' => array('id')),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array('Entry.id' => $id),
|
||||
));
|
||||
|
||||
$entry['Entry']['opposite_crdr'] =
|
||||
ucfirst($this->Entry->Account->fundamentalOpposite($entry['Entry']['crdr']));
|
||||
$entry['Entry']['matching_entry_id'] =
|
||||
$entry['DoubleEntry'][
|
||||
ucfirst(strtolower($entry['Entry']['opposite_crdr'])) .
|
||||
'Entry']['id'];
|
||||
/* if ($entry['DoubleEntry']['DebitEntry']['id'] == $entry['Entry']['id']) */
|
||||
/* $entry['Entry']['matching_entry_id'] = $entry['DoubleEntry']['CreditEntry']['id']; */
|
||||
/* if ($entry['DoubleEntry']['CreditEntry']['id'] == $entry['Entry']['id']) */
|
||||
/* $entry['Entry']['matching_entry_id'] = $entry['DoubleEntry']['DebitEntry']['id']; */
|
||||
//pr(compact('entry'));
|
||||
|
||||
$reconciled = $this->Entry->reconciledEntries($id);
|
||||
//pr(compact('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->Entry->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 = "Ledger Entry #{$entry['Entry']['id']}";
|
||||
$this->set(compact('entry', 'title', 'reconciled'));
|
||||
}
|
||||
|
||||
function tst($id = null) {
|
||||
$entry = $this->Entry->find
|
||||
('first',
|
||||
array('contain' => array('Account',
|
||||
|
||||
'DoubleEntry',
|
||||
|
||||
'Payment' => array('fields' => array('Payment.*'/*, 'AppliedPayment.*'*/),
|
||||
'DoubleEntry'/* => array('alias' => 'PaymentDoubleEntry')*/),
|
||||
'Charge' => array('fields' => array('Charge.*'/*, 'AppliedCharge.*'*/),
|
||||
'DoubleEntry'/* => array('alias' => 'ChargeDoubleEntry')*/),
|
||||
),
|
||||
'conditions' => array('Entry.id' => $id),
|
||||
));
|
||||
pr($entry);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -238,6 +238,56 @@ class LeasesController extends AppController {
|
||||
*/
|
||||
|
||||
function apply_deposit($id) {
|
||||
// Create some models for convenience
|
||||
$A = new Account();
|
||||
|
||||
if ($this->data) {
|
||||
// Handle the move out based on the data given
|
||||
//pr($this->data);
|
||||
|
||||
// Assume this will succeed
|
||||
$ret = true;
|
||||
|
||||
// Go through the entered payments
|
||||
$receipt_transaction = array_intersect_key($this->data,
|
||||
array('Transaction'=>1,
|
||||
'transaction_id'=>1));
|
||||
foreach ($data['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));
|
||||
}
|
||||
|
||||
$this->Lease->moveOut($this->data['Lease']['id'],
|
||||
'VACANT',
|
||||
$this->data['Lease']['moveout_date'],
|
||||
//true // Close this lease, if able
|
||||
false
|
||||
);
|
||||
|
||||
$this->redirect(array('controller' => 'leases',
|
||||
'action' => 'view',
|
||||
$this->data['Lease']['id']));
|
||||
$this->autoRender = false;
|
||||
return;
|
||||
}
|
||||
|
||||
$A = new Account();
|
||||
|
||||
$lease = $this->Lease->find
|
||||
@@ -351,6 +401,18 @@ class LeasesController extends AppController {
|
||||
*/
|
||||
|
||||
function refund($id) {
|
||||
/* // Obtain the overall lease balance */
|
||||
/* $stats = $this->Lease->stats($id); */
|
||||
/* $outstanding_balance = $stats['balance']; */
|
||||
|
||||
/* // Determine the lease security deposit */
|
||||
/* $deposits = $this->Lease->findSecurityDeposits($id); */
|
||||
/* $outstanding_deposit = $deposits['summary']['balance']; */
|
||||
|
||||
|
||||
/* $this->set(compact('lease', 'title', */
|
||||
/* 'outstanding_deposit', */
|
||||
/* 'outstanding_balance')); */
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,467 +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);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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'));
|
||||
}
|
||||
}
|
||||
@@ -58,26 +58,16 @@ class LedgersController extends AppController {
|
||||
('link' =>
|
||||
array(// Models
|
||||
'Account',
|
||||
'LedgerEntry',
|
||||
'DoubleEntry',
|
||||
'Close',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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');
|
||||
return array_merge(array('Ledger.*',
|
||||
'CONCAT(Account.id, "-", Ledger.sequence) AS id_sequence'),
|
||||
$this->Ledger->DoubleEntry->debitCreditFields());
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
<?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',
|
||||
@@ -327,25 +320,46 @@ class Account extends AppModel {
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findLedgerEntries
|
||||
* function: ledgerEntries
|
||||
* - 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) {
|
||||
function ledgerEntries($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);
|
||||
}
|
||||
/* $this->Entry->find */
|
||||
/* ('all', array */
|
||||
/* ('contain' => array(), */
|
||||
/* 'conditions' => array('Entry.account_id' => $id) */
|
||||
/* )); */
|
||||
|
||||
$stats = $this->stats($id, $all, $cond);
|
||||
$entries = array('Entries' => $entries,
|
||||
'summary' => $stats['Ledger']);
|
||||
$ledgers = $this->ledgers($id, $all);
|
||||
|
||||
/* $this->Ledger->DoubleEntry->find */
|
||||
/* ('all', array */
|
||||
/* ('contain' => array('Ledger'), */
|
||||
/* 'conditions' => array('OR' => */
|
||||
/* array('DoubleEntry.debit_ledger_id' => $ledgers), */
|
||||
/* array('DoubleEntry.credit_ledger_id' => $ledgers)), */
|
||||
/* 'fields' => */
|
||||
/* )); */
|
||||
|
||||
$entries = $this->Ledger->find
|
||||
('all', array
|
||||
('link' =>
|
||||
array('Account',
|
||||
'DoubleEntry' => array
|
||||
('fields' => $this->Ledger->DoubleEntry->debitCreditFields('DoubleEntry', 'Ledger', false)),
|
||||
),
|
||||
|
||||
'conditions' => array('Ledger.id' => $ledgers),
|
||||
));
|
||||
|
||||
/* $stats = $this->stats($id, $all, $cond); */
|
||||
/* $entries = array('Entries' => $entries, */
|
||||
/* 'summary' => $stats['Ledger']); */
|
||||
|
||||
/* pr(array('function' => 'Account::findLedgerEntries', */
|
||||
/* 'args' => compact('id', 'all', 'cond', 'link'), */
|
||||
@@ -379,7 +393,7 @@ class Account extends AppModel {
|
||||
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));
|
||||
array_push($cond, $this->Ledger->DoubleEntry->conditionEntryAsCreditOrDebit($ledger_ids));
|
||||
$entries = $this->findLedgerEntries($id, $all, $cond, $link);
|
||||
|
||||
/* pr(array('function' => 'Account::findLedgerEntriesRelatedToAccount', */
|
||||
@@ -413,28 +427,28 @@ class Account extends AppModel {
|
||||
('link' => array
|
||||
('Ledger' => array
|
||||
('fields' => array(),
|
||||
"LedgerEntry" => array
|
||||
('class' => "{$ucfund}LedgerEntry",
|
||||
"DoubleEntry" => array
|
||||
('class' => "{$ucfund}DoubleEntry",
|
||||
'fields' => array('id', 'customer_id', 'lease_id', 'amount'),
|
||||
"ReconciliationLedgerEntry" => array
|
||||
('class' => "{$ucfund}ReconciliationLedgerEntry",
|
||||
"ReconciliationDoubleEntry" => array
|
||||
('class' => "{$ucfund}ReconciliationDoubleEntry",
|
||||
'fields' => array
|
||||
("COALESCE(SUM(Reconciliation.amount),0) AS 'reconciled'",
|
||||
"LedgerEntry.amount - COALESCE(SUM(Reconciliation.amount),0) AS 'balance'",
|
||||
"DoubleEntry.amount - COALESCE(SUM(Reconciliation.amount),0) AS 'balance'",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'group' => ("LedgerEntry.id" .
|
||||
" HAVING LedgerEntry.amount" .
|
||||
'group' => ("DoubleEntry.id" .
|
||||
" HAVING DoubleEntry.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 = array_merge(array_diff_key($entry["DoubleEntry"], array(0=>true)),
|
||||
$entry[0]);
|
||||
$balance += $entry['balance'];
|
||||
}
|
||||
@@ -448,7 +462,7 @@ class Account extends AppModel {
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reconcileNewLedgerEntry
|
||||
* function: amountWouldReconcile
|
||||
* - Returns which ledger entries a new credit/debit would
|
||||
* reconcile, and how much.
|
||||
*
|
||||
@@ -459,7 +473,7 @@ class Account extends AppModel {
|
||||
* whatever algorithm is simplest.
|
||||
*/
|
||||
|
||||
function reconcileNewLedgerEntry($id, $fundamental_type, $amount, $cond = null) {
|
||||
function amountWouldReconcile($id, $fundamental_type, $amount, $cond = null) {
|
||||
$ofund = $this->fundamentalOpposite($fundamental_type);
|
||||
$unreconciled = array($ofund => array('entry'=>array(), 'balance'=>0));
|
||||
$applied = 0;
|
||||
@@ -498,7 +512,78 @@ class Account extends AppModel {
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: postLedgerEntry
|
||||
* function: reconcileLedgerEntries
|
||||
* - 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 reconcileLedgerEntries($id, $cond = null) {
|
||||
$unreconciled = $this->findUnreconciledLedgerEntries($id, null, $cond);
|
||||
pr(compact('unreconciled'));
|
||||
|
||||
$entry = array();
|
||||
foreach (array('debit', 'credit') AS $dc)
|
||||
$entry[$dc] = array('balance' => 0);
|
||||
|
||||
while ($entry['debit'] && $entry['credit']) {
|
||||
|
||||
// If/When we've exhausted this/these entries, move the next
|
||||
foreach (array('debit', 'credit') AS $dc) {
|
||||
if ($entry[$dc]['balance'] <= 0) {
|
||||
$entry[$dc] =& current($unreconciled[$dc]['entry']);
|
||||
next($unreconciled[$dc]['entry']);
|
||||
$entry[$dc]['applied'] = 0;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, both entries are valid with a positive balance
|
||||
$apply = min($entry['debit']['balance'], $entry['credit']['balance']);
|
||||
|
||||
|
||||
// REVISIT <AP>: 20090716
|
||||
// NOT YET ENTERING THE RECONCILIATION SO THAT WE CAN TEST
|
||||
// MUST ADD THE RECONCILIATION ENTRY!!!!
|
||||
|
||||
foreach (array('debit', 'credit') AS $dc) {
|
||||
$entry[$dc]['applied'] += $apply;
|
||||
$entry[$dc]['reconciled'] += $apply;
|
||||
$entry[$dc]['balance'] -= $apply;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array('debit', 'credit') AS $dc) {
|
||||
$unreconciled[$dc]['applied'] = 0;
|
||||
//$unreconciled[$dc]['unapplied'] = 0;
|
||||
foreach ($unreconciled[$dc]['entry'] AS $i => $entry) {
|
||||
if (isset($entry[$dc]['applied']))
|
||||
$unreconciled[$dc]['applied'] += $entry[$dc]['applied'];
|
||||
else
|
||||
unset($unreconciled[$dc]['entry'][$i]);
|
||||
|
||||
//$unreconciled[$dc]['unapplied'] += $entry[$dc]['balance'];
|
||||
}
|
||||
$unreconciled[$dc]['balance'] -= $unreconciled[$dc]['applied'];
|
||||
}
|
||||
|
||||
$unreconciled['debit'] ['unapplied'] = $unreconciled['credit']['balance'];
|
||||
$unreconciled['credit']['unapplied'] = $unreconciled['debit'] ['balance'];
|
||||
pr(compact('unreconciled'));
|
||||
|
||||
return $unreconciled;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: postDoubleEntry
|
||||
* -
|
||||
* transaction_data
|
||||
* - transaction_id (optional... if set all else is ignored)
|
||||
@@ -513,7 +598,7 @@ class Account extends AppModel {
|
||||
* - name
|
||||
*/
|
||||
|
||||
function postLedgerEntry($transaction_data,
|
||||
function postDoubleEntry($transaction_data,
|
||||
$monetary_data,
|
||||
$entry_data,
|
||||
$reconcile = null) {
|
||||
@@ -566,7 +651,7 @@ class Account extends AppModel {
|
||||
// No distinguishing features of Cash, just
|
||||
// use the shared monetary source
|
||||
$monetary_data['monetary_source_id'] =
|
||||
$this->Ledger->LedgerEntry->MonetarySource->nameToID('Cash');
|
||||
$this->Ledger->DoubleEntry->MonetarySource->nameToID('Cash');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -634,7 +719,7 @@ class Account extends AppModel {
|
||||
|
||||
//pr(array('pre-save', compact('entry_data')));
|
||||
// Create it!
|
||||
$new_entry = new LedgerEntry();
|
||||
$new_entry = new DoubleEntry();
|
||||
$new_entry->create();
|
||||
if (!$new_entry->saveAll($entry_data, array('validate'=>false))) {
|
||||
return array('error' => true);
|
||||
@@ -664,7 +749,7 @@ class Account extends AppModel {
|
||||
|
||||
if ($reconcile_set === 'receipt') {
|
||||
$C = new Customer();
|
||||
$reconciled = $C->reconcileNewLedgerEntry($entry_data['customer_id'],
|
||||
$reconciled = $C->amountWouldReconcile($entry_data['customer_id'],
|
||||
$this->fundamentalOpposite($dc_type),
|
||||
$entry_data['amount']);
|
||||
|
||||
@@ -689,7 +774,7 @@ class Account extends AppModel {
|
||||
|
||||
// Payment must debit the Receipt ledger, and credit the A/R ledger
|
||||
// debit: Receipt credit: A/R
|
||||
$ids = $this->postLedgerEntry
|
||||
$ids = $this->postDoubleEntry
|
||||
($split_transaction,
|
||||
null,
|
||||
array('debit_ledger_id' => $this->currentLedgerID($this->receiptAccountID()),
|
||||
@@ -698,9 +783,9 @@ class Account extends AppModel {
|
||||
'lease_id' => $rec['lease_id'],
|
||||
'customer_id' => $rec['customer_id'],
|
||||
),
|
||||
array('debit' => array(array('LedgerEntry' => array('id' => $new_entry->id,
|
||||
array('debit' => array(array('DoubleEntry' => array('id' => $new_entry->id,
|
||||
'amount' => $rec['applied']))),
|
||||
'credit' => array(array('LedgerEntry' => array('id' => $rec['id'],
|
||||
'credit' => array(array('DoubleEntry' => array('id' => $rec['id'],
|
||||
'amount' => $rec['applied']))))
|
||||
);
|
||||
// Keep using the same split transaction for all reconciled entries
|
||||
@@ -717,19 +802,19 @@ class Account extends AppModel {
|
||||
if (is_array($reconcile_set)) {
|
||||
//pr("reconcile_set is array");
|
||||
foreach ($reconcile_set AS $reconcile_entry) {
|
||||
if (!isset($reconcile_entry['LedgerEntry']['id']))
|
||||
if (!isset($reconcile_entry['DoubleEntry']['id']))
|
||||
continue;
|
||||
|
||||
$amount = $reconcile_entry['LedgerEntry']['amount'];
|
||||
$amount = $reconcile_entry['DoubleEntry']['amount'];
|
||||
if (!$amount)
|
||||
continue;
|
||||
|
||||
if ($dc_type == 'debit') {
|
||||
$debit_ledger_entry_id = $new_entry->id;
|
||||
$credit_ledger_entry_id = $reconcile_entry['LedgerEntry']['id'];
|
||||
$credit_ledger_entry_id = $reconcile_entry['DoubleEntry']['id'];
|
||||
}
|
||||
else {
|
||||
$debit_ledger_entry_id = $reconcile_entry['LedgerEntry']['id'];
|
||||
$debit_ledger_entry_id = $reconcile_entry['DoubleEntry']['id'];
|
||||
$credit_ledger_entry_id = $new_entry->id;
|
||||
}
|
||||
|
||||
@@ -749,9 +834,9 @@ class Account extends AppModel {
|
||||
|
||||
$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']);
|
||||
'id' => $new_entry->data['DoubleEntry']['id'],
|
||||
'transaction_id' => $new_entry->data['DoubleEntry']['transaction_id'],
|
||||
'monetary_source_id' => $new_entry->data['DoubleEntry']['monetary_source_id']);
|
||||
|
||||
if (isset($split_transaction['transaction_id']))
|
||||
$ret['split_transaction_id'] = $split_transaction['transaction_id'];
|
||||
@@ -783,7 +868,7 @@ class Account extends AppModel {
|
||||
if ($ledger['total'] == 0)
|
||||
continue;
|
||||
|
||||
$ids = $this->postLedgerEntry
|
||||
$ids = $this->postDoubleEntry
|
||||
($transaction,
|
||||
null,
|
||||
array('debit_account_id' => $deposit_account_id,
|
||||
@@ -795,7 +880,7 @@ class Account extends AppModel {
|
||||
//pr(compact('ids'));
|
||||
|
||||
if ($ids['error'])
|
||||
die("closeAndDeposit : postLedgerEntry returned error!");
|
||||
die("closeAndDeposit : postDoubleEntry returned error!");
|
||||
|
||||
$transaction = array_intersect_key($ids, array('transaction_id'=>1));
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ class LinkableBehavior extends ModelBehavior {
|
||||
protected $_defaults = array('type' => 'LEFT');
|
||||
|
||||
function pr($lev, $mixed) {
|
||||
if ($lev >= 5)
|
||||
if ($lev >= 3)
|
||||
return;
|
||||
|
||||
pr($mixed);
|
||||
@@ -159,7 +159,7 @@ class LinkableBehavior extends ModelBehavior {
|
||||
$iterations = Set::normalize($iterator);
|
||||
$this->pr(25,
|
||||
array('checkpoint' => 'Iterations',
|
||||
compact('iterations'),
|
||||
compact('cont', 'iterator', 'iterations'),
|
||||
));
|
||||
foreach ($iterations as $alias => $options) {
|
||||
if (is_null($options)) {
|
||||
@@ -246,15 +246,32 @@ class LinkableBehavior extends ModelBehavior {
|
||||
else
|
||||
$associationAlias = $modelAlias;
|
||||
|
||||
// A couple exceptions before performing a union of
|
||||
// options and association. Namely, most fields result
|
||||
// in either/or, but a couple should include BOTH the
|
||||
// options AND the association settings.
|
||||
foreach (array('fields', 'conditions') AS $fld) {
|
||||
if (isset($options[$fld]) && is_array($options[$fld]) &&
|
||||
isset($association[$fld]) && is_array($association[$fld]))
|
||||
$options[$fld] = array_merge($options[$fld],
|
||||
$association[$fld]);
|
||||
}
|
||||
|
||||
// For any option that's not already set, use
|
||||
// whatever is specified by the assocation.
|
||||
$options += array_intersect_key($association, $this->_options);
|
||||
|
||||
// Replace all instances of the MODEL_ALIAS variable
|
||||
// tag with the correct model alias.
|
||||
$this->recursive_array_replace("%{MODEL_ALIAS}",
|
||||
$associationAlias,
|
||||
$association['conditions']);
|
||||
$options['conditions']);
|
||||
|
||||
$this->pr(15,
|
||||
array('checkpoint' => 'Models Established - Check Associations',
|
||||
'primaryModel' => $primaryAlias .' : '. $primaryModel->name,
|
||||
'foreignModel' => $foreignAlias .' : '. $foreignModel->name,
|
||||
compact('type', 'association'),
|
||||
compact('type', 'association', 'options'),
|
||||
));
|
||||
|
||||
if ($type === 'hasAndBelongsToMany') {
|
||||
@@ -270,16 +287,29 @@ class LinkableBehavior extends ModelBehavior {
|
||||
else
|
||||
$linkAlias = $Link->alias;
|
||||
|
||||
// foreignKey and associationForeignKey can refer to either
|
||||
// the model or the reference, depending on which class
|
||||
// actually defines the association. Make sure to we're
|
||||
// using the foreign keys to point to the right class.
|
||||
if ($associatedThroughReference) {
|
||||
$modelAFK = 'associationForeignKey';
|
||||
$referenceAFK = 'foreignKey';
|
||||
} else {
|
||||
$modelAFK = 'foreignKey';
|
||||
$referenceAFK = 'associationForeignKey';
|
||||
}
|
||||
|
||||
$this->pr(17,
|
||||
array('checkpoint' => 'Linking HABTM',
|
||||
compact('linkClass', 'linkAlias'),
|
||||
compact('linkClass', 'linkAlias',
|
||||
'modelAFK', 'referenceAFK'),
|
||||
));
|
||||
|
||||
// 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);
|
||||
$modelLink = $Link->escapeField($association[$modelAFK], $linkAlias);
|
||||
$referenceLink = $Link->escapeField($association[$referenceAFK], $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
|
||||
@@ -300,6 +330,12 @@ class LinkableBehavior extends ModelBehavior {
|
||||
$referenceKey = $Reference->escapeField(null, $referenceAlias);
|
||||
$modelKey = $_Model->escapeField(null, $modelAlias);
|
||||
|
||||
$this->pr(21,
|
||||
array('checkpoint' => 'HABTM links/keys',
|
||||
array(compact('modelLink', 'modelKey'),
|
||||
compact('referenceLink', 'referenceKey')),
|
||||
));
|
||||
|
||||
// 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
|
||||
@@ -345,20 +381,6 @@ class LinkableBehavior extends ModelBehavior {
|
||||
|
||||
$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'],
|
||||
),
|
||||
));
|
||||
@@ -372,13 +394,16 @@ class LinkableBehavior extends ModelBehavior {
|
||||
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'])));
|
||||
$query['fields'] = array_merge($query['fields'], $options['fields']);
|
||||
|
||||
$options[$this->_key] = am($options[$this->_key], array_diff_key($options, $optionsKeys));
|
||||
$options = array_intersect_key($options, $optionsKeys);
|
||||
if (!empty($options[$this->_key])) {
|
||||
$this->pr(24,
|
||||
array('checkpoint' => 'Add new iterator',
|
||||
'options[this->_key]' => $options[$this->_key],
|
||||
compact('defaults', 'modelClass', 'modelAlias'),
|
||||
));
|
||||
$iterators[] = $options[$this->_key] +
|
||||
array('defaults' =>
|
||||
array_merge($defaults,
|
||||
|
||||
@@ -19,17 +19,14 @@ class Customer extends AppModel {
|
||||
'conditions' => 'CurrentLease.close_date IS NULL',
|
||||
),
|
||||
'Lease',
|
||||
'LedgerEntry',
|
||||
'DoubleEntry',
|
||||
'ContactsCustomer',
|
||||
|
||||
'Transaction',
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
'Contact',
|
||||
'Transaction' => array(
|
||||
'joinTable' => 'ledger_entries',
|
||||
'foreignKey' => 'customer_id',
|
||||
'associationForeignKey' => 'transaction_id',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -87,7 +84,7 @@ class Customer extends AppModel {
|
||||
$A = new Account();
|
||||
$entries = $A->findLedgerEntries
|
||||
($A->securityDepositAccountID(),
|
||||
true, array('LedgerEntry.customer_id' => $id), $link);
|
||||
true, array('DoubleEntry.customer_id' => $id), $link);
|
||||
|
||||
/* pr(array('function' => 'Customer::findSecurityDeposits', */
|
||||
/* 'args' => compact('id', 'link'), */
|
||||
@@ -112,7 +109,7 @@ class Customer extends AppModel {
|
||||
$unreconciled = $A->findUnreconciledLedgerEntries
|
||||
($A->accountReceivableAccountID(),
|
||||
$fundamental_type,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
array('DoubleEntry.customer_id' => $id));
|
||||
|
||||
return $unreconciled;
|
||||
}
|
||||
@@ -121,7 +118,7 @@ class Customer extends AppModel {
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reconcileNewLedgerEntry
|
||||
* function: amountWouldReconcile
|
||||
* - Returns which ledger entries a new credit/debit would
|
||||
* reconcile, and how much.
|
||||
*
|
||||
@@ -132,18 +129,41 @@ class Customer extends AppModel {
|
||||
* whatever algorithm is simplest.
|
||||
*/
|
||||
|
||||
function reconcileNewLedgerEntry($id, $fundamental_type, $amount) {
|
||||
function amountWouldReconcile($id, $fundamental_type, $amount) {
|
||||
$A = new Account();
|
||||
$reconciled = $A->reconcileNewLedgerEntry
|
||||
$reconciled = $A->amountWouldReconcile
|
||||
($A->accountReceivableAccountID(),
|
||||
$fundamental_type,
|
||||
$amount,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
array('DoubleEntry.customer_id' => $id));
|
||||
|
||||
return $reconciled;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reconcileLedgerEntries
|
||||
* - 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 reconcileLedgerEntries($id) {
|
||||
$A = new Account();
|
||||
$reconciled = $A->reconcileLedgerEntries
|
||||
($A->accountReceivableAccountID(),
|
||||
array('DoubleEntry.customer_id' => $id));
|
||||
|
||||
return $reconciled;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -274,7 +294,7 @@ class Customer extends AppModel {
|
||||
|
||||
$A = new Account();
|
||||
$stats = $A->stats($A->accountReceivableAccountID(), true,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
array('DoubleEntry.customer_id' => $id));
|
||||
|
||||
// Pull to the top level and return
|
||||
$stats = $stats['Ledger'];
|
||||
|
||||
259
models/double_entry.php
Normal file
259
models/double_entry.php
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
class DoubleEntry extends AppModel {
|
||||
|
||||
var $name = 'DoubleEntry';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'transaction_id' => array('numeric'),
|
||||
'amount' => array('money')
|
||||
);
|
||||
|
||||
var $hasOne = array(
|
||||
'DebitEntry' => array(
|
||||
'className' => 'Entry',
|
||||
'conditions' => array('DebitEntry.crdr' => 'DEBIT'),
|
||||
),
|
||||
'CreditEntry' => array(
|
||||
'className' => 'Entry',
|
||||
'conditions' => array('CreditEntry.crdr' => 'CREDIT'),
|
||||
),
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'Entry',
|
||||
);
|
||||
|
||||
|
||||
var $belongsTo = array(
|
||||
'Transaction',
|
||||
'Invoice' => array(
|
||||
'className' => 'Transaction',
|
||||
'conditions' => array('Invoice.type' => 'INVOICE'),
|
||||
),
|
||||
'Receipt' => array(
|
||||
'className' => 'Transaction',
|
||||
'conditions' => array('Invoice.type' => 'RECEIPT'),
|
||||
),
|
||||
|
||||
|
||||
'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' => ''
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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 debitCreditFields($double_name = 'DoubleEntry', $ledger_name = 'Ledger', $sum = true) {
|
||||
$fields = array
|
||||
(
|
||||
($sum ? 'SUM(' : '') .
|
||||
"IF({$double_name}.debit_ledger_id = {$ledger_name}.id,
|
||||
{$double_name}.amount, NULL)" .
|
||||
($sum ? ')' : '') . ' AS debit' . ($sum ? 's' : ''),
|
||||
|
||||
($sum ? 'SUM(' : '') .
|
||||
"IF({$double_name}.credit_ledger_id = {$ledger_name}.id,
|
||||
{$double_name}.amount, NULL)" .
|
||||
($sum ? ')' : '') . ' AS credit' . ($sum ? 's' : ''),
|
||||
|
||||
($sum ? 'SUM(' : '') .
|
||||
"IF(Account.type IN ('ASSET', 'EXPENSE'),
|
||||
IF({$double_name}.debit_ledger_id = {$ledger_name}.id, 1, -1),
|
||||
IF({$double_name}.credit_ledger_id = {$ledger_name}.id, 1, -1)
|
||||
) * IF({$double_name}.amount, {$double_name}.amount, 0)" .
|
||||
($sum ? ')' : '') . ' AS balance',
|
||||
);
|
||||
|
||||
if ($sum)
|
||||
$fields[] = "COUNT({$double_name}.id) AS entries";
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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',
|
||||
'lease_id', 'customer_id', 'comment', 'amount');
|
||||
|
||||
if (isset($ledger_id)) {
|
||||
$fields[] = ("IF(DoubleEntry.debit_ledger_id = $ledger_id," .
|
||||
" DoubleEntry.amount, NULL) AS debit");
|
||||
$fields[] = ("IF(DoubleEntry.credit_ledger_id = $ledger_id," .
|
||||
" DoubleEntry.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(DoubleEntry.{$ledger_type}_ledger_id = $ledger_id," .
|
||||
" 1, -1) * DoubleEntry.amount) AS balance");
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function ledgerContextFields2($ledger_id = null, $account_id = null, $account_type = null) {
|
||||
$fields = array('id', 'effective_date', 'comment', 'amount');
|
||||
|
||||
if (isset($ledger_id)) {
|
||||
$fields[] = ("IF(DoubleEntry.debit_ledger_id = $ledger_id," .
|
||||
" SUM(DoubleEntry.amount), NULL) AS debit");
|
||||
$fields[] = ("IF(DoubleEntry.credit_ledger_id = $ledger_id," .
|
||||
" SUM(DoubleEntry.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(DoubleEntry.{$account_ftype}_ledger_id = $ledger_id," .
|
||||
" 1, -1) * SUM(DoubleEntry.amount)) AS balance");
|
||||
}
|
||||
}
|
||||
elseif (isset($account_id)) {
|
||||
$fields[] = ("IF(DebitLedger.account_id = $account_id," .
|
||||
" SUM(DoubleEntry.amount), NULL) AS debit");
|
||||
$fields[] = ("IF(CreditLedger.account_id = $account_id," .
|
||||
" SUM(DoubleEntry.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(DoubleEntry.amount)) AS balance");
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
|
||||
function ledgerContextConditions($ledger_id, $account_type) {
|
||||
if (isset($ledger_id)) {
|
||||
return array
|
||||
('OR' =>
|
||||
array(array('DoubleEntry.debit_ledger_id' => $ledger_id),
|
||||
array('DoubleEntry.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 double_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: stats
|
||||
* - Returns summary data from the requested double entry
|
||||
*/
|
||||
function stats($id) {
|
||||
|
||||
/* $query = array */
|
||||
/* ( */
|
||||
/* 'fields' => array("SUM(Reconciliation.amount) AS 'reconciled'"), */
|
||||
|
||||
/* 'conditions' => array(isset($cond) ? $cond : array(), */
|
||||
/* array('DoubleEntry.id' => $id)), */
|
||||
|
||||
/* 'group' => 'DoubleEntry.id', */
|
||||
/* ); */
|
||||
|
||||
/* // Get the applied amounts on the debit side */
|
||||
/* $query['link'] = */
|
||||
/* array('DebitReconciliationDoubleEntry' => 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('CreditReconciliationDoubleEntry' => array('alias' => 'CRLE', 'CRLETransaction' => array('class' => 'Transaction'))); */
|
||||
/* $tmpstats = $this->find('first', $query); */
|
||||
/* $stats['credit_amount_reconciled'] = $tmpstats[0]['reconciled']; */
|
||||
|
||||
$stats['debit']['amount_reconciled'] = 0;
|
||||
$stats['credit']['amount_reconciled'] = 0;
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
}
|
||||
578
models/entry.php
Normal file
578
models/entry.php
Normal file
@@ -0,0 +1,578 @@
|
||||
<?php
|
||||
class Entry extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
'Account',
|
||||
'DoubleEntry',
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
|
||||
// The Payments that match THIS Charge (if it is one)
|
||||
'Payment' => array(
|
||||
'className' => 'Entry',
|
||||
'joinTable' => 'charges_payments',
|
||||
'linkalias' => 'AppliedPayment',
|
||||
'foreignKey' => 'charge_entry_id',
|
||||
'associationForeignKey' => 'payment_entry_id',
|
||||
),
|
||||
|
||||
// The Charges that match THIS Payment (if it is one)
|
||||
'Charge' => array(
|
||||
'className' => 'Entry',
|
||||
'joinTable' => 'charges_payments',
|
||||
'linkalias' => 'AppliedCharge',
|
||||
'foreignKey' => 'payment_entry_id',
|
||||
'associationForeignKey' => 'charge_entry_id',
|
||||
),
|
||||
|
||||
// The Debits of this same account matching THIS Credit (if it is one)
|
||||
'Debit' => array(
|
||||
'className' => 'Entry',
|
||||
'joinTable' => 'reconciliations',
|
||||
'linkalias' => 'AppliedDebit',
|
||||
'foreignKey' => 'credit_entry_id',
|
||||
'associationForeignKey' => 'debit_entry_id',
|
||||
),
|
||||
|
||||
// The Credits of this same account matching THIS Debit (if it is one)
|
||||
'Credit' => array(
|
||||
'className' => 'Entry',
|
||||
'joinTable' => 'reconciliations',
|
||||
'linkalias' => 'AppliedCredit',
|
||||
'foreignKey' => 'debit_entry_id',
|
||||
'associationForeignKey' => 'credit_entry_id',
|
||||
),
|
||||
|
||||
/* // The Entries of this same account matching THIS Entry */
|
||||
/* 'REntry' => array( */
|
||||
/* 'className' => 'Entry', */
|
||||
/* 'joinTable' => 'reconciliations', */
|
||||
/* //'linkalias' => 'AppliedCredit', */
|
||||
/* 'foreignKey' => false, */
|
||||
/* 'associationForeignKey' => false, */
|
||||
/* 'conditions' => array( */
|
||||
/* "Reconciliation.credit_entry_id */
|
||||
/* = IF(Entry.crdr = 'CREDIT', Entry.id, REntry.id)", */
|
||||
/* "Reconciliation.debit_entry_id */
|
||||
/* = IF(Entry.crdr = 'DEBIT', Entry.id, REntry.id)" */
|
||||
/* ), */
|
||||
/* ), */
|
||||
);
|
||||
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addCharge
|
||||
* - Adds a new charge
|
||||
*/
|
||||
|
||||
function addCharge($data, $transaction, $customer_id, $lease_id = null) {
|
||||
// Create some models for convenience
|
||||
$A = new Account();
|
||||
|
||||
// Assume this will succeed
|
||||
$ret = true;
|
||||
|
||||
// Establish the key charge parameters
|
||||
$charge = array_intersect_key($data, array('stamp'=>1, 'amount'=>1, 'account_id'=>1));
|
||||
|
||||
$charge['customer_id'] = $customer_id;
|
||||
$charge['lease_id'] = $lease_id;
|
||||
$charge['type'] = 'CHARGE';
|
||||
|
||||
$ids = $this->Entry->Ledger->Account->postDoubleEntry
|
||||
($transaction,
|
||||
$charge,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($data['account_id']),
|
||||
'credit_ledger_id' => $A->currentLedgerID($A->accountReceivableAccountID())
|
||||
) + $data
|
||||
);
|
||||
|
||||
if ($ids['error'])
|
||||
$ret = false;
|
||||
|
||||
return $ids['charge_id'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addPayment
|
||||
* - Adds a new payment
|
||||
*/
|
||||
|
||||
function addPayment($data, $transaction, $customer_id, $lease_id = null) {
|
||||
// Create some models for convenience
|
||||
$A = new Account();
|
||||
|
||||
// Assume this will succeed
|
||||
$ret = true;
|
||||
|
||||
// Establish the key payment parameters
|
||||
$payment = array_intersect_key($data, array('stamp'=>1, 'name'=>1, 'monetary_type'=>1,
|
||||
'data1'=>1, 'data2'=>1, 'data3'=>1, 'data4'=>1,
|
||||
'amount'=>1, 'account_id'=>1));
|
||||
$payment['customer_id'] = $customer_id;
|
||||
$payment['type'] = 'PAYMENT';
|
||||
|
||||
$ids = $this->Entry->Ledger->Account->postDoubleEntry
|
||||
($transaction,
|
||||
$payment,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($data['account_id']),
|
||||
'credit_ledger_id' => $A->currentLedgerID($A->accountReceivableAccountID())
|
||||
) + $data
|
||||
);
|
||||
|
||||
if ($ids['error'])
|
||||
$ret = false;
|
||||
|
||||
return $ids['payment_id'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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: 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('Entry::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('Entry.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['Entry'];
|
||||
$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->postEntry
|
||||
(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('Entry' =>
|
||||
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: reconcileConditions
|
||||
* - Returns entries which reconcile, or match, the set of entries
|
||||
* requested through the $cond argument.
|
||||
*/
|
||||
|
||||
function reconcilingQuery($double_name = 'DoubleEntry', $sum = false) {
|
||||
$applied = array();
|
||||
foreach (array('Payment', 'Charge') AS $pc) {
|
||||
$applied[$pc] = "COALESCE(Applied{$pc}.amount,0)";
|
||||
if ($sum)
|
||||
$applied[$pc] = "SUM({$applied[$pc]})";
|
||||
}
|
||||
|
||||
return array
|
||||
('fields' => array("IF(Entry.type = 'CHARGE'," .
|
||||
" {$applied['Payment']}, {$applied['Charge']}) AS 'applied'",
|
||||
"{$double_name}.amount - IF(Entry.type = 'CHARGE'," .
|
||||
" {$applied['Payment']}, {$applied['Charge']}) AS 'balance'",
|
||||
),
|
||||
'conditions' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: matchingEntries
|
||||
* - Returns entries which reconcile, or match, the set of entries
|
||||
* requested through the $cond argument.
|
||||
*/
|
||||
|
||||
function matchingEntries($cond, $group, $strip_rec = false, $strip_unrec = false) {
|
||||
|
||||
$rquery = $this->reconcilingQuery('DoubleEntry', $group);
|
||||
$fields = array_merge(array('Entry.*'), $rquery['fields']);
|
||||
$cond = array_merge($cond, $rquery['conditions']);
|
||||
|
||||
$reconciled = $this->find
|
||||
('all', array
|
||||
('link' => array('DoubleEntry', 'Account',
|
||||
'Charge' => array('fields' => array('Charge.*', 'AppliedCharge.*')),
|
||||
'Payment' => array('fields' => array('Payment.*', 'AppliedPayment.*')),
|
||||
),
|
||||
'fields' => $fields,
|
||||
'group' => $group,
|
||||
'conditions' => $cond,
|
||||
));
|
||||
|
||||
foreach ($reconciled AS $i => &$entry) {
|
||||
$entry['Entry'] += $entry[0];
|
||||
unset($entry[0]);
|
||||
|
||||
// Since HAVING isn't a builtin feature of CakePHP,
|
||||
// we'll have to post-process to get the desired entries
|
||||
if ($entry['Entry']['balance'] == 0) {
|
||||
if ($strip_rec)
|
||||
unset($reconciled[$i]);
|
||||
}
|
||||
else {
|
||||
if ($strip_unrec)
|
||||
unset($reconciled[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
//pr(compact('reconciled'));
|
||||
return $reconciled;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reconciledEntries
|
||||
* - Returns the list of entries that have been reconciled (if $rec),
|
||||
* or not fully reconciled (if $rec is false), along with the amount
|
||||
* that has already been applied towards the entry as well as the
|
||||
* remaining balance.
|
||||
*/
|
||||
|
||||
function reconciledEntries1($id, $rec = true, $cond = null) {
|
||||
$cond[] = array('Entry.id' => $id);
|
||||
//return $this->matchingEntries($cond, array('Entry.id'), !$rec, $rec);
|
||||
return $this->matchingEntries($cond, array('Entry.id'), false, false);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reconcilingEntries
|
||||
* - Returns a list of entries that reconcile against the given entry.
|
||||
* (such as payments towards a charge).
|
||||
*/
|
||||
|
||||
function reconciledEntries($id, $cond = null) {
|
||||
|
||||
if (!isset($cond))
|
||||
$cond = array();
|
||||
|
||||
$cond[] = array('Entry.id' => $id);
|
||||
|
||||
$entry = $this->find('first', array('recursive' => -1));
|
||||
$entry = $entry['Entry'];
|
||||
|
||||
$contain = array();
|
||||
if ($entry['type'] == 'CHARGE')
|
||||
$contain['Payment'] = array('fields' => array('Payment.*', 'ChargesPayment.amount'));
|
||||
if ($entry['type'] == 'PAYMENT')
|
||||
$contain['Charge'] = array('fields' => array('Charge.*', 'ChargesPayment.amount'));
|
||||
if ($entry['crdr'] == 'DEBIT')
|
||||
$contain['Credit'] = array('fields' => array('Credit.*', 'Reconciliation.amount'));
|
||||
if ($entry['crdr'] == 'CREDIT')
|
||||
$contain['Debit'] = array('fields' => array('Debit.*', 'Reconciliation.amount'));
|
||||
|
||||
//pr(array('reconciledEntries', compact('entry', 'contain')));
|
||||
$result = array();
|
||||
$result['entries'] = $this->find
|
||||
('first', array
|
||||
(
|
||||
'contain' => $contain,
|
||||
|
||||
/* 'zcontain' => array(//'DoubleEntry' => array('fields' => array('amount')), */
|
||||
/* 'REntry' => array('fields' => array('Reconciliation.amount'), */
|
||||
/* 'DoubleEntry'), */
|
||||
/* ), */
|
||||
|
||||
/* 'zlink' => array(//'DoubleEntry' => array('fields' => array('amount')), */
|
||||
/* 'REntry' => array('fields' => array('REntry.*', 'Reconciliation.amount'), */
|
||||
/* 'DoubleEntry'), */
|
||||
/* ), */
|
||||
|
||||
/* 'zlink' => array(//'DoubleEntry' => array('fields' => array('amount')), */
|
||||
/* 'Debit' => array('fields' => array('AppliedDebit.amount'), */
|
||||
/* 'DoubleEntry' => array('alias' => 'DebitDoubleEntry')), */
|
||||
/* 'Credit' => array('fields' => array('AppliedCredit.amount'), */
|
||||
/* 'DoubleEntry' => array('alias' => 'CreditDoubleEntry')), */
|
||||
/* ), */
|
||||
|
||||
'fields' => array('id'),
|
||||
'conditions' => $cond,
|
||||
));
|
||||
unset($result['entries']['Entry']);
|
||||
|
||||
$result['stats'] = $this->stats($id);
|
||||
return $result;
|
||||
|
||||
/* $balance = $this->find */
|
||||
/* ('first', array */
|
||||
/* ('link' => array */
|
||||
/* ( */
|
||||
/* "Charge" => array */
|
||||
/* ('fields' => array("SUM(COALESCE(ChargesPayment.amount,0)) AS 'reconciling_charges'")), */
|
||||
/* "Payment" => array */
|
||||
/* ('fields' => array("SUM(COALESCE(ChargesPayment.amount,0)) AS 'reconciling_payments'")), */
|
||||
/* ), */
|
||||
|
||||
/* 'fields' => array('Entry.amount'), */
|
||||
/* 'group' => array('Entry.id'); */
|
||||
/* 'conditions' => $cond, */
|
||||
/* )); */
|
||||
|
||||
/* pr(compact('balance')); */
|
||||
|
||||
// pull up, since there should only be one
|
||||
//$reconciling = $reconciling[0];
|
||||
|
||||
// Add the calculated fields to Entry
|
||||
|
||||
/* $reconciling['applied'] = 0; */
|
||||
/* $reconciling['balance'] = $reconciling[0]['Entry']['amount']; */
|
||||
/* foreach ($reconciling AS $i => $entry) { */
|
||||
/* $reconciling['applied'] += $entry[0]['applied']; */
|
||||
/* unset($reconciling[$i]['Entry']); */
|
||||
/* } */
|
||||
/* $reconciling['balance'] -= $reconciling['applied']; */
|
||||
|
||||
/* pr(compact('reconciling')); */
|
||||
/* return $reconciling; */
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: stats
|
||||
* - Returns summary data from the requested ledger entry
|
||||
*/
|
||||
function stats($id, $cond = null) {
|
||||
|
||||
if (!isset($cond))
|
||||
$cond = array();
|
||||
|
||||
$cond[] = array('Entry.id' => $id);
|
||||
|
||||
$entry = $this->find('first', array('contain' => array('DoubleEntry.amount'),
|
||||
'fields' => array('Entry.type', 'Entry.crdr')));
|
||||
$entry['Entry'] += $entry['DoubleEntry'];
|
||||
$entry = $entry['Entry'];
|
||||
|
||||
$stats = array();
|
||||
foreach(array('charge', 'payment', 'debit', 'credit') AS $rtype) {
|
||||
$Rtype = ucfirst($rtype);
|
||||
|
||||
if (($rtype == 'charge' && $entry['type'] == 'PAYMENT') ||
|
||||
($rtype == 'payment' && $entry['type'] == 'CHARGE') ||
|
||||
($rtype == 'debit' && $entry['crdr'] == 'CREDIT') ||
|
||||
($rtype == 'credit' && $entry['crdr'] == 'DEBIT')) {
|
||||
$result = $this->find
|
||||
('first', array
|
||||
('link' =>
|
||||
array($Rtype =>
|
||||
array('fields' => array("SUM(Applied{$Rtype}.amount) AS applied"))),
|
||||
'fields' => array(),
|
||||
'conditions' => $cond,
|
||||
'group' => 'Entry.id',
|
||||
));
|
||||
//pr(compact('Rtype', 'result'));
|
||||
|
||||
$sumfld = $Rtype;
|
||||
$stats[$sumfld] = $result[0];
|
||||
if (!isset($stats[$sumfld]['applied']))
|
||||
$stats[$sumfld]['applied'] = 0;
|
||||
$stats[$sumfld]['unapplied'] = $entry['amount'] - $stats[$sumfld]['applied'];
|
||||
}
|
||||
}
|
||||
|
||||
return $stats;
|
||||
|
||||
/* $result = $this->find */
|
||||
/* ('first', array */
|
||||
/* ('link' => array('DoubleEntry' => array('fields' => array('amount')), */
|
||||
/* 'Credit' => array('fields' => array()), */
|
||||
/* 'Debit' => array('fields' => array())), */
|
||||
|
||||
/* 'fields' => array('Entry.crdr', */
|
||||
/* "SUM(AppliedDebit.amount) AS 'applied_debits'", */
|
||||
/* "SUM(AppliedCredit.amount) AS 'applied_credits'"), */
|
||||
|
||||
/* 'conditions' => $cond, */
|
||||
/* 'group' => 'Entry.id', */
|
||||
/* )); */
|
||||
|
||||
//pr(compact('result'));
|
||||
|
||||
$stats = array();
|
||||
if ($result['Entry']['crdr'] == 'DEBIT') {
|
||||
if ($result[0]['applied_debits'])
|
||||
die('INCONSISTENT DATABASE ENTRIES');
|
||||
$stats['applied'] = $result[0]['applied_credits'];
|
||||
}
|
||||
elseif ($result['Entry']['crdr'] == 'CREDIT') {
|
||||
if ($result[0]['applied_credits'])
|
||||
die('INCONSISTENT DATABASE ENTRIES');
|
||||
$stats['applied'] = $result[0]['applied_debits'];
|
||||
}
|
||||
|
||||
if (!isset($stats['applied']))
|
||||
$stats['applied'] = 0;
|
||||
$stats['unapplied'] = $result['DoubleEntry']['amount'] - $stats['applied'];
|
||||
|
||||
|
||||
/* $reconciled = $this->find */
|
||||
/* ('all', array */
|
||||
/* ('link' => array */
|
||||
/* ( */
|
||||
/* "Charge" => array */
|
||||
/* ('fields' => array */
|
||||
/* ('id', */
|
||||
/* "COALESCE(SUM(ChargesPayment.amount),0) AS 'reconciled'", */
|
||||
/* "Entry.amount - COALESCE(SUM(ChargesPayment.amount),0) AS 'balance'", */
|
||||
/* ), */
|
||||
/* ), */
|
||||
|
||||
/* "Payment" => array */
|
||||
/* ('fields' => array */
|
||||
/* ('id', */
|
||||
/* "COALESCE(SUM(ChargesPayment.amount),0) AS 'reconciled'", */
|
||||
/* "Entry.amount - COALESCE(SUM(ChargesPayment.amount),0) AS 'balance'", */
|
||||
/* ), */
|
||||
/* ), */
|
||||
/* ), */
|
||||
|
||||
/* 'conditions' => array(isset($cond) ? $cond : array(), */
|
||||
/* array('Entry.id' => $id)), */
|
||||
|
||||
/* 'group' => 'Entry.id', */
|
||||
/* )); */
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
}
|
||||
117
models/lease.php
117
models/lease.php
@@ -1,27 +1,6 @@
|
||||
<?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',
|
||||
@@ -30,7 +9,7 @@ class Lease extends AppModel {
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry',
|
||||
'DoubleEntry',
|
||||
);
|
||||
|
||||
|
||||
@@ -60,7 +39,7 @@ class Lease extends AppModel {
|
||||
|
||||
if (!isset($cond))
|
||||
$cond = array();
|
||||
$cond[] = array('LedgerEntry.lease_id' => $id);
|
||||
$cond[] = array('DoubleEntry.lease_id' => $id);
|
||||
|
||||
$A = new Account();
|
||||
$entries = $A->findLedgerEntries($this->accountId($id),
|
||||
@@ -89,7 +68,7 @@ class Lease extends AppModel {
|
||||
$A = new Account();
|
||||
$entries = $A->findLedgerEntries
|
||||
($A->securityDepositAccountID(),
|
||||
true, array('LedgerEntry.lease_id' => $id), $link);
|
||||
true, array('DoubleEntry.lease_id' => $id), $link);
|
||||
|
||||
/* pr(array('function' => 'Lease::findSecurityDeposits', */
|
||||
/* 'args' => compact('id', 'link'), */
|
||||
@@ -100,42 +79,6 @@ class Lease extends AppModel {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -153,7 +96,7 @@ class Lease extends AppModel {
|
||||
('all',
|
||||
array('link' =>
|
||||
array(// Models
|
||||
'LedgerEntry' => array
|
||||
'DoubleEntry' => array
|
||||
('Ledger' => array
|
||||
('fields' => array(),
|
||||
'Account' => array
|
||||
@@ -161,12 +104,12 @@ class Lease extends AppModel {
|
||||
'Ledger' => array
|
||||
('alias' => 'Lx',
|
||||
'fields' => array(),
|
||||
'LedgerEntry' => array
|
||||
'DoubleEntry' => array
|
||||
('alias' => 'LEx',
|
||||
'fields' => array(),
|
||||
'conditions' => array
|
||||
('LEx.effective_date = DATE_ADD(LedgerEntry.through_date, INTERVAL 1 day)',
|
||||
'LEx.lease_id = LedgerEntry.lease_id',
|
||||
('LEx.effective_date = DATE_ADD(DoubleEntry.through_date, INTERVAL 1 day)',
|
||||
'LEx.lease_id = DoubleEntry.lease_id',
|
||||
)
|
||||
),
|
||||
),
|
||||
@@ -219,7 +162,7 @@ class Lease extends AppModel {
|
||||
return false;
|
||||
if (count($entries) != 1)
|
||||
return null;
|
||||
return $entries[0]['LedgerEntry']['through_date'];
|
||||
return $entries[0]['DoubleEntry']['through_date'];
|
||||
}
|
||||
|
||||
|
||||
@@ -234,8 +177,8 @@ class Lease extends AppModel {
|
||||
|
||||
// Income / Receipt / Money
|
||||
// debit: A/R credit: Income <-- this entry
|
||||
// debit: Receipt credit: A/R <-- ReceiptLedgerEntry, below
|
||||
// debit: Money credit: Receipt <-- MoneyLedgerEntry, below
|
||||
// debit: Receipt credit: A/R <-- ReceiptDoubleEntry, below
|
||||
// debit: Money credit: Receipt <-- MoneyDoubleEntry, below
|
||||
|
||||
$query = array
|
||||
('link' => array
|
||||
@@ -250,43 +193,43 @@ class Lease extends AppModel {
|
||||
// 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',
|
||||
'DebitReconciliationDoubleEntry' =>
|
||||
array('alias' => 'ReceiptDoubleEntry',
|
||||
'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'),
|
||||
// which reconciles our ReceiptDoubleEntry debit
|
||||
'DebitReconciliationDoubleEntry' =>
|
||||
array('alias' => 'MoneyDoubleEntry',
|
||||
'linkalias' => 'MoneyDoubleEntryR',
|
||||
'fields' => array('SUM(COALESCE(MoneyDoubleEntryR.amount,0)) AS paid'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
'fields' => array('LedgerEntry.amount',
|
||||
'DATE_SUB(LedgerEntry.effective_date, INTERVAL 1 DAY) AS paid_through',
|
||||
'fields' => array('DoubleEntry.amount',
|
||||
'DATE_SUB(DoubleEntry.effective_date, INTERVAL 1 DAY) AS paid_through',
|
||||
),
|
||||
|
||||
'group' => 'LedgerEntry.id HAVING paid <> LedgerEntry.amount',
|
||||
'group' => 'DoubleEntry.id HAVING paid <> DoubleEntry.amount',
|
||||
|
||||
'conditions' => array(array('LedgerEntry.lease_id' => $id),
|
||||
array('Account.id' => $this->LedgerEntry->Ledger->Account->rentAccountID()),
|
||||
'conditions' => array(array('DoubleEntry.lease_id' => $id),
|
||||
array('Account.id' => $this->DoubleEntry->Ledger->Account->rentAccountID()),
|
||||
),
|
||||
'order' => array('LedgerEntry.effective_date',
|
||||
'order' => array('DoubleEntry.effective_date',
|
||||
),
|
||||
);
|
||||
|
||||
$rent = $this->LedgerEntry->find('first', $query);
|
||||
$rent = $this->DoubleEntry->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);
|
||||
$query['fields'] = 'DoubleEntry.through_date';
|
||||
$query['order'] = 'DoubleEntry.through_date DESC';
|
||||
$query['group'] = 'DoubleEntry.id';
|
||||
$rent = $this->DoubleEntry->find('first', $query);
|
||||
if ($rent)
|
||||
return $rent['LedgerEntry']['through_date'];
|
||||
return $rent['DoubleEntry']['through_date'];
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -495,7 +438,7 @@ class Lease extends AppModel {
|
||||
|
||||
$A = new Account();
|
||||
$stats = $A->stats($A->accountReceivableAccountID(), true,
|
||||
array('LedgerEntry.lease_id' => $id));
|
||||
array('DoubleEntry.lease_id' => $id));
|
||||
|
||||
// Pull to the top level and return
|
||||
$stats = $stats['Ledger'];
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
<?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'),
|
||||
@@ -14,32 +8,33 @@ class Ledger extends AppModel {
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry' => array(
|
||||
'DoubleEntry' => 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')),
|
||||
array('DoubleEntry.debit_ledger_id = %{MODEL_ALIAS}.id',
|
||||
'DoubleEntry.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__$})',
|
||||
'finderQuery' => 'SELECT `DoubleEntry`.*
|
||||
FROM pmgr_double_entries AS `DoubleEntry`
|
||||
WHERE DoubleEntry.debit_ledger_id = ({$__cakeID__$})
|
||||
OR DoubleEntry.credit_ledger_id = ({$__cakeID__$})',
|
||||
|
||||
'counterQuery' => ''
|
||||
),
|
||||
|
||||
'DebitLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'className' => 'DoubleEntry',
|
||||
'foreignKey' => 'debit_ledger_id',
|
||||
'dependent' => false,
|
||||
),
|
||||
'CreditLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'className' => 'DoubleEntry',
|
||||
'foreignKey' => 'credit_ledger_id',
|
||||
'dependent' => false,
|
||||
),
|
||||
@@ -124,7 +119,7 @@ class Ledger extends AppModel {
|
||||
'comment' => "Ledger Balance Forward",
|
||||
);
|
||||
|
||||
$carry_entry = new LedgerEntry();
|
||||
$carry_entry = new DoubleEntry();
|
||||
$carry_entry->create();
|
||||
if (!$carry_entry->save($carry_entry_data, false)) {
|
||||
return null;
|
||||
@@ -137,11 +132,11 @@ class Ledger extends AppModel {
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findLedgerEntries
|
||||
* function: ledgerEntries
|
||||
* - Returns an array of ledger entries that belong to a given
|
||||
* ledger. There is extra work done... see the LedgerEntry model.
|
||||
* ledger. There is extra work done... see the DoubleEntry model.
|
||||
*/
|
||||
function findLedgerEntries($id, $account_type = null, $cond = null, $link = null) {
|
||||
function ledgerEntries($id, $account_type = null, $cond = null, $link = null) {
|
||||
/* pr(array('function' => 'Ledger::findLedgerEntries', */
|
||||
/* 'args' => compact('id', 'account_type', 'cond', 'link'), */
|
||||
/* )); */
|
||||
@@ -177,7 +172,7 @@ class Ledger extends AppModel {
|
||||
'credit' => $stats['credits'],
|
||||
'balance' => $stats['balance']),
|
||||
|
||||
'LedgerEntry' => array('id' => null,
|
||||
'DoubleEntry' => array('id' => null,
|
||||
//'comment' => "Balance Forward from $date"),
|
||||
'comment' => "-- SUMMARY OF EXCLUDED ENTRIES --"),
|
||||
|
||||
@@ -188,7 +183,7 @@ class Ledger extends AppModel {
|
||||
));
|
||||
}
|
||||
|
||||
$entries = $this->LedgerEntry->findInLedgerContext($id, $account_type, $cond, $link);
|
||||
$entries = $this->DoubleEntry->findInLedgerContext($id, $account_type, $cond, $link);
|
||||
/* pr(array('function' => 'Ledger::findLedgerEntries', */
|
||||
/* 'args' => compact('id', 'account_type', 'cond', 'link'), */
|
||||
/* 'vars' => compact('ledger'), */
|
||||
@@ -214,23 +209,23 @@ class Ledger extends AppModel {
|
||||
('link' =>
|
||||
array(// Models
|
||||
'Account' => array('fields' => array()),
|
||||
//'LedgerEntry' => array('fields' => array()),
|
||||
'LedgerEntry' =>
|
||||
//'DoubleEntry' => array('fields' => array()),
|
||||
'DoubleEntry' =>
|
||||
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",
|
||||
array("SUM(IF(DoubleEntry.debit_ledger_id = Ledger.id,
|
||||
DoubleEntry.amount, NULL)) AS debits",
|
||||
"SUM(IF(DoubleEntry.credit_ledger_id = Ledger.id,
|
||||
DoubleEntry.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)
|
||||
IF(DoubleEntry.debit_ledger_id = Ledger.id, 1, -1),
|
||||
IF(DoubleEntry.credit_ledger_id = Ledger.id, 1, -1)
|
||||
) * IF(DoubleEntry.amount, DoubleEntry.amount, 0)
|
||||
) AS balance",
|
||||
"COUNT(LedgerEntry.id) AS entries"),
|
||||
"COUNT(DoubleEntry.id) AS entries"),
|
||||
'conditions' => $cond,
|
||||
'group' => 'Ledger.id',
|
||||
));
|
||||
|
||||
@@ -1,525 +0,0 @@
|
||||
<?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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +1,59 @@
|
||||
<?php
|
||||
class MonetarySource extends AppModel {
|
||||
|
||||
var $name = 'MonetarySource';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'name' => array('notempty'),
|
||||
'tillable' => array('boolean')
|
||||
);
|
||||
class Payment extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
'Customer',
|
||||
'Lease',
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry',
|
||||
'DoubleEntry',
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
'DoubleEntry',
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addPayment
|
||||
* - Adds a new payment
|
||||
*/
|
||||
|
||||
function addPayment($data, $customer_id, $lease_id = null, $reconcile = null) {
|
||||
// Create some models for convenience
|
||||
$A = new Account();
|
||||
|
||||
// Assume this will succeed
|
||||
$ret = true;
|
||||
|
||||
// Establish the key payment parameters
|
||||
$payment = array_intersect_key($data, array('stamp'=>1, 'type'=>1, 'name'=>1, 'amount'=>1,
|
||||
'data1'=>1, 'data2'=>1, 'data3'=>1, 'data4'=>1));
|
||||
$payment['customer_id'] = $customer_id;
|
||||
|
||||
// Create the payment entry, and reconcile the credit side
|
||||
// of the double-entry (which should be A/R) as a payment.
|
||||
$ids = $this->DoubleEntry->Ledger->Account->postDoubleEntry
|
||||
($payment,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($entry['account_id']),
|
||||
'credit_ledger_id' => $A->currentLedgerID($A->paymentAccountID())
|
||||
) + $entry,
|
||||
array('debit' => 'payment',
|
||||
'credit' => $reconcile)
|
||||
);
|
||||
|
||||
if ($ids['error'])
|
||||
$ret = false;
|
||||
|
||||
$payment = array_intersect_key($ids,
|
||||
array('payment_id'=>1,
|
||||
'split_payment_id'=>1));
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -48,7 +87,7 @@ class MonetarySource extends AppModel {
|
||||
*/
|
||||
|
||||
function nsf($id, $stamp = null) {
|
||||
pr(array('MonetarySource::nsf',
|
||||
pr(array('Payment::nsf',
|
||||
compact('id')));
|
||||
|
||||
$A = new Account();
|
||||
@@ -58,9 +97,9 @@ class MonetarySource extends AppModel {
|
||||
('first',
|
||||
array('contain' =>
|
||||
array(/* e3 */
|
||||
'LedgerEntry' =>
|
||||
'DoubleEntry' =>
|
||||
array('Transaction.id',
|
||||
'MonetarySource.id',
|
||||
'Payment.id',
|
||||
'Customer.id',
|
||||
'Lease.id',
|
||||
|
||||
@@ -87,7 +126,7 @@ class MonetarySource extends AppModel {
|
||||
),
|
||||
|
||||
/* e2 */
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
'DebitReconciliationDoubleEntry' =>
|
||||
array(/* e2 credit */
|
||||
'CreditLedger' => /* i.e. A/R Ledger */
|
||||
array('fields' => array(),
|
||||
@@ -102,11 +141,11 @@ class MonetarySource extends AppModel {
|
||||
/* e1 */
|
||||
// STUPID CakePHP bug screws up CLASS contains CLASS.
|
||||
// Use the same class, but with different name.
|
||||
'DebitReconciliationLedgerEntry2',
|
||||
'DebitReconciliationDoubleEntry2',
|
||||
),
|
||||
|
||||
/* e4 */
|
||||
'CreditReconciliationLedgerEntry' =>
|
||||
'CreditReconciliationDoubleEntry' =>
|
||||
array(/* e4 debit */
|
||||
'DebitLedger' => /* e.g. BANK Ledger */
|
||||
array('fields' => array('id'),
|
||||
@@ -120,7 +159,7 @@ class MonetarySource extends AppModel {
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array(array('MonetarySource.id' => $id)),
|
||||
'conditions' => array(array('Payment.id' => $id)),
|
||||
));
|
||||
pr($source);
|
||||
|
||||
@@ -131,9 +170,9 @@ class MonetarySource extends AppModel {
|
||||
|
||||
$t4_id = null;
|
||||
$t5_id = null;
|
||||
foreach ($source['LedgerEntry'] AS $e3) {
|
||||
foreach ($source['DoubleEntry'] AS $e3) {
|
||||
// We expect only a single e4 entry
|
||||
$e4 = $e3['CreditReconciliationLedgerEntry'];
|
||||
$e4 = $e3['CreditReconciliationDoubleEntry'];
|
||||
if (count($e4) < 1)
|
||||
continue;
|
||||
if (count($e4) > 1)
|
||||
@@ -149,7 +188,7 @@ class MonetarySource extends AppModel {
|
||||
$bank_account_id = $e4['DebitLedger']['account_id'];
|
||||
|
||||
// post new e5
|
||||
$e5_ids = $A->postLedgerEntry
|
||||
$e5_ids = $A->postDoubleEntry
|
||||
(array('transaction_id' => $t4_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($bank_account_id),
|
||||
@@ -172,7 +211,7 @@ class MonetarySource extends AppModel {
|
||||
// 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
|
||||
$e6_ids = $A->postDoubleEntry
|
||||
(array('transaction_id' => $t5_id),
|
||||
array('monetary_source_id' => $e3['monetary_source_id']),
|
||||
array('debit_ledger_id' => $A->currentLedgerID($nsf_account_id),
|
||||
@@ -184,7 +223,7 @@ class MonetarySource extends AppModel {
|
||||
'comment' => "NSF tracker; Monetary Source #{$id}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('LedgerEntry' =>
|
||||
(array('DoubleEntry' =>
|
||||
array('id' => $e5_ids['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
@@ -198,12 +237,12 @@ class MonetarySource extends AppModel {
|
||||
compact('e6_ids', 'amount')));
|
||||
|
||||
$t6_id = null;
|
||||
foreach ($e3['DebitReconciliationLedgerEntry'] AS $e2) {
|
||||
foreach ($e2['DebitReconciliationLedgerEntry2'] AS $e1) {
|
||||
foreach ($e3['DebitReconciliationDoubleEntry'] AS $e2) {
|
||||
foreach ($e2['DebitReconciliationDoubleEntry2'] AS $e1) {
|
||||
$amount = -1*$e1['Reconciliation']['amount'];
|
||||
|
||||
// post new e7
|
||||
$e7_ids = $A->postLedgerEntry
|
||||
$e7_ids = $A->postDoubleEntry
|
||||
(array('transaction_id' => $t6_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($receipt_account_id),
|
||||
@@ -215,12 +254,12 @@ class MonetarySource extends AppModel {
|
||||
'comment' => "NSF Receipt; Monetary Source #{$id}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('LedgerEntry' =>
|
||||
(array('DoubleEntry' =>
|
||||
array('id' => $e6_ids['id'],
|
||||
'amount' => $amount))),
|
||||
|
||||
'credit' => array
|
||||
(array('LedgerEntry' =>
|
||||
(array('DoubleEntry' =>
|
||||
array('id' => $e1['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
@@ -237,11 +276,11 @@ class MonetarySource extends AppModel {
|
||||
}
|
||||
|
||||
// Cheat for now
|
||||
$customer_id = $source['LedgerEntry'][0]['customer_id'];
|
||||
$customer_id = $source['DoubleEntry'][0]['customer_id'];
|
||||
$lease_id = null;
|
||||
|
||||
// post new e8
|
||||
$e8_ids = $A->postLedgerEntry
|
||||
$e8_ids = $A->postDoubleEntry
|
||||
(array('transaction_id' => $t6_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($ar_account_id),
|
||||
@@ -2,13 +2,11 @@
|
||||
class Reconciliation extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
'DebitLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
//'foreignKey' => 'credit_ledger_entry_id',
|
||||
'DebitEntry' => array(
|
||||
'className' => 'Entry',
|
||||
),
|
||||
'CreditLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
//'foreignKey' => 'credit_ledger_entry_id',
|
||||
'CreditEntry' => array(
|
||||
'className' => 'Entry',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<?php
|
||||
class Transaction extends AppModel {
|
||||
|
||||
var $name = 'Transaction';
|
||||
|
||||
var $validate = array(
|
||||
'stamp' => array('date')
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
'Customer',
|
||||
'Lease',
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry',
|
||||
'DoubleEntry',
|
||||
);
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class Transaction extends AppModel {
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addInvoice
|
||||
* - Adds a new invoice transaction
|
||||
* - Adds a new invoice invoice
|
||||
*/
|
||||
|
||||
function addInvoice($data, $customer_id, $lease_id = null) {
|
||||
@@ -31,32 +31,31 @@ class Transaction extends AppModel {
|
||||
// Assume this will succeed
|
||||
$ret = true;
|
||||
|
||||
// Establish the key invoice parameters
|
||||
$invoice = array_intersect_key($data, array('Invoice'=>1));
|
||||
$invoice['type'] = 'INVOICE';
|
||||
|
||||
// Determine the total charges on the invoice
|
||||
$grand_total = 0;
|
||||
foreach ($data['LedgerEntry'] AS $entry)
|
||||
$grand_total += $entry['amount'];
|
||||
$invoice['amount'] = 0;
|
||||
foreach ($data['DoubleEntry'] AS $entry)
|
||||
$invoice['amount'] += $entry['amount'];
|
||||
|
||||
// Go through the entered charges
|
||||
$invoice_transaction = array_intersect_key($data, array('Transaction'=>1, 'transaction_id'=>1));
|
||||
foreach ($data['LedgerEntry'] AS $entry) {
|
||||
foreach ($data['DoubleEntry'] 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)),
|
||||
$ids = $this->DoubleEntry->Ledger->Account->postDoubleEntry
|
||||
($invoice,
|
||||
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
|
||||
'credit_ledger_id' => $A->currentLedgerID($entry['account_id'])
|
||||
) + $entry
|
||||
);
|
||||
|
||||
if ($ids['error'])
|
||||
$ret = false;
|
||||
|
||||
$invoice_transaction = array_intersect_key($ids, array('transaction_id'=>1));
|
||||
$invoice = array_intersect_key($ids, array('invoice_id'=>1));
|
||||
}
|
||||
|
||||
return $ret;
|
||||
@@ -67,7 +66,7 @@ class Transaction extends AppModel {
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addReceipt
|
||||
* - Adds a new receipt transaction
|
||||
* - Adds a new receipt
|
||||
*/
|
||||
|
||||
function addReceipt($data, $customer_id, $lease_id = null) {
|
||||
@@ -77,32 +76,40 @@ class Transaction extends AppModel {
|
||||
// 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) {
|
||||
// Establish the key receipt parameters
|
||||
$receipt = array_intersect_key($data, array('stamp'=>1, 'type'=>1, 'name'=>1, 'amount'=>1,
|
||||
'data1'=>1, 'data2'=>1, 'data3'=>1, 'data4'=>1));
|
||||
$receipt['type'] = 'RECEIPT';
|
||||
|
||||
// Determine the total charges on the receipt
|
||||
$receipt['amount'] = 0;
|
||||
foreach ($data['DoubleEntry'] AS $entry)
|
||||
$receipt['amount'] += $entry['amount'];
|
||||
|
||||
// Go through the entered charges
|
||||
foreach ($data['DoubleEntry'] 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)),
|
||||
// of the double-entry (which should be A/R) as a receipt.
|
||||
$ids = $this->DoubleEntry->Ledger->Account->postDoubleEntry
|
||||
($receipt,
|
||||
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');
|
||||
'credit_ledger_id' => $A->currentLedgerID($A->receiptAccountID())
|
||||
) + $entry,
|
||||
array('debit' => 'receipt',
|
||||
'credit' => $reconcile)
|
||||
);
|
||||
|
||||
if ($ids['error'])
|
||||
$ret = false;
|
||||
|
||||
$receipt_transaction = array_intersect_key($ids,
|
||||
array('transaction_id'=>1,
|
||||
'split_transaction_id'=>1));
|
||||
$receipt = array_intersect_key($ids,
|
||||
array('receipt_id'=>1,
|
||||
'split_receipt_id'=>1));
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -50,10 +50,11 @@ echo '<div class="ledger-entry view">' . "\n";
|
||||
$transaction = $entry['Transaction'];
|
||||
$ledgers = array('debit' => $entry['DebitLedger'],
|
||||
'credit' => $entry['CreditLedger']);
|
||||
$source = $entry['MonetarySource'];
|
||||
$entries = array('debit' => $entry['DebitEntry'],
|
||||
'credit' => $entry['CreditEntry']);
|
||||
$customer = $entry['Customer'];
|
||||
$lease = $entry['Lease'];
|
||||
$entry = $entry['LedgerEntry'];
|
||||
$entry = $entry['DoubleEntry'];
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array('ID', $entry['id']);
|
||||
@@ -63,7 +64,7 @@ $rows[] = array('Transaction', $html->link('#'.$transaction['id'],
|
||||
$transaction['id'])));
|
||||
$rows[] = array('Timestamp', FormatHelper::datetime($transaction['stamp']));
|
||||
$rows[] = array('Effective', FormatHelper::date($entry['effective_date']));
|
||||
$rows[] = array('Through', FormatHelper::date($entry['through_date']));
|
||||
//$rows[] = array('Through', FormatHelper::date($entry['through_date']));
|
||||
$rows[] = array('Customer', (isset($customer['name'])
|
||||
? $html->link($customer['name'],
|
||||
array('controller' => 'customers',
|
||||
@@ -76,12 +77,6 @@ $rows[] = array('Lease', (isset($lease['id'])
|
||||
'action' => 'view',
|
||||
$lease['id']))
|
||||
: null));
|
||||
$rows[] = array('Monetary Source', (isset($source['name'])
|
||||
? $html->link($source['name'],
|
||||
array('controller' => 'monetary_sources',
|
||||
'action' => 'view',
|
||||
$source['id']))
|
||||
: null));
|
||||
$rows[] = array('Comment', $entry['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
@@ -95,35 +90,39 @@ echo $this->element('table',
|
||||
* LedgerEntry Info Box
|
||||
*/
|
||||
|
||||
echo '<div class="infobox">' . "\n";
|
||||
foreach ($ledgers AS $type => $ledger) {
|
||||
//pr($ledger);
|
||||
if (!$ledger['Account']['trackable'])
|
||||
continue;
|
||||
/* echo '<div class="infobox">' . "\n"; */
|
||||
/* foreach ($ledgers AS $type => $ledger) { */
|
||||
/* //pr($ledger); */
|
||||
/* if (!$ledger['Account']['trackable']) */
|
||||
/* continue; */
|
||||
|
||||
$applied_caption = "Transfers applied";
|
||||
$remaining_caption = "Unapplied amount";
|
||||
/* $applied_caption = "Transfers applied"; */
|
||||
/* $remaining_caption = "Unapplied amount"; */
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array($applied_caption,
|
||||
FormatHelper::currency($stats[$type]['amount_reconciled']));
|
||||
$rows[] = array($remaining_caption,
|
||||
FormatHelper::currency($stats[$type]['amount_remaining']));
|
||||
echo $this->element('table',
|
||||
array('class' => 'item summary',
|
||||
'caption' => "{$ledger['Account']['name']} Ledger Entry",
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value'),
|
||||
//'suppress_alternate_rows' => true,
|
||||
));
|
||||
}
|
||||
/* $rows = array(); */
|
||||
/* $rows[] = array($applied_caption, */
|
||||
/* FormatHelper::currency($stats[$type]['amount_reconciled'])); */
|
||||
/* $rows[] = array($remaining_caption, */
|
||||
/* FormatHelper::currency($stats[$type]['amount_remaining'])); */
|
||||
/* echo $this->element('table', */
|
||||
/* array('class' => 'item summary', */
|
||||
/* 'caption' => "{$ledger['Account']['name']} Ledger Entry", */
|
||||
/* 'rows' => $rows, */
|
||||
/* 'column_class' => array('field', 'value'), */
|
||||
/* //'suppress_alternate_rows' => true, */
|
||||
/* )); */
|
||||
/* } */
|
||||
|
||||
echo '</div>' . "\n";
|
||||
/* echo '</div>' . "\n"; */
|
||||
|
||||
echo ('<DIV CLASS="ledger-double-entry">' . "\n");
|
||||
foreach ($ledgers AS $type => $ledger) {
|
||||
$rows = array();
|
||||
|
||||
$rows[] = array('ID', $html->link('#' . $entries[$type]['id'],
|
||||
array('controller' => 'entries',
|
||||
'action' => 'view',
|
||||
$entries[$type]['id'])));
|
||||
$rows[] = array('Account', $html->link($ledger['Account']['name'],
|
||||
array('controller' => 'accounts',
|
||||
'action' => 'view',
|
||||
@@ -156,30 +155,6 @@ echo ('</DIV>' . "\n");
|
||||
echo '<div CLASS="detail supporting">' . "\n";
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Reconciliation Ledger Entries
|
||||
*/
|
||||
|
||||
foreach ($ledgers AS $type => $ledger) {
|
||||
if (!$ledger['Account']['trackable'])
|
||||
continue;
|
||||
|
||||
$caption = ('Applied transfers ' . ($ledger['Account']['ftype'] == $type ? 'out of' : 'into') .
|
||||
' Account: ' . $ledger['Account']['name']);
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'account_ftype' => $type,
|
||||
'reconcile_id' => $entry['id'],
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
('caption' => $caption,
|
||||
'grid_div_id' => $type.'_reconciliation_ledger_entries',
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
@@ -109,15 +109,20 @@ if (!isset($config['rows']) && !isset($collected_account_id)) {
|
||||
}
|
||||
|
||||
if (isset($reconcile_id)) {
|
||||
$config['action'] = 'reconcile';
|
||||
$grid->customData(compact('reconcile_id'))->limit(20);
|
||||
}
|
||||
|
||||
if (isset($collected_account_id)) {
|
||||
$config['action'] = 'collected';
|
||||
$grid->customData(compact('collected_account_id'))->limit(50);
|
||||
$account_id = $collected_account_id;
|
||||
$grid->limit(50);
|
||||
$grid->sortField('Last Payment');
|
||||
}
|
||||
|
||||
if (isset($entry_ids))
|
||||
$grid->id_list($entry_ids);
|
||||
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Customer', 'Unit'));
|
||||
143
views/elements/entries.ctp
Normal file
143
views/elements/entries.ctp
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['Transaction'] = array('index' => 'Transaction.id', 'formatter' => 'id');
|
||||
$cols['Entry'] = array('index' => 'Entry.id', 'formatter' => 'id');
|
||||
|
||||
$cols['Date'] = array('index' => 'Transaction.stamp', 'formatter' => 'date');
|
||||
$cols['Effective'] = array('index' => 'DoubleEntry.effective_date', 'formatter' => 'date');
|
||||
$cols['Through'] = array('index' => 'Entry.through_date', 'formatter' => 'date');
|
||||
|
||||
$cols['Account'] = array('index' => 'Account.name', 'formatter' => 'name');
|
||||
$cols['Cr/Dr'] = array('index' => 'Entry.crdr', 'formatter' => 'name');
|
||||
|
||||
$cols['Customer'] = array('index' => 'Customer.name', 'formatter' => 'longname');
|
||||
$cols['Lease'] = array('index' => 'Lease.number', 'formatter' => 'id');
|
||||
$cols['Unit'] = array('index' => 'Unit.name', 'formatter' => 'name');
|
||||
|
||||
$cols['Source'] = array('index' => 'Entry.name', 'formatter' => 'name');
|
||||
$cols['Comment'] = array('index' => 'Entry.comment', 'formatter' => 'comment', 'width'=>150);
|
||||
|
||||
$cols['Amount'] = array('index' => 'DoubleEntry.amount', 'formatter' => 'currency');
|
||||
|
||||
$cols['Last Payment'] = array('index' => 'last_paid', 'formatter' => 'date');
|
||||
$cols['Applied'] = array('index' => "applied", 'formatter' => 'currency');
|
||||
$cols['Sub-Total'] = array('index' => 'subtotal-DoubleEntry.amount', 'formatter' => 'currency', 'sortable' => false);
|
||||
|
||||
|
||||
// Since group_by_tx is a boolean, let's just get it
|
||||
// defined, regardless of whether the caller did so.
|
||||
// group_by_tx will cause all entry fields to be
|
||||
// invalidated, and will leave only the transaction
|
||||
// fields. Yes... the caller should just use the
|
||||
// transactions element instead, in theory. However,
|
||||
// it hasn't yet been implemented to the level of
|
||||
// this element, and additionally, the transactions
|
||||
// element will not allow for customer information
|
||||
// (rightly so, since it's a ledger_entry field).
|
||||
// However, at the current implementation, all ledger
|
||||
// entries of a transaction are for the same customer.
|
||||
// So... we allow it for now.
|
||||
if (!isset($group_by_tx))
|
||||
$group_by_tx = false;
|
||||
|
||||
// REVISIT <AP>: 20090715
|
||||
// If we really want to group by transaction, we need
|
||||
// a transaction listing, not a ledger_entry listing.
|
||||
// switch controllers... don't overload this one.
|
||||
$group_by_tx = false;
|
||||
|
||||
if (isset($transaction_id) || isset($reconcile_id))
|
||||
$grid->invalidFields('Transaction');
|
||||
|
||||
if ($group_by_tx)
|
||||
$grid->invalidFields('Entry');
|
||||
|
||||
if ($group_by_tx)
|
||||
$grid->invalidFields(array('Effective', 'Through'));
|
||||
|
||||
if (!isset($collected_account_id))
|
||||
$grid->invalidFields('Last Payment');
|
||||
|
||||
if (isset($account_ftype) || isset($ledger_id) || isset($account_id) || isset($ar_account))
|
||||
$grid->invalidFields(array('Debit Account', 'Credit Account'));
|
||||
else
|
||||
$grid->invalidFields('Account');
|
||||
|
||||
if (isset($no_account) || $group_by_tx || isset($collected_account_id))
|
||||
$grid->invalidFields(array('Account', 'Debit Account', 'Credit Account'));
|
||||
|
||||
if (isset($ledger_id) || isset($account_id) || isset($ar_account)) {
|
||||
$grid->invalidFields('Amount');
|
||||
$cols['Sub-Total']['index'] = 'subtotal-balance';
|
||||
} else {
|
||||
$grid->invalidFields(array('Debit', 'Credit'));
|
||||
$cols['Sub-Total']['index'] = 'subtotal-DoubleEntry.amount';
|
||||
}
|
||||
|
||||
// group_by_tx SHOULD wipe out Customer, but the reality
|
||||
// is that it works good at the present, so we'll leave it.
|
||||
if (isset($lease_id) || isset($customer_id))
|
||||
$grid->invalidFields(array('Customer'));
|
||||
|
||||
if (isset($lease_id) || $group_by_tx)
|
||||
$grid->invalidFields(array('Lease', 'Unit'));
|
||||
|
||||
if (!isset($reconcile_id) && !isset($collected_account_id))
|
||||
$grid->invalidFields('Applied');
|
||||
else
|
||||
$cols['Sub-Total']['index'] = 'subtotal-applied';
|
||||
|
||||
if (isset($account_ftype) || isset($collected_account_id))
|
||||
$grid->invalidFields('Sub-Total');
|
||||
|
||||
|
||||
// Now that columns are defined, establish basic grid parameters
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Date')
|
||||
->defaultFields(array('Entry', 'Date', 'Amount', 'Credit', 'Debit'));
|
||||
|
||||
|
||||
if (!isset($config['rows']) && !isset($collected_account_id)) {
|
||||
$config['action'] = 'ledger';
|
||||
$grid->limit(50);
|
||||
}
|
||||
|
||||
if (isset($reconcile_id)) {
|
||||
$config['action'] = 'reconcile';
|
||||
$grid->customData(compact('reconcile_id'))->limit(20);
|
||||
}
|
||||
|
||||
if (isset($collected_account_id)) {
|
||||
$config['action'] = 'collected';
|
||||
$account_id = $collected_account_id;
|
||||
$grid->limit(50);
|
||||
$grid->sortField('Last Payment');
|
||||
}
|
||||
|
||||
if (isset($entry_ids)) {
|
||||
unset($config['action']);
|
||||
$grid->id_list($entry_ids);
|
||||
}
|
||||
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Customer', 'Unit'));
|
||||
|
||||
// Include custom data
|
||||
$grid->customData(compact('ledger_id', 'account_id', 'ar_account',
|
||||
'account_type', 'account_ftype', 'monetary_source_id',
|
||||
'customer_id', 'lease_id', 'transaction_id', 'group_by_tx'));
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array('Transaction', 'Entry', 'Date', 'Effective', 'Last Payment',
|
||||
'Account', 'Cr/Dr',
|
||||
'Customer', 'Unit',
|
||||
'Comment',
|
||||
'Amount',
|
||||
'Applied', 'Sub-Total')
|
||||
);
|
||||
165
views/entries/view.ctp
Normal file
165
views/entries/view.ctp
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="entry view">' . "\n";
|
||||
|
||||
// The two entry ids, debit and credit, are actually individual
|
||||
// entries in separate accounts (each make up one of the two
|
||||
// entries required for "double entry"). The reconciling entries
|
||||
// are those on the opposite side of the ledger in the specific
|
||||
// account. For example, assume the double entry is:
|
||||
// debit: A/R credit: Cash amount: 55
|
||||
//
|
||||
// Then, our accounts might look like:
|
||||
//
|
||||
// RENT TAX A/R CASH BANK
|
||||
// ------- ------- ------- ------- -------
|
||||
// |20 | 20| | | <-- Unrelated
|
||||
// | | |20 20| | <-- Unrelated
|
||||
// | | | | |
|
||||
// |50 | 50| | | <-- Rent paid by this entry
|
||||
// | |5 5| | | <-- Tax paid by this entry
|
||||
// | | |55 55| | <-- THIS DOUBLE ENTRY
|
||||
// | | | | |
|
||||
// | | | |75 75| <-- Deposit includes this entry
|
||||
// | | | | |
|
||||
//
|
||||
// In this case, assume that THIS specific Entry is the A/R credit
|
||||
// of the Double Entry. We'll need to provide information on the
|
||||
// two A/R entries, 50 & 5, which are both debits, i.e. opposite
|
||||
// entries to the credit of A/R.
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Entry Detail Main Section
|
||||
*/
|
||||
|
||||
$account = $entry['Account'];
|
||||
$double = $entry['DoubleEntry'];
|
||||
$transaction = $double['Transaction'];
|
||||
$customer = $double['Customer'];
|
||||
$lease = $double['Lease'];
|
||||
$entry = $entry['Entry'];
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array('ID', $entry['id']);
|
||||
$rows[] = array('Transaction', $html->link('#'.$transaction['id'],
|
||||
array('controller' => 'transactions',
|
||||
'action' => 'view',
|
||||
$transaction['id'])));
|
||||
$rows[] = array('Timestamp', FormatHelper::datetime($transaction['stamp']));
|
||||
$rows[] = array('Effective', FormatHelper::date($double['effective_date']));
|
||||
$rows[] = array('Through', FormatHelper::date($entry['through_date']));
|
||||
$rows[] = array('Account', $html->link($account['name'],
|
||||
array('controller' => 'accounts',
|
||||
'action' => 'view',
|
||||
$account['id'])));
|
||||
$rows[] = array('Cr/Dr', ($entry['crdr'] .
|
||||
' (Matching ' . $entry['opposite_crdr'] . ': ' .
|
||||
$html->link('#'.$entry['matching_entry_id'],
|
||||
array('controller' => 'entries',
|
||||
'action' => 'view',
|
||||
$entry['matching_entry_id'])) .
|
||||
')'));
|
||||
$rows[] = array('Double Entry', $html->link('#'.$double['id'],
|
||||
array('controller' => 'double_entries',
|
||||
'action' => 'view',
|
||||
$double['id'])));
|
||||
$rows[] = array('Customer', (isset($customer['name'])
|
||||
? $html->link($customer['name'],
|
||||
array('controller' => 'customers',
|
||||
'action' => 'view',
|
||||
$customer['id']))
|
||||
: null));
|
||||
$rows[] = array('Lease', (isset($lease['id'])
|
||||
? $html->link('#'.$lease['id'],
|
||||
array('controller' => 'leases',
|
||||
'action' => 'view',
|
||||
$lease['id']))
|
||||
: null));
|
||||
$rows[] = array('Comment', $entry['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item entry detail',
|
||||
'caption' => 'Ledger Entry Detail',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value')));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Entry Info Box
|
||||
*/
|
||||
|
||||
echo '<div class="infobox">' . "\n";
|
||||
|
||||
$applied_caption = "Transfers applied";
|
||||
$remaining_caption = "Unapplied amount";
|
||||
|
||||
foreach ($reconciled['stats'] AS $Rtype => $stats) {
|
||||
$rtype = strtolower($Rtype);
|
||||
|
||||
$applied_caption = "Transfers applied";
|
||||
$remaining_caption = "Unapplied amount";
|
||||
/* $applied_caption = $Rtype . 's Applied'; */
|
||||
/* $remaining_caption = 'Remaining for ' . $Rtype . 's'; */
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array($applied_caption,
|
||||
'<SPAN id="'.$rtype.'-applied">' .
|
||||
FormatHelper::currency($stats['applied']) .
|
||||
'</SPAN>');
|
||||
$rows[] = array($remaining_caption,
|
||||
'<SPAN id="'.$rtype.'-unapplied">' .
|
||||
FormatHelper::currency($stats['unapplied']) .
|
||||
'</SPAN>');
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item summary',
|
||||
'caption' => $Rtype . 's',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value'),
|
||||
//'suppress_alternate_rows' => true,
|
||||
));
|
||||
}
|
||||
|
||||
echo '</div>' . "\n";
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Supporting Elements Section
|
||||
*/
|
||||
|
||||
echo '<div CLASS="detail supporting">' . "\n";
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Reconciliation Ledger Entries
|
||||
*/
|
||||
|
||||
foreach ($reconciled['entries'] AS $Rtype => $entries) {
|
||||
$rtype = strtolower($Rtype);
|
||||
|
||||
$caption = $Rtype . 's applied';
|
||||
echo $this->element('entries', array
|
||||
(// Element configuration
|
||||
'entry_ids' => $entries,
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
('caption' => $caption,
|
||||
'grid_div_id' => $rtype.'-entries',
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
/* End page div */
|
||||
echo '</div>' . "\n";
|
||||
@@ -91,6 +91,7 @@ class GridHelper extends AppHelper {
|
||||
= array_map(create_function('$data',
|
||||
'return $data["id"];'),
|
||||
$items);
|
||||
$this->jqGrid_options['action'] = 'idlist';
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,11 @@ echo ('<DIV CLASS="apply-deposit grid-selection-text">' .
|
||||
'</DIV>' . "\n");
|
||||
|
||||
|
||||
echo $form->create(null, array('id' => 'receipt-form',
|
||||
'url' => array('controller' => 'transactions',
|
||||
'action' => 'postReceipt')));
|
||||
echo $form->create(null, array('id' => 'apply-deposit-form',
|
||||
/* 'url' => array('controller' => 'lease', */
|
||||
/* 'action' => 'apply_deposit') */
|
||||
)
|
||||
);
|
||||
|
||||
echo $form->input("Customer.id",
|
||||
array('id' => 'customer-id',
|
||||
@@ -33,7 +35,7 @@ echo $form->input("Lease.id",
|
||||
'type' => 'hidden',
|
||||
'value' => $lease['id']));
|
||||
|
||||
echo $form->input("LedgerEntry.0.account_id",
|
||||
echo $form->input("LedgerEntry.Account.id",
|
||||
array('id' => 'account-id',
|
||||
'type' => 'hidden',
|
||||
'value' => $account['id']));
|
||||
@@ -47,7 +49,10 @@ echo $this->element('form_table',
|
||||
("stamp" => array('opts' => array('type' => 'text'),
|
||||
'between' => '<A HREF="#" ONCLICK="datepickerNow(\'TransactionStamp\'); return false;">Now</A>',
|
||||
),
|
||||
"amount" => array('prefix' => 'LedgerEntry.0'),
|
||||
"amount" => array('prefix' => 'LedgerEntry',
|
||||
'value' => min($lease['stats']['balance'],
|
||||
$deposit['summary']['balance']),
|
||||
),
|
||||
"comment" => array('opts' => array('size' => 50),
|
||||
),
|
||||
)));
|
||||
|
||||
74
views/leases/refund.ctp
Normal file
74
views/leases/refund.ctp
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="account deposit">' . "\n";
|
||||
echo '<H2>Perform Bank Deposit</H2>' . "\n";
|
||||
echo '<P>Make sure to select the checkboxes below for only those types of currency (Cash, Check, etc) which you intend to actually deposit (you can see all the individual items by dropping down the list below the checkbox). Then, select the Deposit Account where you will make the deposit, and click "Perform Deposit" to close the books on the selected currency types and reset them to a zero balance. On the next page, you will be provided with a deposit slip to prepare the actual deposit.' . "\n";
|
||||
echo '<P><BR>' . "\n";
|
||||
|
||||
//pr(compact('tillableAccount', 'depositableAccount'));
|
||||
|
||||
echo $form->create(null, array('id' => 'deposit-form',
|
||||
'url' => array('controller' => 'accounts',
|
||||
'action' => 'deposit')));
|
||||
|
||||
foreach ($tillableAccount AS $acct) {
|
||||
//$acct = $acct['Account'];
|
||||
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.checked',
|
||||
array(//'label' => $acct['Account']['name'],
|
||||
'type' => 'checkbox',
|
||||
'checked' => true,
|
||||
'value' => true,
|
||||
'label' => (" I have exactly " .
|
||||
FormatHelper::currency($acct['Account']['stats']['Ledger']['balance']) .
|
||||
" in " . ($acct['Account']['name'] === 'Cash'
|
||||
? 'Cash'
|
||||
: Inflector::pluralize($acct['Account']['name'])) .
|
||||
" and will be depositing it all.")
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.amount',
|
||||
array('type' => 'hidden',
|
||||
'value' => $acct['Account']['stats']['Ledger']['balance'],
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.account_id',
|
||||
array('type' => 'hidden',
|
||||
'value' => $acct['Account']['id'],
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.account_name',
|
||||
array('type' => 'hidden',
|
||||
'value' => $acct['Account']['name'],
|
||||
));
|
||||
echo "\n";
|
||||
|
||||
$grid_div_id = 'ledger_entries'.$acct['CurrentLedger']['id'].'-list';
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'ledger_id' => $acct['CurrentLedger']['id'],
|
||||
'no_account' => true,
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'grid_div_id' => $grid_div_id,
|
||||
'caption' => ('<A HREF="#" ONCLICK="$(\'#'.$grid_div_id.' .HeaderButton\').click();'.
|
||||
' return false;">Items in '.$acct['Account']['name'].' Ledger</A>'),
|
||||
'grid_setup' => array('hiddengrid' => true),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
$options = array();
|
||||
foreach ($depositableAccount AS $acct) {
|
||||
$options[$acct['Account']['id']] = $acct['Account']['name'];
|
||||
}
|
||||
|
||||
echo $form->input('Deposit.Account.id', array('label' => 'Deposit Account ',
|
||||
'options' => $options));
|
||||
echo $form->end('Perform Deposit');
|
||||
|
||||
/* End page div */
|
||||
echo '</div>' . "\n";
|
||||
Reference in New Issue
Block a user