git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716/site@493 97e9348a-65ac-dc4b-aefc-98561f571b83
721 lines
25 KiB
PHP
721 lines
25 KiB
PHP
<?php
|
|
class StatementEntry extends AppModel {
|
|
|
|
var $belongsTo = array(
|
|
'Transaction',
|
|
'Customer',
|
|
'Lease',
|
|
'Account',
|
|
|
|
// The charge to which this disbursement applies (if it is one)
|
|
'ChargeEntry' => array(
|
|
'className' => 'StatementEntry',
|
|
),
|
|
);
|
|
|
|
var $hasMany = array(
|
|
// The disbursements that apply to this charge (if it is one)
|
|
'DisbursementEntry' => array(
|
|
'className' => 'StatementEntry',
|
|
'foreignKey' => 'charge_entry_id',
|
|
),
|
|
|
|
);
|
|
|
|
//var $default_log_level = array('log' => 30, 'show' => 15);
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: debit/creditTypes
|
|
*/
|
|
|
|
function debitTypes() {
|
|
return array('CHARGE', 'PAYMENT', 'REFUND');
|
|
}
|
|
|
|
function creditTypes() {
|
|
return array('DISBURSEMENT', 'WAIVER', 'SURPLUS');
|
|
}
|
|
|
|
function voidTypes() {
|
|
return array('VOID');
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: chargeDisbursementFields
|
|
*/
|
|
|
|
function chargeDisbursementFields($sum = false, $entry_name = 'StatementEntry') {
|
|
$debits = $this->debitTypes();
|
|
$credits = $this->creditTypes();
|
|
$voids = $this->voidTypes();
|
|
|
|
foreach ($debits AS &$enum)
|
|
$enum = "'" . $enum . "'";
|
|
foreach ($credits AS &$enum)
|
|
$enum = "'" . $enum . "'";
|
|
foreach ($voids AS &$enum)
|
|
$enum = "'" . $enum . "'";
|
|
|
|
$debit_set = implode(", ", $debits);
|
|
$credit_set = implode(", ", $credits);
|
|
$void_set = implode(", ", $voids);
|
|
|
|
$fields = array
|
|
(
|
|
($sum ? 'SUM(' : '') .
|
|
"IF({$entry_name}.type IN ({$debit_set})," .
|
|
" {$entry_name}.amount, NULL)" .
|
|
($sum ? ')' : '') . ' AS charge' . ($sum ? 's' : ''),
|
|
|
|
($sum ? 'SUM(' : '') .
|
|
"IF({$entry_name}.type IN({$credit_set})," .
|
|
" {$entry_name}.amount, NULL)" .
|
|
($sum ? ')' : '') . ' AS disbursement' . ($sum ? 's' : ''),
|
|
|
|
($sum ? 'SUM(' : '') .
|
|
"IF({$entry_name}.type IN ({$debit_set}), 1," .
|
|
" IF({$entry_name}.type IN ({$credit_set}), -1, 0))" .
|
|
" * IF({$entry_name}.amount, {$entry_name}.amount, 0)" .
|
|
($sum ? ')' : '') . ' AS balance',
|
|
);
|
|
|
|
if ($sum)
|
|
$fields[] = "COUNT({$entry_name}.id) AS entries";
|
|
|
|
return $fields;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: verifyStatementEntry
|
|
* - Verifies consistenty of new statement entry data
|
|
* (not in a pre-existing statement entry)
|
|
*/
|
|
function verifyStatementEntry($entry) {
|
|
$this->prFunctionLevel(10);
|
|
$this->prEnter(compact('entry'));
|
|
|
|
if (empty($entry['type']) ||
|
|
//empty($entry['effective_date']) ||
|
|
empty($entry['account_id']) ||
|
|
empty($entry['amount'])
|
|
) {
|
|
return $this->prReturn(false);
|
|
}
|
|
|
|
return $this->prReturn(true);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: addStatementEntry
|
|
* - Inserts new Statement Entry into the database
|
|
*/
|
|
function addStatementEntry($entry) {
|
|
$this->prEnter(compact('entry'));
|
|
|
|
$ret = array();
|
|
if (!$this->verifyStatementEntry($entry))
|
|
return array('error' => true, 'verify_data' => $entry) + $ret;
|
|
|
|
$this->pr(20, array('checkpoint' => 'Pre-Save')
|
|
+ compact('entry'));
|
|
|
|
$this->create();
|
|
if (!$this->save($entry))
|
|
return array('error' => true, 'save_data' => $entry) + $ret;
|
|
|
|
$ret['statement_entry_id'] = $this->id;
|
|
return $this->prReturn($ret + array('error' => false));
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: waive
|
|
* - Waives the charges
|
|
*
|
|
*/
|
|
function waive($id, $stamp = null) {
|
|
$this->prEnter(compact('id', 'stamp'));
|
|
|
|
// Get the basic information about the entry to be waived.
|
|
$this->recursive = -1;
|
|
$charge = $this->read(null, $id);
|
|
$charge = $charge['StatementEntry'];
|
|
|
|
// Query the stats to get the remaining balance
|
|
$stats = $this->stats($id);
|
|
|
|
// Build a transaction
|
|
$waiver = array('Transaction' => array(), 'Entry' => array());
|
|
$waiver['Transaction']['stamp'] = $stamp;
|
|
$waiver['Transaction']['comment'] = "Charge Waiver";
|
|
|
|
if ($charge['type'] !== 'CHARGE')
|
|
die("INTERNAL ERROR: WAIVER ITEM IS NOT CHARGE");
|
|
|
|
// Add the charge waiver
|
|
$waiver['Entry'][] =
|
|
array('amount' => $stats['Charge']['balance'],
|
|
'account_id' => $this->Account->waiverAccountID(),
|
|
'comment' => null,
|
|
);
|
|
|
|
// Record the waiver transaction
|
|
return $this->prReturn($this->Transaction->addWaiver
|
|
($waiver, $id, $charge['customer_id'], $charge['lease_id']));
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: reverse
|
|
* - Reverses the charges
|
|
*
|
|
*/
|
|
function reverse($id, $stamp = null) {
|
|
$this->prEnter(compact('id', 'stamp'));
|
|
|
|
$ret = array();
|
|
|
|
// Get the basic information about the entry to be reversed.
|
|
$this->recursive = -1;
|
|
$charge = $this->read(null, $id);
|
|
$charge = $charge['StatementEntry'];
|
|
|
|
$voided_entry_transactions = array();
|
|
$reconciled = $this->reconciledEntries($id);
|
|
$this->pr(21, compact('reconciled'));
|
|
|
|
if ($reconciled) {
|
|
foreach ($reconciled['entries'] AS $entry) {
|
|
$voided_entry_transactions[$entry['DisbursementEntry']['transaction_id']]
|
|
= array_intersect_key($entry['DisbursementEntry'],
|
|
array('customer_id'=>1, 'lease_id'=>1));
|
|
|
|
$this->del($entry['DisbursementEntry']['id']);
|
|
continue;
|
|
|
|
$DE = new StatementEntry();
|
|
$DE->id = $entry['DisbursementEntry']['id'];
|
|
$DE->saveField('type', 'VOID');
|
|
$DE->saveField('charge_entry_id', null);
|
|
}
|
|
$this->pr(17, compact('voided_entry_transactions'));
|
|
}
|
|
|
|
// Query the stats to get the remaining balance
|
|
$stats = $this->stats($id);
|
|
|
|
// Build a transaction
|
|
$reversal = array('Transaction' => array(), 'Entry' => array());
|
|
$reversal['Transaction']['stamp'] = $stamp;
|
|
$reversal['Transaction']['comment'] = "Credit Note: Charge Reversal";
|
|
|
|
if ($charge['type'] !== 'CHARGE')
|
|
die("INTERNAL ERROR: REVERSAL ITEM IS NOT CHARGE");
|
|
|
|
// Add the charge reversal
|
|
$reversal['Entry'][] =
|
|
array('amount' => $stats['Charge']['total'],
|
|
'account_id' => $charge['account_id'],
|
|
'comment' => 'Charge Reversal',
|
|
);
|
|
|
|
// Record the reversal transaction
|
|
$result = $this->Transaction->addReversal
|
|
($reversal, $id, $charge['customer_id'], $charge['lease_id']);
|
|
$this->pr(21, compact('result'));
|
|
$ret['reversal'] = $result;
|
|
if ($result['error'])
|
|
$ret['error'] = true;
|
|
|
|
foreach ($voided_entry_transactions AS $transaction_id => $tx) {
|
|
$result = $this->assignCredits
|
|
(null,
|
|
$transaction_id,
|
|
null,
|
|
null,
|
|
$tx['customer_id'],
|
|
$tx['lease_id']
|
|
);
|
|
$this->pr(21, compact('result'));
|
|
$ret['assigned'][] = $result;
|
|
if ($result['error'])
|
|
$ret['error'] = true;
|
|
}
|
|
|
|
return $this->prReturn($ret + array('error' => false));
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: reconciledSet
|
|
* - Returns the set of entries satisfying the given conditions,
|
|
* along with any entries that they reconcile
|
|
*/
|
|
function reconciledSetQuery($set, $query) {
|
|
$this->queryInit($query);
|
|
|
|
if (in_array($set, $this->debitTypes()))
|
|
$query['link']['DisbursementEntry'] = array('fields' => array("SUM(DisbursementEntry.amount) AS reconciled"));
|
|
elseif (in_array($set, $this->creditTypes()))
|
|
$query['link']['ChargeEntry'] = array('fields' => array("SUM(ChargeEntry.amount) AS reconciled"));
|
|
else
|
|
die("INVALID RECONCILE SET");
|
|
|
|
$query['conditions'][] = array('StatementEntry.type' => $set);
|
|
$query['group'] = 'StatementEntry.id';
|
|
|
|
return $query;
|
|
}
|
|
|
|
function reconciledSet($set, $query = null, $unrec = false, $if_rec_include_partial = false) {
|
|
//$this->prFunctionLevel(16);
|
|
$this->prEnter(compact('set', 'query', 'unrec', 'if_rec_include_partial'));
|
|
$lquery = $this->reconciledSetQuery($set, $query);
|
|
$result = $this->find('all', $lquery);
|
|
|
|
$this->pr(20, compact('lquery', 'result'));
|
|
|
|
$resultset = array();
|
|
foreach ($result AS $i => $entry) {
|
|
$this->pr(25, compact('entry'));
|
|
if (!empty($entry[0]))
|
|
$entry['StatementEntry'] = $entry[0] + $entry['StatementEntry'];
|
|
unset($entry[0]);
|
|
|
|
$entry['StatementEntry']['balance'] =
|
|
$entry['StatementEntry']['amount'] - $entry['StatementEntry']['reconciled'];
|
|
|
|
// Since HAVING isn't a builtin feature of CakePHP,
|
|
// we'll have to post-process to get the desired entries
|
|
|
|
if ($entry['StatementEntry']['balance'] == 0)
|
|
$reconciled = true;
|
|
elseif ($entry['StatementEntry']['reconciled'] == 0)
|
|
$reconciled = false;
|
|
else // Partial disbursement; depends on unrec
|
|
$reconciled = (!$unrec && $if_rec_include_partial);
|
|
|
|
// Add to the set, if it's been requested
|
|
if ($reconciled == !$unrec)
|
|
$resultset[] = $entry;
|
|
}
|
|
|
|
return $this->prReturn(array('entries' => $resultset,
|
|
'summary' => $this->stats(null, $query)));
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: reconciledEntries
|
|
* - Returns a list of entries that reconcile against the given entry.
|
|
* (such as disbursements towards a charge).
|
|
*/
|
|
function reconciledEntriesQuery($id, $query = null) {
|
|
$this->queryInit($query, false);
|
|
|
|
$this->id = $id;
|
|
$this->recursive = -1;
|
|
$this->read();
|
|
|
|
$query['conditions'][] = array('StatementEntry.id' => $id);
|
|
|
|
if (in_array($this->data['StatementEntry']['type'], $this->debitTypes()))
|
|
$query['link']['DisbursementEntry'] = array();
|
|
if (in_array($this->data['StatementEntry']['type'], $this->creditTypes()))
|
|
$query['link']['ChargeEntry'] = array();
|
|
|
|
return $query;
|
|
}
|
|
|
|
function reconciledEntries($id, $query = null) {
|
|
$this->prEnter(compact('id', 'query'));
|
|
$lquery = $this->reconciledEntriesQuery($id, $query);
|
|
|
|
$result = $this->find('all', $lquery);
|
|
foreach (array_keys($result) AS $i)
|
|
unset($result[$i]['StatementEntry']);
|
|
|
|
return $this->prReturn(array('entries' => $result));
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: assignCredits
|
|
* - Assigns all credits to existing charges
|
|
*
|
|
* REVISIT <AP>: 20090726
|
|
* This algorithm shouldn't be hardcoded. We need to allow
|
|
* the user to specify how disbursements should be applied.
|
|
*
|
|
*/
|
|
function assignCredits($query = null, $receipt_id = null,
|
|
$charge_ids = null, $disbursement_type = null,
|
|
$customer_id = null, $lease_id = null)
|
|
{
|
|
//$this->prFunctionLevel(25);
|
|
$this->prEnter(compact('query', 'receipt_id',
|
|
'charge_ids', 'disbursement_type',
|
|
'customer_id', 'lease_id'));
|
|
$this->queryInit($query);
|
|
|
|
if (!empty($customer_id))
|
|
$query['conditions'][] = array('StatementEntry.customer_id' => $customer_id);
|
|
|
|
if (empty($disbursement_type))
|
|
$disbursement_type = 'DISBURSEMENT';
|
|
|
|
$ret = array();
|
|
|
|
// First, find all known credits
|
|
$lquery = $query;
|
|
$lquery['conditions'][] = array('StatementEntry.type' => 'SURPLUS');
|
|
// REVISIT <AP>: 20090804
|
|
// We need to ensure that we're using surplus credits ONLY from either
|
|
// the given lease, or those that do not apply to any specific lease.
|
|
// However, by doing this, it forces any lease surplus amounts to
|
|
// remain frozen with that lease until either there is a lease charge,
|
|
// we refund the money, or we "promote" that surplus to the customer
|
|
// level and out of the leases direct control.
|
|
// That seems like a pain. Perhaps we should allow any customer
|
|
// surplus to be used on any customer charge.
|
|
$lquery['conditions'][] =
|
|
array('OR' =>
|
|
array(array('StatementEntry.lease_id' => null),
|
|
(!empty($lease_id)
|
|
? array('StatementEntry.lease_id' => $lease_id)
|
|
: array()),
|
|
));
|
|
$lquery['order'][] = 'StatementEntry.effective_date ASC';
|
|
$credits = $this->find('all', $lquery);
|
|
$this->pr(18, compact('credits'),
|
|
"Credits Established");
|
|
|
|
// Next, establish credit from the newly added receipt
|
|
$receipt_credit = null;
|
|
if (!empty($receipt_id)) {
|
|
$lquery =
|
|
array('link' =>
|
|
array('StatementEntry',
|
|
'LedgerEntry' =>
|
|
array('conditions' =>
|
|
array('LedgerEntry.account_id !=' =>
|
|
$this->Account->accountReceivableAccountID()),
|
|
),
|
|
),
|
|
'conditions' => array('Transaction.id' => $receipt_id),
|
|
'fields' => array('Transaction.id', 'Transaction.stamp', 'Transaction.amount'),
|
|
);
|
|
$receipt_credit = $this->Transaction->find('first', $lquery);
|
|
if (!$receipt_credit)
|
|
die("INTERNAL ERROR: UNABLE TO LOCATE RECEIPT");
|
|
|
|
//$reconciled = $this->reconciledEntries($id);
|
|
|
|
$stats = $this->Transaction->stats($receipt_id);
|
|
$receipt_credit['balance'] =
|
|
$receipt_credit['Transaction']['amount'] - $stats['Disbursement']['total'];
|
|
|
|
$this->pr(18, compact('receipt_credit'),
|
|
"Receipt Credit Added");
|
|
}
|
|
|
|
// Now find all unpaid charges
|
|
if (isset($charge_ids)) {
|
|
$lquery = array('contain' => false,
|
|
'conditions' => array('StatementEntry.id' => $charge_ids));
|
|
} else {
|
|
$lquery = $query;
|
|
// If we're working with a specific lease, then limit charges to it
|
|
if (!empty($lease_id))
|
|
$lquery['conditions'][] = array('StatementEntry.lease_id' => $lease_id);
|
|
}
|
|
$lquery['order'] = 'StatementEntry.effective_date ASC';
|
|
$charges = array();
|
|
foreach ($this->debitTypes() AS $dtype) {
|
|
$rset = $this->reconciledSet($dtype, $lquery, true);
|
|
$entries = $rset['entries'];
|
|
$charges = array_merge($charges, $entries);
|
|
$this->pr(18, compact('dtype', 'entries'), "Outstanding Debit Entries");
|
|
}
|
|
|
|
// Initialize our list of used credits
|
|
$used_credits = array();
|
|
|
|
// Work through all unpaid charges, applying disbursements as we go
|
|
foreach ($charges AS $charge) {
|
|
|
|
$this->pr(20, compact('charge'),
|
|
'Process Charge');
|
|
|
|
// Check that we have available credits.
|
|
// Technically, this isn't necessary, since the loop
|
|
// will handle everything just fine. However, this
|
|
// just saves extra processing if/when there is no
|
|
// means to resolve a charge anyway.
|
|
if (count($credits) == 0 && empty($receipt_credit['balance'])) {
|
|
$this->pr(17, 'No more available credits');
|
|
break;
|
|
}
|
|
|
|
$charge['balance'] = $charge['StatementEntry']['balance'];
|
|
while ($charge['balance'] > 0 &&
|
|
(count($credits) || !empty($receipt_credit['balance']))) {
|
|
|
|
$this->pr(20, compact('charge'),
|
|
'Attempt Charge Reconciliation');
|
|
|
|
// Use explicit credits before using implicit credits
|
|
// (Not sure it matters though).
|
|
if (count($credits)) {
|
|
// Peel off the first credit available
|
|
$credit =& $credits[0];
|
|
$disbursement_date = $credit['StatementEntry']['effective_date'];
|
|
$disbursement_transaction_id = $credit['StatementEntry']['transaction_id'];
|
|
$disbursement_account_id = $credit['StatementEntry']['account_id'];
|
|
|
|
if (!isset($credit['balance']))
|
|
$credit['balance'] = $credit['StatementEntry']['amount'];
|
|
}
|
|
elseif (!empty($receipt_credit['balance'])) {
|
|
// Use our only receipt credit
|
|
$credit =& $receipt_credit;
|
|
$disbursement_date = $credit['Transaction']['stamp'];
|
|
$disbursement_transaction_id = $credit['Transaction']['id'];
|
|
$disbursement_account_id = $credit['LedgerEntry']['account_id'];
|
|
}
|
|
else {
|
|
die("HOW DID WE GET HERE WITH NO SURPLUS?");
|
|
}
|
|
|
|
// Set the disbursement amount to the maximum amount
|
|
// possible without exceeding the charge or credit balance
|
|
$disbursement_amount = min($charge['balance'], $credit['balance']);
|
|
if (!isset($credit['applied']))
|
|
$credit['applied'] = 0;
|
|
|
|
$credit['applied'] += $disbursement_amount;
|
|
$credit['balance'] -= $disbursement_amount;
|
|
|
|
$this->pr(20, compact('credit'),
|
|
($credit['balance'] > 0 ? 'Utilized' : 'Exhausted') .
|
|
(count($credits) ? ' Credit' : ' Receipt'));
|
|
|
|
if ($credit['balance'] < 0)
|
|
die("HOW DID WE END UP WITH NEGATIVE SURPLUS BALANCE?");
|
|
|
|
// If we've exhausted the credit, get it out of the
|
|
// available credit pool (but keep track of it for later).
|
|
if ($credit['balance'] <= 0 && count($credits))
|
|
$used_credits[] = array_shift($credits);
|
|
|
|
// Add a disbursement that uses the available credit to pay the charge
|
|
$disbursement = array('type' => $disbursement_type,
|
|
'account_id' => $disbursement_account_id,
|
|
'amount' => $disbursement_amount,
|
|
'effective_date' => $disbursement_date,
|
|
'transaction_id' => $disbursement_transaction_id,
|
|
'customer_id' => $charge['StatementEntry']['customer_id'],
|
|
'lease_id' => $charge['StatementEntry']['lease_id'],
|
|
'charge_entry_id' => $charge['StatementEntry']['id'],
|
|
'comment' => null,
|
|
);
|
|
|
|
$this->pr(20, compact('disbursement'),
|
|
'New Disbursement Entry');
|
|
|
|
$result = $this->addStatementEntry($disbursement);
|
|
$ret['Disbursement'][] = $result;
|
|
if ($result['error'])
|
|
$ret['error'] = true;
|
|
|
|
// Adjust the charge balance to reflect the new disbursement
|
|
$charge['balance'] -= $disbursement_amount;
|
|
if ($charge['balance'] < 0)
|
|
die("HOW DID WE GET A NEGATIVE CHARGE AMOUNT?");
|
|
|
|
if ($charge['balance'] <= 0)
|
|
$this->pr(20, 'Fully Paid Charge');
|
|
}
|
|
|
|
}
|
|
|
|
// Partially used credits must be added to the used list
|
|
if (isset($credits[0]['applied']))
|
|
$used_credits[] = array_shift($credits);
|
|
|
|
$this->pr(18, compact('credits', 'used_credits', 'receipt_credit'),
|
|
'Disbursements added');
|
|
|
|
// Clean up any explicit credits that have been used
|
|
foreach ($used_credits AS $credit) {
|
|
if ($credit['balance'] > 0) {
|
|
$this->pr(20, compact('credit'),
|
|
'Update Credit Entry');
|
|
|
|
$this->id = $credit['StatementEntry']['id'];
|
|
$this->saveField('amount', $credit['balance']);
|
|
}
|
|
else {
|
|
$this->pr(20, compact('credit'),
|
|
'Delete Exhausted Credit Entry');
|
|
|
|
$this->del($credit['StatementEntry']['id'], false);
|
|
}
|
|
}
|
|
|
|
// Convert non-exhausted receipt credit to an explicit one
|
|
if (!empty($receipt_credit['balance'])) {
|
|
$credit =& $receipt_credit;
|
|
|
|
$this->pr(18, compact('credit'),
|
|
'Create Explicit Credit');
|
|
|
|
$result = $this->addStatementEntry
|
|
(array('type' => 'SURPLUS',
|
|
'account_id' => $credit['LedgerEntry']['account_id'],
|
|
'amount' => $credit['balance'],
|
|
'effective_date' => $credit['Transaction']['stamp'],
|
|
'transaction_id' => $credit['Transaction']['id'],
|
|
'customer_id' => $customer_id,
|
|
'lease_id' => $lease_id,
|
|
));
|
|
$ret['Credit'] = $result;
|
|
if ($result['error'])
|
|
$ret['error'] = true;
|
|
}
|
|
|
|
return $this->prReturn($ret + array('error' => false));
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: stats
|
|
* - Returns summary data from the requested statement entry
|
|
*/
|
|
function stats($id = null, $query = null) {
|
|
$this->prEnter(compact('id', 'query'));
|
|
|
|
$this->queryInit($query);
|
|
unset($query['group']);
|
|
|
|
$stats = array();
|
|
if (isset($id))
|
|
$query['conditions'][] = array('StatementEntry.id' => $id);
|
|
|
|
// Determine the total in charges
|
|
$charge_query = $query;
|
|
unset($charge_query['link']['ChargeEntry']);
|
|
unset($charge_query['link']['DisbursementEntry']);
|
|
|
|
$charge_query['fields'] = array();
|
|
$charge_query['fields'][] = "SUM(StatementEntry.amount) AS total";
|
|
$charge_query['conditions'][] = array('StatementEntry.type' => $this->debitTypes());
|
|
$result = $this->find('first', $charge_query);
|
|
$stats['Charge'] = $result[0];
|
|
|
|
$this->pr(17, compact('charge_query', 'result'),
|
|
'Charges');
|
|
|
|
// Tally the amount actually _paid_ to those charges
|
|
$charge_disbursement_query = $charge_query;
|
|
$charge_disbursement_query['link']['DisbursementEntry'] = array('fields' => array());
|
|
$charge_disbursement_query['fields'] = array();
|
|
$charge_disbursement_query['fields'][] = "COALESCE(SUM(DisbursementEntry.amount),0) AS paid";
|
|
$charge_disbursement_query['conditions'][] = array('DisbursementEntry.type' => 'DISBURSEMENT');
|
|
$result = $this->find('first', $charge_disbursement_query);
|
|
$stats['Charge'] += $result[0];
|
|
|
|
$this->pr(17, compact('charge_disbursement_query', 'result'),
|
|
'Charge Disbursements');
|
|
|
|
// Tally the amount of charges that have been waived
|
|
$charge_waiver_query = $charge_query;
|
|
$charge_waiver_query['link']['DisbursementEntry'] = array('fields' => array());
|
|
$charge_waiver_query['fields'] = array();
|
|
$charge_waiver_query['fields'][] = "COALESCE(SUM(DisbursementEntry.amount),0) AS waived";
|
|
$charge_waiver_query['conditions'][] = array('DisbursementEntry.type' => 'WAIVER');
|
|
$result = $this->find('first', $charge_waiver_query);
|
|
$stats['Charge'] += $result[0];
|
|
|
|
$this->pr(17, compact('charge_waiver_query', 'result'),
|
|
'Charge Waivers');
|
|
|
|
// Compute some summary information for charges
|
|
$stats['Charge']['reconciled'] =
|
|
$stats['Charge']['paid'] + $stats['Charge']['waived'];
|
|
$stats['Charge']['balance'] =
|
|
$stats['Charge']['total'] - $stats['Charge']['reconciled'];
|
|
if (!isset($stats['Charge']['balance']))
|
|
$stats['Charge']['balance'] = 0;
|
|
|
|
|
|
// Determine the total in disbursements, including those which
|
|
// are charge waivers and those that do not even reconcile
|
|
// to charges (i.e. they are surplus disbursements).
|
|
$disbursement_query = $query;
|
|
unset($disbursement_query['link']['DisbursementEntry']);
|
|
$disbursement_query['link']['ChargeEntry'] = array('fields' => array());
|
|
|
|
$disbursement_query['fields'] = array();
|
|
$disbursement_query['fields'][] = "SUM(StatementEntry.amount) AS total";
|
|
$disbursement_query['fields'][] = "COALESCE(SUM(IF(ChargeEntry.id IS NULL, 0, StatementEntry.amount)), 0) AS charged";
|
|
$disbursement_query['fields'][] = "COALESCE(SUM(IF(ChargeEntry.id IS NULL, StatementEntry.amount, 0)), 0) AS surplus";
|
|
|
|
$disbursement_query['conditions'][] = array('StatementEntry.type' => array('DISBURSEMENT', 'WAIVER', 'SURPLUS'));
|
|
$result = $this->find('first', $disbursement_query);
|
|
$stats['Disbursement'] = $result[0];
|
|
|
|
$this->pr(17, compact('disbursement_query', 'result'),
|
|
'Disbursements');
|
|
|
|
// Compute some summary information for disbursements.
|
|
// Add a reconciled field just for consistency with Charge.
|
|
$stats['Disbursement']['reconciled'] =
|
|
$stats['Disbursement']['charged'];
|
|
$stats['Disbursement']['balance'] =
|
|
$stats['Disbursement']['total'] - $stats['Disbursement']['reconciled'];
|
|
if (!isset($stats['Disbursement']['balance']))
|
|
$stats['Disbursement']['balance'] = 0;
|
|
|
|
// 'balance' is simply the difference between
|
|
// the balances of charges and disbursements
|
|
$stats['balance'] = $stats['Charge']['balance'] - $stats['Disbursement']['balance'];
|
|
if (!isset($stats['balance']))
|
|
$stats['balance'] = 0;
|
|
|
|
// 'account_balance' is really only relevant to
|
|
// callers that have requested charge and disbursement
|
|
// stats with respect to a particular account.
|
|
// It represents the difference between inflow
|
|
// and outflow from that account.
|
|
$stats['account_balance'] = $stats['Charge']['paid'] - $stats['Disbursement']['total'];
|
|
if (!isset($stats['account_balance']))
|
|
$stats['account_balance'] = 0;
|
|
|
|
return $this->prReturn($stats);
|
|
}
|
|
|
|
} |