Compare commits
34 Commits
statement_
...
alt_revers
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
011d1208e8 | ||
|
|
9dd09c4d2e | ||
|
|
2e36d46329 | ||
|
|
1dd0b14861 | ||
|
|
d75cd10f49 | ||
|
|
a69a56c715 | ||
|
|
8f7cf202e5 | ||
|
|
58c4f28956 | ||
|
|
f3eaa40ea5 | ||
|
|
e784931fa8 | ||
|
|
5a7b087ddc | ||
|
|
4d62d7da73 | ||
|
|
cca698d437 | ||
|
|
5247bb8db6 | ||
|
|
cb969ba340 | ||
|
|
094e15ddf9 | ||
|
|
670f0894ea | ||
|
|
9e20473b3b | ||
|
|
11d5deac85 | ||
|
|
67280c89e7 | ||
|
|
1afed6a6e0 | ||
|
|
0ff91bf4d8 | ||
|
|
4d4e96fe1c | ||
|
|
32a98b4b6e | ||
|
|
817b74b085 | ||
|
|
dc1bb56188 | ||
|
|
4707f3314d | ||
|
|
ac2b1530fc | ||
|
|
a5d3ff0b70 | ||
|
|
adc87c0763 | ||
|
|
7d81b9766b | ||
|
|
1aa6273ade | ||
|
|
2c08405d5a | ||
|
|
e2ed6ed1c7 |
@@ -644,6 +644,17 @@ CREATE TABLE `pmgr_customers` (
|
||||
-- contacts_customers table?
|
||||
`primary_contact_id` INT(10) UNSIGNED NOT NULL,
|
||||
|
||||
-- Number of different leases for this customer.
|
||||
-- It's not good to have redundant information,
|
||||
-- but these fields change infrequently, and make
|
||||
-- certain queries much easier, most notably for
|
||||
-- the grid query, in which linking customer to
|
||||
-- lease results in repeated statement entries
|
||||
-- when a customer has more than one lease.
|
||||
`lease_count` SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
`current_lease_count` SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
`past_lease_count` SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
@@ -844,8 +855,8 @@ CREATE TABLE `pmgr_accounts` (
|
||||
-- normal circumstances, when a debit occurs.
|
||||
-- `trackable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
|
||||
`deposits` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for deposits?
|
||||
`charges` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for charges?
|
||||
`payments` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for payments?
|
||||
`invoices` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for invoices?
|
||||
`receipts` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for receipts?
|
||||
`refunds` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for refunds?
|
||||
|
||||
-- Security Level
|
||||
@@ -873,20 +884,21 @@ INSERT INTO `pmgr_accounts` (`type`, `name`)
|
||||
-- us identify how serious the NSF situation is.
|
||||
('ASSET', 'NSF' ),
|
||||
('LIABILITY', 'A/P' );
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `payments`, `refunds`)
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `receipts`, `refunds`)
|
||||
VALUES
|
||||
('ASSET', 'Cash', 1, 1),
|
||||
('ASSET', 'Cash', 1, 0),
|
||||
('ASSET', 'Check', 1, 0),
|
||||
('ASSET', 'Money Order', 1, 0),
|
||||
('ASSET', 'ACH', 1, 0),
|
||||
('ASSET', 'Closing', 0, 0), -- REVISIT <AP>: Temporary
|
||||
('EXPENSE', 'Concession', 1, 0);
|
||||
('EXPENSE', 'Concession', 1, 0),
|
||||
('EXPENSE', 'Waiver', 0, 0);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `refunds`, `deposits`)
|
||||
VALUES
|
||||
-- REVISIT <AP>: 20090710 : We probably don't really want petty cash depositable.
|
||||
-- This is just for testing our deposit code
|
||||
('ASSET', 'Petty Cash', 1, 1);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `charges`)
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `invoices`)
|
||||
VALUES
|
||||
('LIABILITY', 'Tax', 1),
|
||||
('LIABILITY', 'Security Deposit', 1),
|
||||
@@ -949,14 +961,19 @@ DROP TABLE IF EXISTS `pmgr_transactions`;
|
||||
CREATE TABLE `pmgr_transactions` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
`type` ENUM('INVOICE',
|
||||
'RECEIPT',
|
||||
-- REVISIT <AP>: 20090804
|
||||
-- I'm not sure about most of these terms.
|
||||
-- Just as long as they're distinct though... I can rename them later
|
||||
`type` ENUM('INVOICE', -- Sales Invoice
|
||||
'RECEIPT', -- Actual receipt of monies
|
||||
'PURCHASE_INVOICE', -- Committment to pay
|
||||
'CREDIT_NOTE', -- Inverse of Sales Invoice
|
||||
'PAYMENT', -- Actual payment
|
||||
'DEPOSIT',
|
||||
'CLOSE',
|
||||
'CLOSE', -- Essentially an internal (not accounting) transaction
|
||||
-- 'CREDIT',
|
||||
-- 'REFUND',
|
||||
-- 'WAIVER',
|
||||
'TRANSFER')
|
||||
'TRANSFER') -- Unsure of TRANSFERs use
|
||||
NOT NULL,
|
||||
|
||||
`stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
@@ -981,10 +998,6 @@ CREATE TABLE `pmgr_transactions` (
|
||||
-- (e.g. A/R, Bank, etc)
|
||||
`account_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
`ledger_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
-- For convenience. Actually, INVOICE will always set crdr
|
||||
-- to DEBIT, RECEIPT will use CREDIT, and DEPOSIT will use
|
||||
-- DEBIT
|
||||
`crdr` ENUM('DEBIT',
|
||||
'CREDIT')
|
||||
DEFAULT NULL,
|
||||
@@ -1013,9 +1026,6 @@ CREATE TABLE `pmgr_ledger_entries` (
|
||||
-- The account/ledger of the entry
|
||||
`account_id` INT(10) UNSIGNED NOT NULL,
|
||||
`ledger_id` INT(10) UNSIGNED NOT NULL,
|
||||
|
||||
-- For convenience. Actually, CHARGE will always set crdr
|
||||
-- to CREDIT and PAYMENT will use DEBIT.
|
||||
`crdr` ENUM('DEBIT',
|
||||
'CREDIT')
|
||||
NOT NULL,
|
||||
@@ -1061,19 +1071,26 @@ DROP TABLE IF EXISTS `pmgr_statement_entries`;
|
||||
CREATE TABLE `pmgr_statement_entries` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
`type` ENUM('CHARGE',
|
||||
'PAYMENT',
|
||||
'SURPLUS',
|
||||
-- REVISIT <AP>: 20090804
|
||||
-- I'm not sure about most of these terms.
|
||||
-- Just as long as they're distinct though... I can rename them later
|
||||
`type` ENUM('CHARGE', -- Invoiced Charge to Customer
|
||||
'DISBURSEMENT', -- Disbursement of Receipt Funds
|
||||
'REVERSAL', -- Reversal of a charge
|
||||
'VOUCHER', -- Agreement to pay
|
||||
'PAYMENT', -- Payment of a Voucher
|
||||
'REFUND', -- Payment due to refund
|
||||
'SURPLUS', -- Surplus Receipt Funds
|
||||
'WAIVER', -- Waived Charge
|
||||
-- REVISIT <AP>: 20090730
|
||||
-- VOID is just to test out a theory
|
||||
-- for handling NSF and charge reversals
|
||||
'WAIVE',
|
||||
-- VOID is used for handling NSF and perhaps charge reversals.
|
||||
-- It's not clear this is the best way to handle these things.
|
||||
'VOID')
|
||||
NOT NULL,
|
||||
|
||||
`transaction_id` INT(10) UNSIGNED NOT NULL,
|
||||
|
||||
-- Effective date is when the charge/payment/transfer actually
|
||||
-- Effective date is when the charge/disbursement/transfer actually
|
||||
-- takes effect (since it may not be at the time of the transaction).
|
||||
-- Through date is used if/when a charge covers a certain time period,
|
||||
-- like rent. A security deposit, for example, would not use the
|
||||
@@ -1098,7 +1115,7 @@ CREATE TABLE `pmgr_statement_entries` (
|
||||
-- in the statement. Keeping it for now...
|
||||
`account_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
-- Allow the payment to reconcile against the charge
|
||||
-- Allow the disbursement to reconcile against the charge
|
||||
`charge_entry_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
@@ -50,19 +50,19 @@ Operations to be functional
|
||||
X - Assess NSF Fees
|
||||
X - Determine Lease Paid-Through status
|
||||
- Report: List of customers overdue
|
||||
- Flag unit as overlocked
|
||||
- Flag unit as evicting
|
||||
- Flag unit as normal status
|
||||
- Flag unit as dirty
|
||||
X - Flag unit as overlocked
|
||||
X - Flag unit as evicting
|
||||
X - Flag unit as normal status
|
||||
X - Flag unit as dirty
|
||||
- Enter notes when communicating with Customer
|
||||
X - Accept pre-payments
|
||||
X - Record Customer Move-Out from Unit
|
||||
? - Record utilization of Security Deposit
|
||||
- Record issuing of a refund
|
||||
X - Record utilization of Security Deposit
|
||||
X - Record issuing of a refund
|
||||
- Record Deposit into Petty Cash
|
||||
- Record Payment from Petty Cash to expenses
|
||||
- Record Petty Cash to refund.
|
||||
? - Write Off Bad Debt
|
||||
X - Record Petty Cash to refund.
|
||||
X - Write Off Bad Debt
|
||||
X - Perform a Deposit
|
||||
X - Close the Books (nightly / weekly, etc)
|
||||
X - Determine Rents Collected for a given period.
|
||||
|
||||
@@ -1308,18 +1308,13 @@ foreach $row (@{query($sdbh, $query)})
|
||||
# 'effective_date' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'effective_date'},
|
||||
# 'effective_date' => $effective_date;
|
||||
# 'through_date' => $through_date;
|
||||
'lease_id' => ($row->{'entry_type'} eq 'CREDIT'
|
||||
? 0
|
||||
: $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'}),
|
||||
'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'},
|
||||
'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'},
|
||||
'amount' => $reconcile_amount,
|
||||
|
||||
'account_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_account_id'},
|
||||
};
|
||||
|
||||
$row->{'ChargeID'} = undef
|
||||
if $row->{'entry_type'} eq 'CREDIT';
|
||||
|
||||
# Update the receipt & tender customer_id, now that we have payment info
|
||||
$newdb{'tables'}{'transactions'}{'rows'}[
|
||||
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'receipt_id'}
|
||||
@@ -1336,7 +1331,7 @@ foreach $row (@{query($sdbh, $query)})
|
||||
# Add the Payment Statement Entry
|
||||
addRow('statement_entries', {
|
||||
'transaction_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'receipt_id'},
|
||||
'type' => $row->{'entry_type'} || 'PAYMENT',
|
||||
'type' => 'DISBURSEMENT',
|
||||
# 'effective_date' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'effective_date'},
|
||||
# 'through_date' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'through_date'},
|
||||
'effective_date' => $effective_date,
|
||||
@@ -1471,6 +1466,25 @@ addRow('double_entries', {
|
||||
});
|
||||
|
||||
|
||||
######################################################################
|
||||
## Debug ... work from scratch
|
||||
if (defined $work_from_scratch) {
|
||||
# delete $newdb{'tables'}{'contacts'}{'rows'};
|
||||
# delete $newdb{'tables'}{'contacts_methods'}{'rows'};
|
||||
# delete $newdb{'tables'}{'contacts_addresses'}{'rows'};
|
||||
# delete $newdb{'tables'}{'contacts_emails'}{'rows'};
|
||||
# delete $newdb{'tables'}{'contacts_phones'}{'rows'};
|
||||
delete $newdb{'tables'}{'contacts_customers'}{'rows'};
|
||||
delete $newdb{'tables'}{'customers'}{'rows'};
|
||||
delete $newdb{'tables'}{'double_entries'}{'rows'};
|
||||
delete $newdb{'tables'}{'leases'}{'rows'};
|
||||
delete $newdb{'tables'}{'ledger_entries'}{'rows'};
|
||||
delete $newdb{'tables'}{'statement_entries'}{'rows'};
|
||||
delete $newdb{'tables'}{'tenders'}{'rows'};
|
||||
delete $newdb{'tables'}{'transactions'}{'rows'};
|
||||
}
|
||||
|
||||
|
||||
######################################################################
|
||||
## Build the Database
|
||||
|
||||
@@ -1530,6 +1544,21 @@ $query = "UPDATE pmgr_units U, pmgr_leases L
|
||||
WHERE L.unit_id = U.id AND L.close_date IS NULL";
|
||||
query($db_handle, $query);
|
||||
|
||||
# All current_lease_counts will be zero at the moment, since
|
||||
# everything was just created. This will update any customers
|
||||
# that have ever leased anything.
|
||||
$query = "UPDATE pmgr_customers C,
|
||||
(SELECT L.customer_id,
|
||||
SUM(IF(L.close_date IS NULL, 1, 0)) AS current,
|
||||
SUM(IF(L.close_date IS NULL, 0, 1)) AS closed
|
||||
FROM pmgr_leases L
|
||||
GROUP BY L.customer_id) AS X
|
||||
SET C.`lease_count` = X.current + X.closed,
|
||||
C.`current_lease_count` = X.current,
|
||||
C.`past_lease_count` = X.closed
|
||||
WHERE X.customer_id = C.id";
|
||||
query($db_handle, $query);
|
||||
|
||||
|
||||
######################################################################
|
||||
## Invoice/Receipt totals
|
||||
|
||||
@@ -47,6 +47,8 @@ class AppController extends Controller {
|
||||
array('name' => 'Customers', 'url' => array('controller' => 'customers', 'action' => 'index')),
|
||||
array('name' => 'Accounts', 'url' => array('controller' => 'accounts', 'action' => 'index')),
|
||||
array('name' => 'Debug', 'header' => true),
|
||||
array('name' => 'Un-Nuke', 'url' => '#', 'htmlAttributes' =>
|
||||
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')),
|
||||
@@ -187,17 +189,26 @@ class AppController extends Controller {
|
||||
// This SHOULD always be set, except when debugging
|
||||
if (isset($params['post']))
|
||||
$params['post'] = unserialize($params['post']);
|
||||
else
|
||||
$params['post'] = array();
|
||||
|
||||
// Unserialize our complex structure of dynamic post data
|
||||
if (isset($params['dynamic_post']))
|
||||
$params['dynamic_post'] = unserialize($params['dynamic_post']);
|
||||
else
|
||||
$params['dynamic_post'] = null;
|
||||
|
||||
// Unserialize our complex structure of dynamic post data
|
||||
if (isset($params['dynamic_post_replace']))
|
||||
$params['dynamic_post_replace'] = unserialize($params['dynamic_post_replace']);
|
||||
else
|
||||
$params['dynamic_post_replace'] = null;
|
||||
|
||||
// Merge the static and dynamic post data
|
||||
if (empty($params['post']) && !empty($params['dynamic_post']))
|
||||
$params['post'] = $params['dynamic_post'];
|
||||
elseif (!empty($params['post']) && !empty($params['dynamic_post']))
|
||||
//$params['post'] = array_merge($params['post'], $params['dynamic_post']);
|
||||
if (!empty($params['dynamic_post']))
|
||||
$params['post'] = array_merge_recursive($params['post'], $params['dynamic_post']);
|
||||
if (!empty($params['dynamic_post_replace']))
|
||||
$params['post'] = array_merge($params['post'], $params['dynamic_post_replace']);
|
||||
|
||||
// This SHOULD always be set, except when debugging
|
||||
if (!isset($params['post']['fields']))
|
||||
@@ -680,7 +691,7 @@ class AppController extends Controller {
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
// Don't create any links if ordered not to.
|
||||
if (isset($params['nolinks']))
|
||||
if (isset($params['post']['nolinks']))
|
||||
return;
|
||||
|
||||
foreach ($links AS $table => $fields) {
|
||||
|
||||
@@ -114,20 +114,48 @@ class AppModel extends Model {
|
||||
$line = $caller['line'];
|
||||
|
||||
// So, this caller holds the calling function name
|
||||
$caller = array_shift($trace);
|
||||
$caller = $trace[0];
|
||||
$function = $caller['function'];
|
||||
$class = $caller['class'];
|
||||
//$class = $this->name;
|
||||
|
||||
// Adjust the log level from default, if necessary
|
||||
// Use class or function specific log level if available
|
||||
if (isset($this->class_log_level[$class]))
|
||||
$log_level = $this->class_log_level[$class];
|
||||
if (isset($this->function_log_level["{$class}-{$function}"]))
|
||||
$log_level = $this->function_log_level["{$class}-{$function}"];
|
||||
if (isset($this->min_log_level))
|
||||
$log_level = max($log_level, $this->min_log_level);
|
||||
if (isset($this->max_log_level))
|
||||
$log_level = min($log_level, $this->max_log_level);
|
||||
|
||||
// Establish log level minimums
|
||||
$min_log_level = $this->min_log_level;
|
||||
if (is_array($this->min_log_level)) {
|
||||
$min_show_level = $min_log_level['show'];
|
||||
$min_log_level = $min_log_level['log'];
|
||||
}
|
||||
|
||||
// Establish log level maximums
|
||||
$max_log_level = $this->max_log_level;
|
||||
if (is_array($this->max_log_level)) {
|
||||
$max_show_level = $max_log_level['show'];
|
||||
$max_log_level = $max_log_level['log'];
|
||||
}
|
||||
|
||||
// Determine the applicable log and show levels
|
||||
if (is_array($log_level)) {
|
||||
$show_level = $log_level['show'];
|
||||
$log_level = $log_level['log'];
|
||||
}
|
||||
|
||||
// Adjust log level up/down to min/max
|
||||
if (isset($min_log_level))
|
||||
$log_level = max($log_level, $min_log_level);
|
||||
if (isset($max_log_level))
|
||||
$log_level = min($log_level, $max_log_level);
|
||||
|
||||
// Adjust show level up/down to min/max
|
||||
if (isset($min_show_level))
|
||||
$show_level = max($show_level, $min_show_level);
|
||||
if (isset($max_show_level))
|
||||
$show_level = min($show_level, $max_show_level);
|
||||
|
||||
// If the level is insufficient, bail out
|
||||
if ($level > $log_level)
|
||||
@@ -141,13 +169,87 @@ class AppModel extends Model {
|
||||
$mixed = $chk + array($mixed);
|
||||
}
|
||||
|
||||
echo '<DIV CLASS="pr-caller">';
|
||||
echo '<strong>' . substr(str_replace(ROOT, '', $file), 1) . '</strong>';
|
||||
echo ' (line <strong>' . $line . '</strong>)';
|
||||
echo ' : level-' . $level;
|
||||
echo '</DIV>' . "\n";
|
||||
static $pr_unique_number = 0;
|
||||
$pr_id = 'pr-section-class-' . $class . '-print-' . (++$pr_unique_number);
|
||||
$pr_trace_id = $pr_id . '-trace';
|
||||
$pr_output_id = $pr_id . '-output';
|
||||
|
||||
pr(array("{$class}::{$function}()" => $mixed), false, false);
|
||||
$pr_entire_base_class = "pr-section";
|
||||
$pr_entire_class_class = $pr_entire_base_class . '-class-' . $class;
|
||||
$pr_entire_function_class = $pr_entire_class_class . '-function-' . $function;
|
||||
$pr_entire_class = "$pr_entire_base_class $pr_entire_class_class $pr_entire_function_class";
|
||||
$pr_header_class = "pr-caller";
|
||||
$pr_trace_class = "pr-trace";
|
||||
$pr_output_base_class = 'pr-output';
|
||||
$pr_output_class_class = $pr_output_base_class . '-class-' . $class;
|
||||
$pr_output_function_class = $pr_output_class_class . '-function-' . $function;
|
||||
$pr_output_class = "$pr_output_base_class $pr_output_class_class $pr_output_function_class";
|
||||
|
||||
echo '<DIV class="'.$pr_entire_class.'" id="'.$pr_id.'">'."\n";
|
||||
echo '<DIV class="'.$pr_header_class.'">'."\n";
|
||||
echo '<DIV class="'.$pr_trace_class.'" id="'.$pr_trace_id.'" style="display:none;">'."\n";
|
||||
echo '<HR />' . "\n";
|
||||
|
||||
// Flip trace around so the sequence flows from top to bottom
|
||||
// Then print out the entire stack trace (in hidden div)
|
||||
$trace = array_reverse($trace);
|
||||
for ($i = 0; $i < count($trace); ++$i) {
|
||||
$bline = $trace[$i]['line'];
|
||||
$bfile = $trace[$i]['file'];
|
||||
$bfile = str_replace(ROOT.DS, '', $bfile);
|
||||
$bfile = str_replace(CAKE_CORE_INCLUDE_PATH.DS, '', $bfile);
|
||||
|
||||
if ($i > 0) {
|
||||
$bfunc = $trace[$i-1]['function'];
|
||||
$bclas = $trace[$i-1]['class'];
|
||||
} else {
|
||||
$bfunc = null;
|
||||
$bclas = null;
|
||||
}
|
||||
|
||||
echo("$bfile:$bline (" . ($bclas ? "$bclas::$bfunc" : "entry point") . ")<BR>\n");
|
||||
//echo(($bclas ? "$bclas::$bfunc" : "entry point") . "; $bfile : $bline<BR>\n");
|
||||
}
|
||||
echo '</DIV>' . "\n"; // End pr_trace_class
|
||||
$file = str_replace(ROOT.DS, '', $file);
|
||||
$file = str_replace(CAKE_CORE_INCLUDE_PATH.DS, '', $file);
|
||||
|
||||
echo "<strong>$file:$line ($class::$function)</strong>" . ";\n";
|
||||
/* $log_show_level = isset($show_level) ? $show_level : '?'; */
|
||||
/* echo ' L' . $level . "({$log_level}/{$log_show_level})" . ";\n"; */
|
||||
echo ' L' . $level . ";\n";
|
||||
echo ' <A HREF="#" onclick="$' . "('#{$pr_trace_id}').slideToggle(); return false;" . '">stack</A>'.";\n";
|
||||
|
||||
echo " this ";
|
||||
echo '<A HREF="#" onclick="$' . "('#{$pr_output_id}').slideToggle(); return false;" . '">t</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('#{$pr_id}').hide(); return false;" . '">n</A>'.";\n";
|
||||
|
||||
echo " $class ";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_class_class}').slideDown(); return false;" . '">s</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_class_class}').slideUp(); return false;" . '">h</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_entire_class_class}').hide(); return false;" . '">n</A>'.";\n";
|
||||
|
||||
echo " $function ";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_function_class}').slideDown(); return false;" . '">s</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_function_class}').slideUp(); return false;" . '">h</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_entire_function_class}').hide(); return false;" . '">n</A>'.";\n";
|
||||
|
||||
echo " all ";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_base_class}').show(); return false;" . '">s</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_base_class}').hide(); return false;" . '">h</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_entire_base_class}').hide(); return false;" . '">n</A>'."\n";
|
||||
|
||||
echo '</DIV>' . "\n"; // End pr_header_class
|
||||
|
||||
if (isset($show_level) && $level > $show_level)
|
||||
$display = 'none';
|
||||
else
|
||||
$display = 'block';
|
||||
|
||||
echo '<DIV class="'.$pr_output_class.'" id="'.$pr_output_id.'" style="display:'.$display.';">'."\n";
|
||||
pr($mixed, false, false);
|
||||
echo '</DIV>' . "\n"; // End pr_output_class
|
||||
echo '</DIV>' . "\n"; // End pr_entire_class
|
||||
}
|
||||
|
||||
function pr($level, $mixed, $checkpoint = null) {
|
||||
|
||||
@@ -31,6 +31,39 @@
|
||||
* You can also use this to include or require any files in your application.
|
||||
*
|
||||
*/
|
||||
|
||||
function INTERNAL_ERROR($message) {
|
||||
echo '<DIV class="internal-error" style="color:#000; background:#c22; padding:0.5em 1.5em 0.5em 1.5em;">';
|
||||
echo '<H1 style="margin-bottom:0.2em">INTERNAL ERROR:</H1>';
|
||||
echo '<H2 style="margin-top:0; margin-left:1.5em">' . $message . '</H2>';
|
||||
echo '<H4>This error was not caused by anything that you did wrong.';
|
||||
echo '<BR>It is a problem within the application itself and should be reported to the administrator.</H4>';
|
||||
|
||||
// Print out the entire stack trace
|
||||
echo "<HR>Stack Trace:<OL>";
|
||||
$trace = debug_backtrace(false);
|
||||
for ($i = 0; $i < count($trace); ++$i) {
|
||||
$bline = $trace[$i]['line'];
|
||||
$bfile = $trace[$i]['file'];
|
||||
$bfile = str_replace(ROOT.DS, '', $bfile);
|
||||
$bfile = str_replace(CAKE_CORE_INCLUDE_PATH.DS, '', $bfile);
|
||||
|
||||
if ($i < count($trace)-1) {
|
||||
$bfunc = $trace[$i+1]['function'];
|
||||
$bclas = $trace[$i+1]['class'];
|
||||
} else {
|
||||
$bfunc = null;
|
||||
$bclas = null;
|
||||
}
|
||||
|
||||
echo("<LI>$bfile:$bline (" . ($bclas ? "$bclas::$bfunc" : "entry point") . ")</LI>\n");
|
||||
}
|
||||
echo '</OL>';
|
||||
|
||||
echo '</DIV>';
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* The settings below can be used to set additional paths to models, views and controllers.
|
||||
* This is related to Ticket #470 (https://trac.cakephp.org/ticket/470)
|
||||
|
||||
@@ -138,17 +138,18 @@ class AccountsController extends AppController {
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
$payment_accounts = $this->Account->collectableAccounts();
|
||||
//$payment_accounts[$this->Account->nameToID('Closing')] = 'Closing';
|
||||
//$payment_accounts[$this->Account->nameToID('Equity')] = 'Equity';
|
||||
$default_accounts = array_diff_key($this->Account->paymentAccounts(),
|
||||
array($this->Account->concessionAccountID() => 1));
|
||||
$this->set(compact('payment_accounts', 'default_accounts'));
|
||||
|
||||
$this->Account->recursive = -1;
|
||||
$account = $this->Account->read(null, $id);
|
||||
$account = $account['Account'];
|
||||
|
||||
$payment_accounts = $this->Account->collectableAccounts();
|
||||
//$payment_accounts[$this->Account->nameToID('Closing')] = 'Closing';
|
||||
//$payment_accounts[$this->Account->nameToID('Equity')] = 'Equity';
|
||||
//$payment_accounts[$id] = 'Reversals';
|
||||
$default_accounts = array_diff_key($this->Account->receiptAccounts(),
|
||||
array($this->Account->concessionAccountID() => 1));
|
||||
$this->set(compact('payment_accounts', 'default_accounts'));
|
||||
|
||||
$title = ($account['name'] . ': Collected Report');
|
||||
$this->set(compact('account', 'title'));
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ class CustomersController extends AppController {
|
||||
('link' =>
|
||||
array(// Models
|
||||
'PrimaryContact',
|
||||
'CurrentLease' => array('fields' => array()),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -64,19 +63,19 @@ class CustomersController extends AppController {
|
||||
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
$fields[] = ('COUNT(DISTINCT CurrentLease.id) AS lease_count');
|
||||
return array_merge($fields,
|
||||
$this->Customer->StatementEntry->chargePaymentFields(true));
|
||||
$this->Customer->StatementEntry->chargeDisbursementFields(true));
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'current') {
|
||||
$conditions[] = 'CurrentLease.id IS NOT NULL';
|
||||
$conditions[] = array('Customer.current_lease_count >' => 0);
|
||||
}
|
||||
elseif ($params['action'] === 'past') {
|
||||
$conditions[] = 'CurrentLease.id IS NULL';
|
||||
$conditions[] = array('Customer.current_lease_count' => 0);
|
||||
$conditions[] = array('Customer.past_lease_count >' => 0);
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
@@ -99,30 +98,6 @@ class CustomersController extends AppController {
|
||||
return $order;
|
||||
}
|
||||
|
||||
function gridDataCount(&$params, &$model) {
|
||||
|
||||
if ($params['action'] != 'current')
|
||||
return parent::gridDataCount($params, $model);
|
||||
|
||||
// OK, for current customers, we have an issue.
|
||||
// We don't have a good way to use the query to obtain
|
||||
// our count. The problem is that we're relying on the
|
||||
// group by for the query, but that simply won't work
|
||||
// for the count. However, it's not difficult to simply
|
||||
// derive it since 'current' customers are mutually
|
||||
// exclusive to 'past' customers.
|
||||
|
||||
$tmp_params = $params;
|
||||
$tmp_params['action'] = 'all';
|
||||
$all_count = parent::gridDataCount($tmp_params, $model);
|
||||
$tmp_params['action'] = 'past';
|
||||
$past_count = parent::gridDataCount($tmp_params, $model);
|
||||
|
||||
// The current customer count is simply calculated
|
||||
// as all customers that are not past customers.
|
||||
return $all_count - $past_count;
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Customer'] = array('name');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
@@ -203,17 +178,32 @@ class CustomersController extends AppController {
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
/* //$result = $this->Customer->securityDeposits($id); */
|
||||
/* $result = $this->Customer->excessPayments($id); */
|
||||
/* //$result = $this->Customer->unreconciledCharges($id); */
|
||||
/* echo('<HR>'); */
|
||||
/* pr($result); */
|
||||
/* $this->autoRender = false; */
|
||||
/* return; */
|
||||
// Get details on this customer, its contacts and leases
|
||||
$customer = $this->Customer->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Contact' =>
|
||||
array('order' => array('Contact.display_name'),
|
||||
// Models
|
||||
'ContactPhone',
|
||||
'ContactEmail',
|
||||
'ContactAddress',
|
||||
),
|
||||
'Lease' =>
|
||||
array('Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
$customer = $this->Customer->details($id);
|
||||
'conditions' => array('Customer.id' => $id),
|
||||
));
|
||||
//pr($customer);
|
||||
$outstanding_balance = $customer['stats']['balance'];
|
||||
|
||||
// Figure out the outstanding balances for this customer
|
||||
$outstanding_balance = $this->Customer->balance($id);
|
||||
$outstanding_deposit = $this->Customer->securityDepositBalance($id);
|
||||
|
||||
// Figure out if this customer has any non-closed leases
|
||||
@@ -254,6 +244,11 @@ class CustomersController extends AppController {
|
||||
$id));
|
||||
}
|
||||
|
||||
if ($outstanding_balance < 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Issue Refund',
|
||||
'url' => array('action' => 'refund', $id));
|
||||
|
||||
// Prepare to render.
|
||||
$title = 'Customer: ' . $customer['Customer']['name'];
|
||||
$this->set(compact('customer', 'title',
|
||||
@@ -388,7 +383,7 @@ class CustomersController extends AppController {
|
||||
$default_type = $TT->defaultPaymentType();
|
||||
$this->set(compact('payment_types', 'default_type'));
|
||||
|
||||
$title = ($customer['name'] . ': Payment Entry');
|
||||
$title = ($customer['name'] . ': Receipt Entry');
|
||||
$this->set(compact('customer', 'charges', 'stats', 'title'));
|
||||
}
|
||||
|
||||
@@ -411,7 +406,7 @@ class CustomersController extends AppController {
|
||||
));
|
||||
pr(compact('entries'));
|
||||
|
||||
$this->Customer->StatementEntry->reverse($entries);
|
||||
$this->Customer->refund($entries);
|
||||
}
|
||||
|
||||
|
||||
@@ -428,8 +423,8 @@ class CustomersController extends AppController {
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
//Configure::write('debug', '0');
|
||||
//header("Content-type: text/xml;charset=utf-8");
|
||||
Configure::write('debug', '0');
|
||||
header("Content-type: text/xml;charset=utf-8");
|
||||
|
||||
App::import('Helper', 'Xml');
|
||||
$xml = new XmlHelper();
|
||||
@@ -437,16 +432,22 @@ class CustomersController extends AppController {
|
||||
// Find the unreconciled entries, then manipulate the structure
|
||||
// slightly to accomodate the format necessary for XML Helper.
|
||||
$unreconciled = $this->Customer->unreconciledCharges($id);
|
||||
|
||||
foreach ($unreconciled['entries'] AS &$entry)
|
||||
$entry = array_intersect_key($entry['StatementEntry'],
|
||||
array('id'=>1));
|
||||
|
||||
$unreconciled = array('entries' =>
|
||||
array_intersect_key($unreconciled['debit'],
|
||||
array('entry'=>1, 'balance'=>1)));
|
||||
array('entry' => $unreconciled['entries'],
|
||||
'balance' => $unreconciled['summary']['balance']));
|
||||
|
||||
// XML Helper will dump an empty tag if the array is empty
|
||||
if (!count($unreconciled['entries']['entry']))
|
||||
if (empty($unreconciled['entries']['entry']))
|
||||
unset($unreconciled['entries']['entry']);
|
||||
|
||||
//pr($unreconciled);
|
||||
//$reconciled = $cust->reconcileNewStatementEntry($cust_id, 'credit', $amount);
|
||||
/* pr(compact('unreconciled')); */
|
||||
/* echo htmlspecialchars($xml->serialize($unreconciled)); */
|
||||
/* $this->render('/fake'); */
|
||||
|
||||
$opts = array();
|
||||
//$opts['format'] = 'tags';
|
||||
|
||||
@@ -64,7 +64,7 @@ class LeasesController extends AppController {
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
return array_merge($fields,
|
||||
$this->Lease->StatementEntry->chargePaymentFields(true));
|
||||
$this->Lease->StatementEntry->chargeDisbursementFields(true));
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
@@ -172,8 +172,6 @@ class LeasesController extends AppController {
|
||||
);
|
||||
|
||||
$this->redirect($this->data['redirect']);
|
||||
$this->autoRender = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($id))
|
||||
@@ -213,118 +211,68 @@ class LeasesController extends AppController {
|
||||
}
|
||||
|
||||
|
||||
/* /\************************************************************************** */
|
||||
/* ************************************************************************** */
|
||||
/* ************************************************************************** */
|
||||
/* * action: promote_credit */
|
||||
/* * - Moves any lease credit up to the customer level, so that */
|
||||
/* * it may be used for charges other than those on this lease. */
|
||||
/* *\/ */
|
||||
|
||||
/* function promote_surplus($id) { */
|
||||
/* $this->Lease->promoteSurplus($id); */
|
||||
/* pr("PREVENTING REDIRECT"); */
|
||||
/* $this->render('fake'); */
|
||||
/* $this->redirect(array('controller' => 'leases', */
|
||||
/* 'action' => 'view', */
|
||||
/* $id)); */
|
||||
/* } */
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: apply_deposit
|
||||
* - Applies the security deposit to charges. This is much
|
||||
* like a receipt, but it's separated to keep it simple and
|
||||
* to prevent feature overload on the receipt page.
|
||||
* action: refund
|
||||
* - Provides lease customer with a refund
|
||||
*/
|
||||
|
||||
function apply_deposit($id = null) {
|
||||
// Create some models for convenience
|
||||
$A = new Account();
|
||||
|
||||
if ($this->data) {
|
||||
// Handle the move out based on the data given
|
||||
pr($this->data);
|
||||
$this->Lease->releaseSecurityDeposits($this->data['Lease']['id']);
|
||||
die();
|
||||
|
||||
// Assume this will succeed
|
||||
$ret = true;
|
||||
|
||||
// Go through the entered payments
|
||||
$receipt_transaction = array_intersect_key($this->data,
|
||||
array('Transaction'=>1,
|
||||
'transaction_id'=>1));
|
||||
foreach ($data['StatementEntry'] AS $entry) {
|
||||
// Create the receipt entry, and reconcile the credit side
|
||||
// of the double-entry (which should be A/R) as a payment.
|
||||
$ids = $this->StatementEntry->Ledger->Account->postLedgerEntry
|
||||
($receipt_transaction,
|
||||
array_intersect_key($entry, array('MonetarySource'=>1))
|
||||
+ array_intersect_key($entry, array('account_id'=>1)),
|
||||
array('debit_ledger_id' => $A->currentLedgerID($entry['account_id']),
|
||||
'credit_ledger_id' => $A->currentLedgerID($A->receiptAccountID()),
|
||||
'customer_id' => $customer_id,
|
||||
'lease_id' => $lease_id)
|
||||
+ $entry,
|
||||
'receipt');
|
||||
|
||||
if ($ids['error'])
|
||||
$ret = false;
|
||||
|
||||
$db = &$model->getDataSource();
|
||||
$fields = $db->fields($model, $model->alias);
|
||||
$fields[] = ("SUM(IF(Account.id IS NULL, 0," .
|
||||
" IF(LedgerEntry.debit_ledger_id = Account.id," .
|
||||
" 1, -1))" .
|
||||
" * IF(LedgerEntry.amount IS NULL, 0, LedgerEntry.amount))" .
|
||||
" AS 'balance'");
|
||||
return $fields;
|
||||
$receipt_transaction = array_intersect_key($ids,
|
||||
array('transaction_id'=>1,
|
||||
'split_transaction_id'=>1));
|
||||
}
|
||||
|
||||
$this->Lease->moveOut($this->data['Lease']['id'],
|
||||
'VACANT',
|
||||
$this->data['Lease']['moveout_date'],
|
||||
//true // Close this lease, if able
|
||||
false
|
||||
);
|
||||
|
||||
$this->redirect(array('controller' => 'leases',
|
||||
'action' => 'view',
|
||||
$this->data['Lease']['id']));
|
||||
$this->autoRender = false;
|
||||
return;
|
||||
}
|
||||
|
||||
$A = new Account();
|
||||
|
||||
function refund($id) {
|
||||
$lease = $this->Lease->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
'Unit' => array('fields' => array('id', 'name')),
|
||||
'Customer' => array('fields' => array('id', 'name')),
|
||||
),
|
||||
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
// Make sure lease is not closed...
|
||||
array('Lease.close_date' => null),
|
||||
),
|
||||
));
|
||||
if (empty($lease)) {
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
// Determine the lease balance, bailing if the customer owes money
|
||||
$balance = $this->Lease->balance($id);
|
||||
if ($balance >= 0) {
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
// Get the lease balance, part of lease stats
|
||||
$this->Lease->statsMerge($lease['Lease'],
|
||||
array('stats' => $this->Lease->stats($id)));
|
||||
// The refund will be for a positive amount
|
||||
$balance *= -1;
|
||||
|
||||
// Determine the lease security deposit
|
||||
$deposit_balance = $this->Lease->securityDeposits($lease['Lease']['id']);
|
||||
$this->set(compact('deposit_balance'));
|
||||
$this->set('customer', $lease['Customer']);
|
||||
$this->set('unit', $lease['Unit']);
|
||||
$this->set('lease', $lease['Lease']);
|
||||
$this->set('account', array('id' => $A->securityDepositAccountID()));
|
||||
|
||||
/* $redirect = array('controller' => 'leases', */
|
||||
/* 'action' => 'view', */
|
||||
/* $id); */
|
||||
// Get the accounts capable of paying the refund
|
||||
$refundAccounts = $this->Lease->StatementEntry->Account->refundAccounts();
|
||||
$defaultAccount = current($refundAccounts);
|
||||
$this->set(compact('refundAccounts', 'defaultAccount'));
|
||||
|
||||
// Prepare to render
|
||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||
$lease['Unit']['name'] . ': ' .
|
||||
$lease['Customer']['name'] . ': Utilize Security Deposit');
|
||||
$this->set(compact('title', 'redirect'));
|
||||
$lease['Customer']['name'] . ': Refund');
|
||||
$this->set(compact('title', 'lease', 'balance'));
|
||||
}
|
||||
|
||||
|
||||
@@ -332,82 +280,39 @@ class LeasesController extends AppController {
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: bad_debt
|
||||
* - Writes off remaining charges on a lease.
|
||||
* REVISIT <AP>: 20090710
|
||||
* Should this be a customer function? What customer
|
||||
* would have only one lease that results in bad debt.
|
||||
* - Sets up the write-off entry page, so that the
|
||||
* user can write off remaining charges on a lease.
|
||||
*/
|
||||
|
||||
function bad_debt($id) {
|
||||
$A = new Account();
|
||||
|
||||
$lease = $this->Lease->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
'Unit' => array('fields' => array('id', 'name')),
|
||||
'Customer' => array('fields' => array('id', 'name')),
|
||||
),
|
||||
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
// Make sure lease is not closed...
|
||||
array('Lease.close_date' => null),
|
||||
),
|
||||
));
|
||||
|
||||
// Make sure we have a valid lease to write off
|
||||
if (empty($lease))
|
||||
$this->redirect(array('action' => 'view', $id));
|
||||
|
||||
// Get the lease balance, part of lease stats
|
||||
$this->Lease->statsMerge($lease['Lease'],
|
||||
array('stats' => $this->Lease->stats($id)));
|
||||
|
||||
// Determine the lease security deposit
|
||||
$deposit_balance = $this->Lease->securityDepositBalance($lease['Lease']['id']);
|
||||
if ($deposit_balance > 0)
|
||||
die("Still have un-utilized security deposit");
|
||||
|
||||
$this->set('customer', $lease['Customer']);
|
||||
$this->set('unit', $lease['Unit']);
|
||||
$this->set('lease', $lease['Lease']);
|
||||
$this->set('account', array('id' => $A->badDebtAccountID()));
|
||||
|
||||
/* $redirect = array('controller' => 'leases', */
|
||||
/* 'action' => 'view', */
|
||||
/* $id); */
|
||||
$stats = $this->Lease->stats($id);
|
||||
$balance = $stats['balance'];
|
||||
|
||||
// Prepare to render
|
||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||
$lease['Unit']['name'] . ': ' .
|
||||
$lease['Customer']['name'] . ': Write Off Bad Debt');
|
||||
$this->set(compact('title', 'redirect'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: refund
|
||||
* - Provides user with a refund
|
||||
* REVISIT <AP>: 20090710
|
||||
* Should this be a customer function?
|
||||
*/
|
||||
|
||||
function refund($id) {
|
||||
/* // Obtain the overall lease balance */
|
||||
/* $stats = $this->Lease->stats($id); */
|
||||
/* $outstanding_balance = $stats['balance']; */
|
||||
|
||||
/* // Determine the lease security deposit */
|
||||
/* $deposits = $this->Lease->securityDeposits($id); */
|
||||
/* $outstanding_deposit = $deposits['summary']['balance']; */
|
||||
|
||||
|
||||
/* $this->set(compact('lease', 'title', */
|
||||
/* 'outstanding_deposit', */
|
||||
/* 'outstanding_balance')); */
|
||||
$this->set(compact('title', 'lease', 'balance'));
|
||||
$this->render('/transactions/bad_debt');
|
||||
}
|
||||
|
||||
|
||||
@@ -455,7 +360,7 @@ class LeasesController extends AppController {
|
||||
));
|
||||
|
||||
$A = new Account();
|
||||
$charge_accounts = $A->chargeAccounts();
|
||||
$charge_accounts = $A->invoiceAccounts();
|
||||
$default_account = $A->rentAccountID();
|
||||
$this->set(compact('charge_accounts', 'default_account'));
|
||||
|
||||
@@ -504,13 +409,9 @@ class LeasesController extends AppController {
|
||||
$this->set('charge_gaps', $this->Lease->rentChargeGaps($id));
|
||||
$this->set('charge_through', $this->Lease->rentChargeThrough($id));
|
||||
|
||||
// Obtain the overall lease balance
|
||||
$this->Lease->statsMerge($lease['Lease'],
|
||||
array('stats' => $this->Lease->stats($id)));
|
||||
$outstanding_balance = $lease['Lease']['stats']['balance'];
|
||||
|
||||
// Determine the lease security deposit
|
||||
$outstanding_deposit = $this->Lease->securityDepositBalance($lease['Lease']['id']);
|
||||
// Figure out the outstanding balances for this lease
|
||||
$outstanding_balance = $this->Lease->balance($id);
|
||||
$outstanding_deposit = $this->Lease->securityDepositBalance($id);
|
||||
|
||||
// Set up dynamic menu items
|
||||
if (!isset($lease['Lease']['close_date'])) {
|
||||
@@ -531,19 +432,17 @@ class LeasesController extends AppController {
|
||||
'action' => 'receipt',
|
||||
$lease['Customer']['id']));
|
||||
|
||||
if (isset($lease['Lease']['moveout_date']) && $outstanding_deposit > 0 && $outstanding_balance > 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Apply Deposit', 'url' => array('action' => 'apply_deposit',
|
||||
$id));
|
||||
/* if ($outstanding_balance < 0) */
|
||||
/* $this->sidemenu_links[] = */
|
||||
/* array('name' => 'Transfer Credit to Customer', */
|
||||
/* 'url' => array('action' => 'promote_surplus', $id)); */
|
||||
|
||||
if (isset($lease['Lease']['moveout_date']) &&
|
||||
$outstanding_balance <= 0 &&
|
||||
($outstanding_deposit - $outstanding_balance) > 0)
|
||||
if ($outstanding_balance < 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Issue Refund', 'url' => array('action' => 'refund',
|
||||
$id));
|
||||
array('name' => 'Issue Refund',
|
||||
'url' => array('action' => 'refund', $id));
|
||||
|
||||
if (isset($lease['Lease']['moveout_date']) && $outstanding_deposit == 0 && $outstanding_balance > 0)
|
||||
if (isset($lease['Lease']['moveout_date']) && $outstanding_balance > 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Write-Off', 'url' => array('action' => 'bad_debt',
|
||||
$id));
|
||||
|
||||
@@ -16,6 +16,16 @@ class LedgerEntriesController extends AppController {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: index / current / past / all
|
||||
* - Creates a list of ledger entries
|
||||
*/
|
||||
|
||||
function index() { $this->gridView('All Ledger Entries'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
|
||||
@@ -16,6 +16,16 @@ class StatementEntriesController extends AppController {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: index / current / past / all
|
||||
* - Creates a list of statement entries
|
||||
*/
|
||||
|
||||
function index() { $this->gridView('All Statement Entries'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -49,21 +59,21 @@ class StatementEntriesController extends AppController {
|
||||
);
|
||||
|
||||
if (isset($params['post']['custom']['statement_entry_id'])) {
|
||||
$link['PaymentEntry'] = array();
|
||||
$link['DisbursementEntry'] = array();
|
||||
$link['ChargeEntry'] = array();
|
||||
}
|
||||
|
||||
/* if ($params['action'] === 'collected') { */
|
||||
/* $link['PaymentEntry'] = array('Receipt' => array('class' => 'Transaction')); */
|
||||
/* $link['DisbursementEntry'] = array('Receipt' => array('class' => 'Transaction')); */
|
||||
/* $link['ChargeEntry'] = array('Invoice' => array('class' => 'Transaction')); */
|
||||
/* } */
|
||||
|
||||
/* if (count(array_intersect($params['fields'], array('applied'))) == 1) { */
|
||||
/* $link['PaymentEntry'] = array(); */
|
||||
/* $link['DisbursementEntry'] = array(); */
|
||||
/* $link['ChargeEntry'] = array(); */
|
||||
/* } */
|
||||
/* elseif (isset($params['post']['custom']['customer_id']) || isset($params['post']['custom']['lease_id'])) { */
|
||||
/* $link['PaymentEntry'] = array(); */
|
||||
/* $link['DisbursementEntry'] = array(); */
|
||||
/* } */
|
||||
|
||||
return array('link' => $link);
|
||||
@@ -74,18 +84,18 @@ class StatementEntriesController extends AppController {
|
||||
|
||||
if (in_array('applied', $params['post']['fields'])) {
|
||||
$fields[] = ("IF(StatementEntry.type = 'CHARGE'," .
|
||||
" SUM(COALESCE(PaymentEntry.amount,0))," .
|
||||
" SUM(COALESCE(DisbursementEntry.amount,0))," .
|
||||
" SUM(COALESCE(ChargeEntry.amount,0)))" .
|
||||
" AS 'applied'");
|
||||
$fields[] = ("StatementEntry.amount - (" .
|
||||
"IF(StatementEntry.type = 'CHARGE'," .
|
||||
" SUM(COALESCE(PaymentEntry.amount,0))," .
|
||||
" SUM(COALESCE(DisbursementEntry.amount,0))," .
|
||||
" SUM(COALESCE(ChargeEntry.amount,0)))" .
|
||||
") AS 'balance'");
|
||||
}
|
||||
|
||||
$fields = array_merge($fields,
|
||||
$this->StatementEntry->chargePaymentFields());
|
||||
$this->StatementEntry->chargeDisbursementFields());
|
||||
|
||||
return $fields;
|
||||
}
|
||||
@@ -110,7 +120,7 @@ class StatementEntriesController extends AppController {
|
||||
if (isset($statement_entry_id)) {
|
||||
$conditions[] = array('OR' =>
|
||||
array(array('ChargeEntry.id' => $statement_entry_id),
|
||||
array('PaymentEntry.id' => $statement_entry_id)));
|
||||
array('DisbursementEntry.id' => $statement_entry_id)));
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
@@ -144,13 +154,13 @@ class StatementEntriesController extends AppController {
|
||||
if (in_array('applied', $params['post']['fields'])) {
|
||||
$tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1));
|
||||
$tquery['fields'] = array("IF(StatementEntry.type = 'CHARGE'," .
|
||||
" SUM(COALESCE(PaymentEntry.amount,0))," .
|
||||
" SUM(COALESCE(DisbursementEntry.amount,0))," .
|
||||
" SUM(COALESCE(ChargeEntry.amount,0)))" .
|
||||
" AS 'applied'",
|
||||
|
||||
"StatementEntry.amount - (" .
|
||||
"IF(StatementEntry.type = 'CHARGE'," .
|
||||
" SUM(COALESCE(PaymentEntry.amount,0))," .
|
||||
" SUM(COALESCE(DisbursementEntry.amount,0))," .
|
||||
" SUM(COALESCE(ChargeEntry.amount,0)))" .
|
||||
") AS 'balance'",
|
||||
);
|
||||
@@ -179,6 +189,19 @@ class StatementEntriesController extends AppController {
|
||||
|
||||
function reverse($id) {
|
||||
$this->StatementEntry->reverse($id);
|
||||
$this->render('/FAKE');
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: waive the ledger entry
|
||||
*/
|
||||
|
||||
function waive($id) {
|
||||
$this->StatementEntry->waive($id);
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
@@ -200,7 +223,7 @@ class StatementEntriesController extends AppController {
|
||||
$entry = $this->StatementEntry->find
|
||||
('first',
|
||||
array('contain' => array
|
||||
('Transaction' => array('fields' => array('id', 'stamp')),
|
||||
('Transaction' => array('fields' => array('id', 'type', 'stamp')),
|
||||
'Account' => array('id', 'name', 'type'),
|
||||
'Customer' => array('fields' => array('id', 'name')),
|
||||
'Lease' => array('fields' => array('id')),
|
||||
@@ -211,50 +234,40 @@ class StatementEntriesController extends AppController {
|
||||
|
||||
$reconciled = $this->StatementEntry->reconciledEntries($id);
|
||||
|
||||
|
||||
/* // REVISIT <AP>: 20090711 */
|
||||
/* // It's not clear whether we should be able to reverse charges that have */
|
||||
/* // already been paid/cleared/reconciled. Certainly, that will be the */
|
||||
/* // case when someone has pre-paid and then moves out early. However, this */
|
||||
/* // will work well for items accidentally charged but not yet paid for. */
|
||||
/* if ((!$entry['DebitLedger']['Account']['trackable'] || */
|
||||
/* $stats['debit']['amount_reconciled'] == 0) && */
|
||||
/* (!$entry['CreditLedger']['Account']['trackable'] || */
|
||||
/* $stats['credit']['amount_reconciled'] == 0) */
|
||||
|
||||
/* && 0 */
|
||||
|
||||
/* ) */
|
||||
/* { */
|
||||
/* // Set up dynamic menu items */
|
||||
/* $this->sidemenu_links[] = */
|
||||
/* array('name' => 'Operations', 'header' => true); */
|
||||
|
||||
/* $this->sidemenu_links[] = */
|
||||
/* array('name' => 'Undo', */
|
||||
/* 'url' => array('action' => 'reverse', */
|
||||
/* $id)); */
|
||||
/* } */
|
||||
|
||||
/* if ($this->StatementEntry->Ledger->Account->type */
|
||||
/* ($entry['CreditLedger']['Account']['id']) == 'INCOME') */
|
||||
/* { */
|
||||
/* // Set up dynamic menu items */
|
||||
/* $this->sidemenu_links[] = */
|
||||
/* array('name' => 'Operations', 'header' => true); */
|
||||
|
||||
/* $this->sidemenu_links[] = */
|
||||
/* array('name' => 'Reverse', */
|
||||
/* 'url' => array('action' => 'reverse', */
|
||||
/* $id)); */
|
||||
/* } */
|
||||
|
||||
$stats = $this->StatementEntry->stats($id);
|
||||
|
||||
if (strtoupper($entry['StatementEntry']['type']) === 'CHARGE')
|
||||
if (in_array(strtoupper($entry['StatementEntry']['type']), $this->StatementEntry->debitTypes()))
|
||||
$stats = $stats['Charge'];
|
||||
else
|
||||
$stats = $stats['Payment'];
|
||||
$stats = $stats['Disbursement'];
|
||||
|
||||
|
||||
if (strtoupper($entry['StatementEntry']['type']) === 'CHARGE') {
|
||||
|
||||
$reversal = $this->StatementEntry->find
|
||||
('first',
|
||||
array('link' => array('DisbursementEntry'),
|
||||
'conditions' => array(array('StatementEntry.id' => $id),
|
||||
array('DisbursementEntry.type' => 'REVERSAL')),
|
||||
));
|
||||
|
||||
// Set up dynamic menu items
|
||||
if (empty($reversal) || $stats['balance'] > 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
if (empty($reversal))
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Reverse',
|
||||
'url' => array('action' => 'reverse',
|
||||
$id));
|
||||
|
||||
if ($stats['balance'] > 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Waive Balance',
|
||||
'url' => array('action' => 'waive',
|
||||
$id));
|
||||
}
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Statement Entry #{$entry['StatementEntry']['id']}";
|
||||
|
||||
@@ -114,7 +114,7 @@ class TransactionsController extends AppController {
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: postReceipt
|
||||
* - handles the creation of a payment receipt
|
||||
* - handles the creation of a receipt
|
||||
*/
|
||||
|
||||
function postReceipt() {
|
||||
@@ -261,6 +261,97 @@ class TransactionsController extends AppController {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: postWriteOff
|
||||
* - handles the write off of bad debt
|
||||
*/
|
||||
|
||||
function postWriteOff() {
|
||||
if (!$this->RequestHandler->isPost()) {
|
||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->data;
|
||||
$data['Entry'][0]['account_id'] =
|
||||
$this->Transaction->Account->badDebtAccountID();
|
||||
|
||||
if (empty($data['Customer']['id']))
|
||||
$data['Customer']['id'] = null;
|
||||
if (empty($data['Lease']['id']))
|
||||
$data['Lease']['id'] = null;
|
||||
|
||||
pr(compact('data'));
|
||||
|
||||
if (!$this->Transaction->addReceipt($data,
|
||||
$data['Customer']['id'],
|
||||
$data['Lease']['id'])) {
|
||||
$this->Session->setFlash("WRITE OFF FAILED", true);
|
||||
// REVISIT <AP> 20090706:
|
||||
// Until we can work out the session problems,
|
||||
// just die.
|
||||
die("<H1>WRITE-OFF FAILED</H1>");
|
||||
}
|
||||
|
||||
$this->render('/fake');
|
||||
|
||||
// Return to viewing the lease/customer
|
||||
if (empty($data['Lease']['id']))
|
||||
$this->redirect(array('controller' => 'customers',
|
||||
'action' => 'view',
|
||||
$data['Customer']['id']));
|
||||
else
|
||||
$this->redirect(array('controller' => 'leases',
|
||||
'action' => 'view',
|
||||
$data['Lease']['id']));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: postRefund
|
||||
* - handles issuing a customer refund
|
||||
*/
|
||||
|
||||
function postRefund() {
|
||||
if (!$this->RequestHandler->isPost()) {
|
||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->data;
|
||||
if (empty($data['Customer']['id']))
|
||||
$data['Customer']['id'] = null;
|
||||
if (empty($data['Lease']['id']))
|
||||
$data['Lease']['id'] = null;
|
||||
|
||||
if (!$this->Transaction->addRefund($data,
|
||||
$data['Customer']['id'],
|
||||
$data['Lease']['id'])) {
|
||||
$this->Session->setFlash("REFUND FAILED", true);
|
||||
// REVISIT <AP> 20090706:
|
||||
// Until we can work out the session problems,
|
||||
// just die.
|
||||
die("<H1>REFUND FAILED</H1>");
|
||||
}
|
||||
|
||||
$this->render('/fake');
|
||||
|
||||
// Return to viewing the lease/customer
|
||||
if (empty($data['Lease']['id']))
|
||||
$this->redirect(array('controller' => 'customers',
|
||||
'action' => 'view',
|
||||
$data['Customer']['id']));
|
||||
else
|
||||
$this->redirect(array('controller' => 'leases',
|
||||
'action' => 'view',
|
||||
$data['Lease']['id']));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
|
||||
@@ -82,7 +82,7 @@ class UnitsController extends AppController {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
|
||||
return array_merge($fields,
|
||||
$this->Unit->Lease->StatementEntry->chargePaymentFields(true));
|
||||
$this->Unit->Lease->StatementEntry->chargeDisbursementFields(true));
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
@@ -236,6 +236,10 @@ class UnitsController extends AppController {
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Edit', 'url' => array('action' => 'edit',
|
||||
$id));
|
||||
|
||||
if (isset($unit['CurrentLease']['id']) &&
|
||||
!isset($unit['CurrentLease']['moveout_date'])) {
|
||||
$this->sidemenu_links[] =
|
||||
@@ -249,6 +253,10 @@ 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']));
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Payment', 'url' => array('controller' => 'customers',
|
||||
'action' => 'receipt',
|
||||
@@ -261,4 +269,71 @@ class UnitsController extends AppController {
|
||||
'outstanding_balance',
|
||||
'outstanding_deposit'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: edit
|
||||
* - Edit unit information
|
||||
*/
|
||||
|
||||
function edit($id = null) {
|
||||
if (isset($this->data)) {
|
||||
// Check to see if the operation was cancelled.
|
||||
if (isset($this->params['form']['cancel'])) {
|
||||
if (empty($this->data['Unit']['id']))
|
||||
$this->redirect(array('action'=>'index'));
|
||||
|
||||
$this->redirect(array('action'=>'view', $this->data['Unit']['id']));
|
||||
}
|
||||
|
||||
// Make sure we have unit data
|
||||
if (empty($this->data['Unit']))
|
||||
$this->redirect(array('action'=>'index'));
|
||||
|
||||
// Make sure we have a rental rate
|
||||
if (empty($this->data['Unit']['rent']))
|
||||
$this->redirect(array('action'=>'view', $this->data['Unit']['id']));
|
||||
|
||||
// Save the unit and all associated data
|
||||
$this->Unit->create();
|
||||
$this->Unit->id = $this->data['Unit']['id'];
|
||||
if (!$this->Unit->save($this->data, false)) {
|
||||
$this->Session->setFlash("UNIT SAVE FAILED", true);
|
||||
pr("UNIT SAVE FAILED");
|
||||
}
|
||||
|
||||
$this->redirect(array('action'=>'view', $this->Unit->id));
|
||||
|
||||
// For debugging, only if the redirects above have been
|
||||
// commented out, otherwise this section isn't reached.
|
||||
$this->render('/fake');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
$this->data = $this->Unit->findById($id);
|
||||
$title = 'Unit ' . $this->data['Unit']['name'] . " : Edit";
|
||||
}
|
||||
else {
|
||||
$title = "Enter New Unit";
|
||||
$this->data = array();
|
||||
}
|
||||
|
||||
$statusEnums = $this->Unit->allowedStatusSet($id);
|
||||
$statusEnums = array_combine(array_keys($statusEnums),
|
||||
array_keys($statusEnums));
|
||||
$this->set(compact('statusEnums'));
|
||||
|
||||
$unit_sizes = $this->Unit->UnitSize->find
|
||||
('list', array('order' => array('unit_type_id', 'width', 'depth', 'id')));
|
||||
$this->set(compact('unit_sizes'));
|
||||
|
||||
// Prepare to render.
|
||||
pr($this->data);
|
||||
$this->set(compact('title'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -128,10 +128,12 @@ class Account extends AppModel {
|
||||
function nsfChargeAccountID() { return $this->nameToID('NSF Charge'); }
|
||||
function taxAccountID() { return $this->nameToID('Tax'); }
|
||||
function accountReceivableAccountID() { return $this->nameToID('A/R'); }
|
||||
function accountPayableAccountID() { return $this->nameToID('A/P'); }
|
||||
function cashAccountID() { return $this->nameToID('Cash'); }
|
||||
function checkAccountID() { return $this->nameToID('Check'); }
|
||||
function moneyOrderAccountID() { return $this->nameToID('Money Order'); }
|
||||
function concessionAccountID() { return $this->nameToID('Concession'); }
|
||||
function waiverAccountID() { return $this->nameToID('Waiver'); }
|
||||
function pettyCashAccountID() { return $this->nameToID('Petty Cash'); }
|
||||
function invoiceAccountID() { return $this->nameToID('Invoice'); }
|
||||
function receiptAccountID() { return $this->nameToID('Receipt'); }
|
||||
@@ -193,18 +195,22 @@ class Account extends AppModel {
|
||||
* - Returns an array of accounts suitable for activity xxx
|
||||
*/
|
||||
|
||||
function chargeAccounts() {
|
||||
return $this->relatedAccounts('charges', array('order' => 'name'));
|
||||
function invoiceAccounts() {
|
||||
return $this->relatedAccounts('invoices', array('order' => 'name'));
|
||||
}
|
||||
|
||||
function paymentAccounts() {
|
||||
return $this->relatedAccounts('payments', array('order' => 'name'));
|
||||
function receiptAccounts() {
|
||||
return $this->relatedAccounts('receipts', array('order' => 'name'));
|
||||
}
|
||||
|
||||
function depositAccounts() {
|
||||
return $this->relatedAccounts('deposits', array('order' => 'name'));
|
||||
}
|
||||
|
||||
function refundAccounts() {
|
||||
return $this->relatedAccounts('refunds', array('order' => 'name'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -214,7 +220,7 @@ class Account extends AppModel {
|
||||
*/
|
||||
|
||||
function collectableAccounts() {
|
||||
$accounts = $this->paymentAccounts();
|
||||
$accounts = $this->receiptAccounts();
|
||||
|
||||
foreach(array($this->nsfAccountID(),
|
||||
$this->securityDepositAccountID())
|
||||
|
||||
@@ -81,12 +81,7 @@ class Customer extends AppModel {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
|
||||
if (!isset($query['link']['Customer']))
|
||||
$query['link']['Customer'] = array();
|
||||
if (!isset($query['link']['Customer']['fields']))
|
||||
$query['link']['Customer']['fields'] = array();
|
||||
|
||||
$query['conditions'][] = array('Customer.id' => $id);
|
||||
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$query['conditions'][] = array('StatementEntry.account_id' =>
|
||||
$this->StatementEntry->Account->securityDepositAccountID());
|
||||
|
||||
@@ -106,18 +101,12 @@ class Customer extends AppModel {
|
||||
$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.customer_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);
|
||||
return $this->prReturn($stats['account_balance']);
|
||||
}
|
||||
|
||||
|
||||
@@ -132,92 +121,12 @@ class Customer extends AppModel {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
|
||||
if (!isset($query['link']['Customer']))
|
||||
$query['link']['Customer'] = array();
|
||||
if (!isset($query['link']['Customer']['fields']))
|
||||
$query['link']['Customer']['fields'] = array();
|
||||
/* if (!isset($query['link']['StatementEntry'])) */
|
||||
/* $query['link']['StatementEntry'] = array(); */
|
||||
/* if (!isset($query['link']['StatementEntry']['Customer'])) */
|
||||
/* $query['link']['StatementEntry']['Customer'] = array(); */
|
||||
/* if (!isset($query['link']['StatementEntry']['Customer']['fields'])) */
|
||||
/* $query['link']['StatementEntry']['Customer']['fields'] = array(); */
|
||||
|
||||
$query['conditions'][] = array('Customer.id' => $id);
|
||||
|
||||
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, true);
|
||||
return $this->prReturn($set);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: excessPayments
|
||||
* - Returns payments which have not yet been fully utilized
|
||||
*/
|
||||
|
||||
function excessPayments($id, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
|
||||
if (!isset($query['link']['StatementEntry']))
|
||||
$query['link']['StatementEntry'] = array();
|
||||
/* if (!isset($query['link']['StatementEntry']['Customer'])) */
|
||||
/* $query['link']['StatementEntry']['Customer'] = array(); */
|
||||
/* if (!isset($query['link']['StatementEntry']['Customer']['fields'])) */
|
||||
/* $query['link']['StatementEntry']['Customer']['fields'] = array(); */
|
||||
|
||||
$query['conditions'][] = array('Customer.id' => $id);
|
||||
|
||||
$set = $this->StatementEntry->reconciledSet('PAYMENT', $query, true);
|
||||
return $this->prReturn($set);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: details
|
||||
* - Returns detail information for the customer
|
||||
*/
|
||||
|
||||
function details($id = null) {
|
||||
$this->prEnter(compact('id'));
|
||||
|
||||
// Query the DB for need information.
|
||||
$customer = $this->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Contact' =>
|
||||
array('order' => array('Contact.display_name'),
|
||||
// Models
|
||||
'ContactPhone',
|
||||
'ContactEmail',
|
||||
'ContactAddress',
|
||||
),
|
||||
'Lease' =>
|
||||
array('Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array('Customer.id' => $id),
|
||||
));
|
||||
|
||||
// Figure out the outstanding balance for this customer
|
||||
$customer['stats'] = $this->stats($id);
|
||||
|
||||
// Figure out the total security deposit for the current lease.
|
||||
$customer['deposits'] = $this->securityDeposits($id);
|
||||
|
||||
return $this->prReturn($customer);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -294,6 +203,44 @@ class Customer extends AppModel {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -320,14 +267,14 @@ class Customer extends AppModel {
|
||||
/* $stats = $this->StatementEntry->find */
|
||||
/* ('first', array */
|
||||
/* ('contain' => false, */
|
||||
/* 'fields' => $this->StatementEntry->chargePaymentFields(true), */
|
||||
/* 'fields' => $this->StatementEntry->chargeDisbursementFields(true), */
|
||||
/* 'conditions' => array('StatementEntry.customer_id' => $id), */
|
||||
/* )); */
|
||||
|
||||
$find_stats = $this->StatementEntry->find
|
||||
('first', array
|
||||
('contain' => false,
|
||||
'fields' => $this->StatementEntry->chargePaymentFields(true),
|
||||
'fields' => $this->StatementEntry->chargeDisbursementFields(true),
|
||||
'conditions' => array('StatementEntry.customer_id' => $id),
|
||||
));
|
||||
$find_stats = $find_stats[0];
|
||||
|
||||
@@ -12,6 +12,7 @@ class Lease extends AppModel {
|
||||
'StatementEntry',
|
||||
);
|
||||
|
||||
//var $default_log_level = array('log' => 30, 'show' => 15);
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -23,21 +24,11 @@ class Lease extends AppModel {
|
||||
$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.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);
|
||||
}
|
||||
|
||||
@@ -53,18 +44,26 @@ class Lease extends AppModel {
|
||||
$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.lease_id' => $id);
|
||||
$query['conditions'][] = array('StatementEntry.account_id' =>
|
||||
$this->StatementEntry->Account->securityDepositAccountID());
|
||||
|
||||
// REVISIT <AP>: 20090804
|
||||
// Dilemma... how to handle security deposits used to pay
|
||||
// charges on the lease, yet are not part of the lease
|
||||
// security deposit(s)? For example, Lease A & Lease B,
|
||||
// each with $25 Sec. Dep. Move out of Lease A, and
|
||||
// promote the lease surplus to the customer. A new
|
||||
// charge on Lease B for $15, which is assigned $15/$25
|
||||
// from the promoted Lease A surplus. They way this
|
||||
// function works at present, it will presume the $15 is
|
||||
// part of the security deposit balance, and will end up
|
||||
// calculating it as only $10, which is wrong. Perhaps
|
||||
// the fix is to release security deposits into some sort
|
||||
// of income account.
|
||||
|
||||
$stats = $this->StatementEntry->stats(null, $query);
|
||||
$balance = $stats['Charge']['reconciled'] - $stats['Payment']['reconciled'];
|
||||
return $this->prReturn($balance);
|
||||
return $this->prReturn($stats['account_balance']);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,21 +72,18 @@ class Lease extends AppModel {
|
||||
**************************************************************************
|
||||
* function: releaseSecurityDeposits
|
||||
* - Releases all security deposits associated with this lease.
|
||||
* That simply makes a payment out of them, which can be used
|
||||
* That simply makes a disbursement 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'));
|
||||
function releaseSecurityDeposits($id, $stamp = null, $query = null) {
|
||||
//$this->prFunctionLevel(30);
|
||||
$this->prEnter(compact('id', 'stamp', 'query'));
|
||||
|
||||
$secdeps = $this->securityDeposits($id, $query);
|
||||
$secdeps = $secdeps['entries'];
|
||||
$this->pr(20, compact('secdeps'));
|
||||
|
||||
$this->securityDepositBalance($id, $query);
|
||||
die();
|
||||
|
||||
// If there are no paid security deposits, then
|
||||
// we can consider all security deposits released.
|
||||
if (count($secdeps) == 0)
|
||||
@@ -95,23 +91,17 @@ class Lease extends AppModel {
|
||||
|
||||
// Build a transaction
|
||||
$release = array('Transaction' => array(), 'Entry' => array());
|
||||
$release['Transaction']['stamp'] = $stamp;
|
||||
$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
|
||||
// we're reducing any oustanding amount on a security deposit
|
||||
// since we no longer expect it to be owed.
|
||||
// REVISIT <AP>: 20090730
|
||||
// This is kludgy, and I really don't like it. However, this
|
||||
// is not presently something that even happens at the moment,
|
||||
// so this solution will have to work until we come up with
|
||||
// something more robust, like flagging those charges as defunct.
|
||||
if ($charge['StatementEntry']['balance'] > 0) {
|
||||
$this->StatementEntry->id = $charge['StatementEntry']['id'];
|
||||
$this->StatementEntry->saveField('amount', $charge['StatementEntry']['reconciled']);
|
||||
}
|
||||
// any unpaid (or only partially paid) security deposit should
|
||||
// have the remaining balance reversed.
|
||||
if ($charge['StatementEntry']['balance'] > 0)
|
||||
$this->StatementEntry->reverse($charge['StatementEntry']['id'], true, $stamp);
|
||||
|
||||
$release['Entry'][] =
|
||||
array('amount' => $charge['StatementEntry']['reconciled'],
|
||||
@@ -121,7 +111,7 @@ class Lease extends AppModel {
|
||||
}
|
||||
|
||||
$customer_id = $secdeps[0]['StatementEntry']['customer_id'];
|
||||
$lease_id = $secdeps[0]['StatementEntry']['lease_id'];
|
||||
$lease_id = $secdeps[0]['StatementEntry']['lease_id'];
|
||||
|
||||
$result = $this->StatementEntry->Transaction->addReceipt
|
||||
($release, $customer_id, $lease_id);
|
||||
@@ -345,7 +335,10 @@ class Lease extends AppModel {
|
||||
// Set the lease number to be the same as the lease ID
|
||||
$this->id;
|
||||
$this->saveField('number', $this->id);
|
||||
|
||||
|
||||
// Update the current lease count for the customer
|
||||
$this->Customer->updateLeaseCount($customer_id);
|
||||
|
||||
// Update the unit status
|
||||
$this->Unit->updateStatus($unit_id, 'OCCUPIED');
|
||||
|
||||
@@ -386,10 +379,16 @@ class Lease extends AppModel {
|
||||
// Save it!
|
||||
$this->save($this->data, false);
|
||||
|
||||
// Release the security deposit(s)
|
||||
$this->releaseSecurityDeposits($id, $stamp);
|
||||
|
||||
// Close the lease, if so requested
|
||||
if ($close)
|
||||
$this->close($id, $stamp);
|
||||
|
||||
// Update the current lease count for the customer
|
||||
$this->Customer->updateLeaseCount($this->field('customer_id'));
|
||||
|
||||
// Finally, update the unit status
|
||||
$this->recursive = -1;
|
||||
$this->read();
|
||||
@@ -423,6 +422,10 @@ class Lease extends AppModel {
|
||||
|
||||
// Save it!
|
||||
$this->save($this->data, false);
|
||||
|
||||
// Update the current lease count for the customer
|
||||
$this->Customer->updateLeaseCount($this->field('customer_id'));
|
||||
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
@@ -464,12 +467,45 @@ class Lease extends AppModel {
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addCharge
|
||||
* - Adds an additional charge to the lease
|
||||
* function: refund
|
||||
* - Marks any lease balance as payable to the customer.
|
||||
*/
|
||||
|
||||
function addCharge($id, $charge) {
|
||||
|
||||
function refund($id, $stamp = null) {
|
||||
$this->prEnter(compact('id'));
|
||||
$balance = $this->balance($id);
|
||||
|
||||
if ($balance >= 0)
|
||||
return $this->prReturn(array('error' => true));
|
||||
|
||||
$balance *= -1;
|
||||
|
||||
// Build a transaction
|
||||
$refund = array('Transaction' => array(), 'Entry' => array());
|
||||
$refund['Transaction']['stamp'] = $stamp;
|
||||
$refund['Transaction']['comment'] = "Lease Refund";
|
||||
|
||||
$refund['Entry'][] =
|
||||
array('amount' => $balance);
|
||||
|
||||
$result = $this->StatementEntry->Transaction->addRefund
|
||||
($refund, null, $id);
|
||||
|
||||
return $this->prReturn($result);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: balance
|
||||
* - Returns the balance of money owed on the lease
|
||||
*/
|
||||
|
||||
function balance($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
$stats = $this->stats($id);
|
||||
return $this->prReturn($stats['balance']);
|
||||
}
|
||||
|
||||
|
||||
@@ -485,42 +521,14 @@ class Lease extends AppModel {
|
||||
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);
|
||||
$find_stats = $this->StatementEntry->find
|
||||
('first', array
|
||||
('contain' => false,
|
||||
'fields' => $this->StatementEntry->chargeDisbursementFields(true),
|
||||
'conditions' => array('StatementEntry.lease_id' => $id),
|
||||
));
|
||||
$find_stats = $find_stats[0];
|
||||
return $this->prReturn($find_stats);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ class LedgerEntry extends AppModel {
|
||||
|
||||
//pr(array('stats()', compact('id', 'query', 'set')));
|
||||
|
||||
$rtypes = array('charge', 'payment',
|
||||
$rtypes = array('charge', 'disbursement',
|
||||
// 'debit', 'credit',
|
||||
);
|
||||
|
||||
@@ -178,10 +178,8 @@ class LedgerEntry extends AppModel {
|
||||
foreach($rtypes AS $rtype) {
|
||||
$Rtype = ucfirst($rtype);
|
||||
|
||||
if (($rtype == 'charge' && (!isset($set) || $set == 'PAYMENT')) ||
|
||||
($rtype == 'payment' && (!isset($set) || $set == 'CHARGE'))
|
||||
/* ($rtype == 'debit' && (!isset($set) || $set == 'CREDIT')) || */
|
||||
/* ($rtype == 'credit' && (!isset($set) || $set == 'DEBIT')) */
|
||||
if (($rtype == 'charge' && (!isset($set) || $set == 'DISBURSEMENT')) ||
|
||||
($rtype == 'disbursement' && (!isset($set) || $set == 'CHARGE'))
|
||||
) {
|
||||
|
||||
$rquery = $query;
|
||||
|
||||
@@ -7,46 +7,79 @@ class StatementEntry extends AppModel {
|
||||
'Lease',
|
||||
'Account',
|
||||
|
||||
// The charge to which this payment applies (if it is one)
|
||||
// The charge to which this disbursement applies (if it is one)
|
||||
'ChargeEntry' => array(
|
||||
'className' => 'StatementEntry',
|
||||
),
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
// The payments that apply to this charge (if it is one)
|
||||
'PaymentEntry' => array(
|
||||
// The disbursements that apply to this charge (if it is one)
|
||||
'DisbursementEntry' => array(
|
||||
'className' => 'StatementEntry',
|
||||
'foreignKey' => 'charge_entry_id',
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
//var $default_log_level = 30;
|
||||
var $default_log_level = array('log' => 30, 'show' => 15);
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: chargePaymentFields
|
||||
* function: debit/creditTypes
|
||||
*/
|
||||
|
||||
function debitTypes() {
|
||||
return array('CHARGE', 'PAYMENT', 'REFUND');
|
||||
}
|
||||
|
||||
function creditTypes() {
|
||||
return array('DISBURSEMENT', 'WAIVER', 'REVERSAL', 'SURPLUS');
|
||||
}
|
||||
|
||||
function voidTypes() {
|
||||
return array('VOID');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: chargeDisbursementFields
|
||||
*/
|
||||
|
||||
function chargeDisbursementFields($sum = false, $entry_name = 'StatementEntry') {
|
||||
$debits = $this->debitTypes();
|
||||
$credits = $this->creditTypes();
|
||||
$voids = $this->voidTypes();
|
||||
|
||||
foreach ($debits AS &$enum)
|
||||
$enum = "'" . $enum . "'";
|
||||
foreach ($credits AS &$enum)
|
||||
$enum = "'" . $enum . "'";
|
||||
foreach ($voids AS &$enum)
|
||||
$enum = "'" . $enum . "'";
|
||||
|
||||
$debit_set = implode(", ", $debits);
|
||||
$credit_set = implode(", ", $credits);
|
||||
$void_set = implode(", ", $voids);
|
||||
|
||||
function chargePaymentFields($sum = false, $entry_name = 'StatementEntry') {
|
||||
$fields = array
|
||||
(
|
||||
($sum ? 'SUM(' : '') .
|
||||
"IF({$entry_name}.type = 'CHARGE'," .
|
||||
"IF({$entry_name}.type IN ({$debit_set})," .
|
||||
" {$entry_name}.amount, NULL)" .
|
||||
($sum ? ')' : '') . ' AS charge' . ($sum ? 's' : ''),
|
||||
|
||||
($sum ? 'SUM(' : '') .
|
||||
"IF({$entry_name}.type = 'PAYMENT' OR {$entry_name}.type = 'SURPLUS'," .
|
||||
"IF({$entry_name}.type IN({$credit_set})," .
|
||||
" {$entry_name}.amount, NULL)" .
|
||||
($sum ? ')' : '') . ' AS payment' . ($sum ? 's' : ''),
|
||||
($sum ? ')' : '') . ' AS disbursement' . ($sum ? 's' : ''),
|
||||
|
||||
($sum ? 'SUM(' : '') .
|
||||
"IF({$entry_name}.type = 'CHARGE', 1," .
|
||||
" IF({$entry_name}.type = 'PAYMENT' OR {$entry_name}.type = 'SURPLUS', -1, 0))" .
|
||||
"IF({$entry_name}.type IN ({$debit_set}), 1," .
|
||||
" IF({$entry_name}.type IN ({$credit_set}), -1, 0))" .
|
||||
" * IF({$entry_name}.amount, {$entry_name}.amount, 0)" .
|
||||
($sum ? ')' : '') . ' AS balance',
|
||||
);
|
||||
@@ -106,116 +139,128 @@ class StatementEntry extends AppModel {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: waive
|
||||
* - Waives the charge balance
|
||||
*
|
||||
*/
|
||||
function waive($id, $stamp = null) {
|
||||
$this->prEnter(compact('id', 'stamp'));
|
||||
|
||||
// Get the basic information about the entry to be waived.
|
||||
$this->recursive = -1;
|
||||
$charge = $this->read(null, $id);
|
||||
$charge = $charge['StatementEntry'];
|
||||
|
||||
if ($charge['type'] !== 'CHARGE')
|
||||
INTERNAL_ERROR("Waiver item is not CHARGE.");
|
||||
|
||||
// Query the stats to get the remaining balance
|
||||
$stats = $this->stats($id);
|
||||
|
||||
// Build a transaction
|
||||
$waiver = array('Transaction' => array(), 'Entry' => array());
|
||||
$waiver['Transaction']['stamp'] = $stamp;
|
||||
$waiver['Transaction']['comment'] = "Charge Waiver";
|
||||
|
||||
// Add the charge waiver
|
||||
$waiver['Entry'][] =
|
||||
array('amount' => $stats['Charge']['balance'],
|
||||
'account_id' => $this->Account->waiverAccountID(),
|
||||
'comment' => null,
|
||||
);
|
||||
|
||||
// Record the waiver transaction
|
||||
return $this->prReturn($this->Transaction->addWaiver
|
||||
($waiver, $id, $charge['customer_id'], $charge['lease_id']));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reverse
|
||||
* - Reverses the charges
|
||||
*
|
||||
* SAMPLE MOVE IN w/ PRE PAYMENT
|
||||
* DEPOSIT RENT A/R RECEIPT CHECK PETTY BANK
|
||||
* ------- ------- ------- ------- ------- ------- -------
|
||||
* |25 | 25| | | | |
|
||||
* | |20 20| | | | |
|
||||
* | |20 20| | | | |
|
||||
* | |20 20| | | | |
|
||||
* | | |25 25| | | |
|
||||
* | | |20 20| | | |
|
||||
* | | |20 20| | | |
|
||||
* | | |20 20| | | |
|
||||
* | | | |85 85| | |
|
||||
* | | | | |85 | 85|
|
||||
|
||||
* MOVE OUT and REFUND FINAL MONTH
|
||||
* DEPOSIT RENT C/P RECEIPT CHECK PETTY BANK
|
||||
* ------- ------- ------- ------- ------- ------- -------
|
||||
* 25| | |25 | | | | t20 e20a
|
||||
* | 20| |20 | | | | t20 e20b
|
||||
|
||||
* -ONE REFUND CHECK-
|
||||
* | | 25| |25 | | | t30 e30a
|
||||
* | | 20| |20 | | | t30 e30b
|
||||
* | | | 45| | | |45 t40 e40
|
||||
* -OR MULTIPLE-
|
||||
* | | 15| |15 | | | t50a e50a
|
||||
* | | | 15| | |15 | t60a e60a
|
||||
* | | 30| |30 | | | t50b e50b
|
||||
* | | | 30| | | |30 t60b e60b
|
||||
* | | | | | | |
|
||||
|
||||
|
||||
OPTION 1
|
||||
* |-25 | -25| | | | |
|
||||
* | |-20 -20| | | | |
|
||||
* | | |-25 -25| | | |
|
||||
* | | |-20 -20| | | |
|
||||
|
||||
OPTION 2
|
||||
* |-25 | | -25| | | |
|
||||
* | |-20 | -20| | | |
|
||||
* | | | |-15 | -15| |
|
||||
* | | | |-30 | | -30|
|
||||
* | | | | | | |
|
||||
*
|
||||
*/
|
||||
function reverse($ledger_entries, $stamp = null) {
|
||||
$this->prEnter(compact('ledger_entries', 'stamp'));
|
||||
function reverse($id, $balance = false, $stamp = null) {
|
||||
$this->prEnter(compact('id', 'balance', 'stamp'));
|
||||
|
||||
// If the user only wants to reverse one ID, we'll allow it
|
||||
if (!is_array($ledger_entries))
|
||||
$ledger_entries = $this->find
|
||||
('all', array
|
||||
('contain' => false,
|
||||
'conditions' => array('Entry.id' => $ledger_entries)));
|
||||
$ret = array();
|
||||
|
||||
$A = new Account();
|
||||
// Get the basic information about the entry to be reversed.
|
||||
$this->recursive = -1;
|
||||
$charge = $this->read(null, $id);
|
||||
$charge = $charge['StatementEntry'];
|
||||
|
||||
$ar_account_id = $A->accountReceivableAccountID();
|
||||
$receipt_account_id = $A->receiptAccountID();
|
||||
if ($charge['type'] !== 'CHARGE')
|
||||
INTERNAL_ERROR("Reversal item is not CHARGE.");
|
||||
|
||||
$transaction_id = null;
|
||||
foreach ($ledger_entries AS $entry) {
|
||||
$entry = $entry['Entry'];
|
||||
$amount = -1*$entry['amount'];
|
||||
// Build a transaction
|
||||
$reversal = array('Transaction' => array(), 'Entry' => array());
|
||||
$reversal['Transaction']['stamp'] = $stamp;
|
||||
$reversal['Transaction']['comment'] = "Credit Note: Charge Reversal";
|
||||
|
||||
if (isset($entry['credit_account_id']))
|
||||
$refund_account_id = $entry['credit_account_id'];
|
||||
elseif (isset($entry['CreditLedger']['Account']['id']))
|
||||
$refund_account_id = $entry['CreditLedger']['Account']['id'];
|
||||
elseif (isset($entry['credit_ledger_id']))
|
||||
$refund_account_id = $this->Ledger->accountID($entry['credit_ledger_id']);
|
||||
else
|
||||
return $this->prReturn(null);
|
||||
$voided_entry_transactions = array();
|
||||
$reconciled = $this->reconciledEntries($id);
|
||||
$this->pr(21, compact('reconciled'));
|
||||
|
||||
// post new refund in the income account
|
||||
$ids = $A->postEntry
|
||||
(array('transaction_id' => $transaction_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($ar_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($refund_account_id),
|
||||
'effective_date' => $entry['effective_date'],
|
||||
'through_date' => $entry['through_date'],
|
||||
'amount' => $amount,
|
||||
'lease_id' => $entry['lease_id'],
|
||||
'customer_id' => $entry['customer_id'],
|
||||
'comment' => "Refund; Entry #{$entry['id']}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('Entry' =>
|
||||
array('id' => $entry['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
);
|
||||
if ($reconciled && !$balance) {
|
||||
foreach ($reconciled['entries'] AS $entry) {
|
||||
if ($entry['DisbursementEntry']['type'] === 'REVERSAL')
|
||||
INTERNAL_ERROR("Charge has already been reversed");
|
||||
|
||||
if ($ids['error'])
|
||||
return $this->prReturn(null);
|
||||
$transaction_id = $ids['transaction_id'];
|
||||
/* $voided_entry_transactions[$entry['DisbursementEntry']['transaction_id']] */
|
||||
/* = array_intersect_key($entry['DisbursementEntry'], */
|
||||
/* array('customer_id'=>1, 'lease_id'=>1)); */
|
||||
|
||||
$this->pr(15, compact('ids', 'amount', 'refund_account_id', 'ar_account_id'),
|
||||
'Posted Refund Ledger Entry');
|
||||
/* $reversal['Entry'][] = */
|
||||
/* array_intersect_key($entry['DisbursementEntry'], */
|
||||
/* array_flip(array('amount', 'account_id', 'charge_entry_id'))) */
|
||||
/* + array('type' => 'SURPLUS', */
|
||||
/* 'comment' => 'Release of funds applied to reversed charge', */
|
||||
/* ); */
|
||||
}
|
||||
/* $this->pr(17, compact('voided_entry_transactions')); */
|
||||
}
|
||||
|
||||
return $this->prReturn(true);
|
||||
// Query the stats to get the remaining balance
|
||||
$stats = $this->stats($id);
|
||||
|
||||
// Add the charge reversal
|
||||
$reversal['Entry'][] =
|
||||
array('charge_entry_id' => $id,
|
||||
'amount' => -1 * $stats['Charge'][$balance ? 'balance' : 'total'],
|
||||
'account_id' => $charge['account_id'],
|
||||
'comment' => 'Charge Reversal',
|
||||
);
|
||||
|
||||
// Record the reversal transaction
|
||||
$result = $this->Transaction->addReversal
|
||||
($reversal, $id, $charge['customer_id'], $charge['lease_id']);
|
||||
$this->pr(21, compact('result'));
|
||||
$ret['reversal'] = $result;
|
||||
if ($result['error'])
|
||||
$ret['error'] = true;
|
||||
|
||||
foreach ($voided_entry_transactions AS $transaction_id => $tx) {
|
||||
$result = $this->assignCredits
|
||||
(null,
|
||||
$transaction_id,
|
||||
null,
|
||||
null,
|
||||
$tx['customer_id'],
|
||||
$tx['lease_id']
|
||||
);
|
||||
$this->pr(21, compact('result'));
|
||||
$ret['assigned'][] = $result;
|
||||
if ($result['error'])
|
||||
$ret['error'] = true;
|
||||
}
|
||||
|
||||
return $this->prReturn($ret + array('error' => false));
|
||||
}
|
||||
|
||||
|
||||
@@ -229,28 +274,21 @@ OPTION 2
|
||||
function reconciledSetQuery($set, $query) {
|
||||
$this->queryInit($query);
|
||||
|
||||
if ($set == 'CHARGE' || $set == 'PAYMENT')
|
||||
$query['conditions'][] = array('StatementEntry.type' => $set);
|
||||
if (in_array($set, $this->debitTypes()))
|
||||
$query['link']['DisbursementEntry'] = array('fields' => array("SUM(DisbursementEntry.amount) AS reconciled"));
|
||||
elseif (in_array($set, $this->creditTypes()))
|
||||
$query['link']['ChargeEntry'] = array('fields' => array("SUM(ChargeEntry.amount) AS reconciled"));
|
||||
else
|
||||
die("INVALID RECONCILE SET");
|
||||
|
||||
if ($set == 'CHARGE')
|
||||
$query['link']['PaymentEntry'] = array('fields' => array("SUM(PaymentEntry.amount) AS reconciled"));
|
||||
if ($set == 'PAYMENT')
|
||||
$query['link']['ChargeEntry'] = array('fields' => array("SUM(ChargeEntry.amount) AS reconciled"));
|
||||
|
||||
$query['conditions'][] = array('StatementEntry.type' => $set);
|
||||
$query['group'] = 'StatementEntry.id';
|
||||
|
||||
// REVISIT: TESTING
|
||||
//$query['link']['PaymentEntry'] = array('fields' => array("(`PaymentEntry.amount`+0) AS reconciled"));
|
||||
//$query['group'] = null;
|
||||
// END REVISIT
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
function reconciledSet($set, $query = null, $unrec = false, $if_rec_include_partial = false) {
|
||||
//$this->prFunctionLevel(16);
|
||||
//$this->prFunctionLevel(array('log' => 16, 'show' => 10));
|
||||
$this->prEnter(compact('set', 'query', 'unrec', 'if_rec_include_partial'));
|
||||
$lquery = $this->reconciledSetQuery($set, $query);
|
||||
$result = $this->find('all', $lquery);
|
||||
@@ -274,7 +312,7 @@ OPTION 2
|
||||
$reconciled = true;
|
||||
elseif ($entry['StatementEntry']['reconciled'] == 0)
|
||||
$reconciled = false;
|
||||
else // Partial payment; depends on unrec
|
||||
else // Partial disbursement; depends on unrec
|
||||
$reconciled = (!$unrec && $if_rec_include_partial);
|
||||
|
||||
// Add to the set, if it's been requested
|
||||
@@ -292,7 +330,7 @@ OPTION 2
|
||||
**************************************************************************
|
||||
* function: reconciledEntries
|
||||
* - Returns a list of entries that reconcile against the given entry.
|
||||
* (such as payments towards a charge).
|
||||
* (such as disbursements towards a charge).
|
||||
*/
|
||||
function reconciledEntriesQuery($id, $query = null) {
|
||||
$this->queryInit($query, false);
|
||||
@@ -303,10 +341,14 @@ OPTION 2
|
||||
|
||||
$query['conditions'][] = array('StatementEntry.id' => $id);
|
||||
|
||||
if ($this->data['StatementEntry']['type'] == 'CHARGE')
|
||||
$query['link']['PaymentEntry'] = array();
|
||||
if ($this->data['StatementEntry']['type'] == 'PAYMENT')
|
||||
if (in_array($this->data['StatementEntry']['type'], $this->debitTypes())) {
|
||||
$query['link']['DisbursementEntry'] = array();
|
||||
$query['conditions'][] = array('DisbursementEntry.id !=' => null);
|
||||
}
|
||||
if (in_array($this->data['StatementEntry']['type'], $this->creditTypes())) {
|
||||
$query['link']['ChargeEntry'] = array();
|
||||
$query['conditions'][] = array('ChargeEntry.id !=' => null);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
@@ -331,60 +373,121 @@ OPTION 2
|
||||
*
|
||||
* REVISIT <AP>: 20090726
|
||||
* This algorithm shouldn't be hardcoded. We need to allow
|
||||
* the user to specify how payments should be applied.
|
||||
* the user to specify how disbursements should be applied.
|
||||
*
|
||||
*/
|
||||
function assignCredits($query = null, $receipt_id = null) {
|
||||
function assignCredits($query = null, $receipt_id = null,
|
||||
$charge_ids = null, $disbursement_type = null,
|
||||
$customer_id = null, $lease_id = null)
|
||||
{
|
||||
//$this->prFunctionLevel(25);
|
||||
$this->prEnter( compact('query', 'receipt_id'));
|
||||
$this->prEnter(compact('query', 'receipt_id',
|
||||
'charge_ids', 'disbursement_type',
|
||||
'customer_id', 'lease_id'));
|
||||
$this->queryInit($query);
|
||||
|
||||
if (!empty($customer_id))
|
||||
$query['conditions'][] = array('StatementEntry.customer_id' => $customer_id);
|
||||
|
||||
if (empty($disbursement_type))
|
||||
$disbursement_type = 'DISBURSEMENT';
|
||||
|
||||
$ret = array();
|
||||
|
||||
// First, find all known credits
|
||||
$lquery = $query;
|
||||
$lquery['conditions'][] = array('StatementEntry.type' => 'SURPLUS');
|
||||
$lquery['order'][] = 'StatementEntry.effective_date ASC';
|
||||
$credits = $this->find('all', $lquery);
|
||||
$this->pr(18, compact('credits'),
|
||||
"Credits Established");
|
||||
// First, find all known credits, unless this call is to make
|
||||
// credit adjustments to a specific charge
|
||||
// REVISIT <AP>: 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)) {
|
||||
$lquery = $query;
|
||||
$lquery['conditions'][] = array('StatementEntry.type' => 'SURPLUS');
|
||||
// REVISIT <AP>: 20090804
|
||||
// We need to ensure that we're using surplus credits ONLY from either
|
||||
// the given lease, or those that do not apply to any specific lease.
|
||||
// However, by doing this, it forces any lease surplus amounts to
|
||||
// remain frozen with that lease until either there is a lease charge,
|
||||
// we refund the money, or we "promote" that surplus to the customer
|
||||
// level and out of the leases direct control.
|
||||
// That seems like a pain. Perhaps we should allow any customer
|
||||
// surplus to be used on any customer charge.
|
||||
$lquery['conditions'][] =
|
||||
array('OR' =>
|
||||
array(array('StatementEntry.lease_id' => null),
|
||||
(!empty($lease_id)
|
||||
? array('StatementEntry.lease_id' => $lease_id)
|
||||
: array()),
|
||||
));
|
||||
$lquery['order'][] = 'StatementEntry.effective_date ASC';
|
||||
$credits = $this->find('all', $lquery);
|
||||
$this->pr(18, compact('credits'),
|
||||
"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)) {
|
||||
$lquery = $query;
|
||||
$lquery['link'] += array('LedgerEntry' =>
|
||||
array('conditions' =>
|
||||
//array(LedgerEntry.'crdr'=>'DEBIT'),
|
||||
array('LedgerEntry.account_id !=' => $this->Account->accountReceivableAccountID()),
|
||||
));
|
||||
$lquery['fields'] = array('Transaction.id', 'Transaction.stamp', 'Transaction.amount',
|
||||
'LedgerEntry.account_id');
|
||||
// Very specific case here... no extra conditions
|
||||
unset($lquery['conditions']);
|
||||
$this->Transaction->id = $receipt_id;
|
||||
$lquery =
|
||||
array('link' =>
|
||||
array('StatementEntry',
|
||||
'LedgerEntry' =>
|
||||
array('conditions' =>
|
||||
array('LedgerEntry.account_id !=' =>
|
||||
$this->Account->accountReceivableAccountID()),
|
||||
),
|
||||
),
|
||||
'conditions' => array('Transaction.id' => $receipt_id),
|
||||
'fields' => array('Transaction.id', 'Transaction.stamp', 'Transaction.amount'),
|
||||
);
|
||||
$receipt_credit = $this->Transaction->find('first', $lquery);
|
||||
if (!$receipt_credit)
|
||||
die("INTERNAL ERROR: UNABLE TO LOCATE RECEIPT");
|
||||
INTERNAL_ERROR("Unable to locate receipt.");
|
||||
|
||||
$receipt_credit['balance'] = $receipt_credit['Transaction']['amount'];
|
||||
//$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 Added");
|
||||
}
|
||||
|
||||
// Now find all unpaid charges
|
||||
$lquery = $query;
|
||||
if (isset($charge_ids)) {
|
||||
$lquery = array('contain' => false,
|
||||
'conditions' => array('StatementEntry.id' => $charge_ids));
|
||||
} else {
|
||||
$lquery = $query;
|
||||
// If we're working with a specific lease, then limit charges to it
|
||||
if (!empty($lease_id))
|
||||
$lquery['conditions'][] = array('StatementEntry.lease_id' => $lease_id);
|
||||
}
|
||||
$lquery['order'] = 'StatementEntry.effective_date ASC';
|
||||
$charges = $this->reconciledSet('CHARGE', $lquery, true);
|
||||
$this->pr(18, compact('charges'),
|
||||
"Outstanding Charges Determined");
|
||||
$charges = array();
|
||||
foreach ($this->debitTypes() AS $dtype) {
|
||||
$rset = $this->reconciledSet($dtype, $lquery, true);
|
||||
$entries = $rset['entries'];
|
||||
$charges = array_merge($charges, $entries);
|
||||
$this->pr(18, compact('dtype', 'entries'), "Outstanding Debit Entries");
|
||||
}
|
||||
|
||||
// Initialize our list of used credits
|
||||
$used_credits = array();
|
||||
|
||||
// Work through all unpaid charges, applying payments as we go
|
||||
foreach ($charges['entries'] AS $charge) {
|
||||
// REVISIT <AP>: 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');
|
||||
@@ -394,26 +497,26 @@ OPTION 2
|
||||
// will handle everything just fine. However, this
|
||||
// just saves extra processing if/when there is no
|
||||
// means to resolve a charge anyway.
|
||||
if (count($credits) == 0 && empty($receipt_credit['balance'])) {
|
||||
$this->pr(15, 'No more available credits');
|
||||
if (empty($credits) && empty($receipt_credit['balance'])) {
|
||||
$this->pr(17, 'No more available credits');
|
||||
break;
|
||||
}
|
||||
|
||||
$charge['balance'] = $charge['StatementEntry']['balance'];
|
||||
while ($charge['balance'] > 0 &&
|
||||
(count($credits) || !empty($receipt_credit['balance']))) {
|
||||
(!empty($credits) || !empty($receipt_credit['balance']))) {
|
||||
|
||||
$this->pr(20, compact('charge'),
|
||||
'Attempt Charge Reconciliation');
|
||||
|
||||
// Use explicit credits before using implicit credits
|
||||
// (Not sure it matters though).
|
||||
if (count($credits)) {
|
||||
if (!empty($credits)) {
|
||||
// Peel off the first credit available
|
||||
$credit =& $credits[0];
|
||||
$payment_date = $credit['StatementEntry']['effective_date'];
|
||||
$payment_transaction_id = $credit['StatementEntry']['transaction_id'];
|
||||
$payment_account_id = $credit['StatementEntry']['account_id'];
|
||||
$disbursement_date = $credit['StatementEntry']['effective_date'];
|
||||
$disbursement_transaction_id = $credit['StatementEntry']['transaction_id'];
|
||||
$disbursement_account_id = $credit['StatementEntry']['account_id'];
|
||||
|
||||
if (!isset($credit['balance']))
|
||||
$credit['balance'] = $credit['StatementEntry']['amount'];
|
||||
@@ -421,57 +524,57 @@ OPTION 2
|
||||
elseif (!empty($receipt_credit['balance'])) {
|
||||
// Use our only receipt credit
|
||||
$credit =& $receipt_credit;
|
||||
$payment_date = $credit['Transaction']['stamp'];
|
||||
$payment_transaction_id = $credit['Transaction']['id'];
|
||||
$payment_account_id = $credit['LedgerEntry']['account_id'];
|
||||
$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?");
|
||||
}
|
||||
|
||||
// Set the payment amount to the maximum amount
|
||||
// Set the disbursement amount to the maximum amount
|
||||
// possible without exceeding the charge or credit balance
|
||||
$payment_amount = min($charge['balance'], $credit['balance']);
|
||||
$disbursement_amount = min($charge['balance'], $credit['balance']);
|
||||
if (!isset($credit['applied']))
|
||||
$credit['applied'] = 0;
|
||||
|
||||
$credit['applied'] += $payment_amount;
|
||||
$credit['balance'] -= $payment_amount;
|
||||
$credit['applied'] += $disbursement_amount;
|
||||
$credit['balance'] -= $disbursement_amount;
|
||||
|
||||
$this->pr(20, compact('credit'),
|
||||
($credit['balance'] > 0 ? 'Utilized' : 'Exhausted') .
|
||||
(count($credits) ? ' Credit' : ' Receipt'));
|
||||
(!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 && count($credits))
|
||||
if ($credit['balance'] <= 0 && !empty($credits))
|
||||
$used_credits[] = array_shift($credits);
|
||||
|
||||
// Add a payment that uses the available credit to pay the charge
|
||||
$payment = array('type' => 'PAYMENT',
|
||||
'account_id' => $payment_account_id,
|
||||
'amount' => $payment_amount,
|
||||
'effective_date' => $payment_date,
|
||||
'transaction_id' => $payment_transaction_id,
|
||||
// Add a disbursement that uses the available credit to pay the charge
|
||||
$disbursement = array('type' => $disbursement_type,
|
||||
'account_id' => $disbursement_account_id,
|
||||
'amount' => $disbursement_amount,
|
||||
'effective_date' => $disbursement_date,
|
||||
'transaction_id' => $disbursement_transaction_id,
|
||||
'customer_id' => $charge['StatementEntry']['customer_id'],
|
||||
'lease_id' => $charge['StatementEntry']['lease_id'],
|
||||
'charge_entry_id' => $charge['StatementEntry']['id'],
|
||||
'comment' => null,
|
||||
);
|
||||
|
||||
$this->pr(20, compact('payment'),
|
||||
'New Payment Entry');
|
||||
$this->pr(20, compact('disbursement'),
|
||||
'New Disbursement Entry');
|
||||
|
||||
$result = $this->addStatementEntry($payment);
|
||||
$ret['Payment'][] = $result;
|
||||
$result = $this->addStatementEntry($disbursement);
|
||||
$ret['Disbursement'][] = $result;
|
||||
if ($result['error'])
|
||||
$ret['error'] = true;
|
||||
|
||||
// Adjust the charge balance to reflect the new payment
|
||||
$charge['balance'] -= $payment_amount;
|
||||
// Adjust the charge balance to reflect the new disbursement
|
||||
$charge['balance'] -= $disbursement_amount;
|
||||
if ($charge['balance'] < 0)
|
||||
die("HOW DID WE GET A NEGATIVE CHARGE AMOUNT?");
|
||||
|
||||
@@ -486,7 +589,7 @@ OPTION 2
|
||||
$used_credits[] = array_shift($credits);
|
||||
|
||||
$this->pr(18, compact('credits', 'used_credits', 'receipt_credit'),
|
||||
'Payments added');
|
||||
'Disbursements added');
|
||||
|
||||
// Clean up any explicit credits that have been used
|
||||
foreach ($used_credits AS $credit) {
|
||||
@@ -509,20 +612,37 @@ OPTION 2
|
||||
if (!empty($receipt_credit['balance'])) {
|
||||
$credit =& $receipt_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')),
|
||||
));
|
||||
|
||||
$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' => $credit['Customer']['id'],
|
||||
));
|
||||
$ret['Credit'] = $result;
|
||||
if ($result['error'])
|
||||
$ret['error'] = true;
|
||||
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']);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->prReturn($ret + array('error' => false));
|
||||
@@ -536,6 +656,7 @@ OPTION 2
|
||||
* - Returns summary data from the requested statement entry
|
||||
*/
|
||||
function stats($id = null, $query = null) {
|
||||
$this->prFunctionLevel(array('log' => 19, 'show' => 10));
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
|
||||
$this->queryInit($query);
|
||||
@@ -545,48 +666,68 @@ OPTION 2
|
||||
if (isset($id))
|
||||
$query['conditions'][] = array('StatementEntry.id' => $id);
|
||||
|
||||
$rquery = $query;
|
||||
unset($rquery['link']['ChargeEntry']);
|
||||
$rquery['link']['PaymentEntry'] = array('fields' => array());
|
||||
$types = array('Charge', 'Disbursement');
|
||||
foreach ($types AS $type_index => $this_name) {
|
||||
$that_name = $types[($type_index + 1) % 2];
|
||||
if ($this_name === 'Charge') {
|
||||
$this_types = $this->debitTypes();
|
||||
$that_types = $this->creditTypes();
|
||||
} else {
|
||||
$this_types = $this->creditTypes();
|
||||
$that_types = $this->debitTypes();
|
||||
}
|
||||
|
||||
$rquery['fields'] = array();
|
||||
$rquery['fields'][] = "StatementEntry.amount";
|
||||
$rquery['fields'][] = "SUM(PaymentEntry.amount) AS reconciled";
|
||||
$this_query = $query;
|
||||
$this_query['fields'] = array();
|
||||
$this_query['fields'][] = "SUM(StatementEntry.amount) AS total";
|
||||
$this_query['conditions'][] = array('StatementEntry.type' => $this_types);
|
||||
$result = $this->find('first', $this_query);
|
||||
$stats[$this_name] = $result[0];
|
||||
|
||||
$rquery['conditions'][] = array('StatementEntry.type' => 'CHARGE');
|
||||
$rquery['group'] = 'StatementEntry.id';
|
||||
$this->pr(17, compact('this_query', 'result'), $this_name.'s');
|
||||
|
||||
$result = $this->find('all', $rquery);
|
||||
$stats['Charge'] = array('total' => 0, 'reconciled' => 0);
|
||||
foreach($result AS $charge) {
|
||||
$stats['Charge']['total'] += $charge['StatementEntry']['amount'];
|
||||
$stats['Charge']['reconciled'] += $charge[0]['reconciled'];
|
||||
// Tally the different types that result in credits towards the charges
|
||||
$stats[$this_name]['reconciled'] = 0;
|
||||
foreach ($that_types AS $that_type) {
|
||||
$lc_that_type = strtolower($that_type);
|
||||
$that_query = $this_query;
|
||||
$that_query['link']["{$that_name}Entry"] = array('fields' => array());
|
||||
$that_query['fields'] = array();
|
||||
if ($this_name == 'Charge')
|
||||
$that_query['fields'][] = "COALESCE(SUM(${that_name}Entry.amount),0) AS $lc_that_type";
|
||||
else
|
||||
$that_query['fields'][] = "COALESCE(SUM(StatementEntry.amount), 0) AS $lc_that_type";
|
||||
$that_query['conditions'][] = array("{$that_name}Entry.type" => $that_type);
|
||||
$result = $this->find('first', $that_query);
|
||||
$stats[$this_name] += $result[0];
|
||||
|
||||
$this->pr(17, compact('that_query', 'result'), "{$this_name}s: $that_type");
|
||||
$stats[$this_name]['reconciled'] += $stats[$this_name][$lc_that_type];
|
||||
}
|
||||
|
||||
// Compute balance information for charges
|
||||
$stats[$this_name]['balance'] =
|
||||
$stats[$this_name]['total'] - $stats[$this_name]['reconciled'];
|
||||
if (!isset($stats[$this_name]['balance']))
|
||||
$stats[$this_name]['balance'] = 0;
|
||||
}
|
||||
$stats['Charge']['balance'] =
|
||||
$stats['Charge']['total'] - $stats['Charge']['reconciled'];
|
||||
|
||||
$this->pr(17, compact('query', 'result'),
|
||||
'Charges');
|
||||
|
||||
$rquery = $query;
|
||||
unset($rquery['link']['PaymentEntry']);
|
||||
$rquery['link']['ChargeEntry'] = array('fields' => array());
|
||||
// 'balance' is simply the difference between
|
||||
// the balances of charges and disbursements
|
||||
$stats['balance'] = $stats['Charge']['balance'] - $stats['Disbursement']['balance'];
|
||||
if (!isset($stats['balance']))
|
||||
$stats['balance'] = 0;
|
||||
|
||||
$rquery['fields'] = array();
|
||||
$rquery['fields'][] = "SUM(StatementEntry.amount) AS total";
|
||||
$rquery['fields'][] = "SUM(IF(ChargeEntry.id IS NULL, 0, StatementEntry.amount)) AS reconciled";
|
||||
$rquery['fields'][] = "SUM(IF(ChargeEntry.id IS NULL, StatementEntry.amount, 0)) AS balance";
|
||||
|
||||
$rquery['conditions'][] = array('StatementEntry.type' => 'PAYMENT');
|
||||
$result = $this->find('first', $rquery);
|
||||
if (!isset($result[0]['balance']))
|
||||
$result[0]['balance'] = 0;
|
||||
$stats['Payment'] = $result[0];
|
||||
|
||||
$this->pr(17, compact('rquery', 'result'),
|
||||
'Payments');
|
||||
// 'account_balance' is really only relevant to
|
||||
// callers that have requested charge and disbursement
|
||||
// stats with respect to a particular account.
|
||||
// It represents the difference between inflow
|
||||
// and outflow from that account.
|
||||
$stats['account_balance'] = $stats['Charge']['reconciled'] - $stats['Disbursement']['total'];
|
||||
if (!isset($stats['account_balance']))
|
||||
$stats['account_balance'] = 0;
|
||||
|
||||
return $this->prReturn($stats);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -92,8 +92,8 @@ class Tender extends AppModel {
|
||||
* - Add new LedgerEntry (L2a); T1; debit:NSF; -$A; N1
|
||||
* - Add new LedgerEntry (L2b); T1; credit:A/R; -$A
|
||||
* - For Tx associated with LE associated with C1:
|
||||
* - For each Payment SE of Tx:
|
||||
* - Add new StatementEntry (S1n); T1; PAYMENT; -1*S1n.amount
|
||||
* - For each Disbursement SE of Tx:
|
||||
* - Add new StatementEntry (S1n); T1; DISBURSEMENT; -1*S1n.amount
|
||||
* - New Transaction (T2) (?????)
|
||||
* - Add new StatementEntry (S2); T2; CHARGE; NSF; $35
|
||||
* - Add new LedgerEntry (L3a); T2; credit:NSF-Fee; $35
|
||||
|
||||
@@ -112,4 +112,4 @@ class TenderType extends AppModel {
|
||||
return $stats[0];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,9 @@ class Transaction extends AppModel {
|
||||
'conditions' => array('Charge.type' => 'CHARGE')
|
||||
),
|
||||
|
||||
'Payment' => array(
|
||||
'Disbursement' => array(
|
||||
'className' => 'StatementEntry',
|
||||
'conditions' => array('Payment.type' => 'PAYMENT')
|
||||
'conditions' => array('Disbursement.type' => 'DISBURSEMENT')
|
||||
),
|
||||
|
||||
'Debit' => array(
|
||||
@@ -37,7 +37,7 @@ class Transaction extends AppModel {
|
||||
);
|
||||
|
||||
|
||||
var $default_log_level = 30;
|
||||
var $default_log_level = array('log' => 30, 'show' => 30);
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -51,23 +51,25 @@ class Transaction extends AppModel {
|
||||
|
||||
// Establish the transaction as an invoice
|
||||
$invoice =& $data['Transaction'];
|
||||
$invoice['type'] = 'INVOICE';
|
||||
$invoice['crdr'] = 'DEBIT';
|
||||
$invoice['account_id'] = $this->Account->accountReceivableAccountID();
|
||||
$invoice['customer_id'] = $customer_id;
|
||||
$invoice['lease_id'] = $lease_id;
|
||||
$invoice +=
|
||||
array('type' => 'INVOICE',
|
||||
'crdr' => 'DEBIT',
|
||||
'account_id' => $this->Account->accountReceivableAccountID(),
|
||||
'customer_id' => $customer_id,
|
||||
'lease_id' => $lease_id,
|
||||
);
|
||||
|
||||
// Go through the statement entries and flag as charges
|
||||
foreach ($data['Entry'] AS &$entry) {
|
||||
$entry['type'] = 'CHARGE';
|
||||
$entry['crdr'] = 'CREDIT';
|
||||
}
|
||||
foreach ($data['Entry'] AS &$entry)
|
||||
$entry += array('type' => 'CHARGE',
|
||||
'crdr' => 'CREDIT',
|
||||
);
|
||||
|
||||
$ids = $this->addTransaction($data['Transaction'], $data['Entry']);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['invoice_id'] = $ids['transaction_id'];
|
||||
|
||||
return $ids;
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
@@ -83,26 +85,30 @@ class Transaction extends AppModel {
|
||||
|
||||
// Establish the transaction as a receipt
|
||||
$receipt =& $data['Transaction'];
|
||||
$receipt['type'] = 'RECEIPT';
|
||||
$receipt['crdr'] = 'CREDIT';
|
||||
$receipt['account_id'] = $this->Account->accountReceivableAccountID();
|
||||
$receipt['customer_id'] = $customer_id;
|
||||
$receipt['lease_id'] = $lease_id;
|
||||
$receipt +=
|
||||
array('type' => 'RECEIPT',
|
||||
'crdr' => 'CREDIT',
|
||||
'account_id' => $this->Account->accountReceivableAccountID(),
|
||||
'customer_id' => $customer_id,
|
||||
'lease_id' => $lease_id,
|
||||
);
|
||||
|
||||
// Go through the statement entries and flag as payments
|
||||
foreach ($data['Entry'] AS &$entry) {
|
||||
$entry['crdr'] = 'DEBIT';
|
||||
if (empty($entry['account_id']) && isset($entry['Tender']['tender_type_id'])) {
|
||||
$entry['account_id'] = $this->LedgerEntry->Tender->TenderType->
|
||||
accountID($entry['Tender']['tender_type_id']);
|
||||
}
|
||||
}
|
||||
// Go through the statement entries and flag as disbursements
|
||||
foreach ($data['Entry'] AS &$entry)
|
||||
$entry += array('type' => 'DISBURSEMENT', // not used
|
||||
'crdr' => 'DEBIT',
|
||||
'account_id' =>
|
||||
(isset($entry['Tender']['tender_type_id'])
|
||||
? ($this->LedgerEntry->Tender->TenderType->
|
||||
accountID($entry['Tender']['tender_type_id']))
|
||||
: null),
|
||||
);
|
||||
|
||||
$ids = $this->addTransaction($data['Transaction'], $data['Entry']);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['receipt_id'] = $ids['transaction_id'];
|
||||
|
||||
return $ids;
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
@@ -113,36 +119,46 @@ class Transaction extends AppModel {
|
||||
* - Adds a new waiver
|
||||
*/
|
||||
|
||||
function addWaiver($data, $customer_id, $lease_id = null) {
|
||||
$this->prEnter(compact('data', 'customer_id', 'lease_id'));
|
||||
// REVISIT <AP>: 20090802
|
||||
// Completely un-implemented. Just copied from addReceipt
|
||||
// and search-replace receipt with waiver.
|
||||
return ('error' => true);
|
||||
function addWaiver($data, $charge_id, $customer_id, $lease_id = null) {
|
||||
$this->prEnter(compact('data', 'charge_id', 'customer_id', 'lease_id'));
|
||||
|
||||
// Establish the transaction as a waiver
|
||||
$waiver =& $data['Transaction'];
|
||||
$waiver['type'] = 'RECEIPT';
|
||||
$waiver['crdr'] = 'CREDIT';
|
||||
$waiver['account_id'] = $this->Account->accountReceivableAccountID();
|
||||
$waiver['customer_id'] = $customer_id;
|
||||
$waiver['lease_id'] = $lease_id;
|
||||
if (count($data['Entry']) != 1)
|
||||
INTERNAL_ERROR("Should be one Entry for addWaiver");
|
||||
|
||||
// Go through the statement entries and flag as payments
|
||||
foreach ($data['Entry'] AS &$entry) {
|
||||
$entry['type'] = 'WAIVE';
|
||||
$entry['crdr'] = 'DEBIT';
|
||||
if (empty($entry['account_id']) && isset($entry['Tender']['tender_type_id'])) {
|
||||
$entry['account_id'] = $this->LedgerEntry->Tender->TenderType->
|
||||
accountID($entry['Tender']['tender_type_id']);
|
||||
}
|
||||
}
|
||||
// Just make sure the disbursement(s) are marked as waivers
|
||||
// and that they go to cover the specific charge.
|
||||
$data['Transaction']['disbursement_type'] = 'WAIVER';
|
||||
$data['Transaction']['assign_charge_entry_id'] = $charge_id;
|
||||
|
||||
$ids = $this->addTransaction($data['Transaction'], $data['Entry']);
|
||||
// In all other respects this is just a receipt.
|
||||
$ids = $this->addReceipt($data, $customer_id, $lease_id);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['waiver_id'] = $ids['transaction_id'];
|
||||
|
||||
return $ids;
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addReversal
|
||||
* - Adds a new charge reversal
|
||||
*/
|
||||
|
||||
function addReversal($data, $charge_id, $customer_id, $lease_id = null) {
|
||||
$this->prEnter(compact('data', 'charge_id', 'customer_id', 'lease_id'));
|
||||
|
||||
$data['Transaction'] += array('type' => 'CREDIT_NOTE');
|
||||
foreach ($data['Entry'] AS &$entry)
|
||||
$entry += array('type' => 'REVERSAL');
|
||||
|
||||
// In all other respects this is just an invoice
|
||||
$ids = $this->addInvoice($data, $customer_id, $lease_id);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['reversal_id'] = $ids['transaction_id'];
|
||||
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
@@ -158,11 +174,13 @@ class Transaction extends AppModel {
|
||||
|
||||
// Establish the transaction as a deposit
|
||||
$deposit =& $data['Transaction'];
|
||||
$deposit['type'] = 'DEPOSIT';
|
||||
$deposit['crdr'] = 'DEBIT';
|
||||
$deposit['account_id'] = $account_id;
|
||||
$deposit['customer_id'] = null;
|
||||
$deposit['lease_id'] = null;
|
||||
$deposit +=
|
||||
array('type' => 'DEPOSIT',
|
||||
'crdr' => 'DEBIT',
|
||||
'account_id' => $account_id,
|
||||
'customer_id' => null,
|
||||
'lease_id' => null,
|
||||
);
|
||||
|
||||
// Save the list of IDs, so that we can mark their
|
||||
// deposit transaction after it has been created.
|
||||
@@ -192,7 +210,7 @@ class Transaction extends AppModel {
|
||||
);
|
||||
}
|
||||
|
||||
return $ids;
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
@@ -208,10 +226,13 @@ class Transaction extends AppModel {
|
||||
|
||||
// Establish the transaction as a close
|
||||
$close =& $data['Transaction'];
|
||||
$close['type'] = 'CLOSE';
|
||||
$close['account_id'] = null;
|
||||
$close['customer_id'] = null;
|
||||
$close['lease_id'] = null;
|
||||
$close +=
|
||||
array('type' => 'CLOSE',
|
||||
'crdr' => null,
|
||||
'account_id' => null,
|
||||
'customer_id' => null,
|
||||
'lease_id' => null,
|
||||
);
|
||||
|
||||
$ledger_ids = array();
|
||||
$data['Entry'] = array();
|
||||
@@ -249,7 +270,73 @@ class Transaction extends AppModel {
|
||||
);
|
||||
}
|
||||
|
||||
return $ids;
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addRefund
|
||||
* - Adds a new refund
|
||||
*/
|
||||
|
||||
function addRefund($data, $customer_id, $lease_id = null) {
|
||||
$this->prEnter(compact('data', 'customer_id', 'lease_id'));
|
||||
|
||||
// Establish the transaction as a Refund. This is just like a
|
||||
// Payment, except instead of paying out of the account payable,
|
||||
// it comes from the customer credit in the account receivable.
|
||||
// Someday, perhaps we'll just issue a Credit Note or similar,
|
||||
// but for now, a refund means it's time to actually PAY.
|
||||
$refund =& $data['Transaction'];
|
||||
$refund +=
|
||||
array('account_id' => $this->Account->accountReceivableAccountID());
|
||||
|
||||
// Also, to make it clear to the user, we flag as a REFUND
|
||||
// even though that type works and operates just as PAYMENT
|
||||
foreach ($data['Entry'] AS &$entry)
|
||||
$entry += array('type' => 'REFUND');
|
||||
|
||||
$ids = $this->addPayment($data, $customer_id, $lease_id);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['refund_id'] = $ids['transaction_id'];
|
||||
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addPayment
|
||||
* - Adds a new payment transaction, which is money outflow
|
||||
*/
|
||||
|
||||
function addPayment($data, $customer_id, $lease_id = null) {
|
||||
$this->prEnter(compact('data', 'customer_id', 'lease_id'));
|
||||
|
||||
// Establish the transaction as an payment
|
||||
$payment =& $data['Transaction'];
|
||||
$payment +=
|
||||
array('type' => 'PAYMENT',
|
||||
'crdr' => 'DEBIT',
|
||||
'account_id' => $this->Account->accountPayableAccountID(),
|
||||
'customer_id' => $customer_id,
|
||||
'lease_id' => $lease_id,
|
||||
);
|
||||
|
||||
// Go through the statement entries and flag as payments
|
||||
foreach ($data['Entry'] AS &$entry)
|
||||
$entry += array('type' => 'PAYMENT',
|
||||
'crdr' => 'CREDIT',
|
||||
);
|
||||
|
||||
$ids = $this->addTransaction($data['Transaction'], $data['Entry']);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['payment_id'] = $ids['transaction_id'];
|
||||
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
@@ -322,7 +409,7 @@ class Transaction extends AppModel {
|
||||
*
|
||||
* - Entry (array)
|
||||
* - [MANDATORY]
|
||||
* - type (CHARGE, PAYMENT)
|
||||
* - type (CHARGE, DISBURSEMENT)
|
||||
* - account_id
|
||||
* - crdr
|
||||
* - amount
|
||||
@@ -435,16 +522,19 @@ class Transaction extends AppModel {
|
||||
array_flip(array('customer_id', 'lease_id')));
|
||||
$se['comment'] = $entry['statement_entry_comment'];
|
||||
|
||||
// (PAYMENTS will have statement entries created below, when
|
||||
// (DISBURSEMENTS will have statement entries created below, when
|
||||
// assigning credits, and DEPOSITS don't have statement entries)
|
||||
if ($transaction['type'] != 'INVOICE' && $subtype !== 'NSF')
|
||||
if (empty($entry['charge_entry_id']) &&
|
||||
(empty($transaction['customer_id']) ||
|
||||
($transaction['account_id'] == $this->Account->accountReceivableAccountID() &&
|
||||
$transaction['crdr'] == 'CREDIT')))
|
||||
$se = null;
|
||||
|
||||
// NSF transactions don't use LedgerEntries
|
||||
// REVISIT <AP>: 20090731
|
||||
// Doesn't seem right... probably doing this because of the
|
||||
// single A/R entry we add below for NSF
|
||||
if ($subtype === 'NSF')
|
||||
if ($entry['type'] == 'SURPLUS' || $subtype === 'NSF')
|
||||
$le1 = $le1_tender = $le2 = null;
|
||||
|
||||
// Replace combined entry with our new individual entries
|
||||
@@ -476,6 +566,7 @@ class Transaction extends AppModel {
|
||||
$this->create();
|
||||
if (!$this->save($transaction))
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
$transaction_stamp = $this->field('stamp');
|
||||
|
||||
// Set up our return ids array
|
||||
$ret['transaction_id'] = $this->id;
|
||||
@@ -503,6 +594,7 @@ class Transaction extends AppModel {
|
||||
|
||||
if (!empty($se)) {
|
||||
$se['transaction_id'] = $ret['transaction_id'];
|
||||
$se += array('effective_date' => $transaction_stamp);
|
||||
$result = $this->StatementEntry->addStatementEntry($se);
|
||||
$ret['entries'][$e_index]['StatementEntry'] = $result;
|
||||
if ($result['error']) {
|
||||
@@ -512,15 +604,22 @@ class Transaction extends AppModel {
|
||||
}
|
||||
}
|
||||
|
||||
if (($transaction['type'] == 'INVOICE' ||
|
||||
$transaction['type'] == 'RECEIPT') &&
|
||||
$subtype !== 'NSF' && !$ret['error']) {
|
||||
if ($transaction['account_id'] == $this->Account->accountReceivableAccountID()
|
||||
&& !$ret['error']) {
|
||||
$result = $this->StatementEntry->assignCredits
|
||||
(array('link' => array('Customer'),
|
||||
'conditions' => array('Customer.id' => $transaction['customer_id'])),
|
||||
($transaction['type'] == 'RECEIPT'
|
||||
(null,
|
||||
($transaction['crdr'] == 'CREDIT'
|
||||
? $ret['transaction_id']
|
||||
: null));
|
||||
: null),
|
||||
($transaction['crdr'] == 'CREDIT' && !empty($transaction['assign_charge_entry_id'])
|
||||
? $transaction['assign_charge_entry_id']
|
||||
: null),
|
||||
(!empty($transaction['disbursement_type'])
|
||||
? $transaction['disbursement_type']
|
||||
: null),
|
||||
$transaction['customer_id'],
|
||||
$transaction['lease_id']
|
||||
);
|
||||
|
||||
$ret['assigned'] = $result;
|
||||
if ($result['error'])
|
||||
@@ -546,7 +645,7 @@ class Transaction extends AppModel {
|
||||
// Enter the NSF
|
||||
// This is the transaction pulling money from the bank account
|
||||
// and recording it in the NSF account. It has nothing to do
|
||||
// with the customer statement (charges, payments, credits, etc).
|
||||
// with the customer statement (charges, disbursements, credits, etc).
|
||||
$bounce_result = $this->addDeposit
|
||||
(array('Transaction' => array(),
|
||||
'Entry' => array(array('tender_id' => null,
|
||||
@@ -596,20 +695,20 @@ class Transaction extends AppModel {
|
||||
$rollback['Transaction']['customer_id'] = $tender['Tender']['customer_id'];
|
||||
$rollback['Transaction']['amount'] = -1 * $tender['LedgerEntry']['amount'];
|
||||
|
||||
foreach ($nsf_ledger_entry['Transaction']['StatementEntry'] AS $payment) {
|
||||
if ($payment['type'] === 'SURPLUS') {
|
||||
$payment['type'] = 'VOID';
|
||||
$this->StatementEntry->id = $payment['id'];
|
||||
$this->StatementEntry->saveField('type', $payment['type']);
|
||||
foreach ($nsf_ledger_entry['Transaction']['StatementEntry'] AS $disbursement) {
|
||||
if ($disbursement['type'] === 'SURPLUS') {
|
||||
$disbursement['type'] = 'VOID';
|
||||
$this->StatementEntry->id = $disbursement['id'];
|
||||
$this->StatementEntry->saveField('type', $disbursement['type']);
|
||||
}
|
||||
else {
|
||||
$rollback['Entry'][] =
|
||||
array('type' => $payment['type'],
|
||||
'amount' => -1 * $payment['amount'],
|
||||
array('type' => $disbursement['type'],
|
||||
'amount' => -1 * $disbursement['amount'],
|
||||
'account_id' => $this->Account->nsfAccountID(),
|
||||
'customer_id' => $payment['customer_id'],
|
||||
'lease_id' => $payment['lease_id'],
|
||||
'charge_entry_id' => $payment['charge_entry_id'],
|
||||
'customer_id' => $disbursement['customer_id'],
|
||||
'lease_id' => $disbursement['lease_id'],
|
||||
'charge_entry_id' => $disbursement['charge_entry_id'],
|
||||
'effective_date' => $stamp,
|
||||
);
|
||||
}
|
||||
@@ -617,11 +716,13 @@ class Transaction extends AppModel {
|
||||
|
||||
// Record the transaction, which will un-pay previously paid
|
||||
// charges, void any credits, and other similar work.
|
||||
$rollback_result = $this->addTransaction($rollback['Transaction'], $rollback['Entry'], 'NSF');
|
||||
$this->pr(20, compact('rollback', 'rollback_result'));
|
||||
$ret['rollback'] = $rollback_result;
|
||||
if ($rollback_result['error'])
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
if (count($rollback['Entry'])) {
|
||||
$rollback_result = $this->addTransaction($rollback['Transaction'], $rollback['Entry'], 'NSF');
|
||||
$this->pr(20, compact('rollback', 'rollback_result'));
|
||||
$ret['rollback'] = $rollback_result;
|
||||
if ($rollback_result['error'])
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
}
|
||||
|
||||
// Add NSF Charge
|
||||
$charge_result = $this->addInvoice
|
||||
@@ -647,7 +748,8 @@ class Transaction extends AppModel {
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
|
||||
$ret['nsf_transaction_id'] = $ret['bounce']['transaction_id'];
|
||||
$ret['nsf_ledger_entry_id'] = $ret['rollback']['entries'][0]['DoubleEntry']['Entry1']['ledger_entry_id'];
|
||||
if (!empty($ret['rollback']))
|
||||
$ret['nsf_ledger_entry_id'] = $ret['rollback']['entries'][0]['DoubleEntry']['Entry1']['ledger_entry_id'];
|
||||
return $this->prReturn($ret + array('error' => false));
|
||||
}
|
||||
|
||||
@@ -698,7 +800,7 @@ class Transaction extends AppModel {
|
||||
}
|
||||
elseif ($table == 'StatementEntry') {
|
||||
$squery['fields'] = array_merge($squery['fields'],
|
||||
$this->StatementEntry->chargePaymentFields(true));
|
||||
$this->StatementEntry->chargeDisbursementFields(true));
|
||||
}
|
||||
else {
|
||||
$squery['fields'][] = "SUM({$table}.amount) AS total";
|
||||
|
||||
@@ -27,6 +27,8 @@ class Unit extends AppModel {
|
||||
'Lease',
|
||||
);
|
||||
|
||||
//var $default_log_level = array('log' => 30, 'show' => 15);
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -50,7 +52,7 @@ class Unit extends AppModel {
|
||||
}
|
||||
|
||||
function occupiedEnumValue() {
|
||||
return statusValue('OCCUPIED');
|
||||
return $this->statusValue('OCCUPIED');
|
||||
}
|
||||
|
||||
function conditionOccupied() {
|
||||
@@ -68,18 +70,71 @@ class Unit extends AppModel {
|
||||
return ('Unit.status <= ' . $this->statusValue('UNAVAILABLE'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: allowedStatusSet
|
||||
* - Returns the status set allowed for the given unit
|
||||
*/
|
||||
function allowedStatusSet($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
$this->id = $id;
|
||||
$old_status = $this->field('status');
|
||||
$old_val = $this->statusValue($old_status);
|
||||
$this->pr(17, compact('old_status', 'old_val'));
|
||||
|
||||
$enums = $this->activeStatusEnums();
|
||||
$this->pr(21, compact('enums'));
|
||||
|
||||
foreach ($enums AS $enum => $val) {
|
||||
if (($old_val < $this->occupiedEnumValue()) !=
|
||||
($val < $this->occupiedEnumValue())) {
|
||||
unset($enums[$enum]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->prReturn($enums);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: updateStatus
|
||||
* - Update the given unit to the given status
|
||||
*/
|
||||
function updateStatus($id, $status) {
|
||||
function updateStatus($id, $status, $check = false) {
|
||||
$this->prEnter(compact('id', 'status', 'check'));
|
||||
|
||||
/* if ($check) { */
|
||||
/* $old_status = $this->field('status'); */
|
||||
/* $this->pr(17, compact('old_status')); */
|
||||
/* if ($this->statusValue($old_status) < $this->occupiedEnumValue() && */
|
||||
/* $this->statusValue($status) >= $this->occupiedEnumValue()) */
|
||||
/* { */
|
||||
/* die("Can't transition a unit from vacant to occupied"); */
|
||||
/* return $this->prReturn(false); */
|
||||
/* } */
|
||||
/* if ($this->statusValue($old_status) >= $this->occupiedEnumValue() && */
|
||||
/* $this->statusValue($status) < $this->occupiedEnumValue()) */
|
||||
/* { */
|
||||
/* die("Can't transition a unit from occupied to vacant"); */
|
||||
/* return $this->prReturn(false); */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
if ($check) {
|
||||
if (!array_key_exists($status, $this->allowedStatusSet($id)))
|
||||
return $this->prReturn(false);
|
||||
}
|
||||
|
||||
$this->id = $id;
|
||||
//pr(compact('id', 'status'));
|
||||
$this->saveField('status', $status);
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
|
||||
@@ -164,9 +164,9 @@ echo $this->element('statement_entries', array
|
||||
//'grid_setup' => array('hiddengrid' => true),
|
||||
//'caption' => '<SPAN id="receipt-charges-caption"></SPAN>',
|
||||
'caption' => 'Collected ' . Inflector::pluralize($account['name']),
|
||||
'filter' => array('StatementEntry.type' => 'PAYMENT',
|
||||
'filter' => array(//'StatementEntry.type' => 'DISBURSEMENT',
|
||||
'ChargeEntry.account_id' => $account['id']),
|
||||
'exclude' => array('Account', 'Charge'),
|
||||
'exclude' => array('Account', 'Charge', 'Type'),
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ echo $this->element('ledger_entries', array
|
||||
"(". $current_ledger['name'] .")"),
|
||||
'filter' => array('Ledger.id' => $current_ledger['id']),
|
||||
'exclude' => array('Account', 'Amount', 'Cr/Dr', 'Balance',
|
||||
empty($account['payments']) ? 'Tender' : null),
|
||||
empty($account['receipts']) ? 'Tender' : null),
|
||||
'include' => array('Debit', 'Credit', 'Sub-Total'),
|
||||
)));
|
||||
|
||||
@@ -100,7 +100,7 @@ echo $this->element('ledger_entries', array
|
||||
'caption' => "Entire Ledger",
|
||||
'filter' => array('Account.id' => $account['id']),
|
||||
'exclude' => array('Account', 'Amount', 'Cr/Dr', 'Balance',
|
||||
empty($account['payments']) ? 'Tender' : null),
|
||||
empty($account['receipts']) ? 'Tender' : null),
|
||||
'include' => array('Debit', 'Credit', 'Sub-Total'),
|
||||
)));
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ echo($this->element
|
||||
'comment' => true,
|
||||
))) . "\n");
|
||||
|
||||
echo $form->submit('Update') . "\n";
|
||||
echo $form->submit(isset($this->data['Customer']) ? 'Update' : 'Add New Customer') . "\n";
|
||||
?>
|
||||
|
||||
<div CLASS="dynamic-set">
|
||||
@@ -268,7 +268,7 @@ echo $form->submit('Update') . "\n";
|
||||
<?php
|
||||
; // Alignment
|
||||
|
||||
echo $form->submit('Update') . "\n";
|
||||
echo $form->submit(isset($this->data['Customer']) ? 'Update' : 'Add New Customer') . "\n";
|
||||
echo $form->submit('Cancel', array('name' => 'cancel')) . "\n";
|
||||
echo $form->end() . "\n";
|
||||
echo '</div>' . "\n";
|
||||
|
||||
@@ -70,6 +70,8 @@ function showResponse(responseText, statusText) {
|
||||
if (statusText == 'success') {
|
||||
// get a clean slate
|
||||
//resetForm();
|
||||
// REVISIT <AP>: 20090806 Add to resetForm()
|
||||
updateCharges($("#customer-id").val());
|
||||
}
|
||||
else {
|
||||
alert('not successful??');
|
||||
@@ -109,8 +111,6 @@ function onRowSelect(grid_id, customer_id) {
|
||||
$(grid_id).getCell(customer_id, 'Customer-id') +
|
||||
'</A>');
|
||||
$("#receipt-customer-name").html($(grid_id).getCell(customer_id, 'Customer-name'));
|
||||
$("#receipt-balance").html("Calculating...");
|
||||
$("#receipt-charges-caption").html("Please Wait...");
|
||||
|
||||
// Hide the "no customer" message and show the current customer
|
||||
$(".customer-selection-invalid").hide();
|
||||
@@ -143,7 +143,8 @@ function addPaymentSource(flash) {
|
||||
addDiv('payment-entry-id', 'payment', 'payments', flash,
|
||||
// HTML section
|
||||
'<FIELDSET CLASS="payment subset">' +
|
||||
'<LEGEND>Payment #%{id} (%{remove})</LEGEND>' +
|
||||
<?php /* '<LEGEND>Payment #%{id} (%{remove})</LEGEND>' + */ ?>
|
||||
'<LEGEND>Payment</LEGEND>' +
|
||||
|
||||
'<DIV ID="payment-div-%{id}">' +
|
||||
<?php
|
||||
@@ -226,7 +227,10 @@ function switchPaymentType(paymentid_base, paymentid, radioid) {
|
||||
|
||||
|
||||
function updateChargesGrid(idlist) {
|
||||
$('#charge-entries-jqGrid').setPostDataItem('idlist', serialize(idlist));
|
||||
var dynamic_post = new Array();
|
||||
dynamic_post['idlist'] = idlist;
|
||||
|
||||
$('#charge-entries-jqGrid').setPostDataItem('dynamic_post_replace', serialize(dynamic_post));
|
||||
$('#charge-entries-jqGrid')
|
||||
.setGridParam({ page: 1 })
|
||||
.trigger("reloadGrid");
|
||||
@@ -238,6 +242,8 @@ function updateCharges(id) {
|
||||
url += '/'+id;
|
||||
|
||||
$('#charge-entries-jqGrid').clearGridData();
|
||||
$("#receipt-balance").html("Calculating...");
|
||||
$("#receipt-charges-caption").html("Please Wait...");
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
@@ -251,7 +257,13 @@ function updateCharges(id) {
|
||||
$('#receipt-balance').html(fmtCurrency($('entries',xml).attr('balance')));
|
||||
$("#receipt-charges-caption").html("Outstanding Charges");
|
||||
updateChargesGrid(ids);
|
||||
}
|
||||
},
|
||||
error: function(XMLHttpRequest, textStatus, errorThrown) {
|
||||
/* alert('ERROR'); */
|
||||
/* $('#debug').html('<P>request<BR>'+escape(XMLHttpRequest)); */
|
||||
/* $('#debug').append('<P>status<BR>'+escape(textStatus)); */
|
||||
/* $('#debug').append('<P>error<BR>'+escape(errorThrown)); */
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -354,14 +366,18 @@ echo $this->element('form_table',
|
||||
echo $form->submit('Generate Receipt') . "\n";
|
||||
?>
|
||||
|
||||
<?php /*
|
||||
<fieldset CLASS="payment superset">
|
||||
<legend>Payments</legend>
|
||||
*/ ?>
|
||||
<input type="hidden" id="payment-entry-id" value="0">
|
||||
<div id="payments"></div>
|
||||
<?php /*
|
||||
<fieldset> <legend>
|
||||
<a href="#" onClick="addPaymentSource(true); return false;">Add Another Payment</a>
|
||||
</legend> </fieldset>
|
||||
</fieldset>
|
||||
*/ ?>
|
||||
|
||||
<?php echo $form->end('Generate Receipt'); ?>
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ echo $this->element('statement_entries', array
|
||||
'filter' => array('Customer.id' => $customer['Customer']['id'],
|
||||
'type !=' => 'VOID'),
|
||||
'exclude' => array('Customer'),
|
||||
'sort_column' => 'Effective',
|
||||
'sort_order' => 'DESC',
|
||||
)));
|
||||
|
||||
|
||||
@@ -109,6 +111,8 @@ echo $this->element('ledger_entries', array
|
||||
'filter' => array('Customer.id' => $customer['Customer']['id'],
|
||||
'Account.id !=' => '-AR-'),
|
||||
'exclude' => array('Customer'),
|
||||
'sort_column' => 'Date',
|
||||
'sort_order' => 'DESC',
|
||||
)));
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ $cols['Relationship'] = array('index' => 'ContactsCustomer.type', 'formatt
|
||||
$cols['Name'] = array('index' => 'Customer.name', 'formatter' => 'longname');
|
||||
$cols['Last Name'] = array('index' => 'PrimaryContact.last_name', 'formatter' => 'name');
|
||||
$cols['First Name'] = array('index' => 'PrimaryContact.first_name', 'formatter' => 'name');
|
||||
$cols['Leases'] = array('index' => 'lease_count', 'formatter' => 'number');
|
||||
$cols['Leases'] = array('index' => 'current_lease_count', 'formatter' => 'number');
|
||||
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency');
|
||||
$cols['Comment'] = array('index' => 'Customer.comment', 'formatter' => 'comment');
|
||||
|
||||
|
||||
@@ -17,8 +17,9 @@ $cols['Unit'] = array('index' => 'Unit.name', 'formatter' =>
|
||||
|
||||
$cols['Comment'] = array('index' => 'StatementEntry.comment', 'formatter' => 'comment', 'width'=>150);
|
||||
|
||||
$cols['Charge'] = array('index' => 'charge', 'formatter' => 'currency');
|
||||
$cols['Payment'] = array('index' => 'payment', 'formatter' => 'currency');
|
||||
$cols['Type'] = array('index' => 'StatementEntry.type', 'formatter' => 'enum', 'width'=>120);
|
||||
$cols['Debit'] = array('index' => 'charge', 'formatter' => 'currency');
|
||||
$cols['Credit'] = array('index' => 'disbursement', 'formatter' => 'currency');
|
||||
|
||||
$cols['Applied'] = array('index' => "applied", 'formatter' => 'currency');
|
||||
$cols['Sub-Total'] = array('index' => 'subtotal-balance', 'formatter' => 'currency', 'sortable' => false);
|
||||
|
||||
@@ -19,13 +19,13 @@ class FormatHelper extends AppHelper {
|
||||
true));
|
||||
}
|
||||
|
||||
function currency($amount, $spans = false) {
|
||||
function currency($amount, $spans = false, $dollar_sign = null) {
|
||||
if (!isset($amount))
|
||||
return '-';
|
||||
//return null;
|
||||
|
||||
$currency = self::$number->currency($amount,
|
||||
'USD',
|
||||
isset($dollar_sign) ? $dollar_sign : 'USD',
|
||||
$spans ? array('before'=>'', 'after'=>'') : array());
|
||||
|
||||
if ($spans)
|
||||
|
||||
@@ -211,8 +211,9 @@ class GridHelper extends AppHelper {
|
||||
|
||||
// Incorporate all other user options
|
||||
if (isset($config))
|
||||
$this->jqGrid_options = array_merge_recursive($this->jqGrid_options, $config);
|
||||
$this->jqGrid_options = array_merge($this->jqGrid_options, $config);
|
||||
|
||||
//pr(compact('config') + array('jqGrid_options' => $this->jqGrid_options));
|
||||
echo $view->element('jqGrid', $this->jqGrid_options);
|
||||
|
||||
// Since we only have one instance of this class
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="apply-deposit input">' . "\n";
|
||||
|
||||
echo ('<DIV CLASS="apply-deposit grid-selection-text">' .
|
||||
|
||||
'Lease #' . $lease['number'] .
|
||||
' / Customer #' . $customer['id'] .
|
||||
': ' . $customer['name'] .
|
||||
' / Unit ' . $unit['name'] .
|
||||
|
||||
'<DIV CLASS="supporting">' .
|
||||
'<TABLE>' .
|
||||
|
||||
/* '<TR><TD CLASS="field">Balance:</TD><TD CLASS="value">' . */
|
||||
/* FormatHelper::currency($lease['stats']['balance']) . */
|
||||
/* '</TD></TR>' . */
|
||||
|
||||
'<TR><TD CLASS="field">Deposit:</TD><TD CLASS="value">' .
|
||||
FormatHelper::currency($depositBalance) .
|
||||
'</TD></TR>' .
|
||||
|
||||
'</TABLE>' .
|
||||
'</DIV>' .
|
||||
|
||||
'</DIV>' . "\n");
|
||||
|
||||
|
||||
echo $form->create(null, array('id' => 'apply-deposit-form',
|
||||
'url' => array('controller' => 'leases',
|
||||
'action' => 'apply_deposit')
|
||||
)
|
||||
);
|
||||
|
||||
echo $form->input("Customer.id",
|
||||
array('id' => 'customer-id',
|
||||
'type' => 'hidden',
|
||||
'value' => $customer['id']));
|
||||
|
||||
echo $form->input("Lease.id",
|
||||
array('id' => 'lease-id',
|
||||
'type' => 'hidden',
|
||||
'value' => $lease['id']));
|
||||
|
||||
echo $form->input("LedgerEntry.Account.id",
|
||||
array('id' => 'account-id',
|
||||
'type' => 'hidden',
|
||||
'value' => $account['id']));
|
||||
|
||||
|
||||
echo $this->element('form_table',
|
||||
array('class' => "item receipt transaction entry",
|
||||
//'with_name_after' => ':',
|
||||
'field_prefix' => 'Transaction',
|
||||
'fields' => array
|
||||
("stamp" => array('opts' => array('type' => 'text'),
|
||||
'between' => '<A HREF="#" ONCLICK="datepickerNow(\'TransactionStamp\'); return false;">Now</A>',
|
||||
),
|
||||
"amount" => array('prefix' => 'LedgerEntry',
|
||||
'opts' => array('value' => $depositBalance),
|
||||
),
|
||||
"comment" => array('opts' => array('size' => 50),
|
||||
),
|
||||
)));
|
||||
|
||||
echo $form->end('Utilize Deposit');
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
// Reset the form
|
||||
function resetForm() {
|
||||
datepickerNow('TransactionStamp');
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
$("#TransactionStamp")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
|
||||
resetForm();
|
||||
});
|
||||
--></script>
|
||||
|
||||
</div>
|
||||
@@ -1,80 +0,0 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="bad-debt input">' . "\n";
|
||||
|
||||
echo ('<DIV CLASS="bad-debt grid-selection-text">' .
|
||||
|
||||
'Lease #' . $lease['number'] .
|
||||
' / Customer #' . $customer['id'] .
|
||||
': ' . $customer['name'] .
|
||||
' / Unit ' . $unit['name'] .
|
||||
|
||||
'<DIV CLASS="supporting">' .
|
||||
'<TABLE>' .
|
||||
'<TR><TD CLASS="field">Balance:</TD><TD CLASS="value">'.$lease['stats']['balance'].'</TD></TR>' .
|
||||
'</TABLE>' .
|
||||
'</DIV>' .
|
||||
|
||||
'</DIV>' . "\n");
|
||||
|
||||
|
||||
echo $form->create(null, array('id' => 'receipt-form',
|
||||
'url' => array('controller' => 'transactions',
|
||||
'action' => 'postReceipt')));
|
||||
|
||||
echo $form->input("Customer.id",
|
||||
array('id' => 'customer-id',
|
||||
'type' => 'hidden',
|
||||
'value' => $customer['id']));
|
||||
|
||||
echo $form->input("Lease.id",
|
||||
array('id' => 'lease-id',
|
||||
'type' => 'hidden',
|
||||
'value' => $lease['id']));
|
||||
|
||||
echo $form->input("LedgerEntry.0.account_id",
|
||||
array('id' => 'account-id',
|
||||
'type' => 'hidden',
|
||||
'value' => $account['id']));
|
||||
|
||||
echo $form->input("LedgerEntry.0.amount",
|
||||
array('id' => 'amount',
|
||||
'type' => 'hidden',
|
||||
'value' => $lease['stats']['balance']));
|
||||
|
||||
|
||||
echo $this->element('form_table',
|
||||
array('class' => "item receipt transaction entry",
|
||||
//'with_name_after' => ':',
|
||||
'field_prefix' => 'Transaction',
|
||||
'fields' => array
|
||||
("stamp" => array('opts' => array('type' => 'text'),
|
||||
'between' => '<A HREF="#" ONCLICK="datepickerNow(\'TransactionStamp\'); return false;">Now</A>',
|
||||
),
|
||||
"comment" => array('opts' => array('size' => 50),
|
||||
),
|
||||
)));
|
||||
|
||||
echo $form->end('Write Off Remaining Balance');
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
// Reset the form
|
||||
function resetForm() {
|
||||
datepickerNow('TransactionStamp');
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
$("#TransactionStamp")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
|
||||
resetForm();
|
||||
});
|
||||
--></script>
|
||||
|
||||
</div>
|
||||
@@ -1,74 +1,106 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="account deposit">' . "\n";
|
||||
echo '<H2>Perform Bank Deposit</H2>' . "\n";
|
||||
echo '<P>Make sure to select the checkboxes below for only those types of currency (Cash, Check, etc) which you intend to actually deposit (you can see all the individual items by dropping down the list below the checkbox). Then, select the Deposit Account where you will make the deposit, and click "Perform Deposit" to close the books on the selected currency types and reset them to a zero balance. On the next page, you will be provided with a deposit slip to prepare the actual deposit.' . "\n";
|
||||
echo '<div class="refund input">' . "\n";
|
||||
echo '<H2>Issue Refund</H2>' . "\n";
|
||||
echo '<P>Enter the amount to refund, and the account to pay it from.' . "\n";
|
||||
echo '<P><BR>' . "\n";
|
||||
|
||||
//pr(compact('tillableAccount', 'depositableAccount'));
|
||||
|
||||
echo $form->create(null, array('id' => 'deposit-form',
|
||||
'url' => array('controller' => 'accounts',
|
||||
'action' => 'deposit')));
|
||||
|
||||
foreach ($tillableAccount AS $acct) {
|
||||
//$acct = $acct['Account'];
|
||||
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.checked',
|
||||
array(//'label' => $acct['Account']['name'],
|
||||
'type' => 'checkbox',
|
||||
'checked' => true,
|
||||
'value' => true,
|
||||
'label' => (" I have exactly " .
|
||||
FormatHelper::currency($acct['Account']['stats']['Ledger']['balance']) .
|
||||
" in " . ($acct['Account']['name'] === 'Cash'
|
||||
? 'Cash'
|
||||
: Inflector::pluralize($acct['Account']['name'])) .
|
||||
" and will be depositing it all.")
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.amount',
|
||||
array('type' => 'hidden',
|
||||
'value' => $acct['Account']['stats']['Ledger']['balance'],
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.account_id',
|
||||
array('type' => 'hidden',
|
||||
'value' => $acct['Account']['id'],
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.account_name',
|
||||
array('type' => 'hidden',
|
||||
'value' => $acct['Account']['name'],
|
||||
));
|
||||
echo "\n";
|
||||
|
||||
$grid_div_id = 'ledger_entries'.$acct['CurrentLedger']['id'].'-list';
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'ledger_id' => $acct['CurrentLedger']['id'],
|
||||
'no_account' => true,
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'grid_div_id' => $grid_div_id,
|
||||
'caption' => ('<A HREF="#" ONCLICK="$(\'#'.$grid_div_id.' .HeaderButton\').click();'.
|
||||
' return false;">Items in '.$acct['Account']['name'].' Ledger</A>'),
|
||||
'grid_setup' => array('hiddengrid' => true),
|
||||
),
|
||||
));
|
||||
if (isset($lease)) {
|
||||
$customer = $lease['Customer'];
|
||||
$unit = $lease['Unit'];
|
||||
}
|
||||
|
||||
$options = array();
|
||||
foreach ($depositableAccount AS $acct) {
|
||||
$options[$acct['Account']['id']] = $acct['Account']['name'];
|
||||
if (isset($customer['Customer']))
|
||||
$customer = $customer['Customer'];
|
||||
|
||||
if (isset($lease['Lease']))
|
||||
$lease = $lease['Lease'];
|
||||
|
||||
// We're not actually using a grid to select the customer / lease
|
||||
// but we could/should be, and the result would be selection-text
|
||||
echo ('<DIV CLASS="refund grid-selection-text">' .
|
||||
'<TABLE>' . "\n");
|
||||
|
||||
echo ('<TR><TD style="padding-right: 1em;">' . $customer['name'] . '</TD>' .
|
||||
' <TD>' . '(Customer #' . $customer['id'] . ')' . '</TD>' .
|
||||
'</TR>' . "\n");
|
||||
|
||||
if (isset($lease))
|
||||
echo ('<TR><TD style="padding-right: 1em;">' . 'Unit ' . $unit['name'] . '</TD>' .
|
||||
' <TD>' . '(Lease #' . $lease['number'] . ')' . '</TD>' .
|
||||
'</TR>' . "\n");
|
||||
|
||||
echo ('<TR><TD style="padding-right: 1em;">Refundable Balance:</TD>' .
|
||||
' <TD>' . FormatHelper::currency($balance) . '</TD>' .
|
||||
'</TR>' . "\n");
|
||||
|
||||
echo ('</TABLE>' .
|
||||
'</DIV>' . "\n");
|
||||
|
||||
|
||||
echo $form->create(null, array('id' => 'refund-form',
|
||||
'url' => array('controller' => 'transactions',
|
||||
'action' => 'postRefund')));
|
||||
|
||||
|
||||
// REVISIT <AP>: 20090805
|
||||
// Add Tender information to log specifically _how_ refund was paid.
|
||||
|
||||
echo $this->element('form_table',
|
||||
array('class' => "item refund transaction entry",
|
||||
//'with_name_after' => ':',
|
||||
'field_prefix' => 'Transaction',
|
||||
'fields' => array
|
||||
("stamp" => array('opts' =>
|
||||
array('type' => 'text'),
|
||||
'between' => '<A HREF="#" ONCLICK="datepickerNow(\'TransactionStamp\'); return false;">Now</A>',
|
||||
),
|
||||
"amount" => array('prefix' => 'Entry.0',
|
||||
'opts' =>
|
||||
array('value' =>
|
||||
FormatHelper::currency($balance, false, ''),
|
||||
),
|
||||
),
|
||||
"account_id" => array('prefix' => 'Entry.0',
|
||||
'name' => 'Account',
|
||||
'opts' =>
|
||||
array('options' => $refundAccounts,
|
||||
'value' => $defaultAccount,
|
||||
),
|
||||
),
|
||||
"comment" => array('opts' => array('size' => 50),
|
||||
),
|
||||
))) . "\n";
|
||||
|
||||
echo $form->input("Customer.id",
|
||||
array('type' => 'hidden',
|
||||
'value' => $customer['id'])) . "\n";
|
||||
|
||||
if (isset($lease['id']))
|
||||
echo $form->input("Lease.id",
|
||||
array('type' => 'hidden',
|
||||
'value' => $lease['id'])) . "\n";
|
||||
|
||||
echo $form->end('Issue Refund');
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
// Reset the form
|
||||
function resetForm() {
|
||||
datepickerNow('TransactionStamp');
|
||||
}
|
||||
|
||||
echo $form->input('Deposit.Account.id', array('label' => 'Deposit Account ',
|
||||
'options' => $options));
|
||||
echo $form->end('Perform Deposit');
|
||||
$(document).ready(function(){
|
||||
$("#TransactionStamp")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
|
||||
/* End page div */
|
||||
echo '</div>' . "\n";
|
||||
resetForm();
|
||||
});
|
||||
--></script>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -88,6 +88,8 @@ echo $this->element('statement_entries', array
|
||||
'filter' => array('Lease.id' => $lease['id']),
|
||||
'include' => array('Through'),
|
||||
'exclude' => array('Customer', 'Lease', 'Unit'),
|
||||
'sort_column' => 'Effective',
|
||||
'sort_order' => 'DESC',
|
||||
)));
|
||||
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ echo $this->element('ledger_entries', array
|
||||
'filter' => array('Ledger.id' => $ledger['id']),
|
||||
'exclude' => array('Ledger', 'Account',
|
||||
'Amount', 'Cr/Dr', 'Balance',
|
||||
empty($account['payments']) ? 'Tender' : null),
|
||||
empty($account['receipts']) ? 'Tender' : null),
|
||||
'include' => array('Debit', 'Credit', 'Sub-Total'),
|
||||
)));
|
||||
|
||||
|
||||
@@ -15,15 +15,18 @@ $customer = $entry['Customer'];
|
||||
$lease = $entry['Lease'];
|
||||
$entry = $entry['StatementEntry'];
|
||||
|
||||
$Ttype = ucfirst(strtolower($transaction['type']));
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array('ID', $entry['id']);
|
||||
$rows[] = array('Transaction', $html->link('#'.$transaction['id'],
|
||||
$rows[] = array($Ttype, $html->link('#'.$transaction['id'],
|
||||
array('controller' => 'transactions',
|
||||
'action' => 'view',
|
||||
$transaction['id'])));
|
||||
$rows[] = array('Timestamp', FormatHelper::datetime($transaction['stamp']));
|
||||
$rows[] = array('Effective', FormatHelper::date($entry['effective_date']));
|
||||
$rows[] = array('Through', FormatHelper::date($entry['through_date']));
|
||||
if (in_array($entry['type'], array('CHARGE', 'PAYMENT')))
|
||||
$rows[] = array('Through', FormatHelper::date($entry['through_date']));
|
||||
$rows[] = array('Type', $entry['type']);
|
||||
$rows[] = array('Amount', FormatHelper::currency($entry['amount']));
|
||||
$rows[] = array('Account', $html->link($account['name'],
|
||||
@@ -56,12 +59,12 @@ echo $this->element('table',
|
||||
*/
|
||||
|
||||
if (strtoupper($entry['type']) === 'CHARGE') {
|
||||
$applied_caption = "Payments Applied";
|
||||
$applied_caption = "Disbursements Applied";
|
||||
//$remaining_caption = "Charge Balance";
|
||||
}
|
||||
else {
|
||||
$applied_caption = "Applied to Charges";
|
||||
//$remaining_caption = "Payment Balance";
|
||||
$applied_caption = "Disbursed to Charges";
|
||||
//$remaining_caption = "Disbursement Balance";
|
||||
}
|
||||
|
||||
$remaining_caption = "Remaining Balance";
|
||||
|
||||
89
site/views/transactions/bad_debt.ctp
Normal file
89
site/views/transactions/bad_debt.ctp
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="bad-debt input">' . "\n";
|
||||
|
||||
if (isset($lease)) {
|
||||
$customer = $lease['Customer'];
|
||||
$unit = $lease['Unit'];
|
||||
}
|
||||
|
||||
if (isset($customer['Customer']))
|
||||
$customer = $customer['Customer'];
|
||||
|
||||
if (isset($lease['Lease']))
|
||||
$lease = $lease['Lease'];
|
||||
|
||||
// We're not actually using a grid to select the customer / lease
|
||||
// but we could/should be, and the result would be selection-text
|
||||
echo ('<DIV CLASS="bad-debt grid-selection-text">' .
|
||||
'<TABLE>' . "\n");
|
||||
|
||||
echo ('<TR><TD style="padding-right: 1em;">' . $customer['name'] . '</TD>' .
|
||||
' <TD>' . '(Customer #' . $customer['id'] . ')' . '</TD>' .
|
||||
'</TR>' . "\n");
|
||||
|
||||
if (isset($lease))
|
||||
echo ('<TR><TD style="padding-right: 1em;">' . 'Unit ' . $unit['name'] . '</TD>' .
|
||||
' <TD>' . '(Lease #' . $lease['number'] . ')' . '</TD>' .
|
||||
'</TR>' . "\n");
|
||||
|
||||
echo ('<TR><TD style="padding-right: 1em;">Remaining Balance:</TD>' .
|
||||
' <TD>' . FormatHelper::currency($balance) . '</TD>' .
|
||||
'</TR>' . "\n");
|
||||
|
||||
echo ('</TABLE>' .
|
||||
'</DIV>' . "\n");
|
||||
|
||||
|
||||
echo $form->create(null, array('id' => 'receipt-form',
|
||||
'url' => array('controller' => 'transactions',
|
||||
'action' => 'postWriteOff'))) . "\n";
|
||||
|
||||
echo $form->input("Customer.id",
|
||||
array('type' => 'hidden',
|
||||
'value' => $customer['id'])) . "\n";
|
||||
|
||||
if (isset($lease['id']))
|
||||
echo $form->input("Lease.id",
|
||||
array('type' => 'hidden',
|
||||
'value' => $lease['id'])) . "\n";
|
||||
|
||||
echo $form->input("Entry.0.amount",
|
||||
array('type' => 'hidden',
|
||||
'value' => $balance)) . "\n";
|
||||
|
||||
echo $this->element('form_table',
|
||||
array('class' => "item receipt transaction entry",
|
||||
//'with_name_after' => ':',
|
||||
'field_prefix' => 'Transaction',
|
||||
'fields' => array
|
||||
("stamp" => array('opts' => array('type' => 'text'),
|
||||
'between' => '<A HREF="#" ONCLICK="datepickerNow(\'TransactionStamp\'); return false;">Now</A>',
|
||||
),
|
||||
"comment" => array('opts' => array('size' => 50),
|
||||
),
|
||||
))) . "\n";
|
||||
|
||||
echo $form->end('Write Off Remaining Balance');
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
// Reset the form
|
||||
function resetForm() {
|
||||
datepickerNow('TransactionStamp');
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
$("#TransactionStamp")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
|
||||
resetForm();
|
||||
});
|
||||
--></script>
|
||||
|
||||
</div>
|
||||
@@ -67,7 +67,11 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
* Statement Entries
|
||||
*/
|
||||
|
||||
if ($transaction['type'] === 'INVOICE' || $transaction['type'] === 'RECEIPT') {
|
||||
if ($transaction['type'] === 'INVOICE' ||
|
||||
$transaction['type'] === 'RECEIPT' ||
|
||||
$transaction['type'] === 'CREDIT_NOTE' ||
|
||||
$transaction['type'] === 'PAYMENT'
|
||||
) {
|
||||
echo $this->element('statement_entries', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
|
||||
27
site/views/units/edit.ctp
Normal file
27
site/views/units/edit.ctp
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="unit edit">' . "\n";
|
||||
|
||||
echo $form->create('Unit', array('action' => 'edit')) . "\n";
|
||||
echo $form->input('id') . "\n";
|
||||
|
||||
echo($this->element
|
||||
('form_table',
|
||||
array('class' => 'item unit detail',
|
||||
'caption' => isset($this->data['Unit']) ? 'Edit Unit' : 'New Unit',
|
||||
'fields' => array
|
||||
('name' => true,
|
||||
'unit_size_id' => true,
|
||||
'status' => array('opts' =>
|
||||
array('options' => $statusEnums,
|
||||
),
|
||||
),
|
||||
'deposit' => true,
|
||||
'rent' => true,
|
||||
'comment' => true,
|
||||
))) . "\n");
|
||||
|
||||
echo $form->submit('Update') . "\n";
|
||||
echo $form->submit('Cancel', array('name' => 'cancel')) . "\n";
|
||||
echo $form->end() . "\n";
|
||||
echo '</div>' . "\n";
|
||||
@@ -18,6 +18,9 @@ security deposits charged.
|
||||
Customer Selection on the Receipt Page is broken.
|
||||
(Selecting a row and waiting for the update).
|
||||
|
||||
Allow waiving a complete charge, even if it already has payments
|
||||
applied (at the moment, we just can waive the charge balance).
|
||||
|
||||
Get Petty Cash working. We'll need to add one or more expense
|
||||
accounts. We'll also need to implement purchase order
|
||||
functionality, or at least simple an expense page.
|
||||
|
||||
Reference in New Issue
Block a user