Added effective date to each NSF entry, and fixed the account bug with receipts/payments. There is still a bug/question on how to handle nsf credits. It would be nice to simply delete the credit, since the check bounced, but then the original transaction will not add up. Either we decide that it doesn't need to add up, figure out a different type for the credit, or add a void flag to each statement entry and just void out nsf items. Or instead of a void flag, have an nsf_statement_entry_id which will also tie the payment/credit to the nsf payment/credit.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716/site@428 97e9348a-65ac-dc4b-aefc-98561f571b83
This commit is contained in:
@@ -332,6 +332,11 @@ OPTION 2
|
||||
**************************************************************************
|
||||
* 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 payments should be applied.
|
||||
*
|
||||
*/
|
||||
function assignCredits($query = null, $receipt_id = null) {
|
||||
/* pr(array('StatementEntry::assignCredits' => compact('query', 'receipt_id'))); */
|
||||
@@ -344,58 +349,45 @@ OPTION 2
|
||||
$lquery['conditions'][] = array('StatementEntry.type' => 'CREDIT');
|
||||
$lquery['order'][] = 'StatementEntry.effective_date ASC';
|
||||
$credits = $this->find('all', $lquery);
|
||||
/* pr(compact('lquery', 'credits')); */
|
||||
/* pr(array('StatementEntry::assignCredits' => */
|
||||
/* array('checkpoint' => "Credits Established") */
|
||||
/* + compact('credits'))); */
|
||||
|
||||
// Then, find all receipts that have not had all
|
||||
// monies dispursed for either payments or credits
|
||||
// REVISIT <AP>: If we implement CREDITS as we're
|
||||
// anticipating, then this concept of "anonymous"
|
||||
// credits won't exists (i.e. credits that are
|
||||
// not explicitly specified with a statement entry
|
||||
// of type CREDIT). All transactions MUST balance
|
||||
// out to the sum of their statement entries, so
|
||||
// we'll be able to just delete all the anon_credit
|
||||
// code.
|
||||
$lquery = $query;
|
||||
$lquery['link'] = array('StatementEntry' => array('fields' => array()) + $lquery['link']);
|
||||
$lquery['conditions'][] = array('Transaction.type' => 'RECEIPT');
|
||||
$lquery['fields'] = array('Transaction.id', 'Transaction.stamp', 'Transaction.amount',
|
||||
'Transaction.amount - SUM(COALESCE(StatementEntry.amount,0)) AS balance');
|
||||
$lquery['group'] = 'Transaction.id HAVING balance > 0';
|
||||
$anon_credits = $this->Transaction->find('all', $lquery);
|
||||
foreach ($anon_credits AS &$ac) {
|
||||
$ac['Transaction'] += $ac[0];
|
||||
unset($ac[0]);
|
||||
}
|
||||
/* pr(compact('lquery', 'anon_credits')); */
|
||||
|
||||
// Next, add in the credits from the newly added receipt
|
||||
// Next, establish credit from the newly added receipt
|
||||
$receipt_credit = null;
|
||||
if (!empty($receipt_id)) {
|
||||
$lquery = $query;
|
||||
$lquery['conditions'][] = array('Transaction.type' => 'RECEIPT');
|
||||
$lquery['conditions'][] = array('Transaction.id' => $receipt_id);
|
||||
$lquery['link'] += array('LedgerEntry' =>
|
||||
array('conditions' =>
|
||||
//array(LedgerEntry.'crdr'=>'DEBIT'),
|
||||
array('LedgerEntry.account_id !=' => $this->Account->accountReceivableAccountID()),
|
||||
));
|
||||
$lquery['fields'] = array('Transaction.id', 'Transaction.stamp', 'Transaction.amount',
|
||||
'Transaction.amount AS balance');
|
||||
'LedgerEntry.account_id');
|
||||
// Very specific case here... no extra conditions
|
||||
unset($lquery['conditions']);
|
||||
$this->Transaction->id = $receipt_id;
|
||||
$receipt_credit = $this->Transaction->find('first', $lquery);
|
||||
if ($receipt_credit)
|
||||
$anon_credits[] = $receipt_credit;
|
||||
if (!$receipt_credit)
|
||||
die("INTERNAL ERROR: UNABLE TO LOCATE RECEIPT");
|
||||
|
||||
/* pr(compact('lquery', 'anon_credits')); */
|
||||
$receipt_credit['balance'] = $receipt_credit['Transaction']['amount'];
|
||||
|
||||
/* pr(array('StatementEntry::assignCredits' => */
|
||||
/* array('checkpoint' => "Receipt Credit Added") */
|
||||
/* + compact('receipt_credit'))); */
|
||||
}
|
||||
|
||||
// REVISIT <AP>: 20090726
|
||||
// This algorithm shouldn't be hardcoded. We need to allow
|
||||
// the user to specify how payments should be applied.
|
||||
|
||||
// Now find all unpaid charges
|
||||
$lquery = $query;
|
||||
$lquery['order'] = 'StatementEntry.effective_date ASC';
|
||||
$charges = $this->reconciledSet('CHARGE', $query, true);
|
||||
/* pr(compact('lquery', 'charges')); */
|
||||
/* pr(array('StatementEntry::assignCredits' => */
|
||||
/* array('checkpoint' => "Outstanding Charges Determined") */
|
||||
/* + compact('charges'))); */
|
||||
|
||||
// Initialize our list of used credits
|
||||
$used_credits = array();
|
||||
$used_anon_credits = array();
|
||||
|
||||
// Work through all unpaid charges, applying payments as we go
|
||||
foreach ($charges['entries'] AS $charge) {
|
||||
@@ -409,15 +401,15 @@ OPTION 2
|
||||
// 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 && count($anon_credits) == 0) {
|
||||
if (count($credits) == 0 && empty($receipt_credit['balance'])) {
|
||||
/* pr(array('StatementEntry::assignCredits' => */
|
||||
/* array('checkpoint' => 'No available credits'))); */
|
||||
/* array('checkpoint' => 'No more available credits'))); */
|
||||
break;
|
||||
}
|
||||
|
||||
$charge['balance'] = $charge['StatementEntry']['balance'];
|
||||
while ($charge['balance'] > 0 &&
|
||||
(count($credits) || count($anon_credits))) {
|
||||
(count($credits) || !empty($receipt_credit['balance']))) {
|
||||
|
||||
/* pr(array('StatementEntry::assignCredits' => */
|
||||
/* array('checkpoint' => 'Attempt Charge Reconciliation') */
|
||||
@@ -430,19 +422,17 @@ OPTION 2
|
||||
$credit =& $credits[0];
|
||||
$payment_date = $credit['StatementEntry']['effective_date'];
|
||||
$payment_transaction_id = $credit['StatementEntry']['transaction_id'];
|
||||
$payment_account_id = $credit['StatementEntry']['account_id'];
|
||||
|
||||
if (!isset($credit['balance']))
|
||||
$credit['balance'] = $credit['StatementEntry']['amount'];
|
||||
}
|
||||
elseif (count($anon_credits)) {
|
||||
// Peel off the first credit available
|
||||
$credit =& $anon_credits[0];
|
||||
elseif (!empty($receipt_credit['balance'])) {
|
||||
// Use our only receipt credit
|
||||
$credit =& $receipt_credit;
|
||||
$payment_date = $credit['Transaction']['stamp'];
|
||||
$payment_transaction_id = $credit['Transaction']['id'];
|
||||
|
||||
if (!isset($credit['balance']))
|
||||
$credit['balance'] = $credit['Transaction']['balance'];
|
||||
|
||||
$payment_account_id = $credit['LedgerEntry']['account_id'];
|
||||
}
|
||||
else {
|
||||
die("HOW DID WE GET HERE WITH NO CREDITS?");
|
||||
@@ -477,7 +467,7 @@ OPTION 2
|
||||
|
||||
// Add a payment that uses the available credit to pay the charge
|
||||
$payment = array('type' => 'PAYMENT',
|
||||
'account_id' => $this->Account->accountReceivableAccountID(),
|
||||
'account_id' => $payment_account_id,
|
||||
'amount' => $payment_amount,
|
||||
'effective_date' => $payment_date,
|
||||
'transaction_id' => $payment_transaction_id,
|
||||
@@ -535,10 +525,9 @@ OPTION 2
|
||||
}
|
||||
}
|
||||
|
||||
// Convert non-exhausted implicit credits to explicit ones
|
||||
foreach ($anon_credits AS $credit) {
|
||||
if ($credit['balance'] <= 0)
|
||||
die("HOW DID EXHAUSTED ANON CREDITS GET LEFT?");
|
||||
// Convert non-exhausted receipt credit to an explicit one
|
||||
if (!empty($receipt_credit['balance'])) {
|
||||
$credit =& $receipt_credit;
|
||||
|
||||
/* pr(array('StatementEntry::assignCredits' => */
|
||||
/* array('checkpoint' => 'Create Explicit Credit') */
|
||||
@@ -546,13 +535,13 @@ OPTION 2
|
||||
|
||||
$result = $this->addStatementEntry
|
||||
(array('type' => 'CREDIT',
|
||||
'account_id' => $this->Account->accountReceivableAccountID(),
|
||||
'account_id' => $credit['LedgerEntry']['account_id'],
|
||||
'amount' => $credit['balance'],
|
||||
'effective_date' => $credit['Transaction']['stamp'],
|
||||
'transaction_id' => $credit['Transaction']['id'],
|
||||
'customer_id' => $credit['Customer']['id'],
|
||||
));
|
||||
$ret['Credit'][] = $result;
|
||||
$ret['Credit'] = $result;
|
||||
if ($result['error'])
|
||||
$ret['error'] = true;
|
||||
}
|
||||
|
||||
@@ -145,6 +145,10 @@ class Tender extends AppModel {
|
||||
$this->saveField('nsf_transaction_id', $result['transaction_id']);
|
||||
|
||||
|
||||
$nsf_deposit = $this->DepositTransaction->find
|
||||
('first', array('contain' => false, 'id' => $result['transaction_id']));
|
||||
$nsf_deposit = $nsf_deposit['DepositTransaction'];
|
||||
|
||||
$nsf_ledger_entry = $this->LedgerEntry->find
|
||||
('first', array
|
||||
('contain' => array('Transaction' =>
|
||||
@@ -159,22 +163,22 @@ class Tender extends AppModel {
|
||||
|
||||
pr(compact('nsf_ledger_entry'));
|
||||
|
||||
$bounce = array('Transaction' => array(),
|
||||
'Entry' => array());
|
||||
$bounce = array('Transaction' => array(), 'Entry' => array());
|
||||
|
||||
$bounce['Transaction']['stamp'] = $nsf_deposit['stamp'];
|
||||
$bounce['Transaction']['account_id'] = $this->LedgerEntry->Account->nsfAccountID();
|
||||
$bounce['Transaction']['customer_id'] = $tender['Tender']['customer_id'];
|
||||
$bounce['Transaction']['amount'] = -1 * $tender['LedgerEntry']['amount'];
|
||||
|
||||
foreach ($nsf_ledger_entry['Transaction']['StatementEntry'] AS $payment) {
|
||||
$bounce['Entry'][] =
|
||||
array('type' => 'PAYMENT',
|
||||
array('type' => $payment['type'],
|
||||
'amount' => -1 * $payment['amount'],
|
||||
'account_id' => $this->LedgerEntry->Account->nsfAccountID(),
|
||||
'customer_id' => $payment['customer_id'],
|
||||
'lease_id' => $payment['lease_id'],
|
||||
'charge_entry_id' => $payment['charge_entry_id'],
|
||||
//'effective_date' => $tender[ stamp ],
|
||||
'effective_date' => $nsf_deposit['stamp'],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -184,143 +188,10 @@ class Tender extends AppModel {
|
||||
if ($result['error'])
|
||||
die("Unable to save Bounce transaction");
|
||||
|
||||
die();
|
||||
// REVISIT <AP>: 20090730
|
||||
// Add NSF Charge
|
||||
|
||||
$nsf_account_id = $A->nsfAccountID();
|
||||
$nsf_fee_account_id = $A->nsfChargeAccountID();
|
||||
$ar_account_id = $A->accountReceivableAccountID();
|
||||
$receipt_account_id = $A->receiptAccountID();
|
||||
|
||||
$t4_id = null;
|
||||
$t5_id = null;
|
||||
foreach ($source['LedgerEntry'] AS $e3) {
|
||||
// We expect only a single e4 entry
|
||||
$e4 = $e3['CreditReconciliationLedgerEntry'];
|
||||
if (count($e4) < 1)
|
||||
continue;
|
||||
if (count($e4) > 1)
|
||||
die('Too many e4 entries');
|
||||
|
||||
// Pullup e4 from the single member array
|
||||
$e4 = $e4[0];
|
||||
|
||||
// e3 amount
|
||||
$amount = -$e3['amount'];
|
||||
|
||||
// e4 account
|
||||
$bank_account_id = $e4['DebitLedger']['account_id'];
|
||||
|
||||
// post new e5
|
||||
$e5_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t4_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($bank_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($nsf_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => $amount,
|
||||
'lease_id' => $e3['lease_id'],
|
||||
'customer_id' => $e3['customer_id'],
|
||||
'comment' => "NSF Bank Reversal; Monetary Source #{$id}",
|
||||
)
|
||||
);
|
||||
|
||||
if ($e5_ids['error'])
|
||||
return null;
|
||||
$t4_id = $e5_ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e5',
|
||||
compact('e5_ids', 'amount')));
|
||||
|
||||
// post new e6... this will be our crossover point
|
||||
// from typical positive entries to negative entries.
|
||||
// Therefore, no reconciliation on this account.
|
||||
$e6_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t5_id),
|
||||
array('monetary_source_id' => $e3['monetary_source_id']),
|
||||
array('debit_ledger_id' => $A->currentLedgerID($nsf_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($receipt_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => $amount,
|
||||
'lease_id' => $e3['lease_id'],
|
||||
'customer_id' => $e3['customer_id'],
|
||||
'comment' => "NSF tracker; Monetary Source #{$id}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $e5_ids['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
);
|
||||
|
||||
if ($e6_ids['error'])
|
||||
return null;
|
||||
$t5_id = $e6_ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e6',
|
||||
compact('e6_ids', 'amount')));
|
||||
|
||||
$t6_id = null;
|
||||
foreach ($e3['DebitReconciliationLedgerEntry'] AS $e2) {
|
||||
foreach ($e2['DebitReconciliationLedgerEntry2'] AS $e1) {
|
||||
$amount = -1*$e1['Reconciliation']['amount'];
|
||||
|
||||
// post new e7
|
||||
$e7_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t6_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($receipt_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($ar_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => $amount,
|
||||
'lease_id' => $e1['lease_id'],
|
||||
'customer_id' => $e1['customer_id'],
|
||||
'comment' => "NSF Receipt; Monetary Source #{$id}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $e6_ids['id'],
|
||||
'amount' => $amount))),
|
||||
|
||||
'credit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $e1['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
);
|
||||
|
||||
if ($e7_ids['error'])
|
||||
return null;
|
||||
$t6_id = $e7_ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e7',
|
||||
compact('e7_ids', 'amount')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cheat for now
|
||||
$customer_id = $source['LedgerEntry'][0]['customer_id'];
|
||||
$lease_id = null;
|
||||
|
||||
// post new e8
|
||||
$e8_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t6_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($ar_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($nsf_fee_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => 35,
|
||||
'lease_id' => $lease_id,
|
||||
'customer_id' => $customer_id,
|
||||
'comment' => "NSF Fee; Monetary Source #{$id}",
|
||||
)
|
||||
);
|
||||
|
||||
if ($e8_ids['error'])
|
||||
return null;
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e8',
|
||||
compact('e8_ids')));
|
||||
/* $nsf_fee_account_id = $A->nsfChargeAccountID(); */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -406,7 +406,7 @@ class Transaction extends AppModel {
|
||||
|
||||
/* pr(array('Transaction::addTransaction' => */
|
||||
/* array('checkpoint' => 'Pre Transaction Save') */
|
||||
/* + compact('transaction'))); */
|
||||
/* + compact('transaction', 'data'))); */
|
||||
|
||||
// Save transaction to the database
|
||||
$this->create();
|
||||
@@ -521,6 +521,7 @@ class Transaction extends AppModel {
|
||||
$se =
|
||||
array_intersect_key($entry,
|
||||
array_flip(array('type', 'account_id', 'amount',
|
||||
'effective_date', 'through_date', 'due_date',
|
||||
'customer_id', 'lease_id',
|
||||
'charge_entry_id')));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user