First pass implementation at generating an invoice, which seems to be working. Largely untested, but worth checking in. Next is to get receipts using the same algorithm, and after that will be to work on a reconciling mechanism, creating payments, and matching them to charges. This checkin includes an additional customer_id fields as part of the transactions table. I think it was an oversight not to be there, as we need some way to keep track of monies which have been paid by a customer, yet not applied. If there is a different way to do it (short of actually having each ledger entry hold customer_id), it escapes me at the moment.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716/site@372 97e9348a-65ac-dc4b-aefc-98561f571b83
This commit is contained in:
@@ -20,42 +20,23 @@ class Transaction extends AppModel {
|
||||
*/
|
||||
|
||||
function addInvoice($data, $customer_id, $lease_id = null) {
|
||||
// Create some models for convenience
|
||||
$A = new Account();
|
||||
|
||||
//pr(compact('data', 'customer_id', 'lease_id'));
|
||||
|
||||
// Assume this will succeed
|
||||
$ret = true;
|
||||
|
||||
// Establish the key invoice parameters
|
||||
$invoice = array_intersect_key($data, array('Invoice'=>1));
|
||||
// Establish the transaction as an invoice
|
||||
$invoice =& $data['Transaction'];
|
||||
$invoice['type'] = 'INVOICE';
|
||||
$invoice['crdr'] = 'DEBIT';
|
||||
$invoice['account_id'] = $this->Account->accountReceivableAccountID();
|
||||
|
||||
// Determine the total charges on the invoice
|
||||
$invoice['amount'] = 0;
|
||||
foreach ($data['LedgerEntry'] AS $entry)
|
||||
$invoice['amount'] += $entry['amount'];
|
||||
|
||||
// Go through the entered charges
|
||||
foreach ($data['LedgerEntry'] AS $entry) {
|
||||
//pr(compact('entry'));
|
||||
// Create the receipt entry, and reconcile the credit side
|
||||
// of the double-entry (which should be A/R) as a payment.
|
||||
$ids = $this->LedgerEntry->Ledger->Account->postLedgerEntry
|
||||
($invoice,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($A->accountReceivableAccountID()),
|
||||
'credit_ledger_id' => $A->currentLedgerID($entry['account_id'])
|
||||
) + $entry
|
||||
);
|
||||
|
||||
if ($ids['error'])
|
||||
$ret = false;
|
||||
|
||||
$invoice = array_intersect_key($ids, array('invoice_id'=>1));
|
||||
// Go through the statement entries and flag as charges
|
||||
foreach ($data['Entry'] AS &$entry) {
|
||||
$entry['type'] = 'CHARGE';
|
||||
$entry['crdr'] = 'CREDIT';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
$ids = $this->addTransaction($data, $customer_id, $lease_id);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['invoice_id'] = $ids['transaction_id'];
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,43 +48,293 @@ class Transaction extends AppModel {
|
||||
*/
|
||||
|
||||
function addReceipt($data, $customer_id, $lease_id = null) {
|
||||
// Create some models for convenience
|
||||
$A = new Account();
|
||||
|
||||
// Assume this will succeed
|
||||
$ret = true;
|
||||
|
||||
// Establish the key receipt parameters
|
||||
$receipt = array_intersect_key($data, array('stamp'=>1, 'type'=>1, 'name'=>1, 'amount'=>1,
|
||||
'data1'=>1, 'data2'=>1, 'data3'=>1, 'data4'=>1));
|
||||
// Establish the transaction as an receipt
|
||||
$receipt =& $data['Transaction'];
|
||||
$receipt['type'] = 'RECEIPT';
|
||||
$receipt['crdr'] = 'CREDIT';
|
||||
$receipt['account_id'] = $this->Account->accountReceivableAccountID();
|
||||
|
||||
// Determine the total charges on the receipt
|
||||
$receipt['amount'] = 0;
|
||||
foreach ($data['LedgerEntry'] AS $entry)
|
||||
$receipt['amount'] += $entry['amount'];
|
||||
// Go through the statement entries and flag as payments
|
||||
foreach ($data['Entry'] AS &$entry) {
|
||||
$entry['type'] = 'PAYMENT';
|
||||
$entry['crdr'] = 'DEBIT';
|
||||
}
|
||||
|
||||
$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]
|
||||
* - type (CASH, CHECK, etc)
|
||||
* - [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']) ||
|
||||
(isset($entry['Tender']) &&
|
||||
(empty($entry['Tender']['type'])
|
||||
))
|
||||
) {
|
||||
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']['type'])) {
|
||||
pr("Tender verification failed");
|
||||
return array('error' => true);
|
||||
}
|
||||
|
||||
// Keep only relevent items
|
||||
$entry['Tender'] =
|
||||
array_intersect_key($entry['Tender'],
|
||||
array_flip(array('type', '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['LedgerEntry'] AS $entry) {
|
||||
// Create the receipt entry, and reconcile the credit side
|
||||
// of the double-entry (which should be A/R) as a receipt.
|
||||
$ids = $this->LedgerEntry->Ledger->Account->postLedgerEntry
|
||||
($receipt,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($entry['account_id']),
|
||||
'credit_ledger_id' => $A->currentLedgerID($A->receiptAccountID())
|
||||
) + $entry,
|
||||
array('debit' => 'receipt',
|
||||
'credit' => $reconcile)
|
||||
);
|
||||
|
||||
if ($ids['error'])
|
||||
$ret = false;
|
||||
foreach ($data['Entry'] AS $e_index => &$entry) {
|
||||
|
||||
$receipt = array_intersect_key($ids,
|
||||
array('receipt_id'=>1,
|
||||
'split_receipt_id'=>1));
|
||||
$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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user