Files
pmgr/models/transaction.php
abijah ccf7a5fccd This is the remainder of the previous, invalid, partial checkin.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716/site@419 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-07-29 23:25:03 +00:00

463 lines
15 KiB
PHP

<?php
class Transaction extends AppModel {
var $belongsTo = array(
'Account',
'Ledger',
);
var $hasMany = array(
'LedgerEntry',
'StatementEntry',
'DepositTender' => array(
'className' => 'Tender',
'foreignKey' => 'deposit_transaction_id',
),
'Charge' => array(
'className' => 'StatementEntry',
'conditions' => array('Charge.type' => 'CHARGE')
),
'Payment' => array(
'className' => 'StatementEntry',
'conditions' => array('Payment.type' => 'PAYMENT')
),
'Debit' => array(
'className' => 'LedgerEntry',
'conditions' => array('Debit.crdr' => 'DEBIT')
),
'Credit' => array(
'className' => 'LedgerEntry',
'conditions' => array('Credit.crdr' => 'CREDIT')
),
);
/**************************************************************************
**************************************************************************
**************************************************************************
* function: addInvoice
* - Adds a new invoice invoice
*/
function addInvoice($data, $customer_id, $lease_id = null) {
// Establish the transaction as an invoice
$invoice =& $data['Transaction'];
$invoice['type'] = 'INVOICE';
$invoice['crdr'] = 'DEBIT';
$invoice['account_id'] = $this->Account->accountReceivableAccountID();
$invoice['customer_id'] = $customer_id;
$invoice['lease_id'] = $lease_id;
// Go through the statement entries and flag as charges
foreach ($data['Entry'] AS &$entry) {
$entry['type'] = 'CHARGE';
$entry['crdr'] = 'CREDIT';
}
$ids = $this->addTransaction($data);
if (isset($ids['transaction_id']))
$ids['invoice_id'] = $ids['transaction_id'];
return $ids;
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: addReceipt
* - Adds a new receipt
*/
function addReceipt($data, $customer_id, $lease_id = null) {
// Establish the transaction as a receipt
$receipt =& $data['Transaction'];
$receipt['type'] = 'RECEIPT';
$receipt['crdr'] = 'CREDIT';
$receipt['account_id'] = $this->Account->accountReceivableAccountID();
$receipt['customer_id'] = $customer_id;
$receipt['lease_id'] = $lease_id;
// Go through the statement entries and flag as payments
foreach ($data['Entry'] AS &$entry) {
$entry['crdr'] = 'DEBIT';
if (isset($entry['Tender']['tender_type_id'])) {
$entry['account_id'] = $this->LedgerEntry->Tender->TenderType->
accountID($entry['Tender']['tender_type_id']);
}
}
$ids = $this->addTransaction($data);
if (isset($ids['transaction_id']))
$ids['receipt_id'] = $ids['transaction_id'];
return $ids;
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: addDeposit
* - Adds a new bank deposit
*/
function addDeposit($data, $account_id) {
// Establish the transaction as a deposit
$deposit =& $data['Transaction'];
$deposit['type'] = 'DEPOSIT';
$deposit['crdr'] = 'DEBIT';
$deposit['account_id'] = $account_id;
$deposit['customer_id'] = null;
$deposit['lease_id'] = null;
// Save the list of IDs, so that we can mark their
// deposit transaction after it has been created.
$tender_ids = array_map(create_function('$item', 'return $item["tender_id"];'),
$data['Entry']);
// Go through the statement entries and re-group by account id
$group = array();
foreach ($data['Entry'] AS &$entry) {
if (!isset($group[$entry['account_id']]))
$group[$entry['account_id']] =
array('account_id' => $entry['account_id'],
'crdr' => 'CREDIT',
'amount' => 0);
$group[$entry['account_id']]['amount'] += $entry['amount'];
}
$data['Entry'] = $group;
$ids = $this->addTransaction($data);
if (isset($ids['transaction_id']))
$ids['deposit_id'] = $ids['transaction_id'];
if (!empty($ids['deposit_id'])) {
$this->LedgerEntry->Tender->updateAll
(array('Tender.deposit_transaction_id' => $ids['deposit_id']),
array('Tender.id' => $tender_ids)
);
}
return $ids;
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: verifyTransaction
* - Verifies consistenty of new transaction data
* (not in a pre-existing transaction)
*/
function verifyTransaction($transaction, $entries) {
/* pr(array("Transaction::verifyTransaction()" */
/* => compact('transaction', 'entries', 'customer_id', 'lease_id'))); */
// Verify required Transaction data is present
if (empty($transaction['type']) ||
empty($transaction['account_id']) ||
empty($transaction['crdr']) ||
(in_array($transaction['type'], array('INVOICE', 'RECEIPT'))
&& empty($transaction['customer_id'])) ||
(in_array($transaction['type'], array('INVOICE'))
&& empty($transaction['lease_id']))
) {
/* pr(array("Transaction::verifyTransaction()" */
/* => "Transaction verification failed")); */
return false;
}
// Verify all entries
foreach ($entries AS $entry) {
extract($entry);
if (!$this->LedgerEntry->DoubleEntry->verifyDoubleEntry($le1, $le2, $le1_tender)) {
/* pr(array("Transaction::verifyTransaction()" */
/* => "Double Entry verification failed")); */
return false;
}
if ($transaction['type'] == 'INVOICE' &&
!$this->StatementEntry->verifyStatementEntry($se)) {
/* pr(array("Transaction::verifyTransaction()" */
/* => "Statement Entry verification failed")); */
return false;
}
}
return true;
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: addTransaction
* - Adds a new transaction, and the appropriate ledger and statement
* entries, as layed out in the $data['Entry'] array. The array is
* overloaded, since it is used to create both ledger _and_ statement
* entries.
*
* $data
* - Transaction
* - [MANDATORY]
* - type (INVOICE, RECEIPT)
* - account_id
* - crdr
* - [OPTIONAL]
* - stamp
* (default: NOW)
* - comment
* - [AUTOMATICALLY SET] (if set, these items will be overwritten)
* - id
* - amount
* - customer_id
* - ledger_id
*
* - Entry (array)
* - [MANDATORY]
* - type (CHARGE, PAYMENT)
* - account_id
* - crdr
* - amount
* - [OPTIONAL]
* - effective_date
* - through_date
* - due_date
* - comment (used for statement or ledger entry, based on context)
* - ledger_entry_comment
* - statement_entry_comment
* - Tender
* - [MANDATORY]
* - tender_type_id
* - [OPTIONAL]
* - name
* (default: Entry Account Name & data1)
* - data1, data2, data3, data4
* - comment
* - [AUTOMATICALLY SET] (if set, these items will be overwritten)
* - id
* - ledger_entry_id
* - deposit_transaction_id
* - nsf_transaction_id
* - [AUTOMATICALLY SET] (if set, these items will be overwritten)
* - id
* - transaction_id
* - ledger_id
*
*/
function addTransaction($data) {
/* pr(array("Transaction::addTransaction()" */
/* => compact('data'))); */
// Verify that we have a transaction and entries
if (empty($data['Transaction']) || empty($data['Entry']))
return array('error' => true);
// Extract the transaction to a local variable
$transaction = $data['Transaction'];
// set ledger ID as the current ledger of the specified account
$transaction['ledger_id'] =
$this->Account->currentLedgerID($transaction['account_id']);
// Automatically figure out the customer if we have the lease
if (!empty($transaction['lease_id']) && empty($transaction['customer_id'])) {
$L = new Lease();
$L->recursive = -1;
$lease = $L->read(null, $transaction['lease_id']);
$transaction['customer_id'] = $lease['Lease']['customer_id'];
}
// Break each entry out of the combined statement/ledger entry
// and into individual entries appropriate for saving. While
// we're at it, calculate the transaction total as well.
$transaction['amount'] = 0;
foreach ($data['Entry'] AS &$entry) {
// Add entry amount into the transaction total
$transaction['amount'] += $entry['amount'];
$entry['customer_id'] = $transaction['customer_id'];
$entry['lease_id'] = $transaction['lease_id'];
$entry['ledger_id'] =
$this->Account->currentLedgerID($entry['account_id']);
// Set up our comments, possibly using the default 'comment' field
if (empty($entry['ledger_entry_comment'])) {
if ($transaction['type'] != 'INVOICE' && !empty($entry['comment']))
$entry['ledger_entry_comment'] = $entry['comment'];
else
$entry['ledger_entry_comment'] = null;
}
if (empty($entry['statement_entry_comment'])) {
if ($transaction['type'] == 'INVOICE' && !empty($entry['comment']))
$entry['statement_entry_comment'] = $entry['comment'];
else
$entry['statement_entry_comment'] = null;
}
// Create one half of the Double Ledger Entry (and the Tender)
$le1 =
array_intersect_key($entry,
array_flip(array('account_id', 'crdr', 'amount')));
$le1['comment'] = $entry['ledger_entry_comment'];
$le1_tender = isset($entry['Tender']) ? $entry['Tender'] : null;
// Create the second half of the Double Ledger Entry
$le2 =
array_intersect_key($transaction,
array_flip(array('account_id', 'crdr'))) +
array_intersect_key($entry,
array_flip(array('amount')));
if ($transaction['type'] == 'INVOICE') {
// Create a statement entry
// (PAYMENTS will have statement entries created below, when
// assigning credits, and DEPOSITS don't have statement entries)
$se =
array_intersect_key($transaction,
array_flip(array('customer_id', 'lease_id'))) +
array_intersect_key($entry,
array_flip(array('type', 'account_id', 'amount',
'effective_date', 'through_date', 'due_date')));
$se['comment'] = $entry['statement_entry_comment'];
}
// Replace combined entry with our new individual entries
$entry = compact('le1', 'le1_tender', 'le2', 'se');
}
// Move forward, verifying and saving everything.
$ret = array();
if (!$this->verifyTransaction($transaction, $data['Entry']))
return array('error' => true) + $ret;
/* pr(array('Transaction::addTransaction' => */
/* array('checkpoint' => 'Pre Transaction Save') */
/* + compact('transaction'))); */
// Save transaction to the database
$this->create();
if (!$this->save($transaction))
return array('error' => true) + $ret;
// Set up our return ids array
$ret['transaction_id'] = $this->id;
$ret['entries'] = array();
$ret['error'] = false;
// Go through the entered charges
foreach ($data['Entry'] AS $e_index => &$entry) {
extract($entry);
$le1['transaction_id'] = $le2['transaction_id'] = $ret['transaction_id'];
if (isset($le1_tender))
$le1_tender['customer_id'] = $transaction['customer_id'];
$result = $this->LedgerEntry->DoubleEntry->addDoubleEntry($le1, $le2, $le1_tender);
$ret['entries'][$e_index]['DoubleEntry'] = $result;
if ($result['error']) {
$ret['error'] = true;
continue;
}
if (isset($se)) {
$se['transaction_id'] = $ret['transaction_id'];
$result = $this->StatementEntry->addStatementEntry($se);
$ret['entries'][$e_index]['StatementEntry'] = $result;
if ($result['error']) {
$ret['error'] = true;
continue;
}
}
}
if (($transaction['type'] == 'INVOICE' ||
$transaction['type'] == 'RECEIPT') &&
!$ret['error']) {
$result = $this->StatementEntry->assignCredits
(array('link' => array('Customer'),
'conditions' => array('Customer.id' => $transaction['customer_id'])),
($transaction['type'] == 'RECEIPT'
? $ret['transaction_id']
: null));
$ret['assigned'] = $result;
if ($result['error'])
$ret['error'] = true;
}
/* pr(array('Transaction::addTransaction' => */
/* array('return' => $ret))); */
return $ret;
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: stats
* - Returns summary data from the requested transaction
*/
function stats($id = null, $query = null, $balance_account_id = null) {
//pr(array('Transaction::stats' => compact('id', 'query')));
$this->queryInit($query);
unset($query['group']);
if (isset($id)) {
$query['conditions'][] = array('Transaction.id' => $id);
$query['group'] = 'Transaction.id';
}
else
// CakePHP seems to automagically add in our ID as a part
// of the query conditions, but only on a 'first' query,
// not an 'all'. I suppose this is helpful :-/
unset($this->id);
if (empty($query['fields']))
$query['fields'] = array();
$stats = array();
foreach (array_keys($this->hasMany) AS $table) {
$squery = $query;
$squery['link'][$table] = array('fields' => array());
if ($table == 'LedgerEntry') {
if (isset($balance_account_id)) {
$squery['link']['LedgerEntry']['Account'] = array('fields' => array());
$squery['conditions'][] = array("Account.id" => $balance_account_id);
}
$squery['fields'] = array_merge($squery['fields'],
$this->LedgerEntry->debitCreditFields(true, $balance_account_id != null));
}
elseif ($table == 'StatementEntry') {
$squery['fields'] = array_merge($squery['fields'],
$this->StatementEntry->chargePaymentFields(true));
}
else {
$squery['fields'][] = "SUM({$table}.amount) AS total";
$squery['fields'][] = "COUNT({$table}.id) AS entries";
}
$stats[$table] = $this->find('first', $squery);
// REVISIT <AP>: 20090724
// [0][0] is for when we do an 'all' query. This can
// be removed at some point, but I'm keeping it while
// toggling between 'all' and 'first' (testing).
if (isset($stats[$table][0][0]))
$stats[$table] += $stats[$table][0][0];
else
$stats[$table] += $stats[$table][0];
unset($stats[$table][0]);
}
//pr(array('Transaction::stats' => array('return' => compact('stats'))));
return $stats;
}
}
?>