array('numeric'), 'number' => array('alphanumeric'), 'lease_type_id' => array('numeric'), 'unit_id' => array('numeric'), 'late_schedule_id' => array('numeric'), 'lease_date' => array('date'), 'movein_planned_date' => array('date'), 'movein_date' => array('date'), 'moveout_date' => array('date'), 'moveout_planned_date' => array('date'), 'notice_given_date' => array('date'), 'notice_received_date' => array('date'), 'close_date' => array('date'), 'deposit' => array('money'), 'rent' => array('money'), 'next_rent' => array('money'), 'next_rent_date' => array('date') ); var $belongsTo = array( 'LeaseType', 'Unit', 'Customer', 'LateSchedule', ); var $hasMany = array( 'LedgerEntry', ); /************************************************************************** ************************************************************************** ************************************************************************** * function: accountId * - Returns the accountId of the given lease */ function accountId($id) { $A = new Account(); return $A->invoiceAccountID(); } /************************************************************************** ************************************************************************** ************************************************************************** * function: findAccountEntries * - Returns an array of ledger entries from the account of the given * lease. */ function findAccountEntries($id, $all = false, $cond = null, $link = null) { /* pr(array('function' => 'Lease::findAccountEntries', */ /* 'args' => compact('id', 'all', 'cond', 'link'), */ /* )); */ if (!isset($cond)) $cond = array(); $cond[] = array('LedgerEntry.lease_id' => $id); $A = new Account(); $entries = $A->findLedgerEntries($this->accountId($id), $all, $cond, $link); /* pr(array('function' => 'Lease::findAccountEntries', */ /* 'args' => compact('id', 'all', 'cond', 'link'), */ /* 'vars' => compact('lease'), */ /* 'return' => compact('entries'), */ /* )); */ return $entries; } /************************************************************************** ************************************************************************** ************************************************************************** * function: findSecurityDeposits * - Returns an array of security deposit entries */ function findSecurityDeposits($id, $link = null) { /* pr(array('function' => 'Lease::findSecurityDeposits', */ /* 'args' => compact('id', 'link'), */ /* )); */ $A = new Account(); $entries = $A->findLedgerEntries ($A->securityDepositAccountID(), true, array('LedgerEntry.lease_id' => $id), $link); /* pr(array('function' => 'Lease::findSecurityDeposits', */ /* 'args' => compact('id', 'link'), */ /* 'vars' => compact('lease'), */ /* 'return' => compact('entries'), */ /* )); */ return $entries; } /************************************************************************** ************************************************************************** ************************************************************************** * function: findUnreconciledLedgerEntries * - Returns ledger entries that are not yet reconciled * (such as charges not paid). */ function findUnreconciledLedgerEntries($id = null, $fundamental_type = null) { $A = new Account(); return $A->findUnreconciledLedgerEntries ($this->accountId($id), $fundamental_type, array('LedgerEntry.lease_id' => $id)); } /************************************************************************** ************************************************************************** ************************************************************************** * function: reconcileNewLedgerEntry * - Returns which ledger entries a new credit/debit would * reconcile, and how much. * * - REVISIT 20090617 * This should be subject to different algorithms, such * as apply to oldest charges first, newest first, to fees * before rent, etc. Until we get there, I'll hardcode * whatever algorithm is simplest. */ function reconcileNewLedgerEntry($id, $fundamental_type, $amount) { $A = new Account(); return $A->reconcileNewLedgerEntry ($this->accountId($id), $fundamental_type, $amount, array('LedgerEntry.lease_id' => $id)); } /************************************************************************** ************************************************************************** ************************************************************************** * 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) { $A = new Account(); $entries = $this->find ('all', array('link' => array(// Models 'LedgerEntry' => array ('Ledger' => array ('fields' => array(), 'Account' => array ('fields' => array(), 'Ledger' => array ('alias' => 'Lx', 'fields' => array(), 'LedgerEntry' => array ('alias' => 'LEx', 'fields' => array(), 'conditions' => array ('LEx.effective_date = DATE_ADD(LedgerEntry.through_date, INTERVAL 1 day)', 'LEx.lease_id = LedgerEntry.lease_id', ) ), ), ), ), ), ), //'fields' => array('id', 'amount', 'effective_date', 'through_date'), 'fields' => array(), 'conditions' => array(array('Lease.id' => $id), array('Account.id' => $A->rentAccountID()), array('LEx.id' => null), ), ) ); return $entries; } /************************************************************************** ************************************************************************** ************************************************************************** * function: rentChargeGaps * - Checks for gaps in rent charges */ function rentChargeGaps($id) { $entries = $this->rentLastCharges($id); if ($entries && count($entries) > 1) return true; return 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) { $entries = $this->rentLastCharges($id); if (!$entries) return false; if (count($entries) != 1) return null; return $entries[0]['LedgerEntry']['through_date']; } /************************************************************************** ************************************************************************** ************************************************************************** * function: rentPaidThrough * - Determines the date of the first unpaid rent */ function rentPaidThrough($id) { // Income / Receipt / Money // debit: A/R credit: Income <-- this entry // debit: Receipt credit: A/R <-- ReceiptLedgerEntry, below // debit: Money credit: Receipt <-- MoneyLedgerEntry, below $query = array ('link' => array ( 'CreditLedger' => array('fields' => array(), 'Account' => array('fields' => array(), ), ), // We're searching for the Receipt<->A/R entries, // which are debits on the A/R account. Find the // reconciling entries to that A/R debit. 'DebitReconciliationLedgerEntry' => array('alias' => 'ReceiptLedgerEntry', 'fields' => array(), // Finally, the Money (Cash/Check/etc) Entry is the one // which reconciles our ReceiptLedgerEntry debit 'DebitReconciliationLedgerEntry' => array('alias' => 'MoneyLedgerEntry', 'linkalias' => 'MoneyLedgerEntryR', 'fields' => array('SUM(COALESCE(MoneyLedgerEntryR.amount,0)) AS paid'), ), ), ), 'fields' => array('LedgerEntry.amount', 'DATE_SUB(LedgerEntry.effective_date, INTERVAL 1 DAY) AS paid_through', ), 'group' => 'LedgerEntry.id HAVING paid <> LedgerEntry.amount', 'conditions' => array(array('LedgerEntry.lease_id' => $id), array('Account.id' => $this->LedgerEntry->Ledger->Account->rentAccountID()), ), 'order' => array('LedgerEntry.effective_date', ), ); $rent = $this->LedgerEntry->find('first', $query); if ($rent) return $rent[0]['paid_through']; $query['fields'] = 'LedgerEntry.through_date'; $query['order'] = 'LedgerEntry.through_date DESC'; $query['group'] = 'LedgerEntry.id'; $rent = $this->LedgerEntry->find('first', $query); if ($rent) return $rent['LedgerEntry']['through_date']; return 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) { $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 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 : 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->id; } /************************************************************************** ************************************************************************** ************************************************************************** * function: moveOut * - Moves the customer out of the specified lease */ function moveOut($id, $status = 'VACANT', $stamp = null, $close = false) { // 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) { if (!$this->closeable($id)) return 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 true; } /************************************************************************** ************************************************************************** ************************************************************************** * function: closeable * - Indicates whether or not the lease can be closed */ function closeable($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 false; // We can't close a lease that's already closed if (isset($this->data['Lease']['close_date'])) return false; $deposits = $this->findSecurityDeposits($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 ($deposits['summary']['balance'] != 0 || $stats['balance'] != 0) return false; // Apparently this lease meets all the criteria! return 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) { if (!$id) return null; $A = new Account(); $stats = $A->stats($A->accountReceivableAccountID(), true, array('LedgerEntry.lease_id' => $id)); // Pull to the top level and return $stats = $stats['Ledger']; return $stats; } } ?>