array( 'className' => 'Transaction', ), 'NsfTransaction' => array( 'className' => 'Transaction', ), ); /************************************************************************** ************************************************************************** ************************************************************************** * function: verifyTender * - Verifies consistenty of new tender data * (not in a pre-existing tender) */ function verifyTender($tender) { /* pr(array("Tender::verifyTender()" */ /* => compact('tender'))); */ if (empty($tender['tender_type_id'])) { /* pr(array("Tender::verifyTender()" */ /* => "Tender verification failed")); */ return false; } return true; } /************************************************************************** ************************************************************************** ************************************************************************** * function: addTender * - Inserts new Tender into the database */ function addTender($tender) { /* pr(array('Tender::addTender' => */ /* compact('tender'))); */ $ret = array(); if (!$this->verifyTender($tender)) return array('error' => true) + $ret; // Come up with a (not necessarily unique) name for the tender. // For checks & money orders, this will be based on the check // number. For other types of tender, we'll just use the // generic name of the monetary account. // REVISIT : 20090723 // I would like to have cash named "Cash #1234", where // the number would correspond to either the Tender ID // or the LedgerEntry ID. if (empty($tender['name']) && !empty($tender['account_id'])) { $tender['name'] = $this->LedgerEntry->Account->name($tender['account_id']); if ($tender['account_id'] == $this->LedgerEntry->Account->checkAccountID() || $tender['account_id'] == $this->LedgerEntry->Account->moneyOrderAccountID()) { $tender['name'] .= ' #' . $tender['data1']; } } /* pr(array('Tender::addTender' => */ /* array('checkpoint' => 'Pre-Save') */ /* + array('Tender' => $tender))); */ $this->create(); if (!$this->save($tender)) return array('error' => true) + $ret; $ret['tender_id'] = $this->id; return $ret + array('error' => false); } /************************************************************************** ************************************************************************** ************************************************************************** * function: nsf * - Flags the ledger entry as having insufficient funds * - NOTE: nsf only works if given the monetary source id * to transaction e3, below * - NOTE: In order to show that the rent formerly considered * "collected" is now recognized in reverse, we must * credit A/R with a negative amount in order to * reconcile it against the Rent<->A/R ledger entry. * * FEE RENT A/R RECEIPT CHECK NSF BANK * ------- ------- ------- ------- ------- ------- ------- * | |30 30| | | | | t1 e1a : R e2/e7a : * | |20 20| | | | | t1 e1b : R e2/e7b : * | | | | | | | * | | |30 30| | | | t2 e2a : R e3 : R e1a * | | |20 20| | | | t2 e2b : R e3 : R e1b * | | | |50 50| | | t2 e3 : R e4 : R e2 * | | | | | | | * | | | | |50 | 50| t3 e4 : : R e3 * | | | | | | | * | | | | | |-50 -50| t4 e5 : : R e6 * | | | |-50 | -50| | t5 e6 : R e5 : R e7a/e7b * | | |-30 -30| | | | t6 e7a : R e6 : R e1a * | | |-20 -20| | | | t6 e7b : R e6 : R e1b * |35 | 35| | | | | t6 e8 * */ function nsf($id, $stamp = null) { pr(array('Tender::nsf', compact('id'))); $A = new Account(); // Get the LedgerEntries that use this monetary source $source = $this->find ('first', array('contain' => array(/* e3 */ 'LedgerEntry' => array('Transaction.id', 'Tender.id', 'Customer.id', 'Lease.id', /* e3 debit */ 'DebitLedger' => /* e.g. CHECK Ledger */ array('fields' => array(), 'Account' => /* e.g. CHECK Account */ array('fields' => array('id', 'name'), 'conditions' => array('Account.payable' => 1, 'Account.type' => 'ASSET'), ), ), /* e3 credit */ 'CreditLedger' => /* i.e. RECEIPT Ledger */ array('fields' => array('id'), 'Account' => /* i.e. RECEIPT Account */ array('fields' => array('id', 'name'), 'conditions' => array('Account.id' => $A->receiptAccountID()), ), ), /* e2 */ 'DebitReconciliationLedgerEntry' => array(/* e2 credit */ 'CreditLedger' => /* i.e. A/R Ledger */ array('fields' => array(), 'Account' => /* i.e. A/R Account */ array('fields' => array(), 'conditions' => array('Account.id' => $A->accountReceivableAccountID()), ), ), /* e1 */ // STUPID CakePHP bug screws up CLASS contains CLASS. // Use the same class, but with different name. 'DebitReconciliationLedgerEntry2', ), /* e4 */ 'CreditReconciliationLedgerEntry' => array(/* e4 debit */ 'DebitLedger' => /* e.g. BANK Ledger */ array('fields' => array('id'), 'Account' => /* e.g. BANK Account */ array('fields' => array('id', 'name'), 'conditions' => array('Account.depositable' => 1), ), ), ), ), ), 'conditions' => array(array('Tender.id' => $id)), )); pr($source); $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'))); return true; } } ?>