diff --git a/db/schema.sql b/db/schema.sql index e09ee29..91f2555 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -968,6 +968,7 @@ CREATE TABLE `pmgr_transactions` ( 'CREDIT_NOTE', -- Inverse of Sales Invoice 'PAYMENT', -- Actual payment 'DEPOSIT', + 'WITHDRAWAL', 'CLOSE', -- Essentially an internal (not accounting) transaction -- 'CREDIT', -- 'REFUND', diff --git a/site/app_controller.php b/site/app_controller.php index e2e7dba..2f3cac2 100644 --- a/site/app_controller.php +++ b/site/app_controller.php @@ -52,7 +52,7 @@ class AppController extends Controller { array('onclick' => '$(".pr-section").show(); return false;')), array('name' => 'Contacts', 'url' => array('controller' => 'contacts', 'action' => 'index')), array('name' => 'Ledgers', 'url' => array('controller' => 'ledgers', 'action' => 'index')), - //array('name' => 'New Ledgers', 'url' => array('controller' => 'accounts', 'action' => 'newledger')), + array('name' => 'New Ledgers', 'url' => array('controller' => 'accounts', 'action' => 'newledger')), //array('name' => 'RESET DATA', 'url' => array('controller' => 'accounts', 'action' => 'reset_data')), ); } diff --git a/site/controllers/leases_controller.php b/site/controllers/leases_controller.php index 1db1927..54c2fee 100644 --- a/site/controllers/leases_controller.php +++ b/site/controllers/leases_controller.php @@ -171,9 +171,7 @@ class LeasesController extends AppController { $this->Lease->moveOut($this->data['Lease']['id'], 'VACANT', - $this->data['Lease']['moveout_date'], - //true // Close this lease, if able - false + $this->data['Lease']['moveout_date'] ); $this->redirect($this->data['redirect']); @@ -418,9 +416,11 @@ class LeasesController extends AppController { function assess_rent($date = null) { $this->Lease->assessMonthlyRentAll($date); + $this->redirect(array('action'=>'index')); } function assess_late($date = null) { $this->Lease->assessMonthlyLateAll($date); + $this->redirect(array('action'=>'index')); } diff --git a/site/controllers/transactions_controller.php b/site/controllers/transactions_controller.php index 30a2b95..3677324 100644 --- a/site/controllers/transactions_controller.php +++ b/site/controllers/transactions_controller.php @@ -349,8 +349,6 @@ class TransactionsController extends AppController { die("

REFUND FAILED

"); } - $this->render('/fake'); - // Return to viewing the lease/customer if (empty($data['Lease']['id'])) $this->redirect(array('controller' => 'customers', @@ -388,7 +386,9 @@ class TransactionsController extends AppController { 'conditions' => array(array('Transaction.id' => $id), // REVISIT : 20090811 // No security issues have been worked out yet - array('Account.level >=' => 5), + array('OR' => + array(array('Account.level >=' => 5), + array('Account.id' => null))), ), )); diff --git a/site/controllers/units_controller.php b/site/controllers/units_controller.php index 5b452d7..05f26ef 100644 --- a/site/controllers/units_controller.php +++ b/site/controllers/units_controller.php @@ -256,13 +256,13 @@ class UnitsController extends AppController { if (isset($unit['CurrentLease']['id']) && !isset($unit['CurrentLease']['close_date'])) { $this->sidemenu_links[] = - array('name' => 'Charge', 'url' => array('controller' => 'leases', - 'action' => 'invoice', - $unit['CurrentLease']['id'])); + array('name' => 'New Invoice', 'url' => array('controller' => 'leases', + 'action' => 'invoice', + $unit['CurrentLease']['id'])); $this->sidemenu_links[] = - array('name' => 'Payment', 'url' => array('controller' => 'customers', - 'action' => 'receipt', - $unit['CurrentLease']['customer_id'])); + array('name' => 'New Receipt', 'url' => array('controller' => 'customers', + 'action' => 'receipt', + $unit['CurrentLease']['customer_id'])); } // Prepare to render. diff --git a/site/models/customer.php b/site/models/customer.php index 42ac10e..cd2272c 100644 --- a/site/models/customer.php +++ b/site/models/customer.php @@ -142,10 +142,8 @@ class Customer extends AppModel { continue; $I = new Contact(); - $I->create(); - if (!$I->save($contact, false)) { + if (!$I->saveContact(null, array('Contact' => $contact))) return false; - } $contact['id'] = $I->id; } diff --git a/site/models/lease.php b/site/models/lease.php index bedec46..87e263d 100644 --- a/site/models/lease.php +++ b/site/models/lease.php @@ -610,7 +610,7 @@ class Lease extends AppModel { */ function moveOut($id, $status = 'VACANT', - $stamp = null, $close = false) + $stamp = null, $close = true) { $this->prEnter(compact('id', 'status', 'stamp', 'close')); diff --git a/site/models/statement_entry.php b/site/models/statement_entry.php index 322d977..0572196 100644 --- a/site/models/statement_entry.php +++ b/site/models/statement_entry.php @@ -252,7 +252,7 @@ class StatementEntry extends AppModel { null, null, $tx['customer_id'], - $tx['lease_id'] + null ); $this->pr(21, compact('result')); $ret['assigned'][] = $result; @@ -396,10 +396,11 @@ class StatementEntry extends AppModel { // First, find all known credits, unless this call is to make // credit adjustments to a specific charge - // REVISIT : 20090806 - // If the theory below is correct, we should only search for - // explicit credits if we don't have a receipt_id - if (empty($charge_ids)) { + if (empty($receipt_id)) { + + if (!empty($charge_ids)) + INTERNAL_ERROR("Charge IDs, yet no corresponding receipt"); + $lquery = $query; $lquery['conditions'][] = array('StatementEntry.type' => 'SURPLUS'); // REVISIT : 20090804 @@ -424,13 +425,7 @@ class StatementEntry extends AppModel { "Credits Established"); } else { - if (empty($receipt_id)) - INTERNAL_ERROR("Can't make adjustments to a charge without a receipt ID."); - } - - // Next, establish credit from the newly added receipt - $receipt_credit = null; - if (!empty($receipt_id)) { + // Next, establish credit from the newly added receipt $lquery = array('link' => array('StatementEntry', @@ -447,13 +442,13 @@ class StatementEntry extends AppModel { if (!$receipt_credit) INTERNAL_ERROR("Unable to locate receipt."); - //$reconciled = $this->reconciledEntries($id); - $stats = $this->Transaction->stats($receipt_id); $receipt_credit['balance'] = $receipt_credit['Transaction']['amount'] - $stats['Disbursement']['total']; - $this->pr(18, compact('receipt_credit'), + $receipt_credit['receipt'] = true; + $credits = array($receipt_credit); + $this->pr(18, compact('credits'), "Receipt Credit Added"); } @@ -476,44 +471,22 @@ class StatementEntry extends AppModel { $this->pr(18, compact('dtype', 'entries'), "Outstanding Debit Entries"); } - // Initialize our list of used credits - $used_credits = array(); - - // REVISIT : 20090806 - // Testing a theory. We should never have - // explicit credits, as well as a new receipt, - // and yet have outstanding charges. - if (!empty($credits) && !empty($receipt_credit) && !empty($charges)) - INTERNAL_ERROR("Explicit credits that haven't already been applied."); - // Work through all unpaid charges, applying disbursements as we go foreach ($charges AS $charge) { - $this->pr(20, compact('charge'), 'Process Charge'); - // Check that we have available credits. - // Technically, this isn't necessary, since the loop - // will handle everything just fine. However, this - // just saves extra processing if/when there is no - // means to resolve a charge anyway. - if (empty($credits) && empty($receipt_credit['balance'])) { - $this->pr(17, 'No more available credits'); - break; - } - $charge['balance'] = $charge['StatementEntry']['balance']; - while ($charge['balance'] > 0 && - (!empty($credits) || !empty($receipt_credit['balance']))) { - $this->pr(20, compact('charge'), - 'Attempt Charge Reconciliation'); + // Use explicit credits before using the new receipt credit + foreach ($credits AS &$credit) { + if (empty($charge['balance'])) + break; + if ($charge['balance'] < 0) + INTERNAL_ERROR("Negative Charge Balance"); - // Use explicit credits before using implicit credits - // (Not sure it matters though). - if (!empty($credits)) { - // Peel off the first credit available - $credit =& $credits[0]; + if (empty($credit['receipt'])) { + // Explicit Credit $disbursement_date = $credit['StatementEntry']['effective_date']; $disbursement_transaction_id = $credit['StatementEntry']['transaction_id']; $disbursement_account_id = $credit['StatementEntry']['account_id']; @@ -521,16 +494,29 @@ class StatementEntry extends AppModel { if (!isset($credit['balance'])) $credit['balance'] = $credit['StatementEntry']['amount']; } - elseif (!empty($receipt_credit['balance'])) { - // Use our only receipt credit - $credit =& $receipt_credit; + else { + // Receipt Credit $disbursement_date = $credit['Transaction']['stamp']; $disbursement_transaction_id = $credit['Transaction']['id']; $disbursement_account_id = $credit['LedgerEntry']['account_id']; } - else { - die("HOW DID WE GET HERE WITH NO SURPLUS?"); - } + + if (empty($credit['balance'])) + continue; + if ($credit['balance'] < 0) + INTERNAL_ERROR("Negative Credit Balance"); + + $this->pr(20, compact('charge'), + 'Attempt Charge Reconciliation'); + + // REVISIT : 20090811 + // Need to come up with a better strategy for handling + // concessions. For now, just restricting concessions + // to apply only towards rent will resolve the most + // predominant (or only) needed usage case. + if ($disbursement_account_id == $this->Account->concessionAccountID() && + $charge['StatementEntry']['account_id'] != $this->Account->rentAccountID()) + continue; // Set the disbursement amount to the maximum amount // possible without exceeding the charge or credit balance @@ -543,15 +529,7 @@ class StatementEntry extends AppModel { $this->pr(20, compact('credit'), ($credit['balance'] > 0 ? 'Utilized' : 'Exhausted') . - (!empty($credits) ? ' Credit' : ' Receipt')); - - if ($credit['balance'] < 0) - die("HOW DID WE END UP WITH NEGATIVE SURPLUS BALANCE?"); - - // If we've exhausted the credit, get it out of the - // available credit pool (but keep track of it for later). - if ($credit['balance'] <= 0 && !empty($credits)) - $used_credits[] = array_shift($credits); + (empty($credit['receipt']) ? ' Credit' : ' Receipt')); // Add a disbursement that uses the available credit to pay the charge $disbursement = array('type' => $disbursement_type, @@ -581,67 +559,69 @@ class StatementEntry extends AppModel { if ($charge['balance'] <= 0) $this->pr(20, 'Fully Paid Charge'); } - } - // Partially used credits must be added to the used list - if (isset($credits[0]['applied'])) - $used_credits[] = array_shift($credits); - - $this->pr(18, compact('credits', 'used_credits', 'receipt_credit'), - 'Disbursements added'); + $this->pr(18, compact('credits'), + 'Disbursements complete'); // Clean up any explicit credits that have been used - foreach ($used_credits AS $credit) { - if ($credit['balance'] > 0) { - $this->pr(20, compact('credit'), - 'Update Credit Entry'); + foreach ($credits AS $credit) { + if (empty($credit['receipt'])) { + // Explicit Credit + if (empty($credit['applied'])) + continue; - $this->id = $credit['StatementEntry']['id']; - $this->saveField('amount', $credit['balance']); + if ($credit['balance'] > 0) { + $this->pr(20, compact('credit'), + 'Update Credit Entry'); + + $this->id = $credit['StatementEntry']['id']; + $this->saveField('amount', $credit['balance']); + } + else { + $this->pr(20, compact('credit'), + 'Delete Exhausted Credit Entry'); + + $this->del($credit['StatementEntry']['id'], false); + } } else { - $this->pr(20, compact('credit'), - 'Delete Exhausted Credit Entry'); + // Receipt Credit + if (empty($credit['balance'])) + continue; - $this->del($credit['StatementEntry']['id'], false); - } - } + // Convert non-exhausted receipt credit to an explicit one + $explicit_credit = $this->find + ('first', array('contain' => false, + 'conditions' => + array(array('transaction_id' => $credit['Transaction']['id']), + array('type' => 'SURPLUS')), + )); - // Convert non-exhausted receipt credit to an explicit one - if (!empty($receipt_credit['balance'])) { - $credit =& $receipt_credit; + if (empty($explicit_credit)) { + $this->pr(18, compact('credit'), + 'Create Explicit Credit'); - $explicit_credit = $this->find - ('first', array('contain' => false, - 'conditions' => - array(array('transaction_id' => $credit['Transaction']['id']), - array('type' => 'SURPLUS')), - )); - - if (empty($explicit_credit)) { - $this->pr(18, compact('credit'), - 'Create Explicit Credit'); - - $result = $this->addStatementEntry - (array('type' => 'SURPLUS', - 'account_id' => $credit['LedgerEntry']['account_id'], - 'amount' => $credit['balance'], - 'effective_date' => $credit['Transaction']['stamp'], - 'transaction_id' => $credit['Transaction']['id'], - 'customer_id' => $customer_id, - 'lease_id' => $lease_id, - )); - $ret['Credit'] = $result; - if ($result['error']) - $ret['error'] = true; - } - else { - $this->pr(18, compact('explicit_credit', 'credit'), - 'Update Explicit Credit'); - $EC = new StatementEntry(); - $EC->id = $explicit_credit['StatementEntry']['id']; - $EC->saveField('amount', $credit['balance']); + $result = $this->addStatementEntry + (array('type' => 'SURPLUS', + 'account_id' => $credit['LedgerEntry']['account_id'], + 'amount' => $credit['balance'], + 'effective_date' => $credit['Transaction']['stamp'], + 'transaction_id' => $credit['Transaction']['id'], + 'customer_id' => $customer_id, + 'lease_id' => $lease_id, + )); + $ret['Credit'] = $result; + if ($result['error']) + $ret['error'] = true; + } + else { + $this->pr(18, compact('explicit_credit', 'credit'), + 'Update Explicit Credit'); + $EC = new StatementEntry(); + $EC->id = $explicit_credit['StatementEntry']['id']; + $EC->saveField('amount', $credit['balance']); + } } } diff --git a/site/models/tender.php b/site/models/tender.php index 405816e..b0e913b 100644 --- a/site/models/tender.php +++ b/site/models/tender.php @@ -21,7 +21,7 @@ class Tender extends AppModel { * - Performs any work needed before the save occurs */ - function afterSave() { + function afterSave($created) { // Come up with a (not necessarily unique) name for the tender. // For checks & money orders, this will be based on the check // number. For other types of tender, we'll just use the @@ -45,7 +45,7 @@ class Tender extends AppModel { if ($newname !== $this->field('name')) $this->saveField('name', $newname); - return parent::afterSave(); + return parent::afterSave($created); } diff --git a/site/models/transaction.php b/site/models/transaction.php index 0e85c06..8813f99 100644 --- a/site/models/transaction.php +++ b/site/models/transaction.php @@ -685,6 +685,7 @@ class Transaction extends AppModel { // with the customer statement (charges, disbursements, credits, etc). $bounce_result = $this->addDeposit (array('Transaction' => array('stamp' => $stamp, + 'type' => 'WITHDRAWAL', 'crdr' => 'CREDIT'), 'Entry' => array(array('tender_id' => null, 'account_id' => $this->Account->nsfAccountID(), diff --git a/site/views/customers/receipt.ctp b/site/views/customers/receipt.ctp index 76fdb86..35e8cfb 100644 --- a/site/views/customers/receipt.ctp +++ b/site/views/customers/receipt.ctp @@ -315,7 +315,7 @@ echo $this->element('statement_entries', array 'grid_setup' => array('hiddengrid' => true), 'caption' => '', 'action' => 'idlist', - 'exclude' => array('Customer', 'Unit', 'Type', 'Debit', 'Credit'), + 'exclude' => array('Customer', 'Type', 'Debit', 'Credit'), 'include' => array('Applied', 'Balance'), 'remap' => array('Applied' => 'Paid'), 'limit' => 8, diff --git a/site/views/leases/invoice.ctp b/site/views/leases/invoice.ctp index 07aca35..30edd1b 100644 --- a/site/views/leases/invoice.ctp +++ b/site/views/leases/invoice.ctp @@ -71,7 +71,7 @@ function showResponse(responseText, statusText) { } // get a clean slate - //resetForm(); + resetForm(); } else { $('#results').html('

Failed to save invoice!

'); @@ -331,6 +331,7 @@ Configure::write('debug', '0'); ' value="">'); $("#TransactionComment").val('Move-In Charges'); + id = addChargeSource(false); $('#Entry'+id+'Form').removeCol(2); $('#Entry'+id+'Form input, #Entry'+id+'Form select').attr('disabled', true); @@ -351,7 +352,7 @@ Configure::write('debug', '0'); ' value="">'); //$('#Entry'+id+'Comment').val('Move-In Security Deposit'); $('#Entry'+id+'Comment').removeAttr('disabled'); - + id = addChargeSource(false); $('#Entry'+id+'Form').removeCol(2); @@ -376,8 +377,7 @@ Configure::write('debug', '0'); (''); - $('#Entry'+id+'Comment').val('Move-In Rent' - ); + $('#Entry'+id+'Comment').val(""); $('#Entry'+id+'Comment').removeAttr('disabled'); diff --git a/site/views/leases/move.ctp b/site/views/leases/move.ctp index 4787c7d..028a8d6 100644 --- a/site/views/leases/move.ctp +++ b/site/views/leases/move.ctp @@ -128,7 +128,7 @@ if ($move_type !== 'out') { ), 'include' => array('Deposit'), 'exclude' => array('Balance'), - 'action' => 'unoccupied', + 'action' => 'vacant', 'nolinks' => true, 'limit' => 10, ))); diff --git a/site/views/tenders/deposit.ctp b/site/views/tenders/deposit.ctp index 4616d18..b073b28 100644 --- a/site/views/tenders/deposit.ctp +++ b/site/views/tenders/deposit.ctp @@ -30,7 +30,8 @@ foreach ($depositTypes AS $type) { 'separator' => '
', 'onclick' => "switchSelection({$type['id']})", 'legend' => false, - 'value' => $type['stats']['undeposited'] > 0 ? 'all' : 'none', + // REVISIT : 20080811; Make opt-in, or opt-out? + 'value' => $type['stats']['undeposited'] > 0 ? 'none' : 'none', 'disabled' => $type['stats']['undeposited'] <= 0, 'options' => $radioOptions, )); diff --git a/site/views/units/view.ctp b/site/views/units/view.ctp index 47aa53d..cb3741f 100644 --- a/site/views/units/view.ctp +++ b/site/views/units/view.ctp @@ -87,6 +87,8 @@ if (isset($current_lease['id'])) { 'filter' => array('Lease.id' => $current_lease['id']), 'include' => array('Through'), 'exclude' => array('Customer', 'Lease', 'Unit'), + 'sort_column' => 'Effective', + 'sort_order' => 'DESC', ))); }