git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716/site@389 97e9348a-65ac-dc4b-aefc-98561f571b83
422 lines
13 KiB
PHP
422 lines
13 KiB
PHP
<?php
|
|
class Transaction extends AppModel {
|
|
|
|
var $belongsTo = array(
|
|
'Account',
|
|
'Ledger',
|
|
);
|
|
|
|
var $hasMany = array(
|
|
'LedgerEntry',
|
|
'StatementEntry',
|
|
|
|
'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();
|
|
|
|
// Go through the statement entries and flag as charges
|
|
foreach ($data['Entry'] AS &$entry) {
|
|
$entry['type'] = 'CHARGE';
|
|
$entry['crdr'] = 'CREDIT';
|
|
}
|
|
|
|
$ids = $this->addTransaction($data, $customer_id, $lease_id);
|
|
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 an receipt
|
|
$receipt =& $data['Transaction'];
|
|
$receipt['type'] = 'RECEIPT';
|
|
$receipt['crdr'] = 'CREDIT';
|
|
$receipt['account_id'] = $this->Account->accountReceivableAccountID();
|
|
|
|
// Go through the statement entries and flag as payments
|
|
foreach ($data['Entry'] AS &$entry) {
|
|
$entry['type'] = 'PAYMENT';
|
|
$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, $customer_id, $lease_id);
|
|
if (isset($ids['transaction_id']))
|
|
$ids['receipt_id'] = $ids['transaction_id'];
|
|
|
|
return $ids;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* 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, $customer_id, $lease_id = null) {
|
|
pr(compact('data', 'customer_id', 'lease_id'));
|
|
|
|
// Automatically figure out the customer if we have the lease
|
|
if (!empty($lease_id) && empty($customer_id)) {
|
|
$L = new Lease();
|
|
$L->recursive = -1;
|
|
$lease = $L->read(null, $lease_id);
|
|
$customer_id = $lease['Lease']['customer_id'];
|
|
}
|
|
|
|
if (!isset($data['Transaction']) || !isset($data['Entry']))
|
|
return array('error' => true);
|
|
|
|
// Keep only relevent items from Transaction
|
|
$transaction = array_intersect_key($data['Transaction'],
|
|
array_flip(array('type', 'account_id', 'crdr',
|
|
'stamp', 'comment',
|
|
'LedgerEntry')));
|
|
|
|
// Verify required Transaction data is present
|
|
if (empty($transaction['type']) ||
|
|
empty($transaction['account_id']) ||
|
|
empty($transaction['crdr']) ||
|
|
(in_array($transaction['type'], array('INVOICE', 'RECEIPT'))
|
|
&& !$customer_id) ||
|
|
(in_array($transaction['type'], array('INVOICE'))
|
|
&& !$lease_id)
|
|
) {
|
|
pr("Transaction verification failed");
|
|
return array('error' => true);
|
|
}
|
|
|
|
// Verify each Entry, and calculation the transaction total
|
|
$transaction['amount'] = 0;
|
|
foreach ($data['Entry'] AS &$entry) {
|
|
if (empty($entry['type']) ||
|
|
empty($entry['account_id']) ||
|
|
empty($entry['crdr']) ||
|
|
empty($entry['amount'])
|
|
) {
|
|
pr("Entry verification failed");
|
|
return array('error' => true);
|
|
}
|
|
|
|
// Keep only relevent items from Entry
|
|
$entry =
|
|
array_intersect_key($entry,
|
|
array_flip(array('type', 'account_id', 'crdr', 'amount',
|
|
'effective_date', 'through_date', 'due_date',
|
|
'comment',
|
|
'ledger_entry_comment',
|
|
'statement_entry_comment',
|
|
'Tender')));
|
|
|
|
if (isset($entry['Tender'])) {
|
|
|
|
// Verify required Tender data is present
|
|
if (empty($entry['Tender']['tender_type_id'])) {
|
|
pr("Tender verification failed");
|
|
return array('error' => true);
|
|
}
|
|
|
|
// Keep only relevent items
|
|
$entry['Tender'] =
|
|
array_intersect_key($entry['Tender'],
|
|
array_flip(array('tender_type_id', 'name',
|
|
'data1', 'data2', 'data3', 'data4',
|
|
'comment')));
|
|
}
|
|
|
|
// Try to figure out where 'comment' should go
|
|
if (empty($entry['ledger_entry_comment']) &&
|
|
!empty($entry['comment']) &&
|
|
$entry['type'] === 'PAYMENT')
|
|
$entry['ledger_entry_comment'] = $entry['comment'];
|
|
if (empty($entry['statement_entry_comment']) &&
|
|
!empty($entry['comment']) &&
|
|
$entry['type'] === 'CHARGE')
|
|
$entry['statement_entry_comment'] = $entry['comment'];
|
|
|
|
// OK, add entry amount into the transaction total
|
|
$transaction['amount'] += $entry['amount'];
|
|
}
|
|
|
|
// Set the customer based on caller request, and set
|
|
// ledger ID as the current ledger of the specified account
|
|
$transaction['customer_id'] = $customer_id;
|
|
$transaction['ledger_id'] =
|
|
$this->Account->currentLedgerID($transaction['account_id']);
|
|
|
|
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);
|
|
|
|
// Set up our return ids array
|
|
$ret = array('transaction_id' => $this->id,
|
|
'Entry' => array(),
|
|
'error' => false);
|
|
|
|
// Go through the entered charges
|
|
foreach ($data['Entry'] AS $e_index => &$entry) {
|
|
|
|
$entry['transaction_id'] = $this->id;
|
|
$entry['customer_id'] = $customer_id;
|
|
$entry['lease_id'] = $lease_id;
|
|
$entry['ledger_id'] =
|
|
$this->Account->currentLedgerID($entry['account_id']);
|
|
|
|
// Use ledger_entry_comment as the comment
|
|
if (!empty($entry['ledger_entry_comment']))
|
|
$entry['comment'] = $entry['ledger_entry_comment'];
|
|
else
|
|
$entry['comment'] = null;
|
|
|
|
pr(array('Transaction::addTransaction' =>
|
|
array('checkpoint' => 'Pre Ledger Entry Save')
|
|
+ array('entry' => array_diff_key($entry, array('Tender'=>1)))));
|
|
|
|
$LE = new LedgerEntry();
|
|
$LE->create();
|
|
if ($LE->save($entry))
|
|
$ret['Entry'][$e_index]['ledger_entry_id'] = $LE->id;
|
|
else {
|
|
$ret['Entry'][$e_index]['ledger_entry_id'] = null;
|
|
$ret['error'] = true;
|
|
continue;
|
|
}
|
|
|
|
if (isset($entry['Tender'])) {
|
|
// Come up with a non-unique name for the legal tender.
|
|
// For checks & money orders, this will be based on the
|
|
// check number. For cash, we'll just use the generic
|
|
// name of the account.
|
|
// REVISIT <AP>: 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($entry['Tender']['name'])) {
|
|
$entry['Tender']['name'] = $this->Account->name($entry['account_id']);
|
|
if ($entry['account_id'] == $this->Account->checkAccountID() ||
|
|
$entry['account_id'] == $this->Account->moneyOrderAccountID()) {
|
|
$entry['Tender']['name'] .= ' #' . $entry['Tender']['data1'];
|
|
}
|
|
}
|
|
|
|
$entry['Tender']['ledger_entry_id'] = $LE->id;
|
|
|
|
pr(array('Transaction::addTransaction' =>
|
|
array('checkpoint' => 'Pre Tender Save')
|
|
+ array('Tender' => $entry['Tender'])));
|
|
|
|
$Tender = new Tender();
|
|
$Tender->create();
|
|
if ($Tender->save($entry['Tender']))
|
|
$ret['Entry'][$e_index]['tender_id'] = $Tender->id;
|
|
else {
|
|
$ret['Entry'][$e_index]['tender_id'] = null;
|
|
$ret['error'] = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ($entry['type'] === 'CHARGE') {
|
|
// CHARGES need to be added as statement entries.
|
|
// PAYMENTS will be added later after reconciliation
|
|
|
|
$SE = new StatementEntry();
|
|
$SE->create();
|
|
|
|
// Use statement_entry_comment as the comment
|
|
if (!empty($entry['statement_entry_comment']))
|
|
$entry['comment'] = $entry['statement_entry_comment'];
|
|
else
|
|
$entry['comment'] = null;
|
|
|
|
pr(array('Transaction::addTransaction' =>
|
|
array('checkpoint' => 'Pre Statement Entry Save')
|
|
+ array('entry' => array_diff_key($entry, array('Tender'=>1)))));
|
|
|
|
if ($SE->save($entry))
|
|
$ret['Entry'][$e_index]['statement_entry_id'] = $SE->id;
|
|
else {
|
|
$ret['Entry'][$e_index]['statement_entry_id'] = null;
|
|
$ret['error'] = true;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// REVISIT <AP>: 20090723
|
|
// Now that we have new entries, WE MUST RECONCILE
|
|
// THE CHARGES TO CUSTOMER ACCOUNT BALANCE! This
|
|
// is the only way "payments" are generated, and
|
|
// the only way to make use of a positive customer
|
|
// balance if new charges have been entered.
|
|
|
|
pr(array('Transaction::addTransaction' =>
|
|
array('return' => $ret)));
|
|
|
|
return $ret;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: stats
|
|
* - Returns summary data from the requested transaction
|
|
*/
|
|
function stats($id = null, $query = 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') {
|
|
$squery['fields'] = array_merge($squery['fields'],
|
|
$this->LedgerEntry->debitCreditFields(true, false));
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
?>
|