array( 'className' => 'Transaction', ), 'NsfTransaction' => array( 'className' => 'Transaction', ), ); /************************************************************************** ************************************************************************** ************************************************************************** * function: verifyTender * - Verifies consistenty of new tender data * (not in a pre-existing tender) */ function verifyTender($tender) { $this->prFunctionLevel(10); $this->prEnter(compact('tender')); if (empty($tender['tender_type_id'])) { return $this->prReturn(false); } return $this->prReturn(true); } /************************************************************************** ************************************************************************** ************************************************************************** * function: addTender * - Inserts new Tender into the database */ function addTender($tender) { $this->prEnter(compact('tender')); $ret = array(); if (!$this->verifyTender($tender)) return $this->prReturn(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']; } } $this->pr(20, array('Tender' => $tender), 'Pre-Save'); $this->create(); if (!$this->save($tender)) return $this->prReturn(array('error' => true) + $ret); $ret['tender_id'] = $this->id; return $this->prReturn($ret + array('error' => false)); } /************************************************************************** ************************************************************************** ************************************************************************** * function: nsf * - Flags the ledger entry as having insufficient funds * * Steps: * - Get information from Check (C1); for amount $A * - Find Bank Deposit matching to Tender * - New Transaction (T1) * - New Bank Deposit (D1) * - New Tender (N1); NSF; D1, * - Add new LedgerEntry (L1a); T1; debit:bank; -$A * - Add new LedgerEntry (L1b); T1; credit:NSF; -$A * - Add new LedgerEntry (L2a); T1; debit:NSF; -$A; N1 * - Add new LedgerEntry (L2b); T1; credit:A/R; -$A * - For Tx associated with LE associated with C1: * - For each Payment SE of Tx: * - Add new StatementEntry (S1n); T1; PAYMENT; -1*S1n.amount * - New Transaction (T2) (?????) * - Add new StatementEntry (S2); T2; CHARGE; NSF; $35 * - Add new LedgerEntry (L3a); T2; credit:NSF-Fee; $35 * - Add new LedgerEntry (L3b); T2; debit:A/R; $35 * - Set C1.nsf_tx = T1 * - Re-Reconcile (customer may have running credit) */ function nsf($id, $stamp = null) { //$this->prFunctionLevel(30); $this->prEnter(compact('id')); // Get information about this NSF item. $this->id = $id; $tender = $this->find ('first', array ('contain' => array('LedgerEntry', 'DepositTransaction', 'NsfTransaction'), )); //'conditions' => array(array('Tender.id' => $id)), $this->pr(20, compact('tender')); if (!empty($tender['NsfTransaction']['id'])) die("Item has already been set as NSF"); if (empty($tender['DepositTransaction']['id'])) die("Item has not been deposited yet"); // Enter the NSF // This is the transaction pulling money from the bank account // and recording it in the NSF account. It has nothing to do // with the customer statement (charges, payments, credits, etc). $nsf_result = $this->DepositTransaction->addDeposit (array('Transaction' => array(), 'Entry' => array(array('tender_id' => null, 'account_id' => $this->LedgerEntry->Account->nsfAccountID(), 'amount' => -1 * $tender['LedgerEntry']['amount'], ))), $tender['DepositTransaction']['account_id']); $this->pr(20, compact('result')); if ($nsf_result['error']) die("Unable to save NSF transaction"); // Since we may have saved the nsf transaction with a null // timestamp, query it back out of the database to find out // what timestamp was _really_ specified, for later use. $nsf_deposit = $this->DepositTransaction->find ('first', array('contain' => false, 'id' => $nsf_result['transaction_id'])); $nsf_deposit = $nsf_deposit['DepositTransaction']; // OK, now move into customer realm, finding all statement // entries that were affected by the bad payment (tender). $nsf_ledger_entry = $this->LedgerEntry->find ('first', array ('contain' => array('Transaction' => array(//'fields' => array(), 'StatementEntry' => array(//'fields' => array(), ), ), ), 'conditions' => array('LedgerEntry.id' => $tender['LedgerEntry']['id']), )); $this->pr(20, compact('nsf_ledger_entry')); // Build a transaction to adjust all of the statement entries $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) { if ($payment['type'] === 'SURPLUS') { $payment['type'] = 'VOID'; $this->NsfTransaction->StatementEntry->id = $payment['id']; $this->NsfTransaction->StatementEntry->saveField('type', $payment['type']); } else { $bounce['Entry'][] = 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' => $nsf_deposit['stamp'], ); } } // Record the transaction, which will un-pay previously paid // charges, void any credits, and other similar work. $this->pr(20, compact('bounce')); $bounce_result = $this->NsfTransaction->addNsf($bounce); $this->pr(20, compact('bounce_result')); if ($bounce_result['error']) die("Unable to save Bounce transaction"); // Flag the tender as NSF, using the items created above. $this->id = $id; $this->saveField('nsf_transaction_id', $nsf_result['transaction_id']); $this->saveField('nsf_ledger_entry_id', $bounce_result['entries'][0]['DoubleEntry']['Entry1']['ledger_entry_id']); // Add NSF Charge $result = $this->NsfTransaction->addInvoice (array('Transaction' => array('stamp' => $nsf_deposit['stamp'], ), 'Entry' => array (array('account_id' => $this->LedgerEntry->Account->nsfChargeAccountID(), 'effective_date' => $nsf_deposit['stamp'], // REVISIT : 20090730 // BAD, BAD, BAD... who would actually // hardcode a value like this???? ;-) 'amount' => 35, 'comment' => "NSF: " . $tender['Tender']['name'], ), ), ), $tender['Tender']['customer_id']); $this->pr(20, compact('result')); return $this->prReturn(true); } } ?>