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:
@@ -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!");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user