Moved invoice/receipt code into Account. It appears to be broken in that the receipt ledger_entries are all getting a uniqe transaction. I'll have to look at this tomorrow.

git-svn-id: file:///svn-source/pmgr/branches/invoice_receipt_20090629@306 97e9348a-65ac-dc4b-aefc-98561f571b83
This commit is contained in:
abijah
2009-07-11 07:26:17 +00:00
parent 546766d9be
commit e067af589c
2 changed files with 145 additions and 256 deletions

View File

@@ -117,6 +117,8 @@ class Account extends AppModel {
function taxAccountID() { return $this->nameToID('Tax'); }
function accountReceivableAccountID() { return $this->nameToID('A/R'); }
function cashAccountID() { return $this->nameToID('Cash'); }
function checkAccountID() { return $this->nameToID('Check'); }
function moneyOrderAccountID() { return $this->nameToID('Money Order'); }
function pettyCashAccountID() { return $this->nameToID('Petty Cash'); }
function invoiceAccountID() { return $this->nameToID('Invoice'); }
function receiptAccountID() { return $this->nameToID('Receipt'); }
@@ -493,23 +495,24 @@ class Account extends AppModel {
$monetary_data,
$entry_data,
$reconcile = null) {
/* if (!isset($entry_data) || */
/* !isset($entry_data['amount']) || */
/* !$entry_data['amount']) */
/* return false; */
// Create some models for convenience
$A = new Account();
/* // Create a transaction if necessary */
/* if (!isset($transaction_data['id'])) { */
/* $transaction = new Transaction(); */
/* $transaction->create(); */
/* if (!$transaction->save($transaction_data, false)) { */
/* return false; */
/* } */
/* $transaction_data['id'] = $transaction->id; */
/* } */
pr(compact('transaction_data', 'monetary_data', 'entry_data', 'reconcile'));
// Automatically figure out the customer if we have the lease
if (isset($entry_data['lease_id']) && !isset($entry_data['customer_id'])) {
$L = new Lease();
$L->recursive = -1;
$lease = $L->read(null, $entry_data['lease_id']);
$entry_data['customer_id'] = $lease['Lease']['customer_id'];
}
if (!isset($entry_data['lease_id']))
$entry_data['lease_id'] = null;
if (!isset($entry_data['customer_id']))
$entry_data['customer_id'] = null;
// Get the Transaction squared away
if (isset($transaction_data['transaction_id'])) {
@@ -528,17 +531,25 @@ class Account extends AppModel {
// Get the Monetary Source squared away
if (isset($monetary_data['monetary_source_id'])) {
$monetary_data
= array_intersect_key($monetary_data,
array('monetary_source_id'=>1));
}
elseif (isset($monetary_data['account_name'])) {
if ($monetary_data['account_name'] === 'Cash') {
// No distinguishing features of Cash, just
// use the shared monetary source
$monetary_data['monetary_source_id'] =
$this->Ledger->LedgerEntry->MonetarySource->nameToID('Cash');
if (isset($monetary_data)) {
if (!isset($monetary_data['monetary_source_id'])) {
// Convert Account ID to name or vice versa
if (isset($monetary_data['account_id'])) {
$monetary_data['account_name'] = $A->name($monetary_data['account_id']);
} elseif (isset($monetary_data['account_name'])) {
$monetary_data['account_id'] = $A->nameToID($monetary_data['account_name']);
}
if ($monetary_data['account_id'] == $A->cashAccountID()) {
// No distinguishing features of Cash, just
// use the shared monetary source
$monetary_data['monetary_source_id'] =
$this->Ledger->LedgerEntry->MonetarySource->nameToID('Cash');
}
}
if (isset($monetary_data['monetary_source_id'])) {
$monetary_data
= array_intersect_key($monetary_data,
array('monetary_source_id'=>1));
@@ -548,25 +559,17 @@ class Account extends AppModel {
// Create a new one dedicated to this entry
// Give it a fancy name based on the check number
$monetary_data['MonetarySource']['name'] = $monetary_data['account_name'];
if ($monetary_data['account_name'] === 'Check' ||
$monetary_data['account_name'] === 'Money Order') {
if ($monetary_data['account_name'] === $A->name($A->checkAccountID()) ||
$monetary_data['account_name'] === $A->name($A->moneyOrderAccountID())) {
$monetary_data['MonetarySource']['name'] .=
' #' . $monetary_data['MonetarySource']['data1'];
}
else {
$monetary_data['MonetarySource']['name'] = $monetary_data['account_name'];
}
$monetary_data
= array_intersect_key($monetary_data,
array('MonetarySource'=>1));
}
}
elseif (isset($monetary_data)) {
$monetary_data
= array_intersect_key($monetary_data,
array('MonetarySource'=>1));
}
else {
$monetary_data = array();
}
@@ -621,6 +624,12 @@ class Account extends AppModel {
if (isset($reconcile) && is_bool($reconcile) && $reconcile) {
$reconcile = array('debit' => true, 'credit' => true);
}
elseif (isset($reconcile) && $reconcile == 'invoice') {
$reconcile = array('credit' => 'invoice');
}
elseif (isset($reconcile) && $reconcile == 'receipt') {
$reconcile = array('debit' => 'receipt');
}
elseif (!isset($reconcile) || !is_array($reconcile)) {
$reconcile = array();
}
@@ -628,25 +637,63 @@ class Account extends AppModel {
// Reconcile the new entry... assume we'll have success
$err = false;
foreach (array_intersect_key($reconcile, array('credit'=>1,'debit'=>1))
AS $dr => $reconcile_set) {
AS $dc_type => $reconcile_set) {
if (!isset($reconcile_set) || (is_bool($reconcile_set) && !$reconcile_set))
continue;
if (is_bool($reconcile_set) && $reconcile_set) {
// REVISIT <AP>: 20090710
// Take the reconcile code from the Transaction model,
// which utilizes reconcileNewLedgerEntry, and insert
// it here so that we can migrate the bulk of the
// functions addReceipt and addInvoice
if ($reconcile_set === 'receipt') {
$C = new Customer();
$reconciled = $C->reconcileNewLedgerEntry($entry_data['customer_id'],
$this->fundamentalOpposite($dc_type),
$entry_data['amount']);
pr(array("reconcile receipt",
compact('reconciled', 'split_transaction', 'transaction_data')));
$split_transaction = array_intersect_key($transaction_data,
array('Transaction'=>1));
if (is_array($reconciled) && count($reconciled[$dc_type]['entry'])) {
foreach ($reconciled[$dc_type]['entry'] AS $rec) {
//.pr(compact('rec'));
if (!$rec['applied'])
continue;
// Create an entry to handle the splitting of the funds ("Payment")
// and reconcile against the new cash/check/etc entry created above,
// as well as the A/R account.
// Payment must debit the Receipt ledger, and credit the A/R ledger
// debit: Receipt credit: A/R
$ids = $this->postLedgerEntry
($split_transaction,
null,
array('debit_ledger_id' => $A->currentLedgerID($A->receiptAccountID()),
'credit_ledger_id' => $A->currentLedgerID($A->accountReceivableAccountID()),
'amount' => $rec['applied'],
'lease_id' => $rec['lease_id'],
'customer_id' => $rec['customer_id'],
),
array('debit' => array(array('LedgerEntry' => array('id' => $new_entry->id,
'amount' => $rec['applied']))),
'credit' => array(array('LedgerEntry' => array('id' => $rec['id'],
'amount' => $rec['applied']))))
);
// Keep using the same split transaction for all reconciled entries
$split_transaction = array_intersect_key($ids, array('transaction_id'=>1));
pr(compact('ids', 'split_transaction'));
}
}
}
if (is_array($reconcile_set)) {
foreach ($reconcile_set AS $reconcile_entry) {
if (!isset($reconcile_entry['LedgerEntry']['id']))
continue;
$amount = $reconcile_entry['LedgerEntry']['amount'];
if (!$amount)
continue;
if ($dr == 'debit') {
if ($dc_type == 'debit') {
$debit_ledger_entry_id = $new_entry->id;
$credit_ledger_entry_id = $reconcile_entry['LedgerEntry']['id'];
}
@@ -708,6 +755,7 @@ class Account extends AppModel {
// Reconcile the account for cash/check/etc,
// which is the credit side of this entry.
array('credit' => $ledger['entries']));
//pr(compact('ids'));
if ($ids['error'])
die("closeAndDeposit : postLedgerEntry returned error!");

View File

@@ -23,108 +23,55 @@ class Transaction extends AppModel {
*/
function addInvoice($data, $customer_id, $lease_id = null) {
// Sanitize the data
if (!$data['Transaction']['comment'])
$data['Transaction']['comment'] = null;
// Automatically figure out the customer if we have the lease
if ($lease_id && !$customer_id) {
$L = new Lease();
$L->recursive = -1;
$lease = $L->read(null, $lease_id);
$customer_id = $lease['Lease']['customer_id'];
}
// Invoice must be attributed to _someone_
if (!$customer_id)
return false;
// Create some models for convenience
$A = new Account();
//pr(compact('data', 'customer_id', 'lease_id'));
// Assume this will succeed
$ret = true;
// Determine the total charges on the invoice
$grand_total = 0;
foreach ($data['LedgerEntry'] AS $entry)
$grand_total += $entry['amount'];
// Create a transaction for the invoice
$invoice_transaction = new Transaction();
$invoice_transaction->create();
if (!$invoice_transaction->save($data['Transaction'], false))
return false;
// Create a transaction for the A/R
$ar_transaction = new Transaction();
$ar_transaction->create();
if (!$ar_transaction->save($data['Transaction'], false))
return false;
// Create an account receivable entry
// I would prefer to do this last, as it feels more
// logical to me that the entries would flow from the
// charge to the A/R, but in reality it makes no
// difference to the end result. Entering the A/R
// first allows us to reconcile each charge on the
// invoice as we enter them. Otherwise, we would
// need to loop again at the end to reconcile, and
// would need to save each charge entry id in the
// interim. This keeps the logic simple.
// debit: A/R credit: Invoice
$ar_entry_data = array
('debit_ledger_id' => $A->currentLedgerID($A->accountReceivableAccountID()),
'credit_ledger_id' => $A->currentLedgerID($A->invoiceAccountID()),
'transaction_id' => $ar_transaction->id,
'amount' => $grand_total,
'lease_id' => $lease_id,
'customer_id' => $customer_id,
);
// Create a new A/R entry from the data
$ar_entry = new LedgerEntry();
$ar_entry->create();
if (!$ar_entry->save($ar_entry_data, false))
return false;
$ids = $this->LedgerEntry->Ledger->Account->postLedgerEntry
(array_intersect_key($data, array('Transaction'=>1, 'transaction_id'=>1)),
null,
array('debit_ledger_id' => $A->currentLedgerID($A->accountReceivableAccountID()),
'credit_ledger_id' => $A->currentLedgerID($A->invoiceAccountID()),
'customer_id' => $customer_id,
'lease_id' => $lease_id,
'amount' => $grand_total));
$ar_entry_id = $ids['id'];
// Go through the entered charges
$invoice_transaction = array_intersect_key($data, array('Transaction'=>1, 'transaction_id'=>1));
foreach ($data['LedgerEntry'] AS $entry) {
// Invoice Transaction
// debit: Invoice credit: Charge
$entry['transaction_id'] = $invoice_transaction->id;
//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_transaction,
array_intersect_key($entry, array('MonetarySource'=>1))
+ array_intersect_key($entry, array('account_id'=>1)),
array('debit_ledger_id' => $A->currentLedgerID($A->invoiceAccountID()),
'credit_ledger_id' => $A->currentLedgerID($entry['account_id']),
'customer_id' => $customer_id,
'lease_id' => $lease_id)
+ $entry,
array('debit' => array(array('LedgerEntry' => array('id' => $ar_entry_id,
'amount' => $entry['amount']))))
);
// Debit the "invoice" asset...
$entry['debit_ledger_id']
= $A->currentLedgerID($A->invoiceAccountID());
if ($ids['error'])
$ret = false;
// ...and credit the charge ledger
$entry['credit_ledger_id']
= $A->currentLedgerID($entry['account_id']);
$entry['customer_id'] = $customer_id;
$entry['lease_id'] = $lease_id;
// Create it
$invoice_entry = new LedgerEntry();
$invoice_entry->create();
if (!$invoice_entry->save($entry, false))
return false;
// Reconcile the Invoice account. Our two entries are:
// debit: Invoice credit: Charge
// debit: A/R credit: Invoice
// Since this is from the perspective of the Invoice account,
// the credit entry is the Invoice<->A/R, and the debit
// entry is the actual invoice ledger entry.
$R = new Reconciliation();
$R->create();
if (!$R->save(array('debit_ledger_entry_id' => $invoice_entry->id,
'credit_ledger_entry_id' => $ar_entry->id,
'amount' => $entry['amount']), false))
return false;
$invoice_transaction = array_intersect_key($ids, array('transaction_id'=>1));
}
// Return indication of success
return true;
return $ret;
}
@@ -136,141 +83,35 @@ class Transaction extends AppModel {
*/
function addReceipt($data, $customer_id, $lease_id = null) {
// Sanitize the data
if (!$data['Transaction']['comment'])
$data['Transaction']['comment'] = null;
// Create some models for convenience
$A = new Account();
$C = new Customer();
// Receipt must be paid by _someone_
// REVISIT <AP> 20090706:
// Will we really disallow anonymous payments?
if (!$customer_id)
return false;
// Create a transaction for the receipt
$receipt_transaction = new Transaction();
$receipt_transaction->create();
if (!$receipt_transaction->save($data['Transaction'], false))
return false;
// Create a transaction for the splits
$split_transaction = new Transaction();
$split_transaction->create();
if (!$split_transaction->save($data['Transaction'], false))
return false;
// Assume this will succeed
$ret = true;
// Go through the entered payments
$receipt_transaction = array_intersect_key($data, array('Transaction'=>1, 'transaction_id'=>1));
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 payment.
$ids = $this->LedgerEntry->Ledger->Account->postLedgerEntry
($receipt_transaction,
array_intersect_key($entry, array('MonetarySource'=>1))
+ array_intersect_key($entry, array('account_id'=>1)),
array('debit_ledger_id' => $A->currentLedgerID($entry['account_id']),
'credit_ledger_id' => $A->currentLedgerID($A->receiptAccountID()),
'customer_id' => $customer_id,
'lease_id' => $lease_id)
+ $entry,
'receipt');
// Get the Monetary Source squared away
if ($entry['account_id'] == $A->cashAccountID()) {
// No distinguishing features of Cash, just
// use the shared monetary source
$entry['monetary_source_id'] =
$this->LedgerEntry->MonetarySource->nameToID('Cash');
unset($entry['MonetarySource']);
}
elseif (isset($entry['MonetarySource'])) {
$entry['MonetarySource']['name'] = $A->name($entry['account_id']);
if ($ids['error'])
$ret = false;
// Give it a fancy name based on the check number
if ($A->name($entry['account_id']) === 'Check' ||
$A->name($entry['account_id']) === 'Money Order') {
$entry['MonetarySource']['name'] .=
' #' . $entry['MonetarySource']['data1'];
}
}
// This entry of physical money is part of the receipt transaction
// debit: Cash/Check/Etc credit: Receipt
$entry['transaction_id'] = $receipt_transaction->id;
// Receipt must debit the "money" asset (bank, cash, check, etc)...
$entry['debit_ledger_id']
= $A->currentLedgerID($entry['account_id']);
// ...and credit the Receipt ledger
$entry['credit_ledger_id']
= $A->currentLedgerID($A->receiptAccountID());
$entry['customer_id'] = $customer_id;
$entry['lease_id'] = $lease_id;
// Create it
$receipt_entry = new LedgerEntry();
$receipt_entry->create();
if (!$receipt_entry->saveAll($entry,
array('validate' => false,
)))
return false;
$reconciled = $C->reconcileNewLedgerEntry($customer_id,
'credit',
$entry['amount']);
foreach (array_merge($reconciled['debit']['entry'], array
(array('id' => null,
'applied' => $reconciled['debit']['unapplied'],
'customer_id' => $customer_id,
'lease_id' => null))) AS $rec) {
if (!$rec['applied'])
continue;
// Create an entry to handle the splitting of the funds ("Payment")
// Payment must debit the Receipt ledger, and credit the A/R ledger
// debit: Receipt credit: A/R
$split_entry_data = array
('debit_ledger_id' => $A->currentLedgerID($A->receiptAccountID()),
'credit_ledger_id' => $A->currentLedgerID($A->accountReceivableAccountID()),
'transaction_id' => $split_transaction->id,
'amount' => $rec['applied'],
'lease_id' => $rec['lease_id'],
'customer_id' => $rec['customer_id'],
);
// Create a new split entry from the data
$split_entry = new LedgerEntry();
$split_entry->create();
if (!$split_entry->save($split_entry_data, false))
return false;
// Reconcile the Receipt account. Our two entries are:
// debit: Cash/Check/Etc credit: Receipt
// debit: Receipt credit: A/R
// Since this is from the perspective of the Receipt account,
// the debit entry is the Receipt<->A/R, and the credit
// entry is the actual receipt ledger entry.
$R = new Reconciliation();
$R->create();
if (!$R->save(array('debit_ledger_entry_id' => $split_entry->id,
'credit_ledger_entry_id' => $receipt_entry->id,
'amount' => $rec['applied']), false))
return false;
// Only reconcile the A/R account if we have an entry
// to reconcile with, otherwise, just go on.
if (!$rec['id'])
continue;
// Reconcile the A/R account. Our two entries look like:
// debit: Receipt credit: A/R
// debit: A/R credit: Invoice
// Since this is from the perspective of the A/R account,
// the debit entry is the Invoice<->A/R, and the credit
// entry is the Receipt<->A/R.
$R = new Reconciliation();
$R->create();
if (!$R->save(array('debit_ledger_entry_id' => $rec['id'],
'credit_ledger_entry_id' => $split_entry->id,
'amount' => $rec['applied']), false))
return false;
}
$receipt_transaction = array_intersect_key($ids, array('transaction_id'=>1));
}
return true;
return $ret;
}
}