Files
pmgr/models/lease.php

518 lines
17 KiB
PHP

<?php
class Lease extends AppModel {
var $belongsTo = array(
'LeaseType',
'Unit',
'Customer',
'LateSchedule',
);
var $hasMany = array(
'StatementEntry',
);
var $default_log_level = array('log' => 30, 'show' => 15);
/**************************************************************************
**************************************************************************
**************************************************************************
* function: securityDeposits
* - Returns an array of security deposit entries
*/
function securityDeposits($id, $query = null) {
$this->prEnter(compact('id', 'query'));
$this->queryInit($query);
if (!isset($query['link']['Lease']))
$query['link']['Lease'] = array();
if (!isset($query['link']['Lease']['fields']))
$query['link']['Lease']['fields'] = array();
$query['conditions'][] = array('Lease.id' => $id);
$query['conditions'][] = array('StatementEntry.account_id' =>
$this->StatementEntry->Account->securityDepositAccountID());
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, false, true);
/* $set['summary'] = array('total' => $set['summary']['Charge']['total'], */
/* 'balance' => $set['summary']['Charge']['reconciled'], */
/* ); */
return $this->prReturn($set);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: securityDepositBalance
* - Returns the balance of the lease security deposit(s)
*/
function securityDepositBalance($id, $query = null) {
$this->prEnter(compact('id', 'query'));
$this->queryInit($query);
if (!isset($query['link']['Lease']))
$query['link']['Lease'] = array();
if (!isset($query['link']['Lease']['fields']))
$query['link']['Lease']['fields'] = array();
$query['conditions'][] = array('Lease.id' => $id);
$query['conditions'][] = array('StatementEntry.account_id' =>
$this->StatementEntry->Account->securityDepositAccountID());
$stats = $this->StatementEntry->stats(null, $query);
$balance = $stats['Charge']['reconciled'] - $stats['Payment']['reconciled'];
return $this->prReturn($balance);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: releaseSecurityDeposits
* - Releases all security deposits associated with this lease.
* That simply makes a payment out of them, which can be used
* to pay outstanding customer charges, or simply to become
* a customer surplus (customer credit).
*/
function releaseSecurityDeposits($id, $query = null) {
$this->prFunctionLevel(30);
$this->prEnter(compact('id', 'query'));
$secdeps = $this->securityDeposits($id, $query);
$secdeps = $secdeps['entries'];
$this->pr(20, compact('secdeps'));
// If there are no paid security deposits, then
// we can consider all security deposits released.
if (count($secdeps) == 0)
return $this->prReturn(true);
// Build a transaction
$release = array('Transaction' => array(), 'Entry' => array());
$release['Transaction']['comment'] = "Security Deposit Release";
foreach ($secdeps AS $charge) {
if ($charge['StatementEntry']['type'] !== 'CHARGE')
die("INTERNAL ERROR: SECURITY DEPOSIT IS NOT CHARGE");
// Since security deposits are being released, this also means
// any unpaid (or only partially paid) security deposit should
// have the remaining balance simply waived.
if ($charge['StatementEntry']['balance'] > 0)
$this->StatementEntry->waive($charge['StatementEntry']['id']);
$release['Entry'][] =
array('amount' => $charge['StatementEntry']['reconciled'],
'account_id' => $this->StatementEntry->Account->securityDepositAccountID(),
'comment' => "Released Security Deposit",
);
}
$customer_id = $secdeps[0]['StatementEntry']['customer_id'];
$lease_id = $secdeps[0]['StatementEntry']['lease_id'];
$result = $this->StatementEntry->Transaction->addReceipt
($release, $customer_id, $lease_id);
return $this->prReturn($result);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: rentLastCharges
* - Returns a list of rent charges from this lease that
* do not have sequential followup charges. Under normal
* circumstances, there would only be one entry, which is
* the most recent rent charge. However, it's possible
* that there are several, indicating a problem with lease.
*/
function rentLastCharges($id) {
$this->prEnter(compact('id'));
$rent_account_id = $this->StatementEntry->Account->rentAccountID();
$entries = $this->find
('all',
array('link' =>
array(// Models
'StatementEntry',
'SEx' =>
array('class' => 'StatementEntry',
'fields' => array(),
'conditions' => array
('SEx.effective_date = DATE_ADD(StatementEntry.through_date, INTERVAL 1 day)',
'SEx.lease_id = StatementEntry.lease_id',
),
),
),
//'fields' => array('id', 'amount', 'effective_date', 'through_date'),
'fields' => array(),
'conditions' => array(array('Lease.id' => $id),
array('StatementEntry.type' => 'CHARGE'),
array('StatementEntry.account_id' => $rent_account_id),
array('SEx.id' => null),
),
)
);
return $this->prReturn($entries);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: rentChargeGaps
* - Checks for gaps in rent charges
*/
function rentChargeGaps($id) {
$this->prEnter(compact('id'));
$entries = $this->rentLastCharges($id);
if ($entries && count($entries) > 1)
return $this->prReturn(true);
return $this->prReturn(false);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: rentChargeThrough
* - Determines the date that rent has been charged through
* Returns one of:
* null: There are gaps in the charges
* false: There are not yet any charges
* date: The date rent has been charged through
*/
function rentChargeThrough($id) {
$this->prEnter(compact('id'));
$entries = $this->rentLastCharges($id);
if (!$entries)
return $this->prReturn(false);
if (count($entries) != 1)
return $this->prReturn(null);
return $this->prReturn($entries[0]['StatementEntry']['through_date']);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: rentPaidThrough
* - Determines the date of the first unpaid rent
*/
function rentPaidThrough($id) {
$this->prEnter(compact('id'));
$rent_account_id = $this->StatementEntry->Account->rentAccountID();
// First, see if we can find any unpaid entries. Of course,
// the first unpaid entry gives us a very direct indication
// of when the customer is paid up through, which is 1 day
// prior to the effective date of that first unpaid charge.
$rent = $this->StatementEntry->reconciledSet
('CHARGE',
array('fields' =>
array('StatementEntry.*',
'DATE_SUB(StatementEntry.effective_date, INTERVAL 1 DAY) AS paid_through',
),
'conditions' =>
array(array('StatementEntry.lease_id' => $id),
array('StatementEntry.account_id' => $rent_account_id)),
'order' => array('StatementEntry.effective_date'),
),
true);
$this->pr(20, $rent, "Unpaid rent");
if ($rent['entries'])
return $this->prReturn($rent['entries'][0]['StatementEntry']['paid_through']);
// If we don't have any unpaid charges (great!), then the
// customer is paid up through the last day of the last
// charge. So, search for paid charges, which already
// have the paid through date saved as part of the entry.
$rent = $this->StatementEntry->reconciledSet
('CHARGE',
array('conditions' =>
array(array('StatementEntry.lease_id' => $id),
array('StatementEntry.account_id' => $rent_account_id)),
'order' => array('StatementEntry.through_date DESC'),
),
false);
$this->pr(20, $rent, "Paid rent");
if ($rent['entries'])
return $this->prReturn($rent['entries'][0]['StatementEntry']['through_date']);
// After all that, having found that there are no unpaid
// charges, and in fact, no paid charges either, we cannot
// possibly say when the customer is paid through.
return $this->prReturn(null);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: moveIn
* - Moves the specified customer into the specified lease
*/
function moveIn($customer_id, $unit_id,
$deposit = null, $rent = null,
$stamp = null, $comment = null)
{
$this->prEnter(compact('customer_id', 'unit_id',
'deposit', 'rent', 'stamp', 'comment'));
$lt = $this->LeaseType->find('first',
array('conditions' =>
array('code' => 'SL')));
// Use NOW if not given a movein date
if (!isset($stamp))
$stamp = date('Y-m-d G:i:s');
if (!$comment)
$comment = null;
if (!isset($deposit) || !isset($rent)) {
$rates = $this->Unit->find
('first',
array('contain' =>
array('UnitSize' =>
array('deposit', 'rent'),
),
'fields' => array('deposit', 'rent'),
'conditions' => array('Unit.id' => $unit_id),
));
$deposit =
(isset($deposit)
? $deposit
: (isset($rates['Unit']['deposit'])
? $rates['Unit']['deposit']
: (isset($rates['UnitSize']['deposit'])
? $rates['UnitSize']['deposit']
: 0)));
$rent =
(isset($rent)
? $rent
: (isset($rates['Unit']['rent'])
? $rates['Unit']['rent']
: (isset($rates['UnitSize']['rent'])
? $rates['UnitSize']['rent']
: 0)));
}
// Save this new lease.
$this->create();
if (!$this->save(array('lease_type_id' => $lt['LeaseType']['id'],
'unit_id' => $unit_id,
'customer_id' => $customer_id,
'lease_date' => $stamp,
'movein_date' => $stamp,
'deposit' => $deposit,
'rent' => $rent,
'comment' => $comment), false)) {
return $this->prReturn(null);
}
// Set the lease number to be the same as the lease ID
$this->id;
$this->saveField('number', $this->id);
// Update the unit status
$this->Unit->updateStatus($unit_id, 'OCCUPIED');
// REVISIT <AP>: 20090702
// We need to assess the security deposit charge,
// and probably rent as well. Rent, however, will
// require user parameters to indicate whether it
// was waived, pro-rated, etc.
// Return the new lease ID
return $this->prReturn($this->id);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: moveOut
* - Moves the customer out of the specified lease
*/
function moveOut($id, $status = 'VACANT',
$stamp = null, $close = false)
{
$this->prEnter(compact('id', 'status', 'stamp', 'close'));
// Use NOW if not given a moveout date
if (!isset($stamp))
$stamp = date('Y-m-d G:i:s');
// Reset the data
$this->create();
$this->id = $id;
// Set the customer move-out date
$this->data['Lease']['moveout_date'] = $stamp;
// Save it!
$this->save($this->data, false);
// Close the lease, if so requested
if ($close)
$this->close($id, $stamp);
// Finally, update the unit status
$this->recursive = -1;
$this->read();
$this->Unit->updateStatus($this->data['Lease']['unit_id'], $status);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: close
* - Closes the lease to further action
*/
function close($id, $stamp = null) {
$this->prEnter(compact('id', 'stamp'));
if (!$this->closeable($id))
return $this->prReturn(false);
// Reset the data
$this->create();
$this->id = $id;
// Use NOW if not given a moveout date
if (!isset($stamp))
$stamp = date('Y-m-d G:i:s');
// Set the close date
$this->data['Lease']['close_date'] = $stamp;
// Save it!
$this->save($this->data, false);
return $this->prReturn(true);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: closeable
* - Indicates whether or not the lease can be closed
*/
function closeable($id) {
$this->prEnter(compact('id'));
$this->recursive = -1;
$this->read(null, $id);
// We can't close a lease that's still in use
if (!isset($this->data['Lease']['moveout_date']))
return $this->prReturn(false);
// We can't close a lease that's already closed
if (isset($this->data['Lease']['close_date']))
return $this->prReturn(false);
$deposit_balance = $this->securityDepositBalance($id);
$stats = $this->stats($id);
// A lease can only be closed if there are no outstanding
// security deposits, and if the account balance is zero.
if ($deposit_balance != 0)
return $this->prReturn(false);
// Apparently this lease meets all the criteria!
return $this->prReturn(true);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: addCharge
* - Adds an additional charge to the lease
*/
function addCharge($id, $charge) {
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: stats
* - Returns summary data from the requested lease.
*/
function stats($id = null, $query = null) {
$this->prEnter(compact('id', 'query'));
if (!$id)
return $this->prReturn(null);
$this->queryInit($query);
//$query['link'] = array('Lease' => $query['link']);
/* if (!isset($query['link']['StatementEntry'])) */
/* $query['link']['StatementEntry'] = array(); */
/* if (!isset($query['link']['StatementEntry']['ChargeEntry'])) */
/* $query['link']['StatementEntry']['ChargeEntry'] = array(); */
/* $query['link']['StatementEntry']['fields'] = array(); */
/* $query['link']['ChargeEntry']['fields'] = array(); */
/* $query['link']['ChargeEntry']['Account']['fields'] = array(); */
/* $query['link']['ChargeEntry']['StatementEntry']['fields'] = array(); */
/* $query['link']['ChargeEntry']['StatementEntry']['Invoice']['fields'] = array(); */
if (!isset($query['fields']))
$query['fields'] = array();
$query['fields'] = array_merge($query['fields'],
$this->StatementEntry->chargePaymentFields(true));
$query['conditions'][] = array('StatementEntry.lease_id' => $id);
$query['group'] = null;
$stats = $this->StatementEntry->find('first', $query);
//$this->pr(20, compact('query', 'stats'));
// The fields are all tucked into the [0] index,
// and the rest of the array is useless (empty).
$stats = $stats[0];
// Make sure we have a non-null balance
if (!isset($stats['balance']))
$stats['balance'] = 0;
return $this->prReturn($stats);
}
}
?>