all(); } function all() { $this->gridView('All Transactions', 'all'); } function invoice() { $this->gridView('Invoices'); } function receipt() { $this->gridView('Receipts'); } function deposit() { if (empty($this->params['admin'])) $this->sideMenuEnable('CONTROLLER', $this->std_area, false); $this->addSideMenuLink('New Deposit', array('controller' => 'tenders', 'action' => 'deposit'), null, 'ACTION'); $this->gridView('Deposits'); } function gridView($title, $action = null, $element = null) { $this->addSideMenuLink('Invoices', array('controller' => 'transactions', 'action' => 'invoice'), null, 'CONTROLLER'); $this->addSideMenuLink('Receipts', array('controller' => 'transactions', 'action' => 'receipt'), null, 'CONTROLLER'); $this->addSideMenuLink('Deposits', array('controller' => 'transactions', 'action' => 'deposit'), null, 'CONTROLLER'); $this->addSideMenuLink('All', array('controller' => 'transactions', 'action' => 'all'), null, 'CONTROLLER'); parent::gridView($title, $action, $element); } /************************************************************************** ************************************************************************** ************************************************************************** * virtuals: gridData * - With the application controller handling the gridData action, * these virtual functions ensure that the correct data is passed * to jqGrid. */ function gridDataCountTables(&$params, &$model) { return array ('link' => array(// Models 'Account' => array('fields' => array()), ), ); } function gridDataTables(&$params, &$model) { $link = $this->gridDataCountTables($params, $model); $link['link']['StatementEntry'] = array('fields' => array()); $link['link']['DepositTender'] = array('fields' => array()); return $link; } function gridDataFields(&$params, &$model) { $fields = parent::gridDataFields($params, $model); //$fields[] = 'COUNT(StatementEntry.id) AS entries'; $fields[] = ("IF(Transaction.type = 'DEPOSIT'," . " COUNT(DepositTender.id)," . " COUNT(StatementEntry.id)) AS entries"); return $fields; } function gridDataConditions(&$params, &$model) { $conditions = parent::gridDataConditions($params, $model); if (in_array($params['action'], array('invoice', 'receipt', 'deposit'))) $conditions[] = array('Transaction.type' => strtoupper($params['action'])); // REVISIT : 20090811 // No security issues have been worked out yet $conditions[] = array('Account.level >=' => 5); return $conditions; } function gridDataPostProcessLinks(&$params, &$model, &$records, $links) { $links['Transaction'] = array('id', 'action' => ($params['action'] == 'deposit' ? 'deposit_slip' : 'view')); return parent::gridDataPostProcessLinks($params, $model, $records, $links); } /************************************************************************** ************************************************************************** ************************************************************************** * action: postInvoice * - handles the creation of a charge invoice */ function postInvoice() { if (!$this->RequestHandler->isPost()) { echo('

THIS IS NOT A POST FOR SOME REASON

'); return; } if (!$this->Transaction->addInvoice($this->data, null, $this->data['Lease']['id'])) { $this->Session->setFlash("INVOICE FAILED", true); // REVISIT 20090706: // Until we can work out the session problems, // just die. die("

INVOICE FAILED

"); } $this->layout = null; $this->autoLayout = false; $this->autoRender = false; } /************************************************************************** ************************************************************************** ************************************************************************** * action: postReceipt * - handles the creation of a receipt */ function postReceipt() { if (!$this->RequestHandler->isPost()) { echo('

THIS IS NOT A POST FOR SOME REASON

'); return; } foreach($this->data['Entry'] AS &$entry) { $entry['Tender'] = $entry['type'][$entry['tender_type_id']]; unset($entry['type']); unset($entry['tender_type_id']); } if (!$this->Transaction->addReceipt($this->data, $this->data['Customer']['id'], (isset($this->data['Lease']['id']) ? $this->data['Lease']['id'] : null ))) { $this->Session->setFlash("RECEIPT FAILED", true); // REVISIT 20090706: // Until we can work out the session problems, // just die. die("

RECEIPT FAILED

"); } $this->layout = null; $this->autoLayout = false; $this->autoRender = false; } /************************************************************************** ************************************************************************** ************************************************************************** * action: postDeposit * - handles the creation of a deposit transaction */ function postDeposit() { if (!$this->RequestHandler->isPost()) { echo('

THIS IS NOT A POST FOR SOME REASON

'); return; } //pr($this->data); // Go through each type of tender presented to the user // Determine which are to be deposited, and which are to // have their corresponding account ledgers closed. $deposit_tender_ids = array(); $deposit_type_ids = array(); $close_type_ids = array(); foreach ($this->data['TenderType'] AS $type_id => $type) { $type['items'] = unserialize($type['items']); if (empty($type['selection']) || $type['selection'] === 'none' || ($type['selection'] === 'subset' && count($type['items']) == 0)) continue; // The deposit includes either the whole type, or just certain tenders if ($type['selection'] === 'all') $deposit_type_ids[] = $type_id; else $deposit_tender_ids = array_merge($deposit_tender_ids, $type['items']); // Should we close the ledger for this tender type? // First, the user would have to request that we do so, // but additionally, we shouldn't close a ledger unless // all the tenders are included in this deposit. That // doesn't guarantee that the ledger has a zero balance, // but it does carry the balance forward, and a total // deposit would imply a fresh start, so go for it. if (!empty($type['close']) && $type['selection'] === 'all') $close_type_ids[] = $type_id; } // Make sure we actually have something to deposit if (empty($deposit_type_ids) && empty($deposit_tender_ids)) { $this->Session->setFlash(__('Nothing to Deposit', true)); $this->redirect(array('controller' => 'tenders', 'action'=>'deposit')); } // Build up a set of conditions based on user selection $deposit_conditions = array(); if (!empty($deposit_type_ids)) $deposit_conditions[] = array('TenderType.id' => $deposit_type_ids); if (!empty($deposit_tender_ids)) $deposit_conditions[] = array('DepositTender.id' => $deposit_tender_ids); // Add in confirmation that items have not already been deposited $deposit_conditions = array(array('DepositTender.deposit_transaction_id' => null), array('OR' => $deposit_conditions)); // Lookup the items to be deposited $tenders = $this->Transaction->DepositTender->find ('all', array('contain' => array('TenderType', 'LedgerEntry'), 'conditions' => $deposit_conditions, )); // Build the deposit transaction $deposit = array('Transaction' => array(), 'Entry' => array()); foreach ($tenders AS $tender) { $deposit['Entry'][] = array('tender_id' => $tender['DepositTender']['id'], 'account_id' => $tender['LedgerEntry']['account_id'], 'amount' => $tender['LedgerEntry']['amount'], ); } //pr(compact('deposit_type_ids', 'deposit_tender_ids', 'close_type_ids', 'deposit_conditions', 'deposit')); // OK, perform the deposit and associated accounting $result = $this->Transaction->addDeposit ($deposit, $this->data['Deposit']['Account']['id']); //pr(compact('deposit', 'result')); // Close any ledgers necessary if (!empty($close_type_ids)) { // Find the accounts associated with the types to close ... $accounts = $this->Transaction->DepositTender->find ('all', array('contain' => array('TenderType.account_id'), 'conditions' => array(array('TenderType.id' => $close_type_ids)), )); // ... and close them $this->Transaction->Account->closeCurrentLedgers (array_map(create_function('$item', 'return $item["TenderType"]["account_id"];'), $accounts)); } // Look out for errors if ($result['error']) { $this->Session->setFlash(__('Unable to Create Deposit', true)); $this->redirect(array('controller' => 'tenders', 'action'=>'deposit')); } // Present the deposit slip to the user $this->redirect(array('controller' => 'transactions', 'action' => 'deposit_slip', $result['transaction_id'])); } /************************************************************************** ************************************************************************** ************************************************************************** * action: postWriteOff * - handles the write off of bad debt */ function postWriteOff() { if (!$this->RequestHandler->isPost()) { echo('

THIS IS NOT A POST FOR SOME REASON

'); return; } $data = $this->data; if (empty($data['Customer']['id'])) $data['Customer']['id'] = null; if (empty($data['Lease']['id'])) $data['Lease']['id'] = null; pr(compact('data')); if (!$this->Transaction->addWriteOff($data, $data['Customer']['id'], $data['Lease']['id'])) { $this->Session->setFlash("WRITE OFF FAILED", true); // REVISIT 20090706: // Until we can work out the session problems, // just die. die("

WRITE-OFF FAILED

"); } // Return to viewing the lease/customer if (empty($data['Lease']['id'])) $this->redirect(array('controller' => 'customers', 'action' => 'view', $data['Customer']['id'])); else $this->redirect(array('controller' => 'leases', 'action' => 'view', $data['Lease']['id'])); } /************************************************************************** ************************************************************************** ************************************************************************** * action: postRefund * - handles issuing a customer refund */ function postRefund() { if (!$this->RequestHandler->isPost()) { echo('

THIS IS NOT A POST FOR SOME REASON

'); return; } $data = $this->data; if (empty($data['Customer']['id'])) $data['Customer']['id'] = null; if (empty($data['Lease']['id'])) $data['Lease']['id'] = null; if (!$this->Transaction->addRefund($data, $data['Customer']['id'], $data['Lease']['id'])) { $this->Session->setFlash("REFUND FAILED", true); // REVISIT 20090706: // Until we can work out the session problems, // just die. die("

REFUND FAILED

"); } // Return to viewing the lease/customer if (empty($data['Lease']['id'])) $this->redirect(array('controller' => 'customers', 'action' => 'view', $data['Customer']['id'])); else $this->redirect(array('controller' => 'leases', 'action' => 'view', $data['Lease']['id'])); } /************************************************************************** ************************************************************************** ************************************************************************** * action: destroy * - Deletes a transaction and associated entries * - !!WARNING!! This should be used with EXTREME caution, as it * irreversibly destroys the data. It is not for normal use. */ function destroy($id = null) { $this->Transaction->destroy($id); //$this->redirect(array('action' => 'index')); } /************************************************************************** ************************************************************************** ************************************************************************** * action: view * - Displays information about a specific transaction */ function view($id = null) { $transaction = $this->Transaction->find ('first', array('contain' => array(// Models 'Account(id,name)', 'Ledger(id,sequence)', 'NsfTender(id,name)', ), 'conditions' => array(array('Transaction.id' => $id), // REVISIT : 20090811 // No security issues have been worked out yet array('OR' => array(array('Account.level >=' => 5), array('Account.id' => null))), ), )); // REVISIT : 20090815 // for debug purposes only (pr output) $this->Transaction->stats($id); if (empty($transaction)) { $this->Session->setFlash(__('Invalid Item.', true)); $this->redirect(array('action'=>'index')); } if ($transaction['Transaction']['type'] === 'DEPOSIT') $this->addSideMenuLink('View Slip', array('action' => 'deposit_slip', $id), null, 'ACTION'); if ($this->params['dev']) $this->addSideMenuLink('Destroy', array('action' => 'destroy', $id), array('confirmMessage' => "This may leave the database in an unstable state." . " Do NOT do this unless you know what you're doing." . " Proceed anyway?"), 'ACTION', $this->dev_area); // OK, prepare to render. $title = 'Transaction #' . $transaction['Transaction']['id']; $this->set(compact('transaction', 'title')); } /************************************************************************** ************************************************************************** ************************************************************************** * action: deposit_slip * - Special presentation * Processes the user input and updates the database */ function deposit_slip($id) { // Build a container for the deposit slip data $deposit = array('types' => array()); $this->id = $id; $deposit += $this->Transaction->find('first', array('contain' => false)); // Get a summary of all forms of tender in the deposit $result = $this->Transaction->find ('all', array('link' => array('DepositTender' => array('fields' => array(), 'TenderType', 'LedgerEntry' => array('fields' => array()))), 'fields' => array(//'TenderType.id', 'TenderType.name', "COUNT(DepositTender.id) AS 'count'", "SUM(LedgerEntry.amount) AS 'total'"), //'conditions' => array(array('DepositTender.deposit_transaction_id' => $id)), 'conditions' => array(array('Transaction.id' => $id)), 'group' => 'TenderType.id', )); if (empty($result)) { die(); $this->Session->setFlash(__('Invalid Deposit.', true)); $this->redirect(array('action'=>'deposit')); } // Add the summary to our deposit slip data container foreach ($result AS $type) { $deposit['types'][$type['TenderType']['id']] = $type['TenderType'] + $type[0]; } $deposit_total = 0; foreach ($deposit['types'] AS $type) $deposit_total += $type['total']; if ($deposit['Transaction']['amount'] != $deposit_total) $this->INTERNAL_ERROR("Deposit items do not add up to deposit slip total"); $this->addSideMenuLink('View', array('action' => 'view', $id), null, 'ACTION'); $title = 'Deposit Slip'; $this->set(compact('title', 'deposit')); $this->render('deposit_slip'); return; } }