diff --git a/controllers/transactions_controller.php b/controllers/transactions_controller.php index 55cfefa..cc4d32f 100644 --- a/controllers/transactions_controller.php +++ b/controllers/transactions_controller.php @@ -109,122 +109,13 @@ class TransactionsController extends AppController { return; } -/* $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"); - } - - // Create some models for convenience - $A = new Account(); - $C = new Customer(); - $L = new Lease(); - - $L->recursive = -1; - $lease = $L->read(null, $this->data['Lease']['id']); - - // Create a transaction for the invoice - $invoice_transaction = new Transaction(); - $invoice_transaction->create(); - if (!$invoice_transaction->save($this->data['Transaction'], - array('validate' => false, - ))) { - die("Unknown Database Failure"); - } - $invoice_transaction->read(); - - // Create a transaction for the A/R - $ar_transaction = new Transaction(); - $ar_transaction->create(); - if (!$ar_transaction->save($this->data['Transaction'], - array('validate' => false, - ))) { - die("Unknown Database Failure"); - } - $ar_transaction->read(); - - // Go through the entered charges - $grand_total = 0; - foreach ($this->data['LedgerEntry'] AS &$entry) { - // Invoice Transaction - // debit: Invoice credit: Charge - $entry['transaction_id'] = $invoice_transaction->id; - - // Invoice must debit the "invoice" asset... - $entry['debit_ledger_id'] - = $A->currentLedgerID($A->invoiceAccountID()); - - // ...and credit the charge ledger - // REVISIT 20090706 - // The charge page should have a list of all income types - // and thus the field _should_ contain a valid account - // name. At the moment, however, I'm still cobbling things - // together, and only have two types. -/* $entry['credit_ledger_id'] */ -/* = $A->currentLedgerID($A->nameToID($entry['charge_account'])); */ - if ($entry['charge_type'] === 'rent') { - $entry['credit_ledger_id'] - = $A->currentLedgerID($A->rentAccountID()); - } elseif ($entry['charge_type'] === 'late') { - $entry['credit_ledger_id'] - = $A->currentLedgerID($A->lateChargeAccountID()); - } else { - die("Invalid charge account"); - } - - $entry['customer_id'] - = $lease['Lease']['customer_id']; - $entry['lease_id'] - = $lease['Lease']['id']; - - // Create it - $invoice_entry = new LedgerEntry(); - $invoice_entry->create(); - if (!$invoice_entry->save($entry, false)) { - die("Unknown Database Failure"); - } - $invoice_entry->read(); - - $grand_total += $entry['amount']; - } - - // Create an account receivable entry - // 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['Lease']['id'], - 'customer_id' => $lease['Lease']['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)) { - die("Unknown Database Failure"); - } - - // Reconcile the Invoice account. Our two entries look like: - // 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' => $grand_total), false)) { - die("Unknown Database Failure"); + if (!$this->Transaction->addInvoice($this->data, null, + $this->data['Lease']['id'])) { + $this->Session->setFlash("INVOICE FAILED", true); + // REVISIT 20090706: + // Until we can work out the session problems, + // just die. + die("

INVOICE FAILED

"); } // Comment this out to debug @@ -248,199 +139,19 @@ class TransactionsController extends AppController { return; } + if (!$this->Transaction->addReceipt($this->data, + $this->data['customer_id'])) { + $this->Session->setFlash("RECEIPT FAILED", true); + // REVISIT 20090706: + // Until we can work out the session problems, + // just die. + die("

RECEIPT FAILED

"); + } + $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']); - - // Create some models for convenience - $A = new Account(); - $C = new Customer(); - - // 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')); - - 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); - } - } - } + $this->autoRender = false; + } } diff --git a/models/transaction.php b/models/transaction.php index e471b62..7960cf8 100644 --- a/models/transaction.php +++ b/models/transaction.php @@ -14,5 +14,277 @@ class Transaction extends AppModel { 'LedgerEntry', ); + + /************************************************************************** + ************************************************************************** + ************************************************************************** + * function: addInvoice + * - Adds a new invoice transaction + */ + + 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(); + $C = new Customer(); + + // 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; + + // Go through the entered charges + $grand_total = 0; + foreach ($data['LedgerEntry'] AS $entry) { + // Invoice Transaction + // debit: Invoice credit: Charge + $entry['transaction_id'] = $invoice_transaction->id; + + // Debit the "invoice" asset... + $entry['debit_ledger_id'] + = $A->currentLedgerID($A->invoiceAccountID()); + + // ...and credit the charge ledger + // REVISIT 20090706 + // The charge page should have a list of all income types + // and thus the field _should_ contain a valid account + // name. At the moment, however, I'm still cobbling things + // together, and only have two types. +/* $entry['credit_ledger_id'] */ +/* = $A->currentLedgerID($A->nameToID($entry['charge_account'])); */ + if ($entry['charge_type'] === 'rent') { + $entry['credit_ledger_id'] + = $A->currentLedgerID($A->rentAccountID()); + } elseif ($entry['charge_type'] === 'late') { + $entry['credit_ledger_id'] + = $A->currentLedgerID($A->lateChargeAccountID()); + } else { + die("Invalid charge account"); + } + + $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; + + $grand_total += $entry['amount']; + } + + // Create an account receivable entry + // 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; + + // 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' => $grand_total), false)) + return false; + + // Return indication of success + return true; + } + + + /************************************************************************** + ************************************************************************** + ************************************************************************** + * function: addReceipt + * - Adds a new receipt transaction + */ + + function addReceipt($data, $customer_id) { + // 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 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; + + // Go through the entered payments + foreach ($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->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->LedgerEntry->MonetarySource->MonetaryType + ->nameToID($entry['monetary_type_name']); + + $entry['MonetarySource']['name'] = + $this->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'] = $customer_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; + } + } + + return true; + } + } ?> \ No newline at end of file