array( 'className' => 'StatementEntry', ), ); var $hasMany = array( // The payments that apply to this charge (if it is one) 'PaymentEntry' => array( 'className' => 'StatementEntry', 'foreignKey' => 'charge_entry_id', ), ); /************************************************************************** ************************************************************************** ************************************************************************** * function: chargePaymentFields */ function chargePaymentFields($sum = false, $entry_name = 'StatementEntry') { $fields = array ( ($sum ? 'SUM(' : '') . "IF({$entry_name}.type = 'CHARGE'," . " {$entry_name}.amount, NULL)" . ($sum ? ')' : '') . ' AS charge' . ($sum ? 's' : ''), ($sum ? 'SUM(' : '') . "IF({$entry_name}.type = 'PAYMENT'," . " {$entry_name}.amount, NULL)" . ($sum ? ')' : '') . ' AS payment' . ($sum ? 's' : ''), ($sum ? 'SUM(' : '') . "IF({$entry_name}.type = 'CHARGE', 1, -1)" . " * IF({$entry_name}.amount, {$entry_name}.amount, 0)" . ($sum ? ')' : '') . ' AS balance', ); if ($sum) $fields[] = "COUNT({$entry_name}.id) AS entries"; return $fields; } /************************************************************************** ************************************************************************** ************************************************************************** * function: addCharge * - Adds a new charge */ function addCharge($data, $transaction, $customer_id, $lease_id = null) { // Create some models for convenience $A = new Account(); // Assume this will succeed $ret = true; // Establish the key charge parameters $charge = array_intersect_key($data, array('stamp'=>1, 'amount'=>1, 'account_id'=>1)); $charge['customer_id'] = $customer_id; $charge['lease_id'] = $lease_id; $charge['type'] = 'CHARGE'; $ids = $this->Entry->Ledger->Account->postLedgerEntry ($transaction, $charge, array('debit_ledger_id' => $A->currentLedgerID($data['account_id']), 'credit_ledger_id' => $A->currentLedgerID($A->accountReceivableAccountID()) ) + $data ); if ($ids['error']) $ret = false; return $ids['charge_id']; } /************************************************************************** ************************************************************************** ************************************************************************** * function: addPayment * - Adds a new payment */ function addPayment($data, $transaction, $customer_id, $lease_id = null) { // Create some models for convenience $A = new Account(); // Assume this will succeed $ret = true; // Establish the key payment parameters $payment = array_intersect_key($data, array('stamp'=>1, 'name'=>1, 'monetary_type'=>1, 'data1'=>1, 'data2'=>1, 'data3'=>1, 'data4'=>1, 'amount'=>1, 'account_id'=>1)); $payment['customer_id'] = $customer_id; $payment['type'] = 'PAYMENT'; $ids = $this->Entry->Ledger->Account->postLedgerEntry ($transaction, $payment, array('debit_ledger_id' => $A->currentLedgerID($data['account_id']), 'credit_ledger_id' => $A->currentLedgerID($A->accountReceivableAccountID()) ) + $data ); if ($ids['error']) $ret = false; return $ids['payment_id']; } /************************************************************************** ************************************************************************** ************************************************************************** * function: reverse * - Reverses the charges * * SAMPLE MOVE IN w/ PRE PAYMENT * DEPOSIT RENT A/R RECEIPT CHECK PETTY BANK * ------- ------- ------- ------- ------- ------- ------- * |25 | 25| | | | | * | |20 20| | | | | * | |20 20| | | | | * | |20 20| | | | | * | | |25 25| | | | * | | |20 20| | | | * | | |20 20| | | | * | | |20 20| | | | * | | | |85 85| | | * | | | | |85 | 85| * MOVE OUT and REFUND FINAL MONTH * DEPOSIT RENT C/P RECEIPT CHECK PETTY BANK * ------- ------- ------- ------- ------- ------- ------- * 25| | |25 | | | | t20 e20a * | 20| |20 | | | | t20 e20b * -ONE REFUND CHECK- * | | 25| |25 | | | t30 e30a * | | 20| |20 | | | t30 e30b * | | | 45| | | |45 t40 e40 * -OR MULTIPLE- * | | 15| |15 | | | t50a e50a * | | | 15| | |15 | t60a e60a * | | 30| |30 | | | t50b e50b * | | | 30| | | |30 t60b e60b * | | | | | | | OPTION 1 * |-25 | -25| | | | | * | |-20 -20| | | | | * | | |-25 -25| | | | * | | |-20 -20| | | | OPTION 2 * |-25 | | -25| | | | * | |-20 | -20| | | | * | | | |-15 | -15| | * | | | |-30 | | -30| * | | | | | | | * */ function reverse($ledger_entries, $stamp = null) { pr(array('Entry::reverse', compact('ledger_entries', 'stamp'))); // If the user only wants to reverse one ID, we'll allow it if (!is_array($ledger_entries)) $ledger_entries = $this->find ('all', array ('contain' => false, 'conditions' => array('Entry.id' => $ledger_entries))); $A = new Account(); $ar_account_id = $A->accountReceivableAccountID(); $receipt_account_id = $A->receiptAccountID(); $transaction_id = null; foreach ($ledger_entries AS $entry) { $entry = $entry['Entry']; $amount = -1*$entry['amount']; if (isset($entry['credit_account_id'])) $refund_account_id = $entry['credit_account_id']; elseif (isset($entry['CreditLedger']['Account']['id'])) $refund_account_id = $entry['CreditLedger']['Account']['id']; elseif (isset($entry['credit_ledger_id'])) $refund_account_id = $this->Ledger->accountID($entry['credit_ledger_id']); else return null; // post new refund in the income account $ids = $A->postEntry (array('transaction_id' => $transaction_id), null, array('debit_ledger_id' => $A->currentLedgerID($ar_account_id), 'credit_ledger_id' => $A->currentLedgerID($refund_account_id), 'effective_date' => $entry['effective_date'], 'through_date' => $entry['through_date'], 'amount' => $amount, 'lease_id' => $entry['lease_id'], 'customer_id' => $entry['customer_id'], 'comment' => "Refund; Entry #{$entry['id']}", ), array('debit' => array (array('Entry' => array('id' => $entry['id'], 'amount' => $amount))), ) ); if ($ids['error']) return null; $transaction_id = $ids['transaction_id']; pr(array('checkpoint' => 'Posted Refund Ledger Entry', compact('ids', 'amount', 'refund_account_id', 'ar_account_id'))); } return true; } /************************************************************************** ************************************************************************** ************************************************************************** * function: reconciledSet * - Returns the set of entries satisfying the given conditions, * along with any entries that they reconcile */ function reconciledSetQuery($set, $query) { $this->queryInit($query); if ($set == 'CHARGE' || $set == 'PAYMENT') $query['conditions'][] = array('StatementEntry.type' => $set); else die("INVALID RECONCILE SET"); if ($set == 'CHARGE') $query['link']['PaymentEntry'] = array('fields' => array("SUM(PaymentEntry.amount) AS reconciled")); if ($set == 'PAYMENT') $query['link']['ChargeEntry'] = array('fields' => array("SUM(ChargeEntry.amount) AS reconciled")); $query['group'] = 'StatementEntry.id'; return $query; } function reconciledSet($set, $query = null, $unrec = false) { $lquery = $this->reconciledSetQuery($set, $query); $result = $this->find('all', $lquery); //pr(compact('lquery', 'result')); //pr(array('reconciledSet', compact('set', 'lquery', 'result'))); $resultset = array(); foreach ($result AS $i => $entry) { //pr(compact('entry')); $entry['StatementEntry'] = $entry[0] + $entry['StatementEntry']; unset($entry[0]); $entry['StatementEntry']['balance'] = $entry['StatementEntry']['amount'] - $entry['StatementEntry']['reconciled']; // Since HAVING isn't a builtin feature of CakePHP, // we'll have to post-process to get the desired entries if ($entry['StatementEntry']['balance'] == 0) { if (!$unrec) $resultset[] = $entry; } else { if ($unrec) $resultset[] = $entry; } } //pr(compact('resultset')); //pr(array('StatementEntry::reconciledSet()' => compact('resultset'))); //$resultset['stats'] = $this->stats(null, $query); //pr($this->stats(null, $query)); return array('entries' => $resultset, 'summary' => $this->stats(null, $query)); } /************************************************************************** ************************************************************************** ************************************************************************** * function: reconciledEntries * - Returns a list of entries that reconcile against the given entry. * (such as payments towards a charge). */ function reconciledEntriesQuery($id, $query = null) { $this->queryInit($query, false); $this->id = $id; $this->recursive = -1; $this->read(); $query['conditions'][] = array('StatementEntry.id' => $id); if ($this->data['StatementEntry']['type'] == 'CHARGE') $query['link']['PaymentEntry'] = array(); if ($this->data['StatementEntry']['type'] == 'PAYMENT') $query['link']['ChargeEntry'] = array(); return $query; } function reconciledEntries($id, $query = null) { $lquery = $this->reconciledEntriesQuery($id, $query); //pr(array('reconciledEntries', compact('entry', 'contain'))); $result = $this->find('all', $lquery); foreach (array_keys($result) AS $i) unset($result[$i]['StatementEntry']); //pr(array('StatementEntry::reconciledEntries()' => compact('result'))); return array('entries' => $result); //'summary' => $this->stats($id, $query)); } /************************************************************************** ************************************************************************** ************************************************************************** * function: stats * - Returns summary data from the requested statement entry */ function stats($id = null, $query = null) { $this->queryInit($query); unset($query['group']); if (isset($id)) { $this->id = $id; $this->recursive = -1; $this->read(); if ($this->data['StatementEntry']['type'] == 'PAYMENT') { // Payment has only a _single_ charge, and by definition // 100% of the payment is used to reconcile that charge. return array('total' => $this->data['StatementEntry']['amount'], 'reconciled' => $this->data['StatementEntry']['amount'], 'balance' => 0); } $query['link']['PaymentEntry'] = array('fields' => array("SUM(COALESCE(PaymentEntry.amount,0)) AS reconciled"), //'type' => 'INNER', ); $query['fields'] = array('StatementEntry.amount'); $query['conditions'][] = array('StatementEntry.id' => $id); $query['group'] = 'StatementEntry.id'; $result = $this->find('first', $query); $result[0]['balance'] = $result['StatementEntry']['amount'] - $result[0]['reconciled']; //pr(compact('query', 'result')); return $result['StatementEntry'] + $result[0]; } $stats = array(); $rquery = $query; $rquery['link']['PaymentEntry'] = array('fields' => array("SUM(COALESCE(PaymentEntry.amount,0)) AS reconciled"), //'type' => 'INNER', ); $rquery['fields'] = array(); $rquery['fields'][] = "SUM(StatementEntry.amount) AS total"; $rquery['conditions'][] = array("PaymentEntry.id !=" => null); $result = $this->find('first', $rquery); $result[0]['balance'] = $result[0]['total'] - $result[0]['reconciled']; $stats['Payment'] = $result[0]; //pr(array('stats Payment', compact('rquery', 'stats'))); $rquery = $query; $rquery['fields'] = array(); $rquery['fields'][] = "SUM(StatementEntry.amount) AS total"; $rquery['fields'][] = "SUM(StatementEntry.amount) AS reconciled"; $result = $this->find('first', $rquery); $result[0]['balance'] = $result[0]['total'] - $result[0]['reconciled']; $stats['Charge'] = $result[0]; //pr(array('stats Charge', compact('rquery', 'stats'))); return $stats; $this->id = $id; $this->recursive = -1; $this->read(); //pr(array('stats()', compact('id', 'query', 'set'))); /* if ($this->data['StatementEntry']['type'] == 'CHARGE') */ /* $set = 'PAYMENT'; */ /* else */ /* $set = 'CHARGE'; */ /* $Set = ucfirst(strtolower($set)); */ /* $query['link'][$Set.'Entry'] = */ /* array('fields' => array("SUM(COALESCE({$Set}Entry.amount,0)) AS reconciled"), */ /* //'type' => 'INNER', */ /* ); */ /* $query['fields'] = array(); */ /* $query['fields'][] = "StatementEntry.amount AS total"; */ /* $query['fields'][] = "StatementEntry.amount - SUM(COALESCE({$Set}Entry.amount,0)) AS balance"; */ /* //$query['conditions'][] = array("{$Set}Entry.id !=" => null); */ /* $query['conditions'][] = array('StatementEntry.id' => $id); */ /* $query['group'] = 'StatementEntry.id'; */ /* $result = $this->find('first', $query); */ /* pr(compact('Rtype', 'query', 'result')); */ /* return $result['StatementEntry'] + $result[0]; */ if ($this->data['StatementEntry']['type'] == 'PAYMENT') { // Payment has only a _single_ charge, and by definition // 100% of the payment is used to reconcile that charge. return array('total' => $this->data['StatementEntry']['amount'], 'reconciled' => $this->data['StatementEntry']['amount'], 'balance' => 0); } $query['link']['PaymentEntry'] = array('fields' => array("SUM(COALESCE(PaymentEntry.amount,0)) AS reconciled"), //'type' => 'INNER', ); $query['fields'] = array(); $query['fields'][] = "StatementEntry.amount AS total"; $query['fields'][] = "StatementEntry.amount - SUM(COALESCE(PaymentEntry.amount,0)) AS balance"; //$query['conditions'][] = array("{$Set}Entry.id !=" => null); $query['conditions'][] = array('StatementEntry.id' => $id); $query['group'] = 'StatementEntry.id'; $result = $this->find('first', $query); //pr(compact('Rtype', 'query', 'result')); return $result['StatementEntry'] + $result[0]; } }