Finally, finally... a working version for payment entry. The current schema is working well, and seems to handle our technical needs. However, it does seem to be very confusing with the extra accounts. Nonetheless, it does work and so I'll keep going down this path. This checkin also includes a mechanism to close the books on an account (by closing the underlying ledger) and start a new ledger. One of the decisions worth revisiting is separating out ledger entries that are really part of the same transaction. Without this change, inspecting a transaction results in the transaction total being off by a factor of two, since all money movement is in their twice (once for the expected reason, and again to hit the invoice/receipt ledger).

git-svn-id: file:///svn-source/pmgr/branches/invoice_receipt_20090629/site@195 97e9348a-65ac-dc4b-aefc-98561f571b83
This commit is contained in:
abijah
2009-07-01 08:17:31 +00:00
parent 3550bf775c
commit dfe20e7ef9
16 changed files with 412 additions and 202 deletions

View File

@@ -103,186 +103,204 @@ class TransactionsController extends AppController {
*/
function postReceipt() {
$this->autoRender = false;
if (!$this->RequestHandler->isPost()) {
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
return;
}
//pr(array('thisdata' => $this->data));
if (isset($this->data['customer_id'])) {
$C = new Customer();
$C->recursive = -1;
$customer = $C->find
('first',
array('contain' => array('Account.CurrentLedger.id'),
'fields' => false,
'conditions' => array('Customer.id', $this->data['customer_id']),
));
$ledger_id = $customer['Account']['CurrentLedger']['id'];
}
else {
// Payment by Unit, Lease, etc
$ledger_id = 0;
$this->layout = null;
$this->autoLayout = false;
$this->autoRender = false;
Configure::write('debug', '0');
// Sanitize the transaction data
if (!$this->data['Transaction']['comment'])
$this->data['Transaction']['comment'] = null;
if(empty($this->data['Transaction']['stamp'])) {
die("Time/Date not valid");
}
pr($this->data['Transaction']);
$amount = 0;
foreach ($this->data['LedgerEntry'] AS &$entry) {
$reconciled = $C->reconcileNewLedgerEntry($this->data['customer_id'],
'credit',
$entry['amount']);
pr(compact('entry', 'reconciled'));
foreach ($reconciled['debit']['entry'] AS $rec) {
$entry['DebitReconciliationLedgerEntry'] =
array('amount' => $rec['applied'],
//'debit_ledger_entry_id'
'credit_ledger_entry_id' => $rec['id']
);
}
$amount += isset($entry['amount']) ? $entry['amount'] : 0;
$entry['debit_ledger_id'] = 6; // Cash/Payments
$entry['credit_ledger_id'] = $ledger_id;
}
pr($this->data);
$T = new Transaction();
$T->create();
if ($T->saveAll($this->data,
array(
'validate' => false,
//'fieldList' => array(),
//'callbacks' => true,
))) {
$tid = $T->id;
$this->Session->setFlash(__("New Transaction Created ($tid)!", true));
//$this->redirect(array('action'=>'view', $mid));
}
else {
$this->autoRender = false;
pr(array('checkpoint' => "saveAll failed"));
}
pr($T->data);
// Create some models for convenience
$A = new Account();
$C = new Customer();
$LE = new LedgerEntry();
/* $reconciled = $C->reconcileNewLedgerEntry($this->data['customer_id'], */
/* 'credit', */
/* $amount); */
/* pr(compact('amount', 'unreconciled', 'reconciled')); */
/* return; */
// Create a transaction for the receipt
$receipt_transaction = new Transaction();
$receipt_transaction->create();
if (!$receipt_transaction->save($this->data['Transaction'],
array('validate' => false,
))) {
pr(array('checkpoint' => "receipt transaction save failed"));
die("Unknown Database Failure");
}
pr("New Transaction Created ({$receipt_transaction->id})!");
$receipt_transaction->read();
pr($receipt_transaction->data);
// Create a transaction for the splits
$split_transaction = new Transaction();
$split_transaction->create();
if (!$split_transaction->save($this->data['Transaction'],
array('validate' => false,
))) {
pr(array('checkpoint' => "split transaction save failed"));
die("Unknown Database Failure");
}
pr("New Transaction Created ({$split_transaction->id})!");
$split_transaction->read();
pr($split_transaction->data);
// Go through the entered payments
foreach ($this->data['LedgerEntry'] AS &$entry) {
// Get the Monetary Source squared away
if ($entry['monetary_type_name'] === 'Cash') {
// No distinguishing features of Cash, just
// use the shared monetary source
$entry['monetary_source_id'] =
$this->Transaction->LedgerEntry->MonetarySource->nameToID('Cash');
unset($entry['MonetarySource']);
}
else {
// The monetary source needs to be unique
// Create a new one dedicated to this entry
$entry['MonetarySource']['monetary_type_id'] =
$this->Transaction->LedgerEntry->MonetarySource->MonetaryType
->nameToID($entry['monetary_type_name']);
$entry['MonetarySource']['name'] =
$this->Transaction->LedgerEntry->MonetarySource->MonetaryType
->nameToID($entry['monetary_type_name']);
// Give it a fancy name based on the check number
$entry['MonetarySource']['name'] = $entry['monetary_type_name'];
if ($entry['monetary_type_name'] === 'Check' ||
$entry['monetary_type_name'] === '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($A->nameToID($entry['monetary_type_name']));
// ...and credit the Receipt ledger
$entry['credit_ledger_id']
= $A->currentLedgerID($A->receiptAccountID());
$entry['customer_id']
= $this->data['customer_id'];
// Create it
$receipt_entry = new LedgerEntry();
$receipt_entry->create();
if (!$receipt_entry->saveAll($entry,
array('validate' => false,
))) {
pr(array('checkpoint' => "receipt entry saveAll failed"));
die("Unknown Database Failure");
}
pr("New Receipt LedgerEntry Created ({$receipt_entry->id})!");
$receipt_entry->read();
pr($receipt_entry->data);
$reconciled = $C->reconcileNewLedgerEntry($this->data['customer_id'],
'credit',
$entry['amount']);
pr(compact('entry', 'reconciled'));
continue;
foreach ($reconciled['debit']['entry'] AS $rec) {
$data = array('LedgerEntry' =>
array('DebitReconciliationLedgerEntry' =>
array('amount' => $rec['applied'],
//'debit_ledger_entry_id'
'credit_ledger_entry_id' => $rec['id']
),
),
);
//'DebitReconciliationLedgerEntry' => array(
//pr(compact('amount', 'unreconciled', 'reconciled'));
foreach (array_merge($reconciled['debit']['entry'], array
(array('id' => null,
'applied' => $reconciled['debit']['unapplied'],
'customer_id' => $this->data['customer_id'],
'lease_id' => null))) AS $rec) {
pr(array('checkpoint' => "Handle Reconciled Entry",
compact('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,
array('validate' => false,
))) {
pr(array('checkpoint' => "split entry save failed"));
die("Unknown Database Failure");
}
pr("New Split LedgerEntry Created ({$split_entry->id})!");
$split_entry->read();
pr($split_entry->data);
// Reconcile the Receipt account. Our two entries look like:
// 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']),
array('validate' => false,
))) {
pr(array('checkpoint' => "receipt reconcile save failed"));
die("Unknown Database Failure");
}
pr("New Receipt Reconciliation Created ({$R->id})!");
$R->read();
pr($R->data);
// 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']),
array('validate' => false,
))) {
pr(array('checkpoint' => "split reconcile save failed"));
die("Unknown Database Failure");
}
pr("New Split Reconciliation Created ({$R->id})!");
$R->read();
pr($R->data);
}
}
}
function saveTest() {
$data =
array(
/* 'Customer' => array */
/* ('id' => 7, */
/* ), */
'LedgerEntry' => array
(
'0' => array(
'amount' => 100,
'debit_ledger_id' => 1,
'credit_ledger_id' => 2,
'MonetarySource' => array('name' => 'testmoney', 'monetary_type_id' => 2),
),
'1' => array(
'amount' => 101,
'debit_ledger_id' => 1,
'credit_ledger_id' => 2,
'MonetarySource' => array('name' => 'testmoney2', 'monetary_type_id' => 2),
),
),
'Transaction' => array
(
'stamp' => '06/18/2009',
'comment' => 'no comment',
),
);
$data =
/* array( */
/* 'LedgerEntry' => array */
/* ( */
/* '0' => */
array(
'amount' => 100,
'debit_ledger_id' => 1,
'credit_ledger_id' => 2,
'transaction_id' => 66,
'DebitReconciliationLedgerEntry' =>
array('amount' => 44,
//'debit_ledger_entry_id'
'credit_ledger_entry_id' => 17,
),
/* ), */
/* ), */
);
//$M = new Transaction();
$M = new LedgerEntry();
$M->create();
$retval = $M->saveAll($data,
array(
'validate' => false,
'fieldList' => array(),
'callbacks' => true,
));
$mid = $M->id;
pr(compact('retval', 'mid'));
if ($mid) {
$this->Session->setFlash(__("New Transaction Created ($mid)!", true));
//$this->redirect(array('action'=>'view', $mid));
}
else {
$this->autoRender = false;
pr(array('checkpoint' => "saveAll failed"));
}
/* $LE = new LedgerEntry(); */
/* $LE->create(); */
/* $ret = $LE->save($data, */
/* array( */
/* 'validate' => false, */
/* 'fieldList' => array(), */
/* 'callbacks' => true, */
/* )); */
/* $leid = $LE->id; */
/* pr(array('checkpoint' => "New Ledger Entry", */
/* compact('leid', 'ret'))); */
//pr($LE);
}
}