diff --git a/models/statement_entry.php b/models/statement_entry.php index edcb1be..8e72a7d 100644 --- a/models/statement_entry.php +++ b/models/statement_entry.php @@ -332,6 +332,11 @@ OPTION 2 ************************************************************************** * function: assignCredits * - Assigns all credits to existing charges + * + * REVISIT : 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 : 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 : 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; } diff --git a/models/tender.php b/models/tender.php index 31351c3..4f0d175 100644 --- a/models/tender.php +++ b/models/tender.php @@ -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']; + $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 : 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; } diff --git a/models/transaction.php b/models/transaction.php index f1a33ab..4e28584 100644 --- a/models/transaction.php +++ b/models/transaction.php @@ -391,7 +391,7 @@ class Transaction extends AppModel { array_flip(array('customer_id', 'lease_id'))) + array_intersect_key($entry, array_flip(array('type', 'account_id', 'amount', - 'effective_date', 'through_date', 'due_date'))); + 'effective_date', 'through_date', 'due_date'))); $se['comment'] = $entry['statement_entry_comment']; } @@ -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')));