Another snapshot. I think I'll be taking the CREDIT/DEBIT reconcile functionality out.

git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@353 97e9348a-65ac-dc4b-aefc-98561f571b83
This commit is contained in:
abijah
2009-07-20 02:17:54 +00:00
parent 6ac0204baf
commit fc30dfa2e8
8 changed files with 353 additions and 319 deletions

View File

@@ -1065,11 +1065,13 @@ CREATE TABLE `pmgr_entries` (
`type` ENUM('CHARGE', `type` ENUM('CHARGE',
'PAYMENT', 'PAYMENT',
'DEPOSIT',
'TRANSFER') 'TRANSFER')
NOT NULL, NOT NULL,
-- The account of the entry -- The account/ledger of the entry
`account_id` INT(10) UNSIGNED DEFAULT NULL, `account_id` INT(10) UNSIGNED NOT NULL,
`ledger_id` INT(10) UNSIGNED NOT NULL,
`crdr` ENUM('DEBIT', `crdr` ENUM('DEBIT',
'CREDIT') 'CREDIT')

View File

@@ -981,12 +981,13 @@ foreach $row (@{query($sdbh, $query)}) {
# Add the Charge Entry # Add the Charge Entry
addRow('entries', addRow('entries',
{ 'type' => 'CHARGE', { 'type' => 'CHARGE',
'through_date' => $through_date, 'through_date' => $through_date,
'double_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'double_entry_id'}, 'double_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'double_entry_id'},
'account_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'credit_account_id'}, 'account_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'credit_account_id'},
'crdr' => 'CREDIT', 'ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'credit_ledger_id'},
'comment' => "Charge: $row->{'ChargeID'}; Ledger: $row->{'LedgerID'}", 'crdr' => 'CREDIT',
}); 'comment' => "Charge: $row->{'ChargeID'}; Ledger: $row->{'LedgerID'}",
});
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'entry_id'} $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'entry_id'}
= $newdb{'tables'}{'entries'}{'autoid'}; = $newdb{'tables'}{'entries'}{'autoid'};
@@ -994,11 +995,12 @@ foreach $row (@{query($sdbh, $query)}) {
# Add the A/R entry # Add the A/R entry
addRow('entries', addRow('entries',
{ 'type' => 'TRANSFER', { 'type' => 'TRANSFER',
'double_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'double_entry_id'}, 'double_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'double_entry_id'},
'account_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'debit_account_id'}, 'account_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'debit_account_id'},
'crdr' => 'DEBIT', 'ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'debit_ledger_id'},
'comment' => "Charge A/R: $row->{'ChargeID'}; Ledger: $row->{'LedgerID'}", 'crdr' => 'DEBIT',
}); 'comment' => "Charge A/R: $row->{'ChargeID'}; Ledger: $row->{'LedgerID'}",
});
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'ar_entry_id'} $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'ar_entry_id'}
= $newdb{'tables'}{'entries'}{'autoid'}; = $newdb{'tables'}{'entries'}{'autoid'};
@@ -1206,13 +1208,13 @@ foreach $row (@{query($sdbh, $query)}) {
# debit: Cash/Check/Etc credit: A/R # debit: Cash/Check/Etc credit: A/R
addRow('double_entries', addRow('double_entries',
{ 'transaction_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'receipt_id'}, { 'transaction_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'receipt_id'},
'effective_date' => $effective_date, 'effective_date' => $effective_date,
'customer_id' => undef, # This is set later... 'customer_id' => undef, # This is set later...
'debit_ledger_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_ledger_id'}, 'debit_ledger_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'credit_ledger_id'}, 'credit_ledger_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'credit_ledger_id'},
'amount' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'amount'}, 'amount' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'amount'},
'comment' => "Double Entry Receipt: $row->{'ReceiptNum'}; Type: $row->{'PaymentType'}", 'comment' => "Double Entry Receipt: $row->{'ReceiptNum'}; Type: $row->{'PaymentType'}",
}); });
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'double_entry_id'} $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'double_entry_id'}
= $newdb{'tables'}{'double_entries'}{'autoid'}; = $newdb{'tables'}{'double_entries'}{'autoid'};
@@ -1220,14 +1222,15 @@ foreach $row (@{query($sdbh, $query)}) {
# Add the Payment Entry # Add the Payment Entry
addRow('entries', addRow('entries',
{ 'type' => 'PAYMENT', { 'type' => 'PAYMENT',
'account_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_account_id'}, 'account_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_account_id'},
'crdr' => 'DEBIT', 'ledger_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_ledger_id'},
'double_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'double_entry_id'}, 'crdr' => 'DEBIT',
'name' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'name'}, 'double_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'double_entry_id'},
'monetary_type' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'type'}, 'name' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'name'},
'data1' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'data1'}, 'monetary_type' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'type'},
'comment' => "Receipt: $row->{'ReceiptNum'}; Type: $row->{'PaymentType'}", 'data1' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'data1'},
}); 'comment' => "Receipt: $row->{'ReceiptNum'}; Type: $row->{'PaymentType'}",
});
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'entry_id'} $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'entry_id'}
= $newdb{'tables'}{'entries'}{'autoid'}; = $newdb{'tables'}{'entries'}{'autoid'};
@@ -1235,11 +1238,12 @@ foreach $row (@{query($sdbh, $query)}) {
# Add the A/R Entry # Add the A/R Entry
addRow('entries', addRow('entries',
{ 'type' => 'TRANSFER', { 'type' => 'TRANSFER',
'account_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'credit_account_id'}, 'account_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'credit_account_id'},
'crdr' => 'CREDIT', 'ledger_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'credit_ledger_id'},
'double_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'double_entry_id'}, 'crdr' => 'CREDIT',
'comment' => "Receipt A/R: $row->{'ReceiptNum'}; Type: $row->{'PaymentType'}", 'double_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'double_entry_id'},
}); 'comment' => "Receipt A/R: $row->{'ReceiptNum'}; Type: $row->{'PaymentType'}",
});
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ar_entry_id'} $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ar_entry_id'}
= $newdb{'tables'}{'entries'}{'autoid'}; = $newdb{'tables'}{'entries'}{'autoid'};
@@ -1322,87 +1326,93 @@ print("Set up Petty Cash...\n");
# debit: Equity credit: Loan # debit: Equity credit: Loan
addRow('transactions', addRow('transactions',
{ 'type' => 'TRANSFER', { 'type' => 'TRANSFER',
'stamp' => datefmt('03/25/2009 16:00'), 'stamp' => datefmt('03/25/2009 16:00'),
}); });
addRow('double_entries', addRow('double_entries',
{ 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'}, { 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'},
'effective_date' => $effective_date, 'effective_date' => $effective_date,
'debit_ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'}, 'debit_ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Loan'}{'ledger_id'}, 'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Loan'}{'ledger_id'},
'amount' => 5000, 'amount' => 5000,
'comment' => "HTP Loan #1", 'comment' => "HTP Loan #1",
}); });
addRow('entries', addRow('entries',
{ 'type' => 'TRANSFER', { 'type' => 'TRANSFER',
'account_id' => $newdb{'lookup'}{'account'}{'Equity'}{'account_id'}, 'account_id' => $newdb{'lookup'}{'account'}{'Equity'}{'account_id'},
'crdr' => 'DEBIT', 'ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'},
'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'}, 'crdr' => 'DEBIT',
'comment' => "Equity: HTP Loan #1", 'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'},
'comment' => "Equity: HTP Loan #1",
}); });
addRow('entries', addRow('entries',
{ 'type' => 'TRANSFER', { 'type' => 'TRANSFER',
'account_id' => $newdb{'lookup'}{'account'}{'Loan'}{'account_id'}, 'account_id' => $newdb{'lookup'}{'account'}{'Loan'}{'account_id'},
'crdr' => 'CREDIT', 'ledger_id' => $newdb{'lookup'}{'account'}{'Loan'}{'ledger_id'},
'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'}, 'crdr' => 'CREDIT',
'comment' => "Loan: HTP Loan #1", 'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'},
'comment' => "Loan: HTP Loan #1",
}); });
# Add the second loan # Add the second loan
# debit: Equity credit: Loan # debit: Equity credit: Loan
addRow('transactions', addRow('transactions',
{ 'type' => 'TRANSFER', { 'type' => 'TRANSFER',
'stamp' => datefmt('04/01/2009 16:00'), 'stamp' => datefmt('04/01/2009 16:00'),
}); });
addRow('double_entries', addRow('double_entries',
{ 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'}, { 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'},
'effective_date' => $effective_date, 'effective_date' => $effective_date,
'debit_ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'}, 'debit_ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Loan'}{'ledger_id'}, 'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Loan'}{'ledger_id'},
'amount' => 1000, 'amount' => 1000,
'comment' => "HTP Loan #2", 'comment' => "HTP Loan #2",
}); });
addRow('entries', addRow('entries',
{ 'type' => 'TRANSFER', { 'type' => 'TRANSFER',
'account_id' => $newdb{'lookup'}{'account'}{'Equity'}{'account_id'}, 'account_id' => $newdb{'lookup'}{'account'}{'Equity'}{'account_id'},
'crdr' => 'DEBIT', 'ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'},
'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'}, 'crdr' => 'DEBIT',
'comment' => "Equity: HTP Loan #2", 'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'},
'comment' => "Equity: HTP Loan #2",
}); });
addRow('entries', addRow('entries',
{ 'type' => 'TRANSFER', { 'type' => 'TRANSFER',
'account_id' => $newdb{'lookup'}{'account'}{'Loan'}{'account_id'}, 'account_id' => $newdb{'lookup'}{'account'}{'Loan'}{'account_id'},
'crdr' => 'CREDIT', 'ledger_id' => $newdb{'lookup'}{'account'}{'Loan'}{'ledger_id'},
'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'}, 'crdr' => 'CREDIT',
'comment' => "Loan: HTP Loan #2", 'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'},
'comment' => "Loan: HTP Loan #2",
}); });
# Cheat for now, using equity for Petty Cash # Cheat for now, using equity for Petty Cash
# debit: Petty Cash credit: Equity # debit: Petty Cash credit: Equity
addRow('transactions', addRow('transactions',
{ 'type' => 'TRANSFER', { 'type' => 'TRANSFER',
'stamp' => datefmt('03/25/2009 16:00'), 'stamp' => datefmt('03/25/2009 16:00'),
}); });
addRow('double_entries', addRow('double_entries',
{ 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'}, { 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'},
'effective_date' => $effective_date, 'effective_date' => $effective_date,
'debit_ledger_id' => $newdb{'lookup'}{'account'}{'Petty Cash'}{'ledger_id'}, 'debit_ledger_id' => $newdb{'lookup'}{'account'}{'Petty Cash'}{'ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'}, 'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'},
'amount' => 750, 'amount' => 750,
'comment' => "Petty Cash Funding", 'comment' => "Petty Cash Funding",
}); });
addRow('entries', addRow('entries',
{ 'type' => 'TRANSFER', { 'type' => 'TRANSFER',
'account_id' => $newdb{'lookup'}{'account'}{'Petty Cash'}{'account_id'}, 'account_id' => $newdb{'lookup'}{'account'}{'Petty Cash'}{'account_id'},
'crdr' => 'DEBIT', 'ledger_id' => $newdb{'lookup'}{'account'}{'Petty Cash'}{'ledger_id'},
'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'}, 'crdr' => 'DEBIT',
'comment' => "Petty Cash: Petty Cash Funding", 'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'},
'comment' => "Petty Cash: Petty Cash Funding",
}); });
addRow('entries', addRow('entries',
{ 'type' => 'TRANSFER', { 'type' => 'TRANSFER',
'account_id' => $newdb{'lookup'}{'account'}{'Equity'}{'account_id'}, 'account_id' => $newdb{'lookup'}{'account'}{'Equity'}{'account_id'},
'crdr' => 'CREDIT', 'ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'},
'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'}, 'crdr' => 'CREDIT',
'comment' => "Equity: Petty Cash Funding", 'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'},
'comment' => "Equity: Petty Cash Funding",
}); });
###################################################################### ######################################################################

View File

@@ -270,7 +270,7 @@ class AccountsController extends AppController {
} }
function tst($id) { function tst($id) {
$entries = $this->Account->ledgerEntries($id, true); $entries = $this->Account->unreconciledEntries($id);
pr($entries); pr($entries);
} }
} }

View File

@@ -456,15 +456,15 @@ class CustomersController extends AppController {
$this->layout = null; $this->layout = null;
$this->autoLayout = false; $this->autoLayout = false;
$this->autoRender = false; $this->autoRender = false;
Configure::write('debug', '0'); //Configure::write('debug', '0');
header("Content-type: text/xml;charset=utf-8"); //header("Content-type: text/xml;charset=utf-8");
App::import('Helper', 'Xml'); App::import('Helper', 'Xml');
$xml = new XmlHelper(); $xml = new XmlHelper();
// Find the unreconciled entries, then manipulate the structure // Find the unreconciled entries, then manipulate the structure
// slightly to accomodate the format necessary for XML Helper. // slightly to accomodate the format necessary for XML Helper.
$unreconciled = $this->Customer->findUnreconciledLedgerEntries($id); $unreconciled = $this->Customer->unreconciledCharges($id);
$unreconciled = array('entries' => $unreconciled = array('entries' =>
array_intersect_key($unreconciled['debit'], array_intersect_key($unreconciled['debit'],
array('entry'=>1, 'balance'=>1))); array('entry'=>1, 'balance'=>1)));

View File

@@ -325,83 +325,8 @@ class Account extends AppModel {
* account, either just from the current ledger, or from all ledgers. * account, either just from the current ledger, or from all ledgers.
*/ */
function ledgerEntries($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'), */
/* )); */
/* $this->Entry->find */
/* ('all', array */
/* ('contain' => array(), */
/* 'conditions' => array('Entry.account_id' => $id) */
/* )); */
$ledgers = $this->ledgers($id, $all); $ledgers = $this->ledgers($id, $all);
return $this->Ledger->ledgerEntries($ledgers, $cond, $link);
/* $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'), */
/* '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->DoubleEntry->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;
} }
@@ -413,56 +338,23 @@ class Account extends AppModel {
* (such as charges not paid). * (such as charges not paid).
*/ */
function findUnreconciledLedgerEntries($id = null, $fundamental_type = null, $cond = null) { function unreconciledEntries($id, $set, $cond = null, $link = null) {
if (!isset($cond)) if (!isset($cond))
$cond = array(); $cond = array();
if (!isset($link))
$link = array();
$link['Account'] = array('fields' => array());
$cond[] = array('Account.id' => $id); $cond[] = array('Account.id' => $id);
$set = $this->Ledger->Entry->reconciledSet($set, $cond, $link, true);
foreach (($fundamental_type pr(compact('set'));
? array($fundamental_type) return $set;
: array('debit', 'credit')) AS $fund) {
$ucfund = ucfirst($fund);
$unreconciled[$fund]['entry'] = $this->find
('all', array
('link' => array
('Ledger' => array
('fields' => array(),
"DoubleEntry" => array
('class' => "{$ucfund}DoubleEntry",
'fields' => array('id', 'customer_id', 'lease_id', 'amount'),
"ReconciliationDoubleEntry" => array
('class' => "{$ucfund}ReconciliationDoubleEntry",
'fields' => array
("COALESCE(SUM(Reconciliation.amount),0) AS 'reconciled'",
"DoubleEntry.amount - COALESCE(SUM(Reconciliation.amount),0) AS 'balance'",
),
),
),
),
),
'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["DoubleEntry"], array(0=>true)),
$entry[0]);
$balance += $entry['balance'];
}
$unreconciled[$fund]['balance'] = $balance;
}
return $unreconciled;
} }
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************
* function: amountWouldReconcile * function: paymentWouldReconcile
* - Returns which ledger entries a new credit/debit would * - Returns which ledger entries a new credit/debit would
* reconcile, and how much. * reconcile, and how much.
* *
@@ -473,38 +365,37 @@ class Account extends AppModel {
* whatever algorithm is simplest. * whatever algorithm is simplest.
*/ */
function amountWouldReconcile($id, $fundamental_type, $amount, $cond = null) { function paymentWouldReconcile($id, $amount, $cond = null, $link = null) {
$ofund = $this->fundamentalOpposite($fundamental_type);
$unreconciled = array($ofund => array('entry'=>array(), 'balance'=>0)); $unreconciled = array($ofund => array('entry'=>array(), 'balance'=>0));
$applied = 0; $applied = 0;
// if there is no money in the entry, it can reconcile nothing if ($amount <= 0)
// don't bother wasting time sifting ledger entries. return;
if ($amount > 0) {
$unreconciled = $this->findUnreconciledLedgerEntries($id, $ofund, $cond);
foreach ($unreconciled[$ofund]['entry'] AS $i => &$entry) { $unreconciled = $this->unreconciledEntries($id, 'CHARGE', $cond, $link);
// 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; foreach ($unreconciled AS $i => &$item) {
$entry['reconciled'] += $apply; $entry =& $item['DoubleEntry'];
$entry['balance'] -= $apply; // Determine if amount is sufficient to cover the entry
$applied += $apply; if ($amount > $entry['balance'])
$amount -= $apply; $apply = $entry['balance'];
elseif ($amount > 0)
$apply = $amount;
else {
unset($unreconciled[$i]);
continue;
} }
$entry['applied'] = $apply;
$entry['reconciled'] += $apply;
$entry['balance'] -= $apply;
$applied += $apply;
$amount -= $apply;
} }
$unreconciled[$ofund]['unapplied'] = $amount; $unreconciled['unapplied'] = $amount;
$unreconciled[$ofund]['applied'] = $applied; $unreconciled['applied'] = $applied;
$unreconciled[$ofund]['balance'] -= $applied; $unreconciled['balance'] -= $applied;
return $unreconciled; return $unreconciled;
} }

View File

@@ -82,7 +82,7 @@ class Customer extends AppModel {
/* )); */ /* )); */
$A = new Account(); $A = new Account();
$entries = $A->findLedgerEntries $entries = $A->ledgerEntries
($A->securityDepositAccountID(), ($A->securityDepositAccountID(),
true, array('DoubleEntry.customer_id' => $id), $link); true, array('DoubleEntry.customer_id' => $id), $link);
@@ -96,6 +96,80 @@ class Customer extends AppModel {
} }
/**************************************************************************
**************************************************************************
**************************************************************************
* function: unreconciledCharges
* - Returns charges have not yet been fully paid
*/
function unreconciledCharges($id, $cond = null, $link = null) {
if (!isset($cond))
$cond = array();
if (!isset($link))
$link = array();
if (!isset($link['DoubleEntry']))
$link['DoubleEntry'] = array();
if (!isset($link['DoubleEntry']['Customer']))
$link['DoubleEntry']['Customer'] = array();
if (!isset($link['DoubleEntry']['Customer']['fields']))
$link['DoubleEntry']['Customer']['fields'] = array();
$cond[] = array('Customer.id' => $id);
$set = $this->DoubleEntry->Entry->reconciledSet('CHARGE', $cond, $link, true);
pr(compact('set'));
return $set;
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: paymentWouldReconcile
* - 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 paymentWouldReconcile($id, $amount, $cond = null, $link = null) {
$unreconciled = array($ofund => array('entry'=>array(), 'balance'=>0));
$applied = 0;
if ($amount <= 0)
return;
$unreconciled = $this->unreconciledEntries($id, 'CHARGE', $cond, $link);
foreach ($unreconciled AS $i => &$item) {
$entry =& $item['DoubleEntry'];
// Determine if amount is sufficient to cover the entry
if ($amount > $entry['balance'])
$apply = $entry['balance'];
elseif ($amount > 0)
$apply = $amount;
else {
unset($unreconciled[$i]);
continue;
}
$entry['applied'] = $apply;
$entry['reconciled'] += $apply;
$entry['balance'] -= $apply;
$applied += $apply;
$amount -= $apply;
}
$unreconciled['unapplied'] = $amount;
$unreconciled['applied'] = $applied;
$unreconciled['balance'] -= $applied;
return $unreconciled;
}
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************

View File

@@ -373,19 +373,95 @@ OPTION 2
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************
* function: reconcilingEntries * function: reconciledSet
* - Returns the set of entries satisfying the given conditions,
* along with any entries that they reconcile
*/
function reconciledSet($set, $cond = null, $link = null, $unrec = false) {
if (!isset($cond))
$cond = array();
if (!isset($link))
$link = array();
if ($set == 'CHARGE' || $set == 'PAYMENT')
$cond[] = array('Entry.type' => $set);
elseif ($set == 'DEBIT' || $set == 'CREDIT')
$cond[] = array('Entry.crdr' => $set);
else
die("INVALID RECONCILE SET");
$link['DoubleEntry'] += array();
/* if ($set == 'CHARGE') */
/* $link['Payment'] = array('fields' => array("SUM(ChargesPayment.amount) AS applied")); */
/* if ($set == 'PAYMENT') */
/* $link['Charge'] = array('fields' => array("SUM(ChargesPayment.amount) AS applied")); */
/* if ($set == 'DEBIT') */
/* $link['Credit'] = array('fields' => array("SUM(Reconciliation.amount) AS applied")); */
/* if ($set == 'CREDIT') */
/* $link['Debit'] = array('fields' => array("SUM(Reconciliation.amount) AS applied")); */
if ($set == 'CHARGE')
$link['Payment'] = array('fields' => array("SUM(AppliedPayment.amount) AS applied"));
if ($set == 'PAYMENT')
$link['Charge'] = array('fields' => array("SUM(AppliedCharge.amount) AS applied"));
if ($set == 'DEBIT')
$link['Credit'] = array('fields' => array("SUM(AppliedCredit.amount) AS applied"));
if ($set == 'CREDIT')
$link['Debit'] = array('fields' => array("SUM(AppliedDebit.amount) AS applied"));
$result = $this->find
('all', array
(
'link' => $link,
'conditions' => $cond,
'group' => 'Entry.id',
));
pr(array('reconciledSet', compact('set', 'cond', 'link', 'result')));
$resultset = array();
foreach ($result AS $i => $entry) {
$entry['DoubleEntry'] += $entry[0];
unset($entry[0]);
$entry['DoubleEntry']['balance'] =
$entry['DoubleEntry']['amount'] - $entry['DoubleEntry']['applied'];
// Since HAVING isn't a builtin feature of CakePHP,
// we'll have to post-process to get the desired entries
if ($entry['DoubleEntry']['balance'] == 0) {
if (!$unrec)
$resultset[] = $entry;
}
else {
if ($unrec)
$resultset[] = $entry;
}
}
//$result['stats'] = $this->stats(null, $cond);
pr($this->stats(null, $cond, $link));
return $resultset;
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: reconciledEntries
* - Returns a list of entries that reconcile against the given entry. * - Returns a list of entries that reconcile against the given entry.
* (such as payments towards a charge). * (such as payments towards a charge).
*/ */
function reconciledEntries($id, $cond = null) { function reconciledEntries($id, $cond = null, $link = null) {
if (!isset($cond)) if (!isset($cond))
$cond = array(); $cond = array();
if (!isset($link))
$link = array();
$cond[] = array('Entry.id' => $id); $cond[] = array('Entry.id' => $id);
$entry = $this->find('first', array('recursive' => -1)); $entry = $this->find('first', array('conditions' => array('Entry.id' => $id),
'recursive' => -1));
$entry = $entry['Entry']; $entry = $entry['Entry'];
$contain = array(); $contain = array();
@@ -471,42 +547,49 @@ OPTION 2
* function: stats * function: stats
* - Returns summary data from the requested ledger entry * - Returns summary data from the requested ledger entry
*/ */
function stats($id, $cond = null) { function stats($id = null, $cond = null, $link = null) {
if (!isset($cond)) if (!isset($cond))
$cond = array(); $cond = array();
if (!isset($link))
$link = array();
$cond[] = array('Entry.id' => $id); if (isset($id))
$cond[] = array('Entry.id' => $id);
$entry = $this->find('first', array('contain' => array('DoubleEntry.amount'), /* $entry = $this->find('first', array('contain' => array('DoubleEntry.amount'), */
'fields' => array('Entry.type', 'Entry.crdr'))); /* 'fields' => array('Entry.type', 'Entry.crdr'), */
$entry['Entry'] += $entry['DoubleEntry']; /* 'conditions' => array('Entry.id' => $id))); */
$entry = $entry['Entry']; /* pr(compact('entry')); */
/* $entry['Entry'] += $entry['DoubleEntry']; */
/* $entry = $entry['Entry']; */
$stats = array(); $stats = array();
foreach(array('charge', 'payment', 'debit', 'credit') AS $rtype) { foreach(array('charge', 'payment', 'debit', 'credit') AS $rtype) {
$Rtype = ucfirst($rtype); $Rtype = ucfirst($rtype);
if (($rtype == 'charge' && $entry['type'] == 'PAYMENT') || $rlink = $link;
$rlink[$Rtype] = array('fields' => array("SUM(Applied{$Rtype}.amount) AS applied"));
pr(array('stats()', compact('id', 'cond', 'link', 'rlink')));
if (1 || ($rtype == 'charge' && $entry['type'] == 'PAYMENT') ||
($rtype == 'payment' && $entry['type'] == 'CHARGE') || ($rtype == 'payment' && $entry['type'] == 'CHARGE') ||
($rtype == 'debit' && $entry['crdr'] == 'CREDIT') || ($rtype == 'debit' && $entry['crdr'] == 'CREDIT') ||
($rtype == 'credit' && $entry['crdr'] == 'DEBIT')) { ($rtype == 'credit' && $entry['crdr'] == 'DEBIT')) {
$result = $this->find $result = $this->find
('first', array ('first', array
('link' => ('link' => $rlink,
array($Rtype => 'fields' => array("SUM(DoubleEntry.amount) AS total",
array('fields' => array("SUM(Applied{$Rtype}.amount) AS applied"))), "SUM(DoubleEntry.amount) - SUM(Applied{$Rtype}.amount) AS unapplied"),
'fields' => array(),
'conditions' => $cond, 'conditions' => $cond,
'group' => 'Entry.id', //'group' => 'Entry.id',
)); ));
//pr(compact('Rtype', 'result')); pr(compact('Rtype', 'result'));
$sumfld = $Rtype; $sumfld = $Rtype;
$stats[$sumfld] = $result[0]; $stats[$sumfld] = $result[0];
if (!isset($stats[$sumfld]['applied'])) /* if (!isset($stats[$sumfld]['applied'])) */
$stats[$sumfld]['applied'] = 0; /* $stats[$sumfld]['applied'] = 0; */
$stats[$sumfld]['unapplied'] = $entry['amount'] - $stats[$sumfld]['applied'];
} }
} }

View File

@@ -8,6 +8,8 @@ class Ledger extends AppModel {
); );
var $hasMany = array( var $hasMany = array(
'Entry',
'DoubleEntry' => array( 'DoubleEntry' => array(
'foreignKey' => false, 'foreignKey' => false,
@@ -27,17 +29,6 @@ class Ledger extends AppModel {
'counterQuery' => '' 'counterQuery' => ''
), ),
'DebitLedgerEntry' => array(
'className' => 'DoubleEntry',
'foreignKey' => 'debit_ledger_id',
'dependent' => false,
),
'CreditLedgerEntry' => array(
'className' => 'DoubleEntry',
'foreignKey' => 'credit_ledger_id',
'dependent' => false,
),
); );
@@ -129,66 +120,58 @@ class Ledger extends AppModel {
} }
function debitCreditFields($id, $sum = false, $entry_name = 'Entry', $double_name = 'DoubleEntry') {
$ftype = strtoupper($this->Account->fundamentalType($this->accountID($id)));
$fields = array
(
($sum ? 'SUM(' : '') .
"IF({$entry_name}.crdr = 'DEBIT', {$double_name}.amount, NULL)" .
($sum ? ')' : '') . ' AS debit' . ($sum ? 's' : ''),
($sum ? 'SUM(' : '') .
"IF({$entry_name}.crdr = 'CREDIT', {$double_name}.amount, NULL)" .
($sum ? ')' : '') . ' AS credit' . ($sum ? 's' : ''),
($sum ? 'SUM(' : '') .
"IF({$entry_name}.crdr = '$ftype', 1, -1) * {$double_name}.amount" .
// IF({$double_name}.amount, {$double_name}.amount, 0)" .
($sum ? ')' : '') . ' AS balance',
);
if ($sum)
$fields[] = "COUNT({$entry_name}.id) AS entries";
return $fields;
}
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************
* function: ledgerEntries * function: ledgerEntries
* - Returns an array of ledger entries that belong to a given * - Returns an array of ledger entries that belong to a given
* ledger. There is extra work done... see the DoubleEntry model. * ledger. There is extra work done to establish debit/credit
*/ */
function ledgerEntries($id, $account_type = null, $cond = null, $link = null) { function ledgerEntries($ids, $cond = null, $link = null) {
/* pr(array('function' => 'Ledger::findLedgerEntries', */ if (empty($ids))
/* 'args' => compact('id', 'account_type', 'cond', 'link'), */ return null;
/* )); */
if (!isset($account_type)) { /* $id = (is_array($ids) ? $ids[0] : $ids); */
$ledger = $this->find('first', array /* $ftype = strtoupper($this->Account->fundamentalType($this->accountID($id))); */
('contain' => array
('Account' => array
('fields' => array('type'),
),
),
'fields' => array(),
'conditions' => array(array('Ledger.id' => $id)),
));
$account_type = $ledger['Account']['type'];
}
// If the requested entries are limited by date, we must calculate $entries = $this->Entry->find
// a balance forward, or the resulting balance will be thrown off. ('all', array
// ('contain' => array('DoubleEntry' => array('fields' => array('amount'))),
// REVISIT <AP>: This obviously is more general than date. 'fields' => array_merge(array("Entry.*"),
// As such, it will not work (or, only work if the $this->debitCreditFields(is_array($ids) ? $ids[0] : $ids)),
// condition only manages to exclude the first parts /* "IF(Entry.crdr = 'CREDIT', DoubleEntry.amount, NULL) AS credit", */
// of the ledger, nothing in the middle or at the /* "IF(Entry.crdr = 'DEBIT', DoubleEntry.amount, NULL) AS debit", */
// end. For now, I'll just create an 'other' entry, /* "IF(Entry.crdr = '$ftype', 1, -1) * DoubleEntry.amount AS balance", */
// not necessarily a balance forward.
$bf = array(); 'conditions' => array('Entry.ledger_id' => $ids),
if (0 && isset($cond)) { ));
//$date = '<NOT IMPLEMENTED>';
$stats = $this->stats($id, array('NOT' => array($cond)));
$bf = array(array(array('debit' => $stats['debits'],
'credit' => $stats['credits'],
'balance' => $stats['balance']),
'DoubleEntry' => array('id' => null, pr(compact('entries'));
//'comment' => "Balance Forward from $date"),
'comment' => "-- SUMMARY OF EXCLUDED ENTRIES --"),
'Transaction' => array('id' => null,
//'stamp' => $date,
'stamp' => null,
'comment' => null),
));
}
$entries = $this->DoubleEntry->findInLedgerContext($id, $account_type, $cond, $link);
/* pr(array('function' => 'Ledger::findLedgerEntries', */
/* 'args' => compact('id', 'account_type', 'cond', 'link'), */
/* 'vars' => compact('ledger'), */
/* 'return' => compact('entries'), */
/* )); */
return $entries; return $entries;
} }
@@ -209,23 +192,14 @@ class Ledger extends AppModel {
('link' => ('link' =>
array(// Models array(// Models
'Account' => array('fields' => array()), 'Account' => array('fields' => array()),
//'DoubleEntry' => array('fields' => array()), 'Entry' => array
'DoubleEntry' => ('DoubleEntry' =>
array('fields' => array(), array('fields' => array(),
'Transaction' => array('fields' => array('stamp')), 'Transaction' => array('fields' => array('stamp')),
), ),
),
), ),
'fields' => 'fields' => $this->debitCreditFields($id, true),
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(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(DoubleEntry.id) AS entries"),
'conditions' => $cond, 'conditions' => $cond,
'group' => 'Ledger.id', 'group' => 'Ledger.id',
)); ));