Compare commits
163 Commits
single_AR_
...
invoice_re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3313a18407 | ||
|
|
b2ab134996 | ||
|
|
fbb751d054 | ||
|
|
fff6eaeeca | ||
|
|
1408a3dcb3 | ||
|
|
9507bfddc8 | ||
|
|
aa682c9bb2 | ||
|
|
0c8649d1c5 | ||
|
|
405e20f37f | ||
|
|
75ff8a2a40 | ||
|
|
20c1284788 | ||
|
|
116b014332 | ||
|
|
8fcecb7092 | ||
|
|
8a36b441de | ||
|
|
b1914f7cca | ||
|
|
0649329dde | ||
|
|
50c4ee225f | ||
|
|
d1075a2ea2 | ||
|
|
6210838b22 | ||
|
|
f5b87fa6b0 | ||
|
|
1d8ef76efa | ||
|
|
60f43efdb7 | ||
|
|
abc5f1feff | ||
|
|
8b7c27b5db | ||
|
|
865d9ee617 | ||
|
|
059dbd3190 | ||
|
|
8a3c6ae21b | ||
|
|
03af4e731f | ||
|
|
2cd73ed9e8 | ||
|
|
b97d071bb4 | ||
|
|
6a0a77c116 | ||
|
|
d6be905c08 | ||
|
|
6e1c684a06 | ||
|
|
0c13ef5cda | ||
|
|
9529431be2 | ||
|
|
d2ac8019ee | ||
|
|
1994512d6e | ||
|
|
a462e9be1c | ||
|
|
ccf0138eee | ||
|
|
288e4c05de | ||
|
|
b3ca719517 | ||
|
|
b8fd17efcf | ||
|
|
ece7ab25ae | ||
|
|
860006ed38 | ||
|
|
e067af589c | ||
|
|
546766d9be | ||
|
|
4b43433d2d | ||
|
|
5a1fdedad0 | ||
|
|
6ce9eec1a4 | ||
|
|
d44007a358 | ||
|
|
8d2a544042 | ||
|
|
d9297d89ab | ||
|
|
46db9c8341 | ||
|
|
ad17023570 | ||
|
|
9cac8c26a9 | ||
|
|
90d990e0a5 | ||
|
|
1cb56894ce | ||
|
|
f2f389ee45 | ||
|
|
5877874e82 | ||
|
|
de5f4208eb | ||
|
|
6507009acc | ||
|
|
6e1eba03a0 | ||
|
|
7a31f5cbd2 | ||
|
|
112aa7cf59 | ||
|
|
372578f171 | ||
|
|
645458c081 | ||
|
|
1afaede12a | ||
|
|
5a4cfb0581 | ||
|
|
17b6986758 | ||
|
|
00b473ee4f | ||
|
|
3463ae329b | ||
|
|
b3bceef570 | ||
|
|
80ddeeed6f | ||
|
|
43787d6434 | ||
|
|
72c55459ca | ||
|
|
977b21ed96 | ||
|
|
e4132237b9 | ||
|
|
c2d2a1c400 | ||
|
|
208aaaa504 | ||
|
|
21aa2c1d26 | ||
|
|
d3959e92e5 | ||
|
|
60c4d950fa | ||
|
|
d7db5a32d6 | ||
|
|
1932a9c316 | ||
|
|
d09af4f475 | ||
|
|
437ebabfd3 | ||
|
|
686c58afea | ||
|
|
754e2327c7 | ||
|
|
aca9d13a7d | ||
|
|
311d8be646 | ||
|
|
f77281835d | ||
|
|
1ed98b5e39 | ||
|
|
a2cb53a83b | ||
|
|
dff8394e91 | ||
|
|
014fa0feb9 | ||
|
|
3aacbb94aa | ||
|
|
5f9b9e15cb | ||
|
|
1f95e835f8 | ||
|
|
f7f9da92f3 | ||
|
|
3f275b127b | ||
|
|
f4319364f2 | ||
|
|
54ded6173a | ||
|
|
b36d3117a3 | ||
|
|
06b8ff2ae9 | ||
|
|
40264d3c39 | ||
|
|
5007890d09 | ||
|
|
aab8a994c8 | ||
|
|
a8c91d8b22 | ||
|
|
d5d2d07894 | ||
|
|
7b3f774304 | ||
|
|
e17cf21351 | ||
|
|
807dc48375 | ||
|
|
8491d81496 | ||
|
|
1c164cebc1 | ||
|
|
db5eed24f6 | ||
|
|
c7d7c9e7e4 | ||
|
|
eba1267f00 | ||
|
|
240a16367b | ||
|
|
cb39f6f0eb | ||
|
|
cd07f51ff9 | ||
|
|
cba8e16be7 | ||
|
|
fbeed43e61 | ||
|
|
1d82e263ad | ||
|
|
aa12a1efa4 | ||
|
|
758a5f700c | ||
|
|
9aa598f81f | ||
|
|
e953f2709d | ||
|
|
96dd06b58a | ||
|
|
5fd783dc50 | ||
|
|
689a6dc7aa | ||
|
|
3e60f8d45e | ||
|
|
09cb297dc7 | ||
|
|
ab0e85824b | ||
|
|
6def9cbb02 | ||
|
|
c7d772be53 | ||
|
|
e584997805 | ||
|
|
b7e0ea8313 | ||
|
|
bb61de60ad | ||
|
|
bc3bcd8312 | ||
|
|
c59f48fb84 | ||
|
|
6844513253 | ||
|
|
dc850f4e8e | ||
|
|
dea1454dcd | ||
|
|
6a8d21dad7 | ||
|
|
27503ac4f9 | ||
|
|
f6401c92be | ||
|
|
d77dcfca75 | ||
|
|
a796e9e82d | ||
|
|
504f1e7cc0 | ||
|
|
591e1db81d | ||
|
|
1ae1b6a4f3 | ||
|
|
021c0626a3 | ||
|
|
63b2e7360b | ||
|
|
3ad0cc1d20 | ||
|
|
8d87a0698c | ||
|
|
44df78e69f | ||
|
|
707c9a87ef | ||
|
|
0558c35ebc | ||
|
|
2b2991a13a | ||
|
|
c1aedde374 | ||
|
|
0b69e065ae | ||
|
|
1c8cc70a28 | ||
|
|
c6f68861b3 |
@@ -1,3 +1,3 @@
|
||||
@echo off
|
||||
%~dp0\scripts\sitelink2pmgr.pl %~dp0\db\schema.sql %~dp0db\vss.mdb > NUL
|
||||
%~dp0\scripts\sitelink2pmgr.pl %~dp0\db\schema.sql %~dp0db\vss.mdb %*
|
||||
echo Done!
|
||||
|
||||
196
db/schema.sql
196
db/schema.sql
@@ -22,9 +22,12 @@
|
||||
-- may have to move this logic into the application :-/
|
||||
|
||||
|
||||
DROP DATABASE IF EXISTS `property_manager`;
|
||||
CREATE DATABASE `property_manager`;
|
||||
USE `property_manager`;
|
||||
-- REVISIT <AP>: 20090511
|
||||
-- By not specifying the database, the script can
|
||||
-- make the determination of which one to use.
|
||||
-- DROP DATABASE IF EXISTS `property_manager`;
|
||||
-- CREATE DATABASE `property_manager`;
|
||||
-- USE `property_manager`;
|
||||
|
||||
|
||||
-- ######################################################################
|
||||
@@ -177,7 +180,7 @@ CREATE TABLE `pmgr_contact_phones` (
|
||||
`ext` VARCHAR(6) DEFAULT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
KEY `number_key` (`phone`),
|
||||
UNIQUE KEY `number_key` (`phone`, `ext`),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
@@ -192,7 +195,7 @@ CREATE TABLE `pmgr_contact_emails` (
|
||||
`email` VARCHAR(128) NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
KEY `email_key` (`email`),
|
||||
UNIQUE KEY `email_key` (`email`),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
@@ -204,7 +207,7 @@ CREATE TABLE `pmgr_contact_emails` (
|
||||
DROP TABLE IF EXISTS `pmgr_contacts_methods`;
|
||||
CREATE TABLE `pmgr_contacts_methods` (
|
||||
`contact_id` INT(10) UNSIGNED NOT NULL,
|
||||
`method` ENUM('POST',
|
||||
`method` ENUM('ADDRESS',
|
||||
'PHONE',
|
||||
'EMAIL')
|
||||
NOT NULL,
|
||||
@@ -479,7 +482,7 @@ CREATE TABLE `pmgr_units` (
|
||||
`walk_order` MEDIUMINT UNSIGNED NOT NULL,
|
||||
|
||||
`deposit` FLOAT(12,2) DEFAULT NULL,
|
||||
`amount` FLOAT(12,2) DEFAULT NULL,
|
||||
`rent` FLOAT(12,2) DEFAULT NULL,
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
@@ -519,7 +522,7 @@ CREATE TABLE `pmgr_unit_sizes` (
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
`deposit` FLOAT(12,2) DEFAULT NULL,
|
||||
`amount` FLOAT(12,2) DEFAULT NULL,
|
||||
`rent` FLOAT(12,2) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
@@ -630,17 +633,9 @@ DROP TABLE IF EXISTS `pmgr_customers`;
|
||||
CREATE TABLE `pmgr_customers` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- If NULL, rely on the contact info exclusively
|
||||
`name` VARCHAR(80) DEFAULT NULL,
|
||||
|
||||
-- A customer gets their own account, although any
|
||||
-- lease specific charge (rent, late fees, etc) will
|
||||
-- be debited against the lease account. This one
|
||||
-- will be used for non-lease related charges, as
|
||||
-- well as charges that intentionally span more than
|
||||
-- one lease (such as one security deposit to cover
|
||||
-- two or more units).
|
||||
`account_id` INT(10) UNSIGNED NOT NULL,
|
||||
-- Customer name may be the same as the primary contact
|
||||
-- or it may entirely independent of that person.
|
||||
`name` VARCHAR(80) NOT NULL,
|
||||
|
||||
-- Primary Contact... every customer must have one
|
||||
-- (and presumably, most customers will _be_ one).
|
||||
@@ -708,7 +703,6 @@ CREATE TABLE `pmgr_leases` (
|
||||
`lease_type_id` INT(10) UNSIGNED NOT NULL,
|
||||
`unit_id` INT(10) UNSIGNED NOT NULL,
|
||||
`customer_id` INT(10) UNSIGNED NOT NULL,
|
||||
`account_id` INT(10) UNSIGNED NOT NULL,
|
||||
`late_schedule_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
`lease_date` DATE NOT NULL,
|
||||
@@ -721,10 +715,10 @@ CREATE TABLE `pmgr_leases` (
|
||||
`close_date` DATE DEFAULT NULL,
|
||||
|
||||
`deposit` FLOAT(12,2) DEFAULT NULL,
|
||||
`amount` FLOAT(12,2) DEFAULT NULL,
|
||||
`rent` FLOAT(12,2) DEFAULT NULL,
|
||||
|
||||
`next_amount` FLOAT(12,2) DEFAULT NULL,
|
||||
`next_amount_date` DATE DEFAULT NULL,
|
||||
`next_rent` FLOAT(12,2) DEFAULT NULL,
|
||||
`next_rent_date` DATE DEFAULT NULL,
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
@@ -848,10 +842,15 @@ CREATE TABLE `pmgr_accounts` (
|
||||
-- For LIABILITY, EQUITY, and INCOME, the opposite
|
||||
-- is true, with reconciliations posted, under
|
||||
-- normal circumstances, when a debit occurs.
|
||||
`trackable` INT UNSIGNED DEFAULT 0,
|
||||
`trackable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
|
||||
`tillable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Does manager collect by hand?
|
||||
`depositable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Does this account receive deposits?
|
||||
`chargeable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for charges?
|
||||
`payable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for payments?
|
||||
`refundable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for refunds?
|
||||
|
||||
-- Security Level
|
||||
`level` INT UNSIGNED DEFAULT 1,
|
||||
`level` INT UNSIGNED DEFAULT 10,
|
||||
|
||||
`name` VARCHAR(80) NOT NULL,
|
||||
`external_account` INT(10) UNSIGNED DEFAULT NULL,
|
||||
@@ -863,21 +862,47 @@ CREATE TABLE `pmgr_accounts` (
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
LOCK TABLES `pmgr_accounts` WRITE;
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `level`)
|
||||
VALUES
|
||||
('EQUITY', 'Equity', 1),
|
||||
('LIABILITY', 'Loan', 1);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`)
|
||||
VALUES
|
||||
('ASSET', 'A/R' ),
|
||||
('ASSET', 'Invoice' ),
|
||||
('ASSET', 'Receipt' ),
|
||||
-- REVISIT <AP>: 20090710 : We don't really need NSF, as it
|
||||
-- will always run a zero balance. However, it will help
|
||||
-- us identify how serious the NSF situation is.
|
||||
('ASSET', 'NSF' ),
|
||||
('LIABILITY', 'A/P' );
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `tillable`, `payable`, `refundable`)
|
||||
VALUES
|
||||
('ASSET', 'Cash', 1, 1, 1),
|
||||
('ASSET', 'Check', 1, 1, 0),
|
||||
('ASSET', 'Money Order', 1, 1, 0),
|
||||
('ASSET', 'ACH', 0, 1, 0),
|
||||
('EXPENSE', 'Concession', 0, 1, 0);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `refundable`, `depositable`)
|
||||
VALUES
|
||||
-- REVISIT <AP>: 20090710 : We probably don't really want petty cash depositable.
|
||||
-- This is just for testing our deposit code
|
||||
('ASSET', 'Petty Cash', 1, 1);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `chargeable`, `trackable`)
|
||||
VALUES
|
||||
('LIABILITY', 'Tax', 1, 1),
|
||||
('LIABILITY', 'Security Deposit', 1, 1),
|
||||
('INCOME', 'Rent', 1, 0),
|
||||
('INCOME', 'Late Charge', 1, 0),
|
||||
('INCOME', 'NSF Charge', 1, 0),
|
||||
('INCOME', 'Damage', 1, 0);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `depositable`, `trackable`)
|
||||
VALUES
|
||||
('ASSET', 'Bank', 1, 0);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `trackable`)
|
||||
VALUES
|
||||
('ASSET', 'A/R', 1),
|
||||
('ASSET', 'Invoice', 1),
|
||||
('ASSET', 'Receipt', 1),
|
||||
('LIABILITY', 'A/P', 1),
|
||||
('LIABILITY', 'Tax', 0),
|
||||
('LIABILITY', 'Customer Credit', 1),
|
||||
('ASSET', 'Bank', 0),
|
||||
('ASSET', 'Till', 0),
|
||||
('LIABILITY', 'Security Deposit', 1),
|
||||
('INCOME', 'Rent', 0),
|
||||
('INCOME', 'Late Charge', 0),
|
||||
('EXPENSE', 'Concession', 0),
|
||||
('EXPENSE', 'Bad Debt', 0);
|
||||
('EXPENSE', 'Bad Debt', 0),
|
||||
('EXPENSE', 'Maintenance', 0);
|
||||
UNLOCK TABLES;
|
||||
|
||||
|
||||
@@ -911,23 +936,26 @@ DROP TABLE IF EXISTS `pmgr_ledgers`;
|
||||
CREATE TABLE `pmgr_ledgers` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- REVISIT <AP>: 20090605
|
||||
-- If ledgers do need to be closed, then we'll need
|
||||
-- to add several parameters indicating the period
|
||||
-- this particular ledger is valid and so on.
|
||||
|
||||
`account_id` INT(10) UNSIGNED NOT NULL,
|
||||
`sequence` INT(10) UNSIGNED DEFAULT 1,
|
||||
`closed` INT UNSIGNED DEFAULT 0,
|
||||
|
||||
-- REVISIT <AP>: 20090607
|
||||
-- Probably, a single close should have the ability to close
|
||||
-- many different account ledgers. For now, just timestamping.
|
||||
-- `close_id` INT(10) UNSIGNED NOT NULL,
|
||||
`prior_ledger_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
`close_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
`open_stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`close_stamp` DATETIME DEFAULT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_closes
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_closes`;
|
||||
CREATE TABLE `pmgr_closes` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
@@ -942,8 +970,11 @@ DROP TABLE IF EXISTS `pmgr_transactions`;
|
||||
CREATE TABLE `pmgr_transactions` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- `type` ENUM('INVOICE',
|
||||
-- 'RECEIPT')
|
||||
-- NOT NULL,
|
||||
|
||||
`stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`through_date` DATE DEFAULT NULL,
|
||||
`due_date` DATE DEFAULT NULL,
|
||||
|
||||
-- REVISIT <AP>: 20090604
|
||||
@@ -965,13 +996,28 @@ DROP TABLE IF EXISTS `pmgr_ledger_entries`;
|
||||
CREATE TABLE `pmgr_ledger_entries` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
`name` VARCHAR(80) DEFAULT NULL,
|
||||
-- Effective date may be used for a variety of entries
|
||||
-- charges & payments are not always effective at the
|
||||
-- time of transaction. Through date, on the other hand,
|
||||
-- will probably only be relevant for charges, such as
|
||||
-- rent, which is effective for a range of dates.
|
||||
`effective_date` DATE DEFAULT NULL, -- first day
|
||||
`through_date` DATE DEFAULT NULL, -- last day
|
||||
|
||||
`monetary_source_id` INT(10) UNSIGNED DEFAULT NULL, -- NULL if internal transfer
|
||||
`transaction_id` INT(10) UNSIGNED NOT NULL,
|
||||
`customer_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
`lease_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
`amount` FLOAT(12,2) NOT NULL,
|
||||
|
||||
-- REVISIT <AP>: 20090707
|
||||
-- Experimental. Considering automatically hooking
|
||||
-- charges to their invoice. This may help ease the
|
||||
-- ongoing accounting dilema that we've been having.
|
||||
-- It might allow us to keep the underlying invoice
|
||||
-- ledgers without having to expose the user to them.
|
||||
`root_transaction_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
`debit_ledger_id` INT(10) UNSIGNED NOT NULL,
|
||||
`credit_ledger_id` INT(10) UNSIGNED NOT NULL,
|
||||
|
||||
@@ -1006,13 +1052,24 @@ CREATE TABLE `pmgr_monetary_sources` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
`name` VARCHAR(80) DEFAULT NULL,
|
||||
monetary_type_id INT(10) UNSIGNED NOT NULL,
|
||||
|
||||
-- REVISIT <AP>: 20090605
|
||||
-- Check Number;
|
||||
-- Routing Number, Account Number;
|
||||
-- Card Number, Expiration Date; CCV2 Code
|
||||
-- Card Number, Expiration Date; CVV2 Code
|
||||
-- etc.
|
||||
-- REVISIT <AP> 20090630
|
||||
-- I _think_ that CVV2 is NEVER supposed to
|
||||
-- be stored ANYWHERE. Merchants agree to
|
||||
-- use it only to verify the transaction and
|
||||
-- then leave no record of it, so that even
|
||||
-- if their security is compromised, no one
|
||||
-- will know the CVV2 code unless they are
|
||||
-- in physical possession of the card.
|
||||
`data1` VARCHAR(80) DEFAULT NULL,
|
||||
`data2` VARCHAR(80) DEFAULT NULL,
|
||||
`data3` VARCHAR(80) DEFAULT NULL,
|
||||
`data4` VARCHAR(80) DEFAULT NULL,
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
@@ -1020,37 +1077,6 @@ CREATE TABLE `pmgr_monetary_sources` (
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_monetary_types
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_monetary_types`;
|
||||
CREATE TABLE `pmgr_monetary_types` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
`name` VARCHAR(80) NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
`tillable` TINYINT(1) NOT NULL DEFAULT 1, -- Does manager collect by hand?
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
LOCK TABLES `pmgr_monetary_types` WRITE;
|
||||
INSERT INTO `pmgr_monetary_types` (`id`, `name`, `tillable`)
|
||||
VALUES
|
||||
-- (1, 'Transfer', 0),
|
||||
(2, 'Cash', 1),
|
||||
(3, 'Check', 1),
|
||||
(4, 'Money Order', 1),
|
||||
(5, 'ACH', 0),
|
||||
(6, 'Debit Card', 0),
|
||||
(7, 'Credit Card', 0),
|
||||
(8, 'Other Tillable', 1),
|
||||
(9, 'Other Non-Tillable', 0);
|
||||
UNLOCK TABLES;
|
||||
|
||||
|
||||
|
||||
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
|
||||
69
requirements.txt
Normal file
69
requirements.txt
Normal file
@@ -0,0 +1,69 @@
|
||||
N - GATE
|
||||
N - ACH / CREDIT CARD PROCESSING
|
||||
Y - CREDIT CARD ENTRY
|
||||
Y - ACH ENTRY
|
||||
P - INVENTORY TRACKING / POS
|
||||
Y - UNIT TYPES
|
||||
Y - UNIT SIZES
|
||||
Y - UNITS
|
||||
Y - MOVE IN / OUT
|
||||
Y - UNIT TRANSFERS
|
||||
Y - LEASE TRACKING (PDF Generation)
|
||||
Y - LETTERS (PDF Generation)
|
||||
Y - REMINDERS
|
||||
Y - MULTIPLE LATE RENT SCHEDULES (Tenant A vs Tenant B)
|
||||
Y - ACCOUNTING (assign charges to accounts)
|
||||
Y - DETAILED REPORTING (HTML & PDF)
|
||||
Y - SITE MAP; HOT CLICKABLE
|
||||
P - PROSPECTIVE TENANTS
|
||||
Y - MARKETING
|
||||
P - RESERVATIONS
|
||||
P - MOVE OUT NOTICES
|
||||
P - MULTI-SITE (One database, multiple sites)
|
||||
Y - GENERATE GEOGRAPHIC MAP OF CUSTOMERS USING GOOGLE!
|
||||
- Major advantage here... MapPoint only choice with competitors
|
||||
Y - WEB BASED
|
||||
Y - CUSTOMER VIEW / MANAGER VIEW
|
||||
Y - CUSTOMERS CAN CREATE ACCOUNTS, VIEW HISTORY
|
||||
Y - CUSTOMERS CAN SIGN UP FOR AUTO PAY
|
||||
|
||||
|
||||
|
||||
----------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
Operations to be functional
|
||||
'X' marks functionality sufficiently completed
|
||||
|
||||
X - Create Customer ID/Account
|
||||
X - Add Contact information to Customer
|
||||
X - Move Customer into Unit
|
||||
X - Enter Rent Concessions given
|
||||
X - Asses Rent Charges
|
||||
X - Asses Late Charges
|
||||
X - Asses Security Deposits
|
||||
X - Receive and record Checks
|
||||
X - Receive and record Money Orders
|
||||
X - Receive and record Cash
|
||||
X - Receive and record ACH Deposits
|
||||
x - Reverse rent charges (early moveout on prepaid occupancy)
|
||||
X - Handle NSF checks
|
||||
X - Assess NSF Fees
|
||||
X - Determine Lease Paid-Through status
|
||||
- Report: List of customers overdue
|
||||
- Flag unit as overlocked
|
||||
- Flag unit as evicting
|
||||
- Flag unit as normal status
|
||||
- Flag unit as dirty
|
||||
- Enter notes when communicating with Customer
|
||||
- Accept pre-payments
|
||||
- NOTE: As a temporary solution, disallow customer to run
|
||||
a credit. Require charges be entered first.
|
||||
X - Record Customer Move-Out from Unit
|
||||
X - Record utilization of Security Deposit
|
||||
- Record issuing of a refund
|
||||
- Record Deposit into Petty Cash
|
||||
- Record Payment from Petty Cash to expenses
|
||||
- Record Petty Cash to refund.
|
||||
X - Write Off Bad Debt
|
||||
X - Perform a Deposit and Close the Books
|
||||
X - Determine Rents Collected for a given period.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,7 @@
|
||||
* @subpackage cake.app
|
||||
*/
|
||||
class AppController extends Controller {
|
||||
var $helpers = array('Html', 'Form', 'Javascript', 'Format', 'Time');
|
||||
var $helpers = array('Html', 'Form', 'Javascript', 'Format', 'Time', 'Grid');
|
||||
var $components = array('DebugKit.Toolbar');
|
||||
|
||||
function sideMenuLinks() {
|
||||
@@ -45,9 +45,12 @@ class AppController extends Controller {
|
||||
array('name' => 'Units', 'url' => array('controller' => 'units', 'action' => 'index')),
|
||||
array('name' => 'Leases', 'url' => array('controller' => 'leases', 'action' => 'index')),
|
||||
array('name' => 'Customers', 'url' => array('controller' => 'customers', 'action' => 'index')),
|
||||
array('name' => 'Contacts', 'url' => array('controller' => 'contacts', 'action' => 'index')),
|
||||
array('name' => 'Accounts', 'url' => array('controller' => 'accounts', 'action' => 'index')),
|
||||
array('name' => 'Debug', 'header' => true),
|
||||
array('name' => 'Contacts', 'url' => array('controller' => 'contacts', 'action' => 'index')),
|
||||
array('name' => 'Ledgers', 'url' => array('controller' => 'ledgers', 'action' => 'index')),
|
||||
array('name' => 'New Ledgers', 'url' => array('controller' => 'accounts', 'action' => 'newledger')),
|
||||
array('name' => 'RESET DATA', 'url' => array('controller' => 'accounts', 'action' => 'reset_data')),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,6 +58,28 @@ class AppController extends Controller {
|
||||
$this->set('sidemenu', $this->sideMenuLinks());
|
||||
}
|
||||
|
||||
function reset_data() {
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
Configure::write('debug', '0');
|
||||
$script = $_SERVER['DOCUMENT_ROOT'] . '/pmgr/build.cmd';
|
||||
echo "<P>" . date('r') . "\n";
|
||||
//echo "<P>Script: $script" . "\n";
|
||||
$db = & $this->Account->getDataSource();
|
||||
$script .= ' "' . $db->config['database'] . '"';
|
||||
$script .= ' "' . $db->config['login'] . '"';
|
||||
$script .= ' "' . $db->config['password'] . '"';
|
||||
$handle = popen($script . ' 2>&1', 'r');
|
||||
//echo "<P>Handle: $handle; " . gettype($handle) . "\n";
|
||||
echo "<P><PRE>\n";
|
||||
while (($read = fread($handle, 2096))) {
|
||||
echo $read;
|
||||
}
|
||||
echo "</PRE>\n";
|
||||
pclose($handle);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -63,12 +88,12 @@ class AppController extends Controller {
|
||||
* - called by function to create an index listing
|
||||
*/
|
||||
|
||||
function jqGridView($title, $action = null) {
|
||||
function jqGridView($title, $action = null, $element = null) {
|
||||
$this->set('title', $title);
|
||||
// The resulting page will contain a jqGrid, which will
|
||||
// use ajax to obtain the actual data for this action
|
||||
$this->set('action', $action ? $action : $this->params['action']);
|
||||
$this->render('/elements/' . $this->params['controller']);
|
||||
$this->render('/elements/' . ($element ? $element : $this->params['controller']));
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
@@ -93,7 +118,11 @@ class AppController extends Controller {
|
||||
// Establish the basic query and conditions
|
||||
$query = array_intersect_key($this->jqGridDataCountTables($params, $model),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
$query['conditions'] = $this->jqGridDataConditions($params, $model);
|
||||
$query['conditions'] = $this->jqGridDataCountConditions($params, $model);
|
||||
$query['group'] = $this->jqGridDataCountGroup($params, $model);
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
$params['count_query'] = $query;
|
||||
|
||||
// Get the number of records prior to pagination
|
||||
$count = $this->jqGridDataRecordCount($params, $model, $query);
|
||||
@@ -119,15 +148,15 @@ class AppController extends Controller {
|
||||
$query['fields'] = $this->jqGridDataFields($params, $model);
|
||||
$results = $this->jqGridDataRecords($params, $model, $query);
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
$params['query'] = $query;
|
||||
|
||||
// Post process the records
|
||||
$this->jqGridRecordsPostProcess($params, $model, $results);
|
||||
|
||||
// Add in any needed hyperlinks
|
||||
$this->jqGridRecordLinks($params, $model, $results, array());
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
$params['query'] = $query;
|
||||
|
||||
// Finally, dump out the data
|
||||
$this->jqGridDataOutputHeader($params, $model);
|
||||
echo "<?xml version='1.0' encoding='utf-8'?>\n";
|
||||
@@ -206,6 +235,10 @@ class AppController extends Controller {
|
||||
return array('contain' => false);
|
||||
}
|
||||
|
||||
function jqGridDataCountConditions(&$params, &$model) {
|
||||
return $this->jqGridDataConditions($params, $model);
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$searches = array();
|
||||
|
||||
@@ -261,6 +294,10 @@ class AppController extends Controller {
|
||||
return null;
|
||||
}
|
||||
|
||||
function jqGridDataCountGroup(&$params, &$model) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function jqGridDataGroup(&$params, &$model) {
|
||||
return $model->alias.'.'.$model->primaryKey;
|
||||
}
|
||||
@@ -285,6 +322,14 @@ class AppController extends Controller {
|
||||
$model_alias = $model->alias;
|
||||
$id = $model->primaryKey;
|
||||
|
||||
$subtotals = array();
|
||||
foreach ($params['fields'] AS $field) {
|
||||
if (preg_match('/subtotal-(.*)$/', $field, $matches))
|
||||
$subtotals[] = array('field' => $matches[1],
|
||||
'name' => $field,
|
||||
'amount' => 0);
|
||||
}
|
||||
|
||||
foreach ($records AS &$record) {
|
||||
$record['jqGrid_id'] = $record[$model_alias][$id];
|
||||
// Add the calculated fields (if any), to the model fields
|
||||
@@ -292,6 +337,19 @@ class AppController extends Controller {
|
||||
$record[$model_alias] += $record[0];
|
||||
unset($record[0]);
|
||||
}
|
||||
|
||||
foreach ($subtotals AS &$subtotal) {
|
||||
$field = $subtotal['field'];
|
||||
if (preg_match("/\./", $field)) {
|
||||
list($tbl, $col) = explode(".", $field);
|
||||
$record['subtotal-'.$tbl][$col] =
|
||||
($subtotal['amount'] += $record[$tbl][$col]);
|
||||
}
|
||||
else {
|
||||
$record[$model->alias]['subtotal-'.$field] =
|
||||
($subtotal['amount'] += $record[$model->alias][$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
@@ -299,6 +357,10 @@ class AppController extends Controller {
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
// Don't create any links if ordered not to.
|
||||
if (isset($params['nolinks']))
|
||||
return;
|
||||
|
||||
foreach ($links AS $table => $fields) {
|
||||
$special = array('controller', 'id');
|
||||
$controller = Inflector::pluralize(Inflector::underscore($table));
|
||||
@@ -341,6 +403,11 @@ class AppController extends Controller {
|
||||
echo " <page>$page</page>\n";
|
||||
echo " <total>$total</total>\n";
|
||||
echo " <records>$records</records>\n";
|
||||
|
||||
if (isset($params['userdata'])) {
|
||||
foreach ($params['userdata'] AS $field => $value)
|
||||
echo ' <userdata name="'.$field.'">' . "{$value}</userdata>\n";
|
||||
}
|
||||
}
|
||||
|
||||
function jqGridDataOutputRecords(&$params, &$model, &$records) {
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
class AppModel extends Model {
|
||||
|
||||
var $actsAs = array('Containable', 'Linkable');
|
||||
var $useNullForEmpty = true;
|
||||
var $formatDateFields = true;
|
||||
|
||||
/**
|
||||
* Get Enum Values
|
||||
@@ -47,13 +49,15 @@ class AppModel extends Model {
|
||||
*
|
||||
* Gets the enum values for MySQL 4 and 5 to use in selectTag()
|
||||
*/
|
||||
function getEnumValues($columnName=null, $respectDefault=false)
|
||||
function getEnumValues($columnName=null, $tableName=null)
|
||||
{
|
||||
if ($columnName==null) { return array(); } //no field specified
|
||||
|
||||
//Get the name of the table
|
||||
$db =& ConnectionManager::getDataSource($this->useDbConfig);
|
||||
$tableName = $db->fullTableName($this, false);
|
||||
if (!isset($tableName)) {
|
||||
//Get the name of the table
|
||||
$db =& ConnectionManager::getDataSource($this->useDbConfig);
|
||||
$tableName = $db->fullTableName($this, false);
|
||||
}
|
||||
|
||||
//Get the values for the specified column (database and version specific, needs testing)
|
||||
$result = $this->query("SHOW COLUMNS FROM {$tableName} LIKE '{$columnName}'");
|
||||
@@ -72,12 +76,33 @@ class AppModel extends Model {
|
||||
|
||||
//Get the values
|
||||
return array_flip(array_merge(array(''), // MySQL sets 0 to be the empty string
|
||||
explode("','", preg_replace("/(enum)\('(.+?)'\)/","\\2", $types))
|
||||
explode("','", strtoupper(preg_replace("/(enum)\('(.+?)'\)/","\\2", $types)))
|
||||
));
|
||||
} //end getEnumValues
|
||||
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: nameToID
|
||||
* - Returns the ID of the named item
|
||||
*/
|
||||
function nameToID($name) {
|
||||
$this->cacheQueries = true;
|
||||
$item = $this->find('first', array
|
||||
('recursive' => -1,
|
||||
'conditions' => compact('name'),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
if ($item) {
|
||||
$item = current($item);
|
||||
return $item['id'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -111,6 +136,60 @@ class AppModel extends Model {
|
||||
}
|
||||
|
||||
|
||||
function recursive_array_replace($find, $replace, &$data) {
|
||||
if (!isset($data))
|
||||
return;
|
||||
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $key => &$value) {
|
||||
$this->recursive_array_replace($find, $replace, $value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($replace))
|
||||
$data = preg_replace($find, $replace, $data);
|
||||
elseif (preg_match($find, $data))
|
||||
$data = null;
|
||||
}
|
||||
|
||||
function beforeSave() {
|
||||
/* pr(array('class' => $this->name, */
|
||||
/* 'alias' => $this->alias, */
|
||||
/* 'function' => 'AppModel::beforeSave')); */
|
||||
|
||||
// Replace all empty strings with NULL.
|
||||
// If a particular model doesn't like this, they'll have to
|
||||
// override the behavior, or set useNullForEmpty to false.
|
||||
if ($this->useNullForEmpty)
|
||||
$this->recursive_array_replace("/^\s*$/", null, $this->data);
|
||||
|
||||
if ($this->formatDateFields) {
|
||||
$alias = $this->alias;
|
||||
|
||||
foreach ($this->_schema AS $field => $info) {
|
||||
if ($info['type'] == 'date' || $info['type'] == 'timestamp') {
|
||||
if (isset($this->data[$alias][$field])) {
|
||||
/* pr("Fix Date for '$alias'.'$field'; current value = " . */
|
||||
/* "'{$this->data[$alias][$field]}'"); */
|
||||
if ($this->data[$alias][$field] === 'CURRENT_TIMESTAMP')
|
||||
// Seems CakePHP is broken for the default timestamp.
|
||||
// It tries to automagically set the value to CURRENT_TIMESTAMP
|
||||
// which is wholly rejected by MySQL. Just put it back to NULL
|
||||
// and let the SQL engine deal with the defaults... it's not
|
||||
// Cake's place to do this anyway :-/
|
||||
$this->data[$alias][$field] = null;
|
||||
else
|
||||
$this->data[$alias][$field] =
|
||||
$this->dateFormatBeforeSave($this->data[$alias][$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -119,7 +198,16 @@ class AppModel extends Model {
|
||||
*/
|
||||
|
||||
function dateFormatBeforeSave($dateString) {
|
||||
return date('Y-m-d', strtotime($dateString));
|
||||
/* $time = ''; */
|
||||
/* if (preg_match('/(\d+(:\d+))/', $dateString, $match)) */
|
||||
/* $time = ' '.$match[1]; */
|
||||
/* $dateString = preg_replace('/(\d+(:\d+))/', '', $dateString); */
|
||||
/* return date('Y-m-d', strtotime($dateString)) . $time; */
|
||||
|
||||
if (preg_match('/:/', $dateString))
|
||||
return date('Y-m-d H:i:s', strtotime($dateString));
|
||||
else
|
||||
return date('Y-m-d', strtotime($dateString));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,14 +26,12 @@
|
||||
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Here, we are connecting '/' (base path) to controller called 'Pages',
|
||||
* its action called 'display', and we pass a param to select the view file
|
||||
* to use (in this case, /app/views/pages/home.ctp)...
|
||||
* Here, we are connecting '/' (base path) to our site map.
|
||||
* It's hardcoded to map #1, but at some point we'll implement
|
||||
* a login mechanism and the default path will be to log on instead.
|
||||
*/
|
||||
Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
|
||||
/**
|
||||
* ...and connect the rest of 'Pages' controller's urls.
|
||||
*/
|
||||
Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
|
||||
Router::connect('/', array('controller' => 'maps', 'action' => 'view', '1'));
|
||||
|
||||
?>
|
||||
@@ -12,6 +12,7 @@ class AccountsController extends AppController {
|
||||
array('name' => 'Equity', 'url' => array('controller' => 'accounts', 'action' => 'equity')),
|
||||
array('name' => 'Income', 'url' => array('controller' => 'accounts', 'action' => 'income')),
|
||||
array('name' => 'Expense', 'url' => array('controller' => 'accounts', 'action' => 'expense')),
|
||||
array('name' => 'Bank Deposit', 'url' => array('controller' => 'accounts', 'action' => 'deposit')),
|
||||
);
|
||||
|
||||
|
||||
@@ -119,6 +120,126 @@ class AccountsController extends AppController {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: newledger
|
||||
* - Close the current account ledger and create a new one,
|
||||
* carrying forward any balance if necessary.
|
||||
*/
|
||||
|
||||
function newledger($id = null) {
|
||||
if (!$this->Account->closeCurrentLedger($id)) {
|
||||
$this->Session->setFlash(__('Unable to create new Ledger.', true));
|
||||
}
|
||||
if ($id)
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
else
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: collected
|
||||
* - Displays the items actually collected for the period
|
||||
* e.g. How much was collected in rent from 4/1/09 - 5/1/09
|
||||
*/
|
||||
function collected($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
$payment_accounts = $this->Account->collectableAccounts();
|
||||
//$payment_accounts[$this->Account->nameToID('Bank')] = 'Bank';
|
||||
$default_accounts = array_diff_key($payment_accounts,
|
||||
array($this->Account->concessionAccountID() => 1));
|
||||
$this->set(compact('payment_accounts', 'default_accounts'));
|
||||
|
||||
$this->Account->recursive = -1;
|
||||
$account = $this->Account->read(null, $id);
|
||||
$account = $account['Account'];
|
||||
|
||||
$title = ($account['name'] . ': Collected Report');
|
||||
$this->set(compact('account', 'title'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: deposit
|
||||
* - Prepares the books for a bank deposit
|
||||
*/
|
||||
function deposit() {
|
||||
if ($this->data) {
|
||||
// Action the close based on provided data
|
||||
//pr($this->data);
|
||||
|
||||
// Get data about each closed ledger.
|
||||
$deposit = array('total' => 0, 'ledgers' => array());
|
||||
foreach ($this->data['Tillable']['Ledger'] AS $ledger_id => $ledger) {
|
||||
if (!$ledger['checked'])
|
||||
continue;
|
||||
|
||||
$ledger_entries =
|
||||
$this->Account->Ledger->find
|
||||
('all',
|
||||
array('link' => array
|
||||
('Account' =>
|
||||
array('fields' => array('name')),
|
||||
|
||||
'LedgerEntry' =>
|
||||
array('fields' => array('id', 'amount'),
|
||||
|
||||
'MonetarySource' =>
|
||||
array('fields' => array('name')),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('name')),
|
||||
|
||||
//'Transaction' =>
|
||||
//array('fields' => array('stamp')),
|
||||
),
|
||||
),
|
||||
'fields' => false,
|
||||
'conditions' => array(array('Ledger.id' => $ledger_id),
|
||||
array('LedgerEntry.id IS NOT NULL'),
|
||||
),
|
||||
));
|
||||
|
||||
$deposit['total'] += $ledger['amount'];
|
||||
$deposit['ledgers'][] = array('id' => $ledger_id,
|
||||
'name' => $ledger['account_name'],
|
||||
'total' => $ledger['amount'],
|
||||
'entries' => $ledger_entries);
|
||||
}
|
||||
|
||||
// Perform the accounting work necessary to close the
|
||||
// monetary ledgers and deposit into the bank account.
|
||||
$this->Account->closeAndDeposit($deposit['ledgers'], $this->data['Deposit']['Account']['id']);
|
||||
|
||||
$title = 'Account: Deposit Slip';
|
||||
$this->set(compact('title', 'deposit'));
|
||||
$this->render('deposit_slip');
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare a close page...
|
||||
$tillable_account = $this->Account->relatedAccounts('tillable');
|
||||
$depositable_account = $this->Account->relatedAccounts('depositable');
|
||||
|
||||
foreach ($tillable_account AS &$acct) {
|
||||
$acct['Account']['stats'] = $this->Account->stats($acct['Account']['id']);
|
||||
}
|
||||
|
||||
$title = 'Account: Prepare Deposit';
|
||||
$this->set(compact('title', 'tillable_account', 'depositable_account'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -141,7 +262,8 @@ class AccountsController extends AppController {
|
||||
array('fields' => array('id', 'sequence')),
|
||||
|
||||
'Ledger' =>
|
||||
array('order' => array('Ledger.open_stamp' => 'DESC')),
|
||||
array('Close' => array
|
||||
('order' => array('Close.stamp' => 'DESC'))),
|
||||
),
|
||||
'conditions' => array(array('Account.id' => $id)),
|
||||
)
|
||||
@@ -160,8 +282,16 @@ class AccountsController extends AppController {
|
||||
$stats = $this->Account->stats($id, true);
|
||||
$stats = $stats['Ledger'];
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'New Ledger', 'url' => array('action' => 'newledger', $id));
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Collected', 'url' => array('action' => 'collected', $id));
|
||||
|
||||
// Prepare to render
|
||||
$title = 'Account: ' . $account['Account']['name'];
|
||||
$this->set(compact('account', 'title', 'stats'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,6 +46,11 @@ class ContactsController extends AppController {
|
||||
return $order;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Contact'] = array('id');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -72,8 +77,128 @@ class ContactsController extends AppController {
|
||||
'conditions' => array('Contact.id' => $id),
|
||||
));
|
||||
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Edit',
|
||||
'url' => array('action' => 'edit',
|
||||
$id));
|
||||
|
||||
// Prepare to render.
|
||||
$title = $contact['Contact']['display_name'];
|
||||
$title = 'Contact: ' . $contact['Contact']['display_name'];
|
||||
$this->set(compact('contact', 'title'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: edit
|
||||
*/
|
||||
|
||||
function edit($id = null, $customer_id = null) {
|
||||
if (isset($this->data)) {
|
||||
|
||||
if (isset($this->params['form']['cancel'])) {
|
||||
if (isset($this->data['Contact']['id']))
|
||||
$this->redirect(array('action'=>'view', $this->data['Contact']['id']));
|
||||
/* else */
|
||||
/* $this->redirect(array('controller' => 'customers', */
|
||||
/* 'action'=>'add', $this->data['Customer']['id'])); */
|
||||
return;
|
||||
}
|
||||
|
||||
// Go through each contact method and strip the bogus ID if new
|
||||
foreach (array_intersect_key($this->data,
|
||||
array('ContactPhone'=>1,
|
||||
'ContactAddress'=>1,
|
||||
'ContactEmail'=>1)) AS $type => $arr) {
|
||||
foreach ($arr AS $idx => $item) {
|
||||
if (isset($item['source']) && $item['source'] === 'new')
|
||||
unset($this->data[$type][$idx]['id']);
|
||||
}
|
||||
}
|
||||
|
||||
// Save the contact and all associated data
|
||||
$this->Contact->saveContact($this->data['Contact']['id'], $this->data);
|
||||
|
||||
// Now that the work is done, let the user view the updated contact
|
||||
$this->redirect(array('action'=>'view', $this->data['Contact']['id']));
|
||||
//$this->render('/empty');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
$this->data = $this->Contact->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'ContactPhone',
|
||||
'ContactEmail',
|
||||
'ContactAddress',
|
||||
'Customer'),
|
||||
|
||||
'conditions' => array('Contact.id' => $id),
|
||||
));
|
||||
|
||||
$title = 'Contact: ' . $this->data['Contact']['display_name'] . " : Edit";
|
||||
}
|
||||
else {
|
||||
$title = "Enter New Contact";
|
||||
$this->data = array('ContactPhone' => array(),
|
||||
'ContactAddress' => array(),
|
||||
'ContactEmail' => array());
|
||||
}
|
||||
|
||||
$phone_types = array_flip($this->Contact->ContactPhone->getEnumValues('type'));
|
||||
unset($phone_types[0]);
|
||||
// REVISIT <AP> 20090705
|
||||
// Use this to have a mixed case enum
|
||||
// array_map('ucfirst', array_map('strtolower', $phone_types))
|
||||
$phone_types = array_combine($phone_types, $phone_types);
|
||||
$this->set(compact('phone_types'));
|
||||
|
||||
$method_types = array_flip($this->Contact->getEnumValues
|
||||
('type',
|
||||
$this->Contact->tablePrefix . 'contacts_methods'));
|
||||
unset($method_types[0]);
|
||||
$method_types = array_combine($method_types, $method_types);
|
||||
$this->set(compact('method_types'));
|
||||
|
||||
$method_preferences = array_flip($this->Contact->getEnumValues
|
||||
('preference',
|
||||
$this->Contact->tablePrefix . 'contacts_methods'));
|
||||
unset($method_preferences[0]);
|
||||
$method_preferences = array_combine($method_preferences, $method_preferences);
|
||||
$this->set(compact('method_preferences'));
|
||||
|
||||
$contact_phones = $this->Contact->ContactPhone->phoneList();
|
||||
$this->set(compact('contact_phones'));
|
||||
|
||||
$contact_addresses = $this->Contact->ContactAddress->addressList();
|
||||
$this->set(compact('contact_addresses'));
|
||||
|
||||
$contact_emails = $this->Contact->ContactEmail->emailList();
|
||||
$this->set(compact('contact_emails'));
|
||||
|
||||
// Prepare to render.
|
||||
//pr($this->data);
|
||||
$this->set(compact('title'));
|
||||
$this->render('edit');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: add
|
||||
* - Adds a new contact
|
||||
*/
|
||||
|
||||
function add($customer_id = null) {
|
||||
$this->edit(null, $customer_id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
class CustomersController extends AppController {
|
||||
var $sidemenu_links =
|
||||
array(array('name' => 'Tenants', 'header' => true),
|
||||
array(array('name' => 'Customers', 'header' => true),
|
||||
array('name' => 'Current', 'url' => array('controller' => 'customers', 'action' => 'current')),
|
||||
array('name' => 'Past', 'url' => array('controller' => 'customers', 'action' => 'past')),
|
||||
array('name' => 'All', 'url' => array('controller' => 'customers', 'action' => 'all')),
|
||||
array('name' => 'Add Customer', 'url' => array('controller' => 'customers', 'action' => 'add')),
|
||||
);
|
||||
|
||||
//var $components = array('RequestHandler');
|
||||
@@ -26,13 +27,13 @@ class CustomersController extends AppController {
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: index / current / past / all
|
||||
* - Creates a list of tenants
|
||||
* - Creates a list of customers
|
||||
*/
|
||||
|
||||
function index() { $this->current(); }
|
||||
function current() { $this->jqGridView('Current Tenants'); }
|
||||
function current() { $this->jqGridView('Current Tenants', 'current'); }
|
||||
function past() { $this->jqGridView('Past Tenants'); }
|
||||
function all() { $this->jqGridView('All Tenants', 'all'); }
|
||||
function all() { $this->jqGridView('All Customers'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
@@ -50,7 +51,7 @@ class CustomersController extends AppController {
|
||||
$params['action'] = 'all';
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
function jqGridDataCountTables(&$params, &$model) {
|
||||
return array
|
||||
('link' =>
|
||||
array(// Models
|
||||
@@ -60,9 +61,35 @@ class CustomersController extends AppController {
|
||||
);
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
$link = $this->jqGridDataCountTables($params, $model);
|
||||
$link['link']['LedgerEntry'] = array('fields' => array());
|
||||
$link['link']['LedgerEntry']['Ledger'] = array('fields' => array());
|
||||
$link['link']['LedgerEntry']['Ledger']['Account'] = array('fields' => array());
|
||||
// INNER JOIN would be great, as it would ensure we're only looking
|
||||
// at the ledger entries that we truly want. However, this also
|
||||
// removes from the query any units that do not yet have a ledger
|
||||
// entry in A/R. A solution would be to INNER JOIN these tables,
|
||||
// and LEFT JOIN it to the rest. Grouping of JOINs, however, is
|
||||
// implemented with the 'joins' tag, and is not available through
|
||||
// the Linkable behavior interface.
|
||||
//$link['link']['LedgerEntry']['Ledger']['Account']['type'] = 'INNER';
|
||||
$link['link']['LedgerEntry']['Ledger']['Account']['conditions']
|
||||
= array('Account.id' =>
|
||||
$this->Customer->LedgerEntry->Ledger->Account->accountReceivableAccountID());
|
||||
return $link;
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
return array('Customer.*',
|
||||
'COUNT(CurrentLease.id) AS lease_count');
|
||||
$db = &$model->getDataSource();
|
||||
$fields = $db->fields($model, $model->alias);
|
||||
$fields[] = ('COUNT(DISTINCT CurrentLease.id) AS lease_count');
|
||||
$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;
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
@@ -79,13 +106,19 @@ class CustomersController extends AppController {
|
||||
}
|
||||
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
$order = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
if ($index === 'PrimaryContact.last_name') {
|
||||
$order[] = 'PrimaryContact.first_name ' . $direction;
|
||||
}
|
||||
if ($index === 'PrimaryContact.first_name') {
|
||||
$order[] = 'PrimaryContact.last_name ' . $direction;
|
||||
}
|
||||
$order = array();
|
||||
$order[] = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
if ($index !== 'PrimaryContact.last_name')
|
||||
$order[] = parent::jqGridDataOrder($params, $model,
|
||||
'PrimaryContact.last_name', $direction);
|
||||
if ($index !== 'PrimaryContact.first_name')
|
||||
$order[] = parent::jqGridDataOrder($params, $model,
|
||||
'PrimaryContact.first_name', $direction);
|
||||
if ($index !== 'Customer.id')
|
||||
$order[] = parent::jqGridDataOrder($params, $model,
|
||||
'Customer.id', $direction);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
@@ -140,6 +173,67 @@ class CustomersController extends AppController {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: move_in
|
||||
* - Sets up the move-in page for the given customer.
|
||||
*/
|
||||
|
||||
function move_in($id = null) {
|
||||
$customer = array();
|
||||
$unit = array();
|
||||
|
||||
if (isset($id)) {
|
||||
$this->Customer->recursive = -1;
|
||||
$customer = current($this->Customer->read(null, $id));
|
||||
}
|
||||
$this->set(compact('customer', 'unit'));
|
||||
|
||||
$title = 'Customer Move-In';
|
||||
$this->set(compact('title'));
|
||||
$this->render('/leases/move');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: move_out
|
||||
* - prepare to move a customer out of one of their units
|
||||
*/
|
||||
|
||||
function move_out($id) {
|
||||
|
||||
$customer = $this->Customer->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Lease' =>
|
||||
array('conditions' => array('Lease.moveout_date' => null),
|
||||
// Models
|
||||
'Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array('Customer.id' => $id),
|
||||
));
|
||||
$this->set('customer', $lease['Customer']);
|
||||
$this->set('unit', array());
|
||||
|
||||
$redirect = array('controller' => 'customers',
|
||||
'action' => 'view',
|
||||
$id);
|
||||
|
||||
$title = $customer['Customer']['name'] . ': Prepare Move-Out';
|
||||
$this->set(compact('title', 'customer', 'redirect'));
|
||||
$this->render('/leases/move');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -158,15 +252,46 @@ class CustomersController extends AppController {
|
||||
$outstanding_balance = $customer['stats']['balance'];
|
||||
$outstanding_deposit = $customer['deposits']['summary']['balance'];
|
||||
|
||||
// Figure out if this customer has any non-closed leases
|
||||
$show_moveout = false;
|
||||
$show_payment = false;
|
||||
foreach ($customer['Lease'] AS $lease) {
|
||||
if (!isset($lease['close_date']))
|
||||
$show_payment = true;
|
||||
if (!isset($lease['moveout_date']))
|
||||
$show_moveout = true;
|
||||
}
|
||||
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Payment', 'url' => array('action' => 'payment', $id));
|
||||
array('name' => 'Edit',
|
||||
'url' => array('action' => 'edit',
|
||||
$id));
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Move-Out', 'url' => array('controller' => 'units', 'action' => 'move-out'));
|
||||
array('name' => 'Move-In',
|
||||
'url' => array('action' => 'move_in',
|
||||
$id));
|
||||
|
||||
/* if ($show_moveout) { */
|
||||
/* $this->sidemenu_links[] = */
|
||||
/* array('name' => 'Move-Out', */
|
||||
/* 'url' => array('action' => 'move_out', */
|
||||
/* $id)); */
|
||||
/* } */
|
||||
|
||||
if ($show_payment) {
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Payment',
|
||||
'url' => array('action' => 'receipt',
|
||||
$id));
|
||||
}
|
||||
|
||||
// Prepare to render.
|
||||
$title = $customer['Customer']['name'];
|
||||
$title = 'Customer: ' . $customer['Customer']['name'];
|
||||
$this->set(compact('customer', 'title',
|
||||
'outstanding_balance',
|
||||
'outstanding_deposit'));
|
||||
@@ -176,21 +301,104 @@ class CustomersController extends AppController {
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: payment
|
||||
* - Sets up the payment entry page for the given customer.
|
||||
* action: edit
|
||||
* - Edit customer information
|
||||
*/
|
||||
|
||||
function payment($id = null) {
|
||||
/* if (!$id) { */
|
||||
/* $this->Session->setFlash(__('Invalid Item.', true)); */
|
||||
/* $this->redirect(array('action'=>'index')); */
|
||||
/* } */
|
||||
function edit($id = null) {
|
||||
if (isset($this->data)) {
|
||||
// Check to see if the operation was cancelled.
|
||||
if (isset($this->params['form']['cancel'])) {
|
||||
if (isset($this->data['Customer']['id']))
|
||||
$this->redirect(array('action'=>'view', $this->data['Customer']['id']));
|
||||
else
|
||||
$this->redirect(array('action'=>'index'));
|
||||
return;
|
||||
}
|
||||
|
||||
/* if ($this->RequestHandler->isPost()) { */
|
||||
/* pr($this->data); */
|
||||
/* //$this->redirect(array('action'=>'index')); */
|
||||
/* $customer = $this->data; */
|
||||
/* } */
|
||||
// Make sure we have at least one contact
|
||||
if (!isset($this->data['Contact']) || count($this->data['Contact']) == 0) {
|
||||
$this->Session->setFlash("MUST SPECIFY AT LEAST ONE CONTACT", true);
|
||||
$this->redirect(array('action'=>'view', $this->data['Customer']['id']));
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure there is a primary contact
|
||||
if (!isset($this->data['Customer']['primary_contact_entry'])) {
|
||||
$this->Session->setFlash("MUST SPECIFY A PRIMARY CONTACT", true);
|
||||
$this->redirect(array('action'=>'view', $this->data['Customer']['id']));
|
||||
return;
|
||||
}
|
||||
|
||||
// Go through each customer and strip the bogus ID if new
|
||||
foreach ($this->data['Contact'] AS &$contact) {
|
||||
if (isset($contact['source']) && $contact['source'] === 'new')
|
||||
unset($contact['id']);
|
||||
}
|
||||
|
||||
// Save the customer and all associated data
|
||||
if (!$this->Customer->saveCustomer($this->data['Customer']['id'],
|
||||
$this->data,
|
||||
$this->data['Customer']['primary_contact_entry'])) {
|
||||
$this->Session->setFlash("CUSTOMER SAVE FAILED", true);
|
||||
pr("CUSTOMER SAVE FAILED");
|
||||
}
|
||||
|
||||
// If existing customer, then view it. Otherwise, since
|
||||
// this is a new customer, go to the move in screen.
|
||||
if ($this->data['Customer']['id'])
|
||||
$this->redirect(array('action'=>'view', $this->Customer->id));
|
||||
else
|
||||
$this->redirect(array('action'=>'move_in', $this->Customer->id));
|
||||
|
||||
// For debugging, only if the redirects above have been
|
||||
// commented out, otherwise this section isn't reached.
|
||||
$this->render('/empty');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
$this->data = $this->Customer->details($id);
|
||||
$title = 'Customer: ' . $this->data['Customer']['name'] . " : Edit";
|
||||
}
|
||||
else {
|
||||
$title = "Enter New Customer";
|
||||
$this->data = array('Contact' => array(), 'PrimaryContact' => null);
|
||||
}
|
||||
|
||||
$contact_types = array_flip($this->Customer->ContactsCustomer->getEnumValues('type'));
|
||||
unset($contact_types[0]);
|
||||
$contact_types = array_combine($contact_types, $contact_types);
|
||||
$this->set(compact('contact_types'));
|
||||
|
||||
$contacts = $this->Customer->Contact->contactList();
|
||||
$this->set(compact('contacts'));
|
||||
|
||||
// Prepare to render.
|
||||
//pr($this->data);
|
||||
$this->set(compact('title'));
|
||||
$this->render('edit');
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: add
|
||||
* - Add a new customer
|
||||
*/
|
||||
|
||||
function add() {
|
||||
$this->edit();
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: receipt
|
||||
* - Sets up the receipt entry page for the given customer.
|
||||
*/
|
||||
|
||||
function receipt($id = null) {
|
||||
if (isset($id)) {
|
||||
$this->Customer->recursive = -1;
|
||||
$customer = $this->Customer->read(null, $id);
|
||||
@@ -203,11 +411,38 @@ class CustomersController extends AppController {
|
||||
$charges = array('balance' => 0, 'entry' => array());
|
||||
}
|
||||
|
||||
$title = 'Payment Entry';
|
||||
$A = new Account();
|
||||
$payment_accounts = $A->paymentAccounts();
|
||||
$default_account = $A->cashAccountID();
|
||||
$this->set(compact('payment_accounts', 'default_account'));
|
||||
|
||||
$title = ($customer['name'] . ': Payment Entry');
|
||||
$this->set(compact('customer', 'charges', 'title'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: refund
|
||||
* - Refunds customer charges
|
||||
*/
|
||||
|
||||
function refund() {
|
||||
$entries = $this->Customer->LedgerEntry->find
|
||||
('all', array
|
||||
('contain' => false,
|
||||
'conditions' => array('LedgerEntry.id' =>
|
||||
//array(199,200,201)
|
||||
61
|
||||
),
|
||||
));
|
||||
pr(compact('entries'));
|
||||
|
||||
$this->Customer->LedgerEntry->reverse($entries);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
|
||||
@@ -49,12 +49,42 @@ class LeasesController extends AppController {
|
||||
$params['action'] = 'all';
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
function jqGridDataCountTables(&$params, &$model) {
|
||||
return array
|
||||
('link' => array('Unit' => array('fields' => array('Unit.id', 'Unit.name')),
|
||||
'Customer' => array('fields' => array('Customer.id', 'Customer.name'))));
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
$link = $this->jqGridDataCountTables($params, $model);
|
||||
$link['link']['LedgerEntry'] = array('fields' => array());
|
||||
$link['link']['LedgerEntry']['Ledger'] = array('fields' => array());
|
||||
$link['link']['LedgerEntry']['Ledger']['Account'] = array('fields' => array());
|
||||
// INNER JOIN would be great, as it would ensure we're only looking
|
||||
// at the ledger entries that we truly want. However, this also
|
||||
// removes from the query any leases that do not yet have a ledger
|
||||
// entry in A/R. A solution would be to INNER JOIN these tables,
|
||||
// and LEFT JOIN it to the rest. Grouping of JOINs, however, is
|
||||
// implemented with the 'joins' tag, and is not available through
|
||||
// the Linkable behavior interface.
|
||||
//$link['link']['LedgerEntry']['Ledger']['Account']['type'] = 'INNER';
|
||||
$link['link']['LedgerEntry']['Ledger']['Account']['conditions']
|
||||
= array('Account.id' =>
|
||||
$this->Lease->LedgerEntry->Ledger->Account->accountReceivableAccountID());
|
||||
return $link;
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
$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;
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
|
||||
@@ -68,6 +98,37 @@ class LeasesController extends AppController {
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
// Do not sort by number, which is type varchar and
|
||||
// sorts on an ascii basis. Sort by ID instead.
|
||||
if ($index === 'Lease.number')
|
||||
$index = 'Lease.id';
|
||||
|
||||
// Instead of sorting by name, sort by defined order
|
||||
if ($index === 'Unit.name')
|
||||
$index = 'Unit.sort_order';
|
||||
|
||||
$order = array();
|
||||
$order[] = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
// If sorting by anything other than id/number
|
||||
// add sorting by id as a secondary condition.
|
||||
if ($index !== 'Lease.id' && $index !== 'Lease.number')
|
||||
$order[] = parent::jqGridDataOrder($params, $model,
|
||||
'Lease.id', $direction);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
/* function jqGridRecordsPostProcess(&$params, &$model, &$records) { */
|
||||
/* foreach ($records AS &$record) { */
|
||||
/* $record['Lease']['through_date'] */
|
||||
/* = $this->Lease->rentChargeThrough($record['Lease']['id']); */
|
||||
/* } */
|
||||
|
||||
/* parent::jqGridRecordsPostProcess($params, $model, $records); */
|
||||
/* } */
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Lease'] = array('number');
|
||||
$links['Unit'] = array('name');
|
||||
@@ -75,16 +136,282 @@ class LeasesController extends AppController {
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
function jqGridDataRecords(&$params, &$model, $query) {
|
||||
$leases = parent::jqGridDataRecords($params, $model, $query);
|
||||
|
||||
// Get the balance on each lease.
|
||||
foreach ($leases AS &$lease) {
|
||||
$stats = $this->Lease->stats($lease['Lease']['id']);
|
||||
$lease['Lease']['balance'] = $stats['balance'];
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: move_in
|
||||
* - execute a move in on a new lease
|
||||
*/
|
||||
|
||||
function move_in() {
|
||||
if (!$this->data)
|
||||
die("Should have some data");
|
||||
|
||||
// Handle the move in based on the data given
|
||||
//pr(array('Move-in data', $this->data));
|
||||
|
||||
$lid = $this->Lease->moveIn($this->data['Lease']['customer_id'],
|
||||
$this->data['Lease']['unit_id'],
|
||||
null, null,
|
||||
$this->data['Lease']['movein_date'],
|
||||
$this->data['Lease']['comment']
|
||||
);
|
||||
|
||||
// Since this is a new lease, go to the invoice
|
||||
// screen so we can start assessing charges.
|
||||
$this->redirect(array('action'=>'invoice', $lid));
|
||||
|
||||
// For debugging, only if the redirect above have been
|
||||
// commented out, otherwise this section isn't reached.
|
||||
$this->render('/empty');
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: move_out
|
||||
* - prepare or execute a move out on a specific lease
|
||||
*/
|
||||
|
||||
function move_out($id = null) {
|
||||
if ($this->data) {
|
||||
// Handle the move out based on the data given
|
||||
//pr($this->data);
|
||||
|
||||
$this->Lease->moveOut($this->data['Lease']['id'],
|
||||
'VACANT',
|
||||
$this->data['Lease']['moveout_date'],
|
||||
//true // Close this lease, if able
|
||||
false
|
||||
);
|
||||
|
||||
$this->redirect($this->data['redirect']);
|
||||
$this->autoRender = false;
|
||||
return;
|
||||
}
|
||||
|
||||
return $leases;
|
||||
if (!isset($id))
|
||||
die("Oh Nooooo!!");
|
||||
|
||||
$lease = $this->Lease->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
array('Lease.close_date' => null),
|
||||
),
|
||||
));
|
||||
$this->set('customer', $lease['Customer']);
|
||||
$this->set('unit', $lease['Unit']);
|
||||
$this->set('lease', $lease['Lease']);
|
||||
|
||||
$redirect = array('controller' => 'leases',
|
||||
'action' => 'view',
|
||||
$id);
|
||||
|
||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||
$lease['Unit']['name'] . ': ' .
|
||||
$lease['Customer']['name'] . ': Prepare Move-Out');
|
||||
$this->set(compact('title', 'redirect'));
|
||||
$this->render('/leases/move');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: apply_deposit
|
||||
* - Applies the security deposit to charges. This is much
|
||||
* like a receipt, but it's separated to keep it simple and
|
||||
* to prevent feature overload on the receipt page.
|
||||
*/
|
||||
|
||||
function apply_deposit($id) {
|
||||
$A = new Account();
|
||||
|
||||
$lease = $this->Lease->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
array('Lease.close_date' => null),
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
// Get the lease balance, part of lease stats
|
||||
$this->Lease->statsMerge($lease['Lease'],
|
||||
array('stats' => $this->Lease->stats($id)));
|
||||
|
||||
// Determine the lease security deposit
|
||||
$deposit = $this->Lease->findSecurityDeposits($lease['Lease']['id']);
|
||||
$this->set(compact('deposit'));
|
||||
$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); */
|
||||
|
||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||
$lease['Unit']['name'] . ': ' .
|
||||
$lease['Customer']['name'] . ': Utilize Security Deposit');
|
||||
$this->set(compact('title', 'redirect'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: bad_debt
|
||||
* - Writes off remaining charges on a lease.
|
||||
* REVISIT <AP>: 20090710
|
||||
* Should this be a customer function? What customer
|
||||
* would have only one lease that results in bad debt.
|
||||
*/
|
||||
|
||||
function bad_debt($id) {
|
||||
$A = new Account();
|
||||
|
||||
$lease = $this->Lease->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
array('Lease.close_date' => null),
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
// Get the lease balance, part of lease stats
|
||||
$this->Lease->statsMerge($lease['Lease'],
|
||||
array('stats' => $this->Lease->stats($id)));
|
||||
|
||||
// Determine the lease security deposit
|
||||
$deposit = $this->Lease->findSecurityDeposits($lease['Lease']['id']);
|
||||
if ($deposit['summary']['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); */
|
||||
|
||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||
$lease['Unit']['name'] . ': ' .
|
||||
$lease['Customer']['name'] . ': Write Off Bad Debt');
|
||||
$this->set(compact('title', 'redirect'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: refund
|
||||
* - Provides user with a refund
|
||||
* REVISIT <AP>: 20090710
|
||||
* Should this be a customer function?
|
||||
*/
|
||||
|
||||
function refund($id) {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: close
|
||||
* - Closes a lease to any further action
|
||||
*/
|
||||
|
||||
function close($id) {
|
||||
// REVISIT <AP>: 20090708
|
||||
// We should probably seek confirmation first...
|
||||
$this->Lease->close($id);
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: invoice
|
||||
* - Sets up the invoice entry page for the given customer.
|
||||
*/
|
||||
|
||||
function invoice($id = null, $type = null) {
|
||||
|
||||
$lease = $this->Lease->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
array('Lease.close_date' => null),
|
||||
),
|
||||
));
|
||||
|
||||
$A = new Account();
|
||||
$charge_accounts = $A->chargeAccounts();
|
||||
$default_account = $A->rentAccountID();
|
||||
$this->set(compact('charge_accounts', 'default_account'));
|
||||
|
||||
// REVISIT <AP> 20090705:
|
||||
// Of course, the late charge should come from the late_schedule
|
||||
$default_rent = $lease['Lease']['rent'];
|
||||
$default_late = 10;
|
||||
$this->set(compact('default_rent', 'default_late'));
|
||||
|
||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||
$lease['Unit']['name'] . ': ' .
|
||||
$lease['Customer']['name'] . ': Charge Entry');
|
||||
$this->set(compact('title', 'lease', 'charge'));
|
||||
}
|
||||
|
||||
|
||||
@@ -111,10 +438,15 @@ class LeasesController extends AppController {
|
||||
'Customer',
|
||||
),
|
||||
'conditions' => array(array('Lease.id' => $id)),
|
||||
'limit' => 2
|
||||
)
|
||||
);
|
||||
|
||||
$lease['Lease']['paid_through'] = $this->Lease->rentPaidThrough($id);
|
||||
|
||||
|
||||
$this->set('charge_gaps', $this->Lease->rentChargeGaps($id));
|
||||
$this->set('charge_through', $this->Lease->rentChargeThrough($id));
|
||||
|
||||
// Obtain the overall lease balance
|
||||
$this->Lease->statsMerge($lease['Lease'],
|
||||
array('stats' => $this->Lease->stats($id)));
|
||||
@@ -124,6 +456,48 @@ class LeasesController extends AppController {
|
||||
$deposits = $this->Lease->findSecurityDeposits($lease['Lease']['id']);
|
||||
$outstanding_deposit = $deposits['summary']['balance'];
|
||||
|
||||
// Set up dynamic menu items
|
||||
if (!isset($lease['Lease']['close_date'])) {
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
if (!isset($lease['Lease']['moveout_date']))
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Move-Out', 'url' => array('action' => 'move_out',
|
||||
$id));
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Charges', 'url' => array('action' => 'invoice',
|
||||
$id));
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Payments', 'url' => array('controller' => 'customers',
|
||||
'action' => 'receipt',
|
||||
$lease['Customer']['id']));
|
||||
|
||||
if (isset($lease['Lease']['moveout_date']) && $outstanding_deposit > 0 && $outstanding_balance > 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Apply Deposit', 'url' => array('action' => 'apply_deposit',
|
||||
$id));
|
||||
|
||||
if (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[] =
|
||||
array('name' => 'Write-Off', 'url' => array('action' => 'bad_debt',
|
||||
$id));
|
||||
|
||||
if ($this->Lease->closeable($id))
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Close', 'url' => array('action' => 'close',
|
||||
$id));
|
||||
}
|
||||
|
||||
// Prepare to render
|
||||
$title = 'Lease: #' . $lease['Lease']['id'];
|
||||
$this->set(compact('lease', 'title',
|
||||
|
||||
@@ -79,6 +79,51 @@ class LedgerEntriesController extends AppController {
|
||||
),
|
||||
);
|
||||
}
|
||||
elseif ($params['action'] === 'collected') {
|
||||
// Income / Receipt / Money
|
||||
// debit: A/R credit: Income <-- this entry
|
||||
// debit: Receipt credit: A/R <-- ReceiptLedgerEntry, below
|
||||
// debit: Money credit: Receipt <-- MoneyLedgerEntry, below
|
||||
|
||||
$link['CreditLedger'] =
|
||||
array('fields' => 'sequence',
|
||||
'Account' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
|
||||
// We're searching for the Receipt<->A/R entries,
|
||||
// which are debits on the A/R account. Find the
|
||||
// reconciling entries to that A/R debit.
|
||||
$link['DebitReconciliationLedgerEntry'] =
|
||||
array('alias' => 'ReceiptLedgerEntry',
|
||||
|
||||
'Transaction' =>
|
||||
array('alias' => 'ReceiptTransaction'),
|
||||
|
||||
// Credit Ledger should be A/R;
|
||||
// Debit Ledger should be Receipt
|
||||
'DebitLedger' =>
|
||||
array('alias' => 'ReceiptLedger',
|
||||
'Account' => array('alias' => 'ReceiptAccount'),
|
||||
),
|
||||
|
||||
// Finally, the Money (Cash/Check/etc) Entry is the one
|
||||
// which reconciles our ReceiptLedgerEntry debit
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
array('alias' => 'MoneyLedgerEntry',
|
||||
'linkalias' => 'MoneyLedgerEntryR',
|
||||
|
||||
// Credit Ledger should be Receipt;
|
||||
// Debit Ledger should be our Money Account
|
||||
'DebitLedger' =>
|
||||
array('alias' => 'MoneyLedger',
|
||||
'Account' =>
|
||||
array('alias' => 'MoneyAccount'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$link['DebitLedger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
@@ -130,7 +175,15 @@ class LedgerEntriesController extends AppController {
|
||||
? $params['custom']['account_type']
|
||||
: null);
|
||||
|
||||
return $model->ledgerContextFields2($ledger_id, $account_id, $account_type);
|
||||
$fields = $model->ledgerContextFields2($ledger_id, $account_id, $account_type);
|
||||
|
||||
if (count(array_intersect($params['fields'], array('applied'))) == 1)
|
||||
$fields[] = 'SUM(Reconciliation.amount) AS applied';
|
||||
|
||||
if ($params['action'] === 'collected')
|
||||
$fields[] = 'MAX(ReceiptTransaction.stamp) AS last_paid';
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
@@ -143,6 +196,30 @@ class LedgerEntriesController extends AppController {
|
||||
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'collected') {
|
||||
extract($params['custom']);
|
||||
|
||||
if (isset($collected_account_id))
|
||||
$conditions[] = array('Account.id' => $params['custom']['collected_account_id']);
|
||||
else
|
||||
die("INTERNAL ERROR: COLLECTED ACCOUNT ID NOT SET");
|
||||
|
||||
if (!empty($collected_from_date))
|
||||
$conditions[]
|
||||
= array('ReceiptTransaction.stamp >=' =>
|
||||
$this->LedgerEntry->Transaction->dateFormatBeforeSave($collected_from_date));
|
||||
|
||||
if (!empty($collected_through_date))
|
||||
$conditions[]
|
||||
= array('ReceiptTransaction.stamp <=' =>
|
||||
$this->LedgerEntry->Transaction->dateFormatBeforeSave($collected_through_date . ' 23:59:59'));
|
||||
|
||||
if (isset($collected_payment_accounts))
|
||||
$conditions[] = array('MoneyAccount.id' => $collected_payment_accounts);
|
||||
else
|
||||
$conditions[] = array('NOT' => array(array('MoneyAccount.id' => null)));
|
||||
}
|
||||
|
||||
if ($params['action'] === 'ledger') {
|
||||
$conditions[] = $model->ledgerContextConditions($ledger_id, $account_type);
|
||||
}
|
||||
@@ -204,6 +281,11 @@ class LedgerEntriesController extends AppController {
|
||||
array('Transaction.id' => $params['custom']['transaction_id']);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['monetary_source_id'])) {
|
||||
$conditions[] =
|
||||
array('MonetarySource.id' => $params['custom']['monetary_source_id']);
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
@@ -221,10 +303,10 @@ class LedgerEntriesController extends AppController {
|
||||
}
|
||||
|
||||
function jqGridDataGroup(&$params, &$model) {
|
||||
if (isset($params['custom']['notxgroup']))
|
||||
return parent::jqGridDataGroup($params, $model);
|
||||
if (isset($params['custom']['group_by_tx']) && $params['custom']['group_by_tx'])
|
||||
return $model->alias.'.transaction_id';
|
||||
|
||||
return $model->alias.'.transaction_id';
|
||||
return parent::jqGridDataGroup($params, $model);
|
||||
}
|
||||
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
@@ -239,30 +321,28 @@ class LedgerEntriesController extends AppController {
|
||||
return $order;
|
||||
}
|
||||
|
||||
function jqGridRecordsPostProcess(&$params, &$model, &$records) {
|
||||
parent::jqGridRecordsPostProcess($params, $model, $records);
|
||||
function jqGridDataRecords(&$params, &$model, $query) {
|
||||
if ($params['action'] === 'collected') {
|
||||
$tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1));
|
||||
$tquery['fields'] = array('SUM(Reconciliation.amount) AS applied');
|
||||
$total = $model->find('first', $tquery);
|
||||
|
||||
$subtotal = 0;
|
||||
foreach ($records AS &$record) {
|
||||
$amount = (isset($record['LedgerEntry']['balance'])
|
||||
? $record['LedgerEntry']['balance']
|
||||
: $record['LedgerEntry']['amount']);
|
||||
$record['LedgerEntry']['subtotal'] = ($subtotal += $amount);
|
||||
|
||||
continue;
|
||||
// Experiment to minimize columns by putting the monetary source
|
||||
// as the Account, when available
|
||||
if ($record['MonetarySource']['name'])
|
||||
$record['Account']['name'] = $record['MonetarySource']['name'];
|
||||
$params['userdata']['total'] = $total[0]['applied'];
|
||||
}
|
||||
|
||||
return parent::jqGridDataRecords($params, $model, $query);
|
||||
}
|
||||
|
||||
function jqGridDataOutputRecordCell(&$params, &$model, &$record, $field, $data) {
|
||||
/* if ($field === 'CreditAccount.name') { */
|
||||
/* $data .= '-OK'; */
|
||||
/* } */
|
||||
|
||||
parent::jqGridDataOutputRecordCell($params, $model, $record, $field, $data);
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: reverse the ledger entry
|
||||
*/
|
||||
|
||||
function reverse($id) {
|
||||
$this->LedgerEntry->reverse($id);
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
|
||||
@@ -284,7 +364,6 @@ class LedgerEntriesController extends AppController {
|
||||
('first',
|
||||
array('contain' => array('MonetarySource.id',
|
||||
'MonetarySource.name',
|
||||
'MonetarySource.MonetaryType.id',
|
||||
'Transaction.id',
|
||||
'Transaction.stamp',
|
||||
'DebitLedger.id',
|
||||
@@ -298,13 +377,11 @@ class LedgerEntriesController extends AppController {
|
||||
'Lease.id',
|
||||
),
|
||||
|
||||
'fields' => array('LedgerEntry.id',
|
||||
'LedgerEntry.amount',
|
||||
'LedgerEntry.comment'),
|
||||
'fields' => array('LedgerEntry.*'),
|
||||
|
||||
'conditions' => array('LedgerEntry.id' => $id),
|
||||
));
|
||||
pr($entry);
|
||||
//pr($entry);
|
||||
|
||||
// Because 'DebitLedger' and 'CreditLedger' both relate to 'Account',
|
||||
// CakePHP will not include them in the LedgerEntry->find (or so it
|
||||
@@ -317,6 +394,9 @@ class LedgerEntriesController extends AppController {
|
||||
'fields' => array('Account.id', 'Account.name', 'Account.type', 'Account.trackable'),
|
||||
'conditions' => array('Account.id' => $entry['DebitLedger']['account_id']),
|
||||
));
|
||||
$entry['DebitLedger']['Account']['ftype'] =
|
||||
$this->LedgerEntry->DebitLedger->Account
|
||||
->fundamentalType($entry['DebitLedger']['Account']['type']);
|
||||
|
||||
// Get the Account from CreditLedger
|
||||
$entry['CreditLedger'] += $this->LedgerEntry->CreditLedger->Account->find
|
||||
@@ -325,20 +405,63 @@ class LedgerEntriesController extends AppController {
|
||||
'fields' => array('Account.id', 'Account.name', 'Account.type', 'Account.trackable'),
|
||||
'conditions' => array('Account.id' => $entry['CreditLedger']['account_id']),
|
||||
));
|
||||
$entry['CreditLedger']['Account']['ftype'] =
|
||||
$this->LedgerEntry->CreditLedger->Account
|
||||
->fundamentalType($entry['CreditLedger']['Account']['type']);
|
||||
|
||||
// Get the reconciliation balances for this ledger entry
|
||||
$stats = $this->LedgerEntry->stats($id);
|
||||
$stats['debit']['amount_reconciled'] = $stats['debit_amount_reconciled'];
|
||||
$stats['credit']['amount_reconciled'] = $stats['credit_amount_reconciled'];
|
||||
if ($entry['DebitLedger']['Account']['trackable'])
|
||||
$stats['debit_amount_remaining'] = $entry['LedgerEntry']['amount'] - $stats['debit_amount_reconciled'];
|
||||
$stats['debit']['amount_remaining'] = $entry['LedgerEntry']['amount'] - $stats['debit']['amount_reconciled'];
|
||||
if ($entry['CreditLedger']['Account']['trackable'])
|
||||
$stats['credit_amount_remaining'] = $entry['LedgerEntry']['amount'] - $stats['credit_amount_reconciled'];
|
||||
$stats['credit']['amount_remaining'] = $entry['LedgerEntry']['amount'] - $stats['credit']['amount_reconciled'];
|
||||
//pr($stats);
|
||||
|
||||
$reconciled = $this->LedgerEntry->findReconciledLedgerEntries($id);
|
||||
//pr($reconciled);
|
||||
|
||||
|
||||
// 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->LedgerEntry->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));
|
||||
}
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Ledger Entry #{$entry['LedgerEntry']['id']}";
|
||||
$title = "Double Ledger Entry #{$entry['LedgerEntry']['id']}";
|
||||
$this->set(compact('entry', 'title', 'reconciled', 'stats'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ class LedgersController extends AppController {
|
||||
array(// Models
|
||||
'Account',
|
||||
'LedgerEntry',
|
||||
'Close',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ class MapsController extends AppController {
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
$this->set('info', $this->mapInfo($id, $requested_width));
|
||||
$this->set('title', "Site Map");
|
||||
}
|
||||
|
||||
|
||||
@@ -150,22 +151,6 @@ class MapsController extends AppController {
|
||||
return $info;
|
||||
}
|
||||
|
||||
// Temporary function
|
||||
function unitStatusList() {
|
||||
return
|
||||
array('DELETED' => array(),
|
||||
'DAMAGED' => array(),
|
||||
'COMPANY' => array(),
|
||||
'UNAVAILABLE' => array(),
|
||||
'RESERVED' => array(),
|
||||
'DIRTY' => array(),
|
||||
'VACANT' => array(),
|
||||
'OCCUPIED' => array(),
|
||||
'LATE' => array(),
|
||||
'LOCKED' => array(),
|
||||
'LIENED' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -175,9 +160,10 @@ class MapsController extends AppController {
|
||||
*/
|
||||
|
||||
function legend($id = null, $requested_width = 400) {
|
||||
$status = $this->unitStatusList();
|
||||
$cols = 6;
|
||||
$rows = (int)((count($status) + $cols - 1) / $cols);
|
||||
$status = $this->Map->Unit->activeStatusEnums();
|
||||
//pr($status);
|
||||
$rows = 2;
|
||||
$cols = (int)((count($status) + $rows - 1) / $rows);
|
||||
|
||||
$info = array('units' => array());
|
||||
|
||||
@@ -205,7 +191,7 @@ class MapsController extends AppController {
|
||||
$item_width *= $screen_adjustment_factor;
|
||||
$item_depth *= $screen_adjustment_factor;
|
||||
|
||||
foreach ($status AS $code => $color) {
|
||||
foreach ($status AS $code => $value) {
|
||||
$info['units'][] = array('name' => $code,
|
||||
'status' => $code,
|
||||
'width' => $item_width,
|
||||
|
||||
@@ -38,8 +38,7 @@ class MonetarySourcesController extends AppController {
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
return array
|
||||
('link' => array('MonetaryType' => array('fields' => array('MonetaryType.id', 'MonetaryType.name')),
|
||||
),
|
||||
('contain' => false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -49,6 +48,26 @@ class MonetarySourcesController extends AppController {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: nsf
|
||||
* - Marks a monetary source as having insufficient funds.
|
||||
*/
|
||||
|
||||
function nsf($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
// REVISIT <AP>: 20090713
|
||||
// For testing purposes, must be deleted
|
||||
$stamp = '2009-07-09';
|
||||
|
||||
$this->MonetarySource->nsf($id, $stamp);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -65,11 +84,25 @@ class MonetarySourcesController extends AppController {
|
||||
// Get the MonetarySource and related fields
|
||||
$monetary_source = $this->MonetarySource->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
('MonetaryType',
|
||||
),
|
||||
('contain' => false,
|
||||
));
|
||||
|
||||
// REVISIT <AP>: 20090713
|
||||
// Consider allowing the NSF operation only if the source is used on
|
||||
// a ledger entry that is debited on a "payable" account (perhaps
|
||||
// even restricted to "payable" ASSET accounts), credited on Receipt
|
||||
// (or A/R), and reconciles the credit to an entry that debits on a
|
||||
// "depositable" account.
|
||||
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'NSF',
|
||||
'url' => array('action' => 'nsf',
|
||||
$id));
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Monetary Source #{$monetary_source['MonetarySource']['id']}";
|
||||
$this->set(compact('monetary_source', 'title'));
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
<?php
|
||||
/* SVN FILE: $Id: pages_controller.php 7945 2008-12-19 02:16:01Z gwoo $ */
|
||||
/**
|
||||
* Static content controller.
|
||||
*
|
||||
* This file will render views from views/pages/
|
||||
*
|
||||
* PHP versions 4 and 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
|
||||
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @filesource
|
||||
* @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
|
||||
* @package cake
|
||||
* @subpackage cake.cake.libs.controller
|
||||
* @since CakePHP(tm) v 0.2.9
|
||||
* @version $Revision: 7945 $
|
||||
* @modifiedby $LastChangedBy: gwoo $
|
||||
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
/**
|
||||
* Static content controller
|
||||
*
|
||||
* Override this controller by placing a copy in controllers directory of an application
|
||||
*
|
||||
* @package cake
|
||||
* @subpackage cake.cake.libs.controller
|
||||
*/
|
||||
class PagesController extends AppController {
|
||||
/**
|
||||
* Controller name
|
||||
*
|
||||
* @var string
|
||||
* @access public
|
||||
*/
|
||||
var $name = 'Pages';
|
||||
/**
|
||||
* Default helper
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
var $helpers = array('Html');
|
||||
/**
|
||||
* This controller does not use a model
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
var $uses = array();
|
||||
/**
|
||||
* Displays a view
|
||||
*
|
||||
* @param mixed What page to display
|
||||
* @access public
|
||||
*/
|
||||
function display() {
|
||||
$path = func_get_args();
|
||||
|
||||
$count = count($path);
|
||||
if (!$count) {
|
||||
$this->redirect('/');
|
||||
}
|
||||
$page = $subpage = $title = null;
|
||||
|
||||
if (!empty($path[0])) {
|
||||
$page = $path[0];
|
||||
}
|
||||
if (!empty($path[1])) {
|
||||
$subpage = $path[1];
|
||||
}
|
||||
if (!empty($path[$count - 1])) {
|
||||
$title = Inflector::humanize($path[$count - 1]);
|
||||
}
|
||||
$this->set(compact('page', 'subpage', 'title'));
|
||||
$this->render(join('/', $path));
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -95,6 +95,35 @@ class TransactionsController extends AppController {
|
||||
$this->set(compact('transaction', 'title', 'total'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: postInvoice
|
||||
* - handles the creation of a charge invoice
|
||||
*/
|
||||
|
||||
function postInvoice() {
|
||||
if (!$this->RequestHandler->isPost()) {
|
||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->Transaction->addInvoice($this->data, null,
|
||||
$this->data['Lease']['id'])) {
|
||||
$this->Session->setFlash("INVOICE FAILED", true);
|
||||
// REVISIT <AP> 20090706:
|
||||
// Until we can work out the session problems,
|
||||
// just die.
|
||||
die("<H1>INVOICE FAILED</H1>");
|
||||
}
|
||||
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -103,186 +132,35 @@ class TransactionsController extends AppController {
|
||||
*/
|
||||
|
||||
function postReceipt() {
|
||||
$this->autoRender = false;
|
||||
|
||||
if (!$this->RequestHandler->isPost()) {
|
||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||
return;
|
||||
}
|
||||
//pr(array('thisdata' => $this->data));
|
||||
|
||||
if (isset($this->data['customer_id'])) {
|
||||
$C = new Customer();
|
||||
$C->recursive = -1;
|
||||
$customer = $C->find
|
||||
('first',
|
||||
array('contain' => array('Account.CurrentLedger.id'),
|
||||
'fields' => false,
|
||||
'conditions' => array('Customer.id', $this->data['customer_id']),
|
||||
));
|
||||
$ledger_id = $customer['Account']['CurrentLedger']['id'];
|
||||
foreach($this->data['LedgerEntry'] AS &$entry) {
|
||||
if (!isset($entry['acct'][$entry['account_id']]))
|
||||
continue;
|
||||
|
||||
$entry['MonetarySource'] = $entry['acct'][$entry['account_id']];
|
||||
}
|
||||
else {
|
||||
// Payment by Unit, Lease, etc
|
||||
$ledger_id = 0;
|
||||
}
|
||||
|
||||
$amount = 0;
|
||||
foreach ($this->data['LedgerEntry'] AS &$entry) {
|
||||
$reconciled = $C->reconcileNewLedgerEntry($this->data['customer_id'],
|
||||
'credit',
|
||||
$entry['amount']);
|
||||
pr(compact('entry', 'reconciled'));
|
||||
|
||||
foreach ($reconciled['debit']['entry'] AS $rec) {
|
||||
$entry['DebitReconciliationLedgerEntry'] =
|
||||
array('amount' => $rec['applied'],
|
||||
//'debit_ledger_entry_id'
|
||||
'credit_ledger_entry_id' => $rec['id']
|
||||
);
|
||||
}
|
||||
|
||||
$amount += isset($entry['amount']) ? $entry['amount'] : 0;
|
||||
$entry['debit_ledger_id'] = 6; // Cash/Payments
|
||||
$entry['credit_ledger_id'] = $ledger_id;
|
||||
}
|
||||
|
||||
|
||||
pr($this->data);
|
||||
$T = new Transaction();
|
||||
$T->create();
|
||||
if ($T->saveAll($this->data,
|
||||
array(
|
||||
'validate' => false,
|
||||
//'fieldList' => array(),
|
||||
//'callbacks' => true,
|
||||
))) {
|
||||
$tid = $T->id;
|
||||
$this->Session->setFlash(__("New Transaction Created ($tid)!", true));
|
||||
//$this->redirect(array('action'=>'view', $mid));
|
||||
}
|
||||
else {
|
||||
$this->autoRender = false;
|
||||
pr(array('checkpoint' => "saveAll failed"));
|
||||
}
|
||||
pr($T->data);
|
||||
|
||||
$C = new Customer();
|
||||
$LE = new LedgerEntry();
|
||||
/* $reconciled = $C->reconcileNewLedgerEntry($this->data['customer_id'], */
|
||||
/* 'credit', */
|
||||
/* $amount); */
|
||||
/* pr(compact('amount', 'unreconciled', 'reconciled')); */
|
||||
/* return; */
|
||||
|
||||
foreach ($this->data['LedgerEntry'] AS &$entry) {
|
||||
$reconciled = $C->reconcileNewLedgerEntry($this->data['customer_id'],
|
||||
'credit',
|
||||
$entry['amount']);
|
||||
pr(compact('entry', 'reconciled'));
|
||||
continue;
|
||||
|
||||
foreach ($reconciled['debit']['entry'] AS $rec) {
|
||||
$data = array('LedgerEntry' =>
|
||||
array('DebitReconciliationLedgerEntry' =>
|
||||
array('amount' => $rec['applied'],
|
||||
//'debit_ledger_entry_id'
|
||||
'credit_ledger_entry_id' => $rec['id']
|
||||
),
|
||||
),
|
||||
);
|
||||
//'DebitReconciliationLedgerEntry' => array(
|
||||
//pr(compact('amount', 'unreconciled', 'reconciled'));
|
||||
}
|
||||
if (!$this->Transaction->addReceipt($this->data,
|
||||
$this->data['Customer']['id'],
|
||||
(isset($this->data['Lease']['id'])
|
||||
? $this->data['Lease']['id']
|
||||
: null ))) {
|
||||
$this->Session->setFlash("RECEIPT FAILED", true);
|
||||
// REVISIT <AP> 20090706:
|
||||
// Until we can work out the session problems,
|
||||
// just die.
|
||||
die("<H1>RECEIPT FAILED</H1>");
|
||||
}
|
||||
|
||||
}
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
}
|
||||
|
||||
function saveTest() {
|
||||
$data =
|
||||
array(
|
||||
/* 'Customer' => array */
|
||||
/* ('id' => 7, */
|
||||
/* ), */
|
||||
|
||||
'LedgerEntry' => array
|
||||
(
|
||||
'0' => array(
|
||||
'amount' => 100,
|
||||
'debit_ledger_id' => 1,
|
||||
'credit_ledger_id' => 2,
|
||||
'MonetarySource' => array('name' => 'testmoney', 'monetary_type_id' => 2),
|
||||
),
|
||||
'1' => array(
|
||||
'amount' => 101,
|
||||
'debit_ledger_id' => 1,
|
||||
'credit_ledger_id' => 2,
|
||||
'MonetarySource' => array('name' => 'testmoney2', 'monetary_type_id' => 2),
|
||||
),
|
||||
),
|
||||
|
||||
'Transaction' => array
|
||||
(
|
||||
'stamp' => '06/18/2009',
|
||||
'comment' => 'no comment',
|
||||
),
|
||||
);
|
||||
|
||||
$data =
|
||||
/* array( */
|
||||
/* 'LedgerEntry' => array */
|
||||
/* ( */
|
||||
/* '0' => */
|
||||
array(
|
||||
'amount' => 100,
|
||||
'debit_ledger_id' => 1,
|
||||
'credit_ledger_id' => 2,
|
||||
'transaction_id' => 66,
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
array('amount' => 44,
|
||||
//'debit_ledger_entry_id'
|
||||
'credit_ledger_entry_id' => 17,
|
||||
),
|
||||
/* ), */
|
||||
/* ), */
|
||||
|
||||
);
|
||||
|
||||
//$M = new Transaction();
|
||||
$M = new LedgerEntry();
|
||||
$M->create();
|
||||
$retval = $M->saveAll($data,
|
||||
array(
|
||||
'validate' => false,
|
||||
'fieldList' => array(),
|
||||
'callbacks' => true,
|
||||
));
|
||||
|
||||
$mid = $M->id;
|
||||
pr(compact('retval', 'mid'));
|
||||
if ($mid) {
|
||||
$this->Session->setFlash(__("New Transaction Created ($mid)!", true));
|
||||
//$this->redirect(array('action'=>'view', $mid));
|
||||
}
|
||||
else {
|
||||
$this->autoRender = false;
|
||||
pr(array('checkpoint' => "saveAll failed"));
|
||||
}
|
||||
|
||||
|
||||
/* $LE = new LedgerEntry(); */
|
||||
/* $LE->create(); */
|
||||
/* $ret = $LE->save($data, */
|
||||
/* array( */
|
||||
/* 'validate' => false, */
|
||||
/* 'fieldList' => array(), */
|
||||
/* 'callbacks' => true, */
|
||||
/* )); */
|
||||
/* $leid = $LE->id; */
|
||||
/* pr(array('checkpoint' => "New Ledger Entry", */
|
||||
/* compact('leid', 'ret'))); */
|
||||
//pr($LE);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,11 +51,11 @@ class UnitsController extends AppController {
|
||||
$params['action'] = 'all';
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
function jqGridDataCountTables(&$params, &$model) {
|
||||
$link = array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'UnitSize' => array('fields' => array('name')),
|
||||
'UnitSize' => array('fields' => array('id', 'name')),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -70,6 +70,35 @@ class UnitsController extends AppController {
|
||||
return $link;
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
$link = $this->jqGridDataCountTables($params, $model);
|
||||
$link['link']['CurrentLease']['LedgerEntry'] = array('fields' => array());
|
||||
$link['link']['CurrentLease']['LedgerEntry']['Ledger'] = array('fields' => array());
|
||||
$link['link']['CurrentLease']['LedgerEntry']['Ledger']['Account'] = array('fields' => array());
|
||||
// INNER JOIN would be great, as it would ensure we're only looking
|
||||
// at the ledger entries that we truly want. However, this also
|
||||
// removes from the query any leases that do not yet have a ledger
|
||||
// entry in A/R. A solution would be to INNER JOIN these tables,
|
||||
// and LEFT JOIN it to the rest. Grouping of JOINs, however, is
|
||||
// implemented with the 'joins' tag, and is not available through
|
||||
// the Linkable behavior interface.
|
||||
//$link['link']['CurrentLease']['LedgerEntry']['Ledger']['Account']['type'] = 'INNER';
|
||||
$link['link']['CurrentLease']['LedgerEntry']['Ledger']['Account']['conditions']
|
||||
= array('Account.id' =>
|
||||
$this->Unit->CurrentLease->LedgerEntry->Ledger->Account->accountReceivableAccountID());
|
||||
return $link;
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
$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))" .
|
||||
" * LedgerEntry.amount) AS 'balance'");
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
|
||||
@@ -82,15 +111,95 @@ class UnitsController extends AppController {
|
||||
elseif ($params['action'] === 'occupied') {
|
||||
$conditions[] = $this->Unit->conditionOccupied();
|
||||
}
|
||||
elseif ($params['action'] === 'unoccupied') {
|
||||
$conditions[] = array('NOT' => array($this->Unit->conditionOccupied()));
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
if ($index === 'Unit.name') {
|
||||
// Instead of sorting by name, sort by defined order
|
||||
if ($index === 'Unit.name')
|
||||
$index = 'Unit.sort_order';
|
||||
|
||||
$order = array();
|
||||
$order[] = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
// If sorting by anything other than name (defined order)
|
||||
// add the sort-order as a secondary condition
|
||||
if ($index !== 'Unit.name')
|
||||
$order[] = parent::jqGridDataOrder($params, $model,
|
||||
'Unit.sort_order', $direction);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Unit'] = array('name');
|
||||
$links['UnitSize'] = array('name');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: move_in
|
||||
* - Sets up the move-in page for the given unit.
|
||||
*/
|
||||
|
||||
function move_in($id = null) {
|
||||
$customer = array();
|
||||
$unit = array();
|
||||
|
||||
if (isset($id)) {
|
||||
$this->Unit->recursive = -1;
|
||||
$unit = current($this->Unit->read(null, $id));
|
||||
}
|
||||
return parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
$title = 'Unit Move-In';
|
||||
$this->set(compact('customer', 'unit', 'title'));
|
||||
$this->render('/leases/move');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: move_out
|
||||
* - prepare or execute a move out on a specific lease
|
||||
*/
|
||||
|
||||
function move_out($id) {
|
||||
|
||||
$unit = $this->Unit->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'CurrentLease' =>
|
||||
array(//'conditions' => array('Lease.moveout_date' => null),
|
||||
// Models
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
),
|
||||
'conditions' => array('Unit.id' => $id),
|
||||
));
|
||||
$this->set('customer', $unit['CurrentLease']['Customer']);
|
||||
$this->set('unit', $unit['Unit']);
|
||||
$this->set('lease', $unit['CurrentLease']);
|
||||
|
||||
$redirect = array('controller' => 'units',
|
||||
'action' => 'view',
|
||||
$id);
|
||||
|
||||
$title = ('Lease #' . $unit['CurrentLease']['number'] . ': ' .
|
||||
$unit['Unit']['name'] . ': ' .
|
||||
$unit['CurrentLease']['Customer']['name'] . ': Prepare Move-Out');
|
||||
$this->set(compact('title', 'redirect'));
|
||||
$this->render('/leases/move');
|
||||
}
|
||||
|
||||
|
||||
@@ -137,10 +246,28 @@ class UnitsController extends AppController {
|
||||
$outstanding_deposit = $deposits['summary']['balance'];
|
||||
}
|
||||
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Move-Out', 'url' => array('controller' => 'units', 'action' => 'move-out'));
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
if (isset($unit['CurrentLease']['id']) &&
|
||||
!isset($unit['CurrentLease']['moveout_date'])) {
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Move-Out', 'url' => array('action' => 'move_out',
|
||||
$id));
|
||||
} else {
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Move-In', 'url' => array('action' => 'move_in',
|
||||
$id));
|
||||
}
|
||||
|
||||
if (isset($unit['CurrentLease']['id']) &&
|
||||
!isset($unit['CurrentLease']['close_date'])) {
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Payment', 'url' => array('controller' => 'customers',
|
||||
'action' => 'receipt',
|
||||
$unit['CurrentLease']['customer_id']));
|
||||
}
|
||||
|
||||
// Prepare to render.
|
||||
$title = 'Unit ' . $unit['Unit']['name'];
|
||||
|
||||
@@ -11,7 +11,13 @@ class Account extends AppModel {
|
||||
var $hasOne = array(
|
||||
'CurrentLedger' => array(
|
||||
'className' => 'Ledger',
|
||||
'conditions' => array('NOT' => array('CurrentLedger.closed'))
|
||||
// REVISIT <AP> 20090702:
|
||||
// I would prefer this statement, which has no
|
||||
// engine specific code. However, it doesn't
|
||||
// work with the Linkable behavior. I need to
|
||||
// look into that, just not right now.
|
||||
//'conditions' => array('CurrentLedger.close_id' => null),
|
||||
'conditions' => array('CurrentLedger.close_id IS NULL'),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -82,26 +88,165 @@ class Account extends AppModel {
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: accountNameToID
|
||||
* - Returns the ID of the named account
|
||||
* function: name
|
||||
* - Returns the name of this account
|
||||
*/
|
||||
function accountNameToID($name) {
|
||||
function name($id) {
|
||||
$this->cacheQueries = true;
|
||||
$account = $this->find('first', array
|
||||
('recursive' => -1,
|
||||
'conditions' => compact('name'),
|
||||
'fields' => array('name'),
|
||||
'conditions' => array(array('Account.id' => $id)),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
return $account['Account']['id'];
|
||||
|
||||
return $account['Account']['name'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: Account IDs
|
||||
* - Returns the ID of the desired account
|
||||
*/
|
||||
|
||||
function securityDepositAccountID() { return $this->nameToID('Security Deposit'); }
|
||||
function rentAccountID() { return $this->nameToID('Rent'); }
|
||||
function lateChargeAccountID() { return $this->nameToID('Late Charge'); }
|
||||
function nsfAccountID() { return $this->nameToID('NSF'); }
|
||||
function nsfChargeAccountID() { return $this->nameToID('NSF Charge'); }
|
||||
function taxAccountID() { return $this->nameToID('Tax'); }
|
||||
function accountReceivableAccountID() { return $this->nameToID('A/R'); }
|
||||
function cashAccountID() { return $this->nameToID('Cash'); }
|
||||
function checkAccountID() { return $this->nameToID('Check'); }
|
||||
function moneyOrderAccountID() { return $this->nameToID('Money Order'); }
|
||||
function concessionAccountID() { return $this->nameToID('Concession'); }
|
||||
function pettyCashAccountID() { return $this->nameToID('Petty Cash'); }
|
||||
function invoiceAccountID() { return $this->nameToID('Invoice'); }
|
||||
function receiptAccountID() { return $this->nameToID('Receipt'); }
|
||||
function badDebtAccountID() { return $this->nameToID('Bad Debt'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: fundamentalAccounts
|
||||
* - Returns an array of accounts by their fundamental type
|
||||
*/
|
||||
|
||||
function fundamentalAccounts($ftype) {
|
||||
$this->cacheQueries = true;
|
||||
$account = $this->find('all', array
|
||||
('contain' => array('CurrentLedger'),
|
||||
'fields' => array('Account.id', 'Account.type', 'Account.name', 'CurrentLedger.id'),
|
||||
'conditions' => array('Account.type' => strtoupper($ftype))
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: relatedAccounts
|
||||
* - Returns an array of accounts related by similar attributes
|
||||
*/
|
||||
|
||||
function relatedAccounts($attribute, $extra = null) {
|
||||
$this->cacheQueries = true;
|
||||
$account = $this->find('all', array
|
||||
('contain' => array('CurrentLedger'),
|
||||
'fields' => array('Account.id', 'Account.type', 'Account.name', 'CurrentLedger.id'),
|
||||
'conditions' => array('Account.'.$attribute => true),
|
||||
'order' => array('Account.name'),
|
||||
) + (isset($extra) ? $extra : array())
|
||||
);
|
||||
$this->cacheQueries = false;
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: chargeAccounts
|
||||
* - Returns an array of accounts suitable for charges
|
||||
*/
|
||||
|
||||
function chargeAccounts() {
|
||||
// Get all accounts that support charges
|
||||
$accounts = $this->relatedAccounts('chargeable', array('order' => 'name'));
|
||||
|
||||
// Rearrange to be of the form (id => name)
|
||||
$charge_accounts = array();
|
||||
foreach ($accounts AS $acct) {
|
||||
$charge_accounts[$acct['Account']['id']] = $acct['Account']['name'];
|
||||
}
|
||||
|
||||
return $charge_accounts;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: paymentAccounts
|
||||
* - Returns an array of accounts suitable for payments
|
||||
*/
|
||||
|
||||
function paymentAccounts() {
|
||||
// Get all accounts that support payments
|
||||
$accounts = $this->relatedAccounts('payable', array('order' => 'name'));
|
||||
|
||||
// Rearrange to be of the form (id => name)
|
||||
$payment_accounts = array();
|
||||
foreach ($accounts AS $acct) {
|
||||
$payment_accounts[$acct['Account']['id']] = $acct['Account']['name'];
|
||||
}
|
||||
|
||||
return $payment_accounts;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: collectableAccounts
|
||||
* - Returns an array of accounts suitable to show income collection
|
||||
*/
|
||||
|
||||
function collectableAccounts() {
|
||||
$accounts = $this->paymentAccounts();
|
||||
|
||||
foreach(array($this->nsfAccountID(),
|
||||
$this->securityDepositAccountID())
|
||||
AS $account_id) {
|
||||
$accounts[$account_id] = $this->name($account_id);
|
||||
}
|
||||
|
||||
return $accounts;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: currentLedgerID
|
||||
* - Returns the current ledger ID of the account
|
||||
*/
|
||||
function currentLedgerID($id) {
|
||||
$this->cacheQueries = true;
|
||||
$item = $this->find('first', array
|
||||
('contain' => 'CurrentLedger',
|
||||
'conditions' => array('Account.id' => $id),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
return $item['CurrentLedger']['id'];
|
||||
}
|
||||
|
||||
function securityDepositAccountID() { return $this->accountNameToID('Security Deposit'); }
|
||||
function rentAccountID() { return $this->accountNameToID('Rent'); }
|
||||
function accountReceivableAccountID() { return $this->accountNameToID('A/R'); }
|
||||
function invoiceAccountID() { return $this->accountReceivableAccountID(); }
|
||||
function receiptAccountID() { return $this->accountReceivableAccountID(); }
|
||||
//function invoiceAccountID() { return $this->accountNameToID('Invoice'); }
|
||||
//function receiptAccountID() { return $this->accountNameToID('Receipt'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
@@ -142,6 +287,43 @@ class Account extends AppModel {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: closeCurrentLedger
|
||||
* - Closes the current account ledger, and opens a new one
|
||||
* with the old balance carried forward.
|
||||
*/
|
||||
function closeCurrentLedger($id = null, $close_id = null) {
|
||||
$contain = array('CurrentLedger' => array('fields' => array('CurrentLedger.id')));
|
||||
|
||||
if (!$close_id) {
|
||||
$close = new Close();
|
||||
$close->create();
|
||||
if (!$close->save(array('stamp' => null), false)) {
|
||||
return false;
|
||||
}
|
||||
$close_id = $close->id;
|
||||
}
|
||||
|
||||
$this->cacheQueries = true;
|
||||
$account = $this->find('all', array
|
||||
('contain' => $contain,
|
||||
'fields' => array(),
|
||||
'conditions' =>
|
||||
$id ? array(array('Account.id' => $id)) : array()
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
//pr(compact('id', 'account'));
|
||||
|
||||
foreach ($account AS $acct) {
|
||||
if (!$this->Ledger->closeLedger($acct['CurrentLedger']['id'], $close_id))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -233,7 +415,7 @@ class Account extends AppModel {
|
||||
('fields' => array(),
|
||||
"LedgerEntry" => array
|
||||
('class' => "{$ucfund}LedgerEntry",
|
||||
'fields' => array('id', 'amount'),
|
||||
'fields' => array('id', 'customer_id', 'lease_id', 'amount'),
|
||||
"ReconciliationLedgerEntry" => array
|
||||
('class' => "{$ucfund}ReconciliationLedgerEntry",
|
||||
'fields' => array
|
||||
@@ -312,6 +494,316 @@ class Account extends AppModel {
|
||||
return $unreconciled;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: postLedgerEntry
|
||||
* -
|
||||
* transaction_data
|
||||
* - transaction_id (optional... if set all else is ignored)
|
||||
* - Transaction
|
||||
* - stamp (optional... otherwise NOW is used)
|
||||
* - comment
|
||||
*
|
||||
* monetary_source_data
|
||||
* - monetary_source_id (optional... if set all else is ignored)
|
||||
* - account_name
|
||||
* - MonetarySource
|
||||
* - name
|
||||
*/
|
||||
|
||||
function postLedgerEntry($transaction_data,
|
||||
$monetary_data,
|
||||
$entry_data,
|
||||
$reconcile = null) {
|
||||
//pr(compact('transaction_data', 'monetary_data', 'entry_data', 'reconcile'));
|
||||
|
||||
// Automatically figure out the customer if we have the lease
|
||||
if (isset($entry_data['lease_id']) && !isset($entry_data['customer_id'])) {
|
||||
$L = new Lease();
|
||||
$L->recursive = -1;
|
||||
$lease = $L->read(null, $entry_data['lease_id']);
|
||||
$entry_data['customer_id'] = $lease['Lease']['customer_id'];
|
||||
}
|
||||
|
||||
if (!isset($entry_data['lease_id']))
|
||||
$entry_data['lease_id'] = null;
|
||||
|
||||
if (!isset($entry_data['customer_id']))
|
||||
$entry_data['customer_id'] = null;
|
||||
|
||||
// Get the Transaction squared away
|
||||
if (isset($transaction_data['transaction_id'])) {
|
||||
$transaction_data
|
||||
= array_intersect_key($transaction_data,
|
||||
array('transaction_id'=>1,
|
||||
'split_transaction_id'=>1));
|
||||
}
|
||||
elseif (isset($transaction_data['Transaction'])) {
|
||||
$transaction_data
|
||||
= array_intersect_key($transaction_data,
|
||||
array('Transaction'=>1,
|
||||
'split_transaction_id'=>1));
|
||||
}
|
||||
else {
|
||||
$transaction_data = array('Transaction'=>array('stamp' => null));
|
||||
}
|
||||
|
||||
|
||||
// Get the Monetary Source squared away
|
||||
if (isset($monetary_data)) {
|
||||
if (!isset($monetary_data['monetary_source_id'])) {
|
||||
|
||||
// Convert Account ID to name or vice versa
|
||||
if (isset($monetary_data['account_id'])) {
|
||||
$monetary_data['account_name'] = $this->name($monetary_data['account_id']);
|
||||
} elseif (isset($monetary_data['account_name'])) {
|
||||
$monetary_data['account_id'] = $this->nameToID($monetary_data['account_name']);
|
||||
}
|
||||
|
||||
if ($monetary_data['account_id'] == $this->cashAccountID()) {
|
||||
// No distinguishing features of Cash, just
|
||||
// use the shared monetary source
|
||||
$monetary_data['monetary_source_id'] =
|
||||
$this->Ledger->LedgerEntry->MonetarySource->nameToID('Cash');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($monetary_data['monetary_source_id'])) {
|
||||
$monetary_data
|
||||
= array_intersect_key($monetary_data,
|
||||
array('monetary_source_id'=>1));
|
||||
}
|
||||
else {
|
||||
// The monetary source needs to be unique
|
||||
// Create a new one dedicated to this entry
|
||||
// Give it a fancy name based on the check number
|
||||
$monetary_data['MonetarySource']['name'] = $monetary_data['account_name'];
|
||||
if ($monetary_data['account_name'] === $this->name($this->checkAccountID()) ||
|
||||
$monetary_data['account_name'] === $this->name($this->moneyOrderAccountID())) {
|
||||
$monetary_data['MonetarySource']['name'] .=
|
||||
' #' . $monetary_data['MonetarySource']['data1'];
|
||||
}
|
||||
|
||||
$monetary_data
|
||||
= array_intersect_key($monetary_data,
|
||||
array('MonetarySource'=>1));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$monetary_data = array();
|
||||
}
|
||||
|
||||
// Make sure to clean out any unwanted data from the entry
|
||||
$entry_data
|
||||
= array_diff_key($entry_data,
|
||||
array('transaction_id'=>1, 'Transaction'=>1,
|
||||
'monetary_source_id'=>1, 'MonetarySource'=>1));
|
||||
|
||||
// Then add in the transaction and monetary source data
|
||||
//pr(compact('transaction_data', 'monetary_data', 'entry_data'));
|
||||
if (isset($transaction_data))
|
||||
$entry_data += $transaction_data;
|
||||
if (isset($monetary_data))
|
||||
$entry_data += $monetary_data;
|
||||
|
||||
// Set up the debit ledger id
|
||||
if (!isset($entry_data['debit_ledger_id'])) {
|
||||
$entry_data['debit_ledger_id'] =
|
||||
(isset($entry_data['debit_account_id'])
|
||||
? $this->currentLedgerID($entry_data['debit_account_id'])
|
||||
: (isset($entry_data['debit_account_name'])
|
||||
? $this->currentLedgerID($this->nameToID($entry_data['debit_account_name']))
|
||||
: null
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Set up the credit ledger id
|
||||
if (!isset($entry_data['credit_ledger_id'])) {
|
||||
$entry_data['credit_ledger_id'] =
|
||||
(isset($entry_data['credit_account_id'])
|
||||
? $this->currentLedgerID($entry_data['credit_account_id'])
|
||||
: (isset($entry_data['credit_account_name'])
|
||||
? $this->currentLedgerID($this->nameToID($entry_data['credit_account_name']))
|
||||
: null
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
//pr(array('pre-save', compact('entry_data')));
|
||||
// Create it!
|
||||
$new_entry = new LedgerEntry();
|
||||
$new_entry->create();
|
||||
if (!$new_entry->saveAll($entry_data, array('validate'=>false))) {
|
||||
return array('error' => true);
|
||||
}
|
||||
|
||||
// See if the user has entered some sort of non-array
|
||||
// for the reconcile parameter.
|
||||
if (isset($reconcile) && is_bool($reconcile) && $reconcile) {
|
||||
$reconcile = array('debit' => true, 'credit' => true);
|
||||
}
|
||||
elseif (isset($reconcile) && $reconcile == 'invoice') {
|
||||
$reconcile = array('credit' => 'invoice');
|
||||
}
|
||||
elseif (isset($reconcile) && $reconcile == 'receipt') {
|
||||
$reconcile = array('debit' => 'receipt');
|
||||
}
|
||||
elseif (!isset($reconcile) || !is_array($reconcile)) {
|
||||
$reconcile = array();
|
||||
}
|
||||
|
||||
// Reconcile the new entry... assume we'll have success
|
||||
$err = false;
|
||||
foreach (array_intersect_key($reconcile, array('credit'=>1,'debit'=>1))
|
||||
AS $dc_type => $reconcile_set) {
|
||||
if (!isset($reconcile_set) || (is_bool($reconcile_set) && !$reconcile_set))
|
||||
continue;
|
||||
|
||||
if ($reconcile_set === 'receipt') {
|
||||
$C = new Customer();
|
||||
$reconciled = $C->reconcileNewLedgerEntry($entry_data['customer_id'],
|
||||
$this->fundamentalOpposite($dc_type),
|
||||
$entry_data['amount']);
|
||||
|
||||
/* pr(array("reconcile receipt", */
|
||||
/* compact('reconciled', 'split_transaction', 'transaction_data'))); */
|
||||
$split_transaction = array_intersect_key($transaction_data,
|
||||
array('Transaction'=>1,
|
||||
'split_transaction_id'=>1));
|
||||
|
||||
if (isset($split_transaction['split_transaction_id']))
|
||||
$split_transaction['transaction_id'] = $split_transaction['split_transaction_id'];
|
||||
|
||||
if (is_array($reconciled) && count($reconciled[$dc_type]['entry'])) {
|
||||
foreach ($reconciled[$dc_type]['entry'] AS $rec) {
|
||||
//pr(compact('rec', 'split_transaction'));
|
||||
if (!$rec['applied'])
|
||||
continue;
|
||||
|
||||
// Create an entry to handle the splitting of the funds ("Payment")
|
||||
// and reconcile against the new cash/check/etc entry created above,
|
||||
// as well as the A/R account.
|
||||
|
||||
// Payment must debit the Receipt ledger, and credit the A/R ledger
|
||||
// debit: Receipt credit: A/R
|
||||
$ids = $this->postLedgerEntry
|
||||
($split_transaction,
|
||||
null,
|
||||
array('debit_ledger_id' => $this->currentLedgerID($this->receiptAccountID()),
|
||||
'credit_ledger_id' => $this->currentLedgerID($this->accountReceivableAccountID()),
|
||||
'amount' => $rec['applied'],
|
||||
'lease_id' => $rec['lease_id'],
|
||||
'customer_id' => $rec['customer_id'],
|
||||
),
|
||||
array('debit' => array(array('LedgerEntry' => array('id' => $new_entry->id,
|
||||
'amount' => $rec['applied']))),
|
||||
'credit' => array(array('LedgerEntry' => array('id' => $rec['id'],
|
||||
'amount' => $rec['applied']))))
|
||||
);
|
||||
// Keep using the same split transaction for all reconciled entries
|
||||
$split_transaction = array_intersect_key($ids, array('transaction_id'=>1));
|
||||
//pr(compact('ids', 'split_transaction'));
|
||||
}
|
||||
|
||||
//pr("end reconciled is array");
|
||||
}
|
||||
|
||||
//pr("end reconcile receipt");
|
||||
}
|
||||
|
||||
if (is_array($reconcile_set)) {
|
||||
//pr("reconcile_set is array");
|
||||
foreach ($reconcile_set AS $reconcile_entry) {
|
||||
if (!isset($reconcile_entry['LedgerEntry']['id']))
|
||||
continue;
|
||||
|
||||
$amount = $reconcile_entry['LedgerEntry']['amount'];
|
||||
if (!$amount)
|
||||
continue;
|
||||
|
||||
if ($dc_type == 'debit') {
|
||||
$debit_ledger_entry_id = $new_entry->id;
|
||||
$credit_ledger_entry_id = $reconcile_entry['LedgerEntry']['id'];
|
||||
}
|
||||
else {
|
||||
$debit_ledger_entry_id = $reconcile_entry['LedgerEntry']['id'];
|
||||
$credit_ledger_entry_id = $new_entry->id;
|
||||
}
|
||||
|
||||
$R = new Reconciliation();
|
||||
$R->create();
|
||||
if (!$R->save(compact('amount',
|
||||
'debit_ledger_entry_id',
|
||||
'credit_ledger_entry_id'), false))
|
||||
$err = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$new_entry->recursive = -1;
|
||||
$new_entry->read();
|
||||
//pr(array('post-save', $entry->data));
|
||||
|
||||
$ret = array
|
||||
('error' => $err,
|
||||
'id' => $new_entry->data['LedgerEntry']['id'],
|
||||
'transaction_id' => $new_entry->data['LedgerEntry']['transaction_id'],
|
||||
'monetary_source_id' => $new_entry->data['LedgerEntry']['monetary_source_id']);
|
||||
|
||||
if (isset($split_transaction['transaction_id']))
|
||||
$ret['split_transaction_id'] = $split_transaction['transaction_id'];
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: closeAndDeposit
|
||||
* - Closes the current set of ledgers, transferring
|
||||
* their balances to specified ledger.
|
||||
*/
|
||||
function closeAndDeposit($set, $deposit_account_id) {
|
||||
|
||||
$close = new Close();
|
||||
$close->create();
|
||||
if (!$close->save(array('stamp' => null, 'comment' => 'Deposit'), false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$transaction = array();
|
||||
foreach ($set AS $ledger) {
|
||||
// REVISIT <AP>: 20090710
|
||||
// If the user said to include a ledger in the
|
||||
// set, should we really be excluding it?
|
||||
if ($ledger['total'] == 0)
|
||||
continue;
|
||||
|
||||
$ids = $this->postLedgerEntry
|
||||
($transaction,
|
||||
null,
|
||||
array('debit_account_id' => $deposit_account_id,
|
||||
'credit_ledger_id' => $ledger['id'],
|
||||
'amount' => $ledger['total']),
|
||||
// Reconcile the account for cash/check/etc,
|
||||
// which is the credit side of this entry.
|
||||
array('credit' => $ledger['entries']));
|
||||
//pr(compact('ids'));
|
||||
|
||||
if ($ids['error'])
|
||||
die("closeAndDeposit : postLedgerEntry returned error!");
|
||||
|
||||
$transaction = array_intersect_key($ids, array('transaction_id'=>1));
|
||||
|
||||
$this->Ledger->closeLedger($ledger['id'], $close->id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
|
||||
@@ -85,6 +85,28 @@ class LinkableBehavior extends ModelBehavior {
|
||||
|
||||
protected $_defaults = array('type' => 'LEFT');
|
||||
|
||||
function pr($lev, $mixed) {
|
||||
if ($lev >= 5)
|
||||
return;
|
||||
|
||||
pr($mixed);
|
||||
return;
|
||||
|
||||
$trace = debug_backtrace(false);
|
||||
//array_shift($trace);
|
||||
$calls = array();
|
||||
foreach ($trace AS $call) {
|
||||
$call = array_intersect_key($call,
|
||||
array('file'=>1,
|
||||
'line'=>1,
|
||||
//'class'=>1,
|
||||
'function'=>1,
|
||||
));
|
||||
$calls[] = implode("; ", $call);
|
||||
}
|
||||
pr(array('debug' => $mixed, 'stack' => $calls));
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a function for made recursive str_replaces in an array
|
||||
* NOTE: The palacement of this function is terrible, but I don't
|
||||
@@ -108,7 +130,10 @@ class LinkableBehavior extends ModelBehavior {
|
||||
}
|
||||
|
||||
public function beforeFind(&$Model, $query) {
|
||||
/* pr("Linkable::beforeFind() begin"); pr($query); */
|
||||
$this->pr(10,
|
||||
array('function' => 'Linkable::beforeFind',
|
||||
'args' => array('Model->alias' => '$Model->alias') + compact('query'),
|
||||
));
|
||||
if (isset($query[$this->_key])) {
|
||||
$optionsDefaults = $this->_defaults + array('reference' =>
|
||||
array('class' => $Model->alias,
|
||||
@@ -132,7 +157,10 @@ class LinkableBehavior extends ModelBehavior {
|
||||
unset($iterator['defaults']);
|
||||
}
|
||||
$iterations = Set::normalize($iterator);
|
||||
/* pr(array('checkpoint' => 'Iterations', compact('iterations'))); */
|
||||
$this->pr(25,
|
||||
array('checkpoint' => 'Iterations',
|
||||
compact('iterations'),
|
||||
));
|
||||
foreach ($iterations as $alias => $options) {
|
||||
if (is_null($options)) {
|
||||
$options = array();
|
||||
@@ -145,7 +173,15 @@ class LinkableBehavior extends ModelBehavior {
|
||||
if (empty($options['class']))
|
||||
$options['class'] = $alias;
|
||||
|
||||
/* pr(array('checkpoint' => 'Begin Model Work', compact('alias', 'options'))); */
|
||||
if (!isset($options['conditions']))
|
||||
$options['conditions'] = array();
|
||||
elseif (!is_array($options['conditions']))
|
||||
$options['conditions'] = array($options['conditions']);
|
||||
|
||||
$this->pr(20,
|
||||
array('checkpoint' => 'Begin Model Work',
|
||||
compact('alias', 'options'),
|
||||
));
|
||||
|
||||
$modelClass = $options['class'];
|
||||
$modelAlias = $options['alias'];
|
||||
@@ -154,11 +190,13 @@ class LinkableBehavior extends ModelBehavior {
|
||||
|
||||
$_Model =& ClassRegistry::init($modelClass); // the incoming model to be linked in query
|
||||
$Reference =& ClassRegistry::init($referenceClass); // the already in query model that links to $_Model
|
||||
/* pr(array('checkpoint' => 'Aliases Established', */
|
||||
/* 'Model' => ($modelAlias .' : '. $modelClass . */
|
||||
/* ' ('. $_Model->alias .' : '. $_Model->name .')'), */
|
||||
/* 'Reference' => ($referenceAlias .' : '. $referenceClass . */
|
||||
/* ' ('. $Reference->alias .' : '. $Reference->name .')'))); */
|
||||
$this->pr(12,
|
||||
array('checkpoint' => 'Aliases Established',
|
||||
'Model' => ($modelAlias .' : '. $modelClass .
|
||||
' ('. $_Model->alias .' : '. $_Model->name .')'),
|
||||
'Reference' => ($referenceAlias .' : '. $referenceClass .
|
||||
' ('. $Reference->alias .' : '. $Reference->name .')'),
|
||||
));
|
||||
|
||||
$db =& $_Model->getDataSource();
|
||||
|
||||
@@ -169,20 +207,20 @@ class LinkableBehavior extends ModelBehavior {
|
||||
// a relationship if one doesn't otherwise already exists.
|
||||
if (($associations = $Reference->getAssociated()) &&
|
||||
isset($associations[$_Model->alias])) {
|
||||
/* pr("Reference defines association to _Model"); */
|
||||
$this->pr(12, array('checkpoint' => "Reference defines association to _Model"));
|
||||
$associatedThroughReference = 1;
|
||||
$type = $associations[$_Model->alias];
|
||||
$association = $Reference->{$type}[$_Model->alias];
|
||||
}
|
||||
elseif (($associations = $_Model->getAssociated()) &&
|
||||
isset($associations[$Reference->alias])) {
|
||||
/* pr("_Model defines association to Reference"); */
|
||||
$this->pr(12, array('checkpoint' => "_Model defines association to Reference"));
|
||||
$type = $associations[$Reference->alias];
|
||||
$association = $_Model->{$type}[$Reference->alias];
|
||||
}
|
||||
else {
|
||||
// No relationship... make our best effort to create one.
|
||||
/* pr("No assocation between _Model and Reference"); */
|
||||
$this->pr(12, array('checkpoint' => "No assocation between _Model and Reference"));
|
||||
$type = 'belongsTo';
|
||||
$_Model->bind($Reference->alias);
|
||||
// Grab the association now, since we'll unbind in a moment.
|
||||
@@ -212,93 +250,119 @@ class LinkableBehavior extends ModelBehavior {
|
||||
$associationAlias,
|
||||
$association['conditions']);
|
||||
|
||||
/* pr(array('checkpoint' => 'Models Established - Check Associations', */
|
||||
/* 'primaryModel' => $primaryAlias .' : '. $primaryModel->name, */
|
||||
/* 'foreignModel' => $foreignAlias .' : '. $foreignModel->name, */
|
||||
/* compact('type', 'association'))); */
|
||||
$this->pr(15,
|
||||
array('checkpoint' => 'Models Established - Check Associations',
|
||||
'primaryModel' => $primaryAlias .' : '. $primaryModel->name,
|
||||
'foreignModel' => $foreignAlias .' : '. $foreignModel->name,
|
||||
compact('type', 'association'),
|
||||
));
|
||||
|
||||
if (empty($options['conditions'])) {
|
||||
if ($type === 'hasAndBelongsToMany') {
|
||||
if (isset($association['with']))
|
||||
$linkClass = $association['with'];
|
||||
else
|
||||
$linkClass = Inflector::classify($association['joinTable']);
|
||||
if ($type === 'hasAndBelongsToMany') {
|
||||
if (isset($association['with']))
|
||||
$linkClass = $association['with'];
|
||||
else
|
||||
$linkClass = Inflector::classify($association['joinTable']);
|
||||
|
||||
$Link =& $_Model->{$linkClass};
|
||||
$Link =& $_Model->{$linkClass};
|
||||
|
||||
if (isset($options['linkalias']))
|
||||
$linkAlias = $options['linkalias'];
|
||||
else
|
||||
$linkAlias = $Link->alias;
|
||||
if (isset($options['linkalias']))
|
||||
$linkAlias = $options['linkalias'];
|
||||
else
|
||||
$linkAlias = $Link->alias;
|
||||
|
||||
// Get the foreign key fields (for the link table) directly from
|
||||
// the defined model associations, if they exists. This is the
|
||||
// users direct specification, and therefore definitive if present.
|
||||
$modelLink = $Link->escapeField($association['foreignKey'], $linkAlias);
|
||||
$referenceLink = $Link->escapeField($association['associationForeignKey'], $linkAlias);
|
||||
$this->pr(17,
|
||||
array('checkpoint' => 'Linking HABTM',
|
||||
compact('linkClass', 'linkAlias'),
|
||||
));
|
||||
|
||||
// If we haven't figured out the foreign keys, see if there is a
|
||||
// model for the link table, and if it has the appropriate
|
||||
// associations with the two tables we're trying to join.
|
||||
if (empty($modelLink) && isset($Link->belongsTo[$_Model->alias]))
|
||||
$modelLink = $Link->escapeField($Link->belongsTo[$_Model->alias]['foreignKey'], $linkAlias);
|
||||
if (empty($referenceLink) && isset($Link->belongsTo[$Reference->alias]))
|
||||
$referenceLink = $Link->escapeField($Link->belongsTo[$Reference->alias]['foreignKey'], $linkAlias);
|
||||
// Get the foreign key fields (for the link table) directly from
|
||||
// the defined model associations, if they exists. This is the
|
||||
// users direct specification, and therefore definitive if present.
|
||||
$modelLink = $Link->escapeField($association['foreignKey'], $linkAlias);
|
||||
$referenceLink = $Link->escapeField($association['associationForeignKey'], $linkAlias);
|
||||
|
||||
// We're running quite thin here. None of the models spell
|
||||
// out the appropriate linkages. We'll have to SWAG it.
|
||||
if (empty($modelLink))
|
||||
$modelLink = $Link->escapeField(Inflector::underscore($_Model->alias) . '_id', $linkAlias);
|
||||
if (empty($referenceLink))
|
||||
$referenceLink = $Link->escapeField(Inflector::underscore($Reference->alias) . '_id', $linkAlias);
|
||||
// If we haven't figured out the foreign keys, see if there is a
|
||||
// model for the link table, and if it has the appropriate
|
||||
// associations with the two tables we're trying to join.
|
||||
if (empty($modelLink) && isset($Link->belongsTo[$_Model->alias]))
|
||||
$modelLink = $Link->escapeField($Link->belongsTo[$_Model->alias]['foreignKey'], $linkAlias);
|
||||
if (empty($referenceLink) && isset($Link->belongsTo[$Reference->alias]))
|
||||
$referenceLink = $Link->escapeField($Link->belongsTo[$Reference->alias]['foreignKey'], $linkAlias);
|
||||
|
||||
// Get the primary key from the tables we're joining.
|
||||
$referenceKey = $Reference->escapeField(null, $referenceAlias);
|
||||
$modelKey = $_Model->escapeField(null, $modelAlias);
|
||||
// We're running quite thin here. None of the models spell
|
||||
// out the appropriate linkages. We'll have to SWAG it.
|
||||
if (empty($modelLink))
|
||||
$modelLink = $Link->escapeField(Inflector::underscore($_Model->alias) . '_id', $linkAlias);
|
||||
if (empty($referenceLink))
|
||||
$referenceLink = $Link->escapeField(Inflector::underscore($Reference->alias) . '_id', $linkAlias);
|
||||
|
||||
// Get the primary key from the tables we're joining.
|
||||
$referenceKey = $Reference->escapeField(null, $referenceAlias);
|
||||
$modelKey = $_Model->escapeField(null, $modelAlias);
|
||||
|
||||
// Join the linkage table to our model. We'll use an inner join,
|
||||
// as the whole purpose of the linkage table is to make this
|
||||
// connection. As we are embedding this join, the INNER will not
|
||||
// cause any problem with the overall query, should the user not
|
||||
// be concerned with whether or not the join has any results.
|
||||
// They control that with the 'type' parameter which will be at
|
||||
// the top level join.
|
||||
$options['joins'][] = array('type' => 'INNER',
|
||||
'alias' => $modelAlias,
|
||||
'conditions' => "{$modelKey} = {$modelLink}",
|
||||
'table' => $db->fullTableName($_Model, true));
|
||||
|
||||
// Now for the top level join. This will be added into the list
|
||||
// of joins down below, outside of the HABTM specific code.
|
||||
$options['class'] = $linkClass;
|
||||
$options['alias'] = $linkAlias;
|
||||
$options['table'] = $Link->getDataSource()->fullTableName($Link);
|
||||
$options['conditions'][] = "{$referenceLink} = {$referenceKey}";
|
||||
}
|
||||
elseif (isset($association['foreignKey']) && $association['foreignKey']) {
|
||||
$foreignKey = $primaryModel->escapeField($association['foreignKey'], $primaryAlias);
|
||||
$primaryKey = $foreignModel->escapeField($foreignModel->primaryKey, $foreignAlias);
|
||||
|
||||
// Only differentiating to help show the logical flow.
|
||||
// Either way works and this test can be tossed out
|
||||
if (($type === 'hasMany' || $type === 'hasOne') ^ $associatedThroughReference)
|
||||
$options['conditions'][] = "{$primaryKey} = {$foreignKey}";
|
||||
else
|
||||
$options['conditions'][] = "{$foreignKey} = {$primaryKey}";
|
||||
}
|
||||
else {
|
||||
// No Foreign Key... nothing we can do.
|
||||
$options['conditions'] = array();
|
||||
}
|
||||
|
||||
// The user may have specified conditions directly in the model
|
||||
// for this join. Make sure to adhere to those conditions.
|
||||
if (isset($association['conditions']) && is_array($association['conditions']))
|
||||
$options['conditions'] = array_merge($options['conditions'], $association['conditions']);
|
||||
elseif (!empty($association['conditions']))
|
||||
$options['conditions'][] = $association['conditions'];
|
||||
// Join the linkage table to our model. We'll use an inner join,
|
||||
// as the whole purpose of the linkage table is to make this
|
||||
// connection. As we are embedding this join, the INNER will not
|
||||
// cause any problem with the overall query, should the user not
|
||||
// be concerned with whether or not the join has any results.
|
||||
// They control that with the 'type' parameter which will be at
|
||||
// the top level join.
|
||||
$options['joins'][] = array('type' => 'INNER',
|
||||
'alias' => $modelAlias,
|
||||
'conditions' => "{$modelKey} = {$modelLink}",
|
||||
'table' => $db->fullTableName($_Model, true));
|
||||
|
||||
// Now for the top level join. This will be added into the list
|
||||
// of joins down below, outside of the HABTM specific code.
|
||||
$options['class'] = $linkClass;
|
||||
$options['alias'] = $linkAlias;
|
||||
$options['table'] = $Link->getDataSource()->fullTableName($Link);
|
||||
$options['conditions'][] = "{$referenceLink} = {$referenceKey}";
|
||||
}
|
||||
elseif (isset($association['foreignKey']) && $association['foreignKey']) {
|
||||
$foreignKey = $primaryModel->escapeField($association['foreignKey'], $primaryAlias);
|
||||
$primaryKey = $foreignModel->escapeField($foreignModel->primaryKey, $foreignAlias);
|
||||
|
||||
$this->pr(17,
|
||||
array('checkpoint' => 'Linking due to foreignKey',
|
||||
compact('foreignKey', 'primaryKey'),
|
||||
));
|
||||
|
||||
// Only differentiating to help show the logical flow.
|
||||
// Either way works and this test can be tossed out
|
||||
if (($type === 'hasMany' || $type === 'hasOne') ^ $associatedThroughReference)
|
||||
$options['conditions'][] = "{$primaryKey} = {$foreignKey}";
|
||||
else
|
||||
$options['conditions'][] = "{$foreignKey} = {$primaryKey}";
|
||||
}
|
||||
else {
|
||||
$this->pr(17,
|
||||
array('checkpoint' => 'Linking with no logic (expecting user defined)',
|
||||
));
|
||||
|
||||
// No Foreign Key... nothing we can do.
|
||||
}
|
||||
|
||||
$this->pr(19,
|
||||
array('checkpoint' => 'Conditions',
|
||||
array('options[conditions]' => $options['conditions'],
|
||||
'association[conditions]' => $association['conditions'],
|
||||
),
|
||||
));
|
||||
|
||||
// The user may have specified conditions directly in the model
|
||||
// for this join. Make sure to adhere to those conditions.
|
||||
if (isset($association['conditions']) && is_array($association['conditions']))
|
||||
$options['conditions'] = array_merge($options['conditions'], $association['conditions']);
|
||||
elseif (!empty($association['conditions']))
|
||||
$options['conditions'][] = $association['conditions'];
|
||||
|
||||
$this->pr(19,
|
||||
array('checkpoint' => 'Conditions2',
|
||||
array('options[conditions]' => $options['conditions'],
|
||||
),
|
||||
));
|
||||
|
||||
if (empty($options['table'])) {
|
||||
$options['table'] = $db->fullTableName($_Model, true);
|
||||
}
|
||||
@@ -312,26 +376,31 @@ class LinkableBehavior extends ModelBehavior {
|
||||
(empty($association['fields'])
|
||||
? array() : $db->fields($_Model, $modelAlias, $association['fields'])));
|
||||
|
||||
/* pr(array('checkpoint' => 'Model Work Complete', compact('options', 'modelClass', 'modelAlias'))); */
|
||||
|
||||
$options[$this->_key] = am($options[$this->_key], array_diff_key($options, $optionsKeys));
|
||||
$options = array_intersect_key($options, $optionsKeys);
|
||||
if (!empty($options[$this->_key])) {
|
||||
$iterators[] = $options[$this->_key] +
|
||||
array('defaults' =>
|
||||
array_merge($defaults,
|
||||
array_merge($defaults,
|
||||
array('reference' =>
|
||||
array('class' => $modelClass,
|
||||
'alias' => $modelAlias))));
|
||||
}
|
||||
$query['joins'][] = array_intersect_key($options, array('type' => true, 'alias' => true, 'table' => true, 'joins' => true, 'conditions' => true));
|
||||
|
||||
$this->pr(19,
|
||||
array('checkpoint' => 'Model Join Complete',
|
||||
compact('options', 'modelClass', 'modelAlias', 'query'),
|
||||
));
|
||||
}
|
||||
++$cont;
|
||||
$notDone = isset($iterators[$cont]);
|
||||
} while ($notDone);
|
||||
}
|
||||
/* pr(array('checkpoint' => 'Linkable::beforeFind() end', */
|
||||
/* compact('query'))); */
|
||||
$this->pr(20,
|
||||
array('function' => 'Linkable::beforeFind',
|
||||
'return' => compact('query'),
|
||||
));
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
12
site/models/close.php
Normal file
12
site/models/close.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
class Close extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'Ledger',
|
||||
);
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
class Contact extends AppModel {
|
||||
|
||||
var $name = 'Contact';
|
||||
var $displayField = 'display_name';
|
||||
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'display_name' => array('notempty'),
|
||||
@@ -9,13 +10,18 @@ class Contact extends AppModel {
|
||||
'id_exp' => array('date')
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'ContactsMethod',
|
||||
'ContactsCustomer',
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
'Customer',
|
||||
'ContactAddress' => array(
|
||||
'joinTable' => 'contacts_methods',
|
||||
'associationForeignKey' => 'method_id',
|
||||
'unique' => true,
|
||||
'conditions' => "method = 'POST'",
|
||||
'conditions' => "method = 'ADDRESS'",
|
||||
),
|
||||
'ContactPhone' => array(
|
||||
'joinTable' => 'contacts_methods',
|
||||
@@ -31,5 +37,100 @@ class Contact extends AppModel {
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: saveContact
|
||||
* - Saves the contact and related data
|
||||
*/
|
||||
|
||||
function saveContact($id, $data) {
|
||||
|
||||
// Establish a display name if not already given
|
||||
if (!$data['Contact']['display_name'])
|
||||
$data['Contact']['display_name'] =
|
||||
(($data['Contact']['first_name'] &&
|
||||
$data['Contact']['last_name'])
|
||||
? $data['Contact']['last_name'] . ', ' . $data['Contact']['first_name']
|
||||
: ($data['Contact']['first_name']
|
||||
? $data['Contact']['first_name']
|
||||
: $data['Contact']['last_name']));
|
||||
|
||||
// Save the contact data
|
||||
$this->create();
|
||||
if ($id)
|
||||
$this->id = $id;
|
||||
if (!$this->save($data, false)) {
|
||||
return false;
|
||||
}
|
||||
$id = $this->id;
|
||||
|
||||
// Remove all associated ContactMethods, as it ensures
|
||||
// any entries deleted by the user actually get deleted
|
||||
// in the system. We'll recreate the needed ones anyway.
|
||||
// REVISIT <AP>: 20090706
|
||||
// Appears that $this->save() is already doing the
|
||||
// delete. I would have thought this would only happen
|
||||
// on a saveAll??
|
||||
/* $this->ContactsMethod->deleteAll */
|
||||
/* (array('contact_id' => $id), false); */
|
||||
|
||||
// At this point, since we've saved data to contact,
|
||||
// we'll proceed forward as much as possible, even
|
||||
// if we encounter an error. For now, we'll assume
|
||||
// the operation will succeed.
|
||||
$ret = true;
|
||||
|
||||
// Iterate each type of contact method, adding them into
|
||||
// the database as needed and associating with this contact.
|
||||
foreach (array('phone', 'address', 'email') AS $type) {
|
||||
$class = 'Contact' . ucfirst($type);
|
||||
$enum = strtoupper($type);
|
||||
|
||||
// Nothing to do if this contact method isn't used
|
||||
if (!isset($data[$class]))
|
||||
continue;
|
||||
|
||||
// Go through each entry of this contact method
|
||||
foreach ($data[$class] AS &$item) {
|
||||
|
||||
// If the user has entered all new data, we need to
|
||||
// save that as a brand new entry.
|
||||
if (!isset($item['id'])) {
|
||||
$I = new $class();
|
||||
$I->create();
|
||||
if (!$I->save($item, false)) {
|
||||
$ret = false;
|
||||
continue;
|
||||
}
|
||||
$item['id'] = $I->id;
|
||||
}
|
||||
|
||||
// Update the ContactsMethod to reflect the appropriate IDs
|
||||
$item['ContactsMethod']['contact_id'] = $id;
|
||||
$item['ContactsMethod']['method_id'] = $item['id'];
|
||||
$item['ContactsMethod']['method'] = $enum;
|
||||
|
||||
// Save the relationship between contact and phone/email/address
|
||||
$CM = new ContactsMethod();
|
||||
if (!$CM->save($item['ContactsMethod'], false)) {
|
||||
$ret = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the result
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function contactList() {
|
||||
return $this->find('list',
|
||||
array('order' =>
|
||||
//array('last_name', 'first_name', 'middle_name'),
|
||||
array('display_name'),
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -7,6 +7,13 @@ class ContactAddress extends AppModel {
|
||||
'postcode' => array('postal')
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'ContactsMethod' => array(
|
||||
'foreignKey' => 'method_id',
|
||||
'conditions' => "method = 'ADDRESS'",
|
||||
)
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
'Contact' => array(
|
||||
'className' => 'Contact',
|
||||
@@ -14,8 +21,26 @@ class ContactAddress extends AppModel {
|
||||
'foreignKey' => 'method_id',
|
||||
'associationForeignKey' => 'contact_id',
|
||||
'unique' => true,
|
||||
'conditions' => "method = 'POST'",
|
||||
'conditions' => "method = 'ADDRESS'",
|
||||
)
|
||||
);
|
||||
|
||||
function addressList() {
|
||||
$results = $this->find('all',
|
||||
array('contain' => false,
|
||||
'fields' => array('id', 'address', 'city', 'state', 'postcode'),
|
||||
'order' => array('state', 'city', 'postcode', 'address')));
|
||||
|
||||
$list = array();
|
||||
foreach ($results as $key => $val) {
|
||||
$list[$val['ContactAddress']['id']]
|
||||
= preg_replace("/\n/", ", ", $val['ContactAddress']['address'])
|
||||
. ', ' . $val['ContactAddress']['city']
|
||||
. ', ' . $val['ContactAddress']['state']
|
||||
. ' ' . $val['ContactAddress']['postcode']
|
||||
;
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -2,6 +2,7 @@
|
||||
class ContactEmail extends AppModel {
|
||||
|
||||
var $name = 'ContactEmail';
|
||||
var $displayField = 'email';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'email' => array('email')
|
||||
@@ -18,5 +19,9 @@ class ContactEmail extends AppModel {
|
||||
)
|
||||
);
|
||||
|
||||
function emailList() {
|
||||
return $this->find('list');
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
class ContactPhone extends AppModel {
|
||||
|
||||
var $name = 'ContactPhone';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
//'type' => array('inlist'),
|
||||
@@ -9,6 +8,13 @@ class ContactPhone extends AppModel {
|
||||
'ext' => array('numeric')
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'ContactsMethod' => array(
|
||||
'foreignKey' => 'method_id',
|
||||
'conditions' => "method = 'PHONE'",
|
||||
)
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
'Contact' => array(
|
||||
'className' => 'Contact',
|
||||
@@ -20,5 +26,21 @@ class ContactPhone extends AppModel {
|
||||
)
|
||||
);
|
||||
|
||||
function phoneList() {
|
||||
$results = $this->find('all',
|
||||
array('contain' => false,
|
||||
'fields' => array('id', 'phone', 'ext'),
|
||||
'order' => array('phone', 'ext')));
|
||||
|
||||
App::Import('Helper', 'Format');
|
||||
$list = array();
|
||||
foreach ($results as $key => $val) {
|
||||
$list[$val['ContactPhone']['id']]
|
||||
= FormatHelper::phone($val['ContactPhone']['phone'],
|
||||
$val['ContactPhone']['ext']);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
11
site/models/contacts_customer.php
Normal file
11
site/models/contacts_customer.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
class ContactsCustomer extends AppModel {
|
||||
var $primaryKey = false;
|
||||
|
||||
var $belongsTo = array(
|
||||
'Contact',
|
||||
'Customer',
|
||||
);
|
||||
|
||||
}
|
||||
?>
|
||||
25
site/models/contacts_method.php
Normal file
25
site/models/contacts_method.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
class ContactsMethod extends AppModel {
|
||||
var $primaryKey = false;
|
||||
|
||||
var $belongsTo = array(
|
||||
'Contact',
|
||||
'ContactAddress' => array(
|
||||
'foreignKey' => 'method_id',
|
||||
'conditions' => "method = 'ADDRESS'",
|
||||
//'unique' => true,
|
||||
),
|
||||
'ContactPhone' => array(
|
||||
'foreignKey' => 'method_id',
|
||||
'conditions' => "method = 'PHONE'",
|
||||
//'unique' => true,
|
||||
),
|
||||
'ContactEmail' => array(
|
||||
'foreignKey' => 'method_id',
|
||||
'conditions' => "method = 'EMAIL'",
|
||||
//'unique' => true,
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -20,9 +20,7 @@ class Customer extends AppModel {
|
||||
),
|
||||
'Lease',
|
||||
'LedgerEntry',
|
||||
|
||||
// Cheat to get Account set as part of this class
|
||||
'Account',
|
||||
'ContactsCustomer',
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
@@ -86,9 +84,9 @@ class Customer extends AppModel {
|
||||
/* 'args' => compact('id', 'link'), */
|
||||
/* )); */
|
||||
|
||||
$entries = $this->Account->findLedgerEntriesRelatedToAccount
|
||||
($this->Account->invoiceAccountID(),
|
||||
$this->Account->securityDepositAccountID(),
|
||||
$A = new Account();
|
||||
$entries = $A->findLedgerEntries
|
||||
($A->securityDepositAccountID(),
|
||||
true, array('LedgerEntry.customer_id' => $id), $link);
|
||||
|
||||
/* pr(array('function' => 'Customer::findSecurityDeposits', */
|
||||
@@ -110,8 +108,9 @@ class Customer extends AppModel {
|
||||
*/
|
||||
|
||||
function findUnreconciledLedgerEntries($id = null, $fundamental_type = null) {
|
||||
$unreconciled = $this->Account->findUnreconciledLedgerEntries
|
||||
($this->Account->accountReceivableAccountID(),
|
||||
$A = new Account();
|
||||
$unreconciled = $A->findUnreconciledLedgerEntries
|
||||
($A->accountReceivableAccountID(),
|
||||
$fundamental_type,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
|
||||
@@ -134,8 +133,9 @@ class Customer extends AppModel {
|
||||
*/
|
||||
|
||||
function reconcileNewLedgerEntry($id, $fundamental_type, $amount) {
|
||||
$reconciled = $this->Account->reconcileNewLedgerEntry
|
||||
($this->Account->accountReceivableAccountID(),
|
||||
$A = new Account();
|
||||
$reconciled = $A->reconcileNewLedgerEntry
|
||||
($A->accountReceivableAccountID(),
|
||||
$fundamental_type,
|
||||
$amount,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
@@ -158,7 +158,8 @@ class Customer extends AppModel {
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Contact' =>
|
||||
array(// Models
|
||||
array('order' => array('Contact.display_name'),
|
||||
// Models
|
||||
'ContactPhone',
|
||||
'ContactEmail',
|
||||
'ContactAddress',
|
||||
@@ -184,6 +185,82 @@ class Customer extends AppModel {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: saveCustomer
|
||||
* - Saves the customer and related data
|
||||
*/
|
||||
|
||||
function saveCustomer($id, $data, $primary_contact_entry) {
|
||||
|
||||
// Go through each contact, and create new ones as needed
|
||||
foreach ($data['Contact'] AS &$contact) {
|
||||
if (isset($contact['id']))
|
||||
continue;
|
||||
|
||||
$I = new Contact();
|
||||
$I->create();
|
||||
if (!$I->save($contact, false)) {
|
||||
return false;
|
||||
}
|
||||
$contact['id'] = $I->id;
|
||||
}
|
||||
|
||||
// Set the primary contact ID based on caller selection
|
||||
$data['Customer']['primary_contact_id']
|
||||
= $data['Contact'][$primary_contact_entry]['id'];
|
||||
|
||||
// Provide a default customer name if not specified
|
||||
if (!$data['Customer']['name']) {
|
||||
$this->Contact->recursive = -1;
|
||||
$pcontact = $this->Contact->read(null, $data['Customer']['primary_contact_id']);
|
||||
$data['Customer']['name'] = $pcontact['Contact']['display_name'];
|
||||
}
|
||||
|
||||
// Save the customer data
|
||||
$this->create();
|
||||
if ($id)
|
||||
$this->id = $id;
|
||||
if (!$this->save($data, false)) {
|
||||
return false;
|
||||
}
|
||||
$id = $this->id;
|
||||
|
||||
// Remove all associated Customer Contacts, as it ensures
|
||||
// any entries deleted by the user actually get deleted
|
||||
// in the system. We'll recreate the needed ones anyway.
|
||||
// REVISIT <AP>: 20090706
|
||||
// Appears that $this->save() is already doing the
|
||||
// delete. I would have thought this would only happen
|
||||
// on a saveAll??
|
||||
/* $this->ContactsCustomer->deleteAll */
|
||||
/* (array('customer_id' => $id), false); */
|
||||
|
||||
// At this point, since we've saved data to customer,
|
||||
// we'll proceed forward as much as possible, even
|
||||
// if we encounter an error. For now, we'll assume
|
||||
// the operation will succeed.
|
||||
$ret = true;
|
||||
|
||||
// Go through each entry of this customer method
|
||||
foreach ($data['Contact'] AS &$contact) {
|
||||
// Update the ContactsCustomer to reflect the appropriate IDs
|
||||
$contact['ContactsCustomer']['customer_id'] = $id;
|
||||
$contact['ContactsCustomer']['contact_id'] = $contact['id'];
|
||||
|
||||
// Save the relationship between customer and contact
|
||||
$CM = new ContactsCustomer();
|
||||
if (!$CM->save($contact['ContactsCustomer'], false)) {
|
||||
$ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the result
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -195,8 +272,9 @@ class Customer extends AppModel {
|
||||
if (!$id)
|
||||
return null;
|
||||
|
||||
$stats = $this->Account->stats($this->Account->accountReceivableAccountID(), true,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
$A = new Account();
|
||||
$stats = $A->stats($A->accountReceivableAccountID(), true,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
|
||||
// Pull to the top level and return
|
||||
$stats = $stats['Ledger'];
|
||||
|
||||
@@ -17,9 +17,9 @@ class Lease extends AppModel {
|
||||
'notice_received_date' => array('date'),
|
||||
'close_date' => array('date'),
|
||||
'deposit' => array('money'),
|
||||
'amount' => array('money'),
|
||||
'next_amount' => array('money'),
|
||||
'next_amount_date' => array('date')
|
||||
'rent' => array('money'),
|
||||
'next_rent' => array('money'),
|
||||
'next_rent_date' => array('date')
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
@@ -31,9 +31,6 @@ class Lease extends AppModel {
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry',
|
||||
|
||||
// Cheat to get Account set as part of this class
|
||||
'Account',
|
||||
);
|
||||
|
||||
|
||||
@@ -44,7 +41,8 @@ class Lease extends AppModel {
|
||||
* - Returns the accountId of the given lease
|
||||
*/
|
||||
function accountId($id) {
|
||||
return $this->Account->invoiceAccountID();
|
||||
$A = new Account();
|
||||
return $A->invoiceAccountID();
|
||||
}
|
||||
|
||||
|
||||
@@ -64,8 +62,9 @@ class Lease extends AppModel {
|
||||
$cond = array();
|
||||
$cond[] = array('LedgerEntry.lease_id' => $id);
|
||||
|
||||
$entries = $this->Account->findLedgerEntries($this->accountId($id),
|
||||
$all, $cond, $link);
|
||||
$A = new Account();
|
||||
$entries = $A->findLedgerEntries($this->accountId($id),
|
||||
$all, $cond, $link);
|
||||
|
||||
/* pr(array('function' => 'Lease::findAccountEntries', */
|
||||
/* 'args' => compact('id', 'all', 'cond', 'link'), */
|
||||
@@ -87,9 +86,9 @@ class Lease extends AppModel {
|
||||
/* 'args' => compact('id', 'link'), */
|
||||
/* )); */
|
||||
|
||||
$entries = $this->Account->findLedgerEntriesRelatedToAccount
|
||||
($this->accountId($id),
|
||||
$this->Account->securityDepositAccountID(),
|
||||
$A = new Account();
|
||||
$entries = $A->findLedgerEntries
|
||||
($A->securityDepositAccountID(),
|
||||
true, array('LedgerEntry.lease_id' => $id), $link);
|
||||
|
||||
/* pr(array('function' => 'Lease::findSecurityDeposits', */
|
||||
@@ -110,7 +109,8 @@ class Lease extends AppModel {
|
||||
*/
|
||||
|
||||
function findUnreconciledLedgerEntries($id = null, $fundamental_type = null) {
|
||||
return $this->Account->findUnreconciledLedgerEntries
|
||||
$A = new Account();
|
||||
return $A->findUnreconciledLedgerEntries
|
||||
($this->accountId($id), $fundamental_type, array('LedgerEntry.lease_id' => $id));
|
||||
}
|
||||
|
||||
@@ -130,11 +130,358 @@ class Lease extends AppModel {
|
||||
*/
|
||||
|
||||
function reconcileNewLedgerEntry($id, $fundamental_type, $amount) {
|
||||
return $this->Account->reconcileNewLedgerEntry
|
||||
$A = new Account();
|
||||
return $A->reconcileNewLedgerEntry
|
||||
($this->accountId($id), $fundamental_type, $amount, array('LedgerEntry.lease_id' => $id));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: rentLastCharges
|
||||
* - Returns a list of rent charges from this lease that
|
||||
* do not have sequential followup charges. Under normal
|
||||
* circumstances, there would only be one entry, which is
|
||||
* the most recent rent charge. However, it's possible
|
||||
* that there are several, indicating a problem with lease.
|
||||
*/
|
||||
|
||||
function rentLastCharges($id) {
|
||||
$A = new Account();
|
||||
$entries = $this->find
|
||||
('all',
|
||||
array('link' =>
|
||||
array(// Models
|
||||
'LedgerEntry' => array
|
||||
('Ledger' => array
|
||||
('fields' => array(),
|
||||
'Account' => array
|
||||
('fields' => array(),
|
||||
'Ledger' => array
|
||||
('alias' => 'Lx',
|
||||
'fields' => array(),
|
||||
'LedgerEntry' => array
|
||||
('alias' => 'LEx',
|
||||
'fields' => array(),
|
||||
'conditions' => array
|
||||
('LEx.effective_date = DATE_ADD(LedgerEntry.through_date, INTERVAL 1 day)',
|
||||
'LEx.lease_id = LedgerEntry.lease_id',
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
//'fields' => array('id', 'amount', 'effective_date', 'through_date'),
|
||||
'fields' => array(),
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
array('Account.id' => $A->rentAccountID()),
|
||||
array('LEx.id' => null),
|
||||
),
|
||||
)
|
||||
);
|
||||
return $entries;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: rentChargeGaps
|
||||
* - Checks for gaps in rent charges
|
||||
*/
|
||||
|
||||
function rentChargeGaps($id) {
|
||||
$entries = $this->rentLastCharges($id);
|
||||
if ($entries && count($entries) > 1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: rentChargeThrough
|
||||
* - Determines the date that rent has been charged through
|
||||
* Returns one of:
|
||||
* null: There are gaps in the charges
|
||||
* false: There are not yet any charges
|
||||
* date: The date rent has been charged through
|
||||
*/
|
||||
|
||||
function rentChargeThrough($id) {
|
||||
$entries = $this->rentLastCharges($id);
|
||||
if (!$entries)
|
||||
return false;
|
||||
if (count($entries) != 1)
|
||||
return null;
|
||||
return $entries[0]['LedgerEntry']['through_date'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: rentPaidThrough
|
||||
* - Determines the date of the first unpaid rent
|
||||
*/
|
||||
|
||||
function rentPaidThrough($id) {
|
||||
|
||||
// Income / Receipt / Money
|
||||
// debit: A/R credit: Income <-- this entry
|
||||
// debit: Receipt credit: A/R <-- ReceiptLedgerEntry, below
|
||||
// debit: Money credit: Receipt <-- MoneyLedgerEntry, below
|
||||
|
||||
$query = array
|
||||
('link' => array
|
||||
(
|
||||
'CreditLedger' =>
|
||||
array('fields' => array(),
|
||||
'Account' =>
|
||||
array('fields' => array(),
|
||||
),
|
||||
),
|
||||
|
||||
// We're searching for the Receipt<->A/R entries,
|
||||
// which are debits on the A/R account. Find the
|
||||
// reconciling entries to that A/R debit.
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
array('alias' => 'ReceiptLedgerEntry',
|
||||
'fields' => array(),
|
||||
|
||||
// Finally, the Money (Cash/Check/etc) Entry is the one
|
||||
// which reconciles our ReceiptLedgerEntry debit
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
array('alias' => 'MoneyLedgerEntry',
|
||||
'linkalias' => 'MoneyLedgerEntryR',
|
||||
'fields' => array('SUM(COALESCE(MoneyLedgerEntryR.amount,0)) AS paid'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
'fields' => array('LedgerEntry.amount',
|
||||
'DATE_SUB(LedgerEntry.effective_date, INTERVAL 1 DAY) AS paid_through',
|
||||
),
|
||||
|
||||
'group' => 'LedgerEntry.id HAVING paid <> LedgerEntry.amount',
|
||||
|
||||
'conditions' => array(array('LedgerEntry.lease_id' => $id),
|
||||
array('Account.id' => $this->LedgerEntry->Ledger->Account->rentAccountID()),
|
||||
),
|
||||
'order' => array('LedgerEntry.effective_date',
|
||||
),
|
||||
);
|
||||
|
||||
$rent = $this->LedgerEntry->find('first', $query);
|
||||
if ($rent)
|
||||
return $rent[0]['paid_through'];
|
||||
|
||||
$query['fields'] = 'LedgerEntry.through_date';
|
||||
$query['order'] = 'LedgerEntry.through_date DESC';
|
||||
$query['group'] = 'LedgerEntry.id';
|
||||
$rent = $this->LedgerEntry->find('first', $query);
|
||||
if ($rent)
|
||||
return $rent['LedgerEntry']['through_date'];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: moveIn
|
||||
* - Moves the specified customer into the specified lease
|
||||
*/
|
||||
|
||||
function moveIn($customer_id, $unit_id,
|
||||
$deposit = null, $rent = null,
|
||||
$stamp = null, $comment = null) {
|
||||
|
||||
$lt = $this->LeaseType->find('first',
|
||||
array('conditions' =>
|
||||
array('code' => 'SL')));
|
||||
|
||||
// Use NOW if not given a movein date
|
||||
if (!isset($stamp))
|
||||
$stamp = date('Y-m-d G:i:s');
|
||||
|
||||
if (!$comment)
|
||||
$comment = null;
|
||||
|
||||
if (!isset($deposit) || !isset($rent)) {
|
||||
$rates = $this->Unit->find
|
||||
('first',
|
||||
array('contain' =>
|
||||
array('UnitSize' =>
|
||||
array('deposit', 'rent'),
|
||||
),
|
||||
'fields' => array('deposit', 'rent'),
|
||||
'conditions' => array('Unit.id' => $unit_id),
|
||||
));
|
||||
|
||||
$deposit =
|
||||
(isset($deposit)
|
||||
? $deposit
|
||||
: (isset($rates['Unit']['deposit'])
|
||||
? $rates['Unit']['deposit']
|
||||
: (isset($rates['UnitSize']['deposit'])
|
||||
? $rates['UnitSize']['deposit']
|
||||
: 0)));
|
||||
|
||||
$rent =
|
||||
(isset($rent)
|
||||
? $rent
|
||||
: (isset($rates['Unit']['rent'])
|
||||
? $rates['Unit']['rent']
|
||||
: (isset($rates['UnitSize']['rent'])
|
||||
? $rates['UnitSize']['rent']
|
||||
: 0)));
|
||||
}
|
||||
|
||||
|
||||
// Save this new lease.
|
||||
$this->create();
|
||||
if (!$this->save(array('lease_type_id' => $lt['LeaseType']['id'],
|
||||
'unit_id' => $unit_id,
|
||||
'customer_id' => $customer_id,
|
||||
'lease_date' => $stamp,
|
||||
'movein_date' => $stamp,
|
||||
'deposit' => $deposit,
|
||||
'rent' => $rent,
|
||||
'comment' => $comment), false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set the lease number to be the same as the lease ID
|
||||
$this->id;
|
||||
$this->saveField('number', $this->id);
|
||||
|
||||
// Update the unit status
|
||||
$this->Unit->updateStatus($unit_id, 'OCCUPIED');
|
||||
|
||||
// REVISIT <AP>: 20090702
|
||||
// We need to assess the security deposit charge,
|
||||
// and probably rent as well. Rent, however, will
|
||||
// require user parameters to indicate whether it
|
||||
// was waived, pro-rated, etc.
|
||||
|
||||
// Return the new lease ID
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: moveOut
|
||||
* - Moves the customer out of the specified lease
|
||||
*/
|
||||
|
||||
function moveOut($id, $status = 'VACANT',
|
||||
$stamp = null, $close = false) {
|
||||
// Use NOW if not given a moveout date
|
||||
if (!isset($stamp))
|
||||
$stamp = date('Y-m-d G:i:s');
|
||||
|
||||
// Reset the data
|
||||
$this->create();
|
||||
$this->id = $id;
|
||||
|
||||
// Set the customer move-out date
|
||||
$this->data['Lease']['moveout_date'] = $stamp;
|
||||
|
||||
// Save it!
|
||||
$this->save($this->data, false);
|
||||
|
||||
// Close the lease, if so requested
|
||||
if ($close)
|
||||
$this->close($id, $stamp);
|
||||
|
||||
// Finally, update the unit status
|
||||
$this->recursive = -1;
|
||||
$this->read();
|
||||
$this->Unit->updateStatus($this->data['Lease']['unit_id'], $status);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: close
|
||||
* - Closes the lease to further action
|
||||
*/
|
||||
|
||||
function close($id, $stamp = null) {
|
||||
if (!$this->closeable($id))
|
||||
return false;
|
||||
|
||||
// Reset the data
|
||||
$this->create();
|
||||
$this->id = $id;
|
||||
|
||||
// Use NOW if not given a moveout date
|
||||
if (!isset($stamp))
|
||||
$stamp = date('Y-m-d G:i:s');
|
||||
|
||||
// Set the close date
|
||||
$this->data['Lease']['close_date'] = $stamp;
|
||||
|
||||
// Save it!
|
||||
$this->save($this->data, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: closeable
|
||||
* - Indicates whether or not the lease can be closed
|
||||
*/
|
||||
|
||||
function closeable($id) {
|
||||
$this->recursive = -1;
|
||||
$this->read(null, $id);
|
||||
|
||||
// We can't close a lease that's still in use
|
||||
if (!isset($this->data['Lease']['moveout_date']))
|
||||
return false;
|
||||
|
||||
// We can't close a lease that's already closed
|
||||
if (isset($this->data['Lease']['close_date']))
|
||||
return false;
|
||||
|
||||
$deposits = $this->findSecurityDeposits($id);
|
||||
$stats = $this->stats($id);
|
||||
|
||||
// A lease can only be closed if there are no outstanding
|
||||
// security deposits, and if the account balance is zero.
|
||||
if ($deposits['summary']['balance'] != 0 || $stats['balance'] != 0)
|
||||
return false;
|
||||
|
||||
// Apparently this lease meets all the criteria!
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addCharge
|
||||
* - Adds an additional charge to the lease
|
||||
*/
|
||||
|
||||
function addCharge($id, $charge) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -146,8 +493,9 @@ class Lease extends AppModel {
|
||||
if (!$id)
|
||||
return null;
|
||||
|
||||
$stats = $this->Account->stats($this->Account->accountReceivableAccountID(), true,
|
||||
array('LedgerEntry.lease_id' => $id));
|
||||
$A = new Account();
|
||||
$stats = $A->stats($A->accountReceivableAccountID(), true,
|
||||
array('LedgerEntry.lease_id' => $id));
|
||||
|
||||
// Pull to the top level and return
|
||||
$stats = $stats['Ledger'];
|
||||
|
||||
@@ -9,6 +9,8 @@ class Ledger extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
'Account',
|
||||
'PriorLedger' => array('className' => 'Ledger'),
|
||||
'Close',
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
@@ -44,6 +46,94 @@ class Ledger extends AppModel {
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: accountID
|
||||
* - Returns the account ID for the given ledger
|
||||
*/
|
||||
function accountID($id) {
|
||||
$this->cacheQueries = true;
|
||||
$item = $this->find('first', array
|
||||
('contain' => 'Account.id',
|
||||
'conditions' => array('Ledger.id' => $id),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
return $item['Account']['id'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: currentLedgerID
|
||||
* - Returns the current ledger ID of the account for the given ledger.
|
||||
*/
|
||||
function currentLedgerID($id) {
|
||||
return $this->Account->currentLedgerID($this->accountID($id));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: closeLedger
|
||||
* - Closes the current ledger, and returns a fresh one
|
||||
*/
|
||||
function closeLedger($id, $close_id) {
|
||||
$this->recursive = -1;
|
||||
|
||||
$stamp = date('Y-m-d G:i:s');
|
||||
$this->id = $id;
|
||||
$this->read();
|
||||
$this->data['Ledger']['close_id'] = $close_id;
|
||||
$this->save($this->data, false);
|
||||
|
||||
$stats = $this->stats($id);
|
||||
|
||||
$this->read();
|
||||
$this->data['Ledger']['id'] = null;
|
||||
$this->data['Ledger']['close_id'] = null;
|
||||
$this->data['Ledger']['prior_ledger_id'] = $id;
|
||||
$this->data['Ledger']['comment'] = null;
|
||||
++$this->data['Ledger']['sequence'];
|
||||
$this->id = null;
|
||||
$this->save($this->data, false);
|
||||
//pr($this->data);
|
||||
|
||||
if ($stats['balance'] == 0)
|
||||
return $this->id;
|
||||
|
||||
$this->read();
|
||||
$ftype = $this->Account->fundamentalType($this->data['Ledger']['account_id']);
|
||||
$otype = $this->Account->fundamentalOpposite($ftype);
|
||||
|
||||
// Create a transaction for balance transfer
|
||||
$transaction = new Transaction();
|
||||
$transaction->create();
|
||||
if (!$transaction->save(array(), false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create an entry to carry the balance forward
|
||||
$carry_entry_data = array
|
||||
($ftype.'_ledger_id' => $this->id,
|
||||
$otype.'_ledger_id' => $id,
|
||||
'transaction_id' => $transaction->id,
|
||||
'amount' => $stats['balance'],
|
||||
'comment' => "Ledger Balance Forward",
|
||||
);
|
||||
|
||||
$carry_entry = new LedgerEntry();
|
||||
$carry_entry->create();
|
||||
if (!$carry_entry->save($carry_entry_data, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -147,7 +237,13 @@ class Ledger extends AppModel {
|
||||
|
||||
// The fields are all tucked into the [0] index,
|
||||
// and the rest of the array is useless (empty).
|
||||
return $stats[0];
|
||||
$stats = $stats[0];
|
||||
|
||||
// Make sure we have a non-null balance
|
||||
if (!isset($stats['balance']))
|
||||
$stats['balance'] = 0;
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,16 @@ class LedgerEntry extends AppModel {
|
||||
'amount' => array('money')
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'DebitReconciliation' => array(
|
||||
'className' => 'Reconciliation',
|
||||
'foreignKey' => 'debit_ledger_entry_id',
|
||||
),
|
||||
'CreditReconciliation' => array(
|
||||
'className' => 'Reconciliation',
|
||||
'foreignKey' => 'credit_ledger_entry_id',
|
||||
),
|
||||
);
|
||||
var $belongsTo = array(
|
||||
'MonetarySource',
|
||||
'Transaction',
|
||||
@@ -22,6 +32,23 @@ class LedgerEntry extends AppModel {
|
||||
'className' => 'Ledger',
|
||||
'foreignKey' => 'credit_ledger_id',
|
||||
),
|
||||
|
||||
'Ledger' => array(
|
||||
'foreignKey' => false,
|
||||
// conditions will be used when JOINing tables
|
||||
// (such as find with LinkableBehavior)
|
||||
'conditions' => array('OR' =>
|
||||
array('%{MODEL_ALIAS}.debit_ledger_id = Ledger.id',
|
||||
'%{MODEL_ALIAS}.credit_ledger_id = Ledger.id')),
|
||||
|
||||
// finderQuery will be used when tables are put
|
||||
// together across several querys, not with JOIN.
|
||||
// (such as find with ContainableBehavior)
|
||||
'finderQuery' => 'NOT-IMPLEMENTED',
|
||||
|
||||
'counterQuery' => ''
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
@@ -31,6 +58,16 @@ class LedgerEntry extends AppModel {
|
||||
'foreignKey' => 'credit_ledger_entry_id',
|
||||
'associationForeignKey' => 'debit_ledger_entry_id',
|
||||
),
|
||||
// STUPID CakePHP bug screws up when using Containable
|
||||
// and CLASS contains CLASS. This extra HABTM give the
|
||||
// option of multiple depths on one CLASS, since there
|
||||
// isn't an alias specification for Containable.
|
||||
'DebitReconciliationLedgerEntry2' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'joinTable' => 'reconciliations',
|
||||
'foreignKey' => 'credit_ledger_entry_id',
|
||||
'associationForeignKey' => 'debit_ledger_entry_id',
|
||||
),
|
||||
'CreditReconciliationLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'joinTable' => 'reconciliations',
|
||||
@@ -64,7 +101,8 @@ class LedgerEntry extends AppModel {
|
||||
* query the ledger/account to find it out.
|
||||
*/
|
||||
function ledgerContextFields($ledger_id = null, $account_type = null) {
|
||||
$fields = array('id', 'name', 'comment', 'amount');
|
||||
$fields = array('id', 'effective_date', 'through_date',
|
||||
'lease_id', 'customer_id', 'comment', 'amount');
|
||||
|
||||
if (isset($ledger_id)) {
|
||||
$fields[] = ("IF(LedgerEntry.debit_ledger_id = $ledger_id," .
|
||||
@@ -87,7 +125,7 @@ class LedgerEntry extends AppModel {
|
||||
}
|
||||
|
||||
function ledgerContextFields2($ledger_id = null, $account_id = null, $account_type = null) {
|
||||
$fields = array('id', 'name', 'comment', 'amount');
|
||||
$fields = array('id', 'effective_date', 'through_date', 'comment', 'amount');
|
||||
|
||||
if (isset($ledger_id)) {
|
||||
$fields[] = ("IF(LedgerEntry.debit_ledger_id = $ledger_id," .
|
||||
@@ -206,6 +244,251 @@ class LedgerEntry extends AppModel {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reverse
|
||||
* - Reverses the ledger entry
|
||||
*/
|
||||
|
||||
function reverse1($id, $amount = null, $transaction_id = null, $rec_id = null) {
|
||||
/* pr(array('LedgerEntry::reverse', */
|
||||
/* compact('id', 'amount', 'transaction_id', 'rec_id'))); */
|
||||
|
||||
// Get the LedgerEntry and related fields
|
||||
$entry = $this->find
|
||||
('first',
|
||||
array('contain' => array('MonetarySource.id',
|
||||
'Transaction.id',
|
||||
'DebitLedger.id',
|
||||
'DebitLedger.account_id',
|
||||
'CreditLedger.id',
|
||||
'CreditLedger.account_id',
|
||||
'DebitReconciliationLedgerEntry'
|
||||
/* => */
|
||||
/* array('DebitLedger.id', */
|
||||
/* 'DebitLedger.account_id', */
|
||||
/* 'CreditLedger.id', */
|
||||
/* 'CreditLedger.account_id', */
|
||||
/* ) */
|
||||
,
|
||||
'CreditReconciliationLedgerEntry'
|
||||
/* => */
|
||||
/* array('DebitLedger.id', */
|
||||
/* 'DebitLedger.account_id', */
|
||||
/* 'CreditLedger.id', */
|
||||
/* 'CreditLedger.account_id', */
|
||||
/* ) */
|
||||
,
|
||||
'Customer.id',
|
||||
'Lease.id',
|
||||
),
|
||||
|
||||
'fields' => array('LedgerEntry.*'),
|
||||
|
||||
'conditions' => array(array('LedgerEntry.id' => $id),
|
||||
/* array('NOT' => */
|
||||
/* array('OR' => */
|
||||
/* array(array('DebitReconciliationLedgerEntry.id' => $rec_id), */
|
||||
/* array('CreditReconciliationLedgerEntry.id' => $rec_id), */
|
||||
/* ), */
|
||||
/* ), */
|
||||
/* ), */
|
||||
),
|
||||
));
|
||||
//pr($entry);
|
||||
|
||||
if (!isset($amount))
|
||||
$amount = $entry['LedgerEntry']['amount'];
|
||||
|
||||
$A = new Account();
|
||||
|
||||
$ids = $this->Ledger->Account->postLedgerEntry
|
||||
(array('transaction_id' => $transaction_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($entry['CreditLedger']['account_id']),
|
||||
'credit_ledger_id' => $A->currentLedgerID($entry['DebitLedger']['account_id']),
|
||||
'effective_date' => $entry['LedgerEntry']['effective_date'],
|
||||
//'effective_date' => $entry['LedgerEntry']['effective_date'],
|
||||
'amount' => $amount,
|
||||
'lease_id' => $entry['Lease']['id'],
|
||||
'customer_id' => $entry['Customer']['id'],
|
||||
'comment' => "Reversal of Ledger Entry #{$id}",
|
||||
),
|
||||
array('debit' => array(array('LedgerEntry' => array('id' => $entry['LedgerEntry']['id'],
|
||||
'amount' => $amount,
|
||||
))),
|
||||
'credit' => array(array('LedgerEntry' => array('id' => $entry['LedgerEntry']['id'],
|
||||
'amount' => $amount,
|
||||
))),
|
||||
));
|
||||
|
||||
if ($ids['error'])
|
||||
return null;
|
||||
|
||||
$tid = $ids['transaction_id'];
|
||||
|
||||
pr(compact('entry'));
|
||||
|
||||
foreach (array('Debit', 'Credit') AS $dc_type) {
|
||||
foreach ($entry[$dc_type . 'ReconciliationLedgerEntry'] AS $RLE) {
|
||||
pr(array('checkpoint' => "Reverse $dc_type LE",
|
||||
compact('id', 'rec_id', 'RLE')));
|
||||
if ($RLE['id'] == $rec_id) {
|
||||
pr(array('checkpoint' => "Skipping Reverse $dc_type LE, due to rec_id",
|
||||
compact('id', 'RLE')));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->reverse($RLE['id'], $RLE['Reconciliation']['amount'], $tid, $id))
|
||||
$ids['error'] = true;
|
||||
|
||||
/* $rids = $this->Ledger->Account->postLedgerEntry */
|
||||
/* (array('transaction_id' => $tid), */
|
||||
/* null, */
|
||||
/* array('debit_ledger_id' => $A->currentLedgerID($RLE['CreditLedger']['account_id']), */
|
||||
/* 'credit_ledger_id' => $A->currentLedgerID($RLE['DebitLedger']['account_id']), */
|
||||
/* 'effective_date' => $RLE['effective_date'], */
|
||||
/* //'effective_date' => $RLE['effective_date'], */
|
||||
/* 'amount' => $RLE['Reconciliation']['amount'], */
|
||||
/* 'lease_id' => $entry['Lease']['id'], */
|
||||
/* 'customer_id' => $entry['Customer']['id'], */
|
||||
/* 'comment' => "Reversal of Ledger Entry #{$RLE['id']}", */
|
||||
/* ), */
|
||||
/* array('debit' => array(array('LedgerEntry' => array('id' => $RLE['id'], */
|
||||
/* 'amount' => $RLE['Reconciliation']['amount'], */
|
||||
/* ))), */
|
||||
/* 'credit' => array(array('LedgerEntry' => array('id' => $RLE['id'], */
|
||||
/* 'amount' => $RLE['Reconciliation']['amount'], */
|
||||
/* ))), */
|
||||
/* )); */
|
||||
|
||||
/* if ($rids['error']) */
|
||||
/* $ids['error'] = true; */
|
||||
}
|
||||
}
|
||||
|
||||
if ($ids['error'])
|
||||
return null;
|
||||
|
||||
return $ids['id'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reverse
|
||||
* - Reverses the charges
|
||||
*
|
||||
* SAMPLE MOVE IN w/ PRE PAYMENT
|
||||
* DEPOSIT RENT A/R RECEIPT CHECK PETTY BANK
|
||||
* ------- ------- ------- ------- ------- ------- -------
|
||||
* |25 | 25| | | | |
|
||||
* | |20 20| | | | |
|
||||
* | |20 20| | | | |
|
||||
* | |20 20| | | | |
|
||||
* | | |25 25| | | |
|
||||
* | | |20 20| | | |
|
||||
* | | |20 20| | | |
|
||||
* | | |20 20| | | |
|
||||
* | | | |85 85| | |
|
||||
* | | | | |85 | 85|
|
||||
|
||||
* MOVE OUT and REFUND FINAL MONTH
|
||||
* DEPOSIT RENT C/P RECEIPT CHECK PETTY BANK
|
||||
* ------- ------- ------- ------- ------- ------- -------
|
||||
* 25| | |25 | | | | t20 e20a
|
||||
* | 20| |20 | | | | t20 e20b
|
||||
|
||||
* -ONE REFUND CHECK-
|
||||
* | | 25| |25 | | | t30 e30a
|
||||
* | | 20| |20 | | | t30 e30b
|
||||
* | | | 45| | | |45 t40 e40
|
||||
* -OR MULTIPLE-
|
||||
* | | 15| |15 | | | t50a e50a
|
||||
* | | | 15| | |15 | t60a e60a
|
||||
* | | 30| |30 | | | t50b e50b
|
||||
* | | | 30| | | |30 t60b e60b
|
||||
* | | | | | | |
|
||||
|
||||
|
||||
OPTION 1
|
||||
* |-25 | -25| | | | |
|
||||
* | |-20 -20| | | | |
|
||||
* | | |-25 -25| | | |
|
||||
* | | |-20 -20| | | |
|
||||
|
||||
OPTION 2
|
||||
* |-25 | | -25| | | |
|
||||
* | |-20 | -20| | | |
|
||||
* | | | |-15 | -15| |
|
||||
* | | | |-30 | | -30|
|
||||
* | | | | | | |
|
||||
*
|
||||
*/
|
||||
function reverse($ledger_entries, $stamp = null) {
|
||||
pr(array('LedgerEntry::reverse',
|
||||
compact('ledger_entries', 'stamp')));
|
||||
|
||||
// If the user only wants to reverse one ID, we'll allow it
|
||||
if (!is_array($ledger_entries))
|
||||
$ledger_entries = $this->find
|
||||
('all', array
|
||||
('contain' => false,
|
||||
'conditions' => array('LedgerEntry.id' => $ledger_entries)));
|
||||
|
||||
$A = new Account();
|
||||
|
||||
$ar_account_id = $A->accountReceivableAccountID();
|
||||
$receipt_account_id = $A->receiptAccountID();
|
||||
|
||||
$transaction_id = null;
|
||||
foreach ($ledger_entries AS $entry) {
|
||||
$entry = $entry['LedgerEntry'];
|
||||
$amount = -1*$entry['amount'];
|
||||
|
||||
if (isset($entry['credit_account_id']))
|
||||
$refund_account_id = $entry['credit_account_id'];
|
||||
elseif (isset($entry['CreditLedger']['Account']['id']))
|
||||
$refund_account_id = $entry['CreditLedger']['Account']['id'];
|
||||
elseif (isset($entry['credit_ledger_id']))
|
||||
$refund_account_id = $this->Ledger->accountID($entry['credit_ledger_id']);
|
||||
else
|
||||
return null;
|
||||
|
||||
// post new refund in the income account
|
||||
$ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $transaction_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($ar_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($refund_account_id),
|
||||
'effective_date' => $entry['effective_date'],
|
||||
'through_date' => $entry['through_date'],
|
||||
'amount' => $amount,
|
||||
'lease_id' => $entry['lease_id'],
|
||||
'customer_id' => $entry['customer_id'],
|
||||
'comment' => "Refund; Entry #{$entry['id']}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $entry['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
);
|
||||
|
||||
if ($ids['error'])
|
||||
return null;
|
||||
$transaction_id = $ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Refund Ledger Entry',
|
||||
compact('ids', 'amount', 'refund_account_id', 'ar_account_id')));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
|
||||
@@ -9,12 +9,260 @@ class MonetarySource extends AppModel {
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
'MonetaryType',
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry',
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: nsf
|
||||
* - Flags the ledger entry as having insufficient funds
|
||||
* - NOTE: nsf only works if given the monetary source id
|
||||
* to transaction e3, below
|
||||
* - NOTE: In order to show that the rent formerly considered
|
||||
* "collected" is now recognized in reverse, we must
|
||||
* credit A/R with a negative amount in order to
|
||||
* reconcile it against the Rent<->A/R ledger entry.
|
||||
*
|
||||
* FEE RENT A/R RECEIPT CHECK NSF BANK
|
||||
* ------- ------- ------- ------- ------- ------- -------
|
||||
* | |30 30| | | | | t1 e1a : R e2/e7a :
|
||||
* | |20 20| | | | | t1 e1b : R e2/e7b :
|
||||
* | | | | | | |
|
||||
* | | |30 30| | | | t2 e2a : R e3 : R e1a
|
||||
* | | |20 20| | | | t2 e2b : R e3 : R e1b
|
||||
* | | | |50 50| | | t2 e3 : R e4 : R e2
|
||||
* | | | | | | |
|
||||
* | | | | |50 | 50| t3 e4 : : R e3
|
||||
* | | | | | | |
|
||||
* | | | | | |-50 -50| t4 e5 : : R e6
|
||||
* | | | |-50 | -50| | t5 e6 : R e5 : R e7a/e7b
|
||||
* | | |-30 -30| | | | t6 e7a : R e6 : R e1a
|
||||
* | | |-20 -20| | | | t6 e7b : R e6 : R e1b
|
||||
* |35 | 35| | | | | t6 e8
|
||||
*
|
||||
*/
|
||||
|
||||
function nsf($id, $stamp = null) {
|
||||
pr(array('MonetarySource::nsf',
|
||||
compact('id')));
|
||||
|
||||
$A = new Account();
|
||||
|
||||
// Get the LedgerEntries that use this monetary source
|
||||
$source = $this->find
|
||||
('first',
|
||||
array('contain' =>
|
||||
array(/* e3 */
|
||||
'LedgerEntry' =>
|
||||
array('Transaction.id',
|
||||
'MonetarySource.id',
|
||||
'Customer.id',
|
||||
'Lease.id',
|
||||
|
||||
/* e3 debit */
|
||||
'DebitLedger' => /* e.g. CHECK Ledger */
|
||||
array('fields' => array(),
|
||||
|
||||
'Account' => /* e.g. CHECK Account */
|
||||
array('fields' => array('id', 'name'),
|
||||
'conditions' =>
|
||||
array('Account.payable' => 1,
|
||||
'Account.type' => 'ASSET'),
|
||||
),
|
||||
),
|
||||
|
||||
/* e3 credit */
|
||||
'CreditLedger' => /* i.e. RECEIPT Ledger */
|
||||
array('fields' => array('id'),
|
||||
'Account' => /* i.e. RECEIPT Account */
|
||||
array('fields' => array('id', 'name'),
|
||||
'conditions' =>
|
||||
array('Account.id' => $A->receiptAccountID()),
|
||||
),
|
||||
),
|
||||
|
||||
/* e2 */
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
array(/* e2 credit */
|
||||
'CreditLedger' => /* i.e. A/R Ledger */
|
||||
array('fields' => array(),
|
||||
|
||||
'Account' => /* i.e. A/R Account */
|
||||
array('fields' => array(),
|
||||
'conditions' =>
|
||||
array('Account.id' => $A->accountReceivableAccountID()),
|
||||
),
|
||||
),
|
||||
|
||||
/* e1 */
|
||||
// STUPID CakePHP bug screws up CLASS contains CLASS.
|
||||
// Use the same class, but with different name.
|
||||
'DebitReconciliationLedgerEntry2',
|
||||
),
|
||||
|
||||
/* e4 */
|
||||
'CreditReconciliationLedgerEntry' =>
|
||||
array(/* e4 debit */
|
||||
'DebitLedger' => /* e.g. BANK Ledger */
|
||||
array('fields' => array('id'),
|
||||
'Account' => /* e.g. BANK Account */
|
||||
array('fields' => array('id', 'name'),
|
||||
'conditions' =>
|
||||
array('Account.depositable' => 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array(array('MonetarySource.id' => $id)),
|
||||
));
|
||||
pr($source);
|
||||
|
||||
$nsf_account_id = $A->nsfAccountID();
|
||||
$nsf_fee_account_id = $A->nsfChargeAccountID();
|
||||
$ar_account_id = $A->accountReceivableAccountID();
|
||||
$receipt_account_id = $A->receiptAccountID();
|
||||
|
||||
$t4_id = null;
|
||||
$t5_id = null;
|
||||
foreach ($source['LedgerEntry'] AS $e3) {
|
||||
// We expect only a single e4 entry
|
||||
$e4 = $e3['CreditReconciliationLedgerEntry'];
|
||||
if (count($e4) < 1)
|
||||
continue;
|
||||
if (count($e4) > 1)
|
||||
die('Too many e4 entries');
|
||||
|
||||
// Pullup e4 from the single member array
|
||||
$e4 = $e4[0];
|
||||
|
||||
// e3 amount
|
||||
$amount = -$e3['amount'];
|
||||
|
||||
// e4 account
|
||||
$bank_account_id = $e4['DebitLedger']['account_id'];
|
||||
|
||||
// post new e5
|
||||
$e5_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t4_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($bank_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($nsf_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => $amount,
|
||||
'lease_id' => $e3['lease_id'],
|
||||
'customer_id' => $e3['customer_id'],
|
||||
'comment' => "NSF Bank Reversal; Monetary Source #{$id}",
|
||||
)
|
||||
);
|
||||
|
||||
if ($e5_ids['error'])
|
||||
return null;
|
||||
$t4_id = $e5_ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e5',
|
||||
compact('e5_ids', 'amount')));
|
||||
|
||||
// post new e6... this will be our crossover point
|
||||
// from typical positive entries to negative entries.
|
||||
// Therefore, no reconciliation on this account.
|
||||
$e6_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t5_id),
|
||||
array('monetary_source_id' => $e3['monetary_source_id']),
|
||||
array('debit_ledger_id' => $A->currentLedgerID($nsf_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($receipt_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => $amount,
|
||||
'lease_id' => $e3['lease_id'],
|
||||
'customer_id' => $e3['customer_id'],
|
||||
'comment' => "NSF tracker; Monetary Source #{$id}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $e5_ids['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
);
|
||||
|
||||
if ($e6_ids['error'])
|
||||
return null;
|
||||
$t5_id = $e6_ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e6',
|
||||
compact('e6_ids', 'amount')));
|
||||
|
||||
$t6_id = null;
|
||||
foreach ($e3['DebitReconciliationLedgerEntry'] AS $e2) {
|
||||
foreach ($e2['DebitReconciliationLedgerEntry2'] AS $e1) {
|
||||
$amount = -1*$e1['Reconciliation']['amount'];
|
||||
|
||||
// post new e7
|
||||
$e7_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t6_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($receipt_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($ar_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => $amount,
|
||||
'lease_id' => $e1['lease_id'],
|
||||
'customer_id' => $e1['customer_id'],
|
||||
'comment' => "NSF Receipt; Monetary Source #{$id}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $e6_ids['id'],
|
||||
'amount' => $amount))),
|
||||
|
||||
'credit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $e1['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
);
|
||||
|
||||
if ($e7_ids['error'])
|
||||
return null;
|
||||
$t6_id = $e7_ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e7',
|
||||
compact('e7_ids', 'amount')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cheat for now
|
||||
$customer_id = $source['LedgerEntry'][0]['customer_id'];
|
||||
$lease_id = null;
|
||||
|
||||
// post new e8
|
||||
$e8_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t6_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($ar_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($nsf_fee_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => 35,
|
||||
'lease_id' => $lease_id,
|
||||
'customer_id' => $customer_id,
|
||||
'comment' => "NSF Fee; Monetary Source #{$id}",
|
||||
)
|
||||
);
|
||||
|
||||
if ($e8_ids['error'])
|
||||
return null;
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e8',
|
||||
compact('e8_ids')));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
class MonetaryType extends AppModel {
|
||||
|
||||
var $name = 'MonetaryType';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'name' => array('notempty'),
|
||||
'tillable' => array('boolean')
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'MonetarySource',
|
||||
);
|
||||
|
||||
}
|
||||
?>
|
||||
15
site/models/reconciliation.php
Normal file
15
site/models/reconciliation.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
class Reconciliation extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
'DebitLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
//'foreignKey' => 'credit_ledger_entry_id',
|
||||
),
|
||||
'CreditLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
//'foreignKey' => 'credit_ledger_entry_id',
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
@@ -2,12 +2,12 @@
|
||||
class Transaction extends AppModel {
|
||||
|
||||
var $name = 'Transaction';
|
||||
/* var $validate = array( */
|
||||
/* 'stamp' => array('date') */
|
||||
/* ); */
|
||||
|
||||
var $validate = array(
|
||||
'stamp' => array('date')
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
'Customer',
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
@@ -15,14 +15,94 @@ class Transaction extends AppModel {
|
||||
);
|
||||
|
||||
|
||||
function beforeSave() {
|
||||
|
||||
if(!empty($this->data['Transaction']['stamp'])) {
|
||||
$this->data['Transaction']['stamp'] =
|
||||
$this->dateFormatBeforeSave($this->data['Transaction']['stamp']);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addInvoice
|
||||
* - Adds a new invoice transaction
|
||||
*/
|
||||
|
||||
function addInvoice($data, $customer_id, $lease_id = null) {
|
||||
// Create some models for convenience
|
||||
$A = new Account();
|
||||
|
||||
//pr(compact('data', 'customer_id', 'lease_id'));
|
||||
|
||||
// Assume this will succeed
|
||||
$ret = true;
|
||||
|
||||
// Determine the total charges on the invoice
|
||||
$grand_total = 0;
|
||||
foreach ($data['LedgerEntry'] AS $entry)
|
||||
$grand_total += $entry['amount'];
|
||||
|
||||
// Go through the entered charges
|
||||
$invoice_transaction = array_intersect_key($data, array('Transaction'=>1, 'transaction_id'=>1));
|
||||
foreach ($data['LedgerEntry'] AS $entry) {
|
||||
//pr(compact('entry'));
|
||||
// Create the receipt entry, and reconcile the credit side
|
||||
// of the double-entry (which should be A/R) as a payment.
|
||||
$ids = $this->LedgerEntry->Ledger->Account->postLedgerEntry
|
||||
($invoice_transaction,
|
||||
array_intersect_key($entry, array('MonetarySource'=>1))
|
||||
+ array_intersect_key($entry, array('account_id'=>1)),
|
||||
array('debit_ledger_id' => $A->currentLedgerID($A->accountReceivableAccountID()),
|
||||
'credit_ledger_id' => $A->currentLedgerID($entry['account_id']),
|
||||
'customer_id' => $customer_id,
|
||||
'lease_id' => $lease_id)
|
||||
+ $entry
|
||||
);
|
||||
|
||||
if ($ids['error'])
|
||||
$ret = false;
|
||||
|
||||
$invoice_transaction = array_intersect_key($ids, array('transaction_id'=>1));
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addReceipt
|
||||
* - Adds a new receipt transaction
|
||||
*/
|
||||
|
||||
function addReceipt($data, $customer_id, $lease_id = null) {
|
||||
// Create some models for convenience
|
||||
$A = new Account();
|
||||
|
||||
// Assume this will succeed
|
||||
$ret = true;
|
||||
|
||||
// Go through the entered payments
|
||||
$receipt_transaction = array_intersect_key($data, array('Transaction'=>1, 'transaction_id'=>1));
|
||||
foreach ($data['LedgerEntry'] 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->LedgerEntry->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;
|
||||
|
||||
$receipt_transaction = array_intersect_key($ids,
|
||||
array('transaction_id'=>1,
|
||||
'split_transaction_id'=>1));
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -19,7 +19,7 @@ class Unit extends AppModel {
|
||||
var $hasOne = array(
|
||||
'CurrentLease' => array(
|
||||
'className' => 'Lease',
|
||||
'conditions' => 'CurrentLease.close_date IS NULL',
|
||||
'conditions' => 'CurrentLease.moveout_date IS NULL',
|
||||
),
|
||||
);
|
||||
|
||||
@@ -27,36 +27,58 @@ class Unit extends AppModel {
|
||||
'Lease',
|
||||
);
|
||||
|
||||
function statusEnums() {
|
||||
static $status_enums;
|
||||
if (!isset($status_enums))
|
||||
$status_enums = $this->getEnumValues('status');
|
||||
return $status_enums;
|
||||
}
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* helpers: status enumerations
|
||||
*/
|
||||
|
||||
function statusValue($enum) {
|
||||
$enums = $this->statusEnums();
|
||||
return $enums[$enum];
|
||||
}
|
||||
function statusEnums() {
|
||||
static $status_enums;
|
||||
if (!isset($status_enums))
|
||||
$status_enums = $this->getEnumValues('status');
|
||||
return $status_enums;
|
||||
}
|
||||
|
||||
function occupiedEnumValue() {
|
||||
return statusValue('OCCUPIED');
|
||||
}
|
||||
function activeStatusEnums() {
|
||||
return array_diff_key($this->statusEnums(), array(''=>1, 'DELETED'=>1));
|
||||
}
|
||||
|
||||
function conditionOccupied() {
|
||||
return ('Unit.status >= ' . $this->statusValue('OCCUPIED'));
|
||||
}
|
||||
function statusValue($enum) {
|
||||
$enums = $this->statusEnums();
|
||||
return $enums[$enum];
|
||||
}
|
||||
|
||||
function conditionVacant() {
|
||||
return ('Unit.status BETWEEN ' .
|
||||
($this->statusValue('UNAVAILABLE')+1) .
|
||||
' AND ' .
|
||||
($this->statusValue('OCCUPIED')-1));
|
||||
}
|
||||
function occupiedEnumValue() {
|
||||
return statusValue('OCCUPIED');
|
||||
}
|
||||
|
||||
function conditionUnavailable() {
|
||||
return ('Unit.status <= ' . $this->statusValue('UNAVAILABLE'));
|
||||
}
|
||||
function conditionOccupied() {
|
||||
return ('Unit.status >= ' . $this->statusValue('OCCUPIED'));
|
||||
}
|
||||
|
||||
function conditionVacant() {
|
||||
return ('Unit.status BETWEEN ' .
|
||||
($this->statusValue('UNAVAILABLE')+1) .
|
||||
' AND ' .
|
||||
($this->statusValue('OCCUPIED')-1));
|
||||
}
|
||||
|
||||
function conditionUnavailable() {
|
||||
return ('Unit.status <= ' . $this->statusValue('UNAVAILABLE'));
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: updateStatus
|
||||
* - Update the given unit to the given status
|
||||
*/
|
||||
function updateStatus($id, $status) {
|
||||
$this->id = $id;
|
||||
//pr(compact('id', 'status'));
|
||||
$this->saveField('status', $status);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
|
||||
204
site/views/accounts/collected.ctp
Normal file
204
site/views/accounts/collected.ctp
Normal file
@@ -0,0 +1,204 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="account collected">' . "\n";
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Javascript
|
||||
*/
|
||||
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
// Reset the form
|
||||
function resetForm() {
|
||||
/* updateEntriesGrid(); */
|
||||
}
|
||||
|
||||
function onGridLoadComplete() {
|
||||
var userdata = $('#collected-entries-jqGrid').getGridParam('userData');
|
||||
$('#collected-total').html(fmtCurrency(userdata['total']));
|
||||
}
|
||||
|
||||
function updateEntriesGrid() {
|
||||
var cust = new Array();
|
||||
|
||||
var account_ids = new Array();
|
||||
$("INPUT[type='checkbox']:checked").each(function(i) {
|
||||
account_ids.push($(this).val());
|
||||
});
|
||||
|
||||
cust['collected_account_id'] = <?php echo $account['id']; ?>;
|
||||
cust['collected_from_date'] = $('#TxFromDate').val();
|
||||
cust['collected_through_date'] = $('#TxThroughDate').val();
|
||||
cust['collected_payment_accounts'] = account_ids;
|
||||
|
||||
$('#collected-total').html('Calculating...');
|
||||
$('#collected-entries-jqGrid').clearGridData();
|
||||
$('#collected-entries-jqGrid').setPostDataItem('custom', serialize(cust));
|
||||
$('#collected-entries-jqGrid')
|
||||
.setGridParam({ page: 1 })
|
||||
.trigger("reloadGrid");
|
||||
|
||||
//$('#collected-entries .HeaderButton').click();
|
||||
}
|
||||
|
||||
|
||||
--></script>
|
||||
|
||||
<?php
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Summary Info Box
|
||||
*/
|
||||
|
||||
echo '<div class="infobox">' . "\n";
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array('Total:', '<SPAN id="collected-total"></SPAN>');
|
||||
echo $this->element('table',
|
||||
array('class' => 'summary',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value'),
|
||||
'suppress_alternate_rows' => true,
|
||||
));
|
||||
echo '</div>' . "\n";
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* User Configuration
|
||||
*/
|
||||
|
||||
echo $form->create(null, array('id' => 'collected-form',
|
||||
'onsubmit' => 'return false',
|
||||
'url' => null));
|
||||
|
||||
echo $form->input("id",
|
||||
array('id' => 'account-id',
|
||||
'type' => 'hidden',
|
||||
'value' => 0));
|
||||
|
||||
/* echo '<fieldset class="account collected entry">'; */
|
||||
/* echo ' <legend>Payment Account</legend>'; */
|
||||
/* echo $form->input('Tx.account_id', */
|
||||
/* array(//'label' => 'Payment<BR>Account', */
|
||||
/* 'label' => false, */
|
||||
/* 'type' => 'select', */
|
||||
/* 'multiple' => 'checkbox', */
|
||||
/* 'options' => $paymentAccounts, */
|
||||
/* 'selected' => array_keys($defaultAccounts), */
|
||||
/* ) */
|
||||
/* ); */
|
||||
/* echo '</fieldset>'; */
|
||||
|
||||
echo $this->element('form_table',
|
||||
array('class' => "item account collected entry",
|
||||
//'with_name_after' => ':',
|
||||
'field_prefix' => 'Tx.',
|
||||
'fields' => array
|
||||
("account_id" => array('name' => 'Payment<BR>Account',
|
||||
'opts' =>
|
||||
array('type' => 'select',
|
||||
'multiple' => 'checkbox',
|
||||
'options' => $paymentAccounts,
|
||||
'selected' => array_keys($defaultAccounts),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
echo $this->element('form_table',
|
||||
array('class' => "item account collected entry",
|
||||
//'with_name_after' => ':',
|
||||
'field_prefix' => 'Tx.',
|
||||
'fields' => array
|
||||
("from_date" => array('opts' =>
|
||||
array('type' => 'text'),
|
||||
'between' => '<A HREF="#" ONCLICK="datepickerBOM(null,\'TxFromDate\'); return false;">BOM</A>',
|
||||
),
|
||||
"through_date" => array('opts' =>
|
||||
array('type' => 'text'),
|
||||
'between' => '<A HREF="#" ONCLICK="datepickerEOM(\'TxFromDate\',\'TxThroughDate\'); return false;">EOM</A>',
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
echo $form->button('Update',
|
||||
array('onclick' => 'updateEntriesGrid(); return false',
|
||||
));
|
||||
|
||||
echo $form->end();
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Supporting Elements Section
|
||||
*/
|
||||
|
||||
echo '<div CLASS="detail supporting">' . "\n";
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Entries
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'collected_account_id' => $account['id'],
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'grid_div_id' => 'collected-entries',
|
||||
'grid_div_class' => 'text-below',
|
||||
'grid_events' => array('loadComplete' => 'onGridLoadComplete()'),
|
||||
//'grid_setup' => array('hiddengrid' => true),
|
||||
//'caption' => '<SPAN id="receipt-charges-caption"></SPAN>',
|
||||
'caption' => 'Collected ' . Inflector::pluralize($account['name'])
|
||||
),
|
||||
));
|
||||
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
$(document).ready(function(){
|
||||
$("#TxFromDate")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
|
||||
$("#TxThroughDate")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
|
||||
resetForm();
|
||||
});
|
||||
--></script>
|
||||
|
||||
<?php
|
||||
|
||||
/* End "detail supporting" div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
/* End page div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
?>
|
||||
|
||||
<a href="#" onClick="$('#debug').html(''); return false;">Clear Debug Output</a>
|
||||
74
site/views/accounts/deposit.ctp
Normal file
74
site/views/accounts/deposit.ctp
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="account deposit">' . "\n";
|
||||
echo '<H2>Perform Bank Deposit</H2>' . "\n";
|
||||
echo '<P>Make sure to select the checkboxes below for only those types of currency (Cash, Check, etc) which you intend to actually deposit (you can see all the individual items by dropping down the list below the checkbox). Then, select the Deposit Account where you will make the deposit, and click "Perform Deposit" to close the books on the selected currency types and reset them to a zero balance. On the next page, you will be provided with a deposit slip to prepare the actual deposit.' . "\n";
|
||||
echo '<P><BR>' . "\n";
|
||||
|
||||
//pr(compact('tillableAccount', 'depositableAccount'));
|
||||
|
||||
echo $form->create(null, array('id' => 'deposit-form',
|
||||
'url' => array('controller' => 'accounts',
|
||||
'action' => 'deposit')));
|
||||
|
||||
foreach ($tillableAccount AS $acct) {
|
||||
//$acct = $acct['Account'];
|
||||
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.checked',
|
||||
array(//'label' => $acct['Account']['name'],
|
||||
'type' => 'checkbox',
|
||||
'checked' => true,
|
||||
'value' => true,
|
||||
'label' => (" I have exactly " .
|
||||
FormatHelper::currency($acct['Account']['stats']['Ledger']['balance']) .
|
||||
" in " . ($acct['Account']['name'] === 'Cash'
|
||||
? 'Cash'
|
||||
: Inflector::pluralize($acct['Account']['name'])) .
|
||||
" and will be depositing it all.")
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.amount',
|
||||
array('type' => 'hidden',
|
||||
'value' => $acct['Account']['stats']['Ledger']['balance'],
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.account_id',
|
||||
array('type' => 'hidden',
|
||||
'value' => $acct['Account']['id'],
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.account_name',
|
||||
array('type' => 'hidden',
|
||||
'value' => $acct['Account']['name'],
|
||||
));
|
||||
echo "\n";
|
||||
|
||||
$grid_div_id = 'ledger_entries'.$acct['CurrentLedger']['id'].'-list';
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'ledger_id' => $acct['CurrentLedger']['id'],
|
||||
'no_account' => true,
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'grid_div_id' => $grid_div_id,
|
||||
'caption' => ('<A HREF="#" ONCLICK="$(\'#'.$grid_div_id.' .HeaderButton\').click();'.
|
||||
' return false;">Items in '.$acct['Account']['name'].' Ledger</A>'),
|
||||
'grid_setup' => array('hiddengrid' => true),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
$options = array();
|
||||
foreach ($depositableAccount AS $acct) {
|
||||
$options[$acct['Account']['id']] = $acct['Account']['name'];
|
||||
}
|
||||
|
||||
echo $form->input('Deposit.Account.id', array('label' => 'Deposit Account ',
|
||||
'options' => $options));
|
||||
echo $form->end('Perform Deposit');
|
||||
|
||||
/* End page div */
|
||||
echo '</div>' . "\n";
|
||||
53
site/views/accounts/deposit_slip.ctp
Normal file
53
site/views/accounts/deposit_slip.ctp
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<H2>Deposit Slip: ' . date('l, F jS, Y, g:ia') . '</H2>' . "\n";
|
||||
|
||||
//pr(compact('deposit'));
|
||||
|
||||
// Handle account summaries
|
||||
$rows = array();
|
||||
$row_class = array();
|
||||
foreach ($deposit['ledgers'] AS $ledger) {
|
||||
$row_class[] = array();
|
||||
$rows[] = array($ledger['name'].':',
|
||||
FormatHelper::_n(count($ledger['entries']), 'Item'),
|
||||
FormatHelper::currency($ledger['total'], true));
|
||||
}
|
||||
$row_class[] = 'grand';
|
||||
$rows[] = array('Deposit Total:',
|
||||
null,
|
||||
FormatHelper::currency($deposit['total'], true));
|
||||
echo $this->element('table',
|
||||
array('class' => 'deposit-summary',
|
||||
'rows' => $rows,
|
||||
'row_class' => $row_class,
|
||||
'column_class' => array('account', 'quantity', 'total'),
|
||||
'suppress_alternate_rows' => true,
|
||||
));
|
||||
|
||||
|
||||
// Print out the items of each ledger
|
||||
foreach ($deposit['ledgers'] AS $ledger) {
|
||||
//echo ('Count: ' . count($ledger['entries']) . '<BR>');
|
||||
//pr($ledger['entries']);
|
||||
if (count($ledger['entries']) == 0)
|
||||
continue;
|
||||
|
||||
$rows = array();
|
||||
foreach ($ledger['entries'] AS $entry) {
|
||||
$rows[] = array($entry['Customer']['name'],
|
||||
$entry['MonetarySource']['name'],
|
||||
$entry['LedgerEntry']['amount']);
|
||||
}
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item deposit-slip list',
|
||||
'caption' => $ledger['name'] . ' Items',
|
||||
'rows' => $rows,
|
||||
'headers' => array('Customer', 'Item', 'Amount'),
|
||||
'column_class' => array('customer', 'item', 'amount'),
|
||||
));
|
||||
}
|
||||
|
||||
/* End page div */
|
||||
//echo '</div>' . "\n";
|
||||
@@ -55,20 +55,33 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
* Ledgers
|
||||
*/
|
||||
|
||||
echo $this->element('ledgers',
|
||||
array('caption' => $account['Account']['name'] . " Ledgers",
|
||||
'ledgers' => $account['Ledger']));
|
||||
echo $this->element('ledgers', array
|
||||
('config' => array
|
||||
('caption' => $account['Account']['name'] . " Ledgers",
|
||||
'rows' => $account['Ledger'],
|
||||
)));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Current Ledger
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries',
|
||||
array('caption' => "Current Ledger: (#{$account['CurrentLedger']['sequence']})",
|
||||
'ledger_id' => $account['CurrentLedger']['id'],
|
||||
'account_type' => $account['Account']['type'],
|
||||
));
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'ledger_id' => $account['CurrentLedger']['id'],
|
||||
'account_type' => $account['Account']['type'],
|
||||
'group_by_tx' => true,
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
('caption' =>
|
||||
"Current Ledger: (" .
|
||||
"#{$account['Account']['id']}" .
|
||||
"-" .
|
||||
"{$account['CurrentLedger']['sequence']}" .
|
||||
")",
|
||||
),
|
||||
));
|
||||
|
||||
/* End "detail supporting" div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
403
site/views/contacts/edit.ctp
Normal file
403
site/views/contacts/edit.ctp
Normal file
@@ -0,0 +1,403 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
/**********************************************************************
|
||||
* Because we make use of functions here,
|
||||
* we need to put our top level variables somewhere
|
||||
* we can access them.
|
||||
*/
|
||||
$this->varstore = compact('methodTypes', 'methodPreferences',
|
||||
'contactPhones', 'phoneTypes',
|
||||
'contactAddresses',
|
||||
'contactEmails');
|
||||
|
||||
//pr($this->data);
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Javascript
|
||||
*/
|
||||
|
||||
function contactMethodDiv($obj, $type, $legend, $values = null) {
|
||||
|
||||
$div =
|
||||
// BEGIN type-div
|
||||
'<DIV>' . "\n" .
|
||||
// BEGIN type-fieldset
|
||||
'<FIELDSET CLASS="'.$type.' subset">' . "\n" .
|
||||
'<LEGEND>'.$legend.' #%{id} (%{remove})</LEGEND>' . "\n" .
|
||||
|
||||
// BEGIN source-div
|
||||
'<DIV ID="'.$type.'-%{id}-source-div">' . "\n" .
|
||||
// BEGIN source-fieldset
|
||||
'<FIELDSET>' . "\n" .
|
||||
//'<LEGEND>Source</LEGEND>' . "\n" .
|
||||
''
|
||||
;
|
||||
|
||||
if (!isset($values)) {
|
||||
foreach (array('Existing', 'New') AS $sname) {
|
||||
$stype = strtolower($sname);
|
||||
$div .=
|
||||
'<INPUT TYPE="radio" ' . "\n" .
|
||||
' NAME="data[Contact'.ucfirst($type).'][%{id}][source]"' . "\n" .
|
||||
' ONCLICK="switchMethodSource(%{id}, '."'{$type}', '{$stype}'".')"' . "\n" .
|
||||
' CLASS="'.$type.'-method-%{id}-source" ' . "\n" .
|
||||
' ID="'.$type.'-method-%{id}-source-'.$stype.'"' . "\n" .
|
||||
' VALUE="'.$stype.'"' . "\n" .
|
||||
' />' . "\n" .
|
||||
' <LABEL FOR="'.$type.'-method-%{id}-source-'.$stype.'">'.$sname.'</LABEL>' . "\n" .
|
||||
' ';
|
||||
}
|
||||
}
|
||||
$div .= "\n";
|
||||
|
||||
if (isset($values)) {
|
||||
$div .= contactMethodTypeDiv($obj, $type, 'show', $values);
|
||||
}
|
||||
else {
|
||||
$div .= contactMethodTypeDiv($obj, $type, 'existing');
|
||||
$div .= contactMethodTypeDiv($obj, $type, 'new');
|
||||
}
|
||||
|
||||
$div .=
|
||||
// END source-fieldset
|
||||
'</FIELDSET>' . "\n" .
|
||||
// END source-div
|
||||
'</DIV>' . "\n" .
|
||||
|
||||
// BEGIN method-div
|
||||
'<div id="'.$type.'-%{id}-method-div"' . "\n" .
|
||||
|
||||
$obj->element
|
||||
('form_table',
|
||||
array('class' => "item contact-{$type} entry",
|
||||
'field_prefix' => 'Contact'.ucfirst($type).'.%{id}.ContactsMethod',
|
||||
'fields' => array
|
||||
(
|
||||
'preference' => array
|
||||
('opts' => array
|
||||
('options' => $obj->varstore['methodPreferences'],
|
||||
'selected' => (isset($values) ? $values['ContactsMethod']['preference'] : null),
|
||||
)),
|
||||
|
||||
'type' => array
|
||||
('opts' => array
|
||||
('options' => $obj->varstore['methodTypes'],
|
||||
'selected' => (isset($values) ? $values['ContactsMethod']['type'] : null),
|
||||
)),
|
||||
|
||||
'comment' => array
|
||||
('opts' => array
|
||||
('value' => (isset($values) ? $values['ContactsMethod']['comment'] : null),
|
||||
)),
|
||||
|
||||
))) . "\n" .
|
||||
|
||||
// END method-div
|
||||
'</div>' . "\n" .
|
||||
|
||||
// END type-fieldset
|
||||
'</FIELDSET>' . "\n" .
|
||||
// END type-div
|
||||
'</DIV>'
|
||||
;
|
||||
|
||||
return $div;
|
||||
}
|
||||
|
||||
function contactMethodTypeDiv($obj, $type, $stype, $values = null) {
|
||||
|
||||
$element = 'form_table';
|
||||
|
||||
if ($type === 'phone') {
|
||||
if ($stype === 'existing') {
|
||||
$fields = array
|
||||
('id' => array('name' => 'Phone/Ext',
|
||||
'opts' => array('options' => $obj->varstore['contactPhones'])),
|
||||
);
|
||||
}
|
||||
elseif ($stype === 'new') {
|
||||
$fields = array
|
||||
('type' => array('opts' => array('options' => $obj->varstore['phoneTypes'])),
|
||||
'phone' => true,
|
||||
'ext' => array('name' => "Extension"),
|
||||
'comment' => true,
|
||||
);
|
||||
}
|
||||
elseif ($stype === 'show') {
|
||||
$element = 'table';
|
||||
$column_class = array('field', 'value');
|
||||
$rows = array
|
||||
(array('Type', $values['type']),
|
||||
array('Phone', $values['phone']),
|
||||
array('Extension', $values['ext']),
|
||||
array('Comment', $values['comment']),
|
||||
);
|
||||
}
|
||||
else {
|
||||
die("\n\nInvalid stype ($stype)\n\n");
|
||||
}
|
||||
|
||||
}
|
||||
elseif ($type === 'address') {
|
||||
if ($stype === 'existing') {
|
||||
$fields = array
|
||||
('id' => array('name' => 'Address',
|
||||
'opts' => array('options' => $obj->varstore['contactAddresses'])),
|
||||
);
|
||||
}
|
||||
elseif ($stype === 'new') {
|
||||
$fields = array
|
||||
('address' => true,
|
||||
'city' => true,
|
||||
'state' => true,
|
||||
'postcode' => array('name' => 'Zip Code'),
|
||||
'country' => true,
|
||||
'comment' => true,
|
||||
);
|
||||
}
|
||||
elseif ($stype === 'show') {
|
||||
$element = 'table';
|
||||
$column_class = array('field', 'value');
|
||||
$rows = array
|
||||
(array('Address', preg_replace("/\n/", "<BR>", $values['address'])),
|
||||
array('City', $values['city']),
|
||||
array('State', $values['state']),
|
||||
array('Zip Code', $values['postcode']),
|
||||
array('Country', $values['country']),
|
||||
array('Comment', $values['comment']),
|
||||
);
|
||||
}
|
||||
else {
|
||||
die("\n\nInvalid stype ($stype)\n\n");
|
||||
}
|
||||
}
|
||||
elseif ($type === 'email') {
|
||||
if ($stype === 'existing') {
|
||||
$fields = array
|
||||
('id' => array('name' => 'Email',
|
||||
'opts' => array('options' => $obj->varstore['contactEmails'])),
|
||||
);
|
||||
}
|
||||
elseif ($stype === 'new') {
|
||||
$fields = array
|
||||
('email' => true,
|
||||
'comment' => true,
|
||||
);
|
||||
}
|
||||
elseif ($stype === 'show') {
|
||||
$element = 'table';
|
||||
$column_class = array('field', 'value');
|
||||
$rows = array
|
||||
(array('Email', $values['email']),
|
||||
array('Comment', $values['comment']),
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
die("\n\nInvalid type ($type)\n\n");
|
||||
}
|
||||
|
||||
return
|
||||
// BEGIN sourcetype-div
|
||||
'<div ' . "\n" .
|
||||
' class="'.$type.'-%{id}-div"' . "\n" .
|
||||
' id="'.$type.'-%{id}-'.$stype.'-div"' . "\n" .
|
||||
(isset($values) ? '' : ' STYLE="display:none;"' . "\n") .
|
||||
'>' . "\n" .
|
||||
|
||||
$obj->element
|
||||
($element,
|
||||
array('class' => "item contact-{$type} {$stype}",
|
||||
'field_prefix' => 'Contact'.ucfirst($type).'.%{id}')
|
||||
+ compact('rows', 'fields', 'column_class')) .
|
||||
|
||||
($stype === 'show'
|
||||
? '<input type="hidden" name="data[Contact'.ucfirst($type).'][%{id}][id]" value="'.$values['id'].'"/>' . "\n"
|
||||
: '') .
|
||||
|
||||
// END sourcetype-div
|
||||
'</div>' . "\n" .
|
||||
'';
|
||||
}
|
||||
|
||||
|
||||
//pr($this->data);
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
function addPhone(flash) {
|
||||
addDiv('phone-entry-id', 'phone', 'phones', flash, <?php
|
||||
echo FormatHelper::phpVarToJavascript(contactMethodDiv($this,
|
||||
'phone',
|
||||
'Phone Number'),
|
||||
null,
|
||||
' ');
|
||||
?>
|
||||
);
|
||||
}
|
||||
|
||||
function addAddress(flash) {
|
||||
addDiv('address-entry-id', 'address', 'addresses', flash, <?php
|
||||
echo FormatHelper::phpVarToJavascript(contactMethodDiv($this,
|
||||
'address',
|
||||
'Address'),
|
||||
null,
|
||||
' ');
|
||||
?>
|
||||
);
|
||||
}
|
||||
|
||||
function addEmail(flash) {
|
||||
addDiv('email-entry-id', 'email', 'emails', flash, <?php
|
||||
echo FormatHelper::phpVarToJavascript(contactMethodDiv($this,
|
||||
'email',
|
||||
'Email Address'),
|
||||
null,
|
||||
' ');
|
||||
?>
|
||||
);
|
||||
}
|
||||
|
||||
// Reset the form
|
||||
function resetForm() {
|
||||
$('#phones').html('');
|
||||
$('#addresses').html('');
|
||||
$('#emails').html('');
|
||||
$('#phone-entry-id').val(1);
|
||||
$('#address-entry-id').val(1);
|
||||
$('#email-entry-id').val(1);
|
||||
|
||||
<?php foreach ($this->data['ContactPhone'] AS $phone): ?>
|
||||
addDiv('phone-entry-id', 'phone', 'phones', false, <?php
|
||||
echo FormatHelper::phpVarToJavascript(contactMethodDiv($this,
|
||||
'phone',
|
||||
'Phone Number',
|
||||
$phone
|
||||
),
|
||||
null,
|
||||
' ');
|
||||
?>
|
||||
);
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php foreach ($this->data['ContactAddress'] AS $address): ?>
|
||||
addDiv('address-entry-id', 'address', 'addresses', false, <?php
|
||||
echo FormatHelper::phpVarToJavascript(contactMethodDiv($this,
|
||||
'address',
|
||||
'Address',
|
||||
$address
|
||||
),
|
||||
null,
|
||||
' ');
|
||||
?>
|
||||
);
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php foreach ($this->data['ContactEmail'] AS $email): ?>
|
||||
addDiv('email-entry-id', 'email', 'emails', false, <?php
|
||||
echo FormatHelper::phpVarToJavascript(contactMethodDiv($this,
|
||||
'email',
|
||||
'Email Address',
|
||||
$email
|
||||
),
|
||||
null,
|
||||
' ');
|
||||
?>
|
||||
);
|
||||
<?php endforeach; ?>
|
||||
|
||||
}
|
||||
|
||||
function switchMethodSource(id, type, source) {
|
||||
$("."+type+"-"+id+"-div")
|
||||
.slideUp();
|
||||
|
||||
$("#"+type+"-"+id+"-"+source+"-div")
|
||||
.slideDown();
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
resetForm();
|
||||
});
|
||||
|
||||
--></script>
|
||||
|
||||
<?php
|
||||
; // alignment
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Contact Edit
|
||||
*/
|
||||
|
||||
echo '<div class="contact edit">' . "\n";
|
||||
|
||||
echo $form->create('Contact', array('action' => 'edit')) . "\n";
|
||||
echo $form->input('id') . "\n";
|
||||
|
||||
echo($this->element
|
||||
('form_table',
|
||||
array('class' => 'item contact detail',
|
||||
'caption' => isset($this->data['Contact']) ? 'Edit Contact' : 'New Contact',
|
||||
'fields' => array
|
||||
('first_name' => true,
|
||||
'last_name' => true,
|
||||
'middle_name' => true,
|
||||
'display_name' => true,
|
||||
'company_name' => array('name' => 'Company'),
|
||||
'id_federal' => array('name' => 'SSN'),
|
||||
'id_local' => array('name' => 'ID #'),
|
||||
'id_local_state' => array('name' => 'ID State'),
|
||||
/* 'id_local_exp' => array('name' => 'ID Expiration', */
|
||||
/* 'opts' => array('empty' => true)), */
|
||||
'comment' => true,
|
||||
))) . "\n");
|
||||
|
||||
echo $form->submit('Update') . "\n";
|
||||
?>
|
||||
|
||||
<div CLASS="dynamic-set">
|
||||
<fieldset CLASS="phone superset">
|
||||
<legend>Phone Numbers</legend>
|
||||
<input type="hidden" id="phone-entry-id" value="0">
|
||||
<div id="phones"></div>
|
||||
<fieldset> <legend>
|
||||
<a href="#" onClick="addPhone(true); return false;">Add a Phone Number</a>
|
||||
</legend> </fieldset>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div CLASS="dynamic-set">
|
||||
<fieldset CLASS="address superset">
|
||||
<legend>Mailing Addresses</legend>
|
||||
<input type="hidden" id="address-entry-id" value="0">
|
||||
<div id="addresses"></div>
|
||||
<fieldset> <legend>
|
||||
<a href="#" onClick="addAddress(true); return false;">Add a Mailing Address</a>
|
||||
</legend> </fieldset>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div CLASS="dynamic-set">
|
||||
<fieldset CLASS="email superset">
|
||||
<legend>Email Addresses</legend>
|
||||
<input type="hidden" id="email-entry-id" value="0">
|
||||
<div id="emails"></div>
|
||||
<fieldset> <legend>
|
||||
<a href="#" onClick="addEmail(true); return false;">Add an Email Address</a>
|
||||
</legend> </fieldset>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
; // Alignment
|
||||
|
||||
echo $form->submit('Update') . "\n";
|
||||
echo $form->submit('Cancel', array('name' => 'cancel')) . "\n";
|
||||
echo $form->end() . "\n";
|
||||
echo '</div>' . "\n";
|
||||
@@ -74,26 +74,6 @@ echo $this->element('table',
|
||||
'column_class' => $headers));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Emails
|
||||
*/
|
||||
$headers = array('Email', 'Preference', 'Comment');
|
||||
$rows = array();
|
||||
foreach($contact['ContactEmail'] AS $email) {
|
||||
$rows[] = array($email['email'],
|
||||
$email['ContactsMethod']['preference'] . " / " .
|
||||
$email['ContactsMethod']['type'],
|
||||
$email['comment']);
|
||||
}
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item email list',
|
||||
'caption' => 'Email',
|
||||
'headers' => $headers,
|
||||
'rows' => $rows,
|
||||
'column_class' => $headers));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Addresses
|
||||
*/
|
||||
@@ -118,12 +98,35 @@ echo $this->element('table',
|
||||
'column_class' => $headers));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Emails
|
||||
*/
|
||||
$headers = array('Email', 'Preference', 'Comment');
|
||||
$rows = array();
|
||||
foreach($contact['ContactEmail'] AS $email) {
|
||||
$rows[] = array($email['email'],
|
||||
$email['ContactsMethod']['preference'] . " / " .
|
||||
$email['ContactsMethod']['type'],
|
||||
$email['comment']);
|
||||
}
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item email list',
|
||||
'caption' => 'Email',
|
||||
'headers' => $headers,
|
||||
'rows' => $rows,
|
||||
'column_class' => $headers));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Customers
|
||||
*/
|
||||
echo $this->element('customers', array('heading' => '',
|
||||
'caption' => 'Related Customers',
|
||||
'customers' => $contact['Customer']));
|
||||
|
||||
echo $this->element('customers', array
|
||||
('config' => array
|
||||
('caption' => 'Related Customers',
|
||||
'rows' => $contact['Customer'],
|
||||
)));
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
|
||||
274
site/views/customers/edit.ctp
Normal file
274
site/views/customers/edit.ctp
Normal file
@@ -0,0 +1,274 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
/**********************************************************************
|
||||
* Because we make use of functions here,
|
||||
* we need to put our top level variables somewhere
|
||||
* we can access them.
|
||||
*/
|
||||
$this->varstore = compact('contactTypes', 'contacts');
|
||||
|
||||
//pr($this->data);
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Javascript
|
||||
*/
|
||||
|
||||
function customerContactDiv($obj, $values = null, $primary = false) {
|
||||
|
||||
$div =
|
||||
// BEGIN type-div
|
||||
'<DIV>' . "\n" .
|
||||
// BEGIN type-fieldset
|
||||
'<FIELDSET CLASS="contact subset">' . "\n" .
|
||||
'<LEGEND>Contact #%{id} (%{remove})</LEGEND>' . "\n" .
|
||||
|
||||
// BEGIN source-div
|
||||
'<DIV ID="contact-%{id}-source-div">' . "\n" .
|
||||
// BEGIN source-fieldset
|
||||
'<FIELDSET>' . "\n" .
|
||||
//'<LEGEND>Source</LEGEND>' . "\n" .
|
||||
''
|
||||
;
|
||||
|
||||
if (!isset($values)) {
|
||||
foreach (array('Existing', 'New') AS $sname) {
|
||||
$stype = strtolower($sname);
|
||||
$div .=
|
||||
'<INPUT TYPE="radio" ' . "\n" .
|
||||
' NAME="data[Contact][%{id}][source]"' . "\n" .
|
||||
' ONCLICK="switchContactSource(%{id}, '."'{$stype}'".')"' . "\n" .
|
||||
' CLASS="contact-%{id}-source" ' . "\n" .
|
||||
' ID="contact-%{id}-source-'.$stype.'"' . "\n" .
|
||||
' VALUE="'.$stype.'"' . "\n" .
|
||||
//' CHECKED' . "\n" .
|
||||
' />' . "\n" .
|
||||
' <LABEL FOR="contact-%{id}-source-'.$stype.'">'.$sname.'</LABEL>' . "\n" .
|
||||
' ';
|
||||
}
|
||||
}
|
||||
$div .= "\n";
|
||||
|
||||
if (isset($values)) {
|
||||
$div .= customerContactTypeDiv($obj, 'show', $values);
|
||||
}
|
||||
else {
|
||||
$div .= customerContactTypeDiv($obj, 'existing');
|
||||
$div .= customerContactTypeDiv($obj, 'new');
|
||||
}
|
||||
|
||||
$div .=
|
||||
// END source-fieldset
|
||||
'</FIELDSET>' . "\n" .
|
||||
// END source-div
|
||||
'</DIV>' . "\n" .
|
||||
|
||||
// BEGIN contact-div
|
||||
'<div id="contact-%{id}-contact-div"' . "\n" .
|
||||
|
||||
$obj->element
|
||||
('form_table',
|
||||
array('class' => "item contact entry",
|
||||
'field_prefix' => 'Contact.%{id}.ContactsCustomer',
|
||||
'fields' => array
|
||||
(
|
||||
'Customer.primary_contact_entry' => array
|
||||
('name' => 'Primary Contact',
|
||||
'no_prefix' => true,
|
||||
'opts' => array
|
||||
('type' => 'radio',
|
||||
'options' => array('%{id}' => false),
|
||||
'value' => ($primary ? '%{id}' : 'bogus-value-to-suppress-hidden-input'),
|
||||
)),
|
||||
|
||||
'type' => array
|
||||
('opts' => array
|
||||
('options' => $obj->varstore['contactTypes'],
|
||||
'selected' => (isset($values) ? $values['ContactsCustomer']['type'] : null),
|
||||
)),
|
||||
|
||||
'comment' => array
|
||||
('opts' => array
|
||||
('value' => (isset($values) ? $values['ContactsCustomer']['comment'] : null),
|
||||
)),
|
||||
|
||||
))) . "\n" .
|
||||
|
||||
// END contact-div
|
||||
'</div>' . "\n" .
|
||||
|
||||
// END type-fieldset
|
||||
'</FIELDSET>' . "\n" .
|
||||
// END type-div
|
||||
'</DIV>'
|
||||
;
|
||||
|
||||
return $div;
|
||||
}
|
||||
|
||||
function customerContactTypeDiv($obj, $stype, $values = null) {
|
||||
|
||||
$element = 'form_table';
|
||||
$class = $stype;
|
||||
|
||||
if ($stype === 'existing') {
|
||||
$fields = array
|
||||
('id' => array('name' => 'Contact',
|
||||
'opts' => array('options' => $obj->varstore['contacts'])),
|
||||
);
|
||||
}
|
||||
elseif ($stype === 'new') {
|
||||
$fields = array
|
||||
('first_name' => true,
|
||||
'last_name' => true,
|
||||
'middle_name' => true,
|
||||
'display_name' => true,
|
||||
'company_name' => array('name' => 'Company'),
|
||||
'id_federal' => array('name' => 'SSN'),
|
||||
'id_local' => array('name' => 'ID #'),
|
||||
'id_local_state' => array('name' => 'ID State'),
|
||||
/* 'id_local_exp' => array('name' => 'ID Expiration', */
|
||||
/* 'opts' => array('empty' => true)), */
|
||||
'comment' => true,
|
||||
);
|
||||
}
|
||||
elseif ($stype === 'show') {
|
||||
$element = 'table';
|
||||
$class = 'detail';
|
||||
$column_class = array('field', 'value');
|
||||
$rows = array(array('First Name', $values['first_name']),
|
||||
array('Last Name', $values['last_name']),
|
||||
array('Company', $values['company_name']),
|
||||
array('Comment', $values['comment']));
|
||||
}
|
||||
else {
|
||||
die("\n\nInvalid stype ($stype)\n\n");
|
||||
}
|
||||
|
||||
return
|
||||
// BEGIN sourcetype-div
|
||||
'<div ' . "\n" .
|
||||
' class="contact-%{id}-div"' . "\n" .
|
||||
' id="contact-%{id}-'.$stype.'-div"' . "\n" .
|
||||
(isset($values) ? '' : ' STYLE="display:none;"' . "\n") .
|
||||
'>' . "\n" .
|
||||
|
||||
$obj->element
|
||||
($element,
|
||||
array('class' => "item contact {$class}",
|
||||
'field_prefix' => 'Contact.%{id}')
|
||||
+ compact('rows', 'fields', 'column_class')) .
|
||||
|
||||
($stype === 'show'
|
||||
? '<input type="hidden" name="data[Contact][%{id}][id]" value="'.$values['id'].'"/>' . "\n"
|
||||
: '') .
|
||||
|
||||
// END sourcetype-div
|
||||
'</div>' . "\n" .
|
||||
'';
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
function addContact(flash) {
|
||||
addDiv('contact-entry-id', 'contact', 'contacts', flash, <?php
|
||||
echo FormatHelper::phpVarToJavascript
|
||||
(customerContactDiv($this),
|
||||
null,
|
||||
' ');
|
||||
?>
|
||||
);
|
||||
}
|
||||
|
||||
// Reset the form
|
||||
function resetForm() {
|
||||
$('#contacts').html('');
|
||||
$('#contact-entry-id').val(1);
|
||||
|
||||
<?php foreach ($this->data['Contact'] AS $contact): ?>
|
||||
addDiv('contact-entry-id', 'contact', 'contacts', false, <?php
|
||||
echo FormatHelper::phpVarToJavascript
|
||||
(customerContactDiv($this,
|
||||
$contact,
|
||||
$contact['id'] == $this->data['Customer']['primary_contact_id']
|
||||
),
|
||||
null,
|
||||
' ');
|
||||
?>
|
||||
);
|
||||
<?php endforeach; ?>
|
||||
|
||||
if ($("#contact-entry-id").val() == 1) {
|
||||
addDiv('contact-entry-id', 'contact', 'contacts', false, <?php
|
||||
echo FormatHelper::phpVarToJavascript
|
||||
(customerContactDiv($this, null, true),
|
||||
null,
|
||||
' ');
|
||||
?>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function switchContactSource(id, source) {
|
||||
$(".contact-"+id+"-div")
|
||||
.slideUp();
|
||||
|
||||
$("#contact-"+id+"-"+source+"-div")
|
||||
.slideDown();
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
resetForm();
|
||||
});
|
||||
|
||||
--></script>
|
||||
|
||||
<?php
|
||||
; // alignment
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Customer Edit
|
||||
*/
|
||||
|
||||
echo '<div class="customer edit">' . "\n";
|
||||
|
||||
echo $form->create('Customer', array('action' => 'edit')) . "\n";
|
||||
echo $form->input('id') . "\n";
|
||||
|
||||
echo($this->element
|
||||
('form_table',
|
||||
array('class' => 'item customer detail',
|
||||
'caption' => isset($this->data['Customer']) ? 'Edit Customer' : 'New Customer',
|
||||
'fields' => array
|
||||
('name' => true,
|
||||
'comment' => true,
|
||||
))) . "\n");
|
||||
|
||||
echo $form->submit('Update') . "\n";
|
||||
?>
|
||||
|
||||
<div CLASS="dynamic-set">
|
||||
<fieldset CLASS="contact superset">
|
||||
<legend>Contacts</legend>
|
||||
<input type="hidden" id="contact-entry-id" value="0">
|
||||
<div id="contacts"></div>
|
||||
<fieldset> <legend>
|
||||
<a href="#" onClick="addContact(true); return false;">Add a Contact</a>
|
||||
</legend> </fieldset>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
; // Alignment
|
||||
|
||||
echo $form->submit('Update') . "\n";
|
||||
echo $form->submit('Cancel', array('name' => 'cancel')) . "\n";
|
||||
echo $form->end() . "\n";
|
||||
echo '</div>' . "\n";
|
||||
@@ -1,503 +0,0 @@
|
||||
<?php /* -*- mode:PHP -*- */ ?>
|
||||
|
||||
<div class="payment input">
|
||||
|
||||
<?php
|
||||
; // Editor alignment
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Transaction Detail Main Section
|
||||
*/
|
||||
|
||||
/* $rows = array(array('ID', $transaction['Transaction']['id']), */
|
||||
/* array('Timestamp', FormatHelper::datetime($transaction['Transaction']['stamp'])), */
|
||||
/* array('Through', FormatHelper::date($transaction['Transaction']['through_date'])), */
|
||||
/* array('Due', FormatHelper::date($transaction['Transaction']['due_date'])), */
|
||||
/* array('Comment', $transaction['Transaction']['comment'])); */
|
||||
|
||||
/* echo $this->element('table', */
|
||||
/* array('class' => 'item transaction detail', */
|
||||
/* 'caption' => 'Transaction Detail', */
|
||||
/* 'rows' => $rows, */
|
||||
/* 'column_class' => array('field', 'value'))); */
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Transaction Info Box
|
||||
*/
|
||||
|
||||
?>
|
||||
|
||||
<!--
|
||||
<DIV CLASS="infobox">
|
||||
<DIV CLASS="summary grand total">
|
||||
Total: <?php /*echo FormatHelper::currency($total);*/ ?>
|
||||
</DIV>
|
||||
</DIV>
|
||||
-->
|
||||
|
||||
|
||||
<?php
|
||||
; // Editor alignment
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
*
|
||||
*/
|
||||
|
||||
$grid_setup = array();
|
||||
|
||||
if (isset($customer['id']))
|
||||
$grid_setup['hiddengrid'] = true;
|
||||
|
||||
$grid_setup['onSelectRow'] = array
|
||||
('--special' =>
|
||||
'function(ids) { if (ids != null) { onRowSelect("#"+$(this).attr("id"), ids); } }'
|
||||
);
|
||||
|
||||
|
||||
// Customer
|
||||
// Outstanding balance
|
||||
// Balance on each lease
|
||||
// Balance on personal account
|
||||
// Multiple fields for payments (cash, check, charge, etc)
|
||||
// How to apply (even split, oldest charges first, etc)
|
||||
|
||||
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
// prepare the form when the DOM is ready
|
||||
$(document).ready(function() {
|
||||
var options = {
|
||||
target: '#output-debug', // target element(s) to be updated with server response
|
||||
beforeSubmit: verifyRequest, // pre-submit callback
|
||||
success: showResponse, // post-submit callback
|
||||
|
||||
// other available options:
|
||||
//url: url, // override for form's 'action' attribute
|
||||
//type: 'get', // 'get' or 'post', override for form's 'method' attribute
|
||||
//dataType: null, // 'xml', 'script', or 'json' (expected server response type)
|
||||
//clearForm: true, // clear all form fields after successful submit
|
||||
//resetForm: true, // reset the form after successful submit
|
||||
|
||||
// $.ajax options can be used here too, for example:
|
||||
//timeout: 3000,
|
||||
};
|
||||
|
||||
// get a clean slate
|
||||
resetPaymentForm();
|
||||
|
||||
// bind form using 'ajaxForm'
|
||||
$('#payment-form').ajaxForm(options);
|
||||
});
|
||||
|
||||
// pre-submit callback
|
||||
function verifyRequest(formData, jqForm, options) {
|
||||
// formData is an array; here we use $.param to convert it to a string to display it
|
||||
// but the form plugin does this for you automatically when it submits the data
|
||||
//var_dump(formData);
|
||||
//$('#request-debug').html('<PRE>'+dump(formData)+'</PRE>');
|
||||
$('#request-debug').html('Ommitted');
|
||||
//return false;
|
||||
|
||||
$('#response-debug').html('Loading <BLINK>...</BLINK>');
|
||||
$('#output-debug').html('Loading <BLINK>...</BLINK>');
|
||||
|
||||
// here we could return false to prevent the form from being submitted;
|
||||
// returning anything other than false will allow the form submit to continue
|
||||
return true;
|
||||
}
|
||||
|
||||
// post-submit callback
|
||||
function showResponse(responseText, statusText) {
|
||||
// for normal html responses, the first argument to the success callback
|
||||
// is the XMLHttpRequest object's responseText property
|
||||
|
||||
// if the ajaxForm method was passed an Options Object with the dataType
|
||||
// property set to 'xml' then the first argument to the success callback
|
||||
// is the XMLHttpRequest object's responseXML property
|
||||
|
||||
// if the ajaxForm method was passed an Options Object with the dataType
|
||||
// property set to 'json' then the first argument to the success callback
|
||||
// is the json data object returned by the server
|
||||
|
||||
if (statusText == 'success') {
|
||||
// get a clean slate
|
||||
//resetPaymentForm();
|
||||
}
|
||||
else {
|
||||
alert('not successful??');
|
||||
}
|
||||
|
||||
|
||||
$('#response-debug').html('<PRE>'+dump(statusText)+'</PRE>');
|
||||
}
|
||||
|
||||
// Reset payment fields
|
||||
function resetPaymentForm() {
|
||||
// Get a clean slate for our payments
|
||||
$('#payments').html('');
|
||||
$('#payment-id').val(0);
|
||||
addPaymentSource(false);
|
||||
}
|
||||
|
||||
function addPaymentSource(flash) {
|
||||
addDiv('payment-id', 'payment', 'payments', flash,
|
||||
// HTML section
|
||||
'<FIELDSET CLASS="payment subset">' +
|
||||
'<LEGEND>Payment #%{id} (%{remove})</LEGEND>' +
|
||||
|
||||
'<DIV ID="payment-type-div-%{id}">' +
|
||||
<?php
|
||||
/* REVISIT <AP> 20090616:
|
||||
* MUST GET THIS FROM THE DATABASE!!
|
||||
* HARDCODED VALUES BAD... VERY BAD...
|
||||
*/
|
||||
$monetary_type_ids = array('Cash' => 2,
|
||||
'Check' => 3,
|
||||
'Money Order' => 4,
|
||||
'ACH' => 5,
|
||||
'Credit Card' => 7,
|
||||
);
|
||||
|
||||
$types = array();
|
||||
foreach(array('Cash', 'Check', 'Money Order', /*'ACH', 'Credit Card'*/) AS $name)
|
||||
$types[preg_replace("/ /", "", strtolower($name))] = $name;
|
||||
|
||||
foreach ($types AS $type => $name) {
|
||||
$div = '<DIV>';
|
||||
$div .= '<INPUT TYPE="hidden" NAME="data[LedgerEntry][%{id}][bogus]" VALUE="1">';
|
||||
$div .= '<INPUT TYPE="radio" NAME="data[LedgerEntry][%{id}][MonetarySource][monetary_type_id]"';
|
||||
$div .= ' ONCLICK="switchPaymentType(%{id}, \\\''.$type.'\\\')"';
|
||||
$div .= ' CLASS="payment-type-%{id}" ID="payment-type-'.$type.'-%{id}"';
|
||||
$div .= ' VALUE="'.$monetary_type_ids[$name].'" ' . ($name == 'Cash' ? 'CHECKED ' : '') . '/>';
|
||||
$div .= ' <LABEL FOR="payment-type-'.$type.'-%{id}">'.$name.'</LABEL>';
|
||||
$div .= '</DIV>';
|
||||
echo "'$div' +\n";
|
||||
}
|
||||
?>
|
||||
'</DIV>' +
|
||||
|
||||
'<DIV ID="payment-amount-div-%{id}" CLASS="input text required">' +
|
||||
' <LABEL FOR="payment-amount-%{id}">Amount</LABEL>' +
|
||||
' <INPUT TYPE="text" SIZE="20"' +
|
||||
' NAME="data[LedgerEntry][%{id}][amount]"' +
|
||||
' ID="payment-amount-%{id}" />' +
|
||||
'</DIV>' +
|
||||
|
||||
<?php
|
||||
foreach ($types AS $type => $name) {
|
||||
if ($type == 'cash')
|
||||
continue;
|
||||
|
||||
$div = '<DIV';
|
||||
$div .= ' ID="payment-'.$type.'-div-%{id}"';
|
||||
$div .= ' CLASS="payment-type-div-%{id}"';
|
||||
$div .= ' STYLE="display:none;">';
|
||||
$div .= '</DIV>';
|
||||
echo "'$div' +\n";
|
||||
}
|
||||
?>
|
||||
|
||||
'</FIELDSET>'
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
function switchPaymentType(paymentid, type) {
|
||||
$(".payment-type-div-"+paymentid).slideUp();
|
||||
$(".payment-type-div-"+paymentid).html('');
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case 'cash':
|
||||
break;
|
||||
|
||||
case 'check':
|
||||
html =
|
||||
'<DIV CLASS="input text required">' +
|
||||
' <LABEL FOR="payment-check-number-'+paymentid+'">Check Number</LABEL>' +
|
||||
// REVISIT <AP>: 20090617: Use comment field for now.
|
||||
' <INPUT TYPE="text" SIZE="6" NAME="data[LedgerEntry]['+paymentid+'][MonetarySource][comment]"' +
|
||||
' ID="payment-check-number-'+paymentid+'" />' +
|
||||
'</DIV>';
|
||||
break;
|
||||
|
||||
case 'moneyorder':
|
||||
html =
|
||||
'<DIV CLASS="input text required">' +
|
||||
' <LABEL FOR="payment-moneyorder-number-'+paymentid+'">Money Order Number</LABEL>' +
|
||||
// REVISIT <AP>: 20090617: Use comment field for now.
|
||||
' <INPUT TYPE="text" SIZE="6" NAME="data[LedgerEntry]['+paymentid+'][MonetarySource][comment]"' +
|
||||
' ID="payment-moneyorder-number-'+paymentid+'" />' +
|
||||
'</DIV>';
|
||||
break;
|
||||
|
||||
case 'ach':
|
||||
html =
|
||||
'<DIV CLASS="input text required">' +
|
||||
' <LABEL FOR="payment-ach-routing-'+paymentid+'">Routing Number</LABEL>' +
|
||||
// REVISIT <AP>: 20090617: Use comment field for now.
|
||||
' <INPUT TYPE="text" SIZE="9" NAME="data[LedgerEntry]['+paymentid+'][MonetarySource][comment]"' +
|
||||
' ID="payment-ach-routing-'+paymentid+'" />' +
|
||||
'</DIV>' +
|
||||
|
||||
'<DIV CLASS="input text required">' +
|
||||
' <LABEL FOR="payment-ach-account-'+paymentid+'">Account Number</LABEL>' +
|
||||
// REVISIT <AP>: 20090617: Use comment field for now.
|
||||
' <INPUT TYPE="text" SIZE="17" NAME="data[LedgerEntry]['+paymentid+'][MonetarySource][comment]"' +
|
||||
' ID="payment-ach-account-'+paymentid+'" />' +
|
||||
'</DIV>';
|
||||
break;
|
||||
|
||||
case 'creditcard':
|
||||
html =
|
||||
'<DIV CLASS="input text required">' +
|
||||
' <LABEL FOR="payment-creditcard-account-'+paymentid+'">Account Number</LABEL>' +
|
||||
// REVISIT <AP>: 20090617: Use comment field for now.
|
||||
' <INPUT TYPE="text" SIZE="16" NAME="data[LedgerEntry]['+paymentid+'][MonetarySource][comment]' +
|
||||
' ID="payment-creditcard-account-'+paymentid+'" />' +
|
||||
'</DIV>' +
|
||||
|
||||
'<DIV CLASS="input text required">' +
|
||||
' <LABEL FOR="payment-creditcard-expiration-'+paymentid+'">Expiration Date</LABEL>' +
|
||||
// REVISIT <AP>: 20090617: Use comment field for now.
|
||||
' <INPUT TYPE="text" SIZE="10" NAME="data[LedgerEntry]['+paymentid+'][MonetarySource][comment]' +
|
||||
' ID="payment-creditcard-expiration-'+paymentid+'" />' +
|
||||
' </DIV>' +
|
||||
|
||||
'<DIV CLASS="input text required">' +
|
||||
' <LABEL FOR="payment-creditcard-cvv2-'+paymentid+'">CVV2 Code</LABEL>' +
|
||||
// REVISIT <AP>: 20090617: Use comment field for now.
|
||||
' <INPUT TYPE="text" SIZE="10" NAME=data[LedgerEntry]['+paymentid+'][MonetarySource][comment]' +
|
||||
' ID="payment-creditcard-cvv2-'+paymentid+'" />' +
|
||||
'</DIV>';
|
||||
break;
|
||||
|
||||
default:
|
||||
html = '<DIV><H2>INVALID TYPE ('+type+')</H2></DIV>';
|
||||
break;
|
||||
}
|
||||
|
||||
$("#payment-"+type+"-div-"+paymentid).html(html);
|
||||
$("#payment-"+type+"-div-"+paymentid).slideDown();
|
||||
}
|
||||
|
||||
|
||||
function updateChargesCaption(customer_name, balance) {
|
||||
$('#charge-entries-jqGrid').setCaption('Outstanding Charges for ' +
|
||||
customer_name + ': ' +
|
||||
fmtCurrency(balance));
|
||||
}
|
||||
|
||||
function updateChargesGrid(idlist, balance) {
|
||||
updateChargesCaption($("#payment_customer").html(), balance);
|
||||
|
||||
$('#charge-entries-jqGrid').setPostDataItem('idlist', serialize(idlist));
|
||||
$('#charge-entries-jqGrid')
|
||||
.setGridParam({ page: 1 })
|
||||
.trigger("reloadGrid");
|
||||
}
|
||||
|
||||
function updateCharges(id) {
|
||||
var url = '<?php echo ($html->url(array("controller" => $this->params["controller"],
|
||||
"action" => "unreconciled"))); ?>';
|
||||
//url += '/'+$("#customer-id").val();
|
||||
url += '/'+id;
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: url,
|
||||
dataType: "xml",
|
||||
success: function(xml) {
|
||||
var ids = new Array();
|
||||
$('#update-target ol').html('<A HREF="'+url+'">Data URL</A>');
|
||||
$('entry',xml).each(function(i){
|
||||
ids.push($(this).attr('id'));
|
||||
$('#update-target').append("Push: len=" + ids.length + '<BR>');
|
||||
});
|
||||
updateChargesGrid(ids, $('entries',xml).attr('balance'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onRowSelect(grid_id, cust_id) {
|
||||
// Set the customer id that will be returned with the form
|
||||
$("#customer-id").val(cust_id);
|
||||
|
||||
// Get the customer name from the grid
|
||||
$("#payment_customer").html($(grid_id).getCell(cust_id, "Customer-name"));
|
||||
|
||||
// Replace that with just the text portion of the hyperlink
|
||||
$("#payment_customer").html($("#payment_customer a").html());
|
||||
|
||||
// Hide the "no customer" message and show the current customer
|
||||
$("#no_customer").hide();
|
||||
$("#current_customer").show();
|
||||
|
||||
updateCharges(cust_id);
|
||||
}
|
||||
|
||||
--></script>
|
||||
|
||||
<?php
|
||||
; // align
|
||||
//echo '<DIV ID="dialog">' . "\n";
|
||||
|
||||
echo $this->element('customers',
|
||||
array('grid_div_id' => 'customers-list',
|
||||
'caption' => ('<A HREF="#" ONCLICK="$(\'#customers-list .HeaderButton\').click();'.
|
||||
' return false;">Select Customer</A>'),
|
||||
'grid_setup' => $grid_setup,
|
||||
));
|
||||
|
||||
echo $this->element('ledger_entries',
|
||||
array('grid_div_id' => 'charge-entries',
|
||||
'caption' => 'Outstanding Charges',
|
||||
'account_ftype' => 'credit',
|
||||
'ledger_entries' => $charges['entry'],
|
||||
'limit' => 8,
|
||||
));
|
||||
|
||||
echo ('<H2>' .
|
||||
'<SPAN id="current_customer" style="display:'.(isset($customer['id'])?"inline":"none").'">' .
|
||||
'Enter new receipt for ' .
|
||||
'<SPAN id="payment_customer">' . (isset($customer['name']) ? $customer['name'] : "") . '</SPAN>' .
|
||||
'</SPAN>' .
|
||||
'<SPAN id="no_customer" style="display:'.(isset($customer['id'])?"none":"inline").'">' .
|
||||
'Please select customer' .
|
||||
'</SPAN>' .
|
||||
'</H2>' . "\n");
|
||||
|
||||
echo $form->create(null, array('id' => 'payment-form',
|
||||
'url' => array('controller' => 'transactions',
|
||||
'action' => 'postReceipt')));
|
||||
|
||||
/***************************************************
|
||||
* Post data should look like this:
|
||||
*
|
||||
* data => Array
|
||||
* (
|
||||
* [Transaction] => Array
|
||||
* (
|
||||
* stamp => <Transaction Time Stamp>
|
||||
* through_date => <Transaction Through Date, if any>
|
||||
* due_date => <Transaction Due Date, if any>
|
||||
* comment => <Transaction Comment>
|
||||
* )
|
||||
*
|
||||
* [LedgerEntry] => Array
|
||||
* (
|
||||
* [0] => Array
|
||||
* (
|
||||
* name => <NOT TO INCLUDE??>
|
||||
* amount => <Payment Amount>
|
||||
* debit_ledger_id => <ID of the debit account's current ledger>
|
||||
* credit_ledger_id => <ID of the debit account's current ledger>
|
||||
* comment => <Ledger Entry Comment>
|
||||
*
|
||||
* [MonetarySource] => Array
|
||||
* (
|
||||
* name => <Name (may be useless parameter)>
|
||||
* monetary_type_id => <Monetary Type ID>
|
||||
* comment => <Monetary Source Comment>
|
||||
* )
|
||||
*
|
||||
* REVISIT: Reconciliations will be tricker.
|
||||
* )
|
||||
*
|
||||
* )
|
||||
*
|
||||
* )
|
||||
*
|
||||
***************************************************/
|
||||
|
||||
?>
|
||||
<input type="hidden" id="customer-id" name="data[customer_id]" value="<?php echo $customer['id']; ?>">
|
||||
|
||||
<fieldset CLASS="payment superset">
|
||||
<legend>Payments</legend>
|
||||
<input type="hidden" id="payment-id" value="0">
|
||||
<div id="payments"></div>
|
||||
<fieldset> <legend>
|
||||
<a href="#" onClick="addPaymentSource(true); return false;">Add Another Payment</a>
|
||||
</legend> </fieldset>
|
||||
</fieldset>
|
||||
|
||||
<?php
|
||||
|
||||
echo 'Date: <input id="datepicker" name="data[Transaction][stamp]" type="text" /><BR>' . "\n";
|
||||
echo 'Comment: <input id="comment" name="data[Transaction][comment]" type="text" SIZE=80 /><BR>' . "\n";
|
||||
echo $form->end('Post Payment');
|
||||
|
||||
//echo '</DIV>' . "\n"; // End of the dialog DIV
|
||||
?>
|
||||
|
||||
<?php
|
||||
/* <button id="post-payment" class="ui-button ui-state-default ui-corner-all">Create Payment</button> */
|
||||
?>
|
||||
|
||||
<div><H4>Request</H4><div id="request-debug"></div></div>
|
||||
<div><H4>Response</H4><div id="response-debug"></div></div>
|
||||
<div><H4>Output</H4><div id="output-debug"></div></div>
|
||||
<?php
|
||||
|
||||
?>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$(document).ready(function(){
|
||||
$("#datepicker").datepicker()
|
||||
.datepicker('setDate', '+0');
|
||||
|
||||
<?php if (isset($customer['id'])) { ?>
|
||||
$("#customer-id").val(<?php echo $customer['id']; ?>);
|
||||
updateChargesCaption("<?php echo $customer['name']; ?>",
|
||||
<?php echo $charges['balance']; ?>);
|
||||
<?php } ?>
|
||||
|
||||
|
||||
/* $("#dialog").dialog({ */
|
||||
/* bgiframe: true, */
|
||||
/* autoOpen: false, */
|
||||
/* height: 500, */
|
||||
/* width: 600, */
|
||||
/* modal: true, */
|
||||
/* buttons: { */
|
||||
/* 'Post a Payment': function() { */
|
||||
/* var bValid = true; */
|
||||
/* if (bValid) { */
|
||||
/* $('#debug').append('<H2>POSTED!</H2>'); */
|
||||
/* $(this).dialog('close'); */
|
||||
/* } */
|
||||
/* }, */
|
||||
/* Cancel: function() { */
|
||||
/* $(this).dialog('close'); */
|
||||
/* } */
|
||||
/* }, */
|
||||
/* close: function() { */
|
||||
/* } */
|
||||
/* }); */
|
||||
|
||||
/* $('#post-payment').click(function() { */
|
||||
/* $('#dialog').dialog('open'); */
|
||||
/* }); */
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#" onClick="$('#debug').html(''); return false;">Clear Debug Output</a>
|
||||
453
site/views/customers/receipt.ctp
Normal file
453
site/views/customers/receipt.ctp
Normal file
@@ -0,0 +1,453 @@
|
||||
<?php /* -*- mode:PHP -*- */ ?>
|
||||
|
||||
<div class="receipt input">
|
||||
<?php
|
||||
; // Editor alignment
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Javascript
|
||||
*/
|
||||
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
// prepare the form when the DOM is ready
|
||||
$(document).ready(function() {
|
||||
var options = {
|
||||
target: '#output-debug', // target element(s) to be updated with server response
|
||||
beforeSubmit: verifyRequest, // pre-submit callback
|
||||
success: showResponse, // post-submit callback
|
||||
|
||||
// other available options:
|
||||
//url: url, // override for form's 'action' attribute
|
||||
//type: 'get', // 'get' or 'post', override for form's 'method' attribute
|
||||
//dataType: null, // 'xml', 'script', or 'json' (expected server response type)
|
||||
//clearForm: true, // clear all form fields after successful submit
|
||||
//resetForm: true, // reset the form after successful submit
|
||||
|
||||
// $.ajax options can be used here too, for example:
|
||||
//timeout: 3000,
|
||||
};
|
||||
|
||||
// bind form using 'ajaxForm'
|
||||
$('#receipt-form').ajaxForm(options);
|
||||
});
|
||||
|
||||
// pre-submit callback
|
||||
function verifyRequest(formData, jqForm, options) {
|
||||
// formData is an array; here we use $.param to convert it to a string to display it
|
||||
// but the form plugin does this for you automatically when it submits the data
|
||||
//var_dump(formData);
|
||||
//$('#request-debug').html('<PRE>'+dump(formData)+'</PRE>');
|
||||
$('#request-debug').html('Ommitted');
|
||||
//return false;
|
||||
|
||||
$('#response-debug').html('Loading <BLINK>...</BLINK>');
|
||||
$('#output-debug').html('Loading <BLINK>...</BLINK>');
|
||||
|
||||
// here we could return false to prevent the form from being submitted;
|
||||
// returning anything other than false will allow the form submit to continue
|
||||
return true;
|
||||
}
|
||||
|
||||
// post-submit callback
|
||||
function showResponse(responseText, statusText) {
|
||||
// for normal html responses, the first argument to the success callback
|
||||
// is the XMLHttpRequest object's responseText property
|
||||
|
||||
// if the ajaxForm method was passed an Options Object with the dataType
|
||||
// property set to 'xml' then the first argument to the success callback
|
||||
// is the XMLHttpRequest object's responseXML property
|
||||
|
||||
// if the ajaxForm method was passed an Options Object with the dataType
|
||||
// property set to 'json' then the first argument to the success callback
|
||||
// is the json data object returned by the server
|
||||
|
||||
if (statusText == 'success') {
|
||||
// get a clean slate
|
||||
//resetForm();
|
||||
}
|
||||
else {
|
||||
alert('not successful??');
|
||||
}
|
||||
|
||||
|
||||
$('#response-debug').html('<PRE>'+dump(statusText)+'</PRE>');
|
||||
}
|
||||
|
||||
// Reset the form
|
||||
function resetForm() {
|
||||
$('#payment-entry-id').val(1);
|
||||
$('#payments').html('');
|
||||
|
||||
$("#receipt-customer-id").html("INTERNAL ERROR");
|
||||
$("#receipt-customer-name").html("INTERNAL ERROR");
|
||||
$("#receipt-balance").html("INTERNAL ERROR");
|
||||
$("#receipt-charges-caption").html("Outstanding Charges");
|
||||
|
||||
addPaymentSource(false);
|
||||
datepickerNow('TransactionStamp');
|
||||
}
|
||||
|
||||
function onRowSelect(grid_id, customer_id) {
|
||||
// Set the customer id that will be returned with the form
|
||||
$("#customer-id").val(customer_id);
|
||||
|
||||
// Get the item names from the grid
|
||||
//$("#receipt-customer-id").html($(grid_id).getCell(customer_id, 'Customer-id'));
|
||||
// REVISIT <AP>: 20090708
|
||||
// This is not intended as a long term solution,
|
||||
// but I need a way to enter data and then view
|
||||
// the results. This link will help.
|
||||
$("#receipt-customer-id").html('<A HREF="/pmgr/site/customers/view/' +
|
||||
$(grid_id).getCell(customer_id, 'Customer-id').replace(/^#/,'') +
|
||||
'">' +
|
||||
$(grid_id).getCell(customer_id, 'Customer-id') +
|
||||
'</A>');
|
||||
$("#receipt-customer-name").html($(grid_id).getCell(customer_id, 'Customer-name'));
|
||||
$("#receipt-balance").html("Calculating...");
|
||||
$("#receipt-charges-caption").html("Please Wait...");
|
||||
|
||||
// Hide the "no customer" message and show the current customer
|
||||
$(".customer-selection-invalid").hide();
|
||||
$(".customer-selection-valid").show();
|
||||
|
||||
// Update the charges grid to reflect this customer
|
||||
updateCharges(customer_id);
|
||||
|
||||
// Collapse the grid now that the user has selected
|
||||
$("#customers-list .HeaderButton").click();
|
||||
}
|
||||
|
||||
function onGridState(grid_id, state) {
|
||||
if (state == 'visible') {
|
||||
$(".customer-selection-invalid").hide();
|
||||
$(".customer-selection-valid").hide();
|
||||
}
|
||||
else {
|
||||
if ($("#customer-id").val() > 0) {
|
||||
$(".customer-selection-valid").show();
|
||||
$(".customer-selection-invalid").hide();
|
||||
} else {
|
||||
$(".customer-selection-valid").hide();
|
||||
$(".customer-selection-invalid").show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addPaymentSource(flash) {
|
||||
addDiv('payment-entry-id', 'payment', 'payments', flash,
|
||||
// HTML section
|
||||
'<FIELDSET CLASS="payment subset">' +
|
||||
'<LEGEND>Payment #%{id} (%{remove})</LEGEND>' +
|
||||
|
||||
'<DIV ID="payment-div-%{id}">' +
|
||||
<?php
|
||||
echo FormatHelper::phpVarToJavascript
|
||||
($form->input('LedgerEntry.%{id}.account_id',
|
||||
array('type' => 'radio',
|
||||
'separator' => '<BR>',
|
||||
'onclick' => ('switchPaymentType(' .
|
||||
'\\\'payment-type-div\\\', ' .
|
||||
'%{id}, ' .
|
||||
'$(this).attr("id")' .
|
||||
')' .
|
||||
//' return false;'
|
||||
''
|
||||
),
|
||||
'legend' => false,
|
||||
'value' => $defaultAccount,
|
||||
'options' => $paymentAccounts))) . "+\n";
|
||||
?>
|
||||
'</DIV>' +
|
||||
|
||||
'<DIV ID="payment-amount-div-%{id}" CLASS="input text required">' +
|
||||
' <LABEL FOR="payment-amount-%{id}">Amount</LABEL>' +
|
||||
' <INPUT TYPE="text" SIZE="20"' +
|
||||
' NAME="data[LedgerEntry][%{id}][amount]"' +
|
||||
' ID="payment-amount-%{id}" />' +
|
||||
'</DIV>' +
|
||||
|
||||
<?php
|
||||
foreach ($paymentAccounts AS $account_id => $name) {
|
||||
$div = '<DIV';
|
||||
$div .= ' ID="payment-type-div-%{id}-'.$account_id.'"';
|
||||
$div .= ' CLASS="payment-type-div-%{id}"';
|
||||
$div .= ' STYLE="display:none;">';
|
||||
|
||||
if ($name == 'Check') {
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <LABEL FOR="payment-check-number-%{id}">Check Number</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="6" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data1]"';
|
||||
$div .= ' ID="payment-check-number-%{id}" />';
|
||||
$div .= '</DIV>';
|
||||
}
|
||||
elseif ($name == 'Money Order') {
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <LABEL FOR="payment-moneyorder-number-%{id}">Money Order Number</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="6" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data1]"';
|
||||
$div .= ' ID="payment-moneyorder-number-%{id}" />';
|
||||
$div .= '</DIV>';
|
||||
}
|
||||
elseif ($name == 'ACH') {
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <LABEL FOR="payment-ach-routing-%{id}">Routing Number</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="9" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data1]"';
|
||||
$div .= ' ID="payment-ach-routing-%{id}" />';
|
||||
$div .= '</DIV>';
|
||||
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <LABEL FOR="payment-ach-account-%{id}">Account Number</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="17" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data2]"';
|
||||
$div .= ' ID="payment-ach-account-%{id}" />';
|
||||
$div .= '</DIV>';
|
||||
}
|
||||
elseif ($name == 'Credit Card') {
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <LABEL FOR="payment-creditcard-account-%{id}">Account Number</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="16" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data1]"';
|
||||
$div .= ' ID="payment-creditcard-account-%{id}" />';
|
||||
$div .= '</DIV>';
|
||||
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <LABEL FOR="payment-creditcard-expiration-%{id}">Expiration Date</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="10" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data2]"';
|
||||
$div .= ' ID="payment-creditcard-expiration-%{id}" />';
|
||||
$div .= ' </DIV>';
|
||||
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <LABEL FOR="payment-creditcard-cvv2-%{id}">CVV2 Code</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="10" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data3]"';
|
||||
$div .= ' ID="payment-creditcard-cvv2-%{id}" />';
|
||||
$div .= '</DIV>';
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
|
||||
$div .= '</DIV>';
|
||||
echo "'$div' +\n";
|
||||
}
|
||||
?>
|
||||
|
||||
'</FIELDSET>'
|
||||
);
|
||||
}
|
||||
|
||||
function switchPaymentType(paymentid_base, paymentid, radioid) {
|
||||
$("."+paymentid_base+"-"+paymentid).slideUp();
|
||||
var account_id = $("#"+radioid).val();
|
||||
$("#"+paymentid_base+"-"+paymentid+"-"+account_id).slideDown();
|
||||
}
|
||||
|
||||
|
||||
function updateChargesGrid(idlist) {
|
||||
$('#charge-entries-jqGrid').setPostDataItem('idlist', serialize(idlist));
|
||||
$('#charge-entries-jqGrid')
|
||||
.setGridParam({ page: 1 })
|
||||
.trigger("reloadGrid");
|
||||
}
|
||||
|
||||
function updateCharges(id) {
|
||||
var url = '<?php echo ($html->url(array("controller" => $this->params["controller"],
|
||||
"action" => "unreconciled"))); ?>';
|
||||
url += '/'+id;
|
||||
|
||||
$('#charge-entries-jqGrid').clearGridData();
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: url,
|
||||
dataType: "xml",
|
||||
success: function(xml) {
|
||||
var ids = new Array();
|
||||
$('entry',xml).each(function(i){
|
||||
ids.push($(this).attr('id'));
|
||||
});
|
||||
$('#receipt-balance').html(fmtCurrency($('entries',xml).attr('balance')));
|
||||
$("#receipt-charges-caption").html("Outstanding Charges");
|
||||
updateChargesGrid(ids);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
--></script>
|
||||
|
||||
<?php
|
||||
; // align
|
||||
//echo '<DIV ID="dialog">' . "\n";
|
||||
|
||||
echo $this->element('customers', array
|
||||
('config' => array
|
||||
('grid_div_id' => 'customers-list',
|
||||
'grid_div_class' => 'text-below',
|
||||
'caption' => ('<A HREF="#" ONCLICK="$(\'#customers-list .HeaderButton\').click();'.
|
||||
' return false;">Select Customer</A>'),
|
||||
'grid_setup' => array('hiddengrid' => isset($customer['id'])),
|
||||
'grid_events' => array('onSelectRow' =>
|
||||
array('ids' =>
|
||||
'if (ids != null){onRowSelect("#"+$(this).attr("id"), ids);}'),
|
||||
'onHeaderClick' =>
|
||||
array('gridstate' =>
|
||||
'onGridState("#"+$(this).attr("id"), gridstate)'),
|
||||
),
|
||||
'nolinks' => true,
|
||||
'limit' => 10,
|
||||
)));
|
||||
|
||||
echo ('<DIV CLASS="receipt grid-selection-text">' .
|
||||
|
||||
'<DIV CLASS="customer-selection-valid" style="display:none">' .
|
||||
'Customer <SPAN id="receipt-customer-id"></SPAN>' .
|
||||
': <SPAN id="receipt-customer-name"></SPAN>' .
|
||||
|
||||
/* '<DIV CLASS="supporting">' . */
|
||||
/* '<TABLE>' . */
|
||||
/* '<TR><TD CLASS="field">Balance:</TD><TD CLASS="value"><SPAN id="receipt-balance"></SPAN></TD></TR>' . */
|
||||
/* '</TABLE>' . */
|
||||
/* '</DIV>' . */
|
||||
|
||||
'</DIV>' . // END customer-selection-valid
|
||||
|
||||
'<DIV CLASS="customer-selection-invalid" style="display:none">' .
|
||||
'Please select customer' .
|
||||
'</DIV>' .
|
||||
|
||||
'</DIV>' . "\n");
|
||||
|
||||
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'account_ftype' => 'credit',
|
||||
'limit' => 8,
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'grid_div_id' => 'charge-entries',
|
||||
'grid_div_class' => 'text-below',
|
||||
'caption' => '<SPAN id="receipt-charges-caption"></SPAN>',
|
||||
'rows' => $charges['entry'],
|
||||
),
|
||||
));
|
||||
|
||||
echo('<DIV CLASS="receipt grid-selection-text">' .
|
||||
'<DIV CLASS="customer-selection-valid" style="display:none">' .
|
||||
|
||||
//'<DIV CLASS="supporting">' .
|
||||
'<TABLE ID="supporting-table">' .
|
||||
'<TR><TD CLASS="field">Balance:</TD><TD CLASS="value"><SPAN id="receipt-balance"></SPAN></TD></TR>' .
|
||||
'</TABLE>' .
|
||||
//'</DIV>' .
|
||||
|
||||
'</DIV>' . // END customer-selection-valid
|
||||
'</DIV>' .
|
||||
"\n");
|
||||
|
||||
echo $form->create(null, array('id' => 'receipt-form',
|
||||
'url' => array('controller' => 'transactions',
|
||||
'action' => 'postReceipt')));
|
||||
|
||||
|
||||
echo $form->input("id",
|
||||
array('id' => 'customer-id',
|
||||
'type' => 'hidden',
|
||||
'value' => 0));
|
||||
|
||||
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->submit('Generate Receipt') . "\n";
|
||||
?>
|
||||
|
||||
<fieldset CLASS="payment superset">
|
||||
<legend>Payments</legend>
|
||||
<input type="hidden" id="payment-entry-id" value="0">
|
||||
<div id="payments"></div>
|
||||
<fieldset> <legend>
|
||||
<a href="#" onClick="addPaymentSource(true); return false;">Add Another Payment</a>
|
||||
</legend> </fieldset>
|
||||
</fieldset>
|
||||
|
||||
<?php echo $form->end('Generate Receipt'); ?>
|
||||
|
||||
<?php /* echo '</DIV>' . "\n"; // End of the dialog DIV */ ?>
|
||||
|
||||
<div><H4>Request</H4><div id="request-debug"></div></div>
|
||||
<div><H4>Response</H4><div id="response-debug"></div></div>
|
||||
<div><H4>Output</H4><div id="output-debug"></div></div>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
$(document).ready(function(){
|
||||
$("#TransactionStamp")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
|
||||
resetForm();
|
||||
|
||||
<?php if (isset($customer['id'])): ?>
|
||||
$("#customer-id").val(<?php echo $customer['id']; ?>);
|
||||
//$("#receipt-customer-id").html("<?php echo '#'.$customer['id']; ?>");
|
||||
$("#receipt-customer-id").html('<A HREF="/pmgr/site/customers/view/' +
|
||||
"<?php echo $customer['id']; ?>" +
|
||||
'">#' +
|
||||
"<?php echo $customer['id']; ?>" +
|
||||
'</A>');
|
||||
$("#receipt-customer-name").html("<?php echo $customer['name']; ?>");
|
||||
$("#receipt-balance").html(fmtCurrency("<?php echo $charges['balance']; ?>"));
|
||||
onGridState(null, 'hidden');
|
||||
<?php else: ?>
|
||||
onGridState(null, 'visible');
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
|
||||
/* $("#dialog").dialog({ */
|
||||
/* bgiframe: true, */
|
||||
/* autoOpen: false, */
|
||||
/* height: 500, */
|
||||
/* width: 600, */
|
||||
/* modal: true, */
|
||||
/* buttons: { */
|
||||
/* 'Post a Payment': function() { */
|
||||
/* var bValid = true; */
|
||||
/* if (bValid) { */
|
||||
/* $('#debug').append('<H2>POSTED!</H2>'); */
|
||||
/* $(this).dialog('close'); */
|
||||
/* } */
|
||||
/* }, */
|
||||
/* Cancel: function() { */
|
||||
/* $(this).dialog('close'); */
|
||||
/* } */
|
||||
/* }, */
|
||||
/* close: function() { */
|
||||
/* } */
|
||||
/* }); */
|
||||
|
||||
/* $('#post-payment').click(function() { */
|
||||
/* $('#dialog').dialog('open'); */
|
||||
/* }); */
|
||||
|
||||
});
|
||||
--></script>
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#" onClick="$('#debug').html(''); return false;">Clear Debug Output</a>
|
||||
@@ -14,7 +14,7 @@ $rows = array(array('Name', $customer['Customer']['name']),
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item customer detail',
|
||||
'caption' => 'Tenant Info',
|
||||
'caption' => 'Customer Info',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value')));
|
||||
|
||||
@@ -49,28 +49,39 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
/**********************************************************************
|
||||
* Contacts
|
||||
*/
|
||||
echo $this->element('contacts',
|
||||
array('caption' => 'Customer Contacts',
|
||||
'contacts' => $customer['Contact']));
|
||||
|
||||
echo $this->element('contacts', array
|
||||
('config' => array
|
||||
('caption' => 'Customer Contacts',
|
||||
'rows' => $customer['Contact'],
|
||||
)));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Lease History
|
||||
*/
|
||||
echo $this->element('leases',
|
||||
array('caption' => 'Lease History',
|
||||
'leases' => $customer['Lease']));
|
||||
|
||||
echo $this->element('leases', array
|
||||
('config' => array
|
||||
('caption' => 'Lease History',
|
||||
'rows' => $customer['Lease'],
|
||||
)));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Customer Account History
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries',
|
||||
array('caption' => 'Account',
|
||||
'customer_id' => $customer['Customer']['id'],
|
||||
'ar_account' => true,
|
||||
));
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'customer_id' => $customer['Customer']['id'],
|
||||
'ar_account' => true,
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Account',
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
|
||||
@@ -3,27 +3,21 @@
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['ID'] = array('index' => 'Account.id', 'formatter' => 'id');
|
||||
$cols['Name'] = array('index' => 'Account.name', 'formatter' => 'name', 'width' => '250');
|
||||
$cols['Type'] = array('index' => 'Account.type', 'width' => '60');
|
||||
$cols['Name'] = array('index' => 'Account.name', 'formatter' => 'longname');
|
||||
$cols['Type'] = array('index' => 'Account.type', 'formatter' => 'name');
|
||||
$cols['Entries'] = array('index' => 'entries', 'width' => '60', 'align' => 'right');
|
||||
$cols['Debits'] = array('index' => 'debits', 'formatter' => 'currency');
|
||||
$cols['Credits'] = array('index' => 'credits', 'formatter' => 'currency');
|
||||
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency');
|
||||
$cols['Comment'] = array('index' => 'Account.comment', 'formatter' => 'comment');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'accounts',
|
||||
'caption' => isset($caption) ? $caption : null);
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Name'));
|
||||
|
||||
if (isset($accounts)) {
|
||||
$jqGrid_options += array('custom_ids' =>
|
||||
array_map(create_function('$data',
|
||||
'return $data["id"];'),
|
||||
$accounts),
|
||||
'limit' => 5);
|
||||
}
|
||||
else {
|
||||
$jqGrid_options += array('search_fields' => array('Name'));
|
||||
}
|
||||
|
||||
echo $this->element('jqGrid', $jqGrid_options);
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Name')
|
||||
->defaultFields(array('ID', 'Name'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
|
||||
@@ -12,19 +12,13 @@ if (0) { // REVISIT<AP>: Need to figure out how to put this in play
|
||||
}
|
||||
$cols['Comment'] = array('index' => 'Contact.comment', 'formatter' => 'comment');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'contacts',
|
||||
'caption' => isset($caption) ? $caption : null);
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Last Name', 'First Name'));
|
||||
|
||||
if (isset($contacts)) {
|
||||
$jqGrid_options += array('custom_ids' =>
|
||||
array_map(create_function('$data',
|
||||
'return $data["id"];'),
|
||||
$contacts),
|
||||
'limit' => 5);
|
||||
}
|
||||
else {
|
||||
$jqGrid_options += array('search_fields' => array('Last Name', 'First Name'));
|
||||
}
|
||||
|
||||
echo $this->element('jqGrid', $jqGrid_options);
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Last Name')
|
||||
->defaultFields(array('ID', 'Last Name', 'First Name'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
|
||||
@@ -9,26 +9,16 @@ $cols['Name'] = array('index' => 'Customer.name', 'formatter
|
||||
$cols['Last Name'] = array('index' => 'PrimaryContact.last_name', 'formatter' => 'name');
|
||||
$cols['First Name'] = array('index' => 'PrimaryContact.first_name', 'formatter' => 'name');
|
||||
$cols['Leases'] = array('index' => 'lease_count', 'width' => '60');
|
||||
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency', 'sortable' => false);
|
||||
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency');
|
||||
$cols['Comment'] = array('index' => 'Customer.comment', 'formatter' => 'comment');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'customers');
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Last Name', 'First Name'));
|
||||
|
||||
// User requested options have priority
|
||||
$jqGrid_options += compact('grid_div_id', 'grid_id', 'caption', 'grid_setup', 'limit');
|
||||
|
||||
if (isset($customers)) {
|
||||
$jqGrid_options += array('custom_ids' =>
|
||||
array_map(create_function('$data',
|
||||
'return $data["id"];'),
|
||||
$customers),
|
||||
'limit' => 5);
|
||||
}
|
||||
|
||||
// Not the long term solution here... just for testing
|
||||
if (isset($searchfields)) {
|
||||
$jqGrid_options += array('search_fields' => array('Last Name', 'First Name'));
|
||||
}
|
||||
|
||||
echo $this->element('jqGrid', $jqGrid_options);
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Name')
|
||||
->defaultFields(array('ID', 'Name'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
|
||||
129
site/views/elements/form_table.ctp
Normal file
129
site/views/elements/form_table.ctp
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
/* form_table.ctp */
|
||||
/*
|
||||
*
|
||||
* @filesource
|
||||
* @copyright Copyright 2009, Abijah Perkins
|
||||
* @package pmgr
|
||||
*/
|
||||
|
||||
$include_before = false;
|
||||
if (isset($before) && $before)
|
||||
$include_before = true;
|
||||
foreach ($fields AS $field => $config) {
|
||||
if (isset($config['before']))
|
||||
$include_before = true;
|
||||
}
|
||||
|
||||
$include_between = false;
|
||||
if (isset($between) && $between)
|
||||
$include_between = true;
|
||||
foreach ($fields AS $field => $config) {
|
||||
if (isset($config['between']))
|
||||
$include_between = true;
|
||||
}
|
||||
|
||||
$include_after = false;
|
||||
if (isset($after) && $after)
|
||||
$include_after = true;
|
||||
foreach ($fields AS $field => $config) {
|
||||
if (isset($config['after']))
|
||||
$include_after = true;
|
||||
}
|
||||
|
||||
|
||||
$column_class = array();
|
||||
if ($include_before)
|
||||
$column_class[] = 'before';
|
||||
$column_class[] = 'field';
|
||||
if ($include_between)
|
||||
$column_class[] = 'between';
|
||||
$column_class[] = 'value';
|
||||
if ($include_after)
|
||||
$column_class[] = 'after';
|
||||
|
||||
$rows = array();
|
||||
foreach ($fields AS $field => $config) {
|
||||
if (!isset($config))
|
||||
continue;
|
||||
|
||||
if (is_bool($config) && !$config)
|
||||
continue;
|
||||
|
||||
if (is_bool($config) && $config)
|
||||
$config = array();
|
||||
|
||||
if (!isset($config['name']))
|
||||
$config['name'] = implode(' ', array_map('ucfirst', explode('_', $field)));
|
||||
if (!isset($config['opts']))
|
||||
$config['opts'] = null;
|
||||
|
||||
if (isset($config['prefix']) && !isset($config['no_prefix']))
|
||||
$field = $config['prefix'] . '.' . $field;
|
||||
elseif (isset($field_prefix) && !isset($config['no_prefix']))
|
||||
$field = $field_prefix . '.' . $field;
|
||||
|
||||
if (!isset($config['opts']['label']))
|
||||
$config['opts']['label'] = false;
|
||||
if (!isset($config['opts']['div']))
|
||||
$config['opts']['div'] = false;
|
||||
|
||||
$cells = array();
|
||||
if ($include_before) {
|
||||
if (isset($config['before']))
|
||||
$cells[] = $config['before'];
|
||||
elseif (isset($before) && $before)
|
||||
$cells[] = $before;
|
||||
else
|
||||
$cells[] = null;
|
||||
}
|
||||
|
||||
$name = $config['name'];
|
||||
if (isset($config['with_name_before']))
|
||||
$name = $config['with_name_before'] . $name;
|
||||
elseif (isset($with_name_before))
|
||||
$name = $with_name_before . $name;
|
||||
if (isset($config['with_name_after']))
|
||||
$name = $name . $config['with_name_after'];
|
||||
elseif (isset($with_name_after))
|
||||
$name = $name . $with_name_after;
|
||||
$cells[] = $name;
|
||||
|
||||
if ($include_between) {
|
||||
if (isset($config['between']))
|
||||
$cells[] = $config['between'];
|
||||
elseif (isset($between) && $between)
|
||||
$cells[] = $between;
|
||||
else
|
||||
$cells[] = null;
|
||||
}
|
||||
|
||||
$value = $form->input($field, $config['opts']);
|
||||
if (isset($config['with_value_before']))
|
||||
$value = $config['with_value_before'] . $value;
|
||||
elseif (isset($with_value_before))
|
||||
$value = $with_value_before . $value;
|
||||
if (isset($config['with_value_after']))
|
||||
$value = $value . $config['with_value_after'];
|
||||
elseif (isset($with_value_after))
|
||||
$value = $valeu . $with_value_after;
|
||||
$cells[] = $value;
|
||||
|
||||
if ($include_after) {
|
||||
if (isset($config['after']))
|
||||
$cells[] = $config['after'];
|
||||
elseif (isset($after) && $after)
|
||||
$cells[] = $after;
|
||||
else
|
||||
$cells[] = null;
|
||||
}
|
||||
|
||||
$rows[] = $cells;
|
||||
}
|
||||
|
||||
echo $this->element('table',
|
||||
compact('class', 'caption', 'headers',
|
||||
'rows', 'row_class', 'suppress_alternate_rows',
|
||||
'column_class')
|
||||
);
|
||||
@@ -8,9 +8,9 @@ if (!isset($limit))
|
||||
|
||||
if (!isset($limitOptions)) {
|
||||
$limitOptions = array($limit);
|
||||
if ($limit < 10)
|
||||
if ($limit <= 10)
|
||||
array_push($limitOptions, 2, 5);
|
||||
if ($limit < 30)
|
||||
if ($limit <= 30)
|
||||
array_push($limitOptions, 10, 25);
|
||||
if ($limit > 10)
|
||||
array_push($limitOptions, 25, 50, 200);
|
||||
@@ -39,6 +39,9 @@ if (!isset($grid_id))
|
||||
if (!isset($search_fields))
|
||||
$search_fields = array();
|
||||
|
||||
if (!isset($grid_events))
|
||||
$grid_events = array();
|
||||
|
||||
if (!isset($grid_setup))
|
||||
$grid_setup = array();
|
||||
|
||||
@@ -93,6 +96,9 @@ if (isset($custom_post_data)) {
|
||||
// correct subset of data
|
||||
$postData['action'] = $action;
|
||||
|
||||
if (isset($nolinks)) {
|
||||
$postData['nolinks'] = true;
|
||||
}
|
||||
|
||||
// Perform column customizations.
|
||||
// This will largely be based off of the 'formatter' parameter,
|
||||
@@ -114,12 +120,12 @@ foreach ($jqGridColumns AS &$col) {
|
||||
elseif ($col['formatter'] === 'currency') {
|
||||
// Switch currency over to our own custom formatting
|
||||
$col['formatter'] = array('--special' => 'currencyFormatter');
|
||||
$default['width'] = 80;
|
||||
$default['width'] = 85;
|
||||
$default['align'] = 'right';
|
||||
}
|
||||
elseif ($col['formatter'] === 'date') {
|
||||
$default['formatoptions'] = array('newformat' => 'm/d/Y');
|
||||
$default['width'] = 90;
|
||||
$default['width'] = 95;
|
||||
$default['align'] = 'center';
|
||||
}
|
||||
elseif ($col['formatter'] === 'name' || $col['formatter'] === 'longname') {
|
||||
@@ -151,6 +157,40 @@ if (isset($sort_column)) {
|
||||
}
|
||||
$sortname = $sortname['index'];
|
||||
|
||||
// Set the default sort order
|
||||
if (isset($sort_order)) {
|
||||
$sortorder = $sort_order;
|
||||
} else {
|
||||
$sortorder = 'ASC';
|
||||
}
|
||||
|
||||
if (1) { // debug
|
||||
$caption .= ' :: <span id="'.$grid_id.'-query"></span>';
|
||||
}
|
||||
|
||||
foreach (array_merge(array('loadComplete' => '', 'loadError' => ''),
|
||||
$grid_events) AS $event => $statement) {
|
||||
$params = '';
|
||||
if (is_array($statement)) {
|
||||
$params = key($statement);
|
||||
$statement = current($statement);
|
||||
}
|
||||
|
||||
if ($event == 'loadComplete') {
|
||||
$grid_events[$event] =
|
||||
array('--special' => "function($params) {url=jQuery('#{$grid_id}').getGridParam('url');url=url.replace(/\/debug.*$/,'?'); pd=jQuery('#{$grid_id}').getPostData();$.each(pd,function(i){ url+=i+'='+escape(pd[i])+'&'; }); jQuery('#{$grid_id}-query').html('<A HREF=\"'+url+'\">Grid Query</A><BR>'); $statement;}");
|
||||
}
|
||||
elseif ($event == 'loadError') {
|
||||
$grid_events[$event] =
|
||||
array('--special' => "function($params) {url=jQuery('#{$grid_id}').getGridParam('url');url=url.replace(/\/debug.*$/,'?'); pd=jQuery('#{$grid_id}').getPostData();$.each(pd,function(i){ url+=i+'='+escape(pd[i])+'&'; }); jQuery('#{$grid_id}-query').html('<A HREF=\"'+url+'\">Grid Error Query</A><BR>'); $statement;}");
|
||||
}
|
||||
else {
|
||||
$grid_events[$event] =
|
||||
array('--special' => "function($params) { $statement; }");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Configure the grid setup, giving priority to user defined parameters
|
||||
$jqGrid_setup = array_merge
|
||||
(array('mtype' => 'GET',
|
||||
@@ -163,15 +203,13 @@ $jqGrid_setup = array_merge
|
||||
'rowNum' => $limit,
|
||||
'rowList' => $limitOptions,
|
||||
'sortname' => $sortname,
|
||||
'sortorder' => $sortorder,
|
||||
'caption' => $caption,
|
||||
'imgpath' => $imgpath,
|
||||
'viewrecords' => true,
|
||||
'pager' => $grid_id.'-pager',
|
||||
|
||||
'loadComplete' => array('--special' => "function() {url=jQuery('#{$grid_id}').getGridParam('url');url=url.replace(/\/debug.*$/,'?'); pd=jQuery('#{$grid_id}').getPostData();$.each(pd,function(i){ url+=i+'='+escape(pd[i])+'&'; }); jQuery('#{$grid_id}-query').html('<A HREF=\"'+url+'\">Grid Query</A><BR>');}"),
|
||||
'loadError' => array('--special' => "function(xhr,st,err) {url=jQuery('#{$grid_id}').getGridParam('url');url=url.replace(/\/debug.*$/,'?'); pd=jQuery('#{$grid_id}').getPostData();$.each(pd,function(i){ url+=i+'='+escape(pd[i])+'&'; }); jQuery('#{$grid_id}-query').html('<A HREF=\"'+url+'\">Grid Error Query</A><BR>');}"),
|
||||
|
||||
),
|
||||
$grid_events,
|
||||
$grid_setup
|
||||
);
|
||||
//pr(compact('grid_setup', 'jqGrid_setup'));
|
||||
@@ -184,7 +222,6 @@ $jqGrid_setup = array_merge
|
||||
<DIV ID="<?php echo $grid_div_id; ?>" CLASS="<?php echo $grid_div_class; ?>">
|
||||
<table id="<?php echo $grid_id; ?>" class="scroll"></table>
|
||||
<div id="<?php echo $grid_id; ?>-pager" class="scroll" style="text-align:right"></div>
|
||||
<div id="<?php echo $grid_id; ?>-query"></div>
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
jQuery(document).ready(function(){
|
||||
|
||||
@@ -2,28 +2,25 @@
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['LeaseID'] = array('index' => 'Lease.id', 'hidden' => true);
|
||||
$cols['Lease'] = array('index' => 'Lease.number', 'formatter' => 'id');
|
||||
$cols['Unit'] = array('index' => 'Unit.name', 'width' => '50', 'align' => 'center');
|
||||
$cols['Customer'] = array('index' => 'Customer.name', 'formatter' => 'longname');
|
||||
$cols['Rent'] = array('index' => 'Lease.rent', 'formatter' => 'currency', 'hiddenz' => true);
|
||||
$cols['Deposit'] = array('index' => 'Lease.deposit', 'formatter' => 'currency', 'hiddenz' => true);
|
||||
$cols['Signed'] = array('index' => 'Lease.lease_date', 'formatter' => 'date');
|
||||
$cols['Move-In'] = array('index' => 'Lease.movein_date', 'formatter' => 'date');
|
||||
$cols['Move-Out'] = array('index' => 'Lease.moveout_date', 'formatter' => 'date');
|
||||
$cols['Balance'] = array('index' => 'Lease.balance', 'formatter' => 'currency', 'sortable'=>false);
|
||||
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency');
|
||||
$cols['Comment'] = array('index' => 'Lease.comment', 'formatter' => 'comment');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'leases',
|
||||
'caption' => isset($caption) ? $caption : null);
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Customer', 'Unit'));
|
||||
|
||||
if (isset($leases)) {
|
||||
$jqGrid_options += array('custom_ids' =>
|
||||
array_map(create_function('$data',
|
||||
'return $data["id"];'),
|
||||
$leases),
|
||||
'limit' => 5);
|
||||
}
|
||||
else {
|
||||
$jqGrid_options += array('search_fields' => array('Customer', 'Unit'));
|
||||
}
|
||||
|
||||
echo $this->element('jqGrid', $jqGrid_options);
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('LeaseID')
|
||||
->defaultFields(array('LeaseID', 'Lease'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
|
||||
@@ -1,102 +1,139 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
if (isset($account_ftype) || isset($ledger_id) || isset($account_id) || isset($ar_account)) {
|
||||
$single_account = true;
|
||||
} else {
|
||||
$single_account = false;
|
||||
}
|
||||
|
||||
if (isset($ledger_id) || isset($account_id) || isset($ar_account)) {
|
||||
$single_amount = false;
|
||||
} else {
|
||||
$single_amount = true;
|
||||
}
|
||||
|
||||
if (isset($reconcile_id)) {
|
||||
$applied_amount = true;
|
||||
} else {
|
||||
$applied_amount = false;
|
||||
}
|
||||
|
||||
if (isset($account_ftype)) {
|
||||
$subtotal_amount = false;
|
||||
} else {
|
||||
$subtotal_amount = true;
|
||||
}
|
||||
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
if (0) {
|
||||
if (isset($notxgroup))
|
||||
$cols['Entry'] = array('index' => 'LedgerEntry.id', 'formatter' => 'id');
|
||||
else
|
||||
$cols['Transaction'] = array('index' => 'Transaction.id', 'formatter' => 'id');
|
||||
} else {
|
||||
$notxgroup = false;
|
||||
$cols['Transaction'] = array('index' => 'Transaction.id', 'formatter' => 'id');
|
||||
$cols['Entry'] = array('index' => 'LedgerEntry.id', 'formatter' => 'id');
|
||||
}
|
||||
$cols['Transaction'] = array('index' => 'Transaction.id', 'formatter' => 'id');
|
||||
$cols['Entry'] = array('index' => 'LedgerEntry.id', 'formatter' => 'id');
|
||||
|
||||
$cols['Date'] = array('index' => 'Transaction.stamp', 'formatter' => 'date');
|
||||
$cols['Effective'] = array('index' => 'LedgerEntry.effective_date', 'formatter' => 'date');
|
||||
$cols['Through'] = array('index' => 'LedgerEntry.through_date', 'formatter' => 'date');
|
||||
|
||||
$cols['Account'] = array('index' => 'Account.name', 'formatter' => 'name');
|
||||
$cols['Debit Account'] = array('index' => 'DebitAccount.name', 'formatter' => 'name');
|
||||
$cols['Credit Account'] = array('index' => 'CreditAccount.name', 'formatter' => 'name');
|
||||
|
||||
if ($single_account) {
|
||||
$cols['Account'] = array('index' => 'Account.name', 'formatter' => 'name');
|
||||
}
|
||||
else {
|
||||
$cols['Debit Account'] = array('index' => 'DebitAccount.name', 'formatter' => 'name');
|
||||
$cols['Credit Account'] = array('index' => 'CreditAccount.name', 'formatter' => 'name');
|
||||
}
|
||||
$cols['Customer'] = array('index' => 'Customer.name', 'formatter' => 'longname');
|
||||
$cols['Lease'] = array('index' => 'Lease.number', 'formatter' => 'id');
|
||||
$cols['Unit'] = array('index' => 'Unit.name', 'formatter' => 'name');
|
||||
|
||||
$cols['Source'] = array('index' => 'MonetarySource.name', 'formatter' => 'name');
|
||||
$cols['Comment'] = array('index' => 'LedgerEntry.comment', 'formatter' => 'comment', 'width'=>150);
|
||||
|
||||
if ($single_amount) {
|
||||
$cols['Amount'] = array('index' => 'LedgerEntry.amount', 'formatter' => 'currency');
|
||||
}
|
||||
else {
|
||||
$cols['Debit'] = array('index' => 'debit', 'formatter' => 'currency');
|
||||
$cols['Credit'] = array('index' => 'credit', 'formatter' => 'currency');
|
||||
$cols['Amount'] = array('index' => 'LedgerEntry.amount', 'formatter' => 'currency');
|
||||
$cols['Debit'] = array('index' => 'debit', 'formatter' => 'currency');
|
||||
$cols['Credit'] = array('index' => 'credit', 'formatter' => 'currency');
|
||||
|
||||
$cols['Last Payment'] = array('index' => 'last_paid', 'formatter' => 'date');
|
||||
$cols['Applied'] = array('index' => "applied", 'formatter' => 'currency');
|
||||
$cols['Sub-Total'] = array('index' => 'subtotal-LedgerEntry.amount', 'formatter' => 'currency', 'sortable' => false);
|
||||
|
||||
|
||||
// Since group_by_tx is a boolean, let's just get it
|
||||
// defined, regardless of whether the caller did so.
|
||||
// group_by_tx will cause all entry fields to be
|
||||
// invalidated, and will leave only the transaction
|
||||
// fields. Yes... the caller should just use the
|
||||
// transactions element instead, in theory. However,
|
||||
// it hasn't yet been implemented to the level of
|
||||
// this element, and additionally, the transactions
|
||||
// element will not allow for customer information
|
||||
// (rightly so, since it's a ledger_entry field).
|
||||
// However, at the current implementation, all ledger
|
||||
// entries of a transaction are for the same customer.
|
||||
// So... we allow it for now.
|
||||
if (!isset($group_by_tx))
|
||||
$group_by_tx = false;
|
||||
|
||||
// REVISIT <AP>: 20090715
|
||||
// If we really want to group by transaction, we need
|
||||
// a transaction listing, not a ledger_entry listing.
|
||||
// switch controllers... don't overload this one.
|
||||
$group_by_tx = false;
|
||||
|
||||
if (isset($transaction_id) || isset($reconcile_id))
|
||||
$grid->invalidFields('Transaction');
|
||||
|
||||
if ($group_by_tx)
|
||||
$grid->invalidFields('Entry');
|
||||
|
||||
if ($group_by_tx)
|
||||
$grid->invalidFields(array('Effective', 'Through'));
|
||||
|
||||
if (!isset($collected_account_id))
|
||||
$grid->invalidFields('Last Payment');
|
||||
|
||||
if (isset($account_ftype) || isset($ledger_id) || isset($account_id) || isset($ar_account))
|
||||
$grid->invalidFields(array('Debit Account', 'Credit Account'));
|
||||
else
|
||||
$grid->invalidFields('Account');
|
||||
|
||||
if (isset($no_account) || $group_by_tx || isset($collected_account_id))
|
||||
$grid->invalidFields(array('Account', 'Debit Account', 'Credit Account'));
|
||||
|
||||
if (isset($ledger_id) || isset($account_id) || isset($ar_account)) {
|
||||
$grid->invalidFields('Amount');
|
||||
$cols['Sub-Total']['index'] = 'subtotal-balance';
|
||||
} else {
|
||||
$grid->invalidFields(array('Debit', 'Credit'));
|
||||
$cols['Sub-Total']['index'] = 'subtotal-LedgerEntry.amount';
|
||||
}
|
||||
|
||||
if ($applied_amount) {
|
||||
$cols['Applied'] = array('index' => "Reconciliation.amount", 'formatter' => 'currency');
|
||||
}
|
||||
// group_by_tx SHOULD wipe out Customer, but the reality
|
||||
// is that it works good at the present, so we'll leave it.
|
||||
if (isset($lease_id) || isset($customer_id))
|
||||
$grid->invalidFields(array('Customer'));
|
||||
|
||||
if ($subtotal_amount) {
|
||||
$cols['Sub-Total'] = array('index' => 'subtotal', 'formatter' => 'currency', 'sortable' => false);
|
||||
}
|
||||
if (isset($lease_id) || $group_by_tx)
|
||||
$grid->invalidFields(array('Lease', 'Unit'));
|
||||
|
||||
$custom_post_data = compact('ledger_id', 'account_id', 'ar_account',
|
||||
'account_type', 'account_ftype',
|
||||
'customer_id', 'lease_id', 'transaction_id', 'notxgroup');
|
||||
if (!isset($reconcile_id) && !isset($collected_account_id))
|
||||
$grid->invalidFields('Applied');
|
||||
else
|
||||
$cols['Sub-Total']['index'] = 'subtotal-applied';
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'ledger_entries',
|
||||
);
|
||||
if (isset($account_ftype) || isset($collected_account_id))
|
||||
$grid->invalidFields('Sub-Total');
|
||||
|
||||
$jqGrid_options += compact('grid_div_id', 'grid_id', 'caption', 'grid_setup', 'limit');
|
||||
|
||||
if (isset($ledger_entries)) {
|
||||
$jqGrid_options += array('custom_ids' =>
|
||||
array_map(create_function('$data',
|
||||
'return $data["id"];'),
|
||||
$ledger_entries),
|
||||
'limit' => 10);
|
||||
}
|
||||
else {
|
||||
$jqGrid_options += array('action' => 'ledger',
|
||||
'limit' => 50);
|
||||
// Now that columns are defined, establish basic grid parameters
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Date')
|
||||
->defaultFields(array('Entry', 'Date', 'Amount', 'Credit', 'Debit'));
|
||||
|
||||
|
||||
if (!isset($config['rows']) && !isset($collected_account_id)) {
|
||||
$config['action'] = 'ledger';
|
||||
$grid->limit(50);
|
||||
}
|
||||
|
||||
if (isset($reconcile_id)) {
|
||||
$custom_post_data += compact('reconcile_id');
|
||||
$jqGrid_options += array('limit' => 5);
|
||||
$grid->customData(compact('reconcile_id'))->limit(20);
|
||||
}
|
||||
|
||||
$jqGrid_options += compact('custom_post_data');
|
||||
$jqGrid_options['sort_column'] = 'Date';
|
||||
echo $this->element('jqGrid', $jqGrid_options);
|
||||
if (isset($collected_account_id)) {
|
||||
$config['action'] = 'collected';
|
||||
$grid->customData(compact('collected_account_id'))->limit(50);
|
||||
$grid->sortField('Last Payment');
|
||||
}
|
||||
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Customer', 'Unit'));
|
||||
|
||||
// Include custom data
|
||||
$grid->customData(compact('ledger_id', 'account_id', 'ar_account',
|
||||
'account_type', 'account_ftype', 'monetary_source_id',
|
||||
'customer_id', 'lease_id', 'transaction_id', 'group_by_tx'));
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array('Transaction', 'Entry', 'Date', 'Effective', 'Last Payment',
|
||||
'Account', 'Debit Account', 'Credit Account',
|
||||
'Customer', 'Unit',
|
||||
'Comment',
|
||||
'Amount', 'Debit', 'Credit',
|
||||
'Applied', 'Sub-Total')
|
||||
);
|
||||
|
||||
@@ -3,27 +3,22 @@
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['ID'] = array('index' => 'id_sequence', 'formatter' => 'id');
|
||||
$cols['Account'] = array('index' => 'Account.name', 'formatter' => 'name', 'width' => '250');
|
||||
$cols['Account'] = array('index' => 'Account.name', 'formatter' => 'longname');
|
||||
//$cols['Open Date'] = array('index' => 'PriorClose.stamp', 'formatter' => 'date');
|
||||
$cols['Close Date'] = array('index' => 'Close.stamp', 'formatter' => 'date');
|
||||
$cols['Comment'] = array('index' => 'Ledger.comment', 'formatter' => 'comment');
|
||||
$cols['Entries'] = array('index' => 'entries', 'width' => '60', 'align' => 'right');
|
||||
$cols['Debits'] = array('index' => 'debits', 'formatter' => 'currency');
|
||||
$cols['Credits'] = array('index' => 'credits', 'formatter' => 'currency');
|
||||
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency');
|
||||
$cols['Close Date'] = array('index' => 'Ledger.close_stamp', 'formatter' => 'date');
|
||||
$cols['Comment'] = array('index' => 'Ledger.comment', 'formatter' => 'comment');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'ledgers',
|
||||
'caption' => isset($caption) ? $caption : null);
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Account', 'Comment'));
|
||||
|
||||
if (isset($ledgers)) {
|
||||
$jqGrid_options += array('custom_ids' =>
|
||||
array_map(create_function('$data',
|
||||
'return $data["id"];'),
|
||||
$ledgers),
|
||||
'limit' => 5);
|
||||
}
|
||||
else {
|
||||
$jqGrid_options += array('search_fields' => array('Account'));
|
||||
}
|
||||
|
||||
echo $this->element('jqGrid', $jqGrid_options);
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('ID', 'DESC')
|
||||
->defaultFields(array('ID', 'Account'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
|
||||
@@ -9,16 +9,13 @@ $cols['Width'] = array('index' => 'Map.width', 'width' => '50', 'align
|
||||
$cols['Depth'] = array('index' => 'Map.depth', 'width' => '50', 'align' => 'right');
|
||||
$cols['Comment'] = array('index' => 'Map.comment', 'formatter' => 'comment');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'maps',
|
||||
'caption' => isset($caption) ? $caption : null);
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Name'));
|
||||
|
||||
if (isset($maps)) {
|
||||
$jqGrid_options += array('custom_ids' =>
|
||||
array_map(create_function('$data',
|
||||
'return $data["id"];'),
|
||||
$maps),
|
||||
'limit' => 5);
|
||||
}
|
||||
|
||||
echo $this->element('jqGrid', $jqGrid_options);
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Name')
|
||||
->defaultFields(array('ID', 'Name'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
|
||||
@@ -4,19 +4,15 @@
|
||||
$cols = array();
|
||||
$cols['ID'] = array('index' => 'MonetarySource.id', 'formatter' => 'id');
|
||||
$cols['Name'] = array('index' => 'MonetarySource.name', 'formatter' => 'longname');
|
||||
$cols['Type'] = array('index' => 'MonetaryType.name', 'formatter' => 'name');
|
||||
$cols['Comment'] = array('index' => 'MonetarySource.comment', 'formatter' => 'comment');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'monetary_sources',
|
||||
'caption' => isset($caption) ? $caption : null);
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('ID', 'Name'));
|
||||
|
||||
if (isset($monetary_sources)) {
|
||||
$jqGrid_options += array('custom_ids' =>
|
||||
array_map(create_function('$data',
|
||||
'return $data["id"];'),
|
||||
$monetary_sources),
|
||||
'limit' => 5);
|
||||
}
|
||||
|
||||
echo $this->element('jqGrid', $jqGrid_options);
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('ID')
|
||||
->defaultFields(array('ID', 'Name'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
|
||||
@@ -58,18 +58,29 @@ if (isset($rows) && is_array($rows) && count($rows)) {
|
||||
$row[$c] = array($col, array('class' => $cell_class));
|
||||
}
|
||||
}
|
||||
|
||||
// Allow user to specify a list of classes
|
||||
if (isset($class) && is_array($class))
|
||||
$class = implode(' ', $class);
|
||||
|
||||
// OK, output the table HTML
|
||||
echo('<TABLE' . (isset($class) ? ' CLASS="'.$class.'"' : '') . '>' . "\n");
|
||||
|
||||
if (isset($caption))
|
||||
echo(' <CAPTION>' . $caption . '</CAPTION>' . "\n");
|
||||
|
||||
if (isset($headers) && is_array($headers))
|
||||
if (isset($headers) && is_array($headers)) {
|
||||
echo(' <THEAD>' . "\n");
|
||||
echo $html->tableHeaders($headers) . "\n";
|
||||
echo(' </THEAD>' . "\n");
|
||||
}
|
||||
|
||||
echo(' <TBODY>' . "\n");
|
||||
echo $html->tableCells($rows,
|
||||
$suppress_alternate_rows ? null : array('class' => "oddrow"),
|
||||
$suppress_alternate_rows ? null : array('class' => "evnrow"),
|
||||
false, false) . "\n";
|
||||
echo(' </TBODY>' . "\n");
|
||||
|
||||
echo('</TABLE>' . "\n");
|
||||
}
|
||||
|
||||
@@ -4,24 +4,17 @@
|
||||
$cols = array();
|
||||
$cols['ID'] = array('index' => 'Transaction.id', 'formatter' => 'id');
|
||||
//$cols['Customer'] = array('index' => 'Customer.name', 'formatter' => 'longname');
|
||||
$cols['Timesamp'] = array('index' => 'Transaction.stamp', 'formatter' => 'date');
|
||||
$cols['Through'] = array('index' => 'Transaction.through_date', 'formatter' => 'date');
|
||||
$cols['Timestamp'] = array('index' => 'Transaction.stamp', 'formatter' => 'date');
|
||||
$cols['Due'] = array('index' => 'Transaction.due_date', 'formatter' => 'date');
|
||||
$cols['Comment'] = array('index' => 'Transaction.comment', 'formatter' => 'comment');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'transactions',
|
||||
'caption' => isset($caption) ? $caption : null);
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Due', 'Comment'));
|
||||
|
||||
if (isset($transactions)) {
|
||||
$jqGrid_options += array('custom_ids' =>
|
||||
array_map(create_function('$data',
|
||||
'return $data["id"];'),
|
||||
$transactions),
|
||||
'limit' => 5);
|
||||
}
|
||||
else {
|
||||
$jqGrid_options += array('search_fields' => array('Due', 'Comment'));
|
||||
}
|
||||
|
||||
echo $this->element('jqGrid', $jqGrid_options);
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('ID')
|
||||
->defaultFields(array('ID', 'Timestamp'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
|
||||
@@ -2,26 +2,23 @@
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['Sort'] = array('index' => 'Unit.sort_order', 'hidden' => true);
|
||||
//$cols['Sort'] = array('index' => 'Unit.sort_order');
|
||||
//$cols['Walk'] = array('index' => 'Unit.walk_order');
|
||||
$cols['ID'] = array('index' => 'Unit.id', 'formatter' => 'id');
|
||||
$cols['Unit'] = array('index' => 'Unit.name', 'width' => '50');
|
||||
$cols['Size'] = array('index' => 'UnitSize.name', 'width' => '75');
|
||||
$cols['Status'] = array('index' => 'Unit.status', 'width' => '75');
|
||||
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency');
|
||||
$cols['Comment'] = array('index' => 'Unit.comment', 'formatter' => 'comment');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'units',
|
||||
'caption' => isset($caption) ? $caption : null);
|
||||
|
||||
if (isset($units)) {
|
||||
$jqGrid_options += array('custom_ids' =>
|
||||
array_map(create_function('$data',
|
||||
'return $data["id"];'),
|
||||
$units),
|
||||
'limit' => 5);
|
||||
}
|
||||
else {
|
||||
$jqGrid_options += array('search_fields' => array('Unit', 'Size', 'Status'));
|
||||
}
|
||||
|
||||
echo $this->element('jqGrid', $jqGrid_options);
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Unit', 'Size', 'Status'));
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Sort')
|
||||
->defaultFields(array('Sort', 'ID', 'Unit'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
|
||||
0
site/views/empty.ctp
Normal file
0
site/views/empty.ctp
Normal file
@@ -19,14 +19,20 @@ class FormatHelper extends AppHelper {
|
||||
true));
|
||||
}
|
||||
|
||||
function currency($amount) {
|
||||
function currency($amount, $spans = false) {
|
||||
if (!isset($amount))
|
||||
return '-';
|
||||
//return null;
|
||||
|
||||
return (isset($amount)
|
||||
? self::$number->currency($amount)
|
||||
: null);
|
||||
$currency = self::$number->currency($amount,
|
||||
'USD',
|
||||
$spans ? array('before'=>'', 'after'=>'') : array());
|
||||
|
||||
if ($spans)
|
||||
return ('<SPAN CLASS="dollar-sign">$</SPAN>' .
|
||||
'<SPAN CLASS="dollar-amount">' . $currency . '</SPAN>');
|
||||
|
||||
return $currency;
|
||||
}
|
||||
|
||||
function date($date, $age = false) {
|
||||
@@ -44,17 +50,20 @@ class FormatHelper extends AppHelper {
|
||||
return self::$time->nice($datetime);
|
||||
}
|
||||
|
||||
function phone($phone) {
|
||||
function phone($phone, $ext = null) {
|
||||
if (!isset($phone))
|
||||
return null;
|
||||
|
||||
$phone = preg_replace("/\D/", "", $phone);
|
||||
if(strlen($phone) == 7)
|
||||
return preg_replace("/(\d{3})(\d{4})/", "$1-$2", $phone);
|
||||
$phone = preg_replace("/(\d{3})(\d{4})/", "$1-$2", $phone);
|
||||
elseif(strlen($phone) == 10)
|
||||
return preg_replace("/(\d{3})(\d{3})(\d{4})/", "$1-$2-$3", $phone);
|
||||
else
|
||||
return $phone;
|
||||
$phone = preg_replace("/(\d{3})(\d{3})(\d{4})/", "$1-$2-$3", $phone);
|
||||
|
||||
if ($ext)
|
||||
$phone .= ' x' . $ext;
|
||||
|
||||
return $phone;
|
||||
}
|
||||
|
||||
function comment($comment) {
|
||||
@@ -211,7 +220,17 @@ class FormatHelper extends AppHelper {
|
||||
return $prefix . $var;
|
||||
|
||||
// OK, must just be a string after all
|
||||
return $prefix . "'" . preg_replace("/'/", '\\\'', $var) . "'";
|
||||
$str = '';
|
||||
$first = true;
|
||||
$lines = explode("\n", $var);
|
||||
//pr(compact('var', 'lines'));
|
||||
foreach ($lines AS $line) {
|
||||
if (!$first)
|
||||
$str .= ' + "\n" +' . "\n";
|
||||
$str .= $prefix . "'" . preg_replace("/'/", '\\\'', $line) . "'";
|
||||
$first = false;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
// The only thing left that we know how to dump
|
||||
|
||||
211
site/views/helpers/grid.php
Normal file
211
site/views/helpers/grid.php
Normal file
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
class GridHelper extends AppHelper {
|
||||
|
||||
var $jqGrid_options;
|
||||
var $included, $invalid;
|
||||
var $columns;
|
||||
var $controller;
|
||||
|
||||
function __construct() {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
function reset() {
|
||||
$this->jqGrid_options
|
||||
= array('limit' => 20,
|
||||
'search_fields' => array(),
|
||||
'custom_post_data' => array(),
|
||||
);
|
||||
|
||||
$this->columns = array();
|
||||
$this->included = array();
|
||||
$this->invalid = array();
|
||||
$this->controller = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Add to the set of columns for this grid
|
||||
function columns($columns) {
|
||||
if (isset($columns)) {
|
||||
if (is_array($columns))
|
||||
$this->columns = array_merge($this->columns, $columns);
|
||||
else
|
||||
$this->columns[] = $columns;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Default fields will be included unless excluded.
|
||||
// This should be used for fields that ALWAYS need
|
||||
// to be included unless specifically overridden.
|
||||
function defaultFields($fields) {
|
||||
if (isset($fields)) {
|
||||
if (is_array($fields))
|
||||
$this->included = array_merge($this->included, $fields);
|
||||
else
|
||||
$this->included[] = $fields;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Invalid fields will simply not be accepted as
|
||||
// part of the inclusion set, even if specified as
|
||||
// a column AND explicit requested for inclusion.
|
||||
function invalidFields($fields) {
|
||||
if (isset($fields)) {
|
||||
if (is_array($fields))
|
||||
$this->invalid = array_merge($this->invalid, $fields);
|
||||
else
|
||||
$this->invalid[] = $fields;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
function searchFields($fields) {
|
||||
if (isset($fields)) {
|
||||
if (is_array($fields))
|
||||
$this->jqGrid_options['search_fields']
|
||||
= array_merge($this->jqGrid_options['search_fields'], $fields);
|
||||
else
|
||||
$this->jqGrid_options['search_fields'][] = $fields;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
function sortField($field, $order = null) {
|
||||
$this->jqGrid_options['sort_column'] = $field;
|
||||
if ($order)
|
||||
$this->jqGrid_options['sort_order'] = $order;
|
||||
return $this;
|
||||
}
|
||||
|
||||
function limit($limit) {
|
||||
$this->jqGrid_options['limit'] = $limit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
function id_list($items) {
|
||||
$this->jqGrid_options['custom_ids']
|
||||
= array_map(create_function('$data',
|
||||
'return $data["id"];'),
|
||||
$items);
|
||||
return $this;
|
||||
}
|
||||
|
||||
function customData($data) {
|
||||
if (isset($data)) {
|
||||
if (is_array($data))
|
||||
$this->jqGrid_options['custom_post_data']
|
||||
= array_merge($this->jqGrid_options['custom_post_data'], $data);
|
||||
else
|
||||
$this->jqGrid_options['custom_post_data'][] = $data;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: render
|
||||
* - Renders the grid.
|
||||
* Unless manipulated with the included / excluded parameters,
|
||||
* the grid will contain the columns defined as part of the
|
||||
* standard set, as set through use of the the included() function.
|
||||
*
|
||||
* args
|
||||
* included:
|
||||
* Array of column names to include in the grid.
|
||||
* - OR -
|
||||
* true: includes all columns.
|
||||
* - OR -
|
||||
* false: includes no additional columns beyond
|
||||
* the standard defined set.
|
||||
|
||||
* excluded:
|
||||
* Array of column names to exclude from the grid.
|
||||
* These will be excluded even if explicitly included
|
||||
* through the included parameter.
|
||||
* - OR -
|
||||
* true: excludes all columns other than those
|
||||
* explicitly included through the included parameter.
|
||||
* - OR -
|
||||
* false: does not exclude any included columns.
|
||||
*/
|
||||
|
||||
function render($view, $config = null, $included = true, $excluded = false) {
|
||||
// Handle null values for included/excluded
|
||||
if (!isset($included))
|
||||
$included = false;
|
||||
if (!isset($excluded))
|
||||
$included = false;
|
||||
|
||||
// Convert true/false into an array
|
||||
if (is_bool($included) && $included)
|
||||
$included = array_keys($this->columns);
|
||||
elseif (is_bool($included) && !$included)
|
||||
$included = array();
|
||||
|
||||
// Convert true/false into an array
|
||||
if (is_bool($excluded) && $excluded)
|
||||
$excluded = array_diff_key(array_keys($this->columns), $included);
|
||||
elseif (is_bool($excluded) && !$excluded)
|
||||
$excluded = array();
|
||||
|
||||
// Tack on any config include/exclude requests
|
||||
if (isset($config['include']))
|
||||
$included = array_merge($included, $config['include']);
|
||||
if (isset($config['exclude']))
|
||||
$excluded = array_merge($excluded, $config['exclude']);
|
||||
|
||||
// Calculate the actual inclusion set
|
||||
$included = array_diff(array_merge($this->included, $included),
|
||||
array_merge($this->invalid, $excluded));
|
||||
|
||||
// Extract the columns that correspond to the inclusion set
|
||||
$this->jqGrid_options['jqGridColumns']
|
||||
= array_intersect_key($this->columns, array_flip($included));
|
||||
|
||||
// As an exception to the normal config variables,
|
||||
// handle 'rows' here. The reason is so that we
|
||||
// ease the burden on views which have a list of
|
||||
// items in a CakePHP style array, but jqGrid is
|
||||
// expecting just an array if IDs. So, we'll run
|
||||
// as middle man (that is our purpose, after all)
|
||||
if (isset($config['rows'])) {
|
||||
// Shrink the limit... user can always override
|
||||
$this->id_list($config['rows'])->limit(10);
|
||||
}
|
||||
|
||||
// Figure out what controller we're using to
|
||||
// populate the grid via ajax, and set it.
|
||||
$controller = $this->controller;
|
||||
if (!isset($controller)) {
|
||||
// Try to get the controller based on the convention
|
||||
// that our grid elements are named after the controller.
|
||||
// If this doesn't work, user will have to manually set
|
||||
// $this->controller to whatever they need.
|
||||
$trace = debug_backtrace(false);
|
||||
if (preg_match('%elements[/\\\\]([^.]+)\.ctp$%',
|
||||
$trace[0]['file'], $matches))
|
||||
$controller = $matches[1];
|
||||
}
|
||||
$this->jqGrid_options['controller'] = $controller;
|
||||
|
||||
// Incorporate all other user options
|
||||
if (isset($config))
|
||||
$this->jqGrid_options = array_merge($this->jqGrid_options, $config);
|
||||
|
||||
echo $view->element('jqGrid', $this->jqGrid_options);
|
||||
|
||||
// Since we only have one instance of this class
|
||||
// as a helper, we must assume it could be used
|
||||
// again later to render an entirely different grid.
|
||||
// Reset the variables to prevent contamination.
|
||||
$this->reset();
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
77
site/views/leases/apply_deposit.ctp
Normal file
77
site/views/leases/apply_deposit.ctp
Normal file
@@ -0,0 +1,77 @@
|
||||
<?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">'.$lease['stats']['balance'].'</TD></TR>' .
|
||||
'<TR><TD CLASS="field">Deposit:</TD><TD CLASS="value">'.$deposit['summary']['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 $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.0'),
|
||||
"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>
|
||||
80
site/views/leases/bad_debt.ctp
Normal file
80
site/views/leases/bad_debt.ctp
Normal 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>
|
||||
321
site/views/leases/invoice.ctp
Normal file
321
site/views/leases/invoice.ctp
Normal file
@@ -0,0 +1,321 @@
|
||||
<?php /* -*- mode:PHP -*- */ ?>
|
||||
|
||||
<div class="invoice input">
|
||||
<?php
|
||||
; // Editor alignment
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Javascript
|
||||
*/
|
||||
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
// prepare the form when the DOM is ready
|
||||
$(document).ready(function() {
|
||||
var options = {
|
||||
target: '#output-debug', // target element(s) to be updated with server response
|
||||
beforeSubmit: verifyRequest, // pre-submit callback
|
||||
success: showResponse, // post-submit callback
|
||||
|
||||
// other available options:
|
||||
//url: url, // override for form's 'action' attribute
|
||||
//type: 'get', // 'get' or 'post', override for form's 'method' attribute
|
||||
//dataType: null, // 'xml', 'script', or 'json' (expected server response type)
|
||||
//clearForm: true, // clear all form fields after successful submit
|
||||
//resetForm: true, // reset the form after successful submit
|
||||
|
||||
// $.ajax options can be used here too, for example:
|
||||
//timeout: 3000,
|
||||
};
|
||||
|
||||
// bind form using 'ajaxForm'
|
||||
$('#invoice-form').ajaxForm(options);
|
||||
});
|
||||
|
||||
// pre-submit callback
|
||||
function verifyRequest(formData, jqForm, options) {
|
||||
// formData is an array; here we use $.param to convert it to a string to display it
|
||||
// but the form plugin does this for you automatically when it submits the data
|
||||
//var_dump(formData);
|
||||
//$('#request-debug').html('<PRE>'+dump(formData)+'</PRE>');
|
||||
$('#request-debug').html('Ommitted');
|
||||
//return false;
|
||||
|
||||
$('#response-debug').html('Loading <BLINK>...</BLINK>');
|
||||
$('#output-debug').html('Loading <BLINK>...</BLINK>');
|
||||
|
||||
// here we could return false to prevent the form from being submitted;
|
||||
// returning anything other than false will allow the form submit to continue
|
||||
return true;
|
||||
}
|
||||
|
||||
// post-submit callback
|
||||
function showResponse(responseText, statusText) {
|
||||
// for normal html responses, the first argument to the success callback
|
||||
// is the XMLHttpRequest object's responseText property
|
||||
|
||||
// if the ajaxForm method was passed an Options Object with the dataType
|
||||
// property set to 'xml' then the first argument to the success callback
|
||||
// is the XMLHttpRequest object's responseXML property
|
||||
|
||||
// if the ajaxForm method was passed an Options Object with the dataType
|
||||
// property set to 'json' then the first argument to the success callback
|
||||
// is the json data object returned by the server
|
||||
|
||||
if (statusText == 'success') {
|
||||
// get a clean slate
|
||||
//resetForm();
|
||||
}
|
||||
else {
|
||||
alert('not successful??');
|
||||
}
|
||||
|
||||
|
||||
$('#response-debug').html('<PRE>'+dump(statusText)+'</PRE>');
|
||||
}
|
||||
|
||||
// Reset the form
|
||||
function resetForm() {
|
||||
$("#charge-entry-id").val(1);
|
||||
|
||||
$("#invoice-lease").html("INTERNAL ERROR");
|
||||
$("#invoice-unit").html("INTERNAL ERROR");
|
||||
$("#invoice-customer").html("INTERNAL ERROR");
|
||||
$("#invoice-rent").html("INTERNAL ERROR");
|
||||
$("#invoice-late").html("INTERNAL ERROR");
|
||||
$("#invoice-deposit").html("INTERNAL ERROR");
|
||||
|
||||
addChargeSource(false);
|
||||
datepickerNow('TransactionStamp');
|
||||
}
|
||||
|
||||
|
||||
function onRowSelect(grid_id, lease_id) {
|
||||
// Set the item id that will be returned with the form
|
||||
$("#lease-id").val(lease_id);
|
||||
|
||||
// Get the item names from the grid
|
||||
//$("#invoice-lease").html($(grid_id).getCell(lease_id, 'Lease-number'));
|
||||
// REVISIT <AP>: 20090708
|
||||
// This is not intended as a long term solution,
|
||||
// but I need a way to enter data and then view
|
||||
// the results. This link will help.
|
||||
$("#invoice-lease").html('<A HREF="/pmgr/site/leases/view/' +
|
||||
$(grid_id).getCell(lease_id, 'Lease-id').replace(/^#/,'') +
|
||||
'">' +
|
||||
$(grid_id).getCell(lease_id, 'Lease-number') +
|
||||
'</A>');
|
||||
$("#invoice-unit").html($(grid_id).getCell(lease_id, 'Unit-name'));
|
||||
$("#invoice-customer").html($(grid_id).getCell(lease_id, 'Customer-name'));
|
||||
$("#invoice-rent").html($(grid_id).getCell(lease_id, 'Lease-rent'));
|
||||
$("#invoice-late").html('$10.00');
|
||||
$("#invoice-deposit").html($(grid_id).getCell(lease_id, 'Lease-deposit')
|
||||
? $(grid_id).getCell(lease_id, 'Lease-deposit')
|
||||
: '-');
|
||||
|
||||
// Hide the "no lease" message and show the current lease
|
||||
$(".lease-selection-invalid").hide();
|
||||
$(".lease-selection-valid").show();
|
||||
|
||||
// Collapse the grid now that the user has selected
|
||||
$("#leases-list .HeaderButton").click();
|
||||
}
|
||||
|
||||
function onGridState(grid_id, state) {
|
||||
if (state == 'visible') {
|
||||
$(".lease-selection-invalid").hide();
|
||||
$(".lease-selection-valid").hide();
|
||||
}
|
||||
else {
|
||||
if ($("#lease-id").val() > 0) {
|
||||
$(".lease-selection-valid").show();
|
||||
$(".lease-selection-invalid").hide();
|
||||
} else {
|
||||
$(".lease-selection-valid").hide();
|
||||
$(".lease-selection-invalid").show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addChargeSource(flash) {
|
||||
var id = $("#charge-entry-id").val();
|
||||
addDiv('charge-entry-id', 'charge', 'charges', flash,
|
||||
// HTML section
|
||||
'<FIELDSET CLASS="charge subset">' +
|
||||
'<LEGEND>Charge #%{id} (%{remove})</LEGEND>' +
|
||||
|
||||
<?php
|
||||
echo FormatHelper::phpVarToJavascript
|
||||
($this->element('form_table',
|
||||
array('class' => "item invoice ledger-entry entry",
|
||||
//'with_name_after' => ':',
|
||||
'field_prefix' => 'LedgerEntry.%{id}',
|
||||
'fields' => array
|
||||
("account_id" => array('name' => 'Account',
|
||||
'opts' =>
|
||||
array('options' => $chargeAccounts,
|
||||
'value' => $defaultAccount,
|
||||
),
|
||||
),
|
||||
"effective_date" => array('opts' =>
|
||||
array('type' => 'text'),
|
||||
'between' => '<A HREF="#" ONCLICK="datepickerBOM(\'TransactionStamp\',\'LedgerEntry%{id}EffectiveDate\'); return false;">BOM</A>',
|
||||
),
|
||||
"through_date" => array('opts' =>
|
||||
array('type' => 'text'),
|
||||
'between' => '<A HREF="#" ONCLICK="datepickerEOM(\'LedgerEntry%{id}EffectiveDate\',\'LedgerEntry%{id}ThroughDate\'); return false;">EOM</A>',
|
||||
),
|
||||
"amount" => true,
|
||||
"comment" => array('opts' => array('size' => 50)),
|
||||
),
|
||||
))) . "+\n";
|
||||
?>
|
||||
|
||||
'</FIELDSET>'
|
||||
);
|
||||
|
||||
$("#LedgerEntry"+id+"EffectiveDate")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
|
||||
$("#LedgerEntry"+id+"ThroughDate")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
}
|
||||
|
||||
--></script>
|
||||
|
||||
<?php
|
||||
; // align
|
||||
|
||||
echo $this->element('leases', array
|
||||
('config' => array
|
||||
('grid_div_id' => 'leases-list',
|
||||
'grid_div_class' => 'text-below',
|
||||
'caption' => ('<A HREF="#" ONCLICK="$(\'#leases-list .HeaderButton\').click();'.
|
||||
' return false;">Select Lease</A>'),
|
||||
'grid_setup' => array('hiddengrid' => isset($lease['Lease']['id'])),
|
||||
'grid_events' => array('onSelectRow' =>
|
||||
array('ids' =>
|
||||
'if (ids != null){onRowSelect("#"+$(this).attr("id"), ids);}'),
|
||||
'onHeaderClick' =>
|
||||
array('gridstate' =>
|
||||
'onGridState("#"+$(this).attr("id"), gridstate)'),
|
||||
),
|
||||
'nolinks' => true,
|
||||
'limit' => 10,
|
||||
)));
|
||||
|
||||
echo ('<DIV CLASS="invoice grid-selection-text">' .
|
||||
|
||||
'<DIV CLASS="lease-selection-valid" style="display:none">' .
|
||||
'Lease <SPAN id="invoice-lease"></SPAN>' . ' / ' .
|
||||
'Unit: <SPAN id="invoice-unit"></SPAN>' . ' / ' .
|
||||
'Customer: <SPAN id="invoice-customer"></SPAN>' .
|
||||
|
||||
'<DIV CLASS="supporting">' .
|
||||
'<TABLE>' .
|
||||
'<TR><TD CLASS="field">Rent:</TD><TD CLASS="value"><SPAN id="invoice-rent"></SPAN></TD></TR>' .
|
||||
'<TR><TD CLASS="field">Late Fee:</TD><TD CLASS="value"><SPAN id="invoice-late"></SPAN></TD></TR>' .
|
||||
'<TR><TD CLASS="field">Deposit:</TD><TD CLASS="value"><SPAN id="invoice-deposit"></SPAN></TD></TR>' .
|
||||
'</TABLE>' .
|
||||
'</DIV>' .
|
||||
|
||||
'</DIV>' .
|
||||
|
||||
'<DIV CLASS="lease-selection-invalid" style="display:none">' .
|
||||
'Please select lease' .
|
||||
'</DIV>' .
|
||||
|
||||
'</DIV>' . "\n");
|
||||
|
||||
echo $form->create(null, array('id' => 'invoice-form',
|
||||
'url' => array('controller' => 'transactions',
|
||||
'action' => 'postInvoice')));
|
||||
|
||||
echo $form->input("Lease.id",
|
||||
array('id' => 'lease-id',
|
||||
'type' => 'hidden',
|
||||
'value' => 0));
|
||||
|
||||
/* echo '<fieldset CLASS="invoice">' . "\n"; */
|
||||
/* echo ' <legend>Invoice</legend>' . "\n"; */
|
||||
|
||||
echo $this->element('form_table',
|
||||
array('class' => "item invoice 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 '</fieldset>' . "\n"; */
|
||||
|
||||
echo $form->submit('Generate Invoice') . "\n";
|
||||
?>
|
||||
|
||||
<fieldset CLASS="charge superset">
|
||||
<legend>Charges</legend>
|
||||
<input type="hidden" id="charge-entry-id" value="0">
|
||||
<div id="charges"></div>
|
||||
<fieldset> <legend>
|
||||
<a href="#" onClick="addChargeSource(true); return false;">Add Another Charge</a>
|
||||
</legend> </fieldset>
|
||||
</fieldset>
|
||||
|
||||
<?php echo $form->end('Generate Invoice'); ?>
|
||||
|
||||
<div><H4>Request</H4><div id="request-debug"></div></div>
|
||||
<div><H4>Response</H4><div id="response-debug"></div></div>
|
||||
<div><H4>Output</H4><div id="output-debug"></div></div>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
$(document).ready(function(){
|
||||
$("#TransactionStamp")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
|
||||
resetForm();
|
||||
|
||||
<?php if (isset($lease['Lease']['id'])): ?>
|
||||
$("#lease-id").val(<?php echo $lease['Lease']['id']; ?>);
|
||||
//$("#invoice-lease").html("<?php echo '#'.$lease['Lease']['number']; ?>");
|
||||
$("#invoice-lease").html('<A HREF="/pmgr/site/leases/view/' +
|
||||
"<?php echo $lease['Lease']['id']; ?>" +
|
||||
'">#' +
|
||||
"<?php echo $lease['Lease']['number']; ?>" +
|
||||
'</A>');
|
||||
$("#invoice-unit").html("<?php echo $lease['Unit']['name']; ?>");
|
||||
$("#invoice-customer").html("<?php echo $lease['Customer']['name']; ?>");
|
||||
$("#invoice-rent").html("<?php echo FormatHelper::currency($lease['Lease']['rent']); ?>");
|
||||
$("#invoice-late").html('$10.00');
|
||||
$("#invoice-deposit").html("<?php echo FormatHelper::currency($lease['Lease']['deposit']); ?>");
|
||||
onGridState(null, 'hidden');
|
||||
<?php else: ?>
|
||||
onGridState(null, 'visible');
|
||||
<?php endif; ?>
|
||||
});
|
||||
--></script>
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#" onClick="$('#debug').html(''); return false;">Clear Debug Output</a>
|
||||
228
site/views/leases/move.ctp
Normal file
228
site/views/leases/move.ctp
Normal file
@@ -0,0 +1,228 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
$move_action = $this->action;
|
||||
//$move_class = preg_replace("/_/", "-", $this->action);
|
||||
//$move_date = preg_replace("/_/", "", $this->action) . "_date";
|
||||
$move_type = preg_replace("/.*_/", "", $this->action);
|
||||
|
||||
?>
|
||||
|
||||
<div class="move-inout input">
|
||||
|
||||
<?php
|
||||
; // Editor alignment
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Javascript
|
||||
*/
|
||||
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
// Reset the form
|
||||
function resetForm() {
|
||||
$("#customer-id").val(0);
|
||||
$("#move-customer").html("INTERNAL ERROR");
|
||||
$("#unit-id").val(0);
|
||||
$("#move-unit").html("INTERNAL ERROR");
|
||||
datepickerNow('LeaseMoveDate', false);
|
||||
}
|
||||
|
||||
function onRowSelect(grid_id, item_type, item_id) {
|
||||
cell_name = item_type.charAt(0).toUpperCase() + item_type.substr(1) + "-name";
|
||||
|
||||
// Set the item id that will be returned with the form
|
||||
$("#"+item_type+"-id").val(item_id);
|
||||
|
||||
// Get the item name from the grid
|
||||
$("#move-"+item_type).html($(grid_id).getCell(item_id, cell_name));
|
||||
|
||||
// Hide the "no customer" message and show the current customer
|
||||
$("."+item_type+"-selection-invalid").hide();
|
||||
$("."+item_type+"-selection-valid").show();
|
||||
|
||||
$("#"+item_type+"s-list .HeaderButton").click();
|
||||
}
|
||||
|
||||
function onGridState(grid_id, item_type, state) {
|
||||
if (state == 'visible') {
|
||||
$("."+item_type+"-selection-invalid").hide();
|
||||
$("."+item_type+"-selection-valid").hide();
|
||||
}
|
||||
else {
|
||||
//if ($(grid_id).getGridParam("selrow"))
|
||||
//alert("id:" + $("#"+item_type+"-id").val());
|
||||
if ($("#"+item_type+"-id").val() > 0) {
|
||||
$("."+item_type+"-selection-invalid").hide();
|
||||
$("."+item_type+"-selection-valid").show();
|
||||
} else {
|
||||
$("."+item_type+"-selection-invalid").show();
|
||||
$("."+item_type+"-selection-valid").hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
--></script>
|
||||
|
||||
<?php
|
||||
; // align
|
||||
|
||||
if ($move_type !== 'out') {
|
||||
echo $this->element('customers', array
|
||||
('config' => array
|
||||
('grid_div_id' => 'customers-list',
|
||||
'grid_div_class' => 'text-below',
|
||||
'caption' => ('<A HREF="#" ONCLICK="$(\'#customers-list .HeaderButton\').click();'.
|
||||
' return false;">Select Customer</A>'),
|
||||
'grid_setup' => array('hiddengrid' => isset($customer['id'])),
|
||||
'grid_events' => array('onSelectRow' =>
|
||||
array('ids' =>
|
||||
'if (ids != null){onRowSelect("#"+$(this).attr("id"), "customer", ids);}'),
|
||||
'onHeaderClick' =>
|
||||
array('gridstate' =>
|
||||
'onGridState("#"+$(this).attr("id"), "customer", gridstate)'),
|
||||
),
|
||||
'nolinks' => true,
|
||||
'limit' => 10,
|
||||
)));
|
||||
}
|
||||
|
||||
echo ('<DIV CLASS="move-inout grid-selection-text">' .
|
||||
|
||||
'<DIV CLASS="customer-selection-valid" style="display:none">' .
|
||||
'Customer: <SPAN id="move-customer"></SPAN>' .
|
||||
'</DIV>' .
|
||||
|
||||
'<DIV CLASS="customer-selection-invalid" style="display:none">' .
|
||||
'Please select customer' .
|
||||
'</DIV>' .
|
||||
|
||||
'</DIV>' . "\n");
|
||||
|
||||
|
||||
if ($move_type !== 'out') {
|
||||
echo $this->element('units', array
|
||||
('config' => array
|
||||
('grid_div_id' => 'units-list',
|
||||
'grid_div_class' => 'text-below',
|
||||
'caption' => ('<A HREF="#" ONCLICK="$(\'#units-list .HeaderButton\').click();'.
|
||||
' return false;">Select Unit</A>'),
|
||||
'grid_setup' => array('hiddengrid' => isset($unit['id'])),
|
||||
'grid_events' => array('onSelectRow' =>
|
||||
array('ids' =>
|
||||
'if (ids != null){onRowSelect("#"+$(this).attr("id"), "unit", ids);}'),
|
||||
'onHeaderClick' =>
|
||||
array('gridstate' =>
|
||||
'onGridState("#"+$(this).attr("id"), "unit", gridstate)'),
|
||||
),
|
||||
'action' => 'unoccupied',
|
||||
'nolinks' => true,
|
||||
'limit' => 10,
|
||||
)));
|
||||
}
|
||||
|
||||
echo ('<DIV CLASS="move-inout grid-selection-text">' .
|
||||
|
||||
'<DIV CLASS="unit-selection-valid" style="display:none">' .
|
||||
'Unit: <SPAN id="move-unit"></SPAN>' .
|
||||
'</DIV>' .
|
||||
|
||||
'<DIV CLASS="unit-selection-invalid" style="display:none">' .
|
||||
'Please select unit' .
|
||||
'</DIV>' .
|
||||
|
||||
'</DIV>' . "\n");
|
||||
|
||||
echo $form->create(null, array('id' => 'move-inout-form',
|
||||
'url' => array('controller' => 'leases',
|
||||
'action' => $move_action)));
|
||||
|
||||
echo $form->input("Lease.customer_id",
|
||||
array('id' => 'customer-id',
|
||||
'type' => 'hidden',
|
||||
'value' => 0));
|
||||
|
||||
echo $form->input("Lease.unit_id",
|
||||
array('id' => 'unit-id',
|
||||
'type' => 'hidden',
|
||||
'value' => 0));
|
||||
|
||||
if ($move_type === 'out') {
|
||||
echo $form->input('Lease.id',
|
||||
array('type' => 'hidden',
|
||||
'value' => $lease['id'],
|
||||
));
|
||||
}
|
||||
|
||||
echo $this->element('form_table',
|
||||
array('class' => "item move-inout entry",
|
||||
'field_prefix' => 'Lease',
|
||||
'fields' => array
|
||||
('move'.$move_type.'_date' =>
|
||||
array('opts' =>
|
||||
array('type' => 'text',
|
||||
'id' => "LeaseMoveDate"),
|
||||
'between' => '<A HREF="#" ONCLICK="datepickerNow(\'LeaseMoveDate\', false); return false;">Now</A>',
|
||||
),
|
||||
"comment" =>
|
||||
($move_type !== 'out'
|
||||
? array('opts' => array('size' => 50))
|
||||
: null),
|
||||
)));
|
||||
|
||||
if ($move_type === 'out') {
|
||||
echo('<P><BR>Be sure that you really want to move this customer out of the unit,<BR>' .
|
||||
'select the correct moveout date,' .
|
||||
'and press the "Perform Move Out" button.' . "\n");
|
||||
}
|
||||
|
||||
// Set up a redirect page. I use lower case 'redirect' here
|
||||
// to avoid the model convention, which starts with upper-case.
|
||||
if (isset($redirect)) {
|
||||
foreach ($redirect AS $name => $value) {
|
||||
echo $form->input("redirect.$name",
|
||||
array('type' => 'hidden',
|
||||
'value' => $value,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
echo $form->end('Perform Move ' . ucfirst($move_type));
|
||||
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
$(document).ready(function(){
|
||||
$("#LeaseMoveDate")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
|
||||
resetForm();
|
||||
|
||||
<?php if (isset($customer['id'])): ?>
|
||||
$("#customer-id").val(<?php echo $customer['id']; ?>);
|
||||
$("#move-customer").html("<?php echo $customer['name']; ?>");
|
||||
onGridState(null, 'customer', 'hidden');
|
||||
<?php else: ?>
|
||||
onGridState(null, 'customer', 'visible');
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($unit['id'])): ?>
|
||||
$("#unit-id").val(<?php echo $unit['id']; ?>);
|
||||
$("#move-unit").html("<?php echo $unit['name']; ?>");
|
||||
onGridState(null, 'unit', 'hidden');
|
||||
<?php else: ?>
|
||||
onGridState(null, 'unit', 'visible');
|
||||
<?php endif; ?>
|
||||
});
|
||||
--></script>
|
||||
|
||||
</div>
|
||||
@@ -36,7 +36,8 @@ $rows = array(array('ID', $lease['id']),
|
||||
array('Notice Received', FormatHelper::date($lease['notice_received_date'], true)),
|
||||
array('Closed', FormatHelper::date($lease['close_date'], true)),
|
||||
array('Deposit', FormatHelper::currency($lease['deposit'])),
|
||||
array('Rent', FormatHelper::currency($lease['amount'])),
|
||||
array('Rent', FormatHelper::currency($lease['rent'])),
|
||||
array('Paid Through', FormatHelper::date($lease['paid_through'], true)),
|
||||
array('Comment', $lease['comment']));
|
||||
|
||||
|
||||
@@ -78,11 +79,16 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
* Lease Account History
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries',
|
||||
array('caption' => 'Account',
|
||||
'lease_id' => $lease['id'],
|
||||
'ar_account' => true,
|
||||
));
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'lease_id' => $lease['id'],
|
||||
'ar_account' => true,
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Account',
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
|
||||
@@ -2,6 +2,44 @@
|
||||
|
||||
echo '<div class="ledger-entry view">' . "\n";
|
||||
|
||||
// The two entry ids, debit and credit, are actually individual
|
||||
// entries in separate accounts (each make up one of the two
|
||||
// entries required for "double entry"). This, when we provide
|
||||
// reconcile information, we're really providing reconcile info
|
||||
// for two independent accounts. The reconciling entries,
|
||||
// therefore, are those on the opposite side of the ledger in
|
||||
// each account. For example, assume this "double" entry is
|
||||
//
|
||||
// debit: A/R credit: Cash amount: 55
|
||||
//
|
||||
// Then, our accounts might look like:
|
||||
//
|
||||
// RENT TAX A/R CASH BANK
|
||||
// ------- ------- ------- ------- -------
|
||||
// |20 | 20| | | <-- Unrelated
|
||||
// | | |20 20| | <-- Unrelated
|
||||
// | | | | |
|
||||
// |50 | 50| | | <-- Rent paid by this entry
|
||||
// | |5 5| | | <-- Tax paid by this entry
|
||||
// | | |55 55| | <-- THIS ENTRY
|
||||
// | | | | |
|
||||
// | | | |75 75| <-- Deposit includes this entry
|
||||
// | | | | |
|
||||
//
|
||||
// In this case, we're looking to provide reconcile information
|
||||
// of A/R for (the credit side of) this entry, and also of Cash
|
||||
// (for the debit side). Taking the accounts as individual
|
||||
// entries, instead of the "double entry" representation in the
|
||||
// database, we're actually providing information on the two
|
||||
// A/R entries, 50 & 5, which are both debits, i.e. opposite
|
||||
// entries to the credit of A/R. The cash account entry
|
||||
// reconciles against the credit of 75. Again, this is the
|
||||
// opposite entry to the debit of Cash.
|
||||
//
|
||||
// Thus, for our debit_ledger_id, we're reconciling against
|
||||
// credits, and for our credit_ledger_id, against debits.
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
@@ -10,66 +48,45 @@ echo '<div class="ledger-entry view">' . "\n";
|
||||
*/
|
||||
|
||||
$transaction = $entry['Transaction'];
|
||||
$debit_ledger = $entry['DebitLedger'];
|
||||
$credit_ledger = $entry['CreditLedger'];
|
||||
$ledgers = array('debit' => $entry['DebitLedger'],
|
||||
'credit' => $entry['CreditLedger']);
|
||||
$source = $entry['MonetarySource'];
|
||||
$customer = $entry['Customer'];
|
||||
$lease = $entry['Lease'];
|
||||
$entry = $entry['LedgerEntry'];
|
||||
|
||||
$rows = array(array('ID', $entry['id']),
|
||||
array('Transaction', $html->link('#'.$transaction['id'],
|
||||
array('controller' => 'transactions',
|
||||
'action' => 'view',
|
||||
$transaction['id']))),
|
||||
array('Timestamp', FormatHelper::datetime($transaction['stamp'])),
|
||||
array('Customer', (isset($customer['name'])
|
||||
? $html->link($customer['name'],
|
||||
array('controller' => 'customers',
|
||||
'action' => 'view',
|
||||
$customer['id']))
|
||||
: null)),
|
||||
array('Lease', (isset($lease['id'])
|
||||
? $html->link('#'.$lease['id'],
|
||||
array('controller' => 'leases',
|
||||
'action' => 'view',
|
||||
$lease['id']))
|
||||
: null)),
|
||||
array('Monetary Source', (isset($source['name'])
|
||||
? $html->link($source['name'],
|
||||
array('controller' => 'monetary_sources',
|
||||
'action' => 'view',
|
||||
$source['id']))
|
||||
: null)),
|
||||
array('Amount', FormatHelper::currency($entry['amount'])),
|
||||
array('Debit', ($html->link($debit_ledger['Account']['name'],
|
||||
array('controller' => 'accounts',
|
||||
'action' => 'view',
|
||||
$debit_ledger['Account']['id']))
|
||||
. ' ('
|
||||
. $html->link('#' . $debit_ledger['Account']['id']
|
||||
. '-' . $debit_ledger['sequence'],
|
||||
array('controller' => 'ledgers',
|
||||
'action' => 'view',
|
||||
$debit_ledger['id']))
|
||||
. ')')),
|
||||
array('Credit', ($html->link($credit_ledger['Account']['name'],
|
||||
array('controller' => 'accounts',
|
||||
'action' => 'view',
|
||||
$credit_ledger['Account']['id']))
|
||||
. ' ('
|
||||
. $html->link('#' . $credit_ledger['Account']['id']
|
||||
. '-' . $credit_ledger['sequence'],
|
||||
array('controller' => 'ledgers',
|
||||
'action' => 'view',
|
||||
$credit_ledger['id']))
|
||||
. ')')),
|
||||
array('Comment', $entry['comment']));
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array('ID', $entry['id']);
|
||||
$rows[] = array('Transaction', $html->link('#'.$transaction['id'],
|
||||
array('controller' => 'transactions',
|
||||
'action' => 'view',
|
||||
$transaction['id'])));
|
||||
$rows[] = array('Timestamp', FormatHelper::datetime($transaction['stamp']));
|
||||
$rows[] = array('Effective', FormatHelper::date($entry['effective_date']));
|
||||
$rows[] = array('Through', FormatHelper::date($entry['through_date']));
|
||||
$rows[] = array('Customer', (isset($customer['name'])
|
||||
? $html->link($customer['name'],
|
||||
array('controller' => 'customers',
|
||||
'action' => 'view',
|
||||
$customer['id']))
|
||||
: null));
|
||||
$rows[] = array('Lease', (isset($lease['id'])
|
||||
? $html->link('#'.$lease['id'],
|
||||
array('controller' => 'leases',
|
||||
'action' => 'view',
|
||||
$lease['id']))
|
||||
: null));
|
||||
$rows[] = array('Monetary Source', (isset($source['name'])
|
||||
? $html->link($source['name'],
|
||||
array('controller' => 'monetary_sources',
|
||||
'action' => 'view',
|
||||
$source['id']))
|
||||
: null));
|
||||
$rows[] = array('Comment', $entry['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item ledger-entry detail',
|
||||
'caption' => 'Ledger Entry Detail',
|
||||
'caption' => 'Double Ledger Entry Detail',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value')));
|
||||
|
||||
@@ -79,23 +96,55 @@ echo $this->element('table',
|
||||
*/
|
||||
|
||||
echo '<div class="infobox">' . "\n";
|
||||
$rows = array();
|
||||
if ($debit_ledger['Account']['trackable']) {
|
||||
$rows[] = array('Debit Amount Reconciled:', FormatHelper::currency($stats['debit_amount_reconciled']));
|
||||
$rows[] = array('Debit Amount Remaining:', FormatHelper::currency($stats['debit_amount_remaining']));
|
||||
foreach ($ledgers AS $type => $ledger) {
|
||||
//pr($ledger);
|
||||
if (!$ledger['Account']['trackable'])
|
||||
continue;
|
||||
|
||||
$applied_caption = "Transfers applied";
|
||||
$remaining_caption = "Unapplied amount";
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array($applied_caption,
|
||||
FormatHelper::currency($stats[$type]['amount_reconciled']));
|
||||
$rows[] = array($remaining_caption,
|
||||
FormatHelper::currency($stats[$type]['amount_remaining']));
|
||||
echo $this->element('table',
|
||||
array('class' => 'item summary',
|
||||
'caption' => "{$ledger['Account']['name']} Ledger Entry",
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value'),
|
||||
//'suppress_alternate_rows' => true,
|
||||
));
|
||||
}
|
||||
if ($credit_ledger['Account']['trackable']) {
|
||||
$rows[] = array('Credit Amount Reconciled:', FormatHelper::currency($stats['credit_amount_reconciled']));
|
||||
$rows[] = array('Credit Amount Remaining:', FormatHelper::currency($stats['credit_amount_remaining']));
|
||||
}
|
||||
echo $this->element('table',
|
||||
array('class' => 'summary',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value'),
|
||||
'suppress_alternate_rows' => true,
|
||||
));
|
||||
|
||||
echo '</div>' . "\n";
|
||||
|
||||
echo ('<DIV CLASS="ledger-double-entry">' . "\n");
|
||||
foreach ($ledgers AS $type => $ledger) {
|
||||
$rows = array();
|
||||
|
||||
$rows[] = array('Account', $html->link($ledger['Account']['name'],
|
||||
array('controller' => 'accounts',
|
||||
'action' => 'view',
|
||||
$ledger['Account']['id'])));
|
||||
$rows[] = array('Ledger', $html->link('#' . $ledger['Account']['id']
|
||||
. '-' . $ledger['sequence'],
|
||||
array('controller' => 'ledgers',
|
||||
'action' => 'view',
|
||||
$ledger['id'])));
|
||||
$rows[] = array('Amount', FormatHelper::currency($entry['amount']));
|
||||
$rows[] = array('Effect', $ledger['Account']['ftype'] == $type ? 'INCREASE' : 'DECREASE');
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => array('item', $type, 'detail'),
|
||||
'caption' => ucfirst($type) . ' Ledger Entry',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value')));
|
||||
}
|
||||
echo ('</DIV>' . "\n");
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
@@ -111,24 +160,23 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
* Reconciliation Ledger Entries
|
||||
*/
|
||||
|
||||
if ($debit_ledger['Account']['trackable']) {
|
||||
echo $this->element('ledger_entries',
|
||||
array('caption' => "Payments Received",
|
||||
'grid_div_id' => 'debit_reconciliation_ledger_entries',
|
||||
'account_ftype' => 'debit',
|
||||
'reconcile_id' => $entry['id'],
|
||||
//'ledger_entries' => $reconciled['debit']['entry'],
|
||||
));
|
||||
}
|
||||
foreach ($ledgers AS $type => $ledger) {
|
||||
if (!$ledger['Account']['trackable'])
|
||||
continue;
|
||||
|
||||
if ($credit_ledger['Account']['trackable']) {
|
||||
echo $this->element('ledger_entries',
|
||||
array('caption' => "Charges Paid",
|
||||
'grid_div_id' => 'credit_reconciliation_ledger_entries',
|
||||
'account_ftype' => 'credit',
|
||||
'reconcile_id' => $entry['id'],
|
||||
//'ledger_entries' => $reconciled['credit']['entry'],
|
||||
));
|
||||
$caption = ('Applied transfers ' . ($ledger['Account']['ftype'] == $type ? 'out of' : 'into') .
|
||||
' Account: ' . $ledger['Account']['name']);
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'account_ftype' => $type,
|
||||
'reconcile_id' => $entry['id'],
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
('caption' => $caption,
|
||||
'grid_div_id' => $type.'_reconciliation_ledger_entries',
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ echo '<div class="ledger view">' . "\n";
|
||||
*/
|
||||
|
||||
$account = $ledger['Account'];
|
||||
//$close = $ledger['Close'];
|
||||
|
||||
if (isset($ledger['Ledger']))
|
||||
$ledger = $ledger['Ledger'];
|
||||
@@ -20,7 +21,7 @@ $rows = array(array('ID', $ledger['id']),
|
||||
'action' => 'view',
|
||||
$account['id']))),
|
||||
array('Sequence', $ledger['sequence']),
|
||||
array('Status', $ledger['closed'] ? 'Closed' : 'Open'),
|
||||
array('Status', $ledger['close_id'] ? 'Closed' : 'Open'),
|
||||
array('Comment', $ledger['comment']));
|
||||
|
||||
echo $this->element('table',
|
||||
@@ -62,11 +63,18 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
* Ledger Entries
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries',
|
||||
array('caption' => "Ledger Entries",
|
||||
'ledger_id' => $ledger['id'],
|
||||
'account_type' => $account['type'],
|
||||
));
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'ledger_id' => $ledger['id'],
|
||||
'account_type' => $account['type'],
|
||||
'group_by_tx' => true,
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
('caption' => "Ledger Entries",
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
@@ -9,13 +9,14 @@ echo '<div class="monetary-source view">' . "\n";
|
||||
* MonetarySource Detail Main Section
|
||||
*/
|
||||
|
||||
$type = $monetarySource['MonetaryType'];
|
||||
$source = $monetarySource['MonetarySource'];
|
||||
|
||||
$rows = array(array('ID', $source['id']),
|
||||
array('Name', $source['name']),
|
||||
array('Type', $type['name']),
|
||||
array('Tillable', $type['tillable'] ? 'Yes' : 'No'),
|
||||
array('Data 1', $source['data1']),
|
||||
array('Data 2', $source['data2']),
|
||||
array('Data 3', $source['data3']),
|
||||
array('Data 4', $source['data4']),
|
||||
array('Comment', $source['comment']));
|
||||
|
||||
echo $this->element('table',
|
||||
@@ -49,6 +50,22 @@ echo '</div>' . "\n";
|
||||
|
||||
echo '<div CLASS="detail supporting">' . "\n";
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Ledger Entries
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'monetary_source_id' => $source['id'],
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
('caption' => "Ledger Entries",
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
<h2>Sweet, "Pmgr" got Baked by CakePHP!</h2>
|
||||
|
||||
<?php
|
||||
if (Configure::read() > 0):
|
||||
Debugger::checkSessionKey();
|
||||
endif;
|
||||
?>
|
||||
<p>
|
||||
<?php
|
||||
if (is_writable(TMP)):
|
||||
echo '<span class="notice success">';
|
||||
__('Your tmp directory is writable.');
|
||||
echo '</span>';
|
||||
else:
|
||||
echo '<span class="notice">';
|
||||
__('Your tmp directory is NOT writable.');
|
||||
echo '</span>';
|
||||
endif;
|
||||
?>
|
||||
</p>
|
||||
<p>
|
||||
<?php
|
||||
$settings = Cache::settings();
|
||||
if (!empty($settings)):
|
||||
echo '<span class="notice success">';
|
||||
echo sprintf(__('The %s is being used for caching. To change the config edit APP/config/core.php ', true), '<em>'. $settings['engine'] . 'Engine</em>');
|
||||
echo '</span>';
|
||||
else:
|
||||
echo '<span class="notice">';
|
||||
__('Your cache is NOT working. Please check the settings in APP/config/core.php');
|
||||
echo '</span>';
|
||||
endif;
|
||||
?>
|
||||
</p>
|
||||
<p>
|
||||
<?php
|
||||
$filePresent = null;
|
||||
if (file_exists(CONFIGS . 'database.php')):
|
||||
echo '<span class="notice success">';
|
||||
__('Your database configuration file is present.');
|
||||
$filePresent = true;
|
||||
echo '</span>';
|
||||
else:
|
||||
echo '<span class="notice">';
|
||||
__('Your database configuration file is NOT present.');
|
||||
echo '<br/>';
|
||||
__('Rename config/database.php.default to config/database.php');
|
||||
echo '</span>';
|
||||
endif;
|
||||
?>
|
||||
</p>
|
||||
<?php
|
||||
if (!empty($filePresent)):
|
||||
uses('model' . DS . 'connection_manager');
|
||||
$db = ConnectionManager::getInstance();
|
||||
$connected = $db->getDataSource('default');
|
||||
?>
|
||||
<p>
|
||||
<?php
|
||||
if ($connected->isConnected()):
|
||||
echo '<span class="notice success">';
|
||||
__('Cake is able to connect to the database.');
|
||||
echo '</span>';
|
||||
else:
|
||||
echo '<span class="notice">';
|
||||
__('Cake is NOT able to connect to the database.');
|
||||
echo '</span>';
|
||||
endif;
|
||||
?>
|
||||
</p>
|
||||
<?php endif;?>
|
||||
<h3><?php __('Editing this Page') ?></h3>
|
||||
<p>
|
||||
<?php
|
||||
echo sprintf(__('To change the content of this page, edit: %s
|
||||
To change its layout, edit: %s
|
||||
You can also add some CSS styles for your pages at: %s', true),
|
||||
APP . 'views' . DS . 'pages' . DS . 'home.ctp.<br />', APP . 'views' . DS . 'layouts' . DS . 'default.ctp.<br />', APP . 'webroot' . DS . 'css');
|
||||
?>
|
||||
</p>
|
||||
@@ -11,7 +11,6 @@ echo '<div class="transaction view">' . "\n";
|
||||
|
||||
$rows = array(array('ID', $transaction['Transaction']['id']),
|
||||
array('Timestamp', FormatHelper::datetime($transaction['Transaction']['stamp'])),
|
||||
array('Through', FormatHelper::date($transaction['Transaction']['through_date'])),
|
||||
array('Due', FormatHelper::date($transaction['Transaction']['due_date'])),
|
||||
array('Comment', $transaction['Transaction']['comment']));
|
||||
|
||||
@@ -52,12 +51,21 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
* Entries
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries',
|
||||
array('caption' => 'Entries in Transaction',
|
||||
//'ledger_entries' => $transaction['LedgerEntry'],
|
||||
'transaction_id' => $transaction['Transaction']['id'],
|
||||
'notxgroup' => true,
|
||||
));
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'transaction_id' => $transaction['Transaction']['id'],
|
||||
|
||||
// Default for grouping by transaction is already false,
|
||||
// but we'll get explicit here, since we clearly want to
|
||||
// see all of the ledger entries not grouped by tx.
|
||||
'group_by_tx' => false,
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'caption' => 'Entries in Transaction',
|
||||
),
|
||||
));
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
|
||||
@@ -9,15 +9,25 @@ echo '<div class="unit view">' . "\n";
|
||||
* Unit Detail Main Section
|
||||
*/
|
||||
|
||||
$rows = array(array('Name', $unit['Unit']['name']),
|
||||
array('Status', $unit['Unit']['status']),
|
||||
array('Comment', $unit['Unit']['comment']));
|
||||
$leases = $unit['Lease'];
|
||||
$current_lease = $unit['CurrentLease'];
|
||||
$unit_size = $unit['UnitSize'];
|
||||
|
||||
if (isset($unit['Unit']))
|
||||
$unit = $unit['Unit'];
|
||||
|
||||
$rows = array(array('Name', $unit['name']),
|
||||
array('Status', $unit['status']),
|
||||
array('Size', $unit_size['name']),
|
||||
array('Deposit', FormatHelper::currency($unit['deposit'])),
|
||||
array('Rent', FormatHelper::currency($unit['rent'])),
|
||||
array('Comment', $unit['comment']));
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item unit detail',
|
||||
'caption' => 'Unit Info',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value')));
|
||||
'caption' => 'Unit Info',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value')));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
@@ -51,22 +61,33 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
* Lease History
|
||||
*/
|
||||
|
||||
echo $this->element('leases',
|
||||
array('caption' => 'Lease History',
|
||||
'leases' => $unit['Lease']));
|
||||
echo $this->element('leases', array
|
||||
('config' => array
|
||||
('caption' => 'Lease History',
|
||||
'rows' => $leases,
|
||||
)));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Current Tenant Lease Account History
|
||||
* Current Customer Lease Account History
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries',
|
||||
array('caption' => ('Current Lease Account (' .
|
||||
$unit['CurrentLease']['Customer']['name']
|
||||
. ')'),
|
||||
'ar_account' => true,
|
||||
'lease_id' => $unit['CurrentLease']['id'],
|
||||
));
|
||||
if (isset($current_lease['id'])) {
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'ar_account' => true,
|
||||
'lease_id' => $current_lease['id'],
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'caption' =>
|
||||
('Current Lease Account ('
|
||||
. $current_lease['Customer']['name']
|
||||
. ')'),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
/* table th, td { border: dashed 1px; } */
|
||||
/* .debug-border { border: dashed 1px; } */
|
||||
|
||||
/************************************************************
|
||||
************************************************************
|
||||
@@ -27,9 +28,8 @@ table caption { text-align: left;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
div.item.list { margin-bottom: 1.5em; }
|
||||
/* table.item caption { margin-top: 1em; } */
|
||||
/* table.detail caption { margin-top: 0; } */
|
||||
div.item.list { margin-bottom: 1.5em; }
|
||||
table.item.list { margin-bottom: 1.5em; }
|
||||
|
||||
|
||||
/************************************************************
|
||||
@@ -57,11 +57,16 @@ table.item td { border-right: 1px solid #ccc; }
|
||||
|
||||
tr.evnrow { background: #f4f4f4; }
|
||||
|
||||
/* Generic classes to help with table alignment */
|
||||
td.right { text-align: right; }
|
||||
td.left { text-align: left; }
|
||||
td.center { text-align: center; }
|
||||
|
||||
|
||||
/************************************************************
|
||||
************************************************************
|
||||
* Item detail formats
|
||||
* (such as Tenant Info, Unit Info, etc)
|
||||
* (such as Customer Info, Unit Info, etc)
|
||||
*/
|
||||
|
||||
table.detail { width : 60%;
|
||||
@@ -73,6 +78,43 @@ table.item.detail td.value { white-space : normal; }
|
||||
div.detail.supporting { clear : both;
|
||||
padding-top: 1.5em; }
|
||||
|
||||
/* Exception for Ledger Entry, which is split over 3 tables */
|
||||
.ledger-double-entry { padding-top: 1.0em;
|
||||
float: left;
|
||||
clear : left;
|
||||
width : 60%;
|
||||
}
|
||||
|
||||
.ledger-double-entry table.item.detail {
|
||||
width : 45%;
|
||||
/* border: 2px dashed #0f0; */
|
||||
/* margin-left : 5%; */
|
||||
/* margin-right : 5%; */
|
||||
}
|
||||
|
||||
.ledger-double-entry table.item.detail.debit {
|
||||
/* margin-left : 2%; */
|
||||
float: left;
|
||||
}
|
||||
|
||||
.ledger-double-entry table.item.detail.credit {
|
||||
/* margin-right : 2%; */
|
||||
float: right;
|
||||
|
||||
}
|
||||
|
||||
/* .ledger-double-entry table.debit { clear: */
|
||||
|
||||
|
||||
|
||||
/************************************************************
|
||||
************************************************************
|
||||
* Item edit formats
|
||||
*/
|
||||
|
||||
.edit table.detail { width : 80%;
|
||||
float : none;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************
|
||||
@@ -85,21 +127,26 @@ div.detail.supporting { clear : both;
|
||||
|
||||
div.infobox { float: right;
|
||||
width: 39%;
|
||||
margin-top: 1.5em;
|
||||
margin-top: 2.0em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
table.summary {
|
||||
margin-left:auto;
|
||||
margin-right:0.5em;
|
||||
color: #993;
|
||||
color: #339;
|
||||
font-family:'Gill Sans','lucida grande',helvetica, arial, sans-serif;
|
||||
font-size: 125%;
|
||||
text-align: right;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.2em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
table.summary caption {
|
||||
font-size: 100%;
|
||||
text-align: right;
|
||||
margin-right: 0.6em;
|
||||
}
|
||||
|
||||
table.summary td {
|
||||
padding-left:0.5em;
|
||||
}
|
||||
@@ -147,10 +194,43 @@ table.list.ledger td.date.receipt { padding-left: 1em; }
|
||||
table.list.ledger td.evnrow { background: #f4f4f4; }
|
||||
|
||||
|
||||
/************************************************************
|
||||
************************************************************
|
||||
* Special cases
|
||||
*/
|
||||
|
||||
/* Deposit Slips */
|
||||
table.list.deposit-slip { width : 60%;
|
||||
/* REVISIT <AP>: 20090701
|
||||
* 100% if printing
|
||||
*/
|
||||
}
|
||||
table.list.deposit-slip td.item { width : 30%; }
|
||||
table.list.deposit-slip td.amount { width : 15%; }
|
||||
|
||||
table.deposit-summary { margin-bottom: 1em; }
|
||||
table.deposit-summary td { vertical-align: bottom; }
|
||||
table.deposit-summary td { font-size: 125%; }
|
||||
table.deposit-summary td.grand.total { border-top : 4px double #000; }
|
||||
table.deposit-summary td span.dollar-sign { float: left; }
|
||||
table.deposit-summary td span.dollar-amount { float: right; }
|
||||
table.deposit-summary td.account { padding-right: 0.8em; }
|
||||
table.deposit-summary td.quantity { padding-right: 0.8em; }
|
||||
|
||||
|
||||
/* Collected Income */
|
||||
.account.collected.entry { float : left;
|
||||
clear : none; }
|
||||
form#collected-form input[type=button] { float : left;
|
||||
clear : left; }
|
||||
|
||||
/************************************************************
|
||||
************************************************************
|
||||
* jqGrid
|
||||
*/
|
||||
|
||||
div.grid.text-below { margin-bottom: 0; }
|
||||
|
||||
table.Header th h2 {
|
||||
color: #dd5;
|
||||
font-family:'Gill Sans','lucida grande',helvetica, arial, sans-serif;
|
||||
@@ -163,16 +243,53 @@ div.loading {
|
||||
margin-left: 1.0em;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
************************************************************
|
||||
* Grid Dynamic Selection Text
|
||||
*/
|
||||
|
||||
.grid-selection-text { font-size: 150%;
|
||||
margin-top: 0.4em;
|
||||
margin-bottom: 1em; }
|
||||
.grid-selection-text .supporting { font-size: 80%; }
|
||||
|
||||
.grid-selection-text .supporting table { border: none; }
|
||||
.grid-selection-text .supporting table td.field { width : 5em; }
|
||||
|
||||
|
||||
|
||||
/************************************************************
|
||||
************************************************************
|
||||
* Forms
|
||||
*/
|
||||
|
||||
|
||||
form {
|
||||
margin-right: 20px;
|
||||
padding: 0;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
div.dynamic-set fieldset.superset {
|
||||
margin-top: 1.5em;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
div.dynamic-set fieldset.superset legend {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.dynamic-set fieldset.superset fieldset {
|
||||
margin-top: 0.8em;
|
||||
/* width: 90%; */
|
||||
}
|
||||
|
||||
div.dynamic-set fieldset.superset fieldset legend {
|
||||
font-size: 110%;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #ccc;
|
||||
margin-top: 4px;
|
||||
|
||||
@@ -84,7 +84,7 @@ function htmlEncode(s)
|
||||
function addDiv(id_name, div_name, into_div_name, flash, html, script) {
|
||||
var id = $('#'+id_name).val();
|
||||
|
||||
html = '<DIV class="added-div" id="'+div_name+'-'+id+'">' +
|
||||
html = '<DIV class="added-div" id="'+div_name+'-'+id+'" STYLE="display:none;">' +
|
||||
html.replace(/%{id}/g, id)
|
||||
.replace(/%{remove(:([^}]*))?}/g,
|
||||
'<SPAN class="remove-div-link">' +
|
||||
@@ -103,8 +103,13 @@ function addDiv(id_name, div_name, into_div_name, flash, html, script) {
|
||||
|
||||
if (flash) {
|
||||
$('#'+div_name+'-'+id)
|
||||
.animate({ backgroundColor: "yellow" }, 300)
|
||||
.animate({ backgroundColor: "white" }, 500);
|
||||
.css({'background-color' : 'yellow'})
|
||||
.slideDown()
|
||||
//.animate({ backgroundColor: "yellow" }, 300)
|
||||
.animate({ backgroundColor: "white" }, 500);
|
||||
} else {
|
||||
$('#'+div_name+'-'+id)
|
||||
.show();
|
||||
}
|
||||
|
||||
id = id - 0 + 1;
|
||||
@@ -134,13 +139,59 @@ function fmtCurrency(amount) {
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Datepicker helpers
|
||||
|
||||
function datepickerNow(id, usetime) {
|
||||
now = new Date();
|
||||
// datepicker seems to squash the time portion,
|
||||
// so we have to pass in a copy of now instead.
|
||||
$("#"+id).datepicker('setDate', new Date(now));
|
||||
if (usetime == null)
|
||||
usetime = true;
|
||||
$("#"+id).val($("#"+id).val() +
|
||||
(usetime
|
||||
? (' '
|
||||
+ (now.getHours() < 10 ? '0' : '')
|
||||
+ now.getHours() + ':'
|
||||
+ (now.getMinutes() < 10 ? '0' : '')
|
||||
+ now.getMinutes())
|
||||
: ''));
|
||||
}
|
||||
|
||||
function datepickerSet(fromid, id, a, b) {
|
||||
if (fromid == null)
|
||||
dt = new Date();
|
||||
else
|
||||
dt = new Date($("#"+fromid).datepicker('getDate'));
|
||||
|
||||
if (a != null)
|
||||
dt.setDate(a);
|
||||
if (b != null)
|
||||
dt.setDate(b);
|
||||
|
||||
$("#"+id).datepicker('setDate', dt);
|
||||
}
|
||||
|
||||
function datepickerBOM(fromid, id) {
|
||||
datepickerSet(fromid, id, 1);
|
||||
}
|
||||
|
||||
function datepickerEOM(fromid, id) {
|
||||
datepickerSet(fromid, id, 32, 0);
|
||||
}
|
||||
|
||||
|
||||
// REVISIT <AP>: 20090617
|
||||
// I would rather use XML to pass from JS to PHP, but at the
|
||||
// moment things were working just fine with serialize, and
|
||||
// I'm not keen on redesigning it at the moment. So, here
|
||||
// is a serialize implementation I found on the web.
|
||||
|
||||
function serialize( mixed_value ) {
|
||||
function serialize( mixed_value, depth ) {
|
||||
// http://kevin.vanzonneveld.net
|
||||
// + original by: Arpad Ray (mailto:arpad@php.net)
|
||||
// + improved by: Dino
|
||||
@@ -154,7 +205,19 @@ function serialize( mixed_value ) {
|
||||
// * returns 1: 'a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}'
|
||||
// * example 2: serialize({firstName: 'Kevin', midName: 'van', surName: 'Zonneveld'});
|
||||
// * returns 2: 'a:3:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";s:7:"surName";s:9:"Zonneveld";}'
|
||||
|
||||
// if (depth == null)
|
||||
// depth = '';
|
||||
|
||||
// if (depth == '')
|
||||
// $("#debug").html('<P>');
|
||||
|
||||
// $("#debug").append(depth+"serialize()<BR>\n");
|
||||
|
||||
// if (depth.length > 80) {
|
||||
// $("#debug").append(depth+"OVERFLOW<BR>\n");
|
||||
// return 'ABORTED';
|
||||
// }
|
||||
|
||||
var _getType = function( inp ) {
|
||||
var type = typeof inp, match;
|
||||
var key;
|
||||
@@ -182,6 +245,8 @@ function serialize( mixed_value ) {
|
||||
};
|
||||
var type = _getType(mixed_value);
|
||||
var val, ktype = '';
|
||||
|
||||
// $("#debug").append(depth+" - type: "+type+"<BR>\n");
|
||||
|
||||
switch (type) {
|
||||
case "function":
|
||||
@@ -218,13 +283,16 @@ function serialize( mixed_value ) {
|
||||
var key;
|
||||
for (key in mixed_value) {
|
||||
ktype = _getType(mixed_value[key]);
|
||||
// $("#debug").append(depth+" - key["+count+"] type: "+type+"<BR>\n");
|
||||
if (ktype == "function") {
|
||||
continue;
|
||||
}
|
||||
|
||||
okey = (key.match(/^[0-9]+$/) ? parseInt(key, 10) : key);
|
||||
vals += serialize(okey) +
|
||||
serialize(mixed_value[key]);
|
||||
// $("#debug").append(depth+" - okey: "+okey+"<BR>\n");
|
||||
// $("#debug").append(depth+" - mixed[key]: "+mixed_value[key]+"<BR>\n");
|
||||
vals += serialize(okey, depth+' ') +
|
||||
serialize(mixed_value[key], depth+' ');
|
||||
count++;
|
||||
}
|
||||
val += ":" + count + ":{" + vals + "}";
|
||||
@@ -233,6 +301,7 @@ function serialize( mixed_value ) {
|
||||
if (type != "object" && type != "array") {
|
||||
val += ";";
|
||||
}
|
||||
// $("#debug").append(depth+" - val: "+val+"<BR>\n");
|
||||
return val;
|
||||
|
||||
}
|
||||
|
||||
382
site/webroot/js/pmgr.js.keep
Normal file
382
site/webroot/js/pmgr.js.keep
Normal file
@@ -0,0 +1,382 @@
|
||||
/**
|
||||
* Function : dump()
|
||||
* Arguments: The data - array,hash(associative array),object
|
||||
* The level - OPTIONAL
|
||||
* Returns : The textual representation of the array.
|
||||
* This function was inspired by the print_r function of PHP.
|
||||
* This will accept some data as the argument and return a
|
||||
* text that will be a more readable version of the
|
||||
* array/hash/object that is given.
|
||||
* Docs: http://www.openjs.com/scripts/others/dump_function_php_print_r.php
|
||||
*/
|
||||
function dump(arr,level) {
|
||||
var dumped_text = "";
|
||||
if(!level) level = 0;
|
||||
|
||||
//The padding given at the beginning of the line.
|
||||
var level_padding = "";
|
||||
for(var j=0;j<level+1;j++) level_padding += " ";
|
||||
|
||||
if(typeof(arr) == 'object') { //Array/Hashes/Objects
|
||||
for(var item in arr) {
|
||||
var value = arr[item];
|
||||
|
||||
if(typeof(value) == 'object') { //If it is an array,
|
||||
dumped_text += level_padding + "'" + item + "' ...\n";
|
||||
dumped_text += dump(value,level+1);
|
||||
} else {
|
||||
dumped_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
|
||||
}
|
||||
}
|
||||
} else { //Stings/Chars/Numbers etc.
|
||||
dumped_text = "===>"+arr+"<===("+typeof(arr)+")";
|
||||
}
|
||||
return dumped_text;
|
||||
}
|
||||
|
||||
|
||||
function var_dump(element, limit, depth)
|
||||
{
|
||||
depth = depth?depth:0;
|
||||
limit = limit?limit:1;
|
||||
|
||||
returnString = '<ol>';
|
||||
|
||||
for(property in element)
|
||||
{
|
||||
//Property domConfig isn't accessable
|
||||
if (property != 'domConfig')
|
||||
{
|
||||
returnString += '<li><strong>'+ property + '</strong> <small>(' + (typeof element[property]) +')</small>';
|
||||
|
||||
if (typeof element[property] == 'number' || typeof element[property] == 'boolean')
|
||||
returnString += ' : <em>' + element[property] + '</em>';
|
||||
if (typeof element[property] == 'string' && element[property])
|
||||
returnString += ': <div style="background:#C9C9C9;border:1px solid black; overflow:auto;"><code>' +
|
||||
element[property].replace(/</g, '&lt;').replace(/>/g, '&gt;') + '</code></div>';
|
||||
|
||||
if ((typeof element[property] == 'object') && (depth < limit))
|
||||
returnString += var_dump(element[property], limit, (depth + 1));
|
||||
|
||||
returnString += '</li>';
|
||||
}
|
||||
}
|
||||
returnString += '</ol>';
|
||||
|
||||
if(depth == 0)
|
||||
{
|
||||
winpop = window.open("", "","width=800,height=600,scrollbars,resizable");
|
||||
winpop.document.write('<pre>'+returnString+ '</pre>');
|
||||
winpop.document.close();
|
||||
}
|
||||
|
||||
return returnString;
|
||||
}
|
||||
|
||||
|
||||
function htmlEncode(s)
|
||||
{
|
||||
//return s;
|
||||
return s.replace(/&(?!\w+([;\s]|$))/g, "&")
|
||||
.replace(/</g, "<").replace(/>/g, ">");
|
||||
}
|
||||
|
||||
function addDiv(id_name, div_name, into_div_name, flash, html, script) {
|
||||
var id = $('#'+id_name).val();
|
||||
|
||||
html = '<DIV class="added-div" id="'+div_name+'-'+id+'">' +
|
||||
html.replace(/%{id}/g, id)
|
||||
.replace(/%{remove(:([^}]*))?}/g,
|
||||
'<SPAN class="remove-div-link">' +
|
||||
'<A HREF="#" onClick="removeElement' + "('"+div_name+"-"+id+"')" + '; return false;">' +
|
||||
("$2" == "" ? "$2" : 'remove') + '</A>' + '</SPAN>') +
|
||||
'</DIV>';
|
||||
|
||||
if (script) {
|
||||
html += '<SCRIPT TYPE="text/javascript">';
|
||||
html += script.replace(/%{id}/g, id);
|
||||
html += '</SCRIPT>';
|
||||
}
|
||||
|
||||
//$("#debug").append(htmlEncode(html));
|
||||
$("#"+into_div_name).append(html);
|
||||
|
||||
if (flash) {
|
||||
$('#'+div_name+'-'+id)
|
||||
.animate({ backgroundColor: "yellow" }, 300)
|
||||
.animate({ backgroundColor: "white" }, 500);
|
||||
}
|
||||
|
||||
id = id - 0 + 1;
|
||||
$('#'+id_name).val(id);
|
||||
}
|
||||
|
||||
function removeElement(elem_id) {
|
||||
$('#'+elem_id).remove();
|
||||
}
|
||||
|
||||
// function
|
||||
// var currentTime = new Date()
|
||||
// var month = currentTime.getMonth() + 1
|
||||
// var day = currentTime.getDate()
|
||||
// var year = currentTime.getFullYear()
|
||||
// document.write(month + "/" + day + "/" + year)
|
||||
// //-->
|
||||
// var currentTime = new Date()
|
||||
// var hours = currentTime.getHours()
|
||||
// var minutes = currentTime.getMinutes()
|
||||
// if (minutes < 10){
|
||||
// minutes = "0" + minutes
|
||||
// }
|
||||
// document.write(hours + ":" + minutes + " ")
|
||||
// if(hours > 11){
|
||||
// document.write("PM")
|
||||
// } else {
|
||||
// document.write("AM")
|
||||
// }
|
||||
// //-->
|
||||
|
||||
function fmtCurrency(amount) {
|
||||
if (amount == null || isNaN(amount))
|
||||
return '-';
|
||||
|
||||
// Get rid of any extraneous characters, determine
|
||||
// the sign, and round to the nearest cent.
|
||||
amount = amount.toString().replace(/\$|\,/g,'');
|
||||
sign = (amount == (amount = Math.abs(amount)));
|
||||
amount = (amount+0.0000000001).toFixed(2);
|
||||
|
||||
// Insert thousands separator
|
||||
while (amount != (amount = amount.replace(/(\d)(\d\d\d[.,])/, "$1,$2")));
|
||||
|
||||
// Return formatted amount
|
||||
return (sign?'$':'($') + amount + (sign?'':')');
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Date Format 1.2.2
|
||||
* (c) 2007-2008 Steven Levithan <stevenlevithan.com>
|
||||
* MIT license
|
||||
* Includes enhancements by Scott Trenda <scott.trenda.net> and Kris Kowal <cixar.com/~kris.kowal/>
|
||||
*
|
||||
* Accepts a date, a mask, or a date and a mask.
|
||||
* Returns a formatted version of the given date.
|
||||
* The date defaults to the current date/time.
|
||||
* The mask defaults to dateFormat.masks.default.
|
||||
*/
|
||||
var dateFormat = function () {
|
||||
var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|\"[^\"]*\"|\'[^\']*\'/g,
|
||||
timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
|
||||
timezoneClip = /[^-+\dA-Z]/g,
|
||||
pad = function (val, len) {
|
||||
val = String(val);
|
||||
len = len || 2;
|
||||
while (val.length < len) val = "0" + val;
|
||||
return val;
|
||||
};
|
||||
|
||||
// Regexes and supporting functions are cached through closure
|
||||
return function (date, mask, utc) {
|
||||
var dF = dateFormat;
|
||||
|
||||
// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
|
||||
if (arguments.length == 1 && (typeof date == "string" || date instanceof String) && !/\d/.test(date)) {
|
||||
mask = date;
|
||||
date = undefined;
|
||||
}
|
||||
|
||||
// Passing date through Date applies Date.parse, if necessary
|
||||
date = date ? new Date(date) : new Date();
|
||||
if (isNaN(date)) throw new SyntaxError("invalid date");
|
||||
|
||||
mask = String(dF.masks[mask] || mask || dF.masks["default"]);
|
||||
|
||||
// Allow setting the utc argument via the mask
|
||||
if (mask.slice(0, 4) == "UTC:") {
|
||||
mask = mask.slice(4);
|
||||
utc = true;
|
||||
}
|
||||
|
||||
var _ = utc ? "getUTC" : "get",
|
||||
d = date[_ + "Date"](),
|
||||
D = date[_ + "Day"](),
|
||||
m = date[_ + "Month"](),
|
||||
y = date[_ + "FullYear"](),
|
||||
H = date[_ + "Hours"](),
|
||||
M = date[_ + "Minutes"](),
|
||||
s = date[_ + "Seconds"](),
|
||||
L = date[_ + "Milliseconds"](),
|
||||
o = utc ? 0 : date.getTimezoneOffset(),
|
||||
flags = {
|
||||
d: d,
|
||||
dd: pad(d),
|
||||
ddd: dF.i18n.dayNames[D],
|
||||
dddd: dF.i18n.dayNames[D + 7],
|
||||
m: m + 1,
|
||||
mm: pad(m + 1),
|
||||
mmm: dF.i18n.monthNames[m],
|
||||
mmmm: dF.i18n.monthNames[m + 12],
|
||||
yy: String(y).slice(2),
|
||||
yyyy: y,
|
||||
h: H % 12 || 12,
|
||||
hh: pad(H % 12 || 12),
|
||||
H: H,
|
||||
HH: pad(H),
|
||||
M: M,
|
||||
MM: pad(M),
|
||||
s: s,
|
||||
ss: pad(s),
|
||||
l: pad(L, 3),
|
||||
L: pad(L > 99 ? Math.round(L / 10) : L),
|
||||
t: H < 12 ? "a" : "p",
|
||||
tt: H < 12 ? "am" : "pm",
|
||||
T: H < 12 ? "A" : "P",
|
||||
TT: H < 12 ? "AM" : "PM",
|
||||
Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
|
||||
o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
|
||||
S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
|
||||
};
|
||||
|
||||
return mask.replace(token, function ($0) {
|
||||
return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
|
||||
});
|
||||
};
|
||||
}();
|
||||
|
||||
// Some common format strings
|
||||
dateFormat.masks = {
|
||||
"default": "ddd mmm dd yyyy HH:MM:ss",
|
||||
shortDate: "m/d/yy",
|
||||
mediumDate: "mmm d, yyyy",
|
||||
longDate: "mmmm d, yyyy",
|
||||
fullDate: "dddd, mmmm d, yyyy",
|
||||
shortTime: "h:MM TT",
|
||||
mediumTime: "h:MM:ss TT",
|
||||
longTime: "h:MM:ss TT Z",
|
||||
isoDate: "yyyy-mm-dd",
|
||||
isoTime: "HH:MM:ss",
|
||||
isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
|
||||
isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
|
||||
};
|
||||
|
||||
// Internationalization strings
|
||||
dateFormat.i18n = {
|
||||
dayNames: [
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
|
||||
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
|
||||
],
|
||||
monthNames: [
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
||||
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
|
||||
]
|
||||
};
|
||||
|
||||
// For convenience...
|
||||
Date.prototype.format = function (mask, utc) {
|
||||
return dateFormat(this, mask, utc);
|
||||
};
|
||||
|
||||
|
||||
// REVISIT <AP>: 20090617
|
||||
// I would rather use XML to pass from JS to PHP, but at the
|
||||
// moment things were working just fine with serialize, and
|
||||
// I'm not keen on redesigning it at the moment. So, here
|
||||
// is a serialize implementation I found on the web.
|
||||
|
||||
function serialize( mixed_value ) {
|
||||
// http://kevin.vanzonneveld.net
|
||||
// + original by: Arpad Ray (mailto:arpad@php.net)
|
||||
// + improved by: Dino
|
||||
// + bugfixed by: Andrej Pavlovic
|
||||
// + bugfixed by: Garagoth
|
||||
// + input by: DtTvB (http://dt.in.th/2008-09-16.string-length-in-bytes.html)
|
||||
// + bugfixed by: Russell Walker
|
||||
// % note: We feel the main purpose of this function should be to ease the transport of data between php & js
|
||||
// % note: Aiming for PHP-compatibility, we have to translate objects to arrays
|
||||
// * example 1: serialize(['Kevin', 'van', 'Zonneveld']);
|
||||
// * returns 1: 'a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}'
|
||||
// * example 2: serialize({firstName: 'Kevin', midName: 'van', surName: 'Zonneveld'});
|
||||
// * returns 2: 'a:3:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";s:7:"surName";s:9:"Zonneveld";}'
|
||||
|
||||
var _getType = function( inp ) {
|
||||
var type = typeof inp, match;
|
||||
var key;
|
||||
if (type == 'object' && !inp) {
|
||||
return 'null';
|
||||
}
|
||||
if (type == "object") {
|
||||
if (!inp.constructor) {
|
||||
return 'object';
|
||||
}
|
||||
var cons = inp.constructor.toString();
|
||||
match = cons.match(/(\w+)\(/);
|
||||
if (match) {
|
||||
cons = match[1].toLowerCase();
|
||||
}
|
||||
var types = ["boolean", "number", "string", "array"];
|
||||
for (key in types) {
|
||||
if (cons == types[key]) {
|
||||
type = types[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return type;
|
||||
};
|
||||
var type = _getType(mixed_value);
|
||||
var val, ktype = '';
|
||||
|
||||
switch (type) {
|
||||
case "function":
|
||||
val = "";
|
||||
break;
|
||||
case "undefined":
|
||||
val = "N";
|
||||
break;
|
||||
case "boolean":
|
||||
val = "b:" + (mixed_value ? "1" : "0");
|
||||
break;
|
||||
case "number":
|
||||
val = (Math.round(mixed_value) == mixed_value ? "i" : "d") + ":" + mixed_value;
|
||||
break;
|
||||
case "string":
|
||||
val = "s:" + encodeURIComponent(mixed_value).replace(/%../g, 'x').length + ":\"" + mixed_value + "\"";
|
||||
break;
|
||||
case "array":
|
||||
case "object":
|
||||
val = "a";
|
||||
/*
|
||||
if (type == "object") {
|
||||
var objname = mixed_value.constructor.toString().match(/(\w+)\(\)/);
|
||||
if (objname == undefined) {
|
||||
return;
|
||||
}
|
||||
objname[1] = serialize(objname[1]);
|
||||
val = "O" + objname[1].substring(1, objname[1].length - 1);
|
||||
}
|
||||
*/
|
||||
var count = 0;
|
||||
var vals = "";
|
||||
var okey;
|
||||
var key;
|
||||
for (key in mixed_value) {
|
||||
ktype = _getType(mixed_value[key]);
|
||||
if (ktype == "function") {
|
||||
continue;
|
||||
}
|
||||
|
||||
okey = (key.match(/^[0-9]+$/) ? parseInt(key, 10) : key);
|
||||
vals += serialize(okey) +
|
||||
serialize(mixed_value[key]);
|
||||
count++;
|
||||
}
|
||||
val += ":" + count + ":{" + vals + "}";
|
||||
break;
|
||||
}
|
||||
if (type != "object" && type != "array") {
|
||||
val += ";";
|
||||
}
|
||||
return val;
|
||||
|
||||
}
|
||||
129
todo.notes
Normal file
129
todo.notes
Normal file
@@ -0,0 +1,129 @@
|
||||
Sub-Total is broken, since it will only subtotal the current
|
||||
page of the grid. It needs to be implemented in SQL as it
|
||||
was in early (VERY early) implementations. At that time, I
|
||||
had to a use temporary variable to keep a running total. It
|
||||
worked, but was MySQL specific.
|
||||
|
||||
Sorting by Customer ID is broken. It must think it's already
|
||||
sorted by ID because the first click shows the arrow as
|
||||
DESC even though the sort is ASC. Subsequent clicks don't
|
||||
change anything. You must sort on a different column first
|
||||
then everything works.
|
||||
|
||||
Seems like security deposit is suddenly broken. I believe
|
||||
the customer/lease infobox used to report only PAID
|
||||
security deposits, but it now seems like it's reporting ALL
|
||||
security deposits charged.
|
||||
|
||||
Having a grid of ledger entries grouped by transaction appears
|
||||
to work, from the financial aspect, but the count of entries
|
||||
is incorrect. The problem is the grouping only occurs after
|
||||
the count, which it has to in order for the count to work. We
|
||||
need to obliterate the group_by_tx parameter, and simply use
|
||||
the transanction controller to generate the grid instead of
|
||||
ledger_entries.
|
||||
|
||||
Handle a credit, ensuring that it's applied to new charges
|
||||
- either automatically;
|
||||
- by user opt-in to use credits when invoicing
|
||||
- by user opt-in when entering a receipt
|
||||
- by manually allowing a receipt of credits
|
||||
|
||||
Get Petty Cash working. We'll need to add one or more expense
|
||||
accounts. We'll also need to implement purchase order
|
||||
functionality, or at least simple an expense page.
|
||||
|
||||
Reconcile all entries of a ledger to the c/f entry when
|
||||
"closing" the ledger and creating a new one.
|
||||
|
||||
Automatic assessment of rents, or at least for now, one
|
||||
click manual mechanism to assess rents correctly for all
|
||||
tenants.
|
||||
|
||||
Automatic assessment of late fees, or at least for now, one
|
||||
click manual mechanism to assess late fees correctly for all
|
||||
tenants.
|
||||
|
||||
Update unit status between OCCUPIED / LATE / LOCKED depending
|
||||
on the current situation.
|
||||
|
||||
Have a report indicating Needs-to-be-Locked. Allow manager
|
||||
to go through this list and check off the units actually
|
||||
locked (which will update the unit status). Perhaps we
|
||||
should add a needs-locked status.
|
||||
|
||||
Same as above, except needs-to-be-unlocked.
|
||||
|
||||
Determine when each unit is paid up until. There is actually
|
||||
two things here: invoiced up until, and paid up until. One or
|
||||
both of these should be displayed on the Lease view page.
|
||||
|
||||
Make the default (initial) jqGrid sort order for balance be DESC.
|
||||
|
||||
MUST reports:
|
||||
- Delinquent customers
|
||||
- Locked out units / customers
|
||||
- To-Lock units
|
||||
- Paid up until
|
||||
|
||||
WANT reports:
|
||||
- ???
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
-- DONE !
|
||||
|
||||
|
||||
VERIFY THAT OUR NEW BALANCE QUERY WORKS.
|
||||
(The one that was added to lease). It works for
|
||||
folks that have ledger entries, but I fear that the
|
||||
inner join will prevent customers from showing up
|
||||
in the list if they don't yet have any ledger entries
|
||||
in account receivable. To resolve this we'll have to
|
||||
go back to LEFT JOIN and check for NULL in our SUM()
|
||||
statement.
|
||||
|
||||
Fix sorting on Lease list by Lease Number. Have it
|
||||
reference ID instead.
|
||||
|
||||
Figure out why Brenda Harmon's lease #44, unit #B04, lists
|
||||
the security deposit as $150. She's only paid $25, so it must
|
||||
be a lease issue.
|
||||
|
||||
Modify LedgerEntry to have through_date, since a single
|
||||
invoice could have charges for several months rent. It's
|
||||
not clear whether due_date should also be moved to
|
||||
LedgerEntry, since some charges could have different due
|
||||
dates. The problem is that I can't picture an invoice
|
||||
having more than one due date.
|
||||
|
||||
Consider adding a from_date to LedgerEntry as well.
|
||||
|
||||
Fix Customers index list. To replicate, add a brand
|
||||
new customer. Select Customers. Notice it says
|
||||
'Current Customers', but actually includes the new
|
||||
one in the list.
|
||||
|
||||
There seems to be a problem with the account ledger for the
|
||||
customer. To replicate, see Mancini's account (#47). There
|
||||
are 4 entries in the Account ledger. One of them is was from
|
||||
5/1/09. It's a receipt (Transaction #610, Entry #702). The
|
||||
Transaction is for $111.33 as indicated, but the entry is only
|
||||
for $16.33.
|
||||
-- This was a problem with using notxgroup, the experimental
|
||||
-- field that used to be hardcoded to false in ledger_entries.ctp
|
||||
-- My rework eliminated that field, and everything was getting
|
||||
-- grouped by transaction.
|
||||
|
||||
Figure out how to utilize the security deposit, whether
|
||||
as part of move-out only, or as one of the payment options.
|
||||
|
||||
Reference in New Issue
Block a user