git-svn-id: file:///svn-source/pmgr/branches/v0.3_work@993 97e9348a-65ac-dc4b-aefc-98561f571b83
525 lines
18 KiB
PHP
525 lines
18 KiB
PHP
<?php
|
|
class Customer extends AppModel {
|
|
|
|
var $name = 'Customer';
|
|
var $validate = array(
|
|
'id' => array('numeric'),
|
|
'name' => array('notempty'),
|
|
);
|
|
|
|
var $belongsTo = array(
|
|
'PrimaryContact' => array(
|
|
'className' => 'Contact',
|
|
),
|
|
);
|
|
|
|
var $hasMany = array(
|
|
'CurrentLease' => array(
|
|
'className' => 'Lease',
|
|
'conditions' => 'CurrentLease.close_date IS NULL',
|
|
),
|
|
'Lease',
|
|
'StatementEntry',
|
|
'ContactsCustomer',
|
|
|
|
'Transaction',
|
|
'Tender',
|
|
);
|
|
|
|
var $hasAndBelongsToMany = array(
|
|
'Contact' => array(
|
|
'unique' => true,
|
|
),
|
|
);
|
|
|
|
//var $default_log_level = 20;
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: accountId
|
|
* - Returns the account ID for the given customer
|
|
*/
|
|
function accountId($id) {
|
|
$this->cacheQueries = true;
|
|
$customer = $this->find('first',
|
|
array('contain' => false,
|
|
'fields' => array('account_id'),
|
|
'conditions' => array(array('Customer.id' => $id))));
|
|
$this->cacheQueries = false;
|
|
|
|
return $customer['Customer']['account_id'];
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: leaseIds
|
|
* - Returns the lease IDs for the given customer
|
|
*/
|
|
function leaseIds($id, $current = false) {
|
|
$Lease = $current ? 'CurrentLease' : 'Lease';
|
|
|
|
$this->cacheQueries = true;
|
|
$customer = $this->find('first',
|
|
array('contain' =>
|
|
array($Lease => array('fields' => array('id'))),
|
|
'fields' => array(),
|
|
'conditions' => array(array('Customer.id' => $id))));
|
|
$this->cacheQueries = false;
|
|
|
|
$ids = array();
|
|
foreach ($customer[$Lease] AS $lease)
|
|
$ids[] = $lease['id'];
|
|
|
|
return $ids;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: securityDeposits
|
|
* - Returns an array of security deposit entries
|
|
*/
|
|
function securityDeposits($id, $query = null) {
|
|
$this->prEnter(compact('id', 'query'));
|
|
$this->queryInit($query);
|
|
|
|
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
|
|
$query['conditions'][] = array('StatementEntry.account_id' =>
|
|
$this->StatementEntry->Account->securityDepositAccountID());
|
|
|
|
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, false, true);
|
|
return $this->prReturn($set);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: securityDepositBalance
|
|
* - Returns the balance of the customer security deposit(s)
|
|
*/
|
|
|
|
function securityDepositBalance($id, $query = null) {
|
|
$this->prEnter(compact('id', 'query'));
|
|
$this->queryInit($query);
|
|
|
|
$sd_account_id =
|
|
$this->StatementEntry->Account->securityDepositAccountID();
|
|
|
|
$squery = $query;
|
|
$squery['conditions'][] = array('StatementEntry.customer_id' => $id);
|
|
$squery['conditions'][] = array('StatementEntry.account_id' => $sd_account_id);
|
|
$stats = $this->StatementEntry->stats(null, $squery);
|
|
$this->pr(26, compact('squery', 'stats'));
|
|
|
|
// OK, we know now how much we charged for a security
|
|
// deposit, as well as how much we received to pay for it.
|
|
// Now we need to know if any has been released.
|
|
// Yes... this sucks.
|
|
$lquery = $query;
|
|
$lquery['link'] = array('Transaction' =>
|
|
array('fields' => array(),
|
|
'Customer' =>
|
|
(empty($query['link'])
|
|
? array('fields' => array())
|
|
: $query['link'])));
|
|
$lquery['conditions'][] = array('Transaction.customer_id' => $id);
|
|
$lquery['conditions'][] = array('LedgerEntry.account_id' => $sd_account_id);
|
|
$lquery['conditions'][] = array('LedgerEntry.crdr' => 'DEBIT');
|
|
$lquery['fields'][] = 'SUM(LedgerEntry.amount) AS total';
|
|
$released = $this->StatementEntry->Transaction->LedgerEntry->find
|
|
('first', $lquery);
|
|
$this->pr(26, compact('lquery', 'released'));
|
|
|
|
return $this->prReturn($stats['Charge']['disbursement'] - $released[0]['total']);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: unreconciledCharges
|
|
* - Returns charges have not yet been fully paid
|
|
*/
|
|
|
|
function unreconciledCharges($id, $query = null) {
|
|
$this->prEnter(compact('id', 'query'));
|
|
$this->queryInit($query);
|
|
|
|
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
|
|
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, true);
|
|
return $this->prReturn($set);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: saveCustomer
|
|
* - Saves the customer and related data
|
|
*/
|
|
|
|
function saveCustomer($id, $data, $primary_contact_entry) {
|
|
|
|
// Go through each contact, and create new ones as needed
|
|
foreach ($data['Contact'] AS &$contact) {
|
|
if (isset($contact['id']))
|
|
continue;
|
|
|
|
$I = new Contact();
|
|
if (!$I->saveContact(null, array('Contact' => $contact)))
|
|
return false;
|
|
$contact['id'] = $I->id;
|
|
}
|
|
|
|
// Set the primary contact ID based on caller selection
|
|
$data['Customer']['primary_contact_id']
|
|
= $data['Contact'][$primary_contact_entry]['id'];
|
|
|
|
// Provide a default customer name if not specified
|
|
if (!$data['Customer']['name']) {
|
|
$this->Contact->recursive = -1;
|
|
$pcontact = $this->Contact->read(null, $data['Customer']['primary_contact_id']);
|
|
$data['Customer']['name'] = $pcontact['Contact']['display_name'];
|
|
}
|
|
|
|
// Save the customer data
|
|
$this->create();
|
|
if ($id)
|
|
$this->id = $id;
|
|
if (!$this->save($data, false)) {
|
|
return false;
|
|
}
|
|
$id = $this->id;
|
|
|
|
// Appears that $this->save() "helpfully" choses to add in
|
|
// any missing data fields, populated with default values.
|
|
// So, after saving is complete, the fields 'lease_count',
|
|
// 'past_lease_count', and 'current_lease_count' have all
|
|
// been reset to zero. Gee, thanks Cake...
|
|
$this->update($id);
|
|
|
|
// Remove all associated Customer Contacts, as it ensures
|
|
// any entries deleted by the user actually get deleted
|
|
// in the system. We'll recreate the needed ones anyway.
|
|
// REVISIT <AP>: 20090706
|
|
// Appears that $this->save() is already doing the
|
|
// delete. I would have thought this would only happen
|
|
// on a saveAll??
|
|
/* $this->ContactsCustomer->deleteAll */
|
|
/* (array('customer_id' => $id), false); */
|
|
|
|
// At this point, since we've saved data to customer,
|
|
// we'll proceed forward as much as possible, even
|
|
// if we encounter an error. For now, we'll assume
|
|
// the operation will succeed.
|
|
$ret = true;
|
|
|
|
// Go through each entry of this customer method
|
|
foreach ($data['Contact'] AS &$contact) {
|
|
// Update the ContactsCustomer to reflect the appropriate IDs
|
|
$contact['ContactsCustomer']['customer_id'] = $id;
|
|
$contact['ContactsCustomer']['contact_id'] = $contact['id'];
|
|
|
|
// Save the relationship between customer and contact
|
|
$CM = new ContactsCustomer();
|
|
if (!$CM->save($contact['ContactsCustomer'], false)) {
|
|
$ret = false;
|
|
}
|
|
}
|
|
|
|
// Return the result
|
|
return $ret;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: update
|
|
* - Update any cached or calculated fields
|
|
*/
|
|
function update($id) {
|
|
$this->prEnter(compact('id'));
|
|
|
|
if (empty($id)) {
|
|
$customers = $this->find('all', array('contain' => false, 'fields' => array('id')));
|
|
foreach ($customers AS $customer) {
|
|
// This SHOULDN'T happen, but check to be sure
|
|
// or we'll get infinite recursion.
|
|
if (empty($customer['Customer']['id']))
|
|
continue;
|
|
$this->update($customer['Customer']['id']);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// updateLeaseCount is typically handled directly when needed.
|
|
// However, this function is used to _ensure_ customer info is
|
|
// current, so we're obligated to call it anyway.
|
|
$this->updateLeaseCount($id);
|
|
|
|
$current_leases =
|
|
$this->find('all',
|
|
// REVISIT <AP>: 20090816
|
|
// Do we need to update leases other than the current ones?
|
|
// It may be necessary. For example, a non-current lease
|
|
// can still be hit with an NSF item. In that case, it
|
|
// could have stale data if we look only to current leases.
|
|
//array('link' => array('CurrentLease' => array('type' => 'INNER')),
|
|
array('link' => array('Lease' => array('type' => 'INNER')),
|
|
'conditions' => array('Customer.id' => $id)));
|
|
|
|
foreach ($current_leases AS $lease) {
|
|
if (!empty($lease['CurrentLease']['id']))
|
|
$this->Lease->update($lease['CurrentLease']['id']);
|
|
if (!empty($lease['Lease']['id']))
|
|
$this->Lease->update($lease['Lease']['id']);
|
|
}
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: merge
|
|
* - Merges two customers into one
|
|
*/
|
|
|
|
function merge($dst_id, $src_id, $contacts, $primary_contact_id) {
|
|
$this->prEnter(compact('dst_id', 'src_id', 'contacts', 'primary_contact_id'));
|
|
|
|
$contacts = array_unique($contacts, SORT_NUMERIC);
|
|
if (!in_array($primary_contact_id, $contacts))
|
|
return $this->prReturn(false);
|
|
|
|
$all_contacts = array();
|
|
|
|
$result = $this->find('all',
|
|
array('link' => array('ContactsCustomer'),
|
|
'fields' => array('ContactsCustomer.contact_id', 'ContactsCustomer.type'),
|
|
'conditions' => array(array('id' => $src_id,
|
|
'ContactsCustomer.active' => true))));
|
|
$this->pr(17, compact('result'));
|
|
|
|
foreach ($result AS $contact) {
|
|
$this->pr(17, compact('contact'), "Add contact");
|
|
$all_contacts[$contact['ContactsCustomer']['contact_id']] = $contact['ContactsCustomer'];
|
|
}
|
|
|
|
$result = $this->find('all',
|
|
array('link' => array('ContactsCustomer'),
|
|
'fields' => array('ContactsCustomer.contact_id', 'ContactsCustomer.type'),
|
|
'conditions' => array(array('id' => $dst_id,
|
|
'ContactsCustomer.active' => true))));
|
|
/* array('link' => array('Contact'), */
|
|
/* 'fields' => array('Contact.id'), */
|
|
/* 'conditions' => array(array('Customer.id' => $src_id)))); */
|
|
$this->pr(17, compact('result'));
|
|
|
|
foreach ($result AS $contact) {
|
|
$this->pr(17, compact('contact'), "Add contact");
|
|
$all_contacts[$contact['ContactsCustomer']['contact_id']] = $contact['ContactsCustomer'];
|
|
}
|
|
|
|
$this->pr(17, compact('all_contacts'));
|
|
|
|
foreach ($contacts AS $cid) {
|
|
if (!array_key_exists($cid, $all_contacts))
|
|
return $this->prReturn(false);
|
|
}
|
|
|
|
$this->id = $dst_id;
|
|
$this->recursive = -1;
|
|
//$this->recursive = 1;
|
|
$this->read();
|
|
|
|
$this->pr(17, $this->data);
|
|
|
|
$this->data['Customer']['primary_contact_id'] = $primary_contact_id;
|
|
$this->data['Contact'] = array();
|
|
foreach ($contacts AS $contact_id)
|
|
$this->data['Contact'][] = array('id' => $contact_id,
|
|
'ContactsCustomer' =>
|
|
array('customer_id' => $dst_id) +
|
|
$all_contacts[$contact_id]);
|
|
|
|
/* $this->data['ContactsCustomer'] = array(); */
|
|
/* foreach ($contacts AS $contact_id) { */
|
|
/* $this->data['ContactsCustomer'][] = */
|
|
/* //array('ContactsCustomer' => */
|
|
/* array('customer_id' => $dst_id) + */
|
|
/* $all_contacts[$contact_id] */
|
|
/* //); */
|
|
/* } */
|
|
|
|
$this->pr(17, $this->data);
|
|
|
|
/* $this->ContactsCustomer->deleteAll */
|
|
/* (array('customer_id' => $id), false); */
|
|
|
|
//$this->recursive = 1;
|
|
if (!$this->saveAll($this->data, array('validate' => false))) {
|
|
return $this->prReturn(false);
|
|
}
|
|
|
|
// At this point, since we've saved data to customer,
|
|
// we'll proceed forward as much as possible, even
|
|
// if we encounter an error. For now, we'll assume
|
|
// the operation will succeed.
|
|
$ret = true;
|
|
|
|
$this->Lease->updateAll
|
|
(array('Lease.customer_id' => $dst_id),
|
|
array('Lease.customer_id' => $src_id)
|
|
);
|
|
|
|
$this->Tender->updateAll
|
|
(array('Tender.customer_id' => $dst_id),
|
|
array('Tender.customer_id' => $src_id)
|
|
);
|
|
|
|
$this->StatementEntry->updateAll
|
|
(array('StatementEntry.customer_id' => $dst_id),
|
|
array('StatementEntry.customer_id' => $src_id)
|
|
);
|
|
|
|
$this->Transaction->updateAll
|
|
(array('Transaction.customer_id' => $dst_id),
|
|
array('Transaction.customer_id' => $src_id)
|
|
);
|
|
|
|
// Make sure our lease counts, etc are correct
|
|
$this->update($dst_id);
|
|
|
|
$this->pr(12, compact('src_id'), "Delete Customer");
|
|
$this->delete($src_id);
|
|
|
|
foreach (array_diff_key($all_contacts, array_flip($contacts)) AS $contact_id) {
|
|
// Delete un-used or duplicate contacts
|
|
// REVISIT <AP> 20100716:
|
|
// Not sure if we really want to do this.
|
|
// On the one hand, they're probably really redundant,
|
|
// and only clutter up the list of all contacts. On the
|
|
// other hand, it destroys data, not only losing the
|
|
// history, but making it difficult to recover if the
|
|
// merge is a mistake.
|
|
// We need some sort of Contact.deleted field...
|
|
$this->pr(12, compact('contact_id'), "Delete Contact");
|
|
$this->Contact->delete($contact_id);
|
|
}
|
|
|
|
// Return the result
|
|
return $this->prReturn($ret);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: updateLeaseCount
|
|
* - Updates the internal lease count
|
|
*/
|
|
|
|
function updateLeaseCount($id) {
|
|
$this->id = $id;
|
|
|
|
$lease_count =
|
|
$this->find('count',
|
|
array('link' => array('Lease' => array('type' => 'INNER')),
|
|
'conditions' => array('Customer.id' => $id)));
|
|
$current_count =
|
|
$this->find('count',
|
|
array('link' => array('CurrentLease' => array('type' => 'INNER')),
|
|
'conditions' => array('Customer.id' => $id)));
|
|
|
|
$this->saveField('lease_count', $lease_count);
|
|
$this->saveField('current_lease_count', $current_count);
|
|
$this->saveField('past_lease_count', $lease_count - $current_count);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: balance
|
|
* - Returns the balance of money owed on the lease
|
|
*/
|
|
|
|
function balance($id) {
|
|
$stats = $this->stats($id);
|
|
return $stats['balance'];
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* function: stats
|
|
* - Returns summary data from the requested customer.
|
|
*/
|
|
|
|
function stats($id = null, $query = null) {
|
|
//$this->prFunctionLevel(20);
|
|
$this->prEnter(compact('id', 'query'));
|
|
if (!$id)
|
|
return $this->prExit(null);
|
|
|
|
$this->queryInit($query);
|
|
|
|
// REVISIT <AP>: 20090725
|
|
// We'll need to go directly to the statement entries if
|
|
// transactions are not always associated with the customer.
|
|
// This could happen if either we remove the customer_id
|
|
// field from Transaction, or we allow multiple customers
|
|
// to be part of the same transaction (essentially making
|
|
// the Transaction.customer_id meaningless).
|
|
|
|
/* $stats = $this->StatementEntry->find */
|
|
/* ('first', array */
|
|
/* ('contain' => false, */
|
|
/* 'fields' => $this->StatementEntry->chargeDisbursementFields(true), */
|
|
/* 'conditions' => array('StatementEntry.customer_id' => $id), */
|
|
/* )); */
|
|
|
|
$find_stats = $this->StatementEntry->find
|
|
('first', array
|
|
('contain' => false,
|
|
'fields' => $this->StatementEntry->chargeDisbursementFields(true),
|
|
'conditions' => array('StatementEntry.customer_id' => $id),
|
|
));
|
|
$find_stats = $find_stats[0];
|
|
$this->pr(17, compact('find_stats'));
|
|
|
|
$tquery = $query;
|
|
$tquery['conditions'][] = array('StatementEntry.customer_id' => $id);
|
|
$statement_stats = $this->StatementEntry->stats(null, $tquery);
|
|
$statement_stats['balance'] = $statement_stats['Charge']['balance'];
|
|
$this->pr(17, compact('statement_stats'));
|
|
|
|
$tquery = $query;
|
|
//$tquery['conditions'][] = array('StatementEntry.customer_id' => $id);
|
|
$tquery['conditions'][] = array('Transaction.customer_id' => $id);
|
|
$transaction_stats = $this->Transaction->stats(null, $tquery);
|
|
$transaction_stats += $transaction_stats['StatementEntry'];
|
|
$this->pr(17, compact('transaction_stats'));
|
|
|
|
$tquery = $query;
|
|
//$tquery['conditions'][] = array('StatementEntry.customer_id' => $id);
|
|
$tquery['conditions'][] = array('Transaction.customer_id' => $id);
|
|
$ar_transaction_stats = $this->Transaction->stats(null, $tquery,
|
|
$this->Transaction->Account->accountReceivableAccountID());
|
|
$ar_transaction_stats += $ar_transaction_stats['LedgerEntry'];
|
|
$this->pr(17, compact('ar_transaction_stats'));
|
|
|
|
//$stats = $ar_transaction_stats;
|
|
$stats = $find_stats;
|
|
return $this->prReturn($stats);
|
|
}
|
|
|
|
}
|
|
?>
|