git-svn-id: file:///svn-source/pmgr/branches/invoice_receipt_20090629@307 97e9348a-65ac-dc4b-aefc-98561f571b83
831 lines
28 KiB
PHP
831 lines
28 KiB
PHP
<?php
|
|
class Account extends AppModel {
|
|
|
|
var $name = 'Account';
|
|
var $validate = array(
|
|
'id' => array('numeric'),
|
|
'name' => array('notempty'),
|
|
'external_name' => array('notempty')
|
|
);
|
|
|
|
var $hasOne = array(
|
|
'CurrentLedger' => array(
|
|
'className' => 'Ledger',
|
|
// REVISIT <AP> 20090702:
|
|
// I would prefer this statement, which has no
|
|
// engine specific code. However, it doesn't
|
|
// work with the Linkable behavior. I need to
|
|
// look into that, just not right now.
|
|
//'conditions' => array('CurrentLedger.close_id' => null),
|
|
'conditions' => array('CurrentLedger.close_id IS NULL'),
|
|
),
|
|
);
|
|
|
|
var $hasMany = array(
|
|
'Ledger',
|
|
);
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: type
|
|
* - Returns the type of this account
|
|
*/
|
|
function type($id) {
|
|
$this->cacheQueries = true;
|
|
$account = $this->find('first', array
|
|
('recursive' => -1,
|
|
'fields' => array('type'),
|
|
'conditions' => array(array('Account.id' => $id)),
|
|
));
|
|
$this->cacheQueries = false;
|
|
|
|
return $account['Account']['type'];
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: fundamentalType
|
|
* - Returns the fundmental type of the account, credit or debit
|
|
*/
|
|
function fundamentalType($id_or_type) {
|
|
if (is_numeric($id_or_type))
|
|
$type = $this->type($id_or_type);
|
|
else
|
|
$type = $id_or_type;
|
|
|
|
// Asset and Expense accounts are debit accounts
|
|
if (in_array(strtoupper($type), array('ASSET', 'EXPENSE')))
|
|
return 'debit';
|
|
|
|
// Otherwise, it's a credit account
|
|
return 'credit';
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: fundamentalOpposite
|
|
* - Returns the opposite fundmental type of the account, credit or debit
|
|
*/
|
|
function fundamentalOpposite($id_or_type) {
|
|
if (in_array(strtolower($id_or_type), array('credit', 'debit')))
|
|
$fund = $id_or_type;
|
|
else
|
|
$fund = $this->fundamentalType($id_or_type);
|
|
|
|
if ($fund == 'debit')
|
|
return 'credit';
|
|
|
|
return 'debit';
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: name
|
|
* - Returns the name of this account
|
|
*/
|
|
function name($id) {
|
|
$this->cacheQueries = true;
|
|
$account = $this->find('first', array
|
|
('recursive' => -1,
|
|
'fields' => array('name'),
|
|
'conditions' => array(array('Account.id' => $id)),
|
|
));
|
|
$this->cacheQueries = false;
|
|
|
|
return $account['Account']['name'];
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: Account IDs
|
|
* - Returns the ID of the desired account
|
|
*/
|
|
|
|
function securityDepositAccountID() { return $this->nameToID('Security Deposit'); }
|
|
function rentAccountID() { return $this->nameToID('Rent'); }
|
|
function lateChargeAccountID() { return $this->nameToID('Late Charge'); }
|
|
function taxAccountID() { return $this->nameToID('Tax'); }
|
|
function accountReceivableAccountID() { return $this->nameToID('A/R'); }
|
|
function cashAccountID() { return $this->nameToID('Cash'); }
|
|
function checkAccountID() { return $this->nameToID('Check'); }
|
|
function moneyOrderAccountID() { return $this->nameToID('Money Order'); }
|
|
function pettyCashAccountID() { return $this->nameToID('Petty Cash'); }
|
|
function invoiceAccountID() { return $this->nameToID('Invoice'); }
|
|
function receiptAccountID() { return $this->nameToID('Receipt'); }
|
|
function badDebtAccountID() { return $this->nameToID('Bad Debt'); }
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: fundamentalAccounts
|
|
* - Returns an array of accounts by their fundamental type
|
|
*/
|
|
|
|
function fundamentalAccounts($ftype) {
|
|
$this->cacheQueries = true;
|
|
$account = $this->find('all', array
|
|
('contain' => array('CurrentLedger'),
|
|
'fields' => array('Account.id', 'Account.type', 'Account.name', 'CurrentLedger.id'),
|
|
'conditions' => array('Account.type' => strtoupper($ftype))
|
|
));
|
|
$this->cacheQueries = false;
|
|
|
|
return $account;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: relatedAccounts
|
|
* - Returns an array of accounts related by similar attributes
|
|
*/
|
|
|
|
function relatedAccounts($attribute, $extra = null) {
|
|
$this->cacheQueries = true;
|
|
$account = $this->find('all', array
|
|
('contain' => array('CurrentLedger'),
|
|
'fields' => array('Account.id', 'Account.type', 'Account.name', 'CurrentLedger.id'),
|
|
'conditions' => array('Account.'.$attribute => true),
|
|
'order' => array('Account.name'),
|
|
) + (isset($extra) ? $extra : array())
|
|
);
|
|
$this->cacheQueries = false;
|
|
|
|
return $account;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: chargeAccounts
|
|
* - Returns an array of accounts suitable for charges
|
|
*/
|
|
|
|
function chargeAccounts() {
|
|
// Get all accounts that support charges
|
|
$accounts = $this->relatedAccounts('chargeable', array('order' => 'name'));
|
|
|
|
// Rearrange to be of the form (id => name)
|
|
$charge_accounts = array();
|
|
foreach ($accounts AS $acct) {
|
|
$charge_accounts[$acct['Account']['id']] = $acct['Account']['name'];
|
|
}
|
|
|
|
return $charge_accounts;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: paymentAccounts
|
|
* - Returns an array of accounts suitable for payments
|
|
*/
|
|
|
|
function paymentAccounts() {
|
|
// Get all accounts that support payments
|
|
$accounts = $this->relatedAccounts('payable', array('order' => 'name'));
|
|
|
|
// Rearrange to be of the form (id => name)
|
|
$payment_accounts = array();
|
|
foreach ($accounts AS $acct) {
|
|
$payment_accounts[$acct['Account']['id']] = $acct['Account']['name'];
|
|
}
|
|
|
|
return $payment_accounts;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: currentLedgerID
|
|
* - Returns the current ledger ID of the account
|
|
*/
|
|
function currentLedgerID($id) {
|
|
$this->cacheQueries = true;
|
|
$item = $this->find('first', array
|
|
('contain' => 'CurrentLedger',
|
|
'conditions' => array('Account.id' => $id),
|
|
));
|
|
$this->cacheQueries = false;
|
|
return $item['CurrentLedger']['id'];
|
|
}
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: ledgers
|
|
* - Returns an array of ledger ids from the given account
|
|
*/
|
|
function ledgers($id, $all = false) {
|
|
if ($all) {
|
|
$contain = array('Ledger' => array('fields' => array('Ledger.id')));
|
|
} else {
|
|
$contain = array('CurrentLedger' => array('fields' => array('CurrentLedger.id')));
|
|
}
|
|
|
|
$this->cacheQueries = true;
|
|
$account = $this->find('first', array
|
|
('contain' => $contain,
|
|
'fields' => array(),
|
|
'conditions' => array(array('Account.id' => $id)),
|
|
));
|
|
$this->cacheQueries = false;
|
|
|
|
if ($all) {
|
|
$ledger_ids = array();
|
|
foreach ($account['Ledger'] AS $ledger)
|
|
array_push($ledger_ids, $ledger['id']);
|
|
}
|
|
else {
|
|
$ledger_ids = array($account['CurrentLedger']['id']);
|
|
}
|
|
|
|
/* pr(array('function' => 'Account::ledgers', */
|
|
/* 'args' => compact('id', 'all'), */
|
|
/* 'return' => $ledger_ids)); */
|
|
|
|
return $ledger_ids;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: closeCurrentLedger
|
|
* - Closes the current account ledger, and opens a new one
|
|
* with the old balance carried forward.
|
|
*/
|
|
function closeCurrentLedger($id = null, $close_id = null) {
|
|
$contain = array('CurrentLedger' => array('fields' => array('CurrentLedger.id')));
|
|
|
|
if (!$close_id) {
|
|
$close = new Close();
|
|
$close->create();
|
|
if (!$close->save(array('stamp' => null), false)) {
|
|
return false;
|
|
}
|
|
$close_id = $close->id;
|
|
}
|
|
|
|
$this->cacheQueries = true;
|
|
$account = $this->find('all', array
|
|
('contain' => $contain,
|
|
'fields' => array(),
|
|
'conditions' =>
|
|
$id ? array(array('Account.id' => $id)) : array()
|
|
));
|
|
$this->cacheQueries = false;
|
|
//pr(compact('id', 'account'));
|
|
|
|
foreach ($account AS $acct) {
|
|
if (!$this->Ledger->closeLedger($acct['CurrentLedger']['id'], $close_id))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: findLedgerEntries
|
|
* - Returns an array of ledger entries that belong to the given
|
|
* account, either just from the current ledger, or from all ledgers.
|
|
*/
|
|
function findLedgerEntries($id, $all = false, $cond = null, $link = null) {
|
|
/* pr(array('function' => 'Account::findLedgerEntries', */
|
|
/* 'args' => compact('id', 'all', 'cond', 'link'), */
|
|
/* )); */
|
|
|
|
$entries = array();
|
|
foreach ($this->ledgers($id, $all) AS $ledger_id) {
|
|
$ledger_entries = $this->Ledger->findLedgerEntries
|
|
($ledger_id, $this->type($id), $cond, $link);
|
|
$entries = array_merge($entries, $ledger_entries);
|
|
}
|
|
|
|
$stats = $this->stats($id, $all, $cond);
|
|
$entries = array('Entries' => $entries,
|
|
'summary' => $stats['Ledger']);
|
|
|
|
/* pr(array('function' => 'Account::findLedgerEntries', */
|
|
/* 'args' => compact('id', 'all', 'cond', 'link'), */
|
|
/* 'vars' => compact('stats'), */
|
|
/* 'return' => compact('entries'), */
|
|
/* )); */
|
|
|
|
return $entries;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: findLedgerEntriesRelatedToAccount
|
|
* - Returns an array of ledger entries that belong to the given
|
|
* account, and are related to a specific account, either just from
|
|
* the current ledger, or from all ledgers.
|
|
*/
|
|
function findLedgerEntriesRelatedToAccount($id, $rel_ids, $all = false, $cond = null, $link = null) {
|
|
/* pr(array('function' => 'Account::findLedgerEntriesRelatedToAccount', */
|
|
/* 'args' => compact('id', 'rel_ids', 'all', 'cond', 'link'), */
|
|
/* )); */
|
|
|
|
if (!isset($cond))
|
|
$cond = array();
|
|
if (!is_array($rel_ids))
|
|
$rel_ids = array($rel_ids);
|
|
|
|
$ledger_ids = array();
|
|
foreach ($rel_ids AS $rel_id)
|
|
$ledger_ids = array_merge($ledger_ids, $this->ledgers($rel_id));
|
|
|
|
array_push($cond, $this->Ledger->LedgerEntry->conditionEntryAsCreditOrDebit($ledger_ids));
|
|
$entries = $this->findLedgerEntries($id, $all, $cond, $link);
|
|
|
|
/* pr(array('function' => 'Account::findLedgerEntriesRelatedToAccount', */
|
|
/* 'args' => compact('id', 'relid', 'all', 'cond', 'link'), */
|
|
/* 'vars' => compact('ledger_ids'), */
|
|
/* 'return' => compact('entries'), */
|
|
/* )); */
|
|
return $entries;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: findUnreconciledLedgerEntries
|
|
* - Returns ledger entries that are not yet reconciled
|
|
* (such as charges not paid).
|
|
*/
|
|
|
|
function findUnreconciledLedgerEntries($id = null, $fundamental_type = null, $cond = null) {
|
|
if (!isset($cond))
|
|
$cond = array();
|
|
$cond[] = array('Account.id' => $id);
|
|
|
|
foreach (($fundamental_type
|
|
? array($fundamental_type)
|
|
: array('debit', 'credit')) AS $fund) {
|
|
$ucfund = ucfirst($fund);
|
|
$unreconciled[$fund]['entry'] = $this->find
|
|
('all', array
|
|
('link' => array
|
|
('Ledger' => array
|
|
('fields' => array(),
|
|
"LedgerEntry" => array
|
|
('class' => "{$ucfund}LedgerEntry",
|
|
'fields' => array('id', 'customer_id', 'lease_id', 'amount'),
|
|
"ReconciliationLedgerEntry" => array
|
|
('class' => "{$ucfund}ReconciliationLedgerEntry",
|
|
'fields' => array
|
|
("COALESCE(SUM(Reconciliation.amount),0) AS 'reconciled'",
|
|
"LedgerEntry.amount - COALESCE(SUM(Reconciliation.amount),0) AS 'balance'",
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
'group' => ("LedgerEntry.id" .
|
|
" HAVING LedgerEntry.amount" .
|
|
" <> COALESCE(SUM(Reconciliation.amount),0)"),
|
|
'conditions' => $cond,
|
|
'fields' => array(),
|
|
));
|
|
$balance = 0;
|
|
foreach ($unreconciled[$fund]['entry'] AS &$entry) {
|
|
$entry = array_merge(array_diff_key($entry["LedgerEntry"], array(0=>true)),
|
|
$entry[0]);
|
|
$balance += $entry['balance'];
|
|
}
|
|
$unreconciled[$fund]['balance'] = $balance;
|
|
}
|
|
|
|
return $unreconciled;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: reconcileNewLedgerEntry
|
|
* - Returns which ledger entries a new credit/debit would
|
|
* reconcile, and how much.
|
|
*
|
|
* - REVISIT <AP> 20090617
|
|
* This should be subject to different algorithms, such
|
|
* as apply to oldest charges first, newest first, to fees
|
|
* before rent, etc. Until we get there, I'll hardcode
|
|
* whatever algorithm is simplest.
|
|
*/
|
|
|
|
function reconcileNewLedgerEntry($id, $fundamental_type, $amount, $cond = null) {
|
|
$ofund = $this->fundamentalOpposite($fundamental_type);
|
|
$unreconciled = array($ofund => array('entry'=>array(), 'balance'=>0));
|
|
$applied = 0;
|
|
|
|
// if there is no money in the entry, it can reconcile nothing
|
|
// don't bother wasting time sifting ledger entries.
|
|
if ($amount > 0) {
|
|
$unreconciled = $this->findUnreconciledLedgerEntries($id, $ofund, $cond);
|
|
|
|
foreach ($unreconciled[$ofund]['entry'] AS $i => &$entry) {
|
|
// Determine if amount is sufficient to cover the entry
|
|
if ($amount > $entry['balance'])
|
|
$apply = $entry['balance'];
|
|
elseif ($amount > 0)
|
|
$apply = $amount;
|
|
else {
|
|
unset($unreconciled[$ofund]['entry'][$i]);
|
|
continue;
|
|
}
|
|
|
|
$entry['applied'] = $apply;
|
|
$entry['reconciled'] += $apply;
|
|
$entry['balance'] -= $apply;
|
|
$applied += $apply;
|
|
$amount -= $apply;
|
|
}
|
|
}
|
|
|
|
$unreconciled[$ofund]['unapplied'] = $amount;
|
|
$unreconciled[$ofund]['applied'] = $applied;
|
|
$unreconciled[$ofund]['balance'] -= $applied;
|
|
return $unreconciled;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: postLedgerEntry
|
|
* -
|
|
* transaction_data
|
|
* - transaction_id (optional... if set all else is ignored)
|
|
* - Transaction
|
|
* - stamp (optional... otherwise NOW is used)
|
|
* - comment
|
|
*
|
|
* monetary_source_data
|
|
* - monetary_source_id (optional... if set all else is ignored)
|
|
* - account_name
|
|
* - MonetarySource
|
|
* - name
|
|
*/
|
|
|
|
function postLedgerEntry($transaction_data,
|
|
$monetary_data,
|
|
$entry_data,
|
|
$reconcile = null) {
|
|
// Create some models for convenience
|
|
$A = new Account();
|
|
|
|
//pr(compact('transaction_data', 'monetary_data', 'entry_data', 'reconcile'));
|
|
|
|
// Automatically figure out the customer if we have the lease
|
|
if (isset($entry_data['lease_id']) && !isset($entry_data['customer_id'])) {
|
|
$L = new Lease();
|
|
$L->recursive = -1;
|
|
$lease = $L->read(null, $entry_data['lease_id']);
|
|
$entry_data['customer_id'] = $lease['Lease']['customer_id'];
|
|
}
|
|
|
|
if (!isset($entry_data['lease_id']))
|
|
$entry_data['lease_id'] = null;
|
|
|
|
if (!isset($entry_data['customer_id']))
|
|
$entry_data['customer_id'] = null;
|
|
|
|
// Get the Transaction squared away
|
|
if (isset($transaction_data['transaction_id'])) {
|
|
$transaction_data
|
|
= array_intersect_key($transaction_data,
|
|
array('transaction_id'=>1,
|
|
'split_transaction_id'=>1));
|
|
}
|
|
elseif (isset($transaction_data['Transaction'])) {
|
|
$transaction_data
|
|
= array_intersect_key($transaction_data,
|
|
array('Transaction'=>1,
|
|
'split_transaction_id'=>1));
|
|
}
|
|
else {
|
|
$transaction_data = array('Transaction'=>array('stamp' => null));
|
|
}
|
|
|
|
|
|
// Get the Monetary Source squared away
|
|
if (isset($monetary_data)) {
|
|
if (!isset($monetary_data['monetary_source_id'])) {
|
|
|
|
// Convert Account ID to name or vice versa
|
|
if (isset($monetary_data['account_id'])) {
|
|
$monetary_data['account_name'] = $A->name($monetary_data['account_id']);
|
|
} elseif (isset($monetary_data['account_name'])) {
|
|
$monetary_data['account_id'] = $A->nameToID($monetary_data['account_name']);
|
|
}
|
|
|
|
if ($monetary_data['account_id'] == $A->cashAccountID()) {
|
|
// No distinguishing features of Cash, just
|
|
// use the shared monetary source
|
|
$monetary_data['monetary_source_id'] =
|
|
$this->Ledger->LedgerEntry->MonetarySource->nameToID('Cash');
|
|
}
|
|
}
|
|
|
|
if (isset($monetary_data['monetary_source_id'])) {
|
|
$monetary_data
|
|
= array_intersect_key($monetary_data,
|
|
array('monetary_source_id'=>1));
|
|
}
|
|
else {
|
|
// The monetary source needs to be unique
|
|
// Create a new one dedicated to this entry
|
|
// Give it a fancy name based on the check number
|
|
$monetary_data['MonetarySource']['name'] = $monetary_data['account_name'];
|
|
if ($monetary_data['account_name'] === $A->name($A->checkAccountID()) ||
|
|
$monetary_data['account_name'] === $A->name($A->moneyOrderAccountID())) {
|
|
$monetary_data['MonetarySource']['name'] .=
|
|
' #' . $monetary_data['MonetarySource']['data1'];
|
|
}
|
|
|
|
$monetary_data
|
|
= array_intersect_key($monetary_data,
|
|
array('MonetarySource'=>1));
|
|
}
|
|
}
|
|
else {
|
|
$monetary_data = array();
|
|
}
|
|
|
|
// Make sure to clean out any unwanted data from the entry
|
|
$entry_data
|
|
= array_diff_key($entry_data,
|
|
array('transaction_id'=>1, 'Transaction'=>1,
|
|
'monetary_source_id'=>1, 'MonetarySource'=>1));
|
|
|
|
// Then add in the transaction and monetary source data
|
|
//pr(compact('transaction_data', 'monetary_data', 'entry_data'));
|
|
if (isset($transaction_data))
|
|
$entry_data += $transaction_data;
|
|
if (isset($monetary_data))
|
|
$entry_data += $monetary_data;
|
|
|
|
// Set up the debit ledger id
|
|
if (!isset($entry_data['debit_ledger_id'])) {
|
|
$entry_data['debit_ledger_id'] =
|
|
(isset($entry_data['debit_account_id'])
|
|
? $A->currentLedgerID($entry_data['debit_account_id'])
|
|
: (isset($entry_data['debit_account_name'])
|
|
? $A->currentLedgerID($A->nameToID($entry_data['debit_account_name']))
|
|
: null
|
|
)
|
|
);
|
|
}
|
|
|
|
// Set up the credit ledger id
|
|
if (!isset($entry_data['credit_ledger_id'])) {
|
|
$entry_data['credit_ledger_id'] =
|
|
(isset($entry_data['credit_account_id'])
|
|
? $A->currentLedgerID($entry_data['credit_account_id'])
|
|
: (isset($entry_data['credit_account_name'])
|
|
? $A->currentLedgerID($A->nameToID($entry_data['credit_account_name']))
|
|
: null
|
|
)
|
|
);
|
|
}
|
|
|
|
//pr(array('pre-save', compact('entry_data')));
|
|
// Create it!
|
|
$new_entry = new LedgerEntry();
|
|
$new_entry->create();
|
|
if (!$new_entry->saveAll($entry_data, array('validate'=>false))) {
|
|
return array('error' => true);
|
|
}
|
|
|
|
// See if the user has entered some sort of non-array
|
|
// for the reconcile parameter.
|
|
if (isset($reconcile) && is_bool($reconcile) && $reconcile) {
|
|
$reconcile = array('debit' => true, 'credit' => true);
|
|
}
|
|
elseif (isset($reconcile) && $reconcile == 'invoice') {
|
|
$reconcile = array('credit' => 'invoice');
|
|
}
|
|
elseif (isset($reconcile) && $reconcile == 'receipt') {
|
|
$reconcile = array('debit' => 'receipt');
|
|
}
|
|
elseif (!isset($reconcile) || !is_array($reconcile)) {
|
|
$reconcile = array();
|
|
}
|
|
|
|
// Reconcile the new entry... assume we'll have success
|
|
$err = false;
|
|
foreach (array_intersect_key($reconcile, array('credit'=>1,'debit'=>1))
|
|
AS $dc_type => $reconcile_set) {
|
|
if (!isset($reconcile_set) || (is_bool($reconcile_set) && !$reconcile_set))
|
|
continue;
|
|
|
|
if ($reconcile_set === 'receipt') {
|
|
$C = new Customer();
|
|
$reconciled = $C->reconcileNewLedgerEntry($entry_data['customer_id'],
|
|
$this->fundamentalOpposite($dc_type),
|
|
$entry_data['amount']);
|
|
|
|
/* pr(array("reconcile receipt", */
|
|
/* compact('reconciled', 'split_transaction', 'transaction_data'))); */
|
|
$split_transaction = array_intersect_key($transaction_data,
|
|
array('Transaction'=>1,
|
|
'split_transaction_id'=>1));
|
|
|
|
if (isset($split_transaction['split_transaction_id']))
|
|
$split_transaction['transaction_id'] = $split_transaction['split_transaction_id'];
|
|
|
|
if (is_array($reconciled) && count($reconciled[$dc_type]['entry'])) {
|
|
foreach ($reconciled[$dc_type]['entry'] AS $rec) {
|
|
//pr(compact('rec', 'split_transaction'));
|
|
if (!$rec['applied'])
|
|
continue;
|
|
|
|
// Create an entry to handle the splitting of the funds ("Payment")
|
|
// and reconcile against the new cash/check/etc entry created above,
|
|
// as well as the A/R account.
|
|
|
|
// Payment must debit the Receipt ledger, and credit the A/R ledger
|
|
// debit: Receipt credit: A/R
|
|
$ids = $this->postLedgerEntry
|
|
($split_transaction,
|
|
null,
|
|
array('debit_ledger_id' => $A->currentLedgerID($A->receiptAccountID()),
|
|
'credit_ledger_id' => $A->currentLedgerID($A->accountReceivableAccountID()),
|
|
'amount' => $rec['applied'],
|
|
'lease_id' => $rec['lease_id'],
|
|
'customer_id' => $rec['customer_id'],
|
|
),
|
|
array('debit' => array(array('LedgerEntry' => array('id' => $new_entry->id,
|
|
'amount' => $rec['applied']))),
|
|
'credit' => array(array('LedgerEntry' => array('id' => $rec['id'],
|
|
'amount' => $rec['applied']))))
|
|
);
|
|
// Keep using the same split transaction for all reconciled entries
|
|
$split_transaction = array_intersect_key($ids, array('transaction_id'=>1));
|
|
//pr(compact('ids', 'split_transaction'));
|
|
}
|
|
|
|
//pr("end reconciled is array");
|
|
}
|
|
|
|
//pr("end reconcile receipt");
|
|
}
|
|
|
|
if (is_array($reconcile_set)) {
|
|
//pr("reconcile_set is array");
|
|
foreach ($reconcile_set AS $reconcile_entry) {
|
|
if (!isset($reconcile_entry['LedgerEntry']['id']))
|
|
continue;
|
|
|
|
$amount = $reconcile_entry['LedgerEntry']['amount'];
|
|
if (!$amount)
|
|
continue;
|
|
|
|
if ($dc_type == 'debit') {
|
|
$debit_ledger_entry_id = $new_entry->id;
|
|
$credit_ledger_entry_id = $reconcile_entry['LedgerEntry']['id'];
|
|
}
|
|
else {
|
|
$debit_ledger_entry_id = $reconcile_entry['LedgerEntry']['id'];
|
|
$credit_ledger_entry_id = $new_entry->id;
|
|
}
|
|
|
|
$R = new Reconciliation();
|
|
$R->create();
|
|
if (!$R->save(compact('amount',
|
|
'debit_ledger_entry_id',
|
|
'credit_ledger_entry_id'), false))
|
|
$err = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
$new_entry->recursive = -1;
|
|
$new_entry->read();
|
|
//pr(array('post-save', $entry->data));
|
|
|
|
$ret = array
|
|
('error' => $err,
|
|
'id' => $new_entry->data['LedgerEntry']['id'],
|
|
'transaction_id' => $new_entry->data['LedgerEntry']['transaction_id'],
|
|
'monetary_source_id' => $new_entry->data['LedgerEntry']['monetary_source_id']);
|
|
|
|
if (isset($split_transaction['transaction_id']))
|
|
$ret['split_transaction_id'] = $split_transaction['transaction_id'];
|
|
|
|
return $ret;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: closeAndDeposit
|
|
* - Closes the current set of ledgers, transferring
|
|
* their balances to specified ledger.
|
|
*/
|
|
function closeAndDeposit($set, $deposit_account_id) {
|
|
|
|
$close = new Close();
|
|
$close->create();
|
|
if (!$close->save(array('stamp' => null, 'comment' => 'Deposit'), false)) {
|
|
return false;
|
|
}
|
|
|
|
$transaction = array();
|
|
foreach ($set AS $ledger) {
|
|
// REVISIT <AP>: 20090710
|
|
// If the user said to include a ledger in the
|
|
// set, should we really be excluding it?
|
|
if ($ledger['total'] == 0)
|
|
continue;
|
|
|
|
$ids = $this->postLedgerEntry
|
|
($transaction,
|
|
null,
|
|
array('debit_account_id' => $deposit_account_id,
|
|
'credit_ledger_id' => $ledger['id'],
|
|
'amount' => $ledger['total']),
|
|
// Reconcile the account for cash/check/etc,
|
|
// which is the credit side of this entry.
|
|
array('credit' => $ledger['entries']));
|
|
//pr(compact('ids'));
|
|
|
|
if ($ids['error'])
|
|
die("closeAndDeposit : postLedgerEntry returned error!");
|
|
|
|
$transaction = array_intersect_key($ids, array('transaction_id'=>1));
|
|
|
|
$this->Ledger->closeLedger($ledger['id'], $close->id);
|
|
}
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: stats
|
|
* - Returns summary data from the requested account.
|
|
*/
|
|
|
|
function stats($id = null, $all = false, $cond = null) {
|
|
if (!$id)
|
|
return null;
|
|
|
|
// All old, closed ledgers MUST balance to 0.
|
|
// However, the user may want the ENTIRE running totals,
|
|
// (not just the balance), so we may have to query all
|
|
// ledgers, as dictated by the $all parameter.
|
|
|
|
$account = $this->find('first',
|
|
array('contain' =>
|
|
($all
|
|
? array('Ledger' => array
|
|
('fields' => array('id')))
|
|
: array('CurrentLedger' => array
|
|
('fields' => array('id')))
|
|
),
|
|
'conditions' => array
|
|
(array('Account.id' => $id))
|
|
));
|
|
|
|
$stats = array();
|
|
if ($all) {
|
|
foreach ($account['Ledger'] AS $ledger)
|
|
$this->statsMerge($stats['Ledger'],
|
|
$this->Ledger->stats($ledger['id'], $cond));
|
|
}
|
|
else {
|
|
$stats['Ledger'] =
|
|
$this->Ledger->stats($account['CurrentLedger']['id'], $cond);
|
|
}
|
|
|
|
return $stats;
|
|
}
|
|
|
|
}
|
|
?>
|