Compare commits

..

2 Commits

42 changed files with 1224 additions and 1690 deletions

View File

@@ -644,17 +644,6 @@ CREATE TABLE `pmgr_customers` (
-- contacts_customers table? -- contacts_customers table?
`primary_contact_id` INT(10) UNSIGNED NOT NULL, `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, `comment` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
@@ -855,8 +844,8 @@ CREATE TABLE `pmgr_accounts` (
-- normal circumstances, when a debit occurs. -- normal circumstances, when a debit occurs.
-- `trackable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1, -- `trackable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
`deposits` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for deposits? `deposits` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for deposits?
`invoices` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for invoices? `charges` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for charges?
`receipts` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for receipts? `payments` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for payments?
`refunds` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for refunds? `refunds` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for refunds?
-- Security Level -- Security Level
@@ -884,21 +873,20 @@ INSERT INTO `pmgr_accounts` (`type`, `name`)
-- us identify how serious the NSF situation is. -- us identify how serious the NSF situation is.
('ASSET', 'NSF' ), ('ASSET', 'NSF' ),
('LIABILITY', 'A/P' ); ('LIABILITY', 'A/P' );
INSERT INTO `pmgr_accounts` (`type`, `name`, `receipts`, `refunds`) INSERT INTO `pmgr_accounts` (`type`, `name`, `payments`, `refunds`)
VALUES VALUES
('ASSET', 'Cash', 1, 0), ('ASSET', 'Cash', 1, 1),
('ASSET', 'Check', 1, 0), ('ASSET', 'Check', 1, 0),
('ASSET', 'Money Order', 1, 0), ('ASSET', 'Money Order', 1, 0),
('ASSET', 'ACH', 1, 0), ('ASSET', 'ACH', 1, 0),
('ASSET', 'Closing', 0, 0), -- REVISIT <AP>: Temporary ('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`) INSERT INTO `pmgr_accounts` (`type`, `name`, `refunds`, `deposits`)
VALUES VALUES
-- REVISIT <AP>: 20090710 : We probably don't really want petty cash depositable. -- REVISIT <AP>: 20090710 : We probably don't really want petty cash depositable.
-- This is just for testing our deposit code -- This is just for testing our deposit code
('ASSET', 'Petty Cash', 1, 1); ('ASSET', 'Petty Cash', 1, 1);
INSERT INTO `pmgr_accounts` (`type`, `name`, `invoices`) INSERT INTO `pmgr_accounts` (`type`, `name`, `charges`)
VALUES VALUES
('LIABILITY', 'Tax', 1), ('LIABILITY', 'Tax', 1),
('LIABILITY', 'Security Deposit', 1), ('LIABILITY', 'Security Deposit', 1),
@@ -961,19 +949,14 @@ DROP TABLE IF EXISTS `pmgr_transactions`;
CREATE TABLE `pmgr_transactions` ( CREATE TABLE `pmgr_transactions` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
-- REVISIT <AP>: 20090804 `type` ENUM('INVOICE',
-- I'm not sure about most of these terms. 'RECEIPT',
-- 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', 'DEPOSIT',
'CLOSE', -- Essentially an internal (not accounting) transaction 'CLOSE',
-- 'CREDIT', -- 'CREDIT',
-- 'REFUND', -- 'REFUND',
'TRANSFER') -- Unsure of TRANSFERs use -- 'WAIVER',
'TRANSFER')
NOT NULL, NOT NULL,
`stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
@@ -998,6 +981,10 @@ CREATE TABLE `pmgr_transactions` (
-- (e.g. A/R, Bank, etc) -- (e.g. A/R, Bank, etc)
`account_id` INT(10) UNSIGNED DEFAULT NULL, `account_id` INT(10) UNSIGNED DEFAULT NULL,
`ledger_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', `crdr` ENUM('DEBIT',
'CREDIT') 'CREDIT')
DEFAULT NULL, DEFAULT NULL,
@@ -1013,9 +1000,9 @@ CREATE TABLE `pmgr_transactions` (
) ENGINE=MyISAM DEFAULT CHARSET=utf8; ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- -- ---------------------------------------------------------------------- -- ----------------------------------------------------------------------
-- -- ---------------------------------------------------------------------- -- ----------------------------------------------------------------------
-- -- TABLE pmgr_ledger_entries -- TABLE pmgr_ledger_entries
DROP TABLE IF EXISTS `pmgr_ledger_entries`; DROP TABLE IF EXISTS `pmgr_ledger_entries`;
CREATE TABLE `pmgr_ledger_entries` ( CREATE TABLE `pmgr_ledger_entries` (
@@ -1026,6 +1013,9 @@ CREATE TABLE `pmgr_ledger_entries` (
-- The account/ledger of the entry -- The account/ledger of the entry
`account_id` INT(10) UNSIGNED NOT NULL, `account_id` INT(10) UNSIGNED NOT NULL,
`ledger_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', `crdr` ENUM('DEBIT',
'CREDIT') 'CREDIT')
NOT NULL, NOT NULL,
@@ -1038,9 +1028,9 @@ CREATE TABLE `pmgr_ledger_entries` (
) ENGINE=MyISAM DEFAULT CHARSET=utf8; ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- -- ---------------------------------------------------------------------- -- ----------------------------------------------------------------------
-- -- ---------------------------------------------------------------------- -- ----------------------------------------------------------------------
-- -- TABLE pmgr_double_entries -- TABLE pmgr_double_entries
DROP TABLE IF EXISTS `pmgr_double_entries`; DROP TABLE IF EXISTS `pmgr_double_entries`;
CREATE TABLE `pmgr_double_entries` ( CREATE TABLE `pmgr_double_entries` (
@@ -1063,34 +1053,27 @@ CREATE TABLE `pmgr_double_entries` (
) ENGINE=MyISAM DEFAULT CHARSET=utf8; ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- -- ---------------------------------------------------------------------- -- ----------------------------------------------------------------------
-- -- ---------------------------------------------------------------------- -- ----------------------------------------------------------------------
-- -- TABLE pmgr_statement_entries -- TABLE pmgr_statement_entries
DROP TABLE IF EXISTS `pmgr_statement_entries`; DROP TABLE IF EXISTS `pmgr_statement_entries`;
CREATE TABLE `pmgr_statement_entries` ( CREATE TABLE `pmgr_statement_entries` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
-- REVISIT <AP>: 20090804 `type` ENUM('CHARGE',
-- I'm not sure about most of these terms. 'PAYMENT',
-- Just as long as they're distinct though... I can rename them later 'SURPLUS',
`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 -- REVISIT <AP>: 20090730
-- VOID is used for handling NSF and perhaps charge reversals. -- VOID is just to test out a theory
-- It's not clear this is the best way to handle these things. -- for handling NSF and charge reversals
'WAIVE',
'VOID') 'VOID')
NOT NULL, NOT NULL,
`transaction_id` INT(10) UNSIGNED NOT NULL, `transaction_id` INT(10) UNSIGNED NOT NULL,
-- Effective date is when the charge/disbursement/transfer actually -- Effective date is when the charge/payment/transfer actually
-- takes effect (since it may not be at the time of the transaction). -- 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, -- Through date is used if/when a charge covers a certain time period,
-- like rent. A security deposit, for example, would not use the -- like rent. A security deposit, for example, would not use the
@@ -1107,15 +1090,12 @@ CREATE TABLE `pmgr_statement_entries` (
`amount` FLOAT(12,2) NOT NULL, `amount` FLOAT(12,2) NOT NULL,
-- The account of the entry -- Account ID is used only for those statement entries that have
-- REVISIT <AP>: 20090720 -- a guaranteed single ledger entry, and thus single account.
-- We don't want to confuse statement entries with ledger entries, -- Right now, this is only used for charges.
-- yet we're including account here. It doesn't feel right, but at
-- the same time, it will allow us to show _what_ was charged for
-- in the statement. Keeping it for now...
`account_id` INT(10) UNSIGNED DEFAULT NULL, `account_id` INT(10) UNSIGNED DEFAULT NULL,
-- Allow the disbursement to reconcile against the charge -- Allow the payment to reconcile against the charge
`charge_entry_id` INT(10) UNSIGNED DEFAULT NULL, `charge_entry_id` INT(10) UNSIGNED DEFAULT NULL,
`comment` VARCHAR(255) DEFAULT NULL, `comment` VARCHAR(255) DEFAULT NULL,
@@ -1124,6 +1104,27 @@ CREATE TABLE `pmgr_statement_entries` (
) ENGINE=MyISAM DEFAULT CHARSET=utf8; ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ----------------------------------------------------------------------
-- ----------------------------------------------------------------------
-- TABLE pmgr_statement_entries_ledger_entries
-- TABLE pmgr_statement_fractions
DROP TABLE IF EXISTS `pmgr_statement_fractions`;
CREATE TABLE `pmgr_statement_fractions` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
-- The two entries that make up a "double entry"
`statement_entry_id` INT(10) UNSIGNED NOT NULL,
`ledger_entry_id` INT(10) UNSIGNED NOT NULL,
`amount` FLOAT(12,2) NOT NULL,
`comment` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ---------------------------------------------------------------------- -- ----------------------------------------------------------------------
-- ---------------------------------------------------------------------- -- ----------------------------------------------------------------------
-- TABLE pmgr_tender_types -- TABLE pmgr_tender_types

View File

@@ -50,19 +50,19 @@ Operations to be functional
X - Assess NSF Fees X - Assess NSF Fees
X - Determine Lease Paid-Through status X - Determine Lease Paid-Through status
- Report: List of customers overdue - Report: List of customers overdue
X - Flag unit as overlocked - Flag unit as overlocked
X - Flag unit as evicting - Flag unit as evicting
X - Flag unit as normal status - Flag unit as normal status
X - Flag unit as dirty - Flag unit as dirty
- Enter notes when communicating with Customer - Enter notes when communicating with Customer
X - Accept pre-payments X - Accept pre-payments
X - Record Customer Move-Out from Unit X - Record Customer Move-Out from Unit
X - Record utilization of Security Deposit ? - Record utilization of Security Deposit
X - Record issuing of a refund - Record issuing of a refund
- Record Deposit into Petty Cash - Record Deposit into Petty Cash
- Record Payment from Petty Cash to expenses - Record Payment from Petty Cash to expenses
X - Record Petty Cash to refund. - Record Petty Cash to refund.
X - Write Off Bad Debt ? - Write Off Bad Debt
X - Perform a Deposit X - Perform a Deposit
X - Close the Books (nightly / weekly, etc) X - Close the Books (nightly / weekly, etc)
X - Determine Rents Collected for a given period. X - Determine Rents Collected for a given period.

View File

@@ -1098,6 +1098,17 @@ foreach $row (@{query($sdbh, $query)}) {
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'statement_entry_id'} $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'statement_entry_id'}
= $newdb{'tables'}{'statement_entries'}{'autoid'}; = $newdb{'tables'}{'statement_entries'}{'autoid'};
# Add the Charge Statement Entry Fraction
addRow('statement_fractions', {
'statement_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'statement_entry_id'},
'ledger_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'credit_entry_id'},
'amount' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'amount'},
#'comment' => "Charge: $row->{'ChargeID'}; Ledger: $row->{'LedgerID'}",
});
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'statement_fraction_id'}
= $newdb{'tables'}{'statement_fractions'}{'autoid'};
next unless $row->{'TaxAmount'}; next unless $row->{'TaxAmount'};
# # Add the tax charge entry # # Add the tax charge entry
@@ -1308,13 +1319,19 @@ foreach $row (@{query($sdbh, $query)})
# 'effective_date' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'effective_date'}, # 'effective_date' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'effective_date'},
# 'effective_date' => $effective_date; # 'effective_date' => $effective_date;
# 'through_date' => $through_date; # 'through_date' => $through_date;
'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'}, 'lease_id' => ($row->{'entry_type'} eq 'CREDIT'
? 0
: $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'}),
'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'}, 'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'},
'amount' => $reconcile_amount, 'amount' => $reconcile_amount,
'account_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_account_id'}, 'account_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_account_id'},
'debit_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_entry_id'},
}; };
$row->{'ChargeID'} = undef
if $row->{'entry_type'} eq 'CREDIT';
# Update the receipt & tender customer_id, now that we have payment info # Update the receipt & tender customer_id, now that we have payment info
$newdb{'tables'}{'transactions'}{'rows'}[ $newdb{'tables'}{'transactions'}{'rows'}[
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'receipt_id'} $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'receipt_id'}
@@ -1331,14 +1348,13 @@ foreach $row (@{query($sdbh, $query)})
# Add the Payment Statement Entry # Add the Payment Statement Entry
addRow('statement_entries', { addRow('statement_entries', {
'transaction_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'receipt_id'}, 'transaction_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'receipt_id'},
'type' => 'DISBURSEMENT', 'type' => $row->{'entry_type'} || 'PAYMENT',
# 'effective_date' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'effective_date'}, # 'effective_date' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'effective_date'},
# 'through_date' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'through_date'}, # 'through_date' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'through_date'},
'effective_date' => $effective_date, 'effective_date' => $effective_date,
'through_date' => $through_date, 'through_date' => $through_date,
'customer_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'customer_id'}, 'customer_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'customer_id'},
'lease_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'lease_id'}, 'lease_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'lease_id'},
'account_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'account_id'},
'amount' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'amount'}, 'amount' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'amount'},
'charge_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'statement_entry_id'}, 'charge_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'statement_entry_id'},
'comment' => $comment, 'comment' => $comment,
@@ -1346,6 +1362,17 @@ foreach $row (@{query($sdbh, $query)})
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'statement_entry_id'} $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'statement_entry_id'}
= $newdb{'tables'}{'statement_entries'}{'autoid'}; = $newdb{'tables'}{'statement_entries'}{'autoid'};
# Add the Charge Statement Entry Fraction
addRow('statement_fractions', {
'statement_entry_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'statement_entry_id'},
'ledger_entry_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'debit_entry_id'},
'amount' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'amount'},
});
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'statement_fraction_id'}
= $newdb{'tables'}{'statement_fractions'}{'autoid'};
} }
@@ -1466,25 +1493,6 @@ 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 ## Build the Database
@@ -1544,21 +1552,6 @@ $query = "UPDATE pmgr_units U, pmgr_leases L
WHERE L.unit_id = U.id AND L.close_date IS NULL"; WHERE L.unit_id = U.id AND L.close_date IS NULL";
query($db_handle, $query); 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 ## Invoice/Receipt totals

View File

@@ -47,8 +47,6 @@ class AppController extends Controller {
array('name' => 'Customers', 'url' => array('controller' => 'customers', 'action' => 'index')), array('name' => 'Customers', 'url' => array('controller' => 'customers', 'action' => 'index')),
array('name' => 'Accounts', 'url' => array('controller' => 'accounts', 'action' => 'index')), array('name' => 'Accounts', 'url' => array('controller' => 'accounts', 'action' => 'index')),
array('name' => 'Debug', 'header' => true), 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' => 'Contacts', 'url' => array('controller' => 'contacts', 'action' => 'index')),
array('name' => 'Ledgers', 'url' => array('controller' => 'ledgers', 'action' => 'index')), array('name' => 'Ledgers', 'url' => array('controller' => 'ledgers', 'action' => 'index')),
array('name' => 'New Ledgers', 'url' => array('controller' => 'accounts', 'action' => 'newledger')), array('name' => 'New Ledgers', 'url' => array('controller' => 'accounts', 'action' => 'newledger')),
@@ -189,26 +187,17 @@ class AppController extends Controller {
// This SHOULD always be set, except when debugging // This SHOULD always be set, except when debugging
if (isset($params['post'])) if (isset($params['post']))
$params['post'] = unserialize($params['post']); $params['post'] = unserialize($params['post']);
else
$params['post'] = array();
// Unserialize our complex structure of dynamic post data // Unserialize our complex structure of dynamic post data
if (isset($params['dynamic_post'])) if (isset($params['dynamic_post']))
$params['dynamic_post'] = unserialize($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 // Merge the static and dynamic post data
if (!empty($params['dynamic_post'])) 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']);
$params['post'] = array_merge_recursive($params['post'], $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 // This SHOULD always be set, except when debugging
if (!isset($params['post']['fields'])) if (!isset($params['post']['fields']))
@@ -691,7 +680,7 @@ class AppController extends Controller {
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) { function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
// Don't create any links if ordered not to. // Don't create any links if ordered not to.
if (isset($params['post']['nolinks'])) if (isset($params['nolinks']))
return; return;
foreach ($links AS $table => $fields) { foreach ($links AS $table => $fields) {

View File

@@ -114,48 +114,20 @@ class AppModel extends Model {
$line = $caller['line']; $line = $caller['line'];
// So, this caller holds the calling function name // So, this caller holds the calling function name
$caller = $trace[0]; $caller = array_shift($trace);
$function = $caller['function']; $function = $caller['function'];
$class = $caller['class']; $class = $caller['class'];
//$class = $this->name; //$class = $this->name;
// Use class or function specific log level if available // Adjust the log level from default, if necessary
if (isset($this->class_log_level[$class])) if (isset($this->class_log_level[$class]))
$log_level = $this->class_log_level[$class]; $log_level = $this->class_log_level[$class];
if (isset($this->function_log_level["{$class}-{$function}"])) if (isset($this->function_log_level["{$class}-{$function}"]))
$log_level = $this->function_log_level["{$class}-{$function}"]; $log_level = $this->function_log_level["{$class}-{$function}"];
if (isset($this->min_log_level))
// Establish log level minimums $log_level = max($log_level, $this->min_log_level);
$min_log_level = $this->min_log_level; if (isset($this->max_log_level))
if (is_array($this->min_log_level)) { $log_level = min($log_level, $this->max_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 the level is insufficient, bail out
if ($level > $log_level) if ($level > $log_level)
@@ -169,87 +141,13 @@ class AppModel extends Model {
$mixed = $chk + array($mixed); $mixed = $chk + array($mixed);
} }
static $pr_unique_number = 0; echo '<DIV CLASS="pr-caller">';
$pr_id = 'pr-section-class-' . $class . '-print-' . (++$pr_unique_number); echo '<strong>' . substr(str_replace(ROOT, '', $file), 1) . '</strong>';
$pr_trace_id = $pr_id . '-trace'; echo ' (line <strong>' . $line . '</strong>)';
$pr_output_id = $pr_id . '-output'; echo ' : level-' . $level;
echo '</DIV>' . "\n";
$pr_entire_base_class = "pr-section"; pr(array("{$class}::{$function}()" => $mixed), false, false);
$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) { function pr($level, $mixed, $checkpoint = null) {

View File

@@ -31,39 +31,6 @@
* You can also use this to include or require any files in your application. * 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. * 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) * This is related to Ticket #470 (https://trac.cakephp.org/ticket/470)

View File

@@ -138,18 +138,17 @@ class AccountsController extends AppController {
$this->redirect(array('action'=>'index')); $this->redirect(array('action'=>'index'));
} }
$this->Account->recursive = -1;
$account = $this->Account->read(null, $id);
$account = $account['Account'];
$payment_accounts = $this->Account->collectableAccounts(); $payment_accounts = $this->Account->collectableAccounts();
//$payment_accounts[$this->Account->nameToID('Closing')] = 'Closing'; //$payment_accounts[$this->Account->nameToID('Closing')] = 'Closing';
//$payment_accounts[$this->Account->nameToID('Equity')] = 'Equity'; //$payment_accounts[$this->Account->nameToID('Equity')] = 'Equity';
//$payment_accounts[$id] = 'Reversals'; $default_accounts = array_diff_key($this->Account->paymentAccounts(),
$default_accounts = array_diff_key($this->Account->receiptAccounts(),
array($this->Account->concessionAccountID() => 1)); array($this->Account->concessionAccountID() => 1));
$this->set(compact('payment_accounts', 'default_accounts')); $this->set(compact('payment_accounts', 'default_accounts'));
$this->Account->recursive = -1;
$account = $this->Account->read(null, $id);
$account = $account['Account'];
$title = ($account['name'] . ': Collected Report'); $title = ($account['name'] . ': Collected Report');
$this->set(compact('account', 'title')); $this->set(compact('account', 'title'));
} }

View File

@@ -50,6 +50,7 @@ class CustomersController extends AppController {
('link' => ('link' =>
array(// Models array(// Models
'PrimaryContact', 'PrimaryContact',
'CurrentLease' => array('fields' => array()),
), ),
); );
} }
@@ -63,19 +64,19 @@ class CustomersController extends AppController {
function gridDataFields(&$params, &$model) { function gridDataFields(&$params, &$model) {
$fields = parent::gridDataFields($params, $model); $fields = parent::gridDataFields($params, $model);
$fields[] = ('COUNT(DISTINCT CurrentLease.id) AS lease_count');
return array_merge($fields, return array_merge($fields,
$this->Customer->StatementEntry->chargeDisbursementFields(true)); $this->Customer->StatementEntry->chargePaymentFields(true));
} }
function gridDataConditions(&$params, &$model) { function gridDataConditions(&$params, &$model) {
$conditions = parent::gridDataConditions($params, $model); $conditions = parent::gridDataConditions($params, $model);
if ($params['action'] === 'current') { if ($params['action'] === 'current') {
$conditions[] = array('Customer.current_lease_count >' => 0); $conditions[] = 'CurrentLease.id IS NOT NULL';
} }
elseif ($params['action'] === 'past') { elseif ($params['action'] === 'past') {
$conditions[] = array('Customer.current_lease_count' => 0); $conditions[] = 'CurrentLease.id IS NULL';
$conditions[] = array('Customer.past_lease_count >' => 0);
} }
return $conditions; return $conditions;
@@ -98,6 +99,30 @@ class CustomersController extends AppController {
return $order; 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) { function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
$links['Customer'] = array('name'); $links['Customer'] = array('name');
return parent::gridDataPostProcessLinks($params, $model, $records, $links); return parent::gridDataPostProcessLinks($params, $model, $records, $links);
@@ -178,32 +203,17 @@ class CustomersController extends AppController {
$this->redirect(array('action'=>'index')); $this->redirect(array('action'=>'index'));
} }
// Get details on this customer, its contacts and leases /* //$result = $this->Customer->securityDeposits($id); */
$customer = $this->Customer->find /* $result = $this->Customer->excessPayments($id); */
('first', array /* //$result = $this->Customer->unreconciledCharges($id); */
('contain' => array /* echo('<HR>'); */
(// Models /* pr($result); */
'Contact' => /* $this->autoRender = false; */
array('order' => array('Contact.display_name'), /* return; */
// Models
'ContactPhone',
'ContactEmail',
'ContactAddress',
),
'Lease' =>
array('Unit' =>
array('order' => array('sort_order'),
'fields' => array('id', 'name'),
),
),
),
'conditions' => array('Customer.id' => $id), $customer = $this->Customer->details($id);
));
//pr($customer); //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); $outstanding_deposit = $this->Customer->securityDepositBalance($id);
// Figure out if this customer has any non-closed leases // Figure out if this customer has any non-closed leases
@@ -244,11 +254,6 @@ class CustomersController extends AppController {
$id)); $id));
} }
if ($outstanding_balance < 0)
$this->sidemenu_links[] =
array('name' => 'Issue Refund',
'url' => array('action' => 'refund', $id));
// Prepare to render. // Prepare to render.
$title = 'Customer: ' . $customer['Customer']['name']; $title = 'Customer: ' . $customer['Customer']['name'];
$this->set(compact('customer', 'title', $this->set(compact('customer', 'title',
@@ -383,7 +388,7 @@ class CustomersController extends AppController {
$default_type = $TT->defaultPaymentType(); $default_type = $TT->defaultPaymentType();
$this->set(compact('payment_types', 'default_type')); $this->set(compact('payment_types', 'default_type'));
$title = ($customer['name'] . ': Receipt Entry'); $title = ($customer['name'] . ': Payment Entry');
$this->set(compact('customer', 'charges', 'stats', 'title')); $this->set(compact('customer', 'charges', 'stats', 'title'));
} }
@@ -406,7 +411,7 @@ class CustomersController extends AppController {
)); ));
pr(compact('entries')); pr(compact('entries'));
$this->Customer->refund($entries); $this->Customer->StatementEntry->reverse($entries);
} }
@@ -423,8 +428,8 @@ class CustomersController extends AppController {
$this->layout = null; $this->layout = null;
$this->autoLayout = false; $this->autoLayout = false;
$this->autoRender = false; $this->autoRender = false;
Configure::write('debug', '0'); //Configure::write('debug', '0');
header("Content-type: text/xml;charset=utf-8"); //header("Content-type: text/xml;charset=utf-8");
App::import('Helper', 'Xml'); App::import('Helper', 'Xml');
$xml = new XmlHelper(); $xml = new XmlHelper();
@@ -432,22 +437,16 @@ class CustomersController extends AppController {
// Find the unreconciled entries, then manipulate the structure // Find the unreconciled entries, then manipulate the structure
// slightly to accomodate the format necessary for XML Helper. // slightly to accomodate the format necessary for XML Helper.
$unreconciled = $this->Customer->unreconciledCharges($id); $unreconciled = $this->Customer->unreconciledCharges($id);
foreach ($unreconciled['entries'] AS &$entry)
$entry = array_intersect_key($entry['StatementEntry'],
array('id'=>1));
$unreconciled = array('entries' => $unreconciled = array('entries' =>
array('entry' => $unreconciled['entries'], array_intersect_key($unreconciled['debit'],
'balance' => $unreconciled['summary']['balance'])); array('entry'=>1, 'balance'=>1)));
// XML Helper will dump an empty tag if the array is empty // XML Helper will dump an empty tag if the array is empty
if (empty($unreconciled['entries']['entry'])) if (!count($unreconciled['entries']['entry']))
unset($unreconciled['entries']['entry']); unset($unreconciled['entries']['entry']);
/* pr(compact('unreconciled')); */ //pr($unreconciled);
/* echo htmlspecialchars($xml->serialize($unreconciled)); */ //$reconciled = $cust->reconcileNewStatementEntry($cust_id, 'credit', $amount);
/* $this->render('/fake'); */
$opts = array(); $opts = array();
//$opts['format'] = 'tags'; //$opts['format'] = 'tags';

View File

@@ -64,7 +64,7 @@ class LeasesController extends AppController {
function gridDataFields(&$params, &$model) { function gridDataFields(&$params, &$model) {
$fields = parent::gridDataFields($params, $model); $fields = parent::gridDataFields($params, $model);
return array_merge($fields, return array_merge($fields,
$this->Lease->StatementEntry->chargeDisbursementFields(true)); $this->Lease->StatementEntry->chargePaymentFields(true));
} }
function gridDataConditions(&$params, &$model) { function gridDataConditions(&$params, &$model) {
@@ -172,6 +172,8 @@ class LeasesController extends AppController {
); );
$this->redirect($this->data['redirect']); $this->redirect($this->data['redirect']);
$this->autoRender = false;
return;
} }
if (!isset($id)) if (!isset($id))
@@ -211,68 +213,118 @@ 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: refund * action: apply_deposit
* - Provides lease customer with a refund * - 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.
*/ */
function refund($id) { 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();
$lease = $this->Lease->find $lease = $this->Lease->find
('first', array ('first', array
('contain' => array ('contain' => array
(// Models (// Models
'Unit' => array('fields' => array('id', 'name')), 'Unit' =>
'Customer' => array('fields' => array('id', 'name')), array('order' => array('sort_order'),
'fields' => array('id', 'name'),
),
'Customer' =>
array('fields' => array('id', 'name'),
),
), ),
'conditions' => array(array('Lease.id' => $id), 'conditions' => array(array('Lease.id' => $id),
// Make sure lease is not closed...
array('Lease.close_date' => null), 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));
}
// The refund will be for a positive amount // Get the lease balance, part of lease stats
$balance *= -1; $this->Lease->statsMerge($lease['Lease'],
array('stats' => $this->Lease->stats($id)));
// Get the accounts capable of paying the refund // Determine the lease security deposit
$refundAccounts = $this->Lease->StatementEntry->Account->refundAccounts(); $deposit_balance = $this->Lease->securityDeposits($lease['Lease']['id']);
$defaultAccount = current($refundAccounts); $this->set(compact('deposit_balance'));
$this->set(compact('refundAccounts', 'defaultAccount')); $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); */
// Prepare to render
$title = ('Lease #' . $lease['Lease']['number'] . ': ' . $title = ('Lease #' . $lease['Lease']['number'] . ': ' .
$lease['Unit']['name'] . ': ' . $lease['Unit']['name'] . ': ' .
$lease['Customer']['name'] . ': Refund'); $lease['Customer']['name'] . ': Utilize Security Deposit');
$this->set(compact('title', 'lease', 'balance')); $this->set(compact('title', 'redirect'));
} }
@@ -280,39 +332,82 @@ class LeasesController extends AppController {
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************
* action: bad_debt * action: bad_debt
* - Sets up the write-off entry page, so that the * - Writes off remaining charges on a lease.
* user can write 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.
*/ */
function bad_debt($id) { function bad_debt($id) {
$A = new Account();
$lease = $this->Lease->find $lease = $this->Lease->find
('first', array ('first', array
('contain' => array ('contain' => array
(// Models (// Models
'Unit' => array('fields' => array('id', 'name')), 'Unit' =>
'Customer' => array('fields' => array('id', 'name')), array('order' => array('sort_order'),
'fields' => array('id', 'name'),
),
'Customer' =>
array('fields' => array('id', 'name'),
),
), ),
'conditions' => array(array('Lease.id' => $id), 'conditions' => array(array('Lease.id' => $id),
// Make sure lease is not closed...
array('Lease.close_date' => null), 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 // Get the lease balance, part of lease stats
$stats = $this->Lease->stats($id); $this->Lease->statsMerge($lease['Lease'],
$balance = $stats['balance']; 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); */
// Prepare to render
$title = ('Lease #' . $lease['Lease']['number'] . ': ' . $title = ('Lease #' . $lease['Lease']['number'] . ': ' .
$lease['Unit']['name'] . ': ' . $lease['Unit']['name'] . ': ' .
$lease['Customer']['name'] . ': Write Off Bad Debt'); $lease['Customer']['name'] . ': Write Off Bad Debt');
$this->set(compact('title', 'lease', 'balance')); $this->set(compact('title', 'redirect'));
$this->render('/transactions/bad_debt'); }
/**************************************************************************
**************************************************************************
**************************************************************************
* 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')); */
} }
@@ -360,7 +455,7 @@ class LeasesController extends AppController {
)); ));
$A = new Account(); $A = new Account();
$charge_accounts = $A->invoiceAccounts(); $charge_accounts = $A->chargeAccounts();
$default_account = $A->rentAccountID(); $default_account = $A->rentAccountID();
$this->set(compact('charge_accounts', 'default_account')); $this->set(compact('charge_accounts', 'default_account'));
@@ -409,9 +504,13 @@ class LeasesController extends AppController {
$this->set('charge_gaps', $this->Lease->rentChargeGaps($id)); $this->set('charge_gaps', $this->Lease->rentChargeGaps($id));
$this->set('charge_through', $this->Lease->rentChargeThrough($id)); $this->set('charge_through', $this->Lease->rentChargeThrough($id));
// Figure out the outstanding balances for this lease // Obtain the overall lease balance
$outstanding_balance = $this->Lease->balance($id); $this->Lease->statsMerge($lease['Lease'],
$outstanding_deposit = $this->Lease->securityDepositBalance($id); 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']);
// Set up dynamic menu items // Set up dynamic menu items
if (!isset($lease['Lease']['close_date'])) { if (!isset($lease['Lease']['close_date'])) {
@@ -432,17 +531,19 @@ class LeasesController extends AppController {
'action' => 'receipt', 'action' => 'receipt',
$lease['Customer']['id'])); $lease['Customer']['id']));
/* if ($outstanding_balance < 0) */ if (isset($lease['Lease']['moveout_date']) && $outstanding_deposit > 0 && $outstanding_balance > 0)
/* $this->sidemenu_links[] = */
/* array('name' => 'Transfer Credit to Customer', */
/* 'url' => array('action' => 'promote_surplus', $id)); */
if ($outstanding_balance < 0)
$this->sidemenu_links[] = $this->sidemenu_links[] =
array('name' => 'Issue Refund', array('name' => 'Apply Deposit', 'url' => array('action' => 'apply_deposit',
'url' => array('action' => 'refund', $id)); $id));
if (isset($lease['Lease']['moveout_date']) && $outstanding_balance > 0) if (isset($lease['Lease']['moveout_date']) &&
$outstanding_balance <= 0 &&
($outstanding_deposit - $outstanding_balance) > 0)
$this->sidemenu_links[] =
array('name' => 'Issue Refund', 'url' => array('action' => 'refund',
$id));
if (isset($lease['Lease']['moveout_date']) && $outstanding_deposit == 0 && $outstanding_balance > 0)
$this->sidemenu_links[] = $this->sidemenu_links[] =
array('name' => 'Write-Off', 'url' => array('action' => 'bad_debt', array('name' => 'Write-Off', 'url' => array('action' => 'bad_debt',
$id)); $id));

View File

@@ -16,16 +16,6 @@ class LedgerEntriesController extends AppController {
} }
/**************************************************************************
**************************************************************************
**************************************************************************
* action: index / current / past / all
* - Creates a list of ledger entries
*/
function index() { $this->gridView('All Ledger Entries'); }
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************

View File

@@ -16,16 +16,6 @@ class StatementEntriesController extends AppController {
} }
/**************************************************************************
**************************************************************************
**************************************************************************
* action: index / current / past / all
* - Creates a list of statement entries
*/
function index() { $this->gridView('All Statement Entries'); }
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************
@@ -35,7 +25,7 @@ class StatementEntriesController extends AppController {
* to jqGrid. * to jqGrid.
*/ */
function gridDataTables(&$params, &$model) { function gridDataCountTables(&$params, &$model) {
$link = $link =
array(// Models array(// Models
'Transaction' => 'Transaction' =>
@@ -52,50 +42,53 @@ class StatementEntriesController extends AppController {
array('fields' => array('id', 'name'), array('fields' => array('id', 'name'),
), ),
), ),
'Account' =>
array('fields' => array('id', 'name', 'type'),
),
); );
if (isset($params['post']['custom']['statement_entry_id'])) { if (isset($params['post']['custom']['statement_entry_id'])) {
$link['DisbursementEntry'] = array(); $link['PaymentEntry'] = array();
$link['ChargeEntry'] = array(); $link['ChargeEntry'] = array();
} }
/* if ($params['action'] === 'collected') { */ if (isset($params['post']['custom']['account_id'])) {
/* $link['DisbursementEntry'] = array('Receipt' => array('class' => 'Transaction')); */ $link['LedgerEntry'] = array('fields' => array('id'));
/* $link['ChargeEntry'] = array('Invoice' => array('class' => 'Transaction')); */ $link['LedgerEntry']['Account'] = array('fields' => array('id', 'name', 'type'));
/* } */ }
/* if (count(array_intersect($params['fields'], array('applied'))) == 1) { */ /* if (count(array_intersect($params['fields'], array('applied'))) == 1) { */
/* $link['DisbursementEntry'] = array(); */ /* $link['PaymentEntry'] = array(); */
/* $link['ChargeEntry'] = array(); */ /* $link['ChargeEntry'] = array(); */
/* } */
/* elseif (isset($params['post']['custom']['customer_id']) || isset($params['post']['custom']['lease_id'])) { */
/* $link['DisbursementEntry'] = array(); */
/* } */ /* } */
return array('link' => $link); return array('link' => $link);
} }
function gridDataTables(&$params, &$model) {
$tables = $this->gridDataCountTables($params, $model);
$tables['link']['LedgerEntry'] = array('fields' => array('id'));
$tables['link']['LedgerEntry']['Account'] = array('fields' => array('id', 'name', 'type'));
return $tables;
}
function gridDataFields(&$params, &$model) { function gridDataFields(&$params, &$model) {
//foreach(
$fields = parent::gridDataFields($params, $model); $fields = parent::gridDataFields($params, $model);
$fields[] = "COUNT(LedgerEntry.id) AS ledger_entry_count";
if (in_array('applied', $params['post']['fields'])) { if (in_array('applied', $params['post']['fields'])) {
$fields[] = ("IF(StatementEntry.type = 'CHARGE'," . $fields[] = ("IF(StatementEntry.type = 'CHARGE'," .
" SUM(COALESCE(DisbursementEntry.amount,0))," . " SUM(COALESCE(PaymentEntry.amount,0))," .
" SUM(COALESCE(ChargeEntry.amount,0)))" . " SUM(COALESCE(ChargeEntry.amount,0)))" .
" AS 'applied'"); " AS 'applied'");
$fields[] = ("StatementEntry.amount - (" . $fields[] = ("StatementEntry.amount - (" .
"IF(StatementEntry.type = 'CHARGE'," . "IF(StatementEntry.type = 'CHARGE'," .
" SUM(COALESCE(DisbursementEntry.amount,0))," . " SUM(COALESCE(PaymentEntry.amount,0))," .
" SUM(COALESCE(ChargeEntry.amount,0)))" . " SUM(COALESCE(ChargeEntry.amount,0)))" .
") AS 'balance'"); ") AS 'balance'");
} }
$fields = array_merge($fields, $fields = array_merge($fields,
$this->StatementEntry->chargeDisbursementFields()); $this->StatementEntry->chargePaymentFields());
return $fields; return $fields;
} }
@@ -115,17 +108,25 @@ class StatementEntriesController extends AppController {
$this->StatementEntry->Transaction->dateFormatBeforeSave($through_date . ' 23:59:59')); $this->StatementEntry->Transaction->dateFormatBeforeSave($through_date . ' 23:59:59'));
if (isset($account_id)) if (isset($account_id))
$conditions[] = array('StatementEntry.account_id' => $account_id); $conditions[] = array('LedgerEntry.account_id' => $account_id);
if (isset($statement_entry_id)) { if (isset($statement_entry_id)) {
$conditions[] = array('OR' => $conditions[] = array('OR' =>
array(array('ChargeEntry.id' => $statement_entry_id), array(array('ChargeEntry.id' => $statement_entry_id),
array('DisbursementEntry.id' => $statement_entry_id))); array('PaymentEntry.id' => $statement_entry_id)));
} }
return $conditions; return $conditions;
} }
function gridDataPostProcessCalculatedFields(&$params, &$model, &$records) {
parent::gridDataPostProcessCalculatedFields($params, $model, $records);
foreach ($records AS &$record) {
if ($record['StatementEntry']['ledger_entry_count'] > 1)
$record['Account']['name'] = 'Multiple';
}
}
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) { function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
$links['StatementEntry'] = array('id'); $links['StatementEntry'] = array('id');
$links['Transaction'] = array('id'); $links['Transaction'] = array('id');
@@ -154,13 +155,13 @@ class StatementEntriesController extends AppController {
if (in_array('applied', $params['post']['fields'])) { if (in_array('applied', $params['post']['fields'])) {
$tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1)); $tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1));
$tquery['fields'] = array("IF(StatementEntry.type = 'CHARGE'," . $tquery['fields'] = array("IF(StatementEntry.type = 'CHARGE'," .
" SUM(COALESCE(DisbursementEntry.amount,0))," . " SUM(COALESCE(StatementFraction.amount,0))," .
" SUM(COALESCE(ChargeEntry.amount,0)))" . " SUM(COALESCE(ChargeEntry.amount,0)))" .
" AS 'applied'", " AS 'applied'",
"StatementEntry.amount - (" . "StatementEntry.amount - (" .
"IF(StatementEntry.type = 'CHARGE'," . "IF(StatementEntry.type = 'CHARGE'," .
" SUM(COALESCE(DisbursementEntry.amount,0))," . " SUM(COALESCE(PaymentEntry.amount,0))," .
" SUM(COALESCE(ChargeEntry.amount,0)))" . " SUM(COALESCE(ChargeEntry.amount,0)))" .
") AS 'balance'", ") AS 'balance'",
); );
@@ -189,19 +190,6 @@ class StatementEntriesController extends AppController {
function reverse($id) { function reverse($id) {
$this->StatementEntry->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)); $this->redirect(array('action'=>'view', $id));
} }
@@ -220,54 +208,68 @@ class StatementEntriesController extends AppController {
} }
// Get the StatementEntry and related fields // Get the StatementEntry and related fields
$this->StatementEntry->prClassLevel(30, 'Model');
$entry = $this->StatementEntry->find $entry = $this->StatementEntry->find
('first', ('first',
array('contain' => array array('contain' => array
('Transaction' => array('fields' => array('id', 'type', 'stamp')), ('Transaction' => array('fields' => array('id', 'stamp')),
'Account' => array('id', 'name', 'type'),
'Customer' => array('fields' => array('id', 'name')), 'Customer' => array('fields' => array('id', 'name')),
'Lease' => array('fields' => array('id')), 'Lease' => array('fields' => array('id')),
'LedgerEntry' => array('fields' => array('id'),
'Account' => array('id', 'name', 'type')),
), ),
'conditions' => array('StatementEntry.id' => $id), 'conditions' => array('StatementEntry.id' => $id),
)); ));
pr($entry);
//die();
$reconciled = $this->StatementEntry->reconciledEntries($id); $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); $stats = $this->StatementEntry->stats($id);
if (in_array(strtoupper($entry['StatementEntry']['type']), $this->StatementEntry->debitTypes())) if (strtoupper($entry['StatementEntry']['type']) === 'CHARGE')
$stats = $stats['Charge']; $stats = $stats['Charge'];
else else
$stats = $stats['Disbursement']; $stats = $stats['Payment'];
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. // Prepare to render.
$title = "Statement Entry #{$entry['StatementEntry']['id']}"; $title = "Statement Entry #{$entry['StatementEntry']['id']}";

View File

@@ -114,7 +114,7 @@ class TransactionsController extends AppController {
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************
* action: postReceipt * action: postReceipt
* - handles the creation of a receipt * - handles the creation of a payment receipt
*/ */
function postReceipt() { function postReceipt() {
@@ -261,97 +261,6 @@ 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']));
}
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************

View File

@@ -82,7 +82,7 @@ class UnitsController extends AppController {
$fields = parent::gridDataFields($params, $model); $fields = parent::gridDataFields($params, $model);
return array_merge($fields, return array_merge($fields,
$this->Unit->Lease->StatementEntry->chargeDisbursementFields(true)); $this->Unit->Lease->StatementEntry->chargePaymentFields(true));
} }
function gridDataConditions(&$params, &$model) { function gridDataConditions(&$params, &$model) {
@@ -236,10 +236,6 @@ class UnitsController extends AppController {
$this->sidemenu_links[] = $this->sidemenu_links[] =
array('name' => 'Operations', 'header' => true); array('name' => 'Operations', 'header' => true);
$this->sidemenu_links[] =
array('name' => 'Edit', 'url' => array('action' => 'edit',
$id));
if (isset($unit['CurrentLease']['id']) && if (isset($unit['CurrentLease']['id']) &&
!isset($unit['CurrentLease']['moveout_date'])) { !isset($unit['CurrentLease']['moveout_date'])) {
$this->sidemenu_links[] = $this->sidemenu_links[] =
@@ -253,10 +249,6 @@ class UnitsController extends AppController {
if (isset($unit['CurrentLease']['id']) && if (isset($unit['CurrentLease']['id']) &&
!isset($unit['CurrentLease']['close_date'])) { !isset($unit['CurrentLease']['close_date'])) {
$this->sidemenu_links[] =
array('name' => 'Charge', 'url' => array('controller' => 'leases',
'action' => 'invoice',
$unit['CurrentLease']['id']));
$this->sidemenu_links[] = $this->sidemenu_links[] =
array('name' => 'Payment', 'url' => array('controller' => 'customers', array('name' => 'Payment', 'url' => array('controller' => 'customers',
'action' => 'receipt', 'action' => 'receipt',
@@ -269,71 +261,4 @@ class UnitsController extends AppController {
'outstanding_balance', 'outstanding_balance',
'outstanding_deposit')); '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'));
}
} }

View File

@@ -128,12 +128,10 @@ class Account extends AppModel {
function nsfChargeAccountID() { return $this->nameToID('NSF Charge'); } function nsfChargeAccountID() { return $this->nameToID('NSF Charge'); }
function taxAccountID() { return $this->nameToID('Tax'); } function taxAccountID() { return $this->nameToID('Tax'); }
function accountReceivableAccountID() { return $this->nameToID('A/R'); } function accountReceivableAccountID() { return $this->nameToID('A/R'); }
function accountPayableAccountID() { return $this->nameToID('A/P'); }
function cashAccountID() { return $this->nameToID('Cash'); } function cashAccountID() { return $this->nameToID('Cash'); }
function checkAccountID() { return $this->nameToID('Check'); } function checkAccountID() { return $this->nameToID('Check'); }
function moneyOrderAccountID() { return $this->nameToID('Money Order'); } function moneyOrderAccountID() { return $this->nameToID('Money Order'); }
function concessionAccountID() { return $this->nameToID('Concession'); } function concessionAccountID() { return $this->nameToID('Concession'); }
function waiverAccountID() { return $this->nameToID('Waiver'); }
function pettyCashAccountID() { return $this->nameToID('Petty Cash'); } function pettyCashAccountID() { return $this->nameToID('Petty Cash'); }
function invoiceAccountID() { return $this->nameToID('Invoice'); } function invoiceAccountID() { return $this->nameToID('Invoice'); }
function receiptAccountID() { return $this->nameToID('Receipt'); } function receiptAccountID() { return $this->nameToID('Receipt'); }
@@ -195,22 +193,18 @@ class Account extends AppModel {
* - Returns an array of accounts suitable for activity xxx * - Returns an array of accounts suitable for activity xxx
*/ */
function invoiceAccounts() { function chargeAccounts() {
return $this->relatedAccounts('invoices', array('order' => 'name')); return $this->relatedAccounts('charges', array('order' => 'name'));
} }
function receiptAccounts() { function paymentAccounts() {
return $this->relatedAccounts('receipts', array('order' => 'name')); return $this->relatedAccounts('payments', array('order' => 'name'));
} }
function depositAccounts() { function depositAccounts() {
return $this->relatedAccounts('deposits', array('order' => 'name')); return $this->relatedAccounts('deposits', array('order' => 'name'));
} }
function refundAccounts() {
return $this->relatedAccounts('refunds', array('order' => 'name'));
}
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
@@ -220,7 +214,7 @@ class Account extends AppModel {
*/ */
function collectableAccounts() { function collectableAccounts() {
$accounts = $this->receiptAccounts(); $accounts = $this->paymentAccounts();
foreach(array($this->nsfAccountID(), foreach(array($this->nsfAccountID(),
$this->securityDepositAccountID()) $this->securityDepositAccountID())

View File

@@ -81,9 +81,14 @@ class Customer extends AppModel {
$this->prEnter(compact('id', 'query')); $this->prEnter(compact('id', 'query'));
$this->queryInit($query); $this->queryInit($query);
$query['conditions'][] = array('StatementEntry.customer_id' => $id); 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.account_id' => $query['conditions'][] = array('StatementEntry.account_id' =>
$this->StatementEntry->Account->securityDepositAccountID()); $this->StatementEntry->LedgerEntry->Account->securityDepositAccountID());
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, false, true); $set = $this->StatementEntry->reconciledSet('CHARGE', $query, false, true);
return $this->prReturn($set); return $this->prReturn($set);
@@ -101,12 +106,18 @@ class Customer extends AppModel {
$this->prEnter(compact('id', 'query')); $this->prEnter(compact('id', 'query'));
$this->queryInit($query); $this->queryInit($query);
$query['conditions'][] = array('StatementEntry.customer_id' => $id); if (!isset($query['link']['Lease']))
$query['link']['Lease'] = array();
if (!isset($query['link']['Lease']['fields']))
$query['link']['Lease']['fields'] = array();
$query['conditions'][] = array('Lease.id' => $id);
$query['conditions'][] = array('StatementEntry.account_id' => $query['conditions'][] = array('StatementEntry.account_id' =>
$this->StatementEntry->Account->securityDepositAccountID()); $this->StatementEntry->LedgerEntry->Account->securityDepositAccountID());
$stats = $this->StatementEntry->stats(null, $query); $stats = $this->StatementEntry->stats(null, $query);
return $this->prReturn($stats['account_balance']); $balance = $stats['Charge']['reconciled'] - $stats['Payment']['reconciled'];
return $this->prReturn($balance);
} }
@@ -121,12 +132,92 @@ class Customer extends AppModel {
$this->prEnter(compact('id', 'query')); $this->prEnter(compact('id', 'query'));
$this->queryInit($query); $this->queryInit($query);
$query['conditions'][] = array('StatementEntry.customer_id' => $id); 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);
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, true); $set = $this->StatementEntry->reconciledSet('CHARGE', $query, true);
return $this->prReturn($set); 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);
}
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************
@@ -203,44 +294,6 @@ 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'];
}
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************
@@ -267,14 +320,14 @@ class Customer extends AppModel {
/* $stats = $this->StatementEntry->find */ /* $stats = $this->StatementEntry->find */
/* ('first', array */ /* ('first', array */
/* ('contain' => false, */ /* ('contain' => false, */
/* 'fields' => $this->StatementEntry->chargeDisbursementFields(true), */ /* 'fields' => $this->StatementEntry->chargePaymentFields(true), */
/* 'conditions' => array('StatementEntry.customer_id' => $id), */ /* 'conditions' => array('StatementEntry.customer_id' => $id), */
/* )); */ /* )); */
$find_stats = $this->StatementEntry->find $find_stats = $this->StatementEntry->find
('first', array ('first', array
('contain' => false, ('contain' => false,
'fields' => $this->StatementEntry->chargeDisbursementFields(true), 'fields' => $this->StatementEntry->chargePaymentFields(true),
'conditions' => array('StatementEntry.customer_id' => $id), 'conditions' => array('StatementEntry.customer_id' => $id),
)); ));
$find_stats = $find_stats[0]; $find_stats = $find_stats[0];

View File

@@ -12,7 +12,6 @@ class Lease extends AppModel {
'StatementEntry', 'StatementEntry',
); );
//var $default_log_level = array('log' => 30, 'show' => 15);
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
@@ -24,11 +23,21 @@ class Lease extends AppModel {
$this->prEnter(compact('id', 'query')); $this->prEnter(compact('id', 'query'));
$this->queryInit($query); $this->queryInit($query);
$query['conditions'][] = array('StatementEntry.lease_id' => $id); if (!isset($query['link']['Lease']))
$query['link']['Lease'] = array();
if (!isset($query['link']['Lease']['fields']))
$query['link']['Lease']['fields'] = array();
$query['conditions'][] = array('Lease.id' => $id);
$query['conditions'][] = array('StatementEntry.account_id' => $query['conditions'][] = array('StatementEntry.account_id' =>
$this->StatementEntry->Account->securityDepositAccountID()); $this->StatementEntry->LedgerEntry->Account->securityDepositAccountID());
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, false, true); $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); return $this->prReturn($set);
} }
@@ -44,26 +53,18 @@ class Lease extends AppModel {
$this->prEnter(compact('id', 'query')); $this->prEnter(compact('id', 'query'));
$this->queryInit($query); $this->queryInit($query);
$query['conditions'][] = array('StatementEntry.lease_id' => $id); if (!isset($query['link']['Lease']))
$query['conditions'][] = array('StatementEntry.account_id' => $query['link']['Lease'] = array();
$this->StatementEntry->Account->securityDepositAccountID()); if (!isset($query['link']['Lease']['fields']))
$query['link']['Lease']['fields'] = array();
// REVISIT <AP>: 20090804 $query['conditions'][] = array('Lease.id' => $id);
// Dilemma... how to handle security deposits used to pay $query['conditions'][] = array('StatementEntry.account_id' =>
// charges on the lease, yet are not part of the lease $this->StatementEntry->LedgerEntry->Account->securityDepositAccountID());
// 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); $stats = $this->StatementEntry->stats(null, $query);
return $this->prReturn($stats['account_balance']); $balance = $stats['Charge']['reconciled'] - $stats['Payment']['reconciled'];
return $this->prReturn($balance);
} }
@@ -72,18 +73,21 @@ class Lease extends AppModel {
************************************************************************** **************************************************************************
* function: releaseSecurityDeposits * function: releaseSecurityDeposits
* - Releases all security deposits associated with this lease. * - Releases all security deposits associated with this lease.
* That simply makes a disbursement out of them, which can be used * That simply makes a payment out of them, which can be used
* to pay outstanding customer charges, or simply to become * to pay outstanding customer charges, or simply to become
* a customer surplus (customer credit). * a customer surplus (customer credit).
*/ */
function releaseSecurityDeposits($id, $stamp = null, $query = null) { function releaseSecurityDeposits($id, $query = null) {
//$this->prFunctionLevel(30); $this->prFunctionLevel(30);
$this->prEnter(compact('id', 'stamp', 'query')); $this->prEnter(compact('id', 'query'));
$secdeps = $this->securityDeposits($id, $query); $secdeps = $this->securityDeposits($id, $query);
$secdeps = $secdeps['entries']; $secdeps = $secdeps['entries'];
$this->pr(20, compact('secdeps')); $this->pr(20, compact('secdeps'));
$this->securityDepositBalance($id, $query);
die();
// If there are no paid security deposits, then // If there are no paid security deposits, then
// we can consider all security deposits released. // we can consider all security deposits released.
if (count($secdeps) == 0) if (count($secdeps) == 0)
@@ -91,27 +95,33 @@ class Lease extends AppModel {
// Build a transaction // Build a transaction
$release = array('Transaction' => array(), 'Entry' => array()); $release = array('Transaction' => array(), 'Entry' => array());
$release['Transaction']['stamp'] = $stamp;
$release['Transaction']['comment'] = "Security Deposit Release"; $release['Transaction']['comment'] = "Security Deposit Release";
foreach ($secdeps AS $charge) { foreach ($secdeps AS $charge) {
if ($charge['StatementEntry']['type'] !== 'CHARGE') if ($charge['StatementEntry']['type'] !== 'CHARGE')
die("INTERNAL ERROR: SECURITY DEPOSIT IS NOT CHARGE"); die("INTERNAL ERROR: SECURITY DEPOSIT IS NOT CHARGE");
// Since security deposits are being released, this also means // Since security deposits are being released, this also means
// any unpaid (or only partially paid) security deposit should // we're reducing any oustanding amount on a security deposit
// have the remaining balance reversed. // since we no longer expect it to be owed.
if ($charge['StatementEntry']['balance'] > 0) // REVISIT <AP>: 20090730
$this->StatementEntry->reverse($charge['StatementEntry']['id'], true, $stamp); // 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']);
}
$release['Entry'][] = $release['Entry'][] =
array('amount' => $charge['StatementEntry']['reconciled'], array('amount' => $charge['StatementEntry']['reconciled'],
'account_id' => $this->StatementEntry->Account->securityDepositAccountID(), 'account_id' => $this->StatementEntry->LedgerEntry->Account->securityDepositAccountID(),
'comment' => "Released Security Deposit", 'comment' => "Released Security Deposit",
); );
} }
$customer_id = $secdeps[0]['StatementEntry']['customer_id']; $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 $result = $this->StatementEntry->Transaction->addReceipt
($release, $customer_id, $lease_id); ($release, $customer_id, $lease_id);
@@ -133,7 +143,7 @@ class Lease extends AppModel {
function rentLastCharges($id) { function rentLastCharges($id) {
$this->prEnter(compact('id')); $this->prEnter(compact('id'));
$rent_account_id = $this->StatementEntry->Account->rentAccountID(); $rent_account_id = $this->StatementEntry->LedgerEntry->Account->rentAccountID();
$entries = $this->find $entries = $this->find
('all', ('all',
array('link' => array('link' =>
@@ -211,7 +221,7 @@ class Lease extends AppModel {
function rentPaidThrough($id) { function rentPaidThrough($id) {
$this->prEnter(compact('id')); $this->prEnter(compact('id'));
$rent_account_id = $this->StatementEntry->Account->rentAccountID(); $rent_account_id = $this->StatementEntry->LedgerEntry->Account->rentAccountID();
// First, see if we can find any unpaid entries. Of course, // First, see if we can find any unpaid entries. Of course,
// the first unpaid entry gives us a very direct indication // the first unpaid entry gives us a very direct indication
@@ -336,9 +346,6 @@ class Lease extends AppModel {
$this->id; $this->id;
$this->saveField('number', $this->id); $this->saveField('number', $this->id);
// Update the current lease count for the customer
$this->Customer->updateLeaseCount($customer_id);
// Update the unit status // Update the unit status
$this->Unit->updateStatus($unit_id, 'OCCUPIED'); $this->Unit->updateStatus($unit_id, 'OCCUPIED');
@@ -379,16 +386,10 @@ class Lease extends AppModel {
// Save it! // Save it!
$this->save($this->data, false); $this->save($this->data, false);
// Release the security deposit(s)
$this->releaseSecurityDeposits($id, $stamp);
// Close the lease, if so requested // Close the lease, if so requested
if ($close) if ($close)
$this->close($id, $stamp); $this->close($id, $stamp);
// Update the current lease count for the customer
$this->Customer->updateLeaseCount($this->field('customer_id'));
// Finally, update the unit status // Finally, update the unit status
$this->recursive = -1; $this->recursive = -1;
$this->read(); $this->read();
@@ -422,10 +423,6 @@ class Lease extends AppModel {
// Save it! // Save it!
$this->save($this->data, false); $this->save($this->data, false);
// Update the current lease count for the customer
$this->Customer->updateLeaseCount($this->field('customer_id'));
return $this->prReturn(true); return $this->prReturn(true);
} }
@@ -467,45 +464,12 @@ class Lease extends AppModel {
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************
* function: refund * function: addCharge
* - Marks any lease balance as payable to the customer. * - Adds an additional charge to the lease
*/ */
function refund($id, $stamp = null) { function addCharge($id, $charge) {
$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']);
} }
@@ -521,14 +485,42 @@ class Lease extends AppModel {
if (!$id) if (!$id)
return $this->prReturn(null); return $this->prReturn(null);
$find_stats = $this->StatementEntry->find $this->queryInit($query);
('first', array
('contain' => false, //$query['link'] = array('Lease' => $query['link']);
'fields' => $this->StatementEntry->chargeDisbursementFields(true), /* if (!isset($query['link']['StatementEntry'])) */
'conditions' => array('StatementEntry.lease_id' => $id), /* $query['link']['StatementEntry'] = array(); */
)); /* if (!isset($query['link']['StatementEntry']['ChargeEntry'])) */
$find_stats = $find_stats[0]; /* $query['link']['StatementEntry']['ChargeEntry'] = array(); */
return $this->prReturn($find_stats);
/* $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);
} }
} }

View File

@@ -33,6 +33,12 @@ class LedgerEntry extends AppModel {
'foreignKey' => 'debit_entry_id', 'foreignKey' => 'debit_entry_id',
'associationForeignKey' => 'credit_entry_id', 'associationForeignKey' => 'credit_entry_id',
), ),
//
'StatementEntry' => array(
'joinTable' => 'statement_fractions',
'fields' => 'StatementFraction.amount',
),
); );
@@ -170,7 +176,7 @@ class LedgerEntry extends AppModel {
//pr(array('stats()', compact('id', 'query', 'set'))); //pr(array('stats()', compact('id', 'query', 'set')));
$rtypes = array('charge', 'disbursement', $rtypes = array('charge', 'payment',
// 'debit', 'credit', // 'debit', 'credit',
); );
@@ -178,8 +184,10 @@ class LedgerEntry extends AppModel {
foreach($rtypes AS $rtype) { foreach($rtypes AS $rtype) {
$Rtype = ucfirst($rtype); $Rtype = ucfirst($rtype);
if (($rtype == 'charge' && (!isset($set) || $set == 'DISBURSEMENT')) || if (($rtype == 'charge' && (!isset($set) || $set == 'PAYMENT')) ||
($rtype == 'disbursement' && (!isset($set) || $set == 'CHARGE')) ($rtype == 'payment' && (!isset($set) || $set == 'CHARGE'))
/* ($rtype == 'debit' && (!isset($set) || $set == 'CREDIT')) || */
/* ($rtype == 'credit' && (!isset($set) || $set == 'DEBIT')) */
) { ) {
$rquery = $query; $rquery = $query;

View File

@@ -5,81 +5,55 @@ class StatementEntry extends AppModel {
'Transaction', 'Transaction',
'Customer', 'Customer',
'Lease', 'Lease',
'Account',
// The charge to which this disbursement applies (if it is one) // The charge to which this payment applies (if it is one)
'ChargeEntry' => array( 'ChargeEntry' => array(
'className' => 'StatementEntry', 'className' => 'StatementEntry',
), ),
); );
var $hasMany = array( var $hasMany = array(
// The disbursements that apply to this charge (if it is one) // The payments that apply to this charge (if it is one)
'DisbursementEntry' => array( 'PaymentEntry' => array(
'className' => 'StatementEntry', 'className' => 'StatementEntry',
'foreignKey' => 'charge_entry_id', 'foreignKey' => 'charge_entry_id',
), ),
'StatementFraction',
); );
var $default_log_level = array('log' => 30, 'show' => 15); var $hasAndBelongsToMany = array(
'LedgerEntry' => array(
'joinTable' => 'statement_fractions',
//'fields' => 'StatementFraction.amount',
),
);
//var $default_log_level = 30;
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************
* function: debit/creditTypes * function: chargePaymentFields
*/ */
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 $fields = array
( (
($sum ? 'SUM(' : '') . ($sum ? 'SUM(' : '') .
"IF({$entry_name}.type IN ({$debit_set})," . "IF({$entry_name}.type = 'CHARGE'," .
" {$entry_name}.amount, NULL)" . " {$entry_name}.amount, NULL)" .
($sum ? ')' : '') . ' AS charge' . ($sum ? 's' : ''), ($sum ? ')' : '') . ' AS charge' . ($sum ? 's' : ''),
($sum ? 'SUM(' : '') . ($sum ? 'SUM(' : '') .
"IF({$entry_name}.type IN({$credit_set})," . "IF({$entry_name}.type = 'PAYMENT' OR {$entry_name}.type = 'SURPLUS'," .
" {$entry_name}.amount, NULL)" . " {$entry_name}.amount, NULL)" .
($sum ? ')' : '') . ' AS disbursement' . ($sum ? 's' : ''), ($sum ? ')' : '') . ' AS payment' . ($sum ? 's' : ''),
($sum ? 'SUM(' : '') . ($sum ? 'SUM(' : '') .
"IF({$entry_name}.type IN ({$debit_set}), 1," . "IF({$entry_name}.type = 'CHARGE', 1," .
" IF({$entry_name}.type IN ({$credit_set}), -1, 0))" . " IF({$entry_name}.type = 'PAYMENT' OR {$entry_name}.type = 'SURPLUS', -1, 0))" .
" * IF({$entry_name}.amount, {$entry_name}.amount, 0)" . " * IF({$entry_name}.amount, {$entry_name}.amount, 0)" .
($sum ? ')' : '') . ' AS balance', ($sum ? ')' : '') . ' AS balance',
); );
@@ -99,12 +73,11 @@ class StatementEntry extends AppModel {
* (not in a pre-existing statement entry) * (not in a pre-existing statement entry)
*/ */
function verifyStatementEntry($entry) { function verifyStatementEntry($entry) {
$this->prFunctionLevel(10); $this->prFunctionLevel(30);
$this->prEnter(compact('entry')); $this->prEnter(compact('entry'));
if (empty($entry['type']) || if (empty($entry['type']) ||
//empty($entry['effective_date']) || //empty($entry['effective_date']) ||
empty($entry['account_id']) ||
empty($entry['amount']) empty($entry['amount'])
) { ) {
return $this->prReturn(false); return $this->prReturn(false);
@@ -134,133 +107,127 @@ class StatementEntry extends AppModel {
if (!$this->save($entry)) if (!$this->save($entry))
return array('error' => true, 'save_data' => $entry) + $ret; return array('error' => true, 'save_data' => $entry) + $ret;
foreach ($entry['Fraction'] AS $fraction) {
$fraction['statement_entry_id'] = $this->id;
$this->StatementFraction->id = null;
$this->StatementFraction->save($fraction);
}
$ret['statement_entry_id'] = $this->id; $ret['statement_entry_id'] = $this->id;
return $this->prReturn($ret + array('error' => false)); return $this->prReturn($ret + array('error' => false));
} }
/**************************************************************************
**************************************************************************
**************************************************************************
* 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 * function: reverse
* - Reverses the charges * - 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($id, $balance = false, $stamp = null) { function reverse($ledger_entries, $stamp = null) {
$this->prEnter(compact('id', 'balance', 'stamp')); $this->prEnter(compact('ledger_entries', 'stamp'));
$ret = array(); // 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)));
// Get the basic information about the entry to be reversed. $A = new Account();
$this->recursive = -1;
$charge = $this->read(null, $id);
$charge = $charge['StatementEntry'];
if ($charge['type'] !== 'CHARGE') $ar_account_id = $A->accountReceivableAccountID();
INTERNAL_ERROR("Reversal item is not CHARGE."); $receipt_account_id = $A->receiptAccountID();
// Build a transaction $transaction_id = null;
$reversal = array('Transaction' => array(), 'Entry' => array()); foreach ($ledger_entries AS $entry) {
$reversal['Transaction']['stamp'] = $stamp; $entry = $entry['Entry'];
$reversal['Transaction']['comment'] = "Credit Note: Charge Reversal"; $amount = -1*$entry['amount'];
$voided_entry_transactions = array(); if (isset($entry['credit_account_id']))
$reconciled = $this->reconciledEntries($id); $refund_account_id = $entry['credit_account_id'];
$this->pr(21, compact('reconciled')); 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);
if ($reconciled && !$balance) { // post new refund in the income account
foreach ($reconciled['entries'] AS $entry) { $ids = $A->postEntry
if ($entry['DisbursementEntry']['type'] === 'REVERSAL') (array('transaction_id' => $transaction_id),
INTERNAL_ERROR("Charge has already been reversed");
/* $voided_entry_transactions[$entry['DisbursementEntry']['transaction_id']] */
/* = array_intersect_key($entry['DisbursementEntry'], */
/* array('customer_id'=>1, 'lease_id'=>1)); */
/* $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')); */
}
// 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,
null, array('debit_ledger_id' => $A->currentLedgerID($ar_account_id),
$tx['customer_id'], 'credit_ledger_id' => $A->currentLedgerID($refund_account_id),
$tx['lease_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))),
)
); );
$this->pr(21, compact('result'));
$ret['assigned'][] = $result; if ($ids['error'])
if ($result['error']) return $this->prReturn(null);
$ret['error'] = true; $transaction_id = $ids['transaction_id'];
$this->pr(15, compact('ids', 'amount', 'refund_account_id', 'ar_account_id'),
'Posted Refund Ledger Entry');
} }
return $this->prReturn($ret + array('error' => false)); return $this->prReturn(true);
} }
@@ -274,21 +241,28 @@ class StatementEntry extends AppModel {
function reconciledSetQuery($set, $query) { function reconciledSetQuery($set, $query) {
$this->queryInit($query); $this->queryInit($query);
if (in_array($set, $this->debitTypes())) if ($set == 'CHARGE' || $set == 'PAYMENT')
$query['link']['DisbursementEntry'] = array('fields' => array("SUM(DisbursementEntry.amount) AS reconciled")); $query['conditions'][] = array('StatementEntry.type' => $set);
elseif (in_array($set, $this->creditTypes()))
$query['link']['ChargeEntry'] = array('fields' => array("SUM(ChargeEntry.amount) AS reconciled"));
else else
die("INVALID RECONCILE SET"); die("INVALID RECONCILE SET");
$query['conditions'][] = array('StatementEntry.type' => $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['group'] = 'StatementEntry.id'; $query['group'] = 'StatementEntry.id';
// REVISIT: TESTING
//$query['link']['PaymentEntry'] = array('fields' => array("(`PaymentEntry.amount`+0) AS reconciled"));
//$query['group'] = null;
// END REVISIT
return $query; return $query;
} }
function reconciledSet($set, $query = null, $unrec = false, $if_rec_include_partial = false) { function reconciledSet($set, $query = null, $unrec = false, $if_rec_include_partial = false) {
//$this->prFunctionLevel(array('log' => 16, 'show' => 10)); //$this->prFunctionLevel(16);
$this->prEnter(compact('set', 'query', 'unrec', 'if_rec_include_partial')); $this->prEnter(compact('set', 'query', 'unrec', 'if_rec_include_partial'));
$lquery = $this->reconciledSetQuery($set, $query); $lquery = $this->reconciledSetQuery($set, $query);
$result = $this->find('all', $lquery); $result = $this->find('all', $lquery);
@@ -312,7 +286,7 @@ class StatementEntry extends AppModel {
$reconciled = true; $reconciled = true;
elseif ($entry['StatementEntry']['reconciled'] == 0) elseif ($entry['StatementEntry']['reconciled'] == 0)
$reconciled = false; $reconciled = false;
else // Partial disbursement; depends on unrec else // Partial payment; depends on unrec
$reconciled = (!$unrec && $if_rec_include_partial); $reconciled = (!$unrec && $if_rec_include_partial);
// Add to the set, if it's been requested // Add to the set, if it's been requested
@@ -330,7 +304,7 @@ class StatementEntry extends AppModel {
************************************************************************** **************************************************************************
* function: reconciledEntries * function: reconciledEntries
* - Returns a list of entries that reconcile against the given entry. * - Returns a list of entries that reconcile against the given entry.
* (such as disbursements towards a charge). * (such as payments towards a charge).
*/ */
function reconciledEntriesQuery($id, $query = null) { function reconciledEntriesQuery($id, $query = null) {
$this->queryInit($query, false); $this->queryInit($query, false);
@@ -341,14 +315,10 @@ class StatementEntry extends AppModel {
$query['conditions'][] = array('StatementEntry.id' => $id); $query['conditions'][] = array('StatementEntry.id' => $id);
if (in_array($this->data['StatementEntry']['type'], $this->debitTypes())) { if ($this->data['StatementEntry']['type'] == 'CHARGE')
$query['link']['DisbursementEntry'] = array(); $query['link']['PaymentEntry'] = array();
$query['conditions'][] = array('DisbursementEntry.id !=' => null); if ($this->data['StatementEntry']['type'] == 'PAYMENT')
}
if (in_array($this->data['StatementEntry']['type'], $this->creditTypes())) {
$query['link']['ChargeEntry'] = array(); $query['link']['ChargeEntry'] = array();
$query['conditions'][] = array('ChargeEntry.id !=' => null);
}
return $query; return $query;
} }
@@ -373,121 +343,61 @@ class StatementEntry extends AppModel {
* *
* REVISIT <AP>: 20090726 * REVISIT <AP>: 20090726
* This algorithm shouldn't be hardcoded. We need to allow * This algorithm shouldn't be hardcoded. We need to allow
* the user to specify how disbursements should be applied. * the user to specify how payments should be applied.
* *
*/ */
function assignCredits($query = null, $receipt_id = null, function assignCredits($query = null, $receipt_id = null) {
$charge_ids = null, $disbursement_type = null, $this->prFunctionLevel(25);
$customer_id = null, $lease_id = null) $this->prEnter( compact('query', 'receipt_id'));
{
//$this->prFunctionLevel(25);
$this->prEnter(compact('query', 'receipt_id',
'charge_ids', 'disbursement_type',
'customer_id', 'lease_id'));
$this->queryInit($query); $this->queryInit($query);
if (!empty($customer_id))
$query['conditions'][] = array('StatementEntry.customer_id' => $customer_id);
if (empty($disbursement_type))
$disbursement_type = 'DISBURSEMENT';
$ret = array(); $ret = array();
// First, find all known credits, unless this call is to make // First, find all known credits
// credit adjustments to a specific charge $lquery = $query;
// REVISIT <AP>: 20090806 $lquery['conditions'][] = array('StatementEntry.type' => 'SURPLUS');
// If the theory below is correct, we should only search for $lquery['order'][] = 'StatementEntry.effective_date ASC';
// explicit credits if we don't have a receipt_id $credits = $this->find('all', $lquery);
if (empty($charge_ids)) { $this->pr(18, compact('credits'),
$lquery = $query; "Credits Established");
$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 // Next, establish credit from the newly added receipt
$receipt_credit = null; $receipt_credit = null;
if (!empty($receipt_id)) { if (!empty($receipt_id)) {
$lquery = $lquery = array
array('link' => ('contain' => array
array('StatementEntry', ('LedgerEntry' =>
'LedgerEntry' => array('conditions' =>
array('conditions' => //array(LedgerEntry.'crdr'=>'DEBIT'),
array('LedgerEntry.account_id !=' => array('LedgerEntry.account_id !=' =>
$this->Account->accountReceivableAccountID()), $this->LedgerEntry->Account->accountReceivableAccountID()),
), ),
), ),
'conditions' => array('Transaction.id' => $receipt_id), );
'fields' => array('Transaction.id', 'Transaction.stamp', 'Transaction.amount'),
); $this->Transaction->id = $receipt_id;
$receipt_credit = $this->Transaction->find('first', $lquery); $receipt_credit = $this->Transaction->find('first', $lquery);
if (!$receipt_credit) if (!$receipt_credit)
INTERNAL_ERROR("Unable to locate receipt."); die("INTERNAL ERROR: UNABLE TO LOCATE RECEIPT");
//$reconciled = $this->reconciledEntries($id); $receipt_credit['balance'] = $receipt_credit['Transaction']['amount'];
$stats = $this->Transaction->stats($receipt_id);
$receipt_credit['balance'] =
$receipt_credit['Transaction']['amount'] - $stats['Disbursement']['total'];
$this->pr(18, compact('receipt_credit'), $this->pr(18, compact('receipt_credit'),
"Receipt Credit Added"); "Receipt Credit Added");
} }
// Now find all unpaid charges // Now find all unpaid charges
if (isset($charge_ids)) { $lquery = $query;
$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'; $lquery['order'] = 'StatementEntry.effective_date ASC';
$charges = array(); $charges = $this->reconciledSet('CHARGE', $lquery, true);
foreach ($this->debitTypes() AS $dtype) { $this->pr(18, compact('charges'),
$rset = $this->reconciledSet($dtype, $lquery, true); "Outstanding Charges Determined");
$entries = $rset['entries'];
$charges = array_merge($charges, $entries);
$this->pr(18, compact('dtype', 'entries'), "Outstanding Debit Entries");
}
// Initialize our list of used credits // Initialize our list of used credits
$used_credits = array(); $used_credits = array();
// REVISIT <AP>: 20090806 // Work through all unpaid charges, applying payments as we go
// Testing a theory. We should never have foreach ($charges['entries'] AS $charge) {
// 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'), $this->pr(20, compact('charge'),
'Process Charge'); 'Process Charge');
@@ -497,26 +407,26 @@ class StatementEntry extends AppModel {
// will handle everything just fine. However, this // will handle everything just fine. However, this
// just saves extra processing if/when there is no // just saves extra processing if/when there is no
// means to resolve a charge anyway. // means to resolve a charge anyway.
if (empty($credits) && empty($receipt_credit['balance'])) { if (count($credits) == 0 && empty($receipt_credit['balance'])) {
$this->pr(17, 'No more available credits'); $this->pr(15, 'No more available credits');
break; break;
} }
$charge['balance'] = $charge['StatementEntry']['balance']; $charge['balance'] = $charge['StatementEntry']['balance'];
while ($charge['balance'] > 0 && while ($charge['balance'] > 0 &&
(!empty($credits) || !empty($receipt_credit['balance']))) { (count($credits) || !empty($receipt_credit['balance']))) {
$this->pr(20, compact('charge'), $this->pr(20, compact('charge'),
'Attempt Charge Reconciliation'); 'Attempt Charge Reconciliation');
// Use explicit credits before using implicit credits // Use explicit credits before using implicit credits
// (Not sure it matters though). // (Not sure it matters though).
if (!empty($credits)) { if (count($credits)) {
// Peel off the first credit available // Peel off the first credit available
$credit =& $credits[0]; $credit =& $credits[0];
$disbursement_date = $credit['StatementEntry']['effective_date']; $payment_date = $credit['StatementEntry']['effective_date'];
$disbursement_transaction_id = $credit['StatementEntry']['transaction_id']; $payment_transaction_id = $credit['StatementEntry']['transaction_id'];
$disbursement_account_id = $credit['StatementEntry']['account_id']; $payment_account_id = $credit['StatementEntry']['account_id'];
if (!isset($credit['balance'])) if (!isset($credit['balance']))
$credit['balance'] = $credit['StatementEntry']['amount']; $credit['balance'] = $credit['StatementEntry']['amount'];
@@ -524,57 +434,57 @@ class StatementEntry extends AppModel {
elseif (!empty($receipt_credit['balance'])) { elseif (!empty($receipt_credit['balance'])) {
// Use our only receipt credit // Use our only receipt credit
$credit =& $receipt_credit; $credit =& $receipt_credit;
$disbursement_date = $credit['Transaction']['stamp']; $payment_date = $credit['Transaction']['stamp'];
$disbursement_transaction_id = $credit['Transaction']['id']; $payment_transaction_id = $credit['Transaction']['id'];
$disbursement_account_id = $credit['LedgerEntry']['account_id']; $payment_account_id = $credit['LedgerEntry']['account_id'];
} }
else { else {
die("HOW DID WE GET HERE WITH NO SURPLUS?"); die("HOW DID WE GET HERE WITH NO SURPLUS?");
} }
// Set the disbursement amount to the maximum amount // Set the payment amount to the maximum amount
// possible without exceeding the charge or credit balance // possible without exceeding the charge or credit balance
$disbursement_amount = min($charge['balance'], $credit['balance']); $payment_amount = min($charge['balance'], $credit['balance']);
if (!isset($credit['applied'])) if (!isset($credit['applied']))
$credit['applied'] = 0; $credit['applied'] = 0;
$credit['applied'] += $disbursement_amount; $credit['applied'] += $payment_amount;
$credit['balance'] -= $disbursement_amount; $credit['balance'] -= $payment_amount;
$this->pr(20, compact('credit'), $this->pr(20, compact('credit'),
($credit['balance'] > 0 ? 'Utilized' : 'Exhausted') . ($credit['balance'] > 0 ? 'Utilized' : 'Exhausted') .
(!empty($credits) ? ' Credit' : ' Receipt')); (count($credits) ? ' Credit' : ' Receipt'));
if ($credit['balance'] < 0) if ($credit['balance'] < 0)
die("HOW DID WE END UP WITH NEGATIVE SURPLUS BALANCE?"); die("HOW DID WE END UP WITH NEGATIVE SURPLUS BALANCE?");
// If we've exhausted the credit, get it out of the // If we've exhausted the credit, get it out of the
// available credit pool (but keep track of it for later). // available credit pool (but keep track of it for later).
if ($credit['balance'] <= 0 && !empty($credits)) if ($credit['balance'] <= 0 && count($credits))
$used_credits[] = array_shift($credits); $used_credits[] = array_shift($credits);
// Add a disbursement that uses the available credit to pay the charge // Add a payment that uses the available credit to pay the charge
$disbursement = array('type' => $disbursement_type, $payment = array('type' => 'PAYMENT',
'account_id' => $disbursement_account_id, 'account_id' => $payment_account_id,
'amount' => $disbursement_amount, 'amount' => $payment_amount,
'effective_date' => $disbursement_date, 'effective_date' => $payment_date,
'transaction_id' => $disbursement_transaction_id, 'transaction_id' => $payment_transaction_id,
'customer_id' => $charge['StatementEntry']['customer_id'], 'customer_id' => $charge['StatementEntry']['customer_id'],
'lease_id' => $charge['StatementEntry']['lease_id'], 'lease_id' => $charge['StatementEntry']['lease_id'],
'charge_entry_id' => $charge['StatementEntry']['id'], 'charge_entry_id' => $charge['StatementEntry']['id'],
'comment' => null, 'comment' => null,
); );
$this->pr(20, compact('disbursement'), $this->pr(20, compact('payment'),
'New Disbursement Entry'); 'New Payment Entry');
$result = $this->addStatementEntry($disbursement); $result = $this->addStatementEntry($payment);
$ret['Disbursement'][] = $result; $ret['Payment'][] = $result;
if ($result['error']) if ($result['error'])
$ret['error'] = true; $ret['error'] = true;
// Adjust the charge balance to reflect the new disbursement // Adjust the charge balance to reflect the new payment
$charge['balance'] -= $disbursement_amount; $charge['balance'] -= $payment_amount;
if ($charge['balance'] < 0) if ($charge['balance'] < 0)
die("HOW DID WE GET A NEGATIVE CHARGE AMOUNT?"); die("HOW DID WE GET A NEGATIVE CHARGE AMOUNT?");
@@ -589,7 +499,7 @@ class StatementEntry extends AppModel {
$used_credits[] = array_shift($credits); $used_credits[] = array_shift($credits);
$this->pr(18, compact('credits', 'used_credits', 'receipt_credit'), $this->pr(18, compact('credits', 'used_credits', 'receipt_credit'),
'Disbursements added'); 'Payments added');
// Clean up any explicit credits that have been used // Clean up any explicit credits that have been used
foreach ($used_credits AS $credit) { foreach ($used_credits AS $credit) {
@@ -612,37 +522,20 @@ class StatementEntry extends AppModel {
if (!empty($receipt_credit['balance'])) { if (!empty($receipt_credit['balance'])) {
$credit =& $receipt_credit; $credit =& $receipt_credit;
$explicit_credit = $this->find $this->pr(18, compact('credit'),
('first', array('contain' => false, 'Create Explicit Credit');
'conditions' =>
array(array('transaction_id' => $credit['Transaction']['id']),
array('type' => 'SURPLUS')),
));
if (empty($explicit_credit)) { $result = $this->addStatementEntry
$this->pr(18, compact('credit'), (array('type' => 'SURPLUS',
'Create Explicit Credit'); 'account_id' => $credit['LedgerEntry']['account_id'],
'amount' => $credit['balance'],
$result = $this->addStatementEntry 'effective_date' => $credit['Transaction']['stamp'],
(array('type' => 'SURPLUS', 'transaction_id' => $credit['Transaction']['id'],
'account_id' => $credit['LedgerEntry']['account_id'], 'customer_id' => $credit['Customer']['id'],
'amount' => $credit['balance'], ));
'effective_date' => $credit['Transaction']['stamp'], $ret['Credit'] = $result;
'transaction_id' => $credit['Transaction']['id'], if ($result['error'])
'customer_id' => $customer_id, $ret['error'] = true;
'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)); return $this->prReturn($ret + array('error' => false));
@@ -656,7 +549,6 @@ class StatementEntry extends AppModel {
* - Returns summary data from the requested statement entry * - Returns summary data from the requested statement entry
*/ */
function stats($id = null, $query = null) { function stats($id = null, $query = null) {
$this->prFunctionLevel(array('log' => 19, 'show' => 10));
$this->prEnter(compact('id', 'query')); $this->prEnter(compact('id', 'query'));
$this->queryInit($query); $this->queryInit($query);
@@ -666,66 +558,46 @@ class StatementEntry extends AppModel {
if (isset($id)) if (isset($id))
$query['conditions'][] = array('StatementEntry.id' => $id); $query['conditions'][] = array('StatementEntry.id' => $id);
$types = array('Charge', 'Disbursement'); $rquery = $query;
foreach ($types AS $type_index => $this_name) { unset($rquery['link']['ChargeEntry']);
$that_name = $types[($type_index + 1) % 2]; $rquery['link']['PaymentEntry'] = array('fields' => array());
if ($this_name === 'Charge') {
$this_types = $this->debitTypes();
$that_types = $this->creditTypes();
} else {
$this_types = $this->creditTypes();
$that_types = $this->debitTypes();
}
$this_query = $query; $rquery['fields'] = array();
$this_query['fields'] = array(); $rquery['fields'][] = "StatementEntry.amount";
$this_query['fields'][] = "SUM(StatementEntry.amount) AS total"; $rquery['fields'][] = "SUM(PaymentEntry.amount) AS reconciled";
$this_query['conditions'][] = array('StatementEntry.type' => $this_types);
$result = $this->find('first', $this_query);
$stats[$this_name] = $result[0];
$this->pr(17, compact('this_query', 'result'), $this_name.'s'); $rquery['conditions'][] = array('StatementEntry.type' => 'CHARGE');
$rquery['group'] = 'StatementEntry.id';
// Tally the different types that result in credits towards the charges $result = $this->find('all', $rquery);
$stats[$this_name]['reconciled'] = 0; $stats['Charge'] = array('total' => 0, 'reconciled' => 0);
foreach ($that_types AS $that_type) { foreach($result AS $charge) {
$lc_that_type = strtolower($that_type); $stats['Charge']['total'] += $charge['StatementEntry']['amount'];
$that_query = $this_query; $stats['Charge']['reconciled'] += $charge[0]['reconciled'];
$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'];
// 'balance' is simply the difference between $this->pr(17, compact('query', 'result'),
// the balances of charges and disbursements 'Charges');
$stats['balance'] = $stats['Charge']['balance'] - $stats['Disbursement']['balance'];
if (!isset($stats['balance']))
$stats['balance'] = 0;
// 'account_balance' is really only relevant to $rquery = $query;
// callers that have requested charge and disbursement unset($rquery['link']['PaymentEntry']);
// stats with respect to a particular account. $rquery['link']['ChargeEntry'] = array('fields' => array());
// It represents the difference between inflow
// and outflow from that account. $rquery['fields'] = array();
$stats['account_balance'] = $stats['Charge']['reconciled'] - $stats['Disbursement']['total']; $rquery['fields'][] = "SUM(StatementEntry.amount) AS total";
if (!isset($stats['account_balance'])) $rquery['fields'][] = "SUM(IF(ChargeEntry.id IS NULL, 0, StatementEntry.amount)) AS reconciled";
$stats['account_balance'] = 0; $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');
return $this->prReturn($stats); return $this->prReturn($stats);
} }

View File

@@ -0,0 +1,9 @@
<?php
class StatementFraction extends AppModel {
var $belongsTo = array(
'StatementEntry',
'LedgerEntry',
);
}

View File

@@ -92,8 +92,8 @@ class Tender extends AppModel {
* - Add new LedgerEntry (L2a); T1; debit:NSF; -$A; N1 * - Add new LedgerEntry (L2a); T1; debit:NSF; -$A; N1
* - Add new LedgerEntry (L2b); T1; credit:A/R; -$A * - Add new LedgerEntry (L2b); T1; credit:A/R; -$A
* - For Tx associated with LE associated with C1: * - For Tx associated with LE associated with C1:
* - For each Disbursement SE of Tx: * - For each Payment SE of Tx:
* - Add new StatementEntry (S1n); T1; DISBURSEMENT; -1*S1n.amount * - Add new StatementEntry (S1n); T1; PAYMENT; -1*S1n.amount
* - New Transaction (T2) (?????) * - New Transaction (T2) (?????)
* - Add new StatementEntry (S2); T2; CHARGE; NSF; $35 * - Add new StatementEntry (S2); T2; CHARGE; NSF; $35
* - Add new LedgerEntry (L3a); T2; credit:NSF-Fee; $35 * - Add new LedgerEntry (L3a); T2; credit:NSF-Fee; $35

View File

@@ -19,9 +19,9 @@ class Transaction extends AppModel {
'conditions' => array('Charge.type' => 'CHARGE') 'conditions' => array('Charge.type' => 'CHARGE')
), ),
'Disbursement' => array( 'Payment' => array(
'className' => 'StatementEntry', 'className' => 'StatementEntry',
'conditions' => array('Disbursement.type' => 'DISBURSEMENT') 'conditions' => array('Payment.type' => 'PAYMENT')
), ),
'Debit' => array( 'Debit' => array(
@@ -37,7 +37,7 @@ class Transaction extends AppModel {
); );
var $default_log_level = array('log' => 30, 'show' => 30); var $default_log_level = 30;
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
@@ -51,25 +51,23 @@ class Transaction extends AppModel {
// Establish the transaction as an invoice // Establish the transaction as an invoice
$invoice =& $data['Transaction']; $invoice =& $data['Transaction'];
$invoice += $invoice['type'] = 'INVOICE';
array('type' => 'INVOICE', $invoice['crdr'] = 'DEBIT';
'crdr' => 'DEBIT', $invoice['account_id'] = $this->Account->accountReceivableAccountID();
'account_id' => $this->Account->accountReceivableAccountID(), $invoice['customer_id'] = $customer_id;
'customer_id' => $customer_id, $invoice['lease_id'] = $lease_id;
'lease_id' => $lease_id,
);
// Go through the statement entries and flag as charges // Go through the statement entries and flag as charges
foreach ($data['Entry'] AS &$entry) foreach ($data['Entry'] AS &$entry) {
$entry += array('type' => 'CHARGE', $entry['type'] = 'CHARGE';
'crdr' => 'CREDIT', $entry['crdr'] = 'CREDIT';
); }
$ids = $this->addTransaction($data['Transaction'], $data['Entry']); $ids = $this->addTransaction($data['Transaction'], $data['Entry']);
if (isset($ids['transaction_id'])) if (isset($ids['transaction_id']))
$ids['invoice_id'] = $ids['transaction_id']; $ids['invoice_id'] = $ids['transaction_id'];
return $this->prReturn($ids); return $ids;
} }
@@ -85,30 +83,26 @@ class Transaction extends AppModel {
// Establish the transaction as a receipt // Establish the transaction as a receipt
$receipt =& $data['Transaction']; $receipt =& $data['Transaction'];
$receipt += $receipt['type'] = 'RECEIPT';
array('type' => 'RECEIPT', $receipt['crdr'] = 'CREDIT';
'crdr' => 'CREDIT', $receipt['account_id'] = $this->Account->accountReceivableAccountID();
'account_id' => $this->Account->accountReceivableAccountID(), $receipt['customer_id'] = $customer_id;
'customer_id' => $customer_id, $receipt['lease_id'] = $lease_id;
'lease_id' => $lease_id,
);
// Go through the statement entries and flag as disbursements // Go through the statement entries and flag as payments
foreach ($data['Entry'] AS &$entry) foreach ($data['Entry'] AS &$entry) {
$entry += array('type' => 'DISBURSEMENT', // not used $entry['crdr'] = 'DEBIT';
'crdr' => 'DEBIT', if (empty($entry['account_id']) && isset($entry['Tender']['tender_type_id'])) {
'account_id' => $entry['account_id'] = $this->LedgerEntry->Tender->TenderType->
(isset($entry['Tender']['tender_type_id']) accountID($entry['Tender']['tender_type_id']);
? ($this->LedgerEntry->Tender->TenderType-> }
accountID($entry['Tender']['tender_type_id'])) }
: null),
);
$ids = $this->addTransaction($data['Transaction'], $data['Entry']); $ids = $this->addTransaction($data['Transaction'], $data['Entry']);
if (isset($ids['transaction_id'])) if (isset($ids['transaction_id']))
$ids['receipt_id'] = $ids['transaction_id']; $ids['receipt_id'] = $ids['transaction_id'];
return $this->prReturn($ids); return $ids;
} }
@@ -119,46 +113,36 @@ class Transaction extends AppModel {
* - Adds a new waiver * - Adds a new waiver
*/ */
function addWaiver($data, $charge_id, $customer_id, $lease_id = null) { function addWaiver($data, $customer_id, $lease_id = null) {
$this->prEnter(compact('data', 'charge_id', 'customer_id', 'lease_id')); $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 array('error' => true);
if (count($data['Entry']) != 1) // Establish the transaction as a waiver
INTERNAL_ERROR("Should be one Entry for addWaiver"); $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;
// Just make sure the disbursement(s) are marked as waivers // Go through the statement entries and flag as payments
// and that they go to cover the specific charge. foreach ($data['Entry'] AS &$entry) {
$data['Transaction']['disbursement_type'] = 'WAIVER'; $entry['type'] = 'WAIVE';
$data['Transaction']['assign_charge_entry_id'] = $charge_id; $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']);
}
}
// In all other respects this is just a receipt. $ids = $this->addTransaction($data['Transaction'], $data['Entry']);
$ids = $this->addReceipt($data, $customer_id, $lease_id);
if (isset($ids['transaction_id'])) if (isset($ids['transaction_id']))
$ids['waiver_id'] = $ids['transaction_id']; $ids['waiver_id'] = $ids['transaction_id'];
return $this->prReturn($ids); return $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);
} }
@@ -174,13 +158,11 @@ class Transaction extends AppModel {
// Establish the transaction as a deposit // Establish the transaction as a deposit
$deposit =& $data['Transaction']; $deposit =& $data['Transaction'];
$deposit += $deposit['type'] = 'DEPOSIT';
array('type' => 'DEPOSIT', $deposit['crdr'] = 'DEBIT';
'crdr' => 'DEBIT', $deposit['account_id'] = $account_id;
'account_id' => $account_id, $deposit['customer_id'] = null;
'customer_id' => null, $deposit['lease_id'] = null;
'lease_id' => null,
);
// Save the list of IDs, so that we can mark their // Save the list of IDs, so that we can mark their
// deposit transaction after it has been created. // deposit transaction after it has been created.
@@ -210,7 +192,7 @@ class Transaction extends AppModel {
); );
} }
return $this->prReturn($ids); return $ids;
} }
@@ -226,13 +208,10 @@ class Transaction extends AppModel {
// Establish the transaction as a close // Establish the transaction as a close
$close =& $data['Transaction']; $close =& $data['Transaction'];
$close += $close['type'] = 'CLOSE';
array('type' => 'CLOSE', $close['account_id'] = null;
'crdr' => null, $close['customer_id'] = null;
'account_id' => null, $close['lease_id'] = null;
'customer_id' => null,
'lease_id' => null,
);
$ledger_ids = array(); $ledger_ids = array();
$data['Entry'] = array(); $data['Entry'] = array();
@@ -270,73 +249,7 @@ class Transaction extends AppModel {
); );
} }
return $this->prReturn($ids); return $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);
} }
@@ -409,7 +322,7 @@ class Transaction extends AppModel {
* *
* - Entry (array) * - Entry (array)
* - [MANDATORY] * - [MANDATORY]
* - type (CHARGE, DISBURSEMENT) * - type (CHARGE, PAYMENT)
* - account_id * - account_id
* - crdr * - crdr
* - amount * - amount
@@ -514,7 +427,7 @@ class Transaction extends AppModel {
// Create the statement entry // Create the statement entry
$se = $se =
array_intersect_key($entry, array_intersect_key($entry,
array_flip(array('type', 'account_id', 'amount', array_flip(array('type', 'amount',
'effective_date', 'through_date', 'due_date', 'effective_date', 'through_date', 'due_date',
'customer_id', 'lease_id', 'customer_id', 'lease_id',
'charge_entry_id'))) + 'charge_entry_id'))) +
@@ -522,19 +435,16 @@ class Transaction extends AppModel {
array_flip(array('customer_id', 'lease_id'))); array_flip(array('customer_id', 'lease_id')));
$se['comment'] = $entry['statement_entry_comment']; $se['comment'] = $entry['statement_entry_comment'];
// (DISBURSEMENTS will have statement entries created below, when // (PAYMENTS will have statement entries created below, when
// assigning credits, and DEPOSITS don't have statement entries) // assigning credits, and DEPOSITS don't have statement entries)
if (empty($entry['charge_entry_id']) && if ($transaction['type'] != 'INVOICE' && $subtype !== 'NSF')
(empty($transaction['customer_id']) ||
($transaction['account_id'] == $this->Account->accountReceivableAccountID() &&
$transaction['crdr'] == 'CREDIT')))
$se = null; $se = null;
// NSF transactions don't use LedgerEntries // NSF transactions don't use LedgerEntries
// REVISIT <AP>: 20090731 // REVISIT <AP>: 20090731
// Doesn't seem right... probably doing this because of the // Doesn't seem right... probably doing this because of the
// single A/R entry we add below for NSF // single A/R entry we add below for NSF
if ($entry['type'] == 'SURPLUS' || $subtype === 'NSF') if ($subtype === 'NSF')
$le1 = $le1_tender = $le2 = null; $le1 = $le1_tender = $le2 = null;
// Replace combined entry with our new individual entries // Replace combined entry with our new individual entries
@@ -566,7 +476,6 @@ class Transaction extends AppModel {
$this->create(); $this->create();
if (!$this->save($transaction)) if (!$this->save($transaction))
return $this->prReturn(array('error' => true) + $ret); return $this->prReturn(array('error' => true) + $ret);
$transaction_stamp = $this->field('stamp');
// Set up our return ids array // Set up our return ids array
$ret['transaction_id'] = $this->id; $ret['transaction_id'] = $this->id;
@@ -593,8 +502,11 @@ class Transaction extends AppModel {
} }
if (!empty($se)) { if (!empty($se)) {
//pr($ret['entries'][$e_index]['DoubleEntry']); die;
$le_id = $ret['entries'][0]['DoubleEntry']['Entry1']['ledger_entry_id'];
$se['transaction_id'] = $ret['transaction_id']; $se['transaction_id'] = $ret['transaction_id'];
$se += array('effective_date' => $transaction_stamp); $se['Fraction'] = array(array('ledger_entry_id' => $le_id,
'amount' => $se['amount']));
$result = $this->StatementEntry->addStatementEntry($se); $result = $this->StatementEntry->addStatementEntry($se);
$ret['entries'][$e_index]['StatementEntry'] = $result; $ret['entries'][$e_index]['StatementEntry'] = $result;
if ($result['error']) { if ($result['error']) {
@@ -604,22 +516,15 @@ class Transaction extends AppModel {
} }
} }
if ($transaction['account_id'] == $this->Account->accountReceivableAccountID() if (($transaction['type'] == 'INVOICE' ||
&& !$ret['error']) { $transaction['type'] == 'RECEIPT') &&
$subtype !== 'NSF' && !$ret['error']) {
$result = $this->StatementEntry->assignCredits $result = $this->StatementEntry->assignCredits
(null, (array('link' => array('Customer'),
($transaction['crdr'] == 'CREDIT' 'conditions' => array('Customer.id' => $transaction['customer_id'])),
($transaction['type'] == 'RECEIPT'
? $ret['transaction_id'] ? $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; $ret['assigned'] = $result;
if ($result['error']) if ($result['error'])
@@ -645,7 +550,7 @@ class Transaction extends AppModel {
// Enter the NSF // Enter the NSF
// This is the transaction pulling money from the bank account // This is the transaction pulling money from the bank account
// and recording it in the NSF account. It has nothing to do // and recording it in the NSF account. It has nothing to do
// with the customer statement (charges, disbursements, credits, etc). // with the customer statement (charges, payments, credits, etc).
$bounce_result = $this->addDeposit $bounce_result = $this->addDeposit
(array('Transaction' => array(), (array('Transaction' => array(),
'Entry' => array(array('tender_id' => null, 'Entry' => array(array('tender_id' => null,
@@ -695,20 +600,20 @@ class Transaction extends AppModel {
$rollback['Transaction']['customer_id'] = $tender['Tender']['customer_id']; $rollback['Transaction']['customer_id'] = $tender['Tender']['customer_id'];
$rollback['Transaction']['amount'] = -1 * $tender['LedgerEntry']['amount']; $rollback['Transaction']['amount'] = -1 * $tender['LedgerEntry']['amount'];
foreach ($nsf_ledger_entry['Transaction']['StatementEntry'] AS $disbursement) { foreach ($nsf_ledger_entry['Transaction']['StatementEntry'] AS $payment) {
if ($disbursement['type'] === 'SURPLUS') { if ($payment['type'] === 'SURPLUS') {
$disbursement['type'] = 'VOID'; $payment['type'] = 'VOID';
$this->StatementEntry->id = $disbursement['id']; $this->StatementEntry->id = $payment['id'];
$this->StatementEntry->saveField('type', $disbursement['type']); $this->StatementEntry->saveField('type', $payment['type']);
} }
else { else {
$rollback['Entry'][] = $rollback['Entry'][] =
array('type' => $disbursement['type'], array('type' => $payment['type'],
'amount' => -1 * $disbursement['amount'], 'amount' => -1 * $payment['amount'],
'account_id' => $this->Account->nsfAccountID(), 'account_id' => $this->Account->nsfAccountID(),
'customer_id' => $disbursement['customer_id'], 'customer_id' => $payment['customer_id'],
'lease_id' => $disbursement['lease_id'], 'lease_id' => $payment['lease_id'],
'charge_entry_id' => $disbursement['charge_entry_id'], 'charge_entry_id' => $payment['charge_entry_id'],
'effective_date' => $stamp, 'effective_date' => $stamp,
); );
} }
@@ -716,13 +621,11 @@ class Transaction extends AppModel {
// Record the transaction, which will un-pay previously paid // Record the transaction, which will un-pay previously paid
// charges, void any credits, and other similar work. // charges, void any credits, and other similar work.
if (count($rollback['Entry'])) { $rollback_result = $this->addTransaction($rollback['Transaction'], $rollback['Entry'], 'NSF');
$rollback_result = $this->addTransaction($rollback['Transaction'], $rollback['Entry'], 'NSF'); $this->pr(20, compact('rollback', 'rollback_result'));
$this->pr(20, compact('rollback', 'rollback_result')); $ret['rollback'] = $rollback_result;
$ret['rollback'] = $rollback_result; if ($rollback_result['error'])
if ($rollback_result['error']) return $this->prReturn(array('error' => true) + $ret);
return $this->prReturn(array('error' => true) + $ret);
}
// Add NSF Charge // Add NSF Charge
$charge_result = $this->addInvoice $charge_result = $this->addInvoice
@@ -748,8 +651,7 @@ class Transaction extends AppModel {
return $this->prReturn(array('error' => true) + $ret); return $this->prReturn(array('error' => true) + $ret);
$ret['nsf_transaction_id'] = $ret['bounce']['transaction_id']; $ret['nsf_transaction_id'] = $ret['bounce']['transaction_id'];
if (!empty($ret['rollback'])) $ret['nsf_ledger_entry_id'] = $ret['rollback']['entries'][0]['DoubleEntry']['Entry1']['ledger_entry_id'];
$ret['nsf_ledger_entry_id'] = $ret['rollback']['entries'][0]['DoubleEntry']['Entry1']['ledger_entry_id'];
return $this->prReturn($ret + array('error' => false)); return $this->prReturn($ret + array('error' => false));
} }
@@ -800,7 +702,7 @@ class Transaction extends AppModel {
} }
elseif ($table == 'StatementEntry') { elseif ($table == 'StatementEntry') {
$squery['fields'] = array_merge($squery['fields'], $squery['fields'] = array_merge($squery['fields'],
$this->StatementEntry->chargeDisbursementFields(true)); $this->StatementEntry->chargePaymentFields(true));
} }
else { else {
$squery['fields'][] = "SUM({$table}.amount) AS total"; $squery['fields'][] = "SUM({$table}.amount) AS total";

View File

@@ -27,8 +27,6 @@ class Unit extends AppModel {
'Lease', 'Lease',
); );
//var $default_log_level = array('log' => 30, 'show' => 15);
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************
@@ -52,7 +50,7 @@ class Unit extends AppModel {
} }
function occupiedEnumValue() { function occupiedEnumValue() {
return $this->statusValue('OCCUPIED'); return statusValue('OCCUPIED');
} }
function conditionOccupied() { function conditionOccupied() {
@@ -70,71 +68,18 @@ class Unit extends AppModel {
return ('Unit.status <= ' . $this->statusValue('UNAVAILABLE')); 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 * function: updateStatus
* - Update the given unit to the given status * - Update the given unit to the given status
*/ */
function updateStatus($id, $status, $check = false) { function updateStatus($id, $status) {
$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; $this->id = $id;
//pr(compact('id', 'status'));
$this->saveField('status', $status); $this->saveField('status', $status);
return $this->prReturn(true);
} }
/************************************************************************** /**************************************************************************
************************************************************************** **************************************************************************
************************************************************************** **************************************************************************

View File

@@ -164,9 +164,9 @@ echo $this->element('statement_entries', array
//'grid_setup' => array('hiddengrid' => true), //'grid_setup' => array('hiddengrid' => true),
//'caption' => '<SPAN id="receipt-charges-caption"></SPAN>', //'caption' => '<SPAN id="receipt-charges-caption"></SPAN>',
'caption' => 'Collected ' . Inflector::pluralize($account['name']), 'caption' => 'Collected ' . Inflector::pluralize($account['name']),
'filter' => array(//'StatementEntry.type' => 'DISBURSEMENT', 'filter' => array('StatementEntry.type' => 'PAYMENT',
'ChargeEntry.account_id' => $account['id']), 'ChargeEntry.account_id' => $account['id']),
'exclude' => array('Account', 'Charge', 'Type'), 'exclude' => array('Account', 'Charge'),
), ),
)); ));

View File

@@ -82,7 +82,7 @@ echo $this->element('ledger_entries', array
"(". $current_ledger['name'] .")"), "(". $current_ledger['name'] .")"),
'filter' => array('Ledger.id' => $current_ledger['id']), 'filter' => array('Ledger.id' => $current_ledger['id']),
'exclude' => array('Account', 'Amount', 'Cr/Dr', 'Balance', 'exclude' => array('Account', 'Amount', 'Cr/Dr', 'Balance',
empty($account['receipts']) ? 'Tender' : null), empty($account['payments']) ? 'Tender' : null),
'include' => array('Debit', 'Credit', 'Sub-Total'), 'include' => array('Debit', 'Credit', 'Sub-Total'),
))); )));
@@ -100,7 +100,7 @@ echo $this->element('ledger_entries', array
'caption' => "Entire Ledger", 'caption' => "Entire Ledger",
'filter' => array('Account.id' => $account['id']), 'filter' => array('Account.id' => $account['id']),
'exclude' => array('Account', 'Amount', 'Cr/Dr', 'Balance', 'exclude' => array('Account', 'Amount', 'Cr/Dr', 'Balance',
empty($account['receipts']) ? 'Tender' : null), empty($account['payments']) ? 'Tender' : null),
'include' => array('Debit', 'Credit', 'Sub-Total'), 'include' => array('Debit', 'Credit', 'Sub-Total'),
))); )));

View File

@@ -251,7 +251,7 @@ echo($this->element
'comment' => true, 'comment' => true,
))) . "\n"); ))) . "\n");
echo $form->submit(isset($this->data['Customer']) ? 'Update' : 'Add New Customer') . "\n"; echo $form->submit('Update') . "\n";
?> ?>
<div CLASS="dynamic-set"> <div CLASS="dynamic-set">
@@ -268,7 +268,7 @@ echo $form->submit(isset($this->data['Customer']) ? 'Update' : 'Add New Customer
<?php <?php
; // Alignment ; // Alignment
echo $form->submit(isset($this->data['Customer']) ? 'Update' : 'Add New Customer') . "\n"; echo $form->submit('Update') . "\n";
echo $form->submit('Cancel', array('name' => 'cancel')) . "\n"; echo $form->submit('Cancel', array('name' => 'cancel')) . "\n";
echo $form->end() . "\n"; echo $form->end() . "\n";
echo '</div>' . "\n"; echo '</div>' . "\n";

View File

@@ -70,8 +70,6 @@ function showResponse(responseText, statusText) {
if (statusText == 'success') { if (statusText == 'success') {
// get a clean slate // get a clean slate
//resetForm(); //resetForm();
// REVISIT <AP>: 20090806 Add to resetForm()
updateCharges($("#customer-id").val());
} }
else { else {
alert('not successful??'); alert('not successful??');
@@ -111,6 +109,8 @@ function onRowSelect(grid_id, customer_id) {
$(grid_id).getCell(customer_id, 'Customer-id') + $(grid_id).getCell(customer_id, 'Customer-id') +
'</A>'); '</A>');
$("#receipt-customer-name").html($(grid_id).getCell(customer_id, 'Customer-name')); $("#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 // Hide the "no customer" message and show the current customer
$(".customer-selection-invalid").hide(); $(".customer-selection-invalid").hide();
@@ -143,8 +143,7 @@ function addPaymentSource(flash) {
addDiv('payment-entry-id', 'payment', 'payments', flash, addDiv('payment-entry-id', 'payment', 'payments', flash,
// HTML section // HTML section
'<FIELDSET CLASS="payment subset">' + '<FIELDSET CLASS="payment subset">' +
<?php /* '<LEGEND>Payment #%{id} (%{remove})</LEGEND>' + */ ?> '<LEGEND>Payment #%{id} (%{remove})</LEGEND>' +
'<LEGEND>Payment</LEGEND>' +
'<DIV ID="payment-div-%{id}">' + '<DIV ID="payment-div-%{id}">' +
<?php <?php
@@ -227,10 +226,7 @@ function switchPaymentType(paymentid_base, paymentid, radioid) {
function updateChargesGrid(idlist) { function updateChargesGrid(idlist) {
var dynamic_post = new Array(); $('#charge-entries-jqGrid').setPostDataItem('idlist', serialize(idlist));
dynamic_post['idlist'] = idlist;
$('#charge-entries-jqGrid').setPostDataItem('dynamic_post_replace', serialize(dynamic_post));
$('#charge-entries-jqGrid') $('#charge-entries-jqGrid')
.setGridParam({ page: 1 }) .setGridParam({ page: 1 })
.trigger("reloadGrid"); .trigger("reloadGrid");
@@ -242,8 +238,6 @@ function updateCharges(id) {
url += '/'+id; url += '/'+id;
$('#charge-entries-jqGrid').clearGridData(); $('#charge-entries-jqGrid').clearGridData();
$("#receipt-balance").html("Calculating...");
$("#receipt-charges-caption").html("Please Wait...");
$.ajax({ $.ajax({
type: "GET", type: "GET",
@@ -257,13 +251,7 @@ function updateCharges(id) {
$('#receipt-balance').html(fmtCurrency($('entries',xml).attr('balance'))); $('#receipt-balance').html(fmtCurrency($('entries',xml).attr('balance')));
$("#receipt-charges-caption").html("Outstanding Charges"); $("#receipt-charges-caption").html("Outstanding Charges");
updateChargesGrid(ids); 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)); */
}
}); });
} }
@@ -366,18 +354,14 @@ echo $this->element('form_table',
echo $form->submit('Generate Receipt') . "\n"; echo $form->submit('Generate Receipt') . "\n";
?> ?>
<?php /*
<fieldset CLASS="payment superset"> <fieldset CLASS="payment superset">
<legend>Payments</legend> <legend>Payments</legend>
*/ ?>
<input type="hidden" id="payment-entry-id" value="0"> <input type="hidden" id="payment-entry-id" value="0">
<div id="payments"></div> <div id="payments"></div>
<?php /*
<fieldset> <legend> <fieldset> <legend>
<a href="#" onClick="addPaymentSource(true); return false;">Add Another Payment</a> <a href="#" onClick="addPaymentSource(true); return false;">Add Another Payment</a>
</legend> </fieldset> </legend> </fieldset>
</fieldset> </fieldset>
*/ ?>
<?php echo $form->end('Generate Receipt'); ?> <?php echo $form->end('Generate Receipt'); ?>

View File

@@ -84,8 +84,6 @@ echo $this->element('statement_entries', array
'filter' => array('Customer.id' => $customer['Customer']['id'], 'filter' => array('Customer.id' => $customer['Customer']['id'],
'type !=' => 'VOID'), 'type !=' => 'VOID'),
'exclude' => array('Customer'), 'exclude' => array('Customer'),
'sort_column' => 'Effective',
'sort_order' => 'DESC',
))); )));
@@ -111,8 +109,6 @@ echo $this->element('ledger_entries', array
'filter' => array('Customer.id' => $customer['Customer']['id'], 'filter' => array('Customer.id' => $customer['Customer']['id'],
'Account.id !=' => '-AR-'), 'Account.id !=' => '-AR-'),
'exclude' => array('Customer'), 'exclude' => array('Customer'),
'sort_column' => 'Date',
'sort_order' => 'DESC',
))); )));

View File

@@ -7,7 +7,7 @@ $cols['Relationship'] = array('index' => 'ContactsCustomer.type', 'formatt
$cols['Name'] = array('index' => 'Customer.name', 'formatter' => 'longname'); $cols['Name'] = array('index' => 'Customer.name', 'formatter' => 'longname');
$cols['Last Name'] = array('index' => 'PrimaryContact.last_name', 'formatter' => 'name'); $cols['Last Name'] = array('index' => 'PrimaryContact.last_name', 'formatter' => 'name');
$cols['First Name'] = array('index' => 'PrimaryContact.first_name', 'formatter' => 'name'); $cols['First Name'] = array('index' => 'PrimaryContact.first_name', 'formatter' => 'name');
$cols['Leases'] = array('index' => 'current_lease_count', 'formatter' => 'number'); $cols['Leases'] = array('index' => 'lease_count', 'formatter' => 'number');
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency'); $cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency');
$cols['Comment'] = array('index' => 'Customer.comment', 'formatter' => 'comment'); $cols['Comment'] = array('index' => 'Customer.comment', 'formatter' => 'comment');

View File

@@ -17,9 +17,8 @@ $cols['Unit'] = array('index' => 'Unit.name', 'formatter' =>
$cols['Comment'] = array('index' => 'StatementEntry.comment', 'formatter' => 'comment', 'width'=>150); $cols['Comment'] = array('index' => 'StatementEntry.comment', 'formatter' => 'comment', 'width'=>150);
$cols['Type'] = array('index' => 'StatementEntry.type', 'formatter' => 'enum', 'width'=>120); $cols['Charge'] = array('index' => 'charge', 'formatter' => 'currency');
$cols['Debit'] = array('index' => 'charge', 'formatter' => 'currency'); $cols['Payment'] = array('index' => 'payment', 'formatter' => 'currency');
$cols['Credit'] = array('index' => 'disbursement', 'formatter' => 'currency');
$cols['Applied'] = array('index' => "applied", 'formatter' => 'currency'); $cols['Applied'] = array('index' => "applied", 'formatter' => 'currency');
$cols['Sub-Total'] = array('index' => 'subtotal-balance', 'formatter' => 'currency', 'sortable' => false); $cols['Sub-Total'] = array('index' => 'subtotal-balance', 'formatter' => 'currency', 'sortable' => false);

View File

@@ -19,13 +19,13 @@ class FormatHelper extends AppHelper {
true)); true));
} }
function currency($amount, $spans = false, $dollar_sign = null) { function currency($amount, $spans = false) {
if (!isset($amount)) if (!isset($amount))
return '-'; return '-';
//return null; //return null;
$currency = self::$number->currency($amount, $currency = self::$number->currency($amount,
isset($dollar_sign) ? $dollar_sign : 'USD', 'USD',
$spans ? array('before'=>'', 'after'=>'') : array()); $spans ? array('before'=>'', 'after'=>'') : array());
if ($spans) if ($spans)

View File

@@ -211,9 +211,8 @@ class GridHelper extends AppHelper {
// Incorporate all other user options // Incorporate all other user options
if (isset($config)) if (isset($config))
$this->jqGrid_options = array_merge($this->jqGrid_options, $config); $this->jqGrid_options = array_merge_recursive($this->jqGrid_options, $config);
//pr(compact('config') + array('jqGrid_options' => $this->jqGrid_options));
echo $view->element('jqGrid', $this->jqGrid_options); echo $view->element('jqGrid', $this->jqGrid_options);
// Since we only have one instance of this class // Since we only have one instance of this class

View File

@@ -0,0 +1,88 @@
<?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>

View File

@@ -0,0 +1,80 @@
<?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>

View File

@@ -1,106 +1,74 @@
<?php /* -*- mode:PHP -*- */ <?php /* -*- mode:PHP -*- */
echo '<div class="refund input">' . "\n"; echo '<div class="account deposit">' . "\n";
echo '<H2>Issue Refund</H2>' . "\n"; echo '<H2>Perform Bank Deposit</H2>' . "\n";
echo '<P>Enter the amount to refund, and the account to pay it from.' . "\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 '<P><BR>' . "\n"; echo '<P><BR>' . "\n";
if (isset($lease)) { //pr(compact('tillableAccount', 'depositableAccount'));
$customer = $lease['Customer'];
$unit = $lease['Unit'];
}
if (isset($customer['Customer'])) echo $form->create(null, array('id' => 'deposit-form',
$customer = $customer['Customer']; 'url' => array('controller' => 'accounts',
'action' => 'deposit')));
if (isset($lease['Lease'])) foreach ($tillableAccount AS $acct) {
$lease = $lease['Lease']; //$acct = $acct['Account'];
// We're not actually using a grid to select the customer / lease echo "\n";
// but we could/should be, and the result would be selection-text echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.checked',
echo ('<DIV CLASS="refund grid-selection-text">' . array(//'label' => $acct['Account']['name'],
'<TABLE>' . "\n"); 'type' => 'checkbox',
'checked' => true,
echo ('<TR><TD style="padding-right: 1em;">' . $customer['name'] . '</TD>' . 'value' => true,
' <TD>' . '(Customer #' . $customer['id'] . ')' . '</TD>' . 'label' => (" I have exactly " .
'</TR>' . "\n"); FormatHelper::currency($acct['Account']['stats']['Ledger']['balance']) .
" in " . ($acct['Account']['name'] === 'Cash'
if (isset($lease)) ? 'Cash'
echo ('<TR><TD style="padding-right: 1em;">' . 'Unit ' . $unit['name'] . '</TD>' . : Inflector::pluralize($acct['Account']['name'])) .
' <TD>' . '(Lease #' . $lease['number'] . ')' . '</TD>' . " and will be depositing it all.")
'</TR>' . "\n"); ));
echo "\n";
echo ('<TR><TD style="padding-right: 1em;">Refundable Balance:</TD>' . echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.amount',
' <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', array('type' => 'hidden',
'value' => $lease['id'])) . "\n"; '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";
echo $form->end('Issue Refund'); $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,
<script type="text/javascript"><!-- // Grid configuration
'config' => array
// Reset the form (
function resetForm() { 'grid_div_id' => $grid_div_id,
datepickerNow('TransactionStamp'); 'caption' => ('<A HREF="#" ONCLICK="$(\'#'.$grid_div_id.' .HeaderButton\').click();'.
' return false;">Items in '.$acct['Account']['name'].' Ledger</A>'),
'grid_setup' => array('hiddengrid' => true),
),
));
} }
$(document).ready(function(){ $options = array();
$("#TransactionStamp") foreach ($depositableAccount AS $acct) {
.attr('autocomplete', 'off') $options[$acct['Account']['id']] = $acct['Account']['name'];
.datepicker({ constrainInput: true, }
numberOfMonths: [1, 1],
showCurrentAtPos: 0,
dateFormat: 'mm/dd/yy' });
resetForm(); echo $form->input('Deposit.Account.id', array('label' => 'Deposit Account ',
}); 'options' => $options));
--></script> echo $form->end('Perform Deposit');
</div> /* End page div */
echo '</div>' . "\n";

View File

@@ -88,8 +88,6 @@ echo $this->element('statement_entries', array
'filter' => array('Lease.id' => $lease['id']), 'filter' => array('Lease.id' => $lease['id']),
'include' => array('Through'), 'include' => array('Through'),
'exclude' => array('Customer', 'Lease', 'Unit'), 'exclude' => array('Customer', 'Lease', 'Unit'),
'sort_column' => 'Effective',
'sort_order' => 'DESC',
))); )));

View File

@@ -72,7 +72,7 @@ echo $this->element('ledger_entries', array
'filter' => array('Ledger.id' => $ledger['id']), 'filter' => array('Ledger.id' => $ledger['id']),
'exclude' => array('Ledger', 'Account', 'exclude' => array('Ledger', 'Account',
'Amount', 'Cr/Dr', 'Balance', 'Amount', 'Cr/Dr', 'Balance',
empty($account['receipts']) ? 'Tender' : null), empty($account['payments']) ? 'Tender' : null),
'include' => array('Debit', 'Credit', 'Sub-Total'), 'include' => array('Debit', 'Credit', 'Sub-Total'),
))); )));

View File

@@ -10,23 +10,20 @@ echo '<div class="statement-entry view">' . "\n";
*/ */
$transaction = $entry['Transaction']; $transaction = $entry['Transaction'];
$account = $entry['Account']; $account = $entry['LedgerEntry'][0]['Account'];
$customer = $entry['Customer']; $customer = $entry['Customer'];
$lease = $entry['Lease']; $lease = $entry['Lease'];
$entry = $entry['StatementEntry']; $entry = $entry['StatementEntry'];
$Ttype = ucfirst(strtolower($transaction['type']));
$rows = array(); $rows = array();
$rows[] = array('ID', $entry['id']); $rows[] = array('ID', $entry['id']);
$rows[] = array($Ttype, $html->link('#'.$transaction['id'], $rows[] = array('Transaction', $html->link('#'.$transaction['id'],
array('controller' => 'transactions', array('controller' => 'transactions',
'action' => 'view', 'action' => 'view',
$transaction['id']))); $transaction['id'])));
$rows[] = array('Timestamp', FormatHelper::datetime($transaction['stamp'])); $rows[] = array('Timestamp', FormatHelper::datetime($transaction['stamp']));
$rows[] = array('Effective', FormatHelper::date($entry['effective_date'])); $rows[] = array('Effective', FormatHelper::date($entry['effective_date']));
if (in_array($entry['type'], array('CHARGE', 'PAYMENT'))) $rows[] = array('Through', FormatHelper::date($entry['through_date']));
$rows[] = array('Through', FormatHelper::date($entry['through_date']));
$rows[] = array('Type', $entry['type']); $rows[] = array('Type', $entry['type']);
$rows[] = array('Amount', FormatHelper::currency($entry['amount'])); $rows[] = array('Amount', FormatHelper::currency($entry['amount']));
$rows[] = array('Account', $html->link($account['name'], $rows[] = array('Account', $html->link($account['name'],
@@ -59,12 +56,12 @@ echo $this->element('table',
*/ */
if (strtoupper($entry['type']) === 'CHARGE') { if (strtoupper($entry['type']) === 'CHARGE') {
$applied_caption = "Disbursements Applied"; $applied_caption = "Payments Applied";
//$remaining_caption = "Charge Balance"; //$remaining_caption = "Charge Balance";
} }
else { else {
$applied_caption = "Disbursed to Charges"; $applied_caption = "Applied to Charges";
//$remaining_caption = "Disbursement Balance"; //$remaining_caption = "Payment Balance";
} }
$remaining_caption = "Remaining Balance"; $remaining_caption = "Remaining Balance";

View File

@@ -1,89 +0,0 @@
<?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>

View File

@@ -67,11 +67,7 @@ echo '<div CLASS="detail supporting">' . "\n";
* Statement Entries * Statement Entries
*/ */
if ($transaction['type'] === 'INVOICE' || if ($transaction['type'] === 'INVOICE' || $transaction['type'] === 'RECEIPT') {
$transaction['type'] === 'RECEIPT' ||
$transaction['type'] === 'CREDIT_NOTE' ||
$transaction['type'] === 'PAYMENT'
) {
echo $this->element('statement_entries', array echo $this->element('statement_entries', array
(// Grid configuration (// Grid configuration
'config' => array 'config' => array

View File

@@ -1,27 +0,0 @@
<?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";

View File

@@ -18,9 +18,6 @@ security deposits charged.
Customer Selection on the Receipt Page is broken. Customer Selection on the Receipt Page is broken.
(Selecting a row and waiting for the update). (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 Get Petty Cash working. We'll need to add one or more expense
accounts. We'll also need to implement purchase order accounts. We'll also need to implement purchase order
functionality, or at least simple an expense page. functionality, or at least simple an expense page.