Compare commits
313 Commits
reconcile_
...
alt_revers
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
011d1208e8 | ||
|
|
9dd09c4d2e | ||
|
|
2e36d46329 | ||
|
|
1dd0b14861 | ||
|
|
d75cd10f49 | ||
|
|
a69a56c715 | ||
|
|
8f7cf202e5 | ||
|
|
58c4f28956 | ||
|
|
f3eaa40ea5 | ||
|
|
e784931fa8 | ||
|
|
5a7b087ddc | ||
|
|
4d62d7da73 | ||
|
|
cca698d437 | ||
|
|
5247bb8db6 | ||
|
|
cb969ba340 | ||
|
|
094e15ddf9 | ||
|
|
670f0894ea | ||
|
|
9e20473b3b | ||
|
|
11d5deac85 | ||
|
|
67280c89e7 | ||
|
|
1afed6a6e0 | ||
|
|
0ff91bf4d8 | ||
|
|
4d4e96fe1c | ||
|
|
32a98b4b6e | ||
|
|
817b74b085 | ||
|
|
dc1bb56188 | ||
|
|
4707f3314d | ||
|
|
ac2b1530fc | ||
|
|
a5d3ff0b70 | ||
|
|
adc87c0763 | ||
|
|
7d81b9766b | ||
|
|
1aa6273ade | ||
|
|
2c08405d5a | ||
|
|
e2ed6ed1c7 | ||
|
|
cb716b06b7 | ||
|
|
35d7656bc5 | ||
|
|
8818ad7a80 | ||
|
|
15a4528e75 | ||
|
|
44e4477d38 | ||
|
|
c4cc3ea812 | ||
|
|
1e10fbbf38 | ||
|
|
53a279a6db | ||
|
|
8e0270cc82 | ||
|
|
3a95a994cf | ||
|
|
00c99ea60a | ||
|
|
e09fb7d258 | ||
|
|
37e7212abe | ||
|
|
655e0c3940 | ||
|
|
e47a2cc7aa | ||
|
|
0cdcb6252e | ||
|
|
bbec6ccbb6 | ||
|
|
91fdef9997 | ||
|
|
3e2d219c8d | ||
|
|
57d593fc38 | ||
|
|
65b7137f21 | ||
|
|
323519ab75 | ||
|
|
a1a9c7800b | ||
|
|
fe17f87f7d | ||
|
|
6b9279f5b0 | ||
|
|
2882b1917c | ||
|
|
f707448b05 | ||
|
|
5247139fe8 | ||
|
|
d2a4021d6f | ||
|
|
b759f29d91 | ||
|
|
c0d26a8e95 | ||
|
|
a1f30804a7 | ||
|
|
55b3ec947e | ||
|
|
876d9b3d33 | ||
|
|
d33bf24958 | ||
|
|
424cb0ea4f | ||
|
|
63de30d392 | ||
|
|
07232c77d5 | ||
|
|
1e7557de71 | ||
|
|
d91957faed | ||
|
|
56e6aa1f34 | ||
|
|
6e759e8589 | ||
|
|
9733226dfd | ||
|
|
be2865b4d7 | ||
|
|
adddfecada | ||
|
|
55c5c9e9e6 | ||
|
|
42677ae2f4 | ||
|
|
ec8f540107 | ||
|
|
393c0dbb3f | ||
|
|
f8413b8784 | ||
|
|
e1217cd185 | ||
|
|
37d0fe7f3f | ||
|
|
13f97e5770 | ||
|
|
b789ed62b7 | ||
|
|
2ffe04e3e4 | ||
|
|
cdba179d79 | ||
|
|
5ba6438b77 | ||
|
|
a4459ef0de | ||
|
|
e7b71e0abb | ||
|
|
0c06ef6d71 | ||
|
|
0f42ad9b07 | ||
|
|
c20b287c53 | ||
|
|
ef4d8d4d30 | ||
|
|
e7a659a690 | ||
|
|
f6ee56501d | ||
|
|
826eb63da1 | ||
|
|
6cf6dda3f8 | ||
|
|
5446f6a266 | ||
|
|
b225775a91 | ||
|
|
a5565546d1 | ||
|
|
f0b65dcf74 | ||
|
|
ad1758a5dc | ||
|
|
b3389652fd | ||
|
|
ba62eed027 | ||
|
|
3dc751c863 | ||
|
|
68fe787209 | ||
|
|
d4292a85a0 | ||
|
|
f9635419dd | ||
|
|
d8f10bfd13 | ||
|
|
25b7da57f3 | ||
|
|
29fe265daa | ||
|
|
769c02a5f1 | ||
|
|
476a179e7b | ||
|
|
df8ebe45ef | ||
|
|
0bf458f243 | ||
|
|
18c4f5ea48 | ||
|
|
a0f33054cb | ||
|
|
0f00b42d1f | ||
|
|
1e0b96953e | ||
|
|
b408d86a98 | ||
|
|
e739282d17 | ||
|
|
c73016ecf2 | ||
|
|
122dfb10a0 | ||
|
|
9a32800170 | ||
|
|
04bc284a76 | ||
|
|
708759765f | ||
|
|
9dccbfeda8 | ||
|
|
f8c60ec265 | ||
|
|
234999b4d2 | ||
|
|
0ba5007438 | ||
|
|
8cf8f65474 | ||
|
|
c596dfa5f0 | ||
|
|
8331be454c | ||
|
|
8b65bc5159 | ||
|
|
634f0f5423 | ||
|
|
7aa026f4e0 | ||
|
|
93ebc450fe | ||
|
|
fd856323a5 | ||
|
|
59e6379977 | ||
|
|
ae5d4763f9 | ||
|
|
c002f3f3ba | ||
|
|
f8d4dcef94 | ||
|
|
a7671e76fe | ||
|
|
fc30dfa2e8 | ||
|
|
6ac0204baf | ||
|
|
e303898a95 | ||
|
|
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!
|
||||
|
||||
404
db/schema.sql
404
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).
|
||||
@@ -649,6 +644,17 @@ CREATE TABLE `pmgr_customers` (
|
||||
-- contacts_customers table?
|
||||
`primary_contact_id` INT(10) UNSIGNED NOT NULL,
|
||||
|
||||
-- Number of different leases for this customer.
|
||||
-- It's not good to have redundant information,
|
||||
-- but these fields change infrequently, and make
|
||||
-- certain queries much easier, most notably for
|
||||
-- the grid query, in which linking customer to
|
||||
-- lease results in repeated statement entries
|
||||
-- when a customer has more than one lease.
|
||||
`lease_count` SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
`current_lease_count` SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
`past_lease_count` SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
@@ -708,7 +714,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 +726,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 +853,14 @@ 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,
|
||||
`deposits` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for deposits?
|
||||
`invoices` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for invoices?
|
||||
`receipts` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for receipts?
|
||||
`refunds` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for refunds?
|
||||
|
||||
-- Security Level
|
||||
`level` INT UNSIGNED DEFAULT 1,
|
||||
`level` INT UNSIGNED DEFAULT 10,
|
||||
|
||||
`name` VARCHAR(80) NOT NULL,
|
||||
`external_account` INT(10) UNSIGNED DEFAULT NULL,
|
||||
@@ -863,21 +872,47 @@ CREATE TABLE `pmgr_accounts` (
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
LOCK TABLES `pmgr_accounts` WRITE;
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `trackable`)
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `level`)
|
||||
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);
|
||||
('EQUITY', 'Equity', 1),
|
||||
('LIABILITY', 'Loan', 1);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`)
|
||||
VALUES
|
||||
('ASSET', 'A/R' ),
|
||||
-- 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`, `receipts`, `refunds`)
|
||||
VALUES
|
||||
('ASSET', 'Cash', 1, 0),
|
||||
('ASSET', 'Check', 1, 0),
|
||||
('ASSET', 'Money Order', 1, 0),
|
||||
('ASSET', 'ACH', 1, 0),
|
||||
('ASSET', 'Closing', 0, 0), -- REVISIT <AP>: Temporary
|
||||
('EXPENSE', 'Concession', 1, 0),
|
||||
('EXPENSE', 'Waiver', 0, 0);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `refunds`, `deposits`)
|
||||
VALUES
|
||||
-- REVISIT <AP>: 20090710 : We probably don't really want petty cash depositable.
|
||||
-- This is just for testing our deposit code
|
||||
('ASSET', 'Petty Cash', 1, 1);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `invoices`)
|
||||
VALUES
|
||||
('LIABILITY', 'Tax', 1),
|
||||
('LIABILITY', 'Security Deposit', 1),
|
||||
('INCOME', 'Rent', 1),
|
||||
('INCOME', 'Late Charge', 1),
|
||||
('INCOME', 'NSF Charge', 1),
|
||||
('INCOME', 'Damage', 1);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `deposits`, `refunds`)
|
||||
VALUES
|
||||
('ASSET', 'Bank', 1, 1);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`)
|
||||
VALUES
|
||||
('EXPENSE', 'Bad Debt' ),
|
||||
('EXPENSE', 'Maintenance' );
|
||||
UNLOCK TABLES;
|
||||
|
||||
|
||||
@@ -899,34 +934,18 @@ UNLOCK TABLES;
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_ledgers
|
||||
--
|
||||
-- REVISIT <AP>: 20090605
|
||||
-- We may not really need a ledgers table.
|
||||
-- It's not clear to me though, as we very
|
||||
-- possibly need to close out certain
|
||||
-- ledgers every so often, and just carry
|
||||
-- the balance forward (year end, etc).
|
||||
|
||||
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.
|
||||
`name` VARCHAR(90) NOT NULL,
|
||||
|
||||
`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,
|
||||
|
||||
`open_stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`close_stamp` DATETIME DEFAULT NULL,
|
||||
`prior_ledger_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
`close_transaction_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
@@ -942,14 +961,51 @@ DROP TABLE IF EXISTS `pmgr_transactions`;
|
||||
CREATE TABLE `pmgr_transactions` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
`stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`through_date` DATE DEFAULT NULL,
|
||||
`due_date` DATE DEFAULT NULL,
|
||||
-- REVISIT <AP>: 20090804
|
||||
-- I'm not sure about most of these terms.
|
||||
-- Just as long as they're distinct though... I can rename them later
|
||||
`type` ENUM('INVOICE', -- Sales Invoice
|
||||
'RECEIPT', -- Actual receipt of monies
|
||||
'PURCHASE_INVOICE', -- Committment to pay
|
||||
'CREDIT_NOTE', -- Inverse of Sales Invoice
|
||||
'PAYMENT', -- Actual payment
|
||||
'DEPOSIT',
|
||||
'CLOSE', -- Essentially an internal (not accounting) transaction
|
||||
-- 'CREDIT',
|
||||
-- 'REFUND',
|
||||
'TRANSFER') -- Unsure of TRANSFERs use
|
||||
NOT NULL,
|
||||
|
||||
-- REVISIT <AP>: 20090604
|
||||
-- How should we track which charges have been paid?
|
||||
-- `related_transaction_id` INT(10) UNSIGNED NOT NULL,
|
||||
-- `related_entry_id` INT(10) UNSIGNED NOT NULL,
|
||||
`stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- All entries of a transaction should be for the same
|
||||
-- customer. By keeping track of customer here, it ensures
|
||||
-- that we can always track what's happening with the user
|
||||
-- even if there are only ledger entries, and no statement
|
||||
-- entries for some reason (the primary concern being the
|
||||
-- receipt of money, with nothing to pay for).
|
||||
-- customer_id can be NULL, for internal transfers and such.
|
||||
-- In that case, there should be no statement entries for
|
||||
-- this transaction, only ledger entries.
|
||||
-- REVISIT <AP>: 20090723
|
||||
-- It sounds like a transaction that has customer_id as NULL
|
||||
-- is really a fundamentally different type of "transaction".
|
||||
-- Do we need to have a new table for those type of
|
||||
-- entries / activities?
|
||||
`customer_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
-- The account/ledger of the transaction set
|
||||
-- (e.g. A/R, Bank, etc)
|
||||
`account_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
`ledger_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
`crdr` ENUM('DEBIT',
|
||||
'CREDIT')
|
||||
DEFAULT NULL,
|
||||
|
||||
-- amount is for convenience. It can always be calculated from
|
||||
-- the associated double entries (and therefore will need to be
|
||||
-- updated if they should change in any way).
|
||||
`amount` FLOAT(12,2) DEFAULT NULL,
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
@@ -957,23 +1013,110 @@ CREATE TABLE `pmgr_transactions` (
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_ledger_entries
|
||||
-- -- ----------------------------------------------------------------------
|
||||
-- -- ----------------------------------------------------------------------
|
||||
-- -- TABLE pmgr_ledger_entries
|
||||
|
||||
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,
|
||||
`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,
|
||||
|
||||
-- The account/ledger of the entry
|
||||
`account_id` INT(10) UNSIGNED NOT NULL,
|
||||
`ledger_id` INT(10) UNSIGNED NOT NULL,
|
||||
`crdr` ENUM('DEBIT',
|
||||
'CREDIT')
|
||||
NOT NULL,
|
||||
|
||||
`amount` FLOAT(12,2) NOT NULL,
|
||||
|
||||
`debit_ledger_id` INT(10) UNSIGNED NOT NULL,
|
||||
`credit_ledger_id` INT(10) UNSIGNED NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- -- ----------------------------------------------------------------------
|
||||
-- -- ----------------------------------------------------------------------
|
||||
-- -- TABLE pmgr_double_entries
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_double_entries`;
|
||||
CREATE TABLE `pmgr_double_entries` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- The two entries that make up a "double entry"
|
||||
`debit_entry_id` INT(10) UNSIGNED NOT NULL,
|
||||
`credit_entry_id` INT(10) UNSIGNED NOT NULL,
|
||||
|
||||
-- REVISIT <AP>: 20090720
|
||||
-- The amount from ledger_entries should be moved here to
|
||||
-- eliminate duplication, and crdr should just be deleted.
|
||||
-- However, it can always be changed later, and I thinks
|
||||
-- those fields will come in handy when generating a
|
||||
-- a ledger report. So, duplication for now.
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- -- ----------------------------------------------------------------------
|
||||
-- -- ----------------------------------------------------------------------
|
||||
-- -- TABLE pmgr_statement_entries
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_statement_entries`;
|
||||
CREATE TABLE `pmgr_statement_entries` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- REVISIT <AP>: 20090804
|
||||
-- I'm not sure about most of these terms.
|
||||
-- Just as long as they're distinct though... I can rename them later
|
||||
`type` ENUM('CHARGE', -- Invoiced Charge to Customer
|
||||
'DISBURSEMENT', -- Disbursement of Receipt Funds
|
||||
'REVERSAL', -- Reversal of a charge
|
||||
'VOUCHER', -- Agreement to pay
|
||||
'PAYMENT', -- Payment of a Voucher
|
||||
'REFUND', -- Payment due to refund
|
||||
'SURPLUS', -- Surplus Receipt Funds
|
||||
'WAIVER', -- Waived Charge
|
||||
-- REVISIT <AP>: 20090730
|
||||
-- VOID is used for handling NSF and perhaps charge reversals.
|
||||
-- It's not clear this is the best way to handle these things.
|
||||
'VOID')
|
||||
NOT NULL,
|
||||
|
||||
`transaction_id` INT(10) UNSIGNED NOT NULL,
|
||||
|
||||
-- Effective date is when the charge/disbursement/transfer actually
|
||||
-- takes effect (since it may not be at the time of the transaction).
|
||||
-- Through date is used if/when a charge covers a certain time period,
|
||||
-- like rent. A security deposit, for example, would not use the
|
||||
-- through date.
|
||||
`effective_date` DATE DEFAULT NULL, -- first day
|
||||
`through_date` DATE DEFAULT NULL, -- last day
|
||||
`due_date` DATE DEFAULT NULL,
|
||||
|
||||
-- Customer ID is redundant, since it is saved as part of the
|
||||
-- transaction. Keeping it here anyway, for simplicity. If it's
|
||||
-- truly redundant, and unnecessary, we can always re
|
||||
`customer_id` INT(10) UNSIGNED NOT NULL,
|
||||
`lease_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
`amount` FLOAT(12,2) NOT NULL,
|
||||
|
||||
-- The account of the entry
|
||||
-- REVISIT <AP>: 20090720
|
||||
-- We don't want to confuse statement entries with ledger entries,
|
||||
-- yet we're including account here. It doesn't feel right, but at
|
||||
-- the same time, it will allow us to show _what_ was charged for
|
||||
-- in the statement. Keeping it for now...
|
||||
`account_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
-- Allow the disbursement to reconcile against the charge
|
||||
`charge_entry_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
@@ -983,15 +1126,41 @@ CREATE TABLE `pmgr_ledger_entries` (
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_reconciliations
|
||||
-- TABLE pmgr_tender_types
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_reconciliations`;
|
||||
CREATE TABLE `pmgr_reconciliations` (
|
||||
DROP TABLE IF EXISTS `pmgr_tender_types`;
|
||||
CREATE TABLE `pmgr_tender_types` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
`debit_ledger_entry_id` INT(10) UNSIGNED NOT NULL,
|
||||
`credit_ledger_entry_id` INT(10) UNSIGNED NOT NULL,
|
||||
`amount` FLOAT(12,2) NOT NULL,
|
||||
-- name may (or may not) be used to clarify in reports
|
||||
-- for example, 'Check #1234' as the legal tender name.
|
||||
`name` VARCHAR(80) NOT NULL,
|
||||
|
||||
-- Does this form of legal tender actually change hands?
|
||||
-- If so, then it's tillable. Examples include cash,
|
||||
-- checks, and money orders. Things that are not tillable
|
||||
-- include credit cards, debit cards, and ACH transfers.
|
||||
`tillable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
|
||||
|
||||
-- Names of the 4 data fields (or NULL if not used)
|
||||
-- Not the most robust of solutions, especially since
|
||||
-- it requires (or strongly implicates) that all fields
|
||||
-- be of the same type (ugh). A more complete solution
|
||||
-- would be for each type to have its own table of data
|
||||
-- and to have that table specified here. However, this
|
||||
-- is MUCH simpler, and works for now.
|
||||
`data1_name` VARCHAR(80) DEFAULT NULL,
|
||||
`data2_name` VARCHAR(80) DEFAULT NULL,
|
||||
`data3_name` VARCHAR(80) DEFAULT NULL,
|
||||
`data4_name` VARCHAR(80) DEFAULT NULL,
|
||||
|
||||
-- When we accept legal tender of this form, where does
|
||||
-- it go? Each type of legal tender can specify an
|
||||
-- account, either distinct or non-distinct from others
|
||||
`account_id` INT(10) UNSIGNED NOT NULL,
|
||||
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
@@ -999,20 +1168,50 @@ CREATE TABLE `pmgr_reconciliations` (
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_monetary_sources
|
||||
-- TABLE pmgr_tenders
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_monetary_sources`;
|
||||
CREATE TABLE `pmgr_monetary_sources` (
|
||||
DROP TABLE IF EXISTS `pmgr_tenders`;
|
||||
CREATE TABLE `pmgr_tenders` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- name may (or may not) be used to clarify in reports
|
||||
-- for example, 'Check #1234' as the legal tender name.
|
||||
`name` VARCHAR(80) DEFAULT NULL,
|
||||
monetary_type_id INT(10) UNSIGNED NOT NULL,
|
||||
|
||||
-- The type of this legal tender
|
||||
`tender_type_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
-- The customer that provided this tender
|
||||
-- REVISIT <AP>: 20090728 Do we allow anonymous payments?
|
||||
`customer_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,
|
||||
|
||||
|
||||
-- The ledger entry this legal tender applies to
|
||||
`ledger_entry_id` INT(10) UNSIGNED NOT NULL,
|
||||
-- The ledger entry if this tender is marked NSF
|
||||
`nsf_ledger_entry_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
-- The deposit transaction that included these monies
|
||||
`deposit_transaction_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
-- The NSF transaction coming back from the bank.
|
||||
`nsf_transaction_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
@@ -1022,33 +1221,30 @@ CREATE TABLE `pmgr_monetary_sources` (
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_monetary_types
|
||||
-- TABLE pmgr_deposits
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_monetary_types`;
|
||||
CREATE TABLE `pmgr_monetary_types` (
|
||||
DROP TABLE IF EXISTS `pmgr_deposits`;
|
||||
CREATE TABLE `pmgr_deposits` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
`name` VARCHAR(80) NOT NULL,
|
||||
`transaction_id` INT(10) UNSIGNED NOT NULL,
|
||||
|
||||
-- The account/ledger of the entry
|
||||
`account_id` INT(10) UNSIGNED NOT NULL,
|
||||
`ledger_id` INT(10) UNSIGNED NOT NULL,
|
||||
|
||||
-- For convenience. Should always be DEBIT (unless we
|
||||
-- decide to credit NSF instead of a negative debit).
|
||||
`crdr` ENUM('DEBIT')
|
||||
NOT NULL DEFAULT 'DEBIT',
|
||||
|
||||
`amount` FLOAT(12,2) 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;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
68
requirements.txt
Normal file
68
requirements.txt
Normal file
@@ -0,0 +1,68 @@
|
||||
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
|
||||
? - 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
|
||||
? - 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
|
||||
X - Flag unit as overlocked
|
||||
X - Flag unit as evicting
|
||||
X - Flag unit as normal status
|
||||
X - Flag unit as dirty
|
||||
- Enter notes when communicating with Customer
|
||||
X - Accept pre-payments
|
||||
X - Record Customer Move-Out from Unit
|
||||
X - Record utilization of Security Deposit
|
||||
X - Record issuing of a refund
|
||||
- Record Deposit into Petty Cash
|
||||
- Record Payment from Petty Cash to expenses
|
||||
X - Record Petty Cash to refund.
|
||||
X - Write Off Bad Debt
|
||||
X - Perform a Deposit
|
||||
X - Close the Books (nightly / weekly, etc)
|
||||
X - Determine Rents Collected for a given period.
|
||||
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,14 @@ 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' => 'Un-Nuke', 'url' => '#', 'htmlAttributes' =>
|
||||
array('onclick' => '$(".pr-section").show(); return false;')),
|
||||
array('name' => 'Contacts', 'url' => array('controller' => 'contacts', 'action' => 'index')),
|
||||
array('name' => 'Ledgers', 'url' => array('controller' => 'ledgers', 'action' => 'index')),
|
||||
array('name' => 'New Ledgers', 'url' => array('controller' => 'accounts', 'action' => 'newledger')),
|
||||
array('name' => 'RESET DATA', 'url' => array('controller' => 'accounts', 'action' => 'reset_data')),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,103 +60,109 @@ class AppController extends Controller {
|
||||
$this->set('sidemenu', $this->sideMenuLinks());
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* helper: jqGridView
|
||||
* - called by function to create an index listing
|
||||
*/
|
||||
|
||||
function jqGridView($title, $action = 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']);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: jqGridData
|
||||
* - Fetches the actual data requested by jqGrid as XML
|
||||
* helper: gridView
|
||||
* - called by derived controllers to create an index listing
|
||||
*/
|
||||
|
||||
function jqGridData() {
|
||||
function gridView($title, $action = null, $element = null) {
|
||||
$this->set('title', $title);
|
||||
// The resulting page will contain a grid, which will
|
||||
// use ajax to obtain the actual data for this action
|
||||
$this->set('action', $action ? $action : $this->params['action']);
|
||||
$this->render('/elements/' . ($element ? $element : $this->params['controller']));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: gridData
|
||||
* - Fetches the actual data requested by grid as XML
|
||||
*/
|
||||
|
||||
function gridData() {
|
||||
// Grab a copy of the parameters that control this request
|
||||
$params = array();
|
||||
if (isset($this->params['url']) && is_array($this->params['url']))
|
||||
$params = $this->params['url'];
|
||||
|
||||
// Do any preliminary setup necessary
|
||||
$this->jqGridDataSetup($params);
|
||||
$this->gridDataSetup($params);
|
||||
|
||||
// Get the top level model for this grid
|
||||
$model = $this->jqGridDataModel($params);
|
||||
|
||||
// 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);
|
||||
$model = $this->gridDataModel($params);
|
||||
|
||||
// Get the number of records prior to pagination
|
||||
$count = $this->jqGridDataRecordCount($params, $model, $query);
|
||||
$count = $this->gridDataCount($params, $model);
|
||||
|
||||
// Start the query over, this time getting the actual set
|
||||
// of tables needed, not just those needed for counting.
|
||||
$query = array_intersect_key($this->jqGridDataTables($params, $model),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
$query['conditions'] = $this->jqGridDataConditions($params, $model);
|
||||
// Determine pagination configuration (and save to $params)
|
||||
$pagination = $this->gridDataPagination($params, $model, $count);
|
||||
|
||||
// Verify a few parameters and determine our starting row
|
||||
$limit = $params['rows'];
|
||||
$total = ($count < 0) ? 0 : ceil($count/$limit);
|
||||
$page = ($params['page'] <= 1) ? 1 : (($params['page'] > $total) ? $total : $params['page']);
|
||||
$start = $limit*$page - $limit;
|
||||
|
||||
// Grab the actual records taking pagination into account
|
||||
$query['group'] = $this->jqGridDataGroup($params, $model);
|
||||
$query['order'] = $this->jqGridDataOrder($params, $model,
|
||||
isset($params['sidx']) ? $params['sidx'] : null,
|
||||
isset($params['sord']) ? $params['sord'] : null);
|
||||
$query['limit'] = $this->jqGridDataLimit($params, $model, $start, $limit);
|
||||
$query['fields'] = $this->jqGridDataFields($params, $model);
|
||||
$results = $this->jqGridDataRecords($params, $model, $query);
|
||||
// Retreive the appropriate subset of data
|
||||
$records = $this->gridDataRecords($params, $model, $pagination);
|
||||
|
||||
// Post process the records
|
||||
$this->jqGridRecordsPostProcess($params, $model, $results);
|
||||
$this->gridDataPostProcess($params, $model, $records);
|
||||
|
||||
// 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";
|
||||
echo "<rows>\n";
|
||||
$this->jqGridDataOutputSummary($params, $model, $page, $total, $count);
|
||||
$this->jqGridDataOutputRecords($params, $model, $results);
|
||||
echo "</rows>\n";
|
||||
// Output the resulting record set
|
||||
$this->gridDataOutput($params, $model, $records, $pagination);
|
||||
|
||||
// Call out to finalize everything
|
||||
$this->jqGridDataFinalize($params);
|
||||
$this->gridDataFinalize($params);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtual: jqGridData* functions
|
||||
* - These set up the context for the jqGrid data, and will
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtual: gridData* functions
|
||||
* - These set up the context for the grid data, and will
|
||||
* need to be overridden in the derived class for anything
|
||||
* other than the most basic of grids.
|
||||
*/
|
||||
|
||||
function jqGridDataSetup(&$params) {
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData SETUP / CLEANUP
|
||||
*/
|
||||
|
||||
function gridDataModel(&$params) {
|
||||
return $this->{$this->modelClass};
|
||||
}
|
||||
|
||||
function gridDataSetup(&$params) {
|
||||
// Assume we're debugging.
|
||||
// The actual jqGrid request will set this to false
|
||||
// The actual grid request will set this to false
|
||||
$debug = true;
|
||||
|
||||
if (isset($this->passedArgs['debug']))
|
||||
@@ -159,7 +170,10 @@ class AppController extends Controller {
|
||||
|
||||
$params['debug'] = $debug;
|
||||
|
||||
if (!$debug) {
|
||||
if ($debug) {
|
||||
ob_start();
|
||||
}
|
||||
else {
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
@@ -171,42 +185,359 @@ class AppController extends Controller {
|
||||
'rows' => 20),
|
||||
$params);
|
||||
|
||||
// Unserialize our complex structure of fields
|
||||
// Unserialize our complex structure of post data.
|
||||
// This SHOULD always be set, except when debugging
|
||||
if (isset($params['fields']))
|
||||
$params['fields'] = unserialize($params['fields']);
|
||||
else
|
||||
$params['fields'] = array($this->{$this->modelClass}->alias
|
||||
.'.'.
|
||||
$this->{$this->modelClass}->primarKey);
|
||||
|
||||
// Unserialize the list of ids, if present.
|
||||
if (isset($params['custom']))
|
||||
$params['custom'] = unserialize($params['custom']);
|
||||
if (isset($params['post']))
|
||||
$params['post'] = unserialize($params['post']);
|
||||
else
|
||||
$params['custom'] = null;
|
||||
$params['post'] = array();
|
||||
|
||||
// Unserialize the list of ids, if present.
|
||||
if (isset($params['idlist']))
|
||||
$params['idlist'] = unserialize($params['idlist']);
|
||||
// Unserialize our complex structure of dynamic post data
|
||||
if (isset($params['dynamic_post']))
|
||||
$params['dynamic_post'] = unserialize($params['dynamic_post']);
|
||||
else
|
||||
$params['dynamic_post'] = null;
|
||||
|
||||
// Unserialize our complex structure of dynamic post data
|
||||
if (isset($params['dynamic_post_replace']))
|
||||
$params['dynamic_post_replace'] = unserialize($params['dynamic_post_replace']);
|
||||
else
|
||||
$params['dynamic_post_replace'] = null;
|
||||
|
||||
// Merge the static and dynamic post data
|
||||
if (!empty($params['dynamic_post']))
|
||||
$params['post'] = array_merge_recursive($params['post'], $params['dynamic_post']);
|
||||
if (!empty($params['dynamic_post_replace']))
|
||||
$params['post'] = array_merge($params['post'], $params['dynamic_post_replace']);
|
||||
|
||||
// This SHOULD always be set, except when debugging
|
||||
if (!isset($params['post']['fields']))
|
||||
$params['post']['fields'] = array($this->{$this->modelClass}->alias
|
||||
.'.'.
|
||||
$this->{$this->modelClass}->primaryKey);
|
||||
|
||||
// Make sure the action parameter at least exists, and
|
||||
// promote it to the top level (since it drives the operation).
|
||||
if (isset($params['post']['action']))
|
||||
$params['action'] = $params['post']['action'];
|
||||
else
|
||||
$params['action'] = null;
|
||||
}
|
||||
|
||||
function jqGridDataModel(&$params) {
|
||||
return $this->{$this->modelClass};
|
||||
function gridDataFinalize(&$params) {
|
||||
if ($params['debug']) {
|
||||
$xml = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$xml = preg_replace("/&/", "&", $xml);
|
||||
$xml = preg_replace("/</", "<", $xml);
|
||||
$xml = preg_replace("/>/", ">", $xml);
|
||||
echo ("\n<PRE>\n$xml\n</PRE>\n");
|
||||
}
|
||||
}
|
||||
|
||||
function jqGridDataCountTables(&$params, &$model) {
|
||||
// If not overridden, we use the same tables to
|
||||
// perform our count as we do to for the actual query
|
||||
return $this->jqGridDataTables($params, $model);
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData COUNTING
|
||||
*/
|
||||
|
||||
function gridDataCount(&$params, &$model) {
|
||||
// Establish the tables and conditions for counting
|
||||
$query = array_intersect_key($this->gridDataCountTableSet($params, $model),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
|
||||
// Conditions for the count
|
||||
$query['conditions'] = $this->gridDataCountConditionSet($params, $model);
|
||||
|
||||
// Grouping (which would not be typical)
|
||||
$query['group'] = $this->gridDataCountGroup($params, $model);
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
$params['count_query'] = $query;
|
||||
|
||||
// Get the number of records prior to pagination
|
||||
return $this->gridDataCountExecute($params, $model, $query);
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
return array('contain' => false);
|
||||
function gridDataCountExecute(&$params, &$model, $query) {
|
||||
return $model->find('count', $query);
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
// Same tables for counting as for retreiving
|
||||
return $this->gridDataTables($params, $model);
|
||||
}
|
||||
|
||||
function gridDataCountTableSet(&$params, &$model) {
|
||||
// Preliminary set of tables
|
||||
$query = array_intersect_key($this->gridDataCountTables($params, $model),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
|
||||
// Perform filtering based on user request: $params['post']['filter']
|
||||
return array_intersect_key($this->gridDataFilterTables($params, $model, $query),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
}
|
||||
|
||||
function gridDataCountConditions(&$params, &$model) {
|
||||
// Same conditions for counting as for retreiving
|
||||
return $this->gridDataConditions($params, $model);
|
||||
}
|
||||
|
||||
function gridDataCountConditionSet(&$params, &$model) {
|
||||
// Conditions for the count
|
||||
$conditions = $this->gridDataCountConditions($params, $model);
|
||||
|
||||
// Perform filtering based on user request: $params['post']['filter']
|
||||
return $this->gridDataFilterConditions($params, $model, $conditions);
|
||||
}
|
||||
|
||||
function gridDataCountGroup(&$params, &$model) {
|
||||
// Grouping will screw up the count, since it
|
||||
// causes the results to be split into chunks
|
||||
// based on the GROUP BY clause. We can't have
|
||||
// more than one row of data in the count query,
|
||||
// just a _single_ row with the actual count.
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData FILTERING
|
||||
*/
|
||||
|
||||
function gridDataFilterTables(&$params, &$model, $query) {
|
||||
if (isset($query['contain']))
|
||||
$link = 'contain';
|
||||
else
|
||||
$link = 'link';
|
||||
|
||||
if (empty($params['post']['filter']))
|
||||
return $query;
|
||||
|
||||
foreach ($params['post']['filter'] AS $filter => $filter_value) {
|
||||
$split = $this->gridDataFilterSplit($params, $model, $filter, $filter_value);
|
||||
|
||||
/* pr(array('AppController::gridDataFilterTable' => */
|
||||
/* array('checkpoint' => "Filter split") */
|
||||
/* + compact('split'))); */
|
||||
|
||||
$table = $this->gridDataFilterTablesTable($params, $model, $split['table']);
|
||||
if (!$table)
|
||||
continue;
|
||||
|
||||
$config = $this->gridDataFilterTablesConfig($params, $model, $split['table']);
|
||||
|
||||
/* pr(array('AppController::gridDataFilterTable' => */
|
||||
/* array('checkpoint' => "Add filter config to query") */
|
||||
/* + array('query[link]' => $query[$link]) */
|
||||
/* + compact('config'))); */
|
||||
|
||||
// If the table is already part of the query, append to it
|
||||
if ($table == $model->alias) {
|
||||
$query[$link] =
|
||||
array_merge_recursive(array_diff_key($config, array('fields'=>1)),
|
||||
$query[$link]);
|
||||
}
|
||||
elseif (isset($query[$link][$table])) {
|
||||
$query[$link][$table] =
|
||||
array_merge_recursive($config, $query[$link][$table]);
|
||||
}
|
||||
elseif (in_array($table, $query[$link])) {
|
||||
// REVISIT <AP>: 20090727
|
||||
// This presents a dilema. $config may specify fields
|
||||
// as array(), whereas the user has already indicated
|
||||
// they desire ALL fields (by the fact that table is
|
||||
// a member of the query['link'] array without anything
|
||||
// specified). However, $config _could_ specify a
|
||||
// non-standard field in the array, such as using SUM()
|
||||
// or other equation. In that case, we'd have to add
|
||||
// in all the fields of table to the array. That
|
||||
// wouldn't be very hard, but I'll ignore it at the
|
||||
// moment and wait until it's needed.
|
||||
$key = array_search($table, $query[$link]);
|
||||
$query[$link][$table] = array('fields' => null) + $config;
|
||||
unset($query[$link][$key]);
|
||||
}
|
||||
else {
|
||||
// Table is NOT already part of the query... set it now
|
||||
$query[$link][$table] = $config;
|
||||
}
|
||||
|
||||
/* pr(array('post-filter-table-config' => */
|
||||
/* array('query[link]' => $query[$link]))); */
|
||||
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
function gridDataFilterTablesTable(&$params, &$model, $table) {
|
||||
// By default, don't add anything if the filter
|
||||
// condition occurs on the actual model table
|
||||
if ($table == $model->alias)
|
||||
return null;
|
||||
return $this->gridDataFilterTableName($params, $model, $table);
|
||||
}
|
||||
|
||||
function gridDataFilterTablesConfig(&$params, &$model, $table) {
|
||||
return array('fields' => array());
|
||||
}
|
||||
|
||||
function gridDataFilterConditions(&$params, &$model, $conditions) {
|
||||
if (empty($params['post']['filter']))
|
||||
return $conditions;
|
||||
|
||||
foreach ($params['post']['filter'] AS $filter => $filter_value) {
|
||||
$split = $this->gridDataFilterSplit($params, $model, $filter, $filter_value);
|
||||
|
||||
$table = $this->gridDataFilterConditionsTable($params, $model, $split['table']);
|
||||
$key = $this->gridDataFilterConditionsKey($params, $model, $split['table'], $split['field']);
|
||||
if (!$key)
|
||||
continue;
|
||||
|
||||
$conditions[]
|
||||
= $this->gridDataFilterConditionsStatement($params, $model, $table, $key,
|
||||
array_intersect_key
|
||||
($split,
|
||||
array('value'=>1,
|
||||
'value_present'=>1)));
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function gridDataFilterConditionsTable(&$params, &$model, $table) {
|
||||
return $this->gridDataFilterTableName($params, $model, $table);
|
||||
}
|
||||
|
||||
function gridDataFilterConditionsKey(&$params, &$model, $table, $id) {
|
||||
// REVISIT <AP>: 20090722
|
||||
// When $id is null, we could instantiate the table,
|
||||
// and use the _actual_ primary key. However, I don't
|
||||
// expect that functionality to be used, and will just
|
||||
// stick with 'id' for now.
|
||||
return $id ? $id : 'id';
|
||||
}
|
||||
|
||||
function gridDataFilterConditionsStatement(&$params, &$model, $table, $key, $value) {
|
||||
$key = (empty($table)?"":"{$table}.") . $key;
|
||||
if (isset($value['value_present']) && $value['value_present'])
|
||||
return array($key => $value['value']);
|
||||
else
|
||||
return array($key);
|
||||
}
|
||||
|
||||
function gridDataFilterSplit(&$params, &$model, $filter, $value) {
|
||||
$value_present = true;
|
||||
if (!is_string($filter)) {
|
||||
// only a filter condition was set, not filter=>value
|
||||
$filter = $value;
|
||||
$value = null;
|
||||
$value_present = false;
|
||||
}
|
||||
|
||||
$table = $field = null;
|
||||
if (preg_match("/^\w+\.\w+(\s*[!<>=]+)?$/", $filter)) {
|
||||
list($table, $field) = explode(".", $filter);
|
||||
}
|
||||
elseif (preg_match('/^[A-Z][A-Za-z]*$/', $filter)) {
|
||||
$table = $filter;
|
||||
$field = null;
|
||||
}
|
||||
elseif (preg_match('/^\w+(\s*[!<>=]+)?$/', $filter)) {
|
||||
$table = $model->alias;
|
||||
$field = $filter;
|
||||
}
|
||||
else {
|
||||
// $filter must be a function or other complicated condition
|
||||
$table = null;
|
||||
$field = $filter;
|
||||
}
|
||||
|
||||
return compact('table', 'field', 'value', 'value_present');
|
||||
}
|
||||
|
||||
function gridDataFilterTableName(&$params, &$model, $table) {
|
||||
return Inflector::camelize($table);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData PAGINATION
|
||||
*/
|
||||
|
||||
function gridDataPagination(&$params, &$model, $record_count) {
|
||||
// Retrieve, or calculate, all parameters necesssary for
|
||||
// pagination. Verify the passed in parameters are valid.
|
||||
|
||||
$limit = $params['rows'] > 0 ? $params['rows'] : 10;
|
||||
$total = ($record_count < 0) ? 0 : ceil($record_count/$limit);
|
||||
$page = ($params['page'] <= 1) ? 1 : (($params['page'] > $total) ? $total : $params['page']);
|
||||
$start = $limit * ($page - 1);
|
||||
|
||||
return compact('record_count', 'limit', 'page', 'start', 'total');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData RETREIVAL
|
||||
*/
|
||||
|
||||
function gridDataRecords(&$params, &$model, $pagination) {
|
||||
// Establish the tables for this query
|
||||
$query = array_intersect_key($this->gridDataTableSet($params, $model),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
|
||||
// Specify the fields for the query
|
||||
$query['fields'] = $this->gridDataFields($params, $model);
|
||||
|
||||
// Conditions of the query
|
||||
$query['conditions'] = $this->gridDataConditionSet($params, $model);
|
||||
|
||||
// Data record grouping
|
||||
$query['group'] = $this->gridDataGroup($params, $model);
|
||||
|
||||
// The subset of data based on pagination
|
||||
$query['limit'] = $this->gridDataLimit($params, $model, $pagination['start'], $pagination['limit']);
|
||||
|
||||
// Ordering based on user request
|
||||
$query['order'] = $this->gridDataOrder($params, $model,
|
||||
isset($params['sidx']) ? $params['sidx'] : null,
|
||||
isset($params['sord']) ? $params['sord'] : null);
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
$params['query'] = $query;
|
||||
|
||||
return $this->gridDataRecordsExecute($params, $model, $query);
|
||||
}
|
||||
|
||||
function gridDataRecordsExecute(&$params, &$model, $query) {
|
||||
return $model->find('all', $query);
|
||||
}
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
return array('link' => array());
|
||||
}
|
||||
|
||||
function gridDataTableSet(&$params, &$model) {
|
||||
// Preliminary set of tables
|
||||
$query = array_intersect_key($this->gridDataTables($params, $model),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
|
||||
// Perform filtering based on user request: $params['post']['filter']
|
||||
$query = array_intersect_key($this->gridDataFilterTables($params, $model, $query),
|
||||
array('link'=>1, 'contain'=>1));
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$searches = array();
|
||||
|
||||
if (isset($params['_search']) && $params['_search'] === 'true') {
|
||||
@@ -248,8 +579,8 @@ class AppController extends Controller {
|
||||
}
|
||||
|
||||
if (isset($params['action']) && $params['action'] === 'idlist') {
|
||||
if (count($params['idlist']))
|
||||
$conditions[] = array($model->alias.'.'.$model->primaryKey => $params['idlist']);
|
||||
if (count($params['post']['idlist']))
|
||||
$conditions[] = array($model->alias.'.'.$model->primaryKey => $params['post']['idlist']);
|
||||
else
|
||||
$conditions[] = '0=1';
|
||||
}
|
||||
@@ -257,48 +588,112 @@ class AppController extends Controller {
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
return null;
|
||||
function gridDataConditionSet(&$params, &$model) {
|
||||
// Conditions for record retrieval
|
||||
$conditions = $this->gridDataConditions($params, $model);
|
||||
|
||||
// Perform filtering based on user request: $params['post']['filter']
|
||||
return $this->gridDataFilterConditions($params, $model, $conditions);
|
||||
}
|
||||
|
||||
function jqGridDataGroup(&$params, &$model) {
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$db = &$model->getDataSource();
|
||||
$fields = $db->fields($model, $model->alias);
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function gridDataGroup(&$params, &$model) {
|
||||
return $model->alias.'.'.$model->primaryKey;
|
||||
}
|
||||
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
return $index ? array($index .' '. $direction) : null;
|
||||
}
|
||||
|
||||
function jqGridDataLimit(&$params, &$model, $start, $limit) {
|
||||
function gridDataLimit(&$params, &$model, $start, $limit) {
|
||||
return $start . ', ' . $limit;
|
||||
}
|
||||
|
||||
function jqGridDataRecordCount(&$params, &$model, $query) {
|
||||
return $model->find('count', $query);
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData POST PROCESSING
|
||||
*/
|
||||
|
||||
function gridDataPostProcess(&$params, &$model, &$records) {
|
||||
// Add grid IDs to each record
|
||||
$this->gridDataPostProcessGridIDs($params, $model, $records);
|
||||
|
||||
// Add the calculated fields (if any), to the model fields
|
||||
$this->gridDataPostProcessCalculatedFields($params, $model, $records);
|
||||
|
||||
// Perform any requested subtotaling of fields
|
||||
$this->gridDataPostProcessSubtotal($params, $model, $records);
|
||||
|
||||
// Add in any needed hyperlinks
|
||||
$this->gridDataPostProcessLinks($params, $model, $records, array());
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
//$params['records'] = $records;
|
||||
}
|
||||
|
||||
function jqGridDataRecords(&$params, &$model, $query) {
|
||||
return $model->find('all', $query);
|
||||
}
|
||||
|
||||
function jqGridRecordsPostProcess(&$params, &$model, &$records) {
|
||||
function gridDataPostProcessGridIDs(&$params, &$model, &$records) {
|
||||
$model_alias = $model->alias;
|
||||
$id = $model->primaryKey;
|
||||
|
||||
foreach ($records AS &$record) {
|
||||
$record['jqGrid_id'] = $record[$model_alias][$id];
|
||||
$record['grid_id'] = $record[$model_alias][$id];
|
||||
}
|
||||
}
|
||||
|
||||
function gridDataPostProcessCalculatedFields(&$params, &$model, &$records) {
|
||||
$model_alias = $model->alias;
|
||||
|
||||
foreach ($records AS &$record) {
|
||||
// Add the calculated fields (if any), to the model fields
|
||||
if (isset($record[0])) {
|
||||
$record[$model_alias] += $record[0];
|
||||
unset($record[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG PURPOSES ONLY!
|
||||
//$params['records'] = $records;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
function gridDataPostProcessSubtotal(&$params, &$model, &$records) {
|
||||
$model_alias = $model->alias;
|
||||
|
||||
// REVISIT <AP>: 20090722
|
||||
// Horrible solution to something that should be done
|
||||
// in SQL. But, it works for now, so what the heck...
|
||||
|
||||
$subtotals = array();
|
||||
foreach ($params['post']['fields'] AS $field) {
|
||||
if (preg_match('/subtotal-(.*)$/', $field, $matches))
|
||||
$subtotals[] = array('field' => $matches[1],
|
||||
'name' => $field,
|
||||
'amount' => 0);
|
||||
}
|
||||
|
||||
foreach ($records AS &$record) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
// Don't create any links if ordered not to.
|
||||
if (isset($params['post']['nolinks']))
|
||||
return;
|
||||
|
||||
foreach ($links AS $table => $fields) {
|
||||
$special = array('controller', 'id');
|
||||
$controller = Inflector::pluralize(Inflector::underscore($table));
|
||||
@@ -327,50 +722,80 @@ class AppController extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
function jqGridDataOutputHeader(&$params, &$model) {
|
||||
if ($params['debug']) {
|
||||
ob_start();
|
||||
}
|
||||
else {
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
* gridData OUTPUT
|
||||
*/
|
||||
|
||||
function gridDataOutput(&$params, &$model, &$records, $pagination) {
|
||||
$this->gridDataOutputHeader($params, $model);
|
||||
$this->gridDataOutputXMLHeader($params, $model);
|
||||
$this->gridDataOutputRootNodeBegin($params, $model);
|
||||
$this->gridDataOutputSummary($params, $model, $pagination);
|
||||
$this->gridDataOutputRecords($params, $model, $records);
|
||||
$this->gridDataOutputRootNodeEnd($params, $model);
|
||||
}
|
||||
|
||||
function gridDataOutputHeader(&$params, &$model) {
|
||||
if (!$params['debug']) {
|
||||
header("Content-type: text/xml;charset=utf-8");
|
||||
}
|
||||
}
|
||||
|
||||
function jqGridDataOutputSummary(&$params, &$model, $page, $total, $records) {
|
||||
echo " <params><![CDATA[\n" . print_r($params, true) . "\n]]></params>\n";
|
||||
echo " <page>$page</page>\n";
|
||||
echo " <total>$total</total>\n";
|
||||
echo " <records>$records</records>\n";
|
||||
function gridDataOutputXMLHeader(&$params, &$model) {
|
||||
echo "<?xml version='1.0' encoding='utf-8'?>\n";
|
||||
}
|
||||
|
||||
function jqGridDataOutputRecords(&$params, &$model, &$records) {
|
||||
$id_field = 'jqGrid_id';
|
||||
foreach ($records AS $record) {
|
||||
$this->jqGridDataOutputRecord($params, $model, $record,
|
||||
$record[$id_field], $params['fields']);
|
||||
function gridDataOutputRootNodeBegin(&$params, &$model) {
|
||||
echo "<rows>\n";
|
||||
}
|
||||
|
||||
function gridDataOutputRootNodeEnd(&$params, &$model) {
|
||||
echo "</rows>\n";
|
||||
}
|
||||
|
||||
function gridDataOutputSummary(&$params, &$model, $pagination) {
|
||||
echo " <params><![CDATA[\n" . print_r($params, true) . "\n]]></params>\n";
|
||||
echo " <page>{$pagination['page']}</page>\n";
|
||||
echo " <total>{$pagination['total']}</total>\n";
|
||||
echo " <records>{$pagination['record_count']}</records>\n";
|
||||
|
||||
if (isset($params['userdata'])) {
|
||||
foreach ($params['userdata'] AS $field => $value)
|
||||
echo ' <userdata name="'.$field.'">' . "{$value}</userdata>\n";
|
||||
}
|
||||
}
|
||||
|
||||
function jqGridDataOutputRecord(&$params, &$model, &$record, $id, $fields) {
|
||||
function gridDataOutputRecords(&$params, &$model, &$records) {
|
||||
$id_field = 'grid_id';
|
||||
foreach ($records AS $record) {
|
||||
$this->gridDataOutputRecord($params, $model, $record,
|
||||
$record[$id_field], $params['post']['fields']);
|
||||
}
|
||||
}
|
||||
|
||||
function gridDataOutputRecord(&$params, &$model, &$record, $id, $fields) {
|
||||
echo " <row id='$id'>\n";
|
||||
foreach ($fields AS $field) {
|
||||
$this->jqGridDataOutputRecordField($params, $model, $record, $field);
|
||||
$this->gridDataOutputRecordField($params, $model, $record, $field);
|
||||
}
|
||||
echo " </row>\n";
|
||||
}
|
||||
|
||||
function jqGridDataOutputRecordField(&$params, &$model, &$record, $field) {
|
||||
function gridDataOutputRecordField(&$params, &$model, &$record, $field) {
|
||||
if (preg_match("/\./", $field)) {
|
||||
list($tbl, $col) = explode(".", $field);
|
||||
//pr(compact('record', 'field', 'tbl', 'col'));
|
||||
$data = $record[$tbl][$col];
|
||||
}
|
||||
else {
|
||||
$data = $record[$model->alias][$field];
|
||||
}
|
||||
$this->jqGridDataOutputRecordCell($params, $model, $record, $field, $data);
|
||||
$this->gridDataOutputRecordCell($params, $model, $record, $field, $data);
|
||||
}
|
||||
|
||||
function jqGridDataOutputRecordCell(&$params, &$model, &$record, $field, $data) {
|
||||
function gridDataOutputRecordCell(&$params, &$model, &$record, $field, $data) {
|
||||
// be sure to put text data in CDATA
|
||||
if (preg_match("/^\d*$/", $data))
|
||||
echo " <cell>$data</cell>\n";
|
||||
@@ -378,17 +803,5 @@ class AppController extends Controller {
|
||||
echo " <cell><![CDATA[$data]]></cell>\n";
|
||||
}
|
||||
|
||||
function jqGridDataFinalize(&$params) {
|
||||
if ($params['debug']) {
|
||||
$xml = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$xml = preg_replace("/&/", "&", $xml);
|
||||
$xml = preg_replace("/</", "<", $xml);
|
||||
$xml = preg_replace("/>/", ">", $xml);
|
||||
|
||||
echo ("\n<PRE>\n$xml\n</PRE>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -39,6 +39,250 @@
|
||||
class AppModel extends Model {
|
||||
|
||||
var $actsAs = array('Containable', 'Linkable');
|
||||
var $useNullForEmpty = true;
|
||||
var $formatDateFields = true;
|
||||
|
||||
// Default Log Level, if not specified at the function level
|
||||
var $default_log_level = 5;
|
||||
|
||||
// Class specific log levels
|
||||
var $class_log_level = array('Model' => 5);
|
||||
|
||||
// Function specific log levels
|
||||
var $function_log_level = array();
|
||||
|
||||
// Force the module to log at LEAST at this level
|
||||
var $min_log_level;
|
||||
|
||||
// Force logging of nothing higher than this level
|
||||
var $max_log_level;
|
||||
|
||||
|
||||
// REVISIT <AP>: 20090730
|
||||
// Why is this constructor crashing?
|
||||
// Clearly it's in some sort of infinite
|
||||
// loop, but it seems the correct way
|
||||
// to have a constructor call the parent...
|
||||
|
||||
/* function __construct() { */
|
||||
/* parent::__construct(); */
|
||||
/* $this->prClassLevel(5, 'Model'); */
|
||||
/* } */
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: pr
|
||||
* - Prints out debug information, if the log level allows
|
||||
*/
|
||||
|
||||
function prClassLevel($level, $class = null) {
|
||||
$trace = debug_backtrace(false);
|
||||
$caller = array_shift($trace);
|
||||
$caller = array_shift($trace);
|
||||
if (empty($class))
|
||||
$class = $caller['class'];
|
||||
$this->class_log_level[$class] = $level;
|
||||
}
|
||||
|
||||
function prFunctionLevel($level, $function = null, $class = null) {
|
||||
$trace = debug_backtrace(false);
|
||||
$caller = array_shift($trace);
|
||||
$caller = array_shift($trace);
|
||||
if (empty($class))
|
||||
$class = $caller['class'];
|
||||
if (empty($function))
|
||||
$function = $caller['function'];
|
||||
$this->function_log_level["{$class}-{$function}"] = $level;
|
||||
}
|
||||
|
||||
function _pr($level, $mixed, $checkpoint = null) {
|
||||
if (Configure::read() <= 0)
|
||||
return;
|
||||
|
||||
$log_level = $this->default_log_level;
|
||||
|
||||
$trace = debug_backtrace(false);
|
||||
|
||||
// Get rid of pr/prEnter/prReturn
|
||||
$caller = array_shift($trace);
|
||||
|
||||
// The next entry shows where pr was called from, but it
|
||||
// shows _what_ was called, which is pr/prEntry/prReturn.
|
||||
$caller = array_shift($trace);
|
||||
$file = $caller['file'];
|
||||
$line = $caller['line'];
|
||||
|
||||
// So, this caller holds the calling function name
|
||||
$caller = $trace[0];
|
||||
$function = $caller['function'];
|
||||
$class = $caller['class'];
|
||||
//$class = $this->name;
|
||||
|
||||
// Use class or function specific log level if available
|
||||
if (isset($this->class_log_level[$class]))
|
||||
$log_level = $this->class_log_level[$class];
|
||||
if (isset($this->function_log_level["{$class}-{$function}"]))
|
||||
$log_level = $this->function_log_level["{$class}-{$function}"];
|
||||
|
||||
// Establish log level minimums
|
||||
$min_log_level = $this->min_log_level;
|
||||
if (is_array($this->min_log_level)) {
|
||||
$min_show_level = $min_log_level['show'];
|
||||
$min_log_level = $min_log_level['log'];
|
||||
}
|
||||
|
||||
// Establish log level maximums
|
||||
$max_log_level = $this->max_log_level;
|
||||
if (is_array($this->max_log_level)) {
|
||||
$max_show_level = $max_log_level['show'];
|
||||
$max_log_level = $max_log_level['log'];
|
||||
}
|
||||
|
||||
// Determine the applicable log and show levels
|
||||
if (is_array($log_level)) {
|
||||
$show_level = $log_level['show'];
|
||||
$log_level = $log_level['log'];
|
||||
}
|
||||
|
||||
// Adjust log level up/down to min/max
|
||||
if (isset($min_log_level))
|
||||
$log_level = max($log_level, $min_log_level);
|
||||
if (isset($max_log_level))
|
||||
$log_level = min($log_level, $max_log_level);
|
||||
|
||||
// Adjust show level up/down to min/max
|
||||
if (isset($min_show_level))
|
||||
$show_level = max($show_level, $min_show_level);
|
||||
if (isset($max_show_level))
|
||||
$show_level = min($show_level, $max_show_level);
|
||||
|
||||
// If the level is insufficient, bail out
|
||||
if ($level > $log_level)
|
||||
return;
|
||||
|
||||
if (!empty($checkpoint)) {
|
||||
$chk = array("checkpoint" => $checkpoint);
|
||||
if (is_array($mixed))
|
||||
$mixed = $chk + $mixed;
|
||||
else
|
||||
$mixed = $chk + array($mixed);
|
||||
}
|
||||
|
||||
static $pr_unique_number = 0;
|
||||
$pr_id = 'pr-section-class-' . $class . '-print-' . (++$pr_unique_number);
|
||||
$pr_trace_id = $pr_id . '-trace';
|
||||
$pr_output_id = $pr_id . '-output';
|
||||
|
||||
$pr_entire_base_class = "pr-section";
|
||||
$pr_entire_class_class = $pr_entire_base_class . '-class-' . $class;
|
||||
$pr_entire_function_class = $pr_entire_class_class . '-function-' . $function;
|
||||
$pr_entire_class = "$pr_entire_base_class $pr_entire_class_class $pr_entire_function_class";
|
||||
$pr_header_class = "pr-caller";
|
||||
$pr_trace_class = "pr-trace";
|
||||
$pr_output_base_class = 'pr-output';
|
||||
$pr_output_class_class = $pr_output_base_class . '-class-' . $class;
|
||||
$pr_output_function_class = $pr_output_class_class . '-function-' . $function;
|
||||
$pr_output_class = "$pr_output_base_class $pr_output_class_class $pr_output_function_class";
|
||||
|
||||
echo '<DIV class="'.$pr_entire_class.'" id="'.$pr_id.'">'."\n";
|
||||
echo '<DIV class="'.$pr_header_class.'">'."\n";
|
||||
echo '<DIV class="'.$pr_trace_class.'" id="'.$pr_trace_id.'" style="display:none;">'."\n";
|
||||
echo '<HR />' . "\n";
|
||||
|
||||
// Flip trace around so the sequence flows from top to bottom
|
||||
// Then print out the entire stack trace (in hidden div)
|
||||
$trace = array_reverse($trace);
|
||||
for ($i = 0; $i < count($trace); ++$i) {
|
||||
$bline = $trace[$i]['line'];
|
||||
$bfile = $trace[$i]['file'];
|
||||
$bfile = str_replace(ROOT.DS, '', $bfile);
|
||||
$bfile = str_replace(CAKE_CORE_INCLUDE_PATH.DS, '', $bfile);
|
||||
|
||||
if ($i > 0) {
|
||||
$bfunc = $trace[$i-1]['function'];
|
||||
$bclas = $trace[$i-1]['class'];
|
||||
} else {
|
||||
$bfunc = null;
|
||||
$bclas = null;
|
||||
}
|
||||
|
||||
echo("$bfile:$bline (" . ($bclas ? "$bclas::$bfunc" : "entry point") . ")<BR>\n");
|
||||
//echo(($bclas ? "$bclas::$bfunc" : "entry point") . "; $bfile : $bline<BR>\n");
|
||||
}
|
||||
echo '</DIV>' . "\n"; // End pr_trace_class
|
||||
$file = str_replace(ROOT.DS, '', $file);
|
||||
$file = str_replace(CAKE_CORE_INCLUDE_PATH.DS, '', $file);
|
||||
|
||||
echo "<strong>$file:$line ($class::$function)</strong>" . ";\n";
|
||||
/* $log_show_level = isset($show_level) ? $show_level : '?'; */
|
||||
/* echo ' L' . $level . "({$log_level}/{$log_show_level})" . ";\n"; */
|
||||
echo ' L' . $level . ";\n";
|
||||
echo ' <A HREF="#" onclick="$' . "('#{$pr_trace_id}').slideToggle(); return false;" . '">stack</A>'.";\n";
|
||||
|
||||
echo " this ";
|
||||
echo '<A HREF="#" onclick="$' . "('#{$pr_output_id}').slideToggle(); return false;" . '">t</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('#{$pr_id}').hide(); return false;" . '">n</A>'.";\n";
|
||||
|
||||
echo " $class ";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_class_class}').slideDown(); return false;" . '">s</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_class_class}').slideUp(); return false;" . '">h</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_entire_class_class}').hide(); return false;" . '">n</A>'.";\n";
|
||||
|
||||
echo " $function ";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_function_class}').slideDown(); return false;" . '">s</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_function_class}').slideUp(); return false;" . '">h</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_entire_function_class}').hide(); return false;" . '">n</A>'.";\n";
|
||||
|
||||
echo " all ";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_base_class}').show(); return false;" . '">s</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_output_base_class}').hide(); return false;" . '">h</A>'."/";
|
||||
echo '<A HREF="#" onclick="$' . "('.{$pr_entire_base_class}').hide(); return false;" . '">n</A>'."\n";
|
||||
|
||||
echo '</DIV>' . "\n"; // End pr_header_class
|
||||
|
||||
if (isset($show_level) && $level > $show_level)
|
||||
$display = 'none';
|
||||
else
|
||||
$display = 'block';
|
||||
|
||||
echo '<DIV class="'.$pr_output_class.'" id="'.$pr_output_id.'" style="display:'.$display.';">'."\n";
|
||||
pr($mixed, false, false);
|
||||
echo '</DIV>' . "\n"; // End pr_output_class
|
||||
echo '</DIV>' . "\n"; // End pr_entire_class
|
||||
}
|
||||
|
||||
function pr($level, $mixed, $checkpoint = null) {
|
||||
$this->_pr($level, $mixed, $checkpoint);
|
||||
}
|
||||
|
||||
function prEnter($args, $level = 15) {
|
||||
$this->_pr($level, $args, 'Function Entry');
|
||||
}
|
||||
|
||||
function prReturn($retval, $level = 16) {
|
||||
$this->_pr($level, $retval, 'Function Return');
|
||||
return $retval;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: queryInit
|
||||
* - Initializes the query fields
|
||||
*/
|
||||
function prDump($all = false) {
|
||||
$vars = get_object_vars($this);
|
||||
foreach (array_keys($vars) AS $name) {
|
||||
if (preg_match("/^[A-Z]/", $name))
|
||||
unset($vars[$name]);
|
||||
if (preg_match("/^_/", $name) && !$all)
|
||||
unset($vars[$name]);
|
||||
}
|
||||
pr($vars);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Enum Values
|
||||
@@ -47,13 +291,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,11 +318,56 @@ 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: queryInit
|
||||
* - Initializes the query fields
|
||||
*/
|
||||
function queryInit(&$query, $link = true) {
|
||||
if (!isset($query))
|
||||
$query = array();
|
||||
if (!isset($query['conditions']))
|
||||
$query['conditions'] = array();
|
||||
if (!isset($query['group']))
|
||||
$query['group'] = null;
|
||||
if (!isset($query['fields']))
|
||||
$query['fields'] = null;
|
||||
if ($link && !isset($query['link']))
|
||||
$query['link'] = array();
|
||||
if (!$link && !isset($query['contain']))
|
||||
$query['contain'] = array();
|
||||
|
||||
// In case caller expects query to come back
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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 +402,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 +464,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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,39 @@
|
||||
* You can also use this to include or require any files in your application.
|
||||
*
|
||||
*/
|
||||
|
||||
function INTERNAL_ERROR($message) {
|
||||
echo '<DIV class="internal-error" style="color:#000; background:#c22; padding:0.5em 1.5em 0.5em 1.5em;">';
|
||||
echo '<H1 style="margin-bottom:0.2em">INTERNAL ERROR:</H1>';
|
||||
echo '<H2 style="margin-top:0; margin-left:1.5em">' . $message . '</H2>';
|
||||
echo '<H4>This error was not caused by anything that you did wrong.';
|
||||
echo '<BR>It is a problem within the application itself and should be reported to the administrator.</H4>';
|
||||
|
||||
// Print out the entire stack trace
|
||||
echo "<HR>Stack Trace:<OL>";
|
||||
$trace = debug_backtrace(false);
|
||||
for ($i = 0; $i < count($trace); ++$i) {
|
||||
$bline = $trace[$i]['line'];
|
||||
$bfile = $trace[$i]['file'];
|
||||
$bfile = str_replace(ROOT.DS, '', $bfile);
|
||||
$bfile = str_replace(CAKE_CORE_INCLUDE_PATH.DS, '', $bfile);
|
||||
|
||||
if ($i < count($trace)-1) {
|
||||
$bfunc = $trace[$i+1]['function'];
|
||||
$bclas = $trace[$i+1]['class'];
|
||||
} else {
|
||||
$bfunc = null;
|
||||
$bclas = null;
|
||||
}
|
||||
|
||||
echo("<LI>$bfile:$bline (" . ($bclas ? "$bclas::$bfunc" : "entry point") . ")</LI>\n");
|
||||
}
|
||||
echo '</OL>';
|
||||
|
||||
echo '</DIV>';
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* The settings below can be used to set additional paths to models, views and controllers.
|
||||
* This is related to Ticket #470 (https://trac.cakephp.org/ticket/470)
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
*
|
||||
* $uninflectedPlural = array('.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox');
|
||||
*/
|
||||
$uninflectedPlural = array();
|
||||
$uninflectedPlural = array('.*cash');
|
||||
/**
|
||||
* This is a key => value array of plural irregular words.
|
||||
* If key matches then the value is returned.
|
||||
|
||||
@@ -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,9 @@ 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' => 'Deposits', 'header' => true),
|
||||
array('name' => 'Prior Deposits', 'url' => array('controller' => 'transactions', 'action' => 'deposit')),
|
||||
array('name' => 'New Deposit', 'url' => array('controller' => 'tenders', 'action' => 'deposit')),
|
||||
);
|
||||
|
||||
|
||||
@@ -34,77 +37,57 @@ class AccountsController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function asset() { $this->jqGridView('Asset Accounts'); }
|
||||
function liability() { $this->jqGridView('Liability Accounts'); }
|
||||
function equity() { $this->jqGridView('Equity Accounts'); }
|
||||
function income() { $this->jqGridView('Income Accounts'); }
|
||||
function expense() { $this->jqGridView('Expense Accounts'); }
|
||||
function all() { $this->jqGridView('All Accounts', 'all'); }
|
||||
function asset() { $this->gridView('Asset Accounts'); }
|
||||
function liability() { $this->gridView('Liability Accounts'); }
|
||||
function equity() { $this->gridView('Equity Accounts'); }
|
||||
function income() { $this->gridView('Income Accounts'); }
|
||||
function expense() { $this->gridView('Expense Accounts'); }
|
||||
function all() { $this->gridView('All Accounts', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function jqGridDataSetup(&$params) {
|
||||
parent::jqGridDataSetup($params);
|
||||
function gridDataSetup(&$params) {
|
||||
parent::gridDataSetup($params);
|
||||
if (!isset($params['action']))
|
||||
$params['action'] = 'all';
|
||||
}
|
||||
|
||||
function jqGridDataCountTables(&$params, &$model) {
|
||||
return parent::jqGridDataTables($params, $model);
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
// Our count should NOT include anything extra,
|
||||
// so we need the virtual function to prevent
|
||||
// the base class from just calling our
|
||||
// gridDataTables function
|
||||
return parent::gridDataTables($params, $model);
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
function gridDataTables(&$params, &$model) {
|
||||
return array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'CurrentLedger' => array
|
||||
(// Models
|
||||
'LedgerEntry'
|
||||
/* REVISIT <AP> 20090615:
|
||||
* I'll remove this 'conditions' section on a future checkin,
|
||||
* after I've proven out the %{MODEL_ALIAS} feature will be
|
||||
* sticking around.
|
||||
|
||||
=> array
|
||||
('conditions' =>
|
||||
array('OR' =>
|
||||
array('LedgerEntry.debit_ledger_id = CurrentLedger.id',
|
||||
'LedgerEntry.credit_ledger_id = CurrentLedger.id'),
|
||||
),
|
||||
),
|
||||
|
||||
* END REVISIT
|
||||
*/
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
return array
|
||||
('Account.*',
|
||||
'SUM(IF(LedgerEntry.debit_ledger_id = CurrentLedger.id,
|
||||
LedgerEntry.amount, NULL)) AS debits',
|
||||
'SUM(IF(LedgerEntry.credit_ledger_id = CurrentLedger.id,
|
||||
LedgerEntry.amount, NULL)) AS credits',
|
||||
"SUM(IF(Account.type IN ('ASSET', 'EXPENSE'),
|
||||
IF(LedgerEntry.debit_ledger_id = CurrentLedger.id, 1, -1),
|
||||
IF(LedgerEntry.credit_ledger_id = CurrentLedger.id, 1, -1)
|
||||
) * IF(LedgerEntry.amount, LedgerEntry.amount, 0)
|
||||
) AS balance",
|
||||
'COUNT(LedgerEntry.id) AS entries');
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
return array_merge($fields,
|
||||
$this->Account->Ledger->LedgerEntry->debitCreditFields(true));
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
|
||||
if (in_array($params['action'], array('asset', 'liability', 'equity', 'income', 'expense'))) {
|
||||
$conditions[] = array('Account.type' => strtoupper($params['action']));
|
||||
@@ -113,9 +96,62 @@ class AccountsController extends AppController {
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Account'] = array('name');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: newledger
|
||||
* - Close the current account ledger and create a new one,
|
||||
* carrying forward any balance if necessary.
|
||||
*/
|
||||
|
||||
function newledger($id = null) {
|
||||
$result = $this->Account->closeCurrentLedgers($id);
|
||||
|
||||
if ($result['error']) {
|
||||
pr(compact('result'));
|
||||
die("Unable to create new ledger.");
|
||||
$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'));
|
||||
}
|
||||
|
||||
$this->Account->recursive = -1;
|
||||
$account = $this->Account->read(null, $id);
|
||||
$account = $account['Account'];
|
||||
|
||||
$payment_accounts = $this->Account->collectableAccounts();
|
||||
//$payment_accounts[$this->Account->nameToID('Closing')] = 'Closing';
|
||||
//$payment_accounts[$this->Account->nameToID('Equity')] = 'Equity';
|
||||
//$payment_accounts[$id] = 'Reversals';
|
||||
$default_accounts = array_diff_key($this->Account->receiptAccounts(),
|
||||
array($this->Account->concessionAccountID() => 1));
|
||||
$this->set(compact('payment_accounts', 'default_accounts'));
|
||||
|
||||
$title = ($account['name'] . ': Collected Report');
|
||||
$this->set(compact('account', 'title'));
|
||||
}
|
||||
|
||||
|
||||
@@ -138,30 +174,39 @@ class AccountsController extends AppController {
|
||||
array('contain' =>
|
||||
array(// Models
|
||||
'CurrentLedger' =>
|
||||
array('fields' => array('id', 'sequence')),
|
||||
array('fields' => array('id', 'sequence', 'name')),
|
||||
|
||||
'Ledger' =>
|
||||
array('order' => array('Ledger.open_stamp' => 'DESC')),
|
||||
array('CloseTransaction' => array
|
||||
('order' => array('CloseTransaction.stamp' => 'DESC'))),
|
||||
),
|
||||
'conditions' => array(array('Account.id' => $id)),
|
||||
)
|
||||
);
|
||||
|
||||
// Get all ledger entries of the CURRENT ledger
|
||||
$entries = $this->Account->findLedgerEntries($id);
|
||||
$account['CurrentLedger']['LedgerEntry'] = $entries['Entries'];
|
||||
|
||||
// Summarize each ledger
|
||||
foreach($account['Ledger'] AS &$ledger)
|
||||
$ledger = array_merge($ledger,
|
||||
$this->Account->Ledger->stats($ledger['id']));
|
||||
$entries = $this->Account->ledgerEntries($id);
|
||||
//pr(compact('entries'));
|
||||
$account['CurrentLedger']['LedgerEntry'] = $entries;
|
||||
|
||||
// Obtain stats across ALL ledgers for the summary infobox
|
||||
$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'));
|
||||
}
|
||||
|
||||
function tst($id) {
|
||||
//$entries = $this->Account->($id);
|
||||
pr($entries);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,20 +23,20 @@ class ContactsController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function all() { $this->jqGridView('All Contacts', 'all'); }
|
||||
function all() { $this->gridView('All Contacts', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
$order = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
$order = parent::gridDataOrder($params, $model, $index, $direction);
|
||||
if ($index === 'Contact.last_name') {
|
||||
$order[] = 'Contact.first_name ' . $direction;
|
||||
}
|
||||
@@ -46,6 +46,11 @@ class ContactsController extends AppController {
|
||||
return $order;
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Contact'] = array('id');
|
||||
return parent::gridDataPostProcessLinks($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,117 +27,141 @@ 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 past() { $this->jqGridView('Past Tenants'); }
|
||||
function all() { $this->jqGridView('All Tenants', 'all'); }
|
||||
function current() { $this->gridView('Current Tenants', 'current'); }
|
||||
function past() { $this->gridView('Past Tenants'); }
|
||||
function all() { $this->gridView('All Customers'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function jqGridDataSetup(&$params) {
|
||||
parent::jqGridDataSetup($params);
|
||||
if (!isset($params['action']))
|
||||
$params['action'] = 'all';
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
return array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'PrimaryContact',
|
||||
'CurrentLease' => array('fields' => array()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
return array('Customer.*',
|
||||
'COUNT(CurrentLease.id) AS lease_count');
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$link = $this->gridDataCountTables($params, $model);
|
||||
// StatementEntry is needed to determine customer balance
|
||||
$link['link']['StatementEntry'] = array('fields' => array());
|
||||
return $link;
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
return array_merge($fields,
|
||||
$this->Customer->StatementEntry->chargeDisbursementFields(true));
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'current') {
|
||||
$conditions[] = 'CurrentLease.id IS NOT NULL';
|
||||
$conditions[] = array('Customer.current_lease_count >' => 0);
|
||||
}
|
||||
elseif ($params['action'] === 'past') {
|
||||
$conditions[] = 'CurrentLease.id IS NULL';
|
||||
$conditions[] = array('Customer.current_lease_count' => 0);
|
||||
$conditions[] = array('Customer.past_lease_count >' => 0);
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
$order = array();
|
||||
$order[] = parent::gridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
if ($index !== 'PrimaryContact.last_name')
|
||||
$order[] = parent::gridDataOrder($params, $model,
|
||||
'PrimaryContact.last_name', $direction);
|
||||
if ($index !== 'PrimaryContact.first_name')
|
||||
$order[] = parent::gridDataOrder($params, $model,
|
||||
'PrimaryContact.first_name', $direction);
|
||||
if ($index !== 'Customer.id')
|
||||
$order[] = parent::gridDataOrder($params, $model,
|
||||
'Customer.id', $direction);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
function jqGridDataRecordCount(&$params, &$model, $query) {
|
||||
|
||||
// We don't have a good way to use the query to obtain
|
||||
// our count. The problem is that we're relying on the
|
||||
// group by for the query, which will destroy the count,
|
||||
// whether we omit the group by or leave it in.
|
||||
// So, build a fresh query for counting.
|
||||
|
||||
$query['conditions'] = parent::jqGridDataConditions($params, $model);
|
||||
|
||||
$count = $model->find('count',
|
||||
array_merge(array('link' => array_diff_key($query['link'],
|
||||
array('CurrentLease'=>1))),
|
||||
array_diff_key($query, array('link'=>1))));
|
||||
|
||||
if ($params['action'] === 'all')
|
||||
return $count;
|
||||
|
||||
$query['conditions'][] = 'CurrentLease.id IS NULL';
|
||||
$count_past = $model->find('count', $query);
|
||||
|
||||
// Since we can't easily count 'current' directly, we
|
||||
// can quickly derive it since 'current' customers
|
||||
// are mutually exclusive to 'past' customers.
|
||||
if ($params['action'] == 'current')
|
||||
$count = $count - $count_past;
|
||||
elseif ($params['action'] == 'past') {
|
||||
$count = $count_past;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
function jqGridDataRecords(&$params, &$model, $query) {
|
||||
$customers = parent::jqGridDataRecords($params, $model, $query);
|
||||
|
||||
// Get the balance on each customer.
|
||||
foreach ($customers AS &$customer) {
|
||||
$stats = $this->Customer->stats($customer['Customer']['id']);
|
||||
$customer['Customer']['balance'] = $stats['balance'];
|
||||
}
|
||||
|
||||
return $customers;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Customer'] = array('name');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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');
|
||||
}
|
||||
|
||||
|
||||
@@ -153,20 +178,79 @@ class CustomersController extends AppController {
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
$customer = $this->Customer->details($id);
|
||||
// Get details on this customer, its contacts and leases
|
||||
$customer = $this->Customer->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Contact' =>
|
||||
array('order' => array('Contact.display_name'),
|
||||
// Models
|
||||
'ContactPhone',
|
||||
'ContactEmail',
|
||||
'ContactAddress',
|
||||
),
|
||||
'Lease' =>
|
||||
array('Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
$outstanding_balance = $customer['stats']['balance'];
|
||||
$outstanding_deposit = $customer['deposits']['summary']['balance'];
|
||||
'conditions' => array('Customer.id' => $id),
|
||||
));
|
||||
//pr($customer);
|
||||
|
||||
// Figure out the outstanding balances for this customer
|
||||
$outstanding_balance = $this->Customer->balance($id);
|
||||
$outstanding_deposit = $this->Customer->securityDepositBalance($id);
|
||||
|
||||
// Figure out if this customer has any non-closed leases
|
||||
$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);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
array('name' => 'Edit',
|
||||
'url' => array('action' => 'edit',
|
||||
$id));
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Payment', 'url' => array('action' => 'payment', $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));
|
||||
}
|
||||
|
||||
if ($outstanding_balance < 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Issue Refund',
|
||||
'url' => array('action' => 'refund', $id));
|
||||
|
||||
// Prepare to render.
|
||||
$title = $customer['Customer']['name'];
|
||||
$title = 'Customer: ' . $customer['Customer']['name'];
|
||||
$this->set(compact('customer', 'title',
|
||||
'outstanding_balance',
|
||||
'outstanding_deposit'));
|
||||
@@ -176,42 +260,160 @@ 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);
|
||||
$customer = $customer['Customer'];
|
||||
$unreconciled = $this->Customer->findUnreconciledLedgerEntries($id);
|
||||
$charges = $unreconciled['debit'];
|
||||
$unreconciled = $this->Customer->unreconciledCharges($id);
|
||||
//pr($unreconciled);
|
||||
$charges = $unreconciled['entries'];
|
||||
$stats = $unreconciled['summary']['Charge'];
|
||||
// Kludge until we update receipt to have the unpaid
|
||||
// charges grid generated from a dynamic query instead
|
||||
// of simply pre-providing the list of charge IDs
|
||||
foreach ($charges AS &$charge)
|
||||
$charge['id'] = $charge['StatementEntry']['id'];
|
||||
}
|
||||
else {
|
||||
$customer = null;
|
||||
$charges = array('balance' => 0, 'entry' => array());
|
||||
$charges = array();
|
||||
$stats = array('balance' => 0);
|
||||
}
|
||||
|
||||
$title = 'Payment Entry';
|
||||
$this->set(compact('customer', 'charges', 'title'));
|
||||
$TT = new TenderType();
|
||||
$payment_types = $TT->paymentTypes();
|
||||
$default_type = $TT->defaultPaymentType();
|
||||
$this->set(compact('payment_types', 'default_type'));
|
||||
|
||||
$title = ($customer['name'] . ': Receipt Entry');
|
||||
$this->set(compact('customer', 'charges', 'stats', 'title'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: unreconciledEntries
|
||||
* action: refund
|
||||
* - Refunds customer charges
|
||||
*/
|
||||
|
||||
function refund() {
|
||||
$entries = $this->Customer->StatementEntry->find
|
||||
('all', array
|
||||
('contain' => false,
|
||||
'conditions' => array('StatementEntry.id' =>
|
||||
//array(199,200,201)
|
||||
61
|
||||
),
|
||||
));
|
||||
pr(compact('entries'));
|
||||
|
||||
$this->Customer->refund($entries);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: unreconciled
|
||||
* - returns the list of unreconciled entries
|
||||
*/
|
||||
|
||||
@@ -229,17 +431,23 @@ class CustomersController extends AppController {
|
||||
|
||||
// Find the unreconciled entries, then manipulate the structure
|
||||
// slightly to accomodate the format necessary for XML Helper.
|
||||
$unreconciled = $this->Customer->findUnreconciledLedgerEntries($id);
|
||||
$unreconciled = $this->Customer->unreconciledCharges($id);
|
||||
|
||||
foreach ($unreconciled['entries'] AS &$entry)
|
||||
$entry = array_intersect_key($entry['StatementEntry'],
|
||||
array('id'=>1));
|
||||
|
||||
$unreconciled = array('entries' =>
|
||||
array_intersect_key($unreconciled['debit'],
|
||||
array('entry'=>1, 'balance'=>1)));
|
||||
array('entry' => $unreconciled['entries'],
|
||||
'balance' => $unreconciled['summary']['balance']));
|
||||
|
||||
// XML Helper will dump an empty tag if the array is empty
|
||||
if (!count($unreconciled['entries']['entry']))
|
||||
if (empty($unreconciled['entries']['entry']))
|
||||
unset($unreconciled['entries']['entry']);
|
||||
|
||||
pr($unreconciled);
|
||||
//$reconciled = $cust->reconcileNewLedgerEntry($cust_id, 'credit', $amount);
|
||||
/* pr(compact('unreconciled')); */
|
||||
/* echo htmlspecialchars($xml->serialize($unreconciled)); */
|
||||
/* $this->render('/fake'); */
|
||||
|
||||
$opts = array();
|
||||
//$opts['format'] = 'tags';
|
||||
|
||||
44
site/controllers/double_entries_controller.php
Normal file
44
site/controllers/double_entries_controller.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
class DoubleEntriesController extends AppController {
|
||||
|
||||
var $sidemenu_links = array();
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: view
|
||||
* - Displays information about a specific entry
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
|
||||
}
|
||||
|
||||
// Get the Entry and related fields
|
||||
$entry = $this->DoubleEntry->find
|
||||
('first',
|
||||
array('contain' => array('DebitEntry', 'CreditEntry'),
|
||||
'conditions' => array('DoubleEntry.id' => $id),
|
||||
));
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Double Ledger Entry #{$entry['DoubleEntry']['id']}";
|
||||
$this->set(compact('entry', 'title'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,34 +29,46 @@ class LeasesController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function active() { $this->jqGridView('Active Leases'); }
|
||||
function closed() { $this->jqGridView('Closed Leases'); }
|
||||
function all() { $this->jqGridView('All Leases', 'all'); }
|
||||
function active() { $this->gridView('Active Leases'); }
|
||||
function closed() { $this->gridView('Closed Leases'); }
|
||||
function all() { $this->gridView('All Leases', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function jqGridDataSetup(&$params) {
|
||||
parent::jqGridDataSetup($params);
|
||||
function gridDataSetup(&$params) {
|
||||
parent::gridDataSetup($params);
|
||||
if (!isset($params['action']))
|
||||
$params['action'] = 'all';
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
return array
|
||||
('link' => array('Unit' => array('fields' => array('Unit.id', 'Unit.name')),
|
||||
'Customer' => array('fields' => array('Customer.id', 'Customer.name'))));
|
||||
('link' => array('Unit' => array('fields' => array('id', 'name')),
|
||||
'Customer' => array('fields' => array('id', 'name'))));
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$link = $this->gridDataCountTables($params, $model);
|
||||
$link['link']['StatementEntry'] = array('fields' => array());
|
||||
return $link;
|
||||
}
|
||||
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
return array_merge($fields,
|
||||
$this->Lease->StatementEntry->chargeDisbursementFields(true));
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'active') {
|
||||
$conditions[] = 'Lease.close_date IS NULL';
|
||||
@@ -65,26 +77,303 @@ class LeasesController extends AppController {
|
||||
$conditions[] = 'Lease.close_date IS NOT NULL';
|
||||
}
|
||||
|
||||
if (isset($customer_id))
|
||||
$conditions[] = array('Lease.customer_id' => $customer_id);
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
function gridDataOrder(&$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::gridDataOrder($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::gridDataOrder($params, $model,
|
||||
'Lease.id', $direction);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
/* function gridDataPostProcess(&$params, &$model, &$records) { */
|
||||
/* foreach ($records AS &$record) { */
|
||||
/* $record['Lease']['through_date'] */
|
||||
/* = $this->Lease->rentChargeThrough($record['Lease']['id']); */
|
||||
/* } */
|
||||
|
||||
/* parent::gridDataPostProcess($params, $model, $records); */
|
||||
/* } */
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Lease'] = array('number');
|
||||
$links['Unit'] = array('name');
|
||||
$links['Customer'] = array('name');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
return parent::gridDataPostProcessLinks($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']);
|
||||
}
|
||||
|
||||
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: promote_credit */
|
||||
/* * - Moves any lease credit up to the customer level, so that */
|
||||
/* * it may be used for charges other than those on this lease. */
|
||||
/* *\/ */
|
||||
|
||||
/* function promote_surplus($id) { */
|
||||
/* $this->Lease->promoteSurplus($id); */
|
||||
/* pr("PREVENTING REDIRECT"); */
|
||||
/* $this->render('fake'); */
|
||||
/* $this->redirect(array('controller' => 'leases', */
|
||||
/* 'action' => 'view', */
|
||||
/* $id)); */
|
||||
/* } */
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: refund
|
||||
* - Provides lease customer with a refund
|
||||
*/
|
||||
|
||||
function refund($id) {
|
||||
$lease = $this->Lease->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Unit' => array('fields' => array('id', 'name')),
|
||||
'Customer' => array('fields' => array('id', 'name')),
|
||||
),
|
||||
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
// Make sure lease is not closed...
|
||||
array('Lease.close_date' => null),
|
||||
),
|
||||
));
|
||||
if (empty($lease)) {
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
// Determine the lease balance, bailing if the customer owes money
|
||||
$balance = $this->Lease->balance($id);
|
||||
if ($balance >= 0) {
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
// The refund will be for a positive amount
|
||||
$balance *= -1;
|
||||
|
||||
// Get the accounts capable of paying the refund
|
||||
$refundAccounts = $this->Lease->StatementEntry->Account->refundAccounts();
|
||||
$defaultAccount = current($refundAccounts);
|
||||
$this->set(compact('refundAccounts', 'defaultAccount'));
|
||||
|
||||
// Prepare to render
|
||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||
$lease['Unit']['name'] . ': ' .
|
||||
$lease['Customer']['name'] . ': Refund');
|
||||
$this->set(compact('title', 'lease', 'balance'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: bad_debt
|
||||
* - Sets up the write-off entry page, so that the
|
||||
* user can write off remaining charges on a lease.
|
||||
*/
|
||||
|
||||
function bad_debt($id) {
|
||||
$lease = $this->Lease->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Unit' => array('fields' => array('id', 'name')),
|
||||
'Customer' => array('fields' => array('id', 'name')),
|
||||
),
|
||||
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
// Make sure lease is not closed...
|
||||
array('Lease.close_date' => null),
|
||||
),
|
||||
));
|
||||
|
||||
// Make sure we have a valid lease to write off
|
||||
if (empty($lease))
|
||||
$this->redirect(array('action' => 'view', $id));
|
||||
|
||||
// Get the lease balance, part of lease stats
|
||||
$stats = $this->Lease->stats($id);
|
||||
$balance = $stats['balance'];
|
||||
|
||||
// Prepare to render
|
||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||
$lease['Unit']['name'] . ': ' .
|
||||
$lease['Customer']['name'] . ': Write Off Bad Debt');
|
||||
$this->set(compact('title', 'lease', 'balance'));
|
||||
$this->render('/transactions/bad_debt');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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->invoiceAccounts();
|
||||
$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,18 +400,58 @@ class LeasesController extends AppController {
|
||||
'Customer',
|
||||
),
|
||||
'conditions' => array(array('Lease.id' => $id)),
|
||||
'limit' => 2
|
||||
)
|
||||
);
|
||||
|
||||
// Obtain the overall lease balance
|
||||
$this->Lease->statsMerge($lease['Lease'],
|
||||
array('stats' => $this->Lease->stats($id)));
|
||||
$outstanding_balance = $lease['Lease']['stats']['balance'];
|
||||
$lease['Lease']['paid_through'] = $this->Lease->rentPaidThrough($id);
|
||||
|
||||
// Determine the lease security deposit
|
||||
$deposits = $this->Lease->findSecurityDeposits($lease['Lease']['id']);
|
||||
$outstanding_deposit = $deposits['summary']['balance'];
|
||||
|
||||
$this->set('charge_gaps', $this->Lease->rentChargeGaps($id));
|
||||
$this->set('charge_through', $this->Lease->rentChargeThrough($id));
|
||||
|
||||
// Figure out the outstanding balances for this lease
|
||||
$outstanding_balance = $this->Lease->balance($id);
|
||||
$outstanding_deposit = $this->Lease->securityDepositBalance($id);
|
||||
|
||||
// Set up dynamic menu items
|
||||
if (!isset($lease['Lease']['close_date'])) {
|
||||
$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 ($outstanding_balance < 0) */
|
||||
/* $this->sidemenu_links[] = */
|
||||
/* array('name' => 'Transfer Credit to Customer', */
|
||||
/* 'url' => array('action' => 'promote_surplus', $id)); */
|
||||
|
||||
if ($outstanding_balance < 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Issue Refund',
|
||||
'url' => array('action' => 'refund', $id));
|
||||
|
||||
if (isset($lease['Lease']['moveout_date']) && $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'];
|
||||
|
||||
@@ -19,218 +19,91 @@ class LedgerEntriesController extends AppController {
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* action: index / current / past / all
|
||||
* - Creates a list of ledger entries
|
||||
*/
|
||||
|
||||
function index() { $this->gridView('All Ledger Entries'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function jqGridDataSetup(&$params) {
|
||||
parent::jqGridDataSetup($params);
|
||||
if (isset($params['custom']['ar_account'])) {
|
||||
$params['custom']['account_id'] =
|
||||
$this->LedgerEntry->DebitLedger->Account->accountReceivableAccountID();
|
||||
}
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$link =
|
||||
array(// Models
|
||||
'Transaction' =>
|
||||
array('fields' => array('id', 'stamp'),
|
||||
),
|
||||
'Transaction' =>
|
||||
array('fields' => array('id', 'stamp'),
|
||||
),
|
||||
|
||||
'MonetarySource' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
'Ledger' =>
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'Account' =>
|
||||
array('fields' => array('id', 'name', 'type'),
|
||||
),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
'Tender' =>
|
||||
array('fields' => array('id', 'name', 'nsf_transaction_id'),
|
||||
),
|
||||
|
||||
'Lease' =>
|
||||
array('fields' => array('id', 'number'),
|
||||
'Unit' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
/* 'DebitEntry', */
|
||||
/* 'CreditEntry', */
|
||||
);
|
||||
|
||||
if (isset($params['custom']['account_ftype'])) {
|
||||
$ftype = $params['custom']['account_ftype'];
|
||||
$ftype = ucfirst($ftype);
|
||||
//$ftype = $this->LedgerEntry->DebitLedger->Account->fundamentalOpposite($ftype);
|
||||
$link[$ftype . 'Ledger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'Account' => array('class' => 'Account',
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
}
|
||||
elseif (isset($params['custom']['ledger_id'])) {
|
||||
$ledger_id = $params['custom']['ledger_id'];
|
||||
$link['Ledger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'conditions' => ("Ledger.id = IF(LedgerEntry.debit_ledger_id = $ledger_id," .
|
||||
" LedgerEntry.credit_ledger_id," .
|
||||
" LedgerEntry.debit_ledger_id)"),
|
||||
'Account' => array(
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$link['DebitLedger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'DebitAccount' => array('class' => 'Account',
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
|
||||
$link['CreditLedger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'CreditAccount' => array('class' => 'Account',
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['account_id'])) {
|
||||
$account_id = $params['custom']['account_id'];
|
||||
$link['Ledger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'conditions' => ("Ledger.id = IF(DebitLedger.account_id = $account_id," .
|
||||
" LedgerEntry.credit_ledger_id," .
|
||||
" LedgerEntry.debit_ledger_id)"),
|
||||
'Account' => array(
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['reconcile_id'])) {
|
||||
$ftype = $params['custom']['account_ftype'];
|
||||
$ftype = $this->LedgerEntry->DebitLedger->Account->fundamentalOpposite($ftype);
|
||||
$ftype = ucfirst($ftype);
|
||||
$link[$ftype.'ReconciliationLedgerEntry'] =
|
||||
array('fields' => array('Reconciliation.amount'));
|
||||
}
|
||||
|
||||
return array('link' => $link);
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
$ledger_id = (isset($params['custom']['ledger_id'])
|
||||
? $params['custom']['ledger_id']
|
||||
: null);
|
||||
$account_id = (isset($params['custom']['account_id'])
|
||||
? $params['custom']['account_id']
|
||||
: null);
|
||||
$account_type = (isset($params['custom']['account_type'])
|
||||
? $params['custom']['account_type']
|
||||
: null);
|
||||
|
||||
return $model->ledgerContextFields2($ledger_id, $account_id, $account_type);
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
return array_merge($fields,
|
||||
$this->LedgerEntry->debitCreditFields());
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$ledger_id = (isset($params['custom']['ledger_id'])
|
||||
? $params['custom']['ledger_id']
|
||||
: null);
|
||||
$account_type = (isset($params['custom']['account_type'])
|
||||
? $params['custom']['account_type']
|
||||
: null);
|
||||
function gridDataFilterTablesTable(&$params, &$model, $table) {
|
||||
$table = $this->gridDataFilterTableName($params, $model, $table);
|
||||
// Account is already part of our standard table set.
|
||||
// Ensure we don't add it in again as part of filtering.
|
||||
if ($table == 'Account')
|
||||
return null;
|
||||
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
// Customer needs to be added beneath Transaction
|
||||
if ($table == 'Customer')
|
||||
return 'Transaction';
|
||||
|
||||
if ($params['action'] === 'ledger') {
|
||||
$conditions[] = $model->ledgerContextConditions($ledger_id, $account_type);
|
||||
}
|
||||
if (isset($params['custom']['reconcile_id'])) {
|
||||
$ftype = $params['custom']['account_ftype'];
|
||||
//$ftype = $this->LedgerEntry->DebitLedger->Account->fundamentalOpposite($ftype);
|
||||
$conditions[] = array('Reconciliation.'.$ftype.'_ledger_entry_id' => $params['custom']['reconcile_id']);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['account_id'])) {
|
||||
$conditions[] =
|
||||
array('OR' =>
|
||||
array(array('CreditAccount.id' => $params['custom']['account_id']),
|
||||
array('DebitAccount.id' => $params['custom']['account_id'])));
|
||||
}
|
||||
|
||||
if (isset($params['custom']['customer_id'])) {
|
||||
$conditions[] =
|
||||
array('Customer.id' => $params['custom']['customer_id']);
|
||||
|
||||
/* $Account = new Account(); */
|
||||
/* if (isset($params['custom']['account_ftype']) || */
|
||||
/* isset($params['custom']['ledger_id'])) { */
|
||||
/* $conditions[] = */
|
||||
/* array('OR' => array('Account.id' => $Account->invoiceAccountID(), */
|
||||
/* 'Account.id' => $Account->receiptAccountID())); */
|
||||
/* } else { */
|
||||
/* $conditions[] = */
|
||||
/* array('OR' => array('DebitAccount.id' => $Account->invoiceAccountID(), */
|
||||
/* //'CreditAccount.id' => $Account->invoiceAccountID(), */
|
||||
/* //'DebitAccount.id' => $Account->receiptAccountID(), */
|
||||
/* 'CreditAccount.id' => $Account->receiptAccountID(), */
|
||||
/* )); */
|
||||
/* } */
|
||||
}
|
||||
|
||||
if (isset($params['custom']['lease_id'])) {
|
||||
$conditions[] =
|
||||
array('Lease.id' => $params['custom']['lease_id']);
|
||||
|
||||
/* $Account = new Account(); */
|
||||
/* if (isset($params['custom']['account_ftype']) || */
|
||||
/* isset($params['custom']['ledger_id'])) { */
|
||||
/* $conditions[] = */
|
||||
/* array('OR' => array('Account.id' => $Account->invoiceAccountID(), */
|
||||
/* 'Account.id' => $Account->receiptAccountID())); */
|
||||
/* } else { */
|
||||
/* $conditions[] = */
|
||||
/* array('OR' => array('DebitAccount.id' => $Account->invoiceAccountID(), */
|
||||
/* //'CreditAccount.id' => $Account->invoiceAccountID(), */
|
||||
/* //'DebitAccount.id' => $Account->receiptAccountID(), */
|
||||
/* 'CreditAccount.id' => $Account->receiptAccountID(), */
|
||||
/* )); */
|
||||
/* } */
|
||||
}
|
||||
|
||||
if (isset($params['custom']['transaction_id'])) {
|
||||
$conditions[] =
|
||||
array('Transaction.id' => $params['custom']['transaction_id']);
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
return $table;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Transaction'] = array('id');
|
||||
$links['LedgerEntry'] = array('id');
|
||||
$links['Account'] = array('controller' => 'accounts', 'name');
|
||||
$links['DebitAccount'] = array('controller' => 'accounts', 'name');
|
||||
$links['CreditAccount'] = array('controller' => 'accounts', 'name');
|
||||
$links['MonetarySource'] = array('name');
|
||||
$links['Customer'] = array('name');
|
||||
$links['Lease'] = array('number');
|
||||
$links['Unit'] = array('name');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
function gridDataFilterTablesConfig(&$params, &$model, $table) {
|
||||
$config = parent::gridDataFilterTablesConfig($params, $model, $table);
|
||||
|
||||
// Customer is special in that its linked in by Transaction
|
||||
// Therefore, the actual table used for the join is 'Transaction',
|
||||
// not 'Customer', and so we need to specify Customer here.
|
||||
if ($table == 'Customer')
|
||||
$config = array('Customer' => $config);
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
function jqGridDataGroup(&$params, &$model) {
|
||||
if (isset($params['custom']['notxgroup']))
|
||||
return parent::jqGridDataGroup($params, $model);
|
||||
|
||||
return $model->alias.'.transaction_id';
|
||||
function gridDataFilterConditionsStatement(&$params, &$model, $table, $key, $value) {
|
||||
//pr(compact('table', 'key', 'value'));
|
||||
if ($table == 'Account' && $value['value_present'] && $value['value'] === '-AR-')
|
||||
$value = $this->LedgerEntry->Ledger->Account->accountReceivableAccountID();
|
||||
return parent::gridDataFilterConditionsStatement($params, $model, $table, $key, $value);
|
||||
}
|
||||
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
/* if ($index === 'balance') */
|
||||
/* return ($index .' '. $direction); */
|
||||
$order = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
$order = parent::gridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
if ($index === 'Transaction.stamp') {
|
||||
$order[] = 'LedgerEntry.id ' . $direction;
|
||||
@@ -239,30 +112,28 @@ class LedgerEntriesController extends AppController {
|
||||
return $order;
|
||||
}
|
||||
|
||||
function jqGridRecordsPostProcess(&$params, &$model, &$records) {
|
||||
parent::jqGridRecordsPostProcess($params, $model, $records);
|
||||
|
||||
$subtotal = 0;
|
||||
function gridDataPostProcessCalculatedFields(&$params, &$model, &$records) {
|
||||
parent::gridDataPostProcessCalculatedFields($params, $model, $records);
|
||||
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'];
|
||||
// REVISIT <AP>: 20090730
|
||||
// We really need the grid to handle this. We probably need to
|
||||
// either create a hidden column with the nsf id, or pass back
|
||||
// a list of nsf items as user data. We can then add an onload
|
||||
// function to sweep through the nsf items and format them.
|
||||
// For now... this works.
|
||||
if (!empty($record['Tender']['nsf_transaction_id']))
|
||||
$record['Tender']['name'] =
|
||||
'<SPAN class="nsf-tender">' . $record['Tender']['name'] . '</SPAN>';
|
||||
}
|
||||
}
|
||||
|
||||
function jqGridDataOutputRecordCell(&$params, &$model, &$record, $field, $data) {
|
||||
/* if ($field === 'CreditAccount.name') { */
|
||||
/* $data .= '-OK'; */
|
||||
/* } */
|
||||
|
||||
parent::jqGridDataOutputRecordCell($params, $model, $record, $field, $data);
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['LedgerEntry'] = array('id');
|
||||
$links['Transaction'] = array('id');
|
||||
$links['Ledger'] = array('id');
|
||||
$links['Account'] = array('controller' => 'accounts', 'name');
|
||||
$links['Tender'] = array('name');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -279,66 +150,50 @@ class LedgerEntriesController extends AppController {
|
||||
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
|
||||
}
|
||||
|
||||
// Get the LedgerEntry and related fields
|
||||
// Get the Entry and related fields
|
||||
$entry = $this->LedgerEntry->find
|
||||
('first',
|
||||
array('contain' => array('MonetarySource.id',
|
||||
'MonetarySource.name',
|
||||
'MonetarySource.MonetaryType.id',
|
||||
'Transaction.id',
|
||||
'Transaction.stamp',
|
||||
'DebitLedger.id',
|
||||
'DebitLedger.sequence',
|
||||
'DebitLedger.account_id',
|
||||
'CreditLedger.id',
|
||||
'CreditLedger.sequence',
|
||||
'CreditLedger.account_id',
|
||||
'Customer.id',
|
||||
'Customer.name',
|
||||
'Lease.id',
|
||||
),
|
||||
array('contain' => array
|
||||
(
|
||||
'Transaction' =>
|
||||
array('fields' => array('id', 'stamp'),
|
||||
),
|
||||
|
||||
'fields' => array('LedgerEntry.id',
|
||||
'LedgerEntry.amount',
|
||||
'LedgerEntry.comment'),
|
||||
'Ledger' =>
|
||||
array('fields' => array('id', 'sequence', 'name'),
|
||||
'Account' =>
|
||||
array('fields' => array('id', 'name', 'type'),
|
||||
),
|
||||
),
|
||||
|
||||
'Tender' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'DebitEntry' => array('fields' => array('id', 'crdr')),
|
||||
'CreditEntry' => array('fields' => array('id', 'crdr')),
|
||||
),
|
||||
|
||||
'conditions' => array('LedgerEntry.id' => $id),
|
||||
));
|
||||
pr($entry);
|
||||
|
||||
// Because 'DebitLedger' and 'CreditLedger' both relate to 'Account',
|
||||
// CakePHP will not include them in the LedgerEntry->find (or so it
|
||||
// seems). We'll have to break out each Account separately.
|
||||
if (!empty($entry['DebitEntry']) && !empty($entry['CreditEntry']))
|
||||
die("LedgerEntry has both a matching DebitEntry and CreditEntry");
|
||||
if (empty($entry['DebitEntry']) && empty($entry['CreditEntry']))
|
||||
die("LedgerEntry has neither a matching DebitEntry nor a CreditEntry");
|
||||
if (empty($entry['DebitEntry']) && count($entry['CreditEntry']) != 1)
|
||||
die("LedgerEntry has more than one CreditEntry");
|
||||
if (empty($entry['CreditEntry']) && count($entry['DebitEntry']) != 1)
|
||||
die("LedgerEntry has more than one DebitEntry");
|
||||
|
||||
// Get the Account from DebitLedger
|
||||
$entry['DebitLedger'] += $this->LedgerEntry->DebitLedger->Account->find
|
||||
('first',
|
||||
array('contain' => true,
|
||||
'fields' => array('Account.id', 'Account.name', 'Account.type', 'Account.trackable'),
|
||||
'conditions' => array('Account.id' => $entry['DebitLedger']['account_id']),
|
||||
));
|
||||
|
||||
// Get the Account from CreditLedger
|
||||
$entry['CreditLedger'] += $this->LedgerEntry->CreditLedger->Account->find
|
||||
('first',
|
||||
array('contain' => true,
|
||||
'fields' => array('Account.id', 'Account.name', 'Account.type', 'Account.trackable'),
|
||||
'conditions' => array('Account.id' => $entry['CreditLedger']['account_id']),
|
||||
));
|
||||
|
||||
// Get the reconciliation balances for this ledger entry
|
||||
$stats = $this->LedgerEntry->stats($id);
|
||||
if ($entry['DebitLedger']['Account']['trackable'])
|
||||
$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'];
|
||||
//pr($stats);
|
||||
|
||||
$reconciled = $this->LedgerEntry->findReconciledLedgerEntries($id);
|
||||
//pr($reconciled);
|
||||
if (empty($entry['DebitEntry']))
|
||||
$entry['MatchingEntry'] = $entry['CreditEntry'][0];
|
||||
else
|
||||
$entry['MatchingEntry'] = $entry['DebitEntry'][0];
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Ledger Entry #{$entry['LedgerEntry']['id']}";
|
||||
$this->set(compact('entry', 'title', 'reconciled', 'stats'));
|
||||
$this->set(compact('entry', 'title'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,77 +29,73 @@ class LedgersController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function current() { $this->jqGridView('Current Ledgers'); }
|
||||
function closed() { $this->jqGridView('Closed Ledgers'); }
|
||||
function all() { $this->jqGridView('All Ledgers', 'all'); }
|
||||
function current() { $this->gridView('Current Ledgers'); }
|
||||
function closed() { $this->gridView('Closed Ledgers'); }
|
||||
function all() { $this->gridView('All Ledgers', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function jqGridDataSetup(&$params) {
|
||||
parent::jqGridDataSetup($params);
|
||||
function gridDataSetup(&$params) {
|
||||
parent::gridDataSetup($params);
|
||||
if (!isset($params['action']))
|
||||
$params['action'] = 'all';
|
||||
}
|
||||
|
||||
function jqGridDataCountTables(&$params, &$model) {
|
||||
return array('contain' => false);
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
// Our count should NOT include anything extra,
|
||||
// so we need the virtual function to prevent
|
||||
// the base class from just calling our
|
||||
// gridDataTables function.
|
||||
return parent::gridDataTables($params, $model);
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
function gridDataTables(&$params, &$model) {
|
||||
return array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'Account',
|
||||
'LedgerEntry',
|
||||
'CloseTransaction',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
return array
|
||||
('Ledger.*',
|
||||
'CONCAT(Account.id, "-", Ledger.sequence) AS id_sequence',
|
||||
'SUM(IF(LedgerEntry.debit_ledger_id = Ledger.id,
|
||||
LedgerEntry.amount, NULL)) AS debits',
|
||||
'SUM(IF(LedgerEntry.credit_ledger_id = Ledger.id,
|
||||
LedgerEntry.amount, NULL)) AS credits',
|
||||
"SUM(IF(Account.type IN ('ASSET', 'EXPENSE'),
|
||||
IF(LedgerEntry.debit_ledger_id = Ledger.id, 1, -1),
|
||||
IF(LedgerEntry.credit_ledger_id = Ledger.id, 1, -1)
|
||||
) * IF(LedgerEntry.amount, LedgerEntry.amount, 0)
|
||||
) AS balance",
|
||||
'COUNT(LedgerEntry.id) AS entries');
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
$fields[] = 'CONCAT(Account.id, "-", Ledger.sequence) AS id_sequence';
|
||||
return array_merge($fields,
|
||||
$this->Ledger->LedgerEntry->debitCreditFields(true));
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'current') {
|
||||
$conditions[] = array('NOT' => array('Ledger.closed'));
|
||||
$conditions[] = array('Ledger.close_transaction_id' => null);
|
||||
}
|
||||
elseif ($params['action'] === 'closed') {
|
||||
$conditions[] = 'Ledger.closed';
|
||||
$conditions[] = array('Ledger.close_transaction_id !=' => null);
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
$id_sequence = false;
|
||||
if ($index === 'id_sequence') {
|
||||
$id_sequence = true;
|
||||
$index = 'Ledger.account_id';
|
||||
}
|
||||
|
||||
$order = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
$order = parent::gridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
if ($id_sequence) {
|
||||
$order[] = 'Ledger.sequence ' . $direction;
|
||||
@@ -108,10 +104,10 @@ class LedgersController extends AppController {
|
||||
return $order;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Ledger'] = array('id_sequence');
|
||||
$links['Account'] = array('name');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,28 +15,28 @@ class MapsController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function all() { $this->jqGridView('All Maps', 'all'); }
|
||||
function all() { $this->gridView('All Maps', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
function gridDataTables(&$params, &$model) {
|
||||
return array
|
||||
('link' => array('SiteArea' => array('fields' => array('SiteArea.id', 'SiteArea.name')),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Map'] = array('id');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
class MonetarySourcesController extends AppController {
|
||||
|
||||
var $sidemenu_links = array();
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: index / all
|
||||
* - Generate a listing of MonetarySources
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function all() { $this->jqGridView('All MonetarySources', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
return array
|
||||
('link' => array('MonetaryType' => array('fields' => array('MonetaryType.id', 'MonetaryType.name')),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['MonetarySource'] = array('id');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: view
|
||||
* - Displays information about a specific entry
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
|
||||
}
|
||||
|
||||
// Get the MonetarySource and related fields
|
||||
$monetary_source = $this->MonetarySource->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
('MonetaryType',
|
||||
),
|
||||
));
|
||||
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
277
site/controllers/statement_entries_controller.php
Normal file
277
site/controllers/statement_entries_controller.php
Normal file
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
|
||||
class StatementEntriesController extends AppController {
|
||||
|
||||
var $sidemenu_links = array();
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: index / current / past / all
|
||||
* - Creates a list of statement entries
|
||||
*/
|
||||
|
||||
function index() { $this->gridView('All Statement Entries'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$link =
|
||||
array(// Models
|
||||
'Transaction' =>
|
||||
array('fields' => array('id', 'stamp'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Lease' =>
|
||||
array('fields' => array('id', 'number'),
|
||||
'Unit' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
|
||||
'Account' =>
|
||||
array('fields' => array('id', 'name', 'type'),
|
||||
),
|
||||
);
|
||||
|
||||
if (isset($params['post']['custom']['statement_entry_id'])) {
|
||||
$link['DisbursementEntry'] = array();
|
||||
$link['ChargeEntry'] = array();
|
||||
}
|
||||
|
||||
/* if ($params['action'] === 'collected') { */
|
||||
/* $link['DisbursementEntry'] = array('Receipt' => array('class' => 'Transaction')); */
|
||||
/* $link['ChargeEntry'] = array('Invoice' => array('class' => 'Transaction')); */
|
||||
/* } */
|
||||
|
||||
/* if (count(array_intersect($params['fields'], array('applied'))) == 1) { */
|
||||
/* $link['DisbursementEntry'] = array(); */
|
||||
/* $link['ChargeEntry'] = array(); */
|
||||
/* } */
|
||||
/* elseif (isset($params['post']['custom']['customer_id']) || isset($params['post']['custom']['lease_id'])) { */
|
||||
/* $link['DisbursementEntry'] = array(); */
|
||||
/* } */
|
||||
|
||||
return array('link' => $link);
|
||||
}
|
||||
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
|
||||
if (in_array('applied', $params['post']['fields'])) {
|
||||
$fields[] = ("IF(StatementEntry.type = 'CHARGE'," .
|
||||
" SUM(COALESCE(DisbursementEntry.amount,0))," .
|
||||
" SUM(COALESCE(ChargeEntry.amount,0)))" .
|
||||
" AS 'applied'");
|
||||
$fields[] = ("StatementEntry.amount - (" .
|
||||
"IF(StatementEntry.type = 'CHARGE'," .
|
||||
" SUM(COALESCE(DisbursementEntry.amount,0))," .
|
||||
" SUM(COALESCE(ChargeEntry.amount,0)))" .
|
||||
") AS 'balance'");
|
||||
}
|
||||
|
||||
$fields = array_merge($fields,
|
||||
$this->StatementEntry->chargeDisbursementFields());
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
extract($params['post']['custom']);
|
||||
|
||||
if (!empty($from_date))
|
||||
$conditions[]
|
||||
= array('Transaction.stamp >=' =>
|
||||
$this->StatementEntry->Transaction->dateFormatBeforeSave($from_date));
|
||||
|
||||
if (!empty($through_date))
|
||||
$conditions[]
|
||||
= array('Transaction.stamp <=' =>
|
||||
$this->StatementEntry->Transaction->dateFormatBeforeSave($through_date . ' 23:59:59'));
|
||||
|
||||
if (isset($account_id))
|
||||
$conditions[] = array('StatementEntry.account_id' => $account_id);
|
||||
|
||||
if (isset($statement_entry_id)) {
|
||||
$conditions[] = array('OR' =>
|
||||
array(array('ChargeEntry.id' => $statement_entry_id),
|
||||
array('DisbursementEntry.id' => $statement_entry_id)));
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['StatementEntry'] = array('id');
|
||||
$links['Transaction'] = array('id');
|
||||
$links['Account'] = array('name');
|
||||
$links['Customer'] = array('name');
|
||||
$links['Lease'] = array('number');
|
||||
$links['Unit'] = array('name');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
$order = parent::gridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
// After sorting by whatever the user wants, add these
|
||||
// defaults into the sort mechanism. If we're already
|
||||
// sorting by one of them, it will only be redundant,
|
||||
// and should cause no harm (possible a longer query?)
|
||||
$order[] = 'Transaction.stamp ' . $direction;
|
||||
$order[] = 'StatementEntry.effective_date ' . $direction;
|
||||
$order[] = 'StatementEntry.id ' . $direction;
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
function gridDataRecordsExecute(&$params, &$model, $query) {
|
||||
if (in_array('applied', $params['post']['fields'])) {
|
||||
$tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1));
|
||||
$tquery['fields'] = array("IF(StatementEntry.type = 'CHARGE'," .
|
||||
" SUM(COALESCE(DisbursementEntry.amount,0))," .
|
||||
" SUM(COALESCE(ChargeEntry.amount,0)))" .
|
||||
" AS 'applied'",
|
||||
|
||||
"StatementEntry.amount - (" .
|
||||
"IF(StatementEntry.type = 'CHARGE'," .
|
||||
" SUM(COALESCE(DisbursementEntry.amount,0))," .
|
||||
" SUM(COALESCE(ChargeEntry.amount,0)))" .
|
||||
") AS 'balance'",
|
||||
);
|
||||
|
||||
//pr(compact('tquery'));
|
||||
$total = $model->find('first', $tquery);
|
||||
$params['userdata']['total'] = $total[0]['applied'];
|
||||
$params['userdata']['balance'] = $total[0]['balance'];
|
||||
}
|
||||
else {
|
||||
$tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1));
|
||||
$tquery['fields'] = array("SUM(COALESCE(StatementEntry.amount,0)) AS 'total'");
|
||||
$total = $model->find('first', $tquery);
|
||||
$params['userdata']['total'] = $total[0]['total'];
|
||||
}
|
||||
|
||||
return parent::gridDataRecordsExecute($params, $model, $query);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: reverse the ledger entry
|
||||
*/
|
||||
|
||||
function reverse($id) {
|
||||
$this->StatementEntry->reverse($id);
|
||||
$this->render('/FAKE');
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: waive the ledger entry
|
||||
*/
|
||||
|
||||
function waive($id) {
|
||||
$this->StatementEntry->waive($id);
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: view
|
||||
* - Displays information about a specific entry
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
|
||||
}
|
||||
|
||||
// Get the StatementEntry and related fields
|
||||
$entry = $this->StatementEntry->find
|
||||
('first',
|
||||
array('contain' => array
|
||||
('Transaction' => array('fields' => array('id', 'type', 'stamp')),
|
||||
'Account' => array('id', 'name', 'type'),
|
||||
'Customer' => array('fields' => array('id', 'name')),
|
||||
'Lease' => array('fields' => array('id')),
|
||||
),
|
||||
|
||||
'conditions' => array('StatementEntry.id' => $id),
|
||||
));
|
||||
|
||||
$reconciled = $this->StatementEntry->reconciledEntries($id);
|
||||
|
||||
$stats = $this->StatementEntry->stats($id);
|
||||
|
||||
if (in_array(strtoupper($entry['StatementEntry']['type']), $this->StatementEntry->debitTypes()))
|
||||
$stats = $stats['Charge'];
|
||||
else
|
||||
$stats = $stats['Disbursement'];
|
||||
|
||||
|
||||
if (strtoupper($entry['StatementEntry']['type']) === 'CHARGE') {
|
||||
|
||||
$reversal = $this->StatementEntry->find
|
||||
('first',
|
||||
array('link' => array('DisbursementEntry'),
|
||||
'conditions' => array(array('StatementEntry.id' => $id),
|
||||
array('DisbursementEntry.type' => 'REVERSAL')),
|
||||
));
|
||||
|
||||
// Set up dynamic menu items
|
||||
if (empty($reversal) || $stats['balance'] > 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
if (empty($reversal))
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Reverse',
|
||||
'url' => array('action' => 'reverse',
|
||||
$id));
|
||||
|
||||
if ($stats['balance'] > 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Waive Balance',
|
||||
'url' => array('action' => 'waive',
|
||||
$id));
|
||||
}
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Statement Entry #{$entry['StatementEntry']['id']}";
|
||||
$this->set(compact('entry', 'title', 'reconciled', 'stats'));
|
||||
}
|
||||
|
||||
}
|
||||
167
site/controllers/tenders_controller.php
Normal file
167
site/controllers/tenders_controller.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
class TendersController extends AppController {
|
||||
|
||||
var $sidemenu_links = array();
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: index / all
|
||||
* - Generate a listing of Tenders
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function all() { $this->gridView('All Legal Tender', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
return array
|
||||
('link' =>
|
||||
array('TenderType',
|
||||
'Customer',
|
||||
'LedgerEntry' =>
|
||||
array('Transaction',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function gridDataRecordsExecute(&$params, &$model, $query) {
|
||||
$tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1));
|
||||
$tquery['fields'] = array("SUM(COALESCE(LedgerEntry.amount,0)) AS 'total'");
|
||||
$total = $model->find('first', $tquery);
|
||||
$params['userdata']['total'] = $total[0]['total'];
|
||||
|
||||
return parent::gridDataRecordsExecute($params, $model, $query);
|
||||
}
|
||||
|
||||
function gridDataPostProcessCalculatedFields(&$params, &$model, &$records) {
|
||||
parent::gridDataPostProcessCalculatedFields($params, $model, $records);
|
||||
foreach ($records AS &$record) {
|
||||
// REVISIT <AP>: 20090730
|
||||
// We really need the grid to handle this. We probably need to
|
||||
// either create a hidden column with the nsf id, or pass back
|
||||
// a list of nsf items as user data. We can then add an onload
|
||||
// function to sweep through the nsf items and format them.
|
||||
// For now... this works.
|
||||
if (!empty($record['Tender']['nsf_transaction_id']))
|
||||
$record['Tender']['name'] =
|
||||
'<SPAN class="nsf-tender">' . $record['Tender']['name'] . '</SPAN>';
|
||||
}
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Tender'] = array('name', 'id');
|
||||
$links['Customer'] = array('name');
|
||||
$links['TenderType'] = array('name');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: deposit
|
||||
* - Prepares the books for a bank deposit
|
||||
*/
|
||||
|
||||
function deposit() {
|
||||
// Prepare a close page...
|
||||
$deposit_types = $this->Tender->TenderType->depositTypes(
|
||||
// Testing... limit to only one type
|
||||
//array('limit' => 1)
|
||||
);
|
||||
$deposit_accounts = $this->Tender->TenderType->Account->depositAccounts();
|
||||
|
||||
foreach ($deposit_types AS $type_id => &$type)
|
||||
$type = array('id' => $type_id,
|
||||
'name' => $type,
|
||||
'stats' => $this->Tender->TenderType->stats($type_id));
|
||||
|
||||
//pr(compact('deposit_types', 'deposit_accounts'));
|
||||
|
||||
$title = 'Prepare Deposit';
|
||||
$this->set(compact('title', 'deposit_types', 'deposit_accounts'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: nsf
|
||||
* - Marks a tender as having insufficient funds.
|
||||
*/
|
||||
|
||||
function nsf($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
$this->Tender->nsf($id);
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: view
|
||||
* - Displays information about a specific entry
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
|
||||
}
|
||||
|
||||
// Get the Tender and related fields
|
||||
$tender = $this->Tender->find
|
||||
('first', array
|
||||
('contain' => array('TenderType', 'Customer', 'LedgerEntry' => array('Transaction')),
|
||||
));
|
||||
|
||||
|
||||
if (!empty($tender['Tender']['deposit_transaction_id'])
|
||||
&& empty($tender['Tender']['nsf_transaction_id'])
|
||||
// Hard to tell what types of items can come back as NSF.
|
||||
// For now, assume iff it is a named item, it can be NSF.
|
||||
&& !empty($tender['TenderType']['data1_name'])
|
||||
) {
|
||||
// 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 = "Tender #{$tender['Tender']['id']}";
|
||||
$this->set(compact('tender', 'title'));
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,9 @@ class TransactionsController extends AppController {
|
||||
var $sidemenu_links =
|
||||
array(array('name' => 'Transactions', 'header' => true),
|
||||
array('name' => 'All', 'url' => array('controller' => 'transactions', 'action' => 'all')),
|
||||
array('name' => 'Invoices', 'url' => array('controller' => 'transactions', 'action' => 'invoice')),
|
||||
array('name' => 'Receipts', 'url' => array('controller' => 'transactions', 'action' => 'receipt')),
|
||||
array('name' => 'Deposits', 'url' => array('controller' => 'transactions', 'action' => 'deposit')),
|
||||
);
|
||||
|
||||
|
||||
@@ -29,21 +32,323 @@ class TransactionsController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function all() { $this->jqGridView('All Transactions', 'all'); }
|
||||
function all() { $this->gridView('All Transactions', 'all'); }
|
||||
function invoice() { $this->gridView('Invoices'); }
|
||||
function receipt() { $this->gridView('Receipts'); }
|
||||
function deposit() { $this->gridView('Deposits'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
return parent::gridDataTables($params, $model);
|
||||
}
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$link = $this->gridDataCountTables($params, $model);
|
||||
$link['link']['StatementEntry'] = array('fields' => array());
|
||||
$link['link']['DepositTender'] = array('fields' => array());
|
||||
return $link;
|
||||
}
|
||||
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
//$fields[] = 'COUNT(StatementEntry.id) AS entries';
|
||||
$fields[] = ("IF(Transaction.type = 'DEPOSIT'," .
|
||||
" COUNT(DepositTender.id)," .
|
||||
" COUNT(StatementEntry.id)) AS entries");
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
|
||||
if (in_array($params['action'], array('invoice', 'receipt', 'deposit')))
|
||||
$conditions[] = array('Transaction.type' => strtoupper($params['action']));
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Transaction'] = array('id');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: postReceipt
|
||||
* - handles the creation of a receipt
|
||||
*/
|
||||
|
||||
function postReceipt() {
|
||||
if (!$this->RequestHandler->isPost()) {
|
||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($this->data['Entry'] AS &$entry) {
|
||||
$entry['Tender'] = $entry['type'][$entry['tender_type_id']];
|
||||
unset($entry['type']);
|
||||
unset($entry['tender_type_id']);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: postDeposit
|
||||
* - handles the creation of a deposit transaction
|
||||
*/
|
||||
|
||||
function postDeposit() {
|
||||
if (!$this->RequestHandler->isPost()) {
|
||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||
return;
|
||||
}
|
||||
|
||||
//pr($this->data);
|
||||
|
||||
// Go through each type of tender presented to the user
|
||||
// Determine which are to be deposited, and which are to
|
||||
// have their corresponding account ledgers closed.
|
||||
$deposit_tender_ids = array();
|
||||
$deposit_type_ids = array();
|
||||
$close_type_ids = array();
|
||||
foreach ($this->data['TenderType'] AS $type_id => $type) {
|
||||
$type['items'] = unserialize($type['items']);
|
||||
if (empty($type['selection']) ||
|
||||
$type['selection'] === 'none' ||
|
||||
($type['selection'] === 'subset' && count($type['items']) == 0))
|
||||
continue;
|
||||
|
||||
// The deposit includes either the whole type, or just certain tenders
|
||||
if ($type['selection'] === 'all')
|
||||
$deposit_type_ids[] = $type_id;
|
||||
else
|
||||
$deposit_tender_ids = array_merge($deposit_tender_ids, $type['items']);
|
||||
|
||||
// Should we close the ledger for this tender type?
|
||||
// First, the user would have to request that we do so,
|
||||
// but additionally, we shouldn't close a ledger unless
|
||||
// all the tenders are included in this deposit. That
|
||||
// doesn't guarantee that the ledger has a zero balance,
|
||||
// but it does carry the balance forward, and a total
|
||||
// deposit would imply a fresh start, so go for it.
|
||||
if (!empty($type['close']) && $type['selection'] === 'all')
|
||||
$close_type_ids[] = $type_id;
|
||||
}
|
||||
|
||||
// Make sure we actually have something to deposit
|
||||
if (empty($deposit_type_ids) && empty($deposit_tender_ids)) {
|
||||
$this->Session->setFlash(__('Nothing to Deposit', true));
|
||||
$this->redirect(array('controller' => 'tenders', 'action'=>'deposit'));
|
||||
}
|
||||
|
||||
// Build up a set of conditions based on user selection
|
||||
$deposit_conditions = array();
|
||||
if (!empty($deposit_type_ids))
|
||||
$deposit_conditions[] = array('TenderType.id' => $deposit_type_ids);
|
||||
if (!empty($deposit_tender_ids))
|
||||
$deposit_conditions[] = array('DepositTender.id' => $deposit_tender_ids);
|
||||
|
||||
// Add in confirmation that items have not already been deposited
|
||||
$deposit_conditions =
|
||||
array(array('DepositTender.deposit_transaction_id' => null),
|
||||
array('OR' => $deposit_conditions));
|
||||
|
||||
// Lookup the items to be deposited
|
||||
$tenders = $this->Transaction->DepositTender->find
|
||||
('all',
|
||||
array('contain' => array('TenderType', 'LedgerEntry'),
|
||||
'conditions' => $deposit_conditions,
|
||||
));
|
||||
|
||||
// Build the deposit transaction
|
||||
$deposit = array('Transaction' => array(), 'Entry' => array());
|
||||
foreach ($tenders AS $tender) {
|
||||
$deposit['Entry'][] =
|
||||
array('tender_id' => $tender['DepositTender']['id'],
|
||||
'account_id' => $tender['LedgerEntry']['account_id'],
|
||||
'amount' => $tender['LedgerEntry']['amount'],
|
||||
);
|
||||
}
|
||||
|
||||
//pr(compact('deposit_type_ids', 'deposit_tender_ids', 'close_type_ids', 'deposit_conditions', 'deposit'));
|
||||
|
||||
// OK, perform the deposit and associated accounting
|
||||
$result = $this->Transaction->addDeposit
|
||||
($deposit, $this->data['Deposit']['Account']['id']);
|
||||
//pr(compact('deposit', 'result'));
|
||||
|
||||
// Close any ledgers necessary
|
||||
if (!empty($close_type_ids)) {
|
||||
// Find the accounts associated with the types to close ...
|
||||
$accounts = $this->Transaction->DepositTender->find
|
||||
('all',
|
||||
array('contain' => array('TenderType.account_id'),
|
||||
'conditions' => array(array('TenderType.id' => $close_type_ids)),
|
||||
));
|
||||
|
||||
// ... and close them
|
||||
$this->Transaction->Account->closeCurrentLedgers
|
||||
(array_map(create_function('$item', 'return $item["TenderType"]["account_id"];'), $accounts));
|
||||
}
|
||||
|
||||
// Look out for errors
|
||||
if ($result['error']) {
|
||||
$this->Session->setFlash(__('Unable to Create Deposit', true));
|
||||
$this->redirect(array('controller' => 'tenders', 'action'=>'deposit'));
|
||||
}
|
||||
|
||||
// Present the deposit slip to the user
|
||||
$this->redirect(array('controller' => 'transactions',
|
||||
'action' => 'deposit_slip',
|
||||
$result['transaction_id']));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: postWriteOff
|
||||
* - handles the write off of bad debt
|
||||
*/
|
||||
|
||||
function postWriteOff() {
|
||||
if (!$this->RequestHandler->isPost()) {
|
||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->data;
|
||||
$data['Entry'][0]['account_id'] =
|
||||
$this->Transaction->Account->badDebtAccountID();
|
||||
|
||||
if (empty($data['Customer']['id']))
|
||||
$data['Customer']['id'] = null;
|
||||
if (empty($data['Lease']['id']))
|
||||
$data['Lease']['id'] = null;
|
||||
|
||||
pr(compact('data'));
|
||||
|
||||
if (!$this->Transaction->addReceipt($data,
|
||||
$data['Customer']['id'],
|
||||
$data['Lease']['id'])) {
|
||||
$this->Session->setFlash("WRITE OFF FAILED", true);
|
||||
// REVISIT <AP> 20090706:
|
||||
// Until we can work out the session problems,
|
||||
// just die.
|
||||
die("<H1>WRITE-OFF FAILED</H1>");
|
||||
}
|
||||
|
||||
$this->render('/fake');
|
||||
|
||||
// Return to viewing the lease/customer
|
||||
if (empty($data['Lease']['id']))
|
||||
$this->redirect(array('controller' => 'customers',
|
||||
'action' => 'view',
|
||||
$data['Customer']['id']));
|
||||
else
|
||||
$this->redirect(array('controller' => 'leases',
|
||||
'action' => 'view',
|
||||
$data['Lease']['id']));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: postRefund
|
||||
* - handles issuing a customer refund
|
||||
*/
|
||||
|
||||
function postRefund() {
|
||||
if (!$this->RequestHandler->isPost()) {
|
||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->data;
|
||||
if (empty($data['Customer']['id']))
|
||||
$data['Customer']['id'] = null;
|
||||
if (empty($data['Lease']['id']))
|
||||
$data['Lease']['id'] = null;
|
||||
|
||||
if (!$this->Transaction->addRefund($data,
|
||||
$data['Customer']['id'],
|
||||
$data['Lease']['id'])) {
|
||||
$this->Session->setFlash("REFUND FAILED", true);
|
||||
// REVISIT <AP> 20090706:
|
||||
// Until we can work out the session problems,
|
||||
// just die.
|
||||
die("<H1>REFUND FAILED</H1>");
|
||||
}
|
||||
|
||||
$this->render('/fake');
|
||||
|
||||
// Return to viewing the lease/customer
|
||||
if (empty($data['Lease']['id']))
|
||||
$this->redirect(array('controller' => 'customers',
|
||||
'action' => 'view',
|
||||
$data['Customer']['id']));
|
||||
else
|
||||
$this->redirect(array('controller' => 'leases',
|
||||
'action' => 'view',
|
||||
$data['Lease']['id']));
|
||||
}
|
||||
|
||||
|
||||
@@ -64,225 +369,95 @@ class TransactionsController extends AppController {
|
||||
('first',
|
||||
array('contain' =>
|
||||
array(// Models
|
||||
'LedgerEntry' => array('fields' => array('LedgerEntry.id',
|
||||
'LedgerEntry.amount',
|
||||
'LedgerEntry.comment'),
|
||||
//Models
|
||||
'Account' =>
|
||||
array('fields' => array('Account.id',
|
||||
'Account.name'),
|
||||
),
|
||||
|
||||
'DebitLedger' => array
|
||||
('fields' => array('DebitLedger.id', 'DebitLedger.sequence'),
|
||||
'Account' => array
|
||||
('fields' => array('Account.id', 'Account.name')),
|
||||
),
|
||||
|
||||
'CreditLedger' => array
|
||||
('fields' => array('CreditLedger.id', 'CreditLedger.sequence'),
|
||||
'Account' => array
|
||||
('fields' => array('Account.id', 'Account.name')),
|
||||
),
|
||||
),
|
||||
'Ledger' =>
|
||||
array('fields' => array('Ledger.id',
|
||||
'Ledger.name'),
|
||||
),
|
||||
),
|
||||
'conditions' => array('Transaction.id' => $id),
|
||||
));
|
||||
|
||||
// Figure out the transaction total
|
||||
$total = 0;
|
||||
foreach($transaction['LedgerEntry'] AS $entry)
|
||||
$total += $entry['amount'];
|
||||
if ($transaction['Transaction']['type'] === 'DEPOSIT') {
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'View Slip', 'url' => array('action' => 'deposit_slip', $id));
|
||||
}
|
||||
|
||||
// OK, prepare to render.
|
||||
$title = 'Transaction #' . $transaction['Transaction']['id'];
|
||||
$this->set(compact('transaction', 'title', 'total'));
|
||||
$this->set(compact('transaction', 'title'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: postReceipt
|
||||
* - handles the creation of a payment receipt
|
||||
* action: deposit_slip
|
||||
* - Special presentation
|
||||
* Processes the user input and updates the database
|
||||
*/
|
||||
|
||||
function postReceipt() {
|
||||
$this->autoRender = false;
|
||||
function deposit_slip($id) {
|
||||
// Build a container for the deposit slip data
|
||||
$deposit = array('types' => array());
|
||||
|
||||
if (!$this->RequestHandler->isPost()) {
|
||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||
return;
|
||||
}
|
||||
//pr(array('thisdata' => $this->data));
|
||||
$this->id = $id;
|
||||
$deposit +=
|
||||
$this->Transaction->find('first', array('contain' => false));
|
||||
|
||||
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'];
|
||||
}
|
||||
else {
|
||||
// Payment by Unit, Lease, etc
|
||||
$ledger_id = 0;
|
||||
// Get a summary of all forms of tender in the deposit
|
||||
$result = $this->Transaction->find
|
||||
('all',
|
||||
array('link' => array('DepositTender' =>
|
||||
array('fields' => array(),
|
||||
'TenderType',
|
||||
'LedgerEntry' =>
|
||||
array('fields' => array()))),
|
||||
'fields' => array(//'TenderType.id', 'TenderType.name',
|
||||
"COUNT(DepositTender.id) AS 'count'",
|
||||
"SUM(LedgerEntry.amount) AS 'total'"),
|
||||
//'conditions' => array(array('DepositTender.deposit_transaction_id' => $id)),
|
||||
'conditions' => array(array('Transaction.id' => $id)),
|
||||
'group' => 'TenderType.id',
|
||||
));
|
||||
|
||||
if (empty($result)) {
|
||||
die();
|
||||
$this->Session->setFlash(__('Invalid Deposit.', true));
|
||||
$this->redirect(array('action'=>'deposit'));
|
||||
}
|
||||
|
||||
$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;
|
||||
// Add the summary to our deposit slip data container
|
||||
foreach ($result AS $type) {
|
||||
$deposit['types'][$type['TenderType']['id']] =
|
||||
$type['TenderType'] + $type[0];
|
||||
}
|
||||
|
||||
// For each form of tender in the deposit, get the deposit items
|
||||
/* foreach ($deposit['types'] AS $type_id => &$type) { */
|
||||
/* $type['entries'] = $this->Transaction->DepositTender->find */
|
||||
/* ('all', */
|
||||
/* array('contain' => array('Customer', 'LedgerEntry'), */
|
||||
/* 'conditions' => array(array('DepositTender.deposit_transaction_id' => $id), */
|
||||
/* array('DepositTender.tender_type_id' => $type_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'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'View Transaction', 'url' => array('action' => 'view', $id));
|
||||
|
||||
$title = 'Deposit Slip';
|
||||
$this->set(compact('title', 'deposit'));
|
||||
$this->render('deposit_slip');
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,48 +30,63 @@ class UnitsController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function unavailable() { $this->jqGridView('Unavailable Units'); }
|
||||
function vacant() { $this->jqGridView('Vacant Units'); }
|
||||
function occupied() { $this->jqGridView('Occupied Units'); }
|
||||
function all() { $this->jqGridView('All Units', 'all'); }
|
||||
function unavailable() { $this->gridView('Unavailable Units'); }
|
||||
function vacant() { $this->gridView('Vacant Units'); }
|
||||
function occupied() { $this->gridView('Occupied Units'); }
|
||||
function all() { $this->gridView('All Units', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: jqGridData
|
||||
* - With the application controller handling the jqGridData action,
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function jqGridDataSetup(&$params) {
|
||||
parent::jqGridDataSetup($params);
|
||||
function gridDataSetup(&$params) {
|
||||
parent::gridDataSetup($params);
|
||||
if (!isset($params['action']))
|
||||
$params['action'] = 'all';
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
$link = array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'UnitSize' => array('fields' => array('name')),
|
||||
),
|
||||
);
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
return array
|
||||
('link' => array('UnitSize' => array('fields' => array('id', 'name')),
|
||||
'CurrentLease' => array('fields' => array('id'))));
|
||||
|
||||
if ($params['action'] === 'occupied')
|
||||
$link['Lease'] = array('fields' => array(),
|
||||
// Models
|
||||
'Contact' => array('fields' => array('display_name'),
|
||||
//'type' => 'LEFT',
|
||||
),
|
||||
);
|
||||
/* if ($params['action'] === 'occupied') */
|
||||
/* $link['Lease'] = array('fields' => array(), */
|
||||
/* // Models */
|
||||
/* 'Contact' => array('fields' => array('display_name'), */
|
||||
/* //'type' => 'LEFT', */
|
||||
/* ), */
|
||||
/* ); */
|
||||
|
||||
}
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$link = $this->gridDataCountTables($params, $model);
|
||||
$link['link']['CurrentLease']['StatementEntry'] = array('fields' => array());
|
||||
return $link;
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
/* function gridDataTables(&$params, &$model) { */
|
||||
/* return array */
|
||||
/* ('link' => array('Unit' => array('fields' => array('Unit.id', 'Unit.name')), */
|
||||
/* 'Customer' => array('fields' => array('Customer.id', 'Customer.name')))); */
|
||||
/* } */
|
||||
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
|
||||
return array_merge($fields,
|
||||
$this->Unit->Lease->StatementEntry->chargeDisbursementFields(true));
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'unavailable') {
|
||||
$conditions[] = $this->Unit->conditionUnavailable();
|
||||
@@ -82,15 +97,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') {
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
// Instead of sorting by name, sort by defined order
|
||||
if ($index === 'Unit.name')
|
||||
$index = 'Unit.sort_order';
|
||||
|
||||
$order = array();
|
||||
$order[] = parent::gridDataOrder($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::gridDataOrder($params, $model,
|
||||
'Unit.sort_order', $direction);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Unit'] = array('name');
|
||||
$links['UnitSize'] = array('name');
|
||||
return parent::gridDataPostProcessLinks($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');
|
||||
}
|
||||
|
||||
|
||||
@@ -133,14 +228,40 @@ class UnitsController extends AppController {
|
||||
$stats['CurrentLease']['balance'];
|
||||
|
||||
// Figure out the total security deposit for the current lease.
|
||||
$deposits = $this->Unit->Lease->findSecurityDeposits($unit['CurrentLease']['id']);
|
||||
$outstanding_deposit = $deposits['summary']['balance'];
|
||||
$deposits = $this->Unit->Lease->securityDeposits($unit['CurrentLease']['id']);
|
||||
$outstanding_deposit = $this->Unit->Lease->securityDepositBalance($unit['CurrentLease']['id']);
|
||||
}
|
||||
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Move-Out', 'url' => array('controller' => 'units', 'action' => 'move-out'));
|
||||
array('name' => 'Edit', 'url' => array('action' => 'edit',
|
||||
$id));
|
||||
|
||||
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' => 'Charge', 'url' => array('controller' => 'leases',
|
||||
'action' => 'invoice',
|
||||
$unit['CurrentLease']['id']));
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Payment', 'url' => array('controller' => 'customers',
|
||||
'action' => 'receipt',
|
||||
$unit['CurrentLease']['customer_id']));
|
||||
}
|
||||
|
||||
// Prepare to render.
|
||||
$title = 'Unit ' . $unit['Unit']['name'];
|
||||
@@ -148,4 +269,71 @@ class UnitsController extends AppController {
|
||||
'outstanding_balance',
|
||||
'outstanding_deposit'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: edit
|
||||
* - Edit unit information
|
||||
*/
|
||||
|
||||
function edit($id = null) {
|
||||
if (isset($this->data)) {
|
||||
// Check to see if the operation was cancelled.
|
||||
if (isset($this->params['form']['cancel'])) {
|
||||
if (empty($this->data['Unit']['id']))
|
||||
$this->redirect(array('action'=>'index'));
|
||||
|
||||
$this->redirect(array('action'=>'view', $this->data['Unit']['id']));
|
||||
}
|
||||
|
||||
// Make sure we have unit data
|
||||
if (empty($this->data['Unit']))
|
||||
$this->redirect(array('action'=>'index'));
|
||||
|
||||
// Make sure we have a rental rate
|
||||
if (empty($this->data['Unit']['rent']))
|
||||
$this->redirect(array('action'=>'view', $this->data['Unit']['id']));
|
||||
|
||||
// Save the unit and all associated data
|
||||
$this->Unit->create();
|
||||
$this->Unit->id = $this->data['Unit']['id'];
|
||||
if (!$this->Unit->save($this->data, false)) {
|
||||
$this->Session->setFlash("UNIT SAVE FAILED", true);
|
||||
pr("UNIT SAVE FAILED");
|
||||
}
|
||||
|
||||
$this->redirect(array('action'=>'view', $this->Unit->id));
|
||||
|
||||
// For debugging, only if the redirects above have been
|
||||
// commented out, otherwise this section isn't reached.
|
||||
$this->render('/fake');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
$this->data = $this->Unit->findById($id);
|
||||
$title = 'Unit ' . $this->data['Unit']['name'] . " : Edit";
|
||||
}
|
||||
else {
|
||||
$title = "Enter New Unit";
|
||||
$this->data = array();
|
||||
}
|
||||
|
||||
$statusEnums = $this->Unit->allowedStatusSet($id);
|
||||
$statusEnums = array_combine(array_keys($statusEnums),
|
||||
array_keys($statusEnums));
|
||||
$this->set(compact('statusEnums'));
|
||||
|
||||
$unit_sizes = $this->Unit->UnitSize->find
|
||||
('list', array('order' => array('unit_type_id', 'width', 'depth', 'id')));
|
||||
$this->set(compact('unit_sizes'));
|
||||
|
||||
// Prepare to render.
|
||||
pr($this->data);
|
||||
$this->set(compact('title'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<?php
|
||||
class Account extends AppModel {
|
||||
|
||||
var $name = 'Account';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'name' => array('notempty'),
|
||||
'external_name' => array('notempty')
|
||||
);
|
||||
|
||||
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(array('CurrentLedger.close_transaction_id' => null)),
|
||||
'conditions' => array('CurrentLedger.close_transaction_id IS NULL'),
|
||||
),
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'Ledger',
|
||||
'LedgerEntry',
|
||||
);
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ class Account extends AppModel {
|
||||
else
|
||||
$fund = $this->fundamentalType($id_or_type);
|
||||
|
||||
if ($fund == 'debit')
|
||||
if (strtolower($fund) == 'debit')
|
||||
return 'credit';
|
||||
|
||||
return 'debit';
|
||||
@@ -82,26 +82,172 @@ 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: debitCreditFields
|
||||
* - Returns the fields necessary to determine whether the queried
|
||||
* entries are a debit, or a credit, and also the effect each have
|
||||
* on the overall balance of the account.
|
||||
*/
|
||||
|
||||
function debitCreditFields($sum = false, $balance = true,
|
||||
$entry_name = 'LedgerEntry', $account_name = 'Account') {
|
||||
return $this->LedgerEntry->debitCreditFields
|
||||
($sum, $balance, $entry_name, $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 accountPayableAccountID() { return $this->nameToID('A/P'); }
|
||||
function cashAccountID() { return $this->nameToID('Cash'); }
|
||||
function checkAccountID() { return $this->nameToID('Check'); }
|
||||
function moneyOrderAccountID() { return $this->nameToID('Money Order'); }
|
||||
function concessionAccountID() { return $this->nameToID('Concession'); }
|
||||
function waiverAccountID() { return $this->nameToID('Waiver'); }
|
||||
function pettyCashAccountID() { return $this->nameToID('Petty Cash'); }
|
||||
function invoiceAccountID() { return $this->nameToID('Invoice'); }
|
||||
function receiptAccountID() { return $this->nameToID('Receipt'); }
|
||||
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;
|
||||
$accounts = $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;
|
||||
|
||||
// Rearrange to be of the form (id => name)
|
||||
$rel_accounts = array();
|
||||
foreach ($accounts AS $acct) {
|
||||
$rel_accounts[$acct['Account']['id']] = $acct['Account']['name'];
|
||||
}
|
||||
|
||||
return $rel_accounts;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: xxxAccounts
|
||||
* - Returns an array of accounts suitable for activity xxx
|
||||
*/
|
||||
|
||||
function invoiceAccounts() {
|
||||
return $this->relatedAccounts('invoices', array('order' => 'name'));
|
||||
}
|
||||
|
||||
function receiptAccounts() {
|
||||
return $this->relatedAccounts('receipts', array('order' => 'name'));
|
||||
}
|
||||
|
||||
function depositAccounts() {
|
||||
return $this->relatedAccounts('deposits', array('order' => 'name'));
|
||||
}
|
||||
|
||||
function refundAccounts() {
|
||||
return $this->relatedAccounts('refunds', array('order' => 'name'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: collectableAccounts
|
||||
* - Returns an array of accounts suitable to show income collection
|
||||
*/
|
||||
|
||||
function collectableAccounts() {
|
||||
$accounts = $this->receiptAccounts();
|
||||
|
||||
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'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
@@ -145,173 +291,44 @@ class Account extends AppModel {
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findLedgerEntries
|
||||
* function: closeCurrentLedger
|
||||
* - Closes the current account ledger, and opens a new one
|
||||
* with the old balance carried forward.
|
||||
*/
|
||||
function closeCurrentLedgers($ids = null) {
|
||||
|
||||
$this->cacheQueries = true;
|
||||
$account = $this->find('all', array
|
||||
('contain' => array('CurrentLedger.id'),
|
||||
'fields' => array(),
|
||||
'conditions' => (empty($ids)
|
||||
? array()
|
||||
: array(array('Account.id' => $ids)))
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
//pr(compact('id', 'account'));
|
||||
|
||||
$ledger_ids = array();
|
||||
foreach ($account AS $acct)
|
||||
$ledger_ids[] = $acct['CurrentLedger']['id'];
|
||||
|
||||
return $this->Ledger->closeLedgers($ledger_ids);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: ledgerEntries
|
||||
* - Returns an array of ledger entries that belong to the given
|
||||
* account, either just from the current ledger, or from all ledgers.
|
||||
*/
|
||||
function findLedgerEntries($id, $all = false, $cond = null, $link = null) {
|
||||
/* pr(array('function' => 'Account::findLedgerEntries', */
|
||||
/* 'args' => compact('id', 'all', 'cond', 'link'), */
|
||||
/* )); */
|
||||
|
||||
$entries = array();
|
||||
foreach ($this->ledgers($id, $all) AS $ledger_id) {
|
||||
$ledger_entries = $this->Ledger->findLedgerEntries
|
||||
($ledger_id, $this->type($id), $cond, $link);
|
||||
$entries = array_merge($entries, $ledger_entries);
|
||||
}
|
||||
|
||||
$stats = $this->stats($id, $all, $cond);
|
||||
$entries = array('Entries' => $entries,
|
||||
'summary' => $stats['Ledger']);
|
||||
|
||||
/* pr(array('function' => 'Account::findLedgerEntries', */
|
||||
/* 'args' => compact('id', 'all', 'cond', 'link'), */
|
||||
/* 'vars' => compact('stats'), */
|
||||
/* 'return' => compact('entries'), */
|
||||
/* )); */
|
||||
|
||||
return $entries;
|
||||
function ledgerEntries($id, $all = false, $cond = null, $link = null) {
|
||||
$ledgers = $this->ledgers($id, $all);
|
||||
return $this->Ledger->ledgerEntries($ledgers, $cond, $link);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findLedgerEntriesRelatedToAccount
|
||||
* - Returns an array of ledger entries that belong to the given
|
||||
* account, and are related to a specific account, either just from
|
||||
* the current ledger, or from all ledgers.
|
||||
*/
|
||||
function findLedgerEntriesRelatedToAccount($id, $rel_ids, $all = false, $cond = null, $link = null) {
|
||||
/* pr(array('function' => 'Account::findLedgerEntriesRelatedToAccount', */
|
||||
/* 'args' => compact('id', 'rel_ids', 'all', 'cond', 'link'), */
|
||||
/* )); */
|
||||
|
||||
if (!isset($cond))
|
||||
$cond = array();
|
||||
if (!is_array($rel_ids))
|
||||
$rel_ids = array($rel_ids);
|
||||
|
||||
$ledger_ids = array();
|
||||
foreach ($rel_ids AS $rel_id)
|
||||
$ledger_ids = array_merge($ledger_ids, $this->ledgers($rel_id));
|
||||
|
||||
array_push($cond, $this->Ledger->LedgerEntry->conditionEntryAsCreditOrDebit($ledger_ids));
|
||||
$entries = $this->findLedgerEntries($id, $all, $cond, $link);
|
||||
|
||||
/* pr(array('function' => 'Account::findLedgerEntriesRelatedToAccount', */
|
||||
/* 'args' => compact('id', 'relid', 'all', 'cond', 'link'), */
|
||||
/* 'vars' => compact('ledger_ids'), */
|
||||
/* 'return' => compact('entries'), */
|
||||
/* )); */
|
||||
return $entries;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findUnreconciledLedgerEntries
|
||||
* - Returns ledger entries that are not yet reconciled
|
||||
* (such as charges not paid).
|
||||
*/
|
||||
|
||||
function findUnreconciledLedgerEntries($id = null, $fundamental_type = null, $cond = null) {
|
||||
if (!isset($cond))
|
||||
$cond = array();
|
||||
$cond[] = array('Account.id' => $id);
|
||||
|
||||
foreach (($fundamental_type
|
||||
? array($fundamental_type)
|
||||
: array('debit', 'credit')) AS $fund) {
|
||||
$ucfund = ucfirst($fund);
|
||||
$unreconciled[$fund]['entry'] = $this->find
|
||||
('all', array
|
||||
('link' => array
|
||||
('Ledger' => array
|
||||
('fields' => array(),
|
||||
"LedgerEntry" => array
|
||||
('class' => "{$ucfund}LedgerEntry",
|
||||
'fields' => array('id', 'amount'),
|
||||
"ReconciliationLedgerEntry" => array
|
||||
('class' => "{$ucfund}ReconciliationLedgerEntry",
|
||||
'fields' => array
|
||||
("COALESCE(SUM(Reconciliation.amount),0) AS 'reconciled'",
|
||||
"LedgerEntry.amount - COALESCE(SUM(Reconciliation.amount),0) AS 'balance'",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'group' => ("LedgerEntry.id" .
|
||||
" HAVING LedgerEntry.amount" .
|
||||
" <> COALESCE(SUM(Reconciliation.amount),0)"),
|
||||
'conditions' => $cond,
|
||||
'fields' => array(),
|
||||
));
|
||||
$balance = 0;
|
||||
foreach ($unreconciled[$fund]['entry'] AS &$entry) {
|
||||
$entry = array_merge(array_diff_key($entry["LedgerEntry"], array(0=>true)),
|
||||
$entry[0]);
|
||||
$balance += $entry['balance'];
|
||||
}
|
||||
$unreconciled[$fund]['balance'] = $balance;
|
||||
}
|
||||
|
||||
return $unreconciled;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reconcileNewLedgerEntry
|
||||
* - Returns which ledger entries a new credit/debit would
|
||||
* reconcile, and how much.
|
||||
*
|
||||
* - REVISIT <AP> 20090617
|
||||
* This should be subject to different algorithms, such
|
||||
* as apply to oldest charges first, newest first, to fees
|
||||
* before rent, etc. Until we get there, I'll hardcode
|
||||
* whatever algorithm is simplest.
|
||||
*/
|
||||
|
||||
function reconcileNewLedgerEntry($id, $fundamental_type, $amount, $cond = null) {
|
||||
$ofund = $this->fundamentalOpposite($fundamental_type);
|
||||
$unreconciled = array($ofund => array('entry'=>array(), 'balance'=>0));
|
||||
$applied = 0;
|
||||
|
||||
// if there is no money in the entry, it can reconcile nothing
|
||||
// don't bother wasting time sifting ledger entries.
|
||||
if ($amount > 0) {
|
||||
$unreconciled = $this->findUnreconciledLedgerEntries($id, $ofund, $cond);
|
||||
|
||||
foreach ($unreconciled[$ofund]['entry'] AS $i => &$entry) {
|
||||
// Determine if amount is sufficient to cover the entry
|
||||
if ($amount > $entry['balance'])
|
||||
$apply = $entry['balance'];
|
||||
elseif ($amount > 0)
|
||||
$apply = $amount;
|
||||
else {
|
||||
unset($unreconciled[$ofund]['entry'][$i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$entry['applied'] = $apply;
|
||||
$entry['reconciled'] += $apply;
|
||||
$entry['balance'] -= $apply;
|
||||
$applied += $apply;
|
||||
$amount -= $apply;
|
||||
}
|
||||
}
|
||||
|
||||
$unreconciled[$ofund]['unapplied'] = $amount;
|
||||
$unreconciled[$ofund]['applied'] = $applied;
|
||||
$unreconciled[$ofund]['balance'] -= $applied;
|
||||
return $unreconciled;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -319,37 +336,16 @@ class Account extends AppModel {
|
||||
* - Returns summary data from the requested account.
|
||||
*/
|
||||
|
||||
function stats($id = null, $all = false, $cond = null) {
|
||||
function stats($id = null, $all = false, $query = null) {
|
||||
if (!$id)
|
||||
return null;
|
||||
|
||||
// All old, closed ledgers MUST balance to 0.
|
||||
// However, the user may want the ENTIRE running totals,
|
||||
// (not just the balance), so we may have to query all
|
||||
// ledgers, as dictated by the $all parameter.
|
||||
$this->queryInit($query);
|
||||
$query['link'] = array('Account' => $query['link']);
|
||||
|
||||
$account = $this->find('first',
|
||||
array('contain' =>
|
||||
($all
|
||||
? array('Ledger' => array
|
||||
('fields' => array('id')))
|
||||
: array('CurrentLedger' => array
|
||||
('fields' => array('id')))
|
||||
),
|
||||
'conditions' => array
|
||||
(array('Account.id' => $id))
|
||||
));
|
||||
|
||||
$stats = array();
|
||||
if ($all) {
|
||||
foreach ($account['Ledger'] AS $ledger)
|
||||
$this->statsMerge($stats['Ledger'],
|
||||
$this->Ledger->stats($ledger['id'], $cond));
|
||||
}
|
||||
else {
|
||||
$stats['Ledger'] =
|
||||
$this->Ledger->stats($account['CurrentLedger']['id'], $cond);
|
||||
}
|
||||
foreach ($this->ledgers($id, $all) AS $ledger)
|
||||
$this->statsMerge($stats['Ledger'],
|
||||
$this->Ledger->stats($ledger, $query));
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,28 @@ class LinkableBehavior extends ModelBehavior {
|
||||
|
||||
protected $_defaults = array('type' => 'LEFT');
|
||||
|
||||
function pr($lev, $mixed) {
|
||||
if ($lev >= 3)
|
||||
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,230 +130,357 @@ class LinkableBehavior extends ModelBehavior {
|
||||
}
|
||||
|
||||
public function beforeFind(&$Model, $query) {
|
||||
/* pr("Linkable::beforeFind() begin"); pr($query); */
|
||||
if (isset($query[$this->_key])) {
|
||||
$optionsDefaults = $this->_defaults + array('reference' =>
|
||||
array('class' => $Model->alias,
|
||||
'alias' => $Model->alias),
|
||||
$this->_key => array());
|
||||
$optionsKeys = $this->_options + array($this->_key => true);
|
||||
if (!isset($query['fields']) || $query['fields'] === true) {
|
||||
//$query['fields'] = array_keys($Model->_schema);
|
||||
$query['fields'] = $Model->getDataSource()->fields($Model);
|
||||
} elseif (!is_array($query['fields'])) {
|
||||
$query['fields'] = array($query['fields']);
|
||||
}
|
||||
$query = am(array('joins' => array()), $query, array('recursive' => -1));
|
||||
$iterators[] = $query[$this->_key];
|
||||
$cont = 0;
|
||||
do {
|
||||
$iterator = $iterators[$cont];
|
||||
$defaults = $optionsDefaults;
|
||||
if (isset($iterator['defaults'])) {
|
||||
$defaults = array_merge($defaults, $iterator['defaults']);
|
||||
unset($iterator['defaults']);
|
||||
}
|
||||
$iterations = Set::normalize($iterator);
|
||||
/* pr(array('checkpoint' => 'Iterations', compact('iterations'))); */
|
||||
foreach ($iterations as $alias => $options) {
|
||||
if (is_null($options)) {
|
||||
$options = array();
|
||||
}
|
||||
$options = am($defaults, compact('alias'), $options);
|
||||
if (empty($options['alias'])) {
|
||||
throw new InvalidArgumentException(sprintf('%s::%s must receive aliased links', get_class($this), __FUNCTION__));
|
||||
}
|
||||
if (!isset($query[$this->_key]))
|
||||
return $query;
|
||||
|
||||
if (empty($options['class']))
|
||||
$options['class'] = $alias;
|
||||
|
||||
/* pr(array('checkpoint' => 'Begin Model Work', compact('alias', 'options'))); */
|
||||
|
||||
$modelClass = $options['class'];
|
||||
$modelAlias = $options['alias'];
|
||||
$referenceClass = $options['reference']['class'];
|
||||
$referenceAlias = $options['reference']['alias'];
|
||||
|
||||
$_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 .')'))); */
|
||||
|
||||
$db =& $_Model->getDataSource();
|
||||
|
||||
$associatedThroughReference = 0;
|
||||
$association = null;
|
||||
|
||||
// Figure out how these two models are related, creating
|
||||
// a relationship if one doesn't otherwise already exists.
|
||||
if (($associations = $Reference->getAssociated()) &&
|
||||
isset($associations[$_Model->alias])) {
|
||||
/* pr("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"); */
|
||||
$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"); */
|
||||
$type = 'belongsTo';
|
||||
$_Model->bind($Reference->alias);
|
||||
// Grab the association now, since we'll unbind in a moment.
|
||||
$association = $_Model->{$type}[$Reference->alias];
|
||||
$_Model->unbindModel(array('belongsTo' => array($Reference->alias)));
|
||||
}
|
||||
|
||||
// Determine which model holds the foreign key
|
||||
if (($type === 'hasMany' || $type === 'hasOne') ^ $associatedThroughReference) {
|
||||
$primaryAlias = $referenceAlias;
|
||||
$foreignAlias = $modelAlias;
|
||||
$primaryModel = $Reference;
|
||||
$foreignModel = $_Model;
|
||||
} else {
|
||||
$primaryAlias = $modelAlias;
|
||||
$foreignAlias = $referenceAlias;
|
||||
$primaryModel = $_Model;
|
||||
$foreignModel = $Reference;
|
||||
}
|
||||
|
||||
if ($associatedThroughReference)
|
||||
$associationAlias = $referenceAlias;
|
||||
else
|
||||
$associationAlias = $modelAlias;
|
||||
|
||||
$this->recursive_array_replace("%{MODEL_ALIAS}",
|
||||
$associationAlias,
|
||||
$association['conditions']);
|
||||
|
||||
/* pr(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']);
|
||||
|
||||
$Link =& $_Model->{$linkClass};
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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'];
|
||||
|
||||
}
|
||||
if (empty($options['table'])) {
|
||||
$options['table'] = $db->fullTableName($_Model, true);
|
||||
}
|
||||
|
||||
if (!isset($options['fields']) || !is_array($options['fields']))
|
||||
$options['fields'] = $db->fields($_Model, $modelAlias);
|
||||
elseif (!empty($options['fields']))
|
||||
$options['fields'] = $db->fields($_Model, $modelAlias, $options['fields']);
|
||||
|
||||
$query['fields'] = array_merge($query['fields'], $options['fields'],
|
||||
(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('reference' =>
|
||||
array('class' => $modelClass,
|
||||
'alias' => $modelAlias))));
|
||||
}
|
||||
$query['joins'][] = array_intersect_key($options, array('type' => true, 'alias' => true, 'table' => true, 'joins' => true, 'conditions' => true));
|
||||
}
|
||||
++$cont;
|
||||
$notDone = isset($iterators[$cont]);
|
||||
} while ($notDone);
|
||||
if (!isset($query['fields']) || $query['fields'] === true) {
|
||||
$query['fields'] = $Model->getDataSource()->fields($Model);
|
||||
} elseif (!is_array($query['fields'])) {
|
||||
$query['fields'] = array($query['fields']);
|
||||
}
|
||||
/* pr(array('checkpoint' => 'Linkable::beforeFind() end', */
|
||||
/* compact('query'))); */
|
||||
return $query;
|
||||
$query = am(array('joins' => array()), $query, array('recursive' => -1));
|
||||
|
||||
$reference = array('class' => $Model->alias,
|
||||
'alias' => $Model->alias,
|
||||
);
|
||||
|
||||
$result = array_diff_key($query, array($this->_key => 1));
|
||||
$this->buildQuery($Model, $Model->alias, $Model->alias, $Model->findQueryType,
|
||||
$query[$this->_key], $result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
function buildQuery(&$Reference, $referenceClass, $referenceAlias, $query_type, $links, &$result) {
|
||||
if (empty($links))
|
||||
return;
|
||||
|
||||
$this->pr(10,
|
||||
array('begin' => 'Linkable::buildQuery',
|
||||
'args' => compact('referenceClass', 'referenceAlias', 'query_type', 'links'),
|
||||
));
|
||||
|
||||
//$defaults = $this->_defaults;// + array($this->_key => array());
|
||||
//$optionsKeys = $this->_options + array($this->_key => true);
|
||||
|
||||
$links = Set::normalize($links);
|
||||
$this->pr(24,
|
||||
array('checkpoint' => 'Normalized links',
|
||||
compact('links'),
|
||||
));
|
||||
foreach ($links as $alias => $options) {
|
||||
if (is_null($options)) {
|
||||
$options = array();
|
||||
}
|
||||
//$options = array_intersect_key($options, $optionsKeys);
|
||||
//$options = am($this->_defaults, compact('alias'), $options);
|
||||
|
||||
$options += compact('alias');
|
||||
$options += $this->_defaults;
|
||||
|
||||
if (empty($options['alias'])) {
|
||||
throw new InvalidArgumentException(sprintf('%s::%s must receive aliased links', get_class($this), __FUNCTION__));
|
||||
}
|
||||
|
||||
if (empty($options['class']))
|
||||
$options['class'] = $alias;
|
||||
|
||||
if (!isset($options['conditions']))
|
||||
$options['conditions'] = null;
|
||||
elseif (!is_array($options['conditions']))
|
||||
$options['conditions'] = array($options['conditions']);
|
||||
|
||||
$this->pr(20,
|
||||
array('checkpoint' => 'Begin Model Work',
|
||||
compact('referenceAlias', 'alias', 'options'),
|
||||
));
|
||||
|
||||
$modelClass = $options['class'];
|
||||
$modelAlias = $options['alias'];
|
||||
$Model =& ClassRegistry::init($modelClass); // the incoming model to be linked in query
|
||||
|
||||
$this->pr(12,
|
||||
array('checkpoint' => 'Model Established',
|
||||
'Reference' => ($referenceAlias .' : '. $referenceClass .
|
||||
' ('. $Reference->alias .' : '. $Reference->name .')'),
|
||||
'Model' => ($modelAlias .' : '. $modelClass .
|
||||
' ('. $Model->alias .' : '. $Model->name .')'),
|
||||
));
|
||||
|
||||
$db =& $Model->getDataSource();
|
||||
|
||||
$associatedThroughReference = 0;
|
||||
$association = null;
|
||||
|
||||
// Figure out how these two models are related, creating
|
||||
// a relationship if one doesn't otherwise already exists.
|
||||
if (($associations = $Reference->getAssociated()) &&
|
||||
isset($associations[$Model->alias])) {
|
||||
$this->pr(12, array('checkpoint' => "Reference ($referenceClass) defines association to Model ($modelClass)"));
|
||||
$associatedThroughReference = 1;
|
||||
$type = $associations[$Model->alias];
|
||||
$association = $Reference->{$type}[$Model->alias];
|
||||
}
|
||||
elseif (($associations = $Model->getAssociated()) &&
|
||||
isset($associations[$Reference->alias])) {
|
||||
$this->pr(12, array('checkpoint' => "Model ($modelClass) defines association to Reference ($referenceClass)"));
|
||||
$type = $associations[$Reference->alias];
|
||||
$association = $Model->{$type}[$Reference->alias];
|
||||
}
|
||||
else {
|
||||
// No relationship... make our best effort to create one.
|
||||
$this->pr(12, array('checkpoint' => "No assocation between Reference ($referenceClass) and Model ($modelClass)"));
|
||||
$type = 'belongsTo';
|
||||
$Model->bind($Reference->alias);
|
||||
// Grab the association now, since we'll unbind in a moment.
|
||||
$association = $Model->{$type}[$Reference->alias];
|
||||
$Model->unbindModel(array('belongsTo' => array($Reference->alias)));
|
||||
}
|
||||
|
||||
// Determine which model holds the foreign key
|
||||
if (($type === 'hasMany' || $type === 'hasOne') ^ $associatedThroughReference) {
|
||||
$primaryAlias = $referenceAlias;
|
||||
$foreignAlias = $modelAlias;
|
||||
$primaryModel = $Reference;
|
||||
$foreignModel = $Model;
|
||||
} else {
|
||||
$primaryAlias = $modelAlias;
|
||||
$foreignAlias = $referenceAlias;
|
||||
$primaryModel = $Model;
|
||||
$foreignModel = $Reference;
|
||||
}
|
||||
|
||||
if ($associatedThroughReference)
|
||||
$associationAlias = $referenceAlias;
|
||||
else
|
||||
$associationAlias = $modelAlias;
|
||||
|
||||
$this->pr(30,
|
||||
array('checkpoint' => 'Options/Association pre-merge',
|
||||
compact('association', 'options'),
|
||||
));
|
||||
|
||||
// A couple exceptions before performing a union of
|
||||
// options and association. Namely, most fields result
|
||||
// in either/or, but a couple should include BOTH the
|
||||
// options AND the association settings.
|
||||
foreach (array('fields', 'conditions') AS $fld) {
|
||||
$this->pr(31,
|
||||
array('checkpoint' => 'Options/Associations field original',
|
||||
compact('fld') +
|
||||
array("options[$fld]" =>
|
||||
array('value' => array_key_exists($fld, $options) ? (isset($options[$fld]) ? $options[$fld] : '-null-') : '-unset-',
|
||||
'type' => array_key_exists($fld, $options) ? (isset($options[$fld]) ? gettype($options[$fld]) : '-null-') : '-unset-'),
|
||||
"association[$fld]" =>
|
||||
array('value' => array_key_exists($fld, $association) ? (isset($association[$fld]) ? $association[$fld] : '-null-') : '-unset-',
|
||||
'type' => array_key_exists($fld, $association) ? (isset($association[$fld]) ? gettype($association[$fld]) : '-null-') : '-unset-'),
|
||||
)));
|
||||
|
||||
if (!isset($options[$fld]) ||
|
||||
(empty($options[$fld]) && !is_array($options[$fld])))
|
||||
unset($options[$fld]);
|
||||
elseif (!empty($options[$fld]) && !is_array($options[$fld]))
|
||||
$options[$fld] = array($options[$fld]);
|
||||
|
||||
if (!isset($association[$fld]) ||
|
||||
(empty($association[$fld]) && !is_array($association[$fld])))
|
||||
unset($association[$fld]);
|
||||
elseif (!empty($association[$fld]) && !is_array($association[$fld]))
|
||||
$association[$fld] = array($association[$fld]);
|
||||
|
||||
|
||||
$this->pr(31,
|
||||
array('checkpoint' => 'Options/Associations field normalize',
|
||||
compact('fld') +
|
||||
array("options[$fld]" =>
|
||||
array('value' => array_key_exists($fld, $options) ? (isset($options[$fld]) ? $options[$fld] : '-null-') : '-unset-',
|
||||
'type' => array_key_exists($fld, $options) ? (isset($options[$fld]) ? gettype($options[$fld]) : '-null-') : '-unset-'),
|
||||
"association[$fld]" =>
|
||||
array('value' => array_key_exists($fld, $association) ? (isset($association[$fld]) ? $association[$fld] : '-null-') : '-unset-',
|
||||
'type' => array_key_exists($fld, $association) ? (isset($association[$fld]) ? gettype($association[$fld]) : '-null-') : '-unset-'),
|
||||
)));
|
||||
|
||||
if (isset($options[$fld]) && isset($association[$fld]))
|
||||
$options[$fld] = array_merge($options[$fld],
|
||||
$association[$fld]);
|
||||
|
||||
$this->pr(31,
|
||||
array('checkpoint' => 'Options/Associations field merge complete',
|
||||
compact('fld') +
|
||||
array("options[$fld]" =>
|
||||
array('value' => array_key_exists($fld, $options) ? (isset($options[$fld]) ? $options[$fld] : '-null-') : '-unset-',
|
||||
'type' => array_key_exists($fld, $options) ? (isset($options[$fld]) ? gettype($options[$fld]) : '-null-') : '-unset-'),
|
||||
"association[$fld]" =>
|
||||
array('value' => array_key_exists($fld, $association) ? (isset($association[$fld]) ? $association[$fld] : '-null-') : '-unset-',
|
||||
'type' => array_key_exists($fld, $association) ? (isset($association[$fld]) ? gettype($association[$fld]) : '-null-') : '-unset-'),
|
||||
)));
|
||||
}
|
||||
|
||||
$this->pr(30,
|
||||
array('checkpoint' => 'Options/Association post-merge',
|
||||
compact('association', 'options'),
|
||||
));
|
||||
|
||||
// For any option that's not already set, use
|
||||
// whatever is specified by the assocation.
|
||||
$options += array_intersect_key($association, $this->_options);
|
||||
|
||||
// Replace all instances of the MODEL_ALIAS variable
|
||||
// tag with the correct model alias.
|
||||
$this->recursive_array_replace("%{MODEL_ALIAS}",
|
||||
$associationAlias,
|
||||
$options['conditions']);
|
||||
|
||||
$this->pr(15,
|
||||
array('checkpoint' => 'Models Established - Check Associations',
|
||||
'primaryModel' => $primaryAlias .' : '. $primaryModel->name,
|
||||
'foreignModel' => $foreignAlias .' : '. $foreignModel->name,
|
||||
compact('type', 'association', 'options'),
|
||||
));
|
||||
|
||||
if ($type === 'hasAndBelongsToMany') {
|
||||
if (isset($association['with']))
|
||||
$linkClass = $association['with'];
|
||||
else
|
||||
$linkClass = Inflector::classify($association['joinTable']);
|
||||
|
||||
$Link =& $Model->{$linkClass};
|
||||
|
||||
if (isset($options['linkalias']))
|
||||
$linkAlias = $options['linkalias'];
|
||||
else
|
||||
$linkAlias = $Link->alias;
|
||||
|
||||
// foreignKey and associationForeignKey can refer to either
|
||||
// the model or the reference, depending on which class
|
||||
// actually defines the association. Make sure to we're
|
||||
// using the foreign keys to point to the right class.
|
||||
if ($associatedThroughReference) {
|
||||
$modelAFK = 'associationForeignKey';
|
||||
$referenceAFK = 'foreignKey';
|
||||
} else {
|
||||
$modelAFK = 'foreignKey';
|
||||
$referenceAFK = 'associationForeignKey';
|
||||
}
|
||||
|
||||
$this->pr(17,
|
||||
array('checkpoint' => 'Linking HABTM',
|
||||
compact('linkClass', 'linkAlias',
|
||||
'modelAFK', 'referenceAFK'),
|
||||
));
|
||||
|
||||
// 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[$modelAFK], $linkAlias);
|
||||
$referenceLink = $Link->escapeField($association[$referenceAFK], $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);
|
||||
|
||||
// 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);
|
||||
|
||||
$this->pr(21,
|
||||
array('checkpoint' => 'HABTM links/keys',
|
||||
array(compact('modelLink', 'modelKey'),
|
||||
compact('referenceLink', 'referenceKey')),
|
||||
));
|
||||
|
||||
// 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'],
|
||||
),
|
||||
));
|
||||
|
||||
if (empty($options['table'])) {
|
||||
$options['table'] = $db->fullTableName($Model, true);
|
||||
}
|
||||
|
||||
if (!isset($options['fields']) || !is_array($options['fields']))
|
||||
$options['fields'] = $db->fields($Model, $modelAlias);
|
||||
elseif (!empty($options['fields']))
|
||||
$options['fields'] = $db->fields($Model, $modelAlias, $options['fields']);
|
||||
|
||||
// When performing a count query, fields are useless.
|
||||
// For everything else, we need to add them into the set.
|
||||
if ($query_type !== 'count')
|
||||
$result['fields'] = array_merge($result['fields'], $options['fields']);
|
||||
|
||||
$result['joins'][] = array_intersect_key($options,
|
||||
array('type' => true,
|
||||
'alias' => true,
|
||||
'table' => true,
|
||||
'joins' => true,
|
||||
'conditions' => true));
|
||||
|
||||
$sublinks = array_diff_key($options, $this->_options);
|
||||
$this->buildQuery($Model, $modelClass, $modelAlias, $query_type, $sublinks, $result);
|
||||
|
||||
$this->pr(19,
|
||||
array('checkpoint' => 'Model Join Complete',
|
||||
compact('referenceAlias', 'modelAlias', 'options', 'result'),
|
||||
));
|
||||
}
|
||||
|
||||
$this->pr(20,
|
||||
array('return' => 'Linkable::buildQuery',
|
||||
compact('referenceAlias'),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
<?php
|
||||
class Contact extends AppModel {
|
||||
|
||||
var $name = 'Contact';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'display_name' => array('notempty'),
|
||||
'id_federal' => array('ssn'),
|
||||
'id_exp' => array('date')
|
||||
var $displayField = 'display_name';
|
||||
|
||||
var $hasMany = array(
|
||||
'ContactsMethod',
|
||||
'ContactsCustomer',
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
@@ -15,7 +14,7 @@ class Contact extends AppModel {
|
||||
'joinTable' => 'contacts_methods',
|
||||
'associationForeignKey' => 'method_id',
|
||||
'unique' => true,
|
||||
'conditions' => "method = 'POST'",
|
||||
'conditions' => "method = 'ADDRESS'",
|
||||
),
|
||||
'ContactPhone' => array(
|
||||
'joinTable' => 'contacts_methods',
|
||||
@@ -31,5 +30,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,
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -19,19 +19,14 @@ class Customer extends AppModel {
|
||||
'conditions' => 'CurrentLease.close_date IS NULL',
|
||||
),
|
||||
'Lease',
|
||||
'LedgerEntry',
|
||||
'StatementEntry',
|
||||
'ContactsCustomer',
|
||||
|
||||
// Cheat to get Account set as part of this class
|
||||
'Account',
|
||||
'Transaction',
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
'Contact',
|
||||
'Transaction' => array(
|
||||
'joinTable' => 'ledger_entries',
|
||||
'foreignKey' => 'customer_id',
|
||||
'associationForeignKey' => 'transaction_id',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -75,112 +70,174 @@ class Customer extends AppModel {
|
||||
return $ids;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findSecurityDeposits
|
||||
* function: securityDeposits
|
||||
* - Returns an array of security deposit entries
|
||||
*/
|
||||
function findSecurityDeposits($id, $link = null) {
|
||||
/* pr(array('function' => 'Customer::findSecurityDeposits', */
|
||||
/* 'args' => compact('id', 'link'), */
|
||||
/* )); */
|
||||
function securityDeposits($id, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
|
||||
$entries = $this->Account->findLedgerEntriesRelatedToAccount
|
||||
($this->Account->invoiceAccountID(),
|
||||
$this->Account->securityDepositAccountID(),
|
||||
true, array('LedgerEntry.customer_id' => $id), $link);
|
||||
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$query['conditions'][] = array('StatementEntry.account_id' =>
|
||||
$this->StatementEntry->Account->securityDepositAccountID());
|
||||
|
||||
/* pr(array('function' => 'Customer::findSecurityDeposits', */
|
||||
/* 'args' => compact('id', 'link'), */
|
||||
/* 'vars' => compact('customer'), */
|
||||
/* 'return' => compact('entries'), */
|
||||
/* )); */
|
||||
|
||||
return $entries;
|
||||
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, false, true);
|
||||
return $this->prReturn($set);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findUnreconciledLedgerEntries
|
||||
* - Returns ledger entries that are not yet reconciled
|
||||
* (such as charges not paid).
|
||||
* function: securityDepositBalance
|
||||
* - Returns the balance of the customer security deposit(s)
|
||||
*/
|
||||
|
||||
function findUnreconciledLedgerEntries($id = null, $fundamental_type = null) {
|
||||
$unreconciled = $this->Account->findUnreconciledLedgerEntries
|
||||
($this->Account->accountReceivableAccountID(),
|
||||
$fundamental_type,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
function securityDepositBalance($id, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
|
||||
return $unreconciled;
|
||||
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$query['conditions'][] = array('StatementEntry.account_id' =>
|
||||
$this->StatementEntry->Account->securityDepositAccountID());
|
||||
|
||||
$stats = $this->StatementEntry->stats(null, $query);
|
||||
return $this->prReturn($stats['account_balance']);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reconcileNewLedgerEntry
|
||||
* - Returns which ledger entries a new credit/debit would
|
||||
* reconcile, and how much.
|
||||
*
|
||||
* - REVISIT <AP> 20090617
|
||||
* This should be subject to different algorithms, such
|
||||
* as apply to oldest charges first, newest first, to fees
|
||||
* before rent, etc. Until we get there, I'll hardcode
|
||||
* whatever algorithm is simplest.
|
||||
* function: unreconciledCharges
|
||||
* - Returns charges have not yet been fully paid
|
||||
*/
|
||||
|
||||
function reconcileNewLedgerEntry($id, $fundamental_type, $amount) {
|
||||
$reconciled = $this->Account->reconcileNewLedgerEntry
|
||||
($this->Account->accountReceivableAccountID(),
|
||||
$fundamental_type,
|
||||
$amount,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
function unreconciledCharges($id, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
|
||||
return $reconciled;
|
||||
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, true);
|
||||
return $this->prReturn($set);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: details
|
||||
* - Returns detail information for the customer
|
||||
* function: saveCustomer
|
||||
* - Saves the customer and related data
|
||||
*/
|
||||
|
||||
function details($id = null) {
|
||||
// Query the DB for need information.
|
||||
$customer = $this->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Contact' =>
|
||||
array(// Models
|
||||
'ContactPhone',
|
||||
'ContactEmail',
|
||||
'ContactAddress',
|
||||
),
|
||||
'Lease' =>
|
||||
array('Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
),
|
||||
function saveCustomer($id, $data, $primary_contact_entry) {
|
||||
|
||||
'conditions' => array('Customer.id' => $id),
|
||||
));
|
||||
// Go through each contact, and create new ones as needed
|
||||
foreach ($data['Contact'] AS &$contact) {
|
||||
if (isset($contact['id']))
|
||||
continue;
|
||||
|
||||
// Figure out the outstanding balance for this customer
|
||||
$customer['stats'] = $this->stats($id);
|
||||
$I = new Contact();
|
||||
$I->create();
|
||||
if (!$I->save($contact, false)) {
|
||||
return false;
|
||||
}
|
||||
$contact['id'] = $I->id;
|
||||
}
|
||||
|
||||
// Figure out the total security deposit for the current lease.
|
||||
$customer['deposits'] = $this->findSecurityDeposits($id);
|
||||
// Set the primary contact ID based on caller selection
|
||||
$data['Customer']['primary_contact_id']
|
||||
= $data['Contact'][$primary_contact_entry]['id'];
|
||||
|
||||
return $customer;
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: updateLeaseCount
|
||||
* - Updates the internal lease count
|
||||
*/
|
||||
|
||||
function updateLeaseCount($id) {
|
||||
$this->id = $id;
|
||||
|
||||
$lease_count =
|
||||
$this->find('count',
|
||||
array('link' => array('Lease' => array('type' => 'INNER')),
|
||||
'conditions' => array('Customer.id' => $id)));
|
||||
$current_count =
|
||||
$this->find('count',
|
||||
array('link' => array('CurrentLease' => array('type' => 'INNER')),
|
||||
'conditions' => array('Customer.id' => $id)));
|
||||
|
||||
$this->saveField('lease_count', $lease_count);
|
||||
$this->saveField('current_lease_count', $current_count);
|
||||
$this->saveField('past_lease_count', $lease_count - $current_count);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: balance
|
||||
* - Returns the balance of money owed on the lease
|
||||
*/
|
||||
|
||||
function balance($id) {
|
||||
$stats = $this->stats($id);
|
||||
return $stats['balance'];
|
||||
}
|
||||
|
||||
|
||||
@@ -191,16 +248,62 @@ class Customer extends AppModel {
|
||||
* - Returns summary data from the requested customer.
|
||||
*/
|
||||
|
||||
function stats($id = null) {
|
||||
function stats($id = null, $query = null) {
|
||||
//$this->prFunctionLevel(20);
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
if (!$id)
|
||||
return null;
|
||||
return $this->prExit(null);
|
||||
|
||||
$stats = $this->Account->stats($this->Account->accountReceivableAccountID(), true,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
$this->queryInit($query);
|
||||
|
||||
// Pull to the top level and return
|
||||
$stats = $stats['Ledger'];
|
||||
return $stats;
|
||||
// REVISIT <AP>: 20090725
|
||||
// We'll need to go directly to the statement entries if
|
||||
// transactions are not always associated with the customer.
|
||||
// This could happen if either we remove the customer_id
|
||||
// field from Transaction, or we allow multiple customers
|
||||
// to be part of the same transaction (essentially making
|
||||
// the Transaction.customer_id meaningless).
|
||||
|
||||
/* $stats = $this->StatementEntry->find */
|
||||
/* ('first', array */
|
||||
/* ('contain' => false, */
|
||||
/* 'fields' => $this->StatementEntry->chargeDisbursementFields(true), */
|
||||
/* 'conditions' => array('StatementEntry.customer_id' => $id), */
|
||||
/* )); */
|
||||
|
||||
$find_stats = $this->StatementEntry->find
|
||||
('first', array
|
||||
('contain' => false,
|
||||
'fields' => $this->StatementEntry->chargeDisbursementFields(true),
|
||||
'conditions' => array('StatementEntry.customer_id' => $id),
|
||||
));
|
||||
$find_stats = $find_stats[0];
|
||||
$this->pr(17, compact('find_stats'));
|
||||
|
||||
$tquery = $query;
|
||||
$tquery['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$statement_stats = $this->StatementEntry->stats(null, $tquery);
|
||||
$statement_stats['balance'] = $statement_stats['Charge']['balance'];
|
||||
$this->pr(17, compact('statement_stats'));
|
||||
|
||||
$tquery = $query;
|
||||
//$tquery['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$tquery['conditions'][] = array('Transaction.customer_id' => $id);
|
||||
$transaction_stats = $this->Transaction->stats(null, $tquery);
|
||||
$transaction_stats += $transaction_stats['StatementEntry'];
|
||||
$this->pr(17, compact('transaction_stats'));
|
||||
|
||||
$tquery = $query;
|
||||
//$tquery['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$tquery['conditions'][] = array('Transaction.customer_id' => $id);
|
||||
$ar_transaction_stats = $this->Transaction->stats(null, $tquery,
|
||||
$this->Transaction->Account->accountReceivableAccountID());
|
||||
$ar_transaction_stats += $ar_transaction_stats['LedgerEntry'];
|
||||
$this->pr(17, compact('ar_transaction_stats'));
|
||||
|
||||
//$stats = $ar_transaction_stats;
|
||||
$stats = $find_stats;
|
||||
return $this->prReturn($stats);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
96
site/models/double_entry.php
Normal file
96
site/models/double_entry.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
class DoubleEntry extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
'DebitEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
),
|
||||
'CreditEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: verifyDoubleEntry
|
||||
* - Verifies consistenty of new double entry data
|
||||
* (not in a pre-existing double entry)
|
||||
*/
|
||||
function verifyDoubleEntry($entry1, $entry2, $entry1_tender = null) {
|
||||
/* pr(array("DoubleEntry::verifyDoubleEntry()" */
|
||||
/* => compact('entry1', 'entry2', 'entry1_tender'))); */
|
||||
|
||||
$LE = new LedgerEntry();
|
||||
if (!$LE->verifyLedgerEntry($entry1, $entry1_tender)) {
|
||||
/* pr(array("DoubleEntry::verifyDoubleEntry()" */
|
||||
/* => "Entry1 verification failed")); */
|
||||
return false;
|
||||
}
|
||||
if (!$LE->verifyLedgerEntry($entry2)) {
|
||||
/* pr(array("DoubleEntry::verifyDoubleEntry()" */
|
||||
/* => "Entry2 verification failed")); */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(($entry1['crdr'] === 'DEBIT' && $entry2['crdr'] === 'CREDIT') ||
|
||||
($entry1['crdr'] === 'CREDIT' && $entry2['crdr'] === 'DEBIT')) ||
|
||||
($entry1['amount'] != $entry2['amount'])) {
|
||||
/* pr(array("DoubleEntry::verifyDoubleEntry()" */
|
||||
/* => "Double Entry verification failed")); */
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addDoubleEntry
|
||||
* - Inserts new Double Entry into the database
|
||||
*/
|
||||
|
||||
function addDoubleEntry($entry1, $entry2, $entry1_tender = null) {
|
||||
/* pr(array('DoubleEntry::addDoubleEntry' => */
|
||||
/* compact('entry1', 'entry2', 'entry1_tender'))); */
|
||||
|
||||
$ret = array();
|
||||
if (!$this->verifyDoubleEntry($entry1, $entry2, $entry1_tender))
|
||||
return array('error' => true) + $ret;
|
||||
|
||||
// Since this model only relates to DebitEntry and CreditEntry...
|
||||
$LE = new LedgerEntry();
|
||||
|
||||
// Add the first ledger entry to the database
|
||||
$result = $LE->addLedgerEntry($entry1, $entry1_tender);
|
||||
$ret['Entry1'] = $result;
|
||||
if ($result['error'])
|
||||
return array('error' => true) + $ret;
|
||||
|
||||
// Add the second ledger entry to the database
|
||||
$result = $LE->addLedgerEntry($entry2);
|
||||
$ret['Entry2'] = $result;
|
||||
if ($result['error'])
|
||||
return array('error' => true) + $ret;
|
||||
|
||||
// Now link them as a double entry
|
||||
$double_entry = array();
|
||||
$double_entry['debit_entry_id'] =
|
||||
($entry1['crdr'] === 'DEBIT') ? $ret['Entry1']['ledger_entry_id'] : $ret['Entry2']['ledger_entry_id'];
|
||||
$double_entry['credit_entry_id'] =
|
||||
($entry1['crdr'] === 'CREDIT') ? $ret['Entry1']['ledger_entry_id'] : $ret['Entry2']['ledger_entry_id'];
|
||||
|
||||
/* pr(array('DoubleEntry::addDoubleEntry' => */
|
||||
/* array('checkpoint' => 'Pre-Save') */
|
||||
/* + compact('double_entry'))); */
|
||||
|
||||
$this->create();
|
||||
if (!$this->save($double_entry))
|
||||
return array('error' => true) + $ret;
|
||||
|
||||
$ret['double_entry_id'] = $this->id;
|
||||
return $ret + array('error' => false);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,6 @@
|
||||
<?php
|
||||
class Lease extends AppModel {
|
||||
|
||||
var $name = 'Lease';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'number' => array('alphanumeric'),
|
||||
'lease_type_id' => array('numeric'),
|
||||
'unit_id' => array('numeric'),
|
||||
'late_schedule_id' => array('numeric'),
|
||||
'lease_date' => array('date'),
|
||||
'movein_planned_date' => array('date'),
|
||||
'movein_date' => array('date'),
|
||||
'moveout_date' => array('date'),
|
||||
'moveout_planned_date' => array('date'),
|
||||
'notice_given_date' => array('date'),
|
||||
'notice_received_date' => array('date'),
|
||||
'close_date' => array('date'),
|
||||
'deposit' => array('money'),
|
||||
'amount' => array('money'),
|
||||
'next_amount' => array('money'),
|
||||
'next_amount_date' => array('date')
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
'LeaseType',
|
||||
'Unit',
|
||||
@@ -30,108 +9,503 @@ class Lease extends AppModel {
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry',
|
||||
|
||||
// Cheat to get Account set as part of this class
|
||||
'Account',
|
||||
'StatementEntry',
|
||||
);
|
||||
|
||||
//var $default_log_level = array('log' => 30, 'show' => 15);
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: accountId
|
||||
* - Returns the accountId of the given lease
|
||||
*/
|
||||
function accountId($id) {
|
||||
return $this->Account->invoiceAccountID();
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findAccountEntries
|
||||
* - Returns an array of ledger entries from the account of the given
|
||||
* lease.
|
||||
*/
|
||||
function findAccountEntries($id, $all = false, $cond = null, $link = null) {
|
||||
/* pr(array('function' => 'Lease::findAccountEntries', */
|
||||
/* 'args' => compact('id', 'all', 'cond', 'link'), */
|
||||
/* )); */
|
||||
|
||||
if (!isset($cond))
|
||||
$cond = array();
|
||||
$cond[] = array('LedgerEntry.lease_id' => $id);
|
||||
|
||||
$entries = $this->Account->findLedgerEntries($this->accountId($id),
|
||||
$all, $cond, $link);
|
||||
|
||||
/* pr(array('function' => 'Lease::findAccountEntries', */
|
||||
/* 'args' => compact('id', 'all', 'cond', 'link'), */
|
||||
/* 'vars' => compact('lease'), */
|
||||
/* 'return' => compact('entries'), */
|
||||
/* )); */
|
||||
return $entries;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findSecurityDeposits
|
||||
* function: securityDeposits
|
||||
* - Returns an array of security deposit entries
|
||||
*/
|
||||
function findSecurityDeposits($id, $link = null) {
|
||||
/* pr(array('function' => 'Lease::findSecurityDeposits', */
|
||||
/* 'args' => compact('id', 'link'), */
|
||||
/* )); */
|
||||
function securityDeposits($id, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
|
||||
$entries = $this->Account->findLedgerEntriesRelatedToAccount
|
||||
($this->accountId($id),
|
||||
$this->Account->securityDepositAccountID(),
|
||||
true, array('LedgerEntry.lease_id' => $id), $link);
|
||||
$query['conditions'][] = array('StatementEntry.lease_id' => $id);
|
||||
$query['conditions'][] = array('StatementEntry.account_id' =>
|
||||
$this->StatementEntry->Account->securityDepositAccountID());
|
||||
|
||||
/* pr(array('function' => 'Lease::findSecurityDeposits', */
|
||||
/* 'args' => compact('id', 'link'), */
|
||||
/* 'vars' => compact('lease'), */
|
||||
/* 'return' => compact('entries'), */
|
||||
/* )); */
|
||||
return $entries;
|
||||
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, false, true);
|
||||
return $this->prReturn($set);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findUnreconciledLedgerEntries
|
||||
* - Returns ledger entries that are not yet reconciled
|
||||
* (such as charges not paid).
|
||||
* function: securityDepositBalance
|
||||
* - Returns the balance of the lease security deposit(s)
|
||||
*/
|
||||
|
||||
function findUnreconciledLedgerEntries($id = null, $fundamental_type = null) {
|
||||
return $this->Account->findUnreconciledLedgerEntries
|
||||
($this->accountId($id), $fundamental_type, array('LedgerEntry.lease_id' => $id));
|
||||
function securityDepositBalance($id, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
|
||||
$query['conditions'][] = array('StatementEntry.lease_id' => $id);
|
||||
$query['conditions'][] = array('StatementEntry.account_id' =>
|
||||
$this->StatementEntry->Account->securityDepositAccountID());
|
||||
|
||||
// REVISIT <AP>: 20090804
|
||||
// Dilemma... how to handle security deposits used to pay
|
||||
// charges on the lease, yet are not part of the lease
|
||||
// security deposit(s)? For example, Lease A & Lease B,
|
||||
// each with $25 Sec. Dep. Move out of Lease A, and
|
||||
// promote the lease surplus to the customer. A new
|
||||
// charge on Lease B for $15, which is assigned $15/$25
|
||||
// from the promoted Lease A surplus. They way this
|
||||
// function works at present, it will presume the $15 is
|
||||
// part of the security deposit balance, and will end up
|
||||
// calculating it as only $10, which is wrong. Perhaps
|
||||
// the fix is to release security deposits into some sort
|
||||
// of income account.
|
||||
|
||||
$stats = $this->StatementEntry->stats(null, $query);
|
||||
return $this->prReturn($stats['account_balance']);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reconcileNewLedgerEntry
|
||||
* - Returns which ledger entries a new credit/debit would
|
||||
* reconcile, and how much.
|
||||
*
|
||||
* - REVISIT <AP> 20090617
|
||||
* This should be subject to different algorithms, such
|
||||
* as apply to oldest charges first, newest first, to fees
|
||||
* before rent, etc. Until we get there, I'll hardcode
|
||||
* whatever algorithm is simplest.
|
||||
* function: releaseSecurityDeposits
|
||||
* - Releases all security deposits associated with this lease.
|
||||
* That simply makes a disbursement out of them, which can be used
|
||||
* to pay outstanding customer charges, or simply to become
|
||||
* a customer surplus (customer credit).
|
||||
*/
|
||||
function releaseSecurityDeposits($id, $stamp = null, $query = null) {
|
||||
//$this->prFunctionLevel(30);
|
||||
$this->prEnter(compact('id', 'stamp', 'query'));
|
||||
|
||||
$secdeps = $this->securityDeposits($id, $query);
|
||||
$secdeps = $secdeps['entries'];
|
||||
$this->pr(20, compact('secdeps'));
|
||||
|
||||
// If there are no paid security deposits, then
|
||||
// we can consider all security deposits released.
|
||||
if (count($secdeps) == 0)
|
||||
return $this->prReturn(true);
|
||||
|
||||
// Build a transaction
|
||||
$release = array('Transaction' => array(), 'Entry' => array());
|
||||
$release['Transaction']['stamp'] = $stamp;
|
||||
$release['Transaction']['comment'] = "Security Deposit Release";
|
||||
foreach ($secdeps AS $charge) {
|
||||
if ($charge['StatementEntry']['type'] !== 'CHARGE')
|
||||
die("INTERNAL ERROR: SECURITY DEPOSIT IS NOT CHARGE");
|
||||
|
||||
// Since security deposits are being released, this also means
|
||||
// any unpaid (or only partially paid) security deposit should
|
||||
// have the remaining balance reversed.
|
||||
if ($charge['StatementEntry']['balance'] > 0)
|
||||
$this->StatementEntry->reverse($charge['StatementEntry']['id'], true, $stamp);
|
||||
|
||||
$release['Entry'][] =
|
||||
array('amount' => $charge['StatementEntry']['reconciled'],
|
||||
'account_id' => $this->StatementEntry->Account->securityDepositAccountID(),
|
||||
'comment' => "Released Security Deposit",
|
||||
);
|
||||
}
|
||||
|
||||
$customer_id = $secdeps[0]['StatementEntry']['customer_id'];
|
||||
$lease_id = $secdeps[0]['StatementEntry']['lease_id'];
|
||||
|
||||
$result = $this->StatementEntry->Transaction->addReceipt
|
||||
($release, $customer_id, $lease_id);
|
||||
|
||||
return $this->prReturn($result);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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 reconcileNewLedgerEntry($id, $fundamental_type, $amount) {
|
||||
return $this->Account->reconcileNewLedgerEntry
|
||||
($this->accountId($id), $fundamental_type, $amount, array('LedgerEntry.lease_id' => $id));
|
||||
function rentLastCharges($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
$rent_account_id = $this->StatementEntry->Account->rentAccountID();
|
||||
$entries = $this->find
|
||||
('all',
|
||||
array('link' =>
|
||||
array(// Models
|
||||
'StatementEntry',
|
||||
|
||||
'SEx' =>
|
||||
array('class' => 'StatementEntry',
|
||||
'fields' => array(),
|
||||
'conditions' => array
|
||||
('SEx.effective_date = DATE_ADD(StatementEntry.through_date, INTERVAL 1 day)',
|
||||
'SEx.lease_id = StatementEntry.lease_id',
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
//'fields' => array('id', 'amount', 'effective_date', 'through_date'),
|
||||
'fields' => array(),
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
array('StatementEntry.type' => 'CHARGE'),
|
||||
array('StatementEntry.account_id' => $rent_account_id),
|
||||
array('SEx.id' => null),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
return $this->prReturn($entries);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: rentChargeGaps
|
||||
* - Checks for gaps in rent charges
|
||||
*/
|
||||
|
||||
function rentChargeGaps($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
$entries = $this->rentLastCharges($id);
|
||||
if ($entries && count($entries) > 1)
|
||||
return $this->prReturn(true);
|
||||
return $this->prReturn(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) {
|
||||
$this->prEnter(compact('id'));
|
||||
$entries = $this->rentLastCharges($id);
|
||||
if (!$entries)
|
||||
return $this->prReturn(false);
|
||||
if (count($entries) != 1)
|
||||
return $this->prReturn(null);
|
||||
return $this->prReturn($entries[0]['StatementEntry']['through_date']);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: rentPaidThrough
|
||||
* - Determines the date of the first unpaid rent
|
||||
*/
|
||||
|
||||
function rentPaidThrough($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
$rent_account_id = $this->StatementEntry->Account->rentAccountID();
|
||||
|
||||
// First, see if we can find any unpaid entries. Of course,
|
||||
// the first unpaid entry gives us a very direct indication
|
||||
// of when the customer is paid up through, which is 1 day
|
||||
// prior to the effective date of that first unpaid charge.
|
||||
$rent = $this->StatementEntry->reconciledSet
|
||||
('CHARGE',
|
||||
array('fields' =>
|
||||
array('StatementEntry.*',
|
||||
'DATE_SUB(StatementEntry.effective_date, INTERVAL 1 DAY) AS paid_through',
|
||||
),
|
||||
|
||||
'conditions' =>
|
||||
array(array('StatementEntry.lease_id' => $id),
|
||||
array('StatementEntry.account_id' => $rent_account_id)),
|
||||
|
||||
'order' => array('StatementEntry.effective_date'),
|
||||
),
|
||||
true);
|
||||
$this->pr(20, $rent, "Unpaid rent");
|
||||
|
||||
if ($rent['entries'])
|
||||
return $this->prReturn($rent['entries'][0]['StatementEntry']['paid_through']);
|
||||
|
||||
|
||||
// If we don't have any unpaid charges (great!), then the
|
||||
// customer is paid up through the last day of the last
|
||||
// charge. So, search for paid charges, which already
|
||||
// have the paid through date saved as part of the entry.
|
||||
$rent = $this->StatementEntry->reconciledSet
|
||||
('CHARGE',
|
||||
array('conditions' =>
|
||||
array(array('StatementEntry.lease_id' => $id),
|
||||
array('StatementEntry.account_id' => $rent_account_id)),
|
||||
|
||||
'order' => array('StatementEntry.through_date DESC'),
|
||||
),
|
||||
false);
|
||||
$this->pr(20, $rent, "Paid rent");
|
||||
|
||||
if ($rent['entries'])
|
||||
return $this->prReturn($rent['entries'][0]['StatementEntry']['through_date']);
|
||||
|
||||
|
||||
// After all that, having found that there are no unpaid
|
||||
// charges, and in fact, no paid charges either, we cannot
|
||||
// possibly say when the customer is paid through.
|
||||
return $this->prReturn(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)
|
||||
{
|
||||
$this->prEnter(compact('customer_id', 'unit_id',
|
||||
'deposit', 'rent', 'stamp', 'comment'));
|
||||
|
||||
$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 $this->prReturn(null);
|
||||
}
|
||||
|
||||
// Set the lease number to be the same as the lease ID
|
||||
$this->id;
|
||||
$this->saveField('number', $this->id);
|
||||
|
||||
// Update the current lease count for the customer
|
||||
$this->Customer->updateLeaseCount($customer_id);
|
||||
|
||||
// Update the unit status
|
||||
$this->Unit->updateStatus($unit_id, 'OCCUPIED');
|
||||
|
||||
// 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->prReturn($this->id);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: moveOut
|
||||
* - Moves the customer out of the specified lease
|
||||
*/
|
||||
|
||||
function moveOut($id, $status = 'VACANT',
|
||||
$stamp = null, $close = false)
|
||||
{
|
||||
$this->prEnter(compact('id', 'status', 'stamp', 'close'));
|
||||
|
||||
// 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);
|
||||
|
||||
// Release the security deposit(s)
|
||||
$this->releaseSecurityDeposits($id, $stamp);
|
||||
|
||||
// Close the lease, if so requested
|
||||
if ($close)
|
||||
$this->close($id, $stamp);
|
||||
|
||||
// Update the current lease count for the customer
|
||||
$this->Customer->updateLeaseCount($this->field('customer_id'));
|
||||
|
||||
// Finally, update the unit status
|
||||
$this->recursive = -1;
|
||||
$this->read();
|
||||
$this->Unit->updateStatus($this->data['Lease']['unit_id'], $status);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: close
|
||||
* - Closes the lease to further action
|
||||
*/
|
||||
|
||||
function close($id, $stamp = null) {
|
||||
$this->prEnter(compact('id', 'stamp'));
|
||||
|
||||
if (!$this->closeable($id))
|
||||
return $this->prReturn(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);
|
||||
|
||||
// Update the current lease count for the customer
|
||||
$this->Customer->updateLeaseCount($this->field('customer_id'));
|
||||
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: closeable
|
||||
* - Indicates whether or not the lease can be closed
|
||||
*/
|
||||
|
||||
function closeable($id) {
|
||||
$this->prEnter(compact('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 $this->prReturn(false);
|
||||
|
||||
// We can't close a lease that's already closed
|
||||
if (isset($this->data['Lease']['close_date']))
|
||||
return $this->prReturn(false);
|
||||
|
||||
$deposit_balance = $this->securityDepositBalance($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 ($deposit_balance != 0)
|
||||
return $this->prReturn(false);
|
||||
|
||||
// Apparently this lease meets all the criteria!
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: refund
|
||||
* - Marks any lease balance as payable to the customer.
|
||||
*/
|
||||
|
||||
function refund($id, $stamp = null) {
|
||||
$this->prEnter(compact('id'));
|
||||
$balance = $this->balance($id);
|
||||
|
||||
if ($balance >= 0)
|
||||
return $this->prReturn(array('error' => true));
|
||||
|
||||
$balance *= -1;
|
||||
|
||||
// Build a transaction
|
||||
$refund = array('Transaction' => array(), 'Entry' => array());
|
||||
$refund['Transaction']['stamp'] = $stamp;
|
||||
$refund['Transaction']['comment'] = "Lease Refund";
|
||||
|
||||
$refund['Entry'][] =
|
||||
array('amount' => $balance);
|
||||
|
||||
$result = $this->StatementEntry->Transaction->addRefund
|
||||
($refund, null, $id);
|
||||
|
||||
return $this->prReturn($result);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: balance
|
||||
* - Returns the balance of money owed on the lease
|
||||
*/
|
||||
|
||||
function balance($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
$stats = $this->stats($id);
|
||||
return $this->prReturn($stats['balance']);
|
||||
}
|
||||
|
||||
|
||||
@@ -142,16 +516,19 @@ class Lease extends AppModel {
|
||||
* - Returns summary data from the requested lease.
|
||||
*/
|
||||
|
||||
function stats($id = null) {
|
||||
function stats($id = null, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
if (!$id)
|
||||
return null;
|
||||
return $this->prReturn(null);
|
||||
|
||||
$stats = $this->Account->stats($this->Account->accountReceivableAccountID(), true,
|
||||
array('LedgerEntry.lease_id' => $id));
|
||||
|
||||
// Pull to the top level and return
|
||||
$stats = $stats['Ledger'];
|
||||
return $stats;
|
||||
$find_stats = $this->StatementEntry->find
|
||||
('first', array
|
||||
('contain' => false,
|
||||
'fields' => $this->StatementEntry->chargeDisbursementFields(true),
|
||||
'conditions' => array('StatementEntry.lease_id' => $id),
|
||||
));
|
||||
$find_stats = $find_stats[0];
|
||||
return $this->prReturn($find_stats);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,109 +1,133 @@
|
||||
<?php
|
||||
class Ledger extends AppModel {
|
||||
|
||||
var $name = 'Ledger';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'name' => array('notempty'),
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
'Account',
|
||||
'PriorLedger' => array('className' => 'Ledger'),
|
||||
'CloseTransaction' => array('className' => 'Transaction'),
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry' => array(
|
||||
'foreignKey' => false,
|
||||
|
||||
// conditions will be used when JOINing tables
|
||||
// (such as find with LinkableBehavior)
|
||||
'conditions' => array('OR' =>
|
||||
array('LedgerEntry.debit_ledger_id = %{MODEL_ALIAS}.id',
|
||||
'LedgerEntry.credit_ledger_id = %{MODEL_ALIAS}.id')),
|
||||
|
||||
// finderQuery will be used when tables are put
|
||||
// together across several querys, not with JOIN.
|
||||
// (such as find with ContainableBehavior)
|
||||
'finderQuery' => 'SELECT `LedgerEntry`.*
|
||||
FROM pmgr_ledger_entries AS `LedgerEntry`
|
||||
WHERE LedgerEntry.debit_ledger_id = ({$__cakeID__$})
|
||||
OR LedgerEntry.credit_ledger_id = ({$__cakeID__$})',
|
||||
|
||||
'counterQuery' => ''
|
||||
),
|
||||
'DebitLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'foreignKey' => 'debit_ledger_id',
|
||||
'dependent' => false,
|
||||
),
|
||||
'CreditLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'foreignKey' => 'credit_ledger_id',
|
||||
'dependent' => false,
|
||||
),
|
||||
'Transaction',
|
||||
'LedgerEntry',
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findLedgerEntries
|
||||
* - Returns an array of ledger entries that belong to a given
|
||||
* ledger. There is extra work done... see the LedgerEntry model.
|
||||
* function: accountID
|
||||
* - Returns the account ID for the given ledger
|
||||
*/
|
||||
function findLedgerEntries($id, $account_type = null, $cond = null, $link = null) {
|
||||
/* pr(array('function' => 'Ledger::findLedgerEntries', */
|
||||
/* 'args' => compact('id', 'account_type', 'cond', 'link'), */
|
||||
/* )); */
|
||||
function accountID($id) {
|
||||
$this->cacheQueries = true;
|
||||
$item = $this->find('first', array
|
||||
('link' => array('Account'),
|
||||
'conditions' => array('Ledger.id' => $id),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
//pr(compact('id', 'item'));
|
||||
return $item['Account']['id'];
|
||||
}
|
||||
|
||||
if (!isset($account_type)) {
|
||||
$ledger = $this->find('first', array
|
||||
('contain' => array
|
||||
('Account' => array
|
||||
('fields' => array('type'),
|
||||
),
|
||||
),
|
||||
'fields' => array(),
|
||||
'conditions' => array(array('Ledger.id' => $id)),
|
||||
));
|
||||
$account_type = $ledger['Account']['type'];
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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 closeLedgers($ids) {
|
||||
$ret = array('new_ledger_ids' => array());
|
||||
|
||||
$entries = array();
|
||||
foreach ($ids AS $id) {
|
||||
// Query stats to get the balance forward
|
||||
$stats = $this->stats($id);
|
||||
|
||||
// Populate fields from the current ledger
|
||||
$this->recursive = -1;
|
||||
$this->id = $id;
|
||||
$this->read();
|
||||
|
||||
// Build a new ledger to replace the current one
|
||||
$this->data['Ledger']['id'] = null;
|
||||
$this->data['Ledger']['close_transaction_id'] = null;
|
||||
$this->data['Ledger']['prior_ledger_id'] = $id;
|
||||
$this->data['Ledger']['comment'] = null;
|
||||
++$this->data['Ledger']['sequence'];
|
||||
$this->data['Ledger']['name'] =
|
||||
($this->data['Ledger']['account_id'] .
|
||||
'-' .
|
||||
$this->data['Ledger']['sequence']);
|
||||
|
||||
// Save the new ledger
|
||||
$this->id = null;
|
||||
if (!$this->save($this->data, false))
|
||||
return array('error' => true, 'new_ledger_data' => $this->data) + $ret;
|
||||
$ret['new_ledger_ids'][] = $this->id;
|
||||
|
||||
$entries[] = array('old_ledger_id' => $id,
|
||||
'new_ledger_id' => $this->id,
|
||||
'amount' => $stats['balance']);
|
||||
}
|
||||
|
||||
// If the requested entries are limited by date, we must calculate
|
||||
// a balance forward, or the resulting balance will be thrown off.
|
||||
//
|
||||
// REVISIT <AP>: This obviously is more general than date.
|
||||
// As such, it will not work (or, only work if the
|
||||
// condition only manages to exclude the first parts
|
||||
// of the ledger, nothing in the middle or at the
|
||||
// end. For now, I'll just create an 'other' entry,
|
||||
// not necessarily a balance forward.
|
||||
// Perform the close
|
||||
$result = $this->Transaction->addClose(array('Transaction' => array(),
|
||||
'Ledger' => $entries));
|
||||
$ret['Transaction'] = $result;
|
||||
if ($result['error'])
|
||||
return array('error' => true) + $ret;
|
||||
|
||||
$bf = array();
|
||||
if (0 && isset($cond)) {
|
||||
//$date = '<NOT IMPLEMENTED>';
|
||||
$stats = $this->stats($id, array('NOT' => array($cond)));
|
||||
$bf = array(array(array('debit' => $stats['debits'],
|
||||
'credit' => $stats['credits'],
|
||||
'balance' => $stats['balance']),
|
||||
|
||||
'LedgerEntry' => array('id' => null,
|
||||
//'comment' => "Balance Forward from $date"),
|
||||
'comment' => "-- SUMMARY OF EXCLUDED ENTRIES --"),
|
||||
|
||||
'Transaction' => array('id' => null,
|
||||
//'stamp' => $date,
|
||||
'stamp' => null,
|
||||
'comment' => null),
|
||||
));
|
||||
return $ret + array('error' => false);
|
||||
}
|
||||
|
||||
$entries = $this->LedgerEntry->findInLedgerContext($id, $account_type, $cond, $link);
|
||||
/* pr(array('function' => 'Ledger::findLedgerEntries', */
|
||||
/* 'args' => compact('id', 'account_type', 'cond', 'link'), */
|
||||
/* 'vars' => compact('ledger'), */
|
||||
/* 'return' => compact('entries'), */
|
||||
/* )); */
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: debitCreditFields
|
||||
* - Returns the fields necessary to determine whether the queried
|
||||
* entries are a debit, or a credit, and also the effect each have
|
||||
* on the overall balance of the ledger.
|
||||
*/
|
||||
function debitCreditFields($sum = false, $balance = true,
|
||||
$entry_name = 'LedgerEntry', $account_name = 'Account') {
|
||||
return $this->LedgerEntry->debitCreditFields
|
||||
($sum, $balance, $entry_name, $account_name);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: ledgerEntries
|
||||
* - Returns an array of ledger entries that belong to a given
|
||||
* ledger. There is extra work done to establish debit/credit
|
||||
*/
|
||||
function ledgerEntries($ids, $query = null) {
|
||||
if (empty($ids))
|
||||
return null;
|
||||
|
||||
$entries = $this->LedgerEntry->find
|
||||
('all', array
|
||||
('link' => array('Ledger' => array('Account')),
|
||||
'fields' => array_merge(array("LedgerEntry.*"),
|
||||
$this->LedgerEntry->debitCreditFields()),
|
||||
'conditions' => array('LedgerEntry.ledger_id' => $ids),
|
||||
));
|
||||
|
||||
//pr(compact('entries'));
|
||||
return $entries;
|
||||
}
|
||||
|
||||
@@ -114,40 +138,41 @@ class Ledger extends AppModel {
|
||||
* function: stats
|
||||
* - Returns summary data from the requested ledger.
|
||||
*/
|
||||
function stats($id, $cond = null) {
|
||||
if (!isset($cond))
|
||||
$cond = array();
|
||||
$cond[] = array('Ledger.id' => $id);
|
||||
function stats($id, $query = null) {
|
||||
if (!$id)
|
||||
return null;
|
||||
|
||||
$stats = $this->find
|
||||
('first', array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'Account' => array('fields' => array()),
|
||||
//'LedgerEntry' => array('fields' => array()),
|
||||
'LedgerEntry' =>
|
||||
array('fields' => array(),
|
||||
'Transaction' => array('fields' => array('stamp')),
|
||||
),
|
||||
),
|
||||
'fields' =>
|
||||
array("SUM(IF(LedgerEntry.debit_ledger_id = Ledger.id,
|
||||
LedgerEntry.amount, NULL)) AS debits",
|
||||
"SUM(IF(LedgerEntry.credit_ledger_id = Ledger.id,
|
||||
LedgerEntry.amount, NULL)) AS credits",
|
||||
"SUM(IF(Account.type IN ('ASSET', 'EXPENSE'),
|
||||
IF(LedgerEntry.debit_ledger_id = Ledger.id, 1, -1),
|
||||
IF(LedgerEntry.credit_ledger_id = Ledger.id, 1, -1)
|
||||
) * IF(LedgerEntry.amount, LedgerEntry.amount, 0)
|
||||
) AS balance",
|
||||
"COUNT(LedgerEntry.id) AS entries"),
|
||||
'conditions' => $cond,
|
||||
'group' => 'Ledger.id',
|
||||
));
|
||||
$this->queryInit($query);
|
||||
|
||||
if (!isset($query['link']['Account']))
|
||||
$query['link']['Account'] = array();
|
||||
if (!isset($query['link']['Account']['fields']))
|
||||
$query['link']['Account']['fields'] = array();
|
||||
if (!isset($query['fields']))
|
||||
$query['fields'] = array();
|
||||
|
||||
$query['fields'] = array_merge($query['fields'],
|
||||
$this->debitCreditFields(true));
|
||||
|
||||
$query['conditions'][] = array('LedgerEntry.ledger_id' => $id);
|
||||
$query['group'][] = 'LedgerEntry.ledger_id';
|
||||
|
||||
$stats = $this->LedgerEntry->find('first', $query);
|
||||
|
||||
// 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 member for debit/credit
|
||||
foreach(array('debits', 'credits') AS $crdr)
|
||||
if (!isset($stats[$crdr]))
|
||||
$stats[$crdr] = null;
|
||||
|
||||
// Make sure we have a non-null balance
|
||||
if (!isset($stats['balance']))
|
||||
$stats['balance'] = 0;
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,208 +1,149 @@
|
||||
<?php
|
||||
class LedgerEntry extends AppModel {
|
||||
|
||||
var $name = 'LedgerEntry';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'transaction_id' => array('numeric'),
|
||||
'amount' => array('money')
|
||||
var $belongsTo = array(
|
||||
'Transaction',
|
||||
'Account',
|
||||
'Ledger',
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
'MonetarySource',
|
||||
'Transaction',
|
||||
'Customer',
|
||||
'Lease',
|
||||
var $hasOne = array(
|
||||
'Tender',
|
||||
'DoubleEntry',
|
||||
);
|
||||
|
||||
'DebitLedger' => array(
|
||||
'className' => 'Ledger',
|
||||
'foreignKey' => 'debit_ledger_id',
|
||||
),
|
||||
'CreditLedger' => array(
|
||||
'className' => 'Ledger',
|
||||
'foreignKey' => 'credit_ledger_id',
|
||||
),
|
||||
var $hasMany = array(
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
'DebitReconciliationLedgerEntry' => array(
|
||||
// The Debit half of the double entry matching THIS Credit (if it is one)
|
||||
'DebitEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'joinTable' => 'reconciliations',
|
||||
'foreignKey' => 'credit_ledger_entry_id',
|
||||
'associationForeignKey' => 'debit_ledger_entry_id',
|
||||
'joinTable' => 'double_entries',
|
||||
'linkalias' => 'DDE',
|
||||
'foreignKey' => 'credit_entry_id',
|
||||
'associationForeignKey' => 'debit_entry_id',
|
||||
),
|
||||
'CreditReconciliationLedgerEntry' => array(
|
||||
|
||||
// The Credit half of the double entry matching THIS Debit (if it is one)
|
||||
'CreditEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'joinTable' => 'reconciliations',
|
||||
'foreignKey' => 'debit_ledger_entry_id',
|
||||
'associationForeignKey' => 'credit_ledger_entry_id',
|
||||
'joinTable' => 'double_entries',
|
||||
'linkalias' => 'CDE',
|
||||
'foreignKey' => 'debit_entry_id',
|
||||
'associationForeignKey' => 'credit_entry_id',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: conditionEntryAsCreditOrDebit
|
||||
* - returns the condition necessary to match a set of
|
||||
* Ledgers to all related LedgerEntries
|
||||
* function: debitCreditFields
|
||||
* - Returns the fields necessary to determine whether the queried
|
||||
* entries are a debit, or a credit, and also the effect each have
|
||||
* on the overall balance of the account/ledger.
|
||||
*/
|
||||
function conditionEntryAsCreditOrDebit($ledger_ids) {
|
||||
return array('OR' =>
|
||||
array(array('debit_ledger_id' => $ledger_ids),
|
||||
array('credit_ledger_id' => $ledger_ids)));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: ledgerContext query helpers
|
||||
* - Returns parameters necessary to generate a query which
|
||||
* puts ledger entries into the context of a ledger. Since
|
||||
* debit/credit depends on the account type, it is required
|
||||
* as an argument for each function to avoid having to
|
||||
* query the ledger/account to find it out.
|
||||
*/
|
||||
function ledgerContextFields($ledger_id = null, $account_type = null) {
|
||||
$fields = array('id', 'name', 'comment', 'amount');
|
||||
function debitCreditFields($sum = false, $balance = true,
|
||||
$entry_name = 'LedgerEntry', $account_name = 'Account') {
|
||||
$fields = array
|
||||
(
|
||||
($sum ? 'SUM(' : '') .
|
||||
"IF({$entry_name}.crdr = 'DEBIT'," .
|
||||
" {$entry_name}.amount, NULL)" .
|
||||
($sum ? ')' : '') . ' AS debit' . ($sum ? 's' : ''),
|
||||
|
||||
if (isset($ledger_id)) {
|
||||
$fields[] = ("IF(LedgerEntry.debit_ledger_id = $ledger_id," .
|
||||
" LedgerEntry.amount, NULL) AS debit");
|
||||
$fields[] = ("IF(LedgerEntry.credit_ledger_id = $ledger_id," .
|
||||
" LedgerEntry.amount, NULL) AS credit");
|
||||
($sum ? 'SUM(' : '') .
|
||||
"IF({$entry_name}.crdr = 'CREDIT'," .
|
||||
" {$entry_name}.amount, NULL)" .
|
||||
($sum ? ')' : '') . ' AS credit' . ($sum ? 's' : ''),
|
||||
);
|
||||
|
||||
if (isset($account_type)) {
|
||||
if (in_array($account_type, array('ASSET', 'EXPENSE')))
|
||||
$ledger_type = 'debit';
|
||||
else
|
||||
$ledger_type = 'credit';
|
||||
if ($balance)
|
||||
$fields[] =
|
||||
($sum ? 'SUM(' : '') .
|
||||
"IF(${account_name}.type IN ('ASSET', 'EXPENSE')," .
|
||||
" IF({$entry_name}.crdr = 'DEBIT', 1, -1)," .
|
||||
" IF({$entry_name}.crdr = 'CREDIT', 1, -1))" .
|
||||
" * IF({$entry_name}.amount, {$entry_name}.amount, 0)" .
|
||||
($sum ? ')' : '') . ' AS balance';
|
||||
|
||||
$fields[] = ("(IF(LedgerEntry.{$ledger_type}_ledger_id = $ledger_id," .
|
||||
" 1, -1) * LedgerEntry.amount) AS balance");
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function ledgerContextFields2($ledger_id = null, $account_id = null, $account_type = null) {
|
||||
$fields = array('id', 'name', 'comment', 'amount');
|
||||
|
||||
if (isset($ledger_id)) {
|
||||
$fields[] = ("IF(LedgerEntry.debit_ledger_id = $ledger_id," .
|
||||
" SUM(LedgerEntry.amount), NULL) AS debit");
|
||||
$fields[] = ("IF(LedgerEntry.credit_ledger_id = $ledger_id," .
|
||||
" SUM(LedgerEntry.amount), NULL) AS credit");
|
||||
|
||||
if (isset($account_id) || isset($account_type)) {
|
||||
$Account = new Account();
|
||||
$account_ftype = $Account->fundamentalType($account_id ? $account_id : $account_type);
|
||||
$fields[] = ("(IF(LedgerEntry.{$account_ftype}_ledger_id = $ledger_id," .
|
||||
" 1, -1) * SUM(LedgerEntry.amount)) AS balance");
|
||||
}
|
||||
}
|
||||
elseif (isset($account_id)) {
|
||||
$fields[] = ("IF(DebitLedger.account_id = $account_id," .
|
||||
" SUM(LedgerEntry.amount), NULL) AS debit");
|
||||
$fields[] = ("IF(CreditLedger.account_id = $account_id," .
|
||||
" SUM(LedgerEntry.amount), NULL) AS credit");
|
||||
|
||||
$Account = new Account();
|
||||
$account_ftype = ucfirst($Account->fundamentalType($account_id));
|
||||
$fields[] = ("(IF({$account_ftype}Ledger.account_id = $account_id," .
|
||||
" 1, -1) * SUM(LedgerEntry.amount)) AS balance");
|
||||
}
|
||||
if ($sum)
|
||||
$fields[] = "COUNT({$entry_name}.id) AS entries";
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
|
||||
function ledgerContextConditions($ledger_id, $account_type) {
|
||||
if (isset($ledger_id)) {
|
||||
return array
|
||||
('OR' =>
|
||||
array(array('LedgerEntry.debit_ledger_id' => $ledger_id),
|
||||
array('LedgerEntry.credit_ledger_id' => $ledger_id)),
|
||||
);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findInLedgerContext
|
||||
* - Returns an array of ledger entries that belong to a given ledger.
|
||||
* There is extra logic to also figure out whether the ledger_entry
|
||||
* amount is either a credit, or a debit, depending on how it was
|
||||
* written into the ledger, as well as whether the amount increases or
|
||||
* decreases the balance depending on the particular account type of
|
||||
* the ledger.
|
||||
* function: verifyLedgerEntry
|
||||
* - Verifies consistenty of new ledger entry data
|
||||
* (not in a pre-existing ledger entry)
|
||||
*/
|
||||
function findInLedgerContext($ledger_id, $account_type, $cond = null, $link = null) {
|
||||
if (!isset($link))
|
||||
$link = array('Transaction');
|
||||
function verifyLedgerEntry($entry, $tender = null) {
|
||||
/* pr(array("LedgerEntry::verifyLedgerEntry()" */
|
||||
/* => compact('entry', 'tender'))); */
|
||||
|
||||
if (!isset($cond))
|
||||
$cond = array();
|
||||
if (empty($entry['account_id']) ||
|
||||
empty($entry['crdr']) ||
|
||||
empty($entry['amount'])
|
||||
) {
|
||||
/* pr(array("LedgerEntry::verifyLedgerEntry()" */
|
||||
/* => "Entry verification failed")); */
|
||||
return false;
|
||||
}
|
||||
|
||||
$fields = $this->ledgerContextFields($ledger_id, $account_type);
|
||||
$cond[] = $this->ledgerContextConditions($ledger_id, $account_type);
|
||||
$order = array('Transaction.stamp');
|
||||
if (isset($tender) && !$this->Tender->verifyTender($tender)) {
|
||||
/* pr(array("LedgerEntry::verifyLedgerEntry()" */
|
||||
/* => "Tender verification failed")); */
|
||||
return false;
|
||||
}
|
||||
|
||||
$entries = $this->find
|
||||
('all',
|
||||
array('link' => $link,
|
||||
'fields' => $fields,
|
||||
'conditions' => $cond,
|
||||
'order' => $order,
|
||||
));
|
||||
|
||||
return $entries;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findReconciledLedgerEntries
|
||||
* - Returns ledger entries that are reconciled to the given entry.
|
||||
* (such as payments towards a charge).
|
||||
* function: addLedgerEntry
|
||||
* - Inserts new Ledger Entry into the database
|
||||
*/
|
||||
function addLedgerEntry($entry, $tender = null) {
|
||||
/* pr(array('LedgerEntry::addLedgerEntry' => */
|
||||
/* compact('entry', 'tender'))); */
|
||||
|
||||
function findReconciledLedgerEntries($id = null, $fundamental_type = null) {
|
||||
foreach (($fundamental_type
|
||||
? array($fundamental_type)
|
||||
: array('debit', 'credit')) AS $fund) {
|
||||
$ucfund = ucfirst($fund);
|
||||
$reconciled[$fund]['entry'] = $this->find
|
||||
('all', array
|
||||
('link' => array
|
||||
("ReconciliationLedgerEntry" => array
|
||||
('class' => "{$ucfund}ReconciliationLedgerEntry",
|
||||
'fields' => array
|
||||
('id',
|
||||
"COALESCE(SUM(Reconciliation.amount),0) AS 'reconciled'",
|
||||
"LedgerEntry.amount - COALESCE(SUM(Reconciliation.amount),0) AS 'balance'",
|
||||
),
|
||||
),
|
||||
),
|
||||
'group' => ("ReconciliationLedgerEntry.id"),
|
||||
'conditions' => array('LedgerEntry.id' => $id),
|
||||
'fields' => array(),
|
||||
));
|
||||
//pr($reconciled);
|
||||
$balance = 0;
|
||||
foreach ($reconciled[$fund]['entry'] AS &$entry) {
|
||||
$entry = array_merge($entry["ReconciliationLedgerEntry"], $entry[0]);
|
||||
$balance += $entry['balance'];
|
||||
}
|
||||
$reconciled[$fund]['balance'] = $balance;
|
||||
$ret = array();
|
||||
if (!$this->verifyLedgerEntry($entry, $tender))
|
||||
return array('error' => true) + $ret;
|
||||
|
||||
if (empty($entry['ledger_id']))
|
||||
$entry['ledger_id'] =
|
||||
$this->Account->currentLedgerID($entry['account_id']);
|
||||
|
||||
/* pr(array('LedgerEntry::addLedgerEntry' => */
|
||||
/* array('checkpoint' => 'Pre-Save') */
|
||||
/* + compact('entry'))); */
|
||||
|
||||
$this->create();
|
||||
if (!$this->save($entry))
|
||||
return array('error' => true) + $ret;
|
||||
|
||||
$ret['ledger_entry_id'] = $this->id;
|
||||
|
||||
if (isset($tender)) {
|
||||
$tender['account_id'] = $entry['account_id'];
|
||||
$tender['ledger_entry_id'] = $ret['ledger_entry_id'];
|
||||
$result = $this->Tender->addTender($tender);
|
||||
$ret['Tender'] = $result;
|
||||
if ($result['error'])
|
||||
return array('error' => true) + $ret;
|
||||
}
|
||||
|
||||
return $reconciled;
|
||||
return $ret + array('error' => false);
|
||||
}
|
||||
|
||||
|
||||
@@ -212,29 +153,53 @@ class LedgerEntry extends AppModel {
|
||||
* function: stats
|
||||
* - Returns summary data from the requested ledger entry
|
||||
*/
|
||||
function stats($id) {
|
||||
function stats($id = null, $query = null, $set = null) {
|
||||
$this->queryInit($query);
|
||||
unset($query['group']);
|
||||
|
||||
$query = array
|
||||
(
|
||||
'fields' => array("SUM(Reconciliation.amount) AS 'reconciled'"),
|
||||
if (!isset($query['link']['DoubleEntry']))
|
||||
$query['link']['DoubleEntry'] = array();
|
||||
/* if (!isset($query['link']['DoubleEntry']['fields'])) */
|
||||
/* $query['link']['DoubleEntry']['fields'] = array(); */
|
||||
|
||||
'conditions' => array(isset($cond) ? $cond : array(),
|
||||
array('LedgerEntry.id' => $id)),
|
||||
if (isset($id))
|
||||
$query['conditions'][] = array('Entry.id' => $id);
|
||||
|
||||
'group' => 'LedgerEntry.id',
|
||||
);
|
||||
if (isset($set))
|
||||
$set = strtoupper($set);
|
||||
|
||||
// Get the applied amounts on the debit side
|
||||
$query['link'] =
|
||||
array('DebitReconciliationLedgerEntry' => array('alias' => 'DRLE', 'DRLETransaction' => array('class' => 'Transaction')));
|
||||
$tmpstats = $this->find('first', $query);
|
||||
$stats['debit_amount_reconciled'] = $tmpstats[0]['reconciled'];
|
||||
//pr(array('stats()', compact('id', 'query', 'set')));
|
||||
|
||||
// Get the applied amounts on the credit side
|
||||
$query['link'] =
|
||||
array('CreditReconciliationLedgerEntry' => array('alias' => 'CRLE', 'CRLETransaction' => array('class' => 'Transaction')));
|
||||
$tmpstats = $this->find('first', $query);
|
||||
$stats['credit_amount_reconciled'] = $tmpstats[0]['reconciled'];
|
||||
$rtypes = array('charge', 'disbursement',
|
||||
// 'debit', 'credit',
|
||||
);
|
||||
|
||||
$stats = array();
|
||||
foreach($rtypes AS $rtype) {
|
||||
$Rtype = ucfirst($rtype);
|
||||
|
||||
if (($rtype == 'charge' && (!isset($set) || $set == 'DISBURSEMENT')) ||
|
||||
($rtype == 'disbursement' && (!isset($set) || $set == 'CHARGE'))
|
||||
) {
|
||||
|
||||
$rquery = $query;
|
||||
$rquery['link'][$Rtype] =
|
||||
array('fields' => array("SUM(COALESCE(Applied{$Rtype}.amount,0)) AS reconciled"));
|
||||
|
||||
$rquery['fields'] = array();
|
||||
//$rquery['fields'][] = "SUM(DoubleEntry.amount) AS total";
|
||||
$rquery['fields'][] = "SUM(DoubleEntry.amount) - SUM(COALESCE(Applied{$Rtype}.amount,0)) AS balance";
|
||||
$rquery['conditions'][] = array("Applied{$Rtype}.id !=" => null);
|
||||
|
||||
$result = $this->find('first', $rquery);
|
||||
//pr(compact('Rtype', 'rquery', 'result'));
|
||||
|
||||
$sumfld = $Rtype;
|
||||
$stats[$sumfld] = $result[0];
|
||||
/* if (!isset($stats[$sumfld]['applied'])) */
|
||||
/* $stats[$sumfld]['applied'] = 0; */
|
||||
}
|
||||
}
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
class MonetarySource extends AppModel {
|
||||
|
||||
var $name = 'MonetarySource';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'name' => array('notempty'),
|
||||
'tillable' => array('boolean')
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
'MonetaryType',
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry',
|
||||
);
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -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',
|
||||
);
|
||||
|
||||
}
|
||||
?>
|
||||
733
site/models/statement_entry.php
Normal file
733
site/models/statement_entry.php
Normal file
@@ -0,0 +1,733 @@
|
||||
<?php
|
||||
class StatementEntry extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
'Transaction',
|
||||
'Customer',
|
||||
'Lease',
|
||||
'Account',
|
||||
|
||||
// The charge to which this disbursement applies (if it is one)
|
||||
'ChargeEntry' => array(
|
||||
'className' => 'StatementEntry',
|
||||
),
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
// The disbursements that apply to this charge (if it is one)
|
||||
'DisbursementEntry' => array(
|
||||
'className' => 'StatementEntry',
|
||||
'foreignKey' => 'charge_entry_id',
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
var $default_log_level = array('log' => 30, 'show' => 15);
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: debit/creditTypes
|
||||
*/
|
||||
|
||||
function debitTypes() {
|
||||
return array('CHARGE', 'PAYMENT', 'REFUND');
|
||||
}
|
||||
|
||||
function creditTypes() {
|
||||
return array('DISBURSEMENT', 'WAIVER', 'REVERSAL', 'SURPLUS');
|
||||
}
|
||||
|
||||
function voidTypes() {
|
||||
return array('VOID');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: chargeDisbursementFields
|
||||
*/
|
||||
|
||||
function chargeDisbursementFields($sum = false, $entry_name = 'StatementEntry') {
|
||||
$debits = $this->debitTypes();
|
||||
$credits = $this->creditTypes();
|
||||
$voids = $this->voidTypes();
|
||||
|
||||
foreach ($debits AS &$enum)
|
||||
$enum = "'" . $enum . "'";
|
||||
foreach ($credits AS &$enum)
|
||||
$enum = "'" . $enum . "'";
|
||||
foreach ($voids AS &$enum)
|
||||
$enum = "'" . $enum . "'";
|
||||
|
||||
$debit_set = implode(", ", $debits);
|
||||
$credit_set = implode(", ", $credits);
|
||||
$void_set = implode(", ", $voids);
|
||||
|
||||
$fields = array
|
||||
(
|
||||
($sum ? 'SUM(' : '') .
|
||||
"IF({$entry_name}.type IN ({$debit_set})," .
|
||||
" {$entry_name}.amount, NULL)" .
|
||||
($sum ? ')' : '') . ' AS charge' . ($sum ? 's' : ''),
|
||||
|
||||
($sum ? 'SUM(' : '') .
|
||||
"IF({$entry_name}.type IN({$credit_set})," .
|
||||
" {$entry_name}.amount, NULL)" .
|
||||
($sum ? ')' : '') . ' AS disbursement' . ($sum ? 's' : ''),
|
||||
|
||||
($sum ? 'SUM(' : '') .
|
||||
"IF({$entry_name}.type IN ({$debit_set}), 1," .
|
||||
" IF({$entry_name}.type IN ({$credit_set}), -1, 0))" .
|
||||
" * IF({$entry_name}.amount, {$entry_name}.amount, 0)" .
|
||||
($sum ? ')' : '') . ' AS balance',
|
||||
);
|
||||
|
||||
if ($sum)
|
||||
$fields[] = "COUNT({$entry_name}.id) AS entries";
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: verifyStatementEntry
|
||||
* - Verifies consistenty of new statement entry data
|
||||
* (not in a pre-existing statement entry)
|
||||
*/
|
||||
function verifyStatementEntry($entry) {
|
||||
$this->prFunctionLevel(10);
|
||||
$this->prEnter(compact('entry'));
|
||||
|
||||
if (empty($entry['type']) ||
|
||||
//empty($entry['effective_date']) ||
|
||||
empty($entry['account_id']) ||
|
||||
empty($entry['amount'])
|
||||
) {
|
||||
return $this->prReturn(false);
|
||||
}
|
||||
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addStatementEntry
|
||||
* - Inserts new Statement Entry into the database
|
||||
*/
|
||||
function addStatementEntry($entry) {
|
||||
$this->prEnter(compact('entry'));
|
||||
|
||||
$ret = array();
|
||||
if (!$this->verifyStatementEntry($entry))
|
||||
return array('error' => true, 'verify_data' => $entry) + $ret;
|
||||
|
||||
$this->pr(20, array('checkpoint' => 'Pre-Save')
|
||||
+ compact('entry'));
|
||||
|
||||
$this->create();
|
||||
if (!$this->save($entry))
|
||||
return array('error' => true, 'save_data' => $entry) + $ret;
|
||||
|
||||
$ret['statement_entry_id'] = $this->id;
|
||||
return $this->prReturn($ret + array('error' => false));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: waive
|
||||
* - Waives the charge balance
|
||||
*
|
||||
*/
|
||||
function waive($id, $stamp = null) {
|
||||
$this->prEnter(compact('id', 'stamp'));
|
||||
|
||||
// Get the basic information about the entry to be waived.
|
||||
$this->recursive = -1;
|
||||
$charge = $this->read(null, $id);
|
||||
$charge = $charge['StatementEntry'];
|
||||
|
||||
if ($charge['type'] !== 'CHARGE')
|
||||
INTERNAL_ERROR("Waiver item is not CHARGE.");
|
||||
|
||||
// Query the stats to get the remaining balance
|
||||
$stats = $this->stats($id);
|
||||
|
||||
// Build a transaction
|
||||
$waiver = array('Transaction' => array(), 'Entry' => array());
|
||||
$waiver['Transaction']['stamp'] = $stamp;
|
||||
$waiver['Transaction']['comment'] = "Charge Waiver";
|
||||
|
||||
// Add the charge waiver
|
||||
$waiver['Entry'][] =
|
||||
array('amount' => $stats['Charge']['balance'],
|
||||
'account_id' => $this->Account->waiverAccountID(),
|
||||
'comment' => null,
|
||||
);
|
||||
|
||||
// Record the waiver transaction
|
||||
return $this->prReturn($this->Transaction->addWaiver
|
||||
($waiver, $id, $charge['customer_id'], $charge['lease_id']));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reverse
|
||||
* - Reverses the charges
|
||||
*
|
||||
*/
|
||||
function reverse($id, $balance = false, $stamp = null) {
|
||||
$this->prEnter(compact('id', 'balance', 'stamp'));
|
||||
|
||||
$ret = array();
|
||||
|
||||
// Get the basic information about the entry to be reversed.
|
||||
$this->recursive = -1;
|
||||
$charge = $this->read(null, $id);
|
||||
$charge = $charge['StatementEntry'];
|
||||
|
||||
if ($charge['type'] !== 'CHARGE')
|
||||
INTERNAL_ERROR("Reversal item is not CHARGE.");
|
||||
|
||||
// Build a transaction
|
||||
$reversal = array('Transaction' => array(), 'Entry' => array());
|
||||
$reversal['Transaction']['stamp'] = $stamp;
|
||||
$reversal['Transaction']['comment'] = "Credit Note: Charge Reversal";
|
||||
|
||||
$voided_entry_transactions = array();
|
||||
$reconciled = $this->reconciledEntries($id);
|
||||
$this->pr(21, compact('reconciled'));
|
||||
|
||||
if ($reconciled && !$balance) {
|
||||
foreach ($reconciled['entries'] AS $entry) {
|
||||
if ($entry['DisbursementEntry']['type'] === 'REVERSAL')
|
||||
INTERNAL_ERROR("Charge has already been reversed");
|
||||
|
||||
/* $voided_entry_transactions[$entry['DisbursementEntry']['transaction_id']] */
|
||||
/* = array_intersect_key($entry['DisbursementEntry'], */
|
||||
/* array('customer_id'=>1, 'lease_id'=>1)); */
|
||||
|
||||
/* $reversal['Entry'][] = */
|
||||
/* array_intersect_key($entry['DisbursementEntry'], */
|
||||
/* array_flip(array('amount', 'account_id', 'charge_entry_id'))) */
|
||||
/* + array('type' => 'SURPLUS', */
|
||||
/* 'comment' => 'Release of funds applied to reversed charge', */
|
||||
/* ); */
|
||||
}
|
||||
/* $this->pr(17, compact('voided_entry_transactions')); */
|
||||
}
|
||||
|
||||
// Query the stats to get the remaining balance
|
||||
$stats = $this->stats($id);
|
||||
|
||||
// Add the charge reversal
|
||||
$reversal['Entry'][] =
|
||||
array('charge_entry_id' => $id,
|
||||
'amount' => -1 * $stats['Charge'][$balance ? 'balance' : 'total'],
|
||||
'account_id' => $charge['account_id'],
|
||||
'comment' => 'Charge Reversal',
|
||||
);
|
||||
|
||||
// Record the reversal transaction
|
||||
$result = $this->Transaction->addReversal
|
||||
($reversal, $id, $charge['customer_id'], $charge['lease_id']);
|
||||
$this->pr(21, compact('result'));
|
||||
$ret['reversal'] = $result;
|
||||
if ($result['error'])
|
||||
$ret['error'] = true;
|
||||
|
||||
foreach ($voided_entry_transactions AS $transaction_id => $tx) {
|
||||
$result = $this->assignCredits
|
||||
(null,
|
||||
$transaction_id,
|
||||
null,
|
||||
null,
|
||||
$tx['customer_id'],
|
||||
$tx['lease_id']
|
||||
);
|
||||
$this->pr(21, compact('result'));
|
||||
$ret['assigned'][] = $result;
|
||||
if ($result['error'])
|
||||
$ret['error'] = true;
|
||||
}
|
||||
|
||||
return $this->prReturn($ret + array('error' => false));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reconciledSet
|
||||
* - Returns the set of entries satisfying the given conditions,
|
||||
* along with any entries that they reconcile
|
||||
*/
|
||||
function reconciledSetQuery($set, $query) {
|
||||
$this->queryInit($query);
|
||||
|
||||
if (in_array($set, $this->debitTypes()))
|
||||
$query['link']['DisbursementEntry'] = array('fields' => array("SUM(DisbursementEntry.amount) AS reconciled"));
|
||||
elseif (in_array($set, $this->creditTypes()))
|
||||
$query['link']['ChargeEntry'] = array('fields' => array("SUM(ChargeEntry.amount) AS reconciled"));
|
||||
else
|
||||
die("INVALID RECONCILE SET");
|
||||
|
||||
$query['conditions'][] = array('StatementEntry.type' => $set);
|
||||
$query['group'] = 'StatementEntry.id';
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
function reconciledSet($set, $query = null, $unrec = false, $if_rec_include_partial = false) {
|
||||
//$this->prFunctionLevel(array('log' => 16, 'show' => 10));
|
||||
$this->prEnter(compact('set', 'query', 'unrec', 'if_rec_include_partial'));
|
||||
$lquery = $this->reconciledSetQuery($set, $query);
|
||||
$result = $this->find('all', $lquery);
|
||||
|
||||
$this->pr(20, compact('lquery', 'result'));
|
||||
|
||||
$resultset = array();
|
||||
foreach ($result AS $i => $entry) {
|
||||
$this->pr(25, compact('entry'));
|
||||
if (!empty($entry[0]))
|
||||
$entry['StatementEntry'] = $entry[0] + $entry['StatementEntry'];
|
||||
unset($entry[0]);
|
||||
|
||||
$entry['StatementEntry']['balance'] =
|
||||
$entry['StatementEntry']['amount'] - $entry['StatementEntry']['reconciled'];
|
||||
|
||||
// Since HAVING isn't a builtin feature of CakePHP,
|
||||
// we'll have to post-process to get the desired entries
|
||||
|
||||
if ($entry['StatementEntry']['balance'] == 0)
|
||||
$reconciled = true;
|
||||
elseif ($entry['StatementEntry']['reconciled'] == 0)
|
||||
$reconciled = false;
|
||||
else // Partial disbursement; depends on unrec
|
||||
$reconciled = (!$unrec && $if_rec_include_partial);
|
||||
|
||||
// Add to the set, if it's been requested
|
||||
if ($reconciled == !$unrec)
|
||||
$resultset[] = $entry;
|
||||
}
|
||||
|
||||
return $this->prReturn(array('entries' => $resultset,
|
||||
'summary' => $this->stats(null, $query)));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reconciledEntries
|
||||
* - Returns a list of entries that reconcile against the given entry.
|
||||
* (such as disbursements towards a charge).
|
||||
*/
|
||||
function reconciledEntriesQuery($id, $query = null) {
|
||||
$this->queryInit($query, false);
|
||||
|
||||
$this->id = $id;
|
||||
$this->recursive = -1;
|
||||
$this->read();
|
||||
|
||||
$query['conditions'][] = array('StatementEntry.id' => $id);
|
||||
|
||||
if (in_array($this->data['StatementEntry']['type'], $this->debitTypes())) {
|
||||
$query['link']['DisbursementEntry'] = array();
|
||||
$query['conditions'][] = array('DisbursementEntry.id !=' => null);
|
||||
}
|
||||
if (in_array($this->data['StatementEntry']['type'], $this->creditTypes())) {
|
||||
$query['link']['ChargeEntry'] = array();
|
||||
$query['conditions'][] = array('ChargeEntry.id !=' => null);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
function reconciledEntries($id, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$lquery = $this->reconciledEntriesQuery($id, $query);
|
||||
|
||||
$result = $this->find('all', $lquery);
|
||||
foreach (array_keys($result) AS $i)
|
||||
unset($result[$i]['StatementEntry']);
|
||||
|
||||
return $this->prReturn(array('entries' => $result));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: assignCredits
|
||||
* - Assigns all credits to existing charges
|
||||
*
|
||||
* REVISIT <AP>: 20090726
|
||||
* This algorithm shouldn't be hardcoded. We need to allow
|
||||
* the user to specify how disbursements should be applied.
|
||||
*
|
||||
*/
|
||||
function assignCredits($query = null, $receipt_id = null,
|
||||
$charge_ids = null, $disbursement_type = null,
|
||||
$customer_id = null, $lease_id = null)
|
||||
{
|
||||
//$this->prFunctionLevel(25);
|
||||
$this->prEnter(compact('query', 'receipt_id',
|
||||
'charge_ids', 'disbursement_type',
|
||||
'customer_id', 'lease_id'));
|
||||
$this->queryInit($query);
|
||||
|
||||
if (!empty($customer_id))
|
||||
$query['conditions'][] = array('StatementEntry.customer_id' => $customer_id);
|
||||
|
||||
if (empty($disbursement_type))
|
||||
$disbursement_type = 'DISBURSEMENT';
|
||||
|
||||
$ret = array();
|
||||
|
||||
// First, find all known credits, unless this call is to make
|
||||
// credit adjustments to a specific charge
|
||||
// REVISIT <AP>: 20090806
|
||||
// If the theory below is correct, we should only search for
|
||||
// explicit credits if we don't have a receipt_id
|
||||
if (empty($charge_ids)) {
|
||||
$lquery = $query;
|
||||
$lquery['conditions'][] = array('StatementEntry.type' => 'SURPLUS');
|
||||
// REVISIT <AP>: 20090804
|
||||
// We need to ensure that we're using surplus credits ONLY from either
|
||||
// the given lease, or those that do not apply to any specific lease.
|
||||
// However, by doing this, it forces any lease surplus amounts to
|
||||
// remain frozen with that lease until either there is a lease charge,
|
||||
// we refund the money, or we "promote" that surplus to the customer
|
||||
// level and out of the leases direct control.
|
||||
// That seems like a pain. Perhaps we should allow any customer
|
||||
// surplus to be used on any customer charge.
|
||||
$lquery['conditions'][] =
|
||||
array('OR' =>
|
||||
array(array('StatementEntry.lease_id' => null),
|
||||
(!empty($lease_id)
|
||||
? array('StatementEntry.lease_id' => $lease_id)
|
||||
: array()),
|
||||
));
|
||||
$lquery['order'][] = 'StatementEntry.effective_date ASC';
|
||||
$credits = $this->find('all', $lquery);
|
||||
$this->pr(18, compact('credits'),
|
||||
"Credits Established");
|
||||
}
|
||||
else {
|
||||
if (empty($receipt_id))
|
||||
INTERNAL_ERROR("Can't make adjustments to a charge without a receipt ID.");
|
||||
}
|
||||
|
||||
// Next, establish credit from the newly added receipt
|
||||
$receipt_credit = null;
|
||||
if (!empty($receipt_id)) {
|
||||
$lquery =
|
||||
array('link' =>
|
||||
array('StatementEntry',
|
||||
'LedgerEntry' =>
|
||||
array('conditions' =>
|
||||
array('LedgerEntry.account_id !=' =>
|
||||
$this->Account->accountReceivableAccountID()),
|
||||
),
|
||||
),
|
||||
'conditions' => array('Transaction.id' => $receipt_id),
|
||||
'fields' => array('Transaction.id', 'Transaction.stamp', 'Transaction.amount'),
|
||||
);
|
||||
$receipt_credit = $this->Transaction->find('first', $lquery);
|
||||
if (!$receipt_credit)
|
||||
INTERNAL_ERROR("Unable to locate receipt.");
|
||||
|
||||
//$reconciled = $this->reconciledEntries($id);
|
||||
|
||||
$stats = $this->Transaction->stats($receipt_id);
|
||||
$receipt_credit['balance'] =
|
||||
$receipt_credit['Transaction']['amount'] - $stats['Disbursement']['total'];
|
||||
|
||||
$this->pr(18, compact('receipt_credit'),
|
||||
"Receipt Credit Added");
|
||||
}
|
||||
|
||||
// Now find all unpaid charges
|
||||
if (isset($charge_ids)) {
|
||||
$lquery = array('contain' => false,
|
||||
'conditions' => array('StatementEntry.id' => $charge_ids));
|
||||
} else {
|
||||
$lquery = $query;
|
||||
// If we're working with a specific lease, then limit charges to it
|
||||
if (!empty($lease_id))
|
||||
$lquery['conditions'][] = array('StatementEntry.lease_id' => $lease_id);
|
||||
}
|
||||
$lquery['order'] = 'StatementEntry.effective_date ASC';
|
||||
$charges = array();
|
||||
foreach ($this->debitTypes() AS $dtype) {
|
||||
$rset = $this->reconciledSet($dtype, $lquery, true);
|
||||
$entries = $rset['entries'];
|
||||
$charges = array_merge($charges, $entries);
|
||||
$this->pr(18, compact('dtype', 'entries'), "Outstanding Debit Entries");
|
||||
}
|
||||
|
||||
// Initialize our list of used credits
|
||||
$used_credits = array();
|
||||
|
||||
// REVISIT <AP>: 20090806
|
||||
// Testing a theory. We should never have
|
||||
// explicit credits, as well as a new receipt,
|
||||
// and yet have outstanding charges.
|
||||
if (!empty($credits) && !empty($receipt_credit) && !empty($charges))
|
||||
INTERNAL_ERROR("Explicit credits that haven't already been applied.");
|
||||
|
||||
// Work through all unpaid charges, applying disbursements as we go
|
||||
foreach ($charges AS $charge) {
|
||||
|
||||
$this->pr(20, compact('charge'),
|
||||
'Process Charge');
|
||||
|
||||
// Check that we have available credits.
|
||||
// Technically, this isn't necessary, since the loop
|
||||
// will handle everything just fine. However, this
|
||||
// just saves extra processing if/when there is no
|
||||
// means to resolve a charge anyway.
|
||||
if (empty($credits) && empty($receipt_credit['balance'])) {
|
||||
$this->pr(17, 'No more available credits');
|
||||
break;
|
||||
}
|
||||
|
||||
$charge['balance'] = $charge['StatementEntry']['balance'];
|
||||
while ($charge['balance'] > 0 &&
|
||||
(!empty($credits) || !empty($receipt_credit['balance']))) {
|
||||
|
||||
$this->pr(20, compact('charge'),
|
||||
'Attempt Charge Reconciliation');
|
||||
|
||||
// Use explicit credits before using implicit credits
|
||||
// (Not sure it matters though).
|
||||
if (!empty($credits)) {
|
||||
// Peel off the first credit available
|
||||
$credit =& $credits[0];
|
||||
$disbursement_date = $credit['StatementEntry']['effective_date'];
|
||||
$disbursement_transaction_id = $credit['StatementEntry']['transaction_id'];
|
||||
$disbursement_account_id = $credit['StatementEntry']['account_id'];
|
||||
|
||||
if (!isset($credit['balance']))
|
||||
$credit['balance'] = $credit['StatementEntry']['amount'];
|
||||
}
|
||||
elseif (!empty($receipt_credit['balance'])) {
|
||||
// Use our only receipt credit
|
||||
$credit =& $receipt_credit;
|
||||
$disbursement_date = $credit['Transaction']['stamp'];
|
||||
$disbursement_transaction_id = $credit['Transaction']['id'];
|
||||
$disbursement_account_id = $credit['LedgerEntry']['account_id'];
|
||||
}
|
||||
else {
|
||||
die("HOW DID WE GET HERE WITH NO SURPLUS?");
|
||||
}
|
||||
|
||||
// Set the disbursement amount to the maximum amount
|
||||
// possible without exceeding the charge or credit balance
|
||||
$disbursement_amount = min($charge['balance'], $credit['balance']);
|
||||
if (!isset($credit['applied']))
|
||||
$credit['applied'] = 0;
|
||||
|
||||
$credit['applied'] += $disbursement_amount;
|
||||
$credit['balance'] -= $disbursement_amount;
|
||||
|
||||
$this->pr(20, compact('credit'),
|
||||
($credit['balance'] > 0 ? 'Utilized' : 'Exhausted') .
|
||||
(!empty($credits) ? ' Credit' : ' Receipt'));
|
||||
|
||||
if ($credit['balance'] < 0)
|
||||
die("HOW DID WE END UP WITH NEGATIVE SURPLUS BALANCE?");
|
||||
|
||||
// If we've exhausted the credit, get it out of the
|
||||
// available credit pool (but keep track of it for later).
|
||||
if ($credit['balance'] <= 0 && !empty($credits))
|
||||
$used_credits[] = array_shift($credits);
|
||||
|
||||
// Add a disbursement that uses the available credit to pay the charge
|
||||
$disbursement = array('type' => $disbursement_type,
|
||||
'account_id' => $disbursement_account_id,
|
||||
'amount' => $disbursement_amount,
|
||||
'effective_date' => $disbursement_date,
|
||||
'transaction_id' => $disbursement_transaction_id,
|
||||
'customer_id' => $charge['StatementEntry']['customer_id'],
|
||||
'lease_id' => $charge['StatementEntry']['lease_id'],
|
||||
'charge_entry_id' => $charge['StatementEntry']['id'],
|
||||
'comment' => null,
|
||||
);
|
||||
|
||||
$this->pr(20, compact('disbursement'),
|
||||
'New Disbursement Entry');
|
||||
|
||||
$result = $this->addStatementEntry($disbursement);
|
||||
$ret['Disbursement'][] = $result;
|
||||
if ($result['error'])
|
||||
$ret['error'] = true;
|
||||
|
||||
// Adjust the charge balance to reflect the new disbursement
|
||||
$charge['balance'] -= $disbursement_amount;
|
||||
if ($charge['balance'] < 0)
|
||||
die("HOW DID WE GET A NEGATIVE CHARGE AMOUNT?");
|
||||
|
||||
if ($charge['balance'] <= 0)
|
||||
$this->pr(20, 'Fully Paid Charge');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Partially used credits must be added to the used list
|
||||
if (isset($credits[0]['applied']))
|
||||
$used_credits[] = array_shift($credits);
|
||||
|
||||
$this->pr(18, compact('credits', 'used_credits', 'receipt_credit'),
|
||||
'Disbursements added');
|
||||
|
||||
// Clean up any explicit credits that have been used
|
||||
foreach ($used_credits AS $credit) {
|
||||
if ($credit['balance'] > 0) {
|
||||
$this->pr(20, compact('credit'),
|
||||
'Update Credit Entry');
|
||||
|
||||
$this->id = $credit['StatementEntry']['id'];
|
||||
$this->saveField('amount', $credit['balance']);
|
||||
}
|
||||
else {
|
||||
$this->pr(20, compact('credit'),
|
||||
'Delete Exhausted Credit Entry');
|
||||
|
||||
$this->del($credit['StatementEntry']['id'], false);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert non-exhausted receipt credit to an explicit one
|
||||
if (!empty($receipt_credit['balance'])) {
|
||||
$credit =& $receipt_credit;
|
||||
|
||||
$explicit_credit = $this->find
|
||||
('first', array('contain' => false,
|
||||
'conditions' =>
|
||||
array(array('transaction_id' => $credit['Transaction']['id']),
|
||||
array('type' => 'SURPLUS')),
|
||||
));
|
||||
|
||||
if (empty($explicit_credit)) {
|
||||
$this->pr(18, compact('credit'),
|
||||
'Create Explicit Credit');
|
||||
|
||||
$result = $this->addStatementEntry
|
||||
(array('type' => 'SURPLUS',
|
||||
'account_id' => $credit['LedgerEntry']['account_id'],
|
||||
'amount' => $credit['balance'],
|
||||
'effective_date' => $credit['Transaction']['stamp'],
|
||||
'transaction_id' => $credit['Transaction']['id'],
|
||||
'customer_id' => $customer_id,
|
||||
'lease_id' => $lease_id,
|
||||
));
|
||||
$ret['Credit'] = $result;
|
||||
if ($result['error'])
|
||||
$ret['error'] = true;
|
||||
}
|
||||
else {
|
||||
$this->pr(18, compact('explicit_credit', 'credit'),
|
||||
'Update Explicit Credit');
|
||||
$EC = new StatementEntry();
|
||||
$EC->id = $explicit_credit['StatementEntry']['id'];
|
||||
$EC->saveField('amount', $credit['balance']);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->prReturn($ret + array('error' => false));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: stats
|
||||
* - Returns summary data from the requested statement entry
|
||||
*/
|
||||
function stats($id = null, $query = null) {
|
||||
$this->prFunctionLevel(array('log' => 19, 'show' => 10));
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
|
||||
$this->queryInit($query);
|
||||
unset($query['group']);
|
||||
|
||||
$stats = array();
|
||||
if (isset($id))
|
||||
$query['conditions'][] = array('StatementEntry.id' => $id);
|
||||
|
||||
$types = array('Charge', 'Disbursement');
|
||||
foreach ($types AS $type_index => $this_name) {
|
||||
$that_name = $types[($type_index + 1) % 2];
|
||||
if ($this_name === 'Charge') {
|
||||
$this_types = $this->debitTypes();
|
||||
$that_types = $this->creditTypes();
|
||||
} else {
|
||||
$this_types = $this->creditTypes();
|
||||
$that_types = $this->debitTypes();
|
||||
}
|
||||
|
||||
$this_query = $query;
|
||||
$this_query['fields'] = array();
|
||||
$this_query['fields'][] = "SUM(StatementEntry.amount) AS total";
|
||||
$this_query['conditions'][] = array('StatementEntry.type' => $this_types);
|
||||
$result = $this->find('first', $this_query);
|
||||
$stats[$this_name] = $result[0];
|
||||
|
||||
$this->pr(17, compact('this_query', 'result'), $this_name.'s');
|
||||
|
||||
// Tally the different types that result in credits towards the charges
|
||||
$stats[$this_name]['reconciled'] = 0;
|
||||
foreach ($that_types AS $that_type) {
|
||||
$lc_that_type = strtolower($that_type);
|
||||
$that_query = $this_query;
|
||||
$that_query['link']["{$that_name}Entry"] = array('fields' => array());
|
||||
$that_query['fields'] = array();
|
||||
if ($this_name == 'Charge')
|
||||
$that_query['fields'][] = "COALESCE(SUM(${that_name}Entry.amount),0) AS $lc_that_type";
|
||||
else
|
||||
$that_query['fields'][] = "COALESCE(SUM(StatementEntry.amount), 0) AS $lc_that_type";
|
||||
$that_query['conditions'][] = array("{$that_name}Entry.type" => $that_type);
|
||||
$result = $this->find('first', $that_query);
|
||||
$stats[$this_name] += $result[0];
|
||||
|
||||
$this->pr(17, compact('that_query', 'result'), "{$this_name}s: $that_type");
|
||||
$stats[$this_name]['reconciled'] += $stats[$this_name][$lc_that_type];
|
||||
}
|
||||
|
||||
// Compute balance information for charges
|
||||
$stats[$this_name]['balance'] =
|
||||
$stats[$this_name]['total'] - $stats[$this_name]['reconciled'];
|
||||
if (!isset($stats[$this_name]['balance']))
|
||||
$stats[$this_name]['balance'] = 0;
|
||||
}
|
||||
|
||||
// 'balance' is simply the difference between
|
||||
// the balances of charges and disbursements
|
||||
$stats['balance'] = $stats['Charge']['balance'] - $stats['Disbursement']['balance'];
|
||||
if (!isset($stats['balance']))
|
||||
$stats['balance'] = 0;
|
||||
|
||||
// 'account_balance' is really only relevant to
|
||||
// callers that have requested charge and disbursement
|
||||
// stats with respect to a particular account.
|
||||
// It represents the difference between inflow
|
||||
// and outflow from that account.
|
||||
$stats['account_balance'] = $stats['Charge']['reconciled'] - $stats['Disbursement']['total'];
|
||||
if (!isset($stats['account_balance']))
|
||||
$stats['account_balance'] = 0;
|
||||
|
||||
return $this->prReturn($stats);
|
||||
}
|
||||
|
||||
}
|
||||
145
site/models/tender.php
Normal file
145
site/models/tender.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
class Tender extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
'TenderType',
|
||||
'Customer',
|
||||
'LedgerEntry',
|
||||
'DepositTransaction' => array(
|
||||
'className' => 'Transaction',
|
||||
),
|
||||
'NsfTransaction' => array(
|
||||
'className' => 'Transaction',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: verifyTender
|
||||
* - Verifies consistenty of new tender data
|
||||
* (not in a pre-existing tender)
|
||||
*/
|
||||
function verifyTender($tender) {
|
||||
$this->prFunctionLevel(10);
|
||||
$this->prEnter(compact('tender'));
|
||||
|
||||
if (empty($tender['tender_type_id'])) {
|
||||
return $this->prReturn(false);
|
||||
}
|
||||
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addTender
|
||||
* - Inserts new Tender into the database
|
||||
*/
|
||||
|
||||
function addTender($tender) {
|
||||
$this->prEnter(compact('tender'));
|
||||
|
||||
$ret = array();
|
||||
if (!$this->verifyTender($tender))
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
|
||||
// Come up with a (not necessarily unique) name for the tender.
|
||||
// For checks & money orders, this will be based on the check
|
||||
// number. For other types of tender, we'll just use the
|
||||
// generic name of the monetary account.
|
||||
// REVISIT <AP>: 20090723
|
||||
// I would like to have cash named "Cash #1234", where
|
||||
// the number would correspond to either the Tender ID
|
||||
// or the LedgerEntry ID.
|
||||
if (empty($tender['name']) && !empty($tender['account_id'])) {
|
||||
$tender['name'] = $this->LedgerEntry->Account->name($tender['account_id']);
|
||||
if ($tender['account_id'] == $this->LedgerEntry->Account->checkAccountID() ||
|
||||
$tender['account_id'] == $this->LedgerEntry->Account->moneyOrderAccountID()) {
|
||||
$tender['name'] .= ' #' . $tender['data1'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->pr(20, array('Tender' => $tender),
|
||||
'Pre-Save');
|
||||
|
||||
$this->create();
|
||||
if (!$this->save($tender))
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
|
||||
$ret['tender_id'] = $this->id;
|
||||
return $this->prReturn($ret + array('error' => false));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: nsf
|
||||
* - Flags the ledger entry as having insufficient funds
|
||||
*
|
||||
* Steps:
|
||||
* - Get information from Check (C1); for amount $A
|
||||
* - Find Bank Deposit matching to Tender
|
||||
* - New Transaction (T1)
|
||||
* - New Bank Deposit (D1)
|
||||
* - New Tender (N1); NSF; D1,
|
||||
* - Add new LedgerEntry (L1a); T1; debit:bank; -$A
|
||||
* - Add new LedgerEntry (L1b); T1; credit:NSF; -$A
|
||||
* - Add new LedgerEntry (L2a); T1; debit:NSF; -$A; N1
|
||||
* - Add new LedgerEntry (L2b); T1; credit:A/R; -$A
|
||||
* - For Tx associated with LE associated with C1:
|
||||
* - For each Disbursement SE of Tx:
|
||||
* - Add new StatementEntry (S1n); T1; DISBURSEMENT; -1*S1n.amount
|
||||
* - New Transaction (T2) (?????)
|
||||
* - Add new StatementEntry (S2); T2; CHARGE; NSF; $35
|
||||
* - Add new LedgerEntry (L3a); T2; credit:NSF-Fee; $35
|
||||
* - Add new LedgerEntry (L3b); T2; debit:A/R; $35
|
||||
* - Set C1.nsf_tx = T1
|
||||
* - Re-Reconcile (customer may have running credit)
|
||||
*/
|
||||
|
||||
function nsf($id, $stamp = null) {
|
||||
$this->prFunctionLevel(30);
|
||||
$this->prEnter(compact('id'));
|
||||
|
||||
// Get information about this NSF item.
|
||||
$this->id = $id;
|
||||
$tender = $this->find
|
||||
('first', array
|
||||
('contain' =>
|
||||
array('LedgerEntry',
|
||||
'DepositTransaction',
|
||||
'NsfTransaction'),
|
||||
));
|
||||
$this->pr(20, compact('tender'));
|
||||
|
||||
if (!empty($tender['NsfTransaction']['id']))
|
||||
die("Item has already been set as NSF");
|
||||
|
||||
if (empty($tender['DepositTransaction']['id']))
|
||||
die("Item has not been deposited yet");
|
||||
|
||||
$tender['Transaction'] = $tender['DepositTransaction'];
|
||||
unset($tender['DepositTransaction']);
|
||||
unset($tender['NsfTransaction']);
|
||||
|
||||
$T = new Transaction();
|
||||
$result = $T->addNsf($tender, $stamp);
|
||||
if ($result['error'])
|
||||
return $this->prReturn(false);
|
||||
|
||||
// Flag the tender as NSF, using the items created from addNsf
|
||||
$this->id = $id;
|
||||
$this->saveField('nsf_transaction_id', $result['nsf_transaction_id']);
|
||||
$this->saveField('nsf_ledger_entry_id', $result['nsf_ledger_entry_id']);
|
||||
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
?>
|
||||
115
site/models/tender_type.php
Normal file
115
site/models/tender_type.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
class TenderType extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
'Account',
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'Tender',
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: accountID
|
||||
* - Returns the intended account ID for receipt of the given tender
|
||||
*/
|
||||
|
||||
function accountID($id) {
|
||||
$this->cacheQueries = true;
|
||||
$item = $this->find('first', array
|
||||
('contain' => false,
|
||||
'conditions' => array('TenderType.id' => $id),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
return $item['TenderType']['account_id'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: paymentTypes
|
||||
* - Returns an array of types that can be used for payments
|
||||
*/
|
||||
|
||||
function paymentTypes($query = null) {
|
||||
$this->queryInit($query);
|
||||
$query['order'][] = 'name';
|
||||
|
||||
$this->cacheQueries = true;
|
||||
$types = $this->find('all', $query);
|
||||
$this->cacheQueries = false;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: paymentTypes
|
||||
* - Returns an array of types that can be deposited
|
||||
*/
|
||||
|
||||
function depositTypes($query = null) {
|
||||
$this->queryInit($query);
|
||||
$query['order'][] = 'name';
|
||||
$query['conditions'][] = array('tillable' => true);
|
||||
|
||||
$this->cacheQueries = true;
|
||||
$types = $this->find('all', $query);
|
||||
$this->cacheQueries = false;
|
||||
|
||||
// Rearrange to be of the form (id => name)
|
||||
$result = array();
|
||||
foreach ($types AS $type)
|
||||
$result[$type['TenderType']['id']] = $type['TenderType']['name'];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: defaultPaymentType
|
||||
* - Returns the ID of the default payment type
|
||||
*/
|
||||
|
||||
function defaultPaymentType() {
|
||||
return $this->nameToID('Check');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: stats
|
||||
* - Returns the stats for the given tender type
|
||||
*/
|
||||
|
||||
function stats($id = null, $query = null) {
|
||||
if (!$id)
|
||||
return null;
|
||||
|
||||
$this->queryInit($query);
|
||||
|
||||
if (!isset($query['link']['Tender']))
|
||||
$query['link']['Tender'] = array('fields' => array());
|
||||
if (!isset($query['link']['Tender']['LedgerEntry']))
|
||||
$query['link']['Tender']['LedgerEntry'] = array('fields' => array());
|
||||
|
||||
$query['fields'][] = "SUM(COALESCE(LedgerEntry.amount,0)) AS 'total'";
|
||||
$query['fields'][] = "SUM(IF(deposit_transaction_id IS NULL, COALESCE(LedgerEntry.amount,0), 0)) AS 'undeposited'";
|
||||
$query['fields'][] = "SUM(IF(deposit_transaction_id IS NULL, 0, COALESCE(LedgerEntry.amount,0))) AS 'deposited'";
|
||||
$query['fields'][] = "SUM(IF(nsf_transaction_id IS NULL, 0, COALESCE(LedgerEntry.amount,0))) AS 'nsf'";
|
||||
|
||||
$this->id = $id;
|
||||
$stats = $this->find('first', $query);
|
||||
return $stats[0];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +1,824 @@
|
||||
<?php
|
||||
class Transaction extends AppModel {
|
||||
|
||||
var $name = 'Transaction';
|
||||
/* var $validate = array( */
|
||||
/* 'stamp' => array('date') */
|
||||
/* ); */
|
||||
|
||||
var $belongsTo = array(
|
||||
'Customer',
|
||||
'Account',
|
||||
'Ledger',
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry',
|
||||
'StatementEntry',
|
||||
'DepositTender' => array(
|
||||
'className' => 'Tender',
|
||||
'foreignKey' => 'deposit_transaction_id',
|
||||
),
|
||||
|
||||
'Charge' => array(
|
||||
'className' => 'StatementEntry',
|
||||
'conditions' => array('Charge.type' => 'CHARGE')
|
||||
),
|
||||
|
||||
'Disbursement' => array(
|
||||
'className' => 'StatementEntry',
|
||||
'conditions' => array('Disbursement.type' => 'DISBURSEMENT')
|
||||
),
|
||||
|
||||
'Debit' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'conditions' => array('Debit.crdr' => 'DEBIT')
|
||||
),
|
||||
|
||||
'Credit' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'conditions' => array('Credit.crdr' => 'CREDIT')
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
|
||||
function beforeSave() {
|
||||
|
||||
if(!empty($this->data['Transaction']['stamp'])) {
|
||||
$this->data['Transaction']['stamp'] =
|
||||
$this->dateFormatBeforeSave($this->data['Transaction']['stamp']);
|
||||
}
|
||||
return true;
|
||||
var $default_log_level = array('log' => 30, 'show' => 30);
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addInvoice
|
||||
* - Adds a new invoice invoice
|
||||
*/
|
||||
|
||||
function addInvoice($data, $customer_id, $lease_id = null) {
|
||||
$this->prEnter(compact('data', 'customer_id', 'lease_id'));
|
||||
|
||||
// Establish the transaction as an invoice
|
||||
$invoice =& $data['Transaction'];
|
||||
$invoice +=
|
||||
array('type' => 'INVOICE',
|
||||
'crdr' => 'DEBIT',
|
||||
'account_id' => $this->Account->accountReceivableAccountID(),
|
||||
'customer_id' => $customer_id,
|
||||
'lease_id' => $lease_id,
|
||||
);
|
||||
|
||||
// Go through the statement entries and flag as charges
|
||||
foreach ($data['Entry'] AS &$entry)
|
||||
$entry += array('type' => 'CHARGE',
|
||||
'crdr' => 'CREDIT',
|
||||
);
|
||||
|
||||
$ids = $this->addTransaction($data['Transaction'], $data['Entry']);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['invoice_id'] = $ids['transaction_id'];
|
||||
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addReceipt
|
||||
* - Adds a new receipt
|
||||
*/
|
||||
|
||||
function addReceipt($data, $customer_id, $lease_id = null) {
|
||||
$this->prEnter(compact('data', 'customer_id', 'lease_id'));
|
||||
|
||||
// Establish the transaction as a receipt
|
||||
$receipt =& $data['Transaction'];
|
||||
$receipt +=
|
||||
array('type' => 'RECEIPT',
|
||||
'crdr' => 'CREDIT',
|
||||
'account_id' => $this->Account->accountReceivableAccountID(),
|
||||
'customer_id' => $customer_id,
|
||||
'lease_id' => $lease_id,
|
||||
);
|
||||
|
||||
// Go through the statement entries and flag as disbursements
|
||||
foreach ($data['Entry'] AS &$entry)
|
||||
$entry += array('type' => 'DISBURSEMENT', // not used
|
||||
'crdr' => 'DEBIT',
|
||||
'account_id' =>
|
||||
(isset($entry['Tender']['tender_type_id'])
|
||||
? ($this->LedgerEntry->Tender->TenderType->
|
||||
accountID($entry['Tender']['tender_type_id']))
|
||||
: null),
|
||||
);
|
||||
|
||||
$ids = $this->addTransaction($data['Transaction'], $data['Entry']);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['receipt_id'] = $ids['transaction_id'];
|
||||
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addWaiver
|
||||
* - Adds a new waiver
|
||||
*/
|
||||
|
||||
function addWaiver($data, $charge_id, $customer_id, $lease_id = null) {
|
||||
$this->prEnter(compact('data', 'charge_id', 'customer_id', 'lease_id'));
|
||||
|
||||
if (count($data['Entry']) != 1)
|
||||
INTERNAL_ERROR("Should be one Entry for addWaiver");
|
||||
|
||||
// Just make sure the disbursement(s) are marked as waivers
|
||||
// and that they go to cover the specific charge.
|
||||
$data['Transaction']['disbursement_type'] = 'WAIVER';
|
||||
$data['Transaction']['assign_charge_entry_id'] = $charge_id;
|
||||
|
||||
// In all other respects this is just a receipt.
|
||||
$ids = $this->addReceipt($data, $customer_id, $lease_id);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['waiver_id'] = $ids['transaction_id'];
|
||||
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addReversal
|
||||
* - Adds a new charge reversal
|
||||
*/
|
||||
|
||||
function addReversal($data, $charge_id, $customer_id, $lease_id = null) {
|
||||
$this->prEnter(compact('data', 'charge_id', 'customer_id', 'lease_id'));
|
||||
|
||||
$data['Transaction'] += array('type' => 'CREDIT_NOTE');
|
||||
foreach ($data['Entry'] AS &$entry)
|
||||
$entry += array('type' => 'REVERSAL');
|
||||
|
||||
// In all other respects this is just an invoice
|
||||
$ids = $this->addInvoice($data, $customer_id, $lease_id);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['reversal_id'] = $ids['transaction_id'];
|
||||
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addDeposit
|
||||
* - Adds a new bank deposit
|
||||
*/
|
||||
|
||||
function addDeposit($data, $account_id) {
|
||||
$this->prEnter(compact('data', 'account_id'));
|
||||
|
||||
// Establish the transaction as a deposit
|
||||
$deposit =& $data['Transaction'];
|
||||
$deposit +=
|
||||
array('type' => 'DEPOSIT',
|
||||
'crdr' => 'DEBIT',
|
||||
'account_id' => $account_id,
|
||||
'customer_id' => null,
|
||||
'lease_id' => null,
|
||||
);
|
||||
|
||||
// Save the list of IDs, so that we can mark their
|
||||
// deposit transaction after it has been created.
|
||||
$tender_ids = array_map(create_function('$item', 'return $item["tender_id"];'),
|
||||
$data['Entry']);
|
||||
|
||||
// Go through the statement entries and re-group by account id
|
||||
$group = array();
|
||||
foreach ($data['Entry'] AS &$entry) {
|
||||
if (!isset($group[$entry['account_id']]))
|
||||
$group[$entry['account_id']] =
|
||||
array('account_id' => $entry['account_id'],
|
||||
'crdr' => 'CREDIT',
|
||||
'amount' => 0);
|
||||
$group[$entry['account_id']]['amount'] += $entry['amount'];
|
||||
}
|
||||
$data['Entry'] = $group;
|
||||
|
||||
$ids = $this->addTransaction($data['Transaction'], $data['Entry']);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['deposit_id'] = $ids['transaction_id'];
|
||||
|
||||
if (!empty($ids['deposit_id'])) {
|
||||
$this->LedgerEntry->Tender->updateAll
|
||||
(array('Tender.deposit_transaction_id' => $ids['deposit_id']),
|
||||
array('Tender.id' => $tender_ids)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addClose
|
||||
* - Adds a new transaction for closing ledgers
|
||||
*/
|
||||
|
||||
function addClose($data) {
|
||||
$this->prEnter(compact('data'));
|
||||
|
||||
// Establish the transaction as a close
|
||||
$close =& $data['Transaction'];
|
||||
$close +=
|
||||
array('type' => 'CLOSE',
|
||||
'crdr' => null,
|
||||
'account_id' => null,
|
||||
'customer_id' => null,
|
||||
'lease_id' => null,
|
||||
);
|
||||
|
||||
$ledger_ids = array();
|
||||
$data['Entry'] = array();
|
||||
foreach ($data['Ledger'] AS $ledger) {
|
||||
$ledger_id = $ledger['old_ledger_id'];
|
||||
$new_ledger_id = $ledger['new_ledger_id'];
|
||||
$amount = $ledger['amount'];
|
||||
$account_id = $this->Account->Ledger->accountID($ledger_id);
|
||||
$crdr = strtoupper($this->Account->fundamentalOpposite($account_id));
|
||||
$comment = "Ledger Carry Forward (c/f)";
|
||||
|
||||
// Save the ledger ID for later, to mark it as closed
|
||||
$ledger_ids[] = $ledger_id;
|
||||
|
||||
// No need to generate ledger entries if there is no balance
|
||||
if (empty($ledger['amount']) || $ledger['amount'] == 0)
|
||||
continue;
|
||||
|
||||
// Add an entry to carry the ledger balance forward
|
||||
$data['Entry'][] = compact('account_id', 'ledger_id', 'new_ledger_id',
|
||||
'crdr', 'amount', 'comment');
|
||||
}
|
||||
unset($data['Ledger']);
|
||||
|
||||
// Add the transaction and carry forward balances
|
||||
$ids = $this->addTransaction($data['Transaction'], $data['Entry']);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['close_id'] = $ids['transaction_id'];
|
||||
|
||||
// Mark the older ledgers as closed
|
||||
if (!empty($ids['close_id'])) {
|
||||
$this->LedgerEntry->Ledger->updateAll
|
||||
(array('Ledger.close_transaction_id' => $ids['close_id']),
|
||||
array('Ledger.id' => $ledger_ids)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addRefund
|
||||
* - Adds a new refund
|
||||
*/
|
||||
|
||||
function addRefund($data, $customer_id, $lease_id = null) {
|
||||
$this->prEnter(compact('data', 'customer_id', 'lease_id'));
|
||||
|
||||
// Establish the transaction as a Refund. This is just like a
|
||||
// Payment, except instead of paying out of the account payable,
|
||||
// it comes from the customer credit in the account receivable.
|
||||
// Someday, perhaps we'll just issue a Credit Note or similar,
|
||||
// but for now, a refund means it's time to actually PAY.
|
||||
$refund =& $data['Transaction'];
|
||||
$refund +=
|
||||
array('account_id' => $this->Account->accountReceivableAccountID());
|
||||
|
||||
// Also, to make it clear to the user, we flag as a REFUND
|
||||
// even though that type works and operates just as PAYMENT
|
||||
foreach ($data['Entry'] AS &$entry)
|
||||
$entry += array('type' => 'REFUND');
|
||||
|
||||
$ids = $this->addPayment($data, $customer_id, $lease_id);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['refund_id'] = $ids['transaction_id'];
|
||||
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addPayment
|
||||
* - Adds a new payment transaction, which is money outflow
|
||||
*/
|
||||
|
||||
function addPayment($data, $customer_id, $lease_id = null) {
|
||||
$this->prEnter(compact('data', 'customer_id', 'lease_id'));
|
||||
|
||||
// Establish the transaction as an payment
|
||||
$payment =& $data['Transaction'];
|
||||
$payment +=
|
||||
array('type' => 'PAYMENT',
|
||||
'crdr' => 'DEBIT',
|
||||
'account_id' => $this->Account->accountPayableAccountID(),
|
||||
'customer_id' => $customer_id,
|
||||
'lease_id' => $lease_id,
|
||||
);
|
||||
|
||||
// Go through the statement entries and flag as payments
|
||||
foreach ($data['Entry'] AS &$entry)
|
||||
$entry += array('type' => 'PAYMENT',
|
||||
'crdr' => 'CREDIT',
|
||||
);
|
||||
|
||||
$ids = $this->addTransaction($data['Transaction'], $data['Entry']);
|
||||
if (isset($ids['transaction_id']))
|
||||
$ids['payment_id'] = $ids['transaction_id'];
|
||||
|
||||
return $this->prReturn($ids);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: verifyTransaction
|
||||
* - Verifies consistenty of new transaction data
|
||||
* (not in a pre-existing transaction)
|
||||
*/
|
||||
function verifyTransaction($transaction, $entries) {
|
||||
//$this->prFunctionLevel(10);
|
||||
$this->prEnter(compact('transaction', 'entries'));
|
||||
|
||||
// Verify required Transaction data is present
|
||||
if (empty($transaction['type']) ||
|
||||
($transaction['type'] != 'CLOSE'
|
||||
&& (empty($transaction['account_id']) ||
|
||||
empty($transaction['crdr']))) ||
|
||||
(in_array($transaction['type'], array('INVOICE', 'RECEIPT'))
|
||||
&& empty($transaction['customer_id']))
|
||||
) {
|
||||
return $this->prReturn(false);
|
||||
}
|
||||
|
||||
// Verify all entries
|
||||
foreach ($entries AS $entry) {
|
||||
// Ensure these items are null'ed out so we don't
|
||||
// accidentally pick up stale data.
|
||||
$le1 = $le1_tender = $le2 = $se = null;
|
||||
extract($entry);
|
||||
if (!empty($le1) && !empty($le2) &&
|
||||
!$this->LedgerEntry->DoubleEntry->verifyDoubleEntry($le1, $le2, $le1_tender)) {
|
||||
return $this->prReturn(false);
|
||||
}
|
||||
if (!empty($se) &&
|
||||
!$this->StatementEntry->verifyStatementEntry($se)) {
|
||||
return $this->prReturn(false);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addTransaction
|
||||
* - Adds a new transaction, and the appropriate ledger and statement
|
||||
* entries, as layed out in the $data['Entry'] array. The array is
|
||||
* overloaded, since it is used to create both ledger _and_ statement
|
||||
* entries.
|
||||
*
|
||||
* $data
|
||||
* - Transaction
|
||||
* - [MANDATORY]
|
||||
* - type (INVOICE, RECEIPT)
|
||||
* - account_id
|
||||
* - crdr
|
||||
* - [OPTIONAL]
|
||||
* - stamp
|
||||
* (default: NOW)
|
||||
* - comment
|
||||
* - [AUTOMATICALLY SET] (if set, these items will be overwritten)
|
||||
* - id
|
||||
* - amount
|
||||
* - customer_id
|
||||
* - ledger_id
|
||||
*
|
||||
* - Entry (array)
|
||||
* - [MANDATORY]
|
||||
* - type (CHARGE, DISBURSEMENT)
|
||||
* - account_id
|
||||
* - crdr
|
||||
* - amount
|
||||
* - [OPTIONAL]
|
||||
* - effective_date
|
||||
* - through_date
|
||||
* - due_date
|
||||
* - comment (used for statement or ledger entry, based on context)
|
||||
* - ledger_entry_comment
|
||||
* - statement_entry_comment
|
||||
* - Tender
|
||||
* - [MANDATORY]
|
||||
* - tender_type_id
|
||||
* - [OPTIONAL]
|
||||
* - name
|
||||
* (default: Entry Account Name & data1)
|
||||
* - data1, data2, data3, data4
|
||||
* - comment
|
||||
* - [AUTOMATICALLY SET] (if set, these items will be overwritten)
|
||||
* - id
|
||||
* - ledger_entry_id
|
||||
* - deposit_transaction_id
|
||||
* - nsf_transaction_id
|
||||
* - [AUTOMATICALLY SET] (if set, these items will be overwritten)
|
||||
* - id
|
||||
* - transaction_id
|
||||
* - ledger_id
|
||||
*
|
||||
*/
|
||||
|
||||
function addTransaction($transaction, $entries, $subtype = null) {
|
||||
$this->prEnter(compact('transaction', 'entries', 'subtype'));
|
||||
|
||||
// Verify that we have a transaction and entries
|
||||
if (empty($transaction) ||
|
||||
($transaction['type'] !== 'CLOSE' && empty($entries)))
|
||||
return $this->prReturn(array('error' => true));
|
||||
|
||||
// set ledger ID as the current ledger of the specified account
|
||||
if (empty($transaction['ledger_id']))
|
||||
$transaction['ledger_id'] =
|
||||
$this->Account->currentLedgerID($transaction['account_id']);
|
||||
|
||||
// Automatically figure out the customer if we have the lease
|
||||
if (!empty($transaction['lease_id']) && empty($transaction['customer_id'])) {
|
||||
$L = new Lease();
|
||||
$L->recursive = -1;
|
||||
$lease = $L->read(null, $transaction['lease_id']);
|
||||
$transaction['customer_id'] = $lease['Lease']['customer_id'];
|
||||
}
|
||||
|
||||
// Break each entry out of the combined statement/ledger entry
|
||||
// and into individual entries appropriate for saving. While
|
||||
// we're at it, calculate the transaction total as well.
|
||||
$transaction['amount'] = 0;
|
||||
foreach ($entries AS &$entry) {
|
||||
// Ensure these items are null'ed out so we don't
|
||||
// accidentally pick up stale data.
|
||||
$le1 = $le1_tender = $le2 = $se = null;
|
||||
|
||||
// Add entry amount into the transaction total
|
||||
$transaction['amount'] += $entry['amount'];
|
||||
|
||||
// Set up our comments, possibly using the default 'comment' field
|
||||
if (empty($entry['ledger_entry_comment'])) {
|
||||
if ($transaction['type'] != 'INVOICE' && !empty($entry['comment']))
|
||||
$entry['ledger_entry_comment'] = $entry['comment'];
|
||||
else
|
||||
$entry['ledger_entry_comment'] = null;
|
||||
}
|
||||
if (empty($entry['statement_entry_comment'])) {
|
||||
if ($transaction['type'] == 'INVOICE' && !empty($entry['comment']))
|
||||
$entry['statement_entry_comment'] = $entry['comment'];
|
||||
else
|
||||
$entry['statement_entry_comment'] = null;
|
||||
}
|
||||
|
||||
// Create one half of the Double Ledger Entry (and the Tender)
|
||||
$le1 =
|
||||
array_intersect_key($entry,
|
||||
array_flip(array('ledger_id', 'account_id', 'crdr', 'amount')));
|
||||
$le1['comment'] = $entry['ledger_entry_comment'];
|
||||
$le1_tender = isset($entry['Tender']) ? $entry['Tender'] : null;
|
||||
|
||||
// Create the second half of the Double Ledger Entry
|
||||
if ($transaction['type'] == 'CLOSE') {
|
||||
$le2 =
|
||||
array_intersect_key($entry,
|
||||
array_flip(array('account_id', 'amount')));
|
||||
$le2['ledger_id'] = $entry['new_ledger_id'];
|
||||
$le2['crdr'] = strtoupper($this->Account->fundamentalOpposite($le1['crdr']));
|
||||
$le2['comment'] = "Ledger Balance Forward (b/f)";
|
||||
}
|
||||
else {
|
||||
$le2 =
|
||||
array_intersect_key($entry,
|
||||
array_flip(array('amount'))) +
|
||||
array_intersect_key($transaction,
|
||||
array_flip(array('ledger_id', 'account_id', 'crdr')));
|
||||
}
|
||||
|
||||
// Create the statement entry
|
||||
$se =
|
||||
array_intersect_key($entry,
|
||||
array_flip(array('type', 'account_id', 'amount',
|
||||
'effective_date', 'through_date', 'due_date',
|
||||
'customer_id', 'lease_id',
|
||||
'charge_entry_id'))) +
|
||||
array_intersect_key($transaction,
|
||||
array_flip(array('customer_id', 'lease_id')));
|
||||
$se['comment'] = $entry['statement_entry_comment'];
|
||||
|
||||
// (DISBURSEMENTS will have statement entries created below, when
|
||||
// assigning credits, and DEPOSITS don't have statement entries)
|
||||
if (empty($entry['charge_entry_id']) &&
|
||||
(empty($transaction['customer_id']) ||
|
||||
($transaction['account_id'] == $this->Account->accountReceivableAccountID() &&
|
||||
$transaction['crdr'] == 'CREDIT')))
|
||||
$se = null;
|
||||
|
||||
// NSF transactions don't use LedgerEntries
|
||||
// REVISIT <AP>: 20090731
|
||||
// Doesn't seem right... probably doing this because of the
|
||||
// single A/R entry we add below for NSF
|
||||
if ($entry['type'] == 'SURPLUS' || $subtype === 'NSF')
|
||||
$le1 = $le1_tender = $le2 = null;
|
||||
|
||||
// Replace combined entry with our new individual entries
|
||||
$entry = compact('le1', 'le1_tender', 'le2', 'se');
|
||||
}
|
||||
|
||||
if ($subtype === 'NSF') {
|
||||
// REVISIT <AP>: 20090731
|
||||
// Should we be doing this, or just doing individual ledger entries
|
||||
// that were created above before we nulled them out
|
||||
array_unshift($entries,
|
||||
array('le1' => array('account_id' => $transaction['account_id'],
|
||||
'crdr' => 'DEBIT',
|
||||
'amount' => $transaction['amount']),
|
||||
'le2' => array('account_id' => $this->Account->accountReceivableAccountID(),
|
||||
'crdr' => 'CREDIT',
|
||||
'amount' => $transaction['amount'])
|
||||
));
|
||||
}
|
||||
|
||||
$this->pr(20, compact('transaction', 'entries'));
|
||||
|
||||
// Move forward, verifying and saving everything.
|
||||
$ret = array();
|
||||
if (!$this->verifyTransaction($transaction, $entries))
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
|
||||
// Save transaction to the database
|
||||
$this->create();
|
||||
if (!$this->save($transaction))
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
$transaction_stamp = $this->field('stamp');
|
||||
|
||||
// Set up our return ids array
|
||||
$ret['transaction_id'] = $this->id;
|
||||
$ret['entries'] = array();
|
||||
$ret['error'] = false;
|
||||
|
||||
// Go through the entries
|
||||
foreach ($entries AS $e_index => &$entry) {
|
||||
// Ensure these items are null'ed out so we don't
|
||||
// accidentally pick up stale data.
|
||||
$le1 = $le1_tender = $le2 = $se = null;
|
||||
extract($entry);
|
||||
|
||||
if (!empty($le1) && !empty($le2)) {
|
||||
$le1['transaction_id'] = $le2['transaction_id'] = $ret['transaction_id'];
|
||||
if (isset($le1_tender))
|
||||
$le1_tender['customer_id'] = $transaction['customer_id'];
|
||||
$result = $this->LedgerEntry->DoubleEntry->addDoubleEntry($le1, $le2, $le1_tender);
|
||||
$ret['entries'][$e_index]['DoubleEntry'] = $result;
|
||||
if ($result['error']) {
|
||||
$ret['error'] = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($se)) {
|
||||
$se['transaction_id'] = $ret['transaction_id'];
|
||||
$se += array('effective_date' => $transaction_stamp);
|
||||
$result = $this->StatementEntry->addStatementEntry($se);
|
||||
$ret['entries'][$e_index]['StatementEntry'] = $result;
|
||||
if ($result['error']) {
|
||||
$ret['error'] = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($transaction['account_id'] == $this->Account->accountReceivableAccountID()
|
||||
&& !$ret['error']) {
|
||||
$result = $this->StatementEntry->assignCredits
|
||||
(null,
|
||||
($transaction['crdr'] == 'CREDIT'
|
||||
? $ret['transaction_id']
|
||||
: null),
|
||||
($transaction['crdr'] == 'CREDIT' && !empty($transaction['assign_charge_entry_id'])
|
||||
? $transaction['assign_charge_entry_id']
|
||||
: null),
|
||||
(!empty($transaction['disbursement_type'])
|
||||
? $transaction['disbursement_type']
|
||||
: null),
|
||||
$transaction['customer_id'],
|
||||
$transaction['lease_id']
|
||||
);
|
||||
|
||||
$ret['assigned'] = $result;
|
||||
if ($result['error'])
|
||||
$ret['error'] = true;
|
||||
}
|
||||
|
||||
return $this->prReturn($ret);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addNsf
|
||||
* - Adds NSF transaction
|
||||
*/
|
||||
|
||||
function addNsf($tender, $stamp) {
|
||||
$this->prEnter(compact('tender', 'stamp'));
|
||||
|
||||
$ret = array();
|
||||
|
||||
// Enter the NSF
|
||||
// This is the transaction pulling money from the bank account
|
||||
// and recording it in the NSF account. It has nothing to do
|
||||
// with the customer statement (charges, disbursements, credits, etc).
|
||||
$bounce_result = $this->addDeposit
|
||||
(array('Transaction' => array(),
|
||||
'Entry' => array(array('tender_id' => null,
|
||||
'account_id' => $this->Account->nsfAccountID(),
|
||||
'amount' => -1 * $tender['LedgerEntry']['amount'],
|
||||
))),
|
||||
$tender['Transaction']['account_id']);
|
||||
|
||||
$this->pr(20, compact('bounce_result'));
|
||||
$ret['bounce'] = $bounce_result;
|
||||
if ($bounce_result['error'])
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
|
||||
// Since we may have saved the nsf transaction with a null
|
||||
// timestamp, query it back out of the database to find out
|
||||
// what timestamp was _really_ specified, for later use.
|
||||
$bounce = $this->find
|
||||
('first', array('contain' => false, 'id' => $bounce_result['transaction_id']));
|
||||
$this->pr(20, compact('bounce'));
|
||||
$stamp = $bounce['Transaction']['stamp'];
|
||||
|
||||
// OK, now move into customer realm, finding all statement
|
||||
// entries that were affected by the bad payment (tender).
|
||||
$nsf_ledger_entry = $this->LedgerEntry->find
|
||||
('first', array
|
||||
('contain' => array('Transaction' =>
|
||||
array(//'fields' => array(),
|
||||
'StatementEntry' =>
|
||||
array(//'fields' => array(),
|
||||
),
|
||||
),
|
||||
),
|
||||
'conditions' => array('LedgerEntry.id' => $tender['LedgerEntry']['id']),
|
||||
));
|
||||
|
||||
$this->pr(20, compact('nsf_ledger_entry'));
|
||||
if (!$nsf_ledger_entry)
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
|
||||
// Build a transaction to adjust all of the statement entries
|
||||
$rollback = array('Transaction' => array(), 'Entry' => array());
|
||||
|
||||
$rollback['Transaction']['stamp'] = $stamp;
|
||||
$rollback['Transaction']['type'] = 'RECEIPT';
|
||||
$rollback['Transaction']['crdr'] = 'DEBIT'; // Unused... keeps verifyTx happy
|
||||
$rollback['Transaction']['account_id'] = $this->Account->nsfAccountID();
|
||||
$rollback['Transaction']['customer_id'] = $tender['Tender']['customer_id'];
|
||||
$rollback['Transaction']['amount'] = -1 * $tender['LedgerEntry']['amount'];
|
||||
|
||||
foreach ($nsf_ledger_entry['Transaction']['StatementEntry'] AS $disbursement) {
|
||||
if ($disbursement['type'] === 'SURPLUS') {
|
||||
$disbursement['type'] = 'VOID';
|
||||
$this->StatementEntry->id = $disbursement['id'];
|
||||
$this->StatementEntry->saveField('type', $disbursement['type']);
|
||||
}
|
||||
else {
|
||||
$rollback['Entry'][] =
|
||||
array('type' => $disbursement['type'],
|
||||
'amount' => -1 * $disbursement['amount'],
|
||||
'account_id' => $this->Account->nsfAccountID(),
|
||||
'customer_id' => $disbursement['customer_id'],
|
||||
'lease_id' => $disbursement['lease_id'],
|
||||
'charge_entry_id' => $disbursement['charge_entry_id'],
|
||||
'effective_date' => $stamp,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Record the transaction, which will un-pay previously paid
|
||||
// charges, void any credits, and other similar work.
|
||||
if (count($rollback['Entry'])) {
|
||||
$rollback_result = $this->addTransaction($rollback['Transaction'], $rollback['Entry'], 'NSF');
|
||||
$this->pr(20, compact('rollback', 'rollback_result'));
|
||||
$ret['rollback'] = $rollback_result;
|
||||
if ($rollback_result['error'])
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
}
|
||||
|
||||
// Add NSF Charge
|
||||
$charge_result = $this->addInvoice
|
||||
(array('Transaction' => compact('stamp'),
|
||||
|
||||
'Entry' =>
|
||||
array
|
||||
(array('account_id' => $this->Account->nsfChargeAccountID(),
|
||||
'effective_date' => $stamp,
|
||||
// REVISIT <AP>: 20090730
|
||||
// BAD, BAD, BAD... who would actually
|
||||
// hardcode a value like this???? ;-)
|
||||
'amount' => 35,
|
||||
'comment' => "NSF: " . $tender['Tender']['name'],
|
||||
),
|
||||
),
|
||||
),
|
||||
$tender['Tender']['customer_id']);
|
||||
|
||||
$this->pr(20, compact('charge_result'));
|
||||
$ret['charge'] = $charge_result;
|
||||
if ($charge_result['error'])
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
|
||||
$ret['nsf_transaction_id'] = $ret['bounce']['transaction_id'];
|
||||
if (!empty($ret['rollback']))
|
||||
$ret['nsf_ledger_entry_id'] = $ret['rollback']['entries'][0]['DoubleEntry']['Entry1']['ledger_entry_id'];
|
||||
return $this->prReturn($ret + array('error' => false));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: stats
|
||||
* - Returns summary data from the requested transaction
|
||||
*/
|
||||
function stats($id = null, $query = null, $balance_account_id = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
|
||||
$this->queryInit($query);
|
||||
unset($query['group']);
|
||||
|
||||
if (isset($id)) {
|
||||
$query['conditions'][] = array('Transaction.id' => $id);
|
||||
$query['group'] = 'Transaction.id';
|
||||
}
|
||||
else
|
||||
// CakePHP seems to automagically add in our ID as a part
|
||||
// of the query conditions, but only on a 'first' query,
|
||||
// not an 'all'. I suppose this is helpful :-/
|
||||
unset($this->id);
|
||||
|
||||
if (empty($query['fields']))
|
||||
$query['fields'] = array();
|
||||
|
||||
$stats = array();
|
||||
foreach ($this->hasMany AS $table => $association) {
|
||||
// Only calculate stats for *Entry types
|
||||
if (!preg_match("/Entry$/", $table) &&
|
||||
!preg_match("/Entry$/", $association['className']))
|
||||
continue;
|
||||
|
||||
$squery = $query;
|
||||
$squery['link'][$table] = array('fields' => array());
|
||||
|
||||
if ($table == 'LedgerEntry') {
|
||||
if (isset($balance_account_id)) {
|
||||
$squery['link']['LedgerEntry']['Account'] = array('fields' => array());
|
||||
$squery['conditions'][] = array("Account.id" => $balance_account_id);
|
||||
}
|
||||
|
||||
$squery['fields'] = array_merge($squery['fields'],
|
||||
$this->LedgerEntry->debitCreditFields(true, $balance_account_id != null));
|
||||
}
|
||||
elseif ($table == 'StatementEntry') {
|
||||
$squery['fields'] = array_merge($squery['fields'],
|
||||
$this->StatementEntry->chargeDisbursementFields(true));
|
||||
}
|
||||
else {
|
||||
$squery['fields'][] = "SUM({$table}.amount) AS total";
|
||||
$squery['fields'][] = "COUNT({$table}.id) AS entries";
|
||||
}
|
||||
$stats[$table] = $this->find('first', $squery);
|
||||
// REVISIT <AP>: 20090724
|
||||
// [0][0] is for when we do an 'all' query. This can
|
||||
// be removed at some point, but I'm keeping it while
|
||||
// toggling between 'all' and 'first' (testing).
|
||||
if (isset($stats[$table][0][0]))
|
||||
$stats[$table] += $stats[$table][0][0];
|
||||
else
|
||||
$stats[$table] += $stats[$table][0];
|
||||
unset($stats[$table][0]);
|
||||
}
|
||||
|
||||
return $this->prReturn($stats);
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -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,113 @@ class Unit extends AppModel {
|
||||
'Lease',
|
||||
);
|
||||
|
||||
function statusEnums() {
|
||||
static $status_enums;
|
||||
if (!isset($status_enums))
|
||||
$status_enums = $this->getEnumValues('status');
|
||||
return $status_enums;
|
||||
}
|
||||
//var $default_log_level = array('log' => 30, 'show' => 15);
|
||||
|
||||
function statusValue($enum) {
|
||||
$enums = $this->statusEnums();
|
||||
return $enums[$enum];
|
||||
}
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* helpers: status enumerations
|
||||
*/
|
||||
|
||||
function occupiedEnumValue() {
|
||||
return statusValue('OCCUPIED');
|
||||
}
|
||||
function statusEnums() {
|
||||
static $status_enums;
|
||||
if (!isset($status_enums))
|
||||
$status_enums = $this->getEnumValues('status');
|
||||
return $status_enums;
|
||||
}
|
||||
|
||||
function conditionOccupied() {
|
||||
return ('Unit.status >= ' . $this->statusValue('OCCUPIED'));
|
||||
}
|
||||
function activeStatusEnums() {
|
||||
return array_diff_key($this->statusEnums(), array(''=>1, 'DELETED'=>1));
|
||||
}
|
||||
|
||||
function conditionVacant() {
|
||||
return ('Unit.status BETWEEN ' .
|
||||
($this->statusValue('UNAVAILABLE')+1) .
|
||||
' AND ' .
|
||||
($this->statusValue('OCCUPIED')-1));
|
||||
}
|
||||
function statusValue($enum) {
|
||||
$enums = $this->statusEnums();
|
||||
return $enums[$enum];
|
||||
}
|
||||
|
||||
function occupiedEnumValue() {
|
||||
return $this->statusValue('OCCUPIED');
|
||||
}
|
||||
|
||||
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: allowedStatusSet
|
||||
* - Returns the status set allowed for the given unit
|
||||
*/
|
||||
function allowedStatusSet($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
$this->id = $id;
|
||||
$old_status = $this->field('status');
|
||||
$old_val = $this->statusValue($old_status);
|
||||
$this->pr(17, compact('old_status', 'old_val'));
|
||||
|
||||
$enums = $this->activeStatusEnums();
|
||||
$this->pr(21, compact('enums'));
|
||||
|
||||
foreach ($enums AS $enum => $val) {
|
||||
if (($old_val < $this->occupiedEnumValue()) !=
|
||||
($val < $this->occupiedEnumValue())) {
|
||||
unset($enums[$enum]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->prReturn($enums);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: updateStatus
|
||||
* - Update the given unit to the given status
|
||||
*/
|
||||
function updateStatus($id, $status, $check = false) {
|
||||
$this->prEnter(compact('id', 'status', 'check'));
|
||||
|
||||
/* if ($check) { */
|
||||
/* $old_status = $this->field('status'); */
|
||||
/* $this->pr(17, compact('old_status')); */
|
||||
/* if ($this->statusValue($old_status) < $this->occupiedEnumValue() && */
|
||||
/* $this->statusValue($status) >= $this->occupiedEnumValue()) */
|
||||
/* { */
|
||||
/* die("Can't transition a unit from vacant to occupied"); */
|
||||
/* return $this->prReturn(false); */
|
||||
/* } */
|
||||
/* if ($this->statusValue($old_status) >= $this->occupiedEnumValue() && */
|
||||
/* $this->statusValue($status) < $this->occupiedEnumValue()) */
|
||||
/* { */
|
||||
/* die("Can't transition a unit from occupied to vacant"); */
|
||||
/* return $this->prReturn(false); */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
if ($check) {
|
||||
if (!array_key_exists($status, $this->allowedStatusSet($id)))
|
||||
return $this->prReturn(false);
|
||||
}
|
||||
|
||||
$this->id = $id;
|
||||
$this->saveField('status', $status);
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
function conditionUnavailable() {
|
||||
return ('Unit.status <= ' . $this->statusValue('UNAVAILABLE'));
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
1243395559
|
||||
a:36:{i:0;s:12:"pmgr_actions";i:1;s:27:"pmgr_actions_late_schedules";i:2;s:17:"pmgr_charge_types";i:3;s:19:"pmgr_config_options";i:4;s:18:"pmgr_config_system";i:5;s:20:"pmgr_config_versions";i:6;s:22:"pmgr_contact_addresses";i:7;s:19:"pmgr_contact_emails";i:8;s:20:"pmgr_contact_methods";i:9;s:19:"pmgr_contact_phones";i:10;s:13:"pmgr_contacts";i:11;s:18:"pmgr_group_options";i:12;s:22:"pmgr_group_permissions";i:13;s:11:"pmgr_groups";i:14;s:19:"pmgr_late_schedules";i:15;s:19:"pmgr_lease_contacts";i:16;s:16:"pmgr_lease_types";i:17;s:11:"pmgr_leases";i:18;s:14:"pmgr_map_units";i:19;s:9:"pmgr_maps";i:20;s:10:"pmgr_notes";i:21;s:18:"pmgr_payment_types";i:22;s:15:"pmgr_site_areas";i:23;s:21:"pmgr_site_memberships";i:24;s:17:"pmgr_site_options";i:25;s:10:"pmgr_sites";i:26;s:24:"pmgr_transaction_charges";i:27;s:25:"pmgr_transaction_payments";i:28;s:25:"pmgr_transaction_receipts";i:29;s:32:"pmgr_transaction_reconciliations";i:30;s:15:"pmgr_unit_sizes";i:31;s:15:"pmgr_unit_types";i:32;s:10:"pmgr_units";i:33;s:17:"pmgr_user_options";i:34;s:10:"pmgr_users";i:35;s:5:"posts";}
|
||||
205
site/views/accounts/collected.ctp
Normal file
205
site/views/accounts/collected.ctp
Normal file
@@ -0,0 +1,205 @@
|
||||
<?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 account_ids = new Array();
|
||||
$("INPUT[type='checkbox']:checked").each(function(i) {
|
||||
account_ids.push($(this).val());
|
||||
});
|
||||
|
||||
var cust = new Array();
|
||||
cust['from_date'] = $('#TxFromDate').val();
|
||||
cust['through_date'] = $('#TxThroughDate').val();
|
||||
cust['account_id'] = account_ids;
|
||||
|
||||
var dynamic_post = new Array();
|
||||
dynamic_post['custom'] = cust;
|
||||
|
||||
$('#collected-total').html('Calculating...');
|
||||
$('#collected-entries-jqGrid').clearGridData();
|
||||
$('#collected-entries-jqGrid').setPostDataItem('dynamic_post', serialize(dynamic_post));
|
||||
$('#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('statement_entries', array
|
||||
(// 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']),
|
||||
'filter' => array(//'StatementEntry.type' => 'DISBURSEMENT',
|
||||
'ChargeEntry.account_id' => $account['id']),
|
||||
'exclude' => array('Account', 'Charge', 'Type'),
|
||||
),
|
||||
));
|
||||
|
||||
?>
|
||||
|
||||
<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>
|
||||
@@ -9,12 +9,19 @@ echo '<div class="account view">' . "\n";
|
||||
* Account Detail Main Section
|
||||
*/
|
||||
|
||||
$rows = array(array('ID', $account['Account']['id']),
|
||||
array('Name', $account['Account']['name']),
|
||||
array('Type', $account['Account']['type']),
|
||||
array('External Name', $account['Account']['external_name']),
|
||||
array('External Account', $account['Account']['external_account']),
|
||||
array('Comment', $account['Account']['comment']));
|
||||
$ledger = $account['Ledger'];
|
||||
$current_ledger = $account['CurrentLedger'];
|
||||
|
||||
if (isset($account['Account']))
|
||||
$account = $account['Account'];
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array('ID', $account['id']);
|
||||
$rows[] = array('Name', $account['name']);
|
||||
$rows[] = array('Type', $account['type']);
|
||||
$rows[] = array('External Name', $account['external_name']);
|
||||
$rows[] = array('External Account', $account['external_account']);
|
||||
$rows[] = array('Comment', $account['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item account detail',
|
||||
@@ -55,20 +62,49 @@ 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
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => $account['name'] . " Ledgers",
|
||||
'filter' => array('Account.id' => $account['id']),
|
||||
)));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* 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
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('grid_div_id' => 'ledger-ledger-entry-list',
|
||||
'caption' => ("Current Ledger: " .
|
||||
"(". $current_ledger['name'] .")"),
|
||||
'filter' => array('Ledger.id' => $current_ledger['id']),
|
||||
'exclude' => array('Account', 'Amount', 'Cr/Dr', 'Balance',
|
||||
empty($account['receipts']) ? 'Tender' : null),
|
||||
'include' => array('Debit', 'Credit', 'Sub-Total'),
|
||||
)));
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Entire Account
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('grid_div_id' => 'account-ledger-entry-list',
|
||||
'grid_setup' => array('hiddengrid' => true),
|
||||
'caption' => "Entire Ledger",
|
||||
'filter' => array('Account.id' => $account['id']),
|
||||
'exclude' => array('Account', 'Amount', 'Cr/Dr', 'Balance',
|
||||
empty($account['receipts']) ? 'Tender' : null),
|
||||
'include' => array('Debit', 'Credit', 'Sub-Total'),
|
||||
)));
|
||||
|
||||
|
||||
|
||||
/* 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";
|
||||
@@ -9,16 +9,24 @@ echo '<div class="contact view">' . "\n";
|
||||
* Contact Detail Main Section
|
||||
*/
|
||||
|
||||
$rows = array(array('First Name', $contact['Contact']['first_name']),
|
||||
array('Middle Name', $contact['Contact']['middle_name']),
|
||||
array('Last Name', $contact['Contact']['last_name']),
|
||||
array('Company', $contact['Contact']['company_name']),
|
||||
array('SSN', $contact['Contact']['id_federal']),
|
||||
array('ID', ($contact['Contact']['id_local']
|
||||
. ($contact['Contact']['id_local']
|
||||
? " - ".$contact['Contact']['id_local_state']
|
||||
: ""))),
|
||||
array('Comment', $contact['Contact']['comment']));
|
||||
$phones = $contact['ContactPhone'];
|
||||
$addresses = $contact['ContactAddress'];
|
||||
$emails = $contact['ContactEmail'];
|
||||
|
||||
if (isset($contact['Contact']))
|
||||
$contact = $contact['Contact'];
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array('First Name', $contact['first_name']);
|
||||
$rows[] = array('Middle Name', $contact['middle_name']);
|
||||
$rows[] = array('Last Name', $contact['last_name']);
|
||||
$rows[] = array('Company', $contact['company_name']);
|
||||
$rows[] = array('SSN', $contact['id_federal']);
|
||||
$rows[] = array('ID', ($contact['id_local']
|
||||
. ($contact['id_local']
|
||||
? " - ".$contact['id_local_state']
|
||||
: "")));
|
||||
$rows[] = array('Comment', $contact['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item contact detail',
|
||||
@@ -57,7 +65,7 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
*/
|
||||
$headers = array('Phone', 'Preference', 'Comment');
|
||||
$rows = array();
|
||||
foreach($contact['ContactPhone'] AS $phone) {
|
||||
foreach($phones AS $phone) {
|
||||
$rows[] = array(FormatHelper::phone($phone['phone']) .
|
||||
($phone['ext'] ? " x".$phone['ext'] : ""),
|
||||
$phone['ContactsMethod']['preference'] . " / " .
|
||||
@@ -74,32 +82,12 @@ 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
|
||||
*/
|
||||
$headers = array('Address', 'Preference', 'Comment');
|
||||
$rows = array();
|
||||
foreach($contact['ContactAddress'] AS $address) {
|
||||
foreach($addresses AS $address) {
|
||||
$rows[] = array(preg_replace("/\n/", "<BR>\n", $address['address']) . "<BR>\n" .
|
||||
$address['city'] . ", " .
|
||||
$address['state'] . " " .
|
||||
@@ -118,12 +106,36 @@ echo $this->element('table',
|
||||
'column_class' => $headers));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Emails
|
||||
*/
|
||||
$headers = array('Email', 'Preference', 'Comment');
|
||||
$rows = array();
|
||||
foreach($emails 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
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Related Customers',
|
||||
'filter' => array('Contact.id' => $contact['id']),
|
||||
)));
|
||||
|
||||
|
||||
/* 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(isset($this->data['Customer']) ? 'Update' : 'Add New Customer') . "\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(isset($this->data['Customer']) ? 'Update' : 'Add New Customer') . "\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>
|
||||
449
site/views/customers/receipt.ctp
Normal file
449
site/views/customers/receipt.ctp
Normal file
@@ -0,0 +1,449 @@
|
||||
<?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();
|
||||
// REVISIT <AP>: 20090806 Add to resetForm()
|
||||
updateCharges($("#customer-id").val());
|
||||
}
|
||||
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'));
|
||||
|
||||
// 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">' +
|
||||
<?php /* '<LEGEND>Payment #%{id} (%{remove})</LEGEND>' + */ ?>
|
||||
'<LEGEND>Payment</LEGEND>' +
|
||||
|
||||
'<DIV ID="payment-div-%{id}">' +
|
||||
<?php
|
||||
// Rearrange to be of the form (id => name)
|
||||
$radioOptions = array();
|
||||
foreach ($paymentTypes AS $type)
|
||||
$radioOptions[$type['TenderType']['id']] = $type['TenderType']['name'];
|
||||
|
||||
echo FormatHelper::phpVarToJavascript
|
||||
($form->input('Entry.%{id}.tender_type_id',
|
||||
array('type' => 'radio',
|
||||
'separator' => '<BR>',
|
||||
'onclick' => ('switchPaymentType(' .
|
||||
'\\\'payment-type-div\\\', ' .
|
||||
'%{id}, ' .
|
||||
'$(this).attr("id")' .
|
||||
')' .
|
||||
//' return false;'
|
||||
''
|
||||
),
|
||||
'legend' => false,
|
||||
'value' => $defaultType,
|
||||
'options' => $radioOptions))) . "+\n";
|
||||
?>
|
||||
'</DIV>' +
|
||||
|
||||
'<DIV ID="payment-amount-div-%{id}" CLASS="input text required">' +
|
||||
' <INPUT TYPE="text" SIZE="20"' +
|
||||
' NAME="data[Entry][%{id}][amount]"' +
|
||||
' CLASS="payment"' +
|
||||
' ID="payment-amount-%{id}" />' +
|
||||
' <LABEL CLASS="payment" FOR="payment-amount-%{id}">Amount</LABEL>' +
|
||||
'</DIV>' +
|
||||
|
||||
<?php
|
||||
foreach ($paymentTypes AS $type) {
|
||||
$type = $type['TenderType'];
|
||||
$div = '<DIV';
|
||||
$div .= ' ID="payment-type-div-%{id}-'.$type['id'].'"';
|
||||
$div .= ' CLASS="payment-type-div-%{id}"';
|
||||
if ($type['id'] != $defaultType)
|
||||
$div .= ' STYLE="display:none;"';
|
||||
$div .= '>';
|
||||
|
||||
$div .= ' <INPUT TYPE="hidden"';
|
||||
$div .= ' NAME="data[Entry][%{id}][type]['.$type['id'].'][tender_type_id]"';
|
||||
$div .= ' VALUE="'.$type['id'].'"';
|
||||
$div .= '>';
|
||||
|
||||
for ($i=1; $i<=4; ++$i) {
|
||||
if (!empty($type["data{$i}_name"])) {
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="20"';
|
||||
$div .= ' NAME="data[Entry][%{id}][type]['.$type['id'].'][data'.$i.']"';
|
||||
$div .= ' CLASS="payment"';
|
||||
$div .= ' ID="payment-data'.$i.'-%{id}" />';
|
||||
$div .= ' <LABEL';
|
||||
$div .= ' CLASS="payment"';
|
||||
$div .= ' FOR="payment-data'.$i.'-%{id}">';
|
||||
$div .= $type["data{$i}_name"];
|
||||
$div .= ' </LABEL>';
|
||||
$div .= '</DIV>';
|
||||
}
|
||||
}
|
||||
|
||||
$div .= '</DIV>';
|
||||
echo "'$div' +\n";
|
||||
}
|
||||
?>
|
||||
|
||||
'</FIELDSET>'
|
||||
);
|
||||
}
|
||||
|
||||
function switchPaymentType(paymentid_base, paymentid, radioid) {
|
||||
$("."+paymentid_base+"-"+paymentid).slideUp();
|
||||
var type_id = $("#"+radioid).val();
|
||||
$("#"+paymentid_base+"-"+paymentid+"-"+type_id).slideDown();
|
||||
}
|
||||
|
||||
|
||||
function updateChargesGrid(idlist) {
|
||||
var dynamic_post = new Array();
|
||||
dynamic_post['idlist'] = idlist;
|
||||
|
||||
$('#charge-entries-jqGrid').setPostDataItem('dynamic_post_replace', serialize(dynamic_post));
|
||||
$('#charge-entries-jqGrid')
|
||||
.setGridParam({ page: 1 })
|
||||
.trigger("reloadGrid");
|
||||
}
|
||||
|
||||
function updateCharges(id) {
|
||||
var url = '<?php echo ($html->url(array("controller" => $this->params["controller"],
|
||||
"action" => "unreconciled"))); ?>';
|
||||
url += '/'+id;
|
||||
|
||||
$('#charge-entries-jqGrid').clearGridData();
|
||||
$("#receipt-balance").html("Calculating...");
|
||||
$("#receipt-charges-caption").html("Please Wait...");
|
||||
|
||||
$.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);
|
||||
},
|
||||
error: function(XMLHttpRequest, textStatus, errorThrown) {
|
||||
/* alert('ERROR'); */
|
||||
/* $('#debug').html('<P>request<BR>'+escape(XMLHttpRequest)); */
|
||||
/* $('#debug').append('<P>status<BR>'+escape(textStatus)); */
|
||||
/* $('#debug').append('<P>error<BR>'+escape(errorThrown)); */
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
--></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('statement_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,
|
||||
),
|
||||
));
|
||||
|
||||
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";
|
||||
?>
|
||||
|
||||
<?php /*
|
||||
<fieldset CLASS="payment superset">
|
||||
<legend>Payments</legend>
|
||||
*/ ?>
|
||||
<input type="hidden" id="payment-entry-id" value="0">
|
||||
<div id="payments"></div>
|
||||
<?php /*
|
||||
<fieldset> <legend>
|
||||
<a href="#" onClick="addPaymentSource(true); return false;">Add Another Payment</a>
|
||||
</legend> </fieldset>
|
||||
</fieldset>
|
||||
*/ ?>
|
||||
|
||||
<?php echo $form->end('Generate Receipt'); ?>
|
||||
|
||||
<?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 $stats['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>
|
||||
@@ -9,12 +9,13 @@ echo '<div class="customer view">' . "\n";
|
||||
* Customer Detail Main Section
|
||||
*/
|
||||
|
||||
$rows = array(array('Name', $customer['Customer']['name']),
|
||||
array('Comment', $customer['Customer']['comment']));
|
||||
$rows = array();
|
||||
$rows[] = array('Name', $customer['Customer']['name']);
|
||||
$rows[] = array('Comment', $customer['Customer']['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item customer detail',
|
||||
'caption' => 'Tenant Info',
|
||||
'caption' => 'Customer Info',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value')));
|
||||
|
||||
@@ -49,28 +50,70 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
/**********************************************************************
|
||||
* Contacts
|
||||
*/
|
||||
echo $this->element('contacts',
|
||||
array('caption' => 'Customer Contacts',
|
||||
'contacts' => $customer['Contact']));
|
||||
|
||||
echo $this->element('contacts', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Customer Contacts',
|
||||
'filter' => array('Customer.id' => $customer['Customer']['id']),
|
||||
'include' => array('Type', 'Active'),
|
||||
)));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Lease History
|
||||
*/
|
||||
echo $this->element('leases',
|
||||
array('caption' => 'Lease History',
|
||||
'leases' => $customer['Lease']));
|
||||
|
||||
echo $this->element('leases', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Lease History',
|
||||
'filter' => array('Customer.id' => $customer['Customer']['id']),
|
||||
'exclude' => array('Customer'),
|
||||
)));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Customer Account History
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries',
|
||||
array('caption' => 'Account',
|
||||
'customer_id' => $customer['Customer']['id'],
|
||||
'ar_account' => true,
|
||||
));
|
||||
echo $this->element('statement_entries', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Account',
|
||||
'filter' => array('Customer.id' => $customer['Customer']['id'],
|
||||
'type !=' => 'VOID'),
|
||||
'exclude' => array('Customer'),
|
||||
'sort_column' => 'Effective',
|
||||
'sort_order' => 'DESC',
|
||||
)));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Customer Ledger History
|
||||
*/
|
||||
|
||||
/*
|
||||
* REVISIT <AP>: 20090724
|
||||
* It's not my intention to really include this, as I believe it
|
||||
* just will confuse folks. However, I've added it at the moment
|
||||
* to help me see the picture of what's happening. It may prove
|
||||
* useful with respect to identifying pre-payments, so after using
|
||||
* it for a while, maybe we can get a feeling for that. I suspect
|
||||
* it will be MUCH more useful just to add the pre-pay amount to
|
||||
* the info box, or provide a list of ledger entries that are JUST
|
||||
* pre-payments. We'll see...
|
||||
*/
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Ledger Entries',
|
||||
'filter' => array('Customer.id' => $customer['Customer']['id'],
|
||||
'Account.id !=' => '-AR-'),
|
||||
'exclude' => array('Customer'),
|
||||
'sort_column' => 'Date',
|
||||
'sort_order' => 'DESC',
|
||||
)));
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
|
||||
162
site/views/double_entries/view.ctp
Normal file
162
site/views/double_entries/view.ctp
Normal file
@@ -0,0 +1,162 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
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.
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* LedgerEntry Detail Main Section
|
||||
*/
|
||||
|
||||
$transaction = $entry['Transaction'];
|
||||
$ledgers = array('debit' => $entry['DebitLedger'],
|
||||
'credit' => $entry['CreditLedger']);
|
||||
$entries = array('debit' => $entry['DebitEntry'],
|
||||
'credit' => $entry['CreditEntry']);
|
||||
$customer = $entry['Customer'];
|
||||
$lease = $entry['Lease'];
|
||||
$entry = $entry['DoubleEntry'];
|
||||
|
||||
$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('Comment', $entry['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item ledger-entry detail',
|
||||
'caption' => 'Double Ledger Entry Detail',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value')));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* LedgerEntry Info Box
|
||||
*/
|
||||
|
||||
/* echo '<div class="infobox">' . "\n"; */
|
||||
/* 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, */
|
||||
/* )); */
|
||||
/* } */
|
||||
|
||||
/* echo '</div>' . "\n"; */
|
||||
|
||||
echo ('<DIV CLASS="ledger-double-entry">' . "\n");
|
||||
foreach ($ledgers AS $type => $ledger) {
|
||||
$rows = array();
|
||||
|
||||
$rows[] = array('ID', $html->link('#' . $entries[$type]['id'],
|
||||
array('controller' => 'entries',
|
||||
'action' => 'view',
|
||||
$entries[$type]['id'])));
|
||||
$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");
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Supporting Elements Section
|
||||
*/
|
||||
|
||||
echo '<div CLASS="detail supporting">' . "\n";
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
/* End page div */
|
||||
echo '</div>' . "\n";
|
||||
@@ -3,27 +3,19 @@
|
||||
// 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['Entries'] = array('index' => 'entries', 'width' => '60', 'align' => 'right');
|
||||
$cols['Name'] = array('index' => 'Account.name', 'formatter' => 'longname');
|
||||
$cols['Type'] = array('index' => 'Account.type', 'formatter' => 'enum');
|
||||
$cols['Entries'] = array('index' => 'entries', 'formatter' => 'number');
|
||||
$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);
|
||||
|
||||
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'))
|
||||
->searchFields(array('Name'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Comment')));
|
||||
|
||||
@@ -2,29 +2,19 @@
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['ID'] = array('index' => 'Contact.id', 'formatter' => 'id');
|
||||
$cols['Last Name'] = array('index' => 'Contact.last_name', 'formatter' => 'name');
|
||||
$cols['First Name'] = array('index' => 'Contact.first_name', 'formatter' => 'name');
|
||||
$cols['Company'] = array('index' => 'Contact.company_name', 'formatter' => 'longname');
|
||||
if (0) { // REVISIT<AP>: Need to figure out how to put this in play
|
||||
$cols['Type'] = array('index' => 'ContactsCustomer.type', 'width' => '75');
|
||||
$cols['Active'] = array('index' => 'ContactsCustomer.active', 'width' => '75');
|
||||
}
|
||||
$cols['Comment'] = array('index' => 'Contact.comment', 'formatter' => 'comment');
|
||||
$cols['ID'] = array('index' => 'Contact.id', 'formatter' => 'id');
|
||||
$cols['Last Name'] = array('index' => 'Contact.last_name', 'formatter' => 'name');
|
||||
$cols['First Name'] = array('index' => 'Contact.first_name', 'formatter' => 'name');
|
||||
$cols['Company'] = array('index' => 'Contact.company_name', 'formatter' => 'longname');
|
||||
$cols['Type'] = array('index' => 'ContactsCustomer.type', 'formatter' => 'enum');
|
||||
$cols['Active'] = array('index' => 'ContactsCustomer.active', 'formatter' => 'enum');
|
||||
$cols['Comment'] = array('index' => 'Contact.comment', 'formatter' => 'comment');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'contacts',
|
||||
'caption' => isset($caption) ? $caption : null);
|
||||
|
||||
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'))
|
||||
->searchFields(array('Last Name', 'First Name', 'Company'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Type', 'Active', 'Comment')));
|
||||
|
||||
@@ -2,33 +2,25 @@
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['ID'] = array('index' => 'Customer.id', 'formatter' => 'id');
|
||||
if (0) // REVISIT<AP>: Need to figure out how to put this in play
|
||||
$cols['Relationship'] = array('index' => 'ContactsCustomer.type', 'width' => '75');
|
||||
$cols['Name'] = array('index' => 'Customer.name', 'formatter' => 'longname');
|
||||
$cols['Last Name'] = array('index' => 'PrimaryContact.last_name', 'formatter' => 'name');
|
||||
$cols['First Name'] = array('index' => 'PrimaryContact.first_name', 'formatter' => 'name');
|
||||
$cols['Leases'] = array('index' => 'lease_count', 'width' => '60');
|
||||
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency', 'sortable' => false);
|
||||
$cols['Comment'] = array('index' => 'Customer.comment', 'formatter' => 'comment');
|
||||
$cols['ID'] = array('index' => 'Customer.id', 'formatter' => 'id');
|
||||
$cols['Relationship'] = array('index' => 'ContactsCustomer.type', 'formatter' => 'enum');
|
||||
$cols['Name'] = array('index' => 'Customer.name', 'formatter' => 'longname');
|
||||
$cols['Last Name'] = array('index' => 'PrimaryContact.last_name', 'formatter' => 'name');
|
||||
$cols['First Name'] = array('index' => 'PrimaryContact.first_name', 'formatter' => 'name');
|
||||
$cols['Leases'] = array('index' => 'current_lease_count', 'formatter' => 'number');
|
||||
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency');
|
||||
$cols['Comment'] = array('index' => 'Customer.comment', 'formatter' => 'comment');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'customers');
|
||||
|
||||
// User requested options have priority
|
||||
$jqGrid_options += compact('grid_div_id', 'grid_id', 'caption', 'grid_setup', 'limit');
|
||||
// Certain fields are only valid with a particular context
|
||||
if (!isset($config['filter']['Contact.id']))
|
||||
$grid->invalidFields('Relationship');
|
||||
|
||||
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'))
|
||||
->searchFields(array('Name', 'Last Name', 'First Name'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Comment')));
|
||||
|
||||
116
site/views/elements/double_entries.ctp
Normal file
116
site/views/elements/double_entries.ctp
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$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');
|
||||
|
||||
$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);
|
||||
|
||||
$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);
|
||||
|
||||
|
||||
if (isset($transaction_id) || isset($reconcile_id))
|
||||
$grid->invalidFields('Transaction');
|
||||
|
||||
if (!isset($collected_account_id))
|
||||
$grid->invalidFields('Last Payment');
|
||||
|
||||
if (isset($account_ftype) || isset($ledger_id) || isset($account_id) || isset($ar_account) || isset($customer_id))
|
||||
$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) || isset($customer_id)) {
|
||||
$grid->invalidFields('Amount');
|
||||
$cols['Sub-Total']['index'] = 'subtotal-balance';
|
||||
} else {
|
||||
$grid->invalidFields(array('Debit', 'Credit'));
|
||||
$cols['Sub-Total']['index'] = 'subtotal-LedgerEntry.amount';
|
||||
}
|
||||
|
||||
// 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 (isset($lease_id) || $group_by_tx)
|
||||
$grid->invalidFields(array('Lease', 'Unit'));
|
||||
|
||||
if (!isset($reconcile_id) && !isset($collected_account_id))
|
||||
$grid->invalidFields('Applied');
|
||||
else
|
||||
$cols['Sub-Total']['index'] = 'subtotal-applied';
|
||||
|
||||
if (isset($account_ftype) || isset($collected_account_id))
|
||||
$grid->invalidFields('Sub-Total');
|
||||
|
||||
|
||||
// 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)) {
|
||||
$config['action'] = 'reconcile';
|
||||
$grid->customData(compact('reconcile_id'))->limit(20);
|
||||
}
|
||||
|
||||
if (isset($collected_account_id)) {
|
||||
$config['action'] = 'collected';
|
||||
$account_id = $collected_account_id;
|
||||
$grid->limit(50);
|
||||
$grid->sortField('Last Payment');
|
||||
}
|
||||
|
||||
if (isset($entry_ids))
|
||||
$grid->id_list($entry_ids);
|
||||
|
||||
// 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')
|
||||
);
|
||||
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();
|
||||
|
||||
@@ -59,84 +62,111 @@ $javascript->link('pmgr_jqGrid', false);
|
||||
// we'll just pass the desired fields to the controller
|
||||
// as part of the data fetch.
|
||||
$url = $html->url(array('controller' => $controller,
|
||||
'action' => 'jqGridData',
|
||||
'action' => 'gridData',
|
||||
'debug' => 0,
|
||||
));
|
||||
|
||||
// Create extra parameters that jqGrid will pass to our
|
||||
// controller whenever data is requested.
|
||||
// 'fields' will allow the controller to return only the
|
||||
// requested fields, and in the right order. Since fields
|
||||
// is a complex structure (an array), we'll need to
|
||||
// serialize it first for transport over HTTP.
|
||||
// requested fields, and in the right order.
|
||||
$postData = array();
|
||||
$postData['fields'] = serialize(array_map(create_function('$col',
|
||||
'return $col["index"];'),
|
||||
array_values($jqGridColumns)));
|
||||
$postData['fields'] = array_map(create_function('$col',
|
||||
'return $col["index"];'),
|
||||
array_values($jqGridColumns));
|
||||
|
||||
// Determine if we're to be using a custom list, or if
|
||||
// the data will simply be action based.
|
||||
if (isset($custom_ids)) {
|
||||
if (!isset($action))
|
||||
$action = 'idlist';
|
||||
$postData['idlist'] = serialize($custom_ids);
|
||||
}
|
||||
elseif (!isset($action)) {
|
||||
$action = null;
|
||||
$postData['idlist'] = $custom_ids;
|
||||
}
|
||||
|
||||
if (isset($custom_post_data)) {
|
||||
$postData['custom'] = serialize($custom_post_data);
|
||||
}
|
||||
if (isset($nolinks))
|
||||
$postData['nolinks'] = true;
|
||||
|
||||
// 'action' will ensure that the controller provides the
|
||||
// correct subset of data
|
||||
$postData['action'] = $action;
|
||||
// 'action' will ensure that the controller does the right thing
|
||||
// 'filter' allows the app controller to automagic filter
|
||||
// 'custom' is for use solely by derived controllers
|
||||
$postData['action'] = isset($action) ? $action : null;
|
||||
$postData['filter'] = isset($filter) ? $filter : null;
|
||||
$postData['custom'] = isset($custom_post_data) ? $custom_post_data : null;
|
||||
|
||||
|
||||
// Perform column customizations.
|
||||
// This will largely be based off of the 'formatter' parameter,
|
||||
// but could be on any pertinent condition.
|
||||
foreach ($jqGridColumns AS &$col) {
|
||||
foreach ($jqGridColumns AS $header => &$col) {
|
||||
$default = array();
|
||||
|
||||
// Make sure every column has a name
|
||||
$default['name'] = preg_replace("/\./", '-', $col['index']);
|
||||
$default['name'] = preg_replace("/\./", '-', $col['index']);
|
||||
$default['force'] = isset($col['forcewidth']) ? $col['forcewidth'] : null;
|
||||
|
||||
// Perform customization based on formatter
|
||||
if (isset($col['formatter'])) {
|
||||
if ($col['formatter'] === 'id') {
|
||||
// Switch currency over to our own custom formatting
|
||||
// Use our custom formatting for ids
|
||||
$col['formatter'] = array('--special' => 'idFormatter');
|
||||
$default['width'] = 50;
|
||||
$default['align'] = 'center';
|
||||
|
||||
// For IDs, force the width by default,
|
||||
// unless otherwise instructed NOT to.
|
||||
if (!isset($default['force']))
|
||||
$default['force'] = true;
|
||||
}
|
||||
elseif ($col['formatter'] === 'number') {
|
||||
$default['width'] = 60;
|
||||
$default['align'] = 'right';
|
||||
|
||||
// No special formatting for number
|
||||
unset($col['formatter']);
|
||||
}
|
||||
elseif ($col['formatter'] === 'currency') {
|
||||
// Switch currency over to our own custom formatting
|
||||
// Use our custom formatting for currency
|
||||
$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') {
|
||||
elseif (preg_match("/^(long|short)?name$/",
|
||||
$col['formatter'], $matches)) {
|
||||
$default['width'] = 100;
|
||||
if ($col['formatter'] === 'longname')
|
||||
if (!empty($matches[1]) && $matches[1] === 'long')
|
||||
$default['width'] *= 1.5;
|
||||
if (!empty($matches[1]) && $matches[1] === 'short')
|
||||
$default['width'] *= 0.7;
|
||||
|
||||
// No special formatting for name
|
||||
unset($col['formatter']);
|
||||
}
|
||||
elseif ($col['formatter'] === 'enum') {
|
||||
$default['width'] = 60;
|
||||
//$default['align'] = 'right';
|
||||
|
||||
// No special formatting for enum
|
||||
unset($col['formatter']);
|
||||
}
|
||||
elseif ($col['formatter'] === 'comment') {
|
||||
$default['width'] = 300;
|
||||
$default['width'] = 150;
|
||||
$default['sortable'] = false;
|
||||
|
||||
// No special formatting for comment
|
||||
unset($col['formatter']);
|
||||
}
|
||||
// else just let the formatter pass through untouched
|
||||
|
||||
// Just a rough approximation to ensure columns
|
||||
// are wide enough to fully display their header.
|
||||
$min_width = strlen($header) * 10;
|
||||
if ((!isset($default['width']) || $default['width'] < $min_width) && !$default['force'])
|
||||
$default['width'] = $min_width;
|
||||
}
|
||||
|
||||
$col = array_merge($default, $col);
|
||||
@@ -151,27 +181,61 @@ 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 class="debug grid-query"> :: <span id="'.$grid_id.'-query"></span></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',
|
||||
'datatype' => 'xml',
|
||||
'url' => $url,
|
||||
'postData' => $postData,
|
||||
// Since postData is a complex structure (an array), we'll
|
||||
// need to serialize it first for transport over HTTP.
|
||||
'postData' => array('post' => serialize($postData)),
|
||||
'colNames' => array_keys($jqGridColumns),
|
||||
'colModel' => array('--special' => $jqGridColumns),
|
||||
'height' => $height,
|
||||
'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 +248,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,23 @@
|
||||
|
||||
// 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);
|
||||
|
||||
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'))
|
||||
->searchFields(array('Customer', 'Unit'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Comment')));
|
||||
|
||||
@@ -1,102 +1,30 @@
|
||||
<?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');
|
||||
|
||||
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['Account'] = array('index' => 'Account.name', 'formatter' => 'name');
|
||||
$cols['Cr/Dr'] = array('index' => 'LedgerEntry.crdr', 'formatter' => 'enum');
|
||||
$cols['Tender'] = array('index' => 'Tender.name', 'formatter' => 'longname');
|
||||
$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['Balance'] = array('index' => 'balance', 'formatter' => 'currency');
|
||||
$cols['Sub-Total'] = array('index' => 'subtotal-balance', 'formatter' => 'currency', 'sortable' => false);
|
||||
|
||||
if ($applied_amount) {
|
||||
$cols['Applied'] = array('index' => "Reconciliation.amount", 'formatter' => 'currency');
|
||||
}
|
||||
|
||||
if ($subtotal_amount) {
|
||||
$cols['Sub-Total'] = array('index' => 'subtotal', 'formatter' => 'currency', 'sortable' => false);
|
||||
}
|
||||
|
||||
$custom_post_data = compact('ledger_id', 'account_id', 'ar_account',
|
||||
'account_type', 'account_ftype',
|
||||
'customer_id', 'lease_id', 'transaction_id', 'notxgroup');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'ledger_entries',
|
||||
);
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
if (isset($reconcile_id)) {
|
||||
$custom_post_data += compact('reconcile_id');
|
||||
$jqGrid_options += array('limit' => 5);
|
||||
}
|
||||
|
||||
$jqGrid_options += compact('custom_post_data');
|
||||
$jqGrid_options['sort_column'] = 'Date';
|
||||
echo $this->element('jqGrid', $jqGrid_options);
|
||||
// Render the grid
|
||||
$grid
|
||||
->limit(50)
|
||||
->columns($cols)
|
||||
->sortField('Date')
|
||||
->defaultFields(array('Entry', 'Date', 'Amount'))
|
||||
->searchFields(array('Customer', 'Unit'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Debit', 'Credit', 'Balance', 'Sub-Total', 'Comment')));
|
||||
|
||||
|
||||
@@ -3,27 +3,21 @@
|
||||
// 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['Entries'] = array('index' => 'entries', 'width' => '60', 'align' => 'right');
|
||||
$cols['Name'] = array('index' => 'Ledger.name', 'formatter' => 'name');
|
||||
$cols['Account'] = array('index' => 'Account.name', 'formatter' => 'longname');
|
||||
$cols['Open Date'] = array('index' => 'PriorCloseTransaction.stamp', 'formatter' => 'date');
|
||||
$cols['Close Date'] = array('index' => 'CloseTransaction.stamp', 'formatter' => 'date');
|
||||
$cols['Comment'] = array('index' => 'Ledger.comment', 'formatter' => 'comment');
|
||||
$cols['Entries'] = array('index' => 'entries', 'formatter' => 'number');
|
||||
$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);
|
||||
|
||||
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', 'ASC')
|
||||
->defaultFields(array('ID', 'Name', 'Account'))
|
||||
->searchFields(array('Account', 'Comment'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Open Date', 'Comment')));
|
||||
|
||||
@@ -5,20 +5,15 @@ $cols = array();
|
||||
$cols['ID'] = array('index' => 'Map.id', 'formatter' => 'id');
|
||||
$cols['Name'] = array('index' => 'Map.name', 'formatter' => 'longname');
|
||||
$cols['Site Area'] = array('index' => 'SiteArea.name', 'formatter' => 'longname');
|
||||
$cols['Width'] = array('index' => 'Map.width', 'width' => '50', 'align' => 'right');
|
||||
$cols['Depth'] = array('index' => 'Map.depth', 'width' => '50', 'align' => 'right');
|
||||
$cols['Width'] = array('index' => 'Map.width', 'formatter' => 'number');
|
||||
$cols['Depth'] = array('index' => 'Map.depth', 'formatter' => 'number');
|
||||
$cols['Comment'] = array('index' => 'Map.comment', 'formatter' => 'comment');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'maps',
|
||||
'caption' => isset($caption) ? $caption : null);
|
||||
|
||||
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'))
|
||||
->searchFields(array('Name'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array()));
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
// Define the table columns
|
||||
$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);
|
||||
|
||||
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);
|
||||
45
site/views/elements/statement_entries.ctp
Normal file
45
site/views/elements/statement_entries.ctp
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['Transaction'] = array('index' => 'Transaction.id', 'formatter' => 'id');
|
||||
$cols['Entry'] = array('index' => 'StatementEntry.id', 'formatter' => 'id');
|
||||
|
||||
$cols['Date'] = array('index' => 'Transaction.stamp', 'formatter' => 'date');
|
||||
$cols['Effective'] = array('index' => 'StatementEntry.effective_date', 'formatter' => 'date');
|
||||
$cols['Through'] = array('index' => 'StatementEntry.through_date', 'formatter' => 'date');
|
||||
|
||||
$cols['Account'] = array('index' => 'Account.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' => 'shortname');
|
||||
|
||||
$cols['Comment'] = array('index' => 'StatementEntry.comment', 'formatter' => 'comment', 'width'=>150);
|
||||
|
||||
$cols['Type'] = array('index' => 'StatementEntry.type', 'formatter' => 'enum', 'width'=>120);
|
||||
$cols['Debit'] = array('index' => 'charge', 'formatter' => 'currency');
|
||||
$cols['Credit'] = array('index' => 'disbursement', 'formatter' => 'currency');
|
||||
|
||||
$cols['Applied'] = array('index' => "applied", 'formatter' => 'currency');
|
||||
$cols['Sub-Total'] = array('index' => 'subtotal-balance', 'formatter' => 'currency', 'sortable' => false);
|
||||
|
||||
|
||||
if (isset($subtotal_column))
|
||||
$cols['Sub-Total']['index'] =
|
||||
'subtotal-' . $cols[$subtotal_column]['index'];
|
||||
|
||||
// Include custom data
|
||||
$grid->customData(compact('statement_entry_id'));
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Date')
|
||||
->defaultFields(array('Entry', 'Date', 'Charge', 'Payment'))
|
||||
->searchFields(array('Customer', 'Unit'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Through', 'Lease',
|
||||
'Applied', 'Sub-Total',
|
||||
'Comment')));
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
21
site/views/elements/tenders.ctp
Normal file
21
site/views/elements/tenders.ctp
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
//$cols['ID'] = array('index' => 'Tender.id', 'formatter' => 'id');
|
||||
$cols['Date'] = array('index' => 'Transaction.stamp', 'formatter' => 'date');
|
||||
$cols['Customer'] = array('index' => 'Customer.name', 'formatter' => 'longname');
|
||||
$cols['Item'] = array('index' => 'Tender.name', 'formatter' => 'longname');
|
||||
$cols['Type'] = array('index' => 'TenderType.name', 'formatter' => 'name');
|
||||
$cols['Comment'] = array('index' => 'Tender.comment', 'formatter' => 'comment');
|
||||
$cols['Amount'] = array('index' => 'LedgerEntry.amount', 'formatter' => 'currency');
|
||||
$cols['Sub-Total'] = array('index' => 'subtotal-LedgerEntry.amount', 'formatter' => 'currency');
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Date')
|
||||
->defaultFields(array('Date', 'Name', 'Amount'))
|
||||
->searchFields(array('Name', 'Type'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Sub-Total')));
|
||||
@@ -3,25 +3,18 @@
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['ID'] = array('index' => 'Transaction.id', 'formatter' => 'id');
|
||||
$cols['Type'] = array('index' => 'Transaction.type', 'formatter' => 'enum');
|
||||
//$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['Due'] = array('index' => 'Transaction.due_date', 'formatter' => 'date');
|
||||
$cols['Timestamp'] = array('index' => 'Transaction.stamp', 'formatter' => 'date');
|
||||
$cols['Amount'] = array('index' => 'Transaction.amount', 'formatter' => 'currency');
|
||||
$cols['entries'] = array('index' => 'entries', 'formatter' => 'number');
|
||||
$cols['Comment'] = array('index' => 'Transaction.comment', 'formatter' => 'comment');
|
||||
|
||||
$jqGrid_options = array('jqGridColumns' => $cols,
|
||||
'controller' => 'transactions',
|
||||
'caption' => isset($caption) ? $caption : null);
|
||||
|
||||
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('Timestamp')
|
||||
->defaultFields(array('ID', 'Timestamp'))
|
||||
->searchFields(array('Type', 'Comment'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Comment')));
|
||||
|
||||
@@ -2,26 +2,21 @@
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['Sort'] = array('index' => 'Unit.sort_order', 'hidden' => true);
|
||||
$cols['Walk'] = array('index' => 'Unit.walk_order', 'formatter' => 'number');
|
||||
$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['Unit'] = array('index' => 'Unit.name', 'formatter' => 'shortname');
|
||||
$cols['Size'] = array('index' => 'UnitSize.name', 'formatter' => 'shortname');
|
||||
$cols['Rent'] = array('index' => 'Unit.rent', 'formatter' => 'currency');
|
||||
$cols['Status'] = array('index' => 'Unit.status', 'formatter' => 'name'); // We have enough real estate
|
||||
$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);
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Sort')
|
||||
->defaultFields(array('Sort', 'ID', 'Unit'))
|
||||
->searchFields(array('Unit', 'Size', 'Status'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Walk', 'Comment')));
|
||||
|
||||
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, $dollar_sign = null) {
|
||||
if (!isset($amount))
|
||||
return '-';
|
||||
//return null;
|
||||
|
||||
return (isset($amount)
|
||||
? self::$number->currency($amount)
|
||||
: null);
|
||||
$currency = self::$number->currency($amount,
|
||||
isset($dollar_sign) ? $dollar_sign : '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) {
|
||||
@@ -35,26 +41,32 @@ class FormatHelper extends AppHelper {
|
||||
$date_fmt = 'm/d/Y';
|
||||
return (self::$time->format($date_fmt, $date) .
|
||||
($age
|
||||
? ' (' . self::age($date) . ')'
|
||||
? ' (' . self::age($date, 60*60*24) . ')'
|
||||
: ''));
|
||||
}
|
||||
|
||||
function datetime($datetime) {
|
||||
function datetime($datetime, $age = false) {
|
||||
if (!$datetime) return null;
|
||||
return self::$time->nice($datetime);
|
||||
return (self::$time->nice($datetime) .
|
||||
($age
|
||||
? ' (' . self::age($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) {
|
||||
@@ -69,7 +81,7 @@ class FormatHelper extends AppHelper {
|
||||
return $comment;
|
||||
}
|
||||
|
||||
function age($datetime) {
|
||||
function age($datetime, $min_span = 0) {
|
||||
if (!isset($datetime))
|
||||
return null;
|
||||
|
||||
@@ -81,52 +93,81 @@ class FormatHelper extends AppHelper {
|
||||
$timeto = $backwards ? $seconds : $now;
|
||||
$span = $timeto - $timefrom;
|
||||
|
||||
// Display seconds if under 45 seconds
|
||||
if ($span === 0) {
|
||||
//pr(compact('now', 'seconds', 'backwards', 'timefrom', 'timeto', 'span', 'min_span'));
|
||||
|
||||
// If now, just return so
|
||||
if ($span === 0)
|
||||
return __('now', true);
|
||||
}
|
||||
if ($span < 45) {
|
||||
|
||||
// Display seconds if under 45 seconds
|
||||
if ($span < 45 && $span >= $min_span) {
|
||||
$approx = round($span);
|
||||
$unit = 'second';
|
||||
}
|
||||
|
||||
// Display minutes if under 45 minutes
|
||||
elseif (($span /= 60) < 45) {
|
||||
$approx = round($span);
|
||||
if (!isset($approx)) {
|
||||
$unit = 'minute';
|
||||
$span /= 60; $min_span /= 60;
|
||||
if ($span < 45 && ($span >= $min_span || $min_span <= 1))
|
||||
$approx = round($span);
|
||||
}
|
||||
// Display hours if under 18 hours
|
||||
elseif (($span /= 60) < 18) {
|
||||
$approx = round($span);
|
||||
$unit = 'hour';
|
||||
}
|
||||
// Display days if under 6.5 days
|
||||
elseif (($span /= 24) < 6.5) {
|
||||
$approx = round($span);
|
||||
$unit = 'day';
|
||||
}
|
||||
// Display weeks if less than 8 weeks
|
||||
elseif (($span /= 7) < 8) {
|
||||
$approx = round($span);
|
||||
$unit = 'week';
|
||||
}
|
||||
// Display months if less than 20 months
|
||||
elseif (($span /= (365.2425 / (7 * 12))) < 20) {
|
||||
$approx = round($span);
|
||||
$unit = 'month';
|
||||
|
||||
// Months are from 28-31 days. If it's too
|
||||
// close to being an exact month, just fudge
|
||||
// by saying the result is 'about' N months
|
||||
// instead of 'almost' or 'over' N months,
|
||||
// since we can't be accurate on this without
|
||||
// taking into account the day of the week.
|
||||
if ((abs($span - $approx) * (365.2425 / 12)) < 3)
|
||||
$relative = 'about';
|
||||
// Display hours if under 18 hours
|
||||
if (!isset($approx)) {
|
||||
$unit = 'hour';
|
||||
$span /= 60; $min_span /= 60;
|
||||
if ($span < 18 && ($span >= $min_span || $min_span <= 1))
|
||||
$approx = round($span);
|
||||
}
|
||||
else {
|
||||
$span /= 12;
|
||||
$approx = round($span);
|
||||
|
||||
// Display days if under 6.5 days
|
||||
if (!isset($approx)) {
|
||||
$unit = 'day';
|
||||
$span /= 24; $min_span /= 24;
|
||||
if ($span < 6.5 && ($span >= $min_span || $min_span <= 1))
|
||||
$approx = round($span);
|
||||
}
|
||||
|
||||
// Display weeks if less than 8 weeks
|
||||
if (!isset($approx)) {
|
||||
$unit = 'week';
|
||||
$span /= 7; $min_span /= 7;
|
||||
if ($span < 8 && ($span >= $min_span || $min_span <= 1))
|
||||
$approx = round($span);
|
||||
}
|
||||
|
||||
// Display months if less than 20 months
|
||||
if (!isset($approx)) {
|
||||
$unit = 'month';
|
||||
$span /= 365.2425 / (7*12); $min_span /= 365.2425 / (7*12);
|
||||
if ($span < 20 && ($span >= $min_span || $min_span <= 1)) {
|
||||
$approx = round($span);
|
||||
// Months are from 28-31 days. If it's too
|
||||
// close to being an exact month, just fudge
|
||||
// by saying the result is 'about' N months
|
||||
// instead of 'almost' or 'over' N months,
|
||||
// since we can't be accurate on this without
|
||||
// taking into account the day of the week.
|
||||
if ((abs($span - $approx) * (365.2425 / 12)) < 3)
|
||||
$relative = 'about';
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, just display years
|
||||
if (!isset($approx)) {
|
||||
$unit = 'year';
|
||||
$span /= 12; $min_span /= 12;
|
||||
$approx = round($span);
|
||||
}
|
||||
|
||||
//pr(compact('span', 'min_span', 'approx', 'unit'));
|
||||
|
||||
if ($approx == 0) {
|
||||
if ($unit == 'day')
|
||||
return __('today', true);
|
||||
|
||||
return __('this ' . $unit, true);
|
||||
}
|
||||
|
||||
return (__(isset($relative)
|
||||
@@ -211,7 +252,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
|
||||
|
||||
227
site/views/helpers/grid.php
Normal file
227
site/views/helpers/grid.php
Normal file
@@ -0,0 +1,227 @@
|
||||
<?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);
|
||||
$this->jqGrid_options['action'] = 'idlist';
|
||||
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']);
|
||||
unset($config['include'], $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));
|
||||
|
||||
// Make sure search fields are all part of the inclusion set
|
||||
$this->jqGrid_options['search_fields']
|
||||
= array_intersect($this->jqGrid_options['search_fields'], $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);
|
||||
unset($config['rows']);
|
||||
}
|
||||
|
||||
// One more exception, as the search fields get
|
||||
// defined, but not passed to jqGrid unless
|
||||
// specifically requested.
|
||||
if (isset($config['search']))
|
||||
unset($config['search']);
|
||||
else
|
||||
unset($this->jqGrid_options['search_fields']);
|
||||
|
||||
// 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);
|
||||
|
||||
//pr(compact('config') + array('jqGrid_options' => $this->jqGrid_options));
|
||||
echo $view->element('jqGrid', $this->jqGrid_options);
|
||||
|
||||
// Since we only have one instance of this class
|
||||
// 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,6 +33,7 @@
|
||||
echo $html->meta('icon') . "\n";
|
||||
echo $html->css('cake.generic') . "\n";
|
||||
echo $html->css('layout') . "\n";
|
||||
echo $html->css('print', null, array('media' => 'print')) . "\n";
|
||||
echo $html->css('sidemenu') . "\n";
|
||||
//echo $html->css('jquery/base/ui.all') . "\n";
|
||||
//echo $html->css('jquery/smoothness/ui.all') . "\n";
|
||||
|
||||
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' => 'Entry.%{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\',\'Entry%{id}EffectiveDate\'); return false;">BOM</A>',
|
||||
),
|
||||
"through_date" => array('opts' =>
|
||||
array('type' => 'text'),
|
||||
'between' => '<A HREF="#" ONCLICK="datepickerEOM(\'Entry%{id}EffectiveDate\',\'Entry%{id}ThroughDate\'); return false;">EOM</A>',
|
||||
),
|
||||
"amount" => true,
|
||||
"comment" => array('opts' => array('size' => 50)),
|
||||
),
|
||||
))) . "+\n";
|
||||
?>
|
||||
|
||||
'</FIELDSET>'
|
||||
);
|
||||
|
||||
$("#Entry"+id+"EffectiveDate")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
|
||||
$("#Entry"+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>
|
||||
106
site/views/leases/refund.ctp
Normal file
106
site/views/leases/refund.ctp
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="refund input">' . "\n";
|
||||
echo '<H2>Issue Refund</H2>' . "\n";
|
||||
echo '<P>Enter the amount to refund, and the account to pay it from.' . "\n";
|
||||
echo '<P><BR>' . "\n";
|
||||
|
||||
if (isset($lease)) {
|
||||
$customer = $lease['Customer'];
|
||||
$unit = $lease['Unit'];
|
||||
}
|
||||
|
||||
if (isset($customer['Customer']))
|
||||
$customer = $customer['Customer'];
|
||||
|
||||
if (isset($lease['Lease']))
|
||||
$lease = $lease['Lease'];
|
||||
|
||||
// We're not actually using a grid to select the customer / lease
|
||||
// but we could/should be, and the result would be selection-text
|
||||
echo ('<DIV CLASS="refund grid-selection-text">' .
|
||||
'<TABLE>' . "\n");
|
||||
|
||||
echo ('<TR><TD style="padding-right: 1em;">' . $customer['name'] . '</TD>' .
|
||||
' <TD>' . '(Customer #' . $customer['id'] . ')' . '</TD>' .
|
||||
'</TR>' . "\n");
|
||||
|
||||
if (isset($lease))
|
||||
echo ('<TR><TD style="padding-right: 1em;">' . 'Unit ' . $unit['name'] . '</TD>' .
|
||||
' <TD>' . '(Lease #' . $lease['number'] . ')' . '</TD>' .
|
||||
'</TR>' . "\n");
|
||||
|
||||
echo ('<TR><TD style="padding-right: 1em;">Refundable Balance:</TD>' .
|
||||
' <TD>' . FormatHelper::currency($balance) . '</TD>' .
|
||||
'</TR>' . "\n");
|
||||
|
||||
echo ('</TABLE>' .
|
||||
'</DIV>' . "\n");
|
||||
|
||||
|
||||
echo $form->create(null, array('id' => 'refund-form',
|
||||
'url' => array('controller' => 'transactions',
|
||||
'action' => 'postRefund')));
|
||||
|
||||
|
||||
// REVISIT <AP>: 20090805
|
||||
// Add Tender information to log specifically _how_ refund was paid.
|
||||
|
||||
echo $this->element('form_table',
|
||||
array('class' => "item refund transaction entry",
|
||||
//'with_name_after' => ':',
|
||||
'field_prefix' => 'Transaction',
|
||||
'fields' => array
|
||||
("stamp" => array('opts' =>
|
||||
array('type' => 'text'),
|
||||
'between' => '<A HREF="#" ONCLICK="datepickerNow(\'TransactionStamp\'); return false;">Now</A>',
|
||||
),
|
||||
"amount" => array('prefix' => 'Entry.0',
|
||||
'opts' =>
|
||||
array('value' =>
|
||||
FormatHelper::currency($balance, false, ''),
|
||||
),
|
||||
),
|
||||
"account_id" => array('prefix' => 'Entry.0',
|
||||
'name' => 'Account',
|
||||
'opts' =>
|
||||
array('options' => $refundAccounts,
|
||||
'value' => $defaultAccount,
|
||||
),
|
||||
),
|
||||
"comment" => array('opts' => array('size' => 50),
|
||||
),
|
||||
))) . "\n";
|
||||
|
||||
echo $form->input("Customer.id",
|
||||
array('type' => 'hidden',
|
||||
'value' => $customer['id'])) . "\n";
|
||||
|
||||
if (isset($lease['id']))
|
||||
echo $form->input("Lease.id",
|
||||
array('type' => 'hidden',
|
||||
'value' => $lease['id'])) . "\n";
|
||||
|
||||
echo $form->end('Issue Refund');
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
// Reset the form
|
||||
function resetForm() {
|
||||
datepickerNow('TransactionStamp');
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
$("#TransactionStamp")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
|
||||
resetForm();
|
||||
});
|
||||
--></script>
|
||||
|
||||
</div>
|
||||
@@ -16,28 +16,31 @@ $unit = $lease['Unit'];
|
||||
if (isset($lease['Lease']))
|
||||
$lease = $lease['Lease'];
|
||||
|
||||
$rows = array(array('ID', $lease['id']),
|
||||
array('Number', $lease['number']),
|
||||
array('Lease Type', $lease_type['name']),
|
||||
array('Unit', $html->link($unit['name'],
|
||||
$rows = array();
|
||||
|
||||
$rows[] = array('ID', $lease['id']);
|
||||
$rows[] = array('Number', $lease['number']);
|
||||
$rows[] = array('Lease Type', $lease_type['name']);
|
||||
$rows[] = array('Unit', $html->link($unit['name'],
|
||||
array('controller' => 'units',
|
||||
'action' => 'view',
|
||||
$unit['id']))),
|
||||
array('Customer', $html->link($customer['name'],
|
||||
$unit['id'])));
|
||||
$rows[] = array('Customer', $html->link($customer['name'],
|
||||
array('controller' => 'customers',
|
||||
'action' => 'view',
|
||||
$customer['id']))),
|
||||
array('Lease_Date', FormatHelper::date($lease['lease_date'], true)),
|
||||
array('Move-in Planned', FormatHelper::date($lease['movein_planned_date'], true)),
|
||||
array('Move-in', FormatHelper::date($lease['movein_date'], true)),
|
||||
array('Move-out', FormatHelper::date($lease['moveout_date'], true)),
|
||||
array('Move-out Planned', FormatHelper::date($lease['moveout_planned_date'], true)),
|
||||
array('Notice Given', FormatHelper::date($lease['notice_given_date'], true)),
|
||||
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('Comment', $lease['comment']));
|
||||
$customer['id'])));
|
||||
$rows[] = array('Lease_Date', FormatHelper::date($lease['lease_date'], true));
|
||||
$rows[] = array('Move-in Planned', FormatHelper::date($lease['movein_planned_date'], true));
|
||||
$rows[] = array('Move-in', FormatHelper::date($lease['movein_date'], true));
|
||||
$rows[] = array('Move-out', FormatHelper::date($lease['moveout_date'], true));
|
||||
$rows[] = array('Move-out Planned', FormatHelper::date($lease['moveout_planned_date'], true));
|
||||
$rows[] = array('Notice Given', FormatHelper::date($lease['notice_given_date'], true));
|
||||
$rows[] = array('Notice Received', FormatHelper::date($lease['notice_received_date'], true));
|
||||
$rows[] = array('Closed', FormatHelper::date($lease['close_date'], true));
|
||||
$rows[] = array('Deposit', FormatHelper::currency($lease['deposit']));
|
||||
$rows[] = array('Rent', FormatHelper::currency($lease['rent']));
|
||||
$rows[] = array('Paid Through', FormatHelper::date($lease['paid_through'], true));
|
||||
$rows[] = array('Comment', $lease['comment']);
|
||||
|
||||
|
||||
echo $this->element('table',
|
||||
@@ -78,11 +81,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('statement_entries', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Account',
|
||||
'filter' => array('Lease.id' => $lease['id']),
|
||||
'include' => array('Through'),
|
||||
'exclude' => array('Customer', 'Lease', 'Unit'),
|
||||
'sort_column' => 'Effective',
|
||||
'sort_order' => 'DESC',
|
||||
)));
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
|
||||
@@ -6,94 +6,58 @@ echo '<div class="ledger-entry view">' . "\n";
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* LedgerEntry Detail Main Section
|
||||
* Ledger Entry Detail Main Section
|
||||
*/
|
||||
|
||||
$transaction = $entry['Transaction'];
|
||||
$debit_ledger = $entry['DebitLedger'];
|
||||
$credit_ledger = $entry['CreditLedger'];
|
||||
$source = $entry['MonetarySource'];
|
||||
$customer = $entry['Customer'];
|
||||
$lease = $entry['Lease'];
|
||||
$ledger = $entry['Ledger'];
|
||||
$account = $ledger['Account'];
|
||||
$tender = $entry['Tender'];
|
||||
$matching = $entry['MatchingEntry'];
|
||||
$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('Amount', FormatHelper::currency($entry['amount']));
|
||||
$rows[] = array('Tender', $html->link($tender['name'],
|
||||
array('controller' => 'tenders',
|
||||
'action' => 'view',
|
||||
$tender['id'])));
|
||||
$rows[] = array('Account', $html->link($account['name'],
|
||||
array('controller' => 'accounts',
|
||||
'action' => 'view',
|
||||
$account['id'])));
|
||||
$rows[] = array('Ledger', $html->link($ledger['name'],
|
||||
array('controller' => 'ledgers',
|
||||
'action' => 'view',
|
||||
$ledger['id'])));
|
||||
$rows[] = array('Cr/Dr', ($entry['crdr'] .
|
||||
' (Matching ' . $matching['crdr'] . ': ' .
|
||||
$html->link('#'.$matching['id'],
|
||||
array('controller' => 'ledger_entries',
|
||||
'action' => 'view',
|
||||
$matching['id'])) .
|
||||
')'));
|
||||
$rows[] = array('Comment', $entry['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item ledger-entry detail',
|
||||
array('class' => 'item entry detail',
|
||||
'caption' => 'Ledger Entry Detail',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value')));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* LedgerEntry Info Box
|
||||
* Entry Info Box
|
||||
*/
|
||||
|
||||
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']));
|
||||
}
|
||||
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";
|
||||
|
||||
|
||||
@@ -107,31 +71,6 @@ echo '</div>' . "\n";
|
||||
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'],
|
||||
));
|
||||
}
|
||||
|
||||
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'],
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
|
||||
@@ -10,18 +10,21 @@ echo '<div class="ledger view">' . "\n";
|
||||
*/
|
||||
|
||||
$account = $ledger['Account'];
|
||||
//$close = $ledger['CloseTransaction'];
|
||||
|
||||
if (isset($ledger['Ledger']))
|
||||
$ledger = $ledger['Ledger'];
|
||||
|
||||
$rows = array(array('ID', $ledger['id']),
|
||||
array('Account', $html->link($account['name'],
|
||||
array('controller' => 'accounts',
|
||||
'action' => 'view',
|
||||
$account['id']))),
|
||||
array('Sequence', $ledger['sequence']),
|
||||
array('Status', $ledger['closed'] ? 'Closed' : 'Open'),
|
||||
array('Comment', $ledger['comment']));
|
||||
$rows = array();
|
||||
$rows[] = array('ID', $ledger['id']);
|
||||
$rows[] = array('Name', $ledger['name']);
|
||||
$rows[] = array('Account', $html->link($account['name'],
|
||||
array('controller' => 'accounts',
|
||||
'action' => 'view',
|
||||
$account['id'])));
|
||||
$rows[] = array('Sequence', $ledger['sequence']);
|
||||
$rows[] = array('Status', $ledger['close_transaction_id'] ? 'Closed' : 'Open');
|
||||
$rows[] = array('Comment', $ledger['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item ledger detail',
|
||||
@@ -62,11 +65,17 @@ 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
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => "Ledger Entries",
|
||||
'filter' => array('Ledger.id' => $ledger['id']),
|
||||
'exclude' => array('Ledger', 'Account',
|
||||
'Amount', 'Cr/Dr', 'Balance',
|
||||
empty($account['receipts']) ? 'Tender' : null),
|
||||
'include' => array('Debit', 'Credit', 'Sub-Total'),
|
||||
)));
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
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('Comment', $source['comment']));
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item monetary-source detail',
|
||||
'caption' => 'Monetary Source Detail',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value')));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* MonetarySource Info Box
|
||||
*/
|
||||
|
||||
echo '<div class="infobox">' . "\n";
|
||||
$rows = array();
|
||||
echo $this->element('table',
|
||||
array('class' => 'summary',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value'),
|
||||
'suppress_alternate_rows' => true,
|
||||
));
|
||||
echo '</div>' . "\n";
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Supporting Elements Section
|
||||
*/
|
||||
|
||||
echo '<div CLASS="detail supporting">' . "\n";
|
||||
|
||||
/* End "detail supporting" div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
/* End page 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>
|
||||
126
site/views/statement_entries/view.ctp
Normal file
126
site/views/statement_entries/view.ctp
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="statement-entry view">' . "\n";
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Entry Detail Main Section
|
||||
*/
|
||||
|
||||
$transaction = $entry['Transaction'];
|
||||
$account = $entry['Account'];
|
||||
$customer = $entry['Customer'];
|
||||
$lease = $entry['Lease'];
|
||||
$entry = $entry['StatementEntry'];
|
||||
|
||||
$Ttype = ucfirst(strtolower($transaction['type']));
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array('ID', $entry['id']);
|
||||
$rows[] = array($Ttype, $html->link('#'.$transaction['id'],
|
||||
array('controller' => 'transactions',
|
||||
'action' => 'view',
|
||||
$transaction['id'])));
|
||||
$rows[] = array('Timestamp', FormatHelper::datetime($transaction['stamp']));
|
||||
$rows[] = array('Effective', FormatHelper::date($entry['effective_date']));
|
||||
if (in_array($entry['type'], array('CHARGE', 'PAYMENT')))
|
||||
$rows[] = array('Through', FormatHelper::date($entry['through_date']));
|
||||
$rows[] = array('Type', $entry['type']);
|
||||
$rows[] = array('Amount', FormatHelper::currency($entry['amount']));
|
||||
$rows[] = array('Account', $html->link($account['name'],
|
||||
array('controller' => 'accounts',
|
||||
'action' => 'view',
|
||||
$account['id'])));
|
||||
$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('Comment', $entry['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item statement-entry detail',
|
||||
'caption' => 'Statement Entry Detail',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value')));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Entry Info Box
|
||||
*/
|
||||
|
||||
if (strtoupper($entry['type']) === 'CHARGE') {
|
||||
$applied_caption = "Disbursements Applied";
|
||||
//$remaining_caption = "Charge Balance";
|
||||
}
|
||||
else {
|
||||
$applied_caption = "Disbursed to Charges";
|
||||
//$remaining_caption = "Disbursement Balance";
|
||||
}
|
||||
|
||||
$remaining_caption = "Remaining Balance";
|
||||
|
||||
echo '<div class="infobox">' . "\n";
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array($applied_caption,
|
||||
'<SPAN id="statement-entry-applied">' .
|
||||
FormatHelper::currency($stats['reconciled']) .
|
||||
'</SPAN>');
|
||||
$rows[] = array($remaining_caption,
|
||||
'<SPAN id="statement-entry-balance">' .
|
||||
FormatHelper::currency($stats['balance']) .
|
||||
'</SPAN>');
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item summary',
|
||||
'caption' => null,
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value'),
|
||||
//'suppress_alternate_rows' => true,
|
||||
));
|
||||
|
||||
echo '</div>' . "\n";
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Supporting Elements Section
|
||||
*/
|
||||
|
||||
echo '<div CLASS="detail supporting">' . "\n";
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Reconciliation Ledger Entries
|
||||
*/
|
||||
|
||||
echo $this->element('statement_entries', array
|
||||
(// Element configuration
|
||||
'statement_entry_id' => $entry['id'],
|
||||
/* 'action' => 'reconcile', */
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Entries Applied',
|
||||
//'filter' => array('id' => $entry['id']),
|
||||
'exclude' => array('Entry'),
|
||||
)));
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
/* End page div */
|
||||
echo '</div>' . "\n";
|
||||
166
site/views/tenders/deposit.ctp
Normal file
166
site/views/tenders/deposit.ctp
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="tender 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('depositTypes', 'depositAccounts'));
|
||||
|
||||
echo $form->create(null, array('id' => 'deposit-form',
|
||||
'onsubmit' => 'return verifyRequest();',
|
||||
'url' => array('controller' => 'transactions',
|
||||
'action' => 'postDeposit')));
|
||||
|
||||
foreach ($depositTypes AS $type) {
|
||||
$names = Inflector::pluralize($type['name']);
|
||||
|
||||
$radioOptions =
|
||||
array('none' => " No {$names} will be deposited",
|
||||
'all' => (" Deposit all {$names} (" .
|
||||
FormatHelper::currency($type['stats']['undeposited']) .
|
||||
")"),
|
||||
'subset' => " Deposit {$names} from the list below",
|
||||
);
|
||||
|
||||
echo "\n";
|
||||
echo $form->input("TenderType.{$type['id']}.selection",
|
||||
array('type' => 'radio',
|
||||
'class' => "type-selection-{$type['id']}",
|
||||
'separator' => '<BR>',
|
||||
'onclick' => "switchSelection({$type['id']})",
|
||||
'legend' => false,
|
||||
'value' => $type['stats']['undeposited'] > 0 ? 'all' : 'none',
|
||||
'disabled' => $type['stats']['undeposited'] <= 0,
|
||||
'options' => $radioOptions,
|
||||
));
|
||||
|
||||
// REVISIT <AP>: 20090729
|
||||
// Would like to present an option for the user to close the ledger
|
||||
// associated with the form of tender, or to just leave it open.
|
||||
// For now, just close it.
|
||||
echo "\n";
|
||||
echo $form->input("TenderType.{$type['id']}.close",
|
||||
array('type' => 'hidden',
|
||||
'value' => true,
|
||||
));
|
||||
|
||||
echo "\n";
|
||||
echo $form->input("TenderType.{$type['id']}.amount",
|
||||
array('type' => 'hidden',
|
||||
'value' => $type['stats']['undeposited'],
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input("TenderType.{$type['id']}.id",
|
||||
array('type' => 'hidden',
|
||||
'value' => $type['id'],
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input("TenderType.{$type['id']}.name",
|
||||
array('type' => 'hidden',
|
||||
'value' => $type['name'],
|
||||
));
|
||||
echo "\n";
|
||||
|
||||
$grid_div_id = "tenders-{$type['id']}-list";
|
||||
echo $this->element('tenders', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'grid_div_id' => $grid_div_id,
|
||||
|
||||
'grid_setup' =>
|
||||
array('hiddengrid' => true,
|
||||
'multiselect' => true),
|
||||
|
||||
'caption' => "{$names} on hand",
|
||||
'filter' => array('deposit_transaction_id' => null,
|
||||
'TenderType.id' => $type['id']),
|
||||
'exclude' => array('Type'),
|
||||
),
|
||||
));
|
||||
|
||||
// Add a hidden item to hold the jqGrid selection,
|
||||
// which we'll populate prior to form submission.
|
||||
echo "\n";
|
||||
echo $form->input("TenderType.{$type['id']}.items",
|
||||
array('type' => 'hidden',
|
||||
'value' => null
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
echo $form->input('Deposit.Account.id', array('label' => 'Deposit Account ',
|
||||
'options' => $depositAccounts));
|
||||
echo $form->end('Perform Deposit');
|
||||
|
||||
/* End page div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
$(document).ready(function(){
|
||||
<?php foreach ($depositTypes AS $type): ?>
|
||||
<?php /* Hide the multiselect column */ ?>
|
||||
switchSelection(<?php echo $type['id']; ?>);
|
||||
<?php endforeach; ?>
|
||||
});
|
||||
|
||||
// pre-submit callback
|
||||
function verifyRequest() {
|
||||
<?php foreach ($depositTypes AS $type): ?>
|
||||
var rows = $('#<?php echo "tenders-{$type['id']}-list-jqGrid"; ?>').getGridParam('selarrrow');
|
||||
$('#<?php echo "TenderType{$type['id']}Items"; ?>').val(serialize(rows));
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php
|
||||
// REVISIT <AP>: 20090730
|
||||
// Verify the request before submitting
|
||||
?>
|
||||
|
||||
// return false to prevent the form from being submitted;
|
||||
// anything other than false will allow submission.
|
||||
return true;
|
||||
}
|
||||
|
||||
function switchSelection(type_id) {
|
||||
var grid_div_id = '#tenders-'+type_id+'-list';
|
||||
var grid_id = grid_div_id+'-jqGrid';
|
||||
var selection = $('.type-selection-'+type_id+':checked').val();
|
||||
var gridstate = $(grid_id).getGridParam('gridstate');
|
||||
|
||||
<?php
|
||||
// It seems that jqGrid doesn't work too well with multiselect
|
||||
// dynamically enabled / disabled. What we'd like to do is:
|
||||
|
||||
/* if (selection == 'subset' && !multiselect) */
|
||||
/* $(grid_id).setGridParam({multiselect:true}).showCol('cb'); */
|
||||
/* if (selection != 'subset' && multiselect) */
|
||||
/* $(grid_id).setGridParam({multiselect:false}).hideCol('cb'); */
|
||||
|
||||
// However, if the grid is reloaded (manually, or page switch) while
|
||||
// multiselect is disabled, then it loads garbage. I have been able
|
||||
// to work around this using the loadBeforeSend event to re-enable
|
||||
// multiselect just for the load, then disable it again using this
|
||||
// function after the load (using the gridComplete event).
|
||||
//
|
||||
// It seems terribly clunky though, and so as a workaround, I've
|
||||
// found that I can leave multiselect enabled all the time, and just
|
||||
// Tell the grid to allow selection through checkboxes only, after
|
||||
// hiding the checkboxes. This essentially disables multiselection
|
||||
// as well, without the grid having to disable the entire mechanism.
|
||||
?>
|
||||
|
||||
// Configure multiselection
|
||||
if (selection == 'subset')
|
||||
$(grid_id).showCol('cb').setGridParam({multiboxonly: false});
|
||||
else
|
||||
$(grid_id).hideCol('cb').setGridParam({multiboxonly: true}).resetSelection();
|
||||
|
||||
// Show or hide the grid, as appropriate
|
||||
if ((selection == 'subset') == (gridstate == 'hidden'))
|
||||
$(grid_div_id + ' .HeaderButton').click();
|
||||
}
|
||||
|
||||
--></script>
|
||||
100
site/views/tenders/view.ctp
Normal file
100
site/views/tenders/view.ctp
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="tender view">' . "\n";
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Tender Detail Main Section
|
||||
*/
|
||||
|
||||
$ttype = $tender['TenderType'];
|
||||
$customer = $tender['Customer'];
|
||||
$entry = $tender['LedgerEntry'];
|
||||
$transaction = $entry['Transaction'];
|
||||
$tender = $tender['Tender'];
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array('ID', $tender['id']);
|
||||
$rows[] = array('Received', FormatHelper::date($transaction['stamp']));
|
||||
$rows[] = array('Customer', $html->link($customer['name'],
|
||||
array('controller' => 'customers',
|
||||
'action' => 'view',
|
||||
$customer['id'])));
|
||||
$rows[] = array('Amount', FormatHelper::currency($entry['amount']));
|
||||
$rows[] = array('Item', $tender['name']);
|
||||
$rows[] = array('Type', $ttype['name']);
|
||||
/* $rows[] = array('Type', $html->link($ttype['name'], */
|
||||
/* array('controller' => 'tender_types', */
|
||||
/* 'action' => 'view', */
|
||||
/* $ttype['id']))); */
|
||||
|
||||
for ($i=1; $i<=4; ++$i)
|
||||
if (!empty($ttype["data{$i}_name"]))
|
||||
$rows[] = array($ttype["data{$i}_name"], $tender["data{$i}"]);
|
||||
|
||||
if (!empty($tender['deposit_transaction_id']))
|
||||
$rows[] = array('Deposit', $html->link('#'.$tender['deposit_transaction_id'],
|
||||
array('controller' => 'transactions',
|
||||
'action' => 'view',
|
||||
$tender['deposit_transaction_id'])));
|
||||
|
||||
if (!empty($tender['nsf_transaction_id']))
|
||||
$rows[] = array('NSF', $html->link('#'.$tender['nsf_transaction_id'],
|
||||
array('controller' => 'transactions',
|
||||
'action' => 'view',
|
||||
$tender['nsf_transaction_id'])));
|
||||
|
||||
$rows[] = array('Comment', $tender['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item tender detail',
|
||||
'caption' => 'Legal Tender Detail',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value')));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Tender Info Box
|
||||
*/
|
||||
|
||||
echo '<div class="infobox">' . "\n";
|
||||
$rows = array();
|
||||
echo $this->element('table',
|
||||
array('class' => 'summary',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value'),
|
||||
'suppress_alternate_rows' => true,
|
||||
));
|
||||
echo '</div>' . "\n";
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* Supporting Elements Section
|
||||
*/
|
||||
|
||||
echo '<div CLASS="detail supporting">' . "\n";
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Ledger Entries
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => "Ledger Entries",
|
||||
'filter' => array('id' => array($tender['ledger_entry_id'], $tender['nsf_ledger_entry_id'])),
|
||||
'exclude' => array('Tender'),
|
||||
)));
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
/* End page div */
|
||||
echo '</div>' . "\n";
|
||||
89
site/views/transactions/bad_debt.ctp
Normal file
89
site/views/transactions/bad_debt.ctp
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="bad-debt input">' . "\n";
|
||||
|
||||
if (isset($lease)) {
|
||||
$customer = $lease['Customer'];
|
||||
$unit = $lease['Unit'];
|
||||
}
|
||||
|
||||
if (isset($customer['Customer']))
|
||||
$customer = $customer['Customer'];
|
||||
|
||||
if (isset($lease['Lease']))
|
||||
$lease = $lease['Lease'];
|
||||
|
||||
// We're not actually using a grid to select the customer / lease
|
||||
// but we could/should be, and the result would be selection-text
|
||||
echo ('<DIV CLASS="bad-debt grid-selection-text">' .
|
||||
'<TABLE>' . "\n");
|
||||
|
||||
echo ('<TR><TD style="padding-right: 1em;">' . $customer['name'] . '</TD>' .
|
||||
' <TD>' . '(Customer #' . $customer['id'] . ')' . '</TD>' .
|
||||
'</TR>' . "\n");
|
||||
|
||||
if (isset($lease))
|
||||
echo ('<TR><TD style="padding-right: 1em;">' . 'Unit ' . $unit['name'] . '</TD>' .
|
||||
' <TD>' . '(Lease #' . $lease['number'] . ')' . '</TD>' .
|
||||
'</TR>' . "\n");
|
||||
|
||||
echo ('<TR><TD style="padding-right: 1em;">Remaining Balance:</TD>' .
|
||||
' <TD>' . FormatHelper::currency($balance) . '</TD>' .
|
||||
'</TR>' . "\n");
|
||||
|
||||
echo ('</TABLE>' .
|
||||
'</DIV>' . "\n");
|
||||
|
||||
|
||||
echo $form->create(null, array('id' => 'receipt-form',
|
||||
'url' => array('controller' => 'transactions',
|
||||
'action' => 'postWriteOff'))) . "\n";
|
||||
|
||||
echo $form->input("Customer.id",
|
||||
array('type' => 'hidden',
|
||||
'value' => $customer['id'])) . "\n";
|
||||
|
||||
if (isset($lease['id']))
|
||||
echo $form->input("Lease.id",
|
||||
array('type' => 'hidden',
|
||||
'value' => $lease['id'])) . "\n";
|
||||
|
||||
echo $form->input("Entry.0.amount",
|
||||
array('type' => 'hidden',
|
||||
'value' => $balance)) . "\n";
|
||||
|
||||
echo $this->element('form_table',
|
||||
array('class' => "item receipt transaction entry",
|
||||
//'with_name_after' => ':',
|
||||
'field_prefix' => 'Transaction',
|
||||
'fields' => array
|
||||
("stamp" => array('opts' => array('type' => 'text'),
|
||||
'between' => '<A HREF="#" ONCLICK="datepickerNow(\'TransactionStamp\'); return false;">Now</A>',
|
||||
),
|
||||
"comment" => array('opts' => array('size' => 50),
|
||||
),
|
||||
))) . "\n";
|
||||
|
||||
echo $form->end('Write Off Remaining Balance');
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
// Reset the form
|
||||
function resetForm() {
|
||||
datepickerNow('TransactionStamp');
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
$("#TransactionStamp")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
|
||||
resetForm();
|
||||
});
|
||||
--></script>
|
||||
|
||||
</div>
|
||||
67
site/views/transactions/deposit_slip.ctp
Normal file
67
site/views/transactions/deposit_slip.ctp
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
//style="display:inline;
|
||||
echo('<H2 style="display:inline;">Deposit Slip: ' .
|
||||
FormatHelper::datetime($deposit['Transaction']['stamp'])
|
||||
. '</H2>' . "\n");
|
||||
/* echo('(' . */
|
||||
/* FormatHelper::age($deposit['Transaction']['stamp'], 60) */
|
||||
/* . ')<BR>' . "\n"); */
|
||||
|
||||
//pr(compact('deposit'));
|
||||
|
||||
// Handle account summaries
|
||||
$rows = array();
|
||||
$row_class = array();
|
||||
foreach ($deposit['types'] AS $type) {
|
||||
$row_class[] = array();
|
||||
$rows[] = array($type['name'].':',
|
||||
FormatHelper::_n($type['count'], 'Item'),
|
||||
FormatHelper::currency($type['total'], true));
|
||||
}
|
||||
$row_class[] = 'grand';
|
||||
$rows[] = array('Deposit Total:',
|
||||
null,
|
||||
FormatHelper::currency($deposit['Transaction']['amount'], 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
|
||||
if (0) {
|
||||
foreach ($deposit['types'] AS $type) {
|
||||
echo $this->element('tenders', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'grid_div_id' => "tenders-{$type['id']}-list",
|
||||
'caption' => $type['name'] . ' Items',
|
||||
'filter' => array('deposit_transaction_id'
|
||||
=> $deposit['Transaction']['id'],
|
||||
'TenderType.id'
|
||||
=> $type['id'],
|
||||
),
|
||||
'exclude' => array('Type'),
|
||||
)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
echo $this->element('tenders', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'caption' => 'Deposited Items',
|
||||
'filter' => array('deposit_transaction_id'
|
||||
=> $deposit['Transaction']['id'],
|
||||
),
|
||||
)));
|
||||
}
|
||||
|
||||
/* End page div */
|
||||
//echo '</div>' . "\n";
|
||||
@@ -9,11 +9,26 @@ echo '<div class="transaction view">' . "\n";
|
||||
* 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']));
|
||||
$account = $transaction['Account'];
|
||||
$ledger = $transaction['Ledger'];
|
||||
|
||||
if (isset($transaction['Transaction']))
|
||||
$transaction = $transaction['Transaction'];
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array('ID', $transaction['id']);
|
||||
$rows[] = array('Type', $transaction['type']);
|
||||
$rows[] = array('Timestamp', FormatHelper::datetime($transaction['stamp']));
|
||||
$rows[] = array('Amount', FormatHelper::currency($transaction['amount']));
|
||||
$rows[] = array('Account', $html->link($account['name'],
|
||||
array('controller' => 'accounts',
|
||||
'action' => 'view',
|
||||
$account['id'])));
|
||||
$rows[] = array('Ledger', $html->link($ledger['name'],
|
||||
array('controller' => 'ledgers',
|
||||
'action' => 'view',
|
||||
$ledger['id'])));
|
||||
$rows[] = array('Comment', $transaction['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item transaction detail',
|
||||
@@ -28,7 +43,7 @@ echo $this->element('table',
|
||||
|
||||
echo '<div class="infobox">' . "\n";
|
||||
$rows = array();
|
||||
$rows[] = array('Total:', FormatHelper::currency($total));
|
||||
$rows[] = array('Total:', FormatHelper::currency($transaction['amount']));
|
||||
echo $this->element('table',
|
||||
array('class' => 'summary',
|
||||
'rows' => $rows,
|
||||
@@ -49,15 +64,55 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Entries
|
||||
* Statement Entries
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries',
|
||||
array('caption' => 'Entries in Transaction',
|
||||
//'ledger_entries' => $transaction['LedgerEntry'],
|
||||
'transaction_id' => $transaction['Transaction']['id'],
|
||||
'notxgroup' => true,
|
||||
));
|
||||
if ($transaction['type'] === 'INVOICE' ||
|
||||
$transaction['type'] === 'RECEIPT' ||
|
||||
$transaction['type'] === 'CREDIT_NOTE' ||
|
||||
$transaction['type'] === 'PAYMENT'
|
||||
) {
|
||||
echo $this->element('statement_entries', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'caption' => 'Statement Entries',
|
||||
'filter' => array('Transaction.id' => $transaction['id'],
|
||||
'type !=' => 'VOID'),
|
||||
'exclude' => array('Transaction', 'Account'),
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Ledger Entries
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'caption' => 'Ledger Entries',
|
||||
'filter' => array('Transaction.id' => $transaction['id'],
|
||||
'Account.id !=' => $account['id']),
|
||||
'exclude' => array('Transaction'),
|
||||
)));
|
||||
|
||||
|
||||
/* /\********************************************************************** */
|
||||
/* * Tenders Deposited */
|
||||
/* *\/ */
|
||||
|
||||
/* if ($transaction['type'] === 'DEPOSIT') { */
|
||||
/* echo $this->element('tenders', array */
|
||||
/* (// Grid configuration */
|
||||
/* 'config' => array */
|
||||
/* ( */
|
||||
/* 'caption' => 'Deposited Items', */
|
||||
/* 'filter' => array('deposit_transaction_id' => $transaction['id']), */
|
||||
/* ))); */
|
||||
/* } */
|
||||
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
|
||||
27
site/views/units/edit.ctp
Normal file
27
site/views/units/edit.ctp
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="unit edit">' . "\n";
|
||||
|
||||
echo $form->create('Unit', array('action' => 'edit')) . "\n";
|
||||
echo $form->input('id') . "\n";
|
||||
|
||||
echo($this->element
|
||||
('form_table',
|
||||
array('class' => 'item unit detail',
|
||||
'caption' => isset($this->data['Unit']) ? 'Edit Unit' : 'New Unit',
|
||||
'fields' => array
|
||||
('name' => true,
|
||||
'unit_size_id' => true,
|
||||
'status' => array('opts' =>
|
||||
array('options' => $statusEnums,
|
||||
),
|
||||
),
|
||||
'deposit' => true,
|
||||
'rent' => true,
|
||||
'comment' => true,
|
||||
))) . "\n");
|
||||
|
||||
echo $form->submit('Update') . "\n";
|
||||
echo $form->submit('Cancel', array('name' => 'cancel')) . "\n";
|
||||
echo $form->end() . "\n";
|
||||
echo '</div>' . "\n";
|
||||
@@ -9,15 +9,26 @@ 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();
|
||||
$rows[] = array('Name', $unit['name']);
|
||||
$rows[] = array('Status', $unit['status']);
|
||||
$rows[] = array('Size', $unit_size['name']);
|
||||
$rows[] = array('Deposit', FormatHelper::currency($unit['deposit']));
|
||||
$rows[] = array('Rent', FormatHelper::currency($unit['rent']));
|
||||
$rows[] = 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 +62,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
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Lease History',
|
||||
'filter' => array('Unit.id' => $unit['id']),
|
||||
'exclude' => array('Unit'),
|
||||
)));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* 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('statement_entries', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'caption' =>
|
||||
('Current Lease Account ('
|
||||
. $current_lease['Customer']['name']
|
||||
. ')'),
|
||||
'filter' => array('Lease.id' => $current_lease['id']),
|
||||
'include' => array('Through'),
|
||||
'exclude' => array('Customer', 'Lease', 'Unit'),
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
|
||||
@@ -209,6 +209,12 @@ div.related {
|
||||
}
|
||||
|
||||
/* Debugging */
|
||||
.pr-caller {
|
||||
color: #000;
|
||||
background: #c8c;
|
||||
/* padding-top: 0.2em; */
|
||||
padding: 0.1em;
|
||||
}
|
||||
pre {
|
||||
color: #000;
|
||||
background: #f0f0f0;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************
|
||||
@@ -84,22 +126,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 +193,63 @@ table.list.ledger td.date.receipt { padding-left: 1em; }
|
||||
table.list.ledger td.evnrow { background: #f4f4f4; }
|
||||
|
||||
|
||||
/************************************************************
|
||||
************************************************************
|
||||
* Receipt Entry
|
||||
*/
|
||||
|
||||
input.payment {
|
||||
width: 10em;
|
||||
}
|
||||
label.payment {
|
||||
padding-left: 0.5em;
|
||||
/* float: left; */
|
||||
/* text-align: right; */
|
||||
/* display: block; */
|
||||
}
|
||||
|
||||
|
||||
/************************************************************
|
||||
************************************************************
|
||||
* 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; }
|
||||
|
||||
/* NSF items */
|
||||
.nsf-tender { text-decoration: line-through; }
|
||||
|
||||
|
||||
/************************************************************
|
||||
************************************************************
|
||||
* 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 +262,66 @@ div.loading {
|
||||
margin-left: 1.0em;
|
||||
}
|
||||
|
||||
div.scroll .selbox {
|
||||
font-family: 'lucida grande',verdana,helvetica,arial,sans-serif;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
/* The "page N / M" input box */
|
||||
div.scroll input[type='text']
|
||||
{
|
||||
text-align: right;
|
||||
padding-right: 0.4em;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************
|
||||
************************************************************
|
||||
* 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;
|
||||
@@ -197,12 +346,20 @@ fieldset fieldset div {
|
||||
clear: left;
|
||||
/* margin: 0 20px; */
|
||||
}
|
||||
form div {
|
||||
clear: both;
|
||||
/* margin-bottom: 1em; */
|
||||
/* padding: .5em; */
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
/*
|
||||
* REVISIT <AP>: 20090728
|
||||
* This "form div" is way too generic, and in fact
|
||||
* it's screwing up the jqGrid header. I'm commenting
|
||||
* it out for now, to see if it actually is needed
|
||||
* anywhere, and hope to delete it in the near future.
|
||||
*/
|
||||
/* form div { */
|
||||
/* clear: both; */
|
||||
/* /\* margin-bottom: 1em; *\/ */
|
||||
/* /\* padding: .5em; *\/ */
|
||||
/* vertical-align: text-top; */
|
||||
/* } */
|
||||
form div.input {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
46
site/webroot/css/print.css
Normal file
46
site/webroot/css/print.css
Normal file
@@ -0,0 +1,46 @@
|
||||
/************************************************************
|
||||
************************************************************
|
||||
* Styles for media type: print
|
||||
*/
|
||||
|
||||
/* No need for the menu */
|
||||
table#layout td#sidecolumn
|
||||
{ display: none; }
|
||||
|
||||
/* No need for the debug kit */
|
||||
div#debug-kit-toolbar
|
||||
{ display: none; }
|
||||
|
||||
/* In fact, no need for any debug stuff */
|
||||
.debug
|
||||
{ display: none; }
|
||||
|
||||
|
||||
/************************************************************
|
||||
* Grid display
|
||||
*/
|
||||
|
||||
/* Grid are not to be truncated when printing, so
|
||||
* set overflow to visible for necessary selectors.
|
||||
*/
|
||||
div#content, div.grid_bdiv
|
||||
{ overflow: visible ! important }
|
||||
|
||||
div.grid_hdiv
|
||||
{ border-bottom: 3px double #000; }
|
||||
|
||||
/* The header is generally useless, except for the <th>,
|
||||
* as well as the footer navtable. The pagination buttons
|
||||
* are of no use, nor is the selbox to set # of rows.
|
||||
*/
|
||||
.GridHeader td,
|
||||
div.scroll .navtable,
|
||||
div.scroll .pgbuttons,
|
||||
div.scroll select.selbox
|
||||
{ display: none; }
|
||||
|
||||
/* The "page N / M" input box... make it look like normal text */
|
||||
div.scroll input[type='text'] {
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
137
todo.notes
Normal file
137
todo.notes
Normal file
@@ -0,0 +1,137 @@
|
||||
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.
|
||||
|
||||
Customer Selection on the Receipt Page is broken.
|
||||
(Selecting a row and waiting for the update).
|
||||
|
||||
Allow waiving a complete charge, even if it already has payments
|
||||
applied (at the moment, we just can waive the charge balance).
|
||||
|
||||
Get Petty Cash working. We'll need to add one or more expense
|
||||
accounts. We'll also need to implement purchase order
|
||||
functionality, or at least simple an expense page.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
Reconcile all entries of a ledger to the c/f entry when
|
||||
"closing" the ledger and creating a new one.
|
||||
|
||||
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.
|
||||
|
||||
20090729: New Ledger doesn't seem to give a balance forward entry.
|
||||
|
||||
Reference in New Issue
Block a user