Compare commits
518 Commits
invoice_re
...
v0.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1da567ccdd | ||
|
|
e175428212 | ||
|
|
ccd1af6154 | ||
|
|
ce633d816a | ||
|
|
83bfb8d32d | ||
|
|
28817cea38 | ||
|
|
3dca204ac6 | ||
|
|
2fb2e6f5aa | ||
|
|
44def81c81 | ||
|
|
03da3afb98 | ||
|
|
3b885e2686 | ||
|
|
e162d35d56 | ||
|
|
3b3ed7a264 | ||
|
|
3b5aa78a47 | ||
|
|
721faa129b | ||
|
|
78806de606 | ||
|
|
d4ea5eea1f | ||
|
|
9213c1c21d | ||
|
|
9674812e78 | ||
|
|
8bda7c2cb0 | ||
|
|
375d63485c | ||
|
|
26045a3db7 | ||
|
|
04ac012754 | ||
|
|
e6f662f0a1 | ||
|
|
04b3c06cda | ||
|
|
24da6d75b5 | ||
|
|
542ae17afd | ||
|
|
97fffaa610 | ||
|
|
3eb5139b62 | ||
|
|
5245393a04 | ||
|
|
e59df1dffb | ||
|
|
61da97974b | ||
|
|
6482cfd4cc | ||
|
|
c3e51a7a6b | ||
|
|
de069ef186 | ||
|
|
5047abba6a | ||
|
|
4e8426fd79 | ||
|
|
6630cdfcd6 | ||
|
|
48d332f40f | ||
|
|
3ede96dad9 | ||
|
|
3e3dff31a8 | ||
|
|
3642724b5e | ||
|
|
0ad68f4d6a | ||
|
|
2628edfbdd | ||
|
|
740bcbedc0 | ||
|
|
2f3046294d | ||
|
|
7a2034aea0 | ||
|
|
bb4046e1da | ||
|
|
f717713842 | ||
|
|
5008452089 | ||
|
|
68a1397ad6 | ||
|
|
ef64644536 | ||
|
|
72ea84ad88 | ||
|
|
0f3aa42f57 | ||
|
|
fb23b7ffaa | ||
|
|
b731ee6165 | ||
|
|
34dcbd8b43 | ||
|
|
87a2ea5cd6 | ||
|
|
6492cd8b22 | ||
|
|
f6a18cbb6c | ||
|
|
7198d7e6f4 | ||
|
|
d79077e279 | ||
|
|
cea9332ac6 | ||
|
|
bf8aaea041 | ||
|
|
63704682fa | ||
|
|
63de5641a0 | ||
|
|
5f6a9ed53f | ||
|
|
328d0f8f51 | ||
|
|
aee6832374 | ||
|
|
63e22ec9bf | ||
|
|
fde8923814 | ||
|
|
696017a82a | ||
|
|
7bcee943a5 | ||
|
|
0c9b945f7b | ||
|
|
5b5707df5e | ||
|
|
c21cdcd9a2 | ||
|
|
a79adbce2d | ||
|
|
a3b376544c | ||
|
|
43c957baa2 | ||
|
|
a66024c889 | ||
|
|
aed090fbe2 | ||
|
|
7904372dff | ||
|
|
1d4dcbd2b0 | ||
|
|
f5f09421c1 | ||
|
|
4d525f6ac9 | ||
|
|
0b773cfc35 | ||
|
|
387e08da06 | ||
|
|
5ef877f64b | ||
|
|
c889e4f4e1 | ||
|
|
b938b5f028 | ||
|
|
1e34cc1de8 | ||
|
|
80fe5ca4de | ||
|
|
fbd716634a | ||
|
|
046cf5fe63 | ||
|
|
2fd2dc603c | ||
|
|
439e9a3de6 | ||
|
|
caee1c90a1 | ||
|
|
08486fc101 | ||
|
|
0758865aae | ||
|
|
85b189a50e | ||
|
|
3ff5967415 | ||
|
|
b2a85a5197 | ||
|
|
f8cdea8872 | ||
|
|
c3ad1b0ea1 | ||
|
|
56bec8d05c | ||
|
|
b89750a4ef | ||
|
|
20309fba39 | ||
|
|
241028603b | ||
|
|
8663c25806 | ||
|
|
03d947a069 | ||
|
|
294aabbb29 | ||
|
|
94188e3f92 | ||
|
|
c261735197 | ||
|
|
63c95da9f3 | ||
|
|
ae1dccfb14 | ||
|
|
9bc699bf51 | ||
|
|
7a1aa536fa | ||
|
|
de9a4acd5d | ||
|
|
bd4610abb3 | ||
|
|
60ec105fca | ||
|
|
67a0c07bc6 | ||
|
|
fc332bef0e | ||
|
|
92668bcca1 | ||
|
|
73eeba04fe | ||
|
|
a1bab966ed | ||
|
|
ee9ff354b2 | ||
|
|
a9410b1351 | ||
|
|
5ad28c809b | ||
|
|
89d7f22e4a | ||
|
|
11be0ff9cb | ||
|
|
65c3f28484 | ||
|
|
4e1ffd14b4 | ||
|
|
2d3b962fe1 | ||
|
|
1d921358a8 | ||
|
|
9a819c72a8 | ||
|
|
24e208bf17 | ||
|
|
86b0c14eda | ||
|
|
da88975fed | ||
|
|
f545502162 | ||
|
|
513182a6d5 | ||
|
|
7b76fd8f0a | ||
|
|
63bb788fdf | ||
|
|
d8760cf2b6 | ||
|
|
feb15cac49 | ||
|
|
40047d89d8 | ||
|
|
94cc553723 | ||
|
|
166a4f44d9 | ||
|
|
633a019fc5 | ||
|
|
bddcca8d9e | ||
|
|
6e6f7f4f43 | ||
|
|
64ce95e404 | ||
|
|
55bcdca935 | ||
|
|
6f035c2052 | ||
|
|
538c8077d2 | ||
|
|
e37e40f355 | ||
|
|
9fca2e7d4f | ||
|
|
5c7df5f2eb | ||
|
|
e0e7fcfc51 | ||
|
|
8ce654d22d | ||
|
|
6cd45236d9 | ||
|
|
009503bf7b | ||
|
|
4f652eb9a1 | ||
|
|
0b9061794e | ||
|
|
1055a00476 | ||
|
|
4f758b5480 | ||
|
|
ec2ea2a60e | ||
|
|
196a7ae6e6 | ||
|
|
24ad1ef49c | ||
|
|
019b59fdbc | ||
|
|
8892583db4 | ||
|
|
ab840075c2 | ||
|
|
10fe7bf230 | ||
|
|
5bcd424055 | ||
|
|
7cb0381060 | ||
|
|
935920a38a | ||
|
|
df3bbb698e | ||
|
|
a2c1779454 | ||
|
|
7a50d02a44 | ||
|
|
22edc5a524 | ||
|
|
bc33f3cb2c | ||
|
|
e3a606ebf0 | ||
|
|
8e91eadf53 | ||
|
|
7e665af8be | ||
|
|
2727812144 | ||
|
|
dd7135df49 | ||
|
|
0297266a34 | ||
|
|
3b1e06bade | ||
|
|
ce54ed2341 | ||
|
|
b4097850dd | ||
|
|
ac23a1be18 | ||
|
|
88d468d20c | ||
|
|
bb9ce64657 | ||
|
|
77a01a16c4 | ||
|
|
d26b6e99bd | ||
|
|
3096759738 | ||
|
|
011481be2d | ||
|
|
21e11298eb | ||
|
|
8b93f6ad91 | ||
|
|
9b9c6d8f29 | ||
|
|
44cda10211 | ||
|
|
0a62c8a816 | ||
|
|
c23d4c6209 | ||
|
|
9923ccc6c3 | ||
|
|
c4d4c3c989 | ||
|
|
b94c8e56b4 | ||
|
|
bde335f68c | ||
|
|
515046efd6 | ||
|
|
8c334f236d | ||
|
|
18848db6b1 | ||
|
|
e4d20cc849 | ||
|
|
355af40ed5 | ||
|
|
9f1a6b8087 | ||
|
|
77de94df54 | ||
|
|
00a306355a | ||
|
|
f7c2493f99 | ||
|
|
2bc2fb951b | ||
|
|
cc86bb99c1 | ||
|
|
eebb7c7c94 | ||
|
|
88a1a62087 | ||
|
|
29c157475b | ||
|
|
237e744002 | ||
|
|
6c38b08700 | ||
|
|
29bb08adca | ||
|
|
ca69698693 | ||
|
|
9274bb9b0c | ||
|
|
32ea8734e1 | ||
|
|
5bb240708f | ||
|
|
3e366d3dad | ||
|
|
0de2931f66 | ||
|
|
966dc495e2 | ||
|
|
df869ef70a | ||
|
|
8dcf5956fe | ||
|
|
654e79803f | ||
|
|
dcbff5a956 | ||
|
|
b1fb5e29db | ||
|
|
a6c3b612ba | ||
|
|
ee9ae2079c | ||
|
|
58495d5607 | ||
|
|
ebcc494e36 | ||
|
|
835853c51c | ||
|
|
622eca3f1a | ||
|
|
65b132ac8e | ||
|
|
410e588e97 | ||
|
|
9aba878ee6 | ||
|
|
bd90424080 | ||
|
|
a939b0839b | ||
|
|
2d8c536f33 | ||
|
|
2d51340a56 | ||
|
|
98c4a66f2f | ||
|
|
29b829d3af | ||
|
|
d0aa3799fa | ||
|
|
5e78d32ec4 | ||
|
|
a76421c858 | ||
|
|
28086651e5 | ||
|
|
da80c3623e | ||
|
|
309512dde0 | ||
|
|
8b1d3c9830 | ||
|
|
31265104e1 | ||
|
|
c719d11df2 | ||
|
|
e6d65a5212 | ||
|
|
e21233954a | ||
|
|
d36cfd9653 | ||
|
|
d141f61055 | ||
|
|
4b8dc3da02 | ||
|
|
709689b15b | ||
|
|
3eb989e03c | ||
|
|
eb019dd9e5 | ||
|
|
719373e534 | ||
|
|
1c5d97f06d | ||
|
|
589168440c | ||
|
|
73c5a20158 | ||
|
|
a44654ec01 | ||
|
|
7a331d5f4f | ||
|
|
daf9fe9391 | ||
|
|
d8767cfb73 | ||
|
|
64792e6fe2 | ||
|
|
424276eeb4 | ||
|
|
377a4cc88c | ||
|
|
170f660528 | ||
|
|
90ecbda541 | ||
|
|
4afe0bd77b | ||
|
|
8440a7c833 | ||
|
|
00d509f23d | ||
|
|
080c82fc10 | ||
|
|
1b02be19f0 | ||
|
|
366d59a5e6 | ||
|
|
2634cf824a | ||
|
|
1429fe720b | ||
|
|
754cdb8522 | ||
|
|
a1a68f3209 | ||
|
|
8843d24baa | ||
|
|
892618db36 | ||
|
|
3decfff33b | ||
|
|
945221d565 | ||
|
|
a968d7abe6 | ||
|
|
8b6b8884f7 | ||
|
|
14805190fc | ||
|
|
a1bdecfcaa | ||
|
|
4896834a96 | ||
|
|
a9c3c40053 | ||
|
|
4125d7ba16 | ||
|
|
26d9b1bc38 | ||
|
|
f30e536e47 | ||
|
|
96a030e340 | ||
|
|
460e9a2e64 | ||
|
|
6e63365604 | ||
|
|
4f85dc243e | ||
|
|
30f755cf42 | ||
|
|
170ba466de | ||
|
|
d2d1bb3fc4 | ||
|
|
d024d333d2 | ||
|
|
778bb43895 | ||
|
|
41321481c7 | ||
|
|
fe9f6ce949 | ||
|
|
f81bfdecc2 | ||
|
|
8dd6fc957d | ||
|
|
d92acf12de | ||
|
|
43d1d2ccf5 | ||
|
|
c06399cf86 | ||
|
|
d1187f9bdd | ||
|
|
b7a77757f9 | ||
|
|
5f199d97fe | ||
|
|
fc292e3366 | ||
|
|
94e300a129 | ||
|
|
791b2d8ab1 | ||
|
|
091920d80a | ||
|
|
1511986ed0 | ||
|
|
1e88e1fce2 | ||
|
|
a2014a916e | ||
|
|
bd52030984 | ||
|
|
f23726783e | ||
|
|
1d27b4dcb5 | ||
|
|
e74f8987d9 | ||
|
|
9c55a047a8 | ||
|
|
6f2038f7b0 | ||
|
|
ec22f4b003 | ||
|
|
70629e360b | ||
|
|
fc4e812d67 | ||
|
|
b6ee958c35 | ||
|
|
f82df229f2 | ||
|
|
96c499786c | ||
|
|
52e0181bfb | ||
|
|
b65c5c1dbf | ||
|
|
cbdce4f166 | ||
|
|
e9b9bdc420 | ||
|
|
704412727d | ||
|
|
72af3f3247 | ||
|
|
f0693bdc05 | ||
|
|
15f885ab8a | ||
|
|
b1a7f41934 | ||
|
|
4f11e27a76 | ||
|
|
f8aef6e794 | ||
|
|
e100c9a88f | ||
|
|
1ce71a3936 | ||
|
|
19f8c18ce9 | ||
|
|
eb28852b87 | ||
|
|
98f3dd7688 | ||
|
|
c33a823e50 | ||
|
|
1f97e8db35 | ||
|
|
76df8c924f | ||
|
|
598ce5784b | ||
|
|
cdb7d4b15c | ||
|
|
07a2be05f7 | ||
|
|
fd1a1f43d4 | ||
|
|
2e2147b238 | ||
|
|
a0c00f1a35 | ||
|
|
868e23b982 | ||
|
|
e6b0313523 | ||
|
|
654eb0960e | ||
|
|
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 | ||
|
|
562ffc9e4b |
51
bookkeeping.notes
Normal file
51
bookkeeping.notes
Normal file
@@ -0,0 +1,51 @@
|
||||
Book Keeping Challenges / Issues
|
||||
- Chart of Accounts is not sufficient
|
||||
- One payment can go towards more than one charge
|
||||
- One charge can be paid for by more than one payment
|
||||
- Need to know: how much money does customer owe?
|
||||
- Need to know: how much money has customer been charged for "Rent"?
|
||||
- Need to know: how much money has customer paid towards "Rent" (nothing else)?
|
||||
|
||||
|
||||
- Reverse a charge
|
||||
To make charge reversal work, I'm presently issuing a credit note
|
||||
(transaction) that has 3 ledger entries:
|
||||
Debit charged account - Credit A/R
|
||||
Debit A/R - Credit "Credit" account
|
||||
Debit "Credit" account - Credit A/R
|
||||
And 3 statement entries:
|
||||
Reversal on charged account
|
||||
Negative Disbursement on "Credit" account
|
||||
Positive Disbursement on "Credit" account
|
||||
|
||||
Obviously, the two "Credit" account ledger entries cancel each other
|
||||
out. However, the monies need to be reversed in the A/R account, and a
|
||||
previous transaction will have already performed an A/R credit and so
|
||||
the two A/R credits will create an imbalance in A/R unless we move
|
||||
those funds to our "Credit" account. The "Credit" account statement
|
||||
entries are needed to undo any previous disbursement to the charge
|
||||
being reversed, and then to re-disburse those funds to a different
|
||||
charge (or possibly just a Surplus entry if no other charges exist.)
|
||||
|
||||
|
||||
Question
|
||||
- What should be recorded with this charge reversal?
|
||||
Charge of $5 for "Parking"
|
||||
Charge of $6 for "Rent"
|
||||
Payment of $6
|
||||
Reverse the $5 "Parking" charge
|
||||
|
||||
- What should be recorded with this charge reversal?
|
||||
Charge of $5 for "Parking"
|
||||
Charge of $6 for "Rent"
|
||||
Charge of $7 for "Pets"
|
||||
Payment of $13
|
||||
Reverse the $5 "Parking" charge
|
||||
|
||||
- What should be recorded with this charge reversal?
|
||||
Charge of $5 for "Parking"
|
||||
Charge of $6 for "Rent"
|
||||
Charge of $7 for "Pets"
|
||||
Payment of $18
|
||||
Reverse the $5 "Parking" charge
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
@echo off
|
||||
%~dp0\scripts\sitelink2pmgr.pl %~dp0\db\schema.sql %~dp0db\vss.mdb %*
|
||||
mysql --user=pmgr --password=pmgruser < %~dp0\db\property_manager.sql
|
||||
echo Done!
|
||||
|
||||
1
db/property_manager.sql
Normal file
1
db/property_manager.sql
Normal file
@@ -0,0 +1 @@
|
||||
Please see previous revisions on /tags/v0.1.0
|
||||
728
db/schema.sql
728
db/schema.sql
@@ -25,9 +25,9 @@
|
||||
-- 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`;
|
||||
DROP DATABASE IF EXISTS `property_manager`;
|
||||
CREATE DATABASE `property_manager`;
|
||||
USE `property_manager`;
|
||||
|
||||
|
||||
-- ######################################################################
|
||||
@@ -241,7 +241,7 @@ CREATE TABLE `pmgr_contacts_methods` (
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ##
|
||||
-- ## GROUPS
|
||||
-- ## GROUPS / USERS
|
||||
-- ##
|
||||
|
||||
|
||||
@@ -256,59 +256,15 @@ CREATE TABLE `pmgr_groups` (
|
||||
-- code may not be userful
|
||||
`code` VARCHAR(12) NOT NULL, -- User style "id"
|
||||
`name` VARCHAR(80) NOT NULL,
|
||||
|
||||
-- Lower ranks are given higher priority
|
||||
`rank` SMALLINT UNSIGNED NOT NULL DEFAULT 100,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_group_options
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_group_options`;
|
||||
CREATE TABLE `pmgr_group_options` (
|
||||
`group_id` INT(10) UNSIGNED NOT NULL,
|
||||
`name` VARCHAR(50) NOT NULL,
|
||||
`value` VARCHAR(255) NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`group_id`, `name`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_group_permissions
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_group_permissions`;
|
||||
CREATE TABLE `pmgr_group_permissions` (
|
||||
`group_id` INT(10) UNSIGNED NOT NULL,
|
||||
`name` CHAR(30) NOT NULL,
|
||||
`access` ENUM('ALLOWED',
|
||||
'DENIED',
|
||||
'FORCED')
|
||||
NOT NULL DEFAULT 'ALLOWED',
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`group_id`, `name`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ##
|
||||
-- ## USERS
|
||||
-- ##
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
@@ -318,14 +274,10 @@ DROP TABLE IF EXISTS `pmgr_users`;
|
||||
CREATE TABLE `pmgr_users` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`code` VARCHAR(12) NOT NULL, -- User style "id"
|
||||
|
||||
-- Login details. Passwords are not yet used (and so NULL).
|
||||
`login` VARCHAR(30) NOT NULL,
|
||||
`salt` CHAR(12) DEFAULT NULL,
|
||||
`passhash` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
-- Contact information for this user
|
||||
`contact_id` INT(10) UNSIGNED NOT NULL,
|
||||
`contact_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
-- Specific comments
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
@@ -334,18 +286,208 @@ CREATE TABLE `pmgr_users` (
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ##
|
||||
-- ## OPTIONS
|
||||
-- ##
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_options
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_options`;
|
||||
CREATE TABLE `pmgr_options` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(50) NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
UNIQUE KEY `name_key` (`name`),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_option_values
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_option_values`;
|
||||
CREATE TABLE `pmgr_option_values` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`option_id` INT(10) UNSIGNED NOT NULL,
|
||||
`value` VARCHAR(255) NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_default_options
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_default_options`;
|
||||
CREATE TABLE `pmgr_default_options` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`option_value_id` INT(10) UNSIGNED NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_group_options
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_group_options`;
|
||||
CREATE TABLE `pmgr_group_options` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`group_id` INT(10) UNSIGNED NOT NULL,
|
||||
`option_value_id` INT(10) UNSIGNED NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
KEY `group_key` (`group_id`),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_user_options
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_user_options`;
|
||||
CREATE TABLE `pmgr_user_options` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` INT(10) UNSIGNED NOT NULL,
|
||||
`name` VARCHAR(50) NOT NULL,
|
||||
`value` VARCHAR(255) NOT NULL,
|
||||
`option_value_id` INT(10) UNSIGNED NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
KEY `user_key` (`user_id`),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
PRIMARY KEY (`user_id`, `name`)
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_site_options
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_site_options`;
|
||||
CREATE TABLE `pmgr_site_options` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`site_id` INT(10) UNSIGNED NOT NULL,
|
||||
`option_value_id` INT(10) UNSIGNED NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
KEY `site_key` (`site_id`),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
|
||||
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ##
|
||||
-- ## PERMISSIONS
|
||||
-- ##
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_permissions
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_permissions`;
|
||||
CREATE TABLE `pmgr_permissions` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(50) NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
UNIQUE KEY `name_key` (`name`),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_permission_values
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_permission_values`;
|
||||
CREATE TABLE `pmgr_permission_values` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`permission_id` INT(10) UNSIGNED NOT NULL,
|
||||
`access` ENUM('ALLOW',
|
||||
'DENY')
|
||||
NOT NULL DEFAULT 'DENY',
|
||||
`level` SMALLINT UNSIGNED DEFAULT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_default_permissions
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_default_permissions`;
|
||||
CREATE TABLE `pmgr_default_permissions` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`permission_value_id` INT(10) UNSIGNED NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_group_permissions
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_group_permissions`;
|
||||
CREATE TABLE `pmgr_group_permissions` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`group_id` INT(10) UNSIGNED NOT NULL,
|
||||
`permission_value_id` INT(10) UNSIGNED NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
KEY `group_key` (`group_id`),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_user_permissions
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_user_permissions`;
|
||||
CREATE TABLE `pmgr_user_permissions` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` INT(10) UNSIGNED NOT NULL,
|
||||
`permission_value_id` INT(10) UNSIGNED NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
KEY `user_key` (`user_id`),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_site_permissions
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_site_permissions`;
|
||||
CREATE TABLE `pmgr_site_permissions` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`site_id` INT(10) UNSIGNED NOT NULL,
|
||||
`permission_value_id` INT(10) UNSIGNED NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
KEY `site_key` (`site_id`),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
@@ -380,46 +522,6 @@ CREATE TABLE `pmgr_sites` (
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_site_options
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_site_options`;
|
||||
CREATE TABLE `pmgr_site_options` (
|
||||
`site_id` INT(10) UNSIGNED NOT NULL,
|
||||
`name` VARCHAR(50) NOT NULL,
|
||||
`value` VARCHAR(255) NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`site_id`, `name`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_site_memberships
|
||||
--
|
||||
-- Which users are allowed to access which sites,
|
||||
-- and under which set of group permissions (possibly multiple)
|
||||
|
||||
-- SELECT U.id, P.name, MAX(P.access)
|
||||
-- FROM pmgr_users U
|
||||
-- LEFT JOIN pmgr_site_membership M ON M.user_id = U.id
|
||||
-- LEFT JOIN pmgr_groups G ON G.id = M.group_id
|
||||
-- LEFT JOIN pmgr_group_permissions P ON P.group_id = G.id
|
||||
-- GROUP BY U.id, P.name
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_site_memberships`;
|
||||
CREATE TABLE `pmgr_site_memberships` (
|
||||
`site_id` INT(10) UNSIGNED NOT NULL,
|
||||
`user_id` INT(10) UNSIGNED NOT NULL,
|
||||
`group_id` INT(10) UNSIGNED NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`site_id`, `user_id`, `group_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_site_areas
|
||||
@@ -437,6 +539,38 @@ CREATE TABLE `pmgr_site_areas` (
|
||||
|
||||
|
||||
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ##
|
||||
-- ## MEMBERSHIPS
|
||||
-- ##
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_memberships
|
||||
--
|
||||
-- Which users are allowed to access which sites,
|
||||
-- and under which set of group permissions (possibly multiple)
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_memberships`;
|
||||
CREATE TABLE `pmgr_memberships` (
|
||||
`site_id` INT(10) UNSIGNED NOT NULL,
|
||||
`user_id` INT(10) UNSIGNED NOT NULL,
|
||||
`group_id` INT(10) UNSIGNED NOT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`site_id`, `user_id`, `group_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
@@ -471,13 +605,10 @@ CREATE TABLE `pmgr_units` (
|
||||
'DIRTY',
|
||||
'VACANT',
|
||||
'OCCUPIED',
|
||||
'LATE', -- NOT SURE
|
||||
'LOCKED',
|
||||
'LIENED')
|
||||
NOT NULL DEFAULT 'VACANT',
|
||||
|
||||
`current_lease_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
`sort_order` MEDIUMINT UNSIGNED NOT NULL,
|
||||
`walk_order` MEDIUMINT UNSIGNED NOT NULL,
|
||||
|
||||
@@ -644,6 +775,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`)
|
||||
@@ -714,6 +856,9 @@ CREATE TABLE `pmgr_leases` (
|
||||
`notice_received_date` DATE DEFAULT NULL,
|
||||
`close_date` DATE DEFAULT NULL,
|
||||
|
||||
`charge_through_date` DATE DEFAULT NULL,
|
||||
`paid_through_date` DATE DEFAULT NULL,
|
||||
|
||||
`deposit` FLOAT(12,2) DEFAULT NULL,
|
||||
`rent` FLOAT(12,2) DEFAULT NULL,
|
||||
|
||||
@@ -842,12 +987,11 @@ CREATE TABLE `pmgr_accounts` (
|
||||
-- For LIABILITY, EQUITY, and INCOME, the opposite
|
||||
-- is true, with reconciliations posted, under
|
||||
-- normal circumstances, when a debit occurs.
|
||||
`trackable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
|
||||
`tillable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Does manager collect by hand?
|
||||
`depositable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Does this account receive deposits?
|
||||
`chargeable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for charges?
|
||||
`payable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for payments?
|
||||
`refundable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, -- Can be used for refunds?
|
||||
-- `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 10,
|
||||
@@ -862,47 +1006,46 @@ CREATE TABLE `pmgr_accounts` (
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
LOCK TABLES `pmgr_accounts` WRITE;
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `level`)
|
||||
VALUES
|
||||
('EQUITY', 'Equity', 1),
|
||||
('LIABILITY', 'Loan', 1);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`)
|
||||
VALUES
|
||||
('ASSET', 'A/R' ),
|
||||
('ASSET', 'Invoice' ),
|
||||
('ASSET', 'Receipt' ),
|
||||
-- REVISIT <AP>: 20090710 : We don't really need NSF, as it
|
||||
-- will always run a zero balance. However, it will help
|
||||
-- us identify how serious the NSF situation is.
|
||||
('LIABILITY', 'A/P' ),
|
||||
('LIABILITY', 'Credit' );
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `receipts`)
|
||||
VALUES
|
||||
('ASSET', 'Cash', 1),
|
||||
('ASSET', 'Check', 1),
|
||||
('ASSET', 'Money Order', 1),
|
||||
('ASSET', 'ACH', 1),
|
||||
('EXPENSE', 'Concession', 1);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`)
|
||||
VALUES
|
||||
('ASSET', 'NSF' ),
|
||||
('LIABILITY', 'A/P' );
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `tillable`, `payable`, `refundable`)
|
||||
('EXPENSE', 'Waiver' ),
|
||||
('EXPENSE', 'Bad Debt' );
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `invoices`)
|
||||
VALUES
|
||||
('ASSET', 'Cash', 1, 1, 1),
|
||||
('ASSET', 'Check', 1, 1, 0),
|
||||
('ASSET', 'Money Order', 1, 1, 0),
|
||||
('ASSET', 'ACH', 0, 1, 0),
|
||||
('EXPENSE', 'Concession', 0, 1, 0);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `refundable`, `depositable`)
|
||||
('LIABILITY', 'Tax', 0),
|
||||
('LIABILITY', 'Security Deposit', 1),
|
||||
('INCOME', 'Rent', 1),
|
||||
('INCOME', 'Late Charge', 1),
|
||||
('INCOME', 'NSF Charge', 1),
|
||||
('INCOME', 'Cleaning', 1),
|
||||
('INCOME', 'Damage', 1);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`)
|
||||
VALUES
|
||||
-- REVISIT <AP>: 20090710 : We probably don't really want petty cash depositable.
|
||||
-- This is just for testing our deposit code
|
||||
('ASSET', 'Petty Cash', 1, 1);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `chargeable`, `trackable`)
|
||||
('EXPENSE', 'Maintenance' );
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `refunds`)
|
||||
VALUES
|
||||
('LIABILITY', 'Tax', 1, 1),
|
||||
('LIABILITY', 'Security Deposit', 1, 1),
|
||||
('INCOME', 'Rent', 1, 0),
|
||||
('INCOME', 'Late Charge', 1, 0),
|
||||
('INCOME', 'NSF Charge', 1, 0),
|
||||
('INCOME', 'Damage', 1, 0);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `depositable`, `trackable`)
|
||||
('ASSET', 'Petty Cash', 1);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `level`, `deposits`, `refunds`)
|
||||
VALUES
|
||||
('ASSET', 'Bank', 1, 0);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `trackable`)
|
||||
('ASSET', 'Bank', 6, 1, 1);
|
||||
INSERT INTO `pmgr_accounts` (`type`, `name`, `level`)
|
||||
VALUES
|
||||
('EXPENSE', 'Bad Debt', 0),
|
||||
('EXPENSE', 'Maintenance', 0);
|
||||
('ASSET', 'Closing', 6),
|
||||
('LIABILITY', 'Loan', 1),
|
||||
('EQUITY', 'Equity', 1);
|
||||
UNLOCK TABLES;
|
||||
|
||||
|
||||
@@ -924,23 +1067,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,
|
||||
|
||||
`name` VARCHAR(90) NOT NULL,
|
||||
|
||||
`account_id` INT(10) UNSIGNED NOT NULL,
|
||||
`sequence` INT(10) UNSIGNED DEFAULT 1,
|
||||
|
||||
`prior_ledger_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
`close_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
`close_transaction_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
@@ -948,20 +1086,6 @@ CREATE TABLE `pmgr_ledgers` (
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_closes
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_closes`;
|
||||
CREATE TABLE `pmgr_closes` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_transactions
|
||||
@@ -970,17 +1094,53 @@ DROP TABLE IF EXISTS `pmgr_transactions`;
|
||||
CREATE TABLE `pmgr_transactions` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- `type` ENUM('INVOICE',
|
||||
-- 'RECEIPT')
|
||||
-- NOT 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',
|
||||
'AUTO_DEPOSIT', -- Fundamentally same as DEPOSIT
|
||||
'WITHDRAWAL',
|
||||
'CLOSE', -- Essentially an internal (not accounting) transaction
|
||||
-- 'CREDIT',
|
||||
-- 'REFUND',
|
||||
'TRANSFER') -- Unsure of TRANSFERs use
|
||||
NOT NULL,
|
||||
|
||||
`stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`due_date` DATE DEFAULT 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,
|
||||
-- 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,
|
||||
|
||||
@@ -988,38 +1148,114 @@ 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,
|
||||
|
||||
-- Effective date may be used for a variety of entries
|
||||
-- charges & payments are not always effective at the
|
||||
-- time of transaction. Through date, on the other hand,
|
||||
-- will probably only be relevant for charges, such as
|
||||
-- rent, which is effective for a range of dates.
|
||||
`effective_date` DATE DEFAULT NULL, -- first day
|
||||
`through_date` DATE DEFAULT NULL, -- last day
|
||||
|
||||
`monetary_source_id` INT(10) UNSIGNED DEFAULT NULL, -- NULL if internal transfer
|
||||
`transaction_id` INT(10) UNSIGNED NOT NULL,
|
||||
`customer_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
`lease_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
-- 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,
|
||||
|
||||
-- REVISIT <AP>: 20090707
|
||||
-- Experimental. Considering automatically hooking
|
||||
-- charges to their invoice. This may help ease the
|
||||
-- ongoing accounting dilema that we've been having.
|
||||
-- It might allow us to keep the underlying invoice
|
||||
-- ledgers without having to expose the user to them.
|
||||
`root_transaction_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
`debit_ledger_id` INT(10) UNSIGNED NOT NULL,
|
||||
`credit_ledger_id` INT(10) UNSIGNED NOT 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
|
||||
'WRITEOFF', -- Write-off bad debt
|
||||
'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,
|
||||
|
||||
-- The transaction that reversed this charge, if any
|
||||
`reverse_transaction_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
@@ -1029,15 +1265,54 @@ 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,
|
||||
|
||||
-- Should these items be deposited automatically?
|
||||
`auto_deposit` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- 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,
|
||||
|
||||
-- The field from pmgr_tenders that is used for helping
|
||||
-- to name the tender. For example, a Check tender type
|
||||
-- might specify data1 as the field, so that tenders
|
||||
-- would be named "Check #0000"
|
||||
`naming_field` VARCHAR(80) DEFAULT 'id',
|
||||
|
||||
-- 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,
|
||||
|
||||
-- Which account should these items be deposited in?
|
||||
-- This may or may not actually be used for all types
|
||||
-- but will likely get used for auto deposit items.
|
||||
`deposit_account_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
`comment` VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
@@ -1045,14 +1320,23 @@ 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,
|
||||
|
||||
-- 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;
|
||||
@@ -1071,12 +1355,52 @@ CREATE TABLE `pmgr_monetary_sources` (
|
||||
`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 ledger entry if this actual deposit transaction
|
||||
`deposit_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,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
-- ----------------------------------------------------------------------
|
||||
-- TABLE pmgr_deposits
|
||||
|
||||
DROP TABLE IF EXISTS `pmgr_deposits`;
|
||||
CREATE TABLE `pmgr_deposits` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
||||
`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,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
|
||||
|
||||
-- ######################################################################
|
||||
-- ######################################################################
|
||||
|
||||
578
db/scratch.sql
Normal file
578
db/scratch.sql
Normal file
@@ -0,0 +1,578 @@
|
||||
-- Delete bad transaction(s)
|
||||
DELETE M
|
||||
FROM
|
||||
pmgr_ledger_entries LE,
|
||||
pmgr_tenders M
|
||||
WHERE
|
||||
M.ledger_entry_id = LE.id AND
|
||||
LE.transaction_id
|
||||
IN (467);
|
||||
DELETE LE
|
||||
FROM
|
||||
pmgr_ledger_entries LE
|
||||
WHERE
|
||||
LE.transaction_id
|
||||
IN (467);
|
||||
DELETE SE
|
||||
FROM
|
||||
pmgr_statement_entries SE
|
||||
WHERE
|
||||
SE.transaction_id
|
||||
IN (467);
|
||||
DELETE T
|
||||
FROM
|
||||
pmgr_transactions T
|
||||
WHERE
|
||||
T.id
|
||||
IN (467);
|
||||
|
||||
-- Delete bad transaction, one variable setting
|
||||
SET @tid = 467;
|
||||
DELETE M FROM pmgr_ledger_entries LE, pmgr_tenders M
|
||||
WHERE M.ledger_entry_id = LE.id AND LE.transaction_id = @tid;
|
||||
DELETE LE FROM pmgr_ledger_entries LE
|
||||
WHERE LE.transaction_id = @tid;
|
||||
DELETE SE FROM pmgr_statement_entries SE
|
||||
WHERE SE.transaction_id = @tid;
|
||||
DELETE T FROM pmgr_transactions T
|
||||
WHERE T.id = @tid;
|
||||
|
||||
|
||||
-- Delete all but one customer
|
||||
SET @cid = 6;
|
||||
-- DELETE T FROM pmgr_transactions T
|
||||
-- LEFT JOIN pmgr_customers C ON C.id = T.customer_id
|
||||
-- WHERE C.id IS NOT NULL AND C.id <> @cid;
|
||||
DELETE C FROM pmgr_customers C
|
||||
WHERE C.id <> @cid;
|
||||
DELETE L FROM pmgr_leases L
|
||||
LEFT JOIN pmgr_customers C ON C.id = L.customer_id
|
||||
WHERE C.id IS NULL;
|
||||
DELETE T FROM pmgr_transactions T
|
||||
LEFT JOIN pmgr_customers C ON C.id = T.customer_id
|
||||
WHERE C.id IS NULL;
|
||||
DELETE SE FROM pmgr_statement_entries SE
|
||||
LEFT JOIN pmgr_customers C ON C.id = SE.customer_id
|
||||
WHERE C.id IS NULL;
|
||||
DELETE LE FROM pmgr_ledger_entries LE
|
||||
LEFT JOIN pmgr_transactions T ON T.id = LE.transaction_id
|
||||
WHERE T.id IS NULL;
|
||||
DELETE M FROM pmgr_tenders M
|
||||
LEFT JOIN pmgr_ledger_entries LE ON M.ledger_entry_id = LE.id
|
||||
WHERE LE.id IS NULL;
|
||||
DELETE DE FROM pmgr_double_entries DE
|
||||
LEFT JOIN pmgr_ledger_entries LE ON LE.id = DE.debit_entry_id
|
||||
WHERE LE.id IS NULL;
|
||||
DELETE DE FROM pmgr_double_entries DE
|
||||
LEFT JOIN pmgr_ledger_entries LE ON LE.id = DE.credit_entry_id
|
||||
WHERE LE.id IS NULL;
|
||||
UPDATE pmgr_ledger_entries LE, pmgr_ledgers L, pmgr_accounts A
|
||||
SET LE.ledger_id = L.id
|
||||
WHERE A.id = LE.account_id AND L.account_id = A.id AND L.sequence = 1;
|
||||
DELETE FROM pmgr_ledgers WHERE sequence > 1;
|
||||
UPDATE pmgr_ledgers SET prior_ledger_id = NULL, close_transaction_id = NULL;
|
||||
|
||||
|
||||
-- Delete a ledger entry, associated double entry, and matching ledger_entry
|
||||
SET @leid = 1365;
|
||||
DELETE FROM pmgr_ledger_entries WHERE id = @leid;
|
||||
DELETE DE FROM pmgr_double_entries DE
|
||||
LEFT JOIN pmgr_ledger_entries LE ON LE.id = DE.debit_entry_id
|
||||
WHERE LE.id IS NULL;
|
||||
DELETE DE FROM pmgr_double_entries DE
|
||||
LEFT JOIN pmgr_ledger_entries LE ON LE.id = DE.credit_entry_id
|
||||
WHERE LE.id IS NULL;
|
||||
DELETE LE FROM pmgr_ledger_entries LE
|
||||
LEFT JOIN pmgr_double_entries DE
|
||||
ON DE.credit_entry_id = LE.id OR DE.debit_entry_id = LE.id
|
||||
WHERE DE.id IS NULL;
|
||||
|
||||
-- Add and update every Tender.ledger_entry_id (for rolling up old databases)
|
||||
-- Takes a while to complete (~30s at time of writing)
|
||||
ALTER TABLE `pmgr_tenders`
|
||||
ADD `deposit_ledger_entry_id` INT UNSIGNED DEFAULT NULL
|
||||
AFTER `nsf_ledger_entry_id`;
|
||||
UPDATE
|
||||
pmgr_tenders Tnd
|
||||
JOIN pmgr_tender_types TndT ON TndT.id = Tnd.tender_type_id
|
||||
JOIN pmgr_transactions T ON T.id = Tnd.deposit_transaction_id
|
||||
JOIN pmgr_ledger_entries LE ON LE.transaction_id = T.id AND LE.account_id = TndT.account_id
|
||||
JOIN pmgr_double_entries DE ON DE.debit_entry_id = LE.id OR DE.credit_entry_id = LE.id
|
||||
JOIN pmgr_ledger_entries LEd ON (DE.debit_entry_id = LEd.id OR DE.credit_entry_id = LEd.id)
|
||||
AND LEd.id <> LE.id
|
||||
SET Tnd.deposit_ledger_entry_id = LEd.id;
|
||||
|
||||
|
||||
-- Add auto_deposit and deposit_account_id to tenders
|
||||
ALTER TABLE `pmgr_tender_types`
|
||||
ADD `auto_deposit` TINYINT(1) UNSIGNED DEFAULT '0' NOT NULL
|
||||
AFTER `tillable`;
|
||||
ALTER TABLE `pmgr_tender_types`
|
||||
ADD `deposit_account_id` INTEGER(10) UNSIGNED DEFAULT NULL
|
||||
AFTER `account_id`;
|
||||
|
||||
|
||||
-- Determine economic conditions
|
||||
SELECT `status`, COUNT(id), SUM(rent) FROM pmgr_units
|
||||
GROUP BY `status` WITH ROLLUP;
|
||||
|
||||
|
||||
-- Check that transaction totals add up correctly
|
||||
SELECT T.id, T.type, T.amount,
|
||||
-- T.type, A.type, E.crdr,
|
||||
SUM(IF(E.account_id = T.account_id,
|
||||
IF(A.type IN ('ASSET','EXPENSE') XOR E.crdr='DEBIT',-1,1),0)
|
||||
*E.amount) AS Tamt,
|
||||
SUM(IF(E.account_id = T.account_id,
|
||||
0,IF(A.type IN ('ASSET','EXPENSE') XOR E.crdr='DEBIT',-1,1))
|
||||
*E.amount) AS Oamt,
|
||||
COUNT(E.id) AS Ecnt
|
||||
FROM pmgr_transactions T
|
||||
-- LEFT JOIN pmgr_statement_entries E ON E.transaction_id = T.id
|
||||
LEFT JOIN pmgr_ledger_entries E ON E.transaction_id = T.id
|
||||
LEFT JOIN pmgr_accounts A ON A.id = T.account_id -- E.account_id
|
||||
-- WHERE
|
||||
-- E.account_id != T.account_id
|
||||
GROUP BY T.id
|
||||
HAVING
|
||||
(T.type = 'INVOICE' AND Tamt <> T.amount)
|
||||
OR
|
||||
(T.type <> 'INVOICE' AND Oamt <> T.amount)
|
||||
OR
|
||||
(Tamt * -1 <> Oamt)
|
||||
|
||||
|
||||
-- Verify that statement entries all have the correct type
|
||||
SELECT SE.id, SE.type, T.id, T.type
|
||||
FROM pmgr_statement_entries SE
|
||||
LEFT JOIN pmgr_transactions T ON T.id = SE.transaction_id
|
||||
WHERE
|
||||
((T.type = 'RECEIPT' OR T.type = 'CREDIT_NOTE') AND
|
||||
SE.type NOT IN ('DISBURSEMENT', 'WAIVER', 'REVERSAL', 'WRITEOFF', 'SURPLUS')
|
||||
)
|
||||
OR
|
||||
((T.type = 'INVOICE' OR T.type = 'PAYMENT') AND
|
||||
SE.type NOT IN ('CHARGE', 'PAYMENT', 'REFUND')
|
||||
)
|
||||
-- catch other types not considered in this query
|
||||
OR T.type NOT IN ('RECEIPT', 'CREDIT_NOTE', 'INVOICE', 'PAYMENT')
|
||||
|
||||
|
||||
|
||||
|
||||
-- #################################################################
|
||||
-- #################################################################
|
||||
-- #################################################################
|
||||
-- #################################################################
|
||||
-- #################################################################
|
||||
-- #################################################################
|
||||
-- #################################################################
|
||||
-- #################################################################
|
||||
-- #################################################################
|
||||
-- ## USER / GROUP
|
||||
|
||||
INSERT INTO pmgr_groups (`code`, `name`, `rank`)
|
||||
VALUES('Owner', 'Owner Group', 25);
|
||||
SET @o_gid = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO pmgr_groups (`code`, `name`, `rank`)
|
||||
VALUES('Admin', 'Admin Group', 50);
|
||||
SET @a_gid = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO pmgr_groups (`code`, `name`, `rank`)
|
||||
VALUES('Manager', 'Manager Group', 75);
|
||||
SET @m_gid = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO pmgr_groups (`code`, `name`)
|
||||
VALUES('Temp', 'Temporary Help');
|
||||
SET @t_gid = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO pmgr_users (`code`, `login`, `contact_id`)
|
||||
VALUES('AP', 'abijah', 0);
|
||||
SET @a_uid = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO pmgr_users (`code`, `login`, `contact_id`)
|
||||
VALUES('SK', 'shirley', 0);
|
||||
SET @s_uid = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO pmgr_users (`code`, `login`, `contact_id`)
|
||||
VALUES('DE', 'dan', 0);
|
||||
SET @d_uid = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO pmgr_users (`code`, `login`, `contact_id`)
|
||||
VALUES('KD', 'kevin', 0);
|
||||
SET @k_uid = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO pmgr_sites (`code`, `name`)
|
||||
VALUES('VSS', 'Valley Storage');
|
||||
SET @v_sid = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO pmgr_sites (`code`, `name`)
|
||||
VALUES('FAKE', 'Fake Site');
|
||||
SET @f_sid = LAST_INSERT_ID();
|
||||
|
||||
-- Site Membership
|
||||
|
||||
INSERT INTO pmgr_site_memberships (`site_id`, `user_id`, `group_id`)
|
||||
VALUES(@v_sid, @a_uid, @o_gid);
|
||||
INSERT INTO pmgr_site_memberships (`site_id`, `user_id`, `group_id`)
|
||||
VALUES(@v_sid, @a_uid, @a_gid);
|
||||
INSERT INTO pmgr_site_memberships (`site_id`, `user_id`, `group_id`)
|
||||
VALUES(@v_sid, @a_uid, @m_gid);
|
||||
INSERT INTO pmgr_site_memberships (`site_id`, `user_id`, `group_id`)
|
||||
VALUES(@v_sid, @s_uid, @m_gid);
|
||||
INSERT INTO pmgr_site_memberships (`site_id`, `user_id`, `group_id`)
|
||||
VALUES(@v_sid, @d_uid, @t_gid);
|
||||
INSERT INTO pmgr_site_memberships (`site_id`, `user_id`, `group_id`)
|
||||
VALUES(@f_sid, @s_uid, @a_gid);
|
||||
INSERT INTO pmgr_site_memberships (`site_id`, `user_id`, `group_id`)
|
||||
VALUES(@f_sid, @s_uid, @m_gid);
|
||||
INSERT INTO pmgr_site_memberships (`site_id`, `user_id`, `group_id`)
|
||||
VALUES(@f_sid, @k_uid, @o_gid);
|
||||
INSERT INTO pmgr_site_memberships (`site_id`, `user_id`, `group_id`)
|
||||
VALUES(@f_sid, @d_uid, @t_gid);
|
||||
|
||||
|
||||
-- Options
|
||||
|
||||
INSERT INTO pmgr_options (`name`) VALUES ('theme');
|
||||
SET @t_oid = LAST_INSERT_ID();
|
||||
INSERT INTO pmgr_options (`name`) VALUES ('menu');
|
||||
SET @m_oid = LAST_INSERT_ID();
|
||||
|
||||
-- Default Option Values
|
||||
|
||||
INSERT INTO pmgr_option_values (`option_id`, `value`) VALUES (@t_oid, 'blue');
|
||||
INSERT INTO pmgr_default_options (`option_value_id`) VALUES(LAST_INSERT_ID());
|
||||
INSERT INTO pmgr_option_values (`option_id`, `value`) VALUES (@m_oid, 'basic');
|
||||
INSERT INTO pmgr_default_options (`option_value_id`) VALUES(LAST_INSERT_ID());
|
||||
|
||||
-- Group options
|
||||
|
||||
INSERT INTO pmgr_option_values (`option_id`, `value`) VALUES (@t_oid, 'gold');
|
||||
INSERT INTO pmgr_group_options (`group_id`, `option_value_id`)
|
||||
VALUES(@o_gid, LAST_INSERT_ID());
|
||||
INSERT INTO pmgr_option_values (`option_id`, `value`) VALUES (@t_oid, 'silver');
|
||||
INSERT INTO pmgr_group_options (`group_id`, `option_value_id`)
|
||||
VALUES(@a_gid, LAST_INSERT_ID());
|
||||
INSERT INTO pmgr_option_values (`option_id`, `value`) VALUES (@t_oid, 'red');
|
||||
INSERT INTO pmgr_group_options (`group_id`, `option_value_id`)
|
||||
VALUES(@m_gid, LAST_INSERT_ID());
|
||||
|
||||
INSERT INTO pmgr_option_values (`option_id`, `value`) VALUES (@m_oid, 'advanced');
|
||||
INSERT INTO pmgr_group_options (`group_id`, `option_value_id`)
|
||||
VALUES(@o_gid, LAST_INSERT_ID());
|
||||
INSERT INTO pmgr_option_values (`option_id`, `value`) VALUES (@m_oid, 'advanced');
|
||||
INSERT INTO pmgr_group_options (`group_id`, `option_value_id`)
|
||||
VALUES(@a_gid, LAST_INSERT_ID());
|
||||
INSERT INTO pmgr_option_values (`option_id`, `value`) VALUES (@m_oid, 'restricted');
|
||||
INSERT INTO pmgr_group_options (`group_id`, `option_value_id`)
|
||||
VALUES(@t_gid, LAST_INSERT_ID());
|
||||
|
||||
-- User Options
|
||||
INSERT INTO pmgr_option_values (`option_id`, `value`) VALUES (@m_oid, 'special');
|
||||
INSERT INTO pmgr_user_options (`user_id`, `option_value_id`)
|
||||
VALUES(@s_uid, LAST_INSERT_ID());
|
||||
|
||||
-- Site Options
|
||||
INSERT INTO pmgr_option_values (`option_id`, `value`) VALUES (@t_oid, 'site-theme');
|
||||
INSERT INTO pmgr_site_options (`site_id`, `option_value_id`)
|
||||
VALUES(@f_sid, LAST_INSERT_ID());
|
||||
|
||||
|
||||
-- SELECT U.id, P.name, MAX(P.access)
|
||||
-- FROM pmgr_users U
|
||||
-- LEFT JOIN pmgr_site_membership M ON M.user_id = U.id
|
||||
-- LEFT JOIN pmgr_groups G ON G.id = M.group_id
|
||||
-- LEFT JOIN pmgr_group_permissions P ON P.group_id = G.id
|
||||
-- GROUP BY U.id, P.name
|
||||
|
||||
|
||||
-- User access to site
|
||||
SELECT U.id, U.login, COUNT(G.id) AS 'groups', MIN(G.rank) AS highest_rank
|
||||
FROM pmgr_users U
|
||||
JOIN pmgr_site_memberships M ON M.user_id = U.id
|
||||
JOIN pmgr_sites S ON S.id = M.site_id
|
||||
JOIN pmgr_groups G ON G.id = M.group_id
|
||||
WHERE S.code = 'VSS'
|
||||
GROUP BY U.id
|
||||
|
||||
|
||||
-- User Options
|
||||
SELECT O.id, O.name, O.default,
|
||||
GROUP_CONCAT(Uopt.value) AS 'value', COUNT(U.id) AS 'count'
|
||||
FROM pmgr_options O
|
||||
LEFT JOIN pmgr_user_options Uopt ON Uopt.option_id = O.id
|
||||
LEFT JOIN pmgr_users U ON U.id = Uopt.user_id
|
||||
WHERE U.id = 1
|
||||
GROUP BY O.id
|
||||
|
||||
-- Group Options
|
||||
SELECT O.id, O.name, O.default,
|
||||
GROUP_CONCAT(Gopt.value) AS 'value', COUNT(G.id) AS 'count'
|
||||
FROM pmgr_options O
|
||||
LEFT JOIN pmgr_group_options Gopt ON Gopt.option_id = O.id
|
||||
LEFT JOIN pmgr_groups G ON G.id = Gopt.group_id
|
||||
WHERE G.id = 1
|
||||
GROUP BY O.id
|
||||
|
||||
|
||||
-- Site Options
|
||||
SELECT O.id, O.name, O.default,
|
||||
GROUP_CONCAT(Sopt.value) AS 'value', COUNT(S.id) AS 'count'
|
||||
FROM pmgr_options O
|
||||
LEFT JOIN pmgr_site_options Sopt ON Sopt.option_id = O.id
|
||||
LEFT JOIN pmgr_sites S ON S.id = Sopt.site_id
|
||||
WHERE S.id = 1
|
||||
GROUP BY O.id
|
||||
|
||||
|
||||
-- Option value for member & site
|
||||
SELECT O.id, O.name, O.default,
|
||||
S.id AS site_id, Sopt.value,
|
||||
G.id AS group_id, Gopt.value,
|
||||
U.id AS user_id, Uopt.value
|
||||
FROM pmgr_options O
|
||||
LEFT JOIN pmgr_site_options Sopt ON Sopt.option_id = O.id
|
||||
LEFT JOIN pmgr_sites S ON S.id = Sopt.site_id
|
||||
LEFT JOIN pmgr_group_options Gopt ON Gopt.option_id = O.id
|
||||
LEFT JOIN pmgr_groups G ON G.id = Gopt.group_id
|
||||
LEFT JOIN pmgr_user_options Uopt ON Uopt.option_id = O.id
|
||||
LEFT JOIN pmgr_users U ON U.id = Uopt.user_id
|
||||
WHERE O.name = 'theme'
|
||||
--GROUP BY O.id
|
||||
|
||||
|
||||
|
||||
-- Option value for member & site
|
||||
-- 1) User
|
||||
SET @sid = 1;
|
||||
SET @uid = 1;
|
||||
SET @oid = 1;
|
||||
SELECT O.name, U.id, Uopt.value
|
||||
FROM pmgr_options O
|
||||
JOIN pmgr_user_options Uopt ON Uopt.option_id = O.id
|
||||
JOIN pmgr_users U ON U.id = Uopt.user_id
|
||||
-- JOIN pmgr_site_memberships M ON M.user_id = U.id
|
||||
-- JOIN pmgr_groups G ON G.id = M.group_id
|
||||
-- JOIN pmgr_sites S ON S.id = M.site_id
|
||||
WHERE -- S.id = @sid AND
|
||||
U.id = @uid AND O.id = @oid
|
||||
;
|
||||
|
||||
-- 2) Group
|
||||
SELECT O.name, G.rank, G.id, Gopt.value
|
||||
FROM pmgr_options O
|
||||
JOIN pmgr_group_options Gopt ON Gopt.option_id = O.id
|
||||
JOIN pmgr_groups G ON G.id = Gopt.group_id
|
||||
JOIN pmgr_site_memberships M ON M.group_id = G.id
|
||||
JOIN pmgr_users U ON U.id = M.user_id
|
||||
JOIN pmgr_sites S ON S.id = M.site_id
|
||||
WHERE S.id = @sid AND U.id = @uid AND O.id = @oid
|
||||
ORDER BY G.rank
|
||||
;
|
||||
|
||||
-- 3) Site
|
||||
SELECT O.name, S.id, Sopt.value
|
||||
FROM pmgr_options O
|
||||
JOIN pmgr_site_options Sopt ON Sopt.option_id = O.id
|
||||
JOIN pmgr_sites S ON S.id = Sopt.site_id
|
||||
-- JOIN pmgr_site_memberships M ON M.site_id = S.id
|
||||
-- JOIN pmgr_groups G ON G.id = M.group_id
|
||||
-- JOIN pmgr_users U ON U.id = M.user_id
|
||||
WHERE S.id = @sid
|
||||
-- AND U.id = @uid
|
||||
AND O.id = @oid
|
||||
;
|
||||
|
||||
-- 3) Default
|
||||
SELECT O.name, O.default AS 'value'
|
||||
FROM pmgr_options O
|
||||
WHERE O.id = @oid
|
||||
;
|
||||
|
||||
|
||||
-- User Permissions
|
||||
|
||||
|
||||
-- Group Permissions
|
||||
|
||||
-- All option values, in order
|
||||
SELECT O.name, V.value,
|
||||
U.id AS uid, G.id AS gid, S.id as sid,
|
||||
Dopt.id AS did, G.rank
|
||||
FROM pmgr_option_values V
|
||||
JOIN pmgr_options O ON O.id = V.option_id
|
||||
LEFT JOIN pmgr_user_options Uopt ON Uopt.option_value_id = V.id
|
||||
LEFT JOIN pmgr_group_options Gopt ON Gopt.option_value_id = V.id
|
||||
LEFT JOIN pmgr_site_options Sopt ON Sopt.option_value_id = V.id
|
||||
LEFT JOIN pmgr_default_options Dopt ON Dopt.option_value_id = V.id
|
||||
LEFT JOIN pmgr_groups G ON G.id = Gopt.group_id
|
||||
LEFT JOIN pmgr_users U ON U.id = Uopt.user_id
|
||||
LEFT JOIN pmgr_sites S ON S.id = Sopt.site_id
|
||||
WHERE O.id = @oid
|
||||
ORDER BY IF(U.id IS NOT NULL, 1,
|
||||
IF (G.id IS NOT NULL, 2,
|
||||
IF (S.id IS NOT NULL, 3, 4))) ASC,
|
||||
IF (G.id IS NOT NULL, G.rank, 0) ASC
|
||||
|
||||
|
||||
-- Option values relevant to the user and site, in order
|
||||
SELECT O.name, V.value,
|
||||
U.id AS uid, G.id AS gid, S.id as sid,
|
||||
Dopt.id AS did, G.rank
|
||||
FROM pmgr_option_values V
|
||||
JOIN pmgr_options O ON O.id = V.option_id
|
||||
LEFT JOIN pmgr_user_options Uopt ON Uopt.option_value_id = V.id
|
||||
LEFT JOIN pmgr_group_options Gopt ON Gopt.option_value_id = V.id
|
||||
LEFT JOIN pmgr_site_options Sopt ON Sopt.option_value_id = V.id
|
||||
LEFT JOIN pmgr_default_options Dopt ON Dopt.option_value_id = V.id
|
||||
LEFT JOIN pmgr_groups G ON G.id = Gopt.group_id
|
||||
LEFT JOIN pmgr_users U ON U.id = Uopt.user_id
|
||||
LEFT JOIN pmgr_sites S ON S.id = Sopt.site_id
|
||||
JOIN pmgr_site_memberships M ON M.user_id = U.id AND M.site_id = S.id
|
||||
WHERE S.id = @sid AND U.id = @uid AND O.id = @oid
|
||||
ORDER BY IF(U.id IS NOT NULL, 1,
|
||||
IF (G.id IS NOT NULL, 2,
|
||||
IF (S.id IS NOT NULL, 3, 4))) ASC,
|
||||
IF (G.id IS NOT NULL, G.rank, 0) ASC
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SET @sid = 1;
|
||||
SET @uid = 1;
|
||||
SET @oid = 1;
|
||||
SELECT O.name, V.value,
|
||||
U.id AS uid,
|
||||
-- G.id AS gid,
|
||||
S.id as sid,
|
||||
Dopt.id AS did
|
||||
-- G.rank
|
||||
FROM pmgr_option_values V
|
||||
JOIN pmgr_options O ON O.id = V.option_id
|
||||
LEFT JOIN pmgr_user_options Uopt ON Uopt.option_value_id = V.id
|
||||
LEFT JOIN pmgr_site_options Sopt ON Sopt.option_value_id = V.id
|
||||
-- LEFT JOIN pmgr_users U ON U.id = Uopt.user_id
|
||||
-- LEFT JOIN pmgr_group_options Gopt ON Gopt.option_value_id = V.id
|
||||
LEFT JOIN pmgr_default_options Dopt ON Dopt.option_value_id = V.id
|
||||
-- LEFT JOIN pmgr_groups G ON G.id = Gopt.group_id
|
||||
LEFT JOIN pmgr_users U ON U.id = Uopt.user_id
|
||||
LEFT JOIN pmgr_sites S ON S.id = Sopt.site_id
|
||||
JOIN pmgr_site_memberships M ON M.user_id = U.id -- AND M.site_id = S.id
|
||||
WHERE -- S.id = @sid AND U.id = @uid AND
|
||||
O.id = @oid
|
||||
ORDER BY IF(U.id IS NOT NULL, 1,
|
||||
-- IF (G.id IS NOT NULL, 2,
|
||||
IF (S.id IS NOT NULL, 3, 4)) -- ) ASC,
|
||||
-- IF (G.id IS NOT NULL, G.rank, 0) ASC
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- ------------------------------------------------------------
|
||||
-- ------------------------------------------------------------
|
||||
-- Working version (without defaults)
|
||||
SET @sid = 1;
|
||||
SET @uid = 1;
|
||||
SET @oid = 1;
|
||||
SELECT O.name, O.id AS oid, V.value, V.id AS vid,
|
||||
U.id AS uid,
|
||||
G.id AS gid,
|
||||
S.id AS sid,
|
||||
-- Dopt.id AS did
|
||||
G.rank
|
||||
FROM pmgr_users U
|
||||
JOIN pmgr_site_memberships M ON M.user_id = U.id
|
||||
JOIN pmgr_sites S ON S.id = M.site_id
|
||||
LEFT JOIN pmgr_groups G ON G.id = M.group_id
|
||||
LEFT JOIN pmgr_user_options Uopt ON Uopt.user_id = U.id
|
||||
LEFT JOIN pmgr_group_options Gopt ON Gopt.group_id = G.id
|
||||
LEFT JOIN pmgr_site_options Sopt ON Sopt.site_id = S.id
|
||||
LEFT JOIN pmgr_option_values V ON (V.id = Uopt.option_value_id OR
|
||||
V.id = Gopt.option_value_id OR
|
||||
V.id = Sopt.option_value_id)
|
||||
JOIN pmgr_options O ON O.id = V.option_id
|
||||
WHERE S.id = @sid AND U.id = @uid AND O.id = @oid
|
||||
ORDER BY IF(U.id IS NOT NULL, 1,
|
||||
IF (G.id IS NOT NULL, 2,
|
||||
IF (S.id IS NOT NULL, 3, 4))) ASC,
|
||||
IF (G.id IS NOT NULL, G.rank, 0) ASC
|
||||
;
|
||||
|
||||
|
||||
|
||||
|
||||
SET @sid = 1;
|
||||
SET @uid = 1;
|
||||
SET @oid = 1;
|
||||
SELECT O.name, O.id AS oid, V.value, V.id AS vid,
|
||||
U.id AS uid,
|
||||
G.id AS gid,
|
||||
S.id AS sid,
|
||||
-- Dopt.id AS did
|
||||
G.rank
|
||||
FROM pmgr_options O
|
||||
LEFT JOIN pmgr_option_values V ON V.option_id = O.id
|
||||
-- Now have the option and all possible values
|
||||
LEFT JOIN pmgr_user_options Uopt ON Uopt.option_value_id = V.id
|
||||
LEFT JOIN pmgr_group_options Gopt ON Gopt.option_value_id = V.id
|
||||
LEFT JOIN pmgr_site_options Sopt ON Sopt.option_value_id = V.id
|
||||
-- Now have the user/group/site that each value applies to
|
||||
LEFT JOIN pmgr_users U U ON Uopt.user_id = U.id OR Uopt.user_id IS NULL
|
||||
-- Now restricted to our user
|
||||
JOIN pmgr_site_memberships M ON M.user_id = U.id
|
||||
JOIN pmgr_sites S ON S.id = M.site_id
|
||||
|
||||
|
||||
|
||||
ON O.id = V.option_id
|
||||
LEFT JOIN pmgr_groups G ON G.id = M.group_id
|
||||
LEFT JOIN pmgr_option_values V ON (V.id = Uopt.option_value_id OR
|
||||
V.id = Gopt.option_value_id OR
|
||||
V.id = Sopt.option_value_id)
|
||||
JOIN
|
||||
WHERE S.id = @sid AND U.id = @uid AND O.id = @oid
|
||||
ORDER BY IF(U.id IS NOT NULL, 1,
|
||||
IF (G.id IS NOT NULL, 2,
|
||||
IF (S.id IS NOT NULL, 3, 4))) ASC,
|
||||
IF (G.id IS NOT NULL, G.rank, 0) ASC
|
||||
;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SET @sid = 1;
|
||||
SET @uid = 1;
|
||||
SET @oid = 1;
|
||||
SELECT O.name, O.id AS oid, V.value, V.id AS vid,
|
||||
U.id AS uid,
|
||||
G.id AS gid,
|
||||
S.id AS sid,
|
||||
-- Dopt.id AS did
|
||||
G.rank
|
||||
FROM pmgr_options O LEFT JOIN pmgr_option_values V ON V.option_id = O.id,
|
||||
pmgr_users U
|
||||
JOIN pmgr_site_memberships M ON M.user_id = U.id
|
||||
JOIN pmgr_sites S ON S.id = M.site_id
|
||||
LEFT JOIN pmgr_groups G ON G.id = M.group_id
|
||||
LEFT JOIN pmgr_user_options Uopt ON Uopt.user_id = U.id
|
||||
LEFT JOIN pmgr_group_options Gopt ON Gopt.group_id = G.id
|
||||
LEFT JOIN pmgr_site_options Sopt ON Sopt.site_id = S.id,
|
||||
WHERE S.id = @sid AND U.id = @uid AND O.id = @oid
|
||||
AND (V.id = Uopt.option_value_id OR
|
||||
V.id = Gopt.option_value_id OR
|
||||
V.id = Sopt.option_value_id)
|
||||
ORDER BY IF(U.id IS NOT NULL, 1,
|
||||
IF (G.id IS NOT NULL, 2,
|
||||
IF (S.id IS NOT NULL, 3, 4))) ASC,
|
||||
IF (G.id IS NOT NULL, G.rank, 0) ASC
|
||||
;
|
||||
@@ -45,25 +45,24 @@ Operations to be functional
|
||||
X - Receive and record Money Orders
|
||||
X - Receive and record Cash
|
||||
X - Receive and record ACH Deposits
|
||||
x - Reverse rent charges (early moveout on prepaid occupancy)
|
||||
X - Reverse rent charges (early moveout on prepaid occupancy)
|
||||
X - Handle NSF checks
|
||||
X - Assess NSF Fees
|
||||
X - Determine Lease Paid-Through status
|
||||
- Report: List of customers overdue
|
||||
- Flag unit as overlocked
|
||||
- Flag unit as evicting
|
||||
- Flag unit as normal status
|
||||
- Flag unit as dirty
|
||||
X - 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
|
||||
- Accept pre-payments
|
||||
- NOTE: As a temporary solution, disallow customer to run
|
||||
a credit. Require charges be entered first.
|
||||
X - Accept pre-payments
|
||||
X - Record Customer Move-Out from Unit
|
||||
X - Record utilization of Security Deposit
|
||||
- Record issuing of a refund
|
||||
X - Record issuing of a refund
|
||||
- Record Deposit into Petty Cash
|
||||
- Record Payment from Petty Cash to expenses
|
||||
- Record Petty Cash to refund.
|
||||
X - Record Petty Cash to refund.
|
||||
X - Write Off Bad Debt
|
||||
X - Perform a Deposit and Close the Books
|
||||
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
@@ -3,3 +3,23 @@
|
||||
RewriteRule ^$ webroot/ [L]
|
||||
RewriteRule (.*) webroot/$1 [L]
|
||||
</IfModule>
|
||||
|
||||
# Lets deny everyone -- its a clean slate!
|
||||
order deny,allow
|
||||
deny from all
|
||||
|
||||
# Now allow local access
|
||||
# Localhost
|
||||
# allow from 127.0.0
|
||||
# Local subnet
|
||||
# allow from 192.168.7
|
||||
|
||||
# Provide a mechanism for user authentication
|
||||
AuthType Digest
|
||||
AuthName "Property Manager"
|
||||
AuthUserFile "D:/Website/auth/pmgr.htpasswd"
|
||||
Require valid-user
|
||||
|
||||
# Instead of satisfy all (too restrictive)
|
||||
# This allows EITHER local domain OR authenticated user
|
||||
satisfy any
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -37,5 +37,14 @@ App::import('Core', 'Helper');
|
||||
* @subpackage cake.cake
|
||||
*/
|
||||
class AppHelper extends Helper {
|
||||
|
||||
function url($url = null, $full = false) {
|
||||
foreach(array('sand_route', 'dev_route') AS $mod) {
|
||||
if (isset($this->params[$mod]) && is_array($url) && !isset($url[$mod]))
|
||||
$url[$mod] = $this->params[$mod];
|
||||
}
|
||||
return parent::url($url, $full);
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -42,6 +42,275 @@ class AppModel extends Model {
|
||||
var $useNullForEmpty = true;
|
||||
var $formatDateFields = true;
|
||||
|
||||
// Loaded related models with no association
|
||||
var $knows = array();
|
||||
var $app_knows = array('Option');
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: __construct
|
||||
*/
|
||||
|
||||
function __construct($id = false, $table = null, $ds = null) {
|
||||
parent::__construct($id, $table, $ds);
|
||||
|
||||
$this->knows = array_merge($this->app_knows, $this->knows);
|
||||
//$this->pr(1, array('knows' => $this->knows));
|
||||
foreach ($this->knows as $alias => $modelName) {
|
||||
if (is_numeric($alias)) {
|
||||
$alias = $modelName;
|
||||
}
|
||||
|
||||
// Don't overwrite any existing alias
|
||||
if (!empty($this->{$alias}) || get_class($this) == $alias)
|
||||
continue;
|
||||
|
||||
$model = array('class' => $modelName, 'alias' => $alias);
|
||||
if (PHP5) {
|
||||
$this->{$alias} = ClassRegistry::init($model);
|
||||
} else {
|
||||
$this->{$alias} =& ClassRegistry::init($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 = get_class($this);
|
||||
$this->pr(50, compact('class', 'level'));
|
||||
$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 = get_class($this);
|
||||
if (empty($function))
|
||||
$function = $caller['function'];
|
||||
$this->pr(50, compact('class', 'function', 'level'));
|
||||
$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]);
|
||||
}
|
||||
//$vars['class'] = get_class_vars(get_class($this));
|
||||
|
||||
$this->pr(1, $vars);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Enum Values
|
||||
* Snippet v0.1.3
|
||||
@@ -81,6 +350,30 @@ class AppModel extends Model {
|
||||
} //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;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -136,6 +429,10 @@ class AppModel extends Model {
|
||||
}
|
||||
|
||||
|
||||
function filter_null($array) {
|
||||
return array_diff_key($array, array_filter($array, 'is_null'));
|
||||
}
|
||||
|
||||
function recursive_array_replace($find, $replace, &$data) {
|
||||
if (!isset($data))
|
||||
return;
|
||||
@@ -210,4 +507,12 @@ class AppModel extends Model {
|
||||
return date('Y-m-d', strtotime($dateString));
|
||||
}
|
||||
|
||||
function INTERNAL_ERROR($msg, $depth = 0, $force_stop = false) {
|
||||
INTERNAL_ERROR($msg, $force_stop, $depth+1);
|
||||
echo $this->requestAction(array('controller' => 'util',
|
||||
'action' => 'render_empty'),
|
||||
array('return', 'bare' => false)
|
||||
);
|
||||
$this->_stop();
|
||||
}
|
||||
}
|
||||
|
||||
5
site/build_devbox.cmd
Normal file
5
site/build_devbox.cmd
Normal file
@@ -0,0 +1,5 @@
|
||||
@echo off
|
||||
mysqldump --user=pmgr --password=pmgruser --opt property_manager > H:\pmgr_dev.sql
|
||||
mysql --user=pmgr --password=pmgruser --database=pmgr_dev < H:\pmgr_dev.sql
|
||||
del H:\pmgr_dev.sql
|
||||
echo Build Complete!
|
||||
5
site/build_sandbox.cmd
Normal file
5
site/build_sandbox.cmd
Normal file
@@ -0,0 +1,5 @@
|
||||
@echo off
|
||||
mysqldump --user=pmgr --password=pmgruser --opt property_manager > H:\pmgr_sand.sql
|
||||
mysql --user=pmgr --password=pmgruser --database=pmgr_sand < H:\pmgr_sand.sql
|
||||
del H:\pmgr_sand.sql
|
||||
echo Build Complete!
|
||||
@@ -31,6 +31,92 @@
|
||||
* You can also use this to include or require any files in your application.
|
||||
*
|
||||
*/
|
||||
|
||||
function sandbox() {
|
||||
$r = Router::requestRoute();
|
||||
return !empty($r[3]['sand_route']);
|
||||
}
|
||||
|
||||
function devbox() {
|
||||
$r = Router::requestRoute();
|
||||
return !empty($r[3]['dev_route']);
|
||||
}
|
||||
|
||||
function server_request_var($var) {
|
||||
if (preg_match("/^HTTP_ACCEPT|REMOTE_PORT/", $var))
|
||||
return false;
|
||||
return (preg_match("/^HTTP|REQUEST|REMOTE/", $var));
|
||||
}
|
||||
|
||||
function INTERNAL_ERROR($message, $exit = true, $drop = 0) {
|
||||
$O = new Object();
|
||||
for ($i=0; $i<3; ++$i) {
|
||||
$O->log(str_repeat("\\", 80));
|
||||
$O->log(str_repeat("/", 80));
|
||||
}
|
||||
$O->log("INTERNAL ERROR: $message");
|
||||
echo '<DIV class="internal-error" style="color:#000; background:#c22; padding:0.5em 1.5em 0.5em 1.5em;">' . "\n";
|
||||
echo '<H1 style="color:#000; margin-bottom:0.2em; font-size:2em;">INTERNAL ERROR:</H1>' . "\n";
|
||||
echo '<H2 style="color:#000; margin-top:0; margin-left:1.5em; font-size:1.5em">' . $message . '</H2>' . "\n";
|
||||
echo '<H4 style="color:#000;">This error was not caused by anything that you did wrong.' . "\n";
|
||||
echo '<BR>It is a problem within the application itself and should be reported to the administrator.</H4>' . "\n";
|
||||
|
||||
// Print out the entire stack trace
|
||||
$O->log(str_repeat("-", 30));
|
||||
$O->log("Stack:");
|
||||
echo '<HR style="margin-top:1.0em; margin-bottom:0.5em;">' . "\nStack Trace:\n";
|
||||
echo '<OL style="margin-top:0.5em; margin-left:0.0em";>' . "\n";
|
||||
$trace = array_slice(debug_backtrace(false), $drop);
|
||||
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;
|
||||
}
|
||||
|
||||
$O->log(" $bfile:$bline (" . ($bclas ? "$bclas::$bfunc" : "entry point") . ")");
|
||||
echo("<LI>$bfile:$bline (" . ($bclas ? "$bclas::$bfunc" : "entry point") . ")</LI>\n");
|
||||
}
|
||||
echo "</OL>\n";
|
||||
|
||||
$O->log(str_repeat("-", 30));
|
||||
$O->log("HTTP Request:");
|
||||
echo '<HR style="margin-top:1.0em; margin-bottom:0.5em;">' . "\nHTTP Request:\n";
|
||||
echo '<UL style="margin-top:0.5em; margin-left:0.0em";>' . "\n";
|
||||
foreach($_REQUEST AS $k => $v) {
|
||||
$O->log(sprintf(" %-20s => %s", $k, $v));
|
||||
echo("<LI>$k => $v</LI>\n");
|
||||
}
|
||||
echo "</UL>\n";
|
||||
|
||||
$O->log(str_repeat("-", 30));
|
||||
$O->log("Server:");
|
||||
$SRV = array_intersect_key($_SERVER, array_flip(array_filter(array_keys($_SERVER), 'server_request_var')));
|
||||
echo '<HR style="margin-top:1.0em; margin-bottom:0.5em;">' . "\nServer:\n";
|
||||
echo '<UL style="margin-top:0.5em; margin-left:0.0em";>' . "\n";
|
||||
foreach($SRV AS $k => $v) {
|
||||
if ($k == 'REQUEST_TIME')
|
||||
$v = date('c', $v);
|
||||
$O->log(sprintf(" %-20s => %s", $k, $v));
|
||||
echo("<LI>$k => $v</LI>\n");
|
||||
}
|
||||
echo "</UL>\n";
|
||||
|
||||
echo '<HR style="margin-top:1.0em; margin-bottom:0.5em;">' . "\n";
|
||||
echo date('c') . "<BR>\n";
|
||||
|
||||
echo '</DIV>';
|
||||
if ($exit)
|
||||
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)
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
/**
|
||||
* The name of CakePHP's session cookie.
|
||||
*/
|
||||
Configure::write('Session.cookie', 'CAKEPHP');
|
||||
Configure::write('Session.cookie', 'PMGR');
|
||||
/**
|
||||
* Session time out time (in seconds).
|
||||
* Actual value depends on 'Security.level' setting.
|
||||
|
||||
@@ -10,5 +10,12 @@ class DATABASE_CONFIG {
|
||||
'database' => 'property_manager',
|
||||
'prefix' => 'pmgr_',
|
||||
);
|
||||
|
||||
function __construct() {
|
||||
if (devbox())
|
||||
$this->default['database'] = 'pmgr_dev';
|
||||
if (sandbox())
|
||||
$this->default['database'] = 'pmgr_sand';
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -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.
|
||||
|
||||
@@ -27,11 +27,29 @@
|
||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||
*/
|
||||
|
||||
$default_path = array('controller' => 'maps', 'action' => 'view', '1');
|
||||
|
||||
/**
|
||||
* 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' => 'maps', 'action' => 'view', '1'));
|
||||
Router::connect('/', $default_path);
|
||||
|
||||
/*
|
||||
* Route for sandbox functionality
|
||||
*/
|
||||
Router::connect('/sand',
|
||||
array('sand_route' => true) + $default_path);
|
||||
Router::connect('/sand/:controller/:action/*',
|
||||
array('sand_route' => true, 'action' => null));
|
||||
|
||||
/*
|
||||
* Route for developement functionality
|
||||
*/
|
||||
Router::connect('/dev',
|
||||
array('dev_route' => true) + $default_path);
|
||||
Router::connect('/dev/:controller/:action/*',
|
||||
array('dev_route' => true, 'action' => null));
|
||||
|
||||
?>
|
||||
@@ -2,28 +2,35 @@
|
||||
|
||||
class AccountsController extends AppController {
|
||||
|
||||
var $uses = array('Account', 'LedgerEntry');
|
||||
|
||||
var $sidemenu_links =
|
||||
array(array('name' => 'Accounts', 'header' => true),
|
||||
array('name' => 'All', 'url' => array('controller' => 'accounts', 'action' => 'all')),
|
||||
array('name' => 'Asset', 'url' => array('controller' => 'accounts', 'action' => 'asset')),
|
||||
array('name' => 'Liability', 'url' => array('controller' => 'accounts', 'action' => 'liability')),
|
||||
array('name' => 'Equity', 'url' => array('controller' => 'accounts', 'action' => 'equity')),
|
||||
array('name' => 'Income', 'url' => array('controller' => 'accounts', 'action' => 'income')),
|
||||
array('name' => 'Expense', 'url' => array('controller' => 'accounts', 'action' => 'expense')),
|
||||
array('name' => 'Bank Deposit', 'url' => array('controller' => 'accounts', 'action' => 'deposit')),
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
* override: addGridViewSideMenuLinks
|
||||
* - Adds grid view navigation side menu links
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
|
||||
function addGridViewSideMenuLinks() {
|
||||
parent::addGridViewSideMenuLinks();
|
||||
|
||||
$this->addSideMenuLink('Asset',
|
||||
array('controller' => 'accounts', 'action' => 'asset'), null,
|
||||
'CONTROLLER', $this->admin_area);
|
||||
$this->addSideMenuLink('Liability',
|
||||
array('controller' => 'accounts', 'action' => 'liability'), null,
|
||||
'CONTROLLER', $this->admin_area);
|
||||
$this->addSideMenuLink('Equity',
|
||||
array('controller' => 'accounts', 'action' => 'equity'), null,
|
||||
'CONTROLLER', $this->admin_area);
|
||||
$this->addSideMenuLink('Income',
|
||||
array('controller' => 'accounts', 'action' => 'income'), null,
|
||||
'CONTROLLER', $this->admin_area);
|
||||
$this->addSideMenuLink('Expense',
|
||||
array('controller' => 'accounts', 'action' => 'expense'), null,
|
||||
'CONTROLLER', $this->admin_area);
|
||||
$this->addSideMenuLink('All',
|
||||
array('controller' => 'accounts', 'action' => 'all'), null,
|
||||
'CONTROLLER', $this->admin_area);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,88 +42,71 @@ 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']));
|
||||
}
|
||||
|
||||
$conditions[] = array('Account.level >=' =>
|
||||
$this->Permission->level('controller.accounts'));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +119,11 @@ class AccountsController extends AppController {
|
||||
*/
|
||||
|
||||
function newledger($id = null) {
|
||||
if (!$this->Account->closeCurrentLedger($id)) {
|
||||
$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)
|
||||
@@ -152,94 +146,20 @@ class AccountsController extends AppController {
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
$payment_accounts = $this->Account->collectableAccounts();
|
||||
//$payment_accounts[$this->Account->nameToID('Bank')] = 'Bank';
|
||||
$default_accounts = array_diff_key($payment_accounts,
|
||||
array($this->Account->concessionAccountID() => 1));
|
||||
$this->set(compact('payment_accounts', 'default_accounts'));
|
||||
|
||||
$this->Account->recursive = -1;
|
||||
$account = $this->Account->read(null, $id);
|
||||
$account = $account['Account'];
|
||||
|
||||
$accounts = $this->Account->collectableAccounts();
|
||||
$payment_accounts = $accounts['all'];
|
||||
$default_accounts = $accounts['default'];
|
||||
$this->set(compact('payment_accounts', 'default_accounts'));
|
||||
|
||||
$title = ($account['name'] . ': Collected Report');
|
||||
$this->set(compact('account', 'title'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: deposit
|
||||
* - Prepares the books for a bank deposit
|
||||
*/
|
||||
function deposit() {
|
||||
if ($this->data) {
|
||||
// Action the close based on provided data
|
||||
//pr($this->data);
|
||||
|
||||
// Get data about each closed ledger.
|
||||
$deposit = array('total' => 0, 'ledgers' => array());
|
||||
foreach ($this->data['Tillable']['Ledger'] AS $ledger_id => $ledger) {
|
||||
if (!$ledger['checked'])
|
||||
continue;
|
||||
|
||||
$ledger_entries =
|
||||
$this->Account->Ledger->find
|
||||
('all',
|
||||
array('link' => array
|
||||
('Account' =>
|
||||
array('fields' => array('name')),
|
||||
|
||||
'LedgerEntry' =>
|
||||
array('fields' => array('id', 'amount'),
|
||||
|
||||
'MonetarySource' =>
|
||||
array('fields' => array('name')),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('name')),
|
||||
|
||||
//'Transaction' =>
|
||||
//array('fields' => array('stamp')),
|
||||
),
|
||||
),
|
||||
'fields' => false,
|
||||
'conditions' => array(array('Ledger.id' => $ledger_id),
|
||||
array('LedgerEntry.id IS NOT NULL'),
|
||||
),
|
||||
));
|
||||
|
||||
$deposit['total'] += $ledger['amount'];
|
||||
$deposit['ledgers'][] = array('id' => $ledger_id,
|
||||
'name' => $ledger['account_name'],
|
||||
'total' => $ledger['amount'],
|
||||
'entries' => $ledger_entries);
|
||||
}
|
||||
|
||||
// Perform the accounting work necessary to close the
|
||||
// monetary ledgers and deposit into the bank account.
|
||||
$this->Account->closeAndDeposit($deposit['ledgers'], $this->data['Deposit']['Account']['id']);
|
||||
|
||||
$title = 'Account: Deposit Slip';
|
||||
$this->set(compact('title', 'deposit'));
|
||||
$this->render('deposit_slip');
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare a close page...
|
||||
$tillable_account = $this->Account->relatedAccounts('tillable');
|
||||
$depositable_account = $this->Account->relatedAccounts('depositable');
|
||||
|
||||
foreach ($tillable_account AS &$acct) {
|
||||
$acct['Account']['stats'] = $this->Account->stats($acct['Account']['id']);
|
||||
}
|
||||
|
||||
$title = 'Account: Prepare Deposit';
|
||||
$this->set(compact('title', 'tillable_account', 'depositable_account'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -248,46 +168,39 @@ class AccountsController extends AppController {
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
// Get details about the account and its ledgers (no ledger entries yet)
|
||||
$account = $this->Account->find
|
||||
('first',
|
||||
array('contain' =>
|
||||
array(// Models
|
||||
'CurrentLedger' =>
|
||||
array('fields' => array('id', 'sequence')),
|
||||
array('fields' => array('id', 'sequence', 'name')),
|
||||
|
||||
'Ledger' =>
|
||||
array('Close' => array
|
||||
('order' => array('Close.stamp' => 'DESC'))),
|
||||
array('CloseTransaction' => array
|
||||
('order' => array('CloseTransaction.stamp' => 'DESC'))),
|
||||
),
|
||||
'conditions' => array(array('Account.id' => $id),
|
||||
array('Account.level >=' =>
|
||||
$this->Permission->level('controller.accounts')),
|
||||
),
|
||||
'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']));
|
||||
if (empty($account)) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
// 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));
|
||||
$this->addSideMenuLink('New Ledger',
|
||||
array('action' => 'newledger', $id), null,
|
||||
'ACTION', $this->admin_area);
|
||||
$this->addSideMenuLink('Collected',
|
||||
array('action' => 'collected', $id), null,
|
||||
'ACTION', $this->admin_area);
|
||||
|
||||
// Prepare to render
|
||||
$title = 'Account: ' . $account['Account']['name'];
|
||||
|
||||
@@ -2,18 +2,6 @@
|
||||
|
||||
class ContactsController 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);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -23,32 +11,47 @@ 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);
|
||||
if ($index === 'Contact.last_name') {
|
||||
$order[] = 'Contact.first_name ' . $direction;
|
||||
function gridDataFilterTablesConfig(&$params, &$model, $table) {
|
||||
$config = parent::gridDataFilterTablesConfig($params, $model, $table);
|
||||
|
||||
// Special case for Customer; We need the Contact/Customer relationship
|
||||
if ($table == 'Customer')
|
||||
$config = array('fields' => array('ContactsCustomer.type',
|
||||
'ContactsCustomer.active'),
|
||||
'conditions' => array('ContactsCustomer.active' => true),
|
||||
);
|
||||
|
||||
return $config;
|
||||
}
|
||||
if ($index === 'Contact.first_name') {
|
||||
|
||||
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[] = 'Contact.last_name ' . $direction;
|
||||
}
|
||||
$order[] = 'Contact.first_name ' . $direction;
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Contact'] = array('id');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Contact'] = array('display_name');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,13 +81,9 @@ class ContactsController extends AppController {
|
||||
));
|
||||
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Edit',
|
||||
'url' => array('action' => 'edit',
|
||||
$id));
|
||||
$this->addSideMenuLink('Edit',
|
||||
array('action' => 'edit', $id), null,
|
||||
'ACTION');
|
||||
|
||||
// Prepare to render.
|
||||
$title = 'Contact: ' . $contact['Contact']['display_name'];
|
||||
@@ -126,8 +125,6 @@ class ContactsController extends AppController {
|
||||
|
||||
// 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) {
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
<?php
|
||||
|
||||
class CustomersController extends AppController {
|
||||
var $sidemenu_links =
|
||||
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');
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
* override: addGridViewSideMenuLinks
|
||||
* - Adds grid view navigation side menu links
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
|
||||
function addGridViewSideMenuLinks() {
|
||||
parent::addGridViewSideMenuLinks();
|
||||
|
||||
$this->addSideMenuLink('Current',
|
||||
array('controller' => 'customers', 'action' => 'current'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('Past',
|
||||
array('controller' => 'customers', 'action' => 'past'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('All',
|
||||
array('controller' => 'customers', 'action' => 'all'), null,
|
||||
'CONTROLLER');
|
||||
|
||||
/* $this->addSideMenuLink('New Customer', */
|
||||
/* array('controller' => 'customers', 'action' => 'add'), null, */
|
||||
/* 'CONTROLLER', $this->new_area); */
|
||||
}
|
||||
|
||||
|
||||
@@ -31,145 +37,86 @@ class CustomersController extends AppController {
|
||||
*/
|
||||
|
||||
function index() { $this->current(); }
|
||||
function current() { $this->jqGridView('Current Tenants', 'current'); }
|
||||
function past() { $this->jqGridView('Past Tenants'); }
|
||||
function all() { $this->jqGridView('All Customers'); }
|
||||
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 jqGridDataCountTables(&$params, &$model) {
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
return array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'PrimaryContact',
|
||||
'CurrentLease' => array('fields' => array()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function jqGridDataTables(&$params, &$model) {
|
||||
$link = $this->jqGridDataCountTables($params, $model);
|
||||
$link['link']['LedgerEntry'] = array('fields' => array());
|
||||
$link['link']['LedgerEntry']['Ledger'] = array('fields' => array());
|
||||
$link['link']['LedgerEntry']['Ledger']['Account'] = array('fields' => array());
|
||||
// INNER JOIN would be great, as it would ensure we're only looking
|
||||
// at the ledger entries that we truly want. However, this also
|
||||
// removes from the query any units that do not yet have a ledger
|
||||
// entry in A/R. A solution would be to INNER JOIN these tables,
|
||||
// and LEFT JOIN it to the rest. Grouping of JOINs, however, is
|
||||
// implemented with the 'joins' tag, and is not available through
|
||||
// the Linkable behavior interface.
|
||||
//$link['link']['LedgerEntry']['Ledger']['Account']['type'] = 'INNER';
|
||||
$link['link']['LedgerEntry']['Ledger']['Account']['conditions']
|
||||
= array('Account.id' =>
|
||||
$this->Customer->LedgerEntry->Ledger->Account->accountReceivableAccountID());
|
||||
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 jqGridDataFields(&$params, &$model) {
|
||||
$db = &$model->getDataSource();
|
||||
$fields = $db->fields($model, $model->alias);
|
||||
$fields[] = ('COUNT(DISTINCT CurrentLease.id) AS lease_count');
|
||||
$fields[] = ("SUM(IF(Account.id IS NULL, 0," .
|
||||
" IF(LedgerEntry.debit_ledger_id = Account.id," .
|
||||
" 1, -1))" .
|
||||
" * IF(LedgerEntry.amount IS NULL, 0, LedgerEntry.amount))" .
|
||||
" AS 'balance'");
|
||||
return $fields;
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
return array_merge($fields,
|
||||
$this->Customer->StatementEntry->chargeDisbursementFields(true));
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
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 = array();
|
||||
$order[] = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
function gridDataFilterTablesConfig(&$params, &$model, $table) {
|
||||
$config = parent::gridDataFilterTablesConfig($params, $model, $table);
|
||||
|
||||
if ($index !== 'PrimaryContact.last_name')
|
||||
$order[] = parent::jqGridDataOrder($params, $model,
|
||||
'PrimaryContact.last_name', $direction);
|
||||
if ($index !== 'PrimaryContact.first_name')
|
||||
$order[] = parent::jqGridDataOrder($params, $model,
|
||||
'PrimaryContact.first_name', $direction);
|
||||
if ($index !== 'Customer.id')
|
||||
$order[] = parent::jqGridDataOrder($params, $model,
|
||||
'Customer.id', $direction);
|
||||
// Special case for Contact; We need the Contact/Customer relationship
|
||||
if ($table == 'Contact')
|
||||
$config = array('fields' => array('ContactsCustomer.type',
|
||||
'ContactsCustomer.active'),
|
||||
'conditions' => array('ContactsCustomer.active' => true),
|
||||
);
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
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[] = 'PrimaryContact.last_name ' . $direction;
|
||||
$order[] = 'PrimaryContact.first_name ' . $direction;
|
||||
$order[] = '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);
|
||||
}
|
||||
|
||||
|
||||
@@ -180,14 +127,18 @@ class CustomersController extends AppController {
|
||||
* - Sets up the move-in page for the given customer.
|
||||
*/
|
||||
|
||||
function move_in($id = null) {
|
||||
function move_in($id = null, $unit_id = null) {
|
||||
$customer = array();
|
||||
$unit = array();
|
||||
|
||||
if (isset($id)) {
|
||||
if (!empty($id)) {
|
||||
$this->Customer->recursive = -1;
|
||||
$customer = current($this->Customer->read(null, $id));
|
||||
}
|
||||
if (!empty($unit_id)) {
|
||||
$this->Customer->Lease->Unit->recursive = -1;
|
||||
$unit = current($this->Customer->Lease->Unit->read(null, $unit_id));
|
||||
}
|
||||
$this->set(compact('customer', 'unit'));
|
||||
|
||||
$title = 'Customer Move-In';
|
||||
@@ -247,49 +198,113 @@ 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);
|
||||
|
||||
// Determine how long this customer has been with us.
|
||||
$leaseinfo = $this->Customer->find
|
||||
('first', array
|
||||
('link' => array('Lease' => array('fields' => array())),
|
||||
'fields' => array('MIN(Lease.movein_date) AS since',
|
||||
'IF(Customer.current_lease_count = 0, MAX(Lease.moveout_date), NULL) AS until'),
|
||||
'conditions' => array('Customer.id' => $id),
|
||||
'group' => 'Customer.id',
|
||||
));
|
||||
$this->set($leaseinfo[0]);
|
||||
|
||||
// Figure out the outstanding balances for this customer
|
||||
//$this->set('stats', $this->Customer->stats($id));
|
||||
$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;
|
||||
$show_moveout = false; $moveout_lease_id = null;
|
||||
$show_payment = false; $payment_lease_id = null;
|
||||
foreach ($customer['Lease'] AS $lease) {
|
||||
if (!isset($lease['close_date']))
|
||||
if (!isset($lease['close_date'])) {
|
||||
if ($show_payment)
|
||||
$payment_lease_id = null;
|
||||
else
|
||||
$payment_lease_id = $lease['id'];
|
||||
$show_payment = true;
|
||||
if (!isset($lease['moveout_date']))
|
||||
}
|
||||
if (!isset($lease['moveout_date'])) {
|
||||
if ($show_moveout)
|
||||
$moveout_lease_id = null;
|
||||
else
|
||||
$moveout_lease_id = $lease['id'];
|
||||
$show_moveout = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Edit',
|
||||
'url' => array('action' => 'edit',
|
||||
$id));
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
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 || $outstanding_balance > 0)
|
||||
$this->addSideMenuLink('New Receipt',
|
||||
array('action' => 'receipt', $id), null,
|
||||
'ACTION');
|
||||
|
||||
if ($show_payment) {
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Payment',
|
||||
'url' => array('action' => 'receipt',
|
||||
$id));
|
||||
/* $ids = $this->Customer->leaseIds($id, true); */
|
||||
/* if (count($ids) == 1) */
|
||||
/* $lease_id = $ids[0]; */
|
||||
/* else */
|
||||
/* $lease_id = null; */
|
||||
|
||||
$this->addSideMenuLink('New Invoice',
|
||||
array('controller' => 'leases',
|
||||
'action' => 'invoice',
|
||||
$payment_lease_id), null,
|
||||
'ACTION');
|
||||
}
|
||||
|
||||
$this->addSideMenuLink('Move-In',
|
||||
array('action' => 'move_in', $id), null,
|
||||
'ACTION');
|
||||
|
||||
if ($show_moveout) {
|
||||
$this->addSideMenuLink('Move-Out',
|
||||
array('controller' => 'leases',
|
||||
'action' => 'move_out',
|
||||
$moveout_lease_id), null,
|
||||
'ACTION');
|
||||
}
|
||||
|
||||
if (!$show_moveout && $outstanding_balance > 0)
|
||||
$this->addSideMenuLink('Write-Off',
|
||||
array('action' => 'bad_debt', $id), null,
|
||||
'ACTION');
|
||||
|
||||
if ($outstanding_balance < 0)
|
||||
$this->addSideMenuLink('Issue Refund',
|
||||
array('action' => 'refund', $id), null,
|
||||
'ACTION');
|
||||
|
||||
$this->addSideMenuLink('Edit',
|
||||
array('action' => 'edit', $id), null,
|
||||
'ACTION');
|
||||
|
||||
// Prepare to render.
|
||||
$title = 'Customer: ' . $customer['Customer']['name'];
|
||||
$this->set(compact('customer', 'title',
|
||||
@@ -311,23 +326,20 @@ class CustomersController extends AppController {
|
||||
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;
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -344,21 +356,56 @@ class CustomersController extends AppController {
|
||||
pr("CUSTOMER SAVE FAILED");
|
||||
}
|
||||
|
||||
// If existing customer, then view it. Otherwise, since
|
||||
// this is a new customer, go to the move in screen.
|
||||
// If existing customer, then view it.
|
||||
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;
|
||||
// Since this is a new customer, go to the move in screen.
|
||||
// First set the move-in unit id, if there is one, ...
|
||||
if (empty($this->data['movein']['Unit']['id']))
|
||||
$unit_id = null;
|
||||
else
|
||||
$unit_id = $this->data['movein']['Unit']['id'];
|
||||
|
||||
// ... then redirect
|
||||
$this->redirect(array('action'=>'move_in',
|
||||
$this->Customer->id,
|
||||
$unit_id,
|
||||
));
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
$this->data = $this->Customer->details($id);
|
||||
// REVISIT <AP>: 20090816
|
||||
// This should never need to be done by a controller.
|
||||
// However, until things stabilize, this gives the
|
||||
// user a way to update any cached items on the
|
||||
// customer, by just clicking Edit then Cancel.
|
||||
$this->Customer->update($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'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array('Customer.id' => $id),
|
||||
));
|
||||
|
||||
$this->data = $customer;
|
||||
$title = 'Customer: ' . $this->data['Customer']['name'] . " : Edit";
|
||||
}
|
||||
else {
|
||||
@@ -387,7 +434,8 @@ class CustomersController extends AppController {
|
||||
* - Add a new customer
|
||||
*/
|
||||
|
||||
function add() {
|
||||
function add($unit_id = null) {
|
||||
$this->set('movein', array('Unit' => array('id' => $unit_id)));
|
||||
$this->edit();
|
||||
}
|
||||
|
||||
@@ -403,21 +451,18 @@ class CustomersController extends AppController {
|
||||
$this->Customer->recursive = -1;
|
||||
$customer = $this->Customer->read(null, $id);
|
||||
$customer = $customer['Customer'];
|
||||
$unreconciled = $this->Customer->findUnreconciledLedgerEntries($id);
|
||||
$charges = $unreconciled['debit'];
|
||||
}
|
||||
else {
|
||||
$customer = null;
|
||||
$charges = array('balance' => 0, 'entry' => array());
|
||||
}
|
||||
|
||||
$A = new Account();
|
||||
$payment_accounts = $A->paymentAccounts();
|
||||
$default_account = $A->cashAccountID();
|
||||
$this->set(compact('payment_accounts', 'default_account'));
|
||||
$TT = new TenderType();
|
||||
$payment_types = $TT->paymentTypes();
|
||||
$default_type = $TT->defaultPaymentType();
|
||||
$this->set(compact('payment_types', 'default_type'));
|
||||
|
||||
$title = ($customer['name'] . ': Payment Entry');
|
||||
$this->set(compact('customer', 'charges', 'title'));
|
||||
$title = ($customer['name'] . ': Receipt Entry');
|
||||
$this->set(compact('customer', 'title'));
|
||||
}
|
||||
|
||||
|
||||
@@ -428,58 +473,64 @@ class CustomersController extends AppController {
|
||||
* - Refunds customer charges
|
||||
*/
|
||||
|
||||
function refund() {
|
||||
$entries = $this->Customer->LedgerEntry->find
|
||||
('all', array
|
||||
function refund($id) {
|
||||
$customer = $this->Customer->find
|
||||
('first', array
|
||||
('contain' => false,
|
||||
'conditions' => array('LedgerEntry.id' =>
|
||||
//array(199,200,201)
|
||||
61
|
||||
'conditions' => array(array('Customer.id' => $id),
|
||||
),
|
||||
));
|
||||
pr(compact('entries'));
|
||||
if (empty($customer)) {
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
$this->Customer->LedgerEntry->reverse($entries);
|
||||
// Determine the customer balance, bailing if the customer owes money
|
||||
$balance = $this->Customer->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->Customer->StatementEntry->Account->refundAccounts();
|
||||
$defaultAccount = current($refundAccounts);
|
||||
$this->set(compact('refundAccounts', 'defaultAccount'));
|
||||
|
||||
// Prepare to render
|
||||
$title = ($customer['Customer']['name'] . ': Refund');
|
||||
$this->set(compact('title', 'customer', 'balance'));
|
||||
$this->render('/transactions/refund');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: unreconciledEntries
|
||||
* - returns the list of unreconciled entries
|
||||
* action: bad_debt
|
||||
* - Sets up the write-off entry page, so that the
|
||||
* user can write off remaining charges of a customer.
|
||||
*/
|
||||
|
||||
function unreconciled($id) {
|
||||
function bad_debt($id) {
|
||||
$this->Customer->id = $id;
|
||||
$customer = $this->Customer->find
|
||||
('first', array
|
||||
('contain' => false,
|
||||
));
|
||||
|
||||
//$this->layout = 'ajax';
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
Configure::write('debug', '0');
|
||||
header("Content-type: text/xml;charset=utf-8");
|
||||
// Make sure we have a valid customer to write off
|
||||
if (empty($customer))
|
||||
$this->redirect(array('action' => 'index'));
|
||||
|
||||
App::import('Helper', 'Xml');
|
||||
$xml = new XmlHelper();
|
||||
// Get the customer balance
|
||||
$balance = $this->Customer->balance($id);
|
||||
|
||||
// Find the unreconciled entries, then manipulate the structure
|
||||
// slightly to accomodate the format necessary for XML Helper.
|
||||
$unreconciled = $this->Customer->findUnreconciledLedgerEntries($id);
|
||||
$unreconciled = array('entries' =>
|
||||
array_intersect_key($unreconciled['debit'],
|
||||
array('entry'=>1, 'balance'=>1)));
|
||||
|
||||
// XML Helper will dump an empty tag if the array is empty
|
||||
if (!count($unreconciled['entries']['entry']))
|
||||
unset($unreconciled['entries']['entry']);
|
||||
|
||||
pr($unreconciled);
|
||||
//$reconciled = $cust->reconcileNewLedgerEntry($cust_id, 'credit', $amount);
|
||||
|
||||
$opts = array();
|
||||
//$opts['format'] = 'tags';
|
||||
echo $xml->header();
|
||||
echo $xml->serialize($unreconciled, $opts);
|
||||
// Prepare to render
|
||||
$title = ($customer['Customer']['name'] . ': Write Off Bad Debt');
|
||||
$this->set(compact('title', 'customer', 'balance'));
|
||||
$this->render('/transactions/bad_debt');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
59
site/controllers/double_entries_controller.php
Normal file
59
site/controllers/double_entries_controller.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
class DoubleEntriesController extends AppController {
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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),
|
||||
));
|
||||
|
||||
$entry += $this->DoubleEntry->DebitEntry->Transaction->find
|
||||
('first',
|
||||
array('contain' => false,
|
||||
'conditions' => array('id' => $entry['DebitEntry']['transaction_id']),
|
||||
));
|
||||
|
||||
$entry += $this->DoubleEntry->DebitEntry->find
|
||||
('first',
|
||||
array('contain' => array('Ledger' => array('Account')),
|
||||
'conditions' => array('DebitEntry.id' => $entry['DebitEntry']['id']),
|
||||
));
|
||||
$entry['Ledger']['link'] =
|
||||
$entry['Ledger']['Account']['level'] >=
|
||||
$this->Permission->level('controller.accounts');
|
||||
$entry['DebitLedger'] = $entry['Ledger'];
|
||||
unset($entry['Ledger']);
|
||||
|
||||
$entry += $this->DoubleEntry->CreditEntry->find
|
||||
('first',
|
||||
array('contain' => array('Ledger' => array('Account')),
|
||||
'conditions' => array('CreditEntry.id' => $entry['CreditEntry']['id']),
|
||||
));
|
||||
$entry['Ledger']['link'] =
|
||||
$entry['Ledger']['Account']['level'] >=
|
||||
$this->Permission->level('controller.accounts');
|
||||
$entry['CreditLedger'] = $entry['Ledger'];
|
||||
unset($entry['Ledger']);
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Double Ledger Entry #{$entry['DoubleEntry']['id']}";
|
||||
$this->set(compact('entry', 'title'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,22 +2,29 @@
|
||||
|
||||
class LeasesController extends AppController {
|
||||
|
||||
var $sidemenu_links =
|
||||
array(array('name' => 'Leases', 'header' => true),
|
||||
array('name' => 'Active', 'url' => array('controller' => 'leases', 'action' => 'active')),
|
||||
array('name' => 'Closed', 'url' => array('controller' => 'leases', 'action' => 'closed')),
|
||||
array('name' => 'All', 'url' => array('controller' => 'leases', 'action' => 'all')),
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
* override: addGridViewSideMenuLinks
|
||||
* - Adds grid view navigation side menu links
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
|
||||
function addGridViewSideMenuLinks() {
|
||||
parent::addGridViewSideMenuLinks();
|
||||
|
||||
$this->addSideMenuLink('Active',
|
||||
array('controller' => 'leases', 'action' => 'active'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('Closed',
|
||||
array('controller' => 'leases', 'action' => 'closed'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('Delinquent',
|
||||
array('controller' => 'leases', 'action' => 'delinquent'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('All',
|
||||
array('controller' => 'leases', 'action' => 'all'), null,
|
||||
'CONTROLLER');
|
||||
}
|
||||
|
||||
|
||||
@@ -28,77 +35,68 @@ class LeasesController extends AppController {
|
||||
* - Generate a listing of leases
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function active() { $this->jqGridView('Active Leases'); }
|
||||
function closed() { $this->jqGridView('Closed Leases'); }
|
||||
function all() { $this->jqGridView('All Leases', 'all'); }
|
||||
function index() { $this->active(); }
|
||||
function active() { $this->gridView('Active Leases', 'active'); }
|
||||
function delinquent() { $this->gridView('Delinquent Leases'); }
|
||||
function closed() { $this->gridView('Closed Leases'); }
|
||||
function all() { $this->gridView('All Leases'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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) {
|
||||
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 jqGridDataTables(&$params, &$model) {
|
||||
$link = $this->jqGridDataCountTables($params, $model);
|
||||
$link['link']['LedgerEntry'] = array('fields' => array());
|
||||
$link['link']['LedgerEntry']['Ledger'] = array('fields' => array());
|
||||
$link['link']['LedgerEntry']['Ledger']['Account'] = array('fields' => array());
|
||||
// INNER JOIN would be great, as it would ensure we're only looking
|
||||
// at the ledger entries that we truly want. However, this also
|
||||
// removes from the query any leases that do not yet have a ledger
|
||||
// entry in A/R. A solution would be to INNER JOIN these tables,
|
||||
// and LEFT JOIN it to the rest. Grouping of JOINs, however, is
|
||||
// implemented with the 'joins' tag, and is not available through
|
||||
// the Linkable behavior interface.
|
||||
//$link['link']['LedgerEntry']['Ledger']['Account']['type'] = 'INNER';
|
||||
$link['link']['LedgerEntry']['Ledger']['Account']['conditions']
|
||||
= array('Account.id' =>
|
||||
$this->Lease->LedgerEntry->Ledger->Account->accountReceivableAccountID());
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$link = $this->gridDataCountTables($params, $model);
|
||||
$link['link']['StatementEntry'] = array('fields' => array());
|
||||
return $link;
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
$db = &$model->getDataSource();
|
||||
$fields = $db->fields($model, $model->alias);
|
||||
$fields[] = ("SUM(IF(Account.id IS NULL, 0," .
|
||||
" IF(LedgerEntry.debit_ledger_id = Account.id," .
|
||||
" 1, -1))" .
|
||||
" * IF(LedgerEntry.amount IS NULL, 0, LedgerEntry.amount))" .
|
||||
" AS 'balance'");
|
||||
return $fields;
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
$fields[] = ("IF(" . $this->Lease->conditionDelinquent() . "," .
|
||||
" 'DELINQUENT', 'CURRENT') AS 'status'");
|
||||
return array_merge($fields,
|
||||
$this->Lease->StatementEntry->chargeDisbursementFields(true));
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'active') {
|
||||
$conditions[] = 'Lease.close_date IS NULL';
|
||||
}
|
||||
elseif ($params['action'] === 'delinquent') {
|
||||
$conditions[] = $this->Lease->conditionDelinquent();
|
||||
}
|
||||
elseif ($params['action'] === 'closed') {
|
||||
$conditions[] = 'Lease.close_date IS NOT NULL';
|
||||
}
|
||||
|
||||
if (isset($customer_id))
|
||||
$conditions[] = array('Lease.customer_id' => $customer_id);
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
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')
|
||||
@@ -109,31 +107,31 @@ class LeasesController extends AppController {
|
||||
$index = 'Unit.sort_order';
|
||||
|
||||
$order = array();
|
||||
$order[] = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
$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::jqGridDataOrder($params, $model,
|
||||
$order[] = parent::gridDataOrder($params, $model,
|
||||
'Lease.id', $direction);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
/* function jqGridRecordsPostProcess(&$params, &$model, &$records) { */
|
||||
/* function gridDataPostProcess(&$params, &$model, &$records) { */
|
||||
/* foreach ($records AS &$record) { */
|
||||
/* $record['Lease']['through_date'] */
|
||||
/* = $this->Lease->rentChargeThrough($record['Lease']['id']); */
|
||||
/* } */
|
||||
|
||||
/* parent::jqGridRecordsPostProcess($params, $model, $records); */
|
||||
/* parent::gridDataPostProcess($params, $model, $records); */
|
||||
/* } */
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -150,21 +148,22 @@ class LeasesController extends AppController {
|
||||
|
||||
// Handle the move in based on the data given
|
||||
//pr(array('Move-in data', $this->data));
|
||||
foreach (array('deposit', 'rent') AS $currency) {
|
||||
$this->data['Lease'][$currency]
|
||||
= str_replace('$', '', $this->data['Lease'][$currency]);
|
||||
}
|
||||
|
||||
$lid = $this->Lease->moveIn($this->data['Lease']['customer_id'],
|
||||
$this->data['Lease']['unit_id'],
|
||||
null, null,
|
||||
$this->data['Lease']['deposit'],
|
||||
$this->data['Lease']['rent'],
|
||||
$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');
|
||||
$this->redirect(array('action'=>'invoice', $lid, 'move-in'));
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
@@ -181,19 +180,15 @@ class LeasesController extends AppController {
|
||||
|
||||
$this->Lease->moveOut($this->data['Lease']['id'],
|
||||
'VACANT',
|
||||
$this->data['Lease']['moveout_date'],
|
||||
//true // Close this lease, if able
|
||||
false
|
||||
$this->data['Lease']['moveout_date']
|
||||
);
|
||||
|
||||
$this->redirect($this->data['redirect']);
|
||||
$this->autoRender = false;
|
||||
return;
|
||||
$this->redirect(array('controller' => 'leases',
|
||||
'action' => 'view',
|
||||
$this->data['Lease']['id']));
|
||||
}
|
||||
|
||||
if (!isset($id))
|
||||
die("Oh Nooooo!!");
|
||||
|
||||
if (isset($id)) {
|
||||
$lease = $this->Lease->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
@@ -216,70 +211,80 @@ class LeasesController extends AppController {
|
||||
$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'));
|
||||
$lease['Customer']['name'] . ': Move-Out');
|
||||
}
|
||||
else {
|
||||
$title = 'Move-Out';
|
||||
}
|
||||
|
||||
$this->set(compact('title'));
|
||||
$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); */
|
||||
/* $this->redirect(array('controller' => 'leases', */
|
||||
/* 'action' => 'view', */
|
||||
/* $id)); */
|
||||
/* } */
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: apply_deposit
|
||||
* - Applies the security deposit to charges. This is much
|
||||
* like a receipt, but it's separated to keep it simple and
|
||||
* to prevent feature overload on the receipt page.
|
||||
* action: refund
|
||||
* - Provides lease customer with a refund
|
||||
*/
|
||||
|
||||
function apply_deposit($id) {
|
||||
$A = new Account();
|
||||
|
||||
function refund($id) {
|
||||
$lease = $this->Lease->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
'Unit' => array('fields' => array('id', 'name')),
|
||||
'Customer' => array('fields' => array('id', 'name')),
|
||||
),
|
||||
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
// Make sure lease is not closed...
|
||||
array('Lease.close_date' => null),
|
||||
),
|
||||
));
|
||||
if (empty($lease)) {
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
// Determine the lease balance, bailing if the customer owes money
|
||||
$balance = $this->Lease->balance($id);
|
||||
if ($balance >= 0) {
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
// Get the lease balance, part of lease stats
|
||||
$this->Lease->statsMerge($lease['Lease'],
|
||||
array('stats' => $this->Lease->stats($id)));
|
||||
// The refund will be for a positive amount
|
||||
$balance *= -1;
|
||||
|
||||
// Determine the lease security deposit
|
||||
$deposit = $this->Lease->findSecurityDeposits($lease['Lease']['id']);
|
||||
$this->set(compact('deposit'));
|
||||
$this->set('customer', $lease['Customer']);
|
||||
$this->set('unit', $lease['Unit']);
|
||||
$this->set('lease', $lease['Lease']);
|
||||
$this->set('account', array('id' => $A->securityDepositAccountID()));
|
||||
|
||||
/* $redirect = array('controller' => 'leases', */
|
||||
/* 'action' => 'view', */
|
||||
/* $id); */
|
||||
// Get the accounts capable of paying the refund
|
||||
$refundAccounts = $this->Lease->StatementEntry->Account->refundAccounts();
|
||||
$defaultAccount = current($refundAccounts);
|
||||
$this->set(compact('refundAccounts', 'defaultAccount'));
|
||||
|
||||
// Prepare to render
|
||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||
$lease['Unit']['name'] . ': ' .
|
||||
$lease['Customer']['name'] . ': Utilize Security Deposit');
|
||||
$this->set(compact('title', 'redirect'));
|
||||
$lease['Customer']['name'] . ': Refund');
|
||||
$this->set(compact('title', 'lease', 'balance'));
|
||||
$this->render('/transactions/refund');
|
||||
}
|
||||
|
||||
|
||||
@@ -287,70 +292,34 @@ class LeasesController extends AppController {
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: bad_debt
|
||||
* - Writes off remaining charges on a lease.
|
||||
* REVISIT <AP>: 20090710
|
||||
* Should this be a customer function? What customer
|
||||
* would have only one lease that results in bad debt.
|
||||
* - Sets up the write-off entry page, so that the
|
||||
* user can write off remaining charges on a lease.
|
||||
*/
|
||||
|
||||
function bad_debt($id) {
|
||||
$A = new Account();
|
||||
|
||||
$this->Lease->id = $id;
|
||||
$lease = $this->Lease->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
array('Lease.close_date' => null),
|
||||
'Unit' => array('fields' => array('id', 'name')),
|
||||
'Customer' => array('fields' => array('id', 'name')),
|
||||
),
|
||||
));
|
||||
|
||||
// Make sure we have a valid lease to write off
|
||||
if (empty($lease))
|
||||
$this->redirect(array('action' => 'view', $id));
|
||||
|
||||
// Get the lease balance, part of lease stats
|
||||
$this->Lease->statsMerge($lease['Lease'],
|
||||
array('stats' => $this->Lease->stats($id)));
|
||||
|
||||
// Determine the lease security deposit
|
||||
$deposit = $this->Lease->findSecurityDeposits($lease['Lease']['id']);
|
||||
if ($deposit['summary']['balance'] > 0)
|
||||
die("Still have un-utilized security deposit");
|
||||
|
||||
$this->set('customer', $lease['Customer']);
|
||||
$this->set('unit', $lease['Unit']);
|
||||
$this->set('lease', $lease['Lease']);
|
||||
$this->set('account', array('id' => $A->badDebtAccountID()));
|
||||
|
||||
/* $redirect = array('controller' => 'leases', */
|
||||
/* 'action' => 'view', */
|
||||
/* $id); */
|
||||
// Get the lease balance
|
||||
$balance = $this->Lease->balance($id);
|
||||
|
||||
// Prepare to render
|
||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||
$lease['Unit']['name'] . ': ' .
|
||||
$lease['Customer']['name'] . ': Write Off Bad Debt');
|
||||
$this->set(compact('title', 'redirect'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: refund
|
||||
* - Provides user with a refund
|
||||
* REVISIT <AP>: 20090710
|
||||
* Should this be a customer function?
|
||||
*/
|
||||
|
||||
function refund($id) {
|
||||
$this->set(compact('title', 'lease', 'balance'));
|
||||
$this->render('/transactions/bad_debt');
|
||||
}
|
||||
|
||||
|
||||
@@ -361,9 +330,23 @@ class LeasesController extends AppController {
|
||||
* - Closes a lease to any further action
|
||||
*/
|
||||
|
||||
// REVISIT <AP>: 20090809
|
||||
// While cleaning up the sitelink data, then delete reldep()
|
||||
function reldep($id) {
|
||||
$this->Lease->id = $id;
|
||||
$stamp = $this->Lease->field('moveout_date');
|
||||
$this->Lease->releaseSecurityDeposits($id, $stamp);
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
function close($id) {
|
||||
// REVISIT <AP>: 20090708
|
||||
// We should probably seek confirmation first...
|
||||
if (!$this->Lease->closeable($id)) {
|
||||
$this->INTERNAL_ERROR("This lease is not ready to close");
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
$this->Lease->close($id);
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
@@ -398,15 +381,36 @@ class LeasesController extends AppController {
|
||||
));
|
||||
|
||||
$A = new Account();
|
||||
$charge_accounts = $A->chargeAccounts();
|
||||
$charge_accounts = $A->invoiceAccounts();
|
||||
$default_account = $A->rentAccountID();
|
||||
$this->set(compact('charge_accounts', 'default_account'));
|
||||
$rent_account = $A->rentAccountID();
|
||||
$security_deposit_account = $A->securityDepositAccountID();
|
||||
$this->set(compact('charge_accounts', 'default_account',
|
||||
'rent_account', 'security_deposit_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'));
|
||||
$this->set(compact('default_late'));
|
||||
|
||||
if ($type === 'move-in') {
|
||||
// Make sure we have a valid lease that we're moving in
|
||||
if (empty($lease))
|
||||
$this->redirect(array('action' => 'index'));
|
||||
|
||||
$movein = array();
|
||||
$movein['time'] = strtotime($lease['Lease']['movein_date']);
|
||||
$movein['effective_time'] = strtotime($lease['Lease']['movein_date']);
|
||||
$movein_date = getdate($movein['effective_time']);
|
||||
$movein['through_time'] = mktime(0, 0, 0, $movein_date['mon'] + 1, 0, $movein_date['year']);
|
||||
$days_in_month = idate('d', $movein['through_time']);
|
||||
$movein['prorated_days'] = $days_in_month - $movein_date['mday'] + 1;
|
||||
$movein['prorated_rent'] = $lease['Lease']['rent'] * $movein['prorated_days'] / $days_in_month;
|
||||
$movein['prorated'] = $movein['prorated_days'] != $days_in_month;
|
||||
$movein['deposit'] = $lease['Lease']['deposit'];
|
||||
$this->set(compact('movein'));
|
||||
}
|
||||
|
||||
|
||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||
$lease['Unit']['name'] . ': ' .
|
||||
@@ -415,6 +419,27 @@ class LeasesController extends AppController {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: assess_rent/late
|
||||
* - Assesses the new monthly rent/late charge, if need be
|
||||
*/
|
||||
|
||||
function assess_rent($date = null) {
|
||||
$this->Lease->assessMonthlyRentAll($date);
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
function assess_late($date = null) {
|
||||
$this->Lease->assessMonthlyLateAll($date);
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
function assess_all($date = null) {
|
||||
$this->Lease->assessMonthlyRentAll($date);
|
||||
$this->Lease->assessMonthlyLateAll($date);
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -433,69 +458,65 @@ class LeasesController extends AppController {
|
||||
('first',
|
||||
array('contain' =>
|
||||
array(// Models
|
||||
'LeaseType',
|
||||
'Unit',
|
||||
'Customer',
|
||||
'LeaseType(id,name)',
|
||||
'Unit(id,name)',
|
||||
'Customer(id,name)',
|
||||
),
|
||||
'fields' => array('Lease.*', $this->Lease->delinquentField()),
|
||||
'conditions' => array(array('Lease.id' => $id)),
|
||||
)
|
||||
);
|
||||
$lease['Lease'] += $lease[0];
|
||||
unset($lease[0]);
|
||||
|
||||
$lease['Lease']['paid_through'] = $this->Lease->rentPaidThrough($id);
|
||||
|
||||
|
||||
$this->set('charge_gaps', $this->Lease->rentChargeGaps($id));
|
||||
$this->set('charge_through', $this->Lease->rentChargeThrough($id));
|
||||
|
||||
// Obtain the overall lease balance
|
||||
$this->Lease->statsMerge($lease['Lease'],
|
||||
array('stats' => $this->Lease->stats($id)));
|
||||
$outstanding_balance = $lease['Lease']['stats']['balance'];
|
||||
|
||||
// Determine the lease security deposit
|
||||
$deposits = $this->Lease->findSecurityDeposits($lease['Lease']['id']);
|
||||
$outstanding_deposit = $deposits['summary']['balance'];
|
||||
|
||||
// Set up dynamic menu items
|
||||
if (!isset($lease['Lease']['close_date'])) {
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
// 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. Normally, these will only be present
|
||||
// on an open lease, but it's possible for a lease to be closed, and
|
||||
// yet still have an outstanding balance. This can happen if someone
|
||||
// were to reverse charges, or if a payment should come back NSF.
|
||||
if (!isset($lease['Lease']['close_date']) || $outstanding_balance > 0) {
|
||||
if (!isset($lease['Lease']['moveout_date']))
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Move-Out', 'url' => array('action' => 'move_out',
|
||||
$id));
|
||||
$this->addSideMenuLink('Move-Out',
|
||||
array('action' => 'move_out', $id), null,
|
||||
'ACTION');
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Charges', 'url' => array('action' => 'invoice',
|
||||
$id));
|
||||
if (!isset($lease['Lease']['close_date']))
|
||||
$this->addSideMenuLink('New Invoice',
|
||||
array('action' => 'invoice', $id), null,
|
||||
'ACTION');
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Payments', 'url' => array('controller' => 'customers',
|
||||
$this->addSideMenuLink('New Receipt',
|
||||
array('controller' => 'customers',
|
||||
'action' => 'receipt',
|
||||
$lease['Customer']['id']));
|
||||
$lease['Customer']['id']), null,
|
||||
'ACTION');
|
||||
|
||||
if (isset($lease['Lease']['moveout_date']) && $outstanding_deposit > 0 && $outstanding_balance > 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Apply Deposit', 'url' => array('action' => 'apply_deposit',
|
||||
$id));
|
||||
// REVISIT <AP>:
|
||||
// Not allowing refund to be issued from the lease, as
|
||||
// in fact, we should never have a positive lease balance.
|
||||
// I'll flag this at the moment, since we might get one
|
||||
// when a charge is reimbursed; a bug that we'll either
|
||||
// need to fix, or we'll have to revisit this assumption.
|
||||
if ($outstanding_balance < 0)
|
||||
$this->INTERNAL_ERROR("Should not have a customer lease credit.");
|
||||
|
||||
if (isset($lease['Lease']['moveout_date']) &&
|
||||
$outstanding_balance <= 0 &&
|
||||
($outstanding_deposit - $outstanding_balance) > 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Issue Refund', 'url' => array('action' => 'refund',
|
||||
$id));
|
||||
/* if ($outstanding_balance < 0) */
|
||||
/* $this->addSideMenuLink('Issue Refund', */
|
||||
/* array('action' => 'refund', $id), null, */
|
||||
/* 'ACTION'); */
|
||||
|
||||
if (isset($lease['Lease']['moveout_date']) && $outstanding_deposit == 0 && $outstanding_balance > 0)
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Write-Off', 'url' => array('action' => 'bad_debt',
|
||||
$id));
|
||||
if (isset($lease['Lease']['moveout_date']) && $outstanding_balance > 0)
|
||||
$this->addSideMenuLink('Write-Off',
|
||||
array('action' => 'bad_debt', $id), null,
|
||||
'ACTION');
|
||||
|
||||
if ($this->Lease->closeable($id))
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Close', 'url' => array('action' => 'close',
|
||||
$id));
|
||||
$this->addSideMenuLink('Close',
|
||||
array('action' => 'close', $id), null,
|
||||
'ACTION');
|
||||
}
|
||||
|
||||
// Prepare to render
|
||||
|
||||
@@ -2,317 +2,95 @@
|
||||
|
||||
class LedgerEntriesController extends AppController {
|
||||
|
||||
var $sidemenu_links = array();
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
* action: index / current / past / all
|
||||
* - Creates a list of ledger entries
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
}
|
||||
|
||||
function index() { $this->gridView('All Ledger Entries'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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['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'),
|
||||
),
|
||||
|
||||
'MonetarySource' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Customer' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'Lease' =>
|
||||
array('fields' => array('id', 'number'),
|
||||
'Unit' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (isset($params['custom']['account_ftype'])) {
|
||||
$ftype = $params['custom']['account_ftype'];
|
||||
$ftype = ucfirst($ftype);
|
||||
//$ftype = $this->LedgerEntry->DebitLedger->Account->fundamentalOpposite($ftype);
|
||||
$link[$ftype . 'Ledger'] =
|
||||
'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'),
|
||||
),
|
||||
);
|
||||
}
|
||||
elseif ($params['action'] === 'collected') {
|
||||
// Income / Receipt / Money
|
||||
// debit: A/R credit: Income <-- this entry
|
||||
// debit: Receipt credit: A/R <-- ReceiptLedgerEntry, below
|
||||
// debit: Money credit: Receipt <-- MoneyLedgerEntry, below
|
||||
|
||||
$link['CreditLedger'] =
|
||||
array('fields' => 'sequence',
|
||||
'Account' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
array('fields' => array('id', 'name', 'type'),
|
||||
),
|
||||
),
|
||||
|
||||
'Tender' =>
|
||||
array('fields' => array('id', 'name', 'nsf_transaction_id'),
|
||||
),
|
||||
|
||||
/* 'DebitEntry', */
|
||||
/* 'CreditEntry', */
|
||||
);
|
||||
|
||||
// We're searching for the Receipt<->A/R entries,
|
||||
// which are debits on the A/R account. Find the
|
||||
// reconciling entries to that A/R debit.
|
||||
$link['DebitReconciliationLedgerEntry'] =
|
||||
array('alias' => 'ReceiptLedgerEntry',
|
||||
|
||||
'Transaction' =>
|
||||
array('alias' => 'ReceiptTransaction'),
|
||||
|
||||
// Credit Ledger should be A/R;
|
||||
// Debit Ledger should be Receipt
|
||||
'DebitLedger' =>
|
||||
array('alias' => 'ReceiptLedger',
|
||||
'Account' => array('alias' => 'ReceiptAccount'),
|
||||
),
|
||||
|
||||
// Finally, the Money (Cash/Check/etc) Entry is the one
|
||||
// which reconciles our ReceiptLedgerEntry debit
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
array('alias' => 'MoneyLedgerEntry',
|
||||
'linkalias' => 'MoneyLedgerEntryR',
|
||||
|
||||
// Credit Ledger should be Receipt;
|
||||
// Debit Ledger should be our Money Account
|
||||
'DebitLedger' =>
|
||||
array('alias' => 'MoneyLedger',
|
||||
'Account' =>
|
||||
array('alias' => 'MoneyAccount'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$link['DebitLedger'] =
|
||||
array('fields' => array('id', 'sequence'),
|
||||
'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);
|
||||
|
||||
$fields = $model->ledgerContextFields2($ledger_id, $account_id, $account_type);
|
||||
|
||||
if (count(array_intersect($params['fields'], array('applied'))) == 1)
|
||||
$fields[] = 'SUM(Reconciliation.amount) AS applied';
|
||||
|
||||
if ($params['action'] === 'collected')
|
||||
$fields[] = 'MAX(ReceiptTransaction.stamp) AS last_paid';
|
||||
|
||||
return $fields;
|
||||
function 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'] === 'collected') {
|
||||
extract($params['custom']);
|
||||
|
||||
if (isset($collected_account_id))
|
||||
$conditions[] = array('Account.id' => $params['custom']['collected_account_id']);
|
||||
else
|
||||
die("INTERNAL ERROR: COLLECTED ACCOUNT ID NOT SET");
|
||||
|
||||
if (!empty($collected_from_date))
|
||||
$conditions[]
|
||||
= array('ReceiptTransaction.stamp >=' =>
|
||||
$this->LedgerEntry->Transaction->dateFormatBeforeSave($collected_from_date));
|
||||
|
||||
if (!empty($collected_through_date))
|
||||
$conditions[]
|
||||
= array('ReceiptTransaction.stamp <=' =>
|
||||
$this->LedgerEntry->Transaction->dateFormatBeforeSave($collected_through_date . ' 23:59:59'));
|
||||
|
||||
if (isset($collected_payment_accounts))
|
||||
$conditions[] = array('MoneyAccount.id' => $collected_payment_accounts);
|
||||
else
|
||||
$conditions[] = array('NOT' => array(array('MoneyAccount.id' => null)));
|
||||
return $table;
|
||||
}
|
||||
|
||||
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']);
|
||||
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;
|
||||
}
|
||||
|
||||
if (isset($params['custom']['account_id'])) {
|
||||
$conditions[] =
|
||||
array('OR' =>
|
||||
array(array('CreditAccount.id' => $params['custom']['account_id']),
|
||||
array('DebitAccount.id' => $params['custom']['account_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);
|
||||
}
|
||||
|
||||
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']);
|
||||
}
|
||||
|
||||
if (isset($params['custom']['monetary_source_id'])) {
|
||||
$conditions[] =
|
||||
array('MonetarySource.id' => $params['custom']['monetary_source_id']);
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
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 jqGridDataGroup(&$params, &$model) {
|
||||
if (isset($params['custom']['group_by_tx']) && $params['custom']['group_by_tx'])
|
||||
return $model->alias.'.transaction_id';
|
||||
|
||||
return parent::jqGridDataGroup($params, $model);
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -321,28 +99,32 @@ class LedgerEntriesController extends AppController {
|
||||
return $order;
|
||||
}
|
||||
|
||||
function jqGridDataRecords(&$params, &$model, $query) {
|
||||
if ($params['action'] === 'collected') {
|
||||
$tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1));
|
||||
$tquery['fields'] = array('SUM(Reconciliation.amount) AS applied');
|
||||
$total = $model->find('first', $tquery);
|
||||
|
||||
$params['userdata']['total'] = $total[0]['applied'];
|
||||
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>';
|
||||
}
|
||||
}
|
||||
|
||||
return parent::jqGridDataRecords($params, $model, $query);
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['LedgerEntry'] = array('id');
|
||||
$links['Transaction'] = array('id');
|
||||
// REVISIT <AP>: 20090827
|
||||
// Need to take 'level' into account
|
||||
if ($this->Permission->allow('controller.accounts')) {
|
||||
$links['Ledger'] = array('id');
|
||||
$links['Account'] = array('name');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: reverse the ledger entry
|
||||
*/
|
||||
|
||||
function reverse($id) {
|
||||
$this->LedgerEntry->reverse($id);
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
$links['Tender'] = array('name');
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -354,114 +136,69 @@ class LedgerEntriesController extends AppController {
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
if (!$id) {
|
||||
$entry = $this->LedgerEntry->find
|
||||
('first',
|
||||
array('contain' => array
|
||||
(
|
||||
'Transaction' =>
|
||||
array('fields' => array('id', 'stamp'),
|
||||
),
|
||||
|
||||
'Ledger' =>
|
||||
array('fields' => array('id', 'sequence', 'name'),
|
||||
'Account' =>
|
||||
array('fields' => array('id', 'name', 'type'),
|
||||
),
|
||||
),
|
||||
|
||||
'Tender' =>
|
||||
array('fields' => array('id', 'name'),
|
||||
),
|
||||
|
||||
'DebitDoubleEntry' => array('id'),
|
||||
'CreditDoubleEntry' => array('id'),
|
||||
|
||||
'DebitEntry' => array('fields' => array('id', 'crdr')),
|
||||
'CreditEntry' => array('fields' => array('id', 'crdr')),
|
||||
),
|
||||
|
||||
'conditions' => array('LedgerEntry.id' => $id),
|
||||
));
|
||||
|
||||
if (empty($entry) || empty($entry['Ledger']['Account'])) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
|
||||
}
|
||||
|
||||
// Get the LedgerEntry and related fields
|
||||
$entry = $this->LedgerEntry->find
|
||||
('first',
|
||||
array('contain' => array('MonetarySource.id',
|
||||
'MonetarySource.name',
|
||||
'Transaction.id',
|
||||
'Transaction.stamp',
|
||||
'DebitLedger.id',
|
||||
'DebitLedger.sequence',
|
||||
'DebitLedger.account_id',
|
||||
'CreditLedger.id',
|
||||
'CreditLedger.sequence',
|
||||
'CreditLedger.account_id',
|
||||
'Customer.id',
|
||||
'Customer.name',
|
||||
'Lease.id',
|
||||
),
|
||||
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");
|
||||
|
||||
'fields' => array('LedgerEntry.*'),
|
||||
if (empty($entry['DebitEntry']))
|
||||
$entry['MatchingEntry'] = $entry['CreditEntry'][0];
|
||||
else
|
||||
$entry['MatchingEntry'] = $entry['DebitEntry'][0];
|
||||
|
||||
'conditions' => array('LedgerEntry.id' => $id),
|
||||
));
|
||||
//pr($entry);
|
||||
if (empty($entry['DebitDoubleEntry']['id']))
|
||||
$entry['DoubleEntry'] = $entry['CreditDoubleEntry'];
|
||||
else
|
||||
$entry['DoubleEntry'] = $entry['DebitDoubleEntry'];
|
||||
|
||||
// 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.
|
||||
|
||||
// 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']),
|
||||
));
|
||||
$entry['DebitLedger']['Account']['ftype'] =
|
||||
$this->LedgerEntry->DebitLedger->Account
|
||||
->fundamentalType($entry['DebitLedger']['Account']['type']);
|
||||
|
||||
// Get the Account from CreditLedger
|
||||
$entry['CreditLedger'] += $this->LedgerEntry->CreditLedger->Account->find
|
||||
('first',
|
||||
array('contain' => true,
|
||||
'fields' => array('Account.id', 'Account.name', 'Account.type', 'Account.trackable'),
|
||||
'conditions' => array('Account.id' => $entry['CreditLedger']['account_id']),
|
||||
));
|
||||
$entry['CreditLedger']['Account']['ftype'] =
|
||||
$this->LedgerEntry->CreditLedger->Account
|
||||
->fundamentalType($entry['CreditLedger']['Account']['type']);
|
||||
|
||||
// Get the reconciliation balances for this ledger entry
|
||||
$stats = $this->LedgerEntry->stats($id);
|
||||
$stats['debit']['amount_reconciled'] = $stats['debit_amount_reconciled'];
|
||||
$stats['credit']['amount_reconciled'] = $stats['credit_amount_reconciled'];
|
||||
if ($entry['DebitLedger']['Account']['trackable'])
|
||||
$stats['debit']['amount_remaining'] = $entry['LedgerEntry']['amount'] - $stats['debit']['amount_reconciled'];
|
||||
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);
|
||||
|
||||
|
||||
// REVISIT <AP>: 20090711
|
||||
// It's not clear whether we should be able to reverse charges that have
|
||||
// already been paid/cleared/reconciled. Certainly, that will be the
|
||||
// case when someone has pre-paid and then moves out early. However, this
|
||||
// will work well for items accidentally charged but not yet paid for.
|
||||
if ((!$entry['DebitLedger']['Account']['trackable'] ||
|
||||
$stats['debit']['amount_reconciled'] == 0) &&
|
||||
(!$entry['CreditLedger']['Account']['trackable'] ||
|
||||
$stats['credit']['amount_reconciled'] == 0)
|
||||
|
||||
&& 0
|
||||
|
||||
)
|
||||
{
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Undo',
|
||||
'url' => array('action' => 'reverse',
|
||||
$id));
|
||||
}
|
||||
|
||||
if ($this->LedgerEntry->Ledger->Account->type
|
||||
($entry['CreditLedger']['Account']['id']) == 'INCOME')
|
||||
{
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Reverse',
|
||||
'url' => array('action' => 'reverse',
|
||||
$id));
|
||||
}
|
||||
// REVISIT <AP>: 20090816
|
||||
// This page doesn't seem very useful, let's just keep it
|
||||
// all to the double entry view.
|
||||
$this->redirect(array('controller' => 'double_entries',
|
||||
'action' => 'view',
|
||||
$entry['DoubleEntry']['id']));
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Double Ledger Entry #{$entry['LedgerEntry']['id']}";
|
||||
$this->set(compact('entry', 'title', 'reconciled', 'stats'));
|
||||
$title = "Ledger Entry #{$entry['LedgerEntry']['id']}";
|
||||
$this->set(compact('entry', 'title'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,22 +2,26 @@
|
||||
|
||||
class LedgersController extends AppController {
|
||||
|
||||
var $sidemenu_links =
|
||||
array(array('name' => 'Ledgers', 'header' => true),
|
||||
array('name' => 'Current', 'url' => array('controller' => 'ledgers', 'action' => 'current')),
|
||||
array('name' => 'Closed', 'url' => array('controller' => 'ledgers', 'action' => 'closed')),
|
||||
array('name' => 'All', 'url' => array('controller' => 'ledgers', 'action' => 'all')),
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
* override: addGridViewSideMenuLinks
|
||||
* - Adds grid view navigation side menu links
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
|
||||
function addGridViewSideMenuLinks() {
|
||||
parent::addGridViewSideMenuLinks();
|
||||
|
||||
$this->addSideMenuLink('Current',
|
||||
array('controller' => 'ledgers', 'action' => 'current'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('Closed',
|
||||
array('controller' => 'ledgers', 'action' => 'closed'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('All',
|
||||
array('controller' => 'ledgers', 'action' => 'all'), null,
|
||||
'CONTROLLER');
|
||||
}
|
||||
|
||||
|
||||
@@ -29,90 +33,86 @@ 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 jqGridDataTables(&$params, &$model) {
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
return array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'Account',
|
||||
'LedgerEntry',
|
||||
'Close',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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 gridDataTables(&$params, &$model) {
|
||||
$tables = $this->gridDataCountTables($params, $model);
|
||||
$tables['link'][] = 'LedgerEntry';
|
||||
$tables['link'][] = 'CloseTransaction';
|
||||
return $tables;
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
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 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);
|
||||
}
|
||||
|
||||
$conditions[] = array('Account.level >=' =>
|
||||
$this->Permission->level('controller.accounts'));
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
$id_sequence = false;
|
||||
if ($index === 'id_sequence') {
|
||||
$id_sequence = true;
|
||||
$index = 'Ledger.account_id';
|
||||
}
|
||||
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||
$order = parent::gridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
$order = parent::jqGridDataOrder($params, $model, $index, $direction);
|
||||
|
||||
if ($id_sequence) {
|
||||
// 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[] = 'Account.name ' . $direction;
|
||||
$order[] = 'Ledger.sequence ' . $direction;
|
||||
}
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Ledger'] = array('id_sequence');
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
// REVISIT <AP>: 20090827
|
||||
// Need to take 'level' into account
|
||||
if ($this->Permission->allow('controller.accounts')) {
|
||||
$links['Ledger'] = array('sequence');
|
||||
$links['Account'] = array('name');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -124,22 +124,24 @@ class LedgersController extends AppController {
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
// Get details about the ledger itself (no entries yet)
|
||||
$ledger = $this->Ledger->find
|
||||
('first',
|
||||
array('contain' =>
|
||||
array(// Models
|
||||
'Account',
|
||||
),
|
||||
'conditions' => array(array('Ledger.id' => $id)),
|
||||
'conditions' => array(array('Ledger.id' => $id),
|
||||
array('Account.level >=' =>
|
||||
$this->Permission->level('controller.accounts')),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if (empty($ledger)) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
// Get ledger stats for our summary box
|
||||
$stats = $this->Ledger->stats($id);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ class MapsController extends AppController {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
$this->sideMenuEnable('SITE', $this->op_area);
|
||||
$this->set('info', $this->mapInfo($id, $requested_width));
|
||||
$this->set('title', "Site Map");
|
||||
}
|
||||
@@ -85,10 +86,34 @@ class MapsController extends AppController {
|
||||
'units' => array());
|
||||
|
||||
// Find all of the map/unit information from this SiteArea
|
||||
$this->Map->recursive = 2;
|
||||
$this->Map->SiteArea->unbindModel(array('hasOne' => array('Map')));
|
||||
$map = $this->Map->read(null, $id);
|
||||
//pr($map);
|
||||
$map = $this->Map->find('first', array('contain' => false,
|
||||
'conditions' => array('id' => $id)));
|
||||
|
||||
$units = $this->Map->Unit->find
|
||||
('all',
|
||||
array('link' =>
|
||||
array('Map' =>
|
||||
array('fields' => array()),
|
||||
|
||||
'CurrentLease' =>
|
||||
array('fields' => array('id', 'paid_through_date',
|
||||
$this->Map->Unit->CurrentLease->
|
||||
delinquentField('CurrentLease')),
|
||||
'Customer'),
|
||||
|
||||
'UnitSize' =>
|
||||
array('fields' => array('id', 'depth', 'width',
|
||||
'MapsUnit.pt_top',
|
||||
'MapsUnit.pt_left',
|
||||
'MapsUnit.transpose')),
|
||||
),
|
||||
'fields' => array('id', 'name', 'status'),
|
||||
'conditions' => array('Map.id' => $id),
|
||||
));
|
||||
|
||||
/* pr(compact('map', 'units')); */
|
||||
/* $this->render('/empty'); */
|
||||
/* return; */
|
||||
|
||||
/*****
|
||||
* The preference would be to leave all things "screen" related
|
||||
@@ -113,7 +138,7 @@ class MapsController extends AppController {
|
||||
$info['depth'] = $bottom * $screen_adjustment_factor;
|
||||
|
||||
// Go through each unit in the map, calculating the map location
|
||||
foreach ($map['Unit'] AS $unit) {
|
||||
foreach ($units AS $unit) {
|
||||
$lft = $unit['MapsUnit']['pt_left'] + $boundary_adjustment;
|
||||
$top = $unit['MapsUnit']['pt_top'] + $boundary_adjustment;
|
||||
|
||||
@@ -132,10 +157,9 @@ class MapsController extends AppController {
|
||||
$width *= $screen_adjustment_factor;
|
||||
$depth *= $screen_adjustment_factor;
|
||||
|
||||
//$info['units'][$unit['id']] =
|
||||
$info['units'][] =
|
||||
array( 'id' => $unit['id'],
|
||||
'name' => $unit['name'],
|
||||
array( 'id' => $unit['Unit']['id'],
|
||||
'name' => $unit['Unit']['name'],
|
||||
'left' => $lft,
|
||||
'right' => $lft + $width,
|
||||
'top' => $top,
|
||||
@@ -143,11 +167,15 @@ class MapsController extends AppController {
|
||||
'width' => $width,
|
||||
'depth' => $depth,
|
||||
'n-s' => $unit['MapsUnit']['transpose'] ? 0 : 1,
|
||||
'status' => $unit['status']
|
||||
'status' => (($unit['Unit']['status'] === 'OCCUPIED' &&
|
||||
!empty($unit[0]['delinquent']))
|
||||
? 'LATE' : $unit['Unit']['status']),
|
||||
'data' => $unit,
|
||||
);
|
||||
}
|
||||
|
||||
//pr($info);
|
||||
/* pr($info); */
|
||||
/* $this->render('/empty'); exit(); */
|
||||
return $info;
|
||||
}
|
||||
|
||||
@@ -160,8 +188,10 @@ class MapsController extends AppController {
|
||||
*/
|
||||
|
||||
function legend($id = null, $requested_width = 400) {
|
||||
$status = $this->Map->Unit->activeStatusEnums();
|
||||
//pr($status);
|
||||
$status = array_keys($this->Map->Unit->activeStatusEnums());
|
||||
$occupied_key = array_search('OCCUPIED', $status);
|
||||
array_splice($status, $occupied_key+1, 0, array('LATE'));
|
||||
|
||||
$rows = 2;
|
||||
$cols = (int)((count($status) + $rows - 1) / $rows);
|
||||
|
||||
@@ -191,7 +221,7 @@ class MapsController extends AppController {
|
||||
$item_width *= $screen_adjustment_factor;
|
||||
$item_depth *= $screen_adjustment_factor;
|
||||
|
||||
foreach ($status AS $code => $value) {
|
||||
foreach ($status AS $code) {
|
||||
$info['units'][] = array('name' => $code,
|
||||
'status' => $code,
|
||||
'width' => $item_width,
|
||||
@@ -241,9 +271,9 @@ class MapsController extends AppController {
|
||||
$info['palate']['unit']['DIRTY']['bg'] = array('red' => 128, 'green' => 192, 'blue' => 192);
|
||||
$info['palate']['unit']['VACANT']['bg'] = array('red' => 0, 'green' => 255, 'blue' => 128);
|
||||
$info['palate']['unit']['OCCUPIED']['bg'] = array('red' => 0, 'green' => 128, 'blue' => 255);
|
||||
$info['palate']['unit']['LATE']['bg'] = array('red' => 255, 'green' => 64, 'blue' => 64);
|
||||
$info['palate']['unit']['LOCKED']['bg'] = array('red' => 255, 'green' => 128, 'blue' => 128);
|
||||
$info['palate']['unit']['LIENED']['bg'] = array('red' => 255, 'green' => 192, 'blue' => 192);
|
||||
$info['palate']['unit']['LATE']['bg'] = array('red' => 255, 'green' => 192, 'blue' => 192);
|
||||
$info['palate']['unit']['LOCKED']['bg'] = array('red' => 255, 'green' => 64, 'blue' => 64);
|
||||
$info['palate']['unit']['LIENED']['bg'] = array('red' => 255, 'green' => 0, 'blue' => 128);
|
||||
|
||||
// Determine text color to go with each background
|
||||
foreach ($info['palate']['unit'] AS &$code) {
|
||||
|
||||
@@ -1,110 +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
|
||||
('contain' => false,
|
||||
);
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
$links['MonetarySource'] = array('id');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: nsf
|
||||
* - Marks a monetary source as having insufficient funds.
|
||||
*/
|
||||
|
||||
function nsf($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
// REVISIT <AP>: 20090713
|
||||
// For testing purposes, must be deleted
|
||||
$stamp = '2009-07-09';
|
||||
|
||||
$this->MonetarySource->nsf($id, $stamp);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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' => false,
|
||||
));
|
||||
|
||||
// REVISIT <AP>: 20090713
|
||||
// Consider allowing the NSF operation only if the source is used on
|
||||
// a ledger entry that is debited on a "payable" account (perhaps
|
||||
// even restricted to "payable" ASSET accounts), credited on Receipt
|
||||
// (or A/R), and reconciles the credit to an entry that debits on a
|
||||
// "depositable" account.
|
||||
|
||||
// Set up dynamic menu items
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Operations', 'header' => true);
|
||||
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'NSF',
|
||||
'url' => array('action' => 'nsf',
|
||||
$id));
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Monetary Source #{$monetary_source['MonetarySource']['id']}";
|
||||
$this->set(compact('monetary_source', 'title'));
|
||||
}
|
||||
}
|
||||
313
site/controllers/statement_entries_controller.php
Normal file
313
site/controllers/statement_entries_controller.php
Normal file
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
class StatementEntriesController extends AppController {
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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 gridDataCountTables(&$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 (!empty($params['post']['custom']['statement_entry_id'])) {
|
||||
$link['ChargeEntry'] = array();
|
||||
// This query actually represents a union...
|
||||
// Unpaid Charge/Surplus: ChargeID - NULL; DisbursementID - NULL
|
||||
// Paid Charge/Refund: ChargeID - NULL; DisbursementID - !NULL
|
||||
// Disbursement/Reversal: ChargeID - !NULL; DisbursementID - NULL
|
||||
// <EMPTY SET>: ChargeID - !NULL; DisbursementID - !NULL
|
||||
//
|
||||
// The query is really slow unless we add the `id` condition to the join.
|
||||
// A cleaner query would be nice, but we must work within the Cake framework.
|
||||
$link['DisbursementEntry'] = array('conditions' =>
|
||||
'`DisbursementEntry`.`id` = '
|
||||
. $params['post']['custom']['statement_entry_id']);
|
||||
}
|
||||
|
||||
return array('link' => $link);
|
||||
}
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$tables = $this->gridDataCountTables($params, $model);
|
||||
|
||||
if (in_array('applied', $params['post']['fields'])) {
|
||||
$tables['link'] +=
|
||||
array('ChargeEntry' => array(),
|
||||
'DisbursementEntry' => array());
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
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'");
|
||||
}
|
||||
if (in_array('unapplied', $params['post']['fields'])) {
|
||||
$fields[] = ("StatementEntry.amount - (" .
|
||||
"IF(StatementEntry.type = 'CHARGE'," .
|
||||
" SUM(COALESCE(DisbursementEntry.amount,0))," .
|
||||
" SUM(COALESCE(ChargeEntry.amount,0)))" .
|
||||
") AS 'unapplied'");
|
||||
}
|
||||
|
||||
$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($customer_id))
|
||||
$conditions[] = array('StatementEntry.customer_id' => $customer_id);
|
||||
|
||||
if (isset($statement_entry_id))
|
||||
$conditions[] = array('OR' =>
|
||||
array(array('ChargeEntry.id' => $statement_entry_id),
|
||||
array('DisbursementEntry.id' => $statement_entry_id)));
|
||||
|
||||
if ($params['action'] === 'unreconciled') {
|
||||
$query = array('conditions' => $conditions);
|
||||
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, true);
|
||||
|
||||
$entries = array();
|
||||
foreach ($set['entries'] AS $entry)
|
||||
$entries[] = $entry['StatementEntry']['id'];
|
||||
|
||||
$conditions[] = array('StatementEntry.id' => $entries);
|
||||
$params['userdata']['balance'] = $set['summary']['balance'];
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['StatementEntry'] = array('id');
|
||||
$links['Transaction'] = array('id');
|
||||
// REVISIT <AP>: 20090827
|
||||
// Need to take 'level' into account
|
||||
if ($this->Permission->allow('controller.accounts'))
|
||||
$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 ($params['action'] === '???') { */
|
||||
/* $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']; */
|
||||
/* } */
|
||||
if ($params['action'] === 'collected') {
|
||||
$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 = null) {
|
||||
if ($this->data) {
|
||||
//pr($this->data); die();
|
||||
|
||||
$this->StatementEntry->reverse
|
||||
($this->data['StatementEntry']['id'],
|
||||
$this->data['Transaction']['stamp'],
|
||||
$this->data['Transaction']['comment']);
|
||||
|
||||
$this->redirect(array('action'=>'view',
|
||||
$this->data['StatementEntry']['id']));
|
||||
$this->INTERNAL_ERROR('SHOULD HAVE REDIRECTED');
|
||||
}
|
||||
|
||||
$this->StatementEntry->id = $id;
|
||||
$entry = $this->StatementEntry->find
|
||||
('first', array
|
||||
('contain' => array('Customer', 'Transaction', 'Account'),
|
||||
));
|
||||
|
||||
if (empty($entry)) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('controller' => 'customers',
|
||||
'action'=>'index'));
|
||||
}
|
||||
|
||||
if (!$this->StatementEntry->reversable($id)) {
|
||||
$this->Session->setFlash(__('Item not reversable.', true));
|
||||
$this->redirect(array('action'=>'view', $id));
|
||||
}
|
||||
|
||||
// Prepare to render.
|
||||
$title = ("Charge #{$entry['StatementEntry']['id']}" .
|
||||
" : {$entry['StatementEntry']['amount']}" .
|
||||
" : Reverse");
|
||||
$this->set(compact('entry', 'title'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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) {
|
||||
$entry = $this->StatementEntry->find
|
||||
('first',
|
||||
array('contain' => array
|
||||
('Transaction' => array('fields' => array('id', 'type', 'stamp')),
|
||||
'Account' => array('id', 'name', 'type', 'level'),
|
||||
'Customer' => array('fields' => array('id', 'name')),
|
||||
'Lease' => array('fields' => array('id', 'number')),
|
||||
),
|
||||
|
||||
'conditions' => array(array('StatementEntry.id' => $id),
|
||||
),
|
||||
));
|
||||
|
||||
if (empty($entry)) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
|
||||
}
|
||||
|
||||
$entry['Account']['link'] =
|
||||
$entry['Account']['level'] >=
|
||||
$this->Permission->level('controller.accounts');
|
||||
|
||||
$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') {
|
||||
|
||||
// Set up dynamic menu items
|
||||
if ($this->StatementEntry->reversable($id))
|
||||
$this->addSideMenuLink('Reverse',
|
||||
array('action' => 'reverse', $id), null,
|
||||
'ACTION');
|
||||
|
||||
if ($stats['balance'] > 0)
|
||||
$this->addSideMenuLink('Waive Balance',
|
||||
array('action' => 'waive', $id), null,
|
||||
'ACTION');
|
||||
}
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Statement Entry #{$entry['StatementEntry']['id']}";
|
||||
$this->set(compact('entry', 'title', 'stats'));
|
||||
}
|
||||
|
||||
}
|
||||
240
site/controllers/tenders_controller.php
Normal file
240
site/controllers/tenders_controller.php
Normal file
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
class TendersController extends AppController {
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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 ($this->data) {
|
||||
$result = $this->Tender->nsf
|
||||
($this->data['Tender']['id'],
|
||||
$this->data['Transaction']['stamp'],
|
||||
$this->data['Transaction']['comment']);
|
||||
$this->redirect(array('controller' => 'tenders',
|
||||
'action' => 'view',
|
||||
$this->data['Tender']['id']));
|
||||
}
|
||||
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
$this->Tender->id = $id;
|
||||
$tender = $this->Tender->find
|
||||
('first', array
|
||||
('contain' => array('Customer', 'LedgerEntry' => array('Transaction')),
|
||||
));
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Tender #{$tender['Tender']['id']} : {$tender['Tender']['name']} : NSF";
|
||||
$this->set(compact('tender', 'title'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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
|
||||
$this->Tender->id = $id;
|
||||
$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.
|
||||
// (or if we're in development mode)
|
||||
&& (!empty($tender['TenderType']['data1_name']) || !empty($this->params['dev']))
|
||||
) {
|
||||
$this->addSideMenuLink('NSF',
|
||||
array('action' => 'nsf', $id), null,
|
||||
'ACTION');
|
||||
}
|
||||
|
||||
// Watch out for the special "Closing" entries, which have
|
||||
// tender_type_id set to NULL. Otherwise, allow editing.
|
||||
if (!empty($tender['TenderType']['id']))
|
||||
$this->addSideMenuLink('Edit',
|
||||
array('action' => 'edit', $id), null,
|
||||
'ACTION');
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Tender #{$tender['Tender']['id']} : {$tender['Tender']['name']}";
|
||||
$this->set(compact('tender', 'title'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: edit
|
||||
* - Edit tender 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['Tender']['id']))
|
||||
$this->redirect(array('action'=>'index'));
|
||||
|
||||
$this->redirect(array('action'=>'view', $this->data['Tender']['id']));
|
||||
}
|
||||
|
||||
// Make sure we have tender data
|
||||
if (empty($this->data['Tender']) || empty($this->data['Tender']['id']))
|
||||
$this->redirect(array('action'=>'index'));
|
||||
|
||||
// Figure out which tender type was chosen
|
||||
// REVISIT <AP>: 20090810; Not ready to change tender type
|
||||
// $tender_type_id = $this->data['Tender']['tender_type_id'];
|
||||
$tender_type_id = $this->Tender->field('tender_type_id');
|
||||
if (empty($tender_type_id))
|
||||
$this->redirect(array('action'=>'view', $this->data['Tender']['id']));
|
||||
|
||||
// Get data fields from the selected tender type
|
||||
$this->data['Tender'] += $this->data['type'][$tender_type_id];
|
||||
unset($this->data['type']);
|
||||
|
||||
// Save the tender and all associated data
|
||||
$this->Tender->create();
|
||||
$this->Tender->id = $this->data['Tender']['id'];
|
||||
if (!$this->Tender->save($this->data, false)) {
|
||||
$this->Session->setFlash("TENDER SAVE FAILED", true);
|
||||
pr("TENDER SAVE FAILED");
|
||||
}
|
||||
|
||||
$this->redirect(array('action'=>'view', $this->Tender->id));
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
$this->data = $this->Tender->findById($id);
|
||||
} else {
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
$tender_types = $this->Tender->TenderType->find
|
||||
('list', array('order' => array('name')));
|
||||
$this->set(compact('tender_types'));
|
||||
|
||||
$types = $this->Tender->TenderType->find('all', array('contain' => false));
|
||||
$this->set(compact('types'));
|
||||
|
||||
// Prepare to render.
|
||||
$title = ('Tender #' . $this->data['Tender']['id'] .
|
||||
' : ' . $this->data['Tender']['name'] .
|
||||
" : Edit");
|
||||
$this->set(compact('title'));
|
||||
}
|
||||
}
|
||||
@@ -4,20 +4,35 @@ class TransactionsController extends AppController {
|
||||
|
||||
var $components = array('RequestHandler');
|
||||
|
||||
var $sidemenu_links =
|
||||
array(array('name' => 'Transactions', 'header' => true),
|
||||
array('name' => 'All', 'url' => array('controller' => 'transactions', 'action' => 'all')),
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
* override: addGridViewSideMenuLinks
|
||||
* - Adds grid view navigation side menu links
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
|
||||
function addGridViewSideMenuLinks() {
|
||||
parent::addGridViewSideMenuLinks();
|
||||
|
||||
$this->addSideMenuLink('Invoices',
|
||||
array('controller' => 'transactions', 'action' => 'invoice'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('Receipts',
|
||||
array('controller' => 'transactions', 'action' => 'receipt'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('Deposits',
|
||||
array('controller' => 'transactions', 'action' => 'deposit'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('All',
|
||||
array('controller' => 'transactions', 'action' => 'all'), null,
|
||||
'CONTROLLER');
|
||||
|
||||
// REVISIT <AP>: 20090824
|
||||
// Right now, we wish to keep things simple. Don't make these
|
||||
// links available to non-admin users.
|
||||
if (empty($this->params['admin']))
|
||||
$this->sideMenuEnable('CONTROLLER', $this->std_area, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,70 +44,64 @@ 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->addSideMenuLink('New Deposit', */
|
||||
/* array('controller' => 'tenders', 'action' => 'deposit'), null, */
|
||||
/* 'CONTROLLER', $this->new_area); */
|
||||
$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) {
|
||||
$links['Transaction'] = array('id');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: view
|
||||
* - Displays information about a specific transaction
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
if (!$id) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
$transaction = $this->Transaction->find
|
||||
('first',
|
||||
array('contain' =>
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
return array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'LedgerEntry' => array('fields' => array('LedgerEntry.id',
|
||||
'LedgerEntry.amount',
|
||||
'LedgerEntry.comment'),
|
||||
//Models
|
||||
|
||||
'DebitLedger' => array
|
||||
('fields' => array('DebitLedger.id', 'DebitLedger.sequence'),
|
||||
'Account' => array
|
||||
('fields' => array('Account.id', 'Account.name')),
|
||||
'Account' => array('fields' => array()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
'CreditLedger' => array
|
||||
('fields' => array('CreditLedger.id', 'CreditLedger.sequence'),
|
||||
'Account' => array
|
||||
('fields' => array('Account.id', 'Account.name')),
|
||||
),
|
||||
),
|
||||
),
|
||||
'conditions' => array('Transaction.id' => $id),
|
||||
));
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$link = $this->gridDataCountTables($params, $model);
|
||||
$link['link']['StatementEntry'] = array('fields' => array());
|
||||
$link['link']['DepositTender'] = array('fields' => array());
|
||||
return $link;
|
||||
}
|
||||
|
||||
// Figure out the transaction total
|
||||
$total = 0;
|
||||
foreach($transaction['LedgerEntry'] AS $entry)
|
||||
$total += $entry['amount'];
|
||||
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;
|
||||
}
|
||||
|
||||
// OK, prepare to render.
|
||||
$title = 'Transaction #' . $transaction['Transaction']['id'];
|
||||
$this->set(compact('transaction', 'title', 'total'));
|
||||
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', 'action' => ($params['action'] == 'deposit'
|
||||
? 'deposit_slip' : 'view'));
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +112,7 @@ class TransactionsController extends AppController {
|
||||
* - handles the creation of a charge invoice
|
||||
*/
|
||||
|
||||
function postInvoice() {
|
||||
function postInvoice($redirect = true) {
|
||||
if (!$this->RequestHandler->isPost()) {
|
||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||
return;
|
||||
@@ -118,6 +127,17 @@ class TransactionsController extends AppController {
|
||||
die("<H1>INVOICE FAILED</H1>");
|
||||
}
|
||||
|
||||
if ($redirect) {
|
||||
if (!empty($this->data['Customer']['id']))
|
||||
$this->redirect(array('controller' => 'customers',
|
||||
'action' => 'receipt',
|
||||
$this->data['Customer']['id']));
|
||||
else
|
||||
$this->redirect(array('controller' => 'leases',
|
||||
'action' => 'view',
|
||||
$this->data['Lease']['id']));
|
||||
}
|
||||
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
@@ -128,23 +148,21 @@ class TransactionsController extends AppController {
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: postReceipt
|
||||
* - handles the creation of a payment receipt
|
||||
* - handles the creation of a receipt
|
||||
*/
|
||||
|
||||
function postReceipt() {
|
||||
function postReceipt($redirect = true) {
|
||||
if (!$this->RequestHandler->isPost()) {
|
||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($this->data['LedgerEntry'] AS &$entry) {
|
||||
if (!isset($entry['acct'][$entry['account_id']]))
|
||||
continue;
|
||||
|
||||
$entry['MonetarySource'] = $entry['acct'][$entry['account_id']];
|
||||
foreach($this->data['Entry'] AS &$entry) {
|
||||
$entry['Tender'] = $entry['type'][$entry['tender_type_id']];
|
||||
unset($entry['type']);
|
||||
unset($entry['tender_type_id']);
|
||||
}
|
||||
|
||||
pr($this->data);
|
||||
if (!$this->Transaction->addReceipt($this->data,
|
||||
$this->data['Customer']['id'],
|
||||
(isset($this->data['Lease']['id'])
|
||||
@@ -157,10 +175,337 @@ class TransactionsController extends AppController {
|
||||
die("<H1>RECEIPT FAILED</H1>");
|
||||
}
|
||||
|
||||
if ($redirect)
|
||||
$this->redirect(array('controller' => 'customers',
|
||||
'action' => 'view',
|
||||
$this->data['Customer']['id']));
|
||||
|
||||
$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;
|
||||
if (empty($data['Customer']['id']))
|
||||
$data['Customer']['id'] = null;
|
||||
if (empty($data['Lease']['id']))
|
||||
$data['Lease']['id'] = null;
|
||||
|
||||
pr(compact('data'));
|
||||
|
||||
if (!$this->Transaction->addWriteOff($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>");
|
||||
}
|
||||
|
||||
// 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>");
|
||||
}
|
||||
|
||||
// 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: destroy
|
||||
* - Deletes a transaction and associated entries
|
||||
* - !!WARNING!! This should be used with EXTREME caution, as it
|
||||
* irreversibly destroys the data. It is not for normal use.
|
||||
*/
|
||||
|
||||
function destroy($id) {
|
||||
$this->Transaction->id = $id;
|
||||
$customer_id = $this->Transaction->field('customer_id');
|
||||
$this->Transaction->destroy($id);
|
||||
$this->redirect(array('controller' => 'customers', 'action' => 'view', $customer_id));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: view
|
||||
* - Displays information about a specific transaction
|
||||
*/
|
||||
|
||||
function view($id = null) {
|
||||
$transaction = $this->Transaction->find
|
||||
('first',
|
||||
array('contain' =>
|
||||
array(// Models
|
||||
'Account(id,name,level)',
|
||||
'Ledger(id,sequence)',
|
||||
'NsfTender(id,name)',
|
||||
),
|
||||
'conditions' => array(array('Transaction.id' => $id),
|
||||
),
|
||||
));
|
||||
|
||||
if (empty($transaction)) {
|
||||
$this->Session->setFlash(__('Invalid Item.', true));
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
$transaction['Account']['link'] =
|
||||
$transaction['Account']['level'] >=
|
||||
$this->Permission->level('controller.accounts');
|
||||
|
||||
if ($transaction['Transaction']['type'] === 'DEPOSIT')
|
||||
$this->addSideMenuLink('View Slip',
|
||||
array('action' => 'deposit_slip', $id), null,
|
||||
'ACTION');
|
||||
|
||||
$this->addSideMenuLink('Destroy',
|
||||
array('action' => 'destroy', $id),
|
||||
array('confirmMessage' =>
|
||||
"This may leave the database in an unstable state." .
|
||||
" Do NOT do this unless you know what you're doing." .
|
||||
" Proceed anyway?"),
|
||||
'ACTION', $this->admin_area);
|
||||
|
||||
// OK, prepare to render.
|
||||
$title = 'Transaction #' . $transaction['Transaction']['id'];
|
||||
$this->set(compact('transaction', 'title'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: deposit_slip
|
||||
* - Special presentation
|
||||
* Processes the user input and updates the database
|
||||
*/
|
||||
|
||||
function deposit_slip($id) {
|
||||
// Find the deposit transaction
|
||||
$this->Transaction->id = $id;
|
||||
$deposit = $this->Transaction->find('first', array('contain' => false));
|
||||
|
||||
// Get a summary of all forms of tender in the deposit
|
||||
$tenders = $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',
|
||||
));
|
||||
|
||||
// Verify the deposit exists, and that something was actually deposited
|
||||
if (empty($deposit) || empty($tenders)) {
|
||||
$this->Session->setFlash(__('Invalid Deposit.', true));
|
||||
$this->redirect(array('action'=>'deposit'));
|
||||
}
|
||||
|
||||
// Add the summary to our deposit slip data container
|
||||
$deposit['types'] = array();
|
||||
foreach ($tenders AS $tender) {
|
||||
$deposit['types'][$tender['TenderType']['id']] =
|
||||
$tender['TenderType'] + $tender[0];
|
||||
}
|
||||
|
||||
$deposit_total = 0;
|
||||
foreach ($deposit['types'] AS $type)
|
||||
$deposit_total += $type['total'];
|
||||
|
||||
if ($deposit['Transaction']['amount'] != $deposit_total)
|
||||
$this->INTERNAL_ERROR("Deposit items do not add up to deposit slip total");
|
||||
|
||||
$this->addSideMenuLink('View',
|
||||
array('action' => 'view', $id), null,
|
||||
'ACTION');
|
||||
|
||||
$title = 'Deposit Slip';
|
||||
$this->set(compact('title', 'deposit'));
|
||||
$this->render('deposit_slip');
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
238
site/controllers/unit_sizes_controller.php
Normal file
238
site/controllers/unit_sizes_controller.php
Normal file
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
|
||||
class UnitSizesController extends AppController {
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: addGridViewSideMenuLinks
|
||||
* - Adds grid view navigation side menu links
|
||||
*/
|
||||
|
||||
function addGridViewSideMenuLinks() {
|
||||
parent::addGridViewSideMenuLinks();
|
||||
|
||||
$this->addSideMenuLink('1 Bedroom',
|
||||
array('controller' => 'unit_sizes', 'action' => 'bd1'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('2 Bedroom',
|
||||
array('controller' => 'unit_sizes', 'action' => 'bd2'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('3 Bedroom',
|
||||
array('controller' => 'unit_sizes', 'action' => 'bd3'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('4+ Bedroom',
|
||||
array('controller' => 'unit_sizes', 'action' => 'bd4'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('Auto',
|
||||
array('controller' => 'unit_sizes', 'action' => 'auto'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('Boat',
|
||||
array('controller' => 'unit_sizes', 'action' => 'boat'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('RV',
|
||||
array('controller' => 'unit_sizes', 'action' => 'rv'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('All',
|
||||
array('controller' => 'unit_sizes', 'action' => 'all'), null,
|
||||
'CONTROLLER');
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: index / unavailable / vacant / occupied / all
|
||||
* - Generate a listing of units
|
||||
*/
|
||||
|
||||
function index() { $this->all(); }
|
||||
function bd1() { $this->gridView('Sizes for 1 Bedroom'); }
|
||||
function bd2() { $this->gridView('Sizes for 2 Bedrooms'); }
|
||||
function bd3() { $this->gridView('Sizes for 3 Bedroom'); }
|
||||
function bd4() { $this->gridView('Sizes for 4+ Bedroom'); }
|
||||
function auto() { $this->gridView('Sizes for an Automobile'); }
|
||||
function boat() { $this->gridView('Sizes for a Boat'); }
|
||||
function rv() { $this->gridView('Sizes for an RV'); }
|
||||
function all() { $this->gridView('All Unit Sizes', 'all'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* virtuals: gridData
|
||||
* - With the application controller handling the gridData action,
|
||||
* these virtual functions ensure that the correct data is passed
|
||||
* to jqGrid.
|
||||
*/
|
||||
|
||||
function gridDataCountTables(&$params, &$model) {
|
||||
return array('link' => array('UnitType'));
|
||||
}
|
||||
|
||||
function gridDataTables(&$params, &$model) {
|
||||
$tables = $this->gridDataCountTables($params, $model);
|
||||
$tables['link']['Unit'] = array();
|
||||
return $tables;
|
||||
}
|
||||
|
||||
function gridDataFields(&$params, &$model) {
|
||||
$fields = parent::gridDataFields($params, $model);
|
||||
$fields[] = 'ROUND(UnitSize.width/12, 1) AS width';
|
||||
$fields[] = 'ROUND(UnitSize.depth/12, 1) AS depth';
|
||||
$fields[] = 'ROUND(UnitSize.height/12, 1) AS height';
|
||||
$fields[] = 'ROUND(UnitSize.width/12 * UnitSize.depth/12, 0) AS sqft';
|
||||
$fields[] = 'ROUND(UnitSize.width/12 * UnitSize.depth/12 * UnitSize.height/12, 0) AS cuft';
|
||||
$fields[] = 'ROUND(UnitSize.rent / (UnitSize.width/12 * UnitSize.depth/12), 2) AS sqcost';
|
||||
$fields[] = 'ROUND(UnitSize.rent / (UnitSize.width/12 * UnitSize.depth/12 * UnitSize.height/12), 2) AS cucost';
|
||||
|
||||
$fields[] = 'COUNT(Unit.id) AS units';
|
||||
$fields[] = 'SUM(IF(' . $this->UnitSize->Unit->conditionUnavailable() . ', 1, 0)) AS unavailable';
|
||||
$fields[] = 'SUM(IF(' . $this->UnitSize->Unit->conditionAvailable() . ', 1, 0)) AS available';
|
||||
$fields[] = 'SUM(IF(' . $this->UnitSize->Unit->conditionOccupied() . ', 1, 0)) AS occupied';
|
||||
$fields[] = 'SUM(IF(' . $this->UnitSize->Unit->conditionOccupied() . ', 0, 1)) / COUNT(unit.id) AS vacancy';
|
||||
$fields[] = 'SUM(IF(' . $this->UnitSize->Unit->conditionOccupied() . ', 1, 0)) / COUNT(unit.id) AS occupancy';
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
|
||||
// REVISIT <AP>: 20090825
|
||||
// Sizes should come from the database.
|
||||
// For now, I took an assumed average need, then bracketed
|
||||
// with +/- 50 sqft. This gives a 100sqft range for each.
|
||||
if ($params['action'] === 'bd1') { // 75 sqft
|
||||
$conditions[] = array('UnitType.id' => array_keys($this->UnitSize->UnitType->enclosedTypes()));
|
||||
$conditions[] = '(UnitSize.width/12 * UnitSize.depth/12) <= 125';
|
||||
}
|
||||
elseif ($params['action'] === 'bd2') { // 125 sqft
|
||||
$conditions[] = array('UnitType.id' => array_keys($this->UnitSize->UnitType->enclosedTypes()));
|
||||
$conditions[] = '(UnitSize.width/12 * UnitSize.depth/12) >= 75';
|
||||
$conditions[] = '(UnitSize.width/12 * UnitSize.depth/12) <= 175';
|
||||
}
|
||||
elseif ($params['action'] === 'bd3') { // 175 sqft
|
||||
$conditions[] = array('UnitType.id' => array_keys($this->UnitSize->UnitType->enclosedTypes()));
|
||||
$conditions[] = '(UnitSize.width/12 * UnitSize.depth/12) >= 125';
|
||||
$conditions[] = '(UnitSize.width/12 * UnitSize.depth/12) <= 225';
|
||||
}
|
||||
elseif ($params['action'] === 'bd4') { // 225 sqft
|
||||
$conditions[] = array('UnitType.id' => array_keys($this->UnitSize->UnitType->enclosedTypes()));
|
||||
$conditions[] = '(UnitSize.width/12 * UnitSize.depth/12) >= 175';
|
||||
}
|
||||
elseif (in_array($params['action'], array('auto', 'boat', 'rv'))) {
|
||||
$conditions[] = array('UnitType.id' =>
|
||||
array_merge(array_keys($this->UnitSize->UnitType->enclosedTypes()),
|
||||
array_keys($this->UnitSize->UnitType->outdoorTypes())));
|
||||
list($width, $depth, $height) = array(8, 15, null);
|
||||
if ($params['action'] === 'auto')
|
||||
$depth = 15;
|
||||
elseif ($params['action'] === 'boat')
|
||||
$depth = 15;
|
||||
elseif ($params['action'] === 'rv')
|
||||
list($width, $depth, $height) = array(10, 25, 12);
|
||||
|
||||
$conditions[] = "(UnitSize.width/12) >= $width";
|
||||
$conditions[] = "(UnitSize.depth/12) >= $depth";
|
||||
if (isset($height))
|
||||
$conditions[] = array('OR' =>
|
||||
array("(UnitSize.height/12) >= $height",
|
||||
//"UnitSize.height IS NULL",
|
||||
array('UnitType.id' =>
|
||||
array_keys($this->UnitSize->UnitType->outdoorTypes())),
|
||||
));
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['UnitSize'] = array('name');
|
||||
return parent::gridDataPostProcessLinks($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 UnitSize and related fields
|
||||
$this->UnitSize->id = $id;
|
||||
$size = $this->UnitSize->find
|
||||
('first', array
|
||||
('contain' => array('UnitType'),
|
||||
'fields' => array('UnitSize.*', 'UnitType.*',
|
||||
'ROUND(UnitSize.width/12, 1) AS width',
|
||||
'ROUND(UnitSize.depth/12, 1) AS depth',
|
||||
'ROUND(UnitSize.height/12, 1) AS height',
|
||||
'ROUND(UnitSize.width/12 * UnitSize.depth/12, 0) AS sqft',
|
||||
'ROUND(UnitSize.width/12 * UnitSize.depth/12 * UnitSize.height/12, 0) AS cuft'),
|
||||
));
|
||||
$size['UnitSize'] = $size[0] + $size['UnitSize'];
|
||||
unset($size[0]);
|
||||
|
||||
$this->set(compact('size'));
|
||||
$this->set('stats', $this->UnitSize->stats($id));
|
||||
|
||||
// Prepare to render.
|
||||
$title = "Unit Size : {$size['UnitSize']['name']}";
|
||||
$this->set(compact('title'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: edit
|
||||
* - Edit unit_size information
|
||||
*/
|
||||
|
||||
function edit($id = null) {
|
||||
$this->INTERNAL_ERROR('NOT READY');
|
||||
if (isset($this->data)) {
|
||||
// Check to see if the operation was cancelled.
|
||||
if (isset($this->params['form']['cancel'])) {
|
||||
if (empty($this->data['UnitSize']['id']))
|
||||
$this->redirect(array('action'=>'index'));
|
||||
|
||||
$this->redirect(array('action'=>'view', $this->data['UnitSize']['id']));
|
||||
}
|
||||
|
||||
// Make sure we have unit_size data
|
||||
if (empty($this->data['UnitSize']) || empty($this->data['UnitSize']['id']))
|
||||
$this->redirect(array('action'=>'index'));
|
||||
|
||||
// Save the unit_size and all associated data
|
||||
$this->UnitSize->create();
|
||||
$this->UnitSize->id = $this->data['UnitSize']['id'];
|
||||
if (!$this->UnitSize->save($this->data, false)) {
|
||||
$this->Session->setFlash("UNIT_SIZE SAVE FAILED", true);
|
||||
pr("UNIT_SIZE SAVE FAILED");
|
||||
}
|
||||
|
||||
$this->redirect(array('action'=>'view', $this->UnitSize->id));
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
$this->data = $this->UnitSize->findById($id);
|
||||
} else {
|
||||
$this->redirect(array('action'=>'index'));
|
||||
}
|
||||
|
||||
// Prepare to render.
|
||||
$title = ('UnitSize ' . $this->data['UnitSize']['name'] .
|
||||
" : Edit");
|
||||
$this->set(compact('title'));
|
||||
}
|
||||
}
|
||||
@@ -2,23 +2,35 @@
|
||||
|
||||
class UnitsController extends AppController {
|
||||
|
||||
var $sidemenu_links =
|
||||
array(array('name' => 'Units', 'header' => true),
|
||||
array('name' => 'Occupied', 'url' => array('controller' => 'units', 'action' => 'occupied')),
|
||||
array('name' => 'Vacant', 'url' => array('controller' => 'units', 'action' => 'vacant')),
|
||||
array('name' => 'Unavailable', 'url' => array('controller' => 'units', 'action' => 'unavailable')),
|
||||
array('name' => 'All', 'url' => array('controller' => 'units', 'action' => 'all')),
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* override: sideMenuLinks
|
||||
* - Generates controller specific links for the side menu
|
||||
* override: addGridViewSideMenuLinks
|
||||
* - Adds grid view navigation side menu links
|
||||
*/
|
||||
function sideMenuLinks() {
|
||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
||||
|
||||
function addGridViewSideMenuLinks() {
|
||||
parent::addGridViewSideMenuLinks();
|
||||
|
||||
$this->addSideMenuLink('Unavailable',
|
||||
array('controller' => 'units', 'action' => 'unavailable'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('Vacant',
|
||||
array('controller' => 'units', 'action' => 'vacant'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('Occupied',
|
||||
array('controller' => 'units', 'action' => 'occupied'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('Overlocked',
|
||||
array('controller' => 'units', 'action' => 'locked'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('Liened',
|
||||
array('controller' => 'units', 'action' => 'liened'), null,
|
||||
'CONTROLLER');
|
||||
$this->addSideMenuLink('All',
|
||||
array('controller' => 'units', 'action' => 'all'), null,
|
||||
'CONTROLLER');
|
||||
}
|
||||
|
||||
|
||||
@@ -30,77 +42,66 @@ 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 locked() { $this->gridView('Overlocked Units'); }
|
||||
function liened() { $this->gridView('Liened 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 jqGridDataCountTables(&$params, &$model) {
|
||||
$link = array
|
||||
('link' =>
|
||||
array(// Models
|
||||
'UnitSize' => array('fields' => array('id', '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 jqGridDataTables(&$params, &$model) {
|
||||
$link = $this->jqGridDataCountTables($params, $model);
|
||||
$link['link']['CurrentLease']['LedgerEntry'] = array('fields' => array());
|
||||
$link['link']['CurrentLease']['LedgerEntry']['Ledger'] = array('fields' => array());
|
||||
$link['link']['CurrentLease']['LedgerEntry']['Ledger']['Account'] = array('fields' => array());
|
||||
// INNER JOIN would be great, as it would ensure we're only looking
|
||||
// at the ledger entries that we truly want. However, this also
|
||||
// removes from the query any leases that do not yet have a ledger
|
||||
// entry in A/R. A solution would be to INNER JOIN these tables,
|
||||
// and LEFT JOIN it to the rest. Grouping of JOINs, however, is
|
||||
// implemented with the 'joins' tag, and is not available through
|
||||
// the Linkable behavior interface.
|
||||
//$link['link']['CurrentLease']['LedgerEntry']['Ledger']['Account']['type'] = 'INNER';
|
||||
$link['link']['CurrentLease']['LedgerEntry']['Ledger']['Account']['conditions']
|
||||
= array('Account.id' =>
|
||||
$this->Unit->CurrentLease->LedgerEntry->Ledger->Account->accountReceivableAccountID());
|
||||
return $link;
|
||||
/* function 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);
|
||||
|
||||
$fields[] = 'ROUND(UnitSize.width/12 * UnitSize.depth/12, 0) AS sqft';
|
||||
return array_merge($fields,
|
||||
$this->Unit->Lease->StatementEntry->chargeDisbursementFields(true));
|
||||
}
|
||||
|
||||
function jqGridDataFields(&$params, &$model) {
|
||||
$db = &$model->getDataSource();
|
||||
$fields = $db->fields($model, $model->alias);
|
||||
$fields[] = ("SUM(IF(Account.id IS NULL, 0," .
|
||||
" IF(LedgerEntry.debit_ledger_id = Account.id," .
|
||||
" 1, -1))" .
|
||||
" * LedgerEntry.amount) AS 'balance'");
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function jqGridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::jqGridDataConditions($params, $model);
|
||||
function gridDataConditions(&$params, &$model) {
|
||||
$conditions = parent::gridDataConditions($params, $model);
|
||||
|
||||
if ($params['action'] === 'unavailable') {
|
||||
$conditions[] = $this->Unit->conditionUnavailable();
|
||||
@@ -114,31 +115,37 @@ class UnitsController extends AppController {
|
||||
elseif ($params['action'] === 'unoccupied') {
|
||||
$conditions[] = array('NOT' => array($this->Unit->conditionOccupied()));
|
||||
}
|
||||
elseif ($params['action'] === 'locked') {
|
||||
$conditions[] = $this->Unit->conditionLocked();
|
||||
}
|
||||
elseif ($params['action'] === 'liened') {
|
||||
$conditions[] = $this->Unit->conditionLiened();
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
||||
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::jqGridDataOrder($params, $model, $index, $direction);
|
||||
$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::jqGridDataOrder($params, $model,
|
||||
$order[] = parent::gridDataOrder($params, $model,
|
||||
'Unit.sort_order', $direction);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
||||
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||
$links['Unit'] = array('name');
|
||||
$links['UnitSize'] = array('name');
|
||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
||||
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||
}
|
||||
|
||||
|
||||
@@ -153,7 +160,7 @@ class UnitsController extends AppController {
|
||||
$customer = array();
|
||||
$unit = array();
|
||||
|
||||
if (isset($id)) {
|
||||
if (!empty($id)) {
|
||||
$this->Unit->recursive = -1;
|
||||
$unit = current($this->Unit->read(null, $id));
|
||||
}
|
||||
@@ -203,6 +210,21 @@ class UnitsController extends AppController {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* action: lock/unlock
|
||||
* - Transitions the unit into / out of the LOCKED state
|
||||
*/
|
||||
|
||||
function status($id, $status) {
|
||||
$this->Unit->updateStatus($id, $status, true);
|
||||
$this->redirect(array('action' => 'view', $id));
|
||||
}
|
||||
function lock($id) { $this->status($id, 'LOCKED'); }
|
||||
function unlock($id) { $this->status($id, 'OCCUPIED'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -242,37 +264,135 @@ 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);
|
||||
// If the unit is occupied, but not locked, provide a
|
||||
// mechanism to do so. This doesn't have to be restricted
|
||||
// to past due customers. There are times we need to
|
||||
// overlock customers in good standing, such as if their
|
||||
// lock breaks, is cut, or missing for any reason.
|
||||
if ($this->Unit->occupied($unit['Unit']['status']) &&
|
||||
!$this->Unit->locked($unit['Unit']['status']))
|
||||
$this->addSideMenuLink('Lock',
|
||||
array('action' => 'Lock', $id), null,
|
||||
'ACTION');
|
||||
|
||||
if (isset($unit['CurrentLease']['id']) &&
|
||||
!isset($unit['CurrentLease']['moveout_date'])) {
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Move-Out', 'url' => array('action' => 'move_out',
|
||||
$id));
|
||||
// If the unit is locked, provide an option to unlock it,
|
||||
// unless it's locked due to lien, which is not so simple.
|
||||
if ($this->Unit->locked($unit['Unit']['status']) &&
|
||||
!$this->Unit->liened($unit['Unit']['status']))
|
||||
$this->addSideMenuLink('Unlock',
|
||||
array('action' => 'unlock', $id), null,
|
||||
'ACTION');
|
||||
|
||||
// If there is a current lease on this unit, then provide
|
||||
// a link to move the tenant out. Current lease for a unit
|
||||
// has a bit different definition than a current lease for
|
||||
// a customer, since a lease stays with a customer until it
|
||||
// is finally closed. A lease, however, only stays with a
|
||||
// unit while occupied (since a unit is not responsible for
|
||||
// any lingering financial obligations, like a customer is).
|
||||
// Of course, if there is no current lease, provide a link
|
||||
// to move a new tenant in (if the unit is available).
|
||||
if (isset($unit['CurrentLease']['id'])) {
|
||||
$this->addSideMenuLink('Move-Out',
|
||||
array('action' => 'move_out', $id), null,
|
||||
'ACTION');
|
||||
} elseif ($this->Unit->available($unit['Unit']['status'])) {
|
||||
$this->addSideMenuLink('Move-In',
|
||||
array('action' => 'move_in', $id), null,
|
||||
'ACTION');
|
||||
} else {
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Move-In', 'url' => array('action' => 'move_in',
|
||||
$id));
|
||||
// Unit is unavailable (dirty, damaged, reserved, business-use, etc)
|
||||
}
|
||||
|
||||
if (isset($unit['CurrentLease']['id']) &&
|
||||
!isset($unit['CurrentLease']['close_date'])) {
|
||||
$this->sidemenu_links[] =
|
||||
array('name' => 'Payment', 'url' => array('controller' => 'customers',
|
||||
// If there is a current lease, allow new charges to
|
||||
// be added, and payments to be made.
|
||||
if (isset($unit['CurrentLease']['id'])) {
|
||||
$this->addSideMenuLink('New Invoice',
|
||||
array('controller' => 'leases',
|
||||
'action' => 'invoice',
|
||||
$unit['CurrentLease']['id']), null,
|
||||
'ACTION');
|
||||
$this->addSideMenuLink('New Receipt',
|
||||
array('controller' => 'customers',
|
||||
'action' => 'receipt',
|
||||
$unit['CurrentLease']['customer_id']));
|
||||
$unit['CurrentLease']['customer_id']), null,
|
||||
'ACTION');
|
||||
}
|
||||
|
||||
// Always allow the unit to be edited.
|
||||
$this->addSideMenuLink('Edit',
|
||||
array('action' => 'edit', $id), null,
|
||||
'ACTION');
|
||||
|
||||
// Prepare to render.
|
||||
$title = 'Unit ' . $unit['Unit']['name'];
|
||||
$this->set(compact('unit', 'title',
|
||||
'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));
|
||||
}
|
||||
|
||||
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.
|
||||
$this->set(compact('title'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
76
site/controllers/util_controller.php
Normal file
76
site/controllers/util_controller.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
class UtilController extends AppController {
|
||||
|
||||
var $uses = array();
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reset_data
|
||||
* - Development function. TO BE DELETED
|
||||
*/
|
||||
|
||||
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";
|
||||
$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);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: rebuild_box
|
||||
*/
|
||||
|
||||
function rebuild_box($type) {
|
||||
$this->layout = null;
|
||||
$this->autoLayout = false;
|
||||
$this->autoRender = false;
|
||||
Configure::write('debug', '0');
|
||||
$script = preg_replace('%/webroot/index.php$%',
|
||||
'/build_'.$type.'box.cmd',
|
||||
$_SERVER['SCRIPT_FILENAME']);
|
||||
|
||||
// REVISIT <AP>: 20090828
|
||||
// Just use system call
|
||||
$handle = popen($script . ' 2>&1', 'r');
|
||||
while (($read = fread($handle, 2096))) {
|
||||
// Do nothing
|
||||
}
|
||||
pclose($handle);
|
||||
|
||||
$url = $_SERVER['HTTP_REFERER'];
|
||||
if (empty($url))
|
||||
$url = "/";
|
||||
|
||||
$this->redirect($url);
|
||||
}
|
||||
|
||||
function rebuild_sandbox() { $this->rebuild_box('sand'); }
|
||||
function rebuild_devbox() { $this->rebuild_box('dev'); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: logmsg
|
||||
* - action to allow posting log message data
|
||||
*/
|
||||
|
||||
function logmsg() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,6 @@
|
||||
<?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',
|
||||
@@ -16,13 +9,14 @@ class Account extends AppModel {
|
||||
// engine specific code. However, it doesn't
|
||||
// work with the Linkable behavior. I need to
|
||||
// look into that, just not right now.
|
||||
//'conditions' => array('CurrentLedger.close_id' => null),
|
||||
'conditions' => array('CurrentLedger.close_id IS NULL'),
|
||||
//'conditions' => array(array('CurrentLedger.close_transaction_id' => null)),
|
||||
'conditions' => array('CurrentLedger.close_transaction_id IS NULL'),
|
||||
),
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'Ledger',
|
||||
'LedgerEntry',
|
||||
);
|
||||
|
||||
|
||||
@@ -78,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';
|
||||
@@ -104,6 +98,22 @@ class Account extends AppModel {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -111,22 +121,42 @@ class Account extends AppModel {
|
||||
* - Returns the ID of the desired account
|
||||
*/
|
||||
|
||||
function securityDepositAccountID() { return $this->nameToID('Security Deposit'); }
|
||||
function rentAccountID() { return $this->nameToID('Rent'); }
|
||||
function lateChargeAccountID() { return $this->nameToID('Late Charge'); }
|
||||
function nsfAccountID() { return $this->nameToID('NSF'); }
|
||||
function nsfChargeAccountID() { return $this->nameToID('NSF Charge'); }
|
||||
function taxAccountID() { return $this->nameToID('Tax'); }
|
||||
function accountReceivableAccountID() { return $this->nameToID('A/R'); }
|
||||
function cashAccountID() { return $this->nameToID('Cash'); }
|
||||
function checkAccountID() { return $this->nameToID('Check'); }
|
||||
function moneyOrderAccountID() { return $this->nameToID('Money Order'); }
|
||||
function concessionAccountID() { return $this->nameToID('Concession'); }
|
||||
function pettyCashAccountID() { return $this->nameToID('Petty Cash'); }
|
||||
function invoiceAccountID() { return $this->nameToID('Invoice'); }
|
||||
function receiptAccountID() { return $this->nameToID('Receipt'); }
|
||||
function badDebtAccountID() { return $this->nameToID('Bad Debt'); }
|
||||
function lookup($name, $check = true) {
|
||||
$id = $this->nameToID($name);
|
||||
if (empty($id) && $check)
|
||||
$this->INTERNAL_ERROR("Missing Account '$name'");
|
||||
return $id;
|
||||
}
|
||||
|
||||
function securityDepositAccountID() { return $this->lookup('Security Deposit'); }
|
||||
function rentAccountID() { return $this->lookup('Rent'); }
|
||||
function lateChargeAccountID() { return $this->lookup('Late Charge'); }
|
||||
function nsfAccountID() { return $this->lookup('NSF'); }
|
||||
function nsfChargeAccountID() { return $this->lookup('NSF Charge'); }
|
||||
function taxAccountID() { return $this->lookup('Tax'); }
|
||||
function accountReceivableAccountID() { return $this->lookup('A/R'); }
|
||||
function accountPayableAccountID() { return $this->lookup('A/P'); }
|
||||
function cashAccountID() { return $this->lookup('Cash'); }
|
||||
function checkAccountID() { return $this->lookup('Check'); }
|
||||
function moneyOrderAccountID() { return $this->lookup('Money Order'); }
|
||||
function achAccountID() { return $this->lookup('ACH'); }
|
||||
function concessionAccountID() { return $this->lookup('Concession'); }
|
||||
function waiverAccountID() { return $this->lookup('Waiver'); }
|
||||
function pettyCashAccountID() { return $this->lookup('Petty Cash'); }
|
||||
function invoiceAccountID() { return $this->lookup('Invoice'); }
|
||||
function receiptAccountID() { return $this->lookup('Receipt'); }
|
||||
function badDebtAccountID() { return $this->lookup('Bad Debt'); }
|
||||
function customerCreditAccountID() { return $this->lookup(
|
||||
// REVISIT <AP>: 20090816
|
||||
// Use of A/R works, and saves an excess of accounts.
|
||||
// However, a dedicated account is nice, since it can
|
||||
// quickly be spotted how much is _really_ due, vs
|
||||
// how much has been pre-paid. Customer credits in
|
||||
// A/R is not as clear, although a report is an
|
||||
// obvious solution.
|
||||
//'A/R'
|
||||
'Credit'
|
||||
); }
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -157,59 +187,36 @@ class Account extends AppModel {
|
||||
|
||||
function relatedAccounts($attribute, $extra = null) {
|
||||
$this->cacheQueries = true;
|
||||
$account = $this->find('all', array
|
||||
('contain' => array('CurrentLedger'),
|
||||
'fields' => array('Account.id', 'Account.type', 'Account.name', 'CurrentLedger.id'),
|
||||
$accounts = $this->find('all', array
|
||||
('fields' => array('Account.id', 'Account.name'),
|
||||
'conditions' => array('Account.'.$attribute => true),
|
||||
'order' => array('Account.name'),
|
||||
) + (isset($extra) ? $extra : array())
|
||||
);
|
||||
$this->cacheQueries = false;
|
||||
|
||||
return $account;
|
||||
// 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: chargeAccounts
|
||||
* - Returns an array of accounts suitable for charges
|
||||
* function: xxxAccounts
|
||||
* - Returns an array of accounts suitable for activity xxx
|
||||
*/
|
||||
|
||||
function chargeAccounts() {
|
||||
// Get all accounts that support charges
|
||||
$accounts = $this->relatedAccounts('chargeable', array('order' => 'name'));
|
||||
function invoiceAccounts() { return $this->relatedAccounts('invoices'); }
|
||||
function receiptAccounts() { return $this->relatedAccounts('receipts'); }
|
||||
function depositAccounts() { return $this->relatedAccounts('deposits'); }
|
||||
function refundAccounts() { return $this->relatedAccounts('refunds'); }
|
||||
|
||||
// Rearrange to be of the form (id => name)
|
||||
$charge_accounts = array();
|
||||
foreach ($accounts AS $acct) {
|
||||
$charge_accounts[$acct['Account']['id']] = $acct['Account']['name'];
|
||||
}
|
||||
|
||||
return $charge_accounts;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: paymentAccounts
|
||||
* - Returns an array of accounts suitable for payments
|
||||
*/
|
||||
|
||||
function paymentAccounts() {
|
||||
// Get all accounts that support payments
|
||||
$accounts = $this->relatedAccounts('payable', array('order' => 'name'));
|
||||
|
||||
// Rearrange to be of the form (id => name)
|
||||
$payment_accounts = array();
|
||||
foreach ($accounts AS $acct) {
|
||||
$payment_accounts[$acct['Account']['id']] = $acct['Account']['name'];
|
||||
}
|
||||
|
||||
return $payment_accounts;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -219,14 +226,30 @@ class Account extends AppModel {
|
||||
*/
|
||||
|
||||
function collectableAccounts() {
|
||||
$accounts = $this->paymentAccounts();
|
||||
$accounts = $this->receiptAccounts();
|
||||
|
||||
foreach(array($this->nsfAccountID(),
|
||||
$this->securityDepositAccountID())
|
||||
foreach(array($this->customerCreditAccountID(),
|
||||
$this->securityDepositAccountID(),
|
||||
$this->nsfAccountID(),
|
||||
$this->waiverAccountID(),
|
||||
$this->badDebtAccountID(),
|
||||
//$this->lookup('Closing'),
|
||||
//$this->lookup('Equity'),
|
||||
)
|
||||
AS $account_id) {
|
||||
$accounts[$account_id] = $this->name($account_id);
|
||||
}
|
||||
|
||||
$accounts['all'] = $accounts['default'] = $accounts;
|
||||
|
||||
foreach(array($this->concessionAccountID(),
|
||||
$this->waiverAccountID(),
|
||||
$this->badDebtAccountID(),
|
||||
)
|
||||
AS $account_id) {
|
||||
unset($accounts['default'][$account_id]);
|
||||
}
|
||||
|
||||
return $accounts;
|
||||
}
|
||||
|
||||
@@ -294,513 +317,37 @@ class Account extends AppModel {
|
||||
* - Closes the current account ledger, and opens a new one
|
||||
* with the old balance carried forward.
|
||||
*/
|
||||
function closeCurrentLedger($id = null, $close_id = null) {
|
||||
$contain = array('CurrentLedger' => array('fields' => array('CurrentLedger.id')));
|
||||
|
||||
if (!$close_id) {
|
||||
$close = new Close();
|
||||
$close->create();
|
||||
if (!$close->save(array('stamp' => null), false)) {
|
||||
return false;
|
||||
}
|
||||
$close_id = $close->id;
|
||||
}
|
||||
function closeCurrentLedgers($ids = null) {
|
||||
|
||||
$this->cacheQueries = true;
|
||||
$account = $this->find('all', array
|
||||
('contain' => $contain,
|
||||
('contain' => array('CurrentLedger.id'),
|
||||
'fields' => array(),
|
||||
'conditions' =>
|
||||
$id ? array(array('Account.id' => $id)) : array()
|
||||
'conditions' => (empty($ids)
|
||||
? array()
|
||||
: array(array('Account.id' => $ids)))
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
//pr(compact('id', 'account'));
|
||||
|
||||
foreach ($account AS $acct) {
|
||||
if (!$this->Ledger->closeLedger($acct['CurrentLedger']['id'], $close_id))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
$ledger_ids = array();
|
||||
foreach ($account AS $acct)
|
||||
$ledger_ids[] = $acct['CurrentLedger']['id'];
|
||||
|
||||
return $this->Ledger->closeLedgers($ledger_ids);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findLedgerEntries
|
||||
* 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: 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', 'customer_id', 'lease_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;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: postLedgerEntry
|
||||
* -
|
||||
* transaction_data
|
||||
* - transaction_id (optional... if set all else is ignored)
|
||||
* - Transaction
|
||||
* - stamp (optional... otherwise NOW is used)
|
||||
* - comment
|
||||
*
|
||||
* monetary_source_data
|
||||
* - monetary_source_id (optional... if set all else is ignored)
|
||||
* - account_name
|
||||
* - MonetarySource
|
||||
* - name
|
||||
*/
|
||||
|
||||
function postLedgerEntry($transaction_data,
|
||||
$monetary_data,
|
||||
$entry_data,
|
||||
$reconcile = null) {
|
||||
//pr(compact('transaction_data', 'monetary_data', 'entry_data', 'reconcile'));
|
||||
|
||||
// Automatically figure out the customer if we have the lease
|
||||
if (isset($entry_data['lease_id']) && !isset($entry_data['customer_id'])) {
|
||||
$L = new Lease();
|
||||
$L->recursive = -1;
|
||||
$lease = $L->read(null, $entry_data['lease_id']);
|
||||
$entry_data['customer_id'] = $lease['Lease']['customer_id'];
|
||||
}
|
||||
|
||||
if (!isset($entry_data['lease_id']))
|
||||
$entry_data['lease_id'] = null;
|
||||
|
||||
if (!isset($entry_data['customer_id']))
|
||||
$entry_data['customer_id'] = null;
|
||||
|
||||
// Get the Transaction squared away
|
||||
if (isset($transaction_data['transaction_id'])) {
|
||||
$transaction_data
|
||||
= array_intersect_key($transaction_data,
|
||||
array('transaction_id'=>1,
|
||||
'split_transaction_id'=>1));
|
||||
}
|
||||
elseif (isset($transaction_data['Transaction'])) {
|
||||
$transaction_data
|
||||
= array_intersect_key($transaction_data,
|
||||
array('Transaction'=>1,
|
||||
'split_transaction_id'=>1));
|
||||
}
|
||||
else {
|
||||
$transaction_data = array('Transaction'=>array('stamp' => null));
|
||||
}
|
||||
|
||||
|
||||
// Get the Monetary Source squared away
|
||||
if (isset($monetary_data)) {
|
||||
if (!isset($monetary_data['monetary_source_id'])) {
|
||||
|
||||
// Convert Account ID to name or vice versa
|
||||
if (isset($monetary_data['account_id'])) {
|
||||
$monetary_data['account_name'] = $this->name($monetary_data['account_id']);
|
||||
} elseif (isset($monetary_data['account_name'])) {
|
||||
$monetary_data['account_id'] = $this->nameToID($monetary_data['account_name']);
|
||||
}
|
||||
|
||||
if ($monetary_data['account_id'] == $this->cashAccountID()) {
|
||||
// No distinguishing features of Cash, just
|
||||
// use the shared monetary source
|
||||
$monetary_data['monetary_source_id'] =
|
||||
$this->Ledger->LedgerEntry->MonetarySource->nameToID('Cash');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($monetary_data['monetary_source_id'])) {
|
||||
$monetary_data
|
||||
= array_intersect_key($monetary_data,
|
||||
array('monetary_source_id'=>1));
|
||||
}
|
||||
else {
|
||||
// The monetary source needs to be unique
|
||||
// Create a new one dedicated to this entry
|
||||
// Give it a fancy name based on the check number
|
||||
$monetary_data['MonetarySource']['name'] = $monetary_data['account_name'];
|
||||
if ($monetary_data['account_name'] === $this->name($this->checkAccountID()) ||
|
||||
$monetary_data['account_name'] === $this->name($this->moneyOrderAccountID())) {
|
||||
$monetary_data['MonetarySource']['name'] .=
|
||||
' #' . $monetary_data['MonetarySource']['data1'];
|
||||
}
|
||||
|
||||
$monetary_data
|
||||
= array_intersect_key($monetary_data,
|
||||
array('MonetarySource'=>1));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$monetary_data = array();
|
||||
}
|
||||
|
||||
// Make sure to clean out any unwanted data from the entry
|
||||
$entry_data
|
||||
= array_diff_key($entry_data,
|
||||
array('transaction_id'=>1, 'Transaction'=>1,
|
||||
'monetary_source_id'=>1, 'MonetarySource'=>1));
|
||||
|
||||
// Then add in the transaction and monetary source data
|
||||
//pr(compact('transaction_data', 'monetary_data', 'entry_data'));
|
||||
if (isset($transaction_data))
|
||||
$entry_data += $transaction_data;
|
||||
if (isset($monetary_data))
|
||||
$entry_data += $monetary_data;
|
||||
|
||||
// Set up the debit ledger id
|
||||
if (!isset($entry_data['debit_ledger_id'])) {
|
||||
$entry_data['debit_ledger_id'] =
|
||||
(isset($entry_data['debit_account_id'])
|
||||
? $this->currentLedgerID($entry_data['debit_account_id'])
|
||||
: (isset($entry_data['debit_account_name'])
|
||||
? $this->currentLedgerID($this->nameToID($entry_data['debit_account_name']))
|
||||
: null
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Set up the credit ledger id
|
||||
if (!isset($entry_data['credit_ledger_id'])) {
|
||||
$entry_data['credit_ledger_id'] =
|
||||
(isset($entry_data['credit_account_id'])
|
||||
? $this->currentLedgerID($entry_data['credit_account_id'])
|
||||
: (isset($entry_data['credit_account_name'])
|
||||
? $this->currentLedgerID($this->nameToID($entry_data['credit_account_name']))
|
||||
: null
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
//pr(array('pre-save', compact('entry_data')));
|
||||
// Create it!
|
||||
$new_entry = new LedgerEntry();
|
||||
$new_entry->create();
|
||||
if (!$new_entry->saveAll($entry_data, array('validate'=>false))) {
|
||||
return array('error' => true);
|
||||
}
|
||||
|
||||
// See if the user has entered some sort of non-array
|
||||
// for the reconcile parameter.
|
||||
if (isset($reconcile) && is_bool($reconcile) && $reconcile) {
|
||||
$reconcile = array('debit' => true, 'credit' => true);
|
||||
}
|
||||
elseif (isset($reconcile) && $reconcile == 'invoice') {
|
||||
$reconcile = array('credit' => 'invoice');
|
||||
}
|
||||
elseif (isset($reconcile) && $reconcile == 'receipt') {
|
||||
$reconcile = array('debit' => 'receipt');
|
||||
}
|
||||
elseif (!isset($reconcile) || !is_array($reconcile)) {
|
||||
$reconcile = array();
|
||||
}
|
||||
|
||||
// Reconcile the new entry... assume we'll have success
|
||||
$err = false;
|
||||
foreach (array_intersect_key($reconcile, array('credit'=>1,'debit'=>1))
|
||||
AS $dc_type => $reconcile_set) {
|
||||
if (!isset($reconcile_set) || (is_bool($reconcile_set) && !$reconcile_set))
|
||||
continue;
|
||||
|
||||
if ($reconcile_set === 'receipt') {
|
||||
$C = new Customer();
|
||||
$reconciled = $C->reconcileNewLedgerEntry($entry_data['customer_id'],
|
||||
$this->fundamentalOpposite($dc_type),
|
||||
$entry_data['amount']);
|
||||
|
||||
/* pr(array("reconcile receipt", */
|
||||
/* compact('reconciled', 'split_transaction', 'transaction_data'))); */
|
||||
$split_transaction = array_intersect_key($transaction_data,
|
||||
array('Transaction'=>1,
|
||||
'split_transaction_id'=>1));
|
||||
|
||||
if (isset($split_transaction['split_transaction_id']))
|
||||
$split_transaction['transaction_id'] = $split_transaction['split_transaction_id'];
|
||||
|
||||
if (is_array($reconciled) && count($reconciled[$dc_type]['entry'])) {
|
||||
foreach ($reconciled[$dc_type]['entry'] AS $rec) {
|
||||
//pr(compact('rec', 'split_transaction'));
|
||||
if (!$rec['applied'])
|
||||
continue;
|
||||
|
||||
// Create an entry to handle the splitting of the funds ("Payment")
|
||||
// and reconcile against the new cash/check/etc entry created above,
|
||||
// as well as the A/R account.
|
||||
|
||||
// Payment must debit the Receipt ledger, and credit the A/R ledger
|
||||
// debit: Receipt credit: A/R
|
||||
$ids = $this->postLedgerEntry
|
||||
($split_transaction,
|
||||
null,
|
||||
array('debit_ledger_id' => $this->currentLedgerID($this->receiptAccountID()),
|
||||
'credit_ledger_id' => $this->currentLedgerID($this->accountReceivableAccountID()),
|
||||
'amount' => $rec['applied'],
|
||||
'lease_id' => $rec['lease_id'],
|
||||
'customer_id' => $rec['customer_id'],
|
||||
),
|
||||
array('debit' => array(array('LedgerEntry' => array('id' => $new_entry->id,
|
||||
'amount' => $rec['applied']))),
|
||||
'credit' => array(array('LedgerEntry' => array('id' => $rec['id'],
|
||||
'amount' => $rec['applied']))))
|
||||
);
|
||||
// Keep using the same split transaction for all reconciled entries
|
||||
$split_transaction = array_intersect_key($ids, array('transaction_id'=>1));
|
||||
//pr(compact('ids', 'split_transaction'));
|
||||
}
|
||||
|
||||
//pr("end reconciled is array");
|
||||
}
|
||||
|
||||
//pr("end reconcile receipt");
|
||||
}
|
||||
|
||||
if (is_array($reconcile_set)) {
|
||||
//pr("reconcile_set is array");
|
||||
foreach ($reconcile_set AS $reconcile_entry) {
|
||||
if (!isset($reconcile_entry['LedgerEntry']['id']))
|
||||
continue;
|
||||
|
||||
$amount = $reconcile_entry['LedgerEntry']['amount'];
|
||||
if (!$amount)
|
||||
continue;
|
||||
|
||||
if ($dc_type == 'debit') {
|
||||
$debit_ledger_entry_id = $new_entry->id;
|
||||
$credit_ledger_entry_id = $reconcile_entry['LedgerEntry']['id'];
|
||||
}
|
||||
else {
|
||||
$debit_ledger_entry_id = $reconcile_entry['LedgerEntry']['id'];
|
||||
$credit_ledger_entry_id = $new_entry->id;
|
||||
}
|
||||
|
||||
$R = new Reconciliation();
|
||||
$R->create();
|
||||
if (!$R->save(compact('amount',
|
||||
'debit_ledger_entry_id',
|
||||
'credit_ledger_entry_id'), false))
|
||||
$err = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$new_entry->recursive = -1;
|
||||
$new_entry->read();
|
||||
//pr(array('post-save', $entry->data));
|
||||
|
||||
$ret = array
|
||||
('error' => $err,
|
||||
'id' => $new_entry->data['LedgerEntry']['id'],
|
||||
'transaction_id' => $new_entry->data['LedgerEntry']['transaction_id'],
|
||||
'monetary_source_id' => $new_entry->data['LedgerEntry']['monetary_source_id']);
|
||||
|
||||
if (isset($split_transaction['transaction_id']))
|
||||
$ret['split_transaction_id'] = $split_transaction['transaction_id'];
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: closeAndDeposit
|
||||
* - Closes the current set of ledgers, transferring
|
||||
* their balances to specified ledger.
|
||||
*/
|
||||
function closeAndDeposit($set, $deposit_account_id) {
|
||||
|
||||
$close = new Close();
|
||||
$close->create();
|
||||
if (!$close->save(array('stamp' => null, 'comment' => 'Deposit'), false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$transaction = array();
|
||||
foreach ($set AS $ledger) {
|
||||
// REVISIT <AP>: 20090710
|
||||
// If the user said to include a ledger in the
|
||||
// set, should we really be excluding it?
|
||||
if ($ledger['total'] == 0)
|
||||
continue;
|
||||
|
||||
$ids = $this->postLedgerEntry
|
||||
($transaction,
|
||||
null,
|
||||
array('debit_account_id' => $deposit_account_id,
|
||||
'credit_ledger_id' => $ledger['id'],
|
||||
'amount' => $ledger['total']),
|
||||
// Reconcile the account for cash/check/etc,
|
||||
// which is the credit side of this entry.
|
||||
array('credit' => $ledger['entries']));
|
||||
//pr(compact('ids'));
|
||||
|
||||
if ($ids['error'])
|
||||
die("closeAndDeposit : postLedgerEntry returned error!");
|
||||
|
||||
$transaction = array_intersect_key($ids, array('transaction_id'=>1));
|
||||
|
||||
$this->Ledger->closeLedger($ledger['id'], $close->id);
|
||||
}
|
||||
function ledgerEntries($id, $all = false, $cond = null, $link = null) {
|
||||
$ledgers = $this->ledgers($id, $all);
|
||||
return $this->Ledger->ledgerEntries($ledgers, $cond, $link);
|
||||
}
|
||||
|
||||
|
||||
@@ -811,37 +358,17 @@ 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.
|
||||
|
||||
$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))
|
||||
));
|
||||
$this->queryInit($query);
|
||||
$query['link'] = array('Account' => $query['link']);
|
||||
|
||||
$stats = array();
|
||||
if ($all) {
|
||||
foreach ($account['Ledger'] AS $ledger)
|
||||
foreach ($this->ledgers($id, $all) AS $ledger)
|
||||
$this->statsMerge($stats['Ledger'],
|
||||
$this->Ledger->stats($ledger['id'], $cond));
|
||||
}
|
||||
else {
|
||||
$stats['Ledger'] =
|
||||
$this->Ledger->stats($account['CurrentLedger']['id'], $cond);
|
||||
}
|
||||
$this->Ledger->stats($ledger, $query));
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ class LinkableBehavior extends ModelBehavior {
|
||||
protected $_defaults = array('type' => 'LEFT');
|
||||
|
||||
function pr($lev, $mixed) {
|
||||
if ($lev >= 5)
|
||||
if ($lev >= 3)
|
||||
return;
|
||||
|
||||
pr($mixed);
|
||||
@@ -130,42 +130,53 @@ class LinkableBehavior extends ModelBehavior {
|
||||
}
|
||||
|
||||
public function beforeFind(&$Model, $query) {
|
||||
$this->pr(10,
|
||||
array('function' => 'Linkable::beforeFind',
|
||||
'args' => array('Model->alias' => '$Model->alias') + compact('query'),
|
||||
));
|
||||
if (isset($query[$this->_key])) {
|
||||
$optionsDefaults = $this->_defaults + array('reference' =>
|
||||
array('class' => $Model->alias,
|
||||
'alias' => $Model->alias),
|
||||
$this->_key => array());
|
||||
$optionsKeys = $this->_options + array($this->_key => true);
|
||||
if (!isset($query[$this->_key]))
|
||||
return $query;
|
||||
|
||||
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']);
|
||||
|
||||
$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;
|
||||
}
|
||||
$iterations = Set::normalize($iterator);
|
||||
$this->pr(25,
|
||||
array('checkpoint' => 'Iterations',
|
||||
compact('iterations'),
|
||||
|
||||
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'),
|
||||
));
|
||||
foreach ($iterations as $alias => $options) {
|
||||
|
||||
//$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 = am($defaults, compact('alias'), $options);
|
||||
//$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__));
|
||||
}
|
||||
@@ -174,31 +185,28 @@ class LinkableBehavior extends ModelBehavior {
|
||||
$options['class'] = $alias;
|
||||
|
||||
if (!isset($options['conditions']))
|
||||
$options['conditions'] = array();
|
||||
$options['conditions'] = null;
|
||||
elseif (!is_array($options['conditions']))
|
||||
$options['conditions'] = array($options['conditions']);
|
||||
|
||||
$this->pr(20,
|
||||
array('checkpoint' => 'Begin Model Work',
|
||||
compact('alias', 'options'),
|
||||
compact('referenceAlias', '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
|
||||
|
||||
$_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
|
||||
$this->pr(12,
|
||||
array('checkpoint' => 'Aliases Established',
|
||||
'Model' => ($modelAlias .' : '. $modelClass .
|
||||
' ('. $_Model->alias .' : '. $_Model->name .')'),
|
||||
array('checkpoint' => 'Model Established',
|
||||
'Reference' => ($referenceAlias .' : '. $referenceClass .
|
||||
' ('. $Reference->alias .' : '. $Reference->name .')'),
|
||||
'Model' => ($modelAlias .' : '. $modelClass .
|
||||
' ('. $Model->alias .' : '. $Model->name .')'),
|
||||
));
|
||||
|
||||
$db =& $_Model->getDataSource();
|
||||
$db =& $Model->getDataSource();
|
||||
|
||||
$associatedThroughReference = 0;
|
||||
$association = null;
|
||||
@@ -206,26 +214,26 @@ class LinkableBehavior extends ModelBehavior {
|
||||
// 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 defines association to _Model"));
|
||||
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];
|
||||
$type = $associations[$Model->alias];
|
||||
$association = $Reference->{$type}[$Model->alias];
|
||||
}
|
||||
elseif (($associations = $_Model->getAssociated()) &&
|
||||
elseif (($associations = $Model->getAssociated()) &&
|
||||
isset($associations[$Reference->alias])) {
|
||||
$this->pr(12, array('checkpoint' => "_Model defines association to Reference"));
|
||||
$this->pr(12, array('checkpoint' => "Model ($modelClass) defines association to Reference ($referenceClass)"));
|
||||
$type = $associations[$Reference->alias];
|
||||
$association = $_Model->{$type}[$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 _Model and Reference"));
|
||||
$this->pr(12, array('checkpoint' => "No assocation between Reference ($referenceClass) and Model ($modelClass)"));
|
||||
$type = 'belongsTo';
|
||||
$_Model->bind($Reference->alias);
|
||||
$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)));
|
||||
$association = $Model->{$type}[$Reference->alias];
|
||||
$Model->unbindModel(array('belongsTo' => array($Reference->alias)));
|
||||
}
|
||||
|
||||
// Determine which model holds the foreign key
|
||||
@@ -233,11 +241,11 @@ class LinkableBehavior extends ModelBehavior {
|
||||
$primaryAlias = $referenceAlias;
|
||||
$foreignAlias = $modelAlias;
|
||||
$primaryModel = $Reference;
|
||||
$foreignModel = $_Model;
|
||||
$foreignModel = $Model;
|
||||
} else {
|
||||
$primaryAlias = $modelAlias;
|
||||
$foreignAlias = $referenceAlias;
|
||||
$primaryModel = $_Model;
|
||||
$primaryModel = $Model;
|
||||
$foreignModel = $Reference;
|
||||
}
|
||||
|
||||
@@ -246,15 +254,87 @@ class LinkableBehavior extends ModelBehavior {
|
||||
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,
|
||||
$association['conditions']);
|
||||
$options['conditions']);
|
||||
|
||||
$this->pr(15,
|
||||
array('checkpoint' => 'Models Established - Check Associations',
|
||||
'primaryModel' => $primaryAlias .' : '. $primaryModel->name,
|
||||
'foreignModel' => $foreignAlias .' : '. $foreignModel->name,
|
||||
compact('type', 'association'),
|
||||
compact('type', 'association', 'options'),
|
||||
));
|
||||
|
||||
if ($type === 'hasAndBelongsToMany') {
|
||||
@@ -263,42 +343,61 @@ class LinkableBehavior extends ModelBehavior {
|
||||
else
|
||||
$linkClass = Inflector::classify($association['joinTable']);
|
||||
|
||||
$Link =& $_Model->{$linkClass};
|
||||
$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'),
|
||||
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['foreignKey'], $linkAlias);
|
||||
$referenceLink = $Link->escapeField($association['associationForeignKey'], $linkAlias);
|
||||
$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($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);
|
||||
$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);
|
||||
$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
|
||||
@@ -310,7 +409,7 @@ class LinkableBehavior extends ModelBehavior {
|
||||
$options['joins'][] = array('type' => 'INNER',
|
||||
'alias' => $modelAlias,
|
||||
'conditions' => "{$modelKey} = {$modelLink}",
|
||||
'table' => $db->fullTableName($_Model, true));
|
||||
'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.
|
||||
@@ -345,62 +444,43 @@ class LinkableBehavior extends ModelBehavior {
|
||||
|
||||
$this->pr(19,
|
||||
array('checkpoint' => 'Conditions',
|
||||
array('options[conditions]' => $options['conditions'],
|
||||
'association[conditions]' => $association['conditions'],
|
||||
),
|
||||
));
|
||||
|
||||
// The user may have specified conditions directly in the model
|
||||
// for this join. Make sure to adhere to those conditions.
|
||||
if (isset($association['conditions']) && is_array($association['conditions']))
|
||||
$options['conditions'] = array_merge($options['conditions'], $association['conditions']);
|
||||
elseif (!empty($association['conditions']))
|
||||
$options['conditions'][] = $association['conditions'];
|
||||
|
||||
$this->pr(19,
|
||||
array('checkpoint' => 'Conditions2',
|
||||
array('options[conditions]' => $options['conditions'],
|
||||
),
|
||||
));
|
||||
|
||||
if (empty($options['table'])) {
|
||||
$options['table'] = $db->fullTableName($_Model, true);
|
||||
$options['table'] = $db->fullTableName($Model, true);
|
||||
}
|
||||
|
||||
if (!isset($options['fields']) || !is_array($options['fields']))
|
||||
$options['fields'] = $db->fields($_Model, $modelAlias);
|
||||
$options['fields'] = $db->fields($Model, $modelAlias);
|
||||
elseif (!empty($options['fields']))
|
||||
$options['fields'] = $db->fields($_Model, $modelAlias, $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'])));
|
||||
// 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']);
|
||||
|
||||
$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));
|
||||
$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('options', 'modelClass', 'modelAlias', 'query'),
|
||||
compact('referenceAlias', 'modelAlias', 'options', 'result'),
|
||||
));
|
||||
}
|
||||
++$cont;
|
||||
$notDone = isset($iterators[$cont]);
|
||||
} while ($notDone);
|
||||
}
|
||||
|
||||
$this->pr(20,
|
||||
array('function' => 'Linkable::beforeFind',
|
||||
'return' => compact('query'),
|
||||
array('return' => 'Linkable::buildQuery',
|
||||
compact('referenceAlias'),
|
||||
));
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
class Close extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'Ledger',
|
||||
);
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -3,13 +3,6 @@ class Contact extends AppModel {
|
||||
|
||||
var $displayField = 'display_name';
|
||||
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'display_name' => array('notempty'),
|
||||
'id_federal' => array('ssn'),
|
||||
'id_exp' => array('date')
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'ContactsMethod',
|
||||
'ContactsCustomer',
|
||||
@@ -48,14 +41,15 @@ class Contact extends AppModel {
|
||||
function saveContact($id, $data) {
|
||||
|
||||
// Establish a display name if not already given
|
||||
if (!$data['Contact']['display_name'])
|
||||
if (!$data['Contact']['display_name'] &&
|
||||
$data['Contact']['first_name'] && $data['Contact']['last_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']));
|
||||
$data['Contact']['last_name'] . ', ' . $data['Contact']['first_name'];
|
||||
|
||||
foreach (array('last_name', 'first_name', 'company_name') AS $fld) {
|
||||
if (!$data['Contact']['display_name'] && $data['Contact'][$fld])
|
||||
$data['Contact']['display_name'] = $data['Contact'][$fld];
|
||||
}
|
||||
|
||||
// Save the contact data
|
||||
$this->create();
|
||||
|
||||
@@ -19,17 +19,14 @@ class Customer extends AppModel {
|
||||
'conditions' => 'CurrentLease.close_date IS NULL',
|
||||
),
|
||||
'Lease',
|
||||
'LedgerEntry',
|
||||
'StatementEntry',
|
||||
'ContactsCustomer',
|
||||
|
||||
'Transaction',
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
'Contact',
|
||||
'Transaction' => array(
|
||||
'joinTable' => 'ledger_entries',
|
||||
'foreignKey' => 'customer_id',
|
||||
'associationForeignKey' => 'transaction_id',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -57,131 +54,101 @@ class Customer extends AppModel {
|
||||
* function: leaseIds
|
||||
* - Returns the lease IDs for the given customer
|
||||
*/
|
||||
function leaseIds($id) {
|
||||
function leaseIds($id, $current = false) {
|
||||
$Lease = $current ? 'CurrentLease' : 'Lease';
|
||||
|
||||
$this->cacheQueries = true;
|
||||
$customer = $this->find('first',
|
||||
array('contain' =>
|
||||
array('Lease' => array('fields' => array('id'))),
|
||||
array($Lease => array('fields' => array('id'))),
|
||||
'fields' => array(),
|
||||
'conditions' => array(array('Customer.id' => $id))));
|
||||
$this->cacheQueries = false;
|
||||
|
||||
$ids = array();
|
||||
foreach ($customer['Lease'] AS $lease)
|
||||
foreach ($customer[$Lease] AS $lease)
|
||||
$ids[] = $lease['id'];
|
||||
|
||||
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);
|
||||
|
||||
$A = new Account();
|
||||
$entries = $A->findLedgerEntries
|
||||
($A->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) {
|
||||
$A = new Account();
|
||||
$unreconciled = $A->findUnreconciledLedgerEntries
|
||||
($A->accountReceivableAccountID(),
|
||||
$fundamental_type,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
function securityDepositBalance($id, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
|
||||
return $unreconciled;
|
||||
$sd_account_id =
|
||||
$this->StatementEntry->Account->securityDepositAccountID();
|
||||
|
||||
$squery = $query;
|
||||
$squery['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$squery['conditions'][] = array('StatementEntry.account_id' => $sd_account_id);
|
||||
$stats = $this->StatementEntry->stats(null, $squery);
|
||||
$this->pr(26, compact('squery', 'stats'));
|
||||
|
||||
// OK, we know now how much we charged for a security
|
||||
// deposit, as well as how much we received to pay for it.
|
||||
// Now we need to know if any has been released.
|
||||
// Yes... this sucks.
|
||||
$lquery = $query;
|
||||
$lquery['link'] = array('Transaction' =>
|
||||
array('fields' => array(),
|
||||
'Customer' =>
|
||||
(empty($query['link'])
|
||||
? array('fields' => array())
|
||||
: $query['link'])));
|
||||
$lquery['conditions'][] = array('Transaction.customer_id' => $id);
|
||||
$lquery['conditions'][] = array('LedgerEntry.account_id' => $sd_account_id);
|
||||
$lquery['conditions'][] = array('LedgerEntry.crdr' => 'DEBIT');
|
||||
$lquery['fields'][] = 'SUM(LedgerEntry.amount) AS total';
|
||||
$released = $this->StatementEntry->Transaction->LedgerEntry->find
|
||||
('first', $lquery);
|
||||
$this->pr(26, compact('lquery', 'released'));
|
||||
|
||||
return $this->prReturn($stats['Charge']['disbursement'] - $released[0]['total']);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: 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) {
|
||||
$A = new Account();
|
||||
$reconciled = $A->reconcileNewLedgerEntry
|
||||
($A->accountReceivableAccountID(),
|
||||
$fundamental_type,
|
||||
$amount,
|
||||
array('LedgerEntry.customer_id' => $id));
|
||||
function unreconciledCharges($id, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
|
||||
return $reconciled;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: details
|
||||
* - Returns detail information for the customer
|
||||
*/
|
||||
|
||||
function details($id = null) {
|
||||
// Query the DB for need information.
|
||||
$customer = $this->find
|
||||
('first', array
|
||||
('contain' => array
|
||||
(// Models
|
||||
'Contact' =>
|
||||
array('order' => array('Contact.display_name'),
|
||||
// Models
|
||||
'ContactPhone',
|
||||
'ContactEmail',
|
||||
'ContactAddress',
|
||||
),
|
||||
'Lease' =>
|
||||
array('Unit' =>
|
||||
array('order' => array('sort_order'),
|
||||
'fields' => array('id', 'name'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array('Customer.id' => $id),
|
||||
));
|
||||
|
||||
// Figure out the outstanding balance for this customer
|
||||
$customer['stats'] = $this->stats($id);
|
||||
|
||||
// Figure out the total security deposit for the current lease.
|
||||
$customer['deposits'] = $this->findSecurityDeposits($id);
|
||||
|
||||
return $customer;
|
||||
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, true);
|
||||
return $this->prReturn($set);
|
||||
}
|
||||
|
||||
|
||||
@@ -200,10 +167,8 @@ class Customer extends AppModel {
|
||||
continue;
|
||||
|
||||
$I = new Contact();
|
||||
$I->create();
|
||||
if (!$I->save($contact, false)) {
|
||||
if (!$I->saveContact(null, array('Contact' => $contact)))
|
||||
return false;
|
||||
}
|
||||
$contact['id'] = $I->id;
|
||||
}
|
||||
|
||||
@@ -261,6 +226,90 @@ class Customer extends AppModel {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: update
|
||||
* - Update any cached or calculated fields
|
||||
*/
|
||||
function update($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
|
||||
if (empty($id)) {
|
||||
$customers = $this->find('all', array('contain' => false, 'fields' => array('id')));
|
||||
foreach ($customers AS $customer) {
|
||||
// This SHOULDN'T happen, but check to be sure
|
||||
// or we'll get infinite recursion.
|
||||
if (empty($customer['Customer']['id']))
|
||||
continue;
|
||||
$this->update($customer['Customer']['id']);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// updateLeaseCount is typically handled directly when needed.
|
||||
// However, this function is used to _ensure_ customer info is
|
||||
// current, so we're obligated to call it anyway.
|
||||
$this->updateLeaseCount($id);
|
||||
|
||||
$current_leases =
|
||||
$this->find('all',
|
||||
// REVISIT <AP>: 20090816
|
||||
// Do we need to update leases other than the current ones?
|
||||
// It may be necessary. For example, a non-current lease
|
||||
// can still be hit with an NSF item. In that case, it
|
||||
// could have stale data if we look only to current leases.
|
||||
//array('link' => array('CurrentLease' => array('type' => 'INNER')),
|
||||
array('link' => array('Lease' => array('type' => 'INNER')),
|
||||
'conditions' => array('Customer.id' => $id)));
|
||||
|
||||
foreach ($current_leases AS $lease) {
|
||||
if (!empty($lease['CurrentLease']['id']))
|
||||
$this->Lease->update($lease['CurrentLease']['id']);
|
||||
if (!empty($lease['Lease']['id']))
|
||||
$this->Lease->update($lease['Lease']['id']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: 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'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -268,17 +317,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);
|
||||
|
||||
$A = new Account();
|
||||
$stats = $A->stats($A->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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
21
site/models/default_option.php
Normal file
21
site/models/default_option.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
class DefaultOption extends AppModel {
|
||||
|
||||
var $belongsTo =
|
||||
array('OptionValue',
|
||||
);
|
||||
|
||||
|
||||
function values($name = null) {
|
||||
$this->prEnter(compact('name'));
|
||||
|
||||
$query = array();
|
||||
$this->queryInit($query);
|
||||
|
||||
$query['link']['DefaultOption'] = array();
|
||||
$query['link']['DefaultOption']['type'] = 'INNER';
|
||||
$query['link']['DefaultOption']['fields'] = array();
|
||||
return $this->prReturn($this->OptionValue->values($name, $query));
|
||||
}
|
||||
|
||||
}
|
||||
21
site/models/default_permission.php
Normal file
21
site/models/default_permission.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
class DefaultPermission extends AppModel {
|
||||
|
||||
var $belongsTo =
|
||||
array('PermissionValue',
|
||||
);
|
||||
|
||||
|
||||
function values($name = null) {
|
||||
$this->prEnter(compact('name'));
|
||||
|
||||
$query = array();
|
||||
$this->queryInit($query);
|
||||
|
||||
$query['link']['DefaultPermission'] = array();
|
||||
$query['link']['DefaultPermission']['type'] = 'INNER';
|
||||
$query['link']['DefaultPermission']['fields'] = array();
|
||||
return $this->prReturn($this->PermissionValue->values($name, $query));
|
||||
}
|
||||
|
||||
}
|
||||
109
site/models/double_entry.php
Normal file
109
site/models/double_entry.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?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) {
|
||||
//$this->prFunctionLevel(16);
|
||||
$this->prEnter(compact('entry1', 'entry2', 'entry1_tender'));
|
||||
|
||||
$ret = array();
|
||||
if (!$this->verifyDoubleEntry($entry1, $entry2, $entry1_tender))
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
|
||||
// Handle the case where a double entry involves the same
|
||||
// exact ledger. This would not serve any useful purpose.
|
||||
// It is not, however, an error. It is semantically correct
|
||||
// just not really logically correct. To make this easier,
|
||||
// just ensure ledger_id is set for each entry, even though
|
||||
// it would be handled later by the LedgerEntry model.
|
||||
//array($entry1, $entry2) AS &$entry) {
|
||||
for ($i=1; $i <= 2; ++$i) {
|
||||
if (empty(${'entry'.$i}['ledger_id']))
|
||||
${'entry'.$i}['ledger_id'] =
|
||||
$this->DebitEntry->Account->currentLedgerID(${'entry'.$i}['account_id']);
|
||||
}
|
||||
if ($entry1['ledger_id'] == $entry2['ledger_id'])
|
||||
return $this->prReturn(array('error' => false));
|
||||
|
||||
// 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 $this->prReturn(array('error' => true) + $ret);
|
||||
|
||||
// Add the second ledger entry to the database
|
||||
$result = $LE->addLedgerEntry($entry2);
|
||||
$ret['Entry2'] = $result;
|
||||
if ($result['error'])
|
||||
return $this->prReturn(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'];
|
||||
|
||||
$ret['data'] = $double_entry;
|
||||
|
||||
$this->create();
|
||||
if (!$this->save($double_entry))
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
|
||||
$ret['double_entry_id'] = $this->id;
|
||||
return $this->prReturn($ret + array('error' => false));
|
||||
}
|
||||
}
|
||||
38
site/models/group.php
Normal file
38
site/models/group.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
class Group extends AppModel {
|
||||
|
||||
var $hasMany =
|
||||
array('GroupOption',
|
||||
'Membership',
|
||||
);
|
||||
|
||||
var $knows =
|
||||
array('User',
|
||||
'Site',
|
||||
);
|
||||
|
||||
static $current_group_ids;
|
||||
function currentGroupIds() {
|
||||
if (empty(self::$current_group_ids))
|
||||
self::$current_group_ids = $this->groupIds();
|
||||
|
||||
if (empty(self::$current_group_ids))
|
||||
// We must force a stop here, since this is typically
|
||||
// called very early on, and so will cause a recursive
|
||||
// crash as we try to render the internal error and
|
||||
// again stumble on this problem.
|
||||
$this->INTERNAL_ERROR('INVALID MEMBERSHIP', 0, true);
|
||||
|
||||
return self::$current_group_ids;
|
||||
}
|
||||
|
||||
function groupIds($user_id = null, $site_id = null) {
|
||||
if (empty($user_id))
|
||||
$user_id = $this->User->currentUserId();
|
||||
if (empty($site_id))
|
||||
$site_id = $this->Site->currentSiteId();
|
||||
|
||||
return $this->Membership->memberGroups($user_id, $site_id);
|
||||
}
|
||||
|
||||
}
|
||||
25
site/models/group_option.php
Normal file
25
site/models/group_option.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
class GroupOption extends AppModel {
|
||||
|
||||
var $belongsTo =
|
||||
array('Group',
|
||||
'OptionValue',
|
||||
);
|
||||
|
||||
|
||||
function values($ids, $name = null) {
|
||||
$this->prEnter(compact('id', 'name'));
|
||||
|
||||
$query = array();
|
||||
$this->queryInit($query);
|
||||
|
||||
$query['link']['GroupOption'] = array();
|
||||
$query['link']['GroupOption']['fields'] = array();
|
||||
$query['link']['GroupOption']['Group'] = array();
|
||||
$query['link']['GroupOption']['Group']['fields'] = array();
|
||||
$query['conditions'][] = array('Group.id' => $ids);
|
||||
$query['order'][] = 'Group.rank';
|
||||
return $this->prReturn($this->OptionValue->values($name, $query));
|
||||
}
|
||||
|
||||
}
|
||||
25
site/models/group_permission.php
Normal file
25
site/models/group_permission.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
class GroupPermission extends AppModel {
|
||||
|
||||
var $belongsTo =
|
||||
array('Group',
|
||||
'PermissionValue',
|
||||
);
|
||||
|
||||
|
||||
function values($ids, $name = null) {
|
||||
$this->prEnter(compact('id', 'name'));
|
||||
|
||||
$query = array();
|
||||
$this->queryInit($query);
|
||||
|
||||
$query['link']['GroupPermission'] = array();
|
||||
$query['link']['GroupPermission']['fields'] = array();
|
||||
$query['link']['GroupPermission']['Group'] = array();
|
||||
$query['link']['GroupPermission']['Group']['fields'] = array();
|
||||
$query['conditions'][] = array('Group.id' => $ids);
|
||||
$query['order'][] = 'Group.rank';
|
||||
return $this->prReturn($this->PermissionValue->values($name, $query));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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'),
|
||||
'rent' => array('money'),
|
||||
'next_rent' => array('money'),
|
||||
'next_rent_date' => array('date')
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
'LeaseType',
|
||||
'Unit',
|
||||
@@ -30,109 +9,118 @@ class Lease extends AppModel {
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry',
|
||||
'StatementEntry',
|
||||
);
|
||||
|
||||
//var $default_log_level = array('log' => 30, 'show' => 30);
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: accountId
|
||||
* - Returns the accountId of the given lease
|
||||
*/
|
||||
function accountId($id) {
|
||||
$A = new Account();
|
||||
return $A->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);
|
||||
|
||||
$A = new Account();
|
||||
$entries = $A->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);
|
||||
|
||||
$A = new Account();
|
||||
$entries = $A->findLedgerEntries
|
||||
($A->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) {
|
||||
$A = new Account();
|
||||
return $A->findUnreconciledLedgerEntries
|
||||
($this->accountId($id), $fundamental_type, array('LedgerEntry.lease_id' => $id));
|
||||
function securityDepositBalance($id, $query = null) {
|
||||
$this->prEnter(compact('id', 'query'));
|
||||
$this->queryInit($query);
|
||||
|
||||
// REVISIT <AP>: 20090807
|
||||
// Let's try simplifying the security deposit issue.
|
||||
// Presume that security deposits are NOT used at all,
|
||||
// until the customer moves out of the unit. At that
|
||||
// time, the ENTIRE deposit is converted to customer
|
||||
// credit. Piece of cake.
|
||||
// For more information, see file revision history,
|
||||
// including the revision just before this, r503.
|
||||
|
||||
$this->id = $id;
|
||||
$moveout_date = $this->field('moveout_date');
|
||||
if (!empty($moveout_date))
|
||||
return $this->prReturn(0);
|
||||
|
||||
$query['conditions'][] = array('StatementEntry.lease_id' => $id);
|
||||
$query['conditions'][] = array('StatementEntry.account_id' =>
|
||||
$this->StatementEntry->Account->securityDepositAccountID());
|
||||
|
||||
$stats = $this->StatementEntry->stats(null, $query);
|
||||
return $this->prReturn($stats['Charge']['disbursement']);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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'));
|
||||
|
||||
function reconcileNewLedgerEntry($id, $fundamental_type, $amount) {
|
||||
$A = new Account();
|
||||
return $A->reconcileNewLedgerEntry
|
||||
($this->accountId($id), $fundamental_type, $amount, array('LedgerEntry.lease_id' => $id));
|
||||
$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'];
|
||||
|
||||
// Add receipt of the security deposit funds. Do NOT
|
||||
// flag them as part of the lease, as all received funds
|
||||
// are only associated with the customer, for future
|
||||
// (or present) disbursement on any lease.
|
||||
$result = $this->StatementEntry->Transaction->addReceipt
|
||||
($release, $customer_id, null);
|
||||
|
||||
return $this->prReturn($result);
|
||||
}
|
||||
|
||||
|
||||
@@ -148,42 +136,66 @@ class Lease extends AppModel {
|
||||
*/
|
||||
|
||||
function rentLastCharges($id) {
|
||||
$A = new Account();
|
||||
$this->prEnter(compact('id'));
|
||||
$rent_account_id = $this->StatementEntry->Account->rentAccountID();
|
||||
$entries = $this->find
|
||||
('all',
|
||||
array('link' =>
|
||||
array(// Models
|
||||
'LedgerEntry' => array
|
||||
('Ledger' => array
|
||||
('fields' => array(),
|
||||
'Account' => array
|
||||
('fields' => array(),
|
||||
'Ledger' => array
|
||||
('alias' => 'Lx',
|
||||
'fields' => array(),
|
||||
'LedgerEntry' => array
|
||||
('alias' => 'LEx',
|
||||
'StatementEntry',
|
||||
|
||||
'SEx' =>
|
||||
array('class' => 'StatementEntry',
|
||||
'fields' => array(),
|
||||
'conditions' => array
|
||||
('LEx.effective_date = DATE_ADD(LedgerEntry.through_date, INTERVAL 1 day)',
|
||||
'LEx.lease_id = LedgerEntry.lease_id',
|
||||
)
|
||||
),
|
||||
),
|
||||
('SEx.effective_date = DATE_ADD(StatementEntry.through_date, INTERVAL 1 day)',
|
||||
'SEx.lease_id = StatementEntry.lease_id',
|
||||
'SEx.reverse_transaction_id IS NULL',
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
//'fields' => array('id', 'amount', 'effective_date', 'through_date'),
|
||||
'fields' => array(),
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
array('Account.id' => $A->rentAccountID()),
|
||||
array('LEx.id' => null),
|
||||
array('StatementEntry.type' => 'CHARGE'),
|
||||
array('StatementEntry.account_id' => $rent_account_id),
|
||||
array('StatementEntry.reverse_transaction_id IS NULL'),
|
||||
array('SEx.id' => null),
|
||||
),
|
||||
)
|
||||
);
|
||||
return $entries;
|
||||
|
||||
return $this->prReturn($entries);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: lateCharges
|
||||
* - Returns a list of late charges from this lease
|
||||
*/
|
||||
|
||||
function lateCharges($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
$late_account_id = $this->StatementEntry->Account->lateChargeAccountID();
|
||||
$entries = $this->StatementEntry->find
|
||||
('all',
|
||||
array('link' =>
|
||||
array(// Models
|
||||
'Lease',
|
||||
),
|
||||
|
||||
//'fields' => array('id', 'amount', 'effective_date', 'through_date'),
|
||||
'conditions' => array(array('Lease.id' => $id),
|
||||
array('StatementEntry.type' => 'CHARGE'),
|
||||
array('StatementEntry.account_id' => $late_account_id),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
return $this->prReturn($entries);
|
||||
}
|
||||
|
||||
|
||||
@@ -195,10 +207,11 @@ class Lease extends AppModel {
|
||||
*/
|
||||
|
||||
function rentChargeGaps($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
$entries = $this->rentLastCharges($id);
|
||||
if ($entries && count($entries) > 1)
|
||||
return true;
|
||||
return false;
|
||||
return $this->prReturn(true);
|
||||
return $this->prReturn(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -214,12 +227,13 @@ class Lease extends AppModel {
|
||||
*/
|
||||
|
||||
function rentChargeThrough($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
$entries = $this->rentLastCharges($id);
|
||||
if (!$entries)
|
||||
return false;
|
||||
return $this->prReturn(false);
|
||||
if (count($entries) != 1)
|
||||
return null;
|
||||
return $entries[0]['LedgerEntry']['through_date'];
|
||||
return $this->prReturn(null);
|
||||
return $this->prReturn($entries[0]['StatementEntry']['through_date']);
|
||||
}
|
||||
|
||||
|
||||
@@ -231,64 +245,315 @@ class Lease extends AppModel {
|
||||
*/
|
||||
|
||||
function rentPaidThrough($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
$rent_account_id = $this->StatementEntry->Account->rentAccountID();
|
||||
|
||||
// Income / Receipt / Money
|
||||
// debit: A/R credit: Income <-- this entry
|
||||
// debit: Receipt credit: A/R <-- ReceiptLedgerEntry, below
|
||||
// debit: Money credit: Receipt <-- MoneyLedgerEntry, below
|
||||
|
||||
$query = array
|
||||
('link' => array
|
||||
(
|
||||
'CreditLedger' =>
|
||||
array('fields' => array(),
|
||||
'Account' =>
|
||||
array('fields' => array(),
|
||||
),
|
||||
// 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',
|
||||
),
|
||||
|
||||
// We're searching for the Receipt<->A/R entries,
|
||||
// which are debits on the A/R account. Find the
|
||||
// reconciling entries to that A/R debit.
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
array('alias' => 'ReceiptLedgerEntry',
|
||||
'fields' => array(),
|
||||
|
||||
// Finally, the Money (Cash/Check/etc) Entry is the one
|
||||
// which reconciles our ReceiptLedgerEntry debit
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
array('alias' => 'MoneyLedgerEntry',
|
||||
'linkalias' => 'MoneyLedgerEntryR',
|
||||
'fields' => array('SUM(COALESCE(MoneyLedgerEntryR.amount,0)) AS paid'),
|
||||
),
|
||||
),
|
||||
'conditions' =>
|
||||
array(array('StatementEntry.lease_id' => $id),
|
||||
array('StatementEntry.account_id' => $rent_account_id),
|
||||
array('StatementEntry.reverse_transaction_id IS NULL'),
|
||||
),
|
||||
|
||||
'fields' => array('LedgerEntry.amount',
|
||||
'DATE_SUB(LedgerEntry.effective_date, INTERVAL 1 DAY) AS paid_through',
|
||||
'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),
|
||||
array('StatementEntry.reverse_transaction_id IS NULL'),
|
||||
),
|
||||
|
||||
'group' => 'LedgerEntry.id HAVING paid <> LedgerEntry.amount',
|
||||
'order' => array('StatementEntry.through_date DESC'),
|
||||
),
|
||||
false);
|
||||
$this->pr(20, $rent, "Paid rent");
|
||||
|
||||
'conditions' => array(array('LedgerEntry.lease_id' => $id),
|
||||
array('Account.id' => $this->LedgerEntry->Ledger->Account->rentAccountID()),
|
||||
),
|
||||
'order' => array('LedgerEntry.effective_date',
|
||||
),
|
||||
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: assessMonthlyRent
|
||||
* - Charges rent for the month, if not already charged.
|
||||
*/
|
||||
|
||||
function assessMonthlyRent($id, $date = null) {
|
||||
$this->prEnter(compact('id', 'date'));
|
||||
$this->id = $id;
|
||||
|
||||
if (empty($date))
|
||||
$date = time();
|
||||
|
||||
if (is_string($date))
|
||||
$date = strtotime($date);
|
||||
|
||||
// REVISIT <AP>: 20090808
|
||||
// Anniversary Billing not supported
|
||||
$anniversary = 0 && $this->field('anniversary_billing');
|
||||
if (empty($anniversary)) {
|
||||
$date_parts = getdate($date);
|
||||
$date = mktime(0, 0, 0, $date_parts['mon'], 1, $date_parts['year']);
|
||||
}
|
||||
|
||||
// Make sure we're not trying to assess rent on a closed lease
|
||||
$close_date = $this->field('close_date');
|
||||
$this->pr(17, compact('close_date'));
|
||||
if (!empty($close_date))
|
||||
return $this->prReturn(null);
|
||||
|
||||
// Don't assess rent after customer has moved out
|
||||
$moveout_date = $this->field('moveout_date');
|
||||
$this->pr(17, compact('moveout_date'));
|
||||
if (!empty($moveout_date) && strtotime($moveout_date) < $date)
|
||||
return $this->prReturn(null);
|
||||
|
||||
// Determine when the customer has already been charged through
|
||||
// and, of course, don't charge them if they've already been.
|
||||
$charge_through_date = strtotime($this->rentChargeThrough($id));
|
||||
$this->pr(17, compact('date', 'charge_through_date')
|
||||
+ array('date_str' => date('Y-m-d', $date),
|
||||
'charge_through_date_str' => date('Y-m-d', $charge_through_date)));
|
||||
if ($charge_through_date >= $date)
|
||||
return $this->prReturn(null);
|
||||
|
||||
// OK, it seems we're going to go ahead and charge the customer
|
||||
// on this lease. Calculate the new charge through date, which
|
||||
// is 1 day shy of 1 month from $date. For example, if we're
|
||||
// charging for 8/1/09, charge through will be 8/31/09, and
|
||||
// charging for 8/15/09, charge through will be 9/14/09.
|
||||
$date_parts = getdate($date);
|
||||
$charge_through_date = mktime(0, 0, 0,
|
||||
$date_parts['mon']+1,
|
||||
$date_parts['mday']-1,
|
||||
$date_parts['year']);
|
||||
|
||||
// Build the invoice transaction
|
||||
$invoice = array('Transaction' => array(), 'Entry' => array());
|
||||
// REVISIT <AP>: 20090808
|
||||
// Keeping Transaction.stamp until the existing facility
|
||||
// is up to date. Then we want the stamp to be now()
|
||||
// (and so can just delete the next line).
|
||||
$invoice['Transaction']['stamp'] = date('Y-m-d', $date);
|
||||
$invoice['Entry'][] =
|
||||
array('effective_date' => date('Y-m-d', $date),
|
||||
'through_date' => date('Y-m-d', $charge_through_date),
|
||||
'amount' => $this->field('rent'),
|
||||
'account_id' => $this->StatementEntry->Account->rentAccountId(),
|
||||
);
|
||||
|
||||
$rent = $this->LedgerEntry->find('first', $query);
|
||||
if ($rent)
|
||||
return $rent[0]['paid_through'];
|
||||
// Record the invoice and return the result
|
||||
$this->pr(21, compact('invoice'));
|
||||
$result = $this->StatementEntry->Transaction->addInvoice
|
||||
($invoice, null, $id);
|
||||
return $this->prReturn($result);
|
||||
}
|
||||
|
||||
$query['fields'] = 'LedgerEntry.through_date';
|
||||
$query['order'] = 'LedgerEntry.through_date DESC';
|
||||
$query['group'] = 'LedgerEntry.id';
|
||||
$rent = $this->LedgerEntry->find('first', $query);
|
||||
if ($rent)
|
||||
return $rent['LedgerEntry']['through_date'];
|
||||
|
||||
return null;
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: assessMonthlyRentAll
|
||||
* - Ensures rent has been charged on all open leases
|
||||
*/
|
||||
|
||||
function assessMonthlyRentAll($date = null) {
|
||||
$this->prEnter(compact('date'));
|
||||
$leases = $this->find
|
||||
('all', array('contain' => false,
|
||||
'conditions' => array('Lease.close_date' => null),
|
||||
));
|
||||
|
||||
$ret = array('Lease' => array());
|
||||
foreach ($leases AS $lease) {
|
||||
$result = $this->assessMonthlyRent($lease['Lease']['id'], $date);
|
||||
$ret['Lease'][$lease['Lease']['id']] = $result;
|
||||
if ($result['error'])
|
||||
$ret['error'] = true;
|
||||
}
|
||||
return $this->prReturn($ret + array('error' => false));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: assessMonthlyLate
|
||||
* - Assess late charges for the month, if not already charged.
|
||||
*/
|
||||
|
||||
function assessMonthlyLate($id, $date = null) {
|
||||
$this->prEnter(compact('id', 'date'));
|
||||
$this->id = $id;
|
||||
|
||||
if (empty($date))
|
||||
$date = time();
|
||||
|
||||
if (is_string($date))
|
||||
$date = strtotime($date);
|
||||
|
||||
// REVISIT <AP>: 20090808
|
||||
// Anniversary Billing not supported
|
||||
$anniversary = 0 && $this->field('anniversary_billing');
|
||||
if (empty($anniversary)) {
|
||||
$date_parts = getdate($date);
|
||||
$date = mktime(0, 0, 0, $date_parts['mon'], 11, $date_parts['year']);
|
||||
}
|
||||
|
||||
// Don't assess a late charge if the late charge date hasn't
|
||||
// even come yet. This is questionable whether we really
|
||||
// should restrict, since the user could know what they're
|
||||
// doing, and/or the server clock could be off (although that
|
||||
// would certainly have much larger ramifications). But, the
|
||||
// fact is that this check likely handles the vast majority
|
||||
// of the expected behavior, and presents an issue for very
|
||||
// few users, if any at all.
|
||||
if ($date > time())
|
||||
return $this->prReturn(null);
|
||||
|
||||
// Make sure we're not trying to assess late charges on a closed lease
|
||||
$close_date = $this->field('close_date');
|
||||
$this->pr(17, compact('close_date'));
|
||||
if (!empty($close_date))
|
||||
return $this->prReturn(null);
|
||||
|
||||
// Don't assess late charges after customer has moved out
|
||||
$moveout_date = $this->field('moveout_date');
|
||||
$this->pr(17, compact('moveout_date'));
|
||||
if (!empty($moveout_date) && strtotime($moveout_date) < $date)
|
||||
return $this->prReturn(null);
|
||||
|
||||
// Determine when the customer has been charged through for rent
|
||||
// and don't mark them as late if they haven't even been charged rent
|
||||
$charge_through_date = strtotime($this->rentChargeThrough($id));
|
||||
$this->pr(17, compact('date', 'charge_through_date')
|
||||
+ array('date_str' => date('Y-m-d', $date),
|
||||
'charge_through_date_str' => date('Y-m-d', $charge_through_date)));
|
||||
if ($charge_through_date <= $date)
|
||||
return $this->prReturn(null);
|
||||
|
||||
// Determine if the customer is actually late. This is based on
|
||||
// when they've paid through, plus 10 days before they're late.
|
||||
// REVISIT <AP>: 20090813
|
||||
// Of course, 10 days is a terrible hardcode. This should be
|
||||
// driven from the late schedule, saved as part of the lease
|
||||
// (when finally implemented).
|
||||
$paid_through_date = strtotime($this->rentPaidThrough($id));
|
||||
$this->pr(17, compact('date', 'paid_through_date')
|
||||
+ array('date_str' => date('Y-m-d', $date),
|
||||
'paid_through_date_str' => date('Y-m-d', $paid_through_date)));
|
||||
$date_parts = getdate($paid_through_date);
|
||||
$paid_through_date = mktime(0, 0, 0, $date_parts['mon'], $date_parts['mday']+10, $date_parts['year']);
|
||||
if ($paid_through_date >= $date)
|
||||
return $this->prReturn(null);
|
||||
|
||||
// Determine if the customer has already been charged a late fee
|
||||
// and, of course, don't charge them if they've already been.
|
||||
$late_charges = $this->lateCharges($id);
|
||||
foreach ($late_charges AS $late) {
|
||||
if (strtotime($late['StatementEntry']['effective_date']) == $date)
|
||||
return $this->prReturn(null);
|
||||
}
|
||||
|
||||
// Build the invoice transaction
|
||||
$invoice = array('Transaction' => array(), 'Entry' => array());
|
||||
// REVISIT <AP>: 20090808
|
||||
// Keeping Transaction.stamp until the existing facility
|
||||
// is up to date. Then we want the stamp to be now()
|
||||
// (and so can just delete the next line).
|
||||
$invoice['Transaction']['stamp'] = date('Y-m-d', $date);
|
||||
$invoice['Entry'][] =
|
||||
array('effective_date' => date('Y-m-d', $date),
|
||||
'amount' => 10,
|
||||
'account_id' => $this->StatementEntry->Account->lateChargeAccountId(),
|
||||
);
|
||||
|
||||
// Record the invoice and return the result
|
||||
$this->pr(21, compact('invoice'));
|
||||
$result = $this->StatementEntry->Transaction->addInvoice
|
||||
($invoice, null, $id);
|
||||
return $this->prReturn($result);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: assessMonthlyLateAll
|
||||
* - Ensures rent has been charged on all open leases
|
||||
*/
|
||||
|
||||
function assessMonthlyLateAll($date = null) {
|
||||
$this->prEnter(compact('date'));
|
||||
$leases = $this->find
|
||||
('all', array('contain' => false,
|
||||
'conditions' => array('Lease.close_date' => null),
|
||||
));
|
||||
|
||||
$ret = array('Lease' => array());
|
||||
foreach ($leases AS $lease) {
|
||||
$result = $this->assessMonthlyLate($lease['Lease']['id'], $date);
|
||||
$ret['Lease'][$lease['Lease']['id']] = $result;
|
||||
if ($result['error'])
|
||||
$ret['error'] = true;
|
||||
}
|
||||
return $this->prReturn($ret + array('error' => false));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* functions: delinquency
|
||||
* - SQL fragments to determine whether a lease is delinquent
|
||||
*/
|
||||
|
||||
function conditionDelinquent($table_name = 'Lease') {
|
||||
if (empty($table_name)) $t = ''; else $t = $table_name . '.';
|
||||
return ("({$t}close_date IS NULL AND" .
|
||||
" NOW() > DATE_ADD({$t}paid_through_date, INTERVAL 10 DAY))");
|
||||
}
|
||||
|
||||
function delinquentDaysSQL($table_name = 'Lease') {
|
||||
if (empty($table_name)) $t = ''; else $t = $table_name . '.';
|
||||
return ("IF(" . $this->conditionDelinquent($table_name) . "," .
|
||||
" DATEDIFF(NOW(), {$t}paid_through_date)-1," .
|
||||
" NULL)");
|
||||
}
|
||||
|
||||
function delinquentField($table_name = 'Lease') {
|
||||
return ($this->delinquentDaysSQL($table_name) . " AS 'delinquent'");
|
||||
}
|
||||
|
||||
|
||||
@@ -301,7 +566,10 @@ class Lease extends AppModel {
|
||||
|
||||
function moveIn($customer_id, $unit_id,
|
||||
$deposit = null, $rent = null,
|
||||
$stamp = null, $comment = null) {
|
||||
$stamp = null, $comment = null)
|
||||
{
|
||||
$this->prEnter(compact('customer_id', 'unit_id',
|
||||
'deposit', 'rent', 'stamp', 'comment'));
|
||||
|
||||
$lt = $this->LeaseType->find('first',
|
||||
array('conditions' =>
|
||||
@@ -355,13 +623,16 @@ class Lease extends AppModel {
|
||||
'deposit' => $deposit,
|
||||
'rent' => $rent,
|
||||
'comment' => $comment), false)) {
|
||||
return null;
|
||||
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');
|
||||
|
||||
@@ -372,7 +643,7 @@ class Lease extends AppModel {
|
||||
// was waived, pro-rated, etc.
|
||||
|
||||
// Return the new lease ID
|
||||
return $this->id;
|
||||
return $this->prReturn($this->id);
|
||||
}
|
||||
|
||||
|
||||
@@ -384,7 +655,10 @@ class Lease extends AppModel {
|
||||
*/
|
||||
|
||||
function moveOut($id, $status = 'VACANT',
|
||||
$stamp = null, $close = false) {
|
||||
$stamp = null, $close = true)
|
||||
{
|
||||
$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');
|
||||
@@ -399,10 +673,16 @@ class Lease extends AppModel {
|
||||
// Save it!
|
||||
$this->save($this->data, false);
|
||||
|
||||
// Release the security deposit(s)
|
||||
$this->releaseSecurityDeposits($id, $stamp);
|
||||
|
||||
// Close the lease, if so requested
|
||||
if ($close)
|
||||
$this->close($id, $stamp);
|
||||
|
||||
// Update the current lease count for the customer
|
||||
$this->Customer->updateLeaseCount($this->field('customer_id'));
|
||||
|
||||
// Finally, update the unit status
|
||||
$this->recursive = -1;
|
||||
$this->read();
|
||||
@@ -418,8 +698,10 @@ class Lease extends AppModel {
|
||||
*/
|
||||
|
||||
function close($id, $stamp = null) {
|
||||
$this->prEnter(compact('id', 'stamp'));
|
||||
|
||||
if (!$this->closeable($id))
|
||||
return false;
|
||||
return $this->prReturn(false);
|
||||
|
||||
// Reset the data
|
||||
$this->create();
|
||||
@@ -434,7 +716,11 @@ class Lease extends AppModel {
|
||||
|
||||
// Save it!
|
||||
$this->save($this->data, false);
|
||||
return true;
|
||||
|
||||
// Update the current lease count for the customer
|
||||
$this->Customer->updateLeaseCount($this->field('customer_id'));
|
||||
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -446,39 +732,94 @@ class Lease extends AppModel {
|
||||
*/
|
||||
|
||||
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 false;
|
||||
return $this->prReturn(false);
|
||||
|
||||
// We can't close a lease that's already closed
|
||||
if (isset($this->data['Lease']['close_date']))
|
||||
return false;
|
||||
|
||||
$deposits = $this->findSecurityDeposits($id);
|
||||
$stats = $this->stats($id);
|
||||
return $this->prReturn(false);
|
||||
|
||||
// A lease can only be closed if there are no outstanding
|
||||
// security deposits, and if the account balance is zero.
|
||||
if ($deposits['summary']['balance'] != 0 || $stats['balance'] != 0)
|
||||
return false;
|
||||
// security deposits ...
|
||||
if ($this->securityDepositBalance($id) != 0)
|
||||
return $this->prReturn(false);
|
||||
|
||||
// ... and if the account balance is zero.
|
||||
if ($this->balance($id) != 0)
|
||||
return $this->prReturn(false);
|
||||
|
||||
// Apparently this lease meets all the criteria!
|
||||
return true;
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addCharge
|
||||
* - Adds an additional charge to the lease
|
||||
* function: refund
|
||||
* - Marks any lease balance as payable to the customer.
|
||||
*/
|
||||
|
||||
function addCharge($id, $charge) {
|
||||
function refund($id, $stamp = null) {
|
||||
$this->prEnter(compact('id'));
|
||||
$balance = $this->balance($id);
|
||||
|
||||
if ($balance >= 0)
|
||||
return $this->prReturn(array('error' => true));
|
||||
|
||||
$balance *= -1;
|
||||
|
||||
// Build a transaction
|
||||
$refund = array('Transaction' => array(), 'Entry' => array());
|
||||
$refund['Transaction']['stamp'] = $stamp;
|
||||
$refund['Transaction']['comment'] = "Lease Refund";
|
||||
|
||||
$refund['Entry'][] =
|
||||
array('amount' => $balance);
|
||||
|
||||
$result = $this->StatementEntry->Transaction->addRefund
|
||||
($refund, null, $id);
|
||||
|
||||
return $this->prReturn($result);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: update
|
||||
* - Update any cached or calculated fields
|
||||
*/
|
||||
function update($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
|
||||
$this->id = $id;
|
||||
$this->saveField('charge_through_date', $this->rentChargeThrough($id));
|
||||
$this->saveField('paid_through_date', $this->rentPaidThrough($id));
|
||||
|
||||
$moveout = $this->field('moveout_date');
|
||||
if (empty($moveout))
|
||||
$this->Unit->update($this->field('unit_id'));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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']);
|
||||
}
|
||||
|
||||
|
||||
@@ -489,17 +830,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);
|
||||
|
||||
$A = new Account();
|
||||
$stats = $A->stats($A->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,48 +1,15 @@
|
||||
<?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'),
|
||||
'Close',
|
||||
'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',
|
||||
);
|
||||
|
||||
|
||||
@@ -55,10 +22,11 @@ class Ledger extends AppModel {
|
||||
function accountID($id) {
|
||||
$this->cacheQueries = true;
|
||||
$item = $this->find('first', array
|
||||
('contain' => 'Account.id',
|
||||
('link' => array('Account'),
|
||||
'conditions' => array('Ledger.id' => $id),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
//pr(compact('id', 'item'));
|
||||
return $item['Account']['id'];
|
||||
}
|
||||
|
||||
@@ -80,120 +48,86 @@ class Ledger extends AppModel {
|
||||
* function: closeLedger
|
||||
* - Closes the current ledger, and returns a fresh one
|
||||
*/
|
||||
function closeLedger($id, $close_id) {
|
||||
$this->recursive = -1;
|
||||
|
||||
$stamp = date('Y-m-d G:i:s');
|
||||
$this->id = $id;
|
||||
$this->read();
|
||||
$this->data['Ledger']['close_id'] = $close_id;
|
||||
$this->save($this->data, false);
|
||||
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_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;
|
||||
$this->save($this->data, false);
|
||||
//pr($this->data);
|
||||
if (!$this->save($this->data, false))
|
||||
return array('error' => true, 'new_ledger_data' => $this->data) + $ret;
|
||||
$ret['new_ledger_ids'][] = $this->id;
|
||||
|
||||
if ($stats['balance'] == 0)
|
||||
return $this->id;
|
||||
|
||||
$this->read();
|
||||
$ftype = $this->Account->fundamentalType($this->data['Ledger']['account_id']);
|
||||
$otype = $this->Account->fundamentalOpposite($ftype);
|
||||
|
||||
// Create a transaction for balance transfer
|
||||
$transaction = new Transaction();
|
||||
$transaction->create();
|
||||
if (!$transaction->save(array(), false)) {
|
||||
return null;
|
||||
$entries[] = array('old_ledger_id' => $id,
|
||||
'new_ledger_id' => $this->id,
|
||||
'amount' => $stats['balance']);
|
||||
}
|
||||
|
||||
// Create an entry to carry the balance forward
|
||||
$carry_entry_data = array
|
||||
($ftype.'_ledger_id' => $this->id,
|
||||
$otype.'_ledger_id' => $id,
|
||||
'transaction_id' => $transaction->id,
|
||||
'amount' => $stats['balance'],
|
||||
'comment' => "Ledger Balance Forward",
|
||||
);
|
||||
// Perform the close
|
||||
$result = $this->Transaction->addClose(array('Transaction' => array(),
|
||||
'Ledger' => $entries));
|
||||
$ret['Transaction'] = $result;
|
||||
if ($result['error'])
|
||||
return array('error' => true) + $ret;
|
||||
|
||||
$carry_entry = new LedgerEntry();
|
||||
$carry_entry->create();
|
||||
if (!$carry_entry->save($carry_entry_data, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->id;
|
||||
return $ret + array('error' => false);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findLedgerEntries
|
||||
* - Returns an array of ledger entries that belong to a given
|
||||
* ledger. There is extra work done... see the LedgerEntry model.
|
||||
* 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 findLedgerEntries($id, $account_type = null, $cond = null, $link = null) {
|
||||
/* pr(array('function' => 'Ledger::findLedgerEntries', */
|
||||
/* 'args' => compact('id', 'account_type', 'cond', 'link'), */
|
||||
/* )); */
|
||||
|
||||
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 debitCreditFields($sum = false, $balance = true,
|
||||
$entry_name = 'LedgerEntry', $account_name = 'Account') {
|
||||
return $this->LedgerEntry->debitCreditFields
|
||||
($sum, $balance, $entry_name, $account_name);
|
||||
}
|
||||
|
||||
// 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.
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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;
|
||||
|
||||
$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),
|
||||
$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),
|
||||
));
|
||||
}
|
||||
|
||||
$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'), */
|
||||
/* )); */
|
||||
//pr(compact('entries'));
|
||||
return $entries;
|
||||
}
|
||||
|
||||
@@ -204,41 +138,36 @@ 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).
|
||||
$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;
|
||||
|
||||
@@ -1,525 +1,177 @@
|
||||
<?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 $hasOne = array(
|
||||
'Tender' => array(
|
||||
'dependent' => true,
|
||||
),
|
||||
'DebitDoubleEntry' => array(
|
||||
'className' => 'DoubleEntry',
|
||||
'foreignKey' => 'debit_entry_id',
|
||||
'dependent' => true,
|
||||
),
|
||||
'CreditDoubleEntry' => array(
|
||||
'className' => 'DoubleEntry',
|
||||
'foreignKey' => 'credit_entry_id',
|
||||
'dependent' => true,
|
||||
),
|
||||
'DoubleEntry' => array(
|
||||
'foreignKey' => false,
|
||||
),
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'DebitReconciliation' => array(
|
||||
'className' => 'Reconciliation',
|
||||
'foreignKey' => 'debit_ledger_entry_id',
|
||||
),
|
||||
'CreditReconciliation' => array(
|
||||
'className' => 'Reconciliation',
|
||||
'foreignKey' => 'credit_ledger_entry_id',
|
||||
),
|
||||
);
|
||||
var $belongsTo = array(
|
||||
'MonetarySource',
|
||||
'Transaction',
|
||||
'Customer',
|
||||
'Lease',
|
||||
|
||||
'DebitLedger' => array(
|
||||
'className' => 'Ledger',
|
||||
'foreignKey' => 'debit_ledger_id',
|
||||
),
|
||||
'CreditLedger' => array(
|
||||
'className' => 'Ledger',
|
||||
'foreignKey' => 'credit_ledger_id',
|
||||
),
|
||||
|
||||
'Ledger' => array(
|
||||
'foreignKey' => false,
|
||||
// conditions will be used when JOINing tables
|
||||
// (such as find with LinkableBehavior)
|
||||
'conditions' => array('OR' =>
|
||||
array('%{MODEL_ALIAS}.debit_ledger_id = Ledger.id',
|
||||
'%{MODEL_ALIAS}.credit_ledger_id = Ledger.id')),
|
||||
|
||||
// finderQuery will be used when tables are put
|
||||
// together across several querys, not with JOIN.
|
||||
// (such as find with ContainableBehavior)
|
||||
'finderQuery' => 'NOT-IMPLEMENTED',
|
||||
|
||||
'counterQuery' => ''
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
var $hasAndBelongsToMany = array(
|
||||
'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',
|
||||
),
|
||||
// STUPID CakePHP bug screws up when using Containable
|
||||
// and CLASS contains CLASS. This extra HABTM give the
|
||||
// option of multiple depths on one CLASS, since there
|
||||
// isn't an alias specification for Containable.
|
||||
'DebitReconciliationLedgerEntry2' => array(
|
||||
|
||||
// The Credit half of the double entry matching THIS Debit (if it is one)
|
||||
'CreditEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
'joinTable' => 'reconciliations',
|
||||
'foreignKey' => 'credit_ledger_entry_id',
|
||||
'associationForeignKey' => 'debit_ledger_entry_id',
|
||||
),
|
||||
'CreditReconciliationLedgerEntry' => 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 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: 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 ledgerContextFields($ledger_id = null, $account_type = null) {
|
||||
$fields = array('id', 'effective_date', 'through_date',
|
||||
'lease_id', 'customer_id', 'comment', 'amount');
|
||||
|
||||
if (isset($ledger_id)) {
|
||||
$fields[] = ("IF(LedgerEntry.debit_ledger_id = $ledger_id," .
|
||||
" LedgerEntry.amount, NULL) AS debit");
|
||||
$fields[] = ("IF(LedgerEntry.credit_ledger_id = $ledger_id," .
|
||||
" LedgerEntry.amount, NULL) AS credit");
|
||||
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($account_type)) {
|
||||
if (in_array($account_type, array('ASSET', 'EXPENSE')))
|
||||
$ledger_type = 'debit';
|
||||
else
|
||||
$ledger_type = 'credit';
|
||||
($sum ? 'SUM(' : '') .
|
||||
"IF({$entry_name}.crdr = 'CREDIT'," .
|
||||
" {$entry_name}.amount, NULL)" .
|
||||
($sum ? ')' : '') . ' AS credit' . ($sum ? 's' : ''),
|
||||
);
|
||||
|
||||
$fields[] = ("(IF(LedgerEntry.{$ledger_type}_ledger_id = $ledger_id," .
|
||||
" 1, -1) * LedgerEntry.amount) AS balance");
|
||||
}
|
||||
}
|
||||
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';
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
function ledgerContextFields2($ledger_id = null, $account_id = null, $account_type = null) {
|
||||
$fields = array('id', 'effective_date', 'through_date', '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();
|
||||
|
||||
$fields = $this->ledgerContextFields($ledger_id, $account_type);
|
||||
$cond[] = $this->ledgerContextConditions($ledger_id, $account_type);
|
||||
$order = array('Transaction.stamp');
|
||||
|
||||
$entries = $this->find
|
||||
('all',
|
||||
array('link' => $link,
|
||||
'fields' => $fields,
|
||||
'conditions' => $cond,
|
||||
'order' => $order,
|
||||
));
|
||||
|
||||
return $entries;
|
||||
if (empty($entry['account_id']) ||
|
||||
empty($entry['crdr']) ||
|
||||
empty($entry['amount'])
|
||||
) {
|
||||
/* pr(array("LedgerEntry::verifyLedgerEntry()" */
|
||||
/* => "Entry verification failed")); */
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: findReconciledLedgerEntries
|
||||
* - Returns ledger entries that are reconciled to the given entry.
|
||||
* (such as payments towards a charge).
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return $reconciled;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reverse
|
||||
* - Reverses the ledger entry
|
||||
*/
|
||||
|
||||
function reverse1($id, $amount = null, $transaction_id = null, $rec_id = null) {
|
||||
/* pr(array('LedgerEntry::reverse', */
|
||||
/* compact('id', 'amount', 'transaction_id', 'rec_id'))); */
|
||||
|
||||
// Get the LedgerEntry and related fields
|
||||
$entry = $this->find
|
||||
('first',
|
||||
array('contain' => array('MonetarySource.id',
|
||||
'Transaction.id',
|
||||
'DebitLedger.id',
|
||||
'DebitLedger.account_id',
|
||||
'CreditLedger.id',
|
||||
'CreditLedger.account_id',
|
||||
'DebitReconciliationLedgerEntry'
|
||||
/* => */
|
||||
/* array('DebitLedger.id', */
|
||||
/* 'DebitLedger.account_id', */
|
||||
/* 'CreditLedger.id', */
|
||||
/* 'CreditLedger.account_id', */
|
||||
/* ) */
|
||||
,
|
||||
'CreditReconciliationLedgerEntry'
|
||||
/* => */
|
||||
/* array('DebitLedger.id', */
|
||||
/* 'DebitLedger.account_id', */
|
||||
/* 'CreditLedger.id', */
|
||||
/* 'CreditLedger.account_id', */
|
||||
/* ) */
|
||||
,
|
||||
'Customer.id',
|
||||
'Lease.id',
|
||||
),
|
||||
|
||||
'fields' => array('LedgerEntry.*'),
|
||||
|
||||
'conditions' => array(array('LedgerEntry.id' => $id),
|
||||
/* array('NOT' => */
|
||||
/* array('OR' => */
|
||||
/* array(array('DebitReconciliationLedgerEntry.id' => $rec_id), */
|
||||
/* array('CreditReconciliationLedgerEntry.id' => $rec_id), */
|
||||
/* ), */
|
||||
/* ), */
|
||||
/* ), */
|
||||
),
|
||||
));
|
||||
//pr($entry);
|
||||
|
||||
if (!isset($amount))
|
||||
$amount = $entry['LedgerEntry']['amount'];
|
||||
|
||||
$A = new Account();
|
||||
|
||||
$ids = $this->Ledger->Account->postLedgerEntry
|
||||
(array('transaction_id' => $transaction_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($entry['CreditLedger']['account_id']),
|
||||
'credit_ledger_id' => $A->currentLedgerID($entry['DebitLedger']['account_id']),
|
||||
'effective_date' => $entry['LedgerEntry']['effective_date'],
|
||||
//'effective_date' => $entry['LedgerEntry']['effective_date'],
|
||||
'amount' => $amount,
|
||||
'lease_id' => $entry['Lease']['id'],
|
||||
'customer_id' => $entry['Customer']['id'],
|
||||
'comment' => "Reversal of Ledger Entry #{$id}",
|
||||
),
|
||||
array('debit' => array(array('LedgerEntry' => array('id' => $entry['LedgerEntry']['id'],
|
||||
'amount' => $amount,
|
||||
))),
|
||||
'credit' => array(array('LedgerEntry' => array('id' => $entry['LedgerEntry']['id'],
|
||||
'amount' => $amount,
|
||||
))),
|
||||
));
|
||||
|
||||
if ($ids['error'])
|
||||
return null;
|
||||
|
||||
$tid = $ids['transaction_id'];
|
||||
|
||||
pr(compact('entry'));
|
||||
|
||||
foreach (array('Debit', 'Credit') AS $dc_type) {
|
||||
foreach ($entry[$dc_type . 'ReconciliationLedgerEntry'] AS $RLE) {
|
||||
pr(array('checkpoint' => "Reverse $dc_type LE",
|
||||
compact('id', 'rec_id', 'RLE')));
|
||||
if ($RLE['id'] == $rec_id) {
|
||||
pr(array('checkpoint' => "Skipping Reverse $dc_type LE, due to rec_id",
|
||||
compact('id', 'RLE')));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->reverse($RLE['id'], $RLE['Reconciliation']['amount'], $tid, $id))
|
||||
$ids['error'] = true;
|
||||
|
||||
/* $rids = $this->Ledger->Account->postLedgerEntry */
|
||||
/* (array('transaction_id' => $tid), */
|
||||
/* null, */
|
||||
/* array('debit_ledger_id' => $A->currentLedgerID($RLE['CreditLedger']['account_id']), */
|
||||
/* 'credit_ledger_id' => $A->currentLedgerID($RLE['DebitLedger']['account_id']), */
|
||||
/* 'effective_date' => $RLE['effective_date'], */
|
||||
/* //'effective_date' => $RLE['effective_date'], */
|
||||
/* 'amount' => $RLE['Reconciliation']['amount'], */
|
||||
/* 'lease_id' => $entry['Lease']['id'], */
|
||||
/* 'customer_id' => $entry['Customer']['id'], */
|
||||
/* 'comment' => "Reversal of Ledger Entry #{$RLE['id']}", */
|
||||
/* ), */
|
||||
/* array('debit' => array(array('LedgerEntry' => array('id' => $RLE['id'], */
|
||||
/* 'amount' => $RLE['Reconciliation']['amount'], */
|
||||
/* ))), */
|
||||
/* 'credit' => array(array('LedgerEntry' => array('id' => $RLE['id'], */
|
||||
/* 'amount' => $RLE['Reconciliation']['amount'], */
|
||||
/* ))), */
|
||||
/* )); */
|
||||
|
||||
/* if ($rids['error']) */
|
||||
/* $ids['error'] = true; */
|
||||
}
|
||||
}
|
||||
|
||||
if ($ids['error'])
|
||||
return null;
|
||||
|
||||
return $ids['id'];
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reverse
|
||||
* - Reverses the charges
|
||||
*
|
||||
* SAMPLE MOVE IN w/ PRE PAYMENT
|
||||
* DEPOSIT RENT A/R RECEIPT CHECK PETTY BANK
|
||||
* ------- ------- ------- ------- ------- ------- -------
|
||||
* |25 | 25| | | | |
|
||||
* | |20 20| | | | |
|
||||
* | |20 20| | | | |
|
||||
* | |20 20| | | | |
|
||||
* | | |25 25| | | |
|
||||
* | | |20 20| | | |
|
||||
* | | |20 20| | | |
|
||||
* | | |20 20| | | |
|
||||
* | | | |85 85| | |
|
||||
* | | | | |85 | 85|
|
||||
|
||||
* MOVE OUT and REFUND FINAL MONTH
|
||||
* DEPOSIT RENT C/P RECEIPT CHECK PETTY BANK
|
||||
* ------- ------- ------- ------- ------- ------- -------
|
||||
* 25| | |25 | | | | t20 e20a
|
||||
* | 20| |20 | | | | t20 e20b
|
||||
|
||||
* -ONE REFUND CHECK-
|
||||
* | | 25| |25 | | | t30 e30a
|
||||
* | | 20| |20 | | | t30 e30b
|
||||
* | | | 45| | | |45 t40 e40
|
||||
* -OR MULTIPLE-
|
||||
* | | 15| |15 | | | t50a e50a
|
||||
* | | | 15| | |15 | t60a e60a
|
||||
* | | 30| |30 | | | t50b e50b
|
||||
* | | | 30| | | |30 t60b e60b
|
||||
* | | | | | | |
|
||||
|
||||
|
||||
OPTION 1
|
||||
* |-25 | -25| | | | |
|
||||
* | |-20 -20| | | | |
|
||||
* | | |-25 -25| | | |
|
||||
* | | |-20 -20| | | |
|
||||
|
||||
OPTION 2
|
||||
* |-25 | | -25| | | |
|
||||
* | |-20 | -20| | | |
|
||||
* | | | |-15 | -15| |
|
||||
* | | | |-30 | | -30|
|
||||
* | | | | | | |
|
||||
*
|
||||
*/
|
||||
function reverse($ledger_entries, $stamp = null) {
|
||||
pr(array('LedgerEntry::reverse',
|
||||
compact('ledger_entries', 'stamp')));
|
||||
|
||||
// If the user only wants to reverse one ID, we'll allow it
|
||||
if (!is_array($ledger_entries))
|
||||
$ledger_entries = $this->find
|
||||
('all', array
|
||||
('contain' => false,
|
||||
'conditions' => array('LedgerEntry.id' => $ledger_entries)));
|
||||
|
||||
$A = new Account();
|
||||
|
||||
$ar_account_id = $A->accountReceivableAccountID();
|
||||
$receipt_account_id = $A->receiptAccountID();
|
||||
|
||||
$transaction_id = null;
|
||||
foreach ($ledger_entries AS $entry) {
|
||||
$entry = $entry['LedgerEntry'];
|
||||
$amount = -1*$entry['amount'];
|
||||
|
||||
if (isset($entry['credit_account_id']))
|
||||
$refund_account_id = $entry['credit_account_id'];
|
||||
elseif (isset($entry['CreditLedger']['Account']['id']))
|
||||
$refund_account_id = $entry['CreditLedger']['Account']['id'];
|
||||
elseif (isset($entry['credit_ledger_id']))
|
||||
$refund_account_id = $this->Ledger->accountID($entry['credit_ledger_id']);
|
||||
else
|
||||
return null;
|
||||
|
||||
// post new refund in the income account
|
||||
$ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $transaction_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($ar_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($refund_account_id),
|
||||
'effective_date' => $entry['effective_date'],
|
||||
'through_date' => $entry['through_date'],
|
||||
'amount' => $amount,
|
||||
'lease_id' => $entry['lease_id'],
|
||||
'customer_id' => $entry['customer_id'],
|
||||
'comment' => "Refund; Entry #{$entry['id']}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $entry['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
);
|
||||
|
||||
if ($ids['error'])
|
||||
return null;
|
||||
$transaction_id = $ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Refund Ledger Entry',
|
||||
compact('ids', 'amount', 'refund_account_id', 'ar_account_id')));
|
||||
if (isset($tender) && !$this->Tender->verifyTender($tender)) {
|
||||
/* pr(array("LedgerEntry::verifyLedgerEntry()" */
|
||||
/* => "Tender verification failed")); */
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: addLedgerEntry
|
||||
* - Inserts new Ledger Entry into the database
|
||||
*/
|
||||
function addLedgerEntry($entry, $tender = null) {
|
||||
//$this->prFunctionLevel(16);
|
||||
$this->prEnter(compact('entry', 'tender'));
|
||||
|
||||
$ret = array('data' => $entry);
|
||||
if (!$this->verifyLedgerEntry($entry, $tender))
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
|
||||
if (empty($entry['ledger_id']))
|
||||
$entry['ledger_id'] =
|
||||
$this->Account->currentLedgerID($entry['account_id']);
|
||||
|
||||
$this->create();
|
||||
if (!$this->save($entry))
|
||||
return $this->prReturn(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 $this->prReturn(array('error' => true) + $ret);
|
||||
}
|
||||
|
||||
return $this->prReturn($ret + array('error' => false));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: stats
|
||||
* - Returns summary data from the requested ledger entry
|
||||
*/
|
||||
function stats($id) {
|
||||
function stats($id = null, $query = null, $set = null) {
|
||||
$this->queryInit($query);
|
||||
|
||||
$query = array
|
||||
(
|
||||
'fields' => array("SUM(Reconciliation.amount) AS 'reconciled'"),
|
||||
|
||||
'conditions' => array(isset($cond) ? $cond : array(),
|
||||
array('LedgerEntry.id' => $id)),
|
||||
|
||||
'group' => 'LedgerEntry.id',
|
||||
);
|
||||
|
||||
// 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'];
|
||||
|
||||
// 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'];
|
||||
|
||||
return $stats;
|
||||
// REVISIT <AP>: 20090816
|
||||
// This function appeared to be dramatically broken,
|
||||
// a throwback to an earlier time. I deleted its
|
||||
// contents and added this error to ensure it does
|
||||
// not get used.
|
||||
$this->INTERNAL_ERROR('This function should not be used');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
class MapsUnit extends AppModel {
|
||||
|
||||
var $name = 'MapsUnit';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'map_id' => array('numeric'),
|
||||
'unit_id' => array('numeric'),
|
||||
'pt_top' => array('numeric'),
|
||||
'pt_left' => array('numeric'),
|
||||
'transpose' => array('boolean')
|
||||
);
|
||||
|
||||
}
|
||||
?>
|
||||
37
site/models/membership.php
Normal file
37
site/models/membership.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
class Membership extends AppModel {
|
||||
|
||||
var $belongsTo =
|
||||
array('User',
|
||||
'Site',
|
||||
'Group'
|
||||
);
|
||||
|
||||
function memberGroups($user_id, $site_id) {
|
||||
$this->prEnter(compact('user_id', 'site_id'));
|
||||
|
||||
$this->cacheQueries = true;
|
||||
$groups = $this->find('all', array
|
||||
('recursive' => -1,
|
||||
'fields' => array('group_id'),
|
||||
'conditions' => array(array('user_id' => $user_id),
|
||||
array('site_id' => $site_id)),
|
||||
));
|
||||
$this->cacheQueries = false;
|
||||
|
||||
if (empty($groups))
|
||||
return $this->prReturn(null);
|
||||
|
||||
$group_ids = array();
|
||||
foreach ($groups AS $group)
|
||||
$group_ids[] = $group['Membership']['group_id'];
|
||||
|
||||
return $this->prReturn($group_ids);
|
||||
}
|
||||
|
||||
function memberOf($user_id, $site_id) {
|
||||
$groups = $this->memberGroups($user_id, $site_id);
|
||||
return (!empty($groups));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,268 +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(
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'LedgerEntry',
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: nsf
|
||||
* - Flags the ledger entry as having insufficient funds
|
||||
* - NOTE: nsf only works if given the monetary source id
|
||||
* to transaction e3, below
|
||||
* - NOTE: In order to show that the rent formerly considered
|
||||
* "collected" is now recognized in reverse, we must
|
||||
* credit A/R with a negative amount in order to
|
||||
* reconcile it against the Rent<->A/R ledger entry.
|
||||
*
|
||||
* FEE RENT A/R RECEIPT CHECK NSF BANK
|
||||
* ------- ------- ------- ------- ------- ------- -------
|
||||
* | |30 30| | | | | t1 e1a : R e2/e7a :
|
||||
* | |20 20| | | | | t1 e1b : R e2/e7b :
|
||||
* | | | | | | |
|
||||
* | | |30 30| | | | t2 e2a : R e3 : R e1a
|
||||
* | | |20 20| | | | t2 e2b : R e3 : R e1b
|
||||
* | | | |50 50| | | t2 e3 : R e4 : R e2
|
||||
* | | | | | | |
|
||||
* | | | | |50 | 50| t3 e4 : : R e3
|
||||
* | | | | | | |
|
||||
* | | | | | |-50 -50| t4 e5 : : R e6
|
||||
* | | | |-50 | -50| | t5 e6 : R e5 : R e7a/e7b
|
||||
* | | |-30 -30| | | | t6 e7a : R e6 : R e1a
|
||||
* | | |-20 -20| | | | t6 e7b : R e6 : R e1b
|
||||
* |35 | 35| | | | | t6 e8
|
||||
*
|
||||
*/
|
||||
|
||||
function nsf($id, $stamp = null) {
|
||||
pr(array('MonetarySource::nsf',
|
||||
compact('id')));
|
||||
|
||||
$A = new Account();
|
||||
|
||||
// Get the LedgerEntries that use this monetary source
|
||||
$source = $this->find
|
||||
('first',
|
||||
array('contain' =>
|
||||
array(/* e3 */
|
||||
'LedgerEntry' =>
|
||||
array('Transaction.id',
|
||||
'MonetarySource.id',
|
||||
'Customer.id',
|
||||
'Lease.id',
|
||||
|
||||
/* e3 debit */
|
||||
'DebitLedger' => /* e.g. CHECK Ledger */
|
||||
array('fields' => array(),
|
||||
|
||||
'Account' => /* e.g. CHECK Account */
|
||||
array('fields' => array('id', 'name'),
|
||||
'conditions' =>
|
||||
array('Account.payable' => 1,
|
||||
'Account.type' => 'ASSET'),
|
||||
),
|
||||
),
|
||||
|
||||
/* e3 credit */
|
||||
'CreditLedger' => /* i.e. RECEIPT Ledger */
|
||||
array('fields' => array('id'),
|
||||
'Account' => /* i.e. RECEIPT Account */
|
||||
array('fields' => array('id', 'name'),
|
||||
'conditions' =>
|
||||
array('Account.id' => $A->receiptAccountID()),
|
||||
),
|
||||
),
|
||||
|
||||
/* e2 */
|
||||
'DebitReconciliationLedgerEntry' =>
|
||||
array(/* e2 credit */
|
||||
'CreditLedger' => /* i.e. A/R Ledger */
|
||||
array('fields' => array(),
|
||||
|
||||
'Account' => /* i.e. A/R Account */
|
||||
array('fields' => array(),
|
||||
'conditions' =>
|
||||
array('Account.id' => $A->accountReceivableAccountID()),
|
||||
),
|
||||
),
|
||||
|
||||
/* e1 */
|
||||
// STUPID CakePHP bug screws up CLASS contains CLASS.
|
||||
// Use the same class, but with different name.
|
||||
'DebitReconciliationLedgerEntry2',
|
||||
),
|
||||
|
||||
/* e4 */
|
||||
'CreditReconciliationLedgerEntry' =>
|
||||
array(/* e4 debit */
|
||||
'DebitLedger' => /* e.g. BANK Ledger */
|
||||
array('fields' => array('id'),
|
||||
'Account' => /* e.g. BANK Account */
|
||||
array('fields' => array('id', 'name'),
|
||||
'conditions' =>
|
||||
array('Account.depositable' => 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
'conditions' => array(array('MonetarySource.id' => $id)),
|
||||
));
|
||||
pr($source);
|
||||
|
||||
$nsf_account_id = $A->nsfAccountID();
|
||||
$nsf_fee_account_id = $A->nsfChargeAccountID();
|
||||
$ar_account_id = $A->accountReceivableAccountID();
|
||||
$receipt_account_id = $A->receiptAccountID();
|
||||
|
||||
$t4_id = null;
|
||||
$t5_id = null;
|
||||
foreach ($source['LedgerEntry'] AS $e3) {
|
||||
// We expect only a single e4 entry
|
||||
$e4 = $e3['CreditReconciliationLedgerEntry'];
|
||||
if (count($e4) < 1)
|
||||
continue;
|
||||
if (count($e4) > 1)
|
||||
die('Too many e4 entries');
|
||||
|
||||
// Pullup e4 from the single member array
|
||||
$e4 = $e4[0];
|
||||
|
||||
// e3 amount
|
||||
$amount = -$e3['amount'];
|
||||
|
||||
// e4 account
|
||||
$bank_account_id = $e4['DebitLedger']['account_id'];
|
||||
|
||||
// post new e5
|
||||
$e5_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t4_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($bank_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($nsf_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => $amount,
|
||||
'lease_id' => $e3['lease_id'],
|
||||
'customer_id' => $e3['customer_id'],
|
||||
'comment' => "NSF Bank Reversal; Monetary Source #{$id}",
|
||||
)
|
||||
);
|
||||
|
||||
if ($e5_ids['error'])
|
||||
return null;
|
||||
$t4_id = $e5_ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e5',
|
||||
compact('e5_ids', 'amount')));
|
||||
|
||||
// post new e6... this will be our crossover point
|
||||
// from typical positive entries to negative entries.
|
||||
// Therefore, no reconciliation on this account.
|
||||
$e6_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t5_id),
|
||||
array('monetary_source_id' => $e3['monetary_source_id']),
|
||||
array('debit_ledger_id' => $A->currentLedgerID($nsf_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($receipt_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => $amount,
|
||||
'lease_id' => $e3['lease_id'],
|
||||
'customer_id' => $e3['customer_id'],
|
||||
'comment' => "NSF tracker; Monetary Source #{$id}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $e5_ids['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
);
|
||||
|
||||
if ($e6_ids['error'])
|
||||
return null;
|
||||
$t5_id = $e6_ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e6',
|
||||
compact('e6_ids', 'amount')));
|
||||
|
||||
$t6_id = null;
|
||||
foreach ($e3['DebitReconciliationLedgerEntry'] AS $e2) {
|
||||
foreach ($e2['DebitReconciliationLedgerEntry2'] AS $e1) {
|
||||
$amount = -1*$e1['Reconciliation']['amount'];
|
||||
|
||||
// post new e7
|
||||
$e7_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t6_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($receipt_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($ar_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => $amount,
|
||||
'lease_id' => $e1['lease_id'],
|
||||
'customer_id' => $e1['customer_id'],
|
||||
'comment' => "NSF Receipt; Monetary Source #{$id}",
|
||||
),
|
||||
array('debit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $e6_ids['id'],
|
||||
'amount' => $amount))),
|
||||
|
||||
'credit' => array
|
||||
(array('LedgerEntry' =>
|
||||
array('id' => $e1['id'],
|
||||
'amount' => $amount))),
|
||||
)
|
||||
);
|
||||
|
||||
if ($e7_ids['error'])
|
||||
return null;
|
||||
$t6_id = $e7_ids['transaction_id'];
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e7',
|
||||
compact('e7_ids', 'amount')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cheat for now
|
||||
$customer_id = $source['LedgerEntry'][0]['customer_id'];
|
||||
$lease_id = null;
|
||||
|
||||
// post new e8
|
||||
$e8_ids = $A->postLedgerEntry
|
||||
(array('transaction_id' => $t6_id),
|
||||
null,
|
||||
array('debit_ledger_id' => $A->currentLedgerID($ar_account_id),
|
||||
'credit_ledger_id' => $A->currentLedgerID($nsf_fee_account_id),
|
||||
'effective_date' => $stamp,
|
||||
'amount' => 35,
|
||||
'lease_id' => $lease_id,
|
||||
'customer_id' => $customer_id,
|
||||
'comment' => "NSF Fee; Monetary Source #{$id}",
|
||||
)
|
||||
);
|
||||
|
||||
if ($e8_ids['error'])
|
||||
return null;
|
||||
|
||||
pr(array('checkpoint' => 'Posted Ledger Entry e8',
|
||||
compact('e8_ids')));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
?>
|
||||
76
site/models/option.php
Normal file
76
site/models/option.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
class Option extends AppModel {
|
||||
|
||||
var $hasMany =
|
||||
array('OptionValue',
|
||||
);
|
||||
|
||||
var $knows =
|
||||
array('User', 'Site', 'Group');
|
||||
|
||||
|
||||
static $option_set = array();
|
||||
|
||||
function getAll($name, $force = false) {
|
||||
/* $this->prClassLevel(30); */
|
||||
/* //$this->OptionValue->prClassLevel(30); */
|
||||
/* $this->Group->Membership->prClassLevel(30); */
|
||||
/* $this->OptionValue->SiteOption->prClassLevel(30); */
|
||||
/* $this->OptionValue->UserOption->prClassLevel(30); */
|
||||
/* $this->OptionValue->GroupOption->prClassLevel(30); */
|
||||
/* $this->OptionValue->DefaultOption->prClassLevel(30); */
|
||||
$this->prEnter(compact('name'));
|
||||
|
||||
if (!empty(self::$option_set[$name]) && !$force)
|
||||
return $this->prReturn(self::$option_set[$name]);
|
||||
|
||||
self::$option_set[$name] = array();
|
||||
|
||||
$site_id = $this->Site->currentSiteId();
|
||||
$user_id = $this->User->currentUserId();
|
||||
$group_ids = $this->Group->currentGroupIds();
|
||||
|
||||
/* $site_id = 2; */
|
||||
/* $user_id = 4; */
|
||||
/* $group_ids = $this->Group->groupIds($user_id, $site_id); */
|
||||
|
||||
if (!empty($site_id))
|
||||
self::$option_set[$name] =
|
||||
array_merge(self::$option_set[$name],
|
||||
$this->OptionValue->SiteOption->values($site_id, $name));
|
||||
|
||||
if (!empty($user_id))
|
||||
self::$option_set[$name] =
|
||||
array_merge(self::$option_set[$name],
|
||||
$this->OptionValue->UserOption->values($user_id, $name));
|
||||
|
||||
if (!empty($group_ids))
|
||||
self::$option_set[$name] =
|
||||
array_merge(self::$option_set[$name],
|
||||
$this->OptionValue->GroupOption->values($group_ids, $name));
|
||||
|
||||
self::$option_set[$name] =
|
||||
array_merge(self::$option_set[$name],
|
||||
$this->OptionValue->DefaultOption->values($name));
|
||||
|
||||
return $this->prReturn(self::$option_set[$name]);
|
||||
}
|
||||
|
||||
function get($name) {
|
||||
$this->prEnter(compact('name'));
|
||||
$values = $this->getAll($name);
|
||||
if (empty($values))
|
||||
return null;
|
||||
return $this->prReturn($values[0]);
|
||||
}
|
||||
|
||||
function enabled($name) {
|
||||
$val = $this->get($name);
|
||||
return (!empty($val));
|
||||
}
|
||||
|
||||
function disabled($name) {
|
||||
return (!$this->enabled($name));
|
||||
}
|
||||
|
||||
}
|
||||
35
site/models/option_value.php
Normal file
35
site/models/option_value.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
class OptionValue extends AppModel {
|
||||
|
||||
var $belongsTo =
|
||||
array('Option',
|
||||
);
|
||||
|
||||
var $hasMany =
|
||||
array('UserOption',
|
||||
'SiteOption',
|
||||
'GroupOption',
|
||||
'DefaultOption',
|
||||
);
|
||||
|
||||
function values($name = null, $query = null) {
|
||||
$this->prEnter(compact('name', 'query'));
|
||||
$this->queryInit($query);
|
||||
|
||||
$query['link']['Option'] = array();
|
||||
|
||||
if (!empty($name)) {
|
||||
$query['conditions'][] = array('Option.name' => $name);
|
||||
$query['link']['Option']['fields'] = array();
|
||||
}
|
||||
|
||||
$this->cacheQueries = true;
|
||||
$values = array();
|
||||
foreach ($this->find('all', $query) AS $result)
|
||||
$values[] = $result['OptionValue']['value'];
|
||||
$this->cacheQueries = false;
|
||||
|
||||
return $this->prReturn($values);
|
||||
}
|
||||
|
||||
}
|
||||
105
site/models/permission.php
Normal file
105
site/models/permission.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
class Permission extends AppModel {
|
||||
|
||||
var $hasMany =
|
||||
array('PermissionValue',
|
||||
);
|
||||
|
||||
var $knows =
|
||||
array('User', 'Site', 'Group');
|
||||
|
||||
static $permission_set = array();
|
||||
|
||||
function getAll($name, $force = false) {
|
||||
/* $this->prClassLevel(30); */
|
||||
/* $this->PermissionValue->prClassLevel(30); */
|
||||
/* $this->Group->Membership->prClassLevel(30); */
|
||||
/* $this->PermissionValue->SitePermission->prClassLevel(30); */
|
||||
/* $this->PermissionValue->UserPermission->prClassLevel(30); */
|
||||
/* $this->PermissionValue->GroupPermission->prClassLevel(30); */
|
||||
/* $this->PermissionValue->DefaultPermission->prClassLevel(30); */
|
||||
$this->prEnter(compact('name'));
|
||||
|
||||
if (!empty(self::$permission_set[$name]) && !$force)
|
||||
return $this->prReturn(self::$permission_set[$name]);
|
||||
|
||||
self::$permission_set[$name] = array();
|
||||
|
||||
$site_id = $this->Site->currentSiteId();
|
||||
$user_id = $this->User->currentUserId();
|
||||
$group_ids = $this->Group->currentGroupIds();
|
||||
|
||||
/* $site_id = 1; */
|
||||
/* $user_id = 2; */
|
||||
/* $group_ids = $this->Group->groupIds($user_id, $site_id); */
|
||||
|
||||
if (empty($group_ids)) {
|
||||
self::$permission_set[$name][$name][] = array('access' => 'DENY', 'level' => null);
|
||||
$site_id = null;
|
||||
$user_id = null;
|
||||
}
|
||||
|
||||
if (!empty($site_id))
|
||||
self::$permission_set[$name] =
|
||||
array_merge(self::$permission_set[$name],
|
||||
$this->PermissionValue->SitePermission->values($site_id, $name));
|
||||
|
||||
if (!empty($user_id))
|
||||
self::$permission_set[$name] =
|
||||
array_merge(self::$permission_set[$name],
|
||||
$this->PermissionValue->UserPermission->values($user_id, $name));
|
||||
|
||||
if (!empty($group_ids)) {
|
||||
self::$permission_set[$name] =
|
||||
array_merge(self::$permission_set[$name],
|
||||
$this->PermissionValue->GroupPermission->values($group_ids, $name));
|
||||
|
||||
self::$permission_set[$name] =
|
||||
array_merge(self::$permission_set[$name],
|
||||
$this->PermissionValue->DefaultPermission->values($name));
|
||||
|
||||
self::$permission_set[$name][] = array('access' => 'ALLOW', 'level' => null);
|
||||
}
|
||||
|
||||
return $this->prReturn(self::$permission_set[$name]);
|
||||
}
|
||||
|
||||
function get($name) {
|
||||
$this->prEnter(compact('name'));
|
||||
|
||||
// REVISIT <AP>: 20090827
|
||||
// This is a pretty crappy algorithm. How do we decide whether DENY really
|
||||
// means DENY, or whether an ALLOW has priority.
|
||||
// Oh well, it works for now...
|
||||
|
||||
$values = $this->getAll($name);
|
||||
$result = array_shift($values);
|
||||
|
||||
foreach ($values AS $value)
|
||||
if (empty($result['level']) || (!empty($value['level']) && $value['level'] < $result['level']))
|
||||
$result['level'] = $value['level'];
|
||||
|
||||
if ($result['access'] !== 'ALLOW')
|
||||
$result['level'] = 9999999;
|
||||
|
||||
return $this->prReturn($result);
|
||||
}
|
||||
|
||||
function allow($name) {
|
||||
$this->prEnter(compact('name'));
|
||||
$result = $this->get($name);
|
||||
return $this->prReturn($result['access'] === 'ALLOW');
|
||||
}
|
||||
|
||||
function deny($name) {
|
||||
$this->prEnter(compact('name'));
|
||||
return $this->prReturn(!$this->allow($name));
|
||||
}
|
||||
|
||||
function level($name) {
|
||||
$this->prEnter(compact('name'));
|
||||
$result = $this->get($name);
|
||||
return $this->prReturn($result['level']);
|
||||
}
|
||||
|
||||
}
|
||||
36
site/models/permission_value.php
Normal file
36
site/models/permission_value.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
class PermissionValue extends AppModel {
|
||||
|
||||
var $belongsTo =
|
||||
array('Permission',
|
||||
);
|
||||
|
||||
var $hasMany =
|
||||
array('UserPermission',
|
||||
'SitePermission',
|
||||
'GroupPermission',
|
||||
'DefaultPermission',
|
||||
);
|
||||
|
||||
function values($name = null, $query = null) {
|
||||
$this->prEnter(compact('name', 'query'));
|
||||
$this->queryInit($query);
|
||||
|
||||
$query['link']['Permission'] = array();
|
||||
|
||||
if (!empty($name)) {
|
||||
$query['conditions'][] = array('Permission.name' => $name);
|
||||
$query['link']['Permission']['fields'] = array();
|
||||
}
|
||||
|
||||
$this->cacheQueries = true;
|
||||
$values = array();
|
||||
foreach ($this->find('all', $query) AS $result)
|
||||
$values[] = array('access' => $result['PermissionValue']['access'],
|
||||
'level' => $result['PermissionValue']['level']);
|
||||
$this->cacheQueries = false;
|
||||
|
||||
return $this->prReturn($values);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
class Reconciliation extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
'DebitLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
//'foreignKey' => 'credit_ledger_entry_id',
|
||||
),
|
||||
'CreditLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
//'foreignKey' => 'credit_ledger_entry_id',
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
@@ -1,16 +1,37 @@
|
||||
<?php
|
||||
class Site extends AppModel {
|
||||
|
||||
var $name = 'Site';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'name' => array('notempty')
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
'SiteArea',
|
||||
var $hasMany =
|
||||
array('SiteArea',
|
||||
'SiteOption',
|
||||
'Membership',
|
||||
);
|
||||
|
||||
static $current_site_id;
|
||||
|
||||
function currentSiteId() {
|
||||
if (!empty(self::$current_site_id))
|
||||
return self::$current_site_id;
|
||||
|
||||
// REVISIT <AP>: 20090827
|
||||
// Must get the actual site
|
||||
$code = 'VSS';
|
||||
|
||||
$site = $this->find
|
||||
('first',
|
||||
array('recursive' => -1,
|
||||
'conditions' => compact('code')));
|
||||
|
||||
if (!empty($site['Site']['id']))
|
||||
self::$current_site_id = $site['Site']['id'];
|
||||
else
|
||||
// We must force a stop here, since this is typically
|
||||
// called very early on, and so will cause a recursive
|
||||
// crash as we try to render the internal error and
|
||||
// again stumble on this problem.
|
||||
$this->INTERNAL_ERROR('UNKNOWN SITE', 0, true);
|
||||
|
||||
return self::$current_site_id;
|
||||
}
|
||||
}
|
||||
?>
|
||||
24
site/models/site_option.php
Normal file
24
site/models/site_option.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
class SiteOption extends AppModel {
|
||||
|
||||
var $belongsTo =
|
||||
array('Site',
|
||||
'OptionValue',
|
||||
);
|
||||
|
||||
|
||||
function values($id, $name = null) {
|
||||
$this->prEnter(compact('id', 'name'));
|
||||
|
||||
$query = array();
|
||||
$this->queryInit($query);
|
||||
|
||||
$query['link']['SiteOption'] = array();
|
||||
$query['link']['SiteOption']['fields'] = array();
|
||||
$query['link']['SiteOption']['Site'] = array();
|
||||
$query['link']['SiteOption']['Site']['fields'] = array();
|
||||
$query['conditions'][] = array('Site.id' => $id);
|
||||
return $this->prReturn($this->OptionValue->values($name, $query));
|
||||
}
|
||||
|
||||
}
|
||||
24
site/models/site_permission.php
Normal file
24
site/models/site_permission.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
class SitePermission extends AppModel {
|
||||
|
||||
var $belongsTo =
|
||||
array('Site',
|
||||
'PermissionValue',
|
||||
);
|
||||
|
||||
|
||||
function values($id, $name = null) {
|
||||
$this->prEnter(compact('id', 'name'));
|
||||
|
||||
$query = array();
|
||||
$this->queryInit($query);
|
||||
|
||||
$query['link']['SitePermission'] = array();
|
||||
$query['link']['SitePermission']['fields'] = array();
|
||||
$query['link']['SitePermission']['Site'] = array();
|
||||
$query['link']['SitePermission']['Site']['fields'] = array();
|
||||
$query['conditions'][] = array('Site.id' => $id);
|
||||
return $this->prReturn($this->PermissionValue->values($name, $query));
|
||||
}
|
||||
|
||||
}
|
||||
725
site/models/statement_entry.php
Normal file
725
site/models/statement_entry.php
Normal file
@@ -0,0 +1,725 @@
|
||||
<?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',
|
||||
'dependent' => true,
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
//var $default_log_level = array('log' => 30, 'show' => 15);
|
||||
var $max_log_level = 19;
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: debit/creditTypes
|
||||
*/
|
||||
|
||||
function debitTypes() {
|
||||
return array('CHARGE', 'PAYMENT', 'REFUND');
|
||||
}
|
||||
|
||||
function creditTypes() {
|
||||
return array('DISBURSEMENT', 'WAIVER', 'REVERSAL', 'WRITEOFF', '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('data' => $entry);
|
||||
if (!$this->verifyStatementEntry($entry))
|
||||
return $this->prReturn(array('error' => true, 'verify_data' => $entry) + $ret);
|
||||
|
||||
$this->create();
|
||||
if (!$this->save($entry))
|
||||
return $this->prReturn(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')
|
||||
$this->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'],
|
||||
'comment' => null,
|
||||
);
|
||||
|
||||
// Record the waiver transaction
|
||||
return $this->prReturn($this->Transaction->addWaiver
|
||||
($waiver, $id, $charge['customer_id'], $charge['lease_id']));
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reversable
|
||||
* - Returns true if the charge can be reversed; false otherwise
|
||||
*/
|
||||
function reversable($id) {
|
||||
$this->prEnter(compact('id'));
|
||||
|
||||
if (empty($id))
|
||||
return $this->prReturn(false);
|
||||
|
||||
// Verify the item is an actual charge
|
||||
$this->id = $id;
|
||||
$charge_type = $this->field('type');
|
||||
if ($charge_type !== 'CHARGE')
|
||||
return $this->prReturn(false);
|
||||
|
||||
// Determine anything reconciled against the charge
|
||||
$reverse_transaction_id = $this->field('reverse_transaction_id');
|
||||
if (!empty($reverse_transaction_id))
|
||||
return $this->prReturn(false);
|
||||
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: reverse
|
||||
* - Reverses the charges
|
||||
*/
|
||||
function reverse($id, $stamp = null, $comment) {
|
||||
$this->prEnter(compact('id', 'stamp'));
|
||||
|
||||
// Verify the item can be reversed
|
||||
if (!$this->reversable($id))
|
||||
$this->INTERNAL_ERROR("Item is not reversable.");
|
||||
|
||||
// Get the basic information about this charge
|
||||
$charge = $this->find('first', array('contain' => true));
|
||||
//$charge = $charge['StatementEntry'];
|
||||
|
||||
// Query the stats to get the remaining balance
|
||||
$stats = $this->stats($id);
|
||||
$charge['paid'] = $stats['Charge']['disbursement'];
|
||||
|
||||
// Record the reversal transaction
|
||||
$result = $this->Transaction->addReversal
|
||||
($charge, $stamp, $comment ? $comment : 'Charge Reversal');
|
||||
|
||||
if (empty($result['error'])) {
|
||||
// Mark the charge as reversed
|
||||
$this->id = $id;
|
||||
$this->saveField('reverse_transaction_id', $result['transaction_id']);
|
||||
}
|
||||
|
||||
return $this->prReturn($result);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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
|
||||
if (empty($receipt_id)) {
|
||||
|
||||
if (!empty($charge_ids))
|
||||
$this->INTERNAL_ERROR("Charge IDs, yet no corresponding receipt");
|
||||
|
||||
$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 {
|
||||
// Establish credit from the (newly added) receipt
|
||||
$lquery =
|
||||
array('link' =>
|
||||
array('StatementEntry',
|
||||
'LedgerEntry' =>
|
||||
array('conditions' =>
|
||||
array('LedgerEntry.account_id <> Transaction.account_id')
|
||||
),
|
||||
),
|
||||
'conditions' => array('Transaction.id' => $receipt_id),
|
||||
'fields' => array('Transaction.id', 'Transaction.stamp', 'Transaction.amount'),
|
||||
);
|
||||
$receipt_credit = $this->Transaction->find('first', $lquery);
|
||||
if (!$receipt_credit)
|
||||
$this->INTERNAL_ERROR("Unable to locate receipt.");
|
||||
|
||||
$stats = $this->Transaction->stats($receipt_id);
|
||||
$receipt_credit['balance'] = $stats['undisbursed'];
|
||||
|
||||
$receipt_credit['receipt'] = true;
|
||||
$credits = array($receipt_credit);
|
||||
$this->pr(18, compact('credits'),
|
||||
"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");
|
||||
}
|
||||
|
||||
// Work through all unpaid charges, applying disbursements as we go
|
||||
foreach ($charges AS $charge) {
|
||||
$this->pr(20, compact('charge'),
|
||||
'Process Charge');
|
||||
|
||||
$charge['balance'] = $charge['StatementEntry']['balance'];
|
||||
|
||||
// Use explicit credits before using the new receipt credit
|
||||
foreach ($credits AS &$credit) {
|
||||
if (empty($charge['balance']))
|
||||
break;
|
||||
if ($charge['balance'] < 0)
|
||||
$this->INTERNAL_ERROR("Negative Charge Balance");
|
||||
|
||||
if (!isset($credit['balance']))
|
||||
$credit['balance'] = $credit['StatementEntry']['amount'];
|
||||
|
||||
if (empty($credit['balance']))
|
||||
continue;
|
||||
if ($credit['balance'] < 0)
|
||||
$this->INTERNAL_ERROR("Negative Credit Balance");
|
||||
|
||||
$this->pr(20, compact('charge'),
|
||||
'Attempt Charge Reconciliation');
|
||||
|
||||
if (empty($credit['receipt']))
|
||||
$disbursement_account_id = $credit['StatementEntry']['account_id'];
|
||||
else
|
||||
$disbursement_account_id = $credit['LedgerEntry']['account_id'];
|
||||
|
||||
// REVISIT <AP>: 20090811
|
||||
// Need to come up with a better strategy for handling
|
||||
// concessions. For now, just restricting concessions
|
||||
// to apply only towards rent will resolve the most
|
||||
// predominant (or only) needed usage case.
|
||||
if ($disbursement_account_id == $this->Account->concessionAccountID() &&
|
||||
$charge['StatementEntry']['account_id'] != $this->Account->rentAccountID())
|
||||
continue;
|
||||
|
||||
// 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($credit['receipt']) ? ' Credit' : ' Receipt'));
|
||||
|
||||
if (strtotime($charge['StatementEntry']['effective_date']) >
|
||||
strtotime($credit['StatementEntry']['effective_date']))
|
||||
$disbursement_edate = $charge['StatementEntry']['effective_date'];
|
||||
else
|
||||
$disbursement_edate = $credit['StatementEntry']['effective_date'];
|
||||
|
||||
if (empty($credit['receipt'])) {
|
||||
// Explicit Credit
|
||||
$result = $this->Transaction->addTransactionEntries
|
||||
(array('include_ledger_entry' => true,
|
||||
'include_statement_entry' => true),
|
||||
array('type' => 'INVOICE',
|
||||
'id' => $credit['StatementEntry']['transaction_id'],
|
||||
'account_id' => $this->Account->accountReceivableAccountID(),
|
||||
'crdr' => 'CREDIT',
|
||||
'customer_id' => $charge['StatementEntry']['customer_id'],
|
||||
'lease_id' => $charge['StatementEntry']['lease_id'],
|
||||
),
|
||||
array
|
||||
(array('type' => $disbursement_type,
|
||||
'effective_date' => $disbursement_edate,
|
||||
'account_id' => $credit['StatementEntry']['account_id'],
|
||||
'amount' => $disbursement_amount,
|
||||
'charge_entry_id' => $charge['StatementEntry']['id'],
|
||||
),
|
||||
));
|
||||
|
||||
$ret['Disbursement'][] = $result;
|
||||
if ($result['error'])
|
||||
$ret['error'] = true;
|
||||
}
|
||||
else {
|
||||
// Receipt Credit
|
||||
|
||||
if (strtotime($charge['StatementEntry']['effective_date']) >
|
||||
strtotime($credit['Transaction']['stamp']))
|
||||
$disbursement_edate = $charge['StatementEntry']['effective_date'];
|
||||
else
|
||||
$disbursement_edate = $credit['Transaction']['stamp'];
|
||||
|
||||
// Add a disbursement that uses the available credit to pay the charge
|
||||
$disbursement =
|
||||
array('type' => $disbursement_type,
|
||||
'effective_date' => $disbursement_edate,
|
||||
'amount' => $disbursement_amount,
|
||||
'account_id' => $credit['LedgerEntry']['account_id'],
|
||||
'transaction_id' => $credit['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');
|
||||
}
|
||||
// Break the $credit reference to avoid future problems
|
||||
unset($credit);
|
||||
}
|
||||
|
||||
$this->pr(18, compact('credits'),
|
||||
'Disbursements complete');
|
||||
|
||||
// Clean up any explicit credits that have been used
|
||||
foreach ($credits AS $credit) {
|
||||
if (!empty($credit['receipt']))
|
||||
continue;
|
||||
|
||||
if (empty($credit['applied']))
|
||||
continue;
|
||||
|
||||
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->delete($credit['StatementEntry']['id'], false);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for any implicit receipt credits, converting
|
||||
// into explicit credits if there is a remaining balance.
|
||||
foreach ($credits AS $credit) {
|
||||
if (empty($credit['receipt']))
|
||||
continue;
|
||||
|
||||
if (empty($credit['balance']))
|
||||
continue;
|
||||
|
||||
// See if there is an existing explicit credit
|
||||
// for this transaction.
|
||||
$explicit_credit = $this->find
|
||||
('first', array('contain' => false,
|
||||
'conditions' =>
|
||||
array(array('transaction_id' => $credit['Transaction']['id']),
|
||||
array('type' => 'SURPLUS')),
|
||||
));
|
||||
|
||||
if (!empty($explicit_credit)) {
|
||||
// REVISIT <AP>: 20090815
|
||||
// Testing whether or not this case occurs
|
||||
$this->INTERNAL_ERROR('Existing explicit credit unexpected');
|
||||
|
||||
// Since there IS an existing explicit credit, we must update
|
||||
// its balance instead of creating a new one, since it has
|
||||
// already been incorporated in the overall credit balance.
|
||||
// If we were to create a new one, we would erroneously create
|
||||
// an excess of credit available.
|
||||
$this->pr(18, compact('explicit_credit', 'credit'),
|
||||
'Update existing explicit credit');
|
||||
$EC = new StatementEntry();
|
||||
$EC->id = $explicit_credit['StatementEntry']['id'];
|
||||
$EC->saveField('amount', $credit['balance']);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($ret['receipt_balance']))
|
||||
$this->INTERNAL_ERROR('Only one receipt expected in assignCredits');
|
||||
|
||||
// Give caller the information necessary to create an explicit
|
||||
// credit from the passed receipt, which we've not exhausted.
|
||||
$this->pr(18, compact('credit'), 'Convert to explicit credit');
|
||||
$ret['receipt_balance'] = $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' => 16, '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);
|
||||
}
|
||||
|
||||
}
|
||||
166
site/models/tender.php
Normal file
166
site/models/tender.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
class Tender extends AppModel {
|
||||
|
||||
var $belongsTo = array(
|
||||
'TenderType',
|
||||
'Customer',
|
||||
'LedgerEntry',
|
||||
'DepositTransaction' => array(
|
||||
'className' => 'Transaction',
|
||||
),
|
||||
'DepositLedgerEntry' => array(
|
||||
'className' => 'LedgerEntry',
|
||||
),
|
||||
'NsfTransaction' => array(
|
||||
'className' => 'Transaction',
|
||||
'dependent' => true,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: afterSave
|
||||
* - Performs any work needed after the save occurs
|
||||
*/
|
||||
|
||||
function afterSave($created) {
|
||||
// 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 tender type, and the tender ID
|
||||
|
||||
// Determine our tender type, and set the ID of that model
|
||||
$this->TenderType->id = $this->field('tender_type_id');
|
||||
|
||||
// REVISIT <AP>: 20090810
|
||||
// The only tender expected to have no tender type
|
||||
// is our special "Closing" tender.
|
||||
if (empty($this->TenderType->id))
|
||||
$newname = 'Closing';
|
||||
else {
|
||||
$newname = $this->TenderType->field('name');
|
||||
$naming_field = $this->TenderType->field('naming_field');
|
||||
if (!empty($naming_field))
|
||||
$newname .= ' #' . $this->field($naming_field);
|
||||
}
|
||||
|
||||
if ($newname !== $this->field('name'))
|
||||
$this->saveField('name', $newname);
|
||||
|
||||
return parent::afterSave($created);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: beforeDelete
|
||||
* - Performs any work needed before the delete occurs
|
||||
*/
|
||||
|
||||
function beforeDelete($cascade = true) {
|
||||
// REVISIT <AP>: 20090814
|
||||
// Experimental, and incomplete mechanism to protect
|
||||
// against trying to delete data that shouldn't be deleted.
|
||||
|
||||
$deposit_id = $this->field('deposit_transaction_id');
|
||||
pr(compact('deposit_id'));
|
||||
// If this tender has already been deposited, it would
|
||||
// be a rats nest to figure out how to delete this tender.
|
||||
if (!empty($deposit_id))
|
||||
return false;
|
||||
|
||||
return parent::beforeDelete($cascade);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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('data' => $tender);
|
||||
if (!$this->verifyTender($tender))
|
||||
return $this->prReturn(array('error' => true) + $ret);
|
||||
|
||||
$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
|
||||
*/
|
||||
|
||||
function nsf($id, $stamp = null, $comment = null) {
|
||||
$this->prEnter(compact('id', 'stamp', 'comment'));
|
||||
|
||||
// Get information about this NSF item.
|
||||
$this->id = $id;
|
||||
$tender = $this->find
|
||||
('first', array
|
||||
('contain' =>
|
||||
array('LedgerEntry',
|
||||
'DepositTransaction',
|
||||
'DepositLedgerEntry',
|
||||
'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, $comment);
|
||||
if (empty($result['error'])) {
|
||||
// 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($result);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
?>
|
||||
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];
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,8 @@ class Unit extends AppModel {
|
||||
'Lease',
|
||||
);
|
||||
|
||||
//var $default_log_level = array('log' => 30, 'show' => 15);
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
@@ -50,13 +52,70 @@ class Unit extends AppModel {
|
||||
}
|
||||
|
||||
function occupiedEnumValue() {
|
||||
return statusValue('OCCUPIED');
|
||||
return $this->statusValue('OCCUPIED');
|
||||
}
|
||||
|
||||
function statusCheck($id_or_enum,
|
||||
$min = null, $min_strict = false,
|
||||
$max = null, $max_strict = false)
|
||||
{
|
||||
$this->prEnter(compact('id_or_enum', 'min', 'min_strict', 'max', 'max_strict'));
|
||||
|
||||
if (is_int($id_or_enum)) {
|
||||
$this->id = $id_or_enum;
|
||||
$id_or_enum = $this->field('status');
|
||||
}
|
||||
|
||||
$enum_val = $this->statusValue($id_or_enum);
|
||||
if (isset($min) && is_string($min))
|
||||
$min = $this->statusValue($min);
|
||||
if (isset($max) && is_string($max))
|
||||
$max = $this->statusValue($max);
|
||||
|
||||
$this->pr(17, compact('enum_val', 'min', 'min_strict', 'max', 'max_strict'));
|
||||
|
||||
if (isset($min) &&
|
||||
($enum_val < $min ||
|
||||
($min_strict && $enum_val == $min)))
|
||||
return $this->prReturn(false);
|
||||
|
||||
if (isset($max) &&
|
||||
($enum_val > $max ||
|
||||
($max_strict && $enum_val == $max)))
|
||||
return $this->prReturn(false);
|
||||
|
||||
return $this->prReturn(true);
|
||||
}
|
||||
|
||||
function locked($enum) {
|
||||
return $this->statusCheck($enum, 'LOCKED', false, null, false);
|
||||
}
|
||||
|
||||
function conditionLocked() {
|
||||
//return array('Unit.status' => 'LOCKED');
|
||||
return ('Unit.status >= ' . $this->statusValue('LOCKED'));
|
||||
}
|
||||
|
||||
function liened($enum) {
|
||||
return $this->statusCheck($enum, 'LIENED', false, null, false);
|
||||
}
|
||||
|
||||
function conditionLiened() {
|
||||
return ('Unit.status >= ' . $this->statusValue('LIENED'));
|
||||
}
|
||||
|
||||
function occupied($enum) {
|
||||
return $this->statusCheck($enum, 'OCCUPIED', false, null, false);
|
||||
}
|
||||
|
||||
function conditionOccupied() {
|
||||
return ('Unit.status >= ' . $this->statusValue('OCCUPIED'));
|
||||
}
|
||||
|
||||
function vacant($enum) {
|
||||
return $this->statusCheck($enum, 'UNAVAILABLE', true, 'OCCUPIED', true);
|
||||
}
|
||||
|
||||
function conditionVacant() {
|
||||
return ('Unit.status BETWEEN ' .
|
||||
($this->statusValue('UNAVAILABLE')+1) .
|
||||
@@ -64,22 +123,92 @@ class Unit extends AppModel {
|
||||
($this->statusValue('OCCUPIED')-1));
|
||||
}
|
||||
|
||||
function unavailable($enum) {
|
||||
return $this->statusCheck($enum, null, false, 'UNAVAILABLE', false);
|
||||
}
|
||||
|
||||
function conditionUnavailable() {
|
||||
return ('Unit.status <= ' . $this->statusValue('UNAVAILABLE'));
|
||||
}
|
||||
|
||||
function available($enum) { return $this->vacant($enum); }
|
||||
function conditionAvailable() { return $this->conditionVacant($enum); }
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* 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) {
|
||||
$this->id = $id;
|
||||
//pr(compact('id', 'status'));
|
||||
$this->saveField('status', $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: update
|
||||
* - Update any cached or calculated fields
|
||||
*/
|
||||
function update($id) {
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
|
||||
@@ -1,25 +1,64 @@
|
||||
<?php
|
||||
class UnitSize extends AppModel {
|
||||
|
||||
var $name = 'UnitSize';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'unit_type_id' => array('numeric'),
|
||||
'code' => array('notempty'),
|
||||
'name' => array('notempty'),
|
||||
'width' => array('numeric'),
|
||||
'depth' => array('numeric'),
|
||||
'deposit' => array('money'),
|
||||
'amount' => array('money')
|
||||
);
|
||||
|
||||
var $belongsTo = array(
|
||||
var $belongsTo =
|
||||
array(
|
||||
'UnitType',
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
var $hasMany =
|
||||
array(
|
||||
'Unit',
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: stats
|
||||
* - Returns summary data from the requested unit size.
|
||||
*/
|
||||
|
||||
function stats($id = null) {
|
||||
$this->prEnter(compact('id'));
|
||||
|
||||
// Right now, we only work with id not null
|
||||
if (!$id)
|
||||
return null;
|
||||
|
||||
$stats = array();
|
||||
|
||||
// Get the total number of units this size
|
||||
$stats['all'] =
|
||||
$this->find('count',
|
||||
array('link' => array('Unit'),
|
||||
'conditions' => array(array('UnitSize.id' => $id)),
|
||||
));
|
||||
|
||||
// Get numbers for units in the various states
|
||||
foreach (array('unavailable', 'vacant', 'occupied', 'locked', 'liened') AS $status) {
|
||||
$statusfunc = 'condition' . ucfirst($status);
|
||||
$stats[$status] =
|
||||
$this->find('count',
|
||||
array('link' => array('Unit'),
|
||||
'conditions' => array(array('UnitSize.id' => $id),
|
||||
$this->Unit->{$statusfunc}()),
|
||||
));
|
||||
}
|
||||
|
||||
// Count up each unit by physical status
|
||||
foreach
|
||||
($this->find('all',
|
||||
array('link' => array('Unit' => array('fields' => array())),
|
||||
'fields' => array('Unit.status', 'COUNT(Unit.id) AS total'),
|
||||
'conditions' => array(array('UnitSize.id' => $id)),
|
||||
'group' => 'Unit.status',
|
||||
)) AS $status) {
|
||||
$stats['status'][$status['Unit']['status']] = $status[0]['total'];
|
||||
}
|
||||
|
||||
// Return the collection
|
||||
return $this->prReturn($stats);
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -1,16 +1,50 @@
|
||||
<?php
|
||||
class UnitType extends AppModel {
|
||||
|
||||
var $name = 'UnitType';
|
||||
var $validate = array(
|
||||
'id' => array('numeric'),
|
||||
'code' => array('notempty'),
|
||||
'name' => array('notempty')
|
||||
);
|
||||
|
||||
var $hasMany = array(
|
||||
var $hasMany =
|
||||
array(
|
||||
'UnitSize',
|
||||
);
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: relatedTypes
|
||||
* - Returns an array of types related by similar attributes
|
||||
*/
|
||||
|
||||
function relatedTypes($attribute, $extra = null) {
|
||||
$this->cacheQueries = true;
|
||||
$types = $this->find('all', array
|
||||
('fields' => array('UnitType.id', 'UnitType.name'),
|
||||
'conditions' => array('UnitType.'.$attribute => true),
|
||||
'order' => array('UnitType.name'),
|
||||
) + (isset($extra) ? $extra : array())
|
||||
);
|
||||
$this->cacheQueries = false;
|
||||
|
||||
// Rearrange to be of the form (id => name)
|
||||
$rel_types = array();
|
||||
foreach ($types AS $type) {
|
||||
$rel_types[$type['UnitType']['id']] = $type['UnitType']['name'];
|
||||
}
|
||||
|
||||
return $rel_types;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
**************************************************************************
|
||||
**************************************************************************
|
||||
* function: xxxTypes
|
||||
* - Returns an array of types suitable for activity xxx
|
||||
*/
|
||||
|
||||
function residentialTypes() { return $this->relatedTypes('residential'); }
|
||||
function enclosedTypes() { return $this->relatedTypes('enclosed'); }
|
||||
function climateTypes() { return $this->relatedTypes('climate'); }
|
||||
function outdoorTypes() { return $this->relatedTypes('outdoor'); }
|
||||
function coveredTypes() { return $this->relatedTypes('covered'); }
|
||||
|
||||
}
|
||||
?>
|
||||
39
site/models/user.php
Normal file
39
site/models/user.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
class User extends AppModel {
|
||||
|
||||
var $hasMany =
|
||||
array('UserOption',
|
||||
'Membership',
|
||||
);
|
||||
|
||||
static $current_user_id;
|
||||
|
||||
function currentUser() {
|
||||
if (!empty($_SERVER['REMOTE_USER']))
|
||||
return $_SERVER['REMOTE_USER'];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function currentUserId() {
|
||||
if (!empty(self::$current_user_id))
|
||||
return self::$current_user_id;
|
||||
|
||||
$user = $this->find
|
||||
('first',
|
||||
array('recursive' => -1,
|
||||
'conditions' => array('login' => $this->currentUser())));
|
||||
|
||||
if (!empty($user['User']['id']))
|
||||
self::$current_user_id = $user['User']['id'];
|
||||
else
|
||||
// We must force a stop here, since this is typically
|
||||
// called very early on, and so will cause a recursive
|
||||
// crash as we try to render the internal error and
|
||||
// again stumble on this problem.
|
||||
$this->INTERNAL_ERROR('UNKNOWN USER', 0, true);
|
||||
|
||||
return self::$current_user_id;
|
||||
}
|
||||
|
||||
}
|
||||
24
site/models/user_option.php
Normal file
24
site/models/user_option.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
class UserOption extends AppModel {
|
||||
|
||||
var $belongsTo =
|
||||
array('User',
|
||||
'OptionValue',
|
||||
);
|
||||
|
||||
|
||||
function values($id, $name = null) {
|
||||
$this->prEnter(compact('id', 'name'));
|
||||
|
||||
$query = array();
|
||||
$this->queryInit($query);
|
||||
|
||||
$query['link']['UserOption'] = array();
|
||||
$query['link']['UserOption']['fields'] = array();
|
||||
$query['link']['UserOption']['User'] = array();
|
||||
$query['link']['UserOption']['User']['fields'] = array();
|
||||
$query['conditions'][] = array('User.id' => $id);
|
||||
return $this->prReturn($this->OptionValue->values($name, $query));
|
||||
}
|
||||
|
||||
}
|
||||
24
site/models/user_permission.php
Normal file
24
site/models/user_permission.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
class UserPermission extends AppModel {
|
||||
|
||||
var $belongsTo =
|
||||
array('User',
|
||||
'PermissionValue',
|
||||
);
|
||||
|
||||
|
||||
function values($id, $name = null) {
|
||||
$this->prEnter(compact('id', 'name'));
|
||||
|
||||
$query = array();
|
||||
$this->queryInit($query);
|
||||
|
||||
$query['link']['UserPermission'] = array();
|
||||
$query['link']['UserPermission']['fields'] = array();
|
||||
$query['link']['UserPermission']['User'] = array();
|
||||
$query['link']['UserPermission']['User']['fields'] = array();
|
||||
$query['conditions'][] = array('User.id' => $id);
|
||||
return $this->prReturn($this->PermissionValue->values($name, $query));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";}
|
||||
@@ -15,7 +15,8 @@ echo '<div class="account collected">' . "\n";
|
||||
|
||||
// Reset the form
|
||||
function resetForm() {
|
||||
/* updateEntriesGrid(); */
|
||||
// Kick off the grid
|
||||
updateEntriesGrid();
|
||||
}
|
||||
|
||||
function onGridLoadComplete() {
|
||||
@@ -24,26 +25,30 @@ function onGridLoadComplete() {
|
||||
}
|
||||
|
||||
function updateEntriesGrid() {
|
||||
var cust = new Array();
|
||||
|
||||
var account_ids = new Array();
|
||||
$("INPUT[type='checkbox']:checked").each(function(i) {
|
||||
account_ids.push($(this).val());
|
||||
});
|
||||
|
||||
cust['collected_account_id'] = <?php echo $account['id']; ?>;
|
||||
cust['collected_from_date'] = $('#TxFromDate').val();
|
||||
cust['collected_through_date'] = $('#TxThroughDate').val();
|
||||
cust['collected_payment_accounts'] = account_ids;
|
||||
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('custom', serialize(cust));
|
||||
$('#collected-entries-jqGrid').setPostDataItem('dynamic_post_replace', serialize(dynamic_post));
|
||||
$('#collected-entries-jqGrid')
|
||||
.setGridParam({ page: 1 })
|
||||
.trigger("reloadGrid");
|
||||
|
||||
//$('#collected-entries .HeaderButton').click();
|
||||
//$('#debug').html("<PRE>\n"+htmlEncode(dump($('#collected-entries-jqGrid').getGridParam()))+"\n</PRE>")
|
||||
var gridstate = $('#collected-entries-jqGrid').getGridParam('gridstate');
|
||||
if (gridstate == 'hidden')
|
||||
$('#collected-entries .HeaderButton').click();
|
||||
}
|
||||
|
||||
|
||||
@@ -153,19 +158,19 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
* Entries
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'collected_account_id' => $account['id'],
|
||||
|
||||
// Grid configuration
|
||||
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'])
|
||||
'grid_setup' => array('hiddengrid' => true),
|
||||
'caption' => 'Collected ' . Inflector::pluralize($account['name']),
|
||||
'action' => 'collected',
|
||||
'filter' => array('ChargeEntry.account_id' => $account['id']),
|
||||
'include' => array('Amount'),
|
||||
'exclude' => array(/*'Type',*/ 'Debit', 'Credit'),
|
||||
),
|
||||
));
|
||||
|
||||
@@ -173,20 +178,8 @@ echo $this->element('ledger_entries', array
|
||||
|
||||
<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' });
|
||||
|
||||
datepicker('TxFromDate');
|
||||
datepicker('TxThroughDate');
|
||||
resetForm();
|
||||
});
|
||||
--></script>
|
||||
@@ -200,5 +193,3 @@ echo '</div>' . "\n";
|
||||
echo '</div>' . "\n";
|
||||
|
||||
?>
|
||||
|
||||
<a href="#" onClick="$('#debug').html(''); return false;">Clear Debug Output</a>
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="account deposit">' . "\n";
|
||||
echo '<H2>Perform Bank Deposit</H2>' . "\n";
|
||||
echo '<P>Make sure to select the checkboxes below for only those types of currency (Cash, Check, etc) which you intend to actually deposit (you can see all the individual items by dropping down the list below the checkbox). Then, select the Deposit Account where you will make the deposit, and click "Perform Deposit" to close the books on the selected currency types and reset them to a zero balance. On the next page, you will be provided with a deposit slip to prepare the actual deposit.' . "\n";
|
||||
echo '<P><BR>' . "\n";
|
||||
|
||||
//pr(compact('tillableAccount', 'depositableAccount'));
|
||||
|
||||
echo $form->create(null, array('id' => 'deposit-form',
|
||||
'url' => array('controller' => 'accounts',
|
||||
'action' => 'deposit')));
|
||||
|
||||
foreach ($tillableAccount AS $acct) {
|
||||
//$acct = $acct['Account'];
|
||||
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.checked',
|
||||
array(//'label' => $acct['Account']['name'],
|
||||
'type' => 'checkbox',
|
||||
'checked' => true,
|
||||
'value' => true,
|
||||
'label' => (" I have exactly " .
|
||||
FormatHelper::currency($acct['Account']['stats']['Ledger']['balance']) .
|
||||
" in " . ($acct['Account']['name'] === 'Cash'
|
||||
? 'Cash'
|
||||
: Inflector::pluralize($acct['Account']['name'])) .
|
||||
" and will be depositing it all.")
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.amount',
|
||||
array('type' => 'hidden',
|
||||
'value' => $acct['Account']['stats']['Ledger']['balance'],
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.account_id',
|
||||
array('type' => 'hidden',
|
||||
'value' => $acct['Account']['id'],
|
||||
));
|
||||
echo "\n";
|
||||
echo $form->input('Tillable.Ledger.'.$acct['CurrentLedger']['id'].'.account_name',
|
||||
array('type' => 'hidden',
|
||||
'value' => $acct['Account']['name'],
|
||||
));
|
||||
echo "\n";
|
||||
|
||||
$grid_div_id = 'ledger_entries'.$acct['CurrentLedger']['id'].'-list';
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'ledger_id' => $acct['CurrentLedger']['id'],
|
||||
'no_account' => true,
|
||||
|
||||
// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'grid_div_id' => $grid_div_id,
|
||||
'caption' => ('<A HREF="#" ONCLICK="$(\'#'.$grid_div_id.' .HeaderButton\').click();'.
|
||||
' return false;">Items in '.$acct['Account']['name'].' Ledger</A>'),
|
||||
'grid_setup' => array('hiddengrid' => true),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
$options = array();
|
||||
foreach ($depositableAccount AS $acct) {
|
||||
$options[$acct['Account']['id']] = $acct['Account']['name'];
|
||||
}
|
||||
|
||||
echo $form->input('Deposit.Account.id', array('label' => 'Deposit Account ',
|
||||
'options' => $options));
|
||||
echo $form->end('Perform Deposit');
|
||||
|
||||
/* End page div */
|
||||
echo '</div>' . "\n";
|
||||
@@ -1,53 +0,0 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<H2>Deposit Slip: ' . date('l, F jS, Y, g:ia') . '</H2>' . "\n";
|
||||
|
||||
//pr(compact('deposit'));
|
||||
|
||||
// Handle account summaries
|
||||
$rows = array();
|
||||
$row_class = array();
|
||||
foreach ($deposit['ledgers'] AS $ledger) {
|
||||
$row_class[] = array();
|
||||
$rows[] = array($ledger['name'].':',
|
||||
FormatHelper::_n(count($ledger['entries']), 'Item'),
|
||||
FormatHelper::currency($ledger['total'], true));
|
||||
}
|
||||
$row_class[] = 'grand';
|
||||
$rows[] = array('Deposit Total:',
|
||||
null,
|
||||
FormatHelper::currency($deposit['total'], true));
|
||||
echo $this->element('table',
|
||||
array('class' => 'deposit-summary',
|
||||
'rows' => $rows,
|
||||
'row_class' => $row_class,
|
||||
'column_class' => array('account', 'quantity', 'total'),
|
||||
'suppress_alternate_rows' => true,
|
||||
));
|
||||
|
||||
|
||||
// Print out the items of each ledger
|
||||
foreach ($deposit['ledgers'] AS $ledger) {
|
||||
//echo ('Count: ' . count($ledger['entries']) . '<BR>');
|
||||
//pr($ledger['entries']);
|
||||
if (count($ledger['entries']) == 0)
|
||||
continue;
|
||||
|
||||
$rows = array();
|
||||
foreach ($ledger['entries'] AS $entry) {
|
||||
$rows[] = array($entry['Customer']['name'],
|
||||
$entry['MonetarySource']['name'],
|
||||
$entry['LedgerEntry']['amount']);
|
||||
}
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item deposit-slip list',
|
||||
'caption' => $ledger['name'] . ' Items',
|
||||
'rows' => $rows,
|
||||
'headers' => array('Customer', 'Item', 'Amount'),
|
||||
'column_class' => array('customer', 'item', 'amount'),
|
||||
));
|
||||
}
|
||||
|
||||
/* End page div */
|
||||
//echo '</div>' . "\n";
|
||||
@@ -9,12 +9,18 @@ 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('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',
|
||||
@@ -56,9 +62,10 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
*/
|
||||
|
||||
echo $this->element('ledgers', array
|
||||
('config' => array
|
||||
('caption' => $account['Account']['name'] . " Ledgers",
|
||||
'rows' => $account['Ledger'],
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => $account['name'] . " Ledgers",
|
||||
'filter' => array('Account.id' => $account['id']),
|
||||
)));
|
||||
|
||||
|
||||
@@ -67,21 +74,37 @@ echo $this->element('ledgers', array
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'ledger_id' => $account['CurrentLedger']['id'],
|
||||
'account_type' => $account['Account']['type'],
|
||||
'group_by_tx' => true,
|
||||
|
||||
// Grid configuration
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' =>
|
||||
"Current Ledger: (" .
|
||||
"#{$account['Account']['id']}" .
|
||||
"-" .
|
||||
"{$account['CurrentLedger']['sequence']}" .
|
||||
")",
|
||||
),
|
||||
));
|
||||
('grid_div_id' => 'ledger-ledger-entry-list',
|
||||
'caption' => "Current Ledger: #{$current_ledger['sequence']}",
|
||||
'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'),
|
||||
'limit' => 50,
|
||||
)));
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* 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'),
|
||||
'limit' => 50,
|
||||
)));
|
||||
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
echo '</div>' . "\n";
|
||||
|
||||
@@ -45,6 +45,7 @@ function contactMethodDiv($obj, $type, $legend, $values = null) {
|
||||
' CLASS="'.$type.'-method-%{id}-source" ' . "\n" .
|
||||
' ID="'.$type.'-method-%{id}-source-'.$stype.'"' . "\n" .
|
||||
' VALUE="'.$stype.'"' . "\n" .
|
||||
($stype == 'new' ? ' CHECKED' . "\n" : '') .
|
||||
' />' . "\n" .
|
||||
' <LABEL FOR="'.$type.'-method-%{id}-source-'.$stype.'">'.$sname.'</LABEL>' . "\n" .
|
||||
' ';
|
||||
@@ -76,21 +77,30 @@ function contactMethodDiv($obj, $type, $legend, $values = null) {
|
||||
'fields' => array
|
||||
(
|
||||
'preference' => array
|
||||
('opts' => array
|
||||
('label_attributes' => array('class' => 'required'),
|
||||
'opts' => array
|
||||
('options' => $obj->varstore['methodPreferences'],
|
||||
'selected' => (isset($values) ? $values['ContactsMethod']['preference'] : null),
|
||||
)),
|
||||
),
|
||||
'after' => "Intended purpose for this method of communication.",
|
||||
),
|
||||
|
||||
'type' => array
|
||||
('opts' => array
|
||||
('label_attributes' => array('class' => 'required'),
|
||||
'opts' => array
|
||||
('options' => $obj->varstore['methodTypes'],
|
||||
'selected' => (isset($values) ? $values['ContactsMethod']['type'] : null),
|
||||
)),
|
||||
),
|
||||
'after' => "How / Where this communication reaches the contact.",
|
||||
),
|
||||
|
||||
'comment' => array
|
||||
('opts' => array
|
||||
('label_attributes' => array('class' => 'optional empty'),
|
||||
'opts' => array
|
||||
('value' => (isset($values) ? $values['ContactsMethod']['comment'] : null),
|
||||
)),
|
||||
),
|
||||
'after' => "Optional: Comments on how this form of communication relates to the contact.",
|
||||
),
|
||||
|
||||
))) . "\n" .
|
||||
|
||||
@@ -113,16 +123,23 @@ function contactMethodTypeDiv($obj, $type, $stype, $values = null) {
|
||||
if ($type === 'phone') {
|
||||
if ($stype === 'existing') {
|
||||
$fields = array
|
||||
('id' => array('name' => 'Phone/Ext',
|
||||
('id' => array('label_attributes' => array('class' => 'required empty'),
|
||||
'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,
|
||||
('type' => array('label_attributes' => array('class' => 'required'),
|
||||
'opts' => array('options' => $obj->varstore['phoneTypes']),
|
||||
'after' => "Physical type of the phone."),
|
||||
'phone' => array('label_attributes' => array('class' => 'required empty'),
|
||||
'after' => "Required: Phone number."),
|
||||
'ext' => array('name' => "Extension",
|
||||
'label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional: Extension number."),
|
||||
'comment' => array('label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional: Comments about this phone number."),
|
||||
);
|
||||
}
|
||||
elseif ($stype === 'show') {
|
||||
@@ -149,12 +166,19 @@ function contactMethodTypeDiv($obj, $type, $stype, $values = null) {
|
||||
}
|
||||
elseif ($stype === 'new') {
|
||||
$fields = array
|
||||
('address' => true,
|
||||
'city' => true,
|
||||
'state' => true,
|
||||
'postcode' => array('name' => 'Zip Code'),
|
||||
'country' => true,
|
||||
'comment' => true,
|
||||
('address' => array('label_attributes' => array('class' => 'required empty'),
|
||||
'after' => "Required: First line of mailing address."),
|
||||
'city' => array('label_attributes' => array('class' => 'required empty'),
|
||||
'after' => "Required."),
|
||||
'state' => array('label_attributes' => array('class' => 'required empty'),
|
||||
'after' => "Required."),
|
||||
'postcode' => array('name' => 'Zip Code',
|
||||
'label_attributes' => array('class' => 'required empty'),
|
||||
'after' => "Required."),
|
||||
'country' => array('label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional: USA is presumed."),
|
||||
'comment' => array('label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional: Comments about this mailing address."),
|
||||
);
|
||||
}
|
||||
elseif ($stype === 'show') {
|
||||
@@ -177,13 +201,16 @@ function contactMethodTypeDiv($obj, $type, $stype, $values = null) {
|
||||
if ($stype === 'existing') {
|
||||
$fields = array
|
||||
('id' => array('name' => 'Email',
|
||||
'label_attributes' => array('class' => 'required'),
|
||||
'opts' => array('options' => $obj->varstore['contactEmails'])),
|
||||
);
|
||||
}
|
||||
elseif ($stype === 'new') {
|
||||
$fields = array
|
||||
('email' => true,
|
||||
'comment' => true,
|
||||
('email' => array('label_attributes' => array('class' => 'required empty'),
|
||||
'after' => "Required: E-mail address."),
|
||||
'comment' => array('label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional: Comments about this email address."),
|
||||
);
|
||||
}
|
||||
elseif ($stype === 'show') {
|
||||
@@ -204,7 +231,7 @@ function contactMethodTypeDiv($obj, $type, $stype, $values = null) {
|
||||
'<div ' . "\n" .
|
||||
' class="'.$type.'-%{id}-div"' . "\n" .
|
||||
' id="'.$type.'-%{id}-'.$stype.'-div"' . "\n" .
|
||||
(isset($values) ? '' : ' STYLE="display:none;"' . "\n") .
|
||||
((isset($values) || $stype == 'new') ? '' : ' STYLE="display:none;"' . "\n") .
|
||||
'>' . "\n" .
|
||||
|
||||
$obj->element
|
||||
@@ -319,8 +346,27 @@ function contactMethodTypeDiv($obj, $type, $stype, $values = null) {
|
||||
.slideDown();
|
||||
}
|
||||
|
||||
function setEmpty(input_elem) {
|
||||
selector = "label[for=" + $(input_elem).attr("id") + "]";
|
||||
if ($(input_elem).val() == '')
|
||||
$(selector).addClass('empty');
|
||||
else
|
||||
$(selector).removeClass('empty');
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
resetForm();
|
||||
|
||||
// In case refresh is hit with populated fields
|
||||
$(":input").each(function(i,elem){ setEmpty(elem); });
|
||||
|
||||
// keyup doesn't catch cut from menu
|
||||
$(":input").live('keyup', function(){
|
||||
setEmpty(this);
|
||||
});
|
||||
$(":input").live('mouseup', function(){
|
||||
setEmpty(this);
|
||||
});
|
||||
});
|
||||
|
||||
--></script>
|
||||
@@ -345,17 +391,30 @@ echo($this->element
|
||||
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'),
|
||||
('last_name' => array('label_attributes' => array('class' => 'recommended empty'),
|
||||
'after' => "Recommended."),
|
||||
'first_name' => array('label_attributes' => array('class' => 'recommended empty'),
|
||||
'after' => "Recommended."),
|
||||
'middle_name' => array('label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional."),
|
||||
'company_name' => array('name' => 'Company',
|
||||
'label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional: Company name, if corporate contact."),
|
||||
'display_name' => array('label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional with first/last name; Required otherwise."),
|
||||
'id_federal' => array('name' => 'SSN',
|
||||
'label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional: Social Security Number."),
|
||||
'id_local' => array('name' => 'ID #',
|
||||
'label_attributes' => array('class' => 'recommended empty'),
|
||||
'after' => "Recommended: Driver's license, for example."),
|
||||
'id_local_state' => array('name' => 'ID State',
|
||||
'label_attributes' => array('class' => 'recommended empty'),
|
||||
'after' => "Recommended: State which issued the ID."),
|
||||
/* 'id_local_exp' => array('name' => 'ID Expiration', */
|
||||
/* 'opts' => array('empty' => true)), */
|
||||
'comment' => true,
|
||||
'comment' => array('label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional: Comments about this contact."),
|
||||
))) . "\n");
|
||||
|
||||
echo $form->submit('Update') . "\n";
|
||||
|
||||
@@ -9,16 +9,25 @@ 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('Display Name', $contact['display_name']);
|
||||
$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 +66,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'] . " / " .
|
||||
@@ -79,7 +88,7 @@ echo $this->element('table',
|
||||
*/
|
||||
$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'] . " " .
|
||||
@@ -103,7 +112,7 @@ echo $this->element('table',
|
||||
*/
|
||||
$headers = array('Email', 'Preference', 'Comment');
|
||||
$rows = array();
|
||||
foreach($contact['ContactEmail'] AS $email) {
|
||||
foreach($emails AS $email) {
|
||||
$rows[] = array($email['email'],
|
||||
$email['ContactsMethod']['preference'] . " / " .
|
||||
$email['ContactsMethod']['type'],
|
||||
@@ -123,9 +132,11 @@ echo $this->element('table',
|
||||
*/
|
||||
|
||||
echo $this->element('customers', array
|
||||
('config' => array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Related Customers',
|
||||
'rows' => $contact['Customer'],
|
||||
'filter' => array('Contact.id' => $contact['id']),
|
||||
'include' => array('Relationship'),
|
||||
)));
|
||||
|
||||
|
||||
|
||||
@@ -42,11 +42,12 @@ function customerContactDiv($obj, $values = null, $primary = false) {
|
||||
' CLASS="contact-%{id}-source" ' . "\n" .
|
||||
' ID="contact-%{id}-source-'.$stype.'"' . "\n" .
|
||||
' VALUE="'.$stype.'"' . "\n" .
|
||||
//' CHECKED' . "\n" .
|
||||
($stype == 'new' ? ' CHECKED' . "\n" : '') .
|
||||
' />' . "\n" .
|
||||
' <LABEL FOR="contact-%{id}-source-'.$stype.'">'.$sname.'</LABEL>' . "\n" .
|
||||
' ';
|
||||
}
|
||||
$div .= "<P>(Phone numbers / Addresses can be added later)";
|
||||
}
|
||||
$div .= "\n";
|
||||
|
||||
@@ -75,23 +76,35 @@ function customerContactDiv($obj, $values = null, $primary = false) {
|
||||
(
|
||||
'Customer.primary_contact_entry' => array
|
||||
('name' => 'Primary Contact',
|
||||
'label_attributes' => array('class' => null),
|
||||
'no_prefix' => true,
|
||||
'opts' => array
|
||||
('type' => 'radio',
|
||||
'options' => array('%{id}' => false),
|
||||
'value' => ($primary ? '%{id}' : 'bogus-value-to-suppress-hidden-input'),
|
||||
)),
|
||||
),
|
||||
'after' => ("Check this button if this contact will be the primary" .
|
||||
" contact for this customer (there can be only one primary" .
|
||||
" contact"),
|
||||
),
|
||||
|
||||
|
||||
'type' => array
|
||||
('opts' => array
|
||||
('label_attributes' => array('class' => 'required'),
|
||||
'opts' => array
|
||||
('options' => $obj->varstore['contactTypes'],
|
||||
'selected' => (isset($values) ? $values['ContactsCustomer']['type'] : null),
|
||||
)),
|
||||
),
|
||||
'after' => "An actual tenant, or just an alternate contact?"
|
||||
),
|
||||
|
||||
'comment' => array
|
||||
('opts' => array
|
||||
('label_attributes' => array('class' => 'optional empty'),
|
||||
'opts' => array
|
||||
('value' => (isset($values) ? $values['ContactsCustomer']['comment'] : null),
|
||||
)),
|
||||
),
|
||||
'after' => "Optional: Comments on the relationship between this customer and this contact."
|
||||
),
|
||||
|
||||
))) . "\n" .
|
||||
|
||||
@@ -115,22 +128,37 @@ function customerContactTypeDiv($obj, $stype, $values = null) {
|
||||
if ($stype === 'existing') {
|
||||
$fields = array
|
||||
('id' => array('name' => 'Contact',
|
||||
'opts' => array('options' => $obj->varstore['contacts'])),
|
||||
'label_attributes' => array('class' => 'required empty'),
|
||||
'opts' => array('options' => $obj->varstore['contacts']),
|
||||
'after' => "Select the existing contact."),
|
||||
);
|
||||
}
|
||||
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'),
|
||||
('last_name' => array('label_attributes' => array('class' => 'recommended empty'),
|
||||
'after' => "Recommended."),
|
||||
'first_name' => array('label_attributes' => array('class' => 'recommended empty'),
|
||||
'after' => "Recommended."),
|
||||
'middle_name' => array('label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional."),
|
||||
'company_name' => array('name' => 'Company',
|
||||
'label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional: Company name, if corporate contact."),
|
||||
'display_name' => array('label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional with first/last name; Required otherwise."),
|
||||
'id_federal' => array('name' => 'SSN',
|
||||
'label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional: Social Security Number."),
|
||||
'id_local' => array('name' => 'ID #',
|
||||
'label_attributes' => array('class' => 'recommended empty'),
|
||||
'after' => "Recommended: Driver's license, for example."),
|
||||
'id_local_state' => array('name' => 'ID State',
|
||||
'label_attributes' => array('class' => 'recommended empty'),
|
||||
'after' => "Recommended: State which issued the ID."),
|
||||
/* 'id_local_exp' => array('name' => 'ID Expiration', */
|
||||
/* 'opts' => array('empty' => true)), */
|
||||
'comment' => true,
|
||||
'comment' => array('label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => "Optional: Comments about this contact."),
|
||||
);
|
||||
}
|
||||
elseif ($stype === 'show') {
|
||||
@@ -151,14 +179,14 @@ function customerContactTypeDiv($obj, $stype, $values = null) {
|
||||
'<div ' . "\n" .
|
||||
' class="contact-%{id}-div"' . "\n" .
|
||||
' id="contact-%{id}-'.$stype.'-div"' . "\n" .
|
||||
(isset($values) ? '' : ' STYLE="display:none;"' . "\n") .
|
||||
((isset($values) || $stype == 'new') ? '' : ' STYLE="display:none;"' . "\n") .
|
||||
'>' . "\n" .
|
||||
|
||||
$obj->element
|
||||
($element,
|
||||
array('class' => "item contact {$class}",
|
||||
'field_prefix' => 'Contact.%{id}')
|
||||
+ compact('rows', 'fields', 'column_class')) .
|
||||
+ compact('rows', 'fields', 'row_class', 'column_class')) .
|
||||
|
||||
($stype === 'show'
|
||||
? '<input type="hidden" name="data[Contact][%{id}][id]" value="'.$values['id'].'"/>' . "\n"
|
||||
@@ -221,8 +249,28 @@ function customerContactTypeDiv($obj, $stype, $values = null) {
|
||||
.slideDown();
|
||||
}
|
||||
|
||||
function setEmpty(input_elem) {
|
||||
selector = "label[for=" + $(input_elem).attr("id") + "]";
|
||||
//$("#debug").append($(input_elem).attr("id") + ": " + $(input_elem).val() + "<BR>");
|
||||
if ($(input_elem).val() == '')
|
||||
$(selector).addClass('empty');
|
||||
else
|
||||
$(selector).removeClass('empty');
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
resetForm();
|
||||
|
||||
// In case refresh is hit with populated fields
|
||||
$(":input").each(function(i,elem){ setEmpty(elem); });
|
||||
|
||||
// keyup doesn't catch cut from menu
|
||||
$(":input").live('keyup', function(){
|
||||
setEmpty(this);
|
||||
});
|
||||
$(":input").live('mouseup', function(){
|
||||
setEmpty(this);
|
||||
});
|
||||
});
|
||||
|
||||
--></script>
|
||||
@@ -247,11 +295,15 @@ echo($this->element
|
||||
array('class' => 'item customer detail',
|
||||
'caption' => isset($this->data['Customer']) ? 'Edit Customer' : 'New Customer',
|
||||
'fields' => array
|
||||
('name' => true,
|
||||
'comment' => true,
|
||||
('name' => array('label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => ("Optional: If this field is left blank, the" .
|
||||
" customer name will be set to the name of" .
|
||||
" the primary contact, below.")),
|
||||
'comment' => array('label_attributes' => array('class' => 'optional empty'),
|
||||
'after' => 'Optional: Comments about this customer.'),
|
||||
))) . "\n");
|
||||
|
||||
echo $form->submit('Update') . "\n";
|
||||
echo $form->submit(isset($this->data['Customer']) ? 'Update' : 'Add New Customer') . "\n";
|
||||
?>
|
||||
|
||||
<div CLASS="dynamic-set">
|
||||
@@ -268,7 +320,12 @@ echo $form->submit('Update') . "\n";
|
||||
<?php
|
||||
; // Alignment
|
||||
|
||||
echo $form->submit('Update') . "\n";
|
||||
if (!empty($movein['Unit']['id']))
|
||||
echo $form->input("movein.Unit.id",
|
||||
array('type' => 'hidden',
|
||||
'value' => $movein['Unit']['id'])) . "\n";
|
||||
|
||||
echo $form->submit(isset($this->data['Customer']) ? 'Update' : 'Add New Customer') . "\n";
|
||||
echo $form->submit('Cancel', array('name' => 'cancel')) . "\n";
|
||||
echo $form->end() . "\n";
|
||||
echo '</div>' . "\n";
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
* Javascript
|
||||
*/
|
||||
|
||||
// Warnings _really_ screw up javascript
|
||||
$saved_debug_state = Configure::read('debug');
|
||||
Configure::write('debug', '0');
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
@@ -23,60 +26,94 @@
|
||||
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,
|
||||
url: "<?php echo $html->url(array('controller' => 'transactions',
|
||||
'action' => 'postReceipt', 0)); ?>"
|
||||
};
|
||||
|
||||
// bind form using 'ajaxForm'
|
||||
if ($('#receipt-form').ajaxForm != null)
|
||||
$('#receipt-form').ajaxForm(options);
|
||||
else
|
||||
$('#repeat, label[for=repeat]').remove();
|
||||
});
|
||||
|
||||
// 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');
|
||||
//$("#debug").html('');
|
||||
for (var i = 0; i < formData.length; ++i) {
|
||||
//$("#debug").append(i + ') ' + dump(formData[i]) + '<BR>');
|
||||
if (formData[i]['name'] == "data[Customer][id]" &&
|
||||
!(formData[i]['value'] > 0)) {
|
||||
//$("#debug").append('<P>Missing Customer ID');
|
||||
alert("Please select a customer first.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (formData[i]['name'] == "data[Transaction][stamp]" &&
|
||||
formData[i]['value'] == '') {
|
||||
//$("#debug").append('<P>Bad Stamp');
|
||||
if (formData[i]['value'] != '')
|
||||
alert(formData[i]['value'] + " is not valid date stamp. Please correct it.");
|
||||
else
|
||||
alert("Please enter a valid date stamp first.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Terrible way to accomplish this...
|
||||
for (var j = 0; j < 20; ++j) {
|
||||
if (formData[i]['name'] == "data[Entry]["+j+"][amount]") {
|
||||
var val = formData[i]['value'].replace(/\$/,'');
|
||||
//$("#debug").append('<P>Bad Amount');
|
||||
if (!(val > 0)) {
|
||||
if (formData[i]['value'] == '')
|
||||
alert("Please enter an amount first.");
|
||||
else
|
||||
alert('"'+formData[i]['value']+'"' + " is not valid amount. Please correct it.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//$("#debug").append('OK');
|
||||
//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
|
||||
$('#results').html('Working <BLINK>...</BLINK>');
|
||||
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') {
|
||||
var amount = 0;
|
||||
$("input.payment.amount").each(function(i) {
|
||||
amount += $(this).val();
|
||||
});
|
||||
|
||||
$('#results').html('<H3>Receipt Saved<BR>' +
|
||||
$("#receipt-customer-name").html() +
|
||||
' : ' + fmtCurrency(amount) +
|
||||
'</H3>');
|
||||
|
||||
if (!$("#repeat").attr("checked")) {
|
||||
window.location.href =
|
||||
"<?php echo $html->url(array('controller' => 'customers',
|
||||
'action' => 'view')); ?>"
|
||||
+ "/" + $("#customer-id").val();
|
||||
return;
|
||||
}
|
||||
|
||||
// get a clean slate
|
||||
//resetForm();
|
||||
resetForm();
|
||||
}
|
||||
else {
|
||||
alert('not successful??');
|
||||
$('#results').html('<H2>Failed to save receipt!</H2>');
|
||||
alert('Failed to save receipt.');
|
||||
}
|
||||
|
||||
|
||||
$('#response-debug').html('<PRE>'+dump(statusText)+'</PRE>');
|
||||
}
|
||||
|
||||
// Reset the form
|
||||
@@ -84,33 +121,43 @@ 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');
|
||||
updateCharges($("#customer-id").val());
|
||||
}
|
||||
|
||||
function updateCharges(id) {
|
||||
$('#charge-entries-jqGrid').clearGridData();
|
||||
$("#receipt-balance").html("Calculating...");
|
||||
$("#receipt-charges-caption").html("Please Wait...");
|
||||
|
||||
var custom = new Array();
|
||||
custom['customer_id'] = id;
|
||||
|
||||
var dynamic_post = new Array();
|
||||
dynamic_post['custom'] = custom;
|
||||
|
||||
$('#charge-entries-jqGrid').setPostDataItem('dynamic_post_replace', serialize(dynamic_post));
|
||||
$('#charge-entries-jqGrid')
|
||||
.setGridParam({ page: 1 })
|
||||
.trigger("reloadGrid");
|
||||
|
||||
var gridstate = $('#charge-entries-jqGrid').getGridParam('gridstate');
|
||||
if (gridstate == 'hidden')
|
||||
$('#charge-entries .HeaderButton').click();
|
||||
}
|
||||
|
||||
function onGridLoadComplete() {
|
||||
var userdata = $('#charge-entries-jqGrid').getGridParam('userData');
|
||||
$('#receipt-balance').html(fmtCurrency(userdata['balance']));
|
||||
$("#receipt-charges-caption").html("Outstanding Charges");
|
||||
}
|
||||
|
||||
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>');
|
||||
// Set the customer name, so the user knows who the receipt is for
|
||||
$("#receipt-customer-name").html($(grid_id).getCell(customer_id, 'Customer-name'));
|
||||
$("#receipt-balance").html("Calculating...");
|
||||
$("#receipt-charges-caption").html("Please Wait...");
|
||||
|
||||
// Hide the "no customer" message and show the current customer
|
||||
$(".customer-selection-invalid").hide();
|
||||
@@ -143,12 +190,18 @@ function addPaymentSource(flash) {
|
||||
addDiv('payment-entry-id', 'payment', 'payments', flash,
|
||||
// HTML section
|
||||
'<FIELDSET CLASS="payment subset">' +
|
||||
'<LEGEND>Payment #%{id} (%{remove})</LEGEND>' +
|
||||
<?php /* '<LEGEND>Payment #%{id} (%{remove})</LEGEND>' + */ ?>
|
||||
'<LEGEND>Payment</LEGEND>' +
|
||||
|
||||
'<DIV ID="payment-div-%{id}">' +
|
||||
<?php
|
||||
// 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('LedgerEntry.%{id}.account_id',
|
||||
($form->input('Entry.%{id}.tender_type_id',
|
||||
array('type' => 'radio',
|
||||
'separator' => '<BR>',
|
||||
'onclick' => ('switchPaymentType(' .
|
||||
@@ -160,73 +213,48 @@ function addPaymentSource(flash) {
|
||||
''
|
||||
),
|
||||
'legend' => false,
|
||||
'value' => $defaultAccount,
|
||||
'options' => $paymentAccounts))) . "+\n";
|
||||
'value' => $defaultType,
|
||||
'options' => $radioOptions))) . "+\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]"' +
|
||||
' NAME="data[Entry][%{id}][amount]"' +
|
||||
' CLASS="payment amount"' +
|
||||
' ID="payment-amount-%{id}" />' +
|
||||
' <LABEL CLASS="payment" FOR="payment-amount-%{id}">Amount</LABEL>' +
|
||||
'</DIV>' +
|
||||
|
||||
<?php
|
||||
foreach ($paymentAccounts AS $account_id => $name) {
|
||||
foreach ($paymentTypes AS $type) {
|
||||
$type = $type['TenderType'];
|
||||
$div = '<DIV';
|
||||
$div .= ' ID="payment-type-div-%{id}-'.$account_id.'"';
|
||||
$div .= ' ID="payment-type-div-%{id}-'.$type['id'].'"';
|
||||
$div .= ' CLASS="payment-type-div-%{id}"';
|
||||
$div .= ' STYLE="display:none;">';
|
||||
if ($type['id'] != $defaultType)
|
||||
$div .= ' STYLE="display:none;"';
|
||||
$div .= '>';
|
||||
|
||||
if ($name == 'Check') {
|
||||
$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 .= ' <LABEL FOR="payment-check-number-%{id}">Check Number</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="6" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data1]"';
|
||||
$div .= ' ID="payment-check-number-%{id}" />';
|
||||
$div .= ' <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>';
|
||||
}
|
||||
elseif ($name == 'Money Order') {
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <LABEL FOR="payment-moneyorder-number-%{id}">Money Order Number</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="6" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data1]"';
|
||||
$div .= ' ID="payment-moneyorder-number-%{id}" />';
|
||||
$div .= '</DIV>';
|
||||
}
|
||||
elseif ($name == 'ACH') {
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <LABEL FOR="payment-ach-routing-%{id}">Routing Number</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="9" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data1]"';
|
||||
$div .= ' ID="payment-ach-routing-%{id}" />';
|
||||
$div .= '</DIV>';
|
||||
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <LABEL FOR="payment-ach-account-%{id}">Account Number</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="17" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data2]"';
|
||||
$div .= ' ID="payment-ach-account-%{id}" />';
|
||||
$div .= '</DIV>';
|
||||
}
|
||||
elseif ($name == 'Credit Card') {
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <LABEL FOR="payment-creditcard-account-%{id}">Account Number</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="16" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data1]"';
|
||||
$div .= ' ID="payment-creditcard-account-%{id}" />';
|
||||
$div .= '</DIV>';
|
||||
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <LABEL FOR="payment-creditcard-expiration-%{id}">Expiration Date</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="10" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data2]"';
|
||||
$div .= ' ID="payment-creditcard-expiration-%{id}" />';
|
||||
$div .= ' </DIV>';
|
||||
|
||||
$div .= '<DIV CLASS="input text required">';
|
||||
$div .= ' <LABEL FOR="payment-creditcard-cvv2-%{id}">CVV2 Code</LABEL>';
|
||||
$div .= ' <INPUT TYPE="text" SIZE="10" NAME="data[LedgerEntry][%{id}][acct]['.$account_id.'][data3]"';
|
||||
$div .= ' ID="payment-creditcard-cvv2-%{id}" />';
|
||||
$div .= '</DIV>';
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
|
||||
$div .= '</DIV>';
|
||||
@@ -239,48 +267,20 @@ function addPaymentSource(flash) {
|
||||
}
|
||||
|
||||
function switchPaymentType(paymentid_base, paymentid, radioid) {
|
||||
$("."+paymentid_base+"-"+paymentid).slideUp();
|
||||
var account_id = $("#"+radioid).val();
|
||||
$("#"+paymentid_base+"-"+paymentid+"-"+account_id).slideDown();
|
||||
var type_id = $("#"+radioid).val();
|
||||
$("."+paymentid_base+"-"+paymentid+
|
||||
":not(" +
|
||||
"#"+paymentid_base+"-"+paymentid+"-"+type_id +
|
||||
")").slideUp();
|
||||
$("#"+paymentid_base+"-"+paymentid+"-"+type_id).slideDown();
|
||||
}
|
||||
|
||||
|
||||
function updateChargesGrid(idlist) {
|
||||
$('#charge-entries-jqGrid').setPostDataItem('idlist', serialize(idlist));
|
||||
$('#charge-entries-jqGrid')
|
||||
.setGridParam({ page: 1 })
|
||||
.trigger("reloadGrid");
|
||||
}
|
||||
|
||||
function updateCharges(id) {
|
||||
var url = '<?php echo ($html->url(array("controller" => $this->params["controller"],
|
||||
"action" => "unreconciled"))); ?>';
|
||||
url += '/'+id;
|
||||
|
||||
$('#charge-entries-jqGrid').clearGridData();
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: url,
|
||||
dataType: "xml",
|
||||
success: function(xml) {
|
||||
var ids = new Array();
|
||||
$('entry',xml).each(function(i){
|
||||
ids.push($(this).attr('id'));
|
||||
});
|
||||
$('#receipt-balance').html(fmtCurrency($('entries',xml).attr('balance')));
|
||||
$("#receipt-charges-caption").html("Outstanding Charges");
|
||||
updateChargesGrid(ids);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
--></script>
|
||||
|
||||
<?php
|
||||
; // align
|
||||
//echo '<DIV ID="dialog">' . "\n";
|
||||
// Re-Enable warnings
|
||||
Configure::write('debug', $saved_debug_state);
|
||||
|
||||
echo $this->element('customers', array
|
||||
('config' => array
|
||||
@@ -296,6 +296,7 @@ echo $this->element('customers', array
|
||||
array('gridstate' =>
|
||||
'onGridState("#"+$(this).attr("id"), gridstate)'),
|
||||
),
|
||||
'action' => 'current',
|
||||
'nolinks' => true,
|
||||
'limit' => 10,
|
||||
)));
|
||||
@@ -303,8 +304,7 @@ echo $this->element('customers', array
|
||||
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>' .
|
||||
'Customer: <SPAN id="receipt-customer-name"></SPAN>' .
|
||||
|
||||
/* '<DIV CLASS="supporting">' . */
|
||||
/* '<TABLE>' . */
|
||||
@@ -321,18 +321,20 @@ echo ('<DIV CLASS="receipt grid-selection-text">' .
|
||||
'</DIV>' . "\n");
|
||||
|
||||
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'account_ftype' => 'credit',
|
||||
'limit' => 8,
|
||||
|
||||
// Grid configuration
|
||||
echo $this->element('statement_entries', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
(
|
||||
'grid_div_id' => 'charge-entries',
|
||||
'grid_div_class' => 'text-below',
|
||||
'grid_events' => array('loadComplete' => 'onGridLoadComplete()'),
|
||||
'grid_setup' => array('hiddengrid' => true),
|
||||
'caption' => '<SPAN id="receipt-charges-caption"></SPAN>',
|
||||
'rows' => $charges['entry'],
|
||||
'action' => 'unreconciled',
|
||||
'exclude' => array('Customer', 'Type', 'Debit', 'Credit'),
|
||||
'include' => array('Applied', 'Balance'),
|
||||
'remap' => array('Applied' => 'Paid'),
|
||||
'limit' => 8,
|
||||
),
|
||||
));
|
||||
|
||||
@@ -371,83 +373,65 @@ echo $this->element('form_table',
|
||||
),
|
||||
)));
|
||||
|
||||
echo "<BR>\n";
|
||||
echo $form->input('repeat', array('type' => 'checkbox',
|
||||
'id' => 'repeat',
|
||||
'label' => 'Enter Multiple Receipts')) . "\n";
|
||||
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>
|
||||
<div id="results"></div>
|
||||
<div id="output-debug" style="display:none"></div>
|
||||
|
||||
<?php
|
||||
// Warnings _really_ screw up javascript
|
||||
Configure::write('debug', '0');
|
||||
?>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
$(document).ready(function(){
|
||||
$("#TransactionStamp")
|
||||
.attr('autocomplete', 'off')
|
||||
.datepicker({ constrainInput: true,
|
||||
numberOfMonths: [1, 1],
|
||||
showCurrentAtPos: 0,
|
||||
dateFormat: 'mm/dd/yy' });
|
||||
datepicker('TransactionStamp');
|
||||
|
||||
resetForm();
|
||||
$("#customer-id").val(0);
|
||||
$("#receipt-customer-name").html("INTERNAL ERROR");
|
||||
$("#receipt-balance").html("INTERNAL ERROR");
|
||||
$("#receipt-charges-caption").html("Outstanding Charges");
|
||||
|
||||
<?php if (isset($customer['id'])): ?>
|
||||
$("#customer-id").val(<?php echo $customer['id']; ?>);
|
||||
//$("#receipt-customer-id").html("<?php echo '#'.$customer['id']; ?>");
|
||||
$("#receipt-customer-id").html('<A HREF="/pmgr/site/customers/view/' +
|
||||
"<?php echo $customer['id']; ?>" +
|
||||
'">#' +
|
||||
"<?php echo $customer['id']; ?>" +
|
||||
'</A>');
|
||||
$("#receipt-customer-name").html("<?php echo $customer['name']; ?>");
|
||||
$("#receipt-balance").html(fmtCurrency("<?php echo $charges['balance']; ?>"));
|
||||
$("#receipt-balance").html(fmtCurrency("<?php echo $stats['balance']; ?>"));
|
||||
onGridState(null, 'hidden');
|
||||
<?php else: ?>
|
||||
onGridState(null, 'visible');
|
||||
<?php endif; ?>
|
||||
|
||||
resetForm();
|
||||
datepickerNow('TransactionStamp');
|
||||
|
||||
|
||||
/* $("#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'); */
|
||||
/* }); */
|
||||
<?php if ($this->params['dev']): ?>
|
||||
$('#output-debug').html('Post Output');
|
||||
$('#output-debug').show();
|
||||
<?php endif; ?>
|
||||
|
||||
});
|
||||
--></script>
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#" onClick="$('#debug').html(''); return false;">Clear Debug Output</a>
|
||||
|
||||
@@ -9,8 +9,12 @@ 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('Since', FormatHelper::date($since, true));
|
||||
if (!empty($until))
|
||||
$rows[] = array('Until', FormatHelper::date($until, true));
|
||||
$rows[] = array('Comment', $customer['Customer']['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item customer detail',
|
||||
@@ -26,7 +30,9 @@ echo $this->element('table',
|
||||
echo '<div class="infobox">' . "\n";
|
||||
$rows = array();
|
||||
$rows[] = array('Security Deposit:', FormatHelper::currency($outstandingDeposit));
|
||||
$rows[] = array('Balance:', FormatHelper::currency($outstandingBalance));
|
||||
//$rows[] = array('Charges:', FormatHelper::currency($stats['charges']));
|
||||
//$rows[] = array('Payments:', FormatHelper::currency($stats['disbursements']));
|
||||
$rows[] = array('Balance Owed:', FormatHelper::currency($outstandingBalance));
|
||||
echo $this->element('table',
|
||||
array('class' => 'summary',
|
||||
'rows' => $rows,
|
||||
@@ -51,9 +57,11 @@ echo '<div CLASS="detail supporting">' . "\n";
|
||||
*/
|
||||
|
||||
echo $this->element('contacts', array
|
||||
('config' => array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Customer Contacts',
|
||||
'rows' => $customer['Contact'],
|
||||
'filter' => array('Customer.id' => $customer['Customer']['id']),
|
||||
'include' => array('Relationship'),
|
||||
)));
|
||||
|
||||
|
||||
@@ -62,9 +70,13 @@ echo $this->element('contacts', array
|
||||
*/
|
||||
|
||||
echo $this->element('leases', array
|
||||
('config' => array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Lease History',
|
||||
'rows' => $customer['Lease'],
|
||||
'filter' => array('Customer.id' => $customer['Customer']['id']),
|
||||
'exclude' => array('Customer'),
|
||||
'sort_column' => 'Move-In',
|
||||
'sort_order' => 'DESC',
|
||||
)));
|
||||
|
||||
|
||||
@@ -72,16 +84,32 @@ echo $this->element('leases', array
|
||||
* Customer Account History
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Element configuration
|
||||
'customer_id' => $customer['Customer']['id'],
|
||||
'ar_account' => true,
|
||||
|
||||
// Grid configuration
|
||||
echo $this->element('statement_entries', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Account',
|
||||
('caption' => 'Customer Statement',
|
||||
'filter' => array('Customer.id' => $customer['Customer']['id'],
|
||||
'type !=' => 'VOID'),
|
||||
'exclude' => array('Customer'),
|
||||
)));
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Receipt History
|
||||
*/
|
||||
|
||||
echo $this->element('ledger_entries', array
|
||||
(// Grid configuration
|
||||
'config' => array
|
||||
('caption' => 'Receipts',
|
||||
'filter' => array('Customer.id' => $customer['Customer']['id'],
|
||||
'Transaction.type' => 'RECEIPT',
|
||||
'Tender.id !=' => null,
|
||||
//'Account.id !=' => '-AR-'
|
||||
),
|
||||
));
|
||||
'include' => array('Transaction'),
|
||||
'exclude' => array('Entry', 'Account', 'Cr/Dr'),
|
||||
)));
|
||||
|
||||
|
||||
/* End "detail supporting" div */
|
||||
|
||||
98
site/views/double_entries/view.ctp
Normal file
98
site/views/double_entries/view.ctp
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
echo '<div class="double-entry view">' . "\n";
|
||||
|
||||
// The two entries, debit and credit, are actually individual
|
||||
// entries in separate accounts (each make up one of the two
|
||||
// entries required for "double entry").
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
**********************************************************************
|
||||
* DoubleEntry Detail Main Section
|
||||
*/
|
||||
|
||||
$transaction = $entry['Transaction'];
|
||||
$ledgers = array('debit' => $entry['DebitLedger'],
|
||||
'credit' => $entry['CreditLedger']);
|
||||
$entries = array('debit' => $entry['DebitEntry'],
|
||||
'credit' => $entry['CreditEntry']);
|
||||
$entry = $entry['DoubleEntry'];
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array('Transaction', $html->link('#'.$transaction['id'],
|
||||
array('controller' => 'transactions',
|
||||
'action' => 'view',
|
||||
$transaction['id'])));
|
||||
$rows[] = array('Timestamp', FormatHelper::datetime($transaction['stamp']));
|
||||
$rows[] = array('Comment', $entry['comment']);
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => 'item double-entry detail',
|
||||
'caption' => 'Double Ledger Entry',
|
||||
'rows' => $rows,
|
||||
'column_class' => array('field', 'value')));
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* Debit/Credit Entries
|
||||
*/
|
||||
|
||||
echo ('<DIV CLASS="ledger-double-entry">' . "\n");
|
||||
foreach ($ledgers AS $type => $ledger) {
|
||||
$rows = array();
|
||||
|
||||
// REVISIT <AP>: 20090816
|
||||
// Due to low priority, the ledger_entry/double_entry stuff
|
||||
// is a bit piecemeal at the moment (trying to reuse old
|
||||
// code as much as possible). So, LedgerEntry view is just
|
||||
// redirecting here. Of course, presenting a link for the
|
||||
// LedgerEntry then is, well, quite pointless.
|
||||
$rows[] = array('ID', '#' . $entries[$type]['id']);
|
||||
/* $rows[] = array('ID', $html->link('#' . $entries[$type]['id'], */
|
||||
/* array('controller' => 'entries', */
|
||||
/* 'action' => 'view', */
|
||||
/* $entries[$type]['id']))); */
|
||||
$rows[] = array('Account', ($ledger['link']
|
||||
? $html->link($ledger['Account']['name'],
|
||||
array('controller' => 'accounts',
|
||||
'action' => 'view',
|
||||
$ledger['Account']['id']))
|
||||
: $ledger['Account']['name']));
|
||||
$rows[] = array('Ledger', ($ledger['link']
|
||||
? $html->link('#' . $ledger['sequence'],
|
||||
array('controller' => 'ledgers',
|
||||
'action' => 'view',
|
||||
$ledger['id']))
|
||||
: '#' . $ledger['sequence']));
|
||||
$rows[] = array('Amount', FormatHelper::currency($entries[$type]['amount']));
|
||||
//$rows[] = array('Effect', $ledger['Account']['ftype'] == $type ? 'INCREASE' : 'DECREASE');
|
||||
|
||||
echo $this->element('table',
|
||||
array('class' => array('item', $type, 'detail'),
|
||||
'caption' => ucfirst($type) . ' 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";
|
||||
@@ -2,22 +2,19 @@
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['ID'] = array('index' => 'Account.id', 'formatter' => 'id');
|
||||
$cols['Name'] = array('index' => 'Account.name', 'formatter' => 'longname');
|
||||
$cols['Type'] = array('index' => 'Account.type', 'formatter' => 'name');
|
||||
$cols['Entries'] = array('index' => 'entries', 'width' => '60', 'align' => 'right');
|
||||
$cols['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');
|
||||
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Name'));
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Name')
|
||||
->defaultFields(array('ID', 'Name'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
->defaultFields(array('Name'))
|
||||
->searchFields(array('Name'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Comment')));
|
||||
|
||||
@@ -2,23 +2,18 @@
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['ID'] = array('index' => 'Contact.id', 'formatter' => 'id');
|
||||
$cols['Relationship'] = array('index' => 'ContactsCustomer.type', 'formatter' => 'enum');
|
||||
$cols['Name'] = array('index' => 'Contact.display_name', 'formatter' => 'longname');
|
||||
$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');
|
||||
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Last Name', 'First Name'));
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Last Name')
|
||||
->defaultFields(array('ID', 'Last Name', 'First Name'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
->defaultFields(array('Last Name', 'First Name'))
|
||||
->searchFields(array('Last Name', 'First Name', 'Company'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Relationship', 'Comment')));
|
||||
|
||||
@@ -2,23 +2,19 @@
|
||||
|
||||
// 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['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' => 'lease_count', 'width' => '60');
|
||||
$cols['Leases'] = array('index' => 'current_lease_count', 'formatter' => 'number');
|
||||
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency');
|
||||
$cols['Comment'] = array('index' => 'Customer.comment', 'formatter' => 'comment');
|
||||
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Last Name', 'First Name'));
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Name')
|
||||
->defaultFields(array('ID', 'Name'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
->defaultFields(array('Name'))
|
||||
->searchFields(array('Name', 'Last Name', 'First Name'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Relationship', 'Comment')));
|
||||
|
||||
@@ -32,7 +32,7 @@ foreach ($fields AS $field => $config) {
|
||||
$include_after = true;
|
||||
}
|
||||
|
||||
|
||||
if (empty($column_class))
|
||||
$column_class = array();
|
||||
if ($include_before)
|
||||
$column_class[] = 'before';
|
||||
@@ -79,7 +79,13 @@ foreach ($fields AS $field => $config) {
|
||||
$cells[] = null;
|
||||
}
|
||||
|
||||
if (empty($config['opts']['label']))
|
||||
$name = $form->label($field, $config['name'],
|
||||
empty($config['label_attributes'])
|
||||
? null : $config['label_attributes']);
|
||||
else
|
||||
$name = $config['name'];
|
||||
|
||||
if (isset($config['with_name_before']))
|
||||
$name = $config['with_name_before'] . $name;
|
||||
elseif (isset($with_name_before))
|
||||
@@ -123,7 +129,7 @@ foreach ($fields AS $field => $config) {
|
||||
}
|
||||
|
||||
echo $this->element('table',
|
||||
compact('class', 'caption', 'headers',
|
||||
compact('id', 'class', 'caption', 'headers',
|
||||
'rows', 'row_class', 'suppress_alternate_rows',
|
||||
'column_class')
|
||||
);
|
||||
|
||||
@@ -19,6 +19,7 @@ if (!isset($limitOptions)) {
|
||||
}
|
||||
sort($limitOptions, SORT_NUMERIC);
|
||||
$limitOptions = array_unique($limitOptions, SORT_NUMERIC);
|
||||
//$limitOptions[] = 'ALL'; // Would be nice... jqGrid shows 'NaN of NaN'
|
||||
|
||||
if (!isset($height))
|
||||
$height = 'auto';
|
||||
@@ -46,12 +47,9 @@ if (!isset($grid_setup))
|
||||
$grid_setup = array();
|
||||
|
||||
// Do some prework to bring in the appropriate libraries
|
||||
$imgpath = '/pmgr/site/css/jqGrid/basic/images';
|
||||
$html->css('jqGrid/basic/grid', null, null, false);
|
||||
$html->css('jqGrid/jqModal', null, null, false);
|
||||
$javascript->link('jqGrid/jquery.jqGrid.js', false);
|
||||
$javascript->link('jqGrid/js/jqModal', false);
|
||||
$javascript->link('jqGrid/js/jqDnR', false);
|
||||
$html->css('ui.jqgrid', null, null, false);
|
||||
$javascript->link('jqGrid/grid.locale-en', false);
|
||||
$javascript->link('jqGrid/jquery.jqGrid.min', false);
|
||||
$javascript->link('pmgr_jqGrid', false);
|
||||
|
||||
|
||||
@@ -62,63 +60,74 @@ $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',
|
||||
'debug' => 0,
|
||||
'action' => 'gridData',
|
||||
));
|
||||
|
||||
// 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',
|
||||
$postData['fields'] = array_map(create_function('$col',
|
||||
'return $col["index"];'),
|
||||
array_values($jqGridColumns)));
|
||||
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);
|
||||
}
|
||||
|
||||
// 'action' will ensure that the controller provides the
|
||||
// correct subset of data
|
||||
$postData['action'] = $action;
|
||||
|
||||
if (isset($nolinks)) {
|
||||
if (isset($nolinks))
|
||||
$postData['nolinks'] = true;
|
||||
}
|
||||
|
||||
// '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['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'] === 'percentage') {
|
||||
$col['formatter'] = array('--special' => 'percentageFormatter');
|
||||
$default['width'] = 60;
|
||||
$default['align'] = 'right';
|
||||
}
|
||||
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'] = 85;
|
||||
$default['align'] = 'right';
|
||||
@@ -128,21 +137,45 @@ foreach ($jqGridColumns AS &$col) {
|
||||
$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 (preg_match("/^(long|short)?enum$/",
|
||||
$col['formatter'], $matches)) {
|
||||
$default['width'] = 60;
|
||||
if (!empty($matches[1]) && $matches[1] === 'long')
|
||||
$default['width'] *= 1.5;
|
||||
if (!empty($matches[1]) && $matches[1] === 'short')
|
||||
$default['width'] *= 0.7;
|
||||
|
||||
//$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) * 7;
|
||||
$min_width = 0; // REVISIT <AP>: 20090829; if/while jqGrid is fixed width
|
||||
if ((!isset($default['width']) || $default['width'] < $min_width) && !$default['force'])
|
||||
$default['width'] = $min_width;
|
||||
}
|
||||
|
||||
$col = array_merge($default, $col);
|
||||
@@ -164,9 +197,12 @@ if (isset($sort_order)) {
|
||||
$sortorder = 'ASC';
|
||||
}
|
||||
|
||||
if (1) { // debug
|
||||
$caption .= ' :: <span id="'.$grid_id.'-query"></span>';
|
||||
}
|
||||
$debug = !empty($this->params['dev']);
|
||||
if ($debug)
|
||||
$caption .= '<span class="debug grid-query"> :: <span id="'.$grid_id.'-query"></span></span>';
|
||||
|
||||
$caption .= ('<span class="grid-error" id="'.$grid_id.'-error"' .
|
||||
' style="display:none"> :: Error (Please Reload)</span>');
|
||||
|
||||
foreach (array_merge(array('loadComplete' => '', 'loadError' => ''),
|
||||
$grid_events) AS $event => $statement) {
|
||||
@@ -176,13 +212,21 @@ foreach (array_merge(array('loadComplete' => '', 'loadError' => ''),
|
||||
$statement = current($statement);
|
||||
}
|
||||
|
||||
if ($event == 'loadComplete') {
|
||||
if ($event == 'loadComplete' && $debug) {
|
||||
$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;}");
|
||||
array('--special' => "function($params) {url=jQuery('#{$grid_id}').getGridParam('url');url=url+'/debug:1?'; 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') {
|
||||
elseif ($event == 'loadError' && $debug) {
|
||||
$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;}");
|
||||
array('--special' => "function($params) {url=jQuery('#{$grid_id}').getGridParam('url');url=url+'/debug:1?'; 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;}");
|
||||
}
|
||||
elseif ($event == 'loadComplete' && !$debug) {
|
||||
$grid_events[$event] =
|
||||
array('--special' => "function($params) {jQuery('#{$grid_id}-error').hide(); $statement;}");
|
||||
}
|
||||
elseif ($event == 'loadError' && !$debug) {
|
||||
$grid_events[$event] =
|
||||
array('--special' => "function($params) {jQuery('#{$grid_id}-error').show(); $statement;}");
|
||||
}
|
||||
else {
|
||||
$grid_events[$event] =
|
||||
@@ -196,17 +240,20 @@ $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,
|
||||
'width' => 700,
|
||||
'rowNum' => $limit,
|
||||
'rowList' => $limitOptions,
|
||||
'sortname' => $sortname,
|
||||
'sortorder' => $sortorder,
|
||||
'caption' => $caption,
|
||||
'imgpath' => $imgpath,
|
||||
'viewrecords' => true,
|
||||
'gridview' => true,
|
||||
'pager' => $grid_id.'-pager',
|
||||
),
|
||||
$grid_events,
|
||||
@@ -219,67 +266,46 @@ $jqGrid_setup = array_merge
|
||||
// to kick this thing off.
|
||||
?>
|
||||
|
||||
<?php if ($first_grid): ?>
|
||||
<script type="text/javascript"><!--
|
||||
var currencyFormatter = function(cellval, opts, rowObject) {
|
||||
if (!cellval)
|
||||
return "";
|
||||
return fmtCurrency(cellval);
|
||||
}
|
||||
|
||||
var percentageFormatter = function(cellval, opts, rowObject) {
|
||||
var precision;
|
||||
if (typeof(opts.colModel) != 'undefined' &&
|
||||
typeof(opts.colModel.formatoptions) != 'undefined' &&
|
||||
typeof(opts.colModel.formatoptions.precision) != 'undefined')
|
||||
precision = opts.colModel.formatoptions.precision;
|
||||
else
|
||||
precision = 0;
|
||||
amount = cellval.toString().replace(/\%/g,'');
|
||||
amount = (amount*100).toFixed(precision);
|
||||
return amount+'%';
|
||||
}
|
||||
|
||||
var idFormatter = function(cellval, opts, rowObject) {
|
||||
if (!cellval)
|
||||
return cellval;
|
||||
return '#'+cellval;
|
||||
}
|
||||
--></script>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<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>
|
||||
<script type="text/javascript"><!--
|
||||
|
||||
jQuery(document).ready(function(){
|
||||
currencyFormatter = function(el, cellval, opts) {
|
||||
if (!cellval)
|
||||
return;
|
||||
$(el).html(fmtCurrency(cellval));
|
||||
}
|
||||
|
||||
idFormatter = function(el, cellval, opts) {
|
||||
if (!cellval)
|
||||
return;
|
||||
$(el).html('#'+cellval);
|
||||
}
|
||||
|
||||
jQuery('#<?php echo $grid_id; ?>').jqGrid(
|
||||
<?php echo FormatHelper::phpVarToJavascript($jqGrid_setup); ?>
|
||||
).navGrid('#<?php echo $grid_id; ?>-pager',
|
||||
{ view:false,
|
||||
edit:false,
|
||||
add:false,
|
||||
del:false,
|
||||
search:true,
|
||||
refresh:true});
|
||||
|
||||
<?php
|
||||
/* jQuery('#t_<?php echo $grid_id; ?>').height(25).hide() */
|
||||
/* .filterGrid('#<?php echo $grid_id; ?>', { */
|
||||
/* gridModel:true, */
|
||||
/* gridToolbar:true, */
|
||||
/* autosearch:true, */
|
||||
/* }); */
|
||||
|
||||
/* jQuery('#<?php echo $grid_id; ?>').navGrid('#<?php echo $grid_id; ?>-pager', */
|
||||
/* { view:false, */
|
||||
/* edit:false, */
|
||||
/* add:false, */
|
||||
/* del:false, */
|
||||
/* search:false, */
|
||||
/* refresh:false}) */
|
||||
/* .navButtonAdd('#<?php echo $grid_id; ?>-pager',{ */
|
||||
/* caption:"Search", */
|
||||
/* title:"Toggle Search", */
|
||||
/* buttonimg:'<?php echo $imgpath; ?>' + '/find.gif', */
|
||||
/* onClickButton:function(){ */
|
||||
/* if(jQuery('#t_<?php echo $grid_id; ?>').css("display")=="none") { */
|
||||
/* jQuery('#t_<?php echo $grid_id; ?>').css("display",""); */
|
||||
/* } else { */
|
||||
/* jQuery('#t_<?php echo $grid_id; ?>').css("display","none"); */
|
||||
/* } */
|
||||
/* } */
|
||||
/* }); */
|
||||
?>
|
||||
|
||||
<?php echo FormatHelper::phpVarToJavascript($jqGrid_setup) . "\n"; ?>
|
||||
).navGrid('#<?php echo $grid_id; ?>-pager', { view:false,edit:false,add:false,del:false,search:true,refresh:true});
|
||||
});
|
||||
|
||||
--></script>
|
||||
|
||||
<?php
|
||||
if (count($search_fields) > 0) {
|
||||
echo('<div>Search By:<BR>' . "\n");
|
||||
|
||||
@@ -2,25 +2,35 @@
|
||||
|
||||
// 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['Unit'] = array('index' => 'Unit.name', 'formatter' => 'shortname', '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['Rent'] = array('index' => 'Lease.rent', 'formatter' => 'currency');
|
||||
$cols['Deposit'] = array('index' => 'Lease.deposit', 'formatter' => 'currency');
|
||||
$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['Closed'] = array('index' => 'Lease.close_date', 'formatter' => 'date');
|
||||
$cols['Charge-Thru'] = array('index' => 'Lease.charge_through_date', 'formatter' => 'date');
|
||||
$cols['Paid-Thru'] = array('index' => 'Lease.paid_through_date', 'formatter' => 'date');
|
||||
$cols['Status'] = array('index' => 'status', 'formatter' => 'longenum');
|
||||
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency');
|
||||
$cols['Comment'] = array('index' => 'Lease.comment', 'formatter' => 'comment');
|
||||
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Customer', 'Unit'));
|
||||
if (!empty($this->params['action'])) {
|
||||
if ($this->params['action'] === 'closed')
|
||||
$grid->invalidFields(array('Charge-Thru', 'Paid-Thru', 'Status'));
|
||||
elseif ($this->params['action'] === 'active')
|
||||
$grid->invalidFields(array('Closed'));
|
||||
elseif ($this->params['action'] === 'delinquent')
|
||||
$grid->invalidFields(array('Closed'));
|
||||
}
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('LeaseID')
|
||||
->defaultFields(array('LeaseID', 'Lease'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
->sortField('Lease')
|
||||
->defaultFields(array('Lease'))
|
||||
->searchFields(array('Customer', 'Unit'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Signed', 'Charge-Thru', 'Status', 'Comment')));
|
||||
|
||||
@@ -4,136 +4,27 @@
|
||||
$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['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);
|
||||
|
||||
$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);
|
||||
|
||||
$cols['Last Payment'] = array('index' => 'last_paid', 'formatter' => 'date');
|
||||
$cols['Applied'] = array('index' => "applied", 'formatter' => 'currency');
|
||||
$cols['Sub-Total'] = array('index' => 'subtotal-LedgerEntry.amount', 'formatter' => 'currency', 'sortable' => false);
|
||||
|
||||
|
||||
// Since group_by_tx is a boolean, let's just get it
|
||||
// defined, regardless of whether the caller did so.
|
||||
// group_by_tx will cause all entry fields to be
|
||||
// invalidated, and will leave only the transaction
|
||||
// fields. Yes... the caller should just use the
|
||||
// transactions element instead, in theory. However,
|
||||
// it hasn't yet been implemented to the level of
|
||||
// this element, and additionally, the transactions
|
||||
// element will not allow for customer information
|
||||
// (rightly so, since it's a ledger_entry field).
|
||||
// However, at the current implementation, all ledger
|
||||
// entries of a transaction are for the same customer.
|
||||
// So... we allow it for now.
|
||||
if (!isset($group_by_tx))
|
||||
$group_by_tx = false;
|
||||
|
||||
// REVISIT <AP>: 20090715
|
||||
// If we really want to group by transaction, we need
|
||||
// a transaction listing, not a ledger_entry listing.
|
||||
// switch controllers... don't overload this one.
|
||||
$group_by_tx = false;
|
||||
|
||||
if (isset($transaction_id) || isset($reconcile_id))
|
||||
$grid->invalidFields('Transaction');
|
||||
|
||||
if ($group_by_tx)
|
||||
$grid->invalidFields('Entry');
|
||||
|
||||
if ($group_by_tx)
|
||||
$grid->invalidFields(array('Effective', 'Through'));
|
||||
|
||||
if (!isset($collected_account_id))
|
||||
$grid->invalidFields('Last Payment');
|
||||
|
||||
if (isset($account_ftype) || isset($ledger_id) || isset($account_id) || isset($ar_account))
|
||||
$grid->invalidFields(array('Debit Account', 'Credit Account'));
|
||||
else
|
||||
$grid->invalidFields('Account');
|
||||
|
||||
if (isset($no_account) || $group_by_tx || isset($collected_account_id))
|
||||
$grid->invalidFields(array('Account', 'Debit Account', 'Credit Account'));
|
||||
|
||||
if (isset($ledger_id) || isset($account_id) || isset($ar_account)) {
|
||||
$grid->invalidFields('Amount');
|
||||
$cols['Sub-Total']['index'] = 'subtotal-balance';
|
||||
} else {
|
||||
$grid->invalidFields(array('Debit', 'Credit'));
|
||||
$cols['Sub-Total']['index'] = 'subtotal-LedgerEntry.amount';
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
$grid->customData(compact('reconcile_id'))->limit(20);
|
||||
}
|
||||
|
||||
if (isset($collected_account_id)) {
|
||||
$config['action'] = 'collected';
|
||||
$grid->customData(compact('collected_account_id'))->limit(50);
|
||||
$grid->sortField('Last Payment');
|
||||
}
|
||||
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Customer', 'Unit'));
|
||||
|
||||
// Include custom data
|
||||
$grid->customData(compact('ledger_id', 'account_id', 'ar_account',
|
||||
'account_type', 'account_ftype', 'monetary_source_id',
|
||||
'customer_id', 'lease_id', 'transaction_id', 'group_by_tx'));
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Date', 'DESC')
|
||||
->defaultFields(array('Entry', 'Date', 'Amount'))
|
||||
->searchFields(array('Customer', 'Unit'))
|
||||
->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')
|
||||
);
|
||||
array_diff(array_keys($cols), array('Transaction', 'Debit', 'Credit',
|
||||
'Balance', 'Sub-Total', 'Comment')));
|
||||
|
||||
|
||||
@@ -2,23 +2,21 @@
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['ID'] = array('index' => 'id_sequence', 'formatter' => 'id');
|
||||
$cols['Account'] = array('index' => 'Account.name', 'formatter' => 'longname');
|
||||
//$cols['Open Date'] = array('index' => 'PriorClose.stamp', 'formatter' => 'date');
|
||||
$cols['Close Date'] = array('index' => 'Close.stamp', 'formatter' => 'date');
|
||||
$cols['Sequence'] = array('index' => 'Ledger.sequence', 'formatter' => 'id');
|
||||
$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', 'width' => '60', 'align' => 'right');
|
||||
$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');
|
||||
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Account', 'Comment'));
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('ID', 'DESC')
|
||||
->defaultFields(array('ID', 'Account'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
->sortField('Sequence')
|
||||
->defaultFields(array('Sequence'))
|
||||
->searchFields(array('Comment'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Account', 'Open Date', 'Comment')));
|
||||
|
||||
@@ -2,20 +2,17 @@
|
||||
|
||||
// Define the table columns
|
||||
$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');
|
||||
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Name'));
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Name')
|
||||
->defaultFields(array('ID', 'Name'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
->defaultFields(array('Name'))
|
||||
->searchFields(array('Name'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array()));
|
||||
|
||||
@@ -1,18 +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['Comment'] = array('index' => 'MonetarySource.comment', 'formatter' => 'comment');
|
||||
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('ID', 'Name'));
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('ID')
|
||||
->defaultFields(array('ID', 'Name'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
@@ -8,17 +8,78 @@
|
||||
* @package pmgr
|
||||
*/
|
||||
|
||||
foreach ($menu AS $item) {
|
||||
if (isset($item['header']))
|
||||
echo('<DIV CLASS="header">' . $item['name'] . '</DIV>' . "\n");
|
||||
elseif (isset($item['hr']))
|
||||
echo('<HR>' . "\n");
|
||||
elseif (isset($item['url']))
|
||||
echo('<DIV CLASS="item">'
|
||||
// REVISIT <AP>: 20090823
|
||||
// Add way to slide the entire menu off the page
|
||||
|
||||
// The sidemenu-container is necessary to define the
|
||||
// bounds as the parent of the sidemenu div, which will
|
||||
// be heavily manipulated by the accordion module. If
|
||||
// we don't have good control over the parent, the
|
||||
// accordion will get confused and behave poorly.
|
||||
echo('<DIV ID="sidemenu-container">' . "\n");
|
||||
echo('<DIV ID="sidemenu">' . "\n");
|
||||
|
||||
$section = 0;
|
||||
$active_section = null;
|
||||
foreach ($menu['areas'] AS $area_name => $area) {
|
||||
if (empty($area['subareas']))
|
||||
continue;
|
||||
|
||||
foreach ($area['subareas'] AS $subarea_name => $subarea) {
|
||||
if (empty($subarea['priorities']))
|
||||
continue;
|
||||
|
||||
if (!isset($active_section) &&
|
||||
!empty($menu['active']['area']) && $area_name == $menu['active']['area'] &&
|
||||
(empty($menu['active']['subarea']) || $subarea_name == $menu['active']['subarea']))
|
||||
$active_section = $section;
|
||||
|
||||
++$section;
|
||||
|
||||
echo('<H3' .
|
||||
//' id="sidemenu-section-'.$area_name.'-'.$subarea_name.'"' .
|
||||
' class="sidemenu-header">' .
|
||||
$subarea['name'] .
|
||||
"</H3>\n");
|
||||
|
||||
echo('<DIV class="sidemenu-content">' . "\n");
|
||||
foreach ($subarea['priorities'] AS $priority) {
|
||||
foreach ($priority AS $item) {
|
||||
if (isset($item['url'])) {
|
||||
echo('<DIV CLASS="sidemenu-item">'
|
||||
. $html->link($item['name'], $item['url'],
|
||||
isset($item['htmlAttributes']) ? $item['htmlAttributes'] : null,
|
||||
isset($item['confirmMessage']) ? $item['confirmMessage'] : null,
|
||||
isset($item['escapeTitle']) ? $item['escapeTitle'] : null)
|
||||
|
||||
. '</DIV>' . "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
echo('</DIV>' . "\n");
|
||||
}
|
||||
}
|
||||
|
||||
echo('</DIV>' . "\n"); // End #sidemenu
|
||||
echo('</DIV>' . "\n"); // End #sidemenu-container
|
||||
|
||||
// Uses both hoverintent, which is a more user friendly mechanism
|
||||
// than mouseover, as well as click. This provides 1) a workable
|
||||
// solution for those browsers that don't use pointers, such as
|
||||
// a touchscreen, and 2) a means to open the menu if the animation
|
||||
// was running while the user moved the pointer to a new menu area.
|
||||
$javascript->codeBlock(
|
||||
<<<JSCB
|
||||
jQuery(document).ready(function(){
|
||||
if (jQuery("#sidemenu").accordion != null) {
|
||||
jQuery("#sidemenu").accordion
|
||||
({ fillSpace : true,
|
||||
event : "click hoverintent",
|
||||
animated : "bounceslide"
|
||||
JSCB
|
||||
. (isset($active_section) ? ",\n\t active : $active_section\n" : '') .
|
||||
<<<JSCB
|
||||
});
|
||||
}
|
||||
});
|
||||
JSCB
|
||||
, array('inline' => false));
|
||||
|
||||
48
site/views/elements/statement_entries.ctp
Normal file
48
site/views/elements/statement_entries.ctp
Normal file
@@ -0,0 +1,48 @@
|
||||
<?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['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' => 'longenum');
|
||||
$cols['Account'] = array('index' => 'Account.name', 'formatter' => 'name');
|
||||
$cols['Debit'] = array('index' => 'charge', 'formatter' => 'currency');
|
||||
$cols['Credit'] = array('index' => 'disbursement', 'formatter' => 'currency');
|
||||
|
||||
$cols['Amount'] = array('index' => "StatementEntry.amount", 'formatter' => 'currency');
|
||||
$cols['Applied'] = array('index' => "applied", 'formatter' => 'currency');
|
||||
// 'balance' is already in use as part of charge/disbursement/balance.
|
||||
// 'unapplied' isn't quite the right term, but it's not customer visible.
|
||||
$cols['Balance'] = array('index' => "unapplied", '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', 'DESC')
|
||||
->defaultFields(array('Entry', 'Date', 'Charge', 'Payment'))
|
||||
->searchFields(array('Customer', 'Unit'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Transaction', 'Through', 'Lease',
|
||||
'Amount', 'Applied', 'Balance', 'Sub-Total',
|
||||
'Comment')));
|
||||
|
||||
@@ -52,8 +52,8 @@ if (isset($rows) && is_array($rows) && count($rows)) {
|
||||
|
||||
foreach ($rows AS $r => &$row) {
|
||||
foreach ($row AS $c => $col) {
|
||||
$cell_class = implode(" ", array_merge(isset( $row_class[$r]) ? $row_class[$r] : array(),
|
||||
isset($column_class[$c]) ? $column_class[$c] : array()));
|
||||
$cell_class = implode(" ", array_merge(empty( $row_class[$r]) ? array() : $row_class[$r],
|
||||
empty($column_class[$c]) ? array() : $column_class[$c]));
|
||||
if ($cell_class)
|
||||
$row[$c] = array($col, array('class' => $cell_class));
|
||||
}
|
||||
@@ -64,9 +64,12 @@ if (isset($rows) && is_array($rows) && count($rows)) {
|
||||
$class = implode(' ', $class);
|
||||
|
||||
// OK, output the table HTML
|
||||
echo('<TABLE' . (isset($class) ? ' CLASS="'.$class.'"' : '') . '>' . "\n");
|
||||
echo('<TABLE' .
|
||||
(empty($id) ? '' : ' ID="'.$id.'"') .
|
||||
(empty($class) ? '' : ' CLASS="'.$class.'"') .
|
||||
'>' . "\n");
|
||||
|
||||
if (isset($caption))
|
||||
if (!empty($caption))
|
||||
echo(' <CAPTION>' . $caption . '</CAPTION>' . "\n");
|
||||
|
||||
if (isset($headers) && is_array($headers)) {
|
||||
|
||||
20
site/views/elements/tenders.ctp
Normal file
20
site/views/elements/tenders.ctp
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$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('Comment', 'Sub-Total')));
|
||||
@@ -3,18 +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['Timestamp'] = array('index' => 'Transaction.stamp', 'formatter' => 'date');
|
||||
$cols['Due'] = array('index' => 'Transaction.due_date', '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');
|
||||
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Due', 'Comment'));
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('ID')
|
||||
->sortField('Timestamp', 'DESC')
|
||||
->defaultFields(array('ID', 'Timestamp'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
->searchFields(array('Type', 'Comment'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Comment')));
|
||||
|
||||
33
site/views/elements/unit_sizes.ctp
Normal file
33
site/views/elements/unit_sizes.ctp
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['Size'] = array('index' => 'UnitSize.name', 'formatter' => 'shortname');
|
||||
$cols['Width'] = array('index' => 'UnitSize.width', 'formatter' => 'number');
|
||||
$cols['Depth'] = array('index' => 'UnitSize.depth', 'formatter' => 'number');
|
||||
$cols['Height'] = array('index' => 'UnitSize.height', 'formatter' => 'number');
|
||||
$cols['Area'] = array('index' => 'sqft', 'formatter' => 'number');
|
||||
$cols['Volume'] = array('index' => 'cuft', 'formatter' => 'number');
|
||||
$cols['Deposit'] = array('index' => 'UnitSize.deposit', 'formatter' => 'currency');
|
||||
$cols['Rent'] = array('index' => 'UnitSize.rent', 'formatter' => 'currency');
|
||||
$cols['A. Cost'] = array('index' => 'sqcost', 'formatter' => 'currency');
|
||||
$cols['V. Cost'] = array('index' => 'cucost', 'formatter' => 'currency');
|
||||
$cols['Unavailable'] = array('index' => 'unavailable', 'formatter' => 'number');
|
||||
$cols['Occupied'] = array('index' => 'occupied', 'formatter' => 'number');
|
||||
$cols['Available'] = array('index' => 'available', 'formatter' => 'number');
|
||||
$cols['Total'] = array('index' => 'units', 'formatter' => 'number');
|
||||
$cols['Occupancy'] = array('index' => 'occupancy', 'formatter' => 'percentage', 'formatoptions' => array('precision' => 0));
|
||||
$cols['Vacancy'] = array('index' => 'vacancy', 'formatter' => 'percentage', 'formatoptions' => array('precision' => 0));
|
||||
$cols['Comment'] = array('index' => 'Unit.comment', 'formatter' => 'comment');
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Area')
|
||||
->defaultFields(array('Size', 'Area'))
|
||||
->searchFields(array('Size', 'Width', 'Depth', 'Area', 'Deposit', 'Rent'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Height', 'Volume',
|
||||
'A. Cost', 'V. Cost',
|
||||
'Occupied', 'Total', 'Occupancy', 'Vacancy',
|
||||
'Comment')));
|
||||
@@ -3,22 +3,24 @@
|
||||
// Define the table columns
|
||||
$cols = array();
|
||||
$cols['Sort'] = array('index' => 'Unit.sort_order', 'hidden' => true);
|
||||
//$cols['Sort'] = array('index' => 'Unit.sort_order');
|
||||
//$cols['Walk'] = array('index' => 'Unit.walk_order');
|
||||
$cols['ID'] = array('index' => 'Unit.id', 'formatter' => 'id');
|
||||
$cols['Unit'] = array('index' => 'Unit.name', 'width' => '50');
|
||||
$cols['Size'] = array('index' => 'UnitSize.name', 'width' => '75');
|
||||
$cols['Status'] = array('index' => 'Unit.status', 'width' => '75');
|
||||
$cols['Walk'] = array('index' => 'Unit.walk_order', 'formatter' => 'number');
|
||||
$cols['Unit'] = array('index' => 'Unit.name', 'formatter' => 'shortname');
|
||||
$cols['Size'] = array('index' => 'UnitSize.name', 'formatter' => 'shortname');
|
||||
$cols['Area'] = array('index' => 'sqft', 'formatter' => 'number');
|
||||
$cols['Rent'] = array('index' => 'Unit.rent', 'formatter' => 'currency');
|
||||
$cols['Deposit'] = array('index' => 'Unit.deposit', 'formatter' => 'currency');
|
||||
$cols['Status'] = array('index' => 'Unit.status', 'formatter' => 'enum');
|
||||
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency');
|
||||
$cols['Comment'] = array('index' => 'Unit.comment', 'formatter' => 'comment');
|
||||
|
||||
// Set up search fields if requested by caller
|
||||
if (isset($searchfields))
|
||||
$grid->searchFields(array('Unit', 'Size', 'Status'));
|
||||
if (in_array($this->params['action'], array('vacant', 'unavailable')))
|
||||
$grid->invalidFields('Balance');
|
||||
|
||||
// Render the grid
|
||||
$grid
|
||||
->columns($cols)
|
||||
->sortField('Sort')
|
||||
->defaultFields(array('Sort', 'ID', 'Unit'))
|
||||
->render($this, isset($config) ? $config : null);
|
||||
->defaultFields(array('Sort', 'Unit'))
|
||||
->searchFields(array('Unit', 'Size', 'Status'))
|
||||
->render($this, isset($config) ? $config : null,
|
||||
array_diff(array_keys($cols), array('Walk', 'Deposit', 'Comment')));
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php /* -*- mode:PHP -*- */
|
||||
if (!empty($message))
|
||||
echo $message;
|
||||
|
||||
@@ -19,14 +19,26 @@ class FormatHelper extends AppHelper {
|
||||
true));
|
||||
}
|
||||
|
||||
function currency($amount, $spans = false) {
|
||||
function currency($amount, $spans = false, $dollar_sign = null) {
|
||||
if (!isset($amount))
|
||||
return '-';
|
||||
//return null;
|
||||
|
||||
$currency = self::$number->currency($amount,
|
||||
'USD',
|
||||
$spans ? array('before'=>'', 'after'=>'') : array());
|
||||
// Use of the $number->currency() function results in the clever,
|
||||
// but problematic, use of cents for amounts less than $1. For
|
||||
// example, 50 cents is shown as '50c', not '$0.50'. We want to
|
||||
// keep everything in terms of dollars, especially for the cases
|
||||
// where this result is placed into a form for input. 50 cents
|
||||
// will end up as 50 dollars upon submission :-(
|
||||
$currency = self::$number->format
|
||||
(abs($amount),
|
||||
array('places' => 2,
|
||||
'before' => $spans ? '' : (isset($dollar_sign) ? $dollar_sign : '$'),
|
||||
'after' => $spans ? '' : null,
|
||||
));
|
||||
|
||||
if ($amount < 0)
|
||||
$currency = '(' . $currency . ')';
|
||||
|
||||
if ($spans)
|
||||
return ('<SPAN CLASS="dollar-sign">$</SPAN>' .
|
||||
@@ -35,19 +47,29 @@ class FormatHelper extends AppHelper {
|
||||
return $currency;
|
||||
}
|
||||
|
||||
function date($date, $age = false) {
|
||||
function date($date, $age = false, $class = null, $time = false) {
|
||||
if (!$date) return null;
|
||||
|
||||
$date_fmt = 'm/d/Y';
|
||||
return (self::$time->format($date_fmt, $date) .
|
||||
($age
|
||||
? ' (' . self::age($date) . ')'
|
||||
: ''));
|
||||
if (empty($class))
|
||||
$class = '';
|
||||
|
||||
if ($time)
|
||||
$date_html = self::$time->nice($date);
|
||||
else
|
||||
$date_html = self::$time->format('m/d/Y', $date);
|
||||
|
||||
$date_html = '<span class="fmt-date '.$class.'">'.$date_html.'</span>';
|
||||
|
||||
if ($age) {
|
||||
$date_html .= ' (' . self::age($date, $class, true, $time ? 0 : 60*60*24) . ')';
|
||||
$date_html = '<span class="fmt-dateage '.$class.'">'.$date_html.'</span>';
|
||||
}
|
||||
|
||||
function datetime($datetime) {
|
||||
if (!$datetime) return null;
|
||||
return self::$time->nice($datetime);
|
||||
return $date_html;
|
||||
}
|
||||
|
||||
function datetime($datetime, $age = false, $class = null) {
|
||||
return self::date($datetime, $age, $class, true);
|
||||
}
|
||||
|
||||
function phone($phone, $ext = null) {
|
||||
@@ -78,10 +100,13 @@ class FormatHelper extends AppHelper {
|
||||
return $comment;
|
||||
}
|
||||
|
||||
function age($datetime) {
|
||||
function age($datetime, $class, $suffix = false, $min_span = 0) {
|
||||
if (!isset($datetime))
|
||||
return null;
|
||||
|
||||
if (empty($class))
|
||||
$class = '';
|
||||
|
||||
$now = time();
|
||||
$seconds = self::$time->fromString($datetime);
|
||||
$backwards = ($seconds > $now);
|
||||
@@ -90,39 +115,58 @@ class FormatHelper extends AppHelper {
|
||||
$timeto = $backwards ? $seconds : $now;
|
||||
$span = $timeto - $timefrom;
|
||||
|
||||
// Display seconds if under 45 seconds
|
||||
//pr(compact('now', 'seconds', 'backwards', 'timefrom', 'timeto', 'span', 'min_span'));
|
||||
|
||||
// If now, just use 'now'
|
||||
if ($span === 0) {
|
||||
return __('now', true);
|
||||
$approx = 0;
|
||||
$unit = 'now';
|
||||
}
|
||||
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);
|
||||
$unit = 'minute';
|
||||
}
|
||||
// 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';
|
||||
|
||||
// Display minutes if under 45 minutes
|
||||
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
|
||||
if (!isset($approx)) {
|
||||
$unit = 'hour';
|
||||
$span /= 60; $min_span /= 60;
|
||||
if ($span < 18 && ($span >= $min_span || $min_span <= 1))
|
||||
$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
|
||||
@@ -132,20 +176,48 @@ class FormatHelper extends AppHelper {
|
||||
if ((abs($span - $approx) * (365.2425 / 12)) < 3)
|
||||
$relative = 'about';
|
||||
}
|
||||
else {
|
||||
$span /= 12;
|
||||
$approx = round($span);
|
||||
$unit = 'year';
|
||||
}
|
||||
|
||||
return (__(isset($relative)
|
||||
? $relative
|
||||
: ($approx == $span
|
||||
? ''
|
||||
: ($approx > $span ? 'almost' : 'over')), true)
|
||||
. ' '
|
||||
. self::_n($approx, $unit)
|
||||
. ($backwards ? '' : __(' ago', true)));
|
||||
// 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 == 'now')
|
||||
$age = 'now';
|
||||
elseif ($unit == 'day')
|
||||
$age = 'today';
|
||||
else
|
||||
$age = 'this ' . $unit;
|
||||
}
|
||||
else {
|
||||
if (isset($relative))
|
||||
$age = $relative;
|
||||
elseif ($approx > $span)
|
||||
$age = 'almost';
|
||||
elseif ($approx < $span)
|
||||
$age = 'over';
|
||||
else
|
||||
$age = '';
|
||||
|
||||
$age .= ' ' . self::_n($approx, $unit);
|
||||
|
||||
if ($suffix) {
|
||||
if ($backwards)
|
||||
$age .= ' from now';
|
||||
else
|
||||
$age .= ' ago';
|
||||
}
|
||||
}
|
||||
|
||||
$age = '<span class="fmt-age '.$class.'">'.__($age, true).'</span>';
|
||||
|
||||
return $age;
|
||||
}
|
||||
|
||||
/*****************************
|
||||
@@ -196,14 +268,21 @@ class FormatHelper extends AppHelper {
|
||||
|
||||
|
||||
// Helper function to convert PHP vars to javascript
|
||||
function phpVarToJavascript($var, $name = '', $depth='', $special = false) {
|
||||
function phpVarToJavascript($var, $name = '', $depth='', $special = false, $pretty = false) {
|
||||
|
||||
// Establish a prefix to use before printing $var
|
||||
if ($pretty) {
|
||||
$prefix = $depth;
|
||||
$pretty_sp = " ";
|
||||
$pretty_nl = "\n";
|
||||
}
|
||||
else {
|
||||
$prefix = $pretty_sp = $pretty_nl = '';
|
||||
}
|
||||
|
||||
// If given a name, set it up JS style
|
||||
if ($name)
|
||||
$prefix .= $name . ": ";
|
||||
$prefix .= $name . ":" . $pretty_sp;
|
||||
|
||||
if (!isset($var))
|
||||
return $prefix . 'null';
|
||||
@@ -256,22 +335,22 @@ class FormatHelper extends AppHelper {
|
||||
// PHP array indices can be a mix of integer and string based.
|
||||
// Just guess here, unless flagged as a special case.
|
||||
if (isset($var[0]) || $special)
|
||||
return ($prefix . "[\n"
|
||||
. implode(",\n",
|
||||
return ($prefix . "[" . $pretty_nl
|
||||
. implode("," . $pretty_nl,
|
||||
array_map(array('FormatHelper', 'phpVarToJavascript'),
|
||||
array_values($var),
|
||||
array(),
|
||||
array_fill(0, count($var), $depth.' ')
|
||||
))
|
||||
. "\n$depth]");
|
||||
. ($pretty ? "\n$depth" : '') . "]");
|
||||
|
||||
return ($prefix . "{\n"
|
||||
. implode(",\n",
|
||||
return ($prefix . "{" . $pretty_nl
|
||||
. implode("," . $pretty_nl,
|
||||
array_map(array('FormatHelper', 'phpVarToJavascript'),
|
||||
array_values($var), array_keys($var),
|
||||
array_fill(0, count($var), $depth.' ')
|
||||
))
|
||||
. "\n$depth}");
|
||||
. ($pretty ? "\n$depth" : '') . "}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ class GridHelper extends AppHelper {
|
||||
var $included, $invalid;
|
||||
var $columns;
|
||||
var $controller;
|
||||
static $first_grid = true;
|
||||
|
||||
function __construct() {
|
||||
$this->reset();
|
||||
@@ -91,6 +92,7 @@ class GridHelper extends AppHelper {
|
||||
= array_map(create_function('$data',
|
||||
'return $data["id"];'),
|
||||
$items);
|
||||
$this->jqGrid_options['action'] = 'idlist';
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -159,14 +161,24 @@ class GridHelper extends AppHelper {
|
||||
$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));
|
||||
// Defined the columns, based on the inclusion set,
|
||||
// remapping column names as necessary.
|
||||
$this->jqGrid_options['jqGridColumns'] = array();
|
||||
foreach (array_intersect_key($this->columns, array_flip($included)) AS $name => $col) {
|
||||
if (!empty($config['remap'][$name]))
|
||||
$name = $config['remap'][$name];
|
||||
$this->jqGrid_options['jqGridColumns'][$name] = $col;
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -177,8 +189,17 @@ class GridHelper extends AppHelper {
|
||||
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;
|
||||
@@ -198,7 +219,12 @@ class GridHelper extends AppHelper {
|
||||
if (isset($config))
|
||||
$this->jqGrid_options = array_merge($this->jqGrid_options, $config);
|
||||
|
||||
// Set flag whether or not this is the first grid
|
||||
$this->jqGrid_options['first_grid'] = self::$first_grid;
|
||||
|
||||
//pr(compact('config') + array('jqGrid_options' => $this->jqGrid_options));
|
||||
echo $view->element('jqGrid', $this->jqGrid_options);
|
||||
self::$first_grid = false;
|
||||
|
||||
// Since we only have one instance of this class
|
||||
// as a helper, we must assume it could be used
|
||||
|
||||
@@ -24,26 +24,80 @@
|
||||
?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<?php
|
||||
/* print("<!--\n"); */
|
||||
/* print("SERVER = "); print_r($_SERVER); print("\n"); */
|
||||
/* print("REQUEST = "); print_r($_REQUEST); print("\n"); */
|
||||
/* print("COOKIE = "); print_r($_COOKIE); print("\n"); */
|
||||
/* print("-->\n"); */
|
||||
?>
|
||||
<head>
|
||||
<?php echo $html->charset(); ?>
|
||||
<title>
|
||||
<?php if (devbox()) echo "*DEVBOX* "; ?>
|
||||
<?php if (sandbox()) echo "*SANDBOX* "; ?>
|
||||
Property Manager: <?php echo $title_for_layout; ?>
|
||||
<?php if (sandbox()) echo " *SANDBOX*"; ?>
|
||||
<?php if (devbox()) echo " *DEVBOX*"; ?>
|
||||
</title>
|
||||
<?php
|
||||
// Reset the __scripts variable, which has already been dumped to
|
||||
// $scripts_for_layout. Elements/Helpers used in the layout may
|
||||
// also have some scripts to add. They cannot be put into the head
|
||||
// but we can at least put them into a relatively benign place, so
|
||||
// scripts don't have to be dumped inline in possibly awkward spots.
|
||||
// Oh, and yes... I know we're not supposed to be using this variable
|
||||
// directly, and will possibly get burned someday. Oh well, Cake
|
||||
// hasn't left us a lot of choice, besides writing our own scripts
|
||||
// mechanism _additional_ to what Cake has provided :-/
|
||||
$this->__scripts = array();
|
||||
|
||||
if (!empty($_SERVER['HTTPS']))
|
||||
$protocol = 'https://';
|
||||
else
|
||||
$protocol = 'http://';
|
||||
|
||||
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";
|
||||
//echo $html->css('jquery/dotluv/ui.all') . "\n";
|
||||
echo $html->css('jquery/start/ui.all') . "\n";
|
||||
echo $javascript->link('jquery/jquery') . "\n";
|
||||
echo $javascript->link('jquery/jquery-ui') . "\n";
|
||||
|
||||
$theme = 'smoothness';
|
||||
$theme = 'base';
|
||||
$theme = 'dotluv';
|
||||
$theme = 'dark-hive';
|
||||
$theme = 'start';
|
||||
if (devbox())
|
||||
$theme = 'dotluv';
|
||||
if (sandbox())
|
||||
$theme = 'darkness';
|
||||
|
||||
echo $html->css('themes/'.$theme.'/ui.all') . "\n";
|
||||
|
||||
echo $javascript->link('jquery-1.3.2.min') . "\n";
|
||||
echo $javascript->link('jquery-ui-1.7.2.custom.min') . "\n";
|
||||
//echo $javascript->link($protocol . 'ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js') . "\n";
|
||||
//echo $javascript->link($protocol . 'ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.js') . "\n";
|
||||
//echo $javascript->link($protocol . 'ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js') . "\n";
|
||||
//echo $javascript->link($protocol . 'ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js') . "\n";
|
||||
echo $javascript->link('jquery.form') . "\n";
|
||||
echo $javascript->link('jquery.hoverIntent') . "\n";
|
||||
echo $javascript->link('pmgr') . "\n";
|
||||
echo $scripts_for_layout . "\n";
|
||||
?>
|
||||
|
||||
<?php if ($this->params['action'] !== 'INTERNAL_ERROR'): ?>
|
||||
<script type="text/javascript"><!--
|
||||
if (typeof(jQuery) == 'undefined') {
|
||||
window.location.href =
|
||||
"<?php echo $html->url(array('controller' => 'util',
|
||||
'action' => 'INTERNAL_ERROR',
|
||||
'jQuery NOT LOADED!')); ?>";
|
||||
}
|
||||
--></script>
|
||||
<?php endif; ?>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -88,5 +142,7 @@
|
||||
<?php echo $cakeDebug; ?>
|
||||
<?php /* pr($this); */ ?>
|
||||
|
||||
<?php echo implode("\n", $this->__scripts) . "\n"; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user