Further tweaked the code to add new transactions, primarily by creating separate verification and addition functions in each underlying model. There are logic changes as well, such as adding in the other half of the missing double entry (as well as the double entry itself). This checkin also introduces the creation of CREDIT statement entries, when the customer has overpayed. It's working fairly well, although undoubtedly will need more tweaking.

git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716/site@394 97e9348a-65ac-dc4b-aefc-98561f571b83
This commit is contained in:
abijah
2009-07-28 01:16:42 +00:00
parent 4adf0dd2fc
commit 24ccb56e50
6 changed files with 447 additions and 374 deletions

View File

@@ -93,6 +93,50 @@ class Transaction extends AppModel {
}
/**************************************************************************
**************************************************************************
**************************************************************************
* 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 (!$this->StatementEntry->verifyStatementEntry($se)) {
pr(array("Transaction::verifyTransaction()"
=> "Statement Entry verification failed"));
return false;
}
}
return true;
}
/**************************************************************************
**************************************************************************
**************************************************************************
@@ -152,7 +196,11 @@ class Transaction extends AppModel {
*/
function addTransaction($data, $customer_id, $lease_id = null) {
pr(compact('data', 'customer_id', 'lease_id'));
pr(array("Transaction::addTransaction()"
=> compact('data', 'customer_id', 'lease_id')));
if (!isset($data['Transaction']) || !isset($data['Entry']))
return array('error' => true);
// Automatically figure out the customer if we have the lease
if (!empty($lease_id) && empty($customer_id)) {
@@ -162,86 +210,74 @@ class Transaction extends AppModel {
$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 = $data['Transaction'];
$transaction['customer_id'] = $customer_id;
$transaction['lease_id'] = $lease_id;
$transaction['ledger_id'] =
$this->Account->currentLedgerID($transaction['account_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 (!empty($entry['comment']) && $entry['type'] === 'PAYMENT')
$entry['ledger_entry_comment'] = $entry['comment'];
else
$entry['ledger_entry_comment'] = null;
}
if (empty($entry['statement_entry_comment'])) {
if (!empty($entry['comment']) && $entry['type'] === 'CHARGE')
$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')));
// Create a statement entry
$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'], $customer_id, $lease_id))
return array('error' => true) + $ret;
pr(array('Transaction::addTransaction' =>
array('checkpoint' => 'Pre Transaction Save')
+ compact('transaction')));
@@ -249,104 +285,39 @@ class Transaction extends AppModel {
// Save transaction to the database
$this->create();
if (!$this->save($transaction))
return array('error' => true);
return array('error' => true) + $ret;
// Set up our return ids array
$ret = array('transaction_id' => $this->id,
'Entry' => array(),
'error' => false);
$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'] = $ret['transaction_id'];
$le2['transaction_id'] = $ret['transaction_id'];
$se['transaction_id'] = $ret['transaction_id'];
$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;
$result = $this->LedgerEntry->DoubleEntry->addDoubleEntry($le1, $le2, $le1_tender);
$ret['entries'][$e_index]['DoubleEntry'] = $result;
if ($result['error']) {
$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') {
if ($se['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;
$result = $this->StatementEntry->addStatementEntry($se);
$ret['entries'][$e_index]['StatementEntry'] = $result;
if ($result['error']) {
$ret['error'] = true;
continue;
}
}
}
// REVISIT <AP>: 20090723
// Now that we have new entries, WE MUST RECONCILE
// THE CHARGES TO CUSTOMER ACCOUNT BALANCE! This
@@ -354,6 +325,19 @@ class Transaction extends AppModel {
// the only way to make use of a positive customer
// balance if new charges have been entered.
if (!$ret['error']) {
$result = $this->StatementEntry->assignCredits
(array('link' => array('Customer'),
'conditions' => array('Customer.id' => $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)));